Implement IIT quality workflow hardening across eQuery deduplication, guard metadata validation, timeline/readability improvements, and chat evidence fallbacks, then synchronize release and development documentation for deployment handoff. Includes migration/scripts for open eQuery dedupe guards, orchestration/status semantics, report/tool readability fixes, and updated module status plus deployment checklist. Made-with: Cursor
14 KiB
14 KiB
Day 25:智能问答功能完成 ✅
开发时间: Day 25
开发人员: AI助手
任务状态: ✅ 已完成
📋 任务概述
实现无项目、无智能体概念的纯AI对话功能,支持可选的@知识库引用。用户可以像在ChatGPT官网一样自由对话,同时可以通过@知识库引用个人文献获得更精准的回答。
✅ 已完成功能
1. 数据库Schema扩展
文件: backend/prisma/schema.prisma
新增两个数据表:
GeneralConversation(通用对话表)
model GeneralConversation {
id String @id @default(uuid())
userId String @map("user_id")
title String
modelName String? @map("model_name")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
deletedAt DateTime? @map("deleted_at")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
messages GeneralMessage[]
}
GeneralMessage(通用消息表)
model GeneralMessage {
id String @id @default(uuid())
conversationId String @map("conversation_id")
role String
content String @db.Text
model String?
metadata Json?
tokens Int?
createdAt DateTime @default(now()) @map("created_at")
conversation GeneralConversation @relation(...)
}
特点:
- ✅ 与项目对话完全独立
- ✅ 不依赖智能体配置
- ✅ 支持元数据存储(如知识库IDs)
2. 后端API实现
文件: backend/src/controllers/chatController.ts
API接口列表
| 接口 | 方法 | 功能 | 参数 |
|---|---|---|---|
/api/v1/chat/stream |
POST | 发送消息(流式) | content, modelType, knowledgeBaseIds?, conversationId? |
/api/v1/chat/conversations |
GET | 获取对话列表 | - |
/api/v1/chat/conversations/:id |
DELETE | 删除对话 | id |
核心实现逻辑
async sendMessageStream(request, reply) {
// 1. 获取或创建对话(无需项目/智能体)
if (conversationId) {
// 续接已有对话
} else {
// 创建新对话
conversation = await prisma.generalConversation.create({
userId,
title: content.substring(0, 50),
modelName: modelType,
});
}
// 2. 检索知识库(如果有)
if (knowledgeBaseIds && knowledgeBaseIds.length > 0) {
for (const kbId of knowledgeBaseIds) {
const searchResult = await knowledgeBaseService.searchKnowledgeBase(
userId, kbId, content, 3
);
// 格式化检索结果
knowledgeBaseContext += formatResults(searchResult);
}
}
// 3. 组装上下文(极简)
const messages = [
{ role: 'system', content: '你是一个专业、友好的AI助手...' },
...historyMessages, // 最近20条
{
role: 'user',
content: knowledgeBaseContext
? `${content}\n\n## 参考资料\n${knowledgeBaseContext}`
: content
}
];
// 4. 流式调用LLM
for await (const chunk of adapter.chatStream(messages)) {
reply.raw.write(`data: ${JSON.stringify(chunk)}\n\n`);
}
// 5. 保存消息到数据库
}
特点:
- ✅ 无需agentId和projectId
- ✅ 自动管理对话历史
- ✅ 集成知识库检索
- ✅ 极简的System Prompt
3. 前端页面实现
文件: frontend/src/pages/ChatPage.tsx
核心特性:
- ✅ 完全独立的对话页面
- ✅ 复用现有组件(MessageList, MessageInput, ModelSelector)
- ✅ 支持@知识库功能
- ✅ 自动创建和续接对话
页面结构:
<ChatPage>
<Header>
💬 智能问答 | [模型选择器]
</Header>
<MessageArea>
{messages.length === 0 ? (
<EmptyState>
💬 与AI自由对话
直接提问,或使用@知识库引用文献
</EmptyState>
) : (
<MessageList />
)}
</MessageArea>
<MessageInput
knowledgeBases={knowledgeBases}
onSend={(content, kbIds) => sendMessage(...)}
/>
</ChatPage>
4. 前端API封装
文件: frontend/src/api/chatApi.ts
接口方法:
// 发送消息(流式)
sendMessageStream(
data: {
content: string,
modelType: string,
knowledgeBaseIds?: string[],
conversationId?: string
},
onChunk: (content) => void,
onComplete: (conversationId) => void,
onError: (error) => void
)
// 获取对话列表
getConversations(): Promise<GeneralConversation[]>
// 删除对话
deleteConversation(id: string): Promise<void>
5. 路由和导航
修改文件:
frontend/src/App.tsx- 添加/chat路由frontend/src/layouts/MainLayout.tsx- 添加"智能问答"菜单项
导航顺序:
- 🏠 首页
- 💬 智能问答 ⭐ 新增
- 🧪 智能体
- 📁 知识库管理
- 📜 历史记录
🔄 完整工作流程
场景1:纯对话(无知识库)
用户访问 /chat
↓
空白对话界面
↓
输入:"介绍一下自己"
↓
[后端] 创建新对话
↓
[后端] 组装上下文:
- System Prompt: "你是一个专业的AI助手"
- User: "介绍一下自己"
↓
[LLM] DeepSeek-V3 回答
↓
[前端] 流式显示回答
场景2:基于知识库对话
用户访问 /chat
↓
点击 @知识库 → 选择"骨质疏松知识库"
↓
输入:"这个知识库讲的是什么?"
↓
[后端] 调用Dify检索API
↓
[后端] 检索到3条相关文档片段
↓
[后端] 组装上下文:
- System Prompt
- User: "这个知识库讲的是什么?"
- 参考资料: 【知识库:骨质疏松知识库】...
↓
[LLM] 基于文档内容回答
↓
[前端] 显示基于文献的精准回答
🎯 技术亮点
1. 极简架构
- 无需项目背景
- 无需智能体配置
- 直达核心:用户 ↔ AI ↔ 知识库
2. 灵活的知识库集成
- 完全可选(不@知识库 = 纯对话)
- 多知识库支持(同时选择多个)
- 实时检索(每次发送时检索最新内容)
3. 代码复用率高
- ✅ 复用 MessageList 组件
- ✅ 复用 MessageInput 组件
- ✅ 复用 ModelSelector 组件
- ✅ 复用 knowledgeBaseService
- ✅ 复用 LLM适配器
4. 独立的数据隔离
- 通用对话存储在独立的表
- 不影响项目对话数据
- 便于后续统计和分析
📊 数据流示例
请求示例:
POST /api/v1/chat/stream
{
"content": "这个知识库讲的是什么?",
"modelType": "deepseek-v3",
"knowledgeBaseIds": ["7d1e08ae-7a40-4e62-8654-bb631dc47293"]
}
知识库检索:
🔍 检索 → Dify API
↓
返回3条记录(相关度50.8%, 46.3%, 43.9%)
↓
格式化:
【知识库:骨质疏松知识库】
1. [相关度50.8%] 文档上传与处理:支持在知识库...
2. [相关度46.3%] AI科研助手产品需求文档(PRD)
3. [相关度43.9%] 知识库融合对话功能...
发送给LLM:
System: 你是一个专业、友好的AI助手...
User: 这个知识库讲的是什么?
## 参考资料(来自知识库)
【知识库:骨质疏松知识库】
1. [相关度50.8%] 文档上传与处理...
...
AI回答:
根据您的知识库内容,这是一份关于"AI科研助手"的产品需求文档(PRD)。
主要内容包括:
1. 文档管理功能...
2. 知识库融合对话功能...
...
【文献来源】骨质疏松知识库
🐛 已知问题
1. 问题描述不当导致检索质量差
现象:问"这个文档里有什么?"检索到的内容相关度低
原因:
- 问题太宽泛,语义模糊
- Dify检索需要具体的关键词或概念
建议:
- 使用具体问题:"骨质疏松的治疗方法有哪些?"
- 使用文档中的关键词提问
2. 英文文档 vs 中文问题
现象:阿尔兹海默症知识库检索到英文片段
原因:
- 文档是英文的
- 中文问题匹配度相对较低
建议:
- 使用英文提问
- 或者在问题中包含文档中的专业术语
📁 涉及文件清单
后端新增
backend/src/controllers/chatController.ts- 通用对话Controllerbackend/src/routes/chatRoutes.ts- 通用对话路由backend/prisma/schema.prisma- 新增通用对话表backend/migrations/add_general_chat.sql- 数据库迁移SQL
后端修改
backend/src/index.ts- 注册chatRoutes
前端新增
frontend/src/pages/ChatPage.tsx- 智能问答页面frontend/src/api/chatApi.ts- 通用对话API封装
前端修改
frontend/src/App.tsx- 添加 /chat 路由frontend/src/layouts/MainLayout.tsx- 添加"智能问答"菜单项
🧪 测试验证
✅ 已验证功能
-
纯对话功能
- ✅ 无需项目/智能体
- ✅ 直接与AI对话
- ✅ 流式输出正常
- ✅ 模型切换正常
-
@知识库功能
- ✅ 下拉菜单选择知识库
- ✅ 检索功能正常(调用Dify API)
- ✅ 知识库内容成功注入到AI上下文
- ✅ AI基于知识库内容回答
-
对话历史
- ✅ 自动创建对话
- ✅ 续接已有对话
- ✅ 上下文连贯(最近20条消息)
🎯 关键技术实现
1. 无依赖的对话架构
传统模式(项目对话):
用户 → 选择项目 → 选择智能体 → 对话
依赖:projectId + agentId
智能问答模式:
用户 → 对话
依赖:无
2. 知识库检索集成
// 检索知识库
if (knowledgeBaseIds && knowledgeBaseIds.length > 0) {
for (const kbId of knowledgeBaseIds) {
const searchResult = await knowledgeBaseService.searchKnowledgeBase(
userId, kbId, content, 3
);
// 格式化并追加到上下文
}
}
// 组装最终Prompt
const userContent = knowledgeBaseContext
? `${content}\n\n## 参考资料(来自知识库)\n${knowledgeBaseContext}`
: content;
3. 对话续接机制
首次发送:
{ "content": "你好", "modelType": "deepseek-v3" }
→ 创建新对话,返回 conversationId
后续发送:
{
"content": "继续聊",
"modelType": "deepseek-v3",
"conversationId": "xxx"
}
→ 续接对话,加载历史消息
💡 设计亮点
1. 用户体验优化
问题:传统智能体模式需要选择项目和智能体,流程复杂 解决:智能问答直达对话,0步骤开始
问题:用户可能不知道如何使用知识库 解决:@知识库完全可选,不影响基础使用
2. 架构清晰性
应用架构:
┌─────────────────────────┐
│ 项目-智能体模式 │
│ - 结构化的研究流程 │
│ - 专业领域AI │
│ - 项目背景上下文 │
└─────────────────────────┘
┌─────────────────────────┐
│ 智能问答模式 │
│ - 自由对话 │
│ - 通用AI助手 │
│ - 可选知识库辅助 │
└─────────────────────────┘
两种模式互不干扰,满足不同场景需求。
3. 代码复用率
新增代码:~400行 复用代码:~2000行(组件、服务、适配器) 复用率:83%
📝 用户使用指南
快速开始
- 访问
http://localhost:3000 - 点击左侧导航"💬 智能问答"
- 输入问题,开始对话
使用@知识库
- 点击输入框下方的"@知识库"按钮
- 选择一个或多个知识库
- 输入问题(建议具体问题,如:"治疗方法有哪些?")
- 点击发送
- AI会基于知识库内容回答
最佳实践
✅ 推荐的问题类型:
- 具体问题:"骨质疏松的病因是什么?"
- 概念解释:"什么是阿尔兹海默症?"
- 信息提取:"文献中提到了哪些治疗方法?"
❌ 不推荐的问题:
- 太宽泛:"这个文档有什么?"(检索效果差)
- 无关问题:"今天天气怎么样?"(浪费检索资源)
🔗 与Day 23-24的关系
Day 23-24:在项目智能体对话中实现@知识库
- ✅ 功能完整
- ⚠️ 但受限于智能体角色(如"选题评价"会忽略知识库内容)
Day 25:独立的智能问答
- ✅ 无角色限制
- ✅ 专注于基于知识库回答问题
- ✅ 提供最纯粹的测试环境
🎉 里程碑1 - 100%完成!
核心功能清单
- ✅ 用户认证与项目管理
- ✅ 12个AI智能体配置与调用
- ✅ 多轮对话上下文管理
- ✅ 流式输出(打字机效果)
- ✅ 模型切换(DeepSeek-V3/Qwen3/Gemini)
- ✅ 个人知识库管理
- ✅ @知识库检索与RAG集成(Day 23-24)
- ✅ 智能问答功能(Day 25)⭐ 今日完成
🚀 下一步规划
里程碑2预览(预计3-4天)
-
对话历史增强
- 对话列表展示
- 搜索和筛选
- 导出为Markdown
-
引用溯源优化
- 点击引用查看原文
- 高亮显示相关片段
- 文档来源追踪
-
项目协作功能
- 成员管理
- 权限控制
- 共享知识库
📊 技术收获
1. 架构设计
- 通过"通用对话"补充"项目对话"的不足
- 两种模式并存,互不干扰
- 代码高度复用
2. API设计
- RESTful风格
- 可选参数灵活性(conversationId?, knowledgeBaseIds?)
- 流式输出性能优化
3. 前端组件化
- MessageList、MessageInput高度解耦
- 易于在不同场景复用
- Props设计合理
文档创建时间: 2025-10-11
最后更新: 2025-10-11