# 字段映射问题修复报告

**日期**: 2025-11-21  
**问题**: 真实LLM筛选失败（成功：0/20）  
**原因**: 字段名不匹配  
**状态**: ✅ 已修复  

---

## 🔍 问题诊断

### 症状
```
任务状态: completed
进度: 20/20
成功: 0 ❌
筛选结果数: 0
```

**表现**：
- 任务瞬间完成（1秒）
- 所有文献处理失败
- 没有保存任何筛选结果

---

## 🎯 根本原因

### 问题1: PICOS字段名不匹配

**前端/数据库格式** (`TitleScreeningSettings.tsx`):
```typescript
picoCriteria: {
  P: '2型糖尿病患者...',
  I: 'SGLT2抑制剂...',
  C: '安慰剂或常规治疗...',
  O: '心血管结局...',
  S: 'RCT'
}
```

**LLM服务期望格式** (`llmScreeningService.ts`):
```typescript
// 实际上支持两种格式，但优先使用短格式
picoCriteria: {
  P: '...',  // ✅
  I: '...',  // ✅
  C: '...',  // ✅
  O: '...',  // ✅
  S: '...'   // ✅
}
```

**诊断**：前端使用 P/I/C/O/S 格式，但 `screeningService.ts` 直接传递了数据库的原始格式，未做映射。

---

### 问题2: 模型名格式不匹配

**前端格式** (`TitleScreeningSettings.tsx`):
```typescript
models: ['DeepSeek-V3', 'Qwen-Max']
```

**LLM服务期望格式** (`llmScreeningService.ts`):
```typescript
models: ['deepseek-chat', 'qwen-max']
```

**原因**：前端使用展示名称，后端需要API名称。

---

### 问题3: 缺少字段验证

文献可能缺少 `title` 或 `abstract`，导致LLM调用失败。

---

## ✅ 修复方案

### 修复1: 添加PICOS字段映射

**文件**: `backend/src/modules/asl/services/screeningService.ts`

```typescript
// 🔧 修复：字段名映射（数据库格式 → LLM服务格式）
const rawPicoCriteria = project.picoCriteria as any;
const picoCriteria = {
  P: rawPicoCriteria?.P || rawPicoCriteria?.population || '',
  I: rawPicoCriteria?.I || rawPicoCriteria?.intervention || '',
  C: rawPicoCriteria?.C || rawPicoCriteria?.comparison || '',
  O: rawPicoCriteria?.O || rawPicoCriteria?.outcome || '',
  S: rawPicoCriteria?.S || rawPicoCriteria?.studyDesign || '',
};
```

**优势**：
- ✅ 兼容两种格式（P/I/C/O/S 或 population/intervention/...）
- ✅ 防御性编程，避免undefined

---

### 修复2: 添加模型名映射

```typescript
// 🔧 修复：模型名映射（前端格式 → API格式）
const MODEL_NAME_MAP: Record<string, string> = {
  'DeepSeek-V3': 'deepseek-chat',
  'Qwen-Max': 'qwen-max',
  'GPT-4o': 'gpt-4o',
  'Claude-4.5': 'claude-sonnet-4.5',
  'deepseek-chat': 'deepseek-chat',  // 兼容直接使用API名
  'qwen-max': 'qwen-max',
  // ... 更多映射
};

const rawModels = screeningConfig?.models || ['deepseek-chat', 'qwen-max'];
const models = rawModels.map((m: string) => MODEL_NAME_MAP[m] || m);
```

**映射表**：
| 前端展示名 | API名称 |
|-----------|---------|
| DeepSeek-V3 | deepseek-chat |
| Qwen-Max | qwen-max |
| GPT-4o | gpt-4o |
| Claude-4.5 | claude-sonnet-4.5 |

---

### 修复3: 添加文献验证

```typescript
// 🔧 验证：必须有标题和摘要
if (!literature.title || !literature.abstract) {
  logger.warn('Skipping literature without title or abstract', {
    literatureId: literature.id,
    hasTitle: !!literature.title,
    hasAbstract: !!literature.abstract,
  });
  console.log(`⚠️  跳过文献 ${processedCount + 1}: 缺少标题或摘要`);
  processedCount++;
  continue;
}
```

---

### 修复4: 增强调试日志

```typescript
console.log('\n🚀 开始真实LLM筛选:');
console.log('  任务ID:', taskId);
console.log('  项目ID:', projectId);
console.log('  文献数:', literatures.length);
console.log('  模型（映射后）:', models);  // ⭐ 显示映射后的值
console.log('  PICOS-P:', picoCriteria.P?.substring(0, 50) || '(空)');
console.log('  PICOS-I:', picoCriteria.I?.substring(0, 50) || '(空)');
console.log('  PICOS-C:', picoCriteria.C?.substring(0, 50) || '(空)');
console.log('  纳入标准:', inclusionCriteria?.substring(0, 50) || '(空)');
console.log('  排除标准:', exclusionCriteria?.substring(0, 50) || '(空)');
```

---

## 🧪 测试步骤

### 1. 重启后端（必须！）

```bash
# 停止当前后端（Ctrl+C）
cd D:\MyCursor\AIclinicalresearch\backend
npm run dev
```

### 2. 测试（小规模）

1. 访问前端
2. 填写PICOS
3. **上传5篇文献**（先测试小规模）
4. 点击"开始AI初筛"

### 3. 查看后端控制台

**应该看到**：
```
🚀 开始真实LLM筛选:
  任务ID: xxx
  文献数: 5
  模型（映射后）: [ 'deepseek-chat', 'qwen-max' ]
  PICOS-P: 2型糖尿病患者...
  PICOS-I: SGLT2抑制剂...
  PICOS-C: 安慰剂...
  纳入标准: 成人2型糖尿病...
  排除标准: 综述、系统评价...

[等待10-20秒]

✅ 文献 1/5 处理成功
  DS: include / Qwen: include
  冲突: 否

[等待10-20秒]

✅ 文献 2/5 处理成功
  DS: exclude / Qwen: exclude
  冲突: 否
...
```

---

## 📊 预期效果

### 修复前
- ⏱️ 1秒完成20篇
- ❌ 成功：0
- ❌ 筛选结果数：0

### 修复后
- ⏱️ 50-100秒完成5篇（每篇10-20秒）
- ✅ 成功：5
- ✅ 筛选结果数：5
- ✅ 证据包含真实的AI分析
- ✅ 证据不包含"模拟证据"

---

## 🔧 修改文件

- ✅ `backend/src/modules/asl/services/screeningService.ts`
  - 添加PICOS字段映射
  - 添加模型名映射
  - 添加文献验证
  - 增强调试日志

---

## 💡 经验教训

### 1. 前后端数据格式一致性
- 前端使用的展示格式 ≠ 后端API格式
- 需要在集成层做映射

### 2. 防御性编程
- 使用 `||` 提供默认值
- 验证必需字段
- 兼容多种格式

### 3. 调试日志的重要性
- 显示映射后的值（不是原始值）
- 输出所有关键参数
- 帮助快速定位问题

---

## 🎯 后续优化

### 短期
1. ✅ 字段映射（已完成）
2. ✅ 模型名映射（已完成）
3. ✅ 验证必需字段（已完成）

### 中期
1. 统一前后端数据格式（使用 TypeScript 接口）
2. 添加数据格式验证中间件
3. 改进错误提示

### 长期
1. 使用 tRPC 或 GraphQL 确保类型安全
2. 自动化测试覆盖
3. Schema验证

---

**报告人**: AI Assistant  
**日期**: 2025-11-21  
**版本**: v1.0.0


