docs(iit): Add QC system UI and LLM format optimization plan

- Add development plan: 07-QC system UI and LLM format optimization

- Phase 1: PromptBuilder + XML clinical slice format (~5.5h)

- Phase 2: QC cockpit + stat cards + risk heatmap (~9.5h)

- Phase 3: Variable tagging system (~5h)

- Add CRA Agent design document and prototype

- Update module status: design 100%, code 45%

- Update system status: real-time QC complete + plan ready

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-02-08 09:59:44 +08:00
parent 5db4a7064c
commit 45c7b32dbb
6 changed files with 1234 additions and 15 deletions

View File

@@ -1,10 +1,12 @@
# AIclinicalresearch 系统当前状态与开发指南
> **文档版本:** v4.7
> **文档版本:** v4.8
> **创建日期:** 2025-11-28
> **维护者:** 开发团队
> **最后更新:** 2026-02-05
> **最后更新:** 2026-02-08
> **🎉 重大里程碑:**
> - **2026-02-08IIT 质控系统优化计划制定!** XML 临床切片格式 + 质控驾驶舱 + 方案 A 确认
> - **2026-02-07IIT 实时质控系统开发完成!** pg-boss 防抖 + 质控日志 + 录入汇总 + 管理端批量操作
> - **2026-02-05IIT Manager Agent V2.9.1 架构设计完成!** 双脑架构 + 三层记忆 + 主动性增强 + 隐私合规
> - **2026-02-02REDCap 生产环境部署完成!** ECS + RDS + HTTPS + 域名全部配置完成
> - **2026-01-28Prompt 知识库集成完成!** Prompt 可动态引用系统知识库内容
@@ -14,12 +16,13 @@
> - **2026-01-22OSS 存储集成完成!** 阿里云 OSS 正式接入平台基础层
> - **2026-01-21成功替换 Dify** PKB 模块完全使用自研 pgvector RAG 引擎
>
> **最新进展IIT Manager Agent V2.9.1 架构 2026-02-05**
> **最新进展IIT Manager Agent 2026-02-08**
> - ✅ **实时质控系统**pg-boss 防抖 + 质控日志(审计轨迹) + 录入汇总upsert
> - ✅ **管理端批量操作**:一键全量质控 + 一键全量汇总(前后端完整)
> - ✅ **质控优化计划**XML 临床切片格式 + 质控驾驶舱 + 变量标签系统
> - ✅ **双脑架构**SOP 状态机(结构化任务) + ReAct 引擎(模糊查询)
> - ✅ **三层记忆**:流水账(全量日志) + 热记忆(高频注入) + 历史书(按需检索)
> - ✅ **主动性增强**Cron Skill 定时任务 + 用户画像 + 反馈循环
> - ✅ **隐私合规**PII 脱敏中间件 + 审计日志 + 可恢复脱敏
> - ✅ **自动化工具**AutoMapper REDCap Schema 自动对齐
>
> **部署状态:** ✅ 生产环境运行中 | 公网地址http://8.140.53.236/
> **REDCap 状态:** ✅ 生产环境运行中 | 地址https://redcap.xunzhengyixue.com/
@@ -59,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集成 | ⭐⭐⭐⭐⭐ | 🎉 **V2.9.1 架构完成 - 准备实施设计100%,代码25%** | **P0** |
| **IIT** | IIT Manager Agent | AI驱动IIT研究助手 - 双脑架构+REDCap集成 | ⭐⭐⭐⭐⭐ | 🎉 **实时质控完成 + UI优化计划制定设计100%,代码45%** | **P0** |
| **SSA** | 智能统计分析 | 队列/预测模型/RCT分析 | ⭐⭐⭐⭐⭐ | 📋 规划中 | P2 |
| **ST** | 统计分析工具 | 100+轻量化统计工具 | ⭐⭐⭐⭐ | 📋 规划中 | P2 |
| **RVW** | 稿件审查系统 | 方法学评估、审稿流程、Word导出 | ⭐⭐⭐⭐ | ✅ **开发完成95%** | P3 |
@@ -143,11 +146,24 @@
---
## 🚀 当前开发状态2026-02-05
## 🚀 当前开发状态2026-02-08
### 🎉 最新进展IIT Manager Agent V2.9.1 架构设计完成2026-02-05
### 🎉 最新进展IIT 实时质控系统完成 + UI 优化计划制定2026-02-08
#### ✅ 双脑架构 + 三层记忆 + 主动性增强 + 隐私合规
#### ✅ 实时质控系统 + 管理端批量操作 + 质控驾驶舱设计
**2026-02-07 开发完成:**
- ✅ 4 个新数据库表:`iit_qc_logs``iit_record_summary``iit_qc_project_stats``iit_field_metadata`
- ✅ pg-boss 防抖机制:`singletonKey` 5 分钟去重
- ✅ Worker 双产出:质控日志(仅新增)+ 录入汇总upsert
- ✅ 管理端批量操作:一键全量质控 + 一键全量汇总
**2026-02-08 计划制定:**
- 📋 XML 临床切片格式:对 LLM 更友好,减少幻觉
- 📋 质控驾驶舱页面:风险热力图 + 统计卡片
- 📋 变量标签系统:按语义标签切片数据
#### ✅ 双脑架构 + 三层记忆 + 主动性增强 + 隐私合规V2.9.1
**重大里程碑**
- 🎉 **完整架构设计**V2.9.1 开发计划发布6份模块化文档

View File

@@ -1,10 +1,11 @@
# IIT Manager Agent模块 - 当前状态与开发指南
> **文档版本:** v2.1
> **文档版本:** v2.2
> **创建日期:** 2026-01-01
> **维护者:** IIT Manager开发团队
> **最后更新:** 2026-02-07 🎉 **实时质控系统核心功能开发完成!**
> **最后更新:** 2026-02-08 🎉 **质控系统 UI 与 LLM 格式优化计划制定完成!**
> **重大里程碑:**
> - ✅ 2026-02-08**质控系统优化计划制定**XML 临床切片格式 + 质控驾驶舱设计 + 方案 A 确认)
> - ✅ 2026-02-07**实时质控系统开发完成**pg-boss防抖 + 质控日志 + 录入汇总 + 管理端批量操作)
> - ✅ 2026-02-05**V2.9.1 完整开发计划发布**(双脑架构 + 三层记忆 + 主动性增强 + 隐私合规)
> - ✅ 2026-02-02**REDCap 生产环境部署完成**ECS + RDS + HTTPS
@@ -82,11 +83,18 @@ IIT Manager Agent研究者发起试验管理助手是一个基于企业微
| **Phase 1** | 基础工具层ToolsService + HardRuleEngine + AutoMapper | P0 | ✅ 部分完成HardRuleEngine 已实现) |
| **Phase 1.5** | 隐私安全AnonymizerService + PII 脱敏) | **P0 合规必需** | 待开始 |
| **Phase 2** | 实时质控系统QC Worker + QcService + 批量操作) | P0 | ✅ **已完成** |
| **Phase 2.5** | 质控 UI 与 LLM 格式优化(驾驶舱 + XML 临床切片) | P0 | 📋 **计划已制定** |
| **Phase 3** | ReAct 引擎 + 流水账ReActEngine + 反馈循环) | P0 | 待开始 |
| **Phase 4** | 调度系统SchedulerService + Cron Skill + ProfilerService | P1 | 待开始 |
| **Phase 5** | 智能路由IntentService + 多意图处理) | P1 | 待开始 |
| **Phase 6** | 视觉能力VisionService | 延后 | 待开始 |
#### 📋 新增开发计划2026-02-08
- **[质控系统 UI 与 LLM 格式优化计划](./04-开发计划/07-质控系统UI与LLM格式优化计划.md)**
- 阶段 1LLM 格式优化PromptBuilder + XML 临床切片)~5.5h
- 阶段 2管理端 UI 增强(质控驾驶舱 + 统计卡片 + 热力图)~9.5h
- 阶段 3变量标签系统按语义标签切片数据~5h
- **部署状态**:✅ REDCap 生产环境运行中https://redcap.xunzhengyixue.com/
- **已知问题**:无
- **开发计划文档**[V2.9.1 综合开发计划](./04-开发计划/IIT%20Manager%20Agent%20V2.6%20综合开发计划.md)

View File

@@ -0,0 +1,215 @@
# **CRA Agent (临床监查) 深度设计与展现方案**
**文档版本:** v2.0
**文档目的:** 针对多变量300+)、高复杂度(入排/PD/AE/伦理)的临床研究场景,设计 CRA Agent 的数据处理逻辑与交互界面。
**适用版本:** IIT Manager Agent V2.9.1
**创建日期:** 2026-02-07
## **🏗️ 一、 架构设计:如何处理 300+ 变量?**
面对 10 个表单、300 个变量,我们不能试图一次性把所有数据塞进 Prompt。我们需要建立一个 **"变量知识图谱" (Variable Metadata Graph)**。
### **1.1 变量分组策略 (Grouping)**
在 Admin 端配置 REDCap 映射时,不仅要映射字段名,还要给变量打 **"语义标签"**
| REDCap 变量名 | 语义标签 (Tag) | 归属域 (Domain) | 关键度 |
| :---- | :---- | :---- | :---- |
| dob | \#demographics | 基线 | High |
| icf\_date | \#consent, \#ethics | 伦理 | Critical |
| visit\_date | \#timeline | 访视 | High |
| alt, ast | \#lab, \#safety | 实验室 | Medium |
| tumor\_size | \#efficacy | 疗效 | High |
**设计价值**
当 CRA Agent 需要核查 "是否符合入排标准" 时,它不需要读取 300 个变量,只需要请求 \#demographics \+ \#lab \+ \#efficacy 标签下的 20 个变量。
### **1.2 双层质控引擎 (Hybrid QC Engine)**
我们将质控分为两层,只有复杂逻辑才交给 LLM既省钱又准确。
* **Layer 1: 基础清洗 (HardRuleEngine)**
* **执行者**Node.js 代码
* **检查内容**:空值、数据类型、数值范围 (如 BMI \> 50)、逻辑跳转。
* **输出**:直接生成错误日志,不消耗 Token。
* **Layer 2: 医学逻辑 (AI Logic Engine)**
* **执行者**LLM (DeepSeek/Qwen) \+ RAG (Protocol)
* **检查内容**入排标准符合性、AE 判定、方案偏离 (PD)、合并用药禁忌。
* **输入**Layer 1 清洗后的数据 \+ 方案规则。
## **🤖 二、 对 LLM 友好的数据格式设计 (Context Protocol)**
为了让 LLM 精准判断医学逻辑,我们放弃 JSON 堆砌,采用 **"临床切片 (Clinical Slice)"** 模式。
### **2.1 场景一:入排标准核查 (Inclusion/Exclusion)**
**LLM Prompt 结构建议:**
\<task\>核查该患者是否符合研究入排标准\</task\>
\<protocol\_criteria\>
\<\!-- 从 RAG 检索出的标准 \--\>
1\. 年龄 18-75 岁。
2\. 病理确诊为非小细胞肺癌。
3\. 肝肾功能正常 (ALT/AST \< 2.5 ULN)。
\</protocol\_criteria\>
\<patient\_slice tag="screening\_data"\>
\<\!-- 仅注入相关变量,已转化为自然语言描述 \--\>
\- 出生日期1950-05-12 (当前年龄 75 岁)
\- 诊断:右肺腺癌 (2025-12-01 确诊)
\- 实验室检查ALT 150 U/L (参考值 0-40), AST 45 U/L。
\</patient\_slice\>
\<instruction\>
请一步步推理,对比患者数据与标准。重点关注肝功能指标。
\</instruction\>
### **2.2 场景二:方案偏离 (Protocol Deviation) \- 时间窗检查**
**LLM Prompt 结构建议:**
\<task\>检查 V2 访视是否超窗\</task\>
\<rule\_context\>
规则V2 访视应在 V1 后 28 天 ± 3 天进行。
前提V1 访视日期为基准。
\</rule\_context\>
\<timeline\_slice\>
\- V1 访视日期2026-01-01
\- V2 实际访视日期2026-02-05
\</timeline\_slice\>
### **2.3 场景三AE 事件侦测 (Safety Monitoring)**
这是最复杂的,需要对比前后两次数据。
\<task\>基于实验室数据变化,判断是否发生潜在 AE\</task\>
\<lab\_comparison\>
\<baseline\>
WBC: 5.5 (正常)
PLT: 200 (正常)
\</baseline\>
\<current\_visit\>
WBC: 2.1 (低于下限 4.0) ⚠️
PLT: 180 (正常)
\</current\_visit\>
\</lab\_comparison\>
\<question\>
白细胞计数 (WBC) 出现显著下降且低于正常值,根据 CTCAE 标准,这属于几级不良事件?是否在 AE 表中记录?
\</question\>
## **🖥️ 三、 后台展现形式设计 (Human UI)**
Admin 端需要一个高密度的 **"CRA 驾驶舱"**,让 CRA 一眼看穿 300 个变量背后的风险。
### **3.1 宏观视图:风险热力图 (Risk Heatmap)**
不要只列出 List要用矩阵。
* **行**:受试者 (Subject 001, 002...)
* **列**:访视/表单 (Screening, Baseline, V1, V2, AE, CM)
* **单元格**:颜色代表风险等级。
* 🟢 **Green**: Clean (无问题)
* 🟡 **Yellow**: Query (有疑问/空值)
* 🔴 **Red**: Critical (违背入排/严重 PD/SAE)
***Gray**: Not Started
**交互**:点击红色单元格,右侧滑出 "智能诊断报告"。
### **3.2 微观视图:智能诊断报告 (The "Why" Panel)**
当点击某个具体的报警(例如 Subject 001 的入排标准红色警告)时,展示如下内容:
#### **📋 诊断卡片:入排标准违规**
**\[结论\]**:❌ **建议剔除**
**\[置信度\]**High (基于硬指标)
**\[AI 推理链\]**
1. **标准要求**ALT \< 2.5倍 ULN (即 \< 100 U/L)。
2. **患者数据**:当前 ALT 为 150 U/L。
3. **判定**150 \> 100不符合入组标准第 3 条。
**\[原始证据\]**
* lab\_alt: 150 (来源于 form: lab\_results, field: alt)
* lab\_date: 2026-01-15
**\[CRA 操作区\]**
* \[ \] 忽略 (医学判断无碍)
* \[ \] 发送 Query (询问 CRC)
* \[ \] 确认违规 (标记为 PD)
### **3.3 调试视图LLM 视角 (Trace Viewer)**
在每条质控记录的详情中,提供一个 **"AI 思考过程"** Tab实现黑盒透明化。
* **Input (Prompt Context)**: 展示发送给 LLM 的 XML+Markdown 格式原始提示词(即“临床切片”)。
* **Process (Thinking)**: 展示 LLM 的思维链Chain of Thought
* **Output (JSON)**: 展示最终生成的结构化结论。
## **⚙️ 四、 配置层设计 (Rule Studio)**
### **4.1 规则配置中心**
采用 **“分层配置,自动托底”** 策略:
1. **Layer 1 字段自动映射 (Auto Mapping)**:
* 系统读取 REDCap Metadata自动生成 Hard Rules空值、范围
* 用户仅需为变量打标签Tagging如 \#safety, \#efficacy。
2. **Layer 2 逻辑构建器 (Logic Builder)**:
* 低代码配置跨表逻辑IF \[V2.Date\] \- \[V1.Date\] \> 31 THEN \[PD Alert\]。
3. **Layer 3 AI 监查员配置 (AI Setup)**:
* **上传 Protocol PDF** \-\> AI 自动提取入排标准 \-\> 人工确认 \-\> 自动生成 Layer 2 检查 Prompt。
## **🛡️ 五、 特殊医学逻辑设计**
针对你提到的 4 个核心需求,设计如下专项 Check
### **5.1 入排标准 (I/E Criteria)**
* **设计**:将 Protocol PDF 中的入排标准 OCR 化,转为结构化规则库。
* **展现**:在 "受试者列表" 增加一列 "I/E Status"。
* Pass: ✅
* Fail: ❌ (Hover 显示具体哪一条没过,如 "I3: Age")
### **5.2 方案偏离 (PD)**
* **核心**:时间窗 (Time Window) 和 漏做检查 (Missing Procedure)。
* **展现****时间轴视图 (Gantt-like)**。
* 展示 "计划访视日" vs "实际访视日"。
* 如果超窗,用红色区间标出。
### **5.3 伦理合规 (Ethics)**
* **核心逻辑**Date(ICF\_Sign) \<= Date(Any\_Study\_Procedure)。
* **逻辑**:如果任何检查日期早于知情同意书签署日期,触发 **最高级别警报 (Critical)**
* **展现**:置顶 Banner 警告,因为这是合规红线。
### **5.4 AE 事件侦测 (Safety)**
* **逻辑**
1. **Change Check**: 实验室指标从 Normal 变为 Abnormal。
2. **Cross Check**: 检查 AE Log 表单中是否有对应日期的记录。
3. 如果 Lab 异常但 AE 表为空 \-\> **提示 "潜在漏报 AE"**
* **展现**
* "Lab vs AE 一致性检查" 面板。
* 左侧显示异常 Lab 指标,右侧显示 AE 记录,中间用连线表示匹配关系。
## **📝 总结建议**
1. **不要全量喂数据**:建立 VariableMetadata按 Tag 切片。
2. **LLM 看"病历"**:将 JSON 转换为 **XML 标签包裹的 Markdown** 格式的“临床叙事”,不仅省 Token还能极大提升 AI 对医学逻辑的理解力。
3. **UI 矩阵化**CRA 需要上帝视角,热力图是最佳选择。
4. **推理透明化**AI 报出错误时,必须展示 "标准 vs 现状" 的对比证据,方便 CRA 快速复核。

View File

@@ -0,0 +1,598 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IIT Manager - CRA 智能监查驾驶舱</title>
<!-- 引入 Tailwind CSS 进行快速样式构建 -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- 引入 FontAwesome 图标 -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- 引入 Google Fonts (Inter) -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
body { font-family: 'Inter', sans-serif; }
/* 自定义滚动条 */
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: #f1f1f1; }
::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: #94a3b8; }
/* 模拟代码高亮 */
.xml-tag { color: #2563eb; }
.xml-attr { color: #9333ea; }
.xml-val { color: #16a34a; }
.xml-content { color: #374151; }
/* 动画 */
.slide-in-right {
animation: slideIn 0.3s ease-out forwards;
}
@keyframes slideIn {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
</style>
</head>
<body class="bg-gray-50 text-gray-800 h-screen flex overflow-hidden">
<!-- 左侧导航栏 -->
<aside class="w-64 bg-slate-900 text-white flex-shrink-0 flex flex-col transition-all duration-300">
<div class="h-16 flex items-center px-6 border-b border-slate-800">
<div class="flex items-center gap-2">
<div class="w-8 h-8 bg-blue-500 rounded-lg flex items-center justify-center">
<i class="fas fa-robot text-white text-sm"></i>
</div>
<span class="font-bold text-lg tracking-tight">IIT Manager</span>
</div>
</div>
<nav class="flex-1 py-6 px-3 space-y-1">
<a href="#" onclick="switchTab('overview')" class="nav-item flex items-center gap-3 px-3 py-2.5 rounded-lg text-slate-400 hover:bg-slate-800 hover:text-white transition-colors">
<i class="fas fa-chart-pie w-5 text-center"></i>
<span>项目概览</span>
</a>
<!-- 激活状态 -->
<a href="#" onclick="switchTab('cockpit')" class="nav-item active flex items-center gap-3 px-3 py-2.5 rounded-lg bg-blue-600 text-white shadow-lg shadow-blue-900/20 transition-colors">
<i class="fas fa-table-cells w-5 text-center"></i>
<span>智能监查驾驶舱</span>
</a>
<a href="#" onclick="switchTab('rules')" class="nav-item flex items-center gap-3 px-3 py-2.5 rounded-lg text-slate-400 hover:bg-slate-800 hover:text-white transition-colors">
<i class="fas fa-sliders w-5 text-center"></i>
<span>规则配置中心</span>
</a>
<div class="pt-4 mt-4 border-t border-slate-800">
<p class="px-3 text-xs font-semibold text-slate-500 uppercase tracking-wider mb-2">数据管理</p>
<a href="#" class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-slate-400 hover:bg-slate-800 hover:text-white transition-colors">
<i class="fas fa-users w-5 text-center"></i>
<span>受试者管理</span>
</a>
<a href="#" class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-slate-400 hover:bg-slate-800 hover:text-white transition-colors">
<i class="fas fa-database w-5 text-center"></i>
<span>REDCap 同步</span>
</a>
</div>
</nav>
<div class="p-4 border-t border-slate-800">
<div class="flex items-center gap-3">
<img src="https://ui-avatars.com/api/?name=Admin&background=random" class="w-8 h-8 rounded-full">
<div>
<p class="text-sm font-medium">Dr. Admin</p>
<p class="text-xs text-slate-500">项目管理员</p>
</div>
</div>
</div>
</aside>
<!-- 主内容区 -->
<main class="flex-1 flex flex-col min-w-0 overflow-hidden relative">
<!-- 顶部栏 -->
<header class="h-16 bg-white border-b border-gray-200 flex items-center justify-between px-8 flex-shrink-0 z-10">
<div>
<h1 class="text-xl font-bold text-gray-800" id="page-title">CRA 智能监查驾驶舱</h1>
<p class="text-xs text-gray-500 mt-0.5">项目: 肺癌靶向治疗研究 (PID: 16) | 最后同步: 10分钟前</p>
</div>
<div class="flex items-center gap-4">
<button class="px-4 py-2 bg-white border border-gray-300 text-gray-700 rounded-lg text-sm font-medium hover:bg-gray-50 flex items-center gap-2">
<i class="fas fa-sync-alt"></i> 全量质控
</button>
<button class="px-4 py-2 bg-blue-600 text-white rounded-lg text-sm font-medium hover:bg-blue-700 shadow-sm flex items-center gap-2">
<i class="fas fa-file-export"></i> 导出监查报告
</button>
</div>
</header>
<!-- 视图容器 -->
<div id="content-area" class="flex-1 overflow-auto p-8 bg-gray-50 relative">
<!-- VIEW 1: 智能监查驾驶舱 (Risk Heatmap) -->
<div id="view-cockpit" class="view-section space-y-6">
<!-- 统计卡片 -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-6">
<div class="bg-white p-5 rounded-xl border border-gray-200 shadow-sm">
<div class="flex justify-between items-start">
<div>
<p class="text-sm text-gray-500 font-medium">总体数据质量分</p>
<h3 class="text-3xl font-bold text-gray-800 mt-2">92 <span class="text-sm font-normal text-gray-400">/100</span></h3>
</div>
<span class="p-2 bg-green-100 text-green-600 rounded-lg"><i class="fas fa-heartbeat"></i></span>
</div>
<div class="mt-4 w-full bg-gray-100 rounded-full h-1.5">
<div class="bg-green-500 h-1.5 rounded-full" style="width: 92%"></div>
</div>
</div>
<div class="bg-white p-5 rounded-xl border border-gray-200 shadow-sm border-l-4 border-l-red-500">
<div class="flex justify-between items-start">
<div>
<p class="text-sm text-gray-500 font-medium">严重违规 (Critical)</p>
<h3 class="text-3xl font-bold text-red-600 mt-2">3 <span class="text-sm font-normal text-gray-500"></span></h3>
</div>
<span class="p-2 bg-red-100 text-red-600 rounded-lg"><i class="fas fa-exclamation-triangle"></i></span>
</div>
<p class="text-xs text-red-500 mt-3 font-medium">需立即处理:入排标准违背 (2)</p>
</div>
<div class="bg-white p-5 rounded-xl border border-gray-200 shadow-sm border-l-4 border-l-yellow-400">
<div class="flex justify-between items-start">
<div>
<p class="text-sm text-gray-500 font-medium">待确认 Query (Major)</p>
<h3 class="text-3xl font-bold text-yellow-600 mt-2">12 <span class="text-sm font-normal text-gray-500"></span></h3>
</div>
<span class="p-2 bg-yellow-100 text-yellow-600 rounded-lg"><i class="fas fa-question-circle"></i></span>
</div>
<p class="text-xs text-yellow-600 mt-3">主要是实验室数据异常</p>
</div>
<div class="bg-white p-5 rounded-xl border border-gray-200 shadow-sm">
<div class="flex justify-between items-start">
<div>
<p class="text-sm text-gray-500 font-medium">方案偏离 (PD)</p>
<h3 class="text-3xl font-bold text-gray-800 mt-2">5 <span class="text-sm font-normal text-gray-500"></span></h3>
</div>
<span class="p-2 bg-purple-100 text-purple-600 rounded-lg"><i class="fas fa-clock"></i></span>
</div>
<p class="text-xs text-gray-500 mt-3">大多为访视超窗</p>
</div>
</div>
<!-- 风险热力图 (The Matrix) -->
<div class="bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden">
<div class="px-6 py-4 border-b border-gray-200 flex justify-between items-center bg-gray-50">
<h3 class="font-bold text-gray-800">受试者风险全景图 (Risk Heatmap)</h3>
<div class="flex items-center gap-4 text-xs text-gray-500">
<span class="flex items-center gap-1"><span class="w-3 h-3 rounded-full bg-green-500"></span> 无异常</span>
<span class="flex items-center gap-1"><span class="w-3 h-3 rounded-full bg-yellow-400"></span> 疑问 (Query)</span>
<span class="flex items-center gap-1"><span class="w-3 h-3 rounded-full bg-red-500"></span> 严重违规</span>
<span class="flex items-center gap-1"><span class="w-3 h-3 rounded-full bg-gray-200"></span> 未开始</span>
</div>
</div>
<div class="overflow-x-auto">
<table class="w-full text-sm text-left">
<thead class="text-xs text-gray-500 uppercase bg-gray-50 border-b border-gray-200">
<tr>
<th class="px-6 py-3 font-medium">受试者 ID</th>
<th class="px-6 py-3 font-medium">入组状态</th>
<th class="px-4 py-3 text-center">Screening<br>(筛查期)</th>
<th class="px-4 py-3 text-center">Baseline<br>(基线期)</th>
<th class="px-4 py-3 text-center">Visit 1<br>(第1周)</th>
<th class="px-4 py-3 text-center">Visit 2<br>(第4周)</th>
<th class="px-4 py-3 text-center">AE Log<br>(不良事件)</th>
<th class="px-4 py-3 text-center">CM Log<br>(合并用药)</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
<!-- 行 1: 有严重问题 -->
<tr class="hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 font-medium text-gray-900">SUB-001</td>
<td class="px-6 py-4"><span class="px-2 py-1 bg-green-100 text-green-700 text-xs rounded-full">已入组</span></td>
<!-- 红色单元格:可点击 -->
<td class="px-4 py-4 text-center cursor-pointer hover:bg-red-50" onclick="openDrawer('SUB-001', 'Screening')">
<div class="w-8 h-8 rounded-lg bg-red-500 text-white flex items-center justify-center mx-auto shadow-sm shadow-red-200 transition-transform hover:scale-110">
<i class="fas fa-times"></i>
</div>
</td>
<td class="px-4 py-4 text-center"><div class="w-3 h-3 rounded-full bg-green-500 mx-auto"></div></td>
<td class="px-4 py-4 text-center"><div class="w-3 h-3 rounded-full bg-green-500 mx-auto"></div></td>
<td class="px-4 py-4 text-center"><div class="w-3 h-3 rounded-full bg-gray-200 mx-auto"></div></td>
<td class="px-4 py-4 text-center"><div class="w-3 h-3 rounded-full bg-green-500 mx-auto"></div></td>
<td class="px-4 py-4 text-center"><div class="w-3 h-3 rounded-full bg-green-500 mx-auto"></div></td>
</tr>
<!-- 行 2: 有警告 -->
<tr class="hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 font-medium text-gray-900">SUB-002</td>
<td class="px-6 py-4"><span class="px-2 py-1 bg-green-100 text-green-700 text-xs rounded-full">已入组</span></td>
<td class="px-4 py-4 text-center"><div class="w-3 h-3 rounded-full bg-green-500 mx-auto"></div></td>
<td class="px-4 py-4 text-center cursor-pointer hover:bg-yellow-50">
<div class="w-8 h-8 rounded-lg bg-yellow-400 text-white flex items-center justify-center mx-auto shadow-sm shadow-yellow-200 transition-transform hover:scale-110">
<i class="fas fa-exclamation"></i>
</div>
</td>
<td class="px-4 py-4 text-center"><div class="w-3 h-3 rounded-full bg-green-500 mx-auto"></div></td>
<td class="px-4 py-4 text-center"><div class="w-3 h-3 rounded-full bg-green-500 mx-auto"></div></td>
<td class="px-4 py-4 text-center"><div class="w-3 h-3 rounded-full bg-green-500 mx-auto"></div></td>
<td class="px-4 py-4 text-center"><div class="w-3 h-3 rounded-full bg-green-500 mx-auto"></div></td>
</tr>
<!-- 更多行... -->
<tr class="hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 font-medium text-gray-900">SUB-003</td>
<td class="px-6 py-4"><span class="px-2 py-1 bg-blue-100 text-blue-700 text-xs rounded-full">筛查中</span></td>
<td class="px-4 py-4 text-center"><div class="w-3 h-3 rounded-full bg-green-500 mx-auto"></div></td>
<td class="px-4 py-4 text-center"><div class="w-3 h-3 rounded-full bg-gray-200 mx-auto"></div></td>
<td class="px-4 py-4 text-center"><div class="w-3 h-3 rounded-full bg-gray-200 mx-auto"></div></td>
<td class="px-4 py-4 text-center"><div class="w-3 h-3 rounded-full bg-gray-200 mx-auto"></div></td>
<td class="px-4 py-4 text-center"><div class="w-3 h-3 rounded-full bg-gray-200 mx-auto"></div></td>
<td class="px-4 py-4 text-center"><div class="w-3 h-3 rounded-full bg-gray-200 mx-auto"></div></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- VIEW 2: 规则配置中心 (Rule Studio) -->
<div id="view-rules" class="view-section hidden space-y-6">
<!-- Layer 3: AI 配置 -->
<div class="bg-white rounded-xl border border-gray-200 shadow-sm p-6">
<div class="flex justify-between items-center mb-6">
<div>
<h3 class="text-lg font-bold text-gray-800 flex items-center gap-2">
<span class="bg-purple-100 text-purple-600 px-2 py-1 rounded text-xs">Layer 3</span>
AI 监查员配置 (AI Setup)
</h3>
<p class="text-sm text-gray-500 mt-1">负责复杂医学逻辑如入排标准、AE判定的智能化配置</p>
</div>
<button class="px-4 py-2 bg-purple-600 text-white rounded-lg text-sm hover:bg-purple-700">
<i class="fas fa-file-pdf mr-2"></i>重新解析 Protocol
</button>
</div>
<div class="grid grid-cols-2 gap-8">
<!-- 左侧Protocol 提取 -->
<div class="bg-gray-50 p-4 rounded-lg border border-gray-200">
<h4 class="text-sm font-bold text-gray-700 mb-3">AI 提取的入排标准 (Top 3)</h4>
<div class="space-y-3">
<div class="p-3 bg-white border border-gray-200 rounded shadow-sm">
<div class="flex justify-between">
<span class="text-xs font-bold text-gray-500">I-03 (入选)</span>
<span class="text-xs text-green-600 bg-green-50 px-1 rounded">已确认</span>
</div>
<p class="text-sm mt-1">肝肾功能正常,定义为 ALT/AST < 2.5倍 ULN</p>
<div class="mt-2 flex gap-2">
<span class="px-2 py-0.5 bg-blue-50 text-blue-600 text-xs rounded border border-blue-100">Tag: #lab</span>
<span class="px-2 py-0.5 bg-blue-50 text-blue-600 text-xs rounded border border-blue-100">Tag: #safety</span>
</div>
</div>
<div class="p-3 bg-white border border-gray-200 rounded shadow-sm">
<div class="flex justify-between">
<span class="text-xs font-bold text-gray-500">E-02 (排除)</span>
<span class="text-xs text-green-600 bg-green-50 px-1 rounded">已确认</span>
</div>
<p class="text-sm mt-1">既往 5 年内有其他恶性肿瘤病史。</p>
<div class="mt-2 flex gap-2">
<span class="px-2 py-0.5 bg-blue-50 text-blue-600 text-xs rounded border border-blue-100">Tag: #history</span>
</div>
</div>
</div>
</div>
<!-- 右侧Prompt 预览 -->
<div class="bg-slate-900 text-slate-300 p-4 rounded-lg font-mono text-xs overflow-auto h-64">
<div class="text-slate-500 mb-2">// 系统自动生成的 Layer 2 检查 Prompt</div>
<p class="text-purple-400">&lt;task&gt;</p>
<p class="pl-4">基于上传的 Protocol核查患者是否符合 I-03 标准。</p>
<p class="text-purple-400">&lt;/task&gt;</p>
<br>
<p class="text-purple-400">&lt;criteria&gt;</p>
<p class="pl-4">标准值ALT < 2.5 * ULN (100 U/L)</p>
<p class="text-purple-400">&lt;/criteria&gt;</p>
<br>
<p class="text-purple-400">&lt;data_requirement&gt;</p>
<p class="pl-4 text-green-400">Tag: #lab, #safety</p>
<p class="text-purple-400">&lt;/data_requirement&gt;</p>
</div>
</div>
</div>
<!-- Layer 1: 字段映射 -->
<div class="bg-white rounded-xl border border-gray-200 shadow-sm p-6">
<h3 class="text-lg font-bold text-gray-800 mb-4 flex items-center gap-2">
<span class="bg-blue-100 text-blue-600 px-2 py-1 rounded text-xs">Layer 1</span>
字段自动映射 (Auto Mapping)
</h3>
<div class="overflow-x-auto">
<table class="w-full text-sm text-left">
<thead class="bg-gray-50 text-gray-500">
<tr>
<th class="px-4 py-2">REDCap 字段</th>
<th class="px-4 py-2">所属表单</th>
<th class="px-4 py-2">自动规则 (Hard Rules)</th>
<th class="px-4 py-2">语义标签 (Tags)</th>
<th class="px-4 py-2">操作</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
<tr>
<td class="px-4 py-3 font-mono">dob</td>
<td class="px-4 py-3">Demographics</td>
<td class="px-4 py-3"><span class="bg-gray-100 px-2 py-1 rounded text-xs">Required</span> <span class="bg-gray-100 px-2 py-1 rounded text-xs">Date</span></td>
<td class="px-4 py-3">
<span class="bg-blue-50 text-blue-600 border border-blue-200 px-2 py-0.5 rounded text-xs">#demographics</span>
</td>
<td class="px-4 py-3 text-blue-600 cursor-pointer">编辑</td>
</tr>
<tr>
<td class="px-4 py-3 font-mono">lab_alt</td>
<td class="px-4 py-3">Lab Test</td>
<td class="px-4 py-3"><span class="bg-gray-100 px-2 py-1 rounded text-xs">Min: 0</span> <span class="bg-gray-100 px-2 py-1 rounded text-xs">Max: 1000</span></td>
<td class="px-4 py-3">
<span class="bg-blue-50 text-blue-600 border border-blue-200 px-2 py-0.5 rounded text-xs">#lab</span>
<span class="bg-red-50 text-red-600 border border-red-200 px-2 py-0.5 rounded text-xs">#safety</span>
</td>
<td class="px-4 py-3 text-blue-600 cursor-pointer">编辑</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- 侧滑抽屉:智能诊断详情 (Subject 360) -->
<div id="drawer-overlay" class="fixed inset-0 bg-black/50 z-40 hidden transition-opacity opacity-0" onclick="closeDrawer()"></div>
<div id="drawer-panel" class="fixed inset-y-0 right-0 w-[1000px] bg-white shadow-2xl z-50 transform translate-x-full transition-transform duration-300 flex flex-col">
<!-- 抽屉头部 -->
<div class="h-16 px-6 border-b border-gray-200 flex items-center justify-between bg-gray-50">
<div class="flex items-center gap-4">
<div class="w-10 h-10 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center font-bold">
01
</div>
<div>
<h2 class="text-lg font-bold text-gray-800">受试者 SUB-001</h2>
<p class="text-xs text-gray-500">访视阶段: Screening | 录入时间: 2026-02-07 14:30</p>
</div>
</div>
<div class="flex items-center gap-2">
<button class="px-3 py-1.5 bg-white border border-gray-300 text-gray-600 rounded text-sm hover:bg-gray-50">
<i class="fas fa-external-link-alt mr-1"></i> REDCap
</button>
<button onclick="closeDrawer()" class="p-2 text-gray-400 hover:text-gray-600 rounded-full hover:bg-gray-100">
<i class="fas fa-times text-xl"></i>
</button>
</div>
</div>
<!-- 抽屉内容:左右分栏 -->
<div class="flex-1 flex overflow-hidden">
<!-- 左栏:真实数据 (Source of Truth) -->
<div class="w-1/2 border-r border-gray-200 flex flex-col bg-white">
<div class="p-4 bg-gray-50 border-b border-gray-200 flex justify-between items-center">
<h3 class="font-bold text-gray-700"><i class="fas fa-database mr-2"></i>真实数据 (Screening Form)</h3>
<span class="text-xs bg-gray-200 text-gray-600 px-2 py-0.5 rounded">Read Only</span>
</div>
<div class="flex-1 overflow-y-auto p-6 space-y-6">
<!-- 表单字段 -->
<div class="space-y-4">
<div>
<label class="block text-xs font-medium text-gray-500 uppercase">Screening Date</label>
<div class="mt-1 p-2 bg-gray-50 border border-gray-200 rounded text-gray-800">2026-01-15</div>
</div>
<!-- 异常字段高亮 -->
<div class="relative group">
<div class="absolute -left-4 top-2 text-red-500"><i class="fas fa-arrow-right"></i></div>
<label class="block text-xs font-medium text-gray-500 uppercase">Lab Test: ALT (U/L)</label>
<div class="mt-1 p-2 bg-red-50 border border-red-300 rounded text-red-700 font-bold flex justify-between items-center">
<span>150</span>
<i class="fas fa-exclamation-circle"></i>
</div>
<p class="text-xs text-red-500 mt-1">Ref Range: 0-40</p>
</div>
<div>
<label class="block text-xs font-medium text-gray-500 uppercase">Lab Test: AST (U/L)</label>
<div class="mt-1 p-2 bg-gray-50 border border-gray-200 rounded text-gray-800">45</div>
</div>
<div>
<label class="block text-xs font-medium text-gray-500 uppercase">Inclusion Criteria Checklist</label>
<div class="mt-1 p-2 bg-gray-50 border border-gray-200 rounded text-gray-800">Complete</div>
</div>
</div>
</div>
</div>
<!-- 右栏AI 监查报告 + LLM Trace -->
<div class="w-1/2 flex flex-col bg-slate-50">
<div class="border-b border-gray-200 bg-white">
<nav class="flex -mb-px">
<button onclick="switchDrawerTab('report')" id="tab-report" class="w-1/2 py-4 px-1 text-center border-b-2 border-blue-500 font-medium text-sm text-blue-600">
<i class="fas fa-robot mr-2"></i>智能诊断报告
</button>
<button onclick="switchDrawerTab('trace')" id="tab-trace" class="w-1/2 py-4 px-1 text-center border-b-2 border-transparent font-medium text-sm text-gray-500 hover:text-gray-700 hover:border-gray-300">
<i class="fas fa-code mr-2"></i>LLM Trace (透视)
</button>
</nav>
</div>
<!-- Tab 1: 报告 -->
<div id="drawer-content-report" class="flex-1 overflow-y-auto p-6 space-y-4">
<!-- 严重报警卡片 -->
<div class="bg-white rounded-lg shadow-sm border border-red-200 overflow-hidden">
<div class="bg-red-50 px-4 py-3 border-b border-red-100 flex justify-between items-center">
<h4 class="text-red-700 font-bold flex items-center gap-2">
<i class="fas fa-times-circle"></i> 违反入排标准 (Critical)
</h4>
<span class="text-xs bg-white text-red-600 px-2 py-1 rounded border border-red-200">置信度: High</span>
</div>
<div class="p-4">
<div class="mb-4">
<p class="text-xs font-bold text-gray-500 uppercase mb-1">AI 观点</p>
<p class="text-sm text-gray-800">
根据 Protocol 标准 I-03受试者肝功能需正常ALT < 2.5倍 ULN < 100
<br>检测到该患者当前 <strong>ALT 为 150</strong>,显著超出允许范围,建议剔除。
</p>
</div>
<div class="mb-4 bg-gray-50 p-3 rounded border border-gray-200 text-xs text-gray-600">
<p class="font-bold mb-1">原始证据链 (Evidence Chain):</p>
<ul class="list-disc pl-4 space-y-1">
<li>Variable: <code>lab_alt</code> = 150</li>
<li>Standard: <code>limit_max</code> = 100</li>
<li>Form: Screening Form</li>
</ul>
</div>
<div class="flex gap-2 pt-2 border-t border-gray-100">
<button class="flex-1 bg-red-600 text-white text-sm py-2 rounded hover:bg-red-700">确认违规 (PD)</button>
<button class="flex-1 bg-white border border-gray-300 text-gray-700 text-sm py-2 rounded hover:bg-gray-50">发送 Query</button>
<button class="px-3 bg-white border border-gray-300 text-gray-400 rounded hover:text-gray-600" title="忽略"><i class="fas fa-trash-alt"></i></button>
</div>
</div>
</div>
<!-- 警告卡片 -->
<div class="bg-white rounded-lg shadow-sm border border-yellow-200 overflow-hidden opacity-75">
<div class="bg-yellow-50 px-4 py-3 border-b border-yellow-100 flex justify-between items-center">
<h4 class="text-yellow-700 font-bold flex items-center gap-2">
<i class="fas fa-exclamation-triangle"></i> 数据完整性警告
</h4>
<span class="text-xs bg-white text-yellow-600 px-2 py-1 rounded border border-yellow-200">置信度: Medium</span>
</div>
<div class="p-4">
<p class="text-sm text-gray-800">AST 字段有值 (45),但未填写单位。虽然数值在正常范围内,建议确认单位是否为 U/L。</p>
</div>
</div>
</div>
<!-- Tab 2: LLM Trace (XML View) -->
<div id="drawer-content-trace" class="hidden flex-1 overflow-y-auto p-0 bg-[#1e1e1e] text-gray-300 font-mono text-xs">
<div class="p-4 border-b border-gray-700 bg-[#252526] text-gray-400 flex justify-between">
<span>Prompt Context (Sent to DeepSeek-V3)</span>
<span class="text-blue-400">XML Protocol</span>
</div>
<div class="p-6 space-y-1">
<div><span class="xml-tag">&lt;task&gt;</span>核查该患者是否符合研究入排标准<span class="xml-tag">&lt;/task&gt;</span></div>
<br>
<div><span class="xml-tag">&lt;protocol_criteria&gt;</span></div>
<div class="pl-4 text-[#ce9178]">1. 年龄 18-75 岁。</div>
<div class="pl-4 text-[#ce9178]">2. 病理确诊为非小细胞肺癌。</div>
<div class="pl-4 text-[#ce9178]">3. 肝肾功能正常 (ALT/AST < 2.5 ULN)</div>
<div><span class="xml-tag">&lt;/protocol_criteria&gt;</span></div>
<br>
<div><span class="xml-tag">&lt;patient_slice</span> <span class="xml-attr">tag</span>=<span class="xml-val">"#lab"</span><span class="xml-tag">&gt;</span></div>
<div class="pl-4 text-gray-400">&lt;!-- Layer 1 Filtered Data --&gt;</div>
<div class="pl-4 text-[#9cdcfe]">- 实验室检查日期: 2026-01-15</div>
<div class="pl-4 text-[#9cdcfe]">- ALT: <span class="bg-red-900 text-white px-1">150</span> U/L</div>
<div class="pl-4 text-[#9cdcfe]">- AST: 45 U/L</div>
<div><span class="xml-tag">&lt;/patient_slice&gt;</span></div>
<br>
<div><span class="xml-tag">&lt;instruction&gt;</span></div>
<div class="pl-4 text-[#ce9178]">请一步步推理,对比患者数据与标准。重点关注肝功能指标。</div>
<div><span class="xml-tag">&lt;/instruction&gt;</span></div>
<div class="mt-8 pt-4 border-t border-gray-700">
<div class="text-green-400 mb-2">// LLM Output (Chain of Thought)</div>
<div class="text-gray-400 italic">
> Thinking: 标准要求 ALT 小于 100 (2.5 * 40)。<br>
> 检查 patient_slice 数据...<br>
> 发现 ALT = 150。<br>
> 150 > 100判定违规。<br>
> 生成 Critical 级别报警。
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
<script>
// 简单的 Tab 切换逻辑
function switchTab(tabId) {
// 隐藏所有视图
document.querySelectorAll('.view-section').forEach(el => el.classList.add('hidden'));
document.querySelectorAll('.nav-item').forEach(el => {
el.classList.remove('bg-blue-600', 'text-white', 'shadow-lg');
el.classList.add('text-slate-400', 'hover:bg-slate-800');
});
// 显示选中视图
document.getElementById('view-' + tabId).classList.remove('hidden');
// 这里的 active 样式逻辑简化处理,实际开发可用 Vue/React 控制 class
const activeLink = document.querySelector(`a[onclick="switchTab('${tabId}')"]`);
activeLink.classList.remove('text-slate-400', 'hover:bg-slate-800');
activeLink.classList.add('bg-blue-600', 'text-white', 'shadow-lg');
// 更新标题
const titles = {
'overview': '项目概览 Dashboard',
'cockpit': 'CRA 智能监查驾驶舱',
'rules': '规则配置中心 (Rule Studio)'
};
document.getElementById('page-title').innerText = titles[tabId];
}
// 抽屉控制
function openDrawer(subjectId, visit) {
const drawerPanel = document.getElementById('drawer-panel');
const drawerOverlay = document.getElementById('drawer-overlay');
drawerOverlay.classList.remove('hidden');
// 强制重绘以触发 transition
setTimeout(() => {
drawerOverlay.classList.remove('opacity-0');
drawerPanel.classList.remove('translate-x-full');
}, 10);
}
function closeDrawer() {
const drawerPanel = document.getElementById('drawer-panel');
const drawerOverlay = document.getElementById('drawer-overlay');
drawerPanel.classList.add('translate-x-full');
drawerOverlay.classList.add('opacity-0');
setTimeout(() => {
drawerOverlay.classList.add('hidden');
}, 300);
}
// 抽屉内 Tab 切换
function switchDrawerTab(tabName) {
const reportContent = document.getElementById('drawer-content-report');
const traceContent = document.getElementById('drawer-content-trace');
const tabReport = document.getElementById('tab-report');
const tabTrace = document.getElementById('tab-trace');
if (tabName === 'report') {
reportContent.classList.remove('hidden');
traceContent.classList.add('hidden');
tabReport.classList.add('border-blue-500', 'text-blue-600');
tabReport.classList.remove('border-transparent', 'text-gray-500');
tabTrace.classList.remove('border-blue-500', 'text-blue-600');
tabTrace.classList.add('border-transparent', 'text-gray-500');
} else {
reportContent.classList.add('hidden');
traceContent.classList.remove('hidden');
tabTrace.classList.add('border-blue-500', 'text-blue-600');
tabTrace.classList.remove('border-transparent', 'text-gray-500');
tabReport.classList.remove('border-blue-500', 'text-blue-600');
tabReport.classList.add('border-transparent', 'text-gray-500');
}
}
</script>
</body>
</html>

View File

@@ -0,0 +1,384 @@
# 质控系统 UI 与 LLM 格式优化开发计划
> **版本:** v1.0
> **创建日期:** 2026-02-08
> **基于讨论:** 团队技术方案评审 + 产品原型对比分析
> **决策确认:** 方案 A渐进式改进
---
## 一、背景与目标
### 1.1 现状问题
1. **LLM 格式问题**:当前使用简单 JSON 格式返回数据LLM 理解力有限,容易产生幻觉
2. **UI 展示缺失**:管理端只有批量操作按钮,无法直观查看质控结果和录入进度
3. **入口层级深**IIT 管理嵌套在运营管理端中,质控监控功能不够突出
### 1.2 参考文档
| 文档 | 路径 | 核心内容 |
|------|------|----------|
| CRA Agent 深度设计方案 | `docs/03-业务模块/IIT Manager Agent/01-需求分析/CRA Agent (临床监查) 深度设计与展现方案.md` | 变量分组、双层质控、临床切片格式 |
| 质控管理原型图 | `docs/03-业务模块/IIT Manager Agent/01-需求分析/质控管理原型图.html` | 风险热力图、统计卡片、智能诊断抽屉 |
### 1.3 目标
1. **提升 AI 回答准确性**:采用 XML 临床切片格式,减少 LLM 幻觉
2. **提升运营效率**:提供可视化质控驾驶舱,一眼看穿数据问题
3. **渐进式改进**:不破坏现有架构,逐步增强功能
---
## 二、详细评估表
### 2.1 团队方案 vs 当前代码对比
| 功能点 | 团队方案描述 | 当前状态 | 建议 |
|--------|-------------|---------|------|
| **1. 变量语义标签 (Tagging)** | 为变量打 `#demographics`, `#lab`, `#safety` 等标签,按需加载 | ❌ 未实现 | 🔴 **采纳** - 在 `iit_field_metadata` 表增加 `tags` 字段 |
| **2. 双层质控引擎** | Layer 1: HardRuleEngine, Layer 2: AI Logic Engine | ✅ Layer 1 已完成 | 🟡 Layer 2 可延后 |
| **3. 临床切片格式** | XML + Markdown 结构化提示词,对 LLM 更友好 | ❌ 使用简单 JSON | 🔴 **采纳** - 新增 `PromptBuilder.buildClinicalSlice()` |
| **4. 风险热力图** | 矩阵式受试者 × 访视/表单视图,颜色表示风险等级 | ❌ 未实现 | 🔴 **采纳** - 管理端新增驾驶舱页面 |
| **5. 统计卡片** | 数据质量分、严重违规数、Query 数、方案偏离数 | ❌ 未实现 | 🔴 **采纳** - 驾驶舱页面顶部 |
| **6. 智能诊断抽屉** | 左侧真实数据 + 右侧 AI 诊断可操作Query/确认/忽略) | ❌ 未实现 | 🟡 **简化采纳** - 先实现详情弹窗 |
| **7. LLM Trace 视图** | 展示发送给 LLM 的 Prompt 和思考过程 | ❌ 未实现 | 🟢 延后 - 调试阶段使用 |
| **8. 规则配置中心** | Layer 1/2/3 分层配置界面 | ✅ 已有质控规则页面 | ✅ 保持现有 |
| **9. AI 自动提取规则** | 上传 Protocol PDF 自动生成规则 | ❌ 未实现 | 🟢 延后 - 高级功能 |
| **10. qc_logs 审计轨迹** | 质控日志仅新增,保留历史记录 | ✅ 已实现 | ✅ 保持现有 |
| **11. record_summary 汇总** | 录入汇总表 upsert 模式 | ✅ 已实现 | ✅ 保持现有 |
| **12. pg-boss 防抖** | Webhook 去重机制 | ✅ 已实现 | ✅ 保持现有 |
### 2.2 LLM 格式对比
| 维度 | 当前 JSON 格式 | XML 临床切片格式 |
|------|---------------|-----------------|
| **Token 消耗** | 较多(括号、引号、嵌套) | 较少(语义化标签) |
| **LLM 理解力** | 一般(需要解析 JSON 结构) | 极强(医学叙事文本) |
| **上下文切割** | 困难(全量或无) | 简单(按 tag 切片) |
| **推理准确性** | 中等 | 高(有明确任务指令) |
| **调试友好度** | 一般 | 好XML 结构清晰) |
**当前 JSON 格式示例:**
```json
{
"projectName": "test0207",
"stats": {
"totalRecords": 13,
"passedRecords": 0,
"failedRecords": 13
},
"problemRecords": [
{
"recordId": "1",
"status": "FAIL",
"issues": [...]
}
]
}
```
**XML 临床切片格式示例:**
```xml
<task>核查该患者是否符合研究入排标准</task>
<protocol_criteria>
1. 年龄 16-35 岁。
2. 月经周期规律28±7天
3. VAS 评分 ≥ 4 分。
4. 签署知情同意书。
</protocol_criteria>
<patient_slice tag="#demographics, #screening">
- 出生日期2003-01-07当前年龄 22 岁)✅
- 月经周期45 天 ⚠️ 超出范围
- VAS 评分:未填写 ⚠️
- 知情同意:未签署 ❌
</patient_slice>
<instruction>
请一步步推理,对比患者数据与标准。如发现异常,说明具体哪条标准被违反。
</instruction>
```
---
## 三、架构方案确认
### 3.1 方案 A确认采用
**核心思路:渐进式改进,增加"质控全览图"按钮**
```
运营管理端 → IIT 项目管理 → 项目详情页
├── REDCap 配置 (Tab)
├── 质控规则 (Tab)
├── 通知设置 (Tab)
├── 知识库 (Tab)
└── [⚡一键全量质控] [📊一键全量汇总] [🔍质控全览图]
全新页面(全屏驾驶舱)
/admin/iit-projects/:id/cockpit
```
**优势:**
1. 不破坏现有系统架构
2. 配置与监控职责分离
3. 全屏展示更多内容
4. 开发成本可控
### 3.2 驾驶舱页面布局
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ ← 返回项目配置 test0207 - CRA 智能监查驾驶舱 ⚡全量质控 📄导出报告 │
├─────────────────────────────────────────────────────────────────────────────┤
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐│
│ │ 总体数据质量分 │ │ 严重违规 │ │ 待确认 Query │ │ 录入完成率 ││
│ │ │ │ (Critical) │ │ (Major) │ │ ││
│ │ 0 /100 │ │ 13 条 │ │ 0 条 │ │ 14% ││
│ │ ████░░░░░░░░ │ │ 需立即处理 │ │ │ │ 7个表单/1个完成││
│ └────────────────┘ └────────────────┘ └────────────────┘ └────────────────┘│
├─────────────────────────────────────────────────────────────────────────────┤
│ 受试者风险全景图 (Risk Heatmap) │
│ ────────────────────────────────────────────────────────────────────────── │
│ 🟢 无异常 🟡 疑问(Query) 🔴 严重违规 ⚪ 未开始 │
│ ────────────────────────────────────────────────────────────────────────── │
│ 记录ID │ 入组状态 │ 人口学信息 │ 病史诊断 │ 知情同意 │ 入排标准 │ CMSS │ ...│
│ ───────┼─────────┼───────────┼─────────┼─────────┼─────────┼──────┼─────│
│ 1 │ 入组中 │ 🟢 │ ⚪ │ 🔴 │ 🔴 │ ⚪ │ ⚪ │
│ 2 │ 入组中 │ 🟢 │ ⚪ │ 🔴 │ 🔴 │ ⚪ │ ⚪ │
│ 3 │ 入组中 │ 🟢 │ ⚪ │ 🔴 │ 🔴 │ ⚪ │ ⚪ │
│ ... │ ... │ ... │ ... │ ... │ ... │ ... │ ... │
│ 13 │ 入组中 │ 🟢 │ ⚪ │ 🔴 │ 🔴 │ ⚪ │ ⚪ │
└─────────────────────────────────────────────────────────────────────────────┘
│ 点击红色单元格
┌─────────────────────────────────────────────────────────────────────────────┐
│ 侧滑抽屉:记录 1 - 入排标准详情 │
├─────────────────────────────────────────────────────────────────────────────┤
│ 【质控问题】 │
│ ❌ 年龄不在 25-35 岁范围内当前22岁
│ ❌ 月经周期不在 21-35 天范围内当前45天
│ ❌ VAS 评分 < 4 分(当前:空值) │
│ ❌ 未签署知情同意书 │
│ │
│ 【原始数据】 │
│ age: 22 | menstrual_cycle: 45 | vas_score: null | informed_consent: 0 │
│ │
│ [发送 Query] [确认违规] [忽略] │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
## 四、分阶段开发计划
### 阶段 1LLM 格式优化P0 - 优先级最高)
**目标:提升 AI 回答准确性,减少幻觉**
| 任务 | 描述 | 工作量 | 优先级 |
|------|------|--------|--------|
| 1.1 创建 PromptBuilder 类 | 实现 `buildClinicalSlice()` 方法,生成 XML 格式 | 2h | P0 |
| 1.2 修改 ChatService | 在质控查询时使用 PromptBuilder 格式化数据 | 1.5h | P0 |
| 1.3 添加自然语言摘要 | 返回结果增加 `summary` 字段,便于 LLM 直接引用 | 1h | P0 |
| 1.4 测试验证 | 企业微信端测试 AI 回答准确性 | 1h | P0 |
**技术实现:**
```typescript
// backend/src/modules/iit-manager/services/PromptBuilder.ts
export class PromptBuilder {
/**
* 构建 XML 临床切片格式
*/
static buildClinicalSlice(params: {
task: string;
criteria: string[];
patientData: Record<string, any>;
tags?: string[];
instruction?: string;
}): string {
const { task, criteria, patientData, tags = [], instruction } = params;
return `
<task>${task}</task>
<protocol_criteria>
${criteria.map((c, i) => ` ${i + 1}. ${c}`).join('\n')}
</protocol_criteria>
<patient_slice${tags.length > 0 ? ` tag="${tags.join(', ')}"` : ''}>
${Object.entries(patientData)
.map(([k, v]) => ` - ${k}: ${v ?? '未填写'}`)
.join('\n')}
</patient_slice>
${instruction ? `<instruction>\n${instruction}\n</instruction>` : ''}
`.trim();
}
/**
* 构建质控结果摘要(自然语言)
*/
static buildQcSummary(stats: {
projectName: string;
totalRecords: number;
passRate: number;
topIssues: Array<{ issue: string; count: number }>;
}): string {
const { projectName, totalRecords, passRate, topIssues } = stats;
const issueText = topIssues
.slice(0, 3)
.map(i => `${i.issue}${i.count}条)`)
.join('、');
return `项目 ${projectName} 共有 ${totalRecords} 条记录,` +
`质控通过率 ${passRate.toFixed(1)}%。` +
(topIssues.length > 0 ? `主要问题包括:${issueText}` : '暂无质控问题。');
}
}
```
---
### 阶段 2管理端 UI 增强P0
**目标:提供可视化质控驾驶舱**
| 任务 | 描述 | 工作量 | 优先级 |
|------|------|--------|--------|
| 2.1 添加"质控全览图"按钮 | 在 IitProjectDetailPage 增加按钮 | 0.5h | P0 |
| 2.2 创建驾驶舱页面骨架 | IitQcCockpitPage.tsx + 路由配置 | 1h | P0 |
| 2.3 实现统计卡片组件 | 4 个卡片质量分、违规数、Query 数、完成率 | 1.5h | P0 |
| 2.4 实现风险热力图组件 | 矩阵式表格,颜色表示风险等级 | 3h | P0 |
| 2.5 实现详情抽屉组件 | 点击单元格展示问题详情 | 2h | P1 |
| 2.6 后端 API热力图数据 | 按记录×表单聚合质控状态 | 1.5h | P0 |
**新增文件清单:**
```
frontend-v2/src/modules/admin/
├── pages/
│ └── IitQcCockpitPage.tsx # 驾驶舱主页面
├── components/
│ ├── QcStatCards.tsx # 统计卡片组件
│ ├── RiskHeatmap.tsx # 风险热力图组件
│ └── QcDetailDrawer.tsx # 详情抽屉组件
backend/src/modules/admin/iit-projects/
├── iitQcCockpitController.ts # 驾驶舱数据 API
└── iitQcCockpitRoutes.ts # 路由定义
```
---
### 阶段 3变量标签系统P1
**目标:支持按语义标签切片数据**
| 任务 | 描述 | 工作量 | 优先级 |
|------|------|--------|--------|
| 3.1 扩展数据库表 | `iit_field_metadata` 增加 `tags` 字段 | 0.5h | P1 |
| 3.2 管理端标签编辑 | 质控规则页面支持为字段打标签 | 2h | P1 |
| 3.3 REDCap 同步时自动打标签 | 根据表单名推断默认标签 | 1.5h | P2 |
| 3.4 PromptBuilder 按标签切片 | 只加载指定标签的字段 | 1h | P1 |
**数据库变更:**
```sql
-- 扩展 iit_field_metadata 表
ALTER TABLE iit_schema.field_metadata
ADD COLUMN tags TEXT[] DEFAULT '{}';
-- 示例数据
UPDATE iit_schema.field_metadata
SET tags = ARRAY['#demographics', '#screening']
WHERE field_name IN ('age', 'date_of_birth', 'sex');
UPDATE iit_schema.field_metadata
SET tags = ARRAY['#consent', '#ethics']
WHERE field_name = 'informed_consent';
```
---
### 阶段 4高级功能P2 - 延后)
| 任务 | 描述 | 工作量 | 优先级 |
|------|------|--------|--------|
| 4.1 LLM Trace 视图 | 展示发送给 LLM 的 Prompt | 3h | P2 |
| 4.2 AI 自动提取规则 | 上传 Protocol PDF 自动生成规则 | 8h | P3 |
| 4.3 Layer 2 AI 逻辑引擎 | 复杂医学逻辑判断 | 10h | P3 |
| 4.4 导出监查报告 | 生成 PDF/Word 格式报告 | 4h | P2 |
---
## 五、开发优先级总结
| 优先级 | 阶段 | 核心任务 | 预计工时 |
|--------|------|----------|----------|
| **P0** | 阶段 1 | LLM 格式优化PromptBuilder + ChatService | 5.5h |
| **P0** | 阶段 2 | 管理端 UI驾驶舱 + 统计卡片 + 热力图) | 9.5h |
| **P1** | 阶段 3 | 变量标签系统 | 5h |
| **P2** | 阶段 4 | 高级功能(延后) | 25h+ |
**MVP 阶段(阶段 1 + 2总工时~15 小时**
---
## 六、验收标准
### 6.1 阶段 1 验收
- [ ] 企业微信问"质控情况"AI 回答格式清晰、无幻觉
- [ ] AI 回答包含具体问题和记录数
- [ ] 日志中可看到 XML 格式的 Prompt
### 6.2 阶段 2 验收
- [ ] 点击"质控全览图"按钮可进入驾驶舱页面
- [ ] 统计卡片正确显示质量分、违规数、完成率
- [ ] 热力图正确显示记录×表单的质控状态
- [ ] 点击红色单元格可查看问题详情
### 6.3 阶段 3 验收
- [ ] 字段可配置语义标签
- [ ] PromptBuilder 支持按标签过滤数据
- [ ] AI 查询时只加载相关字段
---
## 七、风险与依赖
| 风险 | 影响 | 缓解措施 |
|------|------|----------|
| 热力图数据量大 | 页面卡顿 | 分页加载、虚拟滚动 |
| XML 格式 Token 计算 | 超出 LLM 上下文限制 | 按标签切片、限制字段数 |
| REDCap 表单结构多变 | 热力图列不固定 | 动态渲染、表单配置 |
---
## 八、相关文档
| 文档 | 说明 |
|------|------|
| [06-实时质控系统开发计划.md](./06-实时质控系统开发计划.md) | 质控后端架构 |
| [CRA Agent 深度设计方案](../01-需求分析/CRA%20Agent%20(临床监查)%20深度设计与展现方案.md) | 技术方案参考 |
| [质控管理原型图](../01-需求分析/质控管理原型图.html) | UI 原型参考 |
---
**文档维护人:** AI Assistant
**创建时间:** 2026-02-08
**下次评审:** 阶段 1 完成后

View File

@@ -989,9 +989,7 @@
font-size: 1.2em;
}.message-bubble .markdown-content h3 {
font-size: 1.1em;
}
.message-bubble .markdown-content ul,
}.message-bubble .markdown-content ul,
.message-bubble .markdown-content ol {
margin: 8px 0 12px 0;
padding-left: 24px;
@@ -1028,4 +1026,4 @@
border-radius: 4px;
font-family: 'Monaco', 'Consolas', 'Courier New', monospace;
font-size: 0.9em;
}
}