
1. 从单轮到多 Agent为什么这个话题值得认真聊1.1 一个绕不开的现实问题如果你最近半年在折腾大模型应用大概率会有一种感觉单轮调用已经不够用了。最开始大家写个 Prompt调一次接口拿结果能跑通就觉得挺美。但真到了业务场景里问题一个接一个冒出来——模型记不住上下文、复杂任务一步做不完、工具调用经常出错、多步骤流程没法自动衔接。这时候你自然会想能不能让模型自己决定下一步做什么能不能让它调用工具、观察结果、再决定下一步这就是 Agent 的起点。我是一名大模型开发工程师过去一年多的时间里从最简单的单轮问答到多 Agent 协作系统几乎每种实现方式都踩过坑。这篇文章不打算给你一个“标准答案”因为 Agent 这个领域本身就没有标准答案。我想做的是把从单轮调用到多 Agent 协作这条演进路径上的核心思路、关键决策点和实操经验尽可能完整地梳理出来。无论你是刚接触 Agent 开发的新手还是已经在做企业级 Agent 平台的同行应该都能从中找到一些有用的东西。1.2 这篇文章适合谁看先说清楚受众。如果你完全没写过大模型调用连 Prompt 都没调过那这篇文章可能会有点吃力建议先补一下基础。但如果你已经能写 Prompt、调 API想搞清楚 Agent 到底怎么从零搭起来那这篇文章就是为你准备的。如果你已经在做 Agent 项目但对多 Agent 协作的架构设计还有困惑那第三、四部分应该能给你一些参考。文章会覆盖几个核心概念Prompt Engineering、Context Engineering、Loop Engineering、Graph Engineering。这四个词听起来像 buzzword但它们其实对应了 Agent 实现方式演进的四个关键阶段。我会用实际代码和架构图文字描述来说明每个阶段解决了什么问题、引入了什么新问题、以及什么时候该用哪种方式。提示这篇文章偏理论架构但所有理论都会配上可落地的实现思路。不会只讲概念不给代码。1.3 先给一个全局地图在深入细节之前先给一个全局视角。从单轮调用到多 Agent 协作大致可以分成四个阶段阶段核心特征关键技术典型场景单轮调用一次输入一次输出Prompt Engineering分类、摘要、简单问答循环调用模型自主决定下一步Loop Engineering工具调用、多步推理上下文管理动态管理记忆和状态Context Engineering长对话、复杂任务多 Agent 协作多个 Agent 分工配合Graph Engineering复杂工作流、企业级应用这四个阶段不是严格线性的很多系统会同时用到多种技术。但理解这个演进路径能帮你在做架构决策时想清楚我现在到底需要哪一层的能力是不是过度设计了还是能力不够2. 单轮调用与 Prompt Engineering一切的基础2.1 单轮调用的本质与局限单轮调用是最简单的形式你给模型一个输入模型给你一个输出。没有记忆没有工具没有循环。听起来很原始但说实话很多业务场景用单轮调用就够了。比如文本分类、情感分析、简单摘要、翻译这些任务不需要多步推理一次调用就能搞定。但单轮调用的局限也很明显。最核心的问题是模型只能基于你给它的输入做决策它没法主动获取额外信息也没法在输出之后根据结果调整策略。举个例子你让模型“查一下今天北京的天气然后告诉我穿什么”单轮调用做不到因为它没法查天气。你只能先把天气信息查好拼到 Prompt 里再让模型给建议。这就是所谓的“人工编排”——你替模型做了决策。我见过不少团队在这个阶段就卡住了觉得 Agent 不过如此。其实问题不在 Agent在于任务本身就不需要 Agent。如果一个任务用单轮调用加好的 Prompt 就能解决那就别上 Agent简单可靠比什么都强。2.2 Prompt Engineering 的核心原则Prompt Engineering 是单轮调用阶段的核心技能。虽然现在大家都在聊 Agent但 Prompt 写得好不好直接决定了 Agent 的上限。我总结了几条在实际项目中反复验证过的原则第一条明确角色和边界。不要只说“你是一个助手”要说“你是一个专门处理电商退货申请的客服助手你只能处理退货相关问题其他问题一律转人工”。角色越具体模型的表现越稳定。第二条给例子比给规则更有效。你写十条规则不如给三个高质量的例子。Few-shot 的效果在大多数场景下都优于 zero-shot尤其是格式要求严格的场景。第三条输出格式要显式约束。如果你需要 JSON就在 Prompt 里写清楚 JSON 的 schema最好给一个示例。不要指望模型“猜”你想要什么格式。第四条把复杂任务拆成步骤。Chain-of-Thought 不是玄学它确实有效。让模型“一步一步思考”在推理类任务上能显著提升准确率。# 一个典型的单轮调用 Prompt 结构 prompt 你是一个电商退货申请处理助手。 ## 你的职责 - 判断用户的退货申请是否符合退货政策 - 如果符合提取退货原因和订单号 - 如果不符合说明原因并建议联系人工客服 ## 退货政策 1. 签收后 7 天内可无理由退货 2. 商品必须未使用、包装完整 3. 定制商品不支持退货 ## 输出格式 请以 JSON 格式输出 { eligible: true/false, reason: 退货原因, order_id: 订单号, message: 给用户的回复 } ## 用户输入 {user_input} 这段 Prompt 看起来简单但每一条都有讲究。角色定义让模型知道自己的边界政策说明给了判断依据输出格式约束让后续程序能直接解析。在实际项目中这种结构化的 Prompt 比随意写的 Prompt 准确率能高出 20% 以上。2.3 什么时候该从单轮升级到循环判断标准其实很简单如果任务需要模型根据中间结果决定下一步做什么那就需要循环。比如需要调用外部工具获取信息需要多步推理且每一步依赖上一步的结果需要根据执行结果决定是否重试或调整策略如果任务不需要这些那就老老实实用单轮调用。我见过太多项目为了“显得智能”硬上 Agent结果复杂度上去了稳定性下来了效果还不如单轮。注意Prompt Engineering 不是“写个提示词”那么简单。它是一套系统化的输入设计方法包括角色定义、任务拆解、格式约束、示例选择等多个维度。在 Agent 时代Prompt 的重要性不是降低了而是更高了——因为 Agent 的每一步决策都依赖 Prompt。3. Loop Engineering让模型自己决定下一步3.1 ReAct 模式循环调用的经典实现Loop Engineering 的核心思想是让模型在一个循环里运行每次循环它可以决定是调用工具、还是给出最终答案。最经典的实现就是 ReActReasoning Acting模式。ReAct 的流程大致是这样的模型接收任务和当前上下文模型输出一个“思考”Thought模型决定一个“行动”Action比如调用某个工具系统执行行动得到“观察结果”Observation把观察结果拼回上下文回到第 1 步直到模型输出“最终答案”Final Answer这个循环看起来简单但实现起来有很多细节要注意。比如怎么判断循环该结束了工具调用失败了怎么办模型陷入死循环怎么处理# ReAct 循环的简化实现 def react_loop(task, tools, max_iterations10): context f任务{task}\n\n for i in range(max_iterations): # 模型决策 response llm_call(context 请思考下一步行动。) # 解析输出 if Final Answer: in response: return extract_final_answer(response) action extract_action(response) tool_name action[tool] tool_input action[input] # 执行工具 if tool_name in tools: try: observation tools[tool_name](tool_input) except Exception as e: observation f工具执行失败{str(e)} else: observation f未知工具{tool_name} # 更新上下文 context f思考{response}\n context f观察{observation}\n\n return 达到最大迭代次数任务未完成这段代码是简化版实际项目中还需要处理更多边界情况。但核心逻辑就是这样模型决策、执行、观察、再决策。3.2 循环终止条件的设计循环终止条件是 Loop Engineering 里最容易被忽视、但最容易出问题的地方。我踩过的坑包括模型一直调用同一个工具、模型在思考阶段就卡住不输出行动、工具返回结果太长导致上下文爆炸。常见的终止条件设计最大迭代次数最简单粗暴但有效。一般设 5-15 次根据任务复杂度调整。重复检测如果模型连续两次调用同一个工具且输入相同强制终止。超时控制整个循环设置一个总超时时间比如 60 秒。显式终止信号模型输出特定标记时终止比如 “FINAL_ANSWER”。实操心得最大迭代次数不要设太大。我见过设 50 次的结果模型在第 30 次还在瞎转浪费 token 不说用户体验极差。一般 10 次以内能解决的任务才是适合用 Agent 的任务。超过 10 次还搞不定的要么是任务太复杂需要拆解要么是 Prompt 有问题。3.3 工具调用的工程化处理工具调用是 Loop Engineering 的核心能力但也是最容易出工程问题的地方。几个关键点工具描述要清晰。模型是根据工具描述来决定调不调的。描述写不清楚模型就会乱调。每个工具的描述应该包括功能说明、输入参数格式、输出格式、使用场景。参数校验要做在工具内部。不要指望模型每次都传对参数。工具函数内部要做类型检查和边界检查返回明确的错误信息让模型知道哪里错了。工具返回结果要精简。如果工具返回一大段文本直接拼到上下文里会迅速消耗 token。最好在工具层面做摘要或截断只返回关键信息。工具调用要有超时和重试。外部 API 可能超时数据库可能连接失败。工具层面要有超时控制和有限重试避免整个循环卡死。# 工具定义的推荐格式 tools { search_weather: { description: 查询指定城市的天气信息, parameters: { city: 城市名称如北京、上海, date: 日期格式 YYYY-MM-DD默认为今天 }, returns: 天气状况、温度、湿度、风力, function: search_weather_impl } }这种结构化的工具定义比单纯写个函数名效果好得多。模型能清楚知道每个工具能做什么、需要什么参数、返回什么。3.4 Loop Engineering 的适用边界Loop Engineering 不是万能的。它适合的任务类型有需要调用外部工具获取信息的任务、需要多步推理且步骤间有依赖的任务、需要根据中间结果调整策略的任务。不适合的任务类型纯文本生成任务单轮就够了、需要严格确定性输出的任务循环引入不确定性、对延迟极度敏感的任务循环意味着多次模型调用。我个人的经验是如果一个任务用单轮调用加好的 Prompt 能解决 80% 的情况那就别上循环。循环带来的复杂度提升是显著的包括调试难度、成本、延迟、稳定性风险。只有当单轮确实搞不定的时候才考虑 Loop Engineering。4. Context EngineeringAgent 的记忆与状态管理4.1 为什么上下文管理是 Agent 的核心难题Loop Engineering 解决了“让模型自己决定下一步”的问题但引入了一个新问题上下文越来越长。每次循环都会往上下文里追加思考、行动、观察结果几轮下来上下文就爆了。而且模型在长上下文里的表现会下降——这是目前所有大模型的通病。Context Engineering 就是解决这个问题的。它的核心目标是在有限的上下文窗口里放入最相关的信息让模型做出最好的决策。这包括几个子问题什么信息该保留什么信息该丢弃什么信息该压缩什么信息该外部存储我见过很多 Agent 项目Prompt 写得不错循环逻辑也没问题但就是效果不稳定。排查下来十有八九是上下文管理出了问题。要么是历史信息太多淹没了关键信息要么是重要信息被截断了要么是工具返回结果格式混乱导致模型理解困难。4.2 上下文窗口的分配策略上下文窗口是有限资源怎么分配直接决定了 Agent 的表现。我通常会把上下文分成几个区域区域内容占比建议说明系统提示角色、规则、工具定义15-20%固定不变每次都要带任务描述当前任务目标5-10%简洁明确历史记录之前的思考和行动30-40%需要动态管理工具结果最近一次工具返回20-30%可能很长需要压缩输出指令格式要求5-10%固定不变这个分配不是绝对的但思路是系统提示和输出指令是固定的任务描述要精简历史记录和工具结果是动态的需要重点管理。注意不同模型的上下文窗口大小不同但“窗口大”不等于“可以随便塞”。实测下来即使模型支持 128K 上下文有效信息超过 30K 之后模型对中间部分的注意力就会明显下降。所以上下文管理不是“能塞多少塞多少”而是“该塞多少塞多少”。4.3 历史记录的压缩与摘要历史记录是上下文里增长最快的部分。每一轮循环都会追加思考、行动、观察结果。如果不管理几轮下来就占满了。常见的压缩策略滑动窗口。只保留最近 N 轮的历史更早的直接丢弃。简单有效但可能丢失重要信息。摘要压缩。把较早的历史让模型总结成一段摘要保留关键信息丢弃细节。比滑动窗口更智能但需要额外的模型调用。关键信息提取。从历史中提取结构化信息比如已完成的步骤、已获取的数据只保留这些关键信息丢弃原始对话。外部存储。把完整历史存到外部数据库上下文里只放一个引用 ID需要时再检索。我通常的组合策略是最近 3 轮保留完整记录3 轮之前的做摘要压缩10 轮之前的只保留关键信息提取结果。这样既能保证近期信息的完整性又能控制上下文长度。# 历史记录管理的简化实现 def manage_history(history, max_recent3, max_summary10): if len(history) max_recent: return history recent history[-max_recent:] older history[:-max_recent] if len(older) max_summary: summary summarize_history(older) return [{type: summary, content: summary}] recent else: # 更早的历史只保留关键信息 key_info extract_key_info(older) return [{type: key_info, content: key_info}] recent4.4 工具返回结果的格式化处理工具返回结果是上下文里的另一个大头。一个搜索工具可能返回几千字的网页内容直接塞进上下文就是灾难。处理原则只保留与当前任务相关的信息其余丢弃或摘要。具体做法结构化提取如果工具返回 JSON只提取需要的字段。长度截断设置最大长度超出部分截断并加省略标记。摘要生成让模型对长文本做摘要只保留关键信息。分页加载如果数据量大先返回摘要模型需要时再加载详情。我踩过的一个坑是搜索工具返回了 10 条结果每条 500 字总共 5000 字全塞进上下文。结果模型在后续推理中完全忽略了这些信息因为信息量太大模型“抓不住重点”。后来改成只返回前 3 条结果的标题和摘要总共 300 字模型反而能有效利用这些信息。4.5 Context Engineering 的实战检查清单在实际项目中我通常会对照这个清单来检查上下文管理是否到位[ ] 系统提示是否精简且完整[ ] 任务描述是否明确无歧义[ ] 历史记录是否有压缩策略[ ] 工具返回结果是否做了格式化[ ] 上下文总长度是否在模型有效范围内[ ] 关键信息是否在上下文中容易被模型注意到[ ] 是否有机制检测上下文溢出并自动处理这个清单看起来简单但每一条在实际项目中都可能出问题。尤其是最后一条很多项目没有上下文溢出的检测机制等到模型开始胡言乱语了才发现上下文爆了。5. Graph Engineering多 Agent 协作的架构设计5.1 从单 Agent 到多 Agent 的演进逻辑当一个 Agent 搞不定的时候自然会想到用多个 Agent。但多 Agent 不是简单地把任务分给几个 Agent 就完事了。它引入了一堆新问题Agent 之间怎么通信任务怎么分配冲突怎么解决状态怎么同步Graph Engineering 就是解决这些问题的。它的核心思想是把多 Agent 系统看作一个有向图节点是 Agent 或工具边是通信路径整个图的执行就是任务的求解过程。这个思路其实借鉴了工作流引擎和分布式系统的设计理念。但和传统工作流不同的是Agent 图里的节点是“智能”的它们可以根据输入动态决定输出而不是执行固定的逻辑。5.2 多 Agent 协作的常见拓扑结构根据任务特点多 Agent 系统可以采用不同的拓扑结构流水线结构。Agent 按顺序执行前一个的输出是后一个的输入。适合步骤明确、线性执行的任务比如“需求分析 - 方案设计 - 代码生成 - 代码审查”。并行结构。多个 Agent 同时执行不同子任务最后汇总结果。适合子任务之间无依赖的场景比如“同时搜索多个数据源”。层级结构。有一个协调者 Agent 负责任务分配和结果汇总多个执行者 Agent 负责具体子任务。适合复杂任务协调者可以根据执行情况动态调整。辩论结构。多个 Agent 对同一问题给出不同答案通过辩论或投票达成一致。适合需要多角度分析的任务比如风险评估。拓扑结构适用场景优点缺点流水线线性任务简单可控无法并行容错差并行独立子任务效率高结果汇总复杂层级复杂任务灵活可扩展协调者成为瓶颈辩论决策类任务多角度分析成本高收敛慢我实际项目中用得最多的是层级结构。一个协调者 Agent 负责理解任务、拆解子任务、分配给执行者 Agent、收集结果、判断是否完成。执行者 Agent 各自负责一个具体能力比如搜索、计算、代码执行。5.3 Agent 间通信协议的设计多 Agent 协作的核心是通信。Agent 之间怎么传递信息、怎么表达意图、怎么处理冲突都需要一套协议。我通常采用的消息格式{ from: coordinator, to: search_agent, type: task, content: { task_id: task_001, description: 搜索2024年AI Agent领域的重要进展, constraints: { max_results: 5, time_range: 2024-01-01 to 2024-12-31 } }, timestamp: 2024-12-15T10:30:00Z }响应格式{ from: search_agent, to: coordinator, type: result, content: { task_id: task_001, status: success, data: [...], summary: 找到5条相关进展... }, timestamp: 2024-12-15T10:30:05Z }这种结构化消息格式的好处是清晰、可追溯、易于调试。每个消息都有明确的发送者、接收者、类型和内容出问题时可以快速定位。实操心得Agent 间通信最容易出的问题是“信息丢失”和“信息过载”。信息丢失是指发送方以为接收方知道某个信息但实际没传过去。信息过载是指发送方把所有信息都塞给接收方导致接收方处理不过来。解决方法是明确每个 Agent 的输入输出契约只传必要信息必要时做摘要。5.4 状态管理与一致性保障多 Agent 系统里状态管理是个大问题。每个 Agent 都有自己的状态Agent 之间的状态需要同步。如果处理不好就会出现“Agent A 以为任务完成了Agent B 还在执行”这种情况。常见的状态管理方案中心化状态。有一个全局状态存储所有 Agent 都读写这个存储。优点是状态一致性好缺点是中心存储可能成为瓶颈。去中心化状态。每个 Agent 维护自己的状态通过消息传递同步。优点是扩展性好缺点是一致性难保证。混合方案。关键状态中心化存储非关键状态各 Agent 自己维护。这是我在实际项目中用得最多的方案。状态一致性保障机制版本号每次状态更新递增版本号冲突时以高版本为准。锁机制关键状态更新时加锁防止并发写冲突。事务多个状态更新打包成一个事务要么全成功要么全失败。补偿状态不一致时通过补偿操作回滚或修正。5.5 多 Agent 系统的可观测性建设多 Agent 系统比单 Agent 复杂得多出问题时排查难度也大得多。可观测性建设不是可选项是必选项。我通常会记录以下信息每个 Agent 的输入输出完整记录用于复现问题。消息传递日志谁在什么时候给谁发了什么消息。状态变更历史状态什么时候被谁改成了什么。性能指标每个 Agent 的响应时间、token 消耗、工具调用次数。错误日志所有异常和错误包括堆栈信息。这些信息汇总到一个可视化面板上可以实时看到整个系统的运行状态。出问题时可以快速定位是哪个 Agent、哪个环节出了问题。# 可观测性埋点的简化实现 class AgentMonitor: def __init__(self): self.logs [] self.metrics {} def log_message(self, from_agent, to_agent, message): self.logs.append({ type: message, from: from_agent, to: to_agent, content: message, timestamp: time.time() }) def log_state_change(self, agent, old_state, new_state): self.logs.append({ type: state_change, agent: agent, old: old_state, new: new_state, timestamp: time.time() }) def record_metric(self, agent, metric_name, value): key f{agent}.{metric_name} if key not in self.metrics: self.metrics[key] [] self.metrics[key].append({ value: value, timestamp: time.time() })这套埋点机制看起来简单但在实际排查问题时非常有用。我遇到过好几次“Agent 莫名其妙不执行”的情况最后都是通过消息日志发现是某个消息格式不对导致接收方解析失败。6. 常见问题与排查技巧实录6.1 Agent 陷入死循环怎么办这是最常见的问题。表现是Agent 反复调用同一个工具或者反复输出同样的思考就是不给最终答案。排查思路检查工具返回结果是不是工具一直返回错误导致 Agent 反复重试如果是修复工具或增加错误处理。检查 Prompt是不是 Prompt 里没有明确的终止条件加上“如果无法完成请输出 FINAL_ANSWER: 无法完成”。检查循环终止逻辑是不是最大迭代次数设太大了调小一点。检查上下文是不是上下文里有什么信息让 Agent 困惑了清理上下文试试。我遇到过一个案例Agent 反复调用搜索工具因为搜索工具返回的结果里包含“未找到相关信息”Agent 以为需要换个关键词再搜。但实际上那个信息确实不存在。后来在 Prompt 里加了“如果搜索三次仍未找到请直接告知用户未找到”问题解决。6.2 工具调用参数错误怎么处理模型传错参数是常态。处理方式工具内部做参数校验类型不对、格式不对、超出范围都返回明确的错误信息。错误信息要具体不要只说“参数错误”要说“city 参数必须是字符串你传的是数字”。给模型重试机会工具返回错误后模型可以根据错误信息修正参数再试。设置重试上限同一个工具连续失败 3 次强制终止避免无限重试。6.3 上下文溢出怎么预防上下文溢出是 Agent 运行一段时间后必然遇到的问题。预防措施实时监控上下文长度每次循环前检查接近上限时触发压缩。设置硬上限超过硬上限直接终止返回错误。压缩策略要提前设计不要等到溢出了才想怎么办。工具返回结果要控制长度从源头控制上下文增长。6.4 多 Agent 协作时任务分配不均表现是有的 Agent 忙死有的 Agent 闲死。协调者把大部分任务都分给了一个 Agent。排查思路检查协调者的分配逻辑是不是分配策略有问题检查 Agent 的能力描述是不是某个 Agent 的能力描述太宽泛导致协调者什么都分给它检查任务拆解粒度是不是任务拆得太粗导致无法并行解决方法是细化 Agent 的能力描述让每个 Agent 的职责更明确优化任务拆解逻辑让子任务更均衡必要时引入负载均衡机制。6.5 常见问题速查表问题可能原因排查方法解决方案Agent 死循环工具一直报错/终止条件不明确查看工具日志和 Prompt修复工具/加终止条件参数传错工具描述不清/模型理解偏差查看工具调用日志优化工具描述/加参数校验上下文溢出历史记录太长/工具返回太长监控上下文长度压缩历史/截断工具返回任务分配不均协调者逻辑问题/能力描述模糊查看任务分配日志优化分配逻辑/细化能力描述Agent 不执行消息格式错误/状态不一致查看消息日志和状态历史修复消息格式/同步状态结果质量差Prompt 问题/上下文问题对比输入输出优化 Prompt/清理上下文提示这张表建议打印出来贴在工位上。Agent 开发中 80% 的问题都能在这张表里找到对应项。7. 从理论到落地一些个人体会7.1 不要为了 Agent 而 Agent这是我最大的体会。Agent 是手段不是目的。如果一个任务用单轮调用能解决就别上循环。如果一个 Agent 能搞定就别上多 Agent。复杂度是有代价的包括开发成本、维护成本、运行成本、调试成本。我见过太多项目明明是个简单的分类任务非要搞个多 Agent 协作结果准确率还不如单轮调用。问为什么答“显得先进”。这种思路要不得。7.2 可观测性比功能更重要Agent 系统出问题是常态不出问题才是意外。所以可观测性建设要优先于功能开发。没有可观测性出了问题就是两眼一抹黑只能靠猜。有了可观测性大部分问题都能在几分钟内定位。我的做法是在写第一行 Agent 代码之前先把日志、监控、追踪的框架搭好。后面每加一个功能都顺手加上埋点。这样系统越复杂可观测性的价值越大。7.3 上下文管理是永恒的主题无论 Agent 架构怎么演进上下文管理始终是核心难题。模型的能力在提升上下文窗口在扩大但“在正确的时间给模型正确的信息”这个挑战不会消失。反而随着任务复杂度提升这个挑战会越来越大。我的建议是把上下文管理当作一个独立的模块来设计和实现不要散落在各个 Agent 的代码里。统一的上下文管理模块能让整个系统的行为更可预测、更可调试。7.4 多 Agent 协作的边界多 Agent 不是银弹。它适合的任务类型是任务可以明确拆解成子任务、子任务之间可以并行或流水线执行、每个子任务需要不同的专业能力。如果任务本身是高度耦合的、需要全局视角的那多 Agent 反而会降低效果。我个人的经验是先从单 Agent 开始遇到瓶颈再考虑多 Agent。单 Agent 的瓶颈通常是上下文不够用、能力太杂导致 Prompt 太长、需要并行执行。这时候再引入多 Agent才有明确的收益。7.5 最后分享一个小技巧在开发 Agent 系统时我习惯先写一个“最小可运行版本”——只有一个 Agent、一个工具、最简单的循环。跑通之后再逐步加功能、加 Agent、加工具。每加一个东西都确保系统还能跑通。这样做的好处是任何时候都有一个可工作的基线出问题可以快速回滚到上一个可用版本。比一次性设计一个大而全的系统然后花几周时间调试效率高得多。这个思路其实适用于所有复杂系统的开发。Agent 系统也不例外。从简单开始逐步演进每一步都确保可运行、可观测、可回滚。这样即使最终系统很复杂你也能清楚地知道每个部分是怎么来的、为什么这么设计。