Files
AIclinicalresearch/docs/05-每日进度/Day12-13-LLM适配器与对话系统完成.md
AI Clinical Dev Team 9acbb0ae2b feat: complete Dify platform deployment (Day 18)
## Dify 閮ㄧ讲瀹屾垚 鉁?
### 瀹屾垚鐨勫伐浣?1. Docker 闀滃儚鍔犻€熷櫒閰嶇疆
   - 閰嶇疆 5 涓浗鍐呴暅鍍忔簮
   - 澶у箙鎻愬崌涓嬭浇閫熷害鍜屾垚鍔熺巼

2. Dify 闀滃儚鎷夊彇 (鍏?11 涓湇鍔?
   - langgenius/dify-api:1.9.1
   - langgenius/dify-web:1.9.1
   - postgres, redis, weaviate, nginx 绛?   - 鎬诲ぇ灏忕害 2GB锛岃€楁椂绾?15 鍒嗛挓

3. Dify 鏈嶅姟鍚姩
   - 鉁?nginx (80/443)
   - 鉁?api, worker, worker_beat
   - 鉁?web (3000)
   - 鉁?db (PostgreSQL), redis
   - 鉁?weaviate (鍚戦噺鏁版嵁搴?
   - 鉁?sandbox, plugin_daemon, ssrf_proxy

4. Dify 鍒濆鍖栭厤缃?   - 鍒涘缓绠$悊鍛樿处鍙?   - 鍒涘缓搴旂敤: AI Clinical Research
   - 鑾峰彇 API Key: app-VZRn0vMXdmltEJkvatHVGv5j

5. 鍚庣鐜閰嶇疆
   - DIFY_API_URL=http://localhost/v1
   - DIFY_API_KEY 宸查厤缃?
### 鏂囨。鏇存柊
- 鏂板: docs/05-姣忔棩杩涘害/Day18-Dify閮ㄧ讲瀹屾垚.md
- 鏇存柊: docs/04-寮€鍙戣鍒?寮€鍙戦噷绋嬬.md (Day 18 鏍囪涓哄畬鎴?

### 涓嬩竴姝?Day 19-24: 鐭ヨ瘑搴撶郴缁熷紑鍙?- Dify 瀹㈡埛绔皝瑁?- 鐭ヨ瘑搴撶鐞?CRUD
- 鏂囨。涓婁紶涓庡鐞?- @鐭ヨ瘑搴撻泦鎴?- RAG 闂瓟楠岃瘉

---
Progress: 閲岀▼纰?1 (MVP) 85% -> 鐭ヨ瘑搴撶郴缁熷紑鍙戜腑
2025-10-11 08:58:41 +08:00

17 KiB
Raw Blame History

Day 12-13 - LLM适配器与对话系统完成

完成时间: 2025-10-10
开发阶段: 里程碑1 - MVP开发
本日目标: 完成LLM适配器、对话服务和流式输出(SSE)


完成清单

LLM适配器层

1. 类型定义和接口

  • types.ts - LLM适配器类型定义57行
    • Message - 消息结构role, content
    • LLMOptions - LLM调用参数
    • LLMResponse - 非流式响应
    • StreamChunk - 流式响应块
    • ILLMAdapter - 适配器接口
    • ModelType - 支持的模型类型

2. DeepSeek适配器

  • DeepSeekAdapter.ts - DeepSeek-V3适配器150行
    • 非流式调用:chat(messages, options)
    • 流式调用:chatStream(messages, options)
    • SSE数据解析
    • 错误处理和重试
    • Token使用统计

3. Qwen适配器

  • QwenAdapter.ts - Qwen3适配器162行
    • DashScope API集成
    • 非流式调用
    • 流式调用X-DashScope-SSE
    • 增量输出支持
    • 完整的错误处理

4. LLM工厂类

  • LLMFactory.ts - 适配器工厂75行
    • getAdapter(modelType) - 获取适配器实例
    • 单例模式,缓存适配器
    • clearCache() - 清除缓存
    • isSupported() - 检查模型支持
    • getSupportedModels() - 获取支持列表

对话系统

5. 对话服务层

  • conversationService.ts - 对话管理服务381行
    • 创建对话createConversation()
    • 获取对话列表getConversations()
    • 获取对话详情getConversationById()
    • 上下文组装assembleContext() - 系统Prompt + 历史消息 + 项目背景
    • 发送消息(非流式)sendMessage() - 完整响应后保存
    • 发送消息(流式)sendMessageStream() - SSE流式输出
    • 删除对话deleteConversation() - 软删除
    • 集成知识库上下文预留Dify RAG接口

6. 对话控制器

  • conversationController.ts - API控制器247行
    • createConversation() - 创建新对话201
    • getConversations() - 获取对话列表200
    • getConversationById() - 获取对话详情200/404
    • sendMessage() - 非流式发送200/400
    • sendMessageStream() - SSE流式发送200
    • deleteConversation() - 删除对话200/400
    • 模型类型验证

7. 对话路由

  • conversations.ts - RESTful API路由36行
    • POST /api/v1/conversations - 创建对话
    • GET /api/v1/conversations - 获取列表
    • GET /api/v1/conversations/:id - 获取详情
    • POST /api/v1/conversations/message - 发送消息
    • POST /api/v1/conversations/message/stream - 流式发送
    • DELETE /api/v1/conversations/:id - 删除对话

配置和环境

8. 环境配置

  • env.ts - 环境变量管理56行
    • 服务器配置port, host, logLevel
    • 数据库配置
    • Redis配置
    • JWT配置
    • LLM API配置DeepSeek, Qwen, Gemini
    • Dify配置
    • 文件上传配置
    • CORS配置
    • validateEnv() - 环境验证

9. 配置模板

  • .env.example - 环境变量模板36行
    • 完整的配置说明
    • API Key配置指南
    • 默认值参考

数据库更新

10. Prisma Schema更新

  • schema.prisma - 数据模型更新
    • Conversation 模型添加字段:
      • metadata (Json?) - 对话元数据
      • deletedAt (DateTime?) - 软删除时间戳
    • Message 模型添加字段:
      • model (String?) - 使用的模型名称
    • 添加索引:@@index([deletedAt])

11. 数据库迁移

  • 迁移文件 - add_conversation_metadata_deletedAt
    • 应用成功,数据库同步

依赖管理

12. 新增依赖

  • axios - HTTP客户端用于LLM API调用
  • js-yaml - YAML解析用于智能体配置
  • @types/js-yaml - TypeScript类型定义
  • zod - Schema验证用于请求验证

服务器集成

13. 主服务器更新

  • 注册对话路由:/api/v1/conversations
  • 添加环境验证:启动时调用validateEnv()
  • 导入配置模块:config, validateEnv

📁 新增/修改文件

后端9个新文件 + 4个修改

新增:

  1. src/adapters/types.ts - 57行
  2. src/adapters/DeepSeekAdapter.ts - 150行
  3. src/adapters/QwenAdapter.ts - 162行
  4. src/adapters/LLMFactory.ts - 75行
  5. src/config/env.ts - 56行
  6. src/services/conversationService.ts - 381行
  7. src/controllers/conversationController.ts - 247行
  8. src/routes/conversations.ts - 36行
  9. .env.example - 36行

修改: 10. src/index.ts - 添加对话路由注册(+5行 11. prisma/schema.prisma - 更新Conversation和Message模型+3行 12. package.json - 添加新依赖(+4行 13. prisma/migrations/ - 新迁移文件

统计

  • 新增代码: ~1200行
  • 新增文件: 9个
  • 修改文件: 4个

🎯 技术亮点

1. 统一的LLM适配器接口

设计优势:

  • 统一的ILLMAdapter接口支持任意LLM
  • 轻松扩展新模型Gemini, Claude, GPT等
  • 工厂模式管理,单例缓存

接口定义:

interface ILLMAdapter {
  modelName: string;
  chat(messages: Message[], options?: LLMOptions): Promise<LLMResponse>;
  chatStream(messages: Message[], options?: LLMOptions): AsyncGenerator<StreamChunk>;
}

2. 流式输出SSE

DeepSeek流式实现

  • 使用Axios responseType: 'stream'
  • 解析SSE数据data: {...}data: [DONE]
  • 逐块yield实时响应

Qwen流式实现

  • 使用X-DashScope-SSE: enable
  • 支持incremental_output增量模式
  • DashScope特殊SSE格式

前端SSE接收

reply.raw.writeHead(200, {
  'Content-Type': 'text/event-stream',
  'Cache-Control': 'no-cache',
  Connection: 'keep-alive',
});

for await (const chunk of conversationService.sendMessageStream(...)) {
  reply.raw.write(`data: ${JSON.stringify(chunk)}\n\n`);
}

3. 智能上下文组装

上下文组装逻辑:

  1. 获取智能体的系统Prompt
  2. 获取最近10条历史消息
  3. 渲染用户Prompt模板注入项目背景、知识库上下文
  4. 组装为LLM API格式的messages数组

代码示例:

private async assembleContext(
  conversationId: string,
  agentId: string,
  projectBackground: string,
  userInput: string,
  knowledgeBaseContext?: string
): Promise<Message[]> {
  const systemPrompt = agentService.getSystemPrompt(agentId);
  const historyMessages = await prisma.message.findMany({
    where: { conversationId },
    orderBy: { createdAt: 'desc' },
    take: 10,
  });
  
  const renderedUserPrompt = agentService.renderUserPrompt(agentId, {
    projectBackground,
    userInput,
    knowledgeBaseContext,
  });

  return [
    { role: 'system', content: systemPrompt },
    ...historyMessages.map(msg => ({ role: msg.role, content: msg.content })),
    { role: 'user', content: renderedUserPrompt },
  ];
}

4. 模型参数配置

从智能体配置读取:

const agent = agentService.getAgentById(conversation.agentId);
const modelConfig = agent?.models?.[modelType];

await adapter.chat(messages, {
  temperature: modelConfig?.temperature,
  maxTokens: modelConfig?.maxTokens,
  topP: modelConfig?.topP,
});

不同模型不同参数:

  • DeepSeek-V3temperature: 0.4, maxTokens: 2000
  • Qwen3-72Btemperature: 0.5, maxTokens: 2000

5. 错误处理

LLM API错误

catch (error: unknown) {
  if (axios.isAxiosError(error)) {
    throw new Error(
      `DeepSeek API调用失败: ${error.response?.data?.error?.message || error.message}`
    );
  }
  throw error;
}

控制器层错误:

catch (error: any) {
  reply.code(400).send({
    success: false,
    message: error.message || '发送消息失败',
  });
}

6. 知识库集成预留

Dify RAG接口预留

// 获取知识库上下文(如果有@知识库)
let knowledgeBaseContext = '';
if (knowledgeBaseIds && knowledgeBaseIds.length > 0) {
  // TODO: 调用Dify RAG获取知识库上下文
  knowledgeBaseContext = '相关文献内容...';
}

准备工作已完成:

  • 数据库已有KnowledgeBaseDocument模型
  • Dify配置已在env.ts中定义
  • 消息metadata中已保存knowledgeBaseIds

📊 API接口文档

1. 创建对话

POST /api/v1/conversations
Content-Type: application/json

{
  "projectId": "uuid",
  "agentId": "topic-evaluation",
  "title": "研究选题讨论"
}

响应201

{
  "success": true,
  "data": {
    "id": "uuid",
    "userId": "uuid",
    "projectId": "uuid",
    "agentId": "topic-evaluation",
    "title": "研究选题讨论",
    "metadata": {
      "agentName": "选题评价智能体",
      "agentCategory": "选题阶段"
    },
    "createdAt": "2025-10-10T12:30:00Z",
    "updatedAt": "2025-10-10T12:30:00Z"
  }
}

2. 获取对话列表

GET /api/v1/conversations?projectId=uuid

响应200

{
  "success": true,
  "data": [
    {
      "id": "uuid",
      "title": "研究选题讨论",
      "agentId": "topic-evaluation",
      "project": {
        "id": "uuid",
        "name": "心血管疾病研究"
      },
      "_count": {
        "messages": 15
      },
      "updatedAt": "2025-10-10T12:30:00Z"
    }
  ]
}

3. 发送消息(非流式)

POST /api/v1/conversations/message
Content-Type: application/json

{
  "conversationId": "uuid",
  "content": "请评价这个研究选题:高血压患者的依从性研究",
  "modelType": "deepseek-v3",
  "knowledgeBaseIds": ["uuid1", "uuid2"]
}

响应200

{
  "success": true,
  "data": {
    "userMessage": {
      "id": "uuid",
      "role": "user",
      "content": "请评价这个研究选题...",
      "createdAt": "2025-10-10T12:30:00Z"
    },
    "assistantMessage": {
      "id": "uuid",
      "role": "assistant",
      "content": "这是一个很有价值的研究选题...",
      "model": "deepseek-chat",
      "tokens": 1250,
      "createdAt": "2025-10-10T12:30:05Z"
    },
    "usage": {
      "promptTokens": 850,
      "completionTokens": 400,
      "totalTokens": 1250
    }
  }
}

4. 发送消息(流式)

POST /api/v1/conversations/message/stream
Content-Type: application/json

{
  "conversationId": "uuid",
  "content": "请评价这个研究选题:高血压患者的依从性研究",
  "modelType": "deepseek-v3"
}

响应200 - SSE流

data: {"content":"这","done":false}

data: {"content":"是","done":false}

data: {"content":"一个","done":false}

...

data: {"content":"。","done":true,"usage":{"promptTokens":850,"completionTokens":400,"totalTokens":1250}}

data: [DONE]

5. 获取对话详情

GET /api/v1/conversations/:id

响应200

{
  "success": true,
  "data": {
    "id": "uuid",
    "title": "研究选题讨论",
    "agentId": "topic-evaluation",
    "project": {
      "id": "uuid",
      "name": "心血管疾病研究",
      "background": "研究心血管疾病的...",
      "researchType": "observational"
    },
    "messages": [
      {
        "id": "uuid",
        "role": "user",
        "content": "请评价...",
        "createdAt": "2025-10-10T12:30:00Z"
      },
      {
        "id": "uuid",
        "role": "assistant",
        "content": "这是一个...",
        "model": "deepseek-chat",
        "tokens": 1250,
        "createdAt": "2025-10-10T12:30:05Z"
      }
    ],
    "createdAt": "2025-10-10T12:00:00Z",
    "updatedAt": "2025-10-10T12:30:05Z"
  }
}

6. 删除对话

DELETE /api/v1/conversations/:id

响应200

{
  "success": true,
  "message": "对话已删除"
}

🧪 测试验证

1. 后端构建

cd backend
npm run build
✅ TypeScript编译通过
✅ 无错误
✅ 生成dist/目录

2. Prisma生成

npx prisma generate
✅ Prisma Client生成成功
✅ 类型定义更新

3. 数据库迁移

npx prisma migrate dev
✅ 迁移文件创建
✅ 数据库schema同步

4. 依赖安装

npm install axios js-yaml zod @types/js-yaml
✅ 所有依赖安装成功

⚠️ 使用前准备

1. 配置环境变量

创建.env文件:

cp .env.example .env

配置LLM API Keys

# DeepSeek API Key (必需)
DEEPSEEK_API_KEY=sk-your-deepseek-api-key

# Qwen API Key (必需)
QWEN_API_KEY=sk-your-qwen-api-key

# 其他可选配置
PORT=3001
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/ai_clinical_research

获取API Keys


2. 手动功能测试需要API Key

测试创建对话

curl -X POST http://localhost:3001/api/v1/conversations \
  -H "Content-Type: application/json" \
  -d '{
    "projectId": "your-project-id",
    "agentId": "topic-evaluation",
    "title": "测试对话"
  }'

测试流式发送使用curl

curl -X POST http://localhost:3001/api/v1/conversations/message/stream \
  -H "Content-Type: application/json" \
  -d '{
    "conversationId": "your-conversation-id",
    "content": "请简单介绍一下临床研究",
    "modelType": "deepseek-v3"
  }' \
  --no-buffer

💡 设计决策

1. 为什么使用适配器模式?

  • 统一接口,易于扩展新模型
  • 隔离各LLM的API差异
  • 便于测试和mock
  • 支持模型切换

2. 为什么使用AsyncGenerator

  • 原生支持异步迭代
  • 内存高效逐块yield
  • 易于与SSE集成
  • 代码简洁清晰

3. 为什么保存完整对话历史?

  • 支持上下文记忆
  • 便于审核和分析
  • 可溯源,提高可信度
  • 方便后续优化Prompt

4. 为什么软删除对话?

  • 数据安全,可恢复
  • 审计追踪
  • 统计分析需要
  • 符合医疗数据管理规范

📈 项目进度

里程碑1 MVP开发进度85%
├── ✅ Day 4: 环境搭建
├── ✅ Day 5: 后端基础架构
├── ✅ Day 6: 前端基础架构
├── ✅ Day 7: 前端完整布局
├── ✅ Day 8-9: 项目管理API
├── ✅ Day 10-11: 智能体配置系统
├── ✅ Day 12-13: LLM适配器 + 对话系统 ⭐ ← 刚完成
└── ⏳ Day 14-17: 前端对话界面 + 知识库最后15%

📤 Git提交

commit ccc09c6
feat: Day 12-13 - LLM Adapters and Conversation System completed

后端:
- 创建LLM适配器类型和接口
- 实现DeepSeekAdapter流式+非流式)
- 实现QwenAdapter流式+非流式)
- 创建LLMFactory工厂类
- 创建env.ts环境配置
- 添加.env.example配置模板
- 创建conversationService完整CRUD和流式
- 创建conversationController SSE支持
- 创建conversation路由
- 更新Prisma schema
- 执行数据库迁移
- 注册对话路由到主服务器
- 添加启动时环境验证

依赖:
- 安装axios用于LLM API调用
- 安装js-yaml用于YAML配置解析
- 安装zod用于验证

构建:后端构建成功

新增文件:
- src/adapters/types.ts (57行)
- src/adapters/DeepSeekAdapter.ts (150行)
- src/adapters/QwenAdapter.ts (162行)
- src/adapters/LLMFactory.ts (75行)
- src/config/env.ts (56行)
- src/services/conversationService.ts (381行)
- src/controllers/conversationController.ts (247行)
- src/routes/conversations.ts (36行)
- .env.example (36行)

总计:~1200行新代码

🎓 经验总结

做得好的地方

  1. 适配器统一接口:易于扩展新模型
  2. 流式输出实现SSE实时响应用户体验好
  3. 上下文智能组装系统Prompt + 历史 + 项目背景
  4. 模型参数配置化:从智能体配置读取
  5. 完整的错误处理LLM API、控制器、验证
  6. 知识库预留为Dify RAG集成做好准备

改进空间 🔧

  1. LLM调用重试:添加指数退避重试机制
  2. 流式超时处理:长时间无响应的超时控制
  3. Token计费:实时统计和限额管理
  4. 缓存优化:相似问题的回复缓存
  5. 异步队列:高并发场景的消息队列
  6. 监控告警LLM API调用成功率、延迟监控

🔜 下一步工作Day 14-17

1. 前端对话界面开发

  • 对话消息列表组件
  • 消息输入框组件
  • 流式输出动画
  • Markdown渲染
  • 代码高亮
  • 模型切换UI

2. 知识库集成

  • Dify API调用
  • @知识库交互
  • 文档上传和处理
  • 引用溯源显示
  • 知识库管理界面

3. 功能完善

  • 对话历史浏览
  • 消息搜索
  • 对话导出
  • 错误重试
  • 离线提示

预计完成: MVP系统100%完成,可进行端到端测试


Day 12-13 任务完成! 🎉
下一步: 前端对话界面和知识库集成

注意: 需要配置DeepSeek和Qwen API Key才能进行实际对话测试