Harrrrrr……啊对,我一开始就是想着 Harness

QVeris · Agent工程

用 10 个 token 的冗余,保护上万元的报告

前几天,我和同事聊起刚解决的一个问题。

我说:

“客户的 Agent 幻觉我给解决了。他们的大模型调实时行情工具,根据公司名瞎编了个股票代码。 我给工具包了一层:它查什么,我都把真实的公司名+代码组合冗余放回工具结果。 10 个 token,降低 99% 幻觉。对 8 个工具同时生效。”

同事问:

"你这难道是传说中的 Harness?"

"Harrrrrr……"

我停顿了五秒,脑子里走马灯一样闪过 Prompt、Context、Tool、Guardrail、Runtime,以及我看过的所有 Harness 八股文。

然后我说:

"啊对,我一开始就是想着 Harness。"

当然,我一开始只是想修一个具体问题。

数据是真的,公司错了

用户想查证的事情需要"锐捷网络"。

模型根据公司名,编出了股票代码301367.SZ,然后调用实时行情工具。

这个代码格式完全合法,工具正常返回了真实行情,最终报告里出现:

锐捷网络|301367.SZ|+7.70%

看起来没有任何问题。

代码是真的,涨跌幅是真的,工具调用也成功了。

但是用户怒气冲冲的指责我们

我们最多也就秒级延迟啊,仔细一查,301367.SZ实际对应 瑞迈特,不是锐捷网络。

模型没有编造整份行情,它只是把两个看起来合理的信息拼错了:

上下文里的公司名:锐捷网络 工具返回的代码:301367.SZ

在金融 Agent 里,最危险的幻觉有时不是假数据,而是:

数据全是真的,但不是这家公司的。

Prompt 里的"必须",不是程序里的"必须"

我们以前的规则写得很"必须":

涉及具体公司的所有问题,必须遵循: 1. 先调用 identify_company 确认身份和股票代码; 2. 再调用对应工具获取数据。

模型可能遵守,也可能跳过。

那反正你最后都要调用工具,我在工具里强制帮你调确认身份的接口,我看你还怎么幻觉。

以前工具主要返回:

{ "ts_code": "301367.SZ", "price_change_pct": 7.7 }

现在会多一组_identity

{ "_identity": { "canonical_name": "瑞迈特", "ts_code": "301367.SZ", "verified": true }, "price_change_pct": 7.7 }

公司名优先取自同一条供应商记录,再回退到本地证券主数据或公司识别服务。

这么简单,也算 Harness?

我觉得算。

以前是:

提醒模型先核对身份 → 模型可能核对,也可能跳过 → 再调工具

没有harness,用户一句相反的指令,就可能把这条规则顶掉。

现在是:

在工具里绑模型核对身份 → 模型一定调工具 → 模看到工具返回的核对身份信息

正确性从"模型应该遵守的规则",下沉成了"工具必须提供的数据契约"。

我还故意让用户指令和 Harness 正面打了一架:名称和代码明明对不上,我却要求它严格沿用用户给出的名称,不要纠正。

Harness 没有抢用户的表达权:名称可以按要求展示,但归因冲突必须明确暴露。用户可以决定怎么称呼,不能悄悄决定数据属于谁。

而且这次不是只修实时行情。同一套归因能力被批量接入八类结构化公司工具:

·实时行情

·公司画像

·财务报表

·估值

·历史行情

·公司公告

·个股资金流

·分钟级技术指标

这些工具由多个 Agent 共享,因此不需要逐个修改 Agent。只要它们使用这批工具,就会同时获得这层保护。

这正是 Harness 的杠杆:

在共享的高价值位置增加一个小约束,让大量下游 Agent 一起变得更可靠。

10 个 token,保护上万元的报告

这里的冗余非常便宜。

完整的上市公司名称—代码映射仍然放在代码和数据层。每次只把当前证券的一组name+code放进工具结果,增加的核心上下文大约只有 10 个 token。

而这些 Agent 生成的金融报告,一篇就可能售价上万元,还可能卖给多个客户。

为了节省 10 个 token,让模型继续自行拼接公司名和代码,几乎省不下任何成本;只要错配一次,整篇报告的可信度却可能归零,错误还会随着分发继续放大。

约 10 个 token → 保护 8 类工具 → 多个 Agent 生效 → 保护上万篇上万元的报告

怎么算都划算。

我没有等这个错误随机复现,而是直接构造冲突:把"锐捷网络"和 301367.SZ 绑在一起,甚至明确要求模型不要纠正公司名。

修复前,Agent 会顺着错误名称继续输出。修复后,工具返回的canonical_name会把冲突直接暴露出来:普通请求会使用权威名称;用户强制保留原称呼时,也会明确告警,不再让错误静悄悄混进报告。

"降低 99%"不是精确的线上 A/B 结果,而是基于机制覆盖的工程估算:在这 8 类工具里,公司名和代码不再由模型自由拼接。

错误何时自然出现,是概率问题; 一旦进入这 8 类工具,归因冲突能否被发现,是确定性问题。

更硬的方案,是在最终报告输出前再做一轮结构化槽位校验。但自由文本报告需要针对不同 Agent 适配事实抽取和输出规则,成本更高。

这次我们先选择共享工具层:成本更小,覆盖更广。

写在最后

所以,我一开始当然不是想着 Harness。

我只是遇到了一个模型经常忘记的规则,然后决定不再只靠提醒,而是把规则做进它所处的环境里。

没有更换模型,也没有逐个修改 Agent,更没有设计复杂的新架构。

我只在公共 Prompt 里补了一条归因规则,再让共享工具把真实的name + code随结果一起返回。

Prompt 还是写了。但这次 Prompt 不再负责教模型怎么猜对,只负责告诉它:别和工具层返回的事实打架。

做完以后回头一看:

Harrrrrr……

啊对,这就是 Harness。

参考资料:

OpenAI:Harness engineering