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>
6.2 KiB
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 人团队来说调试极其友好。
行动指南:
- 不用担心用户乱问,Prompt 会拒绝。
- 不用担心参数传错,写一个简单的 Mapping 字典去拦截。
- 不用担心报错,实现 Self-Correction Loop(出错把错误扔回给 AI),让 AI 自己修。
这就是目前 Agent 落地最务实、最抗造的模式。