微信扫码
添加专属顾问
我要投稿
AI Agent 从概念到落地,为何总是卡在最后20%?作者结合实战经验,揭示构建可靠Agent系统的12条工程法则。 核心内容: 1. Agent系统从Demo到生产环境的主要挑战 2. 传统确定性软件架构与Agent自主规划的冲突 3. 12条提升Agent可靠性的工程实践指南
随着 Agent 概念的爆发,大量开发框架和 Agent 产品不断涌现。从 AutoGPT 到 LangGraph,从 CrewAI 到各种开源 Agent 平台,人们似乎正在接近“数字员工”的愿景:给定一个目标,Agent 就能自主规划、调用工具并完成复杂任务。
然而,当越来越多团队尝试将 Agent 推向真实业务场景时,一个问题逐渐显现——为什么许多令人惊艳的 Demo,最终却难以成为可靠的生产系统?
https://github.com/humanlayer/12-factor-agents/
作者 Dex Horthy 结合自己构建 Agent 产品的经验,对当前流行的 Agent 开发模式进行了反思。他认为,很多开发者过于关注模型的自主能力,却忽视了软件工程的重要性。真正成功的 Agent 往往不是依靠无限循环的推理和工具调用,而是建立在成熟的软件架构之上。
文章首先回顾了软件的发展历程。传统软件系统本质上是一张由开发者设计好的流程图,每一个节点和路径都被明确规定。然而现实情况并没有想象中乐观。作者指出,当 Agent 被用于真实业务后,人们会发现自主规划带来的不仅是灵活性,还有不可预测性。模型可能选择错误路径、重复调用工具、陷入循环,甚至因为上下文管理不当而产生完全错误的结果。很多团队在达到 70% 到 80% 的效果后便遇到了瓶颈,而剩余的 20% 恰恰决定了产品是否能够真正交付给用户。
从软件诞生开始,程序本质上就是一张由开发者设计的流程图。无论是早期的流程图设计,还是今天复杂的业务系统,其核心思想都没有发生根本变化:开发者预先定义好执行路径,程序按照既定规则运行。后来出现的 Airflow、Prefect、Dagster 等工作流编排系统,虽然增强了调度、监控、重试和可观测性等能力,但本质上仍然是在管理一张 DAG(有向无环图)。在这种模式下,软件工程师始终掌握着系统的控制权,每一个步骤和分支都经过明确设计。
随着机器学习技术的发展,软件系统开始引入越来越多的智能能力。 例如,在数据处理流程中增加文本分类模型,在客服系统中增加情感分析模型,或者在推荐系统中引入预测算法。虽然系统中的某些节点变得更加智能,但整体架构并没有发生改变。模型只是流程中的一个环节,而流程本身依然由开发者定义。因此,即使在机器学习时代,软件仍然是一套高度确定性的系统。
Agent 的出现则带来了完全不同的愿景。过去开发者需要设计完整的流程,而 Agent 希望开发者只需要提供目标和工具,剩余的工作交给模型完成。 换句话说,开发者不再定义“应该经过哪些节点”,而是只定义“能够走哪些边”。至于具体路径如何选择,则由大语言模型根据实时情况动态决定。这种模式的吸引力显而易见,因为它意味着软件能够从流程驱动转向目标驱动。面对复杂任务时,开发者不必提前考虑所有可能情况,而是让模型自主规划和调整执行路径。
这种思路也催生了当前大多数 Agent 框架所采用的经典模式:模型根据当前上下文选择下一步行动,系统执行对应工具,将结果写回上下文,然后再次让模型决定下一步操作。 如此循环,直到模型认为任务完成。从表面上看,这种模式极具通用性,因为它能够适应各种不同场景。然而作者指出,问题恰恰出现在这个循环过程中。
随着任务不断推进,系统会持续向上下文窗口中添加新的信息。最初只有用户需求,随后加入工具调用记录、执行结果、错误信息以及各种中间状态。上下文规模不断膨胀,而模型需要在越来越复杂的信息中寻找真正重要的内容。当任务持续十几轮甚至几十轮之后,模型往往开始出现明显的性能退化:它可能忘记最初目标,重复尝试已经失败的方法,或者陷入无意义的循环。
作者认为,这并不是当前模型能力不足所导致的暂时问题,而更像是一种结构性的挑战。即使未来模型支持百万级 Token 的上下文窗口,人们依然会发现,小而聚焦的上下文通常能够获得更好的结果。因为问题不仅在于模型能够容纳多少信息,更在于模型如何有效关注真正重要的信息。随着上下文增长,噪声也会同步增长,而模型对关键信息的判断能力反而可能下降。
正因为如此,我们没有否定 Agent 的价值,而是对 Agent 的使用方式提出了不同看法。 在实际生产环境中,表现最好的往往不是那些试图解决所有问题的超级 Agent,而是被嵌入到确定性工作流中的微型 Agent(Micro Agent)。在这种架构下,整体业务流程依然由传统软件系统控制,而 Agent 只负责处理那些需要语言理解和灵活决策的局部环节。模型不再承担整个系统的规划任务,而是专注于解决一个范围明确、上下文有限的问题。
大语言模型最擅长的不是执行,而是理解。
当用户提出一个需求时,模型最大的价值并不在于直接调用 API、修改数据库或者完成业务操作,而在于理解人类自然语言中的意图,并将这种意图转换成结构化、机器能够理解的指令。换句话说,LLM 应该承担“翻译器”的角色,而不是“执行者”的角色。
例如,当用户说:
请帮我给 Terri 创建一个 750 美元的付款链接,用于赞助二月份的 AI Tinkerers 活动。
对于传统软件来说,这句话几乎无法直接执行,因为其中包含了大量非结构化信息。系统需要识别金额、收款对象、商品信息以及备注内容,并最终转换成 Stripe API 所需要的参数格式。而对于大语言模型而言,这正是它最擅长的任务——从自然语言中提取关键信息,并生成结构化输出。
因此,在作者看来,Agent 的第一步不应该是让模型完成整个任务,而应该是让模型输出类似下面这样的结构化描述:
{
"function": "create_payment_link",
"parameters": {
"amount": 750,
"customer": "...",
"product": "...",
"memo": "..."
}
}
此时,模型并没有真正创建付款链接,它只是告诉系统:“我认为下一步应该执行这个动作,并且这些是所需参数。”
接下来,真正的业务逻辑交由确定性的代码负责。系统根据模型返回的结构化结果,调用对应的 Stripe API,处理异常情况,记录日志,并返回执行结果。整个过程中,模型只负责决策,而代码负责执行。
这种设计思想看似简单,却代表着一种重要的职责划分。过去很多 Agent 系统试图让模型同时承担理解、规划和执行三种角色,但随着系统复杂度提升,这种模式往往会导致不可预测的行为。而当模型只负责“决定下一步应该做什么”时,整个系统的稳定性和可维护性都会显著提高。
更进一步地看,这也是作者重新定义 Agent 的开始。在传统理解中,Tool Calling 往往被描述为一种特殊能力,好像模型拥有了调用外部世界的超能力。但作者认为,所谓 Tool Calling 本质上只是结构化输出(Structured Output)的一种形式。模型并没有真正执行工具,它只是生成了一段符合特定格式的数据。真正与外部系统交互的始终是开发者编写的代码。
这种理解方式能够帮助我们摆脱很多关于 Agent 的误区。很多开发者在设计 Agent 时,会不断思考应该给模型增加哪些工具、开放哪些权限。然而从作者的视角来看,更值得关注的问题其实是:模型应该输出什么样的决策结果,以及系统如何安全地执行这些结果。工具本身只是实现细节,而自然语言到结构化指令的转换能力,才是大语言模型真正独特的价值所在。
从工程实践角度来看,这种模式还有一个重要优势,那就是可控性。 因为模型输出的是标准化结构,而不是直接执行结果,所以开发者可以在执行之前增加各种校验逻辑。例如检查金额是否超过限制、确认用户权限是否满足要求、验证参数是否合法,甚至引入人工审批流程。这样一来,模型既能够提供灵活的自然语言交互能力,又不会破坏系统原有的安全边界。
事实上,如果观察目前最成功的 AI 产品,会发现它们大多采用了类似思路。无论是代码助手生成函数调用、客服机器人触发工单流程,还是办公助手创建日程和发送邮件,其核心模式都不是“模型直接完成任务”,而是“模型将用户意图转换成系统能够执行的动作”。 从这个角度来看,自然语言到工具调用并不仅仅是 Agent 的一个实现技巧,它更像是一种新的软件交互范式——让人类继续使用自然语言,而让系统内部保持结构化和确定性。
不要让模型直接做事,而要让模型决定下一步应该做什么事。
在 Agent 开发领域,一个非常普遍的现象是开发者高度依赖框架。许多框架为了降低使用门槛,会提供类似下面的抽象:
agent = Agent(...)
task = Task(...)
result = agent.run(task)
从开发体验来看,这种方式无疑是友好的。开发者只需要填写角色(Role)、目标(Goal)和工具(Tools),就能快速获得一个能够运行的 Agent。对于原型验证或者概念演示而言,这种方式能够极大缩短开发时间。然而,作者认为,当系统开始进入真实生产环境后,这种便利往往会成为新的问题来源。
原因在于,大多数 Agent 框架都会在底层隐藏大量 Prompt Engineering 逻辑。开发者看到的只是简单的配置接口,而模型真正接收到的 Prompt 却可能包含数百甚至上千个 Token 的系统指令、行为规范和工具描述。这些内容由框架维护,开发者很难完全掌握其细节。当 Agent 表现不符合预期时,人们往往不知道问题究竟出现在模型本身、上下文内容,还是隐藏在框架中的 Prompt 模板。
因此,作者提出了一个看似简单却非常重要的原则:
不要把 Prompt 外包给框架。
在他看来,Prompt 不应该被视为配置文件,而应该被视为代码的一部分。开发者需要像编写业务逻辑一样编写 Prompt,像维护代码一样维护 Prompt。
这种思想背后实际上反映了 Agent 开发与传统软件开发之间的重要差异。在传统软件中,程序逻辑主要存在于代码之中;而在 Agent 系统中,Prompt 本身就是逻辑的一部分。它决定模型如何理解任务、如何规划步骤、如何调用工具以及如何处理异常情况。从某种意义上说,Prompt 已经不再是简单的文本描述,而是一种新的编程接口。
当 Prompt 掌握在自己手中时,开发者能够精确决定模型看到什么信息、遵循什么规则以及如何组织上下文,而不必受到框架默认实现的限制。对于生产环境而言,这种控制能力往往比快速开发更加重要,因为很多实际问题最终都需要针对具体场景进行细粒度调整。
第二个重要价值是可测试性。 在传统软件工程中,我们会为核心逻辑编写单元测试和评估指标;作者认为 Prompt 也应该享受同样待遇。如果 Prompt 是代码,那么它就应该能够被版本管理、自动测试和持续迭代。当模型输出出现异常时,开发者能够快速定位是 Prompt 修改导致的问题,还是模型本身发生了变化。这种工程化能力对于长期维护 Agent 系统尤为关键。
第三个价值来自迭代效率。 现实中的 Prompt Engineering 并不存在放之四海而皆准的最佳实践。一个在客服场景表现优异的 Prompt,未必适用于代码生成或数据分析场景。因此,构建生产级 Agent 的过程本质上是一个不断实验和优化的过程。如果 Prompt 被封装在框架内部,每一次调整都需要绕过框架限制;而当 Prompt 完全由自己管理时,开发者可以快速尝试新的策略,不断寻找最适合当前业务的方案。
更深层次来看,作者实际上是在反对一种“框架万能论”的思维方式。过去几年软件行业的发展证明,没有任何框架能够完全抽象业务逻辑。同样,在 Agent 时代,也不存在一个能够适用于所有场景的 Prompt 模板。框架可以帮助我们快速起步,但最终决定 Agent 效果的,仍然是开发者对业务问题的理解,以及如何将这些理解转化为模型能够执行的指令。
当前 AI 领域有一个常见误区:很多开发者把注意力集中在 Prompt Engineering 上,不断调整系统提示词、优化角色设定或者修改输出格式,却忽略了模型真正接收到的信息远远不止 Prompt 本身。 事实上,对于大语言模型来说,它看到的所有内容——包括系统提示、历史对话、工具调用记录、RAG 检索结果、长期记忆以及结构化输出要求——都属于 Context 的组成部分。
因此,作者提出了一个极具代表性的观点:
在 Agent 的任何一个时刻,模型面对的问题始终只有一个:
“这是目前为止发生的所有事情,下一步应该做什么?”
从这个角度来看,LLM 本质上是一个无状态(Stateless)的函数。模型不会记住上一次调用发生了什么,它只能根据当前输入的信息生成输出。也就是说,Agent 的能力并不来源于模型本身拥有记忆,而来源于开发者如何构建和组织上下文。
这也是近年来越来越流行的“Context Engineering(上下文工程)”理念的核心。相比于调整 Temperature、Top-P 等参数,或者进行复杂的模型微调,很多实际场景中的性能提升往往来自于更合理的上下文设计。因为模型的推理质量,很大程度上取决于它看到了什么信息,以及这些信息是如何组织的。
目前绝大多数 Agent 框架都采用 OpenAI 所推广的 Message 格式:
[
{"role": "system", "content": "..."},
{"role": "user", "content": "..."},
{"role": "assistant", "tool_calls": [...]},
{"role": "tool", "content": "..."}
]
这种格式已经成为事实上的行业标准。它简单、统一,并且兼容大多数模型 API。
然而作者认为,标准并不意味着最优。
对于简单对话场景来说,这种结构完全够用。但当 Agent 需要处理复杂任务时,消息历史会迅速膨胀。大量的 role 标签、工具调用包装信息以及冗余字段不仅消耗 Token,还会分散模型的注意力。
从模型的角度来看,它真正关心的并不是:
{
"role": "tool",
"tool_call_id": "1"
}
而是:
调用了什么工具?
得到了什么结果?
当前任务进展到哪里?
因此,作者建议开发者不要被标准消息格式束缚,而应该根据自己的业务场景设计最适合的 Context 结构。
作者给出了一个非常有启发性的思路:不要把 Context 理解为聊天记录,而要把它理解为系统状态。
例如,一个部署 Agent 的上下文可能并不是连续对话:
User: 请部署后端
Assistant: 好的
Tool: list_git_tags
Result: ...
而是被组织成一系列明确的事件:
<slack_message>
请部署最新版本后端
</slack_message>
<list_git_tags>
...
</list_git_tags>
<list_git_tags_result>
...
</list_git_tags_result>
这种方式看起来更像日志系统或者事件流(Event Stream),而不是聊天记录。
作者甚至建议开发者可以完全抛弃 Message 格式,将系统中的每一次事件抽象为结构化对象,然后在调用模型时动态转换成最适合模型理解的形式。
这样做的核心思想是:
不要为了适配框架而设计 Context,而要为了帮助模型理解任务而设计 Context。
很多开发者第一次接触 RAG 时,会认为 Context Engineering 就是向模型塞更多信息。
但作者认为恰恰相反。优秀的 Context Engineering 关注的不是信息量,而是信息密度。
在 Agent 领域,“Tool Calling(工具调用)”几乎已经成为一个高频词汇。许多框架都会将其包装成一种特殊能力:模型能够调用函数、访问数据库、发送邮件、执行代码,甚至控制外部系统。初学者很容易产生一种印象,仿佛工具调用是一种独立于文本生成之外的新能力。
Tool Calling 并不是什么特殊机制,本质上只是结构化输出(Structured Output)。
这个观点看似简单,却直接改变了理解 Agent 的方式。
当用户说:
帮我创建一个 Jira 工单。
模型真正做的事情并不是调用 Jira API。
模型实际上只是在生成类似这样的数据:
{
"intent": "create_issue",
"title": "Payment API Timeout",
"description": "...",
"assignee": "alice"
}
随后,系统读取这段 JSON:
if result.intent == "create_issue":
jira.create_issue(...)
真正调用 Jira API 的始终是程序代码,而不是模型。
从这个角度来看,无论是 OpenAI 的 Function Calling、Anthropic 的 Tool Use,还是各种 Agent Framework 中的 Tool Calling,本质上都只是让模型按照特定 Schema 输出 JSON 而已。
作者希望开发者意识到:
所谓工具,不过是模型输出的一种结构化数据格式。
模型从来没有真正执行任何工具。
很多 Agent 框架会建立一种强绑定关系:
Tool
↓
Function
↓
Execute
例如:
CreateIssue()
对应:
create_issue()
模型输出工具名称后,系统立即执行对应函数。这种模式非常普遍,但作者认为这实际上限制了开发者的想象空间。因为当我们把 Tool 理解为 Function 时,就会下意识认为:
模型输出什么工具,就必须执行什么函数。
但如果把 Tool 看成一种结构化意图(Intent),事情就完全不同了。
例如:
{
"intent": "deploy_backend"
}
系统收到这个结果后,可以有很多种处理方式:
换句话说:
Tool 描述的是“想做什么”,而不是“怎么做”。
如何执行,始终由业务代码决定。这里实际上体现了作者一贯强调的设计哲学:LLM 负责决策层。软件负责执行层。
因为很多 Agent 系统最终失控,恰恰是因为开发者把 Tool Calling 想得过于强大。
例如:
模型调用:
DeleteCustomer
如果系统立刻执行:
delete_customer()
风险显然非常高。
但如果系统理解为:
{
"intent": "delete_customer",
"customer_id": "123"
}
那么开发者可以在执行前加入:
Agent 不应该直接控制系统,而应该提出建议,由软件系统决定如何落实这些建议。
这样既保留了 LLM 的灵活性,又不会牺牲系统稳定性。
从更高层次来看,作者实际上是在重新定义 Tool 的角色。工具不是能力。工具不是函数。
工具甚至不是执行逻辑。工具本质上只是模型输出的一份结构化决策结果。
而真正复杂的业务流程、权限控制、异步执行、错误恢复和状态管理,仍然应该掌握在软件系统手中。
不要把 Tool Calling 看成模型在执行代码,而应该把它看成模型在输出下一步行动的结构化建议。
当开发者接受这一视角后,Agent 就不再是一个试图接管整个系统的黑盒,而变成了一个负责决策的软件组件。这也为下一节 Factor 5 埋下伏笔——既然 Tool 只是状态变化的一种描述,那么 Agent 的执行状态(Execution State)和业务状态(Business State)是否真的应该分开管理?这正是作者接下来要讨论的问题。
随着 Agent 系统变得越来越复杂,开发者往往会不自觉地引入大量状态管理机制。为了跟踪任务执行过程,系统会维护当前步骤(Current Step)、下一步动作(Next Step)、等待状态(Waiting Status)、重试次数(Retry Count)、任务队列状态等信息;与此同时,还需要记录用户请求、工具调用结果、模型回复以及各种业务事件。久而久之,一个 Agent 系统中便出现了两套并行存在的状态体系:一套用于描述系统正在如何运行,另一套用于描述业务已经发生了什么。
作者将这两类状态分别称为执行状态(Execution State)和业务状态(Business State)。
其中,执行状态关注的是工作流层面的信息。例如 Agent 当前执行到哪一步、是否正在等待用户回复、是否需要重试某个任务、下一次唤醒时间是什么等。这类状态通常由框架或者工作流引擎维护。而业务状态则记录任务本身的进展,例如用户发起了什么请求、模型调用了哪些工具、工具返回了什么结果、人工审核给出了什么反馈等。这些信息通常会进入上下文窗口,成为后续推理的依据。
在很多传统工作流系统中,这两类状态被严格分离。例如一个工作流引擎会单独维护任务状态机,而业务数据则存储在数据库中。这样的设计在大型分布式系统中有其合理性,但作者认为,对于大量 Agent 场景而言,这种分离往往带来了不必要的复杂度。
如果可能,不要维护两套状态系统,而是尽可能统一执行状态和业务状态。
作者认为,在许多 Agent 应用中,所谓的执行状态其实完全可以从业务历史中推导出来。换句话说,系统不一定需要额外保存“当前执行到第几步”这样的字段,因为从已经发生的事件记录中,我们往往就能够知道 Agent 当前处于什么阶段。
如果 Agent 的所有行为都被记录为事件流(Event Stream),那么整个系统实际上就变成了一个事件驱动架构。当前状态并不是存储出来的,而是计算出来的。
这种设计思想与 Event Sourcing(事件溯源)有着明显的相似之处。在事件溯源架构中,系统不会直接存储当前状态,而是存储所有发生过的事件。当需要恢复状态时,只需重新回放这些事件即可。作者实际上是在将这一思想引入 Agent 设计之中。
这种统一状态的方式首先带来的好处是系统结构大幅简化。当执行状态和业务状态共享同一套数据结构时,整个系统只需要维护一个事实来源(Single Source of Truth)。开发者无需担心两套状态之间的数据同步问题,也不需要处理状态不一致带来的各种边界情况。
其次,这种设计天然具备良好的可序列化能力。 由于 Agent 的所有状态都包含在事件历史中,因此整个线程(Thread)可以轻松保存到数据库,也可以随时重新加载。对于需要长时间运行的 Agent 来说,这一点尤为重要。无论任务中断多久,只要重新读取历史记录,就能够恢复到原来的运行状态,而不需要额外恢复复杂的执行上下文。
在调试方面,这种设计也具有明显优势。 传统 Agent 系统出现问题时,开发者往往需要同时查看日志、数据库状态、工作流状态机以及消息队列等多个组件。而在统一状态模型下,所有信息都集中在同一个线程中。开发者只需要查看事件历史,就能够完整还原 Agent 的决策过程和执行过程。这种可观察性对于生产环境中的问题定位极为重要。
更有趣的是,统一状态还为 Agent 提供了天然的“时间旅行”能力。由于所有历史事件都被完整保存,因此开发者可以从任意一个历史节点重新开始执行。 例如当某一步决策出现问题时,可以复制某个历史节点之前的全部事件,创建一个新的线程,让 Agent 基于相同历史重新尝试另一种解决方案。这种能力类似于 Git 中的分支(Branch)机制,也类似于工作流系统中的回溯执行。
此外,统一状态模型对于人机协作场景也十分友好。因为事件流本身就具备较强的可读性,开发者可以很容易地将其转换为 Markdown 报告、聊天记录、审批流程页面或者可视化工作流界面。换句话说,同一份数据既可以作为模型的上下文,也可以作为用户的操作界面来源。
当然,作者并没有主张所有数据都必须进入上下文窗口。一些敏感信息,例如 Session ID、访问令牌、密码、数据库连接信息等,通常不应该直接暴露给模型。这些内容依然可以保存在系统层面,由业务逻辑负责管理。但作者强调的重点在于:
真正无法进入上下文窗口的数据应该越少越好。
因为一旦系统维护了大量独立于上下文之外的状态,Agent 的行为就会越来越依赖外部环境,从而失去可恢复性和可解释性。
前面几个原则已经分别解决了模型如何决策、如何组织 Prompt、如何管理 Context 以及如何理解 Tool Calling 的问题。而这一节则进一步回答了一个工程层面的问题:
Agent 的状态究竟应该保存在哪里?
作者给出的答案非常直接:
最理想的情况是,Agent 的历史记录就是 Agent 的状态。
当上下文、历史记录和状态逐渐融合为同一个概念时,Agent 系统的复杂度会显著下降,而可恢复性、可观测性和可维护性则会大幅提升。
一个成熟的 Agent 不应该被封装在复杂的编排框架内部,而应该像普通服务一样拥有简单、清晰的生命周期管理能力。无论是用户、业务系统、自动化流水线,还是其他 Agent,都应该能够通过简单的 API 启动一个 Agent,而不需要了解其内部的实现细节。
更重要的是,Agent 不应该假设所有任务都能一次性完成。在真实业务场景中,很多操作都需要较长时间。例如等待人工审批、等待第三方 API 返回结果、等待异步任务执行完成,甚至等待用户几小时后回复消息。如果 Agent 在等待期间始终保持运行状态,不仅会浪费资源,也会增加系统复杂度。
因此,作者强调 Agent 必须具备 Pause(暂停) 和 Resume(恢复) 的能力。当 Agent 遇到需要等待的情况时,可以直接暂停执行并保存当前状态;当外部事件发生后,例如收到 Webhook、用户回复消息或异步任务完成通知,再从原来的位置继续执行。这种模式类似于工作流系统中的“挂起与唤醒”,但作者希望它能够以更加轻量化的方式实现。
这里的关键在于,恢复 Agent 不应该依赖复杂的编排器(Orchestrator)。如果 Agent 的状态已经完整保存在事件历史中,那么恢复过程实际上非常简单:重新加载历史记录,将新的事件追加进去,然后再次调用模型推理下一步动作即可。整个过程更像是恢复一个线程(Thread),而不是恢复一个复杂的运行时环境。
从工程实践角度看,这种设计带来了很大的灵活性。一个 Agent 可以由 Slack 消息启动,由 GitHub Webhook 唤醒,由审批系统暂停,再由 CI/CD 流水线恢复。各种外部系统都能够参与 Agent 的执行过程,而无需与某个特定框架深度耦合。
Agent 不应该是一段持续运行的循环,而应该是一种能够随时启动、暂停和恢复的任务。
当 Agent 具备这种能力后,它就不再是一个封闭的 AI 系统,而成为企业软件架构中的一个标准组件,可以自然地融入现有业务流程和自动化系统之中。
传统 Agent 的设计通常遵循一个默认假设:模型要么输出自然语言回复,要么调用工具执行任务。然而作者认为,这种二元选择本身可能限制了 Agent 的能力。因为在真实业务流程中,很多关键决策并不应该由模型独立完成,而是需要人类参与其中。
不要把人与 Agent 的交互看成聊天,而应该把“联系人类”视为一种特殊的工具调用。
例如,当 Agent 准备执行生产环境部署时,它不一定直接调用部署工具,而是先输出一个结构化意图:
{
"intent": "request_human_input",
"question": "是否继续部署到生产环境?"
}
此时系统并不会继续执行,而是暂停 Agent,将问题发送给相关负责人。等到用户通过 Slack、邮件或其他渠道回复后,再将回复作为新的事件加入上下文,恢复 Agent 的执行流程。
这种设计的核心价值在于,它将人工反馈纳入了 Agent 的标准工作流。对于模型来说,人类审批、用户确认和业务决策与调用 API 没有本质区别,都是获取下一步行动所需信息的一种方式。这样不仅能够提高系统安全性,也能让 Agent 更自然地融入企业现有流程。
作者进一步指出,这种模式突破了传统聊天机器人的限制。在 ChatGPT 这类产品中,通常是“人发起对话,Agent 响应”。但在很多企业场景中,流程恰恰相反。一个 Agent 可能由定时任务、监控告警或业务事件触发,然后主动联系相关人员获取信息。例如部署 Agent 主动请求审批,采购 Agent 主动询问预算确认,或者运维 Agent 主动通知故障处理人员。这种模式被作者称为 Outer Loop Agent(外循环 Agent)。
当“联系人类”被抽象成工具调用后,整个系统的扩展能力也会明显增强。Agent 不仅可以向一个人发起请求,还可以同时协调多个审批人;不仅可以与人沟通,也可以与其他 Agent 进行结构化协作。从系统视角来看,无论是人类回复还是 Agent 回复,都只是事件流中的一种输入。
结合前面的状态统一(Factor 5)和暂停恢复机制(Factor 6),这种模式还能构建出持久化、多参与者协同的工作流。即使任务持续数小时甚至数天,Agent 依然能够准确记录每一次请求和响应,并在需要时从原来的位置继续执行。
在生产级 Agent 中,人类不是系统之外的参与者,而是工作流中的一个节点。
当 Agent 学会通过结构化工具主动联系和协调人类时,它就不再只是一个聊天机器人,而开始真正融入现实世界的业务流程之中。
在许多 Agent 框架中,开发者通常只负责定义 Prompt 和工具,而整个执行过程则由框架内部的 Agent Loop 自动管理。模型选择工具、执行工具、接收结果,再继续推理下一步,直到任务结束。这种方式看起来十分方便,但作者认为,真正的生产级 Agent 不应该把控制流完全交给框架。
Agent 可以负责决策,但工作流的控制权必须掌握在开发者手中。
作者认为,不同类型的工具调用对应着不同的处理策略。例如,当模型需要查询 Git 标签时,系统完全可以立即执行工具并将结果返回给模型;但如果模型请求生产环境部署,或者需要用户审批,那么系统就应该暂停执行,等待人工确认后再继续。对于需要长时间运行的任务,例如模型训练、数据处理或外部审批流程,也同样如此。
这意味着 Agent 的执行流程不应该只有简单的“调用工具 → 返回结果”模式,而应该允许开发者根据业务需求自由定义控制逻辑。什么时候继续循环、什么时候暂停、什么时候等待事件触发恢复,都应该由业务代码决定,而不是由框架预设。
这种设计带来的最大价值是灵活性。开发者可以在控制流中加入各种工程能力,例如上下文压缩、缓存机制、日志追踪、性能监控、限流控制、结果评审(LLM-as-Judge)以及长时间休眠和事件唤醒机制。这些能力往往是生产环境真正需要的部分,却很难通过标准 Agent Loop 实现。
作者特别强调了一个长期困扰 Agent 开发的问题:
Agent 应该能够在“选择工具”和“执行工具”之间被中断。
例如,当模型决定执行删除数据、部署系统或发起支付等高风险操作时,系统应该能够先暂停下来,让人类审核这次工具调用,而不是直接执行。遗憾的是,许多现有框架并不支持这种粒度的控制,模型一旦选中工具,系统便立即调用对应函数。
如果缺少这种中断能力,开发者往往只能在三种不理想的方案之间做选择:要么让 Agent 一直阻塞等待结果,要么将 Agent 限制在低风险场景中使用,要么放任 Agent 执行高风险操作并承担潜在后果。无论哪一种,都难以满足生产环境的要求。
LLM 负责决定“做什么”,而系统负责决定“什么时候做、如何做以及是否允许做”。
只有当控制流掌握在开发者手中时,Agent 才能真正融入复杂业务流程,而不是沦为一个不可预测的黑盒。
Agent 相比传统程序的一个重要优势在于其具备一定的“自我修复(Self-Healing)”能力。当工具调用失败时,模型并不一定需要人工介入,它往往能够通过阅读错误信息、异常日志甚至堆栈信息,理解失败原因,并尝试调整下一次行动。
因此,作者建议将错误信息直接作为一种特殊事件写入上下文窗口,而不是简单地终止任务。这样,模型在下一轮推理时不仅能看到之前的决策过程,也能看到执行失败的原因,从而自主寻找新的解决方案。例如,当 API 参数填写错误、资源不存在或权限不足时,模型可能会根据错误提示重新构造正确的工具调用。
这种机制本质上是将“错误反馈”纳入 Agent 的推理循环之中。传统软件中的异常通常意味着流程终止,而在 Agent 系统中,错误可以成为下一步决策的重要输入。通过这种方式,Agent 能够在短流程任务中实现一定程度的自动恢复,而无需频繁依赖人工干预。
不过,作者也提醒,这种能力并非无限有效。如果持续把原始错误不断追加到上下文中,模型很容易陷入“错误循环(Error Spin-Out)”——不断重复同样的失败尝试,却始终无法找到新的解决方案。因此,实际系统中通常需要设置错误阈值,例如连续失败三次后停止自动重试,并将任务升级给人工处理。
更进一步地,错误信息并不一定要原封不动地进入上下文。开发者完全可以结合前面提到的上下文工程(Factor 3)和控制流管理(Factor 8),对错误进行压缩、总结或重构。例如只保留关键失败原因,删除已经解决的历史错误,甚至将多次失败总结为一句简洁的状态描述,从而减少上下文污染。
作者认为,错误压缩的真正目标并不是让 Agent 无限重试,而是在有限次数内帮助模型快速定位问题并恢复执行。当达到恢复极限时,系统应该主动切换到人工介入或其他处理流程,而不是任由 Agent 在错误中不断循环。
不要把错误视为流程终点,而要把错误视为上下文的一部分。
合理利用错误信息能够增强 Agent 的恢复能力,但真正避免错误失控的最佳方法。
在很多人刚开始构建 Agent 时,都会产生一种理想化设想:既然大模型具备推理能力,那么是否可以构建一个万能 Agent,让它负责所有任务、调用所有工具并处理所有业务流程。然而作者认为,这恰恰是许多 Agent 项目最终失败的重要原因。
不要构建无所不能的大型 Agent,而要构建多个小而专注的 Agent。
这一观点的出发点并非软件架构设计,而是来自大语言模型本身的能力边界。随着任务复杂度增加,Agent 需要经历更多推理步骤,产生更长的上下文窗口。而上下文越长,模型越容易遗忘早期信息、混淆目标或者陷入错误循环。很多 Agent 并不是因为工具不足而失败,而是在不断扩展任务范围后逐渐失去焦点。
因此,作者建议将 Agent 的职责控制在一个清晰且有限的领域内。一个 Agent 最好只负责完成某一类任务,例如代码部署、客户支持、故障诊断或文档生成。其工作流程通常保持在 3 到 10 个步骤以内,即使复杂场景也尽量不要超过 20 个步骤。这样既能保证上下文规模可控,也能让模型始终聚焦于当前目标。
从工程实践角度看,小型 Agent 也更符合现代软件设计思想。每个 Agent 都拥有明确职责和边界,便于测试、维护和调试。当出现问题时,开发者能够快速定位故障来源,而不需要在一个庞大的 Agent 系统中排查复杂的推理链路。同时,不同 Agent 还可以像微服务一样进行组合,共同完成更大的业务流程。
与其构建一个什么都会做但经常失败的 Agent,不如构建多个能力边界清晰、行为稳定可靠的小 Agent。
当 Agent 保持专注时,上下文更短、错误更少、可控性更强,而这恰恰是生产级 AI 系统最重要的品质。
在很多人的认知中,Agent 通常存在于聊天界面中:用户打开一个对话窗口,输入问题,然后等待模型给出答案。然而作者认为,这种理解实际上过于狭隘。真正能够融入企业流程和日常工作的 Agent,不应该局限于某一个聊天产品,而应该能够在任何渠道被触发,并通过用户习惯的渠道与人协作。
不要要求用户来到 Agent 面前,而应该让 Agent 主动出现在用户工作的地方。
在实际业务场景中,用户每天使用的工具可能是 Slack、电子邮件、短信、企业微信、工单系统或监控平台,而不是专门打开一个 AI 应用。因此,一个成熟的 Agent 应该能够从这些渠道接收任务,并通过相同渠道返回结果。例如,用户可以直接在 Slack 中发起部署请求,通过邮件审批生产变更,或者在手机短信中确认某项关键操作。整个过程无需切换工作环境,Agent 自然融入现有工作流之中。
这一理念与前面提到的暂停恢复机制(Factor 6)和人类协作机制(Factor 7)紧密相关。当 Agent 在执行过程中遇到关键决策时,可以主动联系相关负责人获取反馈;获得回复后,再自动恢复执行。对于用户而言,这更像是在与一位数字同事协作,而不是操作一个复杂的软件系统。
更重要的是,Agent 的触发者不一定是人。作者提出了“Outer Loop Agent(外循环 Agent)”的概念,即 Agent 可以由各种外部事件自动启动。例如监控系统发现服务异常、CI/CD 流水线完成构建、定时任务到达执行时间,或者业务系统触发某个状态变更,这些都可以成为 Agent 的启动信号。Agent 在后台自主工作数分钟甚至数小时,当遇到需要判断、审批或协助的问题时,再主动联系相关人员。
与此同时,当 Agent 能够快速联系到正确的人并获得反馈时,企业也更容易赋予其更高权限。例如发送客户邮件、修改生产环境数据、执行系统部署等高风险操作。因为每一次关键行为都可以通过结构化流程进行审批和审计,所以即使 Agent 拥有更大的行动能力,系统依然保持可追踪和可控。
最优秀的 Agent 往往不是用户主动寻找的工具,而是在恰当的时候主动出现、主动协作、主动推动事情向前发展的数字同事。
如果 Agent 本质上只是根据已有上下文决定下一步行动,那么它是否可以被设计成一个完全无状态(Stateless)的组件?
这个想法来源于函数式编程中的 Reducer(归约器) 概念。在 Redux、Event Sourcing 等架构中,系统当前状态并不是保存在程序内部,而是通过历史事件不断累积计算出来的。Reducer 的职责非常简单:
输入当前状态和一个事件,输出新的状态。
在这种架构下,Agent 并不需要维护复杂的内部状态,它只需要读取当前上下文,然后根据这些信息生成下一个动作即可。
换句话说,Agent 更像是一个纯函数:
当前上下文 + 新事件
↓
Agent推理
↓
下一步动作
新的动作执行后会产生新的事件,而这些事件又会进入上下文,成为下一轮推理的输入。整个系统不断重复这一过程,就像一个持续运行的状态机。
这种设计最大的价值在于简化系统复杂度。由于 Agent 本身不保存状态,因此无需考虑内存同步、状态恢复或分布式一致性等问题。任何时候,只要重新加载事件历史,就可以完整重建当前状态,并从任意位置恢复执行。这与前面强调的“统一状态”“暂停恢复”“事件驱动”等原则形成了高度一致的整体架构。
从更深层次来看,这也是作者对 Agent 本质的一种重新定义。在很多 Agent 框架中,开发者习惯把 Agent 看作一个拥有记忆、拥有生命周期、能够自主运行的复杂实体。但作者认为,这种理解容易导致过度设计。事实上,Agent 完全可以被简化为一个状态转换器:
它不保存世界状态,只负责根据上下文推导下一步。
当 Agent 被设计成无状态组件后,它就变得更容易测试、更容易扩展,也更容易在不同服务之间迁移和复用。系统真正的核心不再是 Agent 本身,而是围绕事件流构建的上下文和工作流。
Agent 不是魔法,也不是一个无所不能的智能体,它本质上仍然是软件工程。
优秀的 Agent 系统并非依赖复杂的框架和庞大的自主能力,而是通过精心设计的上下文、清晰的控制流、可靠的人机协作机制以及可维护的软件架构,让 LLM 在最擅长的环节发挥价值。
53AI,企业落地大模型首选服务商
产品:场景落地咨询+大模型应用平台+行业解决方案
承诺:免费POC验证,效果达标后再合作。零风险落地应用大模型,已交付160+中大型企业
2026-05-31
亲测有效!Codex桌面版免费接入DeepSeek V4
2026-05-31
阿里开源语音识别「核弹」:170 倍实时、吊打 Whisper,还免费
2026-05-30
企业级 AI Agent 为什么集体转向“基座 + Skills”?
2026-05-25
DeepSeek 要用蜜雪冰城的打法,做中国版 Claude Code
2026-05-25
DeepSeek V4还能更省!新工具缓存命中率高达99.82%,2折稳定到手
2026-05-25
Anthropic开源Claude小企业插件:不用写prompt,15套现成流程顶半个运营团队
2026-05-21
麻省理工团队开源GenCAD,用一张图片生成完整CAD模型与参数化程序
2026-05-21
真Agent框架生态的主语言已经变为TypeScript
2026-03-30
2026-04-09
2026-04-03
2026-03-23
2026-04-01
2026-03-31
2026-03-09
2026-03-30
2026-03-12
2026-04-18
2026-05-30
2026-05-16
2026-04-22
2026-04-21
2026-04-15
2026-04-09
2026-04-01
2026-03-17