跳到正文
Elaine Blog
返回

Harness 怎样控制执行:工具、命令、反馈与停止条件

更新于:
Agent Harness

报告助手执行了三次写文件,第四次仍说“我将生成报告”。如果程序只负责把工具结果送回模型,它可能一直重复下去。控制执行意味着为每次动作建立入口,也为整个任务建立边界。

工具调用的协议与参数基础见Tool Calling 系列。本篇进一步讨论:当读文件、改文件、执行命令和最终交付同时存在时,Harness 如何统一管理它们。

一个动作应该经过哪些步骤

模型返回工具名和参数后,执行器先确认名字在本次允许集合内,再做结构校验、业务授权和预算检查,然后执行。执行结束后记录结果,整理模型能理解的反馈。这个顺序不是装饰:若先执行后校验,错误已经发生。

模型自己提交的 user_id 不能直接成为可信身份。当前租户和用户来自已认证会话,目标对象来自参数,两者在授权层比较。同样,模型提出 max_calls=1000 也不能覆盖应用分配的调用预算。

# 示意控制顺序;各函数的实现见配套 runtime_core.py。
def handle_call(runtime, name, arguments):
    # 白名单在执行入口生效,不能只保存在提示词或状态对象中。
    runtime.require_allowed(name)
    # 消耗任务共享预算,子调用不能重新获得一份无限额度。
    runtime.consume_call()
    # dispatch 内部继续校验参数和资源;不从模型文本动态导入函数。
    return runtime.dispatch(name, arguments)

这里的“允许工具”和“允许操作对象”是两次检查。允许读订单并不表示允许读所有订单。入口集中之后,日志、限额和策略才能一致覆盖各类动作;但如果另有未受控 shell 可绕过入口,集中校验就不是完整边界。

文件工具与 Shell 为什么同时存在

专用文件工具能返回结构化差异、限制单次大小、明确编码;shell 则提供灵活组合,适合编译、搜索和已有脚本。灵活性也意味着参数语义复杂,输出可能很大,命令可能创建后台子进程。

使用命令数组能避免 shell 对字符串进行某些解释,但不是万能安全措施。python script.py 仍然执行脚本代码,curl 的合法参数也能把资料发送出去。安全性取决于允许的程序、参数、身份、文件和网络边界的组合。

命令成功返回退出码 0,只说明该进程报告成功。它可能没有生成期望文件,也可能生成了旧内容。因此命令结果和任务验证要分别处理。下一篇会进一步解释怎样限制进程本身。

反馈怎样影响下一步

如果执行器只返回“失败”,模型不知道该修复参数、申请权限还是等待网络。更有用的反馈至少包含错误类别、相关对象、可修复信息和稳定引用。

例如 version_conflict 可以带预期版本 12、实际版本 13,要求重新读取;permission_denied 表示当前身份不允许这个动作,不应把它包装为可重试网络错误;timeout_unknown 则表示无法确认远端是否完成,需要查询操作状态。

工具输出过大时,可以保存完整日志,返回退出状态、首个相关错误和日志引用。直接截断最后一半可能丢掉真正的异常,全部送给模型又会挤占有效上下文。裁剪策略需要依据输出类型,完整记录的权限也必须与原任务一致。

配套反馈示例下载展示预算耗尽和重复失败计数。它不模拟网络重试,而是让读者清楚看到“允许继续”由程序决定。

执行前后钩子应该做什么

钩子是流程中的可扩展位置,例如执行前做审计标记,执行后汇总耗时。它们适合承载横切逻辑,但回调的名字不等于保证。

如果某个框架的执行前钩子可以阻止工具,才能把它用于该工具入口的拦截;若只是通知事件,返回一个布尔值可能完全无效。若钩子运行在已经拥有宿主权限的扩展进程内,它本身也属于可信代码。源码篇会核对实际调用方如何使用返回值。

对必须强制的业务授权,最稳妥的位置是业务执行入口。界面提示、模型说明和观察事件可以补充体验,但不能替代最终检查。

怎样防止无限循环

预算至少包括总时间、模型轮数、工具次数和产物大小。它们不是同一个指标:一次工具可能运行十分钟,一轮模型也可能提出多个动作。子 Agent 和内部重试应共享或扣减父任务预算,不能每开一个子任务就重置。

重复失败检测可以比较“工具名、规范化参数、错误类别”的摘要。连续三次相同输入产生相同错误,而且没有新的观察,通常说明继续原样尝试没有价值。这个阈值是应用策略,不能声称三次适用于所有任务。

如果输入改变,原来的失败计数也要谨慎处理。修正路径之后再次读取与重复原请求不同;只是换一种措辞请求相同越权资料则不应绕过策略。语义判定不一定简单,因此确定性限额仍然有必要。

暂停、取消与完成是不同终态

缺资料时可以 waiting_input;用户取消时是 cancelled;预算不足而产物未通过时是 incomplete;只有交付契约满足时才是 completed。不要把所有循环退出都映射为成功。

取消一个等待中的 Python 协程,并不自动停止远端任务或已经启动的子进程。进程管理需要记录句柄或进程组,远端系统需要取消接口和操作查询;无法停止时还要记录仍可能发生的副作用。

配套实战把报告状态分为候选、验证失败和完成。即使模型最后说“完成”,入口仍会重新检查实际文件。理解这一点后,Artifact 与 Verifier就不再是附加的检查步骤,而是结束任务的依据。


分享这篇文章:

上一篇
Workspace 怎样设计:让输入、修改和交付物都有明确位置
下一篇
Sandbox 为什么必要:文件、进程、网络与资源怎样隔离