Files
AIclinicalresearch/docs/03-业务模块/IIT Manager Agent/00-系统设计/IIT Manager Agent V2.3:健壮性设计与最佳实践.md
HaHafeng 0c590854b5 docs(iit): Add IIT Manager Agent V2.9 development plan with multi-agent architecture
Features:
- Add V2.9 enhancements: Cron Skill, User Profiling, Feedback Loop, Multi-Intent Handling
- Create modular development plan documents (database, engines, services, memory, tasks)
- Add V2.5/V2.6/V2.8/V2.9 design documents for architecture evolution
- Add system design white papers and implementation guides

Architecture:
- Dual-Brain Architecture (SOP + ReAct engines)
- Three-layer memory system (Flow Log, Hot Memory, History Book)
- ProfilerService for personalized responses
- SchedulerService with Cron Skill support

Also includes:
- Frontend nginx config updates
- Backend test scripts for WeChat signature
- Database backup files

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-05 22:33:26 +08:00

6.2 KiB
Raw Blame History

IIT Manager Agent V2.3:健壮性设计与最佳实践

核心目标: 在极简架构下,解决 LLM 调用工具时的幻觉、参数错误和无结果问题。

适用场景: 2人团队Node.js Service Class 架构。

1. 核心风险分析LLM 会犯什么错?

在让 Agent 调用 read_clinical_data 等工具时,通常会发生以下三类错误:

错误类型 场景举例 后果
参数幻觉 LLM 调用 read_data(id="P001", fields=["gender"]),但 REDCap 里字段名叫 sex。 API 返回空或报错,用户觉得 AI 很蠢。
意图漂移 用户问:“今天天气怎么样?” LLM 试图去 REDCap 查天气。 工具调用失败,浪费 Token。
格式错误 LLM 返回的 JSON 少了一个括号,或者参数类型不对(字符串给了数字)。 程序 Crash前端报错。

2. 解决方案:三层防御体系 (The Defense Layer)

不需要 MCP Server我们在 Service Class 里加三层防御即可。

第一层:模糊映射 (The Translator)

解决: 字段名对不上的问题。

原理: 别指望 LLM 能记住 REDCap 几千个变量名。我们在代码里做一个“模糊翻译器”。

// backend/src/modules/iit-manager/services/ToolsService.ts

class ToolsService {
// 1. 定义常用字段映射字典
private fieldMapping = {
"gender": "sex",
"sex": "sex",
"性别": "sex",
"age": "age_calculated",
"年龄": "age_calculated",
"history": "medical_history_desc",
"病史": "medical_history_desc"
};

async executeTool(name: string, args: any) {
if (name === 'read_clinical_data') {
// 🛡️ 自动纠错逻辑
const correctedFields = args.fields.map(f => {
// 如果字典里有,就替换;没有就保留原样
return this.fieldMapping[f.toLowerCase()] || f;
});

  console.log(\`\[Auto-Fix\] ${args.fields} \-\> ${correctedFields}\`);  
    
  return this.redcapAdapter.exportRecords({  
    ...args,  
    fields: correctedFields  
  });  
}  

}
}

第二层:容错重试 (The Self-Correction Loop)

解决: LLM 调用失败直接报错给用户的问题。

原理: 当工具报错时,不要直接抛给用户,而是把错误信息喂回给 LLM让它重试。这是 Agent 能够“甚至比人更聪明”的关键。

// backend/src/modules/iit-manager/agents/QcAgent.ts

async function runAgentLoop(prompt: string, maxRetries = 3) {
let history = [{ role: 'user', content: prompt }];

for (let i = 0; i < maxRetries; i++) {
// 1. LLM 思考
const response = await llm.chat(history);

// 2\. 如果没有调用工具,直接返回结果  
if (\!response.hasToolCall) return response.content;  
  
// 3\. 尝试执行工具  
try {  
  const result \= await toolsService.execute(response.toolName, response.args);  
    
  // 4\. ✅ 成功:把结果喂给 LLM继续思考  
  history.push({ role: 'tool', content: JSON.stringify(result) });  
    
} catch (error) {  
  // 5\. ❌ 失败:触发自我修正机制  
  console.warn(\`\[Agent Error\] 工具调用失败: ${error.message}\`);  
    
  // 关键步骤:告诉 LLM 它错了,让它重试  
  history.push({   
    role: 'user',   
    content: \`系统报错:${error.message}。请检查你的参数(例如字段名是否存在),然后重试。\`   
  });  
  // 循环继续LLM 会在下一次迭代中修正参数  
}  

}

return "抱歉,系统尝试多次后仍然无法获取数据,请联系管理员。";
}

第三层:空结果兜底 (The Fallback)

解决: 查不到数据时体验不好的问题。

原理: 如果 REDCap 返回空,工具层要返回一段“人话”,而不是 null。

// ToolsService.ts

async execute(name: string, args: any) {
// ...
const data = await this.redcapAdapter.exportRecords(...);

if (!data || data.length === 0) {
// 🛡️ 友好的空结果返回
return {
status: "empty",
message: `未找到 ID 为 ${args.record_id} 的患者数据。请确认 ID 是否正确。`
};
}
return data;
}

3. 最佳实践:如何应对未知问题?

3.1 限制工具的“射程”

不要给 LLM 一个 sql_query 工具让它随便查库。

最佳实践:只提供 read_clinical_data(record_id, fields)。

  • 如果用户问“天气怎么样”LLM 发现没有 get_weather 工具,它自己就会回答:“抱歉,我无法查询天气,我只能查询临床数据。”(这是 LLM 的自带能力)。

3.2 System Prompt 的“紧箍咒”

在 qc_config.json 的 soft_instructions 或者全局 System Prompt 中,明确边界。

你是一个临床研究助手。
1. 你只能回答与【肺癌研究】相关的问题。
2. 如果用户问无关问题(如天气、股票),请礼貌拒绝。
3. 在调用 `read_clinical_data` 时,请务必使用准确的英文变量名。如果不确定,请先调用 `Youtube` 查看字典。

3.3 调试与监控 (Admin Portal)

你们已经有了 运营管理端。利用起来!

  • 记录每一次 Agent 的思考过程Thinking Trace
  • 如果你发现 Agent 总是把 age 拼成 agg就在 第一层防御(映射字典) 里加一行配置。
  • 这就是**“运营驱动开发”**,比改代码快得多。

4. 结论

极简方案Service Class的灵活性和健壮性完全够用。

  • MCP Server 只是把错误抛出来,它不会自动修错。
  • Service Class 允许你在本地代码里直接写 try-catch 和 Retry Loop这对 2 人团队来说调试极其友好。

行动指南:

  1. 不用担心用户乱问Prompt 会拒绝。
  2. 不用担心参数传错,写一个简单的 Mapping 字典去拦截。
  3. 不用担心报错,实现 Self-Correction Loop出错把错误扔回给 AI让 AI 自己修。

这就是目前 Agent 落地最务实、最抗造的模式。