- 新增WechatService(企业微信推送服务,支持文本/卡片/Markdown消息) - 新增WechatCallbackController(异步回复模式,5秒内响应) - 完善iit_quality_check Worker(调用WechatService推送通知) - 新增企业微信回调路由(GET验证+POST接收消息) - 实现LLM意图识别(query_weekly_summary/query_patient_info等) - 安装依赖:@wecom/crypto, xml2js - 更新开发记录文档和MVP开发计划 技术要点: - 使用异步回复模式规避企业微信5秒超时限制 - 使用@wecom/crypto官方库处理XML加解密 - 使用setImmediate实现后台异步处理 - 支持主动推送消息返回LLM处理结果 - 完善审计日志记录(WECHAT_NOTIFICATION_SENT/WECHAT_INTERACTION) 相关文档: - docs/03-业务模块/IIT Manager Agent/06-开发记录/Day3-企业微信集成开发完成记录.md - docs/03-业务模块/IIT Manager Agent/04-开发计划/最小MVP闭环开发计划.md - docs/03-业务模块/IIT Manager Agent/00-模块当前状态与开发指南.md
12 KiB
12 KiB
技术债务:通用对话服务抽取计划
文档类型: 技术债务
创建日期: 2025-12-06
优先级: P2(中期优化)
预计工时: 2-3天
影响模块: AIA、PKB、Tool C、未来AI知识库模块
📋 问题描述
当前现状
后端对话能力分布:
✅ 通用能力层(common/)
├── llm/adapters/ ← LLM适配器(完整)
│ ├── LLMFactory.ts ← 统一工厂类
│ ├── DeepSeekAdapter.ts
│ ├── QwenAdapter.ts
│ ├── GPT5Adapter.ts
│ └── ClaudeAdapter.ts
├── rag/ ← RAG检索(完整)
│ └── DifyClient.ts
└── storage/, logging/, cache/ ← 基础设施(完整)
⚠️ 业务层(legacy/)
└── services/
└── conversationService.ts ← 对话管理逻辑(625行)
├── 创建对话
├── 发送消息
├── 流式输出
├── 上下文组装
├── 历史管理
└── 知识库检索集成
前端组件分布:
❌ 缺少通用对话组件
各模块重复实现:
├── AIA模块 - 自己实现对话UI
├── PKB模块 - 自己实现对话UI
└── Tool C模块 - 自己实现对话UI(Day 3)
🎯 问题分析
代码重复
| 功能 | 当前状态 | 重复度 |
|---|---|---|
| LLM调用 | ✅ 已抽取(common/llm) | 0% |
| 对话管理 | ⚠️ 未抽取(legacy中) | 80% |
| 消息存储 | ⚠️ 各模块独立表 | 60% |
| 上下文组装 | ⚠️ 各模块重复实现 | 70% |
| 流式输出 | ⚠️ 各模块重复实现 | 80% |
| 前端对话UI | ❌ 完全未抽取 | 90% |
影响范围
现有模块:
- AIA(AI智能问答)- 使用legacy/conversationService
- PKB(个人知识库)- 使用legacy/conversationService
- Tool C(数据清洗)- Day 3自己实现(~150行重复代码)
未来模块:
- AI知识库模块 - 需要重复实现
- 其他AI对话场景 - 需要重复实现
💡 优化方案
方案:抽取通用对话服务
第一阶段:后端服务抽取
创建通用对话服务:
// backend/src/common/conversation/ConversationService.ts
export class ConversationService {
/**
* 通用对话接口
* 支持多种场景:AIA、PKB、Tool C等
*/
async chat(config: ChatConfig): Promise<ChatResponse> {
// 1. 构建消息上下文
const messages = await this.buildContext(config);
// 2. 调用LLM(复用LLMFactory)
const llm = LLMFactory.createAdapter(config.modelType);
const response = await llm.chat(messages, config.options);
// 3. 保存消息(根据配置决定存储位置)
await this.saveMessage(config.conversationId, config.userMessage, response);
return response;
}
/**
* 流式对话
*/
async streamChat(config: ChatConfig): AsyncGenerator<StreamChunk> {
const messages = await this.buildContext(config);
const llm = LLMFactory.createAdapter(config.modelType);
for await (const chunk of llm.chatStream(messages, config.options)) {
yield chunk;
}
// 保存完整响应
await this.saveMessage(...);
}
/**
* 获取对话历史
* 支持多种存储方式(统一接口,不同表)
*/
async getHistory(conversationId: string, options?: HistoryOptions) {
// 根据conversationType路由到不同的表
const adapter = this.getStorageAdapter(options.conversationType);
return await adapter.getHistory(conversationId, options);
}
/**
* 保存消息
* 支持多种存储方式
*/
async saveMessage(conversationId: string, userMsg: string, aiResponse: any) {
const adapter = this.getStorageAdapter(this.config.conversationType);
await adapter.saveMessage(conversationId, userMsg, aiResponse);
}
/**
* 构建上下文
* 支持:System Prompt + 历史消息 + 当前消息 + RAG检索
*/
private async buildContext(config: ChatConfig): Promise<Message[]> {
const messages: Message[] = [];
// 1. System Prompt
if (config.systemPrompt) {
messages.push({ role: 'system', content: config.systemPrompt });
}
// 2. 历史消息
if (config.includeHistory) {
const history = await this.getHistory(config.conversationId, {
limit: config.historyLimit || 5
});
messages.push(...history);
}
// 3. RAG检索(如果需要)
if (config.knowledgeBaseIds?.length > 0) {
const ragContext = await this.retrieveRAGContext(
config.userMessage,
config.knowledgeBaseIds
);
messages.push({ role: 'system', content: ragContext });
}
// 4. 当前用户消息
messages.push({ role: 'user', content: config.userMessage });
return messages;
}
}
// 配置接口
interface ChatConfig {
conversationId: string;
conversationType: 'aia' | 'pkb' | 'tool-c' | 'knowledge-base'; // 路由到不同表
modelType: ModelType;
userMessage: string;
systemPrompt?: string;
includeHistory?: boolean;
historyLimit?: number;
knowledgeBaseIds?: string[];
options?: {
temperature?: number;
maxTokens?: number;
topP?: number;
};
}
存储适配器(支持不同模块的不同表):
// backend/src/common/conversation/adapters/StorageAdapter.ts
interface ConversationStorageAdapter {
getHistory(conversationId: string, options?: HistoryOptions): Promise<Message[]>;
saveMessage(conversationId: string, userMsg: string, aiResponse: any): Promise<void>;
}
// AIA/PKB使用通用表
class GeneralStorageAdapter implements ConversationStorageAdapter {
async getHistory(conversationId: string) {
return await prisma.generalMessage.findMany({
where: { conversationId },
orderBy: { createdAt: 'desc' },
take: options.limit
});
}
}
// Tool C使用独立表
class ToolCStorageAdapter implements ConversationStorageAdapter {
async getHistory(conversationId: string) {
return await prisma.dcToolCAiHistory.findMany({
where: { sessionId: conversationId },
orderBy: { createdAt: 'desc' },
take: options.limit
});
}
}
第二阶段:前端组件抽取
创建通用对话组件库:
// frontend-v2/src/shared/components/Chat/
├── ChatContainer.tsx // 对话容器(布局)
├── MessageList.tsx // 消息列表(虚拟滚动)
├── MessageItem.tsx // 单条消息(用户/AI)
├── MessageInput.tsx // 输入框(支持多行、快捷键)
├── StreamingMessage.tsx // 流式渲染(打字机效果)
├── CitationBadge.tsx // 引用标记
├── LoadingIndicator.tsx // 加载动画
└── index.ts
// 使用示例
import { ChatContainer } from '@/shared/components/Chat';
<ChatContainer
conversationId={sessionId}
conversationType="tool-c"
systemPrompt={buildSystemPrompt()}
onSendMessage={handleSendMessage}
enableHistory={true}
enableRAG={false}
/>
组件特性:
- ✅ 支持流式/非流式渲染
- ✅ 支持引用跳转
- ✅ 支持历史消息加载
- ✅ 支持Markdown渲染
- ✅ 支持代码高亮
- ✅ 响应式布局
📊 改造前后对比
代码量对比
| 模块 | 改造前 | 改造后 | 减少 |
|---|---|---|---|
| 后端 | |||
| AIA对话逻辑 | 200行 | 50行(调用通用服务) | -75% |
| PKB对话逻辑 | 180行 | 50行 | -72% |
| Tool C对话逻辑 | 150行 | 50行 | -67% |
| 通用服务 | 0行 | 300行(新建) | +300行 |
| 总计 | 530行 | 450行 | -15% |
| 前端 | |||
| 各模块对话UI | 600行×3 | 200行×3(调用通用组件) | -67% |
| 通用组件 | 0行 | 500行(新建) | +500行 |
| 总计 | 1800行 | 1100行 | -39% |
| 全部合计 | 2330行 | 1550行 | -33% |
质量提升
| 指标 | 改造前 | 改造后 | 提升 |
|---|---|---|---|
| 代码复用率 | 20% | 80% | +300% |
| 统一交互体验 | ❌ | ✅ | 100% |
| 未来扩展成本 | 高(每次重复) | 低(直接复用) | -80% |
| 维护成本 | 高(多处修改) | 低(单点修改) | -70% |
🚀 实施计划
阶段1:后端服务抽取(1.5天)
Day 1上午:设计
- 设计ConversationService接口
- 设计StorageAdapter接口
- 设计ChatConfig配置结构
Day 1下午:实现核心服务
- 实现ConversationService核心逻辑
- 实现GeneralStorageAdapter
- 实现ToolCStorageAdapter
Day 2上午:迁移现有模块
- AIA模块改造(使用通用服务)
- PKB模块改造(使用通用服务)
- Tool C模块改造(使用通用服务)
Day 2下午:测试
- 单元测试
- 集成测试
- 回归测试(确保原功能正常)
阶段2:前端组件抽取(1天)
Day 3上午:设计与实现
- 设计ChatContainer API
- 实现核心组件(6个)
- 样式统一
Day 3下午:迁移与测试
- AIA模块前端改造
- PKB模块前端改造
- Tool C模块前端改造
- UI测试
阶段3:文档与培训(0.5天)
- 编写使用文档
- 编写最佳实践
- 团队培训
📝 验收标准
功能验收
- AIA模块对话功能正常
- PKB模块对话功能正常
- Tool C模块对话功能正常
- 流式输出正常
- 历史消息加载正常
- RAG检索集成正常
代码质量
- 代码复用率≥80%
- 单元测试覆盖率≥80%
- 无TypeScript错误
- 无ESLint警告
用户体验
- 对话交互流畅(响应<2秒)
- 流式输出流畅(无卡顿)
- UI统一美观
- 移动端适配良好
💰 收益分析
短期收益(1个月内)
-
代码质量提升
- 减少重复代码33%
- 提升代码复用率至80%
- 降低维护成本70%
-
开发效率提升
- 新模块对话功能开发时间:从2天→0.5天(-75%)
- Bug修复效率:单点修改,影响全局(+200%)
-
用户体验统一
- 统一交互模式
- 统一视觉风格
- 统一性能标准
长期收益(3-6个月)
-
支持未来模块
- AI知识库模块:直接复用,0额外开发
- 其他AI对话场景:快速实现
-
技术架构优化
- 真正实现分层架构(业务层→通用层)
- 为微服务拆分做准备
-
商业价值
- 模块独立部署更容易
- 模块独立售卖更灵活
- 客户定制成本降低
⚠️ 风险与应对
风险1:回归测试工作量大
应对:
- 优先实现自动化测试
- 分模块逐步迁移
- 保留原代码作为备份
风险2:历史数据迁移
应对:
- 不需要迁移数据
- 只迁移代码逻辑
- 各模块保留独立表
风险3:前端组件复杂度
应对:
- 采用渐进式重构
- 先抽取核心组件
- 后续迭代优化
📅 建议执行时间
推荐时间窗口:
- 选项1:Tool C MVP完成后(Day 10-13)
- 选项2:所有DC模块完成后(Week 8-9)
- 选项3:AI知识库模块启动前(Quarter 2)
当前决策:延后至Tool C MVP完成后
🔗 相关文档
📝 更新记录
| 日期 | 版本 | 更新内容 | 更新人 |
|---|---|---|---|
| 2025-12-06 | V1.0 | 初始创建 | AI Assistant |
文档状态: ✅ 已创建
优先级: P2(中期优化)
下一步: 完成Tool C Day 3 MVP后重新评估