跳转到主要内容

在写第一个 Prompt 之前,先设计好你的 Agent 状态机

阅读需 2 分钟Tian PanTian Pan

大多数工程师在构建第一个 LLM agent 时,都会遵循相同的流程:写一个系统提示词,添加一个调用模型的循环,撒上一些工具调用逻辑,然后看着它在简单的测试用例上运行。六周后,这个 agent 变成了一团难以理解的嵌套条件、粘贴在 f-string 里的 prompt 片段,以及散落在三个文件中的重试逻辑。添加一个功能需要通读整个代码。遇到生产 bug 就得盯着一个上千 token 的上下文窗口,试图重建模型当时在"想"什么。

这就是"意大利面式 agent"问题,在以 prompt 为起点而非设计为起点的团队中几乎普遍存在。解决方案不是更好的提示技巧,也不是换一个框架,而是一种纪律:在写第一个 prompt 之前,先设计好状态机

为什么 Prompt 优先的构建方式在规模化时总会失败

Prompt 优先开发的吸引力是真实存在的。你可以在一小时内让系统跑起来。模型能处理那些显式编码需要数周时间的歧义。感觉就像直接跳到了有趣的部分。

但 prompt 优先的 agent 携带着快速积累的结构性债务。没有显式状态,agent 的行为被隐式编码在 prompt、上下文历史以及模型调用周围散落的条件逻辑中。"agent 现在处于什么状态?"这个问题没有权威答案,因为这个概念在你的系统中根本不存在——只有一个不断增长的对话历史 blob,模型每次运行时对它的解读都不同。

一项分析了七个主要框架中超过 1,600 条标注 agent 执行轨迹的研究发现,42% 的失败可以追溯到系统设计问题:规范不完整、agent 被赋予了模糊的需求、没有清晰地定义"完成"意味着什么。另有 37% 是多 agent 场景下的协调失败,agent 在不同的共享状态心智模型下运行,并悄悄覆盖彼此的工作。这些不是模型失败,而是架构失败——而且通过显式状态设计完全可以预防。

状态机能给你什么

agent 的状态机在概念上很简单:一组有限的命名状态、状态之间的定义转换,以及触发每个转换的显式条件。它对 LLM agent 的强大之处在于,它将所有你本来会烘焙进 prompt 的隐式决策外部化了。

考虑一个客服 agent。没有显式状态,你会有一个系统提示词,大意是"你是一个有帮助的支持 agent,帮助用户解决问题,必要时进行升级"。"必要时"这个词承担了巨大的工作量。有了状态机,你会有:

  • greeting — 从用户那里收集问题陈述
  • classifying — LLM 确定问题类别,路由到适当的解决路径
  • resolving — LLM 提出解决方案,查询知识库
  • confirming — 用户确认解决方案是否有效
  • escalating — 触发人工交接,并附上完整上下文
  • closed — 终止状态,记录结果

每个状态都有明确的目的、明确的输入和明确的退出条件。LLM 是特定状态的组件——而不是所有行为的全局协调者。当出现问题时,你能精确知道它处于哪个状态、收到了什么输入、以及哪个转换触发错误了。

这不仅仅是理论上的整洁。它改变了在操作层面什么是可能的:你可以记录每个状态转换。你可以从保存的检查点重放任何执行。你可以编写单元测试,断言"在状态 X 给定这个输入,agent 应该转换到状态 Y"。当 agent 的逻辑主要存在于 prompt 内部时,这些都是不可行的。

在接触 LLM 之前设计拓扑结构

实际操作过程是:将 agent 设计视为与设计数据库 schema 或 API 合同一样的一等工件——在写任何代码之前进行审查。

从映射快乐路径开始。成功完成任务有哪些离散阶段?给每个阶段起一个对阅读日志文件的人有意义的名字,而不是对 LLM 有意义的名字。然后映射失败边——不是作为异常处理器,而是作为一等状态。retrying_tool_call 状态比 try/except 块更好。waiting_for_human_review 状态比塞在条件语句里的升级 prompt 更好。

对于每个状态,明确回答三个问题:

进入这个状态时,agent 知道什么? 这定义了你的状态 schema——携带跨转换上下文的类型化数据结构。LangGraph 鼓励使用带有标注 reducer 函数的 TypedDict;具体工具不重要,但定义哪些数据流经其中的纪律至关重要。状态 schema 迫使你决定哪些信息是真正需要的,哪些只是"在对话历史的某个地方"。

agent 在这个状态下可以采取哪些行动? 这约束了 LLM 可用的工具调用。处于 classifying 状态的 agent 可能不应该能够发起退款。按状态限制可用工具消除了整类幻觉工具调用——这是长视野任务中最常见的失败模式之一。

从这个状态出去的有效转换是什么,是什么触发了它们? 这是你将 LLM 调用映射到拓扑结构的地方。模型的输出——一个分类、一个是/否判断、一个结构化决策——是触发转换的事件。模型不决定下一步去哪里;状态机根据模型返回的内容来决定。这是一个微妙但至关重要的反转。LLM 提供推理;状态机提供控制流。

会员专享

余下内容仅对会员开放。

会员可读完整内容 —— 每一个公开发布的观点背后,那些框架、决策与推理的全貌。

  • 完整文章,包含未公开存档的部分
  • 可落地的工作框架,附带权衡与决策依据
  • 新文章抢先看,先于公开发布

随时取消 · 一次订阅,畅读全部

参考资料

保持联系,关注我获取更多内容

阅读需 11 分钟

聊天历史是数据库。别再把它当成滚动回溯了。

将对话历史视为滚动回溯(Scrollback),是智能体在第 8 轮对话后就开始跑题,以及上下文费用呈超线性增长的原因。解决办法是回归其本质——一个读密集型数据库——并据此进行设计。

insider
llm
阅读需 11 分钟

提示注入攻击面映射:在攻击者之前找到每一个攻击向量

一份实践者方法论:枚举每一个到达 LLM 提示的外部数据源,对每个注入面进行风险评分,并在不破坏模型推理能力的前提下应用正确的净化模式。

insider
security
阅读需 10 分钟

被反向代理剥离的 SSE Keep-Alive,以及你支付了两次费用的 Prompt

LLM 流式响应看起来像是从模型到用户的通畅管道 —— 直到一次 35 秒的工具调用导致反向代理断开连接,你的客户端针对无状态 API 重试整个 Prompt,最终用户的账单为同一个响应支付了两次费用。

insider
ai-engineering
阅读需 11 分钟

策略文件:为什么你不应该把拒绝规则写在系统提示词里

生产环境的系统提示词就像是披着一件风衣的三个配置文件——对话语气、输出格式和拒绝策略被塞进同一个工件中,共用同一个评审人和发布节奏。每一次策略修改都会导致无关任务的行为回归。这里有一种能显著获益的解耦方案。

insider
llm
阅读需 10 分钟

飞行中转向:无需重启即可重定向长时运行的智能体

大多数智能体 UI 将每次航向修正都变成了完全重启。解决方案是架构层面的——检查点与注入、计划修订钩子以及软中断令牌——外加一套区分了修正、覆盖与取消的“三动词” UX 词汇表。

insider
agents