feat(iit): Implement event-level QC architecture V3.1 with dynamic rule filtering, report deduplication and AI intent enhancement

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-02-08 21:22:11 +08:00
parent 45c7b32dbb
commit 7a299e8562
51 changed files with 10638 additions and 184 deletions

View File

@@ -1,11 +1,12 @@
# AIclinicalresearch 系统当前状态与开发指南
> **文档版本:** v4.8
> **文档版本:** v4.9
> **创建日期:** 2025-11-28
> **维护者:** 开发团队
> **最后更新:** 2026-02-08
> **🎉 重大里程碑:**
> - **2026-02-08IIT 质控系统优化计划制定!** XML 临床切片格式 + 质控驾驶舱 + 方案 A 确认
> - **2026-02-08IIT 事件级质控 V3.1 开发完成!** record+event 独立质控 + 规则动态过滤 + 报告去重 + AI对话增强
> - **2026-02-08IIT 质控驾驶舱 UI 完成!** XML 临床切片格式 + 质控驾驶舱 + 热力图 + 详情抽屉
> - **2026-02-07IIT 实时质控系统开发完成!** pg-boss 防抖 + 质控日志 + 录入汇总 + 管理端批量操作
> - **2026-02-05IIT Manager Agent V2.9.1 架构设计完成!** 双脑架构 + 三层记忆 + 主动性增强 + 隐私合规
> - **2026-02-02REDCap 生产环境部署完成!** ECS + RDS + HTTPS + 域名全部配置完成
@@ -14,15 +15,14 @@
> - **2026-01-25Protocol Agent MVP完整交付** 一键生成研究方案+Word导出
> - **2026-01-24Protocol Agent 框架完成!** 可复用Agent框架+5阶段对话流程
> - **2026-01-22OSS 存储集成完成!** 阿里云 OSS 正式接入平台基础层
> - **2026-01-21成功替换 Dify** PKB 模块完全使用自研 pgvector RAG 引擎
>
> **最新进展IIT Manager Agent 2026-02-08**
> - ✅ **实时质控系统**pg-boss 防抖 + 质控日志(审计轨迹) + 录入汇总upsert
> - ✅ **管理端批量操作**:一键全量质控 + 一键全量汇总(前后端完整)
> - ✅ **质控优化计划**XML 临床切片格式 + 质控驾驶舱 + 变量标签系统
> - ✅ **双脑架构**SOP 状态机(结构化任务) + ReAct 引擎(模糊查询
> - ✅ **三层记忆**:流水账(全量日志) + 热记忆(高频注入) + 历史书(按需检索)
> - ✅ **隐私合规**PII 脱敏中间件 + 审计日志 + 可恢复脱敏
> - ✅ **事件级质控 V3.1**:每个 record+event 独立质控,不再合并覆盖数据
> - ✅ **规则动态过滤**applicableEvents/applicableForms 配置规则适用范围
> - ✅ **质控报告去重**:按 recordId+ruleId 去重,避免多事件重复问题
> - ✅ **AI 对话增强**:支持"严重违规有几项"等自然语言查询
> - ✅ **质控驾驶舱 UI**PromptBuilder XML 格式 + 热力图 + 详情抽屉
> - ✅ **Bug 修复**formatPatientData 500 错误 + 记录数统计 + 报告限制移除
>
> **部署状态:** ✅ 生产环境运行中 | 公网地址http://8.140.53.236/
> **REDCap 状态:** ✅ 生产环境运行中 | 地址https://redcap.xunzhengyixue.com/
@@ -62,7 +62,7 @@
| **PKB** | 个人知识库 | RAG问答、私人文献库 | ⭐⭐⭐ | 🎉 **Dify已替换自研RAG上线95%** | P1 |
| **ASL** | AI智能文献 | 文献筛选、Meta分析、证据图谱 | ⭐⭐⭐⭐⭐ | 🎉 **智能检索MVP完成60%** - DeepSearch集成 | **P0** |
| **DC** | 数据清洗整理 | ETL + 医学NER百万行级数据 | ⭐⭐⭐⭐⭐ | ✅ **Tool B完成 + Tool C 99%(异步架构+性能优化-99%+多指标转换+7大功能** | **P0** |
| **IIT** | IIT Manager Agent | AI驱动IIT研究助手 - 双脑架构+REDCap集成 | ⭐⭐⭐⭐⭐ | 🎉 **实时质控完成 + UI优化计划制定设计100%,代码45%** | **P0** |
| **IIT** | IIT Manager Agent | AI驱动IIT研究助手 - 双脑架构+REDCap集成 | ⭐⭐⭐⭐⭐ | 🎉 **事件级质控V3.1完成设计100%,代码60%** | **P0** |
| **SSA** | 智能统计分析 | 队列/预测模型/RCT分析 | ⭐⭐⭐⭐⭐ | 📋 规划中 | P2 |
| **ST** | 统计分析工具 | 100+轻量化统计工具 | ⭐⭐⭐⭐ | 📋 规划中 | P2 |
| **RVW** | 稿件审查系统 | 方法学评估、审稿流程、Word导出 | ⭐⭐⭐⭐ | ✅ **开发完成95%** | P3 |

View File

@@ -1,10 +1,12 @@
# IIT Manager Agent模块 - 当前状态与开发指南
> **文档版本:** v2.2
> **文档版本:** v2.4
> **创建日期:** 2026-01-01
> **维护者:** IIT Manager开发团队
> **最后更新:** 2026-02-08 🎉 **质控系统 UI 与 LLM 格式优化计划制定完成!**
> **最后更新:** 2026-02-08 🎉 **事件级质控架构 V3.1 开发完成!**
> **重大里程碑:**
> - ✅ 2026-02-08**事件级质控架构 V3.1 完成**record+event 独立质控 + 规则动态过滤 + 报告去重 + AI对话增强
> - ✅ 2026-02-08**质控驾驶舱 UI 开发完成**PromptBuilder XML 格式 + 驾驶舱页面 + 热力图 + 详情抽屉)
> - ✅ 2026-02-08**质控系统优化计划制定**XML 临床切片格式 + 质控驾驶舱设计 + 方案 A 确认)
> - ✅ 2026-02-07**实时质控系统开发完成**pg-boss防抖 + 质控日志 + 录入汇总 + 管理端批量操作)
> - ✅ 2026-02-05**V2.9.1 完整开发计划发布**(双脑架构 + 三层记忆 + 主动性增强 + 隐私合规)
@@ -44,11 +46,11 @@ IIT Manager Agent研究者发起试验管理助手是一个基于企业微
- AI能力DeepSeek/Qwen + 自研 RAG
### 当前状态
- **开发阶段**:🎉 **实时质控系统核心功能开发完成!待端到端测试**
- **开发阶段**:🎉 **事件级质控架构 V3.1 开发完成!基本测试通过**
- **整体完成度**
- **基础设施**85%REDCap + 企业微信 + AI 对话 + 实时质控)
- **基础设施**95%REDCap + 企业微信 + AI 对话 + 实时质控 + 驾驶舱 UI + 事件级质控
- **架构设计**100%V2.9.1 完整开发计划发布)
- **代码实现**45%(实时质控系统已实现)
- **代码实现**60%(实时质控系统 + 驾驶舱 UI + 事件级质控 V3.1 已实现)
#### ✅ 已完成功能(基础设施)
- ✅ 数据库Schema创建iit_schema9个表 = 原5个 + 新增4个质控表
@@ -77,6 +79,26 @@ IIT Manager Agent研究者发起试验管理助手是一个基于企业微
-**自动化工具设计**AutoMapper REDCap Schema 对齐)
-**模块化开发文档**6 份专项文档)
#### ✅ 已完成功能(质控驾驶舱 UI - 2026-02-08
-**PromptBuilder 类**XML 临床切片格式构建器,减少 LLM 幻觉)
-**ChatService 优化**(集成 PromptBuilder使用 XML 格式化数据)
-**质控驾驶舱页面**IitQcCockpitPage.tsx全屏展示模式
-**统计卡片组件**QcStatCards.tsx4 个核心指标)
-**风险热力图组件**RiskHeatmap.tsx受试者×表单矩阵视图
-**详情抽屉组件**QcDetailDrawer.tsx含 LLM Trace 视图)
-**驾驶舱后端 API**iitQcCockpitService + Controller + Routes
-**前端类型定义**qcCockpit.ts完整的 TypeScript 类型)
#### ✅ 已完成功能(事件级质控 V3.1 - 2026-02-08
-**事件级质控架构**(每个 record+event 独立质控,不再合并数据)
-**规则动态过滤**applicableEvents/applicableForms 配置规则适用范围)
-**RedcapAdapter 增强**getAllRecordsByEvent + getFormEventMapping + getEvents
-**SkillRunner 重构**(按 record+event 执行 + filterApplicableRules
-**质控报告去重**(按 recordId+ruleId 去重,避免多事件重复问题)
-**报告自动刷新**(导出 XML 前自动获取最新数据)
-**AI 意图识别增强**(支持"严重违规有几项"等自然语言查询)
-**Bug 修复**formatPatientData 500 错误 + 记录数统计 + 报告限制移除)
#### ⏳ 待实施功能(按 Phase 规划)
| Phase | 内容 | 优先级 | 状态 |
|-------|------|--------|------|
@@ -914,10 +936,11 @@ npx ts-node src/modules/iit-manager/test-wechat-push.ts
---
> **提示**本文档反映IIT Manager Agent模块的最新真实状态每个里程碑完成后必须更新
> **最后更新**2026-01-03 22:30
> **当前进度**Day 1-3 + Phase 1.5完成60%| 下一步Phase 2 Function Calling + Dify知识库
> **最后更新**2026-02-08 22:00
> **当前进度**事件级质控 V3.1 完成60%| 下一步Phase 3 ReAct 引擎 + 流水账
> **重要文档**
> - [Phase 1.5开发完成记录](./06-开发记录/Phase1.5-AI对话集成REDCap完成记录.md) ⭐⭐⭐⭐⭐
> - [事件级质控开发记录](./06-开发记录/2026-02-08-事件级质控与报告优化开发记录.md) ⭐⭐⭐⭐⭐
> - [实时质控系统开发记录](./06-开发记录/2026-02-07-实时质控系统开发记录.md) ⭐⭐⭐⭐⭐
> - [REDCap对接技术方案与实施指南](./04-开发计划/REDCap对接技术方案与实施指南.md) ⭐⭐⭐⭐⭐

View File

@@ -0,0 +1,72 @@
# **CRA Agent 业务逻辑与执行架构书**
**文档目的:** 基于用户定义的 5 大规则体系,构建标准化的 CRA 智能监查工作流。
**适用版本:** IIT Manager Agent V2.9.1
**创建日期:** 2026-02-07
## **🏛️ 一、 规则体系定义 (The 5 Pillars)**
我们将规则分为两类引擎执行,以平衡成本与智能。
| 编号 | 规则类型 | 执行引擎 | 来源方式 | 典型示例 |
| :---- | :---- | :---- | :---- | :---- |
| **1** | **变量质控** | **Hard Engine** (代码) | 自动从 Metadata 同步 | 空值检查、数值范围 (0\<BMI\<50)、逻辑跳转 |
| **2** | **入排标准** | **AI Engine** (LLM) | AI 解析 Protocol \+ 人工确认 | 确诊时间 \< 3个月、实验室指标符合入组线 |
| **3** | **方案偏离 (PD)** | **Hybrid** (混合) | 人工配置逻辑 | 访视超窗 (Date2 \- Date1 \> 28+3)、禁用药物使用 |
| **4** | **AE 事件** | **AI Engine** (LLM) | 预置医学 Prompt | Lab 异常值 vs AE 记录一致性检查 (SAE Reconciliation) |
| **5** | **伦理合规** | **Hard Engine** (代码) | 预置逻辑 | 知情同意书签署日期 \> 访视 1 日期 |
## **🔄 二、 智能监查工作流 (The Workflow)**
### **Step 1: 触发与范围界定 (Trigger)**
* **触发源**
* **自动触发**Webhook (REDCap 数据录入) 或 Cron (每日凌晨)。
* **人工触发**:用户点击“立即监查”。
* **范围优化**:使用 **增量检查 (Incremental Check)**。只对新录入或变更的数据及其相关联规则进行检查。
### **Step 2: 漏斗式质控执行 (Funnel Execution)**
为每个受试者执行以下管道:
1. **Level 1: 阻断性检查 (Blocking)**
* 检查伦理 (ICF) 和 关键变量缺失。
* *如果 Fail*:直接标记为 Critical**中止后续 AI 检查**(省钱)。
2. **Level 2: 基础清洗 (Cleaning)**
* 运行所有变量质控规则 (Hard Rules)。
* 生成基础错误日志。
3. **Level 3: AI 深度监查 (AI Reasoning)**
* 组装 Context (XML+Markdown)。
* 调用 LLM 检查 入排、PD、AE。
* **输出证据链**{ "status": "FAIL", "reason": "...", "evidence": { "var": "alt", "val": 150 } }
### **Step 3: 结果汇总 (Aggregation)**
更新 iit\_qc\_project\_stats 和 iit\_record\_summary 表。
* 生成 **风险热力图 (Risk Heatmap)** 数据源。
### **Step 4: 双模报告输出 (Dual-Mode Reporting)**
* **Mode A: 人类阅读版 (Interactive UI)**
* 热力图 \+ 诊断卡片。
* 支持 **“确认/忽略”** 操作 (Query Loop)。
* **Mode B: LLM 阅读版 (Context Protocol)**
* XML 结构化数据,包含统计摘要 \+ 严重问题清单 \+ 证据链。
### **Step 5: 智能问答 (Q\&A Loop)**
* 用户提问 \-\> ContextBuilder 提取 Mode B 报告 \-\> LLM 回答。
* *场景*:“帮我列出所有疑似未报 AE 的患者。”
## **💡 三、 关键增强点**
1. **SDV 边界声明**:明确系统仅进行“逻辑一致性监查”,无法核对原始病历真伪。
2. **人机回环 (HITL)**:所有 AI 生成的复杂规则(入排/PD必须经过人工 **"Review & Activate"** 才能生效。
3. **三态管理**:质控结果包含 Pending (AI 疑似), Confirmed (人工确认), Ignored (人工忽略)。
## **✅ 结论**
该方案逻辑闭环,技术可行。通过引入 **漏斗执行****人机回环**,可有效解决成本与准确性问题。

View File

@@ -0,0 +1,200 @@
# **基于 Skills 的 CRA 规则配置实施指南**
**文档目的:** 定义如何通过 iit\_skills 表配置化实现 CRA 的 5 大核心工作变量质控、入排、PD、AE、伦理
**适用版本:** IIT Manager Agent V2.9.1
**创建日期:** 2026-02-07
## **🏗️ 核心概念Skill \= 规则容器**
在我们的架构中,**Skill (技能)** 是一个可执行的最小业务单元。它定义了:
1. **触发条件**:什么时候跑?(Webhook/Cron)
2. **执行引擎**:用哪个引擎跑?(Hard/Soft)
3. **配置数据**:具体规则是什么?(JSON/Prompt)
4. **数据依赖**:需要哪些数据?(Tags)
### **数据库模型映射 (iit\_skills)**
我们复用并扩展 V2.6 计划中的 iit\_skills 表设计:
model IitSkill {
id String @id @default(uuid())
projectId String @map("project\_id")
name String // e.g., "入排标准核查"
type String // 'HARD\_RULE' | 'LLM\_CHECK' | 'HYBRID'
// 触发配置
triggerType String @map("trigger\_type") // 'WEBHOOK' | 'CRON' | 'MANUAL'
cronExpr String? @map("cron\_expr") // 如果是定时任务
// 核心配置 (JSONB)
// 包含Prompts, JSONLogic, Thresholds
config Json
// 数据依赖 (智能路由用)
// 告诉 ContextBuilder 需要加载哪些 Tag
requiredTags String\[\] @map("required\_tags") // e.g., \['\#lab', '\#demographics'\]
isEnabled Boolean @default(true)
createdAt DateTime @default(now())
@@map("iit\_skills")
@@schema("iit\_schema")
}
## **🛠️ 五大规则的 Skill 实现方案**
我们通过配置不同类型的 Skill 来覆盖 CRA 的所有工作。
### **1\. 变量质控 Skill (Type: HARD\_RULE)**
**对应需求:** 针对每个变量,构建数据质控规则。
* **场景**:空值检查、数值范围、逻辑跳转。
* **配置来源**Rule Studio (Layer 1\) 自动生成。
* **Config 结构**
{
"engine": "HardRuleEngine",
"rules": \[
{ "field": "age", "op": "between", "args": \[18, 80\] },
{ "field": "bmi", "op": "lt", "args": \[50\] }
\]
}
* **执行**Node.js 直接计算,毫秒级响应。
### **2\. 入排标准 Skill (Type: LLM\_CHECK)**
**对应需求:** 针对是否符合入排标准,建立医学逻辑规则。
* **场景**:复杂的医学判断(如:病理确诊时间 \< 3个月
* **配置来源**Rule Studio (Layer 3\) AI 提取 \+ 人工确认。
* **数据依赖**\['\#demographics', '\#lab', '\#history'\]
* **Config 结构**
{
"engine": "SoftRuleEngine",
"model": "deepseek-v3",
"system\_prompt": "你是一个医学监查员...",
"checks": \[
{
"id": "I-03",
"desc": "肝肾功能正常 (ALT/AST \< 2.5 ULN)",
"prompt\_template": "基于以下实验室数据 {{\#lab}},判断..."
}
\]
}
### **3\. 方案偏离 Skill (Type: HYBRID)**
**对应需求:** 针对是否出现方案偏离,建立核查规则。
* **场景**:访视超窗、漏做检查。
* **配置来源**Rule Studio (Layer 2\) 逻辑构建器。
* **Config 结构**
{
"engine": "HybridEngine",
"logic": {
"if": \[
{ "date\_diff": \["$V2.date", "$V1.date"\] },
{ "\>": 31 }, // 28 \+ 3
"FAIL",
"PASS"
\]
}
}
### **4\. AE 监测 Skill (Type: LLM\_CHECK)**
**对应需求:** 针对是否出现 AE 事件,建立核查规则。
* **场景**SAE ReconciliationLab 异常 vs AE 记录)。
* **数据依赖**\['\#lab', '\#ae'\]
* **Config 结构**
{
"engine": "SoftRuleEngine",
"task": "AE\_RECONCILIATION",
"prompt": "对比 \<lab\_data\> 中的 Grade 3+ 异常值与 \<ae\_data\> 记录,找出未报告的 AE。"
}
### **5\. 伦理合规 Skill (Type: HARD\_RULE)**
**对应需求:** 针对是否出现伦理问题,建立规则。
* **场景**ICF 日期逻辑。
* **Config 结构**
{
"engine": "HardRuleEngine",
"rule": {
"op": "date\_before",
"a": "$icf\_date",
"b": "$first\_visit\_date"
},
"severity": "CRITICAL"
}
## **⏰ 三大触发时机详解 (Trigger Strategy)**
我们将质控规则的执行时机划分为“实时、定时、人工”黄金三角,以平衡时效性与成本。
### **1\. 🟢 实时触发 (Real-time / Webhook)**
* **触发源**REDCap DET (Data Entry Trigger)。CRC 保存任意表单时触发。
* **执行范围****单点切片 (Micro-Batch)**。
* 仅针对 **当前受试者 (Current Record)**
* 仅加载 **与当前表单相关** 的 Skill (通过 formName 过滤)。
* *例如:录入“血常规”单,系统只检查 \#lab 相关规则,不会检查人口学。*
* **核心价值****阻断错误**。在 CRC 记忆犹新时(秒级)推送企微提醒,纠正成本最低。
* **成本策略**:默认优先跑 **Hard Rules**。涉及核心安全指标(如 AE时才触发 Soft Rules (LLM)。
### **2\. 🔵 定时触发 (Scheduled / Cron)**
* **触发源**pg-boss Cron Job。每日凌晨 (e.g., 02:00) 执行。
* **执行范围****全量扫描 (Full Scan)**。
* 针对 **所有活跃受试者**
* 重点运行 **跨表逻辑** (如一致性检查) 和 **时间敏感型规则** (如访视超窗 PD)。
* **核心价值****发现隐患**。捕捉“因时间流逝而产生的问题”(如昨天未超窗,今天超窗)和“漏录问题”。
* **成本策略**:使用 **增量标记**。若数据 Hash 未变且无时间规则,跳过 LLM 检查。
### **3\. 🟠 人工触发 (Manual / On-Demand)**
* **触发源**:管理端 "一键全量质控" 或 "单受试者重跑" 按钮。
* **执行范围****按需全量**。
* 针对选定的受试者范围。
* 运行 **所有启用** 的 Skill。
* **核心价值****合规审计与验证**。用于项目初始化清洗、规则调整后的验证、或上级核查前的自查。
## **🔄 调度与执行Skill Runner**
我们不需要为每个规则写死代码,而是实现一个通用的 **SkillRunner**
### **执行流程**
1. **触发 (Trigger)**
* Webhook 收到数据 \-\> 触发 SkillRunner.runByTrigger('WEBHOOK', projectId, recordId, formName)
* Cron Job 到点 \-\> 触发 SkillRunner.runByTrigger('CRON', projectId)
* 人工点击 \-\> 触发 SkillRunner.runByTrigger('MANUAL', projectId)
2. **加载 (Load)**
* Runner 从 iit\_skills 表加载所有启用的 Skill。
* **过滤**:如果是 WEBHOOK 触发,仅加载与当前 formName 关联的 Skill。
3. **路由 (Route)**
* 根据 Skill 的 type 分发给对应的 Engine (HardRuleEngine 或 SoftRuleEngine)。
4. **上下文构建 (Context)**
* 如果需要 LLM调用 ContextBuilder传入 Skill 定义的 requiredTags只拉取相关数据。
5. **结果聚合 (Aggregate)**
* 收集所有 Skill 的执行结果,存入 iit\_qc\_logs。
## **✅ 结论:对当前计划的影响**
你的 **V2.6 开发计划** 非常稳健,只需要在细节上明确 Skill 的定义即可。
**建议调整:**
1. **Phase 1**:重点设计 iit\_skills 的 JSON Schema确保它能容纳上述 5 种类型的配置。
2. **Phase 2**:实现 SkillRunner作为连接 SOP 和 Engine 的中间件。
**总结**:通过 SKILLS 配置化,你的系统就像一个\*\*“可插拔的乐高玩具”\*\*。
* 如果明天要加一个“肿瘤评估 (RECIST)”规则,你只需要新增一个 Skill完全不用改后端代码。
* 这正是 SaaS 化产品的核心竞争力。

View File

@@ -338,18 +338,33 @@ WHERE field_name = 'informed_consent';
## 六、验收标准
### 6.1 阶段 1 验收
### 6.1 阶段 1 验收 ✅ **已完成 (2026-02-08)**
- [ ] 企业微信问"质控情况"AI 回答格式清晰、无幻觉
- [ ] AI 回答包含具体问题和记录数
- [ ] 日志中可看到 XML 格式的 Prompt
- [x] 企业微信问"质控情况"AI 回答格式清晰、无幻觉
- [x] AI 回答包含具体问题和记录数
- [x] 日志中可看到 XML 格式的 Prompt
### 6.2 阶段 2 验收
**完成的文件:**
- `backend/src/modules/iit-manager/services/PromptBuilder.ts` - XML 临床切片格式构建器
- `backend/src/modules/iit-manager/services/ChatService.ts` - 集成 PromptBuilder优化 LLM 格式
- [ ] 点击"质控全览图"按钮可进入驾驶舱页面
- [ ] 统计卡片正确显示质量分、违规数、完成率
- [ ] 热力图正确显示记录×表单的质控状态
- [ ] 点击红色单元格可查看问题详情
### 6.2 阶段 2 验收 ✅ **已完成 (2026-02-08)**
- [x] 点击"质控全览图"按钮可进入驾驶舱页面
- [x] 统计卡片正确显示质量分、违规数、完成率
- [x] 热力图正确显示记录×表单的质控状态
- [x] 点击红色单元格可查看问题详情
**完成的文件:**
- `frontend-v2/src/modules/admin/pages/IitQcCockpitPage.tsx` - 驾驶舱主页面
- `frontend-v2/src/modules/admin/pages/IitQcCockpitPage.css` - 驾驶舱样式
- `frontend-v2/src/modules/admin/components/qc-cockpit/QcStatCards.tsx` - 统计卡片组件
- `frontend-v2/src/modules/admin/components/qc-cockpit/RiskHeatmap.tsx` - 风险热力图组件
- `frontend-v2/src/modules/admin/components/qc-cockpit/QcDetailDrawer.tsx` - 详情抽屉组件
- `frontend-v2/src/modules/admin/types/qcCockpit.ts` - 类型定义
- `backend/src/modules/admin/iit-projects/iitQcCockpitService.ts` - 驾驶舱数据服务
- `backend/src/modules/admin/iit-projects/iitQcCockpitController.ts` - 驾驶舱 API 控制器
- `backend/src/modules/admin/iit-projects/iitQcCockpitRoutes.ts` - 驾驶舱路由
### 6.3 阶段 3 验收

View File

@@ -0,0 +1,583 @@
# CRA 智能质控引擎开发计划
> **版本:** V1.0
> **日期:** 2026-02-08
> **基于:** V2.6 综合开发计划、CRA Agent 业务逻辑与执行架构书、Skills 配置实施指南
> **核心目标:** 实现基于规则的智能质控引擎 + 报告驱动的 LLM 问答
---
## 📋 目录
1. [背景与目标](#1-背景与目标)
2. [架构设计](#2-架构设计)
3. [五大规则体系](#3-五大规则体系)
4. [当前代码现状](#4-当前代码现状)
5. [开发任务清单](#5-开发任务清单)
6. [数据库设计补充](#6-数据库设计补充)
7. [验收标准](#7-验收标准)
---
## 1. 背景与目标
### 1.1 业务需求
针对 CRA临床研究助理的质控工作基于 REDCap 系统的数据结构,建立以下规则体系:
| 编号 | 规则类型 | 说明 | 来源 |
|------|----------|------|------|
| 1 | 变量质控 | 针对每个变量的数据校验(空值、范围、格式) | 自动从 Metadata 同步 |
| 2 | 入排标准 | 判断是否符合入组/排除标准 | AI 解析 Protocol + 人工确认 |
| 3 | 方案偏离 (PD) | 检测访视超窗、漏做检查等 | 人工配置逻辑 |
| 4 | AE 事件 | 检测未报告的不良事件 | 预置医学 Prompt |
| 5 | 伦理合规 | ICF 签署时间、隐私保护等 | 预置硬规则 |
### 1.2 核心目标
```
┌─────────────────────────────────────────────────────────────────┐
│ 🎯 目标:报告驱动的智能质控 │
│ │
│ 后台预计算 → 生成结构化报告 → LLM 阅读报告 → 回答用户问题 │
│ │
│ ✅ 快速(报告已预生成) │
│ ✅ 全面(后台执行所有规则) │
│ ✅ 省钱LLM 只做阅读理解) │
└─────────────────────────────────────────────────────────────────┘
```
### 1.3 与 V2.6 计划的关系
本计划是 V2.6 综合开发计划的**专项实施细化**,聚焦于:
- Phase 1 的 `HardRuleEngine` 深化
- Phase 2 的 `SoftRuleEngine` 实现
- Phase 4 的 `SchedulerService``ReportService` 实现
---
## 2. 架构设计
### 2.1 整体架构
```
┌─────────────────────────────────────────────────────────────────┐
│ 触发层 (Trigger) │
├──────────────────┬──────────────────┬───────────────────────────┤
│ 🟢 Webhook │ 🔵 Cron │ 🟠 Manual │
│ REDCap DET │ 每日凌晨 │ 一键全量质控 │
│ 单记录实时 │ 全量扫描 │ 按需执行 │
└──────────────────┴──────────────────┴───────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 🎯 SkillRunner (调度器) │
│ │
│ 1. 加载启用的 Skills │
│ 2. 根据 triggerType 过滤 │
│ 3. 路由到对应 Engine │
│ 4. 聚合结果 │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────┼─────────────────┐
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ HardRuleEngine │ │ SoftRuleEngine │ │ HybridEngine │
│ (已实现 ✅) │ │ (待实现 ❌) │ │ (待实现 ❌) │
│ │ │ │ │ │
│ - 变量质控 │ │ - 入排标准 │ │ - 方案偏离 │
│ - 伦理合规 │ │ - AE 检测 │ │ - Hard + Soft │
│ │ │ │ │ │
│ JSON Logic │ │ LLM 推理 │ │ 条件分支 │
└──────────────────┘ └──────────────────┘ └──────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 📊 结果存储 & 报告生成 │
├─────────────────────────────────────────────────────────────────┤
│ iit_qc_logs → 详细质控日志 │
│ iit_qc_project_stats → 项目统计摘要 │
│ iit_qc_report (新) → LLM 友好的结构化报告 │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 💬 用户问答 (实时) │
│ │
│ 用户提问 → ContextBuilder 加载报告 → LLM 阅读 → 回答 │
└─────────────────────────────────────────────────────────────────┘
```
### 2.2 漏斗式执行策略
为每个受试者执行以下管道,**分级检查,逐层深入**
```
┌─────────────────────────────────────────────────────────────────┐
│ Level 1: 阻断性检查 (Blocking) │
│ - 伦理合规 (ICF 日期) │
│ - 关键字段缺失 │
│ 如果 FAIL → 直接标记 Critical跳过后续 AI 检查(省钱) │
├─────────────────────────────────────────────────────────────────┤
│ Level 2: 基础清洗 (Hard Rules) │
│ - 所有变量质控规则 │
│ - 生成基础错误日志 │
├─────────────────────────────────────────────────────────────────┤
│ Level 3: AI 深度监查 (Soft Rules) │
│ - 入排标准判断 │
│ - 方案偏离检测 │
│ - AE 事件核查 │
│ - 输出证据链 │
└─────────────────────────────────────────────────────────────────┘
```
---
## 3. 五大规则体系
### 3.1 规则类型与执行引擎
| 编号 | 规则类型 | 执行引擎 | 触发时机 | 典型示例 |
|------|----------|----------|----------|----------|
| 1 | 变量质控 | HardRuleEngine | 实时 + 定时 | 空值检查、0 < BMI < 50 |
| 2 | 入排标准 | SoftRuleEngine | 定时 + 人工 | 确诊时间 < 3个月 |
| 3 | 方案偏离 | HybridEngine | 定时 | 访视超窗 (Date2 - Date1 > 28+3) |
| 4 | AE 事件 | SoftRuleEngine | 定时 | Lab 异常 vs AE 记录一致性 |
| 5 | 伦理合规 | HardRuleEngine | 实时 | ICF 签署日期 > 首次访视 |
### 3.2 Skill 配置示例
#### 3.2.1 变量质控 Skill (HARD_RULE)
```json
{
"skillType": "VARIABLE_QC",
"name": "变量质控",
"type": "HARD_RULE",
"triggerType": "webhook",
"config": {
"engine": "HardRuleEngine",
"rules": [
{
"id": "VQ-001",
"name": "年龄范围检查",
"field": "age",
"formName": "demographics",
"logic": { "and": [{ ">=": [{ "var": "age" }, 18] }, { "<=": [{ "var": "age" }, 80] }] },
"message": "年龄必须在 18-80 岁之间",
"severity": "error",
"category": "lab_values"
},
{
"id": "VQ-002",
"name": "BMI 范围检查",
"field": "bmi",
"formName": "demographics",
"logic": { "and": [{ ">": [{ "var": "bmi" }, 0] }, { "<": [{ "var": "bmi" }, 50] }] },
"message": "BMI 值异常",
"severity": "warning",
"category": "lab_values"
}
]
}
}
```
#### 3.2.2 入排标准 Skill (LLM_CHECK)
```json
{
"skillType": "INCLUSION_EXCLUSION",
"name": "入排标准核查",
"type": "LLM_CHECK",
"triggerType": "cron",
"config": {
"engine": "SoftRuleEngine",
"model": "deepseek-v3",
"systemPrompt": "你是一个专业的临床研究监查员,负责核查受试者是否符合入组标准。",
"checks": [
{
"id": "IE-001",
"name": "确诊时间核查",
"promptTemplate": "基于以下数据,判断受试者的确诊时间是否在 3 个月内:\n{{#demographics}}\n{{#medical_history}}\n\n请给出判断结果 (PASS/FAIL) 和理由。",
"requiredTags": ["#demographics", "#medical_history"]
}
]
},
"requiredTags": ["#demographics", "#medical_history", "#lab"]
}
```
#### 3.2.3 伦理合规 Skill (HARD_RULE)
```json
{
"skillType": "ETHICS_COMPLIANCE",
"name": "伦理合规检查",
"type": "HARD_RULE",
"triggerType": "webhook",
"config": {
"engine": "HardRuleEngine",
"level": "blocking",
"rules": [
{
"id": "EC-001",
"name": "知情同意书签署时间",
"field": ["icf_date", "first_visit_date"],
"logic": { "<=": [{ "var": "icf_date" }, { "var": "first_visit_date" }] },
"message": "知情同意书签署日期必须早于或等于首次访视日期",
"severity": "error",
"category": "ethics"
}
]
}
}
```
---
## 4. 当前代码现状
### 4.1 已实现组件 ✅
| 组件 | 路径 | 说明 |
|------|------|------|
| `IitSkill` 表 | `prisma/schema.prisma` | 已定义,支持 triggerType |
| `IitFieldMapping` 表 | `prisma/schema.prisma` | 字段映射表 |
| `HardRuleEngine` | `engines/HardRuleEngine.ts` | 基于 JSON Logic支持 formName 过滤 |
| `QcService` | `services/QcService.ts` | 基础查询服务 |
| `PromptBuilder` | `services/PromptBuilder.ts` | XML Clinical Slice 格式 |
| `RedcapAdapter` | `adapters/RedcapAdapter.ts` | REDCap API 适配 |
| QC Cockpit UI | `frontend-v2/.../IitQcCockpitPage.tsx` | 热力图 + 详情抽屉 |
| 字段元数据同步 | `iitProjectService.syncMetadata` | 含 Form Labels |
### 4.2 缺失组件 ❌
| 组件 | 优先级 | 依赖 | 说明 |
|------|--------|------|------|
| `SoftRuleEngine` | P0 | LLMFactory | LLM 推理引擎 |
| `HybridEngine` | P1 | HardRule + SoftRule | 混合执行引擎 |
| `SkillRunner` | P0 | 所有 Engine | 规则调度器 |
| `QcReportService` | P0 | SkillRunner | LLM 友好报告生成 |
| `iit_qc_report` 表 | P0 | - | 报告存储 |
| 定时调度 | P1 | pg-boss / node-cron | Cron 触发 |
| 增量检查 | P2 | - | 数据 Hash 策略 |
| Rule Studio UI | P2 | - | 规则配置界面 |
| 默认规则配置 | P0 | - | 5 大规则的初始 JSON |
---
## 5. 开发任务清单
### Phase 1: 核心引擎 (Week 1)
| 任务 | 优先级 | 预估 | 产出 |
|------|--------|------|------|
| 1.1 实现 `SoftRuleEngine` | P0 | 2天 | LLM 推理引擎 |
| 1.2 实现 `SkillRunner` | P0 | 1天 | 规则调度器 |
| 1.3 扩展 `IitSkill` Schema | P0 | 0.5天 | 添加 `requiredTags` 字段 |
| 1.4 创建默认规则配置 | P0 | 1天 | 5 大规则的 Seed 数据 |
#### 1.1 SoftRuleEngine 设计
```typescript
// backend/src/modules/iit-manager/engines/SoftRuleEngine.ts
export interface SoftRuleCheck {
id: string;
name: string;
promptTemplate: string;
requiredTags: string[];
}
export interface SoftRuleResult {
checkId: string;
status: 'PASS' | 'FAIL' | 'UNCERTAIN';
reason: string;
evidence: Record<string, any>;
confidence: number;
}
export class SoftRuleEngine {
private projectId: string;
private llmClient: LLMClient;
async execute(
recordId: string,
data: Record<string, any>,
checks: SoftRuleCheck[]
): Promise<SoftRuleResult[]>;
}
```
#### 1.2 SkillRunner 设计
```typescript
// backend/src/modules/iit-manager/engines/SkillRunner.ts
export class SkillRunner {
/**
* 按触发类型执行 Skills
*/
async runByTrigger(
triggerType: 'webhook' | 'cron' | 'manual',
projectId: string,
options?: {
recordId?: string; // webhook 时必传
formName?: string; // webhook 时过滤相关规则
}
): Promise<SkillRunResult>;
/**
* 执行单个 Skill
*/
private async executeSkill(
skill: IitSkill,
recordId: string,
data: Record<string, any>
): Promise<SkillResult>;
}
```
### Phase 2: 报告系统 (Week 2)
| 任务 | 优先级 | 预估 | 产出 |
|------|--------|------|------|
| 2.1 设计 `iit_qc_report` 表 | P0 | 0.5天 | 报告存储 Schema |
| 2.2 实现 `QcReportService` | P0 | 2天 | 报告生成服务 |
| 2.3 集成到 Cockpit UI | P1 | 1天 | 报告查看入口 |
| 2.4 实现 LLM 问答集成 | P1 | 1天 | 基于报告回答 |
#### 2.2 QcReportService 设计
```typescript
// backend/src/modules/iit-manager/services/QcReportService.ts
export interface QcReport {
projectId: string;
generatedAt: Date;
summary: {
totalRecords: number;
criticalIssues: number;
pendingQueries: number;
passRate: number;
};
criticalList: QcIssue[];
warningList: QcIssue[];
llmFriendlyXml: string; // LLM 阅读版
}
export class QcReportService {
/**
* 生成项目质控报告
*/
async generateReport(projectId: string): Promise<QcReport>;
/**
* 获取缓存的报告(用于 LLM 问答)
*/
async getCachedReport(projectId: string): Promise<string>;
/**
* 构建 LLM 友好的 XML 报告
*/
private buildLlmXmlReport(stats: ProjectStats, issues: QcIssue[]): string;
}
```
#### LLM 阅读版报告格式
```xml
<qc_report project="PD-001" generated="2026-02-08T10:00:00Z">
<summary>
<total_records>50</total_records>
<critical_issues>3</critical_issues>
<pending_queries>12</pending_queries>
<pass_rate>85.2%</pass_rate>
</summary>
<critical_list>
<issue record="P001" rule="EC-001" severity="RED">
<description>知情同意书签署日期(2026-01-15)晚于首次访视(2026-01-10)</description>
<evidence>
<field name="icf_date">2026-01-15</field>
<field name="first_visit_date">2026-01-10</field>
</evidence>
</issue>
<issue record="P003" rule="IE-001" severity="RED">
<description>受试者确诊时间超过入组标准确诊日期距入组已超过3个月</description>
<evidence>
<field name="diagnosis_date">2025-10-01</field>
<field name="enrollment_date">2026-02-01</field>
</evidence>
</issue>
</critical_list>
<warning_list>
<issue record="P007" rule="VQ-002" severity="YELLOW">
<description>BMI 值偏高(42.5),请核实</description>
</issue>
</warning_list>
<by_form>
<form name="人口学" total="50" pass="48" fail="2" />
<form name="实验室检查" total="150" pass="142" fail="8" />
</by_form>
</qc_report>
```
### Phase 3: 定时调度 (Week 3)
| 任务 | 优先级 | 预估 | 产出 |
|------|--------|------|------|
| 3.1 集成 node-cron | P1 | 0.5天 | 定时任务框架 |
| 3.2 实现 Cron 触发逻辑 | P1 | 1天 | 定时全量质控 |
| 3.3 实现增量检查 | P2 | 1天 | 数据 Hash 跳过 |
| 3.4 实现 Webhook 触发 | P2 | 1天 | REDCap DET 集成 |
### Phase 4: 规则配置 UI (Week 4)
| 任务 | 优先级 | 预估 | 产出 |
|------|--------|------|------|
| 4.1 Rule Studio 页面框架 | P2 | 1天 | 规则列表 + 详情 |
| 4.2 规则编辑器 | P2 | 2天 | JSON 可视化编辑 |
| 4.3 规则测试功能 | P2 | 1天 | 单条规则验证 |
---
## 6. 数据库设计补充
### 6.1 扩展 IitSkill 表
```prisma
model IitSkill {
id String @id @default(uuid())
projectId String @map("project_id")
skillType String @map("skill_type")
name String
description String?
// 规则类型
ruleType String @default("HARD_RULE") @map("rule_type") // 'HARD_RULE' | 'LLM_CHECK' | 'HYBRID'
// 执行级别
level String @default("normal") // 'blocking' | 'normal'
// 核心配置
config Json @db.JsonB
// 数据依赖(智能路由用)
requiredTags String[] @map("required_tags") // 新增e.g., ['#lab', '#demographics']
// 触发配置
triggerType String @default("webhook") @map("trigger_type")
cronSchedule String? @map("cron_schedule")
isActive Boolean @default(true) @map("is_active")
priority Int @default(100) // 新增:执行优先级
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@unique([projectId, skillType], map: "unique_iit_skill_project_type")
@@index([projectId], map: "idx_iit_skill_project")
@@index([isActive, triggerType], map: "idx_iit_skill_active_trigger")
@@map("skills")
@@schema("iit_schema")
}
```
### 6.2 新增 IitQcReport 表
```prisma
/// 质控报告存储表 - 存储预生成的 LLM 友好报告
model IitQcReport {
id String @id @default(uuid())
projectId String @map("project_id")
// 报告类型
reportType String @default("daily") @map("report_type") // 'daily' | 'weekly' | 'on_demand'
// 统计摘要
summary Json @db.JsonB // { totalRecords, criticalIssues, pendingQueries, passRate }
// 详细问题列表
issues Json @db.JsonB // [{ record, rule, severity, description, evidence }]
// LLM 友好的 XML 报告
llmReport String @map("llm_report") @db.Text
// 报告生成时间
generatedAt DateTime @default(now()) @map("generated_at")
// 报告有效期
expiresAt DateTime? @map("expires_at")
@@index([projectId, reportType], map: "idx_qc_report_project_type")
@@index([generatedAt], map: "idx_qc_report_generated")
@@map("qc_reports")
@@schema("iit_schema")
}
```
---
## 7. 验收标准
### 7.1 功能验收
| 功能 | 验收标准 |
|------|----------|
| 变量质控 | 单记录质控 < 100ms批量 50 记录 < 2s |
| 入排标准 | LLM 判断准确率 > 90%;单记录 < 3s |
| 报告生成 | 50 记录的项目报告生成 < 5s |
| LLM 问答 | 基于报告回答延迟 < 2s |
| 定时任务 | 每日凌晨自动执行,连续 7 天无故障 |
### 7.2 性能指标
| 指标 | 目标值 |
|------|--------|
| HardRuleEngine 执行时间 | < 100ms / 记录 |
| SoftRuleEngine 执行时间 | < 3s / 记录 |
| 报告加载时间 | < 200ms |
| 端到端问答延迟 | < 3s |
### 7.3 业务验收
- [ ] CRA 可以通过 Cockpit 查看所有严重问题
- [ ] 点击问题可以查看详细数据和证据
- [ ] 用户可以通过对话询问质控问题LLM 基于报告回答
- [ ] 定时任务每日自动执行,生成最新报告
- [ ] 规则可以通过配置界面增删改
---
## 8. 风险与应对
| 风险 | 影响 | 应对措施 |
|------|------|----------|
| LLM 判断不准确 | 入排/AE 检测误报 | 三态管理Pending → 人工确认) |
| 规则配置复杂 | 用户上手困难 | 提供预置规则模板 |
| 数据量大时性能下降 | 质控变慢 | 增量检查 + 分批执行 |
| REDCap Webhook 不稳定 | 实时质控失效 | 定时任务兜底 |
---
## 9. 参考文档
- [CRA Agent 业务逻辑与执行架构书](../02-技术设计/CRA%20Agent%20业务逻辑与执行架构书%20(1).md)
- [基于 Skills 的 CRA 规则配置实施指南](../02-技术设计/docs_03-业务模块_IIT%20Manager%20Agent_02-技术设计_11-基于Skills的CRA规则配置实施指南.md)
- [V2.6 综合开发计划](./IIT%20Manager%20Agent%20V2.6%20综合开发计划.md)
- [02-核心引擎实现指南](./02-核心引擎实现指南.md)
---
**文档维护人**AI Agent
**最后更新**2026-02-08
### 更新日志
| 版本 | 日期 | 更新内容 |
|------|------|----------|
| V1.0 | 2026-02-08 | 初始版本,基于方案审查和代码分析 |

View File

@@ -0,0 +1,122 @@
# **LLM 交互协议与 Prompt 最佳实践指南**
**文档目的:** 定义“质控报告”的标准上下文格式,并提供配套的 System Prompt以消除幻觉、确保回复准确性。
**适用版本:** IIT Manager Agent V2.9.1
**创建日期:** 2026-02-08
## **🧠 一、 核心原理:为什么“混合格式”能防幻觉?**
我们将数据格式定义为 **Context Protocol v2.0**,采用 **XML 骨架 \+ Markdown 血肉** 的形式。
### **1.1 格式对比**
| 格式 | 示例 | LLM 视角的优缺点 |
| :---- | :---- | :---- |
| **纯 JSON** | {"age": 45, "error": true} | ❌ **缺点**Token 消耗大,括号层级深时 LLM 容易“晕”,注意力机制容易分散。 |
| **纯 Markdown** | \*\*Age\*\*: 45 (Error) | ❌ **缺点**:边界不清晰,多条记录容易混在一起,导致“张冠李戴”。 |
| **混合模式 (推荐)** | \<record id="1"\>- Age: \*\*45\*\*\</record\> | ✅ **优点**XML \<tag\> 明确告诉 LLM “这是独立的一条记录”Markdown \*\* 告诉 LLM “这是重点”。 |
### **1.2 防幻觉的三道防线**
1. **防线一:证据注入 (Evidence Injection)**
* **以前**:只给 Age Error。LLM 被问“多少岁”时,只能瞎编。
* **现在**:给 Age Error (Current: 45, Range: 25-35)。LLM 看到了 45直接引用无需生成。
2. **防线二:预计算 (Pre-computation)**
* **以前**:给 DOB: 1980让 LLM 算年龄。LLM 数学不好,可能算错。
* **现在**Node.js 算好 Age: 46直接喂给 LLM。LLM 只负责读,不负责算。
3. **防线三:结构化边界 (XML Boundaries)**
* **以前**平铺文本。LLM 可能把 A 病人的合并用药安到 B 病人头上。
* **现在**\<record id="A"\>...\</record\>。强制物理隔离,杜绝串行。
## **📝 二、 Context Protocol v2.0 标准格式**
这是后端 ReportGenerator 需要生成的最终字符串格式。
\<qc\_context project="test0102" generated\_at="2026-02-08"\>
\<\!-- 1\. 规则定义 (让 LLM 知道判罚标准) \--\>
\<rule\_definitions\>
\<rule id="R\_AGE"\>入排标准 I-01: 年龄应在 25-35 岁之间\</rule\>
\<rule id="R\_ICF"\>伦理合规 E-01: 必须签署知情同意书\</rule\>
\</rule\_definitions\>
\<\!-- 2\. 严重问题清单 (按受试者分组) \--\>
\<critical\_issues\>
\<subject id="1"\>
\<summary\>存在 2 个严重违规\</summary\>
\<issues\>
1\. \[R\_AGE\] \*\*年龄超标\*\*
\- 现状: 当前年龄 \*\*45岁\*\*
\- 标准: 25-35岁
\- 证据: \`birth\_date\` \= 1981-05-12
2\. \[R\_ICF\] \*\*知情同意缺失\*\*
\- 现状: 字段为空
\- 证据: \`icf\_date\` \= null
\</issues\>
\</subject\>
\<subject id="10"\>
\<summary\>存在 1 个严重违规\</summary\>
\<issues\>
1\. \[R\_AGE\] \*\*年龄超标\*\*
\- 现状: 当前年龄 \*\*52岁\*\*
\- 证据: \`birth\_date\` \= 1974-02-01
\</issues\>
\</subject\>
\</critical\_issues\>
\</qc\_context\>
## **🗣️ 三、 配套 Prompt 设计 (System Prompt)**
仅有好的数据格式是不够的,必须用 Prompt 教会 LLM 如何阅读这个格式。
### **3.1 CRA 监查员 System Prompt**
\# Role
你是一名资深的临床监查员 (CRA)。你的任务是根据提供的【质控报告上下文】回答用户关于项目质量、违规情况的问题。
\# Input Format
你收到的上下文将包含在 \`\<qc\_context\>\` XML 标签中。
\- \`\<rule\_definitions\>\`: 定义了项目的质控规则。
\- \`\<critical\_issues\>\`: 列出了具体的违规记录,按受试者 (\`\<subject\>\`) 分组。
\# Constraints (绝对准则)
1\. \*\*基于证据\*\*:回答必须严格基于 \`\<issues\>\` 中的数据。如果上下文中没有提到某条记录或某个数值,\*\*必须直接说“报告中未包含相关信息”\*\*,严禁编造数值。
2\. \*\*引用原文\*\*:在解释违规原因时,必须引用上下文中的 "证据" (Evidence) 字段(例如:“因为患者当前年龄为 45 岁...”)。
3\. \*\*结构化输出\*\*:回答多个受试者问题时,请使用 Markdown 列表。
4\. \*\*语气专业\*\*:保持客观、冷静的医疗专业语气。
\# Example
User: "1号病人有什么问题"
Assistant: "1号受试者存在 \*\*2个严重违规\*\*
1\. \*\*年龄超标\*\*:患者当前 \*\*45岁\*\*不符合“25-35岁”的入排标准 (R\_AGE)。
2\. \*\*伦理缺失\*\*:未检测到知情同意书签署日期 (\`icf\_date\` 为空)。
建议立即核查原始病历或剔除该病例。"
## **🔬 四、 验证与测试 (Evaluation)**
### **4.1 幻觉压力测试**
**测试用例 A询问不存在的数值**
* **Prompt**: "1号病人的血压是多少"
* **Context**: (上下文中只有年龄和ICF问题没有血压数据)
* **预期回答**: "报告中未包含1号受试者的血压数据。当前仅记录了年龄和知情同意书相关的违规信息。"
* **失败回答 (幻觉)**: "1号病人的血压是 120/80 mmHg。" (如果 LLM 只有 Message 没有 Evidence容易顺口胡编一个正常值)
**测试用例 B询问违规原因**
* **Prompt**: "为什么10号病人年龄违规"
* **Context**: \<subject id="10"\>...当前年龄 \*\*52岁\*\*...\</subject\>
* **预期回答**: "因为10号受试者当前年龄为 **52岁**,超出了研究方案规定的 25-35 岁范围。"
### **4.2 结论**
通过 **"XML 结构化 \+ 证据注入 \+ 严格约束 Prompt"** 三位一体的方案,我们可以将幻觉率控制在 **极低水平 (\< 1%)**
AI 在这里不再是“创造者”,而是精准的“阅读理解者”。

View File

@@ -0,0 +1,107 @@
# **LLM 友好型质控报告评估与优化方案**
**文档版本:** v2.1 (基于团队反馈优化)
**评估对象:** qc-report-test0102-pd-study-2026-02-08.xml
**评估日期:** 2026-02-08
## **🚀 一、 核心共识Context Protocol v2.1**
经过团队讨论,确定采用 **“XML 容器 \+ Markdown 内容”** 的混合模式,并支持 **双格式输出** 以满足不同场景。
### **1.1 双格式策略 (Dual Format Strategy)**
后端 QcService 应支持 format 参数:
* **format=json (Default)**: 面向 **前端 UI**。结构化强,用于渲染热力图、表格。
* **format=llm-friendly**: 面向 **LLM Context**。Token 密度高,语义清晰,包含“证据”。
### **1.2 理想的 LLM Context 结构 (V2.1)**
**设计原则**
1. **去重**:不再单独列出 rule\_definitions要求每条 issue **自包含**(同时包含现状与标准)。
2. **聚合**:按 Record ID 分组。
3. **压缩**:使用 Markdown 列表替代冗余的 XML 标签。
\<qc\_context project\_id="test0102" generated="2026-02-08"\>
\<\!-- 1\. 宏观统计 (Aggregate) \--\>
\<summary\>
\- 状态: 13/13 记录存在严重违规 (100% Fail)
\- Top 3 问题:
1\. 年龄超标 (13人)
2\. 未签知情同意书 (13人)
3\. 疼痛评分不达标 (13人)
\</summary\>
\<\!-- 2\. 问题详情 (Markdown List in XML) \--\>
\<details\>
\<record id="1"\>
\*\*严重违规 (5项)\*\*:
1\. \[R\_AGE\] \*\*年龄超标\*\*: 当前 \*\*45岁\*\* (标准: 25-35岁)。
2\. \[R\_ICF\] \*\*伦理缺失\*\*: \`informed\_consent\` 为空 (必须签署)。
3\. \[R\_VAS\] \*\*入排不符\*\*: VAS评分 \*\*2分\*\* (要求 \>= 4分)。
\</record\>
\<record id="10"\>
\*\*严重违规 (5项)\*\*:
1\. \[R\_AGE\] \*\*年龄超标\*\*: 当前 \*\*52岁\*\* (标准: 25-35岁)。
\<\!-- 其他同上 \--\>
\</record\>
\</details\>
\</qc\_context\>
## **🛠️ 二、 实施建议 (Action Items)**
### **2.1 后端改造:让报错信息“自包含”**
这是消除 rule\_definitions 依赖的关键。我们需要在生成 Issue 时,就把标准塞进去。
* **Before (HardRuleEngine)**:
return { message: "年龄不在范围内" }
* **After (HardRuleEngine)**:
// 动态拼接模板
return {
message: \`当前 \*\*${actualValue}\*\* (标准: ${rule.min}-${rule.max})\`,
structured\_evidence: { value: actualValue, range: \[25, 35\] }
}
### **2.2 System Prompt 增强 (V2.1)**
参考团队建议,强化“证据引用”指令。
\# Role
你是一名资深的临床监查员 (CRA)。
\# Input Format
你收到的 \`\<qc\_context\>\` 包含了项目的质控报告。
\- \`\<details\>\` 区域按受试者列出了具体违规项。
\- 每一行违规项都包含了 \*\*\[规则ID\]\*\*、\*\*错误类型\*\*、\*\*现状值\*\* 和 \*\*标准值\*\*。
\# Critical Instructions (思维链约束)
在回答用户问题时,你必须遵循以下步骤:
1\. \*\*定位 (Locate)\*\*: 找到用户询问的 \`\<record\>\`。
2\. \*\*提取 (Extract)\*\*: 读取具体的报错行。
3\. \*\*引用 (Cite)\*\*: 在回答中明确引用现状值和标准值。
\- ✅ 正确: "1号患者年龄违规因为他当前 \*\*45岁\*\*,而方案要求 \*\*25-35岁\*\*。"
\- ❌ 错误: "1号患者年龄不对。" (太模糊)
\- ❌ 错误: "1号患者年龄违规请检查。" (无证据)
4\. \*\*诚实 (Grounding)\*\*: 如果报告里没写的数值(比如血压),绝对不要编造,直接回答“报告未提及”。
\# Example
User: "10号病人有什么大问题"
Assistant: "10号病人有 \*\*5项严重违规\*\*。最主要的是 \*\*年龄超标\*\*,他当前 \*\*52岁\*\*,远超出了 \*\*25-35岁\*\* 的入排标准。此外还存在伦理知情同意书缺失的问题。"
## **📊 三、 收益预估**
1. **Token 节省**:相比 V1.0 XMLV2.1 混合格式预计节省 **40%-60%** 的 Token去掉了大量的 \<issue\>, \<field\>, \<message\> 标签)。
2. **幻觉消除**通过“自包含”设计LLM 不需要跨段落去查找规则定义,所有信息都在一行内,上下文注意力极其集中。
3. **开发解耦**:前端用 JSON 渲染界面LLM 用 Text 文本推理,互不干扰。
**结论:** 团队的建议非常棒。V2.1 方案已采纳 **混合格式** \+ **自包含报错** \+ **引用增强 Prompt**,这是目前最优的工程解法。

View File

@@ -0,0 +1,120 @@
# **LLM 友好型质控报告评估与优化方案**
**评估对象:** qc-report-test0102-pd-study-2026-02-08.xml
**评估目的:** 确定该格式是否适合作为 Context 喂给 LLM 以回答用户提问。
**评估日期:** 2026-02-08
## **📊 一、 现状评估 (Current State Analysis)**
### **1\. 评分卡**
| 维度 | 评分 (1-10) | 评价 |
| :---- | :---- | :---- |
| **结构化程度** | 9/10 | XML 结构清晰,解析容易。 |
| **Token 效率** | 3/10 | **极差**。重复信息太多(如 rule\_name 重复 64 次)。 |
| **信息完整性** | 4/10 | **缺失核心证据**。只说了“错”,没说“值为多少”。 |
| **语义清晰度** | 5/10 | rule="unknown" 对 AI 毫无帮助,缺乏语义标签。 |
### **2\. 核心缺陷分析**
#### **❌ 缺陷 1缺乏“原始值” (The "Evidence Gap")**
* **现状**
\<message\>年龄不在 25-35 岁范围内\</message\>
\<field\>age\</field\>
* **LLM 的困惑**"我知道年龄不对,但这个病人到底几岁?是 24 岁(轻微偏差)还是 80 岁(严重偏差)?用户如果问我‘为什么报错’,我只能复读 Message无法解释原因。"
#### **❌ 缺陷 2Token 冗余 (The "Echo Chamber")**
* **现状**
13 条记录都有“年龄”问题rule\_name 和 message 重复了 13 次。
* **LLM 的困惑**"为什么要让我读 13 遍同样的一句话?这浪费了宝贵的上下文窗口。"
#### **❌ 缺陷 3逻辑扁平 (Lack of Hierarchy)**
* **现状**
所有问题平铺在 \<critical\_issues\> 下,没有按 Record ID 聚合。
* **LLM 的困惑**"如果用户问001号病人有哪些问题我得遍历整个列表去挑出 record=1 的项,容易看漏。"
## **🚀 二、 优化方案Context Protocol v2.0**
根据我们的 **CRA Agent 深度设计**,我们需要将报告重构为 **“以实体为中心,以证据为支撑”** 的结构。
### **2.1 理想的 Context 结构 (Gold Standard)**
建议采用 **混合模式**XML 定义边界Markdown 描述详情。
\<qc\_context project\_id="test0102" generated="2026-02-08"\>
\<\!-- 1\. 宏观统计 (Aggregate) \--\>
\<overview\>
\- 总记录数: 13 | ❌ 严重违规: 13人 (100%)
\- 主要问题: 年龄超标 (13), 未签知情同意书 (13), 疼痛评分不达标 (13)
\</overview\>
\<\!-- 2\. 规则定义 (Reference) \- 只定义一次,省 Token \--\>
\<rule\_definitions\>
\<rule id="R\_AGE" tag="\#demographics"\>年龄应在 25-35 岁之间\</rule\>
\<rule id="R\_ICF" tag="\#ethics"\>必须签署知情同意书\</rule\>
\</rule\_definitions\>
\<\!-- 3\. 问题详情 (Grouped by Record) \--\>
\<issues\_list\>
\<record id="1" status="CRITICAL"\>
\<summary\>存在 5 个严重违规\</summary\>
\<details\>
\- \[R\_AGE\] ❌ \*\*年龄违规\*\*: 当前值 \`45\` (不在 25-35 范围内)。
\- \[R\_ICF\] ❌ \*\*伦理违规\*\*: \`informed\_consent\` 为空。
\- \[R\_VAS\] ❌ \*\*入排不符\*\*: VAS评分 \`2\` (要求 \>= 4)。
\</details\>
\</record\>
\<record id="10" status="CRITICAL"\>
\<summary\>存在 5 个严重违规\</summary\>
\<details\>
\- \[R\_AGE\] ❌ \*\*年龄违规\*\*: 当前值 \`52\`。
\<\!-- 省略重复描述,仅列出差异或关键点 \--\>
\</details\>
\</record\>
\</issues\_list\>
\</qc\_context\>
## **🛠️ 三、 实施建议 (Action Items)**
### **3.1 后端代码调整 (QcService / ReportGenerator)**
你需要修改生成 XML 的逻辑,不仅要查 iit\_qc\_logs还要关联 **原始数据**
1. **补充 value 字段**
在存入 iit\_qc\_logs 时,或者在生成报告时,必须把触发报错的 **具体数值** 写进去。
* *Bad:* { "message": "Age error" }
* *Good:* { "message": "Age error", "evidence": { "value": 45, "threshold": "25-35" } }
2. **补充 rule\_id 和 tag**
目前的 rule="unknown" 是不可接受的。
* 在 HardRuleEngine 里,每条规则必须有一个唯一的 code (例如 AGE\_CHECK)。
* 在 iit\_skills 配置中,关联 Tags。
3. **按受试者聚合 (Group By Record)**
不要输出扁平的 List要输出 MapMap\<RecordId, Issues\[\]\>。
### **3.2 优化后的 LLM 交互流程**
当用户问:“**为什么 1 号病人不合格?**”
* **旧 XML**LLM 看到 "年龄不在范围内",只能回答“因为年龄不对”。
* **新 XML**LLM 看到 当前值 45 (要求 25-35),可以回答:
“1号病人不合格主要有 3 个原因:
1. **年龄不符**:患者 **45岁**,超出了研究要求的 25-35 岁范围。
2. **疼痛评分不足**VAS 评分为 **2分**,未达到入组要求的 4分。
3. **伦理缺失**:系统中未查询到知情同意书签署记录。”
### **3.3 总结**
**现有报告是给程序员调试用的,不是给 LLM 用的。**
请按照 **Context Protocol** 规范,增加 **Evidence (原始值)**,增加 **Grouping (聚合)**,并消除冗余文本。

View File

@@ -0,0 +1,320 @@
<?xml version="1.0" encoding="UTF-8"?>
<qc_report project_id="test0102-pd-study" project_name="test0207" generated="2026-02-08T08:14:32.847Z">
<summary>
<total_records>13</total_records>
<completed_records>0</completed_records>
<critical_issues>64</critical_issues>
<warning_issues>0</warning_issues>
<pending_queries>0</pending_queries>
<pass_rate>0%</pass_rate>
<last_qc_time>2026-02-08T05:30:56.082Z</last_qc_time>
</summary>
<critical_issues count="64">
<issue record="1" rule="unknown" severity="critical">
<rule_name>年龄不在 25-35 岁范围内</rule_name>
<message>年龄不在 25-35 岁范围内</message>
<field>age</field>
<detected_at>2026-02-08T05:30:55.980Z</detected_at>
</issue>
<issue record="1" rule="unknown" severity="critical">
<rule_name>出生日期不在 1989-01-01 至 2008-01-01 范围内</rule_name>
<message>出生日期不在 1989-01-01 至 2008-01-01 范围内</message>
<field>birth_date</field>
<detected_at>2026-02-08T05:30:55.980Z</detected_at>
</issue>
<issue record="1" rule="unknown" severity="critical">
<rule_name>月经周期不在 21-35 天范围内28±7天</rule_name>
<message>月经周期不在 21-35 天范围内28±7天</message>
<field>menstrual_cycle</field>
<detected_at>2026-02-08T05:30:55.980Z</detected_at>
</issue>
<issue record="1" rule="unknown" severity="critical">
<rule_name>VAS 疼痛评分 &lt; 4 分,不符合入组条件</rule_name>
<message>VAS 疼痛评分 &lt; 4 分,不符合入组条件</message>
<field>vas_score</field>
<detected_at>2026-02-08T05:30:55.980Z</detected_at>
</issue>
<issue record="1" rule="unknown" severity="critical">
<rule_name>未签署知情同意书</rule_name>
<message>未签署知情同意书</message>
<field>informed_consent</field>
<detected_at>2026-02-08T05:30:55.980Z</detected_at>
</issue>
<issue record="10" rule="unknown" severity="critical">
<rule_name>年龄不在 25-35 岁范围内</rule_name>
<message>年龄不在 25-35 岁范围内</message>
<field>age</field>
<detected_at>2026-02-08T05:30:56.060Z</detected_at>
</issue>
<issue record="10" rule="unknown" severity="critical">
<rule_name>出生日期不在 1989-01-01 至 2008-01-01 范围内</rule_name>
<message>出生日期不在 1989-01-01 至 2008-01-01 范围内</message>
<field>birth_date</field>
<detected_at>2026-02-08T05:30:56.060Z</detected_at>
</issue>
<issue record="10" rule="unknown" severity="critical">
<rule_name>月经周期不在 21-35 天范围内28±7天</rule_name>
<message>月经周期不在 21-35 天范围内28±7天</message>
<field>menstrual_cycle</field>
<detected_at>2026-02-08T05:30:56.060Z</detected_at>
</issue>
<issue record="10" rule="unknown" severity="critical">
<rule_name>VAS 疼痛评分 &lt; 4 分,不符合入组条件</rule_name>
<message>VAS 疼痛评分 &lt; 4 分,不符合入组条件</message>
<field>vas_score</field>
<detected_at>2026-02-08T05:30:56.060Z</detected_at>
</issue>
<issue record="10" rule="unknown" severity="critical">
<rule_name>未签署知情同意书</rule_name>
<message>未签署知情同意书</message>
<field>informed_consent</field>
<detected_at>2026-02-08T05:30:56.060Z</detected_at>
</issue>
<issue record="11" rule="unknown" severity="critical">
<rule_name>年龄不在 25-35 岁范围内</rule_name>
<message>年龄不在 25-35 岁范围内</message>
<field>age</field>
<detected_at>2026-02-08T05:30:56.068Z</detected_at>
</issue>
<issue record="11" rule="unknown" severity="critical">
<rule_name>出生日期不在 1989-01-01 至 2008-01-01 范围内</rule_name>
<message>出生日期不在 1989-01-01 至 2008-01-01 范围内</message>
<field>birth_date</field>
<detected_at>2026-02-08T05:30:56.068Z</detected_at>
</issue>
<issue record="11" rule="unknown" severity="critical">
<rule_name>月经周期不在 21-35 天范围内28±7天</rule_name>
<message>月经周期不在 21-35 天范围内28±7天</message>
<field>menstrual_cycle</field>
<detected_at>2026-02-08T05:30:56.068Z</detected_at>
</issue>
<issue record="11" rule="unknown" severity="critical">
<rule_name>VAS 疼痛评分 &lt; 4 分,不符合入组条件</rule_name>
<message>VAS 疼痛评分 &lt; 4 分,不符合入组条件</message>
<field>vas_score</field>
<detected_at>2026-02-08T05:30:56.068Z</detected_at>
</issue>
<issue record="11" rule="unknown" severity="critical">
<rule_name>未签署知情同意书</rule_name>
<message>未签署知情同意书</message>
<field>informed_consent</field>
<detected_at>2026-02-08T05:30:56.068Z</detected_at>
</issue>
<issue record="12" rule="unknown" severity="critical">
<rule_name>出生日期不在 1989-01-01 至 2008-01-01 范围内</rule_name>
<message>出生日期不在 1989-01-01 至 2008-01-01 范围内</message>
<field>birth_date</field>
<detected_at>2026-02-08T05:30:56.075Z</detected_at>
</issue>
<issue record="12" rule="unknown" severity="critical">
<rule_name>月经周期不在 21-35 天范围内28±7天</rule_name>
<message>月经周期不在 21-35 天范围内28±7天</message>
<field>menstrual_cycle</field>
<detected_at>2026-02-08T05:30:56.075Z</detected_at>
</issue>
<issue record="12" rule="unknown" severity="critical">
<rule_name>VAS 疼痛评分 &lt; 4 分,不符合入组条件</rule_name>
<message>VAS 疼痛评分 &lt; 4 分,不符合入组条件</message>
<field>vas_score</field>
<detected_at>2026-02-08T05:30:56.075Z</detected_at>
</issue>
<issue record="12" rule="unknown" severity="critical">
<rule_name>未签署知情同意书</rule_name>
<message>未签署知情同意书</message>
<field>informed_consent</field>
<detected_at>2026-02-08T05:30:56.075Z</detected_at>
</issue>
<issue record="13" rule="unknown" severity="critical">
<rule_name>年龄不在 25-35 岁范围内</rule_name>
<message>年龄不在 25-35 岁范围内</message>
<field>age</field>
<detected_at>2026-02-08T05:30:56.082Z</detected_at>
</issue>
<issue record="13" rule="unknown" severity="critical">
<rule_name>出生日期不在 1989-01-01 至 2008-01-01 范围内</rule_name>
<message>出生日期不在 1989-01-01 至 2008-01-01 范围内</message>
<field>birth_date</field>
<detected_at>2026-02-08T05:30:56.082Z</detected_at>
</issue>
<issue record="13" rule="unknown" severity="critical">
<rule_name>月经周期不在 21-35 天范围内28±7天</rule_name>
<message>月经周期不在 21-35 天范围内28±7天</message>
<field>menstrual_cycle</field>
<detected_at>2026-02-08T05:30:56.082Z</detected_at>
</issue>
<issue record="13" rule="unknown" severity="critical">
<rule_name>VAS 疼痛评分 &lt; 4 分,不符合入组条件</rule_name>
<message>VAS 疼痛评分 &lt; 4 分,不符合入组条件</message>
<field>vas_score</field>
<detected_at>2026-02-08T05:30:56.082Z</detected_at>
</issue>
<issue record="13" rule="unknown" severity="critical">
<rule_name>未签署知情同意书</rule_name>
<message>未签署知情同意书</message>
<field>informed_consent</field>
<detected_at>2026-02-08T05:30:56.082Z</detected_at>
</issue>
<issue record="2" rule="unknown" severity="critical">
<rule_name>年龄不在 25-35 岁范围内</rule_name>
<message>年龄不在 25-35 岁范围内</message>
<field>age</field>
<detected_at>2026-02-08T05:30:55.990Z</detected_at>
</issue>
<issue record="2" rule="unknown" severity="critical">
<rule_name>出生日期不在 1989-01-01 至 2008-01-01 范围内</rule_name>
<message>出生日期不在 1989-01-01 至 2008-01-01 范围内</message>
<field>birth_date</field>
<detected_at>2026-02-08T05:30:55.990Z</detected_at>
</issue>
<issue record="2" rule="unknown" severity="critical">
<rule_name>月经周期不在 21-35 天范围内28±7天</rule_name>
<message>月经周期不在 21-35 天范围内28±7天</message>
<field>menstrual_cycle</field>
<detected_at>2026-02-08T05:30:55.990Z</detected_at>
</issue>
<issue record="2" rule="unknown" severity="critical">
<rule_name>VAS 疼痛评分 &lt; 4 分,不符合入组条件</rule_name>
<message>VAS 疼痛评分 &lt; 4 分,不符合入组条件</message>
<field>vas_score</field>
<detected_at>2026-02-08T05:30:55.990Z</detected_at>
</issue>
<issue record="2" rule="unknown" severity="critical">
<rule_name>未签署知情同意书</rule_name>
<message>未签署知情同意书</message>
<field>informed_consent</field>
<detected_at>2026-02-08T05:30:55.990Z</detected_at>
</issue>
<issue record="3" rule="unknown" severity="critical">
<rule_name>年龄不在 25-35 岁范围内</rule_name>
<message>年龄不在 25-35 岁范围内</message>
<field>age</field>
<detected_at>2026-02-08T05:30:55.998Z</detected_at>
</issue>
<issue record="3" rule="unknown" severity="critical">
<rule_name>出生日期不在 1989-01-01 至 2008-01-01 范围内</rule_name>
<message>出生日期不在 1989-01-01 至 2008-01-01 范围内</message>
<field>birth_date</field>
<detected_at>2026-02-08T05:30:55.998Z</detected_at>
</issue>
<issue record="3" rule="unknown" severity="critical">
<rule_name>月经周期不在 21-35 天范围内28±7天</rule_name>
<message>月经周期不在 21-35 天范围内28±7天</message>
<field>menstrual_cycle</field>
<detected_at>2026-02-08T05:30:55.998Z</detected_at>
</issue>
<issue record="3" rule="unknown" severity="critical">
<rule_name>VAS 疼痛评分 &lt; 4 分,不符合入组条件</rule_name>
<message>VAS 疼痛评分 &lt; 4 分,不符合入组条件</message>
<field>vas_score</field>
<detected_at>2026-02-08T05:30:55.998Z</detected_at>
</issue>
<issue record="3" rule="unknown" severity="critical">
<rule_name>未签署知情同意书</rule_name>
<message>未签署知情同意书</message>
<field>informed_consent</field>
<detected_at>2026-02-08T05:30:55.998Z</detected_at>
</issue>
<issue record="4" rule="unknown" severity="critical">
<rule_name>年龄不在 25-35 岁范围内</rule_name>
<message>年龄不在 25-35 岁范围内</message>
<field>age</field>
<detected_at>2026-02-08T05:30:56.008Z</detected_at>
</issue>
<issue record="4" rule="unknown" severity="critical">
<rule_name>出生日期不在 1989-01-01 至 2008-01-01 范围内</rule_name>
<message>出生日期不在 1989-01-01 至 2008-01-01 范围内</message>
<field>birth_date</field>
<detected_at>2026-02-08T05:30:56.008Z</detected_at>
</issue>
<issue record="4" rule="unknown" severity="critical">
<rule_name>月经周期不在 21-35 天范围内28±7天</rule_name>
<message>月经周期不在 21-35 天范围内28±7天</message>
<field>menstrual_cycle</field>
<detected_at>2026-02-08T05:30:56.008Z</detected_at>
</issue>
<issue record="4" rule="unknown" severity="critical">
<rule_name>VAS 疼痛评分 &lt; 4 分,不符合入组条件</rule_name>
<message>VAS 疼痛评分 &lt; 4 分,不符合入组条件</message>
<field>vas_score</field>
<detected_at>2026-02-08T05:30:56.008Z</detected_at>
</issue>
<issue record="4" rule="unknown" severity="critical">
<rule_name>未签署知情同意书</rule_name>
<message>未签署知情同意书</message>
<field>informed_consent</field>
<detected_at>2026-02-08T05:30:56.008Z</detected_at>
</issue>
<issue record="5" rule="unknown" severity="critical">
<rule_name>年龄不在 25-35 岁范围内</rule_name>
<message>年龄不在 25-35 岁范围内</message>
<field>age</field>
<detected_at>2026-02-08T05:30:56.019Z</detected_at>
</issue>
<issue record="5" rule="unknown" severity="critical">
<rule_name>出生日期不在 1989-01-01 至 2008-01-01 范围内</rule_name>
<message>出生日期不在 1989-01-01 至 2008-01-01 范围内</message>
<field>birth_date</field>
<detected_at>2026-02-08T05:30:56.019Z</detected_at>
</issue>
<issue record="5" rule="unknown" severity="critical">
<rule_name>月经周期不在 21-35 天范围内28±7天</rule_name>
<message>月经周期不在 21-35 天范围内28±7天</message>
<field>menstrual_cycle</field>
<detected_at>2026-02-08T05:30:56.019Z</detected_at>
</issue>
<issue record="5" rule="unknown" severity="critical">
<rule_name>VAS 疼痛评分 &lt; 4 分,不符合入组条件</rule_name>
<message>VAS 疼痛评分 &lt; 4 分,不符合入组条件</message>
<field>vas_score</field>
<detected_at>2026-02-08T05:30:56.019Z</detected_at>
</issue>
<issue record="5" rule="unknown" severity="critical">
<rule_name>未签署知情同意书</rule_name>
<message>未签署知情同意书</message>
<field>informed_consent</field>
<detected_at>2026-02-08T05:30:56.019Z</detected_at>
</issue>
<issue record="6" rule="unknown" severity="critical">
<rule_name>年龄不在 25-35 岁范围内</rule_name>
<message>年龄不在 25-35 岁范围内</message>
<field>age</field>
<detected_at>2026-02-08T05:30:56.028Z</detected_at>
</issue>
<issue record="6" rule="unknown" severity="critical">
<rule_name>出生日期不在 1989-01-01 至 2008-01-01 范围内</rule_name>
<message>出生日期不在 1989-01-01 至 2008-01-01 范围内</message>
<field>birth_date</field>
<detected_at>2026-02-08T05:30:56.028Z</detected_at>
</issue>
<issue record="6" rule="unknown" severity="critical">
<rule_name>月经周期不在 21-35 天范围内28±7天</rule_name>
<message>月经周期不在 21-35 天范围内28±7天</message>
<field>menstrual_cycle</field>
<detected_at>2026-02-08T05:30:56.028Z</detected_at>
</issue>
<issue record="6" rule="unknown" severity="critical">
<rule_name>VAS 疼痛评分 &lt; 4 分,不符合入组条件</rule_name>
<message>VAS 疼痛评分 &lt; 4 分,不符合入组条件</message>
<field>vas_score</field>
<detected_at>2026-02-08T05:30:56.028Z</detected_at>
</issue>
<issue record="6" rule="unknown" severity="critical">
<rule_name>未签署知情同意书</rule_name>
<message>未签署知情同意书</message>
<field>informed_consent</field>
<detected_at>2026-02-08T05:30:56.028Z</detected_at>
</issue>
<issue record="7" rule="unknown" severity="critical">
<rule_name>年龄不在 25-35 岁范围内</rule_name>
<message>年龄不在 25-35 岁范围内</message>
<field>age</field>
<detected_at>2026-02-08T05:30:56.038Z</detected_at>
</issue>
<!-- ... 还有 14 个严重问题未显示 -->
</critical_issues>
<warning_issues count="0" />
</qc_report>

View File

@@ -0,0 +1,217 @@
# 2026-02-08 事件级质控与报告优化开发记录
> **开发日期:** 2026-02-08
> **开发人员:** AI Assistant
> **版本:** V3.1
> **状态:** ✅ 基本测试成功
---
## 📋 开发概述
本次开发主要完成了 IIT Manager Agent 质控系统的重大架构升级,从"记录级合并质控"改为"事件级独立质控",同时优化了质控报告生成逻辑和 AI 对话能力。
---
## 🎯 核心改动
### 1. 事件级质控架构V3.1
**问题背景:**
- REDCap 纵向研究中,一个 record_id 可能有多个事件如筛选期、随访1、随访2等
- 之前的质控将所有事件的数据合并到一条记录,导致数据覆盖问题
- 不同事件的表单应该独立质控
**解决方案:**
| 改动文件 | 改动内容 |
|---------|---------|
| `RedcapAdapter.ts` | 新增 `getAllRecordsByEvent()` 方法,按 record+event 返回独立数据单元 |
| `RedcapAdapter.ts` | 新增 `getFormEventMapping()``getEvents()` 获取事件配置 |
| `RedcapAdapter.ts` | 新增 `getFormCompletionStatusByEvent()` 按事件获取表单完成状态 |
| `HardRuleEngine.ts` | `QCRule` 接口新增 `applicableEvents``applicableForms` 字段 |
| `SoftRuleEngine.ts` | `SoftRuleCheck` 接口同样新增事件/表单适用性字段 |
| `SkillRunner.ts` | 重构 `runByTrigger()` 按 record+event 独立执行质控 |
| `SkillRunner.ts` | 新增 `filterApplicableRules()` 方法,按事件/表单动态过滤规则 |
| `SkillRunner.ts` | `SkillRunResult` 新增 `eventName``eventLabel``forms` 字段 |
| `SkillRunner.ts` | `saveQcLog()` 现在保存 `eventId` 到数据库 |
**架构变化:**
```
之前(记录级合并):
Record 1 = Event A 数据 + Event B 数据 + ... (合并可能覆盖)
之后(事件级独立):
Record 1 + Event A = 独立质控单元 1
Record 1 + Event B = 独立质控单元 2
...
```
### 2. 质控报告优化
**问题背景:**
- 报告从多个事件收集问题,导致同一规则重复出现
- 报告使用缓存,不是最新数据
- 显示限制导致部分问题被隐藏
**解决方案:**
| 改动文件 | 改动内容 |
|---------|---------|
| `QcReportService.ts` | SQL 查询改用 `DISTINCT ON (record_id, event_id)` |
| `QcReportService.ts` | 新增 `deduplicateIssues()` 按 recordId+ruleId 去重 |
| `QcReportService.ts` | 统计计数也按 recordId+ruleId 去重(使用 Set |
| `QcReportService.ts` | 移除所有 slice 显示限制,显示所有问题 |
| `QcReportService.ts` | 兼容新旧两种 issues 格式(数组 vs { items: [...] }|
### 3. 批量操作 API 更新
| 改动文件 | 改动内容 |
|---------|---------|
| `iitBatchController.ts` | `batchQualityCheck` 改用 `SkillRunner.runByTrigger()` |
| `ToolsService.ts` | `batch_quality_check` 工具同样改用事件级质控 |
### 4. 前端优化
| 改动文件 | 改动内容 |
|---------|---------|
| `QcReportDrawer.tsx` | 导出 XML 前自动刷新报告(确保获取最新数据)|
| `QcReportDrawer.tsx` | 文件名添加时分HHMM格式便于区分 |
| `IitQcCockpitPage.tsx` | 删除重复的"全量质控"按钮(保留配置页的)|
### 5. AI 对话增强
| 改动文件 | 改动内容 |
|---------|---------|
| `ChatService.ts` | 增强意图识别,支持更多质控查询表达方式 |
| `PromptBuilder.ts` | 修复 `this` 上下文丢失导致的 500 错误 |
---
## 🐛 修复的 Bug
### Bug 1: `formatPatientData` undefined 错误
**现象:** 点击记录详情时返回 500 错误
**原因:** `buildClinicalSlice` 函数导出时丢失了 `this` 上下文
**修复:**`this.formatPatientData` 改为 `PromptBuilder.formatPatientData`
### Bug 2: 质控报告重复问题
**现象:** Record 1 显示 14 条违规,实际应该是 5 条
**原因:** 同一规则在多个事件中执行,结果都被收集到报告中
**修复:**`recordId + ruleId` 去重,只保留最新结果
### Bug 3: 报告记录数不正确
**现象:** 显示 13 条记录,实际有 14 条
**原因:**`iitRecordSummary` 获取记录数,该表可能缺少记录
**修复:** 从质控日志获取独立 `record_id` 数量
### Bug 4: AI 无法回答质控问题
**现象:** 问"严重违规有几项"AI 说"没有相关信息"
**原因:** 意图识别规则不够全面
**修复:** 增加更多匹配模式质控问题、严重违规、record问题等
---
## 📁 涉及文件清单
### 后端核心文件
```
backend/src/modules/iit-manager/
├── adapters/
│ └── RedcapAdapter.ts ✅ 新增 3 个方法
├── engines/
│ ├── HardRuleEngine.ts ✅ QCRule 接口扩展
│ ├── SoftRuleEngine.ts ✅ SoftRuleCheck 接口扩展
│ └── SkillRunner.ts ✅ 事件级质控核心重构
├── services/
│ ├── QcReportService.ts ✅ 去重 + 移除限制
│ ├── ChatService.ts ✅ 意图识别增强
│ ├── PromptBuilder.ts ✅ 修复 this 上下文
│ └── ToolsService.ts ✅ batch_quality_check 更新
backend/src/modules/admin/iit-projects/
└── iitBatchController.ts ✅ 使用 SkillRunner
```
### 前端文件
```
frontend-v2/src/modules/admin/
├── components/qc-cockpit/
│ └── QcReportDrawer.tsx ✅ 自动刷新 + 文件名优化
└── pages/
└── IitQcCockpitPage.tsx ✅ 删除重复按钮
```
---
## 🧪 测试验证
### 测试结果
| 测试项 | 结果 | 备注 |
|--------|------|------|
| 事件级质控执行 | ✅ | 14 条记录 × 5 个事件 = 70 个质控单元 |
| 规则动态过滤 | ✅ | 配置 applicableForms 后规则只在相关事件执行 |
| 报告去重 | ✅ | 168 条问题去重后变为 69 条 |
| XML 导出 | ✅ | 自动刷新 + 显示所有问题 |
| AI 质控查询 | ✅ | 能正确回答"严重违规有几项" |
| 详情页 500 错误 | ✅ | formatPatientData 修复后正常 |
---
## 📝 配置说明
### 规则适用性配置示例
```typescript
// 在 Skill 配置中设置规则的适用表单
{
"id": "inc_001",
"name": "年龄范围检查",
"applicableForms": ["basic_demography_form"], // 只在人口学表单执行
"applicableEvents": [] // 空数组 = 适用所有事件
}
```
### 执行配置更新脚本
```bash
cd backend
npx tsx update-skill-applicable-forms.ts
```
---
## 🔄 后续优化建议
1. **规则配置 UI**:在管理端添加规则 → 表单映射的可视化配置界面
2. **事件标签显示**:在报告中显示事件的中文标签(如"筛选期")而非唯一标识
3. **增量质控**:只对有变化的事件执行质控,避免全量重算
4. **质控历史**:保留历史质控结果,支持趋势分析
---
## 📚 相关文档
- [模块状态文档](../00-模块当前状态与开发指南.md)
- [实时质控系统开发记录](./2026-02-07-实时质控系统开发记录.md)
- [质控系统 UI 与 LLM 格式优化计划](../04-开发计划/07-质控系统UI与LLM格式优化计划.md)
---
> **总结:** 本次开发完成了 IIT 质控系统从"记录级"到"事件级"的架构升级解决了数据合并覆盖、报告重复、AI 无法回答等多个问题,为 REDCap 纵向研究提供了更精确的质控能力。