Features: - PatientWechatCallbackController for URL verification and message handling - PatientWechatService for template and customer messages - Support for secure mode (message encryption/decryption) - Simplified route /wechat/patient/callback for WeChat config - Event handlers for subscribe/unsubscribe/text messages - Template message for visit reminders Technical details: - Reuse @wecom/crypto for encryption (compatible with Official Account) - Relaxed Fastify schema validation to prevent early request blocking - Access token caching (7000s with 5min pre-refresh) - Comprehensive logging for debugging Testing: Local URL verification passed, ready for SAE deployment Status: Code complete, waiting for WeChat platform configuration
16 KiB
IIT Manager Agent 技术债务清单
文档版本: v1.0
最后更新: 2026-01-04
当前阶段: Phase 1.5 完成
📋 文档说明
本文档记录IIT Manager Agent模块的技术债务,包括当前的临时方案、已知限制、待优化项,以及未来改进计划。
🎯 技术债务优先级定义
| 优先级 | 说明 | 处理时机 |
|---|---|---|
| P0 - 阻塞性 | 影响核心功能,必须立即处理 | 立即 |
| P1 - 高优先级 | 影响用户体验或性能,建议Phase 2处理 | 1个月内 |
| P2 - 中优先级 | 功能增强,可在Phase 3处理 | 3个月内 |
| P3 - 低优先级 | 优化项,长期规划 | 6个月内 |
🔴 P0 - 阻塞性债务
当前状态: ✅ 无P0级债务
🟠 P1 - 高优先级债务
1. 意图识别升级
当前方案: 关键词匹配(正则表达式)
问题:
- ⚠️ 无法理解复杂的自然语言
- ⚠️ 容易误判边缘case
- ⚠️ 需要人工维护关键词列表
影响:
- 当用户使用非标准表达时,可能无法正确识别意图
- 例如:"帮我看一下那个7号的数据" 可能无法识别为
query_record
改进方案:
方案A: LLM意图判断(推荐)
// 使用LLM进行意图分类
const intentPrompt = `你是意图识别专家。用户消息:${userMessage}
请判断用户意图,从以下选项中选择:
1. query_record - 查询特定患者记录
2. count_records - 统计患者数量
3. query_protocol - 查询研究方案文档
4. project_info - 查询项目信息
5. general_chat - 普通对话
只返回JSON: {"intent": "xxx", "params": {...}}
`;
const intentResult = await llm.chat([{ role: 'user', content: intentPrompt }]);
优势:
- ✅ 理解能力强,支持复杂表达
- ✅ 无需维护关键词
- ✅ 适应性好
劣势:
- ❌ 增加一次LLM调用(~1秒,+¥0.0001成本)
- ❌ 总响应时间增加到~6秒
方案B: BERT分类模型
# 训练意图分类模型
from transformers import BertForSequenceClassification
model = BertForSequenceClassification.from_pretrained(
'bert-base-chinese',
num_labels=5
)
# 训练数据:100-200个标注样本
# 推理速度:<100ms
优势:
- ✅ 速度快(<100ms)
- ✅ 理解能力强
- ✅ 成本低
劣势:
- ❌ 需要标注训练数据
- ❌ 需要部署模型服务
- ❌ 需要持续迭代
建议:
- Phase 2: 先使用方案A(LLM判断),快速验证效果
- Phase 3: 如果量大,再切换到方案B(BERT模型)
预计工作量: 2-3天
2. 上下文存储迁移到Redis
当前方案: 内存缓存(Node.js Map)
问题:
- ⚠️ 单机内存,不支持分布式部署
- ⚠️ 服务重启后上下文丢失
- ⚠️ 内存占用无限制(虽有自动清理,但仍有风险)
影响:
- 当后端服务重启时,所有用户的对话上下文会丢失
- 如果部署多个实例,用户请求可能路由到不同实例,导致上下文丢失
改进方案:
// backend/src/modules/iit-manager/agents/SessionMemory.ts
import Redis from 'ioredis';
export class SessionMemory {
private redis: Redis;
private readonly EXPIRE_TIME = 1800; // 30分钟
constructor() {
this.redis = new Redis(config.redisUrl);
}
async addMessage(userId: string, role: 'user' | 'assistant', content: string): Promise<void> {
const key = `session:${userId}`;
const message = { role, content, timestamp: Date.now() };
// 1. 获取当前会话
const sessionData = await this.redis.get(key);
const session = sessionData ? JSON.parse(sessionData) : { messages: [] };
// 2. 添加消息
session.messages.push(message);
// 3. 保持最近3轮
if (session.messages.length > 6) {
session.messages = session.messages.slice(-6);
}
// 4. 存回Redis(30分钟过期)
await this.redis.setex(key, this.EXPIRE_TIME, JSON.stringify(session));
}
async getContext(userId: string): Promise<string> {
const key = `session:${userId}`;
const sessionData = await this.redis.get(key);
if (!sessionData) return '';
const session = JSON.parse(sessionData);
return session.messages
.map(m => `${m.role === 'user' ? '用户' : 'AI'}: ${m.content}`)
.join('\n\n');
}
}
优势:
- ✅ 支持分布式部署
- ✅ 服务重启不丢失上下文
- ✅ 内存占用可控
劣势:
- ❌ 增加Redis依赖
- ❌ 每次读写需要网络IO(~1-2ms)
建议: Phase 2实施,优先级中高
预计工作量: 1天
3. Dify检索结果缓存
当前方案: 每次都调用Dify API检索
问题:
- ⚠️ 相同问题重复检索,浪费时间
- ⚠️ Dify API响应时间占30%(1.5-1.7秒)
影响:
- 响应速度有提升空间
- 相同问题第二次询问仍需1.5秒
改进方案:
// 使用Redis缓存Dify检索结果
private async queryDifyKnowledge(query: string): Promise<string> {
// 1. 生成缓存key(query的hash)
const cacheKey = `dify:${md5(query)}`;
// 2. 尝试从缓存获取
const cached = await redis.get(cacheKey);
if (cached) {
logger.info('Dify检索命中缓存', { query });
return cached;
}
// 3. 调用Dify API
const result = await difyClient.retrieveKnowledge(...);
// 4. 格式化结果
const formattedKnowledge = this.formatDifyResult(result);
// 5. 缓存结果(1小时)
await redis.setex(cacheKey, 3600, formattedKnowledge);
return formattedKnowledge;
}
优势:
- ✅ 相同问题响应速度提升1.5秒(从4.8秒降到3.3秒)
- ✅ 减少Dify API调用次数
- ✅ 降低Dify服务器负载
劣势:
- ❌ 文档更新后需要清除缓存
- ❌ 缓存占用Redis内存
缓存失效策略:
- 文档上传/更新/删除时,清除对应Dataset的所有缓存
- 缓存过期时间:1小时
建议: Phase 2实施,优先级中
预计工作量: 1天
4. REDCap数据缓存
当前方案: 每次都调用REDCap API查询
问题:
- ⚠️ REDCap API响应时间占25%(1.2-1.3秒)
- ⚠️ 患者基本信息变化频率低,不需要每次实时查询
影响:
- 响应速度有提升空间
- 增加REDCap服务器负载
改进方案:
// 分层缓存策略
private async queryRedcapRecord(recordId: string): Promise<any> {
const cacheKey = `redcap:record:${recordId}`;
// 1. 尝试从缓存获取
const cached = await redis.get(cacheKey);
if (cached) {
return JSON.parse(cached);
}
// 2. 调用REDCap API
const records = await redcap.exportRecords({ records: [recordId] });
// 3. 缓存结果(5分钟)
await redis.setex(cacheKey, 300, JSON.stringify(records[0]));
return records[0];
}
缓存策略:
| 数据类型 | 缓存时间 | 原因 |
|---|---|---|
| 患者基本信息 | 5分钟 | 变化频率低 |
| 统计数据 | 1分钟 | 需要较实时 |
| 项目配置 | 1小时 | 几乎不变 |
优势:
- ✅ 响应速度提升1.2秒
- ✅ 减少REDCap服务器负载
劣势:
- ❌ 数据可能有5分钟延迟
建议: Phase 2实施,需与用户确认是否接受5分钟延迟
预计工作量: 1天
🟡 P2 - 中优先级债务
5. 文档上传API开发
当前方案: 手动通过Dify界面上传文档
问题:
- ⚠️ 不支持批量上传
- ⚠️ 需要登录Dify系统
- ⚠️ 无法自动化
影响:
- PI无法自助上传研究文档
- 需要技术人员协助
改进方案:
// backend/src/modules/iit-manager/routes/index.ts
// 上传文档到项目知识库
router.post('/projects/:projectId/documents', async (request, reply) => {
const { projectId } = request.params;
const file = await request.file();
// 1. 获取项目配置
const project = await prisma.iitProject.findUnique({
where: { id: projectId },
select: { difyDatasetId: true }
});
// 2. 上传到Dify
const buffer = await file.toBuffer();
const result = await difyClient.uploadDocumentDirectly(
project.difyDatasetId,
buffer,
file.filename
);
// 3. 等待索引完成
await difyClient.waitForDocumentProcessing(
project.difyDatasetId,
result.document.id
);
return reply.send({ success: true, documentId: result.document.id });
});
// 删除文档
router.delete('/projects/:projectId/documents/:documentId', async (request, reply) => {
// 实现文档删除
});
// 获取文档列表
router.get('/projects/:projectId/documents', async (request, reply) => {
// 实现文档列表查询
});
前端界面(可选):
- 文档上传表单
- 文档列表展示
- 文档状态追踪(uploading → indexing → completed)
建议: Phase 3实施,优先级中
预计工作量: 2-3天
6. 用户绑定默认项目
当前方案: 所有用户共享同一个活跃项目(status='active')
问题:
- ⚠️ 无法支持多项目
- ⚠️ 不同用户可能关注不同项目
影响:
- 如果有多个IIT项目,无法区分用户属于哪个项目
改进方案:
数据模型更新:
// backend/prisma/schema.prisma
model IitUserMapping {
// ... 现有字段 ...
// 新增:用户的默认项目
defaultProjectId String? @map("default_project_id")
}
ChatService更新:
async handleMessage(userId: string, userMessage: string): Promise<string> {
// 1. 获取用户的默认项目
const userMapping = await prisma.iitUserMapping.findFirst({
where: { wecomUserId: userId }
});
const projectId = userMapping?.defaultProjectId;
// 2. 基于项目查询数据
const project = await prisma.iitProject.findUnique({
where: { id: projectId }
});
// 后续逻辑...
}
建议: Phase 2后期实施,当有多个项目时
预计工作量: 1-2天
7. Function Calling升级
当前方案: 关键词意图识别 → 手动调用工具
问题:
- ⚠️ 无法自动决定是否调用工具
- ⚠️ 无法根据上下文自动提取参数
影响:
- 如果用户问"7号患者和8号患者哪个更严重?",当前无法自动查询两个患者
改进方案:
使用DeepSeek-V3的Native Function Calling:
// 定义工具
const tools = [
{
type: 'function',
function: {
name: 'query_redcap_record',
description: '查询REDCap中特定患者的记录',
parameters: {
type: 'object',
properties: {
recordId: {
type: 'string',
description: '患者记录ID'
}
},
required: ['recordId']
}
}
},
{
type: 'function',
function: {
name: 'query_dify_knowledge',
description: '检索研究方案、CRF表格等文档',
parameters: {
type: 'object',
properties: {
query: {
type: 'string',
description: '查询问题'
}
},
required: ['query']
}
}
}
];
// LLM自动决定调用哪些工具
const response = await llm.chat(messages, { tools });
// 处理工具调用
if (response.tool_calls) {
for (const toolCall of response.tool_calls) {
if (toolCall.function.name === 'query_redcap_record') {
const args = JSON.parse(toolCall.function.arguments);
const result = await this.queryRedcapRecord(args.recordId);
// 继续对话...
}
}
}
优势:
- ✅ LLM自动决定是否调用工具
- ✅ 自动提取参数(如recordId)
- ✅ 支持多工具并行调用
- ✅ 更智能、更灵活
劣势:
- ❌ 增加一次LLM调用
- ❌ 总响应时间可能增加到7-8秒
建议: Phase 3实施,作为高级功能
预计工作量: 3-5天
8. 智能引用系统
当前方案: Dify返回的文档片段直接显示
问题:
- ⚠️ 无法溯源到具体文档和位置
- ⚠️ 用户无法验证信息来源
影响:
- 用户体验不够透明
改进方案:
参考ASL模块的智能引用系统:
// 1. 收集引用信息
interface Citation {
id: number;
documentName: string;
segmentIndex: number;
score: number;
content: string;
}
// 2. AI回答中插入引用标记
const answer = `根据研究方案[1]和CRF表格[2],纳入标准包括:
1. 年龄18-75岁[1]
2. 符合诊断标准[1][2]
3. 签署知情同意书[3]
---
📚 **参考文献**
[1] 📄 **研究方案.pdf** - 第3段 (相关度95%)
"纳入标准:年龄18-75岁,符合诊断标准..."
[2] 📄 **CRF表格.docx** - 第5段 (相关度92%)
"基线评估包括:诊断标准、入组时间..."
`;
// 3. 前端高亮显示引用
// 点击[1]跳转到引用详情
建议: Phase 3实施,作为体验优化
预计工作量: 3-4天
🟢 P3 - 低优先级债务
9. 批量数据查询优化
当前方案: 单条记录查询
问题:
- ⚠️ 如果用户问"所有患者的入组情况",需要多次查询
改进方案:
- 支持批量查询
- 生成数据摘要
建议: Phase 4实施
预计工作量: 2-3天
10. 多轮对话优化
当前方案: 保留最近3轮对话
问题:
- ⚠️ 长对话可能丢失重要上下文
改进方案:
- 智能上下文压缩(提取关键信息)
- 长期记忆(存储重要信息到数据库)
建议: Phase 4实施
预计工作量: 5-7天
📊 债务优先级总览
| 债务项 | 优先级 | 影响 | 工作量 | 建议时间 |
|---|---|---|---|---|
| 意图识别升级 | P1 | 准确率 | 2-3天 | Phase 2 |
| 上下文迁移Redis | P1 | 可用性 | 1天 | Phase 2 |
| Dify结果缓存 | P1 | 性能 | 1天 | Phase 2 |
| REDCap数据缓存 | P1 | 性能 | 1天 | Phase 2 |
| 文档上传API | P2 | 功能 | 2-3天 | Phase 3 |
| 用户绑定项目 | P2 | 功能 | 1-2天 | Phase 2后期 |
| Function Calling | P2 | 智能化 | 3-5天 | Phase 3 |
| 智能引用系统 | P2 | 体验 | 3-4天 | Phase 3 |
| 批量查询优化 | P3 | 功能 | 2-3天 | Phase 4 |
| 多轮对话优化 | P3 | 体验 | 5-7天 | Phase 4 |
🎯 Phase 2 债务清偿计划
Week 1-2: 性能优化
- 上下文迁移到Redis(1天)
- Dify结果缓存(1天)
- REDCap数据缓存(1天)
- 性能测试与调优(1天)
预期效果:
- 响应时间从4.8秒降到2-3秒
- 支持分布式部署
- 服务重启不丢失上下文
Week 3-4: 意图识别升级
- 实现LLM意图判断(2天)
- A/B测试对比(1天)
- 上线灰度发布(1天)
预期效果:
- 意图识别准确率保持100%
- 支持复杂自然语言表达
Week 5: 多项目支持
- 用户绑定默认项目(2天)
预期效果:
- 支持多个IIT项目并行
📝 债务跟踪
已清偿债务
| 债务项 | 清偿时间 | 方案 |
|---|---|---|
| AI幻觉问题 | 2026-01-03 | RAG数据注入 + 严格System Prompt |
| 上下文丢失 | 2026-01-03 | SessionMemory(最近3轮) |
| 响应速度慢 | 2026-01-04 | 优化意图识别 + 异步处理 |
进行中债务
| 债务项 | 负责人 | 预计完成 |
|---|---|---|
| 无 | - | - |
待处理债务
见上文优先级列表
🔗 相关文档
- [IIT Manager Agent 技术路径与架构设计](../02-技术设计/IIT Manager Agent 技术路径与架构设计.md)
- Phase1.5-AI对话能力开发计划
- MVP开发任务清单
文档维护: 技术团队
最后更新: 2026-01-04
版本历史:
- v1.0 (2026-01-04): 初始版本,Phase 1.5完成后整理