news 2026/7/31 13:43:33

Agent 可靠性工程实战(六):重试不是 while True,用预算状态机定义何时停

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Agent 可靠性工程实战(六):重试不是 while True,用预算状态机定义何时停

本篇复用上一篇的runs/demo-001/steer.json和此前累积的events.jsonl。转向文件提供下一次尝试的目标与失败摘要,账本提供已花费次数和重复证据;本篇新增budget.jsonmachine.py,输出state_changed事件与state.json。下一篇只会执行状态机批准的工具调用。

一、一次重试其实消耗多种预算

只设置最大轮数会掩盖不同动作的成本:读一个小文件与启动十分钟集成测试不等价,模型调用还有金额和上下文上限。预算至少分为尝试次数、墙钟时间、工具调用数和费用单位。它们任一耗尽都应停止,而且停止原因要进入账本。

状态不能由循环里几个布尔变量拼凑。显式状态包括readygeneratingcheckingretryingsucceededblockedexhausted。每个事件只允许触发有限转换,非法顺序立即暴露。例如没有check_finished就不能宣布成功,权限拒绝也不能伪装成测试失败。

{"schema_version":1,"limits":{"attempts":5,"tool_calls":20,"wall_seconds":900,"cost_units":120},"stop_on_same_failure":3}

运行输出:

limits=4 attempts=5 same_failure=3
from__future__importannotationsimportjsonfromcollectionsimportCounterfromdataclassesimportasdict,dataclassfrompathlibimportPathfromledgerimportLedger,load_events@dataclass(frozen=True)classState:name:strattempts:inttool_calls:intcost_units:intreason:strdefderive(events:list[dict],budget:dict)->State:kinds=Counter(event["kind"]foreventinevents)failures=[event["payload"].get("output_sha256")foreventineventsifevent["kind"]=="check_finished"andnotevent["payload"].get("passed")]ifany(event["kind"]=="check_finished"andevent["payload"].get("passed")foreventinevents):returnState("succeeded",kinds["attempt_started"],kinds["tool_started"],kinds["model_finished"],"checks_passed")repeated=max(Counter(failures).values(),default=0)limits=budget["limits"]ifrepeated>=budget["stop_on_same_failure"]:reason="same_failure_repeated"name="blocked"elifkinds["attempt_started"]>=limits["attempts"]:reason,name="attempts_exhausted","exhausted"elifkinds["tool_started"]>=limits["tool_calls"]:reason,name="tool_calls_exhausted","exhausted"elifkinds["model_finished"]>=limits["cost_units"]:reason,name="cost_exhausted","exhausted"else:reason,name="budget_available","retrying"returnState(name,kinds["attempt_started"],kinds["tool_started"],kinds["model_finished"],reason)defmain()->int:root=Path("runs/demo-001")events=load_events(root/"events.jsonl")budget=json.loads(Path("budget.json").read_text(encoding="utf-8"))state=derive(events,budget)(root/"state.json").write_text(json.dumps(asdict(state),indent=2),encoding="utf-8")Ledger(root/"events.jsonl","demo-001").append("state_changed",asdict(state))print(f"state={state.name}reason={state.reason}attempts={state.attempts}")return0if__name__=="__main__":raiseSystemExit(main())

运行输出:

state=retrying reason=budget_available attempts=1

二、状态应从事件推导,而不是只信 state.json

进程可能在写完事件后、更新状态文件前崩溃。如果恢复时只读state.json,会少算一次调用。事件账本是事实源,状态文件只是缓存;启动时重新推导并覆盖缓存。这个设计叫事件溯源,但不必引入框架,关键是明确哪份数据拥有最终解释权。

墙钟预算不能简单累加事件时间戳差,因为机器休眠和时钟调整会产生异常。运行中用单调时钟计量,恢复后用已提交的分段耗时求和。费用也不应拿“模型返回的估算值”直接做结算,宿主按供应商响应或本地计数器记录。

记忆点是:预算不是为了省钱才停止,而是为了让停止成为正确结果。无限重试会把不可解问题伪装成“还在努力”,占用资源且无法交接。exhausted是带证据的业务状态,不是异常堆栈。

三、重复失败为什么比轮数更有信息

五次不同失败说明循环可能在推进,三次相同摘要则说明它原地踏步。不过只比较完整输出摘要会被时间戳和临时路径扰动。检查器应先规范化非语义字段,再计算故障指纹;同时保留原始输出摘要用于审计。

相同失败也不总该停止。模型第一次读文件、第二次申请新工具,虽然检查结果相同,但信息状态发生变化。可以把“故障指纹 + 已获取证据集合”组成进展键。只有两者都不变才算无进展。这个权衡避免既烧无限预算,也过早结束有希望的探索。

四、恢复与人工接管

状态为blocked时,交接包应包含目标摘要、最后失败证据、已尝试补丁摘要、拒绝工具和剩余预算。人工补充权限或新证据后,不直接修改旧事件,而是追加human_intervention,状态机再决定能否回到retrying。历史仍然完整。

本篇产物state.json明确给出下一步是否合法。下一篇会读取其中的retryingsteer.json,为每个工具调用生成稳定call_id和回执文件;即使进程在工具执行后崩溃,恢复也不会重复产生外部副作用。

并发执行器还需要用任务级锁保护“推导后追加”这一小段,否则两个进程可能同时看到剩余一次预算并各启动一次。锁超时不等于可以强抢,恢复者必须核对持有进程与最后事件,再决定接管并记录lease_recovered。预算边界本身也是共享状态,必须像业务写入一样处理竞态。

参考来源

  • Dev.to|The Dirty Secret Behind AI Agents
  • Python 文档|time — Time access and conversions

👍 觉得有用就点个赞 + 收藏,方便回头查阅;有疑问直接在评论区留言,我看到都会回。

🚀 本文属于《Agent可靠性工程实战》系列,持续更新,关注不迷路。

📌 文章里的代码都能直接跑。想要可直接 clone 的完整工程 + 配套部署脚本 / 踩坑清单?评论一声或发邮件到cj2664@qq.com,我免费发你。
如果你正好在做类似系统、或有工程化难题想找人做,也欢迎邮件聊一句——我按实际情况评估,能落地的就接单或出方案。评论和邮件都能直接找到我,不用跳别的平台。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/31 13:41:04

中文情感分析数据集全攻略:从选型、处理到实战避坑指南

1. 项目概述:为什么你需要一份高质量的中文数据集清单 做文本分类,尤其是情感分析,你遇到的第一个、也往往是最大的拦路虎是什么?不是模型不够新,也不是算力不够强,而是 数据 。我见过太多朋友&#xff0…

作者头像 李华
网站建设 2026/7/31 13:40:29

ok-ww:鸣潮玩家的智能游戏管家,解放双手的终极解决方案

ok-ww:鸣潮玩家的智能游戏管家,解放双手的终极解决方案 【免费下载链接】ok-wuthering-waves 鸣潮 后台自动战斗 自动刷声骸 一键日常 Automation for Wuthering Waves 项目地址: https://gitcode.com/GitHub_Trending/ok/ok-wuthering-waves 你是…

作者头像 李华
网站建设 2026/7/31 13:33:38

装修不该靠吃亏换经验:RenoPit 给普通人一份看得懂的避坑清单

对很多普通家庭来说,装修是人生中少有的几次大额支出。钱是一笔一笔攒下来的,房子也可能要住十年、二十年,可真正做决定时,我们面对的却是看不懂的图纸、密密麻麻的报价和一份不敢轻易签字的合同。开源应用 fthux/RenoPit 就是为这…

作者头像 李华
网站建设 2026/7/31 13:32:08

角色先活起来,故事才会跟上

最近做了两组赛博都市人物设定。一位银发、红瞳,穿着剪裁利落的黑色长外套,手里握着一根金属手杖;另一位短发、戴耳机,白色机能夹克里藏着随时在线的终端。她们没有名字,也还没有完整的剧情。但只要把两张设定图摆在一…

作者头像 李华