Agent 是过去一年 AI 应用开发中最火的概念之一。从 AutoGPT 到 LangGraph从 CrewAI 到各种开源 Agent 平台人们似乎越来越接近一个诱人的愿景只要给 AI 一个目标再给它一些工具它就能像“数字员工”一样自主规划、调用 API、处理异常、完成复杂任务。这个愿景确实令人兴奋。因为传统软件开发中程序员需要提前设计好流程、分支、异常处理和状态管理而 Agent 看起来像是把这些工作交给了模型。开发者不再需要把所有路径写死只需要告诉模型“你可以使用哪些工具”剩下的就由模型自己判断。但真正把 Agent 放进生产环境后很多团队都会遇到同一个问题Demo 很惊艳做到 70% 或 80% 也不难可剩下的 20% 却异常困难。模型会选错工具会重复尝试失败路径会在长上下文里忘记最初目标会把无关信息当作关键线索甚至会陷入循环。于是Agent 从“看起来像数字员工”变成了“看起来像一个很聪明但不稳定的实习生”。Dex Horthy 在《12-Factor Agents》中提出的核心观点正是对这种现象的反思真正可靠的 Agent并不是靠无限放大模型的自主性实现的而是靠成熟的软件工程实现的。换句话说Agent 不是魔法也不是可以替代工程设计的黑盒。它依然是软件系统的一部分必须接受软件工程中的控制流、状态管理、权限校验、错误恢复、可观测性和人机协作机制。这篇文章将围绕 12 条实践原则重新梳理生产级 Agent 应该如何设计。重点不在于介绍某个框架而是理解一种更底层的工程思想LLM 应该负责什么代码应该负责什么Prompt、Context、Tool Calling、状态和人类反馈之间到底是什么关系一、Agent 的理想与现实从流程图到动态决策传统软件本质上是一张流程图。无论是早期业务系统还是今天复杂的微服务、数据平台、自动化流水线本质上都是开发者提前设计好执行路径第一步做什么第二步做什么遇到某种情况走哪个分支失败以后如何重试最终结果如何返回。Airflow、Prefect、Dagster 这类工作流编排系统虽然增强了调度、监控、重试和可观测性能力但它们的核心仍然是一张 DAG也就是有向无环图。开发者始终掌握着流程控制权。机器学习出现以后软件系统变得“智能”了一些。例如在客服系统里增加情感分析在数据处理流程里加入文本分类模型在推荐系统里使用预测算法。但这些模型通常只是流程中的某个节点。整体流程仍然由开发者控制模型只是确定性系统中的一段智能逻辑。Agent 的想象力在于它试图改变这种模式。过去开发者需要定义完整路径而 Agent 希望开发者只定义目标和工具。比如用户说“帮我部署最新版本后端”系统不再提前写死每一步而是让模型自己判断是否要先查看 Git 标签是否要检查 CI 状态是否要请求人工审批是否要执行部署命令是否要把结果通知到 Slack。这意味着软件从“流程驱动”走向“目标驱动”。开发者不再规定每一步怎么走而是给模型一些可用工具让模型根据当前上下文动态选择路径。这种方式当然很有吸引力。因为现实业务场景复杂很难提前穷举所有可能路径。如果模型真的能够自主规划软件开发的复杂度似乎会大幅下降。但问题也正在这里。当模型获得过多控制权以后系统就开始变得不可预测。当前许多 Agent 框架采用的是经典循环模式模型读取上下文选择下一步行动系统执行工具把结果写回上下文模型继续判断下一步。这个循环看起来很通用但在长任务里很容易失控。随着任务推进上下文会越来越大。最初只有用户需求后来加入工具调用记录、执行结果、错误信息、中间状态、用户回复、系统提示、RAG 检索内容等。模型每一轮都要在这些信息里找出真正重要的部分。即使未来上下文窗口扩大到百万 Token也不意味着问题自动解决。因为问题不只是“放不放得下”更是“模型能不能关注到真正重要的信息”。上下文越长噪声越多噪声越多模型越容易偏离目标。所以生产级 Agent 的核心不是让模型更自由而是让模型在合适的边界内发挥作用。最有效的 Agent 往往不是一个无所不能的超级 Agent而是嵌入确定性工作流中的小型 Agent。它只处理那些需要语言理解、模糊判断和灵活决策的局部环节其他部分仍然交给传统软件系统负责。二、让模型负责决策而不是执行理解 Agent 的第一条原则是把 LLM 放在正确的位置上。很多人一开始会把 Agent 想象成“模型调用工具完成任务”。比如用户说“请帮我给 Terri 创建一个 750 美元的付款链接用于赞助二月份的 AI Tinkerers 活动。”于是 Agent 就去调用 Stripe API 创建付款链接。但严格来说模型并没有真正调用 Stripe API。模型真正擅长的是理解自然语言并把自然语言转换成结构化意图。它可以从用户的话里提取金额、对象、用途、备注然后输出类似这样的结构化结果{function:create_payment_link,parameters:{amount:750,customer:Terri,product:AI Tinkerers sponsorship,memo:February event sponsorship}}真正创建付款链接的是后端代码。代码会检查参数是否合法用户是否有权限金额是否超过限制然后调用 Stripe API处理异常记录日志返回结果。这个区分非常重要。LLM 应该负责“判断下一步应该做什么”而不是直接“执行这件事”。执行层永远应该掌握在确定性代码手里。因为执行涉及权限、安全、事务、一致性、异常处理和审计这些都不是大模型最擅长的事情。如果让模型直接控制执行流程系统风险会迅速升高。比如模型决定删除客户数据如果系统立即执行delete_customer()后果可能非常严重。但如果模型只是输出{intent:delete_customer,customer_id:123}系统就可以在执行之前加入权限校验、人工审批、风险评估、审计日志甚至二次确认。这就是生产级 Agent 的基本职责划分LLM 负责理解和决策代码负责执行和约束业务系统负责权限、安全、状态和结果管理。不要让模型直接做事要让模型决定“建议做什么”。至于这个建议是否执行、什么时候执行、如何执行应该由软件系统决定。三、Prompt 不是配置项而是代码很多 Agent 框架为了降低门槛会提供非常简洁的接口agentAgent(...)taskTask(...)resultagent.run(task)开发者只需要填写角色、目标、工具就能快速跑起来一个 Agent。对于 Demo 或原型验证这确实很方便。但一旦进入生产环境这种便利就可能变成问题。因为很多框架会隐藏大量 Prompt Engineering 逻辑。开发者看到的是几个配置字段模型真正接收到的却可能是几百甚至上千个 Token 的系统指令、工具描述和行为规范。这些内容由框架维护开发者很难完全掌握。当 Agent 表现不符合预期时你很难判断问题在哪里是模型不行是上下文组织不好是工具描述有歧义还是框架底层 Prompt 模板写得不适合业务所以作者提出一个很重要的原则不要把 Prompt 外包给框架。在 Agent 系统里Prompt 已经不是简单的提示文本而是程序逻辑的一部分。它决定模型如何理解任务、如何选择行动、如何处理异常、如何输出结构化结果。从这个意义上说Prompt 就是一种新的代码。如果 Prompt 是代码它就应该像代码一样被管理进入版本控制有清晰的变更记录可以被测试可以做灰度发布也可以根据场景持续迭代。很多开发者会把大量精力花在“调 Prompt”上但调完以后只是把它放在一个配置文件里缺少测试和工程化管理。这样做在早期可以但长期维护会非常痛苦。更好的方式是把 Prompt 纳入正式工程体系。比如为不同场景准备测试样例检查模型是否能输出正确结构记录每次 Prompt 修改对输出质量的影响在模型版本变化时重新跑评估集把 Prompt 与业务逻辑一起审查。这听起来麻烦但生产系统本来就需要这些能力。Agent 系统不是一次性写完的而是在真实业务中不断迭代。Prompt 如果不被当成代码维护系统就会越来越不可控。四、真正决定 Agent 上限的不是 Prompt而是 Context很多人以为 Agent 效果不好是 Prompt 不够好。于是不断修改系统提示词调整角色设定增加规则优化输出格式。但在复杂 Agent 系统中Prompt 只是上下文的一部分。模型真正看到的内容包括系统提示、用户输入、历史对话、工具调用记录、工具返回结果、错误信息、RAG 检索结果、长期记忆、结构化输出 Schema 和当前任务状态。这些整体才是 Context。LLM 本质上是一个无状态函数。它不会真的记得上一次发生了什么它只是根据当前输入生成输出。所谓 Agent 的“记忆”其实是开发者把历史信息重新组织后塞进上下文里。因此Agent 的能力很大程度上取决于开发者如何构建上下文。这就是 Context Engineering 的核心。优秀的上下文工程不是把更多信息塞给模型而是把最有价值的信息以最高密度呈现给模型。上下文越长不一定效果越好。很多时候短而清晰的上下文比庞杂的历史记录更有效。现在大多数 Agent 框架使用类似 OpenAI 的 Message 格式[{role:system,content:...},{role:user,content:...},{role:assistant,tool_calls:[...]},{role:tool,content:...}]这种格式简单统一也适合普通聊天。但复杂任务中它未必是最优结构。因为模型真正需要知道的不是某条消息的role是什么而是任务目标是什么已经发生了哪些关键事件调用了什么工具工具返回了什么结果当前还有什么阻塞下一步有哪些可选行动。所以我们可以不把 Context 理解为“聊天记录”而是理解为“系统状态”。例如一个部署 Agent 的上下文不一定要写成连续对话而可以组织成事件流user_request请部署最新版本后端/user_requestlist_git_tags_result最新标签为 v1.8.3/list_git_tags_resultci_status测试通过/ci_statushuman_approval负责人已批准部署/human_approval这种形式更接近日志系统或事件流也更适合让模型快速理解任务状态。上下文工程的目标不是还原所有细节而是帮助模型在当前时刻做出正确判断。该保留的保留该压缩的压缩该删除的删除。尤其是工具调用记录、错误堆栈、重复信息如果不加处理地全部塞进去很容易污染上下文。可以说Prompt 决定模型的行为边界而 Context 决定模型的判断质量。很多时候优化 Context 比优化 Prompt 更重要。五、工具调用本质上只是结构化输出“Tool Calling”经常被描述成 Agent 的核心能力。很多框架会说模型能够调用函数、访问数据库、发送邮件、执行代码仿佛模型真的获得了操作外部世界的能力。但更准确的理解是工具调用只是结构化输出。当用户说“帮我创建一个 Jira 工单”时模型并没有真的访问 Jira。它只是生成一段结构化数据{intent:create_issue,title:Payment API Timeout,description:支付接口出现超时问题,assignee:alice}然后代码读取这段数据ifresult[intent]create_issue:jira.create_issue(titleresult[title],descriptionresult[description],assigneeresult[assignee])真正调用 Jira API 的始终是程序。这个理解能帮助我们摆脱一个常见误区工具不应该被简单等同于函数。很多框架会把工具和函数强绑定模型一旦选择某个工具系统就立即执行对应函数。这种模式在低风险场景下没问题但在生产环境中很危险。更好的方式是把 Tool 看成一种“结构化意图”。模型输出的是“想做什么”不是“立即怎么做”。比如模型输出{intent:deploy_backend,version:v1.8.3}系统可以根据业务规则决定后续动作立即部署先检查部署窗口先发起审批写入任务队列创建异步任务等待人工确认或者拒绝执行并返回原因。这就把 Agent 从“直接执行工具的黑盒”变成了“提出行动建议的决策组件”。一旦接受这个视角我们就会更自然地加入安全边界。高风险操作不应该由模型说了算而应该由软件系统根据结构化意图进行判断。模型负责灵活理解系统负责稳定执行。六、状态应该回归单一事实来源复杂 Agent 系统很容易出现两套状态。一套是执行状态比如当前执行到哪一步、是否正在等待、重试了几次、下一次什么时候唤醒。另一套是业务状态比如用户提出了什么请求、模型做了什么决策、工具返回了什么结果、人工审批是否通过。如果这两套状态分开维护系统复杂度会迅速上升。你需要保证它们同步需要处理状态不一致需要在调试时同时查看日志、数据库、工作流状态机和消息队列。作者建议尽可能统一执行状态和业务状态让 Agent 的历史记录成为单一事实来源。这和 Event Sourcing 的思想很接近。系统不直接保存一个“当前状态”而是保存所有已经发生的事件。当前状态可以从事件历史中推导出来。比如一个部署任务的事件流可能是[{type:user_requested_deploy,version:latest},{type:agent_selected_action,intent:list_git_tags},{type:tool_result,tool:list_git_tags,result:v1.8.3},{type:agent_selected_action,intent:request_human_approval},{type:human_approved,user:tech_lead},{type:agent_selected_action,intent:deploy_backend},{type:tool_result,tool:deploy_backend,result:success}]从这串事件中我们可以知道任务已经完成也可以知道每一步为什么发生。系统不一定需要额外维护一个复杂状态机因为当前状态可以从事件流里计算出来。这种统一状态模型有几个明显好处。它让系统更简单。所有事实都在同一个事件流里减少状态同步问题。它也更容易恢复。任务中断以后只要重新加载事件历史再追加新的事件就能继续运行。它还更容易调试。开发者可以完整回放 Agent 的决策过程知道它为什么选了某个工具工具返回了什么错误发生在哪里。甚至它还可以实现“时间旅行”。如果某一步决策错了可以从历史节点复制一份新线程让 Agent 基于相同上下文重新尝试。当然不是所有数据都应该进入上下文。访问令牌、Session ID、密码、数据库连接信息等敏感数据不应该暴露给模型。但原则上真正不能进入上下文的数据应该越少越好。否则 Agent 的行为就会越来越依赖外部隐藏状态降低可恢复性和可解释性。最理想的情况是Agent 的历史记录就是 Agent 的状态。七、Agent 应该像普通程序一样运行可以暂停也可以恢复很多人想象中的 Agent 是一个持续运行的循环模型思考调用工具读取结果再思考再调用工具直到任务完成。但真实业务并不总是连续完成的。很多任务需要等待等待人工审批等待用户回复等待第三方 API等待异步任务完成等待部署窗口等待定时触发等待 Webhook 回调。如果 Agent 在等待期间一直运行会浪费资源也会增加复杂度。更合理的方式是让 Agent 能够暂停和恢复。当 Agent 遇到需要等待的环节时它应该保存当前事件历史然后停止运行。等外部事件发生比如用户在 Slack 里批准了部署系统再把这个批准事件追加到历史记录中重新启动 Agent让它继续判断下一步。这时恢复 Agent 并不需要复杂的运行时环境。只要 Agent 的状态已经保存在事件流里恢复过程就很简单加载历史事件追加新事件构建当前上下文调用模型生成下一步动作再由系统决定是否执行。这使 Agent 更像一个普通服务而不是一个神秘的长时间运行进程。它可以被 Slack 消息触发可以被 GitHub Webhook 唤醒可以被审批系统暂停也可以被 CI/CD 流水线恢复。换句话说Agent 不应该是一段持续运行的循环而应该是一种可以随时启动、暂停和恢复的任务。这也是 Agent 真正融入企业软件架构的关键。它不需要取代现有系统而是作为一个标准组件嵌入其中。八、把人类也视为一种“工具”在真实业务中很多关键决策不应该由模型独立完成。比如生产环境部署、客户退款、删除数据、发送重要邮件、修改合同条款等都需要人类确认。传统聊天机器人通常是“人问AI 答”。但生产级 Agent 需要一种更主动的人机协作方式当 Agent 遇到关键决策时可以主动联系人类。这时人类反馈也可以被抽象成一种特殊工具调用。比如 Agent 输出{intent:request_human_input,question:是否继续部署到生产环境,approver:tech_lead}系统收到这个结构化意图后不会继续执行部署而是暂停 Agent把问题发送给负责人。负责人通过 Slack、邮件或企业微信回复后系统把回复作为新事件写入事件流再恢复 Agent。这样一来人类审批、用户确认、业务判断都被纳入了 Agent 的标准工作流。对模型来说联系人类和查询 API 没有本质区别都是为了获得下一步决策所需的信息。这个设计非常重要因为它突破了聊天界面的限制。Agent 不一定总是等待用户主动提问它也可以由监控告警、定时任务、业务事件触发然后在需要人类判断时主动联系相关人员。这就是所谓的 Outer Loop Agent。它不是被动聊天机器人而是能在外部业务循环中主动工作的数字同事。当人类也成为工作流中的节点时Agent 才能真正处理高价值任务。因为高价值任务往往伴随高风险而高风险任务必须有人工确认、权限控制和审计机制。九、不要把工作流控制权交给 Agent 框架很多 Agent 框架会自动管理 Agent Loop模型选择工具框架执行工具把结果返回给模型然后继续循环直到任务结束。这很方便但生产环境中不能把控制流完全交给框架。原因是不同工具调用需要不同处理策略。比如模型想查询 Git 标签这种低风险操作可以立即执行但如果模型想部署生产环境就应该暂停并请求审批如果模型要删除数据就应该经过权限校验和人工确认如果模型启动了长时间任务就应该写入队列并等待异步回调。这些控制逻辑很难完全交给通用框架。因为它们强烈依赖具体业务。生产级 Agent 的一个关键能力是系统应该能在“模型选择工具”和“工具真正执行”之间中断。这一步非常关键。模型可以说“我建议执行部署”但系统应该有机会检查这个建议决定是否执行、何时执行、由谁审批、如何记录。如果框架一旦收到工具调用就立即执行开发者就失去了最重要的控制点。最终只能在几个不理想的方案里选择要么让 Agent 只能做低风险任务要么让高风险任务也自动执行要么自己绕开框架做复杂补丁。更合理的设计是LLM 负责输出下一步行动建议业务代码负责控制流程。控制流里可以加入权限校验、人工审批、限流、缓存、上下文压缩、运行日志、评审机制、异步任务、错误重试和风险拦截。一句话模型决定“做什么”系统决定“是否做、什么时候做、如何做”。十、让错误成为 Agent 学习的一部分传统程序里错误通常意味着流程中断。但 Agent 系统里错误可以成为上下文的一部分。如果工具调用失败不一定马上终止任务。可以把错误信息作为事件写入上下文让模型在下一轮推理时看到失败原因并尝试修正。比如 API 返回“参数缺失”模型可能会补充参数后重新调用如果返回“资源不存在”模型可能会先查询资源列表如果返回“权限不足”模型可能会请求人工介入。这种机制让 Agent 具备一定的自我修复能力。但错误信息不能无限累积。否则模型可能陷入错误循环不断尝试类似方法不断失败不断把错误塞进上下文最终上下文越来越混乱。所以错误也需要工程化处理。系统可以设置重试阈值比如连续失败三次后停止自动尝试升级给人工处理。也可以对错误信息进行压缩只保留关键原因而不是把完整堆栈全部塞给模型。还可以把多次失败总结成一句状态描述部署尝试已失败 3 次主要原因是目标服务器权限不足需要人工确认凭据配置。错误不是流程终点而是上下文输入。但错误反馈的目标不是让 Agent 无限重试而是在有限范围内帮助它恢复。如果超过恢复能力就应该交给人类或其他确定性流程处理。十一、让 Agent 专注于一件事很多人一开始想做万能 Agent一个 Agent 负责所有任务拥有所有工具能处理所有业务流程。这通常是失败的开始。因为任务范围越大Agent 需要处理的上下文越复杂工具选择越困难推理步骤越长出错概率越高。模型会更容易忘记最初目标也更容易混淆不同任务之间的边界。更好的方式是构建多个小而专注的 Agent。比如部署 Agent 只负责部署客服 Agent 只负责工单分流文档 Agent 只负责生成说明文档故障诊断 Agent 只负责分析监控和日志财务 Agent 只负责整理报销材料。每个 Agent 的职责应该清晰工具集应该有限工作流程最好保持在 3 到 10 步以内即使复杂任务也尽量不要超过 20 步。小型 Agent 的好处很多。上下文更短模型更容易聚焦工具更少选择错误概率更低职责更清楚测试和调试更容易系统更模块化可以像微服务一样组合。与其构建一个什么都会做但经常失败的大 Agent不如构建多个边界清晰、行为稳定的小 Agent。真正可用的智能系统往往不是一个大脑控制一切而是多个专用组件协同工作。十二、让 Agent 出现在用户工作的地方很多人把 Agent 设计成一个独立聊天界面用户需要打开它输入问题然后等待回答。但在企业场景里用户真正工作的地方可能是 Slack、邮件、企业微信、短信、工单系统、监控平台、CRM 或代码仓库。真正有价值的 Agent不应该要求用户专门来到某个 AI 应用里而应该出现在用户原本工作的地方。例如开发者可以直接在 Slack 里说“部署最新后端版本”负责人可以通过邮件批准一次生产变更客服人员可以在工单系统里让 Agent 总结客户问题运维系统可以在监控告警触发后自动启动故障诊断 Agent财务人员可以在企业微信里确认一笔付款申请。这背后的关键不只是多渠道接入而是 Agent 要能融入真实工作流。在很多场景下Agent 的触发者甚至不一定是人。它可以由外部事件启动比如监控系统发现异常、CI/CD 流水线构建完成、定时任务到达执行时间、客户提交高优先级工单、数据指标突破阈值、合同状态发生变更或者代码仓库出现新的 Pull Request。Agent 在后台开始工作完成能自动完成的步骤当遇到需要判断、审批或补充信息的地方再主动联系相关人员。用户不需要一直盯着 Agent也不需要坐在聊天框前陪它一步步执行。这就让 Agent 更像一个数字同事而不是一个问答机器人。问答机器人通常是被动的用户问一句它答一句。生产级 Agent 则应该是主动协作的它能被事件触发能自己推进流程也能在需要时找到正确的人。这种设计与前面提到的暂停恢复、人类作为工具、事件流状态管理是连在一起的。因为只有 Agent 能够暂停才能等待人类回复只有状态能被持久化才能过几个小时甚至几天后恢复只有人类反馈能被写入事件流Agent 才能继续基于完整上下文做判断。所以Agent 的产品形态不一定是一个聊天窗口。它可能是 Slack 里的一个机器人是邮件里的一个审批助手是工单系统里的一个按钮是监控平台里的一个自动诊断流程也可能是隐藏在业务系统背后的自动化决策组件。优秀的 Agent 往往不是用户主动寻找的工具而是在恰当的时候出现主动推动事情向前发展的协作者。十三、把 Agent 看成状态转换器如果把前面的原则合在一起会得到一个很简洁的 Agent 定义Agent 并不是一个拥有神秘自主意识的智能体而是一个根据当前上下文生成下一步行动的状态转换器。这个思想可以借用函数式编程里的 Reducer 来理解。在 Redux 或 Event Sourcing 这类架构中系统当前状态不是凭空存在的而是由历史事件一步步计算出来的。Reducer 的工作非常简单输入当前状态和一个新事件输出新的状态。Agent 也可以这样理解。每一轮执行时它读取当前上下文也就是到目前为止发生过的事件然后输出下一步行动建议。这个行动被系统处理后又会产生新的事件。新的事件进入历史记录成为下一轮推理的输入。可以把它想成这样历史事件 新事件 ↓ 构建上下文 ↓ LLM 推理 ↓ 结构化行动建议 ↓ 业务系统执行或暂停 ↓ 产生新事件在这个模型里Agent 本身不需要保存复杂内部状态。它不需要一直运行也不需要记住所有事情。它只需要在被调用时根据输入上下文做一次判断。这带来了很大的工程优势。它更容易恢复。因为状态不在 Agent 内部而在事件历史中。任何时候只要重新加载事件历史就能恢复任务。它更容易测试。你可以给 Agent 一段固定上下文看它会输出什么行动建议。这样 Agent 的行为就可以被评估而不是只能靠人工观察。它更容易扩展。多个 Agent 可以基于同一套事件流工作也可以各自负责不同类型的状态转换。它更容易调试。因为每一次输入、输出、工具结果、人工反馈都被记录为事件开发者可以完整追踪系统行为。这种设计也能帮助我们摆脱对“Agent 自主性”的过度迷信。Agent 的价值不是它能脱离系统独立运行而是它能作为软件架构中的一个智能决策组件在合适的位置完成状态转换。它不是替代整个软件系统而是增强某些原本难以编码的环节自然语言理解、模糊意图判断、复杂信息总结、下一步行动建议。十四、一个更接近生产环境的 Agent 架构为了更直观地理解这些原则可以想象一个生产级部署 Agent 的架构。用户在 Slack 里输入帮我部署最新版本后端。系统不会让模型直接执行部署而是把这条消息记录为一个事件{type:user_message,channel:slack,content:帮我部署最新版本后端}然后系统构建上下文把任务目标、可用工具、最近事件和必要约束交给模型。模型输出结构化行动建议{intent:list_git_tags}业务代码判断这是低风险查询于是立即执行工具得到结果{type:tool_result,tool:list_git_tags,result:[v1.8.1,v1.8.2,v1.8.3]}系统再次调用模型。模型判断最新版本是v1.8.3接下来需要检查 CI 状态{intent:check_ci_status,version:v1.8.3}CI 检查通过后模型提出部署建议{intent:deploy_backend,version:v1.8.3,environment:production}但系统不会立刻执行。因为生产部署是高风险操作。业务代码拦截这个意图生成审批请求{type:approval_requested,question:是否批准部署后端 v1.8.3 到生产环境,approver:tech_lead}Agent 暂停。负责人收到 Slack 消息后点击批准。系统追加新事件{type:human_approved,approver:tech_lead,content:批准部署}Agent 恢复根据新的事件历史继续推理。模型再次输出部署行动建议。系统确认审批已通过于是执行部署。部署成功后写入事件流并通知用户。整个过程中模型从未直接控制外部系统。它只是不断根据上下文提出下一步建议。真正的执行、审批、暂停、恢复、安全控制和日志记录都由软件系统完成。这就是 12-Factor Agents 想强调的工程化思路。十五、结语Agent 的成熟是重新回到软件工程Agent 的真正价值不在于让模型摆脱约束、自由行动而在于让模型在可靠的软件架构中承担最适合它的角色。它擅长理解自然语言擅长从复杂信息中提取意图擅长生成结构化建议擅长总结错误并尝试修正擅长在有限上下文中判断下一步。但它不擅长承担系统执行、安全边界、事务一致性、权限控制和长期状态管理。所以生产级 Agent 的方向不是“给模型更多权限”而是“给模型更清晰的边界”。不要构建一个无所不能、不可预测的超级 Agent。更好的方式是构建一组小而专注、可控可测、能暂停恢复、能与人协作、能嵌入业务系统的 Agent。从这个角度看Agent 并不是传统软件工程的终结而是传统软件工程的新阶段。它让自然语言成为新的交互入口让 LLM 成为新的决策组件但系统的可靠性依然来自工程设计。未来真正成功的 Agent 产品可能不是那些看起来最“自主”的系统而是那些在关键时刻最稳定、最可解释、最容易恢复、最懂得向人类求助的系统。也就是说Agent 的成熟不是从软件工程中逃离而是重新回到软件工程。