Features: - Add V2.9 enhancements: Cron Skill, User Profiling, Feedback Loop, Multi-Intent Handling - Create modular development plan documents (database, engines, services, memory, tasks) - Add V2.5/V2.6/V2.8/V2.9 design documents for architecture evolution - Add system design white papers and implementation guides Architecture: - Dual-Brain Architecture (SOP + ReAct engines) - Three-layer memory system (Flow Log, Hot Memory, History Book) - ProfilerService for personalized responses - SchedulerService with Cron Skill support Also includes: - Frontend nginx config updates - Backend test scripts for WeChat signature - Database backup files Co-authored-by: Cursor <cursoragent@cursor.com>
55 KiB
IIT Manager Agent V2.6 综合开发计划
版本: V2.6(极简架构 + SOP状态机 + 双脑路由)
日期: 2026-02-02
团队规模: 2人
预估周期: 6周
核心目标: 实现数据质控 Agent 的完整闭环 + 智能化交互 + 扩展能力层
0. 架构适配性评估
本架构设计充分考虑了临床研究的多种业务场景,通过 SOP状态机 + 双引擎 + 可扩展工具层 的设计,实现了良好的适配性和扩展性。
0.1 目标场景覆盖度
| 场景 | 覆盖度 | 核心支撑组件 | 备注 |
|---|---|---|---|
| 1. 拍照识别 + 自动录入 | 🟡 60% | VisionService + ToolsService | 需新增视觉能力 |
| 2. 数据质控 | 🟢 95% | HardRuleEngine + SoftRuleEngine | 核心场景,完全覆盖 |
| 3. 入排标准判断 | 🟢 90% | SopEngine + 硬规则配置 | 配置 Skill 即可 |
| 4. 方案偏离检测 | 🟢 80% | SoftRuleEngine + search_protocol | 需配置访视窗口规则 |
| 5. AE事件检测 | 🟢 80% | 硬规则触发 + 软指令评估 | 需配置AE识别规则 |
| 6. 伦理合规检测 | 🟢 80% | HardRuleEngine | 配置伦理规则即可 |
| 7. 定期报告生成 | 🟡 50% | SchedulerService + ReportService | 需新增定时任务 |
0.2 架构扩展性评价
为什么说这套架构适配性强?
| 扩展维度 | 实现方式 | 复杂度 |
|---|---|---|
| 新增质控规则 | 在 iit_skills 表插入 JSON 配置 |
⭐ 低 |
| 新增业务场景 | 新增 Skill 类型 + 配置 SOP 流程 | ⭐⭐ 中低 |
| 新增工具能力 | 在 ToolsService 增加工具定义 | ⭐⭐ 中低 |
| 新增数据源 | 新增 Adapter(如 OdmAdapter) | ⭐⭐⭐ 中 |
| 新增交互入口 | 复用 ChatService 路由逻辑 | ⭐⭐ 中低 |
核心优势:
- 配置驱动:新业务场景主要是"写配置"而非"写代码"
- 插件式工具:ToolsService 支持动态注册新工具
- 引擎复用:所有场景共享 HardRuleEngine / SoftRuleEngine
- SOP 状态机:流程可配置、可追溯、可审计
0.3 完整架构图(含扩展能力)
┌─────────────────────────────────────────────────────────────────────┐
│ 入口层 (Multi-Channel) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────┐ │
│ │ 企业微信文本 │ │ 企业微信图片 │ │ PC Workbench │ │ 定时触发 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────┐
│ 路由层 (Router) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────┐ │
│ │ ChatService │ │VisionService │ │ API Routes │ │Scheduler │ │
│ │ (文本路由) │ │ (图片识别) │ │ (REST API) │ │ (定时) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 调度层 (SopEngine) │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Skill 配置 (Postgres) │ │
│ │ • qc_process (质控流程) • ae_detection (AE检测) │ │
│ │ • inclusion_check (入排) • protocol_deviation (方案偏离) │ │
│ │ • ethics_check (伦理) • weekly_report (周报) │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────┼──────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │HardRuleEngine│ │SoftRuleEngine│ │ReportService │ │
│ │ (CPU规则) │ │ (LLM推理) │ │ (报告生成) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 工具层 (ToolsService) │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │read_clinical_data│ │write_clinical_data│ │search_protocol │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │check_visit_window│ │assess_ae_causality│ │check_ethics │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │get_project_stats │ │manage_issue │ │send_notification│ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 适配器层 (Adapters) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────┐ │
│ │RedcapAdapter │ │ DifyClient │ │WechatAdapter │ │VLMAdapter│ │
│ │ (EDC数据) │ │ (知识库) │ │ (消息推送) │ │ (视觉) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────────┘
1. 架构决策总结
本计划基于以下 5 份架构设计文档的综合审查:
| 文档 | 核心观点 | 状态 |
|---|---|---|
| 架构决策白皮书 | 放弃 MCP/复杂框架,采用 Postgres-Only + Service-First | ✅ 认可 |
| V2.2 实施指南 | 混合双引擎(硬规则 + 软指令)+ 用户偏好 | ✅ 认可 |
| V2.2 工具泛化 | 3-5 个"瑞士军刀"通用工具替代 100 个专用工具 | ✅ 认可 |
| V2.3 健壮性设计 | 三层防御(模糊映射 + 容错重试 + 空结果兜底) | ✅ 认可 |
| V2.4 SOP状态机 | 粗粒度 SOP 节点 + 节点内 ReAct | ✅ 认可 |
1.1 最终架构选型:双脑路由模型
核心理念:左脑(SOP) 保证严谨合规,右脑(ReAct) 提供灵活智能
┌─────────────────────────────────────────────────────────────────────┐
│ 企业微信 / 前端入口 │
│ [文本消息] [图片消息] [定时触发] │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 🧠 意图路由层 (IntentService) │
│ LLM 驱动,非正则匹配 │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ 输入: "帮我查下那个发烧的病人是谁?" │ │
│ │ 输出: { type: "QA_QUERY", entities: {...}, needsClarification } │ │
│ └────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ 📐 左脑 (SOP) │ │ 🎨 右脑 (ReAct) │ │ ❓ 追问机制 │
│ 结构化任务 │ │ 开放性查询 │ │ 信息不全 │
│ 写操作必经 │ │ 只读不写 │ │ 主动澄清 │
│ │ │ │ │ │
│ • 质控流程 │ │ • 多步推理 │ │ • "请问您指的是 │
│ • 入排判断 │ │ • 模糊查询 │ │ 哪位患者?" │
│ • 数据录入 │ │ • 统计分析 │ │ │
└──────────────────┘ └──────────────────┘ └──────────────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────────────────────┐
│ SopEngine (状态机) │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ 节点A: HardRuleEngine → 节点B: SoftRuleEngine → 节点C: ... │ │
│ └────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ ToolsService (工具层) │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 🔓 只读工具 (ReAct 可用) │ 🔒 读写工具 (仅 SOP 可用) │ │
│ │ • read_clinical_data │ • write_clinical_data │ │
│ │ • search_protocol │ • manage_issue │ │
│ │ • get_project_stats │ • update_record │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 适配器层 (Adapters) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────┐ │
│ │RedcapAdapter │ │ DifyClient │ │WechatAdapter │ │VLMAdapter│ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────────┘
双脑对比:
| 维度 | 左脑 (SOP 状态机) | 右脑 (ReAct Agent) |
|---|---|---|
| 擅长 | 执行标准流程、合规检查 | 处理模糊提问、多步查询 |
| 典型指令 | "对 P001 进行入排质控" | "帮我查下最近那个发烧的病人" |
| 思维方式 | 线性执行 (Step 1 → Step 2) | 循环推理 (思考→查库→再思考) |
| 数据权限 | 读写皆可 | 只读 (Read-Only) |
| 用户评价 | "靠谱但死板" | "聪明但不可控" |
1.2 核心设计原则
| 原则 | 描述 |
|---|---|
| Postgres-Only | 无 Redis,用 pg-boss 替代队列,用 Postgres 存储 Skill 配置 |
| Service-First | 不用 MCP Server,用 Service Class 实现工具调用 |
| 混合双引擎 | 硬规则(CPU) + 软指令(LLM),能用规则的不用 AI |
| SOP 状态机 | 粗粒度节点控制流程,节点内 ReAct 保持灵活性 |
| 三层防御 | 字段映射 + 自我修正 + 空结果兜底 |
2. 现有代码资产盘点
2.1 已完成的组件
| 组件 | 路径 | 状态 | 备注 |
|---|---|---|---|
| ChatService | services/ChatService.ts |
✅ 可复用 | 需扩展路由逻辑 |
| SessionMemory | agents/SessionMemory.ts |
✅ 可复用 | 内存存储,支持过期清理 |
| RedcapAdapter | adapters/RedcapAdapter.ts |
✅ 可复用 | 核心数据访问层 |
| WechatService | services/WechatService.ts |
✅ 可复用 | 企业微信消息推送 |
| DifyClient | common/rag/DifyClient.ts |
✅ 可复用 | 知识库检索 |
| LLMFactory | common/llm/adapters/LLMFactory.ts |
✅ 可复用 | 统一 LLM 调用 |
2.2 待开发的组件
| 组件 | 优先级 | 说明 |
|---|---|---|
iit_skills 表 |
P0 | Skill 配置存储 |
iit_field_mapping 表 |
P0 | 字段名映射字典 |
ToolsService 类 |
P0 | 统一工具管理 |
HardRuleEngine 类 |
P0 | JSON Logic 执行器 |
SoftRuleEngine 类 |
P1 | LLM 推理 + 自我修正 |
SopEngine 类 |
P1 | 状态机调度器 |
iit_user_preferences 表 |
P2 | 用户偏好存储 |
3. 开发阶段规划
Phase 1:基础设施层(Week 1)
Day 1-2:数据库 Schema 扩展
目标:创建 Skill 配置和字段映射表
任务清单
-
T1.1 设计
iit_skills表结构model IitSkill { id String @id @default(uuid()) projectId String // 绑定项目 skillType String // qc_process | daily_briefing | general_chat name String // 技能名称 config Json // 核心配置 JSON isActive Boolean @default(true) version Int @default(1) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([projectId, skillType]) @@map("iit_skills") @@schema("iit_schema") } -
T1.2 设计
iit_field_mapping表结构model IitFieldMapping { id String @id @default(uuid()) projectId String // 项目级别映射 aliasName String // LLM 可能传的名称(如 "gender", "性别") actualName String // REDCap 实际字段名(如 "sex") createdAt DateTime @default(now()) @@unique([projectId, aliasName]) @@map("iit_field_mapping") @@schema("iit_schema") } -
T1.3 执行数据库迁移
npx prisma db push npx prisma generate -
T1.4 插入测试数据(第一个 Skill 配置)
验收标准:
- 表创建成功
- 能正常 CRUD 操作
- 测试 Skill 配置可查询
Day 3-4:ToolsService 实现
目标:创建统一的工具管理层
任务清单
-
T2.1 创建
ToolsService.tsbackend/src/modules/iit-manager/services/ToolsService.ts -
T2.2 实现工具定义(TOOL_DEFINITIONS)
export const TOOL_DEFINITIONS = [ { name: "read_clinical_data", description: "读取 REDCap 临床数据", parameters: { type: "object", properties: { record_id: { type: "string" }, fields: { type: "array", items: { type: "string" } } }, required: ["record_id", "fields"] } }, { name: "search_protocol", description: "检索研究方案文档", parameters: { ... } }, { name: "manage_issue", description: "提出质疑或发送通知", parameters: { ... } }, { name: "get_project_stats", description: "获取项目统计数据", parameters: { ... } } ]; -
T2.3 实现字段映射逻辑(第一层防御)
private async mapFields(projectId: string, fields: string[]): Promise<string[]> { const mappings = await prisma.iitFieldMapping.findMany({ where: { projectId } }); const mappingDict = Object.fromEntries( mappings.map(m => [m.aliasName.toLowerCase(), m.actualName]) ); return fields.map(f => mappingDict[f.toLowerCase()] || f); } -
T2.4 实现
executeTool()统一入口 -
T2.5 实现空结果兜底(第三层防御)
验收标准:
- 4 个工具定义完成
- 字段映射逻辑生效
- 空结果返回友好消息
Day 5:HardRuleEngine 实现
目标:创建 CPU 执行的硬规则引擎
任务清单
-
T3.1 安装 json-logic-js
npm install json-logic-js npm install -D @types/json-logic-js -
T3.2 创建
HardRuleEngine.tsbackend/src/modules/iit-manager/engines/HardRuleEngine.ts -
T3.3 实现规则执行逻辑
import jsonLogic from 'json-logic-js'; export class HardRuleEngine { run(rules: HardRule[], data: Record<string, any>): RuleResult[] { const errors: RuleResult[] = []; for (const rule of rules) { const passed = jsonLogic.apply(rule.logic, data); if (!passed) { errors.push({ field: rule.field, message: rule.message, severity: rule.severity || 'error' }); } } return errors; } } -
T3.4 编写单元测试
验收标准:
- 能正确执行
{ ">=": [{ "var": "age" }, 18] }类规则 - 返回结构化错误列表
- 单元测试覆盖主要场景
Phase 2:引擎层实现(Week 2)
Day 6-7:SoftRuleEngine 实现
目标:创建 LLM 推理引擎(含自我修正)
任务清单
-
T4.1 创建
SoftRuleEngine.tsbackend/src/modules/iit-manager/engines/SoftRuleEngine.ts -
T4.2 实现自我修正回路(第二层防御)
async runWithRetry( instruction: string, context: any, maxRetries: number = 3 ): Promise<SoftRuleResult> { let history: Message[] = [...]; for (let i = 0; i < maxRetries; i++) { const response = await this.llm.chat(history); if (response.hasToolCall) { try { const result = await this.tools.executeTool( response.toolName, response.args ); history.push({ role: 'tool', content: JSON.stringify(result) }); } catch (error) { // 自我修正:告诉 LLM 它错了 history.push({ role: 'user', content: `工具调用失败: ${error.message}。请检查参数并重试。` }); continue; } } else { return this.parseResult(response.content); } } return { passed: false, reason: '多次重试后仍失败' }; } -
T4.3 实现结果解析(要求 LLM 返回结构化 JSON)
-
T4.4 编写单元测试
验收标准:
- 能调用工具并获取结果
- 工具失败时能自动重试
- 返回结构化判断结果
Day 8-9:SopEngine 实现
目标:创建 SOP 状态机调度器
任务清单
-
T5.1 创建
SopEngine.tsbackend/src/modules/iit-manager/engines/SopEngine.ts -
T5.2 定义 Skill 配置格式(SOP 流程图)
interface SopConfig { name: string; start_node: string; nodes: { [nodeId: string]: { type: 'hard_rule' | 'soft_instruction'; // hard_rule 类型 rules?: HardRule[]; // soft_instruction 类型 instruction?: string; tools?: string[]; // 流转 on_pass: string; on_fail: string; on_error?: string; } } } -
T5.3 实现状态机执行逻辑
async run(skillConfig: SopConfig, data: any): Promise<SopResult> { let currentNodeId = skillConfig.start_node; let context = { ...data }; const trace: TraceItem[] = []; while (currentNodeId && !currentNodeId.startsWith('end')) { const node = skillConfig.nodes[currentNodeId]; trace.push({ node: currentNodeId, timestamp: new Date() }); let result: NodeResult; if (node.type === 'hard_rule') { result = this.hardEngine.run(node.rules, context); } else { result = await this.softEngine.runWithRetry(node.instruction, context); } // 状态流转 currentNodeId = result.passed ? node.on_pass : node.on_fail; // 记录违规 if (!result.passed) { await this.savePendingAction(result); } } return { trace, finalState: currentNodeId }; } -
T5.4 实现违规记录保存(Shadow State)
-
T5.5 编写集成测试
验收标准:
- 能按流程图顺序执行节点
- 硬规则和软指令都能正确调度
- 违规记录保存到
iit_pending_actions表
Day 10:ChatService 集成
目标:将 SopEngine 集成到现有 ChatService
任务清单
-
T6.1 扩展意图识别逻辑
private detectIntent(message: string): Intent { // 识别质控任务 if (/质控|检查|校验|QC/.test(message)) { return { type: 'qc_task', ... }; } // 其他意图... } -
T6.2 增加质控任务路由
async handleMessage(userId: string, message: string): Promise<string> { const intent = this.detectIntent(message); if (intent.type === 'qc_task') { // 路由到 SopEngine return this.handleQcTask(userId, intent); } // 普通问答继续走原有逻辑 return this.handleGeneralChat(userId, message); } -
T6.3 实现
handleQcTask()方法
验收标准:
- 质控任务走 SopEngine
- 普通问答走原有 LLM 逻辑
- 两条路径互不干扰
Phase 3:配置与测试(Week 3)
Day 11-12:第一个完整 Skill 配置
目标:配置第一个项目的质控流程
任务清单
-
T7.1 设计肺癌研究质控流程(示例)
{ "name": "肺癌研究入组质控", "start_node": "baseline_check", "nodes": { "baseline_check": { "type": "hard_rule", "rules": [ { "field": "age", "logic": { ">=": [{"var":"age"}, 18] }, "message": "年龄<18岁" }, { "field": "age", "logic": { "<=": [{"var":"age"}, 75] }, "message": "年龄>75岁" }, { "field": "ecog", "logic": { "<=": [{"var":"ecog"}, 2] }, "message": "ECOG>2" } ], "on_pass": "history_check", "on_fail": "end_with_violation" }, "history_check": { "type": "soft_instruction", "instruction": "检查既往史,排除间质性肺炎、活动性感染、严重心血管疾病。", "tools": ["read_clinical_data"], "on_pass": "medication_check", "on_fail": "end_review_required" }, "medication_check": { "type": "soft_instruction", "instruction": "检查合并用药,排除其他抗肿瘤药物。", "tools": ["read_clinical_data"], "on_pass": "end_success", "on_fail": "end_review_required" } } } -
T7.2 插入 Skill 配置到数据库
-
T7.3 配置字段映射
INSERT INTO iit_field_mapping (project_id, alias_name, actual_name) VALUES ('project-uuid', 'age', 'age_calculated'), ('project-uuid', '年龄', 'age_calculated'), ('project-uuid', 'ecog', 'ecog_score'), ('project-uuid', '既往史', 'medical_history_text'), ('project-uuid', 'history', 'medical_history_text');
验收标准:
- Skill 配置存储成功
- 字段映射配置完成
Day 13-14:端到端测试
目标:完整流程测试
测试场景
| 场景 | 输入 | 期望输出 |
|---|---|---|
| 场景1:年龄不合规 | { "age": 16, "ecog": 1 } |
触发硬规则违规,记录到 pending_actions |
| 场景2:病史违规 | { "age": 30, "ecog": 1, "history": "间质性肺炎" } |
AI 识别违规,转人工复核 |
| 场景3:全部通过 | { "age": 45, "ecog": 0, "history": "无特殊" } |
流程正常结束 |
| 场景4:字段名映射 | Agent 传 fields=["年龄"] |
自动映射为 age_calculated |
| 场景5:工具失败重试 | REDCap 返回空 | Agent 收到友好提示,正确回复用户 |
任务清单
- T8.1 编写端到端测试脚本
- T8.2 通过企业微信发送测试消息
- T8.3 验证 pending_actions 记录
- T8.4 验证 audit_log 记录
- T8.5 性能测试(目标 < 5秒响应)
验收标准:
- 5 个测试场景全部通过
- 响应时间 < 5秒
- 日志完整可追溯
Day 15:文档与收尾
任务清单
- T9.1 更新 API 文档
- T9.2 更新模块状态文档
- T9.3 记录技术债务
- T9.4 代码 Review 与合并
Phase 4:定时任务与高级工具(Week 4 前半)
目标:实现定时报告、高级质控工具
Day 16-17:定时任务与报告生成
目标:实现每周自动生成研究进度报告
任务清单
-
T10.1 创建
SchedulerService.ts(基于 pg-boss)backend/src/modules/iit-manager/services/SchedulerService.ts -
T10.2 实现定时任务调度
import PgBoss from 'pg-boss'; export class SchedulerService { private boss: PgBoss; async init() { this.boss = new PgBoss(process.env.DATABASE_URL); await this.boss.start(); // 注册周报任务处理器 await this.boss.work('weekly-report', this.handleWeeklyReport.bind(this)); // 每周一早上9点执行 await this.boss.schedule('weekly-report', '0 9 * * 1', { projectId: 'all' }); } private async handleWeeklyReport(job: Job) { const report = await this.reportService.generateWeeklyReport(job.data.projectId); await this.wechatService.sendToAdmins(report); } } -
T10.3 创建
ReportService.tsbackend/src/modules/iit-manager/services/ReportService.ts -
T10.4 实现周报生成逻辑
export class ReportService { async generateWeeklyReport(projectId: string): Promise<string> { const stats = await this.getProjectStats(projectId); const report = `
📊 ${stats.projectName} 周报 📅 ${stats.weekRange}
入组进度
- 本周新入组:${stats.newEnrollments} 例
- 累计入组:${stats.totalEnrollments} / ${stats.targetEnrollments} 例
- 完成率:${stats.completionRate}%
数据质量
- 待处理质疑:${stats.pendingQueries} 条
- 本周关闭质疑:${stats.closedQueries} 条
- 方案偏离:${stats.protocolDeviations} 例
AE/SAE
- 本周新增 AE:${stats.newAEs} 例
- 本周新增 SAE:${stats.newSAEs} 例
下周重点
${stats.upcomingVisits.map(v => - ${v.patientId}: ${v.visitName} (${v.dueDate})).join('\n')}
`;
return report;
}
}
- [ ] **T10.5** 配置周报 Skill
```json
{
"skillType": "weekly_report",
"config": {
"schedule": "0 9 * * 1",
"recipients": ["admin_group"],
"sections": ["enrollment", "data_quality", "ae_summary", "upcoming_visits"]
}
}
验收标准:
- 每周一自动生成周报
- 周报通过企业微信发送给管理员
- 周报内容完整、格式美观
Day 18-19:高级质控工具扩展
目标:新增方案偏离、AE评估、伦理检查工具
任务清单
-
T11.1 新增
check_visit_window工具(方案偏离检测){ name: "check_visit_window", description: "检查访视是否在方案允许的时间窗口内", parameters: { record_id: { type: "string" }, visit_id: { type: "string" }, actual_date: { type: "string", format: "date" } }, handler: async (args) => { const baseline = await this.getBaselineDate(args.record_id); const window = await this.getVisitWindow(args.visit_id); const actualDays = this.daysBetween(baseline, args.actual_date); const inWindow = actualDays >= window.minDays && actualDays <= window.maxDays; return { inWindow, expectedRange: `Day ${window.minDays} - Day ${window.maxDays}`, actualDay: actualDays, deviation: inWindow ? 0 : Math.min( Math.abs(actualDays - window.minDays), Math.abs(actualDays - window.maxDays) ) }; } } -
T11.2 新增
assess_ae_causality工具(AE因果关系评估){ name: "assess_ae_causality", description: "评估不良事件与研究药物的因果关系", parameters: { record_id: { type: "string" }, ae_id: { type: "string" } }, handler: async (args) => { const ae = await this.getAEDetails(args.record_id, args.ae_id); const drugInfo = await this.getDrugExposure(args.record_id); // 使用 SoftRuleEngine 评估 const assessment = await this.softEngine.runWithRetry( `根据以下信息评估AE与研究药物的因果关系: AE信息:${JSON.stringify(ae)} 用药信息:${JSON.stringify(drugInfo)} 请给出:肯定相关/可能相关/可能无关/肯定无关/无法评估`, { ae, drugInfo } ); return assessment; } } -
T11.3 新增
check_ethics_compliance工具(伦理合规检查){ name: "check_ethics_compliance", description: "检查是否符合伦理要求", parameters: { record_id: { type: "string" }, check_type: { type: "string", enum: ["informed_consent", "age_requirement", "vulnerable_population"] } }, handler: async (args) => { const rules = { informed_consent: { logic: { "<=": [{ "var": "icf_date" }, { "var": "enrollment_date" }] }, message: "入组日期早于知情同意签署日期" }, age_requirement: { logic: { ">=": [{ "var": "age" }, 18] }, message: "未成年受试者需要法定监护人签署同意书" } }; const data = await this.getRecordData(args.record_id); return this.hardEngine.run([rules[args.check_type]], data); } } -
T11.4 更新 ToolsService 工具列表
验收标准:
- 访视超窗能被正确检测
- AE 因果关系能给出评估结论
- 伦理违规能被识别
Phase 5:智能化增强 - 双脑架构(Week 4 后半 - Week 5)
目标:实现 LLM 意图路由 + ReAct 多步推理 + 追问机制,让 Agent "懂人话"
优先级调整说明:智能化交互比视觉识别更影响用户留存,优先实现
Day 20-21:意图路由层实现
目标:用 LLM 替代正则匹配,实现智能意图识别
任务清单
-
T12.1 创建
IntentService.tsbackend/src/modules/iit-manager/services/IntentService.ts -
T12.2 实现 LLM 驱动的意图识别
export class IntentService { private llm; async detect(message: string, history: Message[]): Promise<IntentResult> { const prompt = `
你是一个临床研究助手的"分诊台"。请分析用户输入,返回 JSON。
用户输入: "${message}"
分类标准:
- QC_TASK: 明确的质控、检查、录入指令(如"检查P001的入排标准")
- QA_QUERY: 模糊的查询、分析、统计问题(如"查下那个发烧的病人是谁")
- PROTOCOL_QA: 关于研究方案的问题(如"访视窗口是多少天")
- UNCLEAR: 指代不清,缺少关键信息(如"他怎么样了?")
返回格式: { "type": "QC_TASK" | "QA_QUERY" | "PROTOCOL_QA" | "UNCLEAR", "confidence": 0.0-1.0, "entities": { "record_id": "...", "visit": "..." }, "missing_info": "如果 UNCLEAR,说明缺什么信息", "clarification_question": "如果 UNCLEAR,生成追问句" }`;
const response = await this.llm.chat([
{ role: 'system', content: prompt },
...history.slice(-3),
{ role: 'user', content: message }
]);
return JSON.parse(response.content);
}
}
- [ ] **T12.3** 实现降级策略(LLM 不可用时回退关键词匹配)
```typescript
async detectWithFallback(message: string, history: Message[]): Promise<IntentResult> {
try {
return await this.detect(message, history);
} catch (error) {
logger.warn('[IntentService] LLM 不可用,回退到关键词匹配');
return this.keywordFallback(message);
}
}
private keywordFallback(message: string): IntentResult {
if (/质控|检查|校验|QC|入排/.test(message)) {
return { type: 'QC_TASK', confidence: 0.6, entities: {} };
}
return { type: 'QA_QUERY', confidence: 0.5, entities: {} };
}
- T12.4 集成到 ChatService 路由层
验收标准:
- "查下那个发烧的病人" → 识别为 QA_QUERY
- "对 P001 进行入排质控" → 识别为 QC_TASK
- "他怎么样了" → 识别为 UNCLEAR,生成追问句
- LLM 服务中断时自动降级
Day 22-23:ReAct Agent 实现
目标:创建多步推理引擎,支持循环思考和工具调用
任务清单
-
T13.1 创建
ReActEngine.tsbackend/src/modules/iit-manager/engines/ReActEngine.ts -
T13.2 实现 ReAct 循环(思考→行动→观察→再思考)
export class ReActEngine { private maxIterations = 5; // 防止死循环 private maxTokens = 4000; // Token 预算限制 private tokenCounter = 0; // 工具白名单:ReAct 只能调用只读工具 private readonly READONLY_TOOLS = [ 'read_clinical_data', 'search_protocol', 'get_project_stats', 'check_visit_window' ]; async run(query: string, context: AgentContext): Promise<ReActResult> { const systemPrompt = `你是一个临床研究智能助手。你可以使用以下工具回答问题:
${this.formatToolDescriptions(this.READONLY_TOOLS)}
请按照 ReAct 模式思考:
- 思考:分析问题,决定下一步
- 行动:调用工具获取信息
- 观察:查看工具返回结果
- 重复以上步骤,直到能回答用户问题
注意:你只能查询数据,不能修改数据。如果用户需要修改操作,请引导他们使用正式的质控流程。`;
let messages: Message[] = [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: query }
];
const trace: TraceItem[] = [];
for (let i = 0; i < this.maxIterations; i++) {
// Token 预算检查
if (this.tokenCounter > this.maxTokens) {
return {
success: false,
content: '抱歉,这个问题比较复杂,请尝试更具体的描述。',
trace
};
}
const response = await this.llm.chat(messages, {
tools: this.getReadonlyToolDefinitions()
});
this.tokenCounter += response.usage?.total_tokens || 0;
trace.push({ iteration: i, response: response.content });
// AI 决定结束
if (!response.toolCalls || response.toolCalls.length === 0) {
return { success: true, content: response.content, trace };
}
// 执行工具调用
let errorCount = 0;
for (const toolCall of response.toolCalls) {
// 安全检查:只允许只读工具
if (!this.READONLY_TOOLS.includes(toolCall.name)) {
messages.push({
role: 'tool',
toolCallId: toolCall.id,
content: `错误:工具 ${toolCall.name} 不在允许列表中。你只能使用只读工具。`
});
errorCount++;
continue;
}
try {
const result = await this.tools.executeTool(toolCall.name, toolCall.args);
messages.push({
role: 'tool',
toolCallId: toolCall.id,
content: JSON.stringify(result)
});
} catch (error) {
errorCount++;
messages.push({
role: 'tool',
toolCallId: toolCall.id,
content: `工具调用失败: ${error.message}`
});
}
}
// 幻觉熔断:连续 2 次工具调用全部失败
if (errorCount >= 2) {
return {
success: false,
content: '抱歉,我遇到了一些技术问题,无法获取相关信息。请稍后重试或联系管理员。',
trace
};
}
}
return {
success: false,
content: '抱歉,我无法在有限步骤内完成这个查询。请尝试更具体的问题。',
trace
};
}
}
- [ ] **T13.3** 实现 trace 日志记录(调试用)
```typescript
private async saveTrace(userId: string, query: string, trace: TraceItem[]) {
await prisma.iitAgentTrace.create({
data: {
userId,
query,
trace: JSON.stringify(trace),
createdAt: new Date()
}
});
}
- T13.4 编写单元测试
验收标准:
- "最近入组的女性患者平均年龄" → 自动调用工具查询并计算
- 连续工具失败时触发熔断
- Token 超预算时优雅终止
- 尝试调用写工具时被拦截
Day 24:追问机制与上下文增强
目标:信息不全时主动追问,增强上下文记忆
任务清单
-
T14.1 实现追问机制
// ChatService 中的追问逻辑 async handleMessage(userId: string, message: string): Promise<string> { const history = this.sessionMemory.getHistory(userId); const intent = await this.intentService.detect(message, history); // 信息不全,主动追问 if (intent.type === 'UNCLEAR') { const clarification = intent.clarification_question || `请问您能具体说明一下吗?例如:${this.getSuggestions(intent)}`; this.sessionMemory.addMessage(userId, 'assistant', clarification); return clarification; } // 根据意图路由 if (intent.type === 'QC_TASK') { return this.sopEngine.run(intent); } else { return this.reactEngine.run(message, { history, intent }); } } -
T14.2 增强 SessionMemory,支持实体记忆
export class SessionMemory { // 新增:记住对话中提到的实体 private entityMemory: Map<string, EntityContext> = new Map(); addEntityContext(userId: string, entities: Record<string, any>) { const existing = this.entityMemory.get(userId) || {}; this.entityMemory.set(userId, { ...existing, ...entities }); } resolveReference(userId: string, reference: string): string | null { const context = this.entityMemory.get(userId); if (!context) return null; // 解析 "他"、"这个患者" 等指代 if (['他', '她', '这个患者', '那个病人'].includes(reference)) { return context.lastMentionedPatient; } return null; } } -
T14.3 实现指代消解
// 在处理消息前,先解析指代 private resolveReferences(userId: string, message: string): string { const pronouns = ['他', '她', '这个患者', '那个病人']; let resolved = message; for (const pronoun of pronouns) { if (message.includes(pronoun)) { const actual = this.sessionMemory.resolveReference(userId, pronoun); if (actual) { resolved = resolved.replace(pronoun, actual); } } } return resolved; }
验收标准:
- "他怎么样了" → 回复 "请问您指的是哪位患者?"
- 如果上文提到 P001,"他怎么样了" → 自动解析为 P001
- 对话上下文在会话内保持连贯
Day 25:集成测试与优化
目标:完成双脑架构的完整测试
测试场景(Phase 5)
| 场景 | 输入 | 期望输出 |
|---|---|---|
| 场景6:模糊查询 | "查下最近入组的病人" | ReAct 自动查询并返回列表 |
| 场景7:多步推理 | "最近入组的女性平均年龄" | 多步工具调用 + 计算结果 |
| 场景8:追问 | "他怎么样了" | "请问您指的是哪位患者?" |
| 场景9:指代消解 | (上文提到P001) "他的入排状态" | 自动识别为 P001 |
| 场景10:写操作拦截 | (ReAct中) 尝试修改数据 | 被拦截并引导到 SOP 流程 |
| 场景11:熔断 | 连续工具失败 | 优雅终止并提示 |
任务清单
- T15.1 编写 Phase 5 测试脚本
- T15.2 验证意图识别准确率(目标 > 85%)
- T15.3 验证 ReAct 多步推理成功率
- T15.4 验证追问机制用户体验
验收标准:
- 11 个测试场景全部通过
- 意图识别准确率 > 85%
- ReAct 平均迭代次数 < 3
- 用户反馈"不再觉得像傻子"
Phase 6:视觉能力(Week 6)
目标:支持拍照上传 → 识别 → 自动录入 REDCap
Day 26-27:视觉能力集成
目标:支持拍照上传 → 识别 → 自动录入 REDCap
任务清单
-
T16.1 创建
VisionService.tsbackend/src/modules/iit-manager/services/VisionService.ts -
T16.2 集成视觉大模型(推荐 Qwen-VL / GPT-4V)
export class VisionService { private vlmAdapter: VLMAdapter; async extractFromImage(imageUrl: string, projectId: string): Promise<ExtractedData> { // 1. 调用视觉模型识别内容 const rawText = await this.vlmAdapter.recognize(imageUrl); // 2. 结构化提取 const structured = await this.structureData(rawText, projectId); // 3. 匹配表单 const formMatch = await this.matchForm(structured, projectId); return { rawText, structured, formMatch }; } } -
T16.3 创建表单模板表
iit_form_templatesmodel IitFormTemplate { id String @id @default(uuid()) projectId String formName String // REDCap 表单名称 fieldSchema Json // 表单字段结构 keywords String[] // 用于匹配的关键词 createdAt DateTime @default(now()) @@map("iit_form_templates") @@schema("iit_schema") } -
T16.4 扩展
RedcapAdapter.writeRecord()写入能力 -
T16.5 ChatService 增加图片消息路由
验收标准:
- 上传化验单图片能识别内容
- 自动匹配到正确的 REDCap 表单
- 高置信度时自动录入,低置信度时人工确认
Day 28:最终集成测试
目标:完成全部场景测试
测试场景(全量)
| 场景 | 输入 | 期望输出 |
|---|---|---|
| 场景12:拍照识别 | 上传化验单图片 | 识别内容,匹配表单,自动录入 |
| 场景13:访视超窗 | V3 访视超出窗口期 5 天 | 检测到方案偏离 |
| 场景14:AE评估 | SAE 事件数据 | 给出因果关系评估 |
| 场景15:伦理违规 | ICF 日期晚于入组日期 | 识别伦理违规 |
| 场景16:周报生成 | 触发定时任务 | 生成并发送周报 |
任务清单
- T17.1 编写全量测试脚本
- T17.2 验证视觉识别准确率(目标 > 85%)
- T17.3 性能优化(图片处理 < 10s)
- T17.4 更新文档和部署指南
验收标准:
- 16 个测试场景全部通过
- 图片识别准确率 > 85%
- 定时任务连续运行 7 天无故障
4. 风险与应对
4.1 基础架构风险
| 风险 | 影响 | 应对措施 |
|---|---|---|
| JSON Logic 表达能力不足 | 复杂规则无法配置 | 支持 function_name 模式,调用预定义函数 |
| LLM 响应慢 | 用户体验差 | 硬规则先行,减少 LLM 调用 |
| 字段映射字典不全 | 工具调用失败 | 自我修正回路 + 日志记录 + 运营补充 |
| REDCap 不可用 | 流程中断 | 增加 on_error 分支,友好提示 |
4.2 双脑架构风险 (Phase 5)
| 风险 | 影响 | 应对措施 |
|---|---|---|
| 意图识别错误 | 路由到错误分支 | 置信度阈值 + 低置信度时追问确认 |
| ReAct 死循环 | 烧钱、用户等待 | 最大迭代次数 = 5,Token 预算 = 4000 |
| ReAct 调用写工具 | 数据被误改 | 工具白名单,只允许只读工具 |
| LLM 幻觉 | 返回错误信息 | 连续 2 次工具失败触发熔断 |
| LLM 服务中断 | 无法响应 | 降级到关键词匹配 + 友好提示 |
| Token 成本失控 | 费用超预算 | Token 计数 + 单次查询预算限制 |
4.3 扩展能力风险 (Phase 4 & 6)
| 风险 | 影响 | 应对措施 |
|---|---|---|
| 视觉模型识别错误 | 录入错误数据 | 低置信度人工确认,高置信度才自动录入 |
| 定时任务失败 | 周报未发送 | pg-boss 自动重试 + 失败告警 |
| 图片处理超时 | 用户体验差 | 异步处理 + 进度提示 |
5. 成功标准
Phase 1-3 验收标准(核心质控)
- 企业微信发送"质控 ID=001",3秒内收到回复
- 硬规则违规能自动识别并记录
- 软指令能正确调用工具并判断
- 字段名映射生效
- 工具失败能自动重试
- 违规记录可在后台查看
Phase 4 验收标准(定时任务与高级工具)
- 访视超窗能被自动检测并记录
- AE 事件能给出因果关系评估
- 伦理违规能被识别并告警
- 每周一自动生成并发送周报
- 定时任务连续运行 7 天无故障
Phase 5 验收标准(双脑架构)
- 自然语言意图识别准确率 > 85%
- "查下最近入组的病人" → ReAct 自动查询返回
- "他怎么样了" → 主动追问 "请问您指的是哪位患者?"
- 上文提到 P001 后,"他的状态" → 自动识别为 P001
- ReAct 尝试调用写工具 → 被拦截并引导到 SOP
- LLM 服务中断 → 自动降级到关键词匹配
- 用户反馈:"不再觉得 Agent 像个傻子"
Phase 6 验收标准(视觉能力)
- 拍照上传化验单,自动识别并录入 REDCap
- 图片识别准确率 > 85%
- 低置信度时要求人工确认
性能指标
| 指标 | 目标值 |
|---|---|
| 硬规则执行时间 | < 100ms |
| 软指令执行时间 | < 3s |
| 端到端响应时间 | < 5s |
| 意图识别时间 | < 1s |
| ReAct 平均迭代次数 | < 3 |
| 图片识别+录入时间 | < 10s |
| 视觉识别准确率 | > 85% |
| 意图识别准确率 | > 85% |
| 自我修正成功率 | > 80% |
6. 附录:文件路径清单
backend/src/modules/iit-manager/
├── services/
│ ├── ChatService.ts # 扩展路由逻辑(双脑路由)
│ ├── IntentService.ts # 新建:LLM 意图识别 (Phase 5)
│ ├── ToolsService.ts # 新建:统一工具管理
│ ├── SchedulerService.ts # 新建:定时任务调度 (Phase 4)
│ ├── ReportService.ts # 新建:报告生成服务 (Phase 4)
│ ├── VisionService.ts # 新建:视觉识别服务 (Phase 6)
│ └── WechatService.ts # 已存在
├── engines/
│ ├── HardRuleEngine.ts # 新建:JSON Logic 执行器
│ ├── SoftRuleEngine.ts # 新建:LLM 推理引擎
│ ├── SopEngine.ts # 新建:状态机调度器
│ └── ReActEngine.ts # 新建:多步推理引擎 (Phase 5)
├── agents/
│ └── SessionMemory.ts # 已存在,扩展实体记忆 (Phase 5)
├── adapters/
│ ├── RedcapAdapter.ts # 已存在,扩展 writeRecord()
│ └── VLMAdapter.ts # 新建:视觉大模型适配器 (Phase 6)
└── types/
└── index.ts # 扩展类型定义
数据库新增表
iit_schema.iit_skills # Skill 配置存储
iit_schema.iit_field_mapping # 字段名映射
iit_schema.iit_agent_trace # ReAct 推理轨迹 (Phase 5)
iit_schema.iit_form_templates # 表单模板 (Phase 6)
7. 参考文档
- 架构决策白皮书:极简主义的胜利
- V2.2 落地实施指南
- V2.2 工具泛化与灵活性提升指南
- V2.3 健壮性设计与最佳实践
- V2.4 架构模式选型与 SOP 状态机推荐
- V2.6 智能化升级方案:双脑架构
文档维护人:AI Agent
最后更新:2026-02-02(V2.6 整合双脑架构)