feat(pkb): Complete PKB module frontend migration with V3 design

Summary:
- Implement PKB Dashboard and Workspace pages based on V3 prototype
- Add single-layer header with integrated Tab navigation
- Implement 3 work modes: Full Text, Deep Read, Batch Processing
- Integrate Ant Design X Chat component for AI conversations
- Create BatchModeComplete with template selection and document processing
- Add compact work mode selector with dropdown design

Backend:
- Migrate PKB controllers and services to /modules/pkb structure
- Register v2 API routes at /api/v2/pkb/knowledge
- Maintain dual API routes for backward compatibility

Technical details:
- Use Zustand for state management
- Handle SSE streaming responses for AI chat
- Support document selection for Deep Read mode
- Implement batch processing with progress tracking

Known issues:
- Batch processing API integration pending
- Knowledge assets page navigation needs optimization

Status: Frontend functional, pending refinement
This commit is contained in:
2026-01-06 22:15:42 +08:00
parent b31255031e
commit 5a17d096a7
226 changed files with 14899 additions and 224 deletions

View File

@@ -0,0 +1,787 @@
# PKB个人知识库功能审查报告 - 阶段0
> **审查日期:** 2026-01-06
> **审查人员:** AI助手
> **审查目标:** 深入理解PKB现有功能为安全迁移做准备
> **状态:** ✅ 进行中
---
## 📋 执行摘要
### 关键发现
**🎯 PKB系统实际上是两个紧密关联的功能模块**
```
Part 1: PKB知识库管理模块
├─ 位置backend/src/legacy/controllers/knowledgeBaseController.ts
├─ 功能:创建、编辑、删除知识库;上传、管理文档
└─ 数据库pkb_schema独立Schema无需迁移
Part 2: AIA智能问答模块中的PKB应用
├─ 位置backend/src/legacy/controllers/chatController.ts
├─ 功能使用知识库进行智能问答3种工作模式
└─ 工作模式:
├─ 全文阅读模式35-50篇文献综合分析
├─ 逐篇精读模式1-5篇文献深度分析
└─ 批处理模式3-50篇文献批量提取
```
---
## 📊 Part 1: PKB知识库管理模块
### 1.1 文件结构
```
backend/src/legacy/
├─ controllers/
│ ├─ knowledgeBaseController.ts # API控制器342行
│ └─ documentController.ts # 文档上传控制器
├─ services/
│ ├─ knowledgeBaseService.ts # 业务逻辑365行
│ ├─ documentService.ts # 文档处理服务
│ └─ tokenService.ts # Token计算和文档选择
└─ routes/
└─ knowledgeBases.ts # 路由定义
```
### 1.2 核心API端点
#### 知识库管理API
```typescript
// 1. 创建知识库
POST /api/v1/knowledge/create
Body: { name: string, description?: string }
kbQuota vs kbUsed
Dify创建Dataset
// 2. 获取知识库列表
GET /api/v1/knowledge/list
+
// 3. 获取知识库详情
GET /api/v1/knowledge/:id
+
// 4. 更新知识库
PUT /api/v1/knowledge/:id
Body: { name?: string, description?: string }
// 5. 删除知识库
DELETE /api/v1/knowledge/:id
Dify Dataset
documents自动删除
// 6. 检索知识库RAG
GET /api/v1/knowledge/:id/search?query=xxx&top_k=15
Dify retrieveKnowledge API
15
// 7. 获取知识库统计
GET /api/v1/knowledge/:id/stats
Token数
// 8. 获取文档选择(全文阅读模式)
GET /api/v1/knowledge/:id/document-selection?max_files=7&max_tokens=750000
Token限制
```
#### 文档管理API
```typescript
// 9. 上传文档
POST /api/v1/documents/upload
Multipart: { file, kbId }
OSS
PDF/Word/TXT/Markdown
Dify进行索引
uploadingparsingindexingcompleted
// 10. 获取文档详情
GET /api/v1/documents/:id
// 11. 删除文档
DELETE /api/v1/documents/:id
Dify删除Document
OSS删除文件
```
### 1.3 数据库Schema
#### 表结构在pkb_schema中
```sql
-- 知识库表
knowledge_bases
id (UUID, PK)
userId (String)
name (String)
description (String?)
difyDatasetId (String, UNIQUE) -- Dify中的Dataset ID
fileCount (Int, default: 0)
totalSizeBytes (BigInt, default: 0)
createdAt (DateTime)
updatedAt (DateTime)
-- 文档表
documents
id (UUID, PK)
kbId (String, FK knowledge_bases.id)
userId (String)
filename (String)
fileType (String) -- pdf/docx/txt/md
fileSizeBytes (BigInt)
fileUrl (String) -- OSS URL
difyDocumentId (String) -- Dify中的Document ID
status (String) -- uploading/parsing/indexing/completed/error
progress (Int, 0-100)
errorMessage (String?)
segmentsCount (Int?) -- Dify索引的片段数
tokensCount (Int?) -- 总Token数
charCount (Int?) -- 字符数
language (String?)
extractedText (String?) -- 提取的全文(用于全文阅读模式)
extractionMethod (String?) -- marker/pymupdf/docx
extractionQuality (Float?)
uploadedAt (DateTime)
processedAt (DateTime?)
-- 批处理任务表
batch_tasks
id (UUID, PK)
userId (String)
kbId (String, FK knowledge_bases.id)
name (String)
templateType (String)
templateId (String?)
prompt (String)
status (String) -- pending/running/completed/failed
totalDocuments (Int)
completedCount (Int, default: 0)
failedCount (Int, default: 0)
modelType (String)
concurrency (Int, default: 3)
startedAt (DateTime?)
completedAt (DateTime?)
durationSeconds (Int?)
createdAt (DateTime)
updatedAt (DateTime)
-- 批处理结果表
batch_results
id (UUID, PK)
taskId (String, FK batch_tasks.id)
documentId (String, FK documents.id)
status (String) -- success/failed
data (Json?) -- 提取的结构化数据
rawOutput (String?) -- LLM原始输出
errorMessage (String?)
processingTimeMs (Int?)
tokensUsed (Int?)
createdAt (DateTime)
-- 任务模板表
task_templates
id (UUID, PK)
userId (String)
name (String)
description (String?)
prompt (String)
isPublic (Boolean, default: false)
outputFields (Json) -- 期望的输出字段
createdAt (DateTime)
updatedAt (DateTime)
```
#### 索引
```sql
-- knowledge_bases
idx_pkb_knowledge_bases_user_id (userId)
idx_pkb_knowledge_bases_dify_dataset_id (difyDatasetId)
-- documents
idx_pkb_documents_kb_id (kbId)
idx_pkb_documents_user_id (userId)
idx_pkb_documents_status (status)
idx_pkb_documents_dify_document_id (difyDocumentId)
idx_pkb_documents_extraction_method (extractionMethod)
-- batch_tasks
idx_pkb_batch_tasks_kb_id (kbId)
idx_pkb_batch_tasks_user_id (userId)
idx_pkb_batch_tasks_status (status)
idx_pkb_batch_tasks_created_at (createdAt)
-- batch_results
idx_pkb_batch_results_task_id (taskId)
idx_pkb_batch_results_document_id (documentId)
idx_pkb_batch_results_status (status)
```
### 1.4 关键业务逻辑
#### 配额管理
```typescript
// 用户表在platform_schema.users中的字段
kbQuota: Int @default(3) // 知识库配额
kbUsed: Int @default(0) // 已使用数量
// 创建知识库时检查
if (user.kbUsed >= user.kbQuota) {
throw new Error('配额已满');
}
// 创建成功后增加计数
await prisma.user.update({
data: { kbUsed: { increment: 1 } }
});
// 删除知识库时减少计数
await prisma.user.update({
data: { kbUsed: { decrement: 1 } }
});
```
#### Dify集成
```typescript
// 创建知识库 → 创建Dify Dataset
const difyDataset = await difyClient.createDataset({
name: `${userId}_${name}_${Date.now()}`,
description,
indexing_technique: 'high_quality',
});
// 检索知识库 → 调用Dify RAG
const results = await difyClient.retrieveKnowledge(
difyDatasetId,
query,
{
retrieval_model: {
search_method: 'semantic_search',
top_k: 15,
},
}
);
```
#### 文档Token计算tokenService.ts
```typescript
// Token计算规则
const TOKEN_LIMITS = {
MAX_FILES: 7, // 最多7篇文献
MAX_TOTAL_TOKENS: 750000, // 总Token限制Qwen-Long: 1M上下文 - 250K对话空间
MAX_SINGLE_DOC_TOKENS: 200000, // 单篇文献最大Token数
};
// 智能选择算法
function selectDocumentsForFullText(
documentTokens,
maxFiles,
maxTokens
) {
// 按Token数升序排序
const sorted = documentTokens.sort((a, b) => a.tokens - b.tokens);
// 贪心算法选择
let totalTokens = 0;
let selectedCount = 0;
const selected = [];
for (const doc of sorted) {
if (selectedCount >= maxFiles) break;
if (totalTokens + doc.tokens > maxTokens) break;
if (doc.tokens > MAX_SINGLE_DOC_TOKENS) continue; // 跳过超大文档
selected.push(doc);
totalTokens += doc.tokens;
selectedCount++;
}
return { selected, totalTokens, excludedDocs };
}
```
---
## 📊 Part 2: AIA模块中的PKB应用
### 2.1 文件结构
```
backend/src/legacy/controllers/
└─ chatController.ts # 通用对话控制器包含3种模式
frontend/src/
├─ pages/ChatPage.tsx # 主对话页面
└─ components/
├─ FullTextMode.tsx # 全文阅读模式组件
├─ DeepReadMode.tsx # 逐篇精读模式组件
└─ BatchMode.tsx # 批处理模式组件
```
### 2.2 三种工作模式详解
#### 模式1全文阅读模式Full Text Mode
**用途**35-50篇文献的综合分析
**实现原理:**
```typescript
// 1. 前端:用户进入知识库模式 → 选择"全文阅读"
const modeState = {
baseMode: 'knowledge_base',
kbMode: 'full_text',
selectedKbId: 'xxx',
};
// 2. 前端:智能加载文献
const selection = await knowledgeBaseApi.getDocumentSelection(kbId, {
max_files: 7,
max_tokens: 750000,
});
// 返回:{ selectedDocuments[], excludedDocuments[], totalTokens }
// 3. 前端自动切换到Qwen-Long模型
if (modeState.kbMode === 'full_text') {
setSelectedModel('qwen-long'); // 1M上下文
showToast('已自动切换到Qwen-Long模型支持1M上下文');
}
// 4. 前端发送消息时传递文档ID列表
await chatApi.sendMessageStream({
content: userQuestion,
modelType: 'qwen-long',
fullTextDocumentIds: loadedDocs.map(d => d.id), // ✅ 关键参数
conversationId,
});
// 5. 后端:加载完整全文
if (fullTextDocumentIds && fullTextDocumentIds.length > 0) {
const documents = await prisma.document.findMany({
where: { id: { in: fullTextDocumentIds } },
select: { id, filename, extractedText, tokensCount },
});
// 6. 组装全文上下文
const fullTextParts = [];
for (let i = 0; i < documents.length; i++) {
const doc = documents[i];
const docNumber = i + 1;
// 格式【文献N文件名】\n全文内容
fullTextParts.push(
`【文献${docNumber}${doc.filename}\n\n${doc.extractedText}`
);
// 添加引用信息
allCitations.push({
id: docNumber,
fileName: doc.filename,
score: 1.0, // 全文相关度100%
content: doc.extractedText.substring(0, 200),
});
}
knowledgeBaseContext = fullTextParts.join('\n\n---\n\n');
}
// 7. 传递给LLM
const systemPrompt = '你是专业的学术文献分析助手。每篇文献用【文献N文件名】标记。请认真阅读所有文献进行深入的综合分析。在回答时请引用具体文献使用【文献N】格式。';
const userContent = `${userQuestion}\n\n## 参考资料(文献全文)\n\n${knowledgeBaseContext}`;
const messages = [
{ role: 'system', content: systemPrompt },
...historyMessages, // 对话历史
{ role: 'user', content: userContent },
];
// 8. 调用Qwen-Long
const response = await LLMFactory.getAdapter('qwen-long').chatStream(messages, {
temperature: 0.7,
maxTokens: 6000, // 全文模式需要更长的回答空间
});
```
**关键特点:**
- ✅ 传递完整全文不是RAG片段
- ✅ 智能选择文献基于Token限制
- ✅ 文献来源标记【文献N文件名】
- ✅ 自动切换到Qwen-Long模型1M上下文
- ✅ 100%相关度(因为是全文)
- ✅ 适合跨文献比较、趋势分析、研究方法归纳
**Token使用**
```
上下文:~750K tokens7篇文献全文
对话空间:~250K tokens
输出长度6000 tokens综合分析需要更长回答
```
---
#### 模式2逐篇精读模式Deep Read Mode
**用途**1-5篇文献的深度分析
**实现原理:**
```typescript
// 1. 前端:用户选择"逐篇精读"
const modeState = {
baseMode: 'knowledge_base',
kbMode: 'deep_read',
selectedKbId: 'xxx',
};
// 2. 前端:用户选择要精读的文档
const selectedDocs = [doc1, doc2, doc3]; // 用户手动选择
// 3. 前端:切换到某个文档
const currentDoc = selectedDocs[0];
// 4. 前端发送消息时传递当前文档ID用于RAG过滤
await chatApi.sendMessageStream({
content: userQuestion,
modelType: selectedModel,
knowledgeBaseIds: [kbId], // 知识库ID
documentIds: [currentDoc.id], // ✅ 关键:只检索当前文档
conversationId: currentDocConversationId, // 每个文档独立对话
});
// 5. 后端RAG检索限定在特定文档
if (documentIds && documentIds.length > 0) {
// 调用Dify RAG但会限定在指定文档范围
const results = await difyClient.retrieveKnowledge(
difyDatasetId,
query,
{
retrieval_model: {
search_method: 'semantic_search',
top_k: 15,
document_ids: documentIds, // ✅ Dify会只检索这些文档
},
}
);
}
```
**关键特点:**
- ✅ 基于RAG检索不是全文
- ✅ 限定在当前文档范围
- ✅ 每个文档有独立的对话历史
- ✅ 用户可以在文档间切换
- ✅ 适合深度理解单篇文献
---
#### 模式3批处理模式Batch Mode
**用途**3-50篇文献的批量信息提取
**实现原理:**
```typescript
// 1. 用户创建批处理任务
POST /api/v1/batch-tasks/create
Body: {
kbId: 'xxx',
name: '提取研究方法',
prompt: '请从这篇文献中提取:研究设计、样本量、统计方法',
templateType: 'custom' | 'preset',
modelType: 'deepseek-v3',
concurrency: 3, // 并发数
}
// 2. 后端:创建任务
const task = await prisma.batchTask.create({
data: {
userId,
kbId,
name,
prompt,
templateType,
modelType,
status: 'pending',
totalDocuments: documentsCount,
concurrency,
},
});
// 3. 后端启动批处理Worker
async function processBatchTask(taskId) {
// 3.1 获取任务和文档列表
const task = await prisma.batchTask.findUnique({
where: { id: taskId },
include: { knowledgeBase: { include: { documents: true } } },
});
const documents = task.knowledgeBase.documents.filter(d => d.status === 'completed');
// 3.2 更新任务状态
await prisma.batchTask.update({
where: { id: taskId },
data: { status: 'running', startedAt: new Date() },
});
// 3.3 并发处理文档
const concurrency = task.concurrency || 3;
const chunks = chunkArray(documents, concurrency);
for (const chunk of chunks) {
await Promise.all(chunk.map(async (doc) => {
try {
// 3.3.1 对每个文档使用其extractedText + prompt调用LLM
const llmPrompt = `${task.prompt}\n\n文献内容\n${doc.extractedText}`;
const response = await LLMFactory.getAdapter(task.modelType).chat([
{ role: 'user', content: llmPrompt },
]);
// 3.3.2 解析LLM输出期望JSON格式
const data = parseJSONResponse(response.content);
// 3.3.3 保存结果
await prisma.batchResult.create({
data: {
taskId: task.id,
documentId: doc.id,
status: 'success',
data,
rawOutput: response.content,
tokensUsed: response.usage.totalTokens,
processingTimeMs: Date.now() - startTime,
},
});
// 3.3.4 更新任务进度
await prisma.batchTask.update({
where: { id: taskId },
data: { completedCount: { increment: 1 } },
});
} catch (error) {
// 3.3.5 处理失败
await prisma.batchResult.create({
data: {
taskId: task.id,
documentId: doc.id,
status: 'failed',
errorMessage: error.message,
},
});
await prisma.batchTask.update({
where: { id: taskId },
data: { failedCount: { increment: 1 } },
});
}
}));
}
// 3.4 任务完成
await prisma.batchTask.update({
where: { id: taskId },
data: {
status: 'completed',
completedAt: new Date(),
durationSeconds: Math.floor((Date.now() - task.startedAt) / 1000),
},
});
}
// 4. 前端:查看批处理结果
GET /api/v1/batch-tasks/:id/results
{
task: { /* 任务信息 */ },
results: [
{
documentId: 'xxx',
filename: 'paper1.pdf',
status: 'success',
data: {
: '随机对照试验',
: '300人',
: 't检验、卡方检验',
},
},
// ...
],
}
// 5. 前端导出结果Excel/CSV
```
**关键特点:**
- ✅ 批量处理多个文档
- ✅ 并发控制默认3个并发
- ✅ 结构化信息提取
- ✅ 进度实时更新
- ✅ 支持自定义模板
- ✅ 结果可导出Excel/CSV
- ✅ 错误处理和重试
---
### 2.3 三种模式的对比
| 维度 | 全文阅读 | 逐篇精读 | 批处理 |
|------|---------|---------|--------|
| **文档数量** | 7篇左右 | 1-5篇 | 3-50篇 |
| **数据来源** | 完整全文 | RAG检索片段 | 完整全文 |
| **LLM调用** | 对话式(多轮) | 对话式(多轮) | 批量(单次) |
| **上下文** | ~750K tokens | ~15K tokens | 单篇全文 |
| **输出方式** | 流式SSE | 流式SSE | 批量保存 |
| **适用场景** | 综合分析、跨文献比较 | 深度理解单篇 | 信息提取、数据表格 |
| **用户交互** | 实时问答 | 实时问答 | 后台处理 |
| **对话历史** | 全局共享 | 每篇独立 | 无对话 |
---
## 📋 API端点完整清单
### PKB管理模块API
```
POST /api/v1/knowledge/create # 创建知识库
GET /api/v1/knowledge/list # 获取知识库列表
GET /api/v1/knowledge/:id # 获取知识库详情
PUT /api/v1/knowledge/:id # 更新知识库
DELETE /api/v1/knowledge/:id # 删除知识库
GET /api/v1/knowledge/:id/search # RAG检索
GET /api/v1/knowledge/:id/stats # 统计信息
GET /api/v1/knowledge/:id/document-selection # 文档选择(全文模式)
POST /api/v1/documents/upload # 上传文档
GET /api/v1/documents/:id # 获取文档详情
DELETE /api/v1/documents/:id # 删除文档
GET /api/v1/documents/:id/content # 获取文档内容(全文)
POST /api/v1/batch-tasks/create # 创建批处理任务
GET /api/v1/batch-tasks/list # 获取批处理任务列表
GET /api/v1/batch-tasks/:id # 获取任务详情
GET /api/v1/batch-tasks/:id/results # 获取任务结果
DELETE /api/v1/batch-tasks/:id # 删除任务
GET /api/v1/task-templates/list # 获取模板列表
POST /api/v1/task-templates/create # 创建模板
DELETE /api/v1/task-templates/:id # 删除模板
```
### AIA对话模块API含PKB集成
```
POST /api/v1/chat/send-message-stream # 发送消息(流式)
参数:
- content: string
- modelType: 'deepseek-v3' | 'qwen3-72b' | 'qwen-long'
- knowledgeBaseIds?: string[] # RAG模式
- documentIds?: string[] # 逐篇精读模式(限定文档)
- fullTextDocumentIds?: string[] # 全文阅读模式(传递全文)
- conversationId?: string
GET /api/v1/chat/conversations # 获取对话列表
GET /api/v1/chat/conversations/:id # 获取对话历史
DELETE /api/v1/chat/conversations/:id # 删除对话
```
---
## 🔗 模块间依赖关系
```
AIA智能问答模块
├─ 依赖 PKB知识库管理模块
│ ├─ 获取知识库列表(选择知识库)
│ ├─ 获取文档列表(选择文档)
│ ├─ 获取文档全文(全文阅读)
│ ├─ RAG检索逐篇精读
│ └─ 文档智能选择(全文阅读)
├─ 依赖 LLM网关
│ ├─ DeepSeek V3
│ ├─ Qwen3-72B
│ └─ Qwen-Long
└─ 依赖 Dify RAG引擎
└─ retrieveKnowledge API
```
---
## 🎯 迁移关键点
### 1. PKB模块迁移
```
✅ 简单:
- 数据库已在pkb_schema无需迁移
- API端点清晰易于复制
- 业务逻辑独立
⚠️ 注意:
- Dify集成需要保持
- OSS文件上传需要保持
- 配额管理需要保持
```
### 2. AIA模块中的PKB集成迁移
```
✅ 简单:
- 接口清晰fullTextDocumentIds/documentIds
- 三种模式逻辑独立
⚠️ 注意:
- chatController.ts需要同时迁移
- 前端3个模式组件需要迁移
- 对话历史管理需要保持
```
### 3. 测试要点
```
必须测试:
✅ PKB CRUD功能
✅ 文档上传和提取
✅ RAG检索功能
✅ 全文阅读模式7篇文献
✅ 逐篇精读模式(文档切换)
✅ 批处理模式(并发处理)
✅ 配额管理
✅ 对话历史管理
✅ 模型切换
```
---
## ✅ 阶段0完成标准
- [x] 深入理解PKB的两个部分
- [x] 列出所有API端点
- [x] 理解数据库Schema
- [x] 理解三种工作模式
- [x] 理解模块间依赖
- [ ] 创建测试用例清单
- [ ] 准备测试数据
---
## 📊 下一步:创建测试用例
即将创建详细的测试用例清单,覆盖所有功能点...
---
**审查状态:** 🟡 进行中90%完成)
**下一步:** 创建测试用例清单和测试数据准备方案