Agent 效果差,先别怪模型:一份从 Harness 到引擎的排障顺序

0 阅读1分钟

Agent 效果差,先别怪模型:一份从 Harness 到引擎的排障顺序

Agent 效果变差时,大多数人的第一反应是"模型不行,换一个"。

但在排查顺序里,模型应该排在最后:先查 Harness,再查引擎配置,最后才怀疑权重。

这个顺序不是拍脑袋定的,是从三层的职责推出来的——模型能力由权重给,权重能不能被发挥,环境有否决权。

一、能力由权重给,发挥被环境卡

同一份权重,放进不同的环境,表现可以差很多。

这不是新结论:模型不动,只改系统提示、上下文注入和验证钩子,表现就能差出一截。模型没变,架子变了。

所以"效果差"要先分成两种:一种是能力不够,一种是能力没用上。前者才需要换模型,后者换模型只是把问题换了个地方藏。

换模型还有一个隐性代价:变量变多了。

原来的问题还没定位,你就同时改了权重、改了对话模板、改了工具解析的行为。哪怕新模型表现变好了,你也不知道是哪一项起了作用;如果变差了,你更不知道从哪查起。

补一句成本账:换模型要重新跑一遍评测集,重新调提示,重新做工具调用(tool call)格式的兼容。这三件事加起来,通常比查一遍配置贵得多。

有一条判断可以先立住:如果同一个模型在干净环境里能过这条任务,问题就不在模型。

干净环境指的是:最小上下文、明确的工具描述、没有历史包袱、权限全开的一次单轮请求。这一步能过,后面要查的都是环境。

Agent 效果不对时的排查漏斗

图 1 排查顺序:先 Harness,再引擎配置,最后才怀疑模型。同一层不通过,就停在该层修。

二、第一层:提示、上下文、工具定义、权限

Harness 是模型外面那一整圈环境——主循环、工具层、上下文管理、记忆、权限与沙箱、验证与可观测。这一层出问题,表现和内层几乎一样:答得不对。

四个高频项按顺序看。

系统提示有没有被改过。 现象通常是行为风格突然变了,或者原本遵守的输出格式开始不守。验证方法很土但有效:把当前提示和上一次表现正常的版本做 diff,拿旧提示跑同一批任务做回归。很多"模型变笨"最后查出来是一次顺手改的提示没进版本库。

上下文是不是被塞爆了。 现象是长会话跑到后面开始忘事,前面说过的约束不再遵守,或者答非所问。验证办法是看每轮实际注入的 token 用量,以及有没有触发截断或压缩。上下文工程做得再好,注入顺序一变、旧约束被压到很后面,效果也会掉。

工具描述是不是含糊。 现象是参数填错位置、该调的工具没调、调了个语义相近但不对的工具。验证办法是拿单个工具做定向测试:只给它一个工具,描述改成两版,看调用成功率差多少。工具描述越含糊,模型越是在猜。

权限有没有把该放行的操作拦住。 这一项最容易被误判成模型能力问题。现象是模型说要执行、但什么也没发生,或者同一操作反复重试。验证办法是看宿主侧的拒绝记录——拒绝发生在宿主,不在模型,模型通常不知道自己的动作被挡了。

合成案例一(非特定项目,数字示意)

现象:一个处理售后的 Agent 上线两周后开始跳过订单核验,直接发起退款,偶尔还会把两个订单号混在一起。团队第一反应是模型退化了。

排查:先按 Harness 四项过一遍。提示做 diff,发现两周前有人为了"回答更简洁"删掉了"先核验再操作"的约束;工具描述没动;上下文没有触发截断;权限规则也没变。

定位:问题在提示和校验钩子两处。约束被删之后,模型不再把核验当成必经步骤;同时校验钩子被挪到了最终回复前,等于错误已经发生才被检查。

修复前后:把约束写回提示,把钩子移回工具执行前。同一批回放任务里(200 条,示意),未核验就退款的比例从 12% 降到 1% 以下。

这一层还有一个结构性难点:四类问题的表现彼此重合。

所以不能从表现反推层,只能按顺序排除。你先假定是 Harness,把四项过一遍;四项都干净,再往下走。跳步的代价是把两层的问题混在一起,最后得出"模型不行"这个错误结论。

三、第二层:量化、硬上限、缓存、解析器

Harness 干净之后,问题常常还在模型外面——在推理引擎的配置里。

这一层的特殊性在于:它不在你的提示词控制范围内,但它对结果有否决权。

量化精度。 同一份权重,Q4 和 FP16 在长链推理上的差距有时很明显,本地跑小模型时尤其明显。受影响最大的是需要精确格式输出和长步骤推理的任务:格式错一位、步骤漏一环,链路就断了。要注意分寸:这是"有时明显",不是"一定崩",具体差异要用自己的任务集测。

上下文硬上限。 引擎侧有一个 max_model_len 之类的硬闸。它先于 Harness 的上下文工程生效:你在上层做了压缩、做了裁剪、做了分层记忆,只要总长度还是超过这个闸,多余部分照样被截掉。

现象很典型:明明做了上下文管理,模型还是"没看到"某段内容。这时候要看的是引擎实际收到的长度,而不是你拼好的长度。

tool parser 的差异。 同一个模型换一个引擎,工具调用的解析成功率可能变。模型输出的文本形式差不多,但不同引擎对边界情况(多余字段、缺字段、多个调用并列、带解释文字)的处理不一样。

现象是:同一段输出,这个引擎能解析成结构化调用,那个引擎解析成空,然后宿主拿到空调用,模型被迫用自然语言硬答。

prefix caching 有没有命中。 它影响成本和延迟,也影响一致性。现象是两边的:命中时多轮对话明显变便宜;不命中时,改前面一句会让后面的行为整体漂移。

合成案例二(非特定项目,数字示意)

现象:同一套 Harness、同一份权重,迁到另一台机器后工具调用成功率明显下降,长任务跑到后半段开始重复调用同一个工具。

排查:Harness 四项都干净。转查引擎,发现部署时把精度换成了 Q4 量化档、max_model_len 被下调、推理后端也换了,新后端的 tool parser 对带解释文字的输出解析失败。

定位:两件事叠加。上下文硬上限先于 Harness 的裁剪生效,长任务在中途被截断,模型丢了之前的步骤记录,于是重复调用;解析器返回空的 tool_call,宿主把空结果当成自然语言继续走。

修复前后:精度调回 FP16 档、max_model_len 与上下文预算对齐、换回兼容的解析器。同一批评测里(数字示意),工具调用成功率从 71% 回到 94%,重复调用从平均 3 次降到 1 次以内。

这一层判断归属,可以用一条标准:这件事需不需要知道"任务是什么"?

需要知道任务的,归 Harness。比如这轮该不该压缩历史、这段结果要不要回灌。只需要知道模型结构的,归引擎。比如 KV cache 怎么分页、一次前向怎么批处理。这条标准比"它看起来像不像外部服务"可靠得多。

四、第三层:什么时候才该换模型

只有前两层都排除之后,才轮到模型。

而且到这一步,还要再分一次:是能力不足,还是能力没用对。

能力不足的判定标准是:同一任务,在干净环境、最小上下文、明确工具描述下,仍然稳定不达标。注意"稳定"两个字——偶尔错一次可能是采样或解析问题,稳定错才是能力问题。

能力没用对的判定标准是:换一种问法、换一种工具描述、换一种步骤拆法就能过,但换个模型也能过。这种情况换模型属于运气,不是定位。

真到要换的时候,把账算全:评测集要不要重跑、提示要不要重调、工具调用格式要不要重新适配、上下文长度要不要重新评估。这四项里任何一项没做,你都无法确认新模型真的更好。

还有一个容易忽略的点:不同模型对同一份 Harness 的敏感度不同。

一个在厚 Harness 下表现很好的模型,换到一个薄 Harness 里可能掉得厉害;反过来也一样。所以"换模型"实际上换的是"模型 + Harness"这组搭配,不是单独换一个零件。

五、按顺序打勾,不要跳步

排查的价值在顺序,不在清单长度。

  • 先确认现象可复现:同一输入、同一配置,能稳定重现
  • 系统提示与上次正常版本做 diff
  • 检查本轮实际注入的 token 用量与截断/压缩记录
  • 拿单个工具做定向测试,对比两版工具描述
  • 查宿主侧的权限拒绝与执行失败记录
  • 查引擎实际收到的长度,对照 max_model_len
  • 换一次引擎配置或解析器,看工具调用成功率
  • 最后才在干净环境里单独评测模型本身

清单只有八行,但每一行都要能回答"通过还是没通过"。答不出来的,说明这一项还没查。

结尾

排障的核心不是"哪个模型更强",而是"这次到底是哪一层没让它发挥"。

顺序的价值在于:每排除一层,你就少一个变量;等排到模型那一层,剩下的问题才是真的属于它。

层搞混是排查里最常见的弯路:先分清哪一层,再决定查什么。