65|失败可恢复:断点续跑与任务日志可重放
在前面的实战中,我们为 Agent 打造了状态机大脑,配备了安全的抓取和读写工具。
现在,你给 Agent 下达了终极任务:“去这 100 个竞争对手的网站上抓取价格信息,生成一份对比报告。”
这是一个典型的长周期任务。由于大模型生成速度慢、网络请求容易超时,这个任务可能需要跑整整 2 个小时。
在第 1 小时 50 分的时候,你电脑休眠了,或者 OpenAI 的接口突然挂了 10 秒钟。
如果没有做特殊的工程处理,你的终端会弹出一片红色的报错,程序彻底退出。
更让人绝望的是,因为之前的抓取数据都存在内存里,随着程序退出,这 90 个网站的劳动成果全部灰飞烟灭。你想拿到报告,只能从第 1 个网站重新再跑两个小时。
本篇是项目 C(多步骤自动化助手)的收官之作。我们将把卷 5 讲过的“事件溯源”理论化为代码实战,让你的 Agent 拥有“存档复活”的能力。
1. 为什么会死?告别对内存的幻想
导致长任务从头再来的根本原因,是我们在写代码时,习惯把中间状态(如已抓取的网页内容、大模型的对话历史)保存在一个 Python 的list或dict变量里。
铁律:内存是极度不可靠的。在 Agent 编程中,任何有价值的思考过程和工具执行结果,必须在生成的第一时间写入硬盘(数据库或日志文件)。
2. 怎么“存档”?建立任务事件日志(Event Log)
我们不需要把内存里的所有东西都存下来,我们只需要记录 Agent“做过什么关键决策”。
在代码实现时,我们引入一个叫EventLogger的组件。
每当 Agent 的状态机发生流转,就向本地的一个 JSONL(每行一个 JSON)文件中追加写入一条记录。
存档的三个关键节点:
- 任务规划完成时:存下要抓取的 100 个 URL 列表。
- 每次工具调用成功时:存下抓取到的网页关键数据(存入本地文件,日志中只记文件路径)。
- 遇到严重报错时:存下当前失败的 URL 和报错原因,然后将任务状态标记为
Suspended(挂起)。
3. 本篇产出:可重放的任务事件日志(实战模板)
这就是我们在项目里真实生成的事件日志task_job_001.jsonl。
注意看它是如何一步步记录“作案痕迹”的,并且看它是如何在第 3 步崩溃挂起的。
{"timestamp": "2024-05-25T10:00:00", "type": "PLANNING", "payload": {"status": "success", "targets": ["site_A.com", "site_B.com", "site_C.com"]}} {"timestamp": "2024-05-25T10:05:00", "type": "TOOL_CALL", "payload": {"tool": "fetch_webpage", "target": "site_A.com", "status": "success", "saved_path": "/tmp/site_A_data.txt"}} {"timestamp": "2024-05-25T10:10:00", "type": "TOOL_CALL", "payload": {"tool": "fetch_webpage", "target": "site_B.com", "status": "error", "error_msg": "API Rate Limit Exceeded", "retry_count": 3}} {"timestamp": "2024-05-25T10:10:05", "type": "SUSPEND", "payload": {"reason": "连续3次请求失败,挂起任务等待恢复", "last_successful_target": "site_A.com"}}4. 怎么“复活”?断点续跑与日志重放
第二天早上,网络恢复了。你重新启动 Agent 脚本。
此时,Agent 的启动逻辑不再是“从头开始问大模型”,而是:
- 读取存档:去检查有没有未完成的
task_job_001.jsonl。 - 状态重放(Replay):顺着日志读下来,系统发现:“哦,计划已经做好了,不需要重新规划了。
site_A.com已经抓完了,数据在/tmp目录下,把它加载到当前上下文中。” - 精准续跑:日志最后停在了
site_B.com的报错挂起上。系统直接将大模型的状态恢复到这里,对它说:“你昨天在抓取site_B.com时遭遇了限流,现在网络恢复了,请继续你的任务。”
就这样,Agent 跳过了前面 1 个小时的漫长工作,直接从报错的断点处原地复活,丝滑地把剩下的任务做完了!
总结与复盘(项目 C 收官)
恭喜你!在项目 C:多步骤自动化助手(Agent + 工具)中,你真正跨入了大模型工程化的深水区:
- [第63篇] 状态机:你用代码画出了 ReAct 循环的轨道,懂得了用
Max_Steps给发疯的 AI 踩刹车。 - [第64篇] 工具与权限:你给大模型装上了“手脚”,并学会了用 Schema 规范它的动作,用沙箱和工作目录限制它的破坏力。
- [第65篇] 失败可恢复:你抛弃了对内存的幻想,用事件日志(Event Sourcing)实现了工业级系统的标配——断点续跑。
现在的这个 Agent,不再是聊天框里那个聊着聊着就忘事的“玩具”,而是一个能够挂在服务器后台、不知疲倦、跌倒了也能自己爬起来接着干的赛博打工人!
下一步去哪儿?
在这个项目里,你的工具(抓网页、写文件)都是写死在当前代码里的。
如果明天公司里的另一个团队也想用你写的这个“极度防呆、自动过滤广告的 Web Fetcher”工具,他们该怎么用?难道要把你的 Python 代码复制粘贴一份过去吗?
这太原始了!
接下来的项目 D:团队可复用技能包(Skills),我们将教你如何把这些好用的能力打包、封装,变成可以在整个团队甚至跨平台共享的超级组件!