- Frontend V3 architecture migration to modules/pkb - Implement three work modes: full-text reading, deep reading, batch processing - Complete batch processing: template selection, progress display, result export (CSV) - Integrate Ant Design X Chat component with streaming support - Add document upload modal with drag-and-drop support - Optimize UI: multi-line table display, citation formatting, auto-scroll - Fix 10+ technical issues: API mapping, state sync, form clearing - Update documentation: development records and module status Performance: 3 docs batch processing ~17-28s Status: PKB module now production-ready (90% complete)
481 lines
14 KiB
Markdown
481 lines
14 KiB
Markdown
# Tool B - 病历结构化机器人 技术债务清单
|
||
|
||
> **创建日期:** 2025-12-03
|
||
> **状态:** 待处理
|
||
> **优先级:** P1=高优先级, P2=中优先级, P3=低优先级
|
||
|
||
---
|
||
|
||
## 📋 技术债务列表
|
||
|
||
### **[P1] #1 - Excel导出与前端显示结果不一致**
|
||
|
||
**问题描述:**
|
||
- 用户在步骤4交叉验证页面看到的提取结果,与导出的Excel文件内容不一致
|
||
- 列顺序混乱,部分字段缺失或数据错位
|
||
|
||
**重现步骤:**
|
||
1. 完成双模型提取并进入步骤4
|
||
2. 点击"导出当前结果"或在步骤5点击"下载结果Excel"
|
||
3. 打开Excel,对比前端显示的结果
|
||
|
||
**根本原因:**
|
||
- JavaScript对象展开`...extractedData`时顺序不固定
|
||
- 未按模板定义的字段顺序构建Excel列
|
||
|
||
**当前状态:**
|
||
- ✅ 已部分修复:按targetFields顺序导出
|
||
- ❌ 仍需验证:多次导出结果是否稳定一致
|
||
|
||
**解决方案:**
|
||
1. 严格按照`task.targetFields`定义的字段顺序导出
|
||
2. 添加表头样式(加粗、冻结首行)
|
||
3. 添加数据验证(确保所有字段都存在)
|
||
4. 添加导出测试用例
|
||
|
||
**预计工时:** 2小时
|
||
**影响范围:** 后端 ExtractionController.exportResults方法
|
||
|
||
---
|
||
|
||
### **[P2] #2 - 步骤3进度条显示不够细腻**
|
||
|
||
**问题描述:**
|
||
- 当前进度条直接从0%跳到100%,缺少中间过程
|
||
- 用户无法感知大模型正在处理第几条记录
|
||
- 没有实时反馈当前处理状态(如"正在处理第3/9条")
|
||
|
||
**期望效果:**
|
||
```
|
||
提取进度: 33% (3/9条已完成)
|
||
|
||
日志输出:
|
||
[13:43:12] 正在创建提取任务...
|
||
[13:43:12] 任务创建成功 (ID: xxx)
|
||
[13:43:12] 初始化双模型引擎 (DeepSeek-V3 & Qwen-Max)...
|
||
[13:43:13] [1/9] 正在提取: 【右肺下叶】浸润性腺癌...
|
||
[13:43:18] [1/9] ✅ 提取完成 (DeepSeek: 549 tokens, Qwen: 627 tokens)
|
||
[13:43:19] [2/9] 正在提取: 【右肺上叶】浸润性腺癌...
|
||
[13:43:24] [2/9] ✅ 提取完成 (DeepSeek: 486 tokens, Qwen: 551 tokens)
|
||
...
|
||
[13:43:30] PII 脱敏完成
|
||
[13:43:30] ✅ 所有记录提取完成!
|
||
```
|
||
|
||
**解决方案:**
|
||
|
||
**后端改动:**
|
||
1. 在`DualModelExtractionService.batchExtract`的for循环中,每处理完一条记录就更新进度
|
||
2. 添加`currentItem`字段到Task表(可选,用于实时显示当前处理的记录)
|
||
3. 或者使用Redis存储实时进度信息(更云原生)
|
||
|
||
**前端改动:**
|
||
1. 轮询API时,解析`processedCount`和`totalCount`
|
||
2. 动态生成日志:`[${processedCount}/${totalCount}] 正在提取...`
|
||
3. 进度条平滑过渡(CSS transition)
|
||
|
||
**预计工时:** 3小时
|
||
**影响范围:**
|
||
- 后端:DualModelExtractionService.batchExtract
|
||
- 前端:Step3Processing.tsx
|
||
|
||
---
|
||
|
||
### **[P1] #3 - Excel文件预处理与脏数据清洗**
|
||
|
||
**问题描述:**
|
||
医疗科研场景下,Excel文件质量参差不齐,存在大量脏数据导致解析失败或结果错误。
|
||
|
||
#### **子问题1:表头特殊字符**
|
||
- **现象:** 列名包含换行符`\n`、空格、制表符等,导致列名匹配失败
|
||
- **示例:** `"病人ID\n(Patient ID)"` → 前端下拉框显示异常
|
||
- **影响:** 用户无法选择正确的列
|
||
|
||
#### **子问题2:公式 (Formulas)**
|
||
- **现象:** 单元格包含公式`=A1+B1`,xlsx库读取时返回公式文本而非计算结果
|
||
- **示例:**
|
||
- 原始值:`=SUM(A1:A10)`
|
||
- 读取结果:字符串`"=SUM(A1:A10)"`(而非数字)
|
||
- 外部引用:`=[外部文件]Sheet1!A1` → `#REF!`
|
||
- **影响:** 数值型字段(如年龄、血糖值)变成文本,无法统计
|
||
|
||
#### **子问题3:合并单元格 (Merged Cells)**
|
||
- **现象:** 医生习惯合并"住院号"列,对应多行化验记录
|
||
- **示例:**
|
||
```
|
||
住院号 检查项目 结果
|
||
H001 血常规 正常 ← 只有这行有住院号
|
||
(合并) 肝功能 异常 ← 这行住院号为null
|
||
(合并) 肾功能 正常 ← 这行住院号为null
|
||
```
|
||
- **影响:** 后续行的关联字段丢失,无法追溯到患者
|
||
|
||
#### **子问题4:日期地狱 (Date Parsing Hell)**
|
||
- **现象:** Excel日期存储为数字(Serial Number),或多种文本格式
|
||
- **示例:**
|
||
- `44927` → 应该解析为 `2023-01-01`
|
||
- `2023.1.1`(文本)
|
||
- `2023年1月1日`(中文)
|
||
- `Jan 1, 2023`(英文)
|
||
- **影响:** 日期字段无法排序、筛选、统计
|
||
|
||
#### **子问题5:不可见字符与脏文本 (Ghost Characters)**
|
||
- **现象:** 看起来是"男",实际包含不可见字符
|
||
- **示例:**
|
||
- `"男 "` (尾部空格)
|
||
- `"男\u200b"` (零宽空格 Zero-Width Space)
|
||
- `"男\ufeff"` (BOM字符)
|
||
- **影响:** 条件判断失败:`if (sex === '男')` → false
|
||
- **医学场景特例:**
|
||
- 化验单复制粘贴时带入富文本格式
|
||
- 不同医院HIS系统导出编码不统一
|
||
|
||
**解决方案:**
|
||
|
||
#### **架构设计:独立的Excel预处理服务**
|
||
```typescript
|
||
// backend/src/modules/dc/services/ExcelPreprocessor.ts
|
||
export class ExcelPreprocessor {
|
||
/**
|
||
* 清洗表头
|
||
*/
|
||
cleanHeaders(headers: string[]): string[] {
|
||
return headers.map(h => h
|
||
.replace(/[\n\r\t]/g, ' ') // 移除换行、制表符
|
||
.trim() // 去除首尾空格
|
||
.replace(/\s+/g, ' ') // 多个空格合并为一个
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 处理公式单元格
|
||
*/
|
||
processFormulas(worksheet: xlsx.WorkSheet): void {
|
||
// 使用 xlsx 的 { cellFormula: false } 选项
|
||
// 或手动遍历单元格,计算公式结果
|
||
}
|
||
|
||
/**
|
||
* 展开合并单元格
|
||
*/
|
||
unflattenMergedCells(worksheet: xlsx.WorkSheet): void {
|
||
// 1. 找到所有合并区域 worksheet['!merges']
|
||
// 2. 将主单元格的值填充到所有子单元格
|
||
}
|
||
|
||
/**
|
||
* 统一日期格式
|
||
*/
|
||
normalizeDates(value: any): string | null {
|
||
if (typeof value === 'number') {
|
||
// Excel Serial Number → ISO Date
|
||
return this.excelSerialToDate(value);
|
||
}
|
||
if (typeof value === 'string') {
|
||
// 尝试多种格式解析
|
||
return this.parseChineseDate(value) ||
|
||
this.parseSlashDate(value) ||
|
||
this.parseDotDate(value);
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 清除不可见字符
|
||
*/
|
||
cleanInvisibleChars(text: string): string {
|
||
return text
|
||
.replace(/\u200b/g, '') // 零宽空格
|
||
.replace(/\ufeff/g, '') // BOM
|
||
.replace(/\u00a0/g, ' ') // 不间断空格 → 普通空格
|
||
.trim();
|
||
}
|
||
}
|
||
```
|
||
|
||
#### **使用位置:**
|
||
1. **uploadFile API** - 上传后立即预处理,返回清洗后的列名
|
||
2. **healthCheck API** - 使用清洗后的数据进行检查
|
||
3. **createTask API** - 使用清洗后的数据创建items
|
||
|
||
**预计工时:** 16小时(复杂度高,需要大量测试)
|
||
**影响范围:**
|
||
- 新增:`ExcelPreprocessor.ts` (~400行)
|
||
- 修改:`ExtractionController.ts` 的文件处理逻辑
|
||
- 测试:覆盖各种脏数据场景
|
||
|
||
**依赖:**
|
||
- xlsx库的高级功能(cellFormula、!merges等)
|
||
- dayjs或date-fns(日期解析)
|
||
|
||
---
|
||
|
||
### **[P2] #4 - 支持用户自定义提取模板**
|
||
|
||
**问题描述:**
|
||
当前系统只支持3个预设模板(肺癌病理、糖尿病入院、高血压门诊),无法满足用户的多样化需求。
|
||
|
||
**需求场景:**
|
||
1. 科研人员研究罕见病(如:系统性红斑狼疮、重症肌无力)
|
||
2. 需要提取的字段与预设模板不同
|
||
3. 每个研究项目的数据规范可能不同
|
||
|
||
**期望功能:**
|
||
|
||
#### **1. 前端:自定义模板编辑器**
|
||
```
|
||
步骤2.1:选择模板来源
|
||
- [ ] 使用系统预设模板
|
||
- [x] 创建自定义模板
|
||
|
||
步骤2.2:定义模板信息
|
||
- 模板名称:[我的肺癌研究模板]
|
||
- 疾病类型:[自定义:系统性红斑狼疮]
|
||
- 报告类型:[自定义:实验室检查]
|
||
|
||
步骤2.3:定义提取字段(可视化编辑)
|
||
┌─────────────────────────────────────┐
|
||
│ 字段1: [抗核抗体滴度] │
|
||
│ 描述: [如 1:320, 1:640] │
|
||
│ 宽度: [w-32] ▼ │
|
||
│ [ 删除 ] │
|
||
├─────────────────────────────────────┤
|
||
│ 字段2: [补体C3] │
|
||
│ 描述: [单位g/L] │
|
||
│ [ 删除 ] │
|
||
└─────────────────────────────────────┘
|
||
[+ 添加字段]
|
||
|
||
步骤2.4:AI生成Prompt(自动化)
|
||
[ 🤖 让AI帮我生成提示词 ]
|
||
|
||
后台自动生成:
|
||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
你是一名风湿免疫科专家。请从以下系统性红斑狼疮
|
||
患者的实验室检查报告中提取关键信息。
|
||
|
||
提取字段(必须返回以下所有字段):
|
||
- 抗核抗体滴度:如 1:320, 1:640
|
||
- 补体C3:单位g/L
|
||
|
||
**输出格式:严格的JSON格式:**
|
||
```json
|
||
{
|
||
"抗核抗体滴度": "...",
|
||
"补体C3": "..."
|
||
}
|
||
```
|
||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
|
||
[ 编辑Prompt ] [ 预览效果 ] [ 保存模板 ]
|
||
```
|
||
|
||
#### **2. 后端:模板管理API**
|
||
```typescript
|
||
// 新增API端点
|
||
POST /api/v1/dc/tool-b/templates // 创建自定义模板
|
||
PUT /api/v1/dc/tool-b/templates/:id // 更新模板
|
||
DELETE /api/v1/dc/tool-b/templates/:id // 删除模板
|
||
GET /api/v1/dc/tool-b/templates/:id // 获取模板详情
|
||
|
||
// Prompt自动生成服务
|
||
POST /api/v1/dc/tool-b/templates/generate-prompt
|
||
Request:
|
||
{
|
||
"diseaseType": "系统性红斑狼疮",
|
||
"reportType": "实验室检查",
|
||
"fields": [
|
||
{ "name": "抗核抗体滴度", "desc": "如 1:320, 1:640" },
|
||
{ "name": "补体C3", "desc": "单位g/L" }
|
||
]
|
||
}
|
||
|
||
Response:
|
||
{
|
||
"promptTemplate": "你是一名风湿免疫科专家...",
|
||
"estimatedTokens": 450
|
||
}
|
||
```
|
||
|
||
#### **3. AI Prompt生成逻辑**
|
||
```typescript
|
||
// 使用元Prompt(Meta-Prompt)
|
||
async generatePrompt(
|
||
diseaseType: string,
|
||
reportType: string,
|
||
fields: { name: string; desc: string }[]
|
||
): Promise<string> {
|
||
const metaPrompt = `
|
||
你是一名医学AI Prompt工程师。请为病历结构化提取任务生成专业的提示词。
|
||
|
||
任务背景:
|
||
- 疾病类型:${diseaseType}
|
||
- 报告类型:${reportType}
|
||
|
||
提取字段:
|
||
${fields.map((f, i) => `${i + 1}. ${f.name}:${f.desc}`).join('\n')}
|
||
|
||
要求:
|
||
1. 模拟该疾病领域的专家角色
|
||
2. 清晰说明每个字段的提取规则
|
||
3. 要求输出严格的JSON格式
|
||
4. 处理"未提及"的情况
|
||
|
||
请生成完整的Prompt。`;
|
||
|
||
// 调用GPT-5或Claude生成Prompt
|
||
const llm = LLMFactory.getAdapter('gpt-5');
|
||
const response = await llm.chat([
|
||
{ role: 'user', content: metaPrompt }
|
||
]);
|
||
|
||
return response.content;
|
||
}
|
||
```
|
||
|
||
**技术亮点:**
|
||
- ✨ **Prompt即代码(Prompt-as-Code)**:模板可版本控制、A/B测试
|
||
- ✨ **AI生成AI的Prompt(Meta-Prompt)**:降低用户门槛
|
||
- ✨ **模板市场(未来)**:用户可分享、下载优质模板
|
||
|
||
**预计工时:** 12小时
|
||
**影响范围:**
|
||
- 新增:`CustomTemplateService.ts` (~300行)
|
||
- 新增:`PromptGeneratorService.ts` (~200行)
|
||
- 前端:Step2Schema.tsx 新增自定义模板编辑UI
|
||
- 数据库:DCTemplate表已支持,无需改动
|
||
|
||
---
|
||
|
||
## 📊 优先级评估
|
||
|
||
| 债务ID | 问题 | 优先级 | 工时 | 影响用户 | 技术风险 |
|
||
|--------|------|--------|------|----------|----------|
|
||
| #1 | Excel导出不一致 | P1 | 2h | 高(核心功能) | 低 |
|
||
| #2 | 进度条显示优化 | P2 | 3h | 中(体验优化) | 低 |
|
||
| #3 | Excel预处理 | P1 | 16h | 高(数据质量) | 中 |
|
||
| #4 | 自定义模板 | P2 | 12h | 中(扩展性) | 中 |
|
||
|
||
**总计:** 33小时(约4个工作日)
|
||
|
||
---
|
||
|
||
## 🎯 建议处理顺序
|
||
|
||
### **Sprint 1:核心功能修复(P1优先)**
|
||
1. ✅ #1 - Excel导出修复(2小时)→ **立即处理**
|
||
2. #3 - Excel预处理(16小时)→ **分阶段实现**
|
||
- Phase 1:表头清洗(2小时)
|
||
- Phase 2:合并单元格展开(4小时)
|
||
- Phase 3:公式处理(3小时)
|
||
- Phase 4:日期统一(3小时)
|
||
- Phase 5:不可见字符清理(2小时)
|
||
- Phase 6:集成测试(2小时)
|
||
|
||
### **Sprint 2:体验优化(P2)**
|
||
1. #2 - 进度条优化(3小时)
|
||
2. #4 - 自定义模板(12小时)
|
||
- Phase 1:后端模板CRUD(4小时)
|
||
- Phase 2:Prompt自动生成(4小时)
|
||
- Phase 3:前端模板编辑器(4小时)
|
||
|
||
---
|
||
|
||
## 💡 长期优化建议
|
||
|
||
### **1. 数据质量评分系统**
|
||
为上传的Excel文件打分(0-100分):
|
||
- ✅ 90-100:优质数据,直接处理
|
||
- ⚠️ 60-89:一般质量,提示可能问题
|
||
- ❌ 0-59:低质量,强制要求用户清洗后再上传
|
||
|
||
### **2. Excel模板标准化**
|
||
提供标准Excel模板下载,用户按模板填写,减少脏数据:
|
||
```
|
||
病历结构化标准模板 v1.0.xlsx
|
||
- 表头行冻结
|
||
- 数据验证(下拉框)
|
||
- 字段说明(批注)
|
||
- 示例数据
|
||
```
|
||
|
||
### **3. 智能修复建议**
|
||
检测到问题时,AI给出修复建议:
|
||
```
|
||
⚠️ 检测到22个合并单元格,可能导致数据丢失
|
||
建议操作:
|
||
[ 自动展开合并单元格 ] [ 忽略并继续 ]
|
||
```
|
||
|
||
---
|
||
|
||
## 📝 开发记录
|
||
|
||
| 日期 | 处理内容 | 状态 | 备注 |
|
||
|------|---------|------|------|
|
||
| 2025-12-03 | 创建技术债务文档 | ✅ | 初始记录4个问题 |
|
||
| 2025-12-03 | #1 Excel导出顺序修复 | 🔄 | 已修改代码,待验证 |
|
||
| - | #2 进度条优化 | ⏸️ | 待开发 |
|
||
| - | #3 Excel预处理 | ⏸️ | 待开发 |
|
||
| - | #4 自定义模板 | ⏸️ | 待开发 |
|
||
|
||
---
|
||
|
||
## 🔗 相关文档
|
||
|
||
- [技术设计文档:工具 B](../02-技术设计/技术设计文档:工具%20B%20-%20病历结构化机器人%20(The%20AI%20Structurer).md)
|
||
- [API设计文档](../02-技术设计/API设计文档-DC模块(完整版).md)
|
||
- [开发计划](../04-开发计划/DC模块Tool-B开发计划.md)
|
||
- [云原生开发规范](../../../04-开发规范/08-云原生开发规范.md)
|
||
|
||
---
|
||
|
||
**文档维护:** 每次处理技术债务时更新此文档
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|