☰
Agent设计模式实战:从被动代码到主动智能体的架构重构
2026/10/19 2:30:02 网站建设 项目流程

1. 从被动代码到主动智能体:这次范式迁移到底在迁移什么

先说个我在团队内部经常提的观察:过去三年我们写的大多数所谓“AI应用”,本质上是把大模型当成一个云端函数来调用——输入文本,输出文本,外面套一层业务逻辑。程序说一句,模型答一句,主动权永远在开发者手里。这种模式没有错,但它没有触及一个更本质的问题:如果模型不仅能回答问题,还能自己决定下一步该调用什么工具、该追问什么信息、该在什么时机停止,应用会长成什么样?

这就是Agent设计模式想要回答的问题。它把软件从**“请求—响应”的被动执行者**,变成**“目标—行动—反馈”的主动智能体**。代码不再是按照预先写好的路径一步步走,而是为一个目标去规划路径、选择工具、处理意外、甚至修正自己的错误。这个转变不是换一个框架或者换一种提示词写法,而是对整个软件架构思维的一次重构。

这篇文章我不会去堆概念,也不打算复制某份框架文档。我想用这篇博客把Agent设计模式的核心逻辑拆开,讲讲我自己从被动代码迁移到主动智能体这个过程中踩过的坑、验证过的方案、以及沉淀下来的设计取舍。受众的话,我默认你至少写过一段时间的业务代码,对大模型API有基本概念。如果你正准备把一个原型从“聊天机器人”升级成“能自己干活的Agent”,这篇文章应该能帮你少走我走过的弯路。

先说清楚一个问题:为什么现在才提Agent设计模式?因为模型能力到了一个临界点。早期模型连带着格式输出都费劲,遑论让它自主决策。而现在,模型在“理解意图”“生成结构化动作序列”“从错误中恢复”这三件事上都足够可靠了,我们才可以把一部分控制权交给它。架构设计里的边界、兜底、可观测性,全部要跟着这个转移重做一遍。

2. Agent范式转变的第一性问题:控制权怎么交

2.1 传统代码的控制流,为什么在Agent里失效了

传统后端服务里,控制流是确定的。用户发起请求,路由到某个Handler,Handler调用Service,Service访问数据库,返回JSON。每个环节都有预定义的输入输出,异常情况也有对应的catch分支。这套模型的优点是可预测、易调试、好测试。缺点是它对“开放性”毫无办法——如果用户的需求超出了预设的流程分支,系统就只能回答“抱歉,我不理解”。

Agent的思路恰恰相反。它面对的不是确定的分支,而是一个开放式的目标空间。以“帮我安排下周的产品评审会议”为例,传统写法是你把“查日历—找会议室—发邀约—发通知”这四步写死。Agent的做法是把这个目标交给模型,让模型自己决定:是不是先查一下参会人的忙闲?会议室满了要不要换时间?要不要先确认评审材料齐没齐?

这个转变最关键的设计点是:**控制权从代码转移给了模型,但转移多少、边界在哪、失败怎么收场,是架构师的责任。**很多初做Agent的团队在这里走偏——把控制权全部交给模型,结果就是模型天马行空地调用工具,或者在一个错误分支里反复循环。好的Agent设计不是放任模型自由发挥,而是给模型一个“有护栏的自由”——让它在规则边界内做决策,而不是彻底甩开缰绳。

2.2 一个生活化类比:从接线员到项目主管

理解这个转变,我用一个类比。传统程序像一个接线员:每一个来电都对应一条预定义的话术路径,说“查余额”就转余额系统,说“挂失”就转挂失流程。它高效、确定,但永远只能回答预设问题。

Agent则像一个项目主管。你给他一个目标“把这个项目交付了”,他需要自己去分解任务、协调资源、处理突发情况。项目主管的能力体现在哪里?不是体现在他能记住多少规章制度,而是体现在面对不确定情况时做出合理的判断。这正好是模型的强项。但这个主管也可能出错——他可能过度承诺,可能漏掉重要干系人,可能在一个并不重要的细节上死磕。所以要给他配一套管理机制:周报(日志)、审批流(人工介入)、KPI(评估指标)。

这个类比能帮你把Agent架构里的组件对号入座:模型是那个主管的“大脑”,工具集是他的“手”,记忆系统是他的“工作笔记”,而Planner、Reflection这些机制,就是他的“管理方法论”。

3. Agent设计模式的核心架构与关键模块拆解

3.1 记忆机制:短期工作记忆与长期事实库

Agent的记忆设计是区别于传统应用最明显的地方。传统程序里,“状态”存在数据库里,读写都是确定性的SQL。Agent的“记忆”则要服务于一个模糊得多的目标:让模型在多个决策步骤之间保持上下文一致,同时能从过往经验中提取可复用的信息。

记忆至少分两层。短期记忆指的是当前任务上下文——Agent正在处理什么目标、已经执行了哪些步骤、收集到了哪些信息。这一层直接塞进模型的context window里,但要管理Token预算。我见过太多Agent死在“上下文爆炸”上:每一步都把全部历史丢给模型,结果到了第十步,模型已经开始忘记第一步的目标了。一种有效做法是结构化压缩——每执行完一步,就把原始结果提炼成几十个Token的摘要,只把摘要留给下一步使用。

长期记忆更接近传统数据库的职责,但它存的不只是结构化记录,还有一个“语义索引”。以“用户偏好”为例,传统数据库用字段存“喜欢简约风格”,Agent则可以把“用户上次说‘这个页面太花哨了’”这一事件连同当时的上下文一起存进去,下次做设计决策时通过向量检索召回。这里有一个我在实践中验证过的点:长期记忆的价值不在于存得多,而在于检索准确。存了一万条偏好但检索不到需要的那条,等于没存。所以写入策略和检索策略需要一起设计,而不是只用embedding就完事。

3.2 规划与任务拆解:把目标翻译成可执行步骤

规划模块承担的是“把大目标拆成子任务”的职责。最简单的方式是让模型直接生成一个待办列表(Todo List),然后逐个执行。更复杂的方式是引入分层规划——高层模型只决定“先做什么、后做什么”,低层模型负责具体步骤的执行。后者更可靠,但延迟更高。

我在实际项目中常用的策略是**“保守规划 + 动态调整”**。所谓保守规划,是在任务开始时让模型基于当前已知信息生成一个初步计划,但明确告知模型:这个计划随时可能因为执行中的新信息而调整。关键设计在于:Agent在执行每一步之后,都要做一次“是否需要修改计划”的判断。这个判断本身也是一个模型调用。

这里有个踩过坑的教训:不要试图让模型一次规划得太长远。模型在长程规划上的能力是衰减的,让它规划4-5步以内的行动表现很好,规划20步以上就开始胡言乱语。我的做法是在规划时只定大方向,细微的路径调整交给执行过程中的动态决策,这比要求“完美规划后再动手”要可靠得多。

3.3 工具调用与外部环境交互:Agent的“手”和“眼睛”

工具是Agent从“纯脑力劳动者”变成“能动手的智能体”的关键。没有工具,Agent只能输出文本;有了工具,它才能查数据库、调API、发消息、操作浏览器。工具设计直接决定了Agent能力的上限。

工具注册表的设计要点第一条:**工具的语义描述比实现更重要。**模型是通过函数的名称和描述来决定何时调用它的。一个含糊的描述(比如“处理数据”)会让模型在错误的时候调用它;一个精确的描述(比如“当用户要求统计销售额时,调用此函数并按日期范围参数传入”)则让调用准确率高一个量级。一个好的实践是在描述里写清楚“什么时候用”和“什么时候不要用”,这能有效减少误调用。

第二个要点是工具的参数设计。模型的JSON生成能力虽然不错,但复杂嵌套的参数结构仍然容易出错。建议所有工具的参数都做成扁平结构,能用一个字符串就别拆成三个字段。参数校验的逻辑放在工具内部,不要让模型去保证参数一定合法。如果参数有问题,工具返回一个结构化的错误信息,模型看到后自己能纠正。

3.4 反思与自我纠错:让Agent有“复盘”能力

Agent比传统程序更接近人的地方在于,它可以对自己的行为进行反思。这个概念在学术圈叫Self-Reflection,实操中体现为:当一个步骤执行失败,或者结果不符合预期时,Agent能够分析失败原因并调整策略,而不是简单地重试。

实现反思的一种朴素做法是:给Agent一个“Critic”角色。执行一个任务时,先让Actor模型负责生成操作,再让Critic模型审查这个操作是否合理。我曾经搭建过一个简单的双模型Reflection流程,Actor执行完一步后,Critic会对结果打分并给出修改建议,Actor根据建议调整后再提交。这个流程让我的Agent在复杂任务上的成功率提升了将近30%,代价是每次循环的Token消耗翻了一倍。

但反思也有副作用。最常见的副作用是“过度反思”——Agent陷入自我怀疑的循环,反复修改同一个步骤,始终不敢往前走。我的经验是给反思次数设上限,比如最多3轮,超过上限就带着当前结果继续往下走。追求“完美无错”的Agent是不现实的,做产品要接受一定比例的容错,把重点放在“错了之后能不能恢复”上。

4. 实操手记:从零搭建一个可落地的轻量Agent

4.1 第一步:选型——框架与模型各司其职

市面上Agent框架已经不少,名字我不逐一列举,各有侧重,有的偏向自动化流程编排,有的偏向对话管理,有的偏向工具调用。我的建议是:**初期不要依赖重型框架,先用最小实现跑通核心循环。**原因很简单——框架帮你省掉的复杂度,最后都会在调试时加倍还回来。你自己写的循环虽然简陋,但每一步发生了什么你都一清二楚,排查问题时的效率高得多。

模型选型上,我推荐使用支持**结构化输出/函数调用(Function Calling)**的模型,这是Agent开发的基础设施级能力。它能保证模型输出一个符合schema的JSON,用于指名要调用哪个工具、传什么参数。没有结构化输出,你要自己解析自然语言文本里的工具调用意图,又要写一堆脆弱代码,而且准确率还上不去。

我的基础技术栈是:Python + 大模型API(支持Function Calling)+ FastAPI(写工具服务)+ Redis(存短期状态)。这套组合足够跑通一个具备“规划—调用—反馈—调整”闭环的Agent原型。

4.2 核心循环实现:规划、行动、观察三件套

Agent的核心循环可以抽象成三个动作:规划(Plan)—行动(Action)—观察(Observation),这是经典的ReAct模式。在代码层面,这个循环长这样:

def run_agent_loop(user_goal, tools, max_steps=10): messages = [{"role": "user", "content": user_goal}] for step in range(max_steps): # 1. 让模型决定下一步动作 response = model.call( messages=messages, tools=tools.get_schemas() # 传入工具定义 ) # 2. 判断模型是想调用工具还是已经给出最终答案 if response.has_tool_calls(): for call in response.tool_calls: result = tools.execute(call.name, call.arguments) # 3. 把工具执行结果作为Observation追加到消息流 messages.append({ "role": "tool", "tool_call_id": call.id, "content": result }) # 让模型基于观察结果继续推理 messages.append({"role": "assistant", "content": response.content}) else: # 模型认为任务已完成,返回最终回答 return response.content return "达到最大步数,任务未完成"

这段代码虽然只有几十行,但它是所有Agent模式的底层骨架。看起来不起眼,但如果你把这里的每次模型调用都打上日志,配上Token计数和耗时统计,再扔到生产环境观察几天,你会发现绝大多数问题都能在这个循环里找到根源——不是循环本身有Bug,而是循环里跑的决策质量不高。

有一个细节值得说明:为什么要把工具调用结果用role: "tool"的方式回传给模型?因为这是模型了解“外部世界发生了什么”的唯一途径。如果你的工具执行之后不把这个结果以结构化方式传回,模型就只能靠猜。这一步做得不好,后面的所有推理都失去基础。

4.3 增强决策质量:状态追踪与记忆压缩

裸的ReAct循环能跑,但在稍微复杂一点的任务上会很拉胯。原因在于:模型只能在对话历史里看到“发生了什么”,但对话历史会越来越长,而且没有“当前状态”的显式概念。加入状态追踪层能让Agent的决策质量上一个台阶。

我实现了一个简单的状态对象,本质是一个字典,用来维护关键任务信息。在每步循环结束前,用一个轻量模型调用把当前状态更新一下,提取重点信息。然后在下一步的Prompt里,把状态信息作为“当前任务进展”注入。这个设计叫State Manager,它解决了两个问题:一是Token压缩,把冗长对话提炼成精炼状态;二是决策聚焦,让模型把注意力放在当前最关键的信息上,而不是被早期无关细节干扰。

记忆压缩的具体做法是:设定一个summary变量,每三步循环更新一次。更新时不是简单地截断,而是让模型阅读“旧摘要+最近三步的对话”,输出一份新的摘要。这样设计的好处是,模型可以决定什么值得保留、什么可以丢弃,而不是靠人来设计压缩规则。

# 每3步执行一次记忆压缩 if step % 3 == 2: summary = model.compress( old_summary=summary, recent_history=messages[-6:], goal=user_goal ) # 压缩后,把早期细节消息丢弃,只保留摘要 messages = [{"role": "system", "content": build_system_prompt(summary)}] + messages[-2:]

注意这里有个坑:不要把原始消息全删了,因为某些具体数值(比如时间、金额、ID)可能在后续步骤还有用。压缩时,我会同时让模型输出一个key_facts列表,单独存这些关键事实,和summary一起注入下一步的Prompt。

4.4 人工介入与兜底策略:不能把项目成败全押在模型身上

作为一个在传统软件工程浸淫过多年的人,我始终坚信一个原则:**任何系统都要有确定性兜底。**Agent系统也不例外。模型可能选错工具、可能误解指令、可能陷入死循环,你的系统必须有能力把这些异常兜住。

我在生产环境里设置了三道兜底防线。第一道是超时与步数限制,任何任务最多执行15步,超过直接终止并通知管理员。第二道是工具调用的权限控制——每个工具都标明了调用门槛,比如“发送消息给外部联系人”这个工具,只有经过审批模式才允许自动执行。低风险工具可以放开,有副作用的一律人工确认。第三道是结果校验器——Agent在交付最终结果前,Code Interpreter式地执行一步自校验,检查输出是否符合用户目标的约束条件,不符合就回到循环里继续修正。

这些兜底策略消耗的不只是开发时间,还有大量的产品讨论。因为每加一道限制,Agent的自主性就弱一分,体验就笨一分。找到“自主”和“可控”的平衡点,是Agent产品设计里最难也最核心的命题。我的建议是分级控制:MVP阶段宁可多限制一点,等数据证明某些环节的可靠度足够高,再逐步放开。这比一开始就追求“全自动”明智得多。

我用一套简单的“信任分”机制来推进这个过程:每个工具都有一个初始信任等级,执行成功率和校验通过率会被记录下来,连续成功100次之后,该工具的信任等级自动提升一级,相应减少人工介入频率。这本质上是把“确定性的质量门禁”和“模型驱动的灵活性”结合起来,既不牺牲可靠性,又不让Agent变成“提线木偶”。

5. 避坑指南:六个月生产环境中总结的排查方法

5.1 模型“幻觉式”工具调用怎么管

生产环境中遇到频率最高的问题是:模型调用了不存在的工具,或者给工具传了语义正确但实际无效的参数。比如用户说“查一下上个月的订单”,商品服务里明明有order_query工具,模型却调用了invoice_query——因为它认为“查订单”和“查发票”足够相似。这类问题叫“幻觉式工具调用”,是最让人头疼的Agent故障之一。

我的排查思路分三步。第一步是检查工具描述,看看工具的description字段是否清晰表达了“什么时候不该用它”。第二步是检查示例数量——在工具的Few-shot示例里挤出两三个典型的错误调用场景,明确标注“这种情况下不要调用本工具”。第三步是在工具执行前加一道“意图校验”,用一个轻量模型判断用户的原始诉求和工具功能是否匹配,不匹配则回退到澄清问题。

实际效果来看,第三步的收益最大。虽然多了一次模型调用,但它能把工具调用的准确率从80%拉到接近95%。付出的成本是延迟增加了几百毫秒,对大多数场景可以接受。

5.2 上下文窗口溢出与Token预算管理

Agent在长对话或多轮任务里经常遇到上下文窗口溢出。大模型API通常有4k、32k、128k等不同档位的上下文限制,但很多人忽略了一个事实:上下文窗口越大,模型在长距离依赖上的注意力越容易丢失,而且延迟和成本都显著上升。所以“把窗口扩大”并不能根治问题,“让上下文更精炼”才是正解。

我实践下来最有效的一套Token管理策略是分层处理:原始信息不直接进模型上下文,而是先经过一次“信息抽取”,提取出对当前任务有用的结构化关键点,只把这些关键点放进上下文。对话记录超过一定长度后,早期内容全部折叠成摘要。附带检索来的资料也分两种情况处理:精确匹配的用全文,模糊相关的只放摘要。这套策略能让同样预算下Agent的有效任务深度提升大约三倍。

还有一个容易被忽视的细节:**系统提示词本身也是上下文的一部分。**很多人喜欢把几十条规则一次性全塞进系统提示词,结果模型抓不住重点。我的做法是系统提示词只放最核心的约束(不超过五条),其余规则写在一个“规则文档”里,让Agent自己判断需不需要翻阅。这符合人类工作的方式——你不应该要求一个新人入职第一天就背下全部规章,而应该教他“哪些情况该去翻哪本手册”。

5.3 死循环、低置信度与任务失败的补偿策略

Agent最容易在生产环境翻车的三类问题是:死循环、低置信度、任务失败。死循环的表现是Agent反复执行同一个无效工具调用,每次都是同样的输入输出,看起来忙得不得了,实际毫无进展。我的对策是加入**“重复检测”机制**——记录最近三次工具调用的签名(工具名+参数哈希),如果完全一致且结果相同,则判定为死循环,强制中断并往Prompt里注入提示:“你刚刚做过相同操作且未能推进任务,请换一种策略。”

低置信度问题出现在模型犹豫不决的时候——它选了一个工具,又撤回来,再选一个,再撤回来。这种情况通常是工具集合太大、边界模糊造成的。解决办法是分层路由:先把工具按领域分组成几个大类,让模型先选大类,再在大类内选具体工具。这样把“从30个工具里选一个”的难题,拆成“从5个领域选一个”和“从6个工具里选一个”两个容易得多的决策。

任务失败后的补偿策略要提前设计。我的做法是定义“补偿动作表”:当任务因某种原因失败时,根据失败类型决定补偿动作。是重新规划、换工具、请求人工澄清,还是直接终止并生成失败报告,都有明确的规则。最重要的原则是:Agent可以失败,但不能“假装成功”。一个敢于承认“我做不到”并给出原因和建议的Agent,比一个硬着头皮给错误答案的Agent可靠得多,也更容易赢得用户的信任。

6. 设计模式全景:四种主流Agent模式的适用场景与选型建议

在跑通了基础循环之后,你大概率会遇到两类问题:一类是这个模式太简单,复杂任务跑不动;另一类是想要更强大的能力,但不知道用什么模式来组织。这里我给你梳理四种主流的Agent设计模式,都是我实际验证过或者拆过源码研究的,它们不冲突,可以在一个系统里混用。

ReAct模式是最基础的推理行动循环,适合任务边界清晰、步骤数量可控的场景。比如“查天气并提醒我带伞”“整理这个目录下的文件”,它不需要长程规划,就是顺着意图走几步。优势是简单透明,成本可控;劣势是面对模糊复杂任务时很容易在一两步内就耗尽能力。

Plan-and-Execute模式在ReAct之上增加了一个独立的规划器,先规划再执行,适合“多步骤、跨系统、有依赖关系”的任务。比如“调研竞品、整理报告、发送邮件”,这种任务需要先有全局计划,然后逐步执行。规划器负责把大任务分解成子任务清单,执行器负责每个子任务的具体动作。我用这个模式做过能自动整理周报的Agent,效果比裸ReAct稳定得多。

Multi-Agent模式是多个Agent角色协作,各司其职。比如一个“研究型Agent”负责搜索资料,一个“写作型Agent”负责撰写内容,一个“批评型Agent”负责审查质量。这种模式特别适合内容创作、方案设计这类需要“多视角交锋”的任务。它的好处是每个Agent的职责单一,系统提示词可以写得很聚焦,推理质量更高。坏处是成本成倍上升,调试复杂度也高。我的建议是:只有当任务确实需要多视角时再用,不要为了炫技而拆。

**反思工作流模式(Reflection)**给Agent增加了一个自我审视的环节。Actor输出的结果,Critic会审视“是否满足需求、是否有遗漏、能否更好”,然后带着反馈意见重新生成。这种模式适合“质量要求高、可迭代”的任务,比如写代码、写文案、做方案。我前面提到过的双模型Critic设计就是这种模式的典型实现。

选型建议的决策逻辑是这样:先看任务的开放度——是封闭目标还是开放目标?再看复杂度——是一步到位还是多步依赖?然后看质量要求——错了能不能接受?最后看成本预算——每次任务能烧多少Token?根据这四个维度交叉判断,基本能确定该用单一模式还是组合模式。记住,设计模式是工具,不是目的。能用简单模式解决的问题,不要硬上复杂的。

7. 关于未来方向的一些经验之谈

从被动代码到主动智能体的范式迁移,本质上是一次控制权交接。代码把“做什么、怎么做、什么时候做”的控制权,逐步让渡给模型。但这个让渡是有前提的——你需要用架构设计来保证系统在模型“不聪明”的时候仍然不崩溃。记忆机制、工具注册、反思循环、兜底策略、状态管理,这些不是花哨的概念堆砌,而是这次范式迁移的承重墙。

我个人的体会是:Agent设计模式现阶段最重要的不是“造一个全能的通用智能体”,而是在限定领域内把可靠性做到可用级别。别想着一步到位。给自己一个6到12个月的时间窗口,先把一个狭窄场景跑通,积累足够多的生产数据,再逐步放宽边界。这比一开始就瞄准“全自动”“零人工”要现实得多,也更能沉淀出属于你自己团队的最佳实践。

最后分享一个小技巧,也是我在多个项目里反复验证有效的经验:**一定要给Agent系统做完整的可观测性设计。**每一步的模型调用、工具执行、状态变更,全部记录成结构化日志。因为Agent的行为是非确定性的,出了问题你没法像传统系统那样靠断点复现。只有完整的Trace记录,才能让你在事后复盘时看清楚Agent在每一个节点上是怎么决策的。这个投入看上去不起眼,但在Agent进入生产之后的排障效率上,价值怎么强调都不为过。

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

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

立即咨询