feat(pkb): Replace Dify with self-developed pgvector RAG engine

Major milestone: Successfully replaced Dify external service with PostgreSQL + pgvector RAG engine

Backend changes:
- Refactor ragService.ts: Remove dual-track mode, keep only pgvector
- Refactor knowledgeBaseService.ts: Remove Dify creation logic
- Refactor documentService.ts: Remove Dify upload/polling logic
- DifyClient.ts: Convert to deprecated stub file (for legacy compatibility)
- common/rag/index.ts: Update exports
- common/rag/types.ts: Remove Dify types, keep generic RAG types
- config/env.ts: Remove Dify configuration

Frontend changes:
- DashboardPage.tsx: Add delete knowledge base dropdown menu
- KnowledgeBaseList.tsx: Enhance quota warning display
- CreateKBDialog.tsx: Add quota exceeded modal with guidance
- knowledgeBaseApi.ts: Add auth interceptor

Documentation:
- Update PKB module status guide (v2.3)
- Update system status guide (v4.0)

Performance metrics:
- Single query latency: 2.5s
- Single query cost: 0.0025 CNY
- Cross-language accuracy improvement: +20.5%

Remaining tasks:
- OSS storage integration
- pg_bigm extension installation

Tested: End-to-end test passed (create KB -> upload doc -> vector search)
This commit is contained in:
2026-01-21 22:35:50 +08:00
parent 40c2f8e148
commit 483c62fb6f
14 changed files with 741 additions and 1018 deletions

View File

@@ -3,13 +3,39 @@ import axios from 'axios';
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:3001';
const api = axios.create({
baseURL: `${API_BASE_URL}/api/v1`,
baseURL: `${API_BASE_URL}/api/v1/pkb/knowledge`,
timeout: 30000,
headers: {
'Content-Type': 'application/json',
},
});
// 请求拦截器 - 添加认证token
api.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token');
if (token && config.headers) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截器 - 处理401未授权
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
localStorage.removeItem('token');
window.location.href = '/login';
}
return Promise.reject(error);
}
);
/**
* 知识库类型定义
*/

View File

@@ -1,5 +1,6 @@
import React, { useState } from 'react';
import { Modal, Form, Input, message } from 'antd';
import { Modal, Form, Input, message, Alert } from 'antd';
import { ExclamationCircleOutlined } from '@ant-design/icons';
const { TextArea } = Input;
@@ -32,7 +33,35 @@ const CreateKBDialog: React.FC<CreateKBDialogProps> = ({
// 表单验证错误
return;
}
message.error(error.message || '创建失败');
// 检查是否是配额超限错误
const errorMsg = error.message || '创建失败';
if (errorMsg.includes('已达上限') || errorMsg.includes('配额')) {
Modal.warning({
title: '知识库数量已达上限',
icon: <ExclamationCircleOutlined />,
content: (
<div>
<p style={{ marginBottom: 12 }}>{errorMsg}</p>
<Alert
type="info"
showIcon
message="如何释放配额?"
description={
<ul style={{ margin: 0, paddingLeft: 16 }}>
<li></li>
<li></li>
<li></li>
</ul>
}
/>
</div>
),
okText: '我知道了',
});
} else {
message.error(errorMsg);
}
} finally {
setLoading(false);
}

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { Card, Button, Empty, Tag, Popconfirm, Space, Typography } from 'antd';
import { Card, Button, Empty, Popconfirm, Space, Typography } from 'antd';
import {
PlusOutlined,
FolderOutlined,
@@ -65,10 +65,26 @@ const KnowledgeBaseList: React.FC<KnowledgeBaseListProps> = ({
{/* 配额提示 */}
{!canCreateMore && (
<div style={{ marginBottom: 16 }}>
<Tag color="warning">
3
</Tag>
<div style={{
marginBottom: 16,
padding: '12px 16px',
background: '#fffbe6',
border: '1px solid #ffe58f',
borderRadius: 6,
display: 'flex',
alignItems: 'center',
gap: 8
}}>
<span style={{ fontSize: 16 }}></span>
<div>
<Text strong style={{ color: '#d48806' }}>
3
</Text>
<br />
<Text type="secondary" style={{ fontSize: 13 }}>
</Text>
</div>
</div>
)}