
当整个科技圈都在为 AI Agent 的巨大潜力而疯狂时,一场深刻的「反思」也正在悄然发生。
我们似乎都听过这样的故事:一个充满激情的团队,用 LangChain 或 CrewAI 等框架,几天之内就搭起了一个看起来足够酷炫的 Agent demo,让 CEO 兴奋不已,当场拍板追加六个人力。
但然后呢?
然后,团队就会一头撞上那堵臭名昭著的「80% 质量墙」。无论如何优化 prompt,无论如何调试,Agent 的可靠性始终在 70%-80% 之间徘徊,无法真正交付给付费客户。
为了突破最后这 20%,开发者不得不深入框架源码,在层层叠叠的调用栈中迷失方向,试图搞清楚一个 prompt 究竟是如何被构建的,工具又是如何被传入的。
最终,许多人的选择是:推倒重来,从头手写。
我惊讶地发现,市面上那些自诩为『AI Agent』的产品,其『智能』程度远没有想象中那么高。
说出此话的,是 Dex Horthy,HumanLayer 的创始人,也是一位资深的 Agent 构建者。

他试用了市面上几乎所有的 Agent 框架,与上百位一线 AI 创业者和工程师深入交流后,得出了一个颠覆性的结论:
真正优秀的、能进入生产环境的 Agent,其本质大多是确定性的代码,只是在关键节点巧妙地「撒」上了一些 LLM 调用。它们遵循的根本不是「给你 prompt、给你工具包、循环执行直到达成目标」的范式,而更像是……普通的软件。
这场关于 Agent 的集体狂热,是否让我们走错了方向?我们究竟该如何构建真正可靠、可扩展、可维护的 LLM 应用?
为了回答这个问题,Dex Horthy 借鉴了云计算时代里程碑式的「12-Factor App」方法论,整理并提出了 「12-Factor Agents」——构建可靠 LLM 应用的 12 条核心原则。
这不仅仅是一份技术文档,更像是一份「反思宣言」,也是写给所有 Agent 框架的「功能许愿单」。它试图将我们从对 Agent 的神化想象中拉回地面,重新用软件工程的第一性原理,审视 LLM 应用的构建之道。
接下来,就让我们深入这 12 条原则,看看它们将如何重塑我们对 AI Agent 的认知。
在展开 12 要素之前,我们必须先理解 Dex 的核心思想,它贯穿了整个方法论:
if 语句、switch 判断、while 循环……这些你早已烂熟于心的编程原语,正是构建可靠 Agent 的基石。这三大思想,构成了「12 要素」的基石。它们呼吁开发者,不要再将希望寄托于一个越来越聪明的「超级大脑」,而是要回归软件工程的本质,用确定性的方法论,驾驭 LLM 的不确定性。
许多人对 Agent 的着迷,源于其看似神奇的自主决策能力。但 Dex 认为,我们首先需要戳破这层「魔法」滤镜。
要素 1:自然语言到工具调用
LLM 最神奇的能力是什么?不是循环,不是推理,甚至不是代码生成。
而是将一句像「给我的团队 Slack 发个消息,说前端部署好了」这样的自然语言,精确地转化为一段像下面这样的 JSON 代码:
{
"tool":"slack.postMessage",
"parameters":{
"channel":"#devops",
"text":"Frontend deployment successful!"
}
}
这就是 LLM 在 Agent 系统中最核心、最不可替代的价值。它是一个通用的、非结构化到结构化的数据转换器。至于拿到这个 JSON 之后做什么,那是其他要素需要解决的问题。但只要你的应用中包含了这一步,你就已经抓住了 Agent 的精髓。
要素 4:工具只是结构化输出
有了上面的认知,我们就能得出一个有些「离经叛道」的结论:「工具调用 (Tool Use)」这个概念是有害的。
Dex 在这里引用了计算机科学史上著名的论文《Go To 语句被认为是有害的》。当年,goto 语句因为会破坏代码结构,导致程序逻辑混乱而备受诟病。
今天,「工具调用」这个术语也给我们带来了类似的困扰。它让我们误以为,Agent 是一个「有意识的实体」在与「环境」进行「交互」。
但实际上发生了什么?LLM 输出了 JSON,我们用确定性的代码 (比如一个 switch 语句) 去解析这个 JSON,然后执行相应的函数。
# 这不是魔法,这只是代码
tool_call = llm.output_json()
switch tool_call.name:
case"slack.postMessage":
slack_client.post_message(...)
case"github.create_pr":
github_client.create_pr(...)
default:
handle_unknown_tool()
所以,不要再把工具看作什么神秘的东西。它就是 JSON 和代码。这个视角的转变,是夺回控制权的第一步。
要素 8:掌控你自己的控制流
这是整个方法论中最核心、最具颠覆性的要素之一。
Agent 框架最初的承诺是什么?是让你扔掉有向无环图 (DAG)。你不再需要像使用 Airflow 或 Prefect 那样,预先定义好工作流的每一步和每一个分支。你只需给 LLM 一个目标和一堆工具,它就能自己规划路径。
这种模式通常被实现为一个简单的循环:
context = [initial_event]
whileTrue:
next_step = llm.determine_next_step(context)
if is_done(next_step):
break
result = execute_step(next_step)
context.append(result)
这个看似美好的模型,在现实中却根本行不通。
主要原因是,随着循环次数增加,上下文窗口 (Context Window) 会变得越来越长、越来越臃肿。即使是 Gemini 1.5 Pro 这样拥有百万级 Token 窗口的模型,当上下文变得混乱时,其输出质量也会急剧下降。没有人会否认,一个精简、清晰的上下文,永远比一个庞大、嘈杂的上下文更能产出高质量的结果。
那么,什么才是有效的模式?
答案是:回归 DAG,但让 LLM 成为其中的「超级节点」。
这个理念的最佳实践,就是 Dex 在 HumanLayer 内部构建的 DevOps 机器人。
案例研究:HumanLayer 的部署机器人
HumanLayer 的大部分部署流程是确定性的 CI/CD 代码。
看到了吗?整个流程是一个巨大的、由工程师编写的、确定性的 DAG。而 Agent 只是其中一个小小的、专注的、生命周期很短的循环。它只负责一件事:在需要灵活决策的节点,将自然语言转化为结构化的下一步指令。
这就是「掌控你的控制流」的真谛。你拥有一个宏观的、可靠的软件架构,然后在这个架构中,策略性地嵌入一些小而美的 Agent 循环,来实现「人机协同」或「动态决策」的魔法。
如果说 LLM 是一个函数,那么上下文就是它的唯一参数。如何精心雕琢这个参数,决定了应用的成败。
要素 2:掌控你的提示词
Prompt 框架和库非常适合快速入门,它们能帮你生成一个不错的基准 prompt。
但如果你想突破 80% 的质量瓶颈,你最终会需要逐字逐句地手写每一个 token。因为只有你,才最了解你的业务逻辑、你的数据结构和你的目标。
你需要能够自由地测试不同的 prompt 结构、不同的措辞、不同的示例,找到那个能让模型性能最大化的「天选之 prompt」。不要让框架的抽象,成为你优化性能的阻碍。
要素 3:掌控你的上下文窗口构建)
这比掌控 prompt 更进了一步。你不仅要控制 prompt 的内容,还要控制整个上下文的构建方式。
标准的 OpenAI 消息格式 ([{role: "system", ...}, {role: "user", ...}]) 是一种选择,但绝不是唯一选择。
在决定下一步时,你真的需要把之前所有的对话历史、工具调用结果、错误信息原封不动地塞进去吗?
或许,你可以把所有历史信息压缩成一段摘要。或许,你可以用一种自定义的、更紧凑的格式来表示事件流。
例如,一个自定义的事件流可能长这样:
EVENT: user_message | "Book a flight to SF"
TOOL_CALL: search_flights | {"destination": "SFO"}
TOOL_ERROR: api_timeout | "Flight search timed out"
TOOL_CALL: search_flights | {"destination": "SFO", "retry": 1}
TOOL_RESULT: flights_found | [{"flight": "UA123", ...}]
这里的关键是,你有能力尝试任何一种你认为最高效的上下文表示方法。你的目标是,在传递给 LLM 的信息密度和清晰度上,做到极致的优化。
要素 9:将错误压缩进上下文
当 Agent 调用工具出错时(比如 API 超时、参数错误),一个常见的做法是把错误信息和堆栈跟踪 (stack trace) 直接追加到上下文中,然后让 Agent 重试。
这很容易导致 Agent 陷入「死亡循环」,不断重复同样的错误,或者被无用的错误信息干扰,丢失了原始的目标。
一个更可靠的策略是:智能地处理错误。
destination_city」。记住,上下文窗口是你最宝贵的资源,不要用垃圾信息污染它。
好的 Agent,首先必须是好的软件。这意味着它需要遵循现代软件架构的最佳实践。
要素 10:小而专注的 Agent
正如前面 HumanLayer 的例子所示,微型 Agent 的模式远比一个大而全的单体 Agent (Monolithic Agent) 要可靠得多。
将复杂的任务分解成一个由确定性代码粘合的、多个微型 Agent 组成的 DAG。每个 Agent 只有单一、明确的职责,上下文窗口保持短小精悍,从而实现极高的可靠性。
Google NotebookLM 团队的一位成员曾分享过一个观点,Dex 对此深表赞同:
在 AI 领域创造卓越体验的唯一途径,就是找到一件恰好处于模型能力边界、它无法 100% 搞定的事情,然后通过你的工程能力,让它变得 100% 可靠。
微型 Agent 架构,正是实现这一目标的完美路径。
要素 5 & 6:统一状态管理,提供标准 API
Agent 的运行也需要状态管理。比如 current_step、retry_count 等执行状态,以及 messages、user_data 等业务状态。一个常见的错误,是把这两者割裂开来。
正确的做法是,将它们统一管理,并将 Agent 的整个状态(包括上下文窗口)序列化后存入数据库 (如 Redis, Postgres)。
这带来了下一个顺理成章的好处:你可以为你的 Agent 提供标准的启动/暂停/恢复 (Launch/Pause/Resume) API。
想象一下这个流程:
state_id。整个执行流程被暂停。state_id 和审批结果被发送回来。state_id 从数据库中加载 Agent 的状态,将审批结果追加到上下文中,然后无缝恢复 Agent 的执行。对于 LLM 来说,它甚至不知道自己中间被「冷冻」了几天。
这就是将 Agent 视作普通软件的威力。你可以用所有熟悉的工具和模式来管理它。
要素 12:让你的 Agent 成为一个无状态的 Reducer
这个要素是对上述思想的总结。Agent 本身应该是无状态的。它就像一个 Redux 中的 reducer 函数:(state, event) => newState。
它接收当前的状态和新的事件(比如一个工具调用的结果),然后决定下一步的动作,并产生一个新的状态。而状态的持久化和管理,则由外部的、确定性的应用逻辑来负责。
最后,可靠的 Agent 必须能够无缝地融入人类的工作流和沟通渠道。
要素 7:用工具调用来联系人类
许多 Agent 框架在输出时,会让 LLM 在「调用工具」和「回复用户」之间二选一。这其实是一个糟糕的设计。
一个更优雅的模式是:将「联系人类」也视为一种工具调用。
{
"tool":"contact_human",
"parameters":{
"message":"I need clarification on the budget.",
"reason":"clarification_needed"
}
}
这样做有两大好处:
ask_for_clarification, request_approval, escalate_to_manager。这让后续的确定性逻辑处理起来更容易。要素 11:从任何地方触发,在任何地方与用户相遇
用户不想为了和你的 Agent 交互,而打开第七个聊天窗口。
可靠的 LLM 应用应该能与用户现有的工具无缝集成。让用户可以通过 Email, Slack, Discord, SMS 等任何他们熟悉的渠道来与 Agent 互动。这意味着你的 Agent 需要被设计成可以从各种事件源(Webhook, Cron Job, 消息队列)触发,并将结果返回到相应的渠道。
通篇读下来,你可能会觉得这是一篇「反框架」的檄文。
但 Dex 强调,他的本意并非抨击框架,而是希望我们能共同思考:我们真正需要什么样的工具?
「十二要素 Agent 方法论」并未提供任何银弹,它更像是一次「正本清源」的呼吁。
他认为,目前许多 Agent 框架更像是 Bootstrap:一个大而全的包裹,为你隐藏了内部细节,但也限制了你的自由度。
而我们真正需要的,可能更像是 shadcn/ui:它不是一个组件库,而是一个脚手架 (Scaffolding)。你用它一键生成代码,然后这些代码就完全属于你了,你可以任意修改和掌控。
许多工具试图将问题中困难的 AI 部分抽离出去,让你即插即用。我认为这应该是反过来的。好的工具,应该将其他困难的部分(如状态管理、API 接口、可观测性)抽离出去,从而让我们能将所有时间,都花在真正困难的 AI 部分上:打磨 prompts,优化控制流,雕琢每一个 token。
「12-Factor Agents」的发布,或许标志着 AI 应用开发领域一个重要转折点的到来。它宣告了「黑箱式」Agent 构建范式的局限性,并吹响了「回归软件工程」的号角。
正如一位 Hacker News 用户评论道:
在职业发展上,学习底层的 LLM 接口,远比依赖某个框架要明智得多。一旦你掌握了底层,切换到任何平台都轻而易举,但反过来则充满挑战。尤其是在 AI 领域,没人知道明年的最佳实践会是什么样。所以,花时间学习底层,而不是把自己绑在一个一年后可能就过时的框架上。
或许,我们离真正可靠的 LLM 应用,差的不是一个更聪明的模型,而是一套更成熟、更回归本质的工程方法论。
或许,当 AI 的浪潮将我们推向一个又一个技术奇点时,我们最需要的,恰恰是回头看看那些早已被验证过的、最朴素的工程原则。
毕竟,无论外面的世界如何变化,工程师的本分,始终是构建可靠、可信的系统。
参考资料:
https://github.com/humanlayer/12-factor-agentshttps://news.ycombinator.com/item?id=43699271https://www.youtube.com/watch?v=8kMaTybvDUw