企业 Agent 为什么必须有状态机

我在一线做 CRM Agent 和企业 Agent 长流程开发时,越来越确定一件事:
很多 Agent 不是不会执行,而是不知道自己执行到了哪一步。
这一篇不只讲道理,直接给一个最小实现骨架:
- 一张任务表:记录这批任务整体到哪了。
- 一张步骤表:记录每个业务对象停在哪一步。
- 一组转移规则:规定什么状态能继续,什么状态必须停。
- 一段续跑伪代码:让系统读状态,而不是让模型猜上下文。
- 五条上线检查线:避免状态机最后只变成日志。
这是《企业 Agent 工程化手记》的第 4 篇。
前三篇写了任务边界、工具治理和异常恢复。上一篇结尾我留了一句话:
要能回滚,先得有状态。一个动作执行前没留下"原来的样子",出错时就没有退路。
这篇继续往下拆:状态到底该放在哪,以及最小状态机怎么落地。
先看真实问题:11 个 Flow 断在半路
前段时间,我处理过一个很具体的任务:批量更新 11 个 live Flow 的授权配置。
单个 Flow 的动作并不复杂:
- 关闭 Flow。
- 修改 Authorization header。
- 重新开启 Flow。
- 记录成功、失败和失败原因。
麻烦在于,它不是一个动作,而是一串动作。11 个 Flow,每个都是真实的线上对象,改的也是真实的开关状态。
这种任务只要足够长,就一定可能断在某个地方。
比如第 6 个 Flow 关闭成功,header 也改了,但重新开启时接口返回 401。
这时候最危险的问题不是"怎么继续调用接口"。
真正危险的问题是:
系统知不知道第 6 个 Flow 现在处在"已关闭、已改 header、但未重新开启"?
如果不知道,Agent 只有两条路。
第一,从头重来,把前 5 个已经完成的 Flow 再关一遍、再改一遍。
第二,凭对话里的残留印象猜一个起点,从第 6 个或第 7 个继续。
这两条路都不安全。
从头重来,会制造重复副作用。猜一个起点,会承接到错误进度。
放在 Flow 上,脏的是线上开关状态。放到 CRM Agent 里,如果是批量改客户阶段、批量发提醒,脏的就是一条条真实客户数据。
这件事后来逼我形成一个习惯:只要是批量写入、长流程更新,我都会在请求最前面补一句类似的话:
失败时不要继续批量写入;先列出成功项、失败项、失败原因和当前线上状态。
这句话看起来是在优化 Prompt。
但本质上,我是在用 Prompt 替系统补状态。
补一次还行。流程一长,补不过来。
(这是我自己的 AI 协作记录,是单点经历,不是行业统计,也不是包装出来的生产事故。)
状态为什么不能放在 Prompt 里?
企业 Agent 长流程里,状态不能依赖对话上下文。
原因很简单:对话不是系统事实。
它会被截断。任务越长,早期步骤越容易被挤出上下文。
它会被重新理解。一次"已经改过"在下一轮可能被读成"准备去改"。
它会丢失。会话结束、进程重启、执行环境切换之后,上一轮对话里的进度不会自动变成系统记录。
所以我看长流程 Agent 时,不会先问模型记不记得。
我会先问三个工程问题:
- 状态存在哪?是在对话里,还是在我能查、能改、能审计的表里。
- 谁来持久化?是模型顺手写在回复里,还是每完成一步系统就落一次检查点。
- 重启后从哪读?是从上下文里猜,还是从状态记录里读出确定的下一步。
这三个问题没人负责,任务能不能跑完就基本靠运气。
只要中间不断,看起来没事。
一旦断了,就开始制造重复动作和脏数据。
最小状态机要落哪两张表?
讲状态机,很容易让人想到工作流引擎、编排框架、复杂平台。
不一定。
最小状态机可以先从两张表开始:任务表和步骤表。
它们不一定真叫这个名字,但职责要分开。
任务表:记录这一批整体到哪了
任务表管的是"这一批任务"。
它回答的问题是:这批任务是谁触发的、影响什么范围、现在整体是运行中还是暂停、是否允许继续。
我会放这些字段:
task_id:这批任务的唯一编号,后面所有步骤都挂在它下面。business_scope:影响范围,比如某个环境、某个租户、某组客户或某批 Flow。task_status:整批任务状态,比如running、paused、completed、failed。stop_policy:遇到什么情况必须停,比如"任意线上对象停在危险中间态,就暂停整批任务"。request_summary:原始意图摘要,方便事后追溯。created_by:谁触发了这批任务。locked_by和lease_until:当前谁在执行,锁什么时候过期,避免两个 Agent 同时续跑同一批任务。updated_at:最后一次状态变化时间,用来判断任务是否卡死。
这里最容易漏掉的是 stop_policy 和锁。
很多实现只做一个 status 字段,然后以为状态机就有了。
但没有停止策略,系统不知道失败后该不该继续。没有锁,系统可能在重试、手动触发、定时任务之间重复执行。
这两个字段,才是长流程进真实系统时最容易救命的部分。
步骤表:记录每个对象停在哪一步
步骤表管的是"每个业务对象"。
总任务只能告诉你"这一批还没完"。真正决定能不能断点续跑的,是每个对象自己的步骤状态。
以 11 个 Flow 为例,不能只记录"第 6 个失败"。
这个信息太粗。
你至少要知道它失败在什么阶段:
pending:还没处理。closing:正在关闭。closed:已关闭。updating_auth:正在修改授权。auth_updated:授权已修改。reopening:正在重新开启。completed:已完成。manual_required:重新开启失败,需要人处理。
为什么要拆这么细?
因为每个状态对应的恢复动作不一样。
pending 很安全,直接做就行。
completed 也很安全,跳过。
但 closed 和 auth_updated 不安全。它们说明线上对象可能已经被关掉,系统不能假装没事继续处理下一条。
这里的"不安全"不是说一定要人工处理。
它的意思是:必须先收尾当前对象。能自动继续,就把它走到 completed;不能自动继续,就暂停整批任务。
所以步骤表除了 phase,还要放这些字段:
item_id:具体业务对象,比如 Flow ID 或客户 ID。previous_snapshot:副作用发生前的关键快照,比如原始开关状态、原始 header 摘要、原始客户阶段。idempotency_key:这一步的幂等键,避免同一个副作用动作被重复提交。attempt_count:当前阶段已经尝试了几次。last_error:最后一次失败原因,保留原始错误码和简短说明。next_action:系统判断出的下一步动作,而不是让模型自由猜。updated_at:这条对象最后一次推进时间。
这里最关键的是 previous_snapshot。
它不是锦上添花。
它决定你能不能回滚。没有它,你只知道"改坏了",但不知道该恢复成什么样。
转移规则怎么写?
状态字段只是记录事实。
真正让系统稳定的是转移规则。
以单个 Flow 为例,主路径可以很窄:
pending-> closing-> closed-> updating_auth-> auth_updated-> reopening-> completed
失败路径也要明确:
closing_failed -> manual_requiredupdating_auth_failed -> manual_requiredreopening_failed -> manual_requiredunknown -> paused_for_reconcile
背后的原则只有一句:
只要一个对象可能停在"线上已改变但未恢复"的状态,就不要继续批量写下一条。
比如第 6 个 Flow 已关闭,但重新开启失败。
这时候整批任务的状态应该从 running 变成 paused,而不是继续跑第 7 个。
这不是保守。
这是把风险关在第 6 个对象里,不让它扩散到后面的 5 个对象。
续跑算法应该怎么写?
有了任务表、步骤表和转移规则,续跑逻辑反而很短。
我会把它设计成这个顺序:
读取任务记录拿锁,避免并发续跑找出未完成的步骤记录逐条读取当前 phase如果 phase 是 completed,跳过如果 phase 是可自动收尾的中间态,优先收尾当前对象如果 phase 是失败或未知状态,暂停整批任务并交给人其他 phase 只执行它对应的下一步每完成一个副作用动作,立即写检查点释放锁
这里最重要的一句话是:
让状态决定下一步,而不是让模型决定下一步。
模型可以生成执行参数、解释错误、整理报告。
但它不应该决定"第 6 个失败后是否继续第 7 个"。
这个决定应该由状态机做。
写成伪代码,大概是这样:
for item in unfinished_items(task_id): state = read_state(item) if state.phase == "completed": continue if state.phase in ["manual_required", "reopening_failed", "unknown"]: pause_batch(task_id, item, state) return handoff_to_human(task_id, item, state) action = next_action_for(state.phase) result = run_action(action, item) persist_checkpoint(item, result)
真实代码当然会更复杂。
但骨架就是这几行。
它把最危险的判断从 Prompt 里拿出来了。
Agent 不再靠"继续"两个字猜上下文,而是每次都从系统状态里读下一步。

状态藏在对话里 vs 状态存在系统里:前者会遗忘,后者可续跑
回到那 11 个 Flow
现在把这套东西放回最开始的场景。
系统每处理完一个 Flow,就在步骤表里留下一条清楚记录:
- 第 1 个:已关闭,header 已改,已重新开启。
- 第 2 个:已关闭,header 已改,已重新开启。
- 第 6 个:已关闭,header 已改,重新开启失败,原因是 401,已重试 2 次。
中途断了,重启。
Agent 不再问自己"我刚才改到哪了"。
它去读状态:前 5 个已完成,第 6 个停在"已关闭未开启"。
于是它只处理第 6 个,不碰前 5 个。
已经开启的 Flow 不会再被关一次,已经改好的 header 不会被重复写。
重点是第 6 个。
如果第 6 个 Flow 关闭成功,header 也改了,但重新开启时返回 401,重试两次仍然失败,状态机要把它钉成一个具体事实:
第 6 个 Flow 当前已关闭,header 已修改,开启失败,失败原因 401,已重试 2 次。
注意,这个状态本身是危险的。
因为这个 Flow 此刻在线上是关着的。
没有状态机,Agent 大概率会做两件事里的一件:
- 当它没失败,继续处理第 7 个,把一个关停的线上 Flow 留在身后没人管。
- 把整批从头再跑一遍,连前 5 个已经开好的也跟着再关一次、改一次。
有了状态机,"开启失败"就是一个明确的停止状态。
它触发的不是继续,也不是无限重试,而是暂停整批任务,把一份可操作的结果交回给人:
- 前 5 个已完成。
- 第 6 个停在"已关闭未开启"。
- 失败原因是 401。
- 当前线上状态是关停。
- 建议下一步是人工确认授权,再单独补第 6 个。
这正是我当初只能手写在 Prompt 最前面的那句"失败时不要继续批量写入"。
区别在于,写在 Prompt 里,它是我每次都要记得补的一句嘱咐;做进状态机,它变成系统每次都会执行的一条转移规则。
断点续跑,这时候才真的是续跑。
不是重来。

长流程中断后,从状态表读出下一步,跳过已完成、只补未完成
上线前,我会卡住哪五条线?
如果这套状态机要进一个真实业务系统,我会额外卡住五条线。
第一,状态不是日志。
日志是事后看的,状态是系统当下要用的。不要只把执行过程打进日志里,然后让人或模型从日志里倒推进度。下一步执行必须直接从状态字段算出来。
第二,副作用前后都要有记录。
只在动作成功后写状态是不够的。像"关闭 Flow"这种动作,执行前要知道原状态,执行后要立刻记录当前已经关闭。否则一旦关完之后断掉,系统就会丢掉最危险的中间态。
第三,重试不能跨状态重试。
如果失败发生在 reopening,就只重试重新开启,不要把关闭、改 header、重新开启整套流程再跑一遍。重试粒度必须和状态粒度一致。
第四,未知状态先对账,不要继续。
如果系统读到的状态和外部真实状态对不上,比如状态表写着已开启,但接口查出来是关闭,那就不要让 Agent 猜。先进入 paused_for_reconcile,由系统做一次对账,或者交给人确认。
第五,人工接管要拿到可执行信息。
交给人不是丢一句"失败了"。至少要交代:哪一批任务、哪个对象、停在哪个 phase、当前线上状态是什么、失败原因是什么、已经重试几次、建议下一步是什么。
这样人接手时不是重新排查,而是从一个确定位置继续处理。
到这里,Agent 的角色就清楚了。
它可以解释错误,可以生成下一步参数,可以整理报告。
但它不能越过状态机,自己决定是否继续批量写入。
流程事实归系统管,状态转移归系统管,是否继续也归系统管。
Agent 工程化的第四课,是把记忆从对话里搬出来
第一课是约束任务边界。
第二课是约束工具。
第三课是设计它怎么失败。
第四课,是把任务的记忆从对话里搬出来,放进系统。
这四课其实是同一件事的不同侧面:
企业 Agent 能不能进生产,不取决于模型那一轮答得多漂亮,而取决于它身后有没有一套不依赖模型记性的工程结构。
边界由系统划。
工具由系统管。
失败由系统兜。
状态由系统记。
状态机解决的,就是"记住做到哪"这一块。
它让系统在任何一次中断之后,都还知道自己站在哪一步,下一步合法地能往哪走。
而一旦系统清楚地知道"现在停在哪一步",下一个问题就自然冒出来了:
有些步骤,本来就不该由 Agent 自己往下走。
常见问题
为什么不能让 Agent 靠 Prompt 和对话记住任务进度?
因为对话上下文会被截断、会被重新理解,也会在会话结束或进程重启后丢失。把"做到哪一步"这种系统事实交给它,意味着任务能不能跑完要靠运气。状态应该存在一个能查、能改、能审计的外部位置,而不是上下文里。
企业 Agent 的状态机是不是必须引入工作流引擎或编排框架?
不是。最小的状态机就是任务表、步骤表和几条合法转移规则,用数据库和少量判断就能起步。是否需要更重的框架,取决于流程本身的复杂度,而不是取决于"状态机"这个词听起来有多重。
为什么步骤表要记录 previous_snapshot?
因为回滚不是一句"恢复一下"。系统必须知道改之前是什么状态,才能恢复到正确位置。对 Flow 来说,至少要记录原始开关状态和关键配置摘要;对 CRM 客户来说,至少要记录原始阶段、负责人和待发送动作。
学AI大模型的正确顺序,千万不要搞错了
🤔2026年AI风口已来!各行各业的AI渗透肉眼可见,超多公司要么转型做AI相关产品,要么高薪挖AI技术人才,机遇直接摆在眼前!
有往AI方向发展,或者本身有后端编程基础的朋友,直接冲AI大模型应用开发转岗超合适!
就算暂时不打算转岗,了解大模型、RAG、Prompt、Agent这些热门概念,能上手做简单项目,也绝对是求职加分王🔋

📝给大家整理了超全最新的AI大模型应用开发学习清单和资料,手把手帮你快速入门!👇👇
学习路线:
✅大模型基础认知—大模型核心原理、发展历程、主流模型(GPT、文心一言等)特点解析
✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑
✅开发基础能力—Python进阶、API接口调用、大模型开发框架(LangChain等)实操
✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用
✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代
✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经
以上6大模块,看似清晰好上手,实则每个部分都有扎实的核心内容需要吃透!
我把大模型的学习全流程已经整理📚好了!抓住AI时代风口,轻松解锁职业新可能,希望大家都能把握机遇,实现薪资/职业跃迁~
这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】

更多推荐
所有评论(0)