feat(iit): QC deep fix + V3.1 architecture plan + project member management

QC System Deep Fix:
- HardRuleEngine: add null tolerance + field availability pre-check (skipped status)
- SkillRunner: baseline data merge for follow-up events + field availability check
- QcReportService: record-level pass rate calculation + accurate LLM XML report
- iitBatchController: legacy log cleanup (eventId=null) + upsert RecordSummary
- seed-iit-qc-rules: null/empty string tolerance + applicableEvents config

V3.1 Architecture Design (docs only, no code changes):
- QC engine V3.1 plan: 5-level data structure (CDISC ODM) + D1-D7 dimensions
- Three-batch implementation strategy (A: foundation, B: bubbling, C: new engines)
- Architecture team review: 4 whitepapers reviewed + feedback doc + 4 critical suggestions
- CRA Agent strategy roadmap + CRA 4-tool explanation doc for clinical experts

Project Member Management:
- Cross-tenant member search and assignment (remove tenant restriction)
- IIT project detail page enhancement with tabbed layout (KB + members)
- IitProjectContext for business-side project selection
- System-KB route access control adjustment for project operators

Frontend:
- AdminLayout sidebar menu restructure
- IitLayout with project context provider
- IitMemberManagePage new component
- Business-side pages adapt to project context

Prisma:
- 2 new migrations (user-project RBAC + is_demo flag)
- Schema updates for project member management

Made-with: Cursor
This commit is contained in:
2026-03-01 15:27:05 +08:00
parent c3f7d54fdf
commit 0b29fe88b5
61 changed files with 6877 additions and 524 deletions

View File

@@ -0,0 +1,357 @@
# CRA 智能质控 Agent — 四大工具工作原理说明
> **文档用途**面向临床专家、PI主要研究者及项目管理人员说明 CRA 智能质控 Agent 背后的四个核心工具是如何工作的。
> **最后更新**2026-02-25
---
## 一、整体工作方式
CRA 智能质控 Agent 是一个基于大语言模型LLM的 AI 助手,专门用于 IIT 临床研究项目的质量监控。当用户在对话框中提问时AI **不是凭空回答**,而是通过调用 4 个专用工具获取真实数据后,再基于数据生成回答。
### 工作流程
```
用户提问(如:"当前通过率是多少?"
AI 分析问题,自动选择合适的工具
工具执行,获取真实数据
AI 基于工具返回的数据,生成自然语言回答
用户看到回答(如:"当前整体通过率为 92.9%14 条记录中 13 条通过..."
```
### 核心原则
- **所有回答必须基于真实数据**AI 不会编造临床数据
- **只读不写**AI 不会修改任何临床数据,如果被要求修改数据会拒绝
- **优先使用报告**80% 的问题通过预计算的质控报告直接回答,保证速度和一致性
---
## 二、四个工具详解
### 工具 1`read_report` — 质控报告查阅
**一句话说明**:查阅预先生成好的质控报告,是最常用的工具。
#### 什么时候使用?
用户问以下类型的问题时AI 会调用此工具:
- "目前通过率是多少?"
- "有哪些严重问题?"
- "3 号受试者有什么质控问题?"
- "哪个表单通过率最低?"
- "最近质控发现了什么趋势?"
#### 报告包含哪些内容?
| 报告章节 | 包含信息 |
|---------|---------|
| **概览summary** | 总记录数、已完成记录数、通过率、严重问题数、警告数、最后质控时间 |
| **严重问题critical_issues** | 每个有问题的受试者具体违反了哪条规则、实际值是什么、标准应该是什么 |
| **警告warning_issues** | 非致命性的数据异常提醒 |
| **表单统计form_stats** | 每张 CRF 表单的通过率 |
| **问题排名trend** | 哪些规则被违反最多,影响了多少人 |
#### 报告示例
以下是 AI 实际读取的报告格式(修复后的真实报告):
```xml
<qc_context project_id="xxx" project_name="test0207" generated="2026-02-25T...">
<summary>
- 状态: 1/14 记录存在严重违规 (7% Fail)
- 通过率: 92.9%
- 严重问题: 1 | 警告: 0
- Top 1 问题:
1. 排除标准检查 (1人)
</summary>
<critical_issues record_count="1" issue_count="1">
<record id="3">
**严重违规 (1项)**:
1. [exc_001] **排除标准检查**: 当前值 **1** (标准: 应为0)
</record>
</critical_issues>
</qc_context>
```
#### 报告数据从哪里来?
```
质控报告 ← 数据库中的质控日志qc_logs 表)
每个受试者 × 每个访视事件,取最新一条质控记录
按受试者+规则去重,避免重复计数
通过率 = 按受试者级别计算(每个受试者取所有访视中最严重的状态)
```
#### 缓存机制
报告生成后会缓存 **24 小时**。在缓存有效期内,多次提问不会重复计算,保证了响应速度。执行"一键全量质控"后会自动刷新报告缓存。
#### 常见问题
**Q报告里只有质控问题不包含录入的原始数据吗**
A对。`read_report` 只包含质控结论(通过/不通过、违规详情。如果要看具体患者的原始录入数据如年龄、实验室指标AI 会自动调用下面的 `look_up_data` 工具。
---
### 工具 2`look_up_data` — 查询原始临床数据
**一句话说明**:从 REDCap 系统实时拉取患者的原始录入数据。
#### 什么时候使用?
用户问以下类型的问题时AI 会调用此工具:
- "3 号受试者的年龄是多少?"
- "帮我查一下 5 号患者的实验室检查结果"
- "这个患者的知情同意日期是什么时候?"
#### 工作流程
```
AI 调用 look_up_data受试者编号=3, 查询字段=[年龄, 性别]
系统将中文字段名翻译为 REDCap 内部字段名(如:年龄 → age
通过 REDCap API 实时查询该患者数据
如果是纵向研究(多次访视),自动合并所有访视的数据
返回给 AI{ record_id: "3", age: "28", gender: "1" }
AI 组织成自然语言回答用户
```
#### 关键特性
- **实时查询**:每次调用都从 REDCap 获取最新数据,不使用缓存
- **支持中文查询**:用户可以说"帮我查年龄",系统会自动翻译为 REDCap 字段名 `age`
- **纵向数据自动合并**:同一患者在不同访视(筛选期、基线、随访等)中录入的数据会自动合并为一条完整记录
- **只读操作**:只查询数据,绝不修改
#### 数据来源
直接通过 REDCap REST API 从 REDCap 数据库实时获取,数据路径为:
```
AI → REDCap APIhttp://REDCap服务器/api/)→ REDCap 数据库 → 返回记录
```
---
### 工具 3`check_quality` — 即时质控检查
**一句话说明**:对患者数据立即执行质控规则检查,可以检查单个患者或全部患者。
#### 什么时候使用?
用户**明确要求重新检查**时AI 才会调用此工具:
- "帮我重新检查一下 3 号受试者"
- "现在执行一次全量质控"
- "数据刚更新了,帮我跑一下质控"
> **注意**:如果用户只是问"通过率是多少"这样的查询性问题AI 会使用 `read_report` 而不是 `check_quality`,因为报告中已有预计算的结果。
#### 两种检查模式
##### 模式 A单患者检查
```
AI 调用 check_quality(record_id="3")
从 REDCap 拉取 3 号患者的最新数据
加载项目配置的所有质控规则(如:年龄范围检查、排除标准检查等)
逐条规则执行:
- 字段缺失?→ 跳过该规则(不算失败)
- 字段有值?→ 用 JSON Logic 规则引擎判定通过/失败
返回结果:
整体状态: FAIL
通过 8 / 9 条规则
违规: 排除标准检查 — 当前值为 1应为 0
```
##### 模式 B全量批量检查
```
AI 调用 check_quality()(不传受试者编号)
从 REDCap 拉取所有患者的所有访视数据
基线数据合并:将基线访视的数据(如年龄、性别)合并到后续访视中
逐个 受试者 × 访视 执行规则引擎
结果写入数据库(追加新记录,不覆盖历史)
返回汇总:
总计: 42 条检查
通过: 39 条
未通过: 3 条
通过率: 92.9%
问题受试者: [3号 - 排除标准检查违规]
```
#### 质控规则引擎说明
系统使用 **JSON Logic 规则引擎** 执行质控检查。目前配置的规则包括:
| 规则类别 | 示例规则 | 说明 |
|---------|---------|------|
| **纳入标准** | 年龄范围检查16-35岁 | 不在范围内则标记为严重问题 |
| **纳入标准** | 知情同意检查 | 必须已签署知情同意 |
| **排除标准** | 排除标准符合性检查 | 符合任何排除标准则标记为严重问题 |
**缺失数据处理策略**V3.2 修复后):
- 如果某个字段在当前访视中没有录入(如随访期没有"年龄"字段),该规则**自动跳过**,不算失败
- 只有字段有值但不符合规则时,才判定为失败
---
### 工具 4`search_knowledge` — 知识库检索
**一句话说明**在项目文档研究方案、CRF、伦理批件等中搜索信息。
#### 什么时候使用?
用户问以下类型的问题时AI 会调用此工具:
- "纳入标准是什么?"
- "研究方案中对随访间隔是怎么规定的?"
- "主要疗效指标是什么?"
- "知情同意的要求有哪些?"
#### 工作流程
```
AI 调用 search_knowledge(query="纳入标准是什么")
查找该项目关联的知识库
将用户问题转化为向量(语义表示)
在知识库中进行语义相似度搜索(不是简单的关键词匹配)
返回最相关的 5 个文档片段:
1. 研究方案V2.0.pdf相关度 87.3%
内容:"纳入标准1. 年龄16-35岁 2. 确诊为..."
2. ICF知情同意书.pdf相关度 72.1%
内容:"..."
AI 综合这些文档片段,生成回答
```
#### 关键特性
- **语义搜索**:不是简单的关键词匹配,而是理解问题含义后搜索。比如问"入组条件"也能找到"纳入标准"的内容
- **文档来源标注**:每个搜索结果都标注来自哪个文档,方便追溯
- **相关度评分**:只返回相关度 ≥ 30% 的结果,过滤无关内容
- **支持多种文档**研究方案、CRF 说明、伦理批件、操作手册等上传到知识库的文档均可搜索
#### 知识库管理
知识库中的文档需要通过项目管理界面上传。上传后,系统会自动:
1. 解析文档内容(支持 PDF、Word 等格式)
2. 将文档分块(每块约 500-1000 字)
3. 为每个文档块生成向量嵌入(用于语义搜索)
4. 存入向量数据库
---
## 三、工具协作示例
以下是一个典型的多工具协作场景:
### 场景:用户问"3 号受试者有什么问题?详细说明一下"
```
第 1 轮AI 调用 read_report(section="critical_issues", record_id="3")
→ 获取质控报告中 3 号受试者的问题列表
→ 发现:排除标准检查失败,实际值为 1
第 2 轮AI 调用 look_up_data(record_id="3", fields=["exclusion_criteria"])
→ 从 REDCap 获取原始数据确认
→ 确认 exclusion_criteria 字段值确实为 1
AI 最终回答:
"3 号受试者存在 1 项严重违规:排除标准检查未通过。
该受试者的排除标准字段值为 1应为 0即不符合任何排除标准
建议核实该受试者是否确实符合入组条件。"
```
### 场景:用户问"纳入标准中年龄范围是多少?有没有超龄的患者?"
```
第 1 轮AI 调用 search_knowledge(query="纳入标准年龄范围")
→ 从研究方案中找到:"纳入标准第1条年龄16-35岁"
第 2 轮AI 调用 read_report(section="critical_issues")
→ 检查是否有年龄相关的质控问题
→ 当前无年龄违规记录
AI 最终回答:
"根据研究方案,纳入标准规定年龄范围为 16-35 岁。
当前质控报告显示,所有已录入受试者的年龄均在合规范围内,
未发现超龄问题。"
```
---
## 四、总结对比
| 工具 | 核心用途 | 数据来源 | 实时性 | 使用频率 |
|------|---------|---------|--------|---------|
| **read_report** | 查阅质控报告 | 数据库(预计算报告) | 缓存 24 小时 | ~80% |
| **look_up_data** | 查询原始数据 | REDCap 实时 API | 实时 | ~10% |
| **check_quality** | 执行质控检查 | REDCap + 规则引擎 | 实时执行 | ~5% |
| **search_knowledge** | 搜索文档知识 | 项目知识库(向量搜索) | 准实时 | ~5% |
### 设计理念
**报告优先,工具兜底**Report-first, Tools-fallback
- 绝大多数质控问题通过预计算的报告直接回答,保证响应速度(秒级)
- 只在需要查看具体原始数据或重新执行质控时,才调用其他工具
- AI 会根据问题类型自动选择最合适的工具,无需用户干预
---
## 附录:技术架构简图
```
┌──────────────────────────────────────────────────────────────────┐
│ 用户对话界面 │
Web / 微信公众号) │
└──────────────────────┬───────────────────────────────────────────┘
│ 用户提问
┌──────────────────────────────────────────────────────────────────┐
│ ChatOrchestrator对话编排器
│ │
│ System Prompt角色定义 + 工具选择策略) │
│ ↓ │
│ LLMDeepSeek-V3← Function Calling 循环(最多 3 轮) │
│ ↓ │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────────┐ ┌───────────┐ │
│ │ read_report │ │look_up_data │ │check_quality │ │search_ │ │
│ │ │ │ │ │ │ │knowledge │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬───────┘ └─────┬─────┘ │
└─────────┼───────────────┼───────────────┼───────────────┼────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐
│ QcReport │ │ REDCap │ │ REDCap │ │ pgvector │
│ Service │ │ REST API │ │ REST API │ │ 向量搜索 │
│ (数据库) │ │ (实时) │ │ + 规则引擎 │ │ (知识库) │
└────────────┘ └────────────┘ └────────────┘ └────────────┘
```