ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

AI Agent架构核心原理:从ReAct循环到多Agent协作与最小落地

AI Agent架构核心原理:从ReAct循环到多Agent协作与最小落地

很多朋友学AI Agent最困惑的一个点是:网上教程一抓一大把,但讲"为什么这样设计"的少。我也是从把Agent当成"加强版聊天机器人"开始踩坑过来的,直到把架构层的逻辑理顺,才真正把Agent从demo推到能稳定跑业务的形态。这一篇就专门聊核心原理和架构,属于整个教程系列的地基部分。看完你会搞清楚三件事:Agent内部到底在循环什么、主流架构有哪些取舍、以及你自己动手搭一套最小架构时该把代码写在哪些位置。

1. 先把Agent拆开看:它和普通LLM到底差在哪

1.1 从"问一句答一句"到"目标驱动的任务闭环"

先说个最直观的区别。你直接用ChatGPT或者调用一个纯LLM接口,本质上是"请求-响应"模式:用户问一句,模型答一句,对话结束。这个模式在处理单点问题、文本生成、知识问答时非常够用,因为需求本身是明确的——"写一封请假邮件"就是写一封,不需要拆解。

但Agent面对的需求往往是模糊的、多步骤的。比如"帮我调研一下市场上主流的向量数据库,输出一份对比报告",这句话里其实藏了至少五个子任务:确定调研范围、收集各家产品的关键参数、做性能对比、分析适用场景、整理成结构化文档。纯粹靠一次LLM调用完成不了,因为模型没有执行能力,它只能"描述"怎么做,而不能"真正去做"。

Agent的核心价值就在这里:它把LLM从"一个会说话的API"变成了"一个能自己做事的系统"。这个系统内部有一个目标驱动的闭环,大致是:

  • 接收一个目标(用户给的,或者上游系统传过来的)
  • 把目标拆解成可执行的步骤
  • 执行每一步,必要时调用外部工具(搜索引擎、数据库、代码解释器等)
  • 观察执行结果,判断是否达成目标
  • 没达成继续循环,达成了输出最终结果

你把这个循环转起来之后,LLM才真正成为你业务中的一个"数字员工",而不是一个"问答窗口"。

1.2 核心四要素:感知、决策、行动、记忆

理解Agent架构之前,必须先记住四个核心要素,所有Agent系统不管多复杂,拆到底都是这四样东西的变体:

感知(Perception):Agent获取外界信息的通道。包括用户输入、系统事件、工具返回的结果、其他Agent发来的消息。感知层的设计决定了Agent能看到什么,也就决定了它能在多大范围内做决策。很多Agent"表现笨",不是模型不行,而是感知层给的上下文太少。

决策(Decision):Agent根据当前状态和目标决定下一步做什么。这一层是LLM的主场——说人话就是,模型在"下一步行动"这个选择题上给出答案。决策不是一次性的,而是嵌在循环里、每执行一步都要做一次。

行动(Action):Agent实际执行决策的能力。LTM(Long-Term Memory,长期记忆)负责跨会话存储知识和经验;STM(Short-Term Memory,短期记忆)就是当前任务里的上下文工作区。记忆层是Agent区别于"每次对话都失忆"的普通LLM调用的关键。

这四要素不是孤立存在的,架构就是把这些要素组织起来、让它们高效配合的那张设计图。

2. Agent核心原理深度解读

2.1 LLM不是大脑,它只是一台"推理引擎"

很多人把LLM称作Agent的"大脑",这个说法有误导性。按我的理解,大脑这个说法暗示LLM应该什么都干,但实际上LLM是一个非常纯粹的推理引擎——你不给它上下文,它什么都干不了;你给它的上下文质量差,它给你的决策质量就烂。

在Agent架构里,LLM承担的是"根据当前状态+历史记忆+目标,推断出最合理的下一步"这件事。它只负责决定"做什么",不负责"记住所有事情"(那是记忆模块的活),也不负责"真正把事情做完"(那是工具和代码的活)。

这个认知很重要,因为它直接决定了你怎么设计Agent。如果你觉得Agent能力弱,先去查你的检索和上下文组装质量,别急着换更大的模型。我在实际项目中见过的案例,很多"Agent变聪明了"的反馈,其实是把Prompt优化和记忆管理做好了,模型根本没换。

2.2 ReAct模式:把思考和行动绑在一起

ReAct(Reasoning and Acting)是目前Agent领域最普及的原理框架,几乎所有主流的Agent框架(LangChain、AutoGPT、BabyAGI这一系)底层都能看到它的影子。它的核心思路极其朴素:让模型在"推理"和"行动"之间交替前进,每走一步都留下思考轨迹。

传统的LLM调用流程是:

用户提问 -> 模型生成回答 -> 结束

ReAct的流程是:

用户提问 -> 模型思考(我现在需要什么信息?) -> 模型决定行动(搜索X) -> 观察结果 -> 模型再思考(还缺什么?) -> 再行动 -> ... -> 模型判断目标达成 -> 生成最终回答

这个"思考-行动-观察"的循环就是Agent的核心运转方式。ReAct最巧妙的一点是,它把思考过程也作为Prompt的一部分传给模型,让模型在"已经想到哪一步"的基础上继续推理,而不是每次从零开始。这既解决了多步骤任务的连贯性问题,也给开发者留出了调试空间——你随时打开日志,就能看到Agent现在是"卡在推理"还是"卡在执行"。

2.3 Planner-Executor拆分:复杂任务不再"一步走到黑"

ReAct适合中等复杂度的任务,但任务再复杂一点,比如"帮我把季度销售数据做清洗、分析、出图表、生成PPT大纲",如果还靠ReAct一步一循环,Agent很容易在前面几步就迷失方向,因为它没有一个全局规划。

这时候就有了Planner-Executor架构。核心思想是:把"规划"和"执行"拆成两个角色。

Planner的角色是:拿到目标,先做全局拆解,生成一份任务清单(比如:1. 读取数据-> 2. 清洗数据-> 3. 统计分析-> 4. 生成图表-> 5. 组装PPT大纲)。这份清单不一定是最终执行顺序,但它给了Agent一张地图。Executor的角色是:按照清单逐项执行,每执行完一项就标记完成,遇到阻塞就返回状态。

这个架构的好处特别明显:

  • 规划变得可审核,你能看到Agent打算怎么做,而不是黑盒
  • 执行可中断,某个子任务失败不影响全局
  • 可并行,清单里的独立任务可以交给不同的执行器并行跑

实际工程中,Planner和Executor可以都是LLM驱动的,也可以Planner用LLM、Executor用固定代码逻辑。比如那些流程极其固定的场景(取数、清洗、出报表),Executor用代码写死反而更稳定、更省钱。

2.4 工具调用与Function Calling机制:Agent的"手"从哪来

Agent和普通聊天机器人的另一个分水岭是"能不能调用工具"。没有工具的Agent只能动嘴,有了工具的Agent才能动手。而工具调用在技术实现上,主流就是Function Calling机制。

这个过程可以简化为三步:

  • 第一步,你把工具的函数签名(函数名、参数、功能描述)以结构化JSON格式传给LLM
  • 第二步,LLM根据用户的请求,在它的"知识"里判断该不该调用某个工具、参数填什么,然后返回一个结构化的调用指令(比如:调用search_web,参数是{"query": "2025年向量数据库排名"})
  • 第三步,你的代码解析这个指令、实际执行对应函数、把结果回传给LLM,LLM再基于结果继续推理

关键点在这个循环上:工具不是LLM自己跑的,是你的代码跑的。LLM只负责"决定调用",实际执行权永远在你手里。这个设计给了开发者一个非常强的控制点——你可以审查工具调用的合理性、加权限校验、做成本控制、甚至在某些场景下直接拦截掉不安全的操作。

我在实操中强烈建议:每个工具的语义描述要写清楚,包括什么时候用、什么时候别用、参数格式什么样。这个细节直接影响LLM的调用准确率,比你在Prompt里反复强调"请谨慎使用工具"有效得多。

3. 主流架构盘点:单Agent、多Agent、LLM+API、微服务

3.1 单Agent架构:简单直接,入门首选

单Agent架构就是:一个Agent实例独立处理目标任务,内部包含完整的感知、决策、行动、记忆闭环。这种架构是入门阶段的首选,也是理解Agent原理的最佳载体。

单Agent架构最适合三类场景:

  • 任务边界清晰,比如"自动回复邮件""定时抓取网页生成摘要"
  • 工具调用链比较固定,Agent基本按既定路径走
  • 你需要快速验证一个Agent idea是否可行

单Agent的局限性在复杂场景里也很明显:一是上下文容易膨胀,Agent在长流程里不断把中间结果塞进上下文,很快就会撞到窗口上限;二是职责耦合,一个Agent既要规划又要执行还要管记忆,代码逻辑复杂后维护成本直线上升;三是单点故障,Agent任何一个环节卡死,整个任务就废了。

3.2 多Agent协作架构:角色分工与通信

多Agent架构是现在行业里最火热的方向之一,核心思路是:不再造一个"全能的超人Agent",而是让多个"专精的Agent"各管一摊,再通过某种协作机制把任务搞定。

最常见的实现是"主控-执行"模式(Orchestrator-Workers)。主控Agent负责理解用户意图、拆分任务、分发给各个执行Agent,然后收集结果、汇总输出。每个执行Agent只负责自己的领域:一个专门写代码、一个专门读文档、一个专门做搜索。

多Agent架构的核心问题不是"怎么造Agent",而是"Agent之间怎么通信"。我见过三种通行方式:

  • 直接调用:主控Agent通过函数调用的方式直接调用子Agent,最简单,但耦合度高
  • 消息队列:Agent之间通过队列传递消息,解耦好,适合异步场景,但延迟高
  • 共享黑板(Blackboard):所有Agent往一个共享空间里写中间结果、读需要的信息,适合复杂任务的协作,但需要设计好数据结构

多Agent架构还有一个隐藏成本:Token消耗。每多一个Agent就多一个LLM实例在推理,Token费用几乎是线性增长。我见过一个四Agent的项目,一次完整任务的Token消耗是同任务单Agent做法的3到4倍。性能和智能化之间的性价比,必须在架构选型时算清楚。

3.3 LLM+API轻量架构:不改造现有业务也能接入Agent

很多团队一开始容易掉进一个误区:上Agent就要上重框架,非要搞复杂的编排系统。其实有一种被严重低估的架构:LLM+API轻量架构,或者叫"内嵌式Agent"。

这在实操里大概长这样:你原本有一个业务系统(比如工单系统),系统里已经有现成的API——创建工单、查询用户信息、更新状态。你把这些API封装成Agent可调用的工具,然后在业务系统里嵌入一个LLM决策模块,让它根据对话内容决定调哪个API、参数是什么。这就是一个"限定域Agent"。

轻量架构最大的优点,一是风险低,Agent只在你的业务闭环里活动,不会乱跑;二是见效快,可能两周就能出一个可演示的版本;三是成本可控,因为领域窄、Prompt短,Token消耗不大。我见过不少传统软件团队就是从这种架构开始接触Agent的,他们反馈说这句话最贴切:"不是我们上了Agent,而是我们把Agent嵌进了业务里。"

3.4 微服务与分布式Agent架构:规模化之后的必经之路

当Agent的应用范围从"一个任务"扩张到"一个平台"时,上面这些单体式架构就hold不住了。这时候就需要考虑微服务化和分布式。

微服务架构的思路是:把Agent系统按职责切成独立的服务,每个服务可以独立开发、独立部署、独立扩容。比如我把"记忆服务"单独拎出来,做成一个可以供多个Agent共享的向量检索服务;我把"工具执行器"拆成独立服务,在并发大的时候单独做弹性伸缩。

分布式Agent架构更进一步,它要解决的是三个问题:

  • 多实例水平扩展,让同一个Agent跑多个副本,实现高并发
  • 状态同步,多个Agent副本之间共享记忆和状态,避免各说各话
  • 容错与恢复,某个节点挂了,任务能被其他节点接管

这个层面的架构,需要引入消息队列(比如Kafka、RabbitMQ)、分布式存储(Redis、向量数据库集群)、任务调度框架(Celery、Temporal)这些基础设施。技术栈会复杂不少,但它支撑的是完全不同的量级——如果说单体Agent能处理每天几百个任务,分布式的目标是每天几百万个。

3.5 架构选型对照表

为了让你在选型的时候心里有数,我把几种架构放到一个表里做一个直接对照:

架构类型适用阶段典型场景优点主要代价
单AgentMVP验证个人助手、单一自动化任务开发快、理解简单、易调试上下文易膨胀、职责耦合、无容错
多Agent协作功能扩展复杂项目、多角色协作职责清晰、可并行、扩展性好Token成本高、通信设计复杂
LLM+API轻量业务内嵌工单系统、CRM、客服辅助风险低、见效快、成本可控只适用于限定领域,通用性弱
微服务化平台化初期多Agent共享基础设施利于独立扩容、技术异构运维复杂、链路长
分布式规模化生产高并发、海量任务平台高可用、强扩展基础设施复杂、调试成本高

我的建议是:不要为了架构而架构。你的Agent业务还没验证就跑去做分布式,大概率浪费一两个月时间。大部分人的正确路径是:先用单Agent跑通核心价值,然后再根据瓶颈决定往哪个方向演进。

4. 实操:从零搭一个Agent最小架构

4.1 先定义骨架:状态、记忆、工具清单

纸上谈兵了这么多,下面进入实操环节。我会用伪代码和Python风格示例,带你搭一个最小的Agent骨架。这个骨架不依赖任何Agent框架,纯粹用LLM API就能工作,目的就是让你看清架构的运转逻辑。

第一步,定义Agent的"结构体"。一个Agent至少要包含三块:状态(当前任务进度)、记忆(历史上下文)、工具清单(可调用函数列表)。代码大致长这样:

class MinimalAgent: def __init__(self, llm, tools): self.llm = llm # LLM推理引擎 self.tools = tools # 工具注册表 {name: function} self.memory = [] # 短期记忆,存放(思考/行动/观察)历史 self.state = { "task": None, # 当前目标 "step": 0, # 已执行步数 "done": False, # 是否完成 }

这个结构看着简单,但它已经把四要素里的决策(llm)、行动(tools)、记忆(memory)、状态(state)都装进去了。

4.2 ReAct循环的代码级实现

Agent的核心运转就是那个ReAct循环。要把这个循环写成代码,其实就是一段简单的while逻辑:

def run(self, task): self.state["task"] = task self.memory.append({"role": "user", "content": task}) while not self.state["done"] and self.state["step"] < self.max_steps: # 1. 让LLM基于当前记忆和工具清单决定下一步 response = self.llm.chat( messages=self.memory, tools=self.tool_schemas, # 工具的函数签名描述 tool_choice="auto" ) # 2. 如果模型说要调用工具 if response.tool_calls: for call in response.tool_calls: # 执行工具 result = self.tools[call.function.name](**call.function.arguments) # 把结果写回记忆,供下一轮推理使用 self.memory.append({ "role": "tool", "content": str(result), "tool_call_id": call.id }) else: # 3. 如果模型没有要求工具,就认为任务完成,输出最终答案 self.state["done"] = True return response.content self.state["step"] += 1

这段代码你仔细看,就四件事:调LLM -> 有工具调用就执行 -> 把结果写回记忆 -> 循环。这就是整个Agent原理的核心了。很多商业框架里跑到"神乎其神"的编排,拆开看也无非是把这个循环加上各种增强(更好的记忆管理、更多的上下文处理器、更复杂的工具定义)。

4.3 给Agent"接上手":工具定义与权限校验

上面代码里给LLM传了一个tool_schemas,这个就是工具描述。以搜索工具为例,大致长这样:

{ "type": "function", "function": { "name": "web_search", "description": "在互联网上搜索给定关键词,返回相关网页标题和摘要", "parameters": { "type": "object", "properties": { "query": {"type": "string", "description": "搜索关键词,尽量精简"} }, "required": ["query"] } } }

这里的description非常关键。LLM是靠这段描述来理解"什么时候该用这个工具"的,描述写得太笼统,模型就会在不需要搜索的时候也去搜索。我自己的经验是,描述要回答三个问题:这个工具干什么、什么时候用、什么时候别用。

权限校验是另一个很容易被忽略的点。Agent在真实业务里能调用工具就意味着能产生真实影响,我在上一家公司就因为Agent自动执行了一个生产环境脚本差点出事故。从那之后,我给自己定了两条铁律:所有工具调用必须过一层校验逻辑(可以是白名单、敏感操作二次确认、或频率限制),所有高风险工具(删除、写入、支付类)默认禁止Agent直接执行。

4.4 可观测性:没有日志的Agent就是定时炸弹

这一点必须单独拿出来说,因为Agent和普通程序调试完全不同。普通程序报错会告诉你哪行代码出了问题,Agent出问题是模型"莫名其妙开始重复某个行为",或者"明明任务已经完成却还在继续调用工具"。没有日志,你根本无迹可寻。

我的做法是给每一步循环都打结构化日志,格式大致是:

[Agent][Step 2] 决策:调用工具 web_search [Agent][Step 2] 参数:{"query": "2025向量数据库"} [Agent][Step 3] 观察:返回7条结果,前3条相关 [Agent][Step 4] 决策:生成最终回答

此外还要记录Token消耗和耗时,这两个指标是评估Agent效率的关键。有些Agent任务做完只需要5步,有些绕了30步——多出来的25步就是纯浪费,通过记录每步日志能清楚地看到卡点在哪。

5. 常见问题与排查实录

5.1 Agent陷入"死循环"怎么办

这是被问得最多的一个问题,Agent反复做同样的操作、就是不结束。原因通常是三种:目标不够明确、工具返回结果不够好、最大步数限制设得太宽松甚至没设。

我在项目里的兜底方案有三个:

  • 给循环加max_steps硬限制,宁可任务完不成也不能无限空转
  • 在Prompt里加"避免重复执行相同操作"的约束,虽然效果有限,但聊胜于无
  • 检测重复模式,如果模型连续三次输出同样的思考内容和工具调用,直接主动打断并把历史摘要发给模型让它换路径

5.2 模型乱调工具,尤其是调了不该调的,怎么防

模型"不听话"是常态,它可能在你只让它查数据的时候顺手调了写操作的工具。这个问题的根治方法不是祈祷模型更聪明,而是从架构上隔离。

我的做法是把工具分成三等:只读工具(搜索、查询)、业务操作工具(创建工单、发消息)、高风险工具(删除、修改生产数据)。Agent默认只能调前两类。第二类如果要调,需要显式的用户确认;第三类直接在工具注册阶段就不给它看到。

在最新版的Function Calling机制里,你甚至可以对不同工具分别设置权限级别,这在多租户场景下尤其有用。

5.3 多Agent通信踩过坑:直接互相调用的坑

做多Agent协作项目时我走过弯路:一开始图省事,把所有Agent放在同一个进程里,直接在代码层面互相调用。等任务量上来后发现两个问题:一个Agent卡住,整个调用链都等它;另一个是消息历史在Agent之间来回传递,同一段信息重复存储,上下文膨胀得很厉害。

后来换成了消息队列方案,每个Agent独立部署、通过队列收发任务消息。虽然多了中间件,但整个系统的稳定性提升了不止一个档次。一个Agent挂了,其他Agent继续工作,主控Agent只需要在超时后重发任务或者走降级方案。教训就是:多Agent系统的通信架构设计,宁可一开始复杂一点,也不要图省事。

5.4 架构演进节奏:什么时候该从单体切到微服务

很多人焦虑"我的Agent架构是不是太简单了,要不要早点上微服务"。我见过一个非常典型的案例,一个团队花了两周时间把本来单体运行的Agent改成微服务,结果线上故障率不降反升。

我现在的判断标准很简单,出现以下三个信号之一再考虑切:

  • 某个模块的负载明显不均(比如记忆服务的QPS是其他模块的十倍)
  • 需要多个团队协作开发同一个Agent系统,单体代码冲突太频繁
  • Agent的某个组件需要独立的技术方案(比如记忆要用向量数据库,但主服务还是PostgreSQL)

最后聊两句我的个人体会

做了这么久的Agent项目,我最深的感受是:Agent架构的复杂度不是来自技术本身,而是来自不确定性。传统软件的每个分支都是确定的,Agent的每个分支都是模型现场生成的。这意味着架构越是复杂、链路越长,出问题的面就越大。所以我自己的实践准则一直是"用最简架构托住当前规模,留好演进接口"。你能看到很多商业Agent平台上的成功案例,多半不是因为他们用了多牛的技术架构,而是他们把某一个单Agent场景打磨到了极致。下一次动手搭Agent之前,不妨先问自己一句:我的Agent最核心的那个循环,我能清晰地画出来吗?画不出来,先去补基础;画得出来,再考虑往上堆架构。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进