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>
1439 lines
55 KiB
Markdown
1439 lines
55 KiB
Markdown
# 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 路由逻辑 | ⭐⭐ 中低 |
|
||
|
||
**核心优势**:
|
||
|
||
1. **配置驱动**:新业务场景主要是"写配置"而非"写代码"
|
||
2. **插件式工具**:ToolsService 支持动态注册新工具
|
||
3. **引擎复用**:所有场景共享 HardRuleEngine / SoftRuleEngine
|
||
4. **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` 表结构
|
||
```prisma
|
||
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` 表结构
|
||
```prisma
|
||
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** 执行数据库迁移
|
||
```bash
|
||
npx prisma db push
|
||
npx prisma generate
|
||
```
|
||
|
||
- [ ] **T1.4** 插入测试数据(第一个 Skill 配置)
|
||
|
||
**验收标准**:
|
||
- [ ] 表创建成功
|
||
- [ ] 能正常 CRUD 操作
|
||
- [ ] 测试 Skill 配置可查询
|
||
|
||
---
|
||
|
||
#### Day 3-4:ToolsService 实现
|
||
|
||
**目标**:创建统一的工具管理层
|
||
|
||
##### 任务清单
|
||
|
||
- [ ] **T2.1** 创建 `ToolsService.ts`
|
||
```
|
||
backend/src/modules/iit-manager/services/ToolsService.ts
|
||
```
|
||
|
||
- [ ] **T2.2** 实现工具定义(TOOL_DEFINITIONS)
|
||
```typescript
|
||
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** 实现字段映射逻辑(第一层防御)
|
||
```typescript
|
||
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
|
||
```bash
|
||
npm install json-logic-js
|
||
npm install -D @types/json-logic-js
|
||
```
|
||
|
||
- [ ] **T3.2** 创建 `HardRuleEngine.ts`
|
||
```
|
||
backend/src/modules/iit-manager/engines/HardRuleEngine.ts
|
||
```
|
||
|
||
- [ ] **T3.3** 实现规则执行逻辑
|
||
```typescript
|
||
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.ts`
|
||
```
|
||
backend/src/modules/iit-manager/engines/SoftRuleEngine.ts
|
||
```
|
||
|
||
- [ ] **T4.2** 实现自我修正回路(第二层防御)
|
||
```typescript
|
||
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.ts`
|
||
```
|
||
backend/src/modules/iit-manager/engines/SopEngine.ts
|
||
```
|
||
|
||
- [ ] **T5.2** 定义 Skill 配置格式(SOP 流程图)
|
||
```typescript
|
||
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** 实现状态机执行逻辑
|
||
```typescript
|
||
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** 扩展意图识别逻辑
|
||
```typescript
|
||
private detectIntent(message: string): Intent {
|
||
// 识别质控任务
|
||
if (/质控|检查|校验|QC/.test(message)) {
|
||
return { type: 'qc_task', ... };
|
||
}
|
||
// 其他意图...
|
||
}
|
||
```
|
||
|
||
- [ ] **T6.2** 增加质控任务路由
|
||
```typescript
|
||
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** 设计肺癌研究质控流程(示例)
|
||
```json
|
||
{
|
||
"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** 配置字段映射
|
||
```sql
|
||
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** 实现定时任务调度
|
||
```typescript
|
||
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.ts`
|
||
```
|
||
backend/src/modules/iit-manager/services/ReportService.ts
|
||
```
|
||
|
||
- [ ] **T10.4** 实现周报生成逻辑
|
||
```typescript
|
||
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` 工具(方案偏离检测)
|
||
```typescript
|
||
{
|
||
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因果关系评估)
|
||
```typescript
|
||
{
|
||
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` 工具(伦理合规检查)
|
||
```typescript
|
||
{
|
||
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.ts`
|
||
```
|
||
backend/src/modules/iit-manager/services/IntentService.ts
|
||
```
|
||
|
||
- [ ] **T12.2** 实现 LLM 驱动的意图识别
|
||
```typescript
|
||
export class IntentService {
|
||
private llm;
|
||
|
||
async detect(message: string, history: Message[]): Promise<IntentResult> {
|
||
const prompt = `
|
||
你是一个临床研究助手的"分诊台"。请分析用户输入,返回 JSON。
|
||
|
||
用户输入: "${message}"
|
||
|
||
分类标准:
|
||
1. QC_TASK: 明确的质控、检查、录入指令(如"检查P001的入排标准")
|
||
2. QA_QUERY: 模糊的查询、分析、统计问题(如"查下那个发烧的病人是谁")
|
||
3. PROTOCOL_QA: 关于研究方案的问题(如"访视窗口是多少天")
|
||
4. 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.ts`
|
||
```
|
||
backend/src/modules/iit-manager/engines/ReActEngine.ts
|
||
```
|
||
|
||
- [ ] **T13.2** 实现 ReAct 循环(思考→行动→观察→再思考)
|
||
```typescript
|
||
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 模式思考:
|
||
1. 思考:分析问题,决定下一步
|
||
2. 行动:调用工具获取信息
|
||
3. 观察:查看工具返回结果
|
||
4. 重复以上步骤,直到能回答用户问题
|
||
|
||
注意:你只能查询数据,不能修改数据。如果用户需要修改操作,请引导他们使用正式的质控流程。`;
|
||
|
||
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** 实现追问机制
|
||
```typescript
|
||
// 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,支持实体记忆
|
||
```typescript
|
||
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** 实现指代消解
|
||
```typescript
|
||
// 在处理消息前,先解析指代
|
||
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.ts`
|
||
```
|
||
backend/src/modules/iit-manager/services/VisionService.ts
|
||
```
|
||
|
||
- [ ] **T16.2** 集成视觉大模型(推荐 Qwen-VL / GPT-4V)
|
||
```typescript
|
||
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_templates`
|
||
```prisma
|
||
model 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. 参考文档
|
||
|
||
1. [架构决策白皮书:极简主义的胜利](../00-系统设计/IIT%20Manager%20Agent%20架构决策白皮书:极简主义的胜利.md)
|
||
2. [V2.2 落地实施指南](../00-系统设计/IIT%20Manager%20Agent%20V2.2%20落地实施指南:极简架构版%20(融合%20Moltbot).md)
|
||
3. [V2.2 工具泛化与灵活性提升指南](../00-系统设计/IIT%20Manager%20Agent%20V2.2:工具泛化与灵活性提升指南.md)
|
||
4. [V2.3 健壮性设计与最佳实践](../00-系统设计/IIT%20Manager%20Agent%20V2.3:健壮性设计与最佳实践.md)
|
||
5. [V2.4 架构模式选型与 SOP 状态机推荐](../00-系统设计/IIT%20Manager%20Agent%20V2.4:架构模式选型与%20SOP%20状态机推荐.md)
|
||
6. [V2.6 智能化升级方案:双脑架构](../00-系统设计/IIT%20Manager%20Agent%20V2.6%20智能化升级方案:双脑架构.md)
|
||
|
||
---
|
||
|
||
**文档维护人**:AI Agent
|
||
**最后更新**:2026-02-02(V2.6 整合双脑架构)
|