☰
删掉复杂架构后,Agent效果反而更好:一次实战简化复盘
2026/10/20 21:37:45 网站建设 项目流程

去年一整年我都在跟 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, tools=tools) # 模型自己决定要不要调用工具 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 系统跑得更轻、更稳,也更容易调试了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询