1. 多步任务为什么总在第 5 步崩掉如果你用 AI Agent 做过超过 3 步的自动化任务大概率遇到过这种场景让它改一个文件准得很让它连续做 5 件事中间就开始出岔子。不是忘了前面的指令就是把已经完成的步骤又跑了一遍。我拿一条 5 步内容流水线做过实验——搜集热点、筛选选题、写初稿、改写润色、格式化输出——跑 20 次只有 8 次完整跑通成功率 40%。问题不在模型不够聪明而在于它没有可靠的记忆。每一步的状态都塞在上下文窗口里窗口一满就触发压缩早期指令被摘要掉关键信息直接蒸发。我实测过一个 8 步任务到第 6 步时 Agent 已经完全忘记了第 2 步的输出文件路径。除此之外还有两个坑一是没有去重机制失败重跑时不知道哪些步骤已完成重复搜索、重复写文件涉及发布操作时甚至重复发文二是步骤之间靠口头传话前一步的输出通过聊天上下文传给下一步信息在传递中被篡改、遗漏、重新解读。这篇要讲的思路叫文件即状态不让 Agent 把进度存在脑子里而是写到磁盘上的文件里。每一步开始前先读文件了解当前进度执行完更新文件记录结果。上下文窗口再怎么压缩文件内容不会丢失败重跑时读文件就知道从哪继续步骤之间的信息传递有据可查。下面给出 5 个核心文件的设计、可复制的 Python 状态管理器以及把成功率从 40% 拉到 90% 的完整配置。2. TaoToken 前置准备拿到可用的 API Key整套方案要跑起来Agent 需要一个稳定的模型调用入口。我用的是 TaoToken它的接口兼容主流协议Python 里直接换 base_url 就能接上不用改业务代码。先到官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册账号然后在控制台创建 API Key。控制台地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite Key 管理页面在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。创建时建议按项目命名比如agent-pipeline方便后面排查是哪个流水线在消耗额度。API 的基础地址是 https://taotoken.net/api 注意这个地址不带任何查询参数直接填进 SDK 的 base_url 即可。如果你要接 Claude Code 这类编码 Agent可以参考 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里的接入说明长期跑编码或 Agent 任务的话Coding Plan 页面 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 有更划算的套餐适合高频调用场景。拿到 Key 之后先做一次最小验证确认链路通import os from openai import OpenAI client OpenAI( api_keyos.environ[TAOTOKEN_API_KEY], base_urlhttps://taotoken.net/api ) resp client.chat.completions.create( modelclaude-sonnet-4-20250514, messages[{role: user, content: 只回复两个字就绪}] ) print(resp.choices[0].message.content)把 Key 写进环境变量而不是硬编码在脚本里这是后面所有步骤的前提。export TAOTOKEN_API_KEY你的key之后状态管理器里的模型调用就能直接复用这个客户端。3. 五个核心文件的可复制配置整套方案只需要 5 个文件全部放在一个状态目录里默认/tmp/pipeline_state。下面逐个给出结构和用途。3.1 run_state.json流水线进度表这个文件记录当前执行到哪一步、每步状态是什么。Agent 每次启动第一件事就是读它如果current_step是 3就知道前两步已完成直接从第 3 步开始。{ pipeline_id: content_gen_20260520, status: running, current_step: 3, steps: { 1_collect: { status: completed, output_file: /tmp/raw_topics.json, finished_at: 2026-05-20T10:01:2308:00 }, 2_filter: { status: completed, output_file: /tmp/filtered_topics.json, finished_at: 2026-05-20T10:03:4508:00 }, 3_draft: { status: running, started_at: 2026-05-20T10:04:0008:00 }, 4_rewrite: { status: pending }, 5_format: { status: pending } }, total_tokens_used: 8500 }3.2 dedupe_index.json防重复执行每个步骤执行前先查这个文件里有没有对应的幂等键有就跳过。幂等键规则是{步骤名}_{日期}同一天同一步骤只执行一次需要强制重跑时手动删掉对应键即可。{ 1_collect_20260520: { executed_at: 2026-05-20T10:01:2308:00, output: /tmp/raw_topics.json, checksum: a3f2b8c1 }, 2_filter_20260520: { executed_at: 2026-05-20T10:03:4508:00, output: /tmp/filtered_topics.json, checksum: d7e4f1a9 } }3.3 handoff.md步骤间的交接文档这个文件解决口头传话问题。每步完成后把关键信息写进去下一步开始前先读它而不是在上下文里翻找前面的对话。## Step 1 → Step 2 交接 搜集到 15 条热点保存在 /tmp/raw_topics.json 其中 AI Agent 相关 6 条MCP 相关 3 条其余杂项 建议优先筛选 AI Agent 方向近期流量最高 ## Step 2 → Step 3 交接 筛选后保留 3 条选题 1. 文件即状态架构热度最高 2. Claude Code 上下文管理技巧实操性强 3. MCP Server 开发入门内容方向匹配 最终选定选题1搜索量最大且有代码可写3.4 execution_log.jsonl执行日志每行一条 JSON 日志记录操作时间、结果、消耗 tokens。出问题直接 grep 排查不用翻聊天记录。{ts:2026-05-20T10:01:0008:00,step:1_collect,action:web_search,query:AI Agent 热点,tokens:1200,result:success,items:15} {ts:2026-05-20T10:01:2008:00,step:1_collect,action:file_write,path:/tmp/raw_topics.json,tokens:100,result:success} {ts:2026-05-20T10:03:0008:00,step:2_filter,action:filter_topics,input_count:15,output_count:3,tokens:800,result:success}3.5 last_success.json成功快照记录最近一次完整跑通时的状态。流水线跑到一半出问题可以回退到上次成功状态而不是从头来。{ pipeline_id: content_gen_20260519, completed_at: 2026-05-19T10:15:0008:00, total_tokens: 12000, outputs: { final_article: /tmp/article_20260519.md, formatted_html: /tmp/article_20260519.html } }4. Python 状态管理器与恢复验证4.1 状态管理器实现下面这个类大概 100 行直接拿去用。核心方法有四个is_done查重、mark_done标记完成、handoff写交接、log记日志。import json import os from datetime import datetime class PipelineState: def __init__(self, pipeline_id, state_dir/tmp/pipeline_state): self.pipeline_id pipeline_id self.state_dir state_dir os.makedirs(state_dir, exist_okTrue) self.state_file f{state_dir}/run_state.json self.dedupe_file f{state_dir}/dedupe_index.json self.handoff_file f{state_dir}/handoff.md self.log_file f{state_dir}/execution_log.jsonl def load_state(self): if os.path.exists(self.state_file): with open(self.state_file) as f: return json.load(f) return {pipeline_id: self.pipeline_id, status: new, current_step: 0, steps: {}} def save_state(self, state): with open(self.state_file, w) as f: json.dump(state, f, ensure_asciiFalse, indent2) def is_done(self, step_key): if not os.path.exists(self.dedupe_file): return False with open(self.dedupe_file) as f: index json.load(f) return step_key in index def mark_done(self, step_key, output_pathNone): index {} if os.path.exists(self.dedupe_file): with open(self.dedupe_file) as f: index json.load(f) index[step_key] { executed_at: datetime.now().isoformat(), output: output_path } with open(self.dedupe_file, w) as f: json.dump(index, f, ensure_asciiFalse, indent2) def handoff(self, from_step, to_step, message): with open(self.handoff_file, a) as f: f.write(f\n## {from_step} → {to_step} 交接\n\n{message}\n) def read_handoff(self): if os.path.exists(self.handoff_file): with open(self.handoff_file) as f: return f.read() return def log(self, step, action, **kwargs): entry {ts: datetime.now().isoformat(), step: step, action: action} entry.update(kwargs) with open(self.log_file, a) as f: f.write(json.dumps(entry, ensure_asciiFalse) \n)4.2 单步执行模板每个步骤都套用同一个模板先查重没做过才执行执行完标记、写交接、记日志、更新总状态。from datetime import datetime pipe PipelineState(content_gen_20260520) state pipe.load_state() today datetime.now().strftime(%Y%m%d) step_key f1_collect_{today} if pipe.is_done(step_key): print(步骤1已完成跳过) else: results do_search(AI Agent 热点) # 你的实际业务函数 save_json(/tmp/raw_topics.json, results) pipe.mark_done(step_key, /tmp/raw_topics.json) pipe.handoff(Step1, Step2, f搜集到{len(results)}条热点保存在/tmp/raw_topics.json) pipe.log(1_collect, web_search, itemslen(results), resultsuccess) state[current_step] 2 state[steps][1_collect] {status: completed} pipe.save_state(state)4.3 恢复验证动作写完状态管理器后必须验证中断后能恢复这个核心能力。做法很简单跑到第 3 步时手动 kill 进程然后重新启动看它是否从第 3 步继续而不是从第 1 步重来。# 模拟中断后重启 pipe PipelineState(content_gen_20260520) state pipe.load_state() print(f当前进度第 {state[current_step]} 步) # 读取交接文档恢复上下文 handoff pipe.read_handoff() print(交接信息) print(handoff[-500:]) # 只看最近一段避免 token 浪费 # 从 current_step 继续执行 resume_from state[current_step] print(f从第 {resume_from} 步继续)实测下来恢复验证通过的标准是重启后current_step与中断前一致dedupe_index.json里已完成的步骤键存在且 Agent 不会重复调用已完成的搜索或写文件操作。如果重启后从第 1 步重来说明save_state没在每步结束后调用检查一下是不是漏了。5. 本篇常见错误排查5.1 JSON 文件被 Agent 写坏Agent 有时会往 JSON 里写不合法内容比如末尾多一个逗号或者把注释写进去读取时直接json.JSONDecodeError。解决办法是每次写入前先json.dumps()序列化一遍确保合法读取时加 try-except解析失败就回退到last_success.json。def safe_load_json(path, fallbackNone): try: with open(path) as f: return json.load(f) except (json.JSONDecodeError, FileNotFoundError): return fallback or {}5.2 并发写入冲突两个 Agent 同时操作同一个run_state.json后写入的会覆盖先写入的。个人项目用文件锁就够了fcntl.flock()在 macOS 和 Linux 上都能用。import fcntl def locked_write(path, data): with open(path, w) as f: fcntl.flock(f.fileno(), fcntl.LOCK_EX) json.dump(data, f, ensure_asciiFalse, indent2) fcntl.flock(f.fileno(), fcntl.LOCK_UN)注意并发量大时文件锁不够用得上 Redis 或数据库。但对个人项目和小团队文件锁足够。5.3 handoff.md 越写越长每次执行都往handoff.md追加跑一个月后文件好几万字Agent 读它时 token 消耗巨大。解决办法是每次新流水线开始时把旧文件归档重命名为handoff_20260519.md再创建空的新文件。5.4 幂等键设计错误如果幂等键只用步骤名不用日期第二天重跑时会被误判为已完成而跳过。正确规则是{步骤名}_{日期}需要强制重跑时手动删键。另外注意时区datetime.now()用的是本地时间跨时区部署时要统一。5.5 模型调用超时导致步骤卡死搜索或生成步骤网络超时后如果没写异常处理run_state.json会一直停在running状态。给每个步骤加超时和重试失败时把状态标为failed并记录到日志下次启动时能识别出来。try: result call_model_with_timeout(prompt, timeout60) pipe.mark_done(step_key, output_path) except TimeoutError: pipe.log(1_collect, model_call, resulttimeout) state[steps][1_collect] {status: failed} pipe.save_state(state)6. 实测数据与接入建议同一条 5 步流水线无状态管理和文件即状态各跑 20 次结果对比如下指标无状态管理文件即状态完整跑通次数8/2018/20成功率40%90%平均耗时12 分钟8 分钟平均 token 消耗150009000重复执行次数平均 2.3 次0 次成功率从 40% 到 90%token 消耗降了 40%省下来的主要来自去重机制避免了重复搜索和重复生成。两次失败分别是网络超时导致搜索步骤彻底无法完成以及生成的文章质量不达标被质检步骤拦截——后者其实算正常工作不是系统故障。这套方案适合定时内容生成、数据处理流水线、多 Agent 协作以及任何超过 3 步的自动化任务单步任务和实时交互类任务不适合文件读写有延迟。如果你正在用 AI Agent 做自动化建议先在一条简单流水线上试通再扩展到复杂场景。模型调用入口统一走 TaoTokenPython 里换 base_url 就能接上配合上面的状态管理器多步任务的稳定性会有明显改善。需要验证模型输出是否稳定时可以直接在模型对话页面 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 里手动跑几轮对比长期跑编码或 Agent 任务的话Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 的额度更适合高频调用。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite API Key 在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 管理。
