真正让 Agent 能上线的,不是模型,而是 Harness

发布时间:2026/8/31 23:53:07
真正让 Agent 能上线的,不是模型,而是 Harness 很多 Agent Demo 看起来都很完整。模型接收用户目标分析上下文选择 Tool然后调用后台接口。只要模型选对了工具整个链路就像已经成立。比如用户说帮我把这张订单退掉。Agent 查询订单判断符合条件然后调用submitRefund(orderId)Demo 到这里通常已经结束了。但真正进入生产环境以后我越来越觉得决定这个系统能不能上线的根本不是模型会不会调用这个 Tool而是模型提出“我要退款”之后到底是谁来决定这次退款真的能不能执行。这中间那一层我更愿意叫它 Harness。它不是一个新的模型也不是一套花哨的 Agent 框架。它更像是包在模型外面的执行环境模型可以提出意图、生成参数、选择下一步但所有真正产生副作用的动作都必须先经过这一层。如果没有这一层Agent 看起来越聪明系统反而越危险。模型应该有建议权但不应该天然拥有执行权很多 Agent 实现最开始都是这种结构User ↓ LLM ↓ Tool ↓ Backend这张图最大的问题是把“模型选择了 Tool”和“Tool 应该执行”当成同一件事。实际上这两件事应该严格分开。模型可以根据上下文判断用户想退款。也可以继续判断这张订单似乎符合退款条件。甚至可以把参数整理好{orderId:O-10086,reason:用户不再需要}这些都没有问题。问题在于模型输出了这组参数以后系统是不是立刻调用退款服务。如果答案是“是”那么模型实际上已经拥有了业务执行权。这不是简单的 Tool Calling而是把整个后台的副作用入口交给了一个概率模型。这条边界我现在会划得非常死模型负责提出动作Harness 负责决定动作是否被允许执行。只有这样Agent 的自主性才不会直接穿透到底层业务。Prompt 可以告诉 Agent 不要乱来但 Prompt 不是安全边界很多系统没有 Harness最后会把越来越多规则塞进 Prompt。不要重复退款。 不要访问其他用户的订单。 超过 5000 元的退款必须人工确认。 订单已经发货以后不要调用 cancelOrder。 支付结果未知时不要再次调用 pay。 每次最多调用 10 个工具。刚开始看起来很合理。模型知道的规则越多调用就越准确。但只要这些规则真的重要它们就不应该只存在于 Prompt 里。因为 Prompt 的本质是指导不是约束。模型可能理解错。可能因为上下文太长忽略某条规则。也可能在重新规划时选择另一条路径。更重要的是Prompt 没办法真正阻止执行。如果系统结构仍然是LLM → refundTool → RefundService那么所谓“高金额退款必须人工确认”最终只是模型自觉遵守的一条建议。真正可靠的结构应该是LLM ↓ Refund Tool Request ↓ Harness ↓ Policy Check ↓ Approve / Reject / Require Human Review ↓ RefundService这个变化看起来只是多了一层实际上把系统性质完全改变了。模型不再是执行者。它只是一个提议者。Harness 最重要的职责不是调 Tool而是拦 Tool很多人理解 Harness会先想到 Tool Registry。模型有哪些工具。每个工具的参数是什么。怎么路由到具体函数。这些当然都属于 Harness但我觉得真正关键的能力反而不是“让模型能调用更多 Tool”而是“让模型在不该调用的时候根本调不下去”。比如用户说帮我把这个订单退款。模型选择了submitRefundHarness 在真正执行前可以拿到当前身份和上下文currentUserId tenantId orderId workflowId riskLevel conversationId然后做一系列确定性检查。if(!permissionService.canAccess(currentUserId,orderId)){returnToolDecision.reject(PERMISSION_DENIED);}if(riskService.requiresApproval(orderId)){returnToolDecision.requireHumanApproval();}if(workflowService.isAlreadyExecuting(workflowId,submitRefund)){returnToolDecision.reject(DUPLICATE_EXECUTION);}这些代码不聪明。也不需要聪明。它们的价值恰恰在于非常无聊、非常确定。这也是 Harness 和 Agent 最大的区别。Agent 的价值来自灵活。Harness 的价值来自保守。Agent 越自主Harness 越应该收紧早期 Agent 只做查询风险并不高。模型查错一次订单最多是回答不准确。但只要 Agent 开始具备写能力情况完全不同。它可以createOrder cancelOrder submitRefund changeAddress createTicket sendMessage再往后如果 Agent 具备规划能力它甚至不会只调用一次。它会自己组织出一条链路queryOrder → cancelOrder → submitRefund → releaseCoupon → notifyUser这时候真正的问题就不再是“模型会不会选错 Tool”。而是如果它连续调用五个 Tool其中第三个失败怎么办如果第二个 Tool 已经产生副作用第三个超时以后模型重新规划又从第一个开始怎么办如果模型为了完成目标换了一条路径调用了另一个同样能达到结果的 Tool会不会重复执行Agent 越自主调用组合越多后台就越不能假设“正确路径是固定的”。Harness 必须成为那层稳定边界。它不关心模型为什么这么想。它只关心这个动作当前允不允许。这个动作之前有没有执行过。这个动作需要什么级别的权限。这个动作失败以后能不能重试。这个动作是否必须进入 Workflow。Identity 不能来自模型参数这是我认为 Agent Harness 里非常重要、但经常被忽略的一点。Tool 参数通常来自模型。比如{orderId:O-10086,userId:U-9527}如果userId也是模型填的这个设计基本已经出问题了。因为模型不应该决定自己代表谁。用户身份、租户、角色、权限范围都必须由 Harness 从可信上下文里注入。模型只应该提供业务参数。模型生成 orderId refundReason requestedAmount而这些必须来自系统currentUserId tenantId permissionScope sessionId traceId最终执行请求可能是RefundCommandcommandnewRefundCommand(toolArgs.orderId(),toolArgs.refundReason(),context.currentUserId(),context.tenantId(),context.traceId());这里的区别很关键。模型可以说“我要退这张订单”。但它不能说“我是管理员所以允许我退”。身份不是语言的一部分。它是系统事实。Harness 的职责之一就是确保模型永远无法通过构造参数重新定义自己的身份。Tool Registry 也不应该只是一个方法列表很多 Agent 系统会维护一个 Tool RegistryqueryOrder cancelOrder submitRefund changeAddress看起来像方法注册表。但如果只是名字、描述和参数其实远远不够。真正进入生产环境以后一个 Tool 至少还应该有这些属性riskLevel readOnly requiresConfirmation requiresApproval timeout retryPolicy idempotent allowedRoles allowedTenants auditLevel比如queryOrder riskLevel LOW readOnly true requiresConfirmation false而submitRefund riskLevel HIGH readOnly false requiresConfirmation true requiresApproval amount 5000这些信息不应该全写进 Prompt。它们应该成为 Harness 能执行的策略。否则每次新增 Tool系统就只能继续往 Prompt 里补一句注意submitRefund 比较危险请谨慎使用。这不是工程。这是祈祷模型足够听话。Harness 需要控制的不只是权限还有“资源”Agent 和普通程序还有一个很大的区别。它可能持续思考也可能持续调用。一个写死的 Java 流程最多执行你写出来的那些步骤。Agent 如果目标没有完成可能继续尝试。查询失败再查。Tool 超时再换一个。模型觉得信息不够再调用三个检索工具。如果系统没有控制很容易出现一种情况业务没出事故成本先失控了。所以 Harness 里还需要另一类限制maxToolCalls 20 maxExecutionTime 30s maxModelTokens 20000 maxRetryPerTool 2 maxParallelTools 3这些限制不是为了“限制 AI 能力”。而是为了让一次 Agent 执行有明确边界。传统后台早就接受了这些思想。线程池有限制。连接池有限制。HTTP 有超时。MQ 有重试次数。熔断器有阈值。Agent 没有理由成为一个无限资源的例外。如果一个模型可以因为“还没想明白”就无限调用 Tool那系统实际上根本没有执行边界。Harness 必须知道什么时候停止。Retry 也不能由模型自己决定Tool 调用失败以后模型最自然的行为就是重试。这在查询类工具上没什么问题。但副作用操作不一样。比如支付接口超时。模型看到TIMEOUT然后决定再试一次。这是一个很合理的语言推理。但在业务上可能是错的。因为超时只能说明“没有拿到结果”不能说明“没有执行成功”。所以 Harness 不能简单把所有失败都交给模型解释。Tool 应该返回结构化结果{status:UNKNOWN,retryable:false,nextAction:QUERY_PAYMENT_STATUS}Harness 根据 Tool 策略决定接下来允许什么动作。模型可以重新规划。但不能绕过这层语义直接再次支付。这里其实又回到了一个很重要的原则系统已经确定的事情不要再让模型猜。是否可以重试不是语言问题。是执行语义。Harness 应该明确知道。幂等应该由 Harness 和 Workflow 协作而不是交给 Agent 记忆Agent 还有一个天然问题它的记忆并不可靠。即使当前上下文里明确写着“刚才已经提交过退款”模型下一轮也可能重新生成一次相同调用。所以副作用 Tool 不能依赖 Agent 自己记住“我刚才已经做过”。Harness 在执行 Tool 时应该有稳定的执行标识。比如workflowId WF-10086 stepId submit-refund生成idempotencyKey WF-10086:submit-refund真正调用后台时refundService.submit(command,idempotencyKey);这样即使模型重复提出三次同一个动作后台也只会接受一次。这件事很重要因为它说明 Harness 不只是“调用拦截器”。它实际上负责把一个不稳定的 Agent 行为转换成稳定的系统行为。模型可以重复执行不能重复。模型可以忘记系统不能忘记。Human-in-the-loop 也应该是一种执行策略很多 Agent 项目把人工介入理解成模型失败以后兜底。我现在更愿意把它看成 Harness 的一种正常执行策略。比如refundAmount 500 → auto execute 500 refundAmount 5000 → require user confirmation refundAmount 5000 → require human approval这不是模型置信度问题。哪怕模型 100% 确定用户真的想退款超过一定金额也可能必须审批。所以 Harness 不应该问模型有多确定而应该问这个动作按系统规则需要什么级别的授权模型负责提出submitRefund(amount8000)Harness 返回REQUIRES_HUMAN_APPROVAL然后 Workflow 暂停。等审批结果回来以后继续。这时候 Agent 不需要“等待”一个线程。也不需要不断轮询。Harness 和 Workflow 一起把执行状态保存下来。从这个角度看Human-in-the-loop 根本不是 Agent 的补丁。它本来就是企业执行模型的一部分。可观测性也应该围绕“决策到执行”这条链路传统后台排查问题经常看request response latency exceptionAgent 系统还多了一层。一次业务动作之前存在一个模型决策。所以真正需要审计的是完整链路用户说了什么 ↓ Agent 选择了什么动作 ↓ Harness 为什么允许或拒绝 ↓ Tool 实际收到什么参数 ↓ Backend 返回什么结果 ↓ Workflow 最终进入什么状态比如一张订单被退款未来真正需要回答的不是refundService 有没有被调用而是谁触发了这次退款Agent 为什么提出这个动作Harness 根据什么策略允许执行是否经过用户确认当时使用的身份和租户是什么这也是我不太赞同把 Agent Tool 调用只记录在普通应用日志里的原因。它本质上已经是一条业务决策链。至少应该有agentRunId workflowId toolCallId userId tenantId toolName policyDecision approvalStatus idempotencyKey executionResult有了这些才真正能还原一次 Agent 行为。否则出了问题以后日志里只能看到一句submitRefund success你仍然不知道它为什么发生。Harness 不是另一个 WorkflowHarness 和 Workflow 很容易混。它们确实会有一些交集。但我更愿意这样区分。Workflow 负责一件事情怎么持续推进。Harness 负责Agent 每一次想执行动作时允许它做到什么程度。比如退款流程可能持续十分钟提交退款 → 等待支付渠道 → 释放优惠券 → 更新订单这属于 Workflow。但 Agent 在提交退款之前Harness 会检查有没有权限 是否需要确认 是否重复执行 金额是否超限 当前 Tool 是否可用这属于 Harness。两者放在一起结构会比较清楚User ↓ Agent ↓ Harness ↓ Workflow ↓ Tool / Capability ↓ Backend并不是所有 Tool 都一定经过 Workflow。一个只读查询可能直接执行。但所有 Tool 都应该经过 Harness。因为只要调用方是 Agent就必须有一层地方负责把模型行为翻译成受控执行。我越来越觉得Harness 更像 AI 时代的 Application Runtime传统 Java 开发其实一直依赖 Runtime。我们很少让业务代码自己管理线程。也不会让每个 Controller 自己实现连接池。超时、线程、连接、事务、资源隔离这些能力都由框架或运行时提供。Agent 现在其实处在一个很类似的阶段。早期大家习惯把所有逻辑写在 Agent 本身Prompt Tool Retry Memory 权限说明 人工确认 错误处理最后 Agent 越来越胖。看起来像一个聪明的大脑实际上已经承担了半个应用运行时的职责。我更认可的方向是把这些横切能力逐渐抽出来。Agent 理解 规划 选择 Harness 权限 策略 预算 超时 重试 幂等 审计 审批 Workflow 状态推进 等待 恢复 补偿 Backend 业务规则 事务 数据一致性这几层越清楚系统越容易长期维护。因为模型迟早会换。Prompt 迟早会改。Tool 迟早会增加。业务流程也会不断变化。但执行边界不能跟着模型一起漂移。真正成熟的 Agent不应该证明自己什么都能做我现在看一个 Agent 系统已经不太关心它展示时有多自主。我更关心另一件事当模型想做一件不该做的事情时系统会发生什么。如果答案是Prompt 会提醒它不要这么做。那我通常不会觉得这个系统已经成熟。如果答案是它可以提出但 Harness 会拒绝。这才是一个真正可控的系统。Agent 的能力没有因此减少。它只是终于有了边界。我觉得这也是很多团队从 Demo 走向生产时必须经历的一次认知变化。最开始我们希望模型更自主。后来我们才发现真正能让自主成立的不是取消限制而是把限制做得足够可靠。因为真正成熟的智能系统不是让模型拥有无限执行权。而是让它在清晰的边界里尽可能自由地完成目标。