Agent 长任务跑到第十步失败就前功尽弃从头再来,烧的 token 全白费还把已发的通知又发一遍:多步骤 Agent 状态持久化与可恢复避坑复盘

我们做了个 AI Agent 自动化处理一类运营任务,一个任务不是一步到位的而是要走十几个步骤,调好几个工具夹杂着好几轮大模型的决策环环相扣把一件复杂的事办完。开发时跑单个任务行云流水效果惊艳,可一上线开始批量地跑大量任务问题就来了:这十几步里只要有任何一步出了岔子,下游接口偶尔超时大模型偶尔抽风网络偶尔抖动,整个任务就直接失败从头再来,前面十几步辛辛苦苦跑出来的成果还烧了不少 token 全部清零白干。而更让我后背发凉的是重跑的时候前面那些已经产生了副作用的步骤,比如某一步已经给用户发过通知了某一步已经往数据库写过数据了又被重新执行了一遍,于是通知发了两遍数据写了两份。这次事故让我意识到做 AI Agent 时一个被严重低估的工程问题:我们把 Agent 执行一个多步骤长任务的过程当成了一个不可中断不可恢复要么全部成功要么从头重来的黑盒,可现实是在分布式依赖大模型和多个外部工具的环境里某一步偶尔失败几乎是必然的,而我们完全没有为这种中途失败做任何准备。这篇文章从这次 Agent 长任务中途失败前功尽弃还重复副作用的事故出发,讲透多步骤 Agent 可靠性避坑:把执行看成有状态工作流而非无状态函数、持久化每步状态支持断点续跑、让有副作用的步骤幂等根治重复、用 Temporal 和 LangGraph 等成熟工作流引擎别重造轮子、让长任务执行可观测,以及一个根本认知——AI Agent 的智能之外是扎实的工程,决定它是玩具还是可靠系统的往往不是它多聪明而是背后工程多扎实。

我们做了个 AI Agent,自动化处理一类运营任务。一个任务不是一步到位的,而是要走十几个步骤——调好几个工具、夹杂着好几轮大模型的决策,环环相扣地把一件复杂的事办完。开发时跑单个任务,行云流水,效果惊艳。可一上线、开始批量地跑大量任务,问题就来了:这十几步里,只要有任何一步出了岔子(下游接口偶尔超时、大模型偶尔抽风、网络偶尔抖动),整个任务就直接失败、从头再来——前面十几步辛辛苦苦跑出来的成果(还烧了不少 token)全部清零,白干。而更让我后背发凉的是:重跑的时候,前面那些"已经产生了副作用"的步骤——比如某一步已经给用户发过通知了、某一步已经往数据库写过数据了——又被重新执行了一遍,于是通知发了两遍、数据写了两份。

这次事故,让我意识到做 AI Agent 时一个被严重低估的工程问题:我们把 Agent 执行一个"多步骤长任务"的过程,当成了一个"不可中断、不可恢复、要么全部成功要么从头重来"的黑盒;可现实是,在分布式、依赖大模型和多个外部工具的环境里,"某一步偶尔失败"几乎是必然的——而我们的 Agent,完全没有为这种"中途失败"做任何准备,既不能从断点接着跑、也防不住重跑带来的重复副作用。说白了,我们只设计了 Agent "一帆风顺时怎么把事办成",却完全没设计它"中途摔了一跤后怎么爬起来"。这篇文章,就从这次"Agent 长任务中途失败、前功尽弃还重复副作用"的事故讲起,聊聊做"多步骤 Agent"时,那个比"让它能跑通"更重要的工程课题:让它的执行过程,变得可持久化、可恢复、可重试。

故障现场:一个"摔一跤就从头再来"的长任务

先把这个"脆弱"的执行模式还原一下:

# 脆弱的写法: 多步任务一气呵成, 中途任何一步失败, 整个任务就崩了、从头来
def run_task(task):
    data = step1_fetch(task)          # 第1步: 拉数据
    enriched = step2_llm_analyze(data) # 第2步: 大模型分析(烧token)
    step3_write_db(enriched)          # 第3步: 写数据库(有副作用!)
    step4_send_notify(task.user)      # 第4步: 发通知(有副作用!)
    result = step5_llm_summarize(...)  # 第5步: 大模型总结(烧token)
    # ... 还有十几步 ...
    step12_finalize(result)
    # 问题: 假如跑到第10步时, 某个下游超时抛异常了
    #   → 整个 run_task 失败, 抛出异常
    #   → 重试时, 又从 step1 开始! 前面9步白跑(token白烧)
    #   → 而 step3/step4 等有副作用的步骤被重复执行 → 重复写库、重复发通知!

看出这个执行模式有多脆弱了吗?整个任务被写成了一个"线性的、一气呵成的"函数,十几步首尾相连,中间没有任何"存档点"。这种写法,在"全程顺利"时毫无问题;可一旦中途某一步抛了异常(在依赖大模型和一堆外部工具的环境里,这几乎无法避免),整个函数就崩了。而崩了之后的重试,只能是"再调一次 run_task",也就是从第一步重新开始——前面已经成功跑完的那些步骤(包括那些烧了真金白银 token 的大模型调用),成果全部丢失、被迫重来。

而"重复副作用"则是雪上加霜的第二重伤害:那些"有副作用"的步骤(写数据库、发通知、调用会改变外部状态的接口),在重跑时会被再执行一遍。第一次跑到第 10 步失败了,前 9 步里的"发通知"已经发出去了;重试从第 1 步开始,跑到第 4 步又发了一次通知——用户就收到了两遍。如果这个副作用是"扣款""下单""发券"呢?那就是重复扣款、重复下单的资损事故了。这就是把"多步骤长任务"当成"不可恢复的原子黑盒"的双重代价:失败即前功尽弃(浪费),重跑即重复副作用(危险)。任务步骤越多、越长、越依赖不稳定的外部因素(大模型、网络),这两重代价就越触目惊心。

第一件事:把 Agent 的执行,看成"有状态的工作流",而非"无状态的函数调用"

要解决这个问题,需要一次根本的思维转变:不要把 Agent 执行一个长任务,看成"调用一个会一口气跑完的函数";而要把它看成"推进一个有状态的工作流(workflow)"——这个工作流由一系列步骤组成,每一步的执行状态和产出,都应该被持久化地记录下来,使得整个任务的进度是"可观测、可保存、可从中间某一步恢复"的。

两种心智模型, 决定了 Agent 的健壮性:

  模型A(脆弱): 把长任务看成"一个一气呵成的函数调用"
    run_task() —— 要么整个成功, 要么整个失败从头来; 无状态、不可恢复

  模型B(健壮): 把长任务看成"一个有状态的工作流"
    每一步都是工作流的一个"节点", 每步的状态(待执行/已完成/失败)
    和产出都被持久化记录; 任务的"进度"是一个可保存、可查询的状态。
    → 失败了, 从"上次成功的那一步之后"继续, 而非从头来。
    → 这就是"工作流/状态机"的思想, 也是所有'可靠的长流程'的基石。

这个思维转变是后面一切解法的基础。"函数调用"模型,把长任务看成一个不可分割的、无记忆的黑盒——它在内存里跑,跑完了状态就没了,中途崩了就什么都没留下,只能从头来。而"工作流"模型,把长任务看成一个"有进度、有状态、状态被持久化保存"的过程——它知道自己跑到了第几步、每一步的结果是什么,这些信息被存在外部(数据库/状态存储)而非仅仅在内存里,所以即便进程崩了、机器挂了,这个"进度"也还在,下次可以接着上次的进度继续跑。这正是工作流引擎、状态机、以及一切"可靠的长流程系统"背后的核心思想。对一个会执行多步骤长任务的 Agent 来说,从"函数调用"心智升级到"有状态工作流"心智,是让它从"脆弱的玩具"走向"可靠的生产系统"的关键一步。我那次的根本问题,正是停留在了"函数调用"的脆弱模型里。

第二件事:正解之一——持久化每一步的状态,支持断点续跑

第一个解法,是把"任务进度"持久化下来,让任务可以从中断的地方"续跑"。具体做法:把任务拆成明确的步骤,每完成一步,就把"我已经跑到第几步了、这一步的产出是什么"记录到持久化存储(数据库)里;失败重试时,先读出"上次跑到哪了",从下一步接着跑,而不是从头来。

# 正解1: 持久化任务进度, 支持从断点续跑
STEPS = [step1_fetch, step2_llm_analyze, step3_write_db,
         step4_send_notify, step5_llm_summarize, ...]  # 步骤列表

def run_task(task_id):
    state = load_state(task_id)        # 从数据库读出任务进度(跑到第几步、各步产出)
    for i, step in enumerate(STEPS):
        if i < state.current_step:     # 这一步之前已经成功跑过了
            continue                   # → 跳过, 不重复执行(关键: 续跑而非重来)
        try:
            output = step(state)       # 执行当前步
            state.outputs[i] = output  # 记录这一步的产出
            state.current_step = i + 1 # 进度推进一步
            save_state(task_id, state) # ★ 每步成功后, 立刻持久化进度!
        except Exception as e:
            save_state(task_id, state) # 失败时也存一下当前进度
            raise                      # 抛出, 等待重试 —— 下次会从 current_step 续跑
    mark_done(task_id)
# 现在: 跑到第10步失败 → 进度停在 step=9 → 重试时从第10步接着跑, 前9步不再重跑!

这个方案的精髓,在那个不起眼的 save_state——每成功跑完一步,就立刻把"当前进度"持久化到数据库。这样,任务的进度就不再只活在内存里(进程一崩就没了),而是被稳稳地存在了外部存储里。于是,重试时只要先 load_state 读出"上次跑到第几步",就能直接跳过那些已经成功的步骤、从断点接着跑——把"失败即从头重来"变成了"失败即从断点续跑"。前面那些昂贵的(烧 token 的大模型调用)、耗时的步骤的成果,都被保住了,不必白白重跑。这就是"状态持久化 + 断点续跑"的威力:它让一个长任务,即便中途失败、即便进程崩溃、即便机器重启,也能不丢进度地、优雅地从中断处继续——这是所有可靠的长流程系统的标配能力。我把"无状态重跑 vs 有状态续跑"的对比画成图:

这张图把两种模式的差距摆得很清楚:无状态模式下,一次失败就让前面所有努力清零;有状态模式下,持久化的进度让每一步的成果都被牢牢守住,失败只损失"出错的那一步",而非"整个任务"。任务越长、单步越贵,这种"续跑"能力省下的浪费就越惊人。

第三件事:正解之二——让每一步"幂等",根治重复副作用

断点续跑解决了"前功尽弃"(浪费)的问题,但还有"重复副作用"(危险)这一重。即便有了续跑,也仍可能出现"某一步执行了一半、产生了副作用、但还没来得及标记完成就崩了,重试时这一步又被执行一遍"的情况。所以,对那些"有副作用"的步骤,还必须叠加一道保险——幂等:让每一步即便被重复执行,也不会产生重复的副作用。

# 正解2: 有副作用的步骤要幂等, 即便被重复执行也不会重复产生副作用
def step4_send_notify(state):
    task_id = state.task_id
    # 用一个唯一键(task_id + 步骤名)防重: 先检查"这个通知发过没"
    if notify_log.exists(task_id, "step4_notify"):
        return  # 已经发过了, 直接跳过, 不重复发
    send_notify(state.user)                       # 真正发通知
    notify_log.record(task_id, "step4_notify")    # 记一笔"已发送"

# 同理, 写库用唯一约束/upsert, 扣款用幂等键... 让"做一次"和"做多次"效果相同

幂等是应对"重复执行"的终极保险,它的核心是让一个操作"做一次"和"做多次"的最终效果完全相同(这一点之前聊定时任务、聊重试时都强调过)。对 Agent 的有副作用步骤,常见的幂等手段就是:用一个唯一标识(比如"任务ID + 步骤名")去登记"这一步是不是已经做过了",做之前先查、做完了再记;已经做过的就直接跳过。这样,无论这一步因为重试被执行多少次,真正的副作用(发通知、写库、扣款)都只会发生一次。"断点续跑"和"步骤幂等",是处理 Agent 长任务可靠性的一对黄金搭档:续跑保证"不浪费"(失败的成本最小化)、幂等保证"不重复"(失败的危害最小化)。两者结合,才能让一个 Agent 长任务,真正做到"中途失败也不慌"——失败了,从断点续跑,且即便有步骤被重跑,也因为幂等而无害。这也呼应了一个分布式系统的根本心法:在一个"失败必然会发生"的环境里,你的设计目标不该是"追求不失败"(做不到),而该是"让失败的代价和危害,都尽可能小"。

第四件事:别重造轮子——用成熟的工作流/编排框架

"持久化状态 + 断点续跑 + 步骤幂等"这套东西,自己手写一套是可行的(就像前面的代码),但当任务复杂起来(有分支、并行、子任务、定时、重试策略、人工介入……),自己造的"轮子"就会越来越笨重、越来越容易出错。这时候,更明智的做法是:站在巨人的肩膀上,用成熟的工作流编排框架/引擎来管理 Agent 的长任务执行。

管理 Agent 长任务, 可借助的成熟方案:

1. 持久化工作流引擎: Temporal、Cadence 等
   —— 天生为"长流程、可恢复、可重试"而生; 它们自动帮你做状态持久化、
      失败恢复、续跑、重试策略, 你只管把业务步骤写出来。

2. Agent/LLM 编排框架: LangGraph、各类 Agent 框架的"图/状态机"模式
   —— 把 Agent 的执行建模成"状态图", 支持持久化每个节点的状态、
      支持从中断处恢复(checkpoint)、支持人工介入(human-in-the-loop)。

3. 消息队列 + 任务表: 朴素但有效
   —— 每一步作为一个消息/任务, 处理完才确认; 失败的消息会被重投。

核心: 这些方案都在帮你解决同一个问题 ——
      让"多步骤、易失败、需恢复"的长流程, 变得可靠。

这里的关键判断是:"让长流程变得可靠(可持久化、可恢复、可重试)"是一个非常普遍、非常成熟的工程问题,业界已经有了一批专门的、久经考验的解决方案——你大可不必从零造轮子。像 Temporal、Cadence 这类"持久化工作流引擎",就是专门为"长流程、可恢复"而生的,它们会自动帮你处理状态持久化、失败恢复、续跑、重试退避等一大堆脏活;而专门面向 Agent/LLM 的编排框架(如 LangGraph),则把"Agent 执行"建模成可持久化、可恢复的"状态图",还支持"人工介入"(human-in-the-loop,在某些关键步骤暂停、等人确认后再继续)这种 Agent 场景特有的需求。所以,当你要做一个严肃的、跑在生产环境的多步骤 Agent 时,优先考虑用这些成熟框架来承载它的执行,而不是自己手搓一套脆弱的状态管理——把精力花在"业务步骤怎么设计"上,把"可靠执行"这件苦差事交给专业的引擎。

第五件事:可观测性——让长任务的执行"看得见"

最后一个常被忽略、却极其重要的方面是可观测性。一个走十几步、还夹杂着大模型决策的长任务,如果它的执行过程是个黑盒,那一旦出问题,你根本不知道它卡在哪、为什么失败、每一步的输入输出是什么——排查起来寸步难行。所以,必须让 Agent 长任务的执行过程"看得见"。我把做一个"可靠的 Agent 长任务"需要的关键能力,整理成一张表:

能力 解决什么 怎么做
状态持久化 失败不丢进度 每步进度/产出存数据库
断点续跑 失败不从头重来 读进度, 跳过已完成步骤
步骤幂等 重跑不产生重复副作用 唯一键去重 / upsert
重试策略 瞬时失败自动恢复 退避重试 + 限次 + 区分可重试错误
可观测性 出问题能定位 记录每步的输入输出/耗时/状态
人工介入 关键步骤要人确认 在节点暂停, 等审批再继续

可观测性这件事,对 Agent 长任务尤其重要——因为它的执行链路长、还掺杂着大模型这个"不确定的黑盒",出错时如果没有详细的执行记录,你连"它到底在第几步、因为什么失败、当时大模型的输入输出是什么"都搞不清楚。所以,要把每一步的"输入、输出、状态、耗时、(如果调了大模型)提示词和返回"都记录下来,让整个长任务的执行过程,变成一条可以回溯、可以审查的清晰轨迹。这样,任务失败时,你能一眼看到它卡在哪一步、为什么;任务结果不对时,你能顺着每一步的输入输出,定位是哪一步的决策出了问题。把 Agent 长任务的可靠性要素汇总成一张"失败模式 → 对策"的对照表:

失败模式 后果(无防护) 对策
中途某步失败 整个任务从头重来 状态持久化 + 断点续跑
重跑有副作用的步 重复发通知/扣款/写数据 步骤幂等
瞬时抖动(超时/网络) 本可恢复却直接失败 退避重试
执行黑盒, 出错难查 排查无从下手 记录每步输入输出, 可观测
关键步骤需人把关 Agent 擅自做了危险动作 human-in-the-loop 人工介入

这张表几乎就是一份"把 Agent 长任务从脆弱玩具改造成可靠系统"的施工清单。它们共同指向一个核心理念:一个生产级的 Agent,不只要"能把事在顺利时办成",更要"能在各种不顺利(某步失败、需要重跑、需要人把关)的情况下,依然稳健地、不出乱子地推进"。而这后一半——对"不顺利"的周全应对——恰恰是把 Agent 从 Demo 带到生产最关键、也最容易被忽视的工程量所在。

一张"设计可靠 Agent 长任务"的决策图

把这次踩坑沉淀成一张图。每当你要做一个"多步骤的 Agent 长任务"时,照着它把可靠性设计好:

这张图把"可靠 Agent 长任务"的设计要点串了起来:建模成有状态工作流、每步持久化、失败续跑、有副作用的步骤幂等、复杂的用成熟引擎、全程可观测。简单短任务不必过度设计,但凡是步骤多、链路长、依赖大模型和外部工具的严肃 Agent 任务,这套设计就是它从"Demo"走向"生产可靠"的必经之路。

我立下的几条 Agent 长任务规矩

这次"Agent 长任务前功尽弃"的事故后,团队做多步骤 Agent 的规范里加了这么几条:

  1. 长任务建模成有状态工作流:多步骤任务别写成一气呵成的函数,建模成有持久化状态、可恢复的工作流。
  2. 每步进度必持久化:每完成一步就把进度和产出存到外部存储,让任务进度不随进程崩溃而丢失。
  3. 失败从断点续跑:重试时跳过已成功的步骤,从断点接着跑,保住前面昂贵步骤(尤其大模型调用)的成果。
  4. 有副作用的步骤必幂等:发通知、写库、扣款等有副作用的步骤用唯一键去重,保证重跑不产生重复副作用。
  5. 复杂任务用成熟引擎:严肃的、长期维护的长任务,用 Temporal/LangGraph 等成熟工作流引擎,别手搓脆弱的状态管理。
  6. 全程可观测:记录每步的输入、输出、状态、耗时(含大模型的提示词与返回),让执行过程可回溯、可排查。
  7. 关键步骤可人工介入:涉及高危/不可逆动作的步骤,支持暂停等人工确认(human-in-the-loop)再继续。

这几条里,第一条是纲。我最大的收获,是认识到:做一个会跑长任务的 Agent,真正的难点和工程量,根本不在"让它顺利时能把事办成"(那部分往往出乎意料地简单),而在"让它在中途失败、需要恢复、需要重试、需要人把关的种种不顺利下,依然稳健可靠"。前者是"happy path",写起来很爽、Demo 很惊艳;后者是"failure path",琐碎、不性感,却是决定一个 Agent 能不能真正放到生产里用的关键。我那次的惨痛,正是只把 happy path 做漂亮了,却对 failure path 毫无准备——于是一上生产、一遇到必然会发生的中途失败,就原形毕露。把对"失败路径"的周全设计,看得和"成功路径"一样重、甚至更重,是做生产级 Agent(乃至一切生产级系统)的成熟标志。

写在最后:AI Agent 的"智能"之外,是扎实的"工程"

这次做 Agent 长任务踩的坑,和我之前做 RAG、做 Agent 工具设计踩的坑,在我心里又一次汇成了同一个越来越坚定的认识:一个 AI Agent 最终能不能在生产环境里靠谱地用起来,大模型本身的"智能"固然是前提,但真正决定成败的,往往是包裹在这份智能之外的那一整套"传统工程"——状态管理、失败恢复、幂等、重试、可观测、编排……这次的事故尤其能说明问题:让 Agent "聪明地决策出该走哪十几步"是大模型的功劳,可让这十几步"在中途失败时不前功尽弃、不重复副作用地可靠跑完",靠的全是工作流、持久化、幂等这些和 AI 一点关系都没有的、扎扎实实的传统工程。大模型给了 Agent "会思考、会决策"的大脑,但要让这个大脑驱动的行动,在真实世界里可靠地落地、经得起失败的考验,靠的是工程为它搭建的、坚实的"骨骼和神经系统"。

想通这一点,我对"AI 应用工程师"这个角色的价值,愈发笃定。在大模型能力越来越强、越来越普及的今天,"会调用大模型让它做决策"已经不再是壁垒;真正的壁垒、真正能让你的 Agent 比别人的更可靠、更能真正解决问题的,是你能不能用扎实的工程,去驾驭和兜底大模型的"智能"——让它的决策可靠地执行、让它的失败优雅地恢复、让它的过程清晰地可观测。这恰恰是我们这些有着传统软件工程功底的人,最大的用武之地。AI 时代非但没有让"工程"贬值,反而给了"工程"一个全新的、更重要的舞台:大模型负责"想",而我们工程师,负责让这份"想",可靠地、健壮地、可控地变成现实。

所以,如果你也在做 AI Agent,我想把这次踩坑最想说的话送给你:别让大模型的"智能"光环,遮蔽了它背后那片同样关键、甚至更关键的"工程"天地。当你为 Agent 能聪明地规划出复杂步骤而惊艳时,请别忘了沉下心来,为这些步骤的"可靠执行"做好那一整套不那么性感、却决定成败的工程:让它有状态、能续跑、会幂等、可观测、有兜底。因为决定你的 Agent 是一个"演示时惊艳、上线就崩"的玩具,还是一个"经得起真实世界千锤百炼"的可靠系统的,往往不是它有多聪明,而是它背后那套工程,有多扎实。那个一遇失败就前功尽弃的长任务,最终教给我的,正是这份对"智能之外的工程"的敬重——它让我明白,在 AI 的浪潮里,最稀缺、也最有价值的,从来不只是"会用大模型",而是"能用扎实的工程,把大模型的智能,变成真正可靠、可用的产品力"。愿你我都能做那个,既仰望 AI 智能的星空、又脚踏工程实地的人。

—— 别看了 · 2026
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
技术教程

联合类型新增了一种成员却忘了在 switch 里加 case,编译一声不吭这类消息全悄悄丢了:TypeScript 穷尽性检查避坑复盘

2026-6-1 16:48:24

技术教程

先数总数日志打着共5000条、紧接着逐条处理的循环却一条没执行数据像凭空蒸发:Python 生成器只能遍历一次的避坑复盘

2026-6-1 17:00:46

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索