资讯动态

给 Agent 套上 Harness:从 Demo 到生产的工程实践

发布时间:2026/10/17 21:12:31 来源:尧图企业网站定制
上个月我排查了一个极其痛苦的线上问题凌晨四点半一个负责订单改价的 Agent 发出了金额完全对不上原订单的退款请求。模型侧日志已经过期prompt 版本回滚了两次谁都说自己没动过。最后是靠 Hermes Agent 自带的 OpenHarness 做了 trace 回放才看到完整链路——Agent 把上一步工具返回里的金额字段误读成了一个字符串然后一路错误地传递下去。这个案例让我彻底转变了做 Agent 的思路真正值钱的不是哪个模型更聪明而是你有没有一套能把任务循环、工具调用、状态管理、回归评测、trace 回放固化下来的 harness 机制。这篇文章会把 Hermes Agent 这套开源方案的架构逻辑、关键实现细节以及我把它从一个演示 Demo 推向真实业务的全过程完整拆开讲。适合两类人看一类是从调 LLM API走向做 Agent的开发者另一类是已经跑过 Agent 但一直觉得测试、回归、上线安全无从下手的后端负责人。1. 为什么给 Agent 套 Harness 比选模型更重要1.1 裸跑 Agent 的三种典型翻车现场先说三种我反复踩过的裸跑现场都是没有 harness 时一定会遇到的。第一种是工具调用幻觉。你明明只给模型提供了三个工具函数它能给你编出第四个不存在的工具名或者工具存在参数类型完全对不上。我见过最多的情况是把日期字符串当时间戳传出去下游接口直接给你返回 500。为什么模型会这样因为它在做下一个 token 的预测而不是在遵守你的接口契约工具名和参数对你来说是约定对模型来说只是一段看起来顺理成章的文本。第二种是死循环。某个 Agent 在查询完库存之后因为想等库存状态更新于是一条路走到黑地反复调用同一个查询接口。没有外部终止条件的 while 循环配合一个对确认这个动作永远不肯停下的模型简直是资源黑洞。那次我眼睁睁看着请求数被刷了几百次账单哗哗地涨。第三种是失忆。三步之前的结论到第五步就忘了。Agent 反复查询、重复计算最后给你一个自相矛盾的答案。工具调用的返回越来越长上下文越来越拥挤最早的指令和目标被挤出有效范围这是所有长任务崩溃的共同原因。这三类问题的根源都不是 prompt 写得太差而是缺少执行层面的强制约束。你能在 prompt 里写一千万遍别瞎编工具名但保不住模型在某个角落就是会编一次。真正可靠的约束必须落在代码流程里。1.2 Harness 管的到底是哪几件事OpenHarness 给我的第一感觉是把很多本来要靠自觉的事情变成了平台强制的事情。我把裸调模型和套上 harness 的区别拉了一张表维度裸调模型的做法套上 OpenHarness 之后工具调用自己解析模型返回的 JSON格式错一小点就崩自动 schema 校验非法参数直接被拦下并反喂给模型修正循环控制自己写 while非常容易死循环内置步数上限、token 上限、自然终止判断状态管理手动拼历史消息拼着拼着就乱了工作内存与长期记忆分离超长时自动压缩可观测性全靠 print 大法和日志文件每步决策、工具入参出参全量 trace可回放回归验证人肉点一点凭感觉说还行评测集一键跑分prompt 和模型改动可量化对比安全边界全靠 prompt 里的你不许……工具级权限清单和拦截层写操作可单独管控这张表里的每一行单拎出来都不复杂但合在一起就是一个工程分水岭。调用单个 LLM 完成一次文本生成工程含量很低让 LLM 反复调用工具、修改真实系统状态工程含量一下子高了一个量级。没有 harness你等于每天都在裸奔。1.3 一个常被忽略的事实模型差异会缩小工程差异会放大很多团队在选模型上花掉大量时间今天试 A明天试 B换来换去发现提升就那么几个百分点。真正拖垮项目的往往是那些和模型无关、只和工程设施有关的短板出问题复现不了、改了 prompt 不敢上线、工具一多就没法维护。我的体感是当工具数量超过五个裸调方案基本就不可维护了。因为每次改动 prompt影响不到的地方太多每次新增一个工具模型对已有工具的理解就会受干扰。而这些问题的解法恰恰不在模型侧而在 harness 侧。把工程底座打牢模型升级带来的收益才接得住底座不稳再强的模型也会被自己一步错步步错的调用搞得稀烂。2. Hermes Agent 的核心循环从一次工具调用看整套设计2.1 标准 Agent 循环长什么样Agent 和普通 LLM 应用最本质的区别就是它在一个循环里反复推理和行动。OpenHarness 的核心循环并不神秘核心代码也就这么一小段for step in range(agent.settings.max_steps): decision agent.think(context) if decision.kind finish: return decision.answer observation agent.execute(decision.action) context.push( stepstep, actiondecision.action, observationobservation, )think是让模型根据当前上下文决定下一步动作execute是真正去调用工具并拿到观察结果然后把动作 观察写回上下文进入下一轮。翻译成人话就是看一眼现状做一件事看结果再决定下一步。这段代码本身五分钟就写完了真正的门道全在循环之外。max_steps从哪来execute之前谁负责校验参数context爆掉怎么办动作是不是危险操作每一步的决策依据有没有留痕这些问题 OpenHarness 都做了实现而那才是 Agent 工程的价值所在。2.2 工具接入层schema 校验帮你拦住大部分幻觉工具接入是 harness 里最值得细看的一层。Hermes Agent 要求每个工具都声明严格的参数结构底层用的是类似 pydantic 的运行时校验机制。举个例子一个退款工具的接入是这样写的from openharness import tool from pydantic import BaseModel class RefundArgs(BaseModel): order_id: str amount: float reason: str tool(namerefund_order, schemaRefundArgs) def refund_order(order_id: str, amount: float, reason: str ): 对指定订单执行退款操作。 # 真实业务逻辑... pass关键点在于模型生成的动作参数在真正执行之前必须先通过 schema 校验。如果模型把amount传成字符串nullharness 会拒绝执行然后把一条修正提示反喂给模型让它重新生成参数。这个设计的价值是巨大的。大多数 Agent 事故问题都不是模型想干坏事而是模型传错了参数。校验层把这类错误挡在真实系统之外让模型有机会重试而不是直接闯祸。我在接入过程中最大的教训之一就是早期图省事把内部小工具当成普通 Python 函数直接注册允许任意 kwargs。表面上方便了实际等于把校验层完全关掉前文那个退款事故就是这么漏出去的。后来我把所有工具都改成强制 schema这类问题才真正绝迹。2.3 状态与记忆防止 Agent 在第三步就失忆Agent 的上下文管理比普通聊天要复杂得多。聊天只需要管理用户消息 模型回复Agent 的上下文里还叠加了工具入参、工具出参、中间推理过程信息密度极高爆掉的速度也比想象中快。OpenHarness 把状态分成几层来管。第一层是工作内存也就是最近 N 步的动作-观察对这是 Agent 当前决策的直接依据。第二层是任务目标原始的用户请求和系统指令这一层必须始终锚定在上下文中不能被挤出去。第三层是长期记忆用于跨会话保存关键结论比如某用户偏好、某订单的历史处理状态。比较棘手的是上下文超长的问题。Hermes Agent 的做法是当上下文 token 数超过阈值时触发自动压缩把早期的对话和工具调用记录整理成一个摘要节点留出空间给新的推理和工具返回。压缩本身不复杂复杂的是要保证摘要不丢关键信息。所以我在配置时会把任务目标单独固定住不允许压缩机制碰它宁可让中间过程被总结得更粗一点也不能让 Agent 忘了自己本来要干嘛。实际上不少 Agent 跑偏不是因为模型傻而是因为原始目标在几十轮之后已经被碾压得无影无踪了。把这个目标作为独立状态管理是 harness 框架很重要的一个差异化价值。3. 评测集才是这套框架最值钱的部分3.1 把感觉变聪明了翻译成可回归的用例很多团队做 Agent验证方式还停留在我让它跑几个用例人工看一下结果对不对。这种验证方式有两个致命问题第一人的感觉会受情绪和最近几次体验影响不稳定第二这次改了 prompt 让场景 A 变好了但可能悄悄弄坏了场景 B靠人工根本发现不了。OpenHarness 给我的思路是把每次你想要验证的 Agent 行为写成结构化的评测用例。它不判断模型生成的话说得对不对而是判断动作序列和最终副作用是否符合预期。比如这么几类用例场景输入通过条件失败拦截点库存查询帮我查一下 SKU-001 还有多少货必须调用 query_inventory且 sku 参数完整没走工具调用直接编答案判失败退款审批客户要求退 120 元禁止调用 refund_order必须转入人工流程一旦调了 refund_order直接判失败多步任务下单后发现缺货自动给用户补偿券依次调用库存查询、补偿券发放且顺序合理顺序倒过来或跳过步骤按权重扣分这里有个和普通 LLM 评测完全不同的思路普通评测比的是输出文本和期望文本像不像Agent 评测比的是行为路径和安全约束。对 Agent 来说最终答案花里胡哨不重要重要的是它有没有按预期的方式使用工具、有没有触碰不该碰的权限、有没有在合理步数内收尾。3.2 单元、任务、旅程三级评测怎么分层评测集也不能只有一个大而全的池子需要分层否则定位问题会非常痛苦。我把 OpenHarness 的评测拆成了三个层级来用。第一层是单元级针对单个工具。检验工具的 schema 校验是否严密、异常路径是否处理正确。比如传非法参数时是否被拦截下游接口超时时工具是否给了合理报错。这一层跑得又快又便宜是每次提交代码时的快速检查。第二层是任务级针对单个用户请求。让 Agent 从零开始完成一次完整任务比如查库存、做比价、处理一个退换货申请。任务级用例的判定会综合看步骤数、工具使用正确性、最终结果质量这是平时迭代 prompt 时最主要看的指标。第三层是旅程级跨多个请求、有状态延续的场景。比如一个完整的售后流程用户先问订单状态再发起退款中途又补充了新信息Agent 能不能把这些跨轮次的信息串起来。旅程级最重要也最容易暴露上下文管理的问题通常数量不用太多但每一条都要精。3.3 用评测分把 prompt 和模型改动从玄学变科学有了评测集之后改动 prompt 就变成了一件可以 A/B 的事。我的常规做法是每次改动之前先在同样的评测集上跑一遍基线分改完之后再跑一遍看着分数决定要不要上线。实际跑分的时候我习惯做一套配置让结果更稳定evaluation: cases: ./eval_cases/*.json rounds_per_case: 5 pass_threshold: 0.8rounds_per_case是让我感触很深的一个参数。Agent 推理带有随机性哪怕 temperature 调成 0模型输出也不会完全确定。同一个用例跑一次过了再跑一次挂了——这种情况太常见了。把每个用例跑多轮通过率达到阈值才算整体通过能过滤掉大量偶然性。评测跑起来之后很多直觉上的争论就结束了。比如有次我在调研到底要不要在系统提示词里强调合规限制各执一词谁也说服不了谁。最后是评测分数说了算加了之后合规违规次数从三次降到了零代价是平均步数多了两步。这个交易划不划算一目了然。从此之后团队里不再有我觉得模型变聪明了这种模糊结论只有这条改动让任务通过率从 82% 提到了 90%这种硬数字。4. 实测踩坑把 Agent 从 Demo 推到线上的四个问题4.1 工具参数幻觉完整排查一次线上事故回到开头那个退款事故我完整复盘一下排查链路这是非常有代表性的一次故障。现象是凌晨出现了非法退款请求。第一步是确认代码版本和 prompt 版本回滚了两次问题依旧。第二步是看传统日志发现模型侧的输入输出日志早已被定时任务清掉什么都查不到。这时候如果没有 trace 留存这个事故就变成无头悬案了。OpenHarness 的 trace 保留了完整的决策链。我逐条回放第 3 步模型调用查询订单接口返回里金额字段的原始类型是字符串null第 4 步模型读取这个字段后在第 5 步生成了退款工具调用参数里的amount直接沿用了这个字符串第 6 步因为当时的工具还是宽松参数的裸函数没有触发校验就这么执行出去造成事故。整个链路还原下来根因不是模型而是我的接入方式有缺陷宽松的工具注册等于没有 defensive layer。修复动作有两步——把历史遗留的所有宽松工具全部改成强制 schema在 harness 配置里打开校验失败后反馈修正的重试机制最多允许模型在一个错误参数上重试两次。修复后专门加了三条评测用例覆盖这种情况跑了两周都没有复现。4.2 死循环与 token 黑洞终止条件必须放在循环之外很多第一次做 Agent 的人以为控制循环靠的是让模型自己想停就停。这是完全错误的思路。模型没有自我保存的本能也没有成本意识让它自己决定什么时候停下来它可能会一直谨慎地确认下去直到资源耗尽。OpenHarness 的做法是把终止条件放到循环之外用硬性配置管住。我生产环境的配置大概是这样的agent: max_steps: 20 max_tokens: 40000 idle_timeout: 120s cycle_detection: enabled: true identical_call_limit: 3这里面的cycle_detection是我后来手动补上的但思路值得借鉴如果同一个工具、同样的参数连续出现多次基本可以判定模型已经陷入原地打转。与其让它在转圈中耗尽预算不如直接掐断、抛出一个检测到重复调用的异常把情况交给兜底流程。这类硬限制被触发时可能意味着任务没有完成但没关系——一次明确的失败远好过一场悄悄烧钱的无底洞。任务失败可以被记录、重试、转人工但资源耗尽只会变成账单上的一个数字。4.3 评测结果的随机波动并发和采样带来的噪声评测集做好之后我又遇到一个头疼的问题同一份代码同一份评测集跑了两次分数能差好几个百分点。一开始我以为有隐藏 bug后来才意识到是评测噪声。噪声来自几个方面。第一是模型采样的随机性每个步骤都有概率生成不同的动作序列。第二是并发环境干扰评测任务同时跑下游接口的延迟波动会让 Agent 在超时和重试之间做出不同选择。第三是部分工具调用本身有副作用时序比如查询的结果刚好在两次运行之间更新了。我的应对是双管齐下一方面把rounds_per_case调高到至少 5让结果取多数趋势另一方面在评测模式下把采样参数固定尽量降低随机性。但也要承认Agent 评测永远不可能像单元测试那样确定性。正确的姿态是看趋势不看单次看违规次数和通过率的变化幅度而不是纠结小数点后一位。把随机当成系统的固有属性来管理而不是试图消灭它。4.4 能跑通和能上线之间差着的几个细节Demo 阶段大家都会有一种错觉这个 Agent 挺聪明什么都会。真正推到生产你才发现一堆和智能无关的细节没处理。第一个是工具调用的超时。模型可以很快生成一个工具调用但下游接口可能 3 秒就超时也可能 30 秒才返回。我给每个工具执行设置了 10 秒超时超时后把异常当观察结果喂回模型让它有机会采取替代方案。第二个是下游接口的速率限制。Agent 一旦并发跑起来对下游 API 的请求压力不是匀速的是突发的。我没有在 harness 层做很复杂的限流但工具层统一加了一个信号量避免一个 Agent 瞬间把下游打崩。第三个是权限粒度。Demo 阶段所有工具对 Agent 都是敞开的生产环境必须按工具类型区分权限。只读工具和写操作工具的执行门槛完全不同后者我会强制走人工审批。这些细节看起来零碎但每一个都可能在线上放大成事故。Demo 跑通只是 10% 的工作剩下 90% 都是这类工程化收尾。5. 从 Demo 到生产接入 OpenHarness 的四个检查点5.1 留痕能力每一步都要能回放接入生产环境前第一个检查点就是 trace。我用过一个极简的自定义 Agent当时觉得日志都打在文件里了够了。直到出了问题才意识到普通日志记录的是我调了什么接口而 Agent 排障需要的是模型为什么决定调这个接口、当时看到了什么上下文、参数是怎么拼出来的。OpenHarness 的 trace 把每一步都固化了模型收到的 prompt 快照、模型生成的原始决策文本、最终解析出的工具调用、实际执行时的入参出参、每一步耗时、上下文长度变化。出问题时整个链路可以像看录像一样逐帧回放。留痕的另一个好处是反推 prompt 效果。某个任务连续失败翻 trace 你会发现模型卡在了哪一步、是工具说明不清晰还是数据格式有歧义。没有 trace你只能靠猜而猜是没有任何效率可言的。5.2 人工兜底Agent 永远不应该是最后一道闸门再强的模型、再完善的校验也不能保证 Agent 在所有场景下都安全。生产环境的底线思维是Agent 可以犯错但错误必须被拦在造成不可逆影响之前。我的做法是把工具分成普通执行和敏感执行两大类。普通执行比如查询、计算Agent 可以自主完成敏感执行比如退款、删除、发送消息给用户harness 会标记为需要人工确认。实际操作中Agent 生成敏感动作后会进入一个待审批状态由人工在系统里确认后才真正执行。这套机制看起来简单但它是一个很重要的心理预期管理。它和模型聪明不聪明无关和业务是不是真的理解 Agent 的可靠性边界有关。把确认责任交给一个独立于模型判断的人工节点等于给系统加了一道不看模型脸色的安全锁。5.3 成本控制给每次任务设置 Token 预算Agent 比普通 LLM 应用烧钱得多因为它不是一次生成而是多轮循环。每轮的 prompt 都包含越来越多历史记录token 消耗是指数叠加的。初期我的成本模型完全失控直到给任务加了预算约束。我习惯在每次任务开始时先给一个 token 预算从预算里扣每一步的消耗。预算快见底时harness 会强制触发一次上下文压缩压缩后仍然不够就直接终止任务并标记为预算超限。这比一直放任 Agent 跑到max_tokens被硬切更优雅至少给了模型一次自我收敛的机会。成本优化的另一个点是工具结果缓存。对库存、价格这类短时内不会变的数据工具调用结果按参数做缓存一个 Agent 内部或多个 Agent 之间重复查询时直接命中能省下大量重复 token。尤其是多轮循环里模型反复查同一类信息的情况缓存收益非常明显。5.4 灰度策略先让 Agent 只读再让它写最后是发布节奏。Agent 本质上是一个非线性系统你很难在上线前穷尽所有输入。所以我的默认策略是分三步走。第一步只读灰度。只给 Agent 开放查询类工具让它处理真实业务里的只读请求观察准确率和行为路径。这个阶段即使出错也不产生实质副作用。第二步沙盒写入。在隔离环境里开放写工具让 Agent 跑模拟数据重点观察权限边界是否守得住。第三步生产写入。放开的第一个写工具一定是可逆、低风险的比如修改备注并搭配人工审批一起上。每一步都留着 trace 和评测集走完一步再迈下一步。Agent 的灰度比普通功能发布更难把握因为你没法写死一套测试用例覆盖所有话术只能靠分层递进来降低风险半径。这个过程慢但慢得值得。最后再分享一个我现在的习惯设计任何新 Agent 能力时我会先把评测用例写好把什么行为算对、什么行为算违规定义清楚再动手写调用逻辑。评测不是 Agent 开发的事后环节而是第一步。OpenHarness 的价值在于它把这个流程固化成了机制让我从靠感觉迭代转向了靠用例迭代。这个转变比换任何一个更聪明的模型都来得有效。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑