Files
AIclinicalresearch/docs/05-每日进度/Day25-智能问答功能完成.md
2025-10-12 10:01:10 +08:00

14 KiB
Raw Blame History

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. 🏠 首页
  2. 💬 智能问答 新增
  3. 🧪 智能体
  4. 📁 知识库管理
  5. 📜 历史记录

🔄 完整工作流程

场景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 - 通用对话Controller
  • backend/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 - 添加"智能问答"菜单项

🧪 测试验证

已验证功能

  1. 纯对话功能

    • 无需项目/智能体
    • 直接与AI对话
    • 流式输出正常
    • 模型切换正常
  2. @知识库功能

    • 下拉菜单选择知识库
    • 检索功能正常调用Dify API
    • 知识库内容成功注入到AI上下文
    • AI基于知识库内容回答
  3. 对话历史

    • 自动创建对话
    • 续接已有对话
    • 上下文连贯最近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%


📝 用户使用指南

快速开始

  1. 访问 http://localhost:3000
  2. 点击左侧导航"💬 智能问答"
  3. 输入问题,开始对话

使用@知识库

  1. 点击输入框下方的"@知识库"按钮
  2. 选择一个或多个知识库
  3. 输入问题(建议具体问题,如:"治疗方法有哪些?"
  4. 点击发送
  5. AI会基于知识库内容回答

最佳实践

推荐的问题类型

  • 具体问题:"骨质疏松的病因是什么?"
  • 概念解释:"什么是阿尔兹海默症?"
  • 信息提取:"文献中提到了哪些治疗方法?"

不推荐的问题

  • 太宽泛:"这个文档有什么?"(检索效果差)
  • 无关问题:"今天天气怎么样?"(浪费检索资源)

🔗 与Day 23-24的关系

Day 23-24:在项目智能体对话中实现@知识库

  • 功能完整
  • ⚠️ 但受限于智能体角色(如"选题评价"会忽略知识库内容)

Day 25:独立的智能问答

  • 无角色限制
  • 专注于基于知识库回答问题
  • 提供最纯粹的测试环境

🎉 里程碑1 - 100%完成!

核心功能清单

  1. 用户认证与项目管理
  2. 12个AI智能体配置与调用
  3. 多轮对话上下文管理
  4. 流式输出(打字机效果)
  5. 模型切换DeepSeek-V3/Qwen3/Gemini
  6. 个人知识库管理
  7. @知识库检索与RAG集成Day 23-24
  8. 智能问答功能Day 25 今日完成

🚀 下一步规划

里程碑2预览预计3-4天

  1. 对话历史增强

    • 对话列表展示
    • 搜索和筛选
    • 导出为Markdown
  2. 引用溯源优化

    • 点击引用查看原文
    • 高亮显示相关片段
    • 文档来源追踪
  3. 项目协作功能

    • 成员管理
    • 权限控制
    • 共享知识库

📊 技术收获

1. 架构设计

  • 通过"通用对话"补充"项目对话"的不足
  • 两种模式并存,互不干扰
  • 代码高度复用

2. API设计

  • RESTful风格
  • 可选参数灵活性conversationId?, knowledgeBaseIds?
  • 流式输出性能优化

3. 前端组件化

  • MessageList、MessageInput高度解耦
  • 易于在不同场景复用
  • Props设计合理

文档创建时间: 2025-10-11
最后更新: 2025-10-11