Agent 给出的最终结论能直接落库吗?怎么让它可信?
简化版
不能直接落库。Agent 的最终结论是模型生成的文本,可能引用不存在的实体、给出越界的数字、漏掉关键风险信号,甚至一个工具都没调就凭记忆作答。可信的做法是「模型负责选,代码负责核」:先要求模型按固定结构输出;执行完先反查过程,确认它真的调用过工具;关键实体(科室、商品、人员)回数据库核对,能由代码确定的字段(比如具体推荐哪位医生)直接由代码按规则选;数值夹到合法区间、枚举归一;安全相关的信号从工具返回的原始数据里由代码判断,不采信模型的自述;给用户看的解释文字,也基于核验过的事实再生成。核验不通过就判失败并说明原因,而不是把不可信的结论写进业务表。
详细版
Agent 循环结束
├─ 1. 过程反查:实际调用了几次工具?0 次 → 失败
├─ 2. 安全信号:从工具返回里查危险标记 → 命中走固定的安全分支
├─ 3. 解析结构:模型输出的 JSON(实体名、分数、等级、依据)
├─ 4. 实体核验:实体名回库查,找不到 → 失败
├─ 5. 代码决定:能按规则确定的字段由代码选
├─ 6. 数值归一:分数夹到区间,等级归一到枚举
└─ 7. 生成解释:基于核验后的事实写理由,失败用代码兜底文案
落库:每个字段标明来源(模型给 / 代码核 / 代码定)
| 字段类型 | 谁给 | 代码做什么 |
|---|---|---|
| 实体(科室、商品) | 模型选 | 回库核对存在性和状态 |
| 可按规则确定的实体(医生、库存) | 代码 | 按规则直接查出来,不让模型填 |
| 数值(置信度、金额) | 模型给 | 夹到合法区间;金额由代码重算 |
| 枚举(等级、类型) | 模型给 | 归一到固定取值 |
| 安全信号(危急、违规) | 代码 | 从工具返回的原始数据里判断 |
| 解释文字 | 模型写 | 素材只给核验过的事实,失败有兜底 |
完整版教学
一、模型的结论会错在哪
即使模型每一步都调了工具、看到了真实数据,最终结论仍然可能出错,常见的有四类:
| 错误 | 例子 | 后果 |
|---|---|---|
| 编造实体 | 推荐一个医院里不存在的「胃肠外科」 | 患者按推荐去挂号,挂不上 |
| 数值越界 | 置信度写成 135 | 页面显示异常,统计口径错乱 |
| 漏判风险 | 工具已经返回「胸痛属危急征象」,结论却写「普通门诊」 | 危急病人被当成普通门诊放走 |
| 没查就答 | 一个工具都没调,凭训练记忆写了一段像模像样的结论 | 结论和系统数据毫无关系 |
这些错误的共同点是:结论读起来很合理,靠人工抽查很难发现。所以核验不能依赖「看起来对不对」,要依赖代码逐项检查。
二、先把结论变成结构
自由文本没法核验。第一步是规定输出结构,只允许模型返回一个 JSON:
{"deptName": "消化内科", "confidence": 82, "urgencyLevel": "普通", "evidence": "上腹隐痛三天伴恶心,症状标签指向消化内科"}
结构里只放模型擅长给的东西:选哪个方向、有多大把握、依据是什么。不要让模型填它不可能知道准确值的字段,比如具体排班、号源、价格,这些由代码查。结构越小,需要核验的面就越小。
三、执行完先反查过程
在看结论之前,先确认模型真的干了活:
本次运行的工具调用步骤数 = 0 → 判失败:「Agent 没有调用任何工具」
模型完全可能一个工具都不调,直接输出一段格式正确的 JSON。只看结论字段,这种情况完全发现不了。反查的依据是代码记录的步骤表,而不是模型自己说「我查过了」。这一节核验的是最终结论;每一次工具调用的返回怎么验证,见「Agent 如何验证工具调用结果?」。
记忆钩子:先问「查过没有」,再问「查得对不对」。
四、实体回库,能定的由代码定
模型给出的实体名要回数据库核对。匹配可以先精确再放宽:先按全名精确匹配,匹配不上再按包含关系匹配(模型写「消化科」也能对上「消化内科」),仍然找不到就判失败。
更进一步,凡是能按规则确定的字段,就不要让模型选。比如「推荐哪位医生」:规则是「这个科室从今天起、可挂号、余号大于 0 的排班里取第一条」,代码一条 SQL 就能查出来,而且一定挂得上;让模型选,它可能选一个当天没出诊的医生。模型只负责代码判断不了的那部分(根据症状选科室),其余的由代码决定。
五、安全信号从原始数据里判断
有些信号关系到安全,比如危急征象、违规内容。这类判断不能采信模型在结论里的自述,而要回到工具返回的原始数据里由代码检查:
遍历本次运行里「症状检索」步骤的返回 JSON
└─ 任何一条匹配结果带有危急标记 → 直接走急诊分支
科室 = 急诊科,医生为空,置信度为空,危急提示取工具返回的文案
这一步放在解析模型 JSON 之前:命中危急时,模型怎么写都不影响结果
顺序很关键:先做安全检查、再解析模型结论。如果先解析,一旦模型写了「普通门诊」而代码又忘了覆盖,风险就漏过去了。
六、数值夹紧、枚举归一
模型给的数值和枚举要落到合法范围:
confidence:135 → 100;-5 → 0;82 → 82
urgencyLevel:「紧急情况」「较紧急」→ 归一到 普通 / 紧急 / 危急 三者之一
夹紧和归一保证落库的数据能被统计:看板要按等级分组,如果库里同时有「紧急」「紧急情况」「比较紧急」,分组就乱了。
七、解释文字基于核验后的事实
给用户看的推荐理由也是模型写的,但素材要换成核验过的事实:科室的擅长病种和位置、选定医生的出诊日期和余号、分诊依据。再调一次模型把这些写成通顺的话。如果模型答非所问(反问患者、说没收到资料)或调用失败,就用代码拼一段兜底文案,保证落库的理由至少是事实的直接陈述。
这样落库的每个字段都能说清来源:哪些是模型选的、哪些是代码核验过的、哪些是代码直接定的。
八、常见误区与追问
- 误区:模型调过工具,结论就是可信的。 看过真实数据的模型仍会编造实体、漏判风险,结论要逐项核验。
- 误区:让模型把所有字段都填上最省事。 能按规则确定的字段(医生、库存、金额)交给代码,模型填的越少核验面越小。
- 误区:安全信号让模型在结论里标出来就行。 安全判断要从工具返回的原始数据里由代码检查,且放在解析结论之前。
- 误区:核验不通过就退回模型的原始输出。 不可信的结论不能落库,应判失败并写明原因,让用户可以重试。
- 误区:解释文字不重要,模型写什么存什么。 理由也要基于核验后的事实,失败时用代码兜底,否则会出现理由和结论对不上。
- 追问:实体名对不上时怎么匹配? 先精确、再包含匹配,仍找不到就判失败,不要猜一个相近的实体。
- 追问:怎么证明 Agent 真的查过数据? 数代码记录的工具调用步骤,而不是相信模型的自述。
九、加强记忆
Agent 结论落库记「先查过程、再查安全、后核实体」:先数工具调用步骤,0 次直接失败;再从工具返回的原始数据里由代码判断危急这类安全信号,放在解析结论之前;然后解析小而固定的 JSON,实体回库先精确后包含匹配,找不到就失败,能按规则确定的字段(医生、号源、金额)由代码直接定。数值夹到区间、枚举归一到固定取值,解释文字只用核验过的事实生成、失败用代码兜底。落库的每个字段都能说清是模型选的、代码核的还是代码定的。
项目实战落地
项目里怎么做的
《AI Agent 智慧医院智能导诊就诊系统》的分诊 Agent 跑完 ReAct 循环后,TriageAgentService 按固定顺序核验,核验通过才写 triage_result:
- 反查过程:按运行 ID 取步骤,数工具编码非空的步骤,一个都没有就判运行失败,提示「分诊Agent没有调用任何工具」;
- 危急拦截在前:遍历工具编码为
symptom_tag_search的步骤返回 JSON,只要匹配结果里有is_red_flag = 是,直接走急诊分支:推荐科室 = 急诊科,医生和置信度留空,危急等级 = 危急,提示文案用工具返回的;这一步先于解析模型 JSON; - 模型只给四样:普通路径下模型只允许输出科室名、置信度、危急等级和依据;
- 科室核验、医生由代码定:科室名用和科室查询工具同款的 SQL 回库,先精确匹配再退化为包含匹配;医生不由模型决定,代码从该科室今天起可挂号且余号大于 0 的排班里取第一条;
- 数值与枚举:置信度夹到 0–100,危急等级归一到普通、紧急、危急;
- 推荐理由:用推荐理由模板再让模型写一遍,素材全部是核验过的事实(科室擅长和位置、医生出诊日期时段余号、分诊依据);模型答非所问或调用失败时,用代码拼的兜底文案。
triage_result 的每个字段都对应「谁填」:科室和医生是代码核验或选定后填的,置信度和危急等级是模型给、代码规整的,是否危急是代码从工具返回反查的。
《AI 多Agent智能相亲交友匹配平台》收尾时不采信模型说的数:
- 交付人数现数。 主管收尾时写「已经有 8 个人通过了」不作数,代码从候选池里数审核结论为「通过」的行,交付人数取它和要求人数中较小的那个;一个都没有就判这次匹配失败,不在页面上显示一个空的「匹配成功」。审核官被停用时,按候选池里的全部候选交付,并在结论里写明这批人没有经过反向审核。
- 每一轮都从库里数。 筛选官每一轮新增了几人,按任务和轮次从候选池里数;这一轮放宽了什么,取写候选池那次工具调用带的放宽说明,不采信模型结论里另报的那一份;审核官每一批审了几位、通过几位也从库里数;模型报的审核人数和库里实际回填的条数对不上时,把这件事写进这一批的结论。
- 判通过的再核一遍。 审核官判「通过」的候选,代码拿候选方标为必须、而且确实有约束的条件,逐项核对申请方的档案;核不过的,把候选和维度写进这一批的结论。审核状态本身不改。
为什么这样取舍
- 核验是底线:模型生成是概率性的,可能编造不存在的科室、推荐一个当天没出诊的医生、把危急病人当成普通门诊放走;这些错了患者就白跑一趟甚至延误病情,必须由代码兜底。
- 医生由代码定:推荐的医生必须当天真的有号,模型只说科室,医生是谁由代码按排班余号决定。
面试官还会追问
- 分诊结果为什么要有「已挂号」这个状态?它还支撑了哪个指标?
- 两个患者同时挂同一个排班的最后一个号,余号会被扣成负数吗?怎么保证?
- 发起分诊时交给 Agent 的输入由哪几部分组成?会话里 AI 的追问算不算主诉?
学完《AI Agent 智慧医院智能导诊就诊系统》,上面这些追问你都会迎刃而解。