资讯动态

删掉复杂架构后,Agent效果反而更好:一次实战简化复盘

发布时间:2026/10/20 21:37:49 来源:尧图企业网站定制
去年一整年我都在跟 Agent 打交道从最初一个能跑通 Demo 的“玩具”到后来真正敢放在线上给用户用的东西。中间换过模型、调过提示词、加过缓存可要说效果提升最大的一次改动说出来你可能不信我把架构删了。这里的“架构”不是泛指代码怎么组织而是我花了好几周搭起来的那套 Agent 基建——编排器、规划器、记忆组件、多智能体调度、状态机、任务队列应有尽有。当时觉得这才叫“专业”做 Agent 就得有个像样的“体统”。结果真跑起来一顿操作猛如虎效果和延迟双双感人用户问一句系统内部来回倒腾五六次模型调用等反应过来人都走了。后来我狠下心把它们一个接一个从主链路上拆掉原来要十几轮才能完成的事变成了“给个目标、查一下工具、直接干活”效果反而稳定了成本也降了不少。这篇文章不聊新框架、不聊新模型就讲讲我这一年的实际经历我为什么把架构删了删之前那套架构到底长什么样删的时候怎么改提示词和调用逻辑删完又踩了哪些坑。想直接照着干的可以跳到第三章想理解“越简单越好”这个结论是怎么来的建议从头看。1. 我在删架构之前搭的是什么1.1 那套“看起来很完整”的四层架构如果你也是从传统后端转来做 Agent 的很容易跟我一样踩进同一个坑先搭骨架再填血肉。我当时给自己规划了一套相当“正规”的方案。最底下是模型层上面架一个编排器负责把用户请求拆成子任务再决定先做哪个后做哪个。编排器下面挂着规划器每一步执行前都要专门调用一次模型“想一想”下一步干什么。旁边还有一块记忆层短时记忆放对话上下文长时记忆扔到向量库里做检索。最热闹的是多智能体调度层我拆了三个子 Agent一个负责理解任务一个负责写回复一个负责挑毛病。整套系统还配了状态机定义了一堆状态比如“待规划”“执行中”“待校验”生怕 Agent 跑着跑着丢了上下文。这套架构在纸面上看起来很完整谁看了都得夸一句“工程化做得好”。实际运行起来呢用户问一句“帮我查一下上季度的销售数据然后整理成摘要发给我”系统要先经过编排器拆解任务规划器决定是“先查数据再写摘要”还是“先写摘要再查数据”中间还得把历史消息走一遍向量检索最后三个子 Agent 轮流上场。整条链路下来少说四五次模型调用多的时候能到八九次延迟直接奔着十几秒去。1.2 问题不在“架构错了”在“替模型想”我当时没有立刻怀疑这套架构因为每个组件单看都有道理。编排器可以并行调度子任务规划器可以让执行更有条理多智能体可以分工协作记忆层可以解决上下文窗口不够的问题。这些都是从软件工程里被验证过的思想搬到 Agent 上来好像顺理成章。但跑了一段时间才发现问题恰恰出在“看起来有道理”上。每个组件本质上都是一次 LLM 调用而 LLM 调用是有概率出错的。编排器拆任务可能拆偏规划器可能想错子 Agent 之间传话可能传丢任何一个环节出了一点小问题经过下一层模型重新理解就会被放大。三条“智能”串在一起叠加出来的不是效果是噪声和延迟。打个不太恰当的比方一家公司办任何事都得经过三个经理层层审批每个经理都要开会讨论一轮。开会本身要花时间、花成本更坑的是经理 A 理解到的东西跟经理 B 转述的东西已经有了偏差等传到经理 C 那儿可能跟当初员工提出来的需求已经对不上了。架构不是没用是过量的架构在“替模型想”反而不让模型自己发挥。我当时做的第一件“删架构”的事就是先把这套过度设计从主流程上拿掉。2. 删掉的东西有哪些真正留下来的又是什么2.1 组件级的删减清单与其含糊地说“删架构”不如把每个组件到底怎么处理、为什么可以删明明白白列一张表。下面是我这次改动里最核心的对比。组件删除前的作用删除后由什么替代为什么可以被替代编排器拆解用户请求生成子任务列表并调度子 Agent主对话循环里由模型直接完成多步操作模型的指令遵循能力和上下文长度已经足够支撑“边想边做”省掉一次前置调用反而少一层理解偏差规划器每次执行前单独调用一次模型生成下一步计划取消单独规划调用模型在工具调用前自主决定动作规划这个动作本身会消耗一次模型调用而且它生成的计划还要被“执行阶段”再理解一遍相当于同一件事理解两次记忆层短时记忆存上下文长时记忆走向量检索上下文摘要 最近几轮原始消息长期数据交给外部工具持久化大多数任务只需要最近几轮信息长期记忆完全可以用一次数据库查询实现不需要在架构层造一个“记忆体”多智能体调度Task Agent、Write Agent、Review Agent 分工协作单 Agent 自行完成全部过程必要时在提示词里切换角色角色分离只是让同一个模型扮演不同人格子 Agent 之间互相通信时会出错、会包装错误收益远低于训练成本2.2 这套删减背后的原则平面化不垂直化你会发现我删掉这么多东西并不是把功能弄丢了而是把“功能在架构里的位置”换了。拿“规划”这件事来说以前规划器是独立的一层站在所有工具之上替模型做决定。现在规划和执行合并在同一次工具调用循环里模型看到用户的请求结合当前对话状态自己判断该调哪个工具、要不要调工具。这本质上就是当下主流模型的原生功能把工具列表一次性提供给模型模型在生成回复的过程中自主决定是否发起函数调用。规划器那套逻辑早就被模型原生能力覆盖了。记忆也一样。以前记忆层是架构的一部分要在每次请求之前从向量库里检索相关历史把得分高的片段拼进提示词。现在我把这层拆掉了改为两种方式短期记忆直接靠对话上下文里的原始消息长线信息靠工具。比如用户需要查询“他上个月提到过的偏好”我不再让 Agent 自己去向量库里“回忆”而是给它一个“查询用户偏好”的工具由它调用数据库拿到结果。这样记忆不再是一个神秘的架构组件而是一个普通的函数调用出错更容易定位数据也更干净。多智能体那边就更直接了。以前是三个 Agent 互相传话现在是同一个上下文里模型在回答前先扮演“阅读理解者”把任务重新表述一遍再扮演“执行者”调用工具最后扮演“审查者”自我检查。角色切换通过提示词实现成本几乎为零还能避免多个 Agent 之间共享上下文造成的信息污染。2.3 有些东西是底线我一条都没删“删架构”不是什么都不要了。有几样东西我始终坚持保留甚至比以前做得更重。一个是工具层。删掉编排器之后工具列表就是模型唯一能操作的“世界”。工具设计得好不好直接决定了 Agent 能力上限。我把原来分散在各个子 Agent 里的功能收敛成一套统一的工具集每个工具必须有清晰的名字、参数说明和返回结构还加了错误码。工具少了以后模型反而更容易选对。另一个是上下文显式管理。我不再依赖架构层的“记忆组件”但不能没有对话消息的持久化。每次运行都会把完整的 messages 写入存储方便复现问题。这就像是代码里不再搞花里胡哨的缓存框架但该有的日志一条都不能少。第三个是兜底策略。包括最大工具调用次数限制、单轮超时、同一个工具连续调用多次后强制停下。这些代码很朴素也就是几个 if 判断的事但它们在删掉编排器之后成了防止 Agent 失控的最后一道防线。3. 具体改动过程我是怎么一步步“删”的3.1 第一步砍掉编排器改成“规划与执行合一”的主循环我先动的是编排器。旧流程大概是这样的伪代码简化过的逻辑# 删除前 def run_agent(request, user_context): tasks orchestrator(request) # 第 1 次模型调用拆任务 results [] for task in tasks: result task_agent(task, user_context) # 每个子任务对应 N 次模型调用 results.append(result) final_reply finalizer(request, tasks, results) # 最后一次模型调用汇总 return final_reply新流程长这样# 删除后 def run_agent(request, tools): messages [{role: system, content: SYSTEM_PROMPT}, {role: user, content: request}] for _ in range(MAX_ITERATIONS): response model.call(messages, toolstools) # 模型自己决定要不要调用工具 if response.has_tool_calls(): messages.append(response) for call in response.tool_calls: result execute_tool(call) messages.append(tool_result(call, result)) continue return response.content这个改动动作不大但意义完全不同。以前系统会“先拆任务再逐个子 Agent 执行最后汇总”现在只有一个循环模型看当前对话状态决定调用哪个工具拿到工具结果后继续思考直到不需要工具为止。我把子 Agent 取消了所有工具放在同一个平面列表里不再做层级分配。顺手做的一件事是把每个工具的超时都从组件层移到了工具实现内部。以前子 Agent 卡住是调度层的问题现在工具返回慢就直接给模型一个超时错误让模型自己决定是重试还是换条路。3.2 第二步把规划器删了提示词里只留“判断”不要“计划”删完编排器我把规划器也从链路里拿掉了。这一步的实际操作主要是改系统提示词。删除前我给模型的那套提示词是典型的“规划型”写法你是一个任务规划器。你的职责是分析用户请求并生成步骤列表每一步必须只调用一个工具。执行完所有步骤后再总结结果返回给用户。看上去逻辑清晰实际上模型经常把计划写得很漂亮却不执行或者计划里的一二三四步到了执行阶段被另一个上下文重新解释导致跑偏。删除后我把它改成了纯判断型的写法你可以使用以下工具完成用户的请求。请根据用户的最新消息自主决定是否需要使用工具、使用哪个工具以及使用工具的顺序。不要执行用户没有要求的步骤。如果所有工具结果已经足够回答用户直接给出最终回复。区别在哪以前是要模型“把计划说出来”现在是要模型“直接做”。省掉了“计划文本”和“执行动作”之间的翻译损耗。模型不需要先产出一份计划给自己看再照着计划逐步执行它可以在每一步根据最新工具返回结果来动态决策。实际跑下来大多数请求的模型调用次数从原来的 4~6 次降到了 2~3 次响应速度显著提升。3.3 第三步记忆组件压成“摘要 最近消息”第三步动的是记忆层。我最开始的记忆层做得非常重有独立的向量库每次请求都要把用户历史消息打成向量做相似度检索取回一堆片段拼到系统提示词里。效果不能说没有但排查问题相当痛苦模型回答奇怪的时候你根本不知道是向量检索出来的历史片段干扰还是模型自己理解错了。我把这套东西砍掉以后记忆只剩两种形态。第一种是“简单摘要”。每次对话结束我会用一次模型调用把这段对话压缩成两三行摘要跟之前的摘要接在一起。新会话开始时系统提示词里只带这个摘要而不是一堆检索片段。第二种是“外部记忆”。如果用户说“我上次让你查的那家店再帮我看看”这种需要精确还原的历史信息我直接做成一个“查询历史上下文”的工具。模型需要精确历史时就调这个工具不需要就不调完全按需加载。这步改动之后提示词体积小了很多模型有更多注意力放在当前任务上上下文被无关历史片段干扰的情况基本消失。3.4 第四步把多智能体拆成“角色切换”最后处理的是多智能体层。说实话删这一层的时候我有点犹豫因为网上到处都在讲多智能体协作是未来。但实践下来我得承认在绝大多数单领域任务里多智能体的“协作感”远小于“互相拖累感”。我的做法不是彻底砍掉角色而是把“多个 Agent 实例”降级成“同一个 Agent 的多段思考”。具体来说就是在提示词里要求模型在回答前做三层自我检查先复述用户需求再列出将要调用的工具最后在拿到工具结果后检查是否真正回答了用户。相当于原来三个 Agent 干的事现在同一个模型分三次思考完成但完全不增加额外模型调用次数因为思考是生成流程的一部分。如果某些场景确实需要“两个声音”出现在回复里我会用角色描述来切换而不是起两个 Agent。例如需要一边写代码一边审查代码的场景我会让模型先以“工程师”角色完成代码再用“审查者”角色输出审查意见。这套做法在成本上几乎为零而且效果比两个 Agent 来回传话稳定得多。4. 为什么删掉架构之后效果反而变好了4.1 链路越短出错的概率越低这是我最深的体会。每次额外加入一次模型调用就相当于在系统里多引入了一次可能的理解偏差。以前那条长链路里任何一个中间环节对用户意图的把握打了一点折扣到最终输出时都会被放大。现在链路只有“用户请求 → 模型自主决策 → 工具调用 → 返回结果”可出错的位置少了排查问题也更容易。延迟的数据变化也挺直观。删除前一次带工具调用的请求平均要经历 4.6 次模型调用删除后降到了 1.8 次。单次调用延迟按 2 秒算用户体感从十秒级降到了三到四秒。这个提升不需要调参不需要换模型只是少让模型“多跑几趟”。4.2 模型的基础能力这几年已经长出来了不得不承认两年前那批模型可能确实需要架构去补能力的短板没有原生函数调用、上下文窗口太小、指令遵循不稳定。但现在的主流模型这几项能力已经相当扎实。我给你一个工具列表它可以直接在生成时输出结构化的工具调用指令我只要拿到这个结果去执行就行。上下文窗口也从几千 token 涨到了几十万以往为了“塞下更多历史”设计的记忆组件在很多场景下已经没有必要。这不是说模型能力无限而是说很多架构层当年解决的问题现在已经被模型层默认覆盖了。如果你还停留在“必须用框架把模型包起来才能做 Agent”的思路很可能是在为一个已经不存在的问题买单。4.3 成本与 token 消耗双降我大致统计过同一个业务脚本在改动前后的 token 消耗。删除前一次完整请求的平均总 token 在 8k 左右删除后降到了 4k 上下。原因很好理解去掉了编排器和规划器的独立调用少了每个子 Agent 都要重复一遍的历史上下文也少了多个 Agent 之间来回传话的中间 token。成本降幅接近一半而且效果更好。指标删除前删除后平均模型调用次数4.6 次1.8 次平均总 token 数约 8k约 4k用户平均等待时间10 秒级3 秒级问题定位难度高不知道哪一层出错低直接看调用链和工具结果4.4 可观测性反而回来了以前架构复杂的时候我最怕用户说“答案不对”。我得先查编排器有没有拆对任务再查规划器生成的步骤是否合理再查每个子 Agent 有没有拿到正确的上下文。排查链路长得要命。删完架构之后整条链路就是一组有序的消息序列外加一串工具调用记录。我随便打开一条日志就能看到模型看到了什么、调了哪个工具、工具返回了什么、模型最终回答了什么。任何一步出问题都能直接定位甚至可以回放整个对话重新验证。这种可观测性上的回报是我一开始完全没想到的“隐藏福利”。5. 删完架构之后踩过的坑以及什么时候必须保留架构5.1 工具结果把上下文撑爆这是删掉架构之后来得最快的坑。以前编排器会控制每个子 Agent 的输入范围工具结果会被收集到中间层不会一股脑全塞给模型。删了之后我把工具结果直接追加到 messages 里有一次一个查询工具返回了一个几万行的 JSON模型当场就“精神错乱”开始编造 JSON 里根本不存在的字段。解决思路有两个方向。一是给工具做返回裁剪只返回最关键的字段和统计值明细数据写到临时文件让模型读文件路径。二是给工具返回加摘要长文本返回前先做一次压缩把核心信息提取出来再交给模型。我现在把“工具结果精简”当成了工具设计的第一原则宁可让模型多调一次工具也不要给它塞一堆噪声。5.2 模型进入“死循环”一直调同一个工具没有编排器之后模型有时候会反复调用同一个失败的接口比如查一个根本不存在的数据模型拿到“查不到”还是要再查一次陷入循环。我加了三层防线第一层是最大迭代数限制一般设 5 到 8 次超过就强制返回当前结果并附上告警第二层是工具内部对可重试次数的判定同一个参数连续失败两次就返回“建议更换条件”第三层是在系统提示词里明确写了“如果工具返回错误不要无意义重试尝试调整参数或告知用户无法完成”。这三层都是三五行的判断逻辑但效果立竿见影。5.3 并发请求时上下文互相污染以前有编排器统一调度我会下意识觉得上下文是调度层在管。删完之后有一段时间我用了一个全局的 messages 变量结果两个用户同时发起请求的时候A 用户的工具结果混到了 B 用户的消息流里回答内容张冠李戴。解决办法很朴素每一个请求都创建独立的上下文实例运行期间不共享任何可变状态。我把整个 Agent 运行过程做成了一个纯函数输入用户消息和系统提示词输出最终回复内部所有中间状态都放在局部变量里。这样并发再多也不串味。5.4 工具失败后没有“重试引导”架构删掉之前子 Agent 执行失败之后编排器会再派一个子 Agent 去“检查”或者“重试”。删了之后没人管这个了模型拿到错误信息经常直接放弃或者给出“系统错误请稍后再试”这种垃圾回复。我的解法是在工具返回结构里增加一个“错误状态”字段并把具体的失败原因放进去。模型看到错误信息后可以自己决定是调整参数再试一次还是换一个工具或者直接说明干不了。关键是错误信息要写得足够清楚不要只说“调用失败”要说“调用失败参数 start_date 格式不正确应为 YYYY-MM-DD”。模型有能力根据这些信息进行下一步判断前提是你把判断依据给它。5.5 什么场景下架构真的不能删说了这么多“删”的好处我也得把话说全。有些场景下架构不但不能删还得做得更严格。如果业务流程有强外部约束比如订票、支付、审批这类需要严格状态流转的环节你不可能让模型自由发挥随意调工具必须有状态机来确保每步动作合法。如果涉及人工审批也就是 Human-in-the-loop那必须有一套事件机制把待确认任务挂起等人确认后再继续这不是提示词能解决的。再比如审计要求每步操作都要有操作人和操作理由多智能体场景下也需要保留一个协调层来沉淀日志。还有多个 Agent 需要并发访问同一个共享内存的场景没有架构层的锁和事务数据一定会乱。在这些场景里我建议保留一个“最小架构骨架”也就是只保留状态流转和必要的校验把能交给模型判断的部分尽量放开。架构是用来兜底的不是用来替模型思考的这个界限要拎清楚。结尾我这一年比较大的体会就是做 Agent 很容易先亢奋后焦虑。亢奋是因为什么都能做焦虑是因为架构越叠越多效果和成本反而一路失控。把架构删掉之后我才慢慢明白所谓的“架构”在 Agent 场景里更合理的角色是兜底网而不是指挥台。模型能自己做的判断就别替它做工具返回能给足信息就别让模型去猜。最后再分享一个我最近养成的习惯每往系统里加一个组件之前先问自己一句“这一层如果删掉模型自己能不能搞定”大多数时候答案都是“能”。那就先别加跑一段看看真实差距再说。这种思路不一定适用于所有项目但至少对我来说它确实让 Agent 系统跑得更轻、更稳也更容易调试了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑