首页 / 资讯中心 / 文章详情

Agent落地成败一半在Harness:拆解执行循环、工具与上下文工程

Agent落地成败一半在Harness:拆解执行循环、工具与上下文工程 ★ FEATURED ARTICLE
上个月有个做代码助手的同行问我一个问题他们把底层模型换成了当时最新的一版任务成功率只涨了三个点后来花两周把外面那层执行循环重写了一遍模型一个字没换成功率涨了二十多个点。他问这正常吗。我说太正常了做过智能体落地的人基本都有类似体感——决定一个 Agent 能不能真正干活的模型权重只占一半另一半在你套在模型外面的那层壳上。这层壳现在圈内习惯叫它Harness。Harness 这个词原本是马具、挽具的意思套在马身上把马的力气导到车上。放到 AI 语境里非常贴切模型是那匹马力气很大但方向不定Harness 是挽具、缰绳、车辕和刹车负责把模型的输出导成一步步可执行、可验证、可回滚的动作。它管的不是模型怎么想而是模型想完之后谁去执行、执行结果怎么喂回去、什么时候该停、出错了怎么办。这篇文章就是把这层壳彻底拆开它由哪几个部件组成、为什么各家做法最后都长得差不多、怎么从零搭一个能跑的最小版本、以及上生产前那些不踩一遍就学不会的坑。适合已经调用过模型接口、想把 demo 变成能交付的东西的读者也适合只写过 prompt、还没接触过执行框架的同学——文中会补足必要的基础概念。1. 把模型从聊天框里拽出来Harness 补的到底是哪块空缺1.1 一个反直觉的事实换外壳的收益常常大于换模型大部分人评估一个 AI 产品的能力时第一反应是看模型排行榜。但排行榜上的分数测的从来不是裸模型而是某个团队写的 Harness 那个模型。同一份模型权重配一个只会把用户输入原样转发、把回复原样返回的极简外壳和配一个带工具调用、带结果校验、带失败重试的完整外壳在同一个任务集上的完成率能差出二十到四十个百分点。这个差距不是玄学来源非常具体。举个具体的例子。让模型修一个代码仓库里的 bug。极简外壳下模型只能输出一段我认为应该这样改的文字改没改对没人知道用户得自己复制粘贴、自己跑测试。带 Harness 的版本流程是这样的Harness 先把仓库结构和相关文件内容装配进上下文模型输出一个读取某文件的动作Harness 真的去读把内容回灌模型输出改写某段Harness 落盘接着 Harness 主动跑一遍测试命令把测试输出包括失败堆栈回灌给模型模型看到自己改错了再改一次直到测试通过或者步数用尽。前者是给建议后者是交结果。这中间的差别全部由外壳提供跟模型有多聪明关系不大。一个判断标准如果你的系统在模型输出之后就没有任何代码逻辑了那你做的不是智能体是一个带界面的文本框。Harness 的存在感正比于模型输出之后你还有多少工程代码在跑。这也解释了一个常见困惑为什么有些团队把模型换得更强效果反而没提升甚至下降。因为旧 Harness 是围绕老模型的脾气调出来的——老模型喜欢啰嗦所以裁剪得狠老模型不擅长并行工具调用所以一次只给一个工具。新模型风格变了旧外壳的假设全部失效能力被外壳卡住了。壳和模型是一对得一起调。1.2 Harness、Prompt、通用框架、Agent四个容易搅在一起的概念这四个词经常被混用但它们指的东西不一样分清楚了后面看架构才不会乱。Prompt是最小单元一段文字指令。它通常是 Harness 的一部分——Harness 负责在每一步动态拼装 system prompt系统规则、可用工具清单、当前状态摘要而不是让用户手写一段固定的。通用框架是脚手架原料提供工具调用的抽象、消息结构、模型适配层。它解决的是怎么写起来省事不解决这个任务该怎么收敛。用同一个框架两个人能搭出天差地别的 Harness。Harness是为特定任务定制的执行体包含主循环、工具集、上下文策略、终止条件、验证逻辑。它是我这个任务专属的作业流程。Agent是对外呈现的产品形态用户感知到的是一个会干活的助手。Harness 是它内部的骨架。一句话概括框架给你螺丝刀Harness 是你用螺丝刀装出来的那台机器Agent 是用户看到的那个能用的产品。理解这个分层很重要因为它决定了你优化时的着力点。效果不好时新手的第一反应是改 prompt第二反应是换模型有经验的人会先看 trace判断问题出在上下文装配、工具设计、还是终止条件上——这三样都在 Harness 层。1.3 什么任务值得上 Harness什么任务纯属给自己找麻烦不是所有 AI 需求都值得套一层 Harness。判断依据是任务是否具备可验证的中间状态。值得上的典型特征任务需要多步、需要跟外部系统交互读写文件、查数据库、调接口、有客观的成功判据测试通过、数据对得上、格式校验通过、单次尝试成功率明显不够比如只有五成。这类任务上 Harness收益是复利式的。不值得上的典型特征一次性生成、结果好坏靠人主观判断写文案、起标题、步骤极短。这类任务套 Harness 只是把一次调用变成三次调用成本和延迟翻倍成功率提升有限。我见过有团队给生成商品短描述套了七步循环加自我批判结果是把一句三十字的话磨了四十秒还经常越改越差。Harness 不是万能增益它是用额外的 token 和延迟换可控性和成功率当任务本身不需要可控性时这笔交易就是亏的。2. 拆开外壳看内脏一个 Harness 的六个必备部件2.1 主循环那个 while 比你想象的难写所有 Harness 的心脏都是一个循环装配上下文 → 调模型 → 解析出动作 → 执行动作 → 把结果追加回上下文 → 判断是否结束 → 回到开头。逻辑上五行代码工程上有五处容易出事。第一处是动作解析。模型返回的可能是结构化工具调用也可能是一段自然语言里夹着动作描述还可能是格式错误的 JSON。解析器必须能容错格式错了不要直接抛异常终止而是把你的输出格式不对正确格式是这样的回灌给模型让它自己修。经验上格式纠错重试一次能救回八成的解析失败。第二处是终止条件。至少要有四重保险模型主动声明完成、达到最大步数、连续 N 步没有产生新的有效动作、总 token 预算耗尽。少了任何一个你都会在某个深夜收到一张离谱的账单。我建议把最大步数设成正常任务所需步数的 2.5 倍既留了纠错余地又不会无限放飞。第三处是并行与顺序。模型一次可能吐出多个互不依赖的动作。并行执行能省时间但如果两个动作有副作用比如都写同一个文件并行就是事故。稳妥做法是只对明确标记为只读的工具开并行写操作一律串行。第四处是中断恢复。用户可能中途插话进程可能被重启。循环的每一步状态都要能落盘恢复时从最后一个检查点继续而不是从头再来一遍——从头再来既贵又可能重复执行副作用。第五处是流式输出与工具调用的冲突。用户想看到模型正在思考的实时文字但工具调用需要完整解析。常见解法是把思考过程和工具参数分开流式文字边出边显示工具参数攒齐了再执行。2.2 工具层JSON Schema 只是入场券描述才是胜负手新手以为工具就是写个函数 生成一份参数 schema。真正决定工具好不好用的是描述文本和错误信息。工具描述要回答三个问题这个工具干什么、什么时候该用它、什么时候不该用它。第三条最容易被忽略也最有用。比如一个读取文件的工具描述里写明如果只是想确认文件是否存在请用 stat 工具不要用本工具读取全文能省掉大量无谓的上下文占用。工具名也要讲究动词开头、语义具体search_docs 比 query 好run_tests 比 execute 好。工具的数量需要控制。经验值是同时暴露给模型的工具别超过十几个超过之后模型的选错率明显上升。工具多了就分组核心工具常驻其余工具通过一个列出更多工具的元工具按需加载。错误信息的设计同样关键。工具失败时不要返回一个裸的异常堆栈也不要返回空。要返回三样东西发生了什么用自然语言、可能的原因、建议的下一步。对比一下# 差的错误信息 return {error: KeyError: user_id} # 好的错误信息 return { error: 查询订单失败请求参数里缺少 user_id 字段。, hint: 订单查询需要 user_id 或 order_no 其中之一。如果你只有订单号请改用 query_by_order_no 工具。 }后一种写法模型大概率下一步就能自己纠正前一种模型会开始瞎猜参数浪费三到五步。这一条改动成本极低收益极高。还有一点工具必须幂等或者可判别。写文件、发消息、扣库存这类有副作用的操作Harness 层要记录这个动作是否已执行过避免模型重试时造成重复副作用。2.3 上下文装配与裁剪真正决定成败的地方如果只能优化 Harness 的一个部件我会选上下文装配。因为模型每一步看到的世界完全由这里决定。上下文通常分四层重要性从高到低系统规则层身份、硬约束、输出格式、任务层当前目标、进度、已完成事项、工具结果层最近若干步的观察、历史对话层更早的交互。裁剪的原则不是从旧到新切而是按预算分层分配系统规则永远完整保留任务层动态更新摘要工具结果层做单条截断历史层做滚动摘要。单条工具结果的截断尤其重要。一次文件读取可能返回几万字一次命令执行可能刷屏几千行日志。直接塞进去两三轮就把上下文撑爆。常见做法是设一个单条上限比如两千 token超了就保留头尾、中间用...省略 N 行...代替同时把完整内容存到外部给模型一个用 offset 继续读的工具。这样既不丢信息又不炸上下文。工具结果的格式也值得抠。JSON 比自由文本好解析但对模型来说冗余的括号和引号非常费 token。常见折中是结构化数据用紧凑格式人读的说明用自然语言两者拼接时用清晰的分隔符。一个容易被忽略的细节上下文里的顺序会影响模型注意力。把最关键的约束放在上下文的最后紧贴模型开始生成的位置比放在最前面有效得多。这叫近因偏好几乎所有模型都有这个倾向。还有一个高阶技巧叫约束重注入。长任务跑到十几步之后模型会逐渐忘记系统规则里的硬约束比如不要修改配置文件。解决办法不是加大 prompt 权重而是每隔若干步把核心约束重新插一遍。成本很低效果立竿见影。2.4 状态、检查点与恢复别把记忆托付给模型的上下文新手最常见的架构错误是把任务进度存在对话历史里指望模型自己记住做到哪一步了。这在五步以内的任务上勉强能用十步以上必然崩。正确做法是状态外置Harness 自己维护一份结构化状态——当前任务、已完成子目标、待办列表、已产生的文件变更、已调用过的工具及结果摘要。每一步把这份状态序列化落到本地存储同时把其中一小段渲染成文字塞进上下文。模型看到的只是状态的一个视图真相在 Harness 手里。这么做有三个直接好处。一是可恢复进程挂了从最后一个检查点读回来即可。二是可审计出问题时能精确回放第几步、看到了什么、做了什么决定。三是可压缩状态是结构化的压缩成摘要比压缩自由文本对话容易得多。检查点的粒度也要想清楚。每一步都存全量快照存储会爆炸存得太粗恢复时丢的步数太多。折中方案是增量存每步存这一步新增的消息 状态变更定期做一次全量快照。2.5 权限、沙箱与副作用隔离出事之后才想起这块就晚了这块在 demo 阶段完全感觉不到存在在上生产的第一周就会给你上课。沙箱要解决四件事文件系统边界只能读写指定目录、网络出口默认禁止白名单放行、资源上限CPU 时间、内存、单次执行超时、危险动作确认删除、覆盖、对外发送这类操作走人工确认或双阶段执行。我倾向的设计是默认拒绝 显式授权工具默认没有任何权限注册时逐项声明它需要什么。这样即使模型被诱导去执行危险动作也会在权限层被拦下来返回一个明确的该操作未获授权错误模型会转而寻找其他路径。另一个常被忽略的是副作用隔离。凡是会改动外部世界的操作都应该先在一个可回滚的环境里试跑确认无误再落到真实环境。代码类任务好办——在临时副本里改、跑测试、通过了再合并。业务类任务麻烦些常见做法是预演模式工具返回如果执行会发生什么的模拟结果模型确认后再真正执行。2.6 观测与回放没有 trace你的优化全是猜的一个没有 trace 的 Harness调优方式是改一改跑一跑感觉好像好点了。这种方式在小任务上还能凑合任务一复杂就完全失效——你根本不知道成功率是从哪一步丢掉的。要记录的东西按重要性排序每一步的完整输入上下文可以只存哈希 增量、模型原始输出的完整文本、解析出的动作、工具的真实输入输出、耗时、token 消耗、以及这一步的判定结果成功/失败/重试。有了这些你才能回答这次失败是因为上下文没给到、还是工具选错了、还是工具执行本身失败了。trace 的另一个用途是构造回归集。把线上真实的失败案例脱敏后存下来每次改 Harness 都跑一遍。这套东西建立起来之后迭代速度会快一个量级——从改完心里没底变成改完跑一遍涨了跌了一眼看到。3. 为什么各家最后都长成一个样架构收敛背后的四股力量3.1 模型的能力边界直接决定了循环必须存在如果模型能一次性把复杂任务做对那就不需要循环。现实是它做不到而且失败模式很规律多步任务里步数越多累积错误率越高。假设单步正确率 95%十步串起来就只剩六成。循环的价值不在于让模型更聪明而在于给每一步一个纠错机会——做完一步立刻验证错了马上回退重来把误差控制在一步之内而不是让它滚雪球。这也是为什么几乎所有 Harness 都有验证环节而且验证往往是外部的跑测试、跑编译、跑 schema 校验、跑对账。让模型自己检查自己效果远不如让一个确定性的程序去检查。3.2 成本与延迟把多智能体按回了地面前两年多智能体协作很热一个任务拆给五六个角色互相讨论。真跑起来问题很明显token 消耗是单体的三到五倍延迟翻几倍而且角色之间的讨论经常退化成互相附和质量并没有提升。现在主流的做法务实多了默认单循环只在明确需要隔离的场景下拆。什么场景需要拆上下文差异极大的一个角色只看代码一个只看日志、需要对抗性检查的一个写一个审、需要并行探索的同时试三条路选最好的。除此之外单循环加好工具性价比高得多。3.3 三种典型形态对应三类任务不同团队的做法看起来相似细看其实分三派各有适用场景。形态结构优点代价适合自由循环一个循环模型自主决定下一步灵活能处理没预设过的路径不可预测容易跑偏成本浮动大探索类、调试类任务编排图预设节点和边模型只在节点内决策可控、可测、成本稳定僵硬遇到图外情况就卡住业务流程固定、合规要求高多角色协作多个带独立上下文的执行体上下文隔离干净可并行通信开销大协调逻辑复杂需要多视角审查、超长任务我个人的倾向是编排图打底 局部自由循环主干流程用图固定死每个节点内部给模型一定自由度。这样既保证了关键路径可控又不至于遇到意外就抓瞎。3.4 收敛的深层原因失败模式是客观的解法自然趋同各家做法趋同不是因为互相抄而是因为面对的是同一批客观失败模式。模型会幻觉出不存在的工具、会编造参数、会重复同一个无效动作、会在长上下文里忘记约束、会自信地宣布完成但实际没做完。这五类问题不管哪家做都得用同一套办法治工具白名单、参数校验、动作去重、约束重注入、外部验证。工程约束摆在那里最优解的空间本来就不大。4. 从零撸一个能跑的最小 Harness4.1 骨架一百多行能跑通的最小闭环先给一个能跑的最小版本不依赖任何框架。核心是消息列表、工具注册表、和一个带熔断的循环。import json, time, hashlib class MiniHarness: def __init__(self, llm, tools, max_steps20, max_tokens200000): self.llm llm self.tools {t.name: t for t in tools} self.max_steps max_steps self.max_tokens max_tokens self.messages [] # 对话与工具结果 self.state {done: False, notes: []} self.seen_actions {} # 动作指纹 - 次数用于去重 self.tokens_used 0 def build_context(self): 分层装配系统规则 状态摘要 最近消息带预算裁剪 sys_prompt self.render_system_prompt() state_text self.render_state() recent self.trim_messages(self.messages, budget60000) return [ {role: system, content: sys_prompt}, {role: system, content: 当前进度\n state_text}, *recent, ] def step(self): ctx self.build_context() resp self.llm.chat(ctx, tools[t.schema for t in self.tools.values()]) self.tokens_used resp.usage.total_tokens self.messages.append({role: assistant, content: resp.text, tool_calls: resp.tool_calls}) if not resp.tool_calls: return self.judge_finish(resp.text) for call in resp.tool_calls: fp hashlib.md5(f{call.name}:{json.dumps(call.args, sort_keysTrue)}.encode()).hexdigest() self.seen_actions[fp] self.seen_actions.get(fp, 0) 1 if self.seen_actions[fp] 2: result {error: 这个动作你已经重复执行过结果不会变。请换一种思路或换一个工具。} else: result self.run_tool(call) self.messages.append({ role: tool, tool_call_id: call.id, content: self.truncate(json.dumps(result, ensure_asciiFalse), 4000) }) return False def run_tool(self, call): tool self.tools.get(call.name) if tool is None: avail , .join(self.tools.keys()) return {error: f工具 {call.name} 不存在。可用工具{avail}} try: return tool(**call.args) except TypeError as e: return {error: f参数不匹配{e}, hint: f该工具的参数定义是 {tool.schema}} except Exception as e: return {error: f执行失败{type(e).__name__}: {e}} def run(self, task): self.messages.append({role: user, content: task}) for i in range(self.max_steps): if self.tokens_used self.max_tokens: return {status: budget_exhausted, steps: i} if self.step(): return {status: done, steps: i 1} return {status: max_steps, steps: self.max_steps}这段代码省略了render_system_prompt、trim_messages、judge_finish、truncate的实现但主干逻辑都在了。注意我在里面埋了三个保护重复动作熔断同一指纹超过两次直接返回提示不真的执行、工具不存在时返回可用清单引导模型自我纠正而不是抛异常、参数错误时回传 schema。这三处加起来不到十行能挡掉实际运行中相当大比例的死循环和无效调用。4.2 工具怎么写才不容易被误用工具的注册信息不是越详细越好而是越有区分度越好。三个同类工具的描述如果都是查询数据模型必然乱选。每一份描述都要能把什么时候不该用它说清楚。tool( nameread_file, description读取指定文件的完整内容。只用于需要理解文件内容的场景。 如果只想确认文件是否存在或行数请用 file_stat不要用本工具。 单次最多返回 2000 行超出部分请用 start_line 参数分页读取。, params{path: 相对仓库根目录的路径, start_line: 可选起始行号从 1 开始} ) def read_file(path: str, start_line: int 1): ...分页这件事值得单独说。一个不受限的读取工具是上下文爆炸的头号元凶。强制分页 明确告知还有多少行没读能让模型养成按需读取的习惯而不是一上来就把整个目录树读一遍。4.3 上下文预算的分配公式预算不能拍脑袋得有分配。我的经验分配是这个比例系统规则与工具定义占 15%任务状态摘要占 10%最近若干轮工具结果占 60%更早历史的压缩摘要占 15%。这个比例不是死的任务越偏探索最近结果那部分可以再放大任务越偏流程化状态摘要的占比可以提高。压缩策略上我推荐两级第一级是单条截断超过阈值的工具结果保留头尾第二级是滚动摘要把超过 N 轮的旧消息交给模型总结成三五条要点替换原文。摘要这一步要小心别把关键的具体值文件路径、错误码、ID总结掉了最好在摘要指令里明确要求保留所有具体路径、编号、报错原文。4.4 什么时候该认输失败分类比重试次数更重要重试不是万能药。要先分清失败类型再决定重试还是不重试。失败类型典型表现处理方式格式错误输出的 JSON 解析不了回传格式要求重试一次工具选错调了不该调的工具参数明显不对回传可用工具清单和各自用途重试参数缺漏缺必填字段回传 schema 和缺失字段名重试执行失败工具真跑了但报错原样回传错误信息让模型决策方向性错误连续三步都在做无效的事不再重试注入提醒或直接终止最后一行是关键。有些失败是再试一次就好有些失败是再试一百次也一样。判别方法是看最近几步是否在同一个状态空间里打转——动作指纹重复、或者工具结果的相似度极高就说明卡住了。卡住的时候正确的做法不是重试而是把当前状态和你已经连续三次做了类似的事但没进展这个事实告诉模型逼它换策略。如果换了策略还是不行那说明这个任务超出了当前能力该体面地失败把中间结果和卡点交给人工。5. 上生产前必须踩过的那些坑5.1 工具描述含糊导致的假调用最典型的一幕模型自信地调用了一个工具参数看起来像模像样但字段名是它自己编的。原因是工具描述里参数说明写得太抽象比如params{filter: 过滤条件}。模型不知道这个 filter 是 SQL 语法、是 JSON、还是自然语言于是自己发明了一种。修法很直接所有参数都要给类型和示例。filter: SQL WHERE 子句片段例如 status active AND created_at 2024-01-01不要包含 WHERE 关键字。加上示例之后参数错误率通常能掉一大半。还有一种假调用是工具存在但当前不该用。比如任务已经完成了模型还在反复调用验证工具。这是终止条件设计的问题——judge_finish不能只看模型说什么还要看客观状态测试是否真的通过了、目标文件是否真的存在了。5.2 上下文腐烂跑到第十步模型开始不听指令这个现象很普遍任务前期一切正常跑到七八步之后模型开始违反最开始定下的硬约束比如明明说了不要改动依赖文件它还是改了。原因不是模型变笨了是那些约束在上下文里被几十条工具结果挤到了最边缘的位置注意力权重天然就低。解法就是前面提过的约束重注入在每一步装配上下文时把三到五条最硬的约束单独拎出来放在紧贴生成位置的地方。我在几个任务上都试过这个改动违反率能从两三成降到个位数。成本是每步多几百 token非常划算。5.3 死循环的三种变体一种是完全重复同样的工具、同样的参数反复调用。熔断指纹能挡住。一种是微小变化参数每次都稍有不同比如分页的 offset 每次加一但读的是空内容指纹挡不住。要在工具层拦——工具自己发现这次返回的和上次一样或为空直接返回没有更多数据了请停止翻页。还有一种是震荡A 改文件B 改回来来回拉锯。这种最难自动检测因为它每一步都是合法的、有效的。解法是记录文件的变更历史发现同一个文件在短时间内被反复改回原状时注入提示你在两个方案之间反复切换请选定一个并说明理由。5.4 过度并行带来的资源踩踏模型学会了并行调用之后会在一步里发五六个工具请求。如果这些请求里有三个是写操作后果是灾难性的——同时写同一个文件、同时发起同一笔支付、同时扣同一份库存。防御必须做在 Harness 层不能指望模型自觉。我的做法是工具注册时标注readonlyTrue/False并发调度器只对只读工具开并发写操作全部排队串行。另外再加一个全局的并发上限防止模型一次发二十个请求把下游打挂。5.5 成本失控的两个隐蔽来源显性成本好控制看账单。隐蔽的有两个。第一个是上下文被重复计费。每一步都把完整上下文重新发一遍如果上下文有一万字跑二十步就是二十万字的输入。这是 Harness 结构的固有开销没法完全消除但可以通过缓存相同前缀的上下文命中缓存、精简工具定义、及时裁剪来压。前缀缓存对多步任务的效果尤其明显因为系统规则和工具定义在每一轮都是完全相同的。第二个是验证环节的重复执行。有些 Harness 每步都跑一遍完整测试套件一个两百个测试用例的仓库二十步就是四千次测试执行。正确做法是分层验证每步只跑相关的子集阶段性节点才跑全量。5.6 一个反直觉的经验工具越少任务成功率越高这条我踩过。早期做代码助手时为了让模型什么都能干一口气装了二十多个工具读文件、写文件、搜代码、跑测试、查依赖、看日志、格式化……结果模型经常在该用搜代码还是该用读文件上纠结或者用一个笨工具去完成本该用另一个工具干的事。后来砍到九个工具把相似功能合并比如搜索和读取合成一个带mode参数的工具成功率反而上去了。原因很简单工具选择本身就是一个消耗注意力的决策。选项越少决策越准。如果确实需要很多能力用工具分组 按需加载的方案而不是把所有选项摊在模型面前。6. 怎么判断你的 Harness 是好是坏评估与迭代6.1 别只看成功率要看四类指标只盯成功率会误导人。一个成功率 40% 但每步都很便宜的系统和一个成功率 60% 但成本翻三倍的系统哪个更好取决于你的业务场景。我一般同时看四个数。指标含义关注点任务完成率客观判据通过的比例核心指标但要按任务类型分桶看平均步数完成任务消耗的循环次数突然变多往往意味着某处退化单位任务成本token 消耗折算与完成率一起看才是效率人工干预率需要人接手或确认的比例决定能不能真正无人值守除了这四个还有一个我很看重的隐性指标失败模式分布。把失败案例归类看是模型能力不够占大头还是工具设计有问题、上下文没给到、终止条件太早占大头。如果后者占多数说明你的 Harness 还有很大优化空间而且这些优化的性价比远高于换模型。6.2 回归集怎么攒从线上失败里长出来评估集不要凭空造。最好的来源是线上真实失败案例。做法是每次线上失败把当时的完整 trace 脱敏后存一份标注失败原因的分类。攒到几十条你就有了一个针对性极强的回归集——它测的都是你的系统真正会犯的错。跑回归的时候注意两点。一是一次只改一个变量。同时改了上下文裁剪和工具描述涨了也不知道是谁的功劳跌了也不知道该回滚哪个。二是关注分布而不是均值。均值涨了 2%可能是某个短板任务涨了 20%也可能是所有任务都微涨这两种情况对应的下一步动作完全不同。6.3 迭代的优先级排序改 Harness 的收益不是均匀分布的。按我自己的经验性价比从高到低大致是这样修工具的错误信息。半天工作量通常能带来最明显的成功率提升。加重复动作熔断。几十行代码直接砍掉最多的一类无效消耗。做约束重注入。改上下文装配的一小段解决长任务的指令漂移。重新设计上下文预算分配。需要一点实验但天花板很高。换主循环结构比如从自由循环改成编排图。工作量大收益取决于任务形态。换模型。放最后因为前面的都没做完就换模型大概率白换。这个顺序不是绝对的但它反映了一个判断大部分 Agent 的问题出在工程层不是模型层。先把工程层的洼地填平再考虑模型的能力上限顺序反了就是白烧钱。最后再分享一个我自己踩过的小坑。早期我很迷信让模型输出思考过程觉得想得越多做得越对。实际跑下来长思考在简单任务上纯属浪费在复杂任务上又容易陷入自我怀疑式的循环。后来的做法是分级简单任务关掉详细思考复杂任务才开而且在循环里加一个思考长度上限的软约束。这个改动让平均延迟降了三成成功率没掉。很多 Harness 的调优就是这样不是加东西是知道什么时候该少加。
阅读完成 · 觉得有帮助?
咨询建站