1. 从单轮到多 Agent:为什么这个话题值得认真聊
Agent 这个词在过去两年被聊烂了,但真正动手写过、踩过坑的人都知道,从“能跑通一个单轮调用”到“多 Agent 稳定协作”,中间隔着的不是一行代码,而是一整套工程认知的升级。我最早接触 Agent 是从一个很朴素的需求开始的:让模型自己决定调用哪个工具、拿回结果、再决定下一步。那时候觉得这玩意儿真神,一个 while 循环加几个 function call 就能干不少事。可一旦任务变复杂——比如要同时查资料、写代码、做校验、再汇总——单 Agent 就开始露怯了:上下文爆炸、职责混乱、错误累积、循环停不下来。
这篇文章想做的事很明确:把 Agent 的实现方式从最朴素的单轮调用,一路讲到多 Agent 协作,把每一层“为什么这么设计”讲透。核心关键词会贯穿始终——Agent、Prompt Engineering、Context Engineering、Loop Engineering、Graph Engineering。这五个词其实代表了五个递进的工程层次,很多人只停留在 Prompt 层,结果做出来的东西永远是个“高级一点的聊天框”。
适合谁看?如果你已经能写出一个能调用工具的 Agent,但一到复杂任务就崩;如果你在纠结“到底要不要上多 Agent”“框架选哪个”“并发怎么扛”;如果你面试被问到 Agent 架构答不上来——那这篇就是给你写的。我会尽量用从业者之间聊天的口吻,把原理、选型、实操、避坑一次讲清楚,能抄作业的地方直接给方案。
先说一个我自己的判断:单 Agent 是函数,多 Agent 是系统。函数可以很聪明,但系统才扛得住复杂度。理解这句话,后面的内容就顺了。
2. 单轮调用:Agent 的最小可用形态
2.1 单轮调用到底在做什么
单轮调用(single-turn invocation)是所有 Agent 的起点。它的结构简单到可以用一句话概括:给模型一个 Prompt,模型返回一个结果,结束。没有循环,没有状态,没有工具编排。很多人第一次“搭 Agent”其实就是写了个带 system prompt 的 API 调用,然后管它叫 Agent——严格说这不算 Agent,只能算一次推理。
真正的单轮 Agent 至少要包含三个要素:指令(instruction)、上下文(context)、可选的工具描述(tool schema)。模型根据这三样东西决定是直接回答,还是输出一个工具调用请求。如果输出工具调用,宿主程序执行工具、把结果塞回去、再调一次模型——注意,这已经是两轮了。所以“单轮”更多是指没有自主循环控制,而不是字面意义上只调一次。
我早期写的一个典型单轮 Agent 是这样的:用户问“帮我查一下这个仓库最近的提交”,Agent 输出一个git_log的工具调用,程序执行后把结果返回,模型总结成自然语言。整个链路清晰、可控、好调试。问题在于,一旦用户问“帮我看看最近提交里有没有引入 bug 的风险”,单轮就撑不住了——它需要先拉提交、再读 diff、再分析、再给结论,这是一个多步骤的推理链。
2.2 单轮调用的能力边界在哪里
单轮调用的天花板非常明显,我总结成三条:
- 上下文一次性注入,无法动态调整。所有信息必须在第一次调用前准备好,模型没有“我再想想需要什么”的机会。
- 没有错误恢复机制。工具调用失败、返回格式不对、模型理解偏差,全都只能靠外层代码硬编码兜底。
- 无法处理需要分解的任务。复杂任务需要“先规划、再执行、再校验”,单轮没有这个结构。
这里要引入第一个关键概念:Prompt Engineering 解决的是“怎么问”,但解决不了“问几次、按什么顺序问”。很多人把 Prompt 写到极致,few-shot 塞了十几个例子,结果还是不稳定,原因就在于他们把工程问题当成了措辞问题。
提示:如果你现在的 Agent 还停留在单轮,先别急着上框架。把工具 schema 写清楚、把返回格式约束好、把失败重试加上,这三件事做完,你会发现单轮能覆盖的场景比想象中多。
2.3 从单轮到循环:Loop Engineering 的登场
单轮撑不住的时候,自然就进入了循环。Loop Engineering 的核心问题是:什么时候继续、什么时候停、每轮带什么上下文进去。这三点听起来简单,做起来全是坑。
最朴素的循环是 ReAct 模式:Thought → Action → Observation → Thought……直到模型输出 Final Answer。这个模式之所以经典,是因为它把“推理”和“行动”显式地交替起来,让模型有机会根据观察结果调整下一步。但 ReAct 的坑也很明显:没有终止保证。我见过模型在一个死循环里反复调用同一个工具十几次,每次都说“让我再确认一下”。
所以 Loop Engineering 的第一课是终止条件设计。我的做法通常是三层保险:最大轮数限制(硬性,比如 15 轮)、重复动作检测(连续两次相同工具+相同参数就强制中断)、以及显式的 finish 工具(让模型主动声明结束)。这三层里,最大轮数是底线,重复检测是质量保障,finish 工具是优雅退出。
第二课是每轮上下文的管理。循环不是把历史全部塞回去就完事,那样上下文会迅速膨胀到爆。你需要决定:哪些观察结果保留原文、哪些压缩成摘要、哪些直接丢弃。这就是 Context Engineering 的雏形,后面会专门展开。
3. Context Engineering:Agent 的记忆与注意力管理
3.1 为什么上下文管理是 Agent 的生死线
如果说 Prompt Engineering 是“怎么说”,那Context Engineering 就是“让模型看到什么”。这两者的重要性完全不在一个量级。我个人的经验是:一个 Agent 表现不好,80% 的问题出在上下文,而不是 Prompt 措辞。
原因很直接:模型的注意力是有限资源。你塞进去 10 万 token 的历史,模型对其中关键信息的召回率会显著下降。更糟的是,无关信息会干扰推理——模型可能抓住一个早就过期的观察结果,做出错误决策。这在多轮循环里尤其致命,因为每一轮的观察都会累积。
Context Engineering 要解决的核心问题有三个:存什么、怎么存、什么时候取。这其实就是 Agent 记忆(memory)的设计问题。热词里“agent 存储 working memory”说的就是这个。
3.2 三层记忆结构:working、episodic、semantic
我在实际项目里最常用的是一种三层记忆结构,借鉴了认知科学的分类:
| 记忆类型 | 存什么 | 生命周期 | 典型实现 |
|---|---|---|---|
| Working Memory | 当前任务的即时状态、最近几轮观察 | 任务结束即销毁 | 直接放在 prompt 里 |
| Episodic Memory | 历史任务的执行轨迹、成功/失败案例 | 中期保留 | 向量库 + 元数据过滤 |
| Semantic Memory | 领域知识、工具用法、稳定事实 | 长期 | 向量库 / 结构化存储 |
Working Memory 是最关键的,因为它直接进 prompt。我的做法是给它设一个 token 预算,比如 4000 token,超了就按“重要性 + 时间衰减”淘汰。重要性怎么定?工具返回的错误信息、用户明确强调的约束,权重高;中间过程的冗余输出,权重低。
Episodic Memory 用来做“经验复用”。比如一个 Agent 之前成功处理过类似的代码审查任务,那这次的执行轨迹可以作为 few-shot 参考。这里要注意:检索出来的历史轨迹必须做脱敏和裁剪,否则会把无关的上下文污染进来。
Semantic Memory 更像是 Agent 的“常识库”。工具怎么用、API 的返回格式、领域的术语定义,这些放进去,能显著减少模型瞎猜的概率。
3.3 上下文压缩的实操技巧
上下文压缩是 Context Engineering 里最考验功力的部分。我试过几种方案,分享下取舍:
- 摘要压缩:让模型把长观察结果总结成几句话。优点是省 token,缺点是可能丢细节。适合中间过程,不适合关键数据。
- 结构化提取:把工具返回的 JSON 只保留需要的字段。这个最稳,但需要针对每个工具写提取逻辑。
- 滑动窗口:只保留最近 N 轮。简单粗暴,适合短任务,长任务会丢早期关键信息。
- 分层保留:关键轮次保留原文,普通轮次压缩。这是我目前最推荐的,兼顾成本和效果。
注意:压缩不是免费的。每次压缩都是一次额外的模型调用,会增加延迟和成本。所以压缩策略要跟任务复杂度匹配,简单任务别过度设计。
还有一个容易被忽略的点:上下文的顺序。模型对开头和结尾的信息召回率最高(这就是所谓的“lost in the middle”现象)。所以关键约束放开头,最新观察放结尾,中间放历史。这个细节调整,实测能提升不少稳定性。
4. Graph Engineering:把 Agent 从循环升级为流程
4.1 从 Loop 到 Graph 的思维转变
Loop Engineering 解决的是“反复试”,但很多任务其实不需要反复试,它需要的是按固定流程走。比如一个内容生产 Agent:先选题、再查资料、再写初稿、再审核、再发布。这是一个有明确阶段和依赖关系的流程,用循环去实现就是杀鸡用牛刀,而且不可控。
Graph Engineering 的核心思想是:把 Agent 的执行建模成一张有向图,节点是执行单元,边是流转条件。这跟传统的工作流引擎很像,但区别在于节点内部可以是模型推理,边可以是模型判断。这就把“确定性流程”和“不确定性推理”结合起来了。
热词里“agent框架与编排”说的就是这个层次。LangGraph、AutoGen 这些框架本质上都在做图编排。但我想强调的是:图编排不是框架的专利,你完全可以用状态机手写。我早期项目就是用 Python 的字典 + 条件判断实现的,比引入框架更轻、更好调试。
4.2 节点设计:每个节点该做什么
图编排里最容易犯的错是节点粒度失控。节点太大,一个节点干五件事,出了问题没法定位;节点太小,一个节点就调一次模型,图会变得极其臃肿。
我的经验法则是:一个节点对应一个明确的职责,且这个职责可以用一句话描述清楚。比如“检索相关资料”是一个节点,“判断资料是否充分”是另一个节点,“生成初稿”又是一个节点。每个节点的输入输出都要显式定义,这样图才可组合、可测试。
节点内部要不要用循环?可以,但要克制。我通常只在“需要重试”或“需要多轮检索”的节点内部用循环,且必须带终止条件。节点内部的循环不应该跨越节点边界,否则图就失去意义了。
4.3 边与条件:流转逻辑怎么写
边是图的灵魂。最简单的边是无条件流转(A 做完直接到 B),复杂一点的是条件流转(根据 A 的输出决定去 B 还是 C)。条件流转的判断可以有两种实现:代码判断(比如检查输出里有没有某个字段)和模型判断(让模型输出一个路由决策)。
我的建议是:能用代码判断就别用模型判断。模型判断虽然灵活,但引入了不确定性,而且多一次调用就多一份延迟和成本。只有当判断逻辑本身需要语义理解时(比如“这段内容是否涉及敏感信息”),才交给模型。
还有一个高级技巧:并行边。有些节点之间没有依赖关系,可以并行执行。比如“查资料”和“准备模板”可以同时做。这在多 Agent 场景里尤其重要,直接决定了系统的吞吐。但并行也带来复杂度:结果怎么合并、失败怎么处理、顺序怎么保证。这些都要在图的层面设计好。
5. 多 Agent 协作:从单体到团队的跃迁
5.1 什么时候该上多 Agent
这是被问得最多的问题。我的答案很直接:当单 Agent 的职责超过三个,或者上下文超过模型舒适区,就该考虑拆了。但拆之前要想清楚,多 Agent 不是免费的午餐,它带来的是通信成本、协调成本和调试成本。
多 Agent 真正有价值的场景有三类:
- 职责差异大:比如一个负责检索、一个负责编码、一个负责审核,它们的 Prompt、工具、上下文需求完全不同,硬塞进一个 Agent 会互相干扰。
- 需要并行:多个子任务可以同时进行,单 Agent 串行做太慢。
- 需要对抗性校验:一个 Agent 生成,另一个 Agent 挑刺,这种“生成-批判”结构能显著提升质量。
反过来,如果任务本身是线性的、职责单一的,硬上多 Agent 只会让系统更难维护。我见过太多项目为了“显得高级”而拆多 Agent,结果调试成本翻了三倍,效果还不如单 Agent。
5.2 多 Agent 的三种典型拓扑
多 Agent 的协作结构,我归纳成三种:
第一种是 Supervisor 模式。一个主 Agent 负责规划和分派,其他 Agent 是执行者。主 Agent 决定“这个任务给谁做”,执行者做完把结果交回。这种结构清晰、易调试,适合大多数场景。缺点是主 Agent 容易成为瓶颈,且它的判断质量直接决定全局。
第二种是 Pipeline 模式。Agent 按顺序串起来,前一个的输出是后一个的输入。这种适合有明确阶段的流程,比如“检索 → 分析 → 写作 → 审核”。优点是简单,缺点是缺乏反馈,前面错了后面全错。
第三种是 Peer-to-Peer 模式。Agent 之间平等通信,可以互相请求。这种最灵活,也最难控制,容易出现通信风暴或死锁。我一般只在需要复杂协商的场景用,且必须加通信轮数上限。
| 拓扑 | 适用场景 | 优点 | 风险 |
|---|---|---|---|
| Supervisor | 任务可分解、需要动态分派 | 结构清晰、易调试 | 主 Agent 瓶颈 |
| Pipeline | 阶段明确、线性流程 | 简单、可预测 | 错误累积 |
| Peer-to-Peer | 需要协商、复杂决策 | 灵活 | 难控制、易死锁 |
5.3 Agent 之间的通信协议设计
多 Agent 协作最容易翻车的地方就是通信。我踩过的坑包括:消息格式不统一导致解析失败、Agent 之间互相等待导致死锁、消息内容过长导致上下文爆炸。
我的做法是定义一套严格的消息 schema,至少包含这几个字段:发送者、接收者、消息类型(请求/响应/通知)、任务 ID、内容、以及可选的元数据。内容部分尽量结构化,能用 JSON 就别用自然语言,因为自然语言在 Agent 之间传递时歧义会被放大。
还有一个关键设计:共享状态 vs 消息传递。共享状态是所有 Agent 读写同一块内存,简单但容易冲突;消息传递是 Agent 之间显式发消息,清晰但需要协议。我倾向于混合:关键状态放共享存储(比如一个任务上下文对象),Agent 之间通过消息触发动作。这样既有全局视图,又有明确的交互边界。
提示:多 Agent 调试时,一定要把每次消息传递都打日志,包括发送者、接收者、内容摘要、时间戳。出问题时,这份日志就是你的救命稻草。
6. 并发、安全与工程化:多 Agent 落地的硬骨头
6.1 AI Agent 怎么扛并发
热词里“ai agent 怎么扛并发”是个真问题。单 Agent 的并发相对简单,多 Agent 的并发就复杂了,因为涉及共享资源竞争和状态一致性。
我的经验是分三层处理:
- 请求层并发:多个用户请求同时进来,用队列 + 工作池处理。每个请求独立上下文,互不干扰。这一层用常规的后端并发方案就行。
- Agent 层并发:同一个任务内多个 Agent 并行执行。这里要注意的是共享状态的读写锁,以及并行结果的合并顺序。我通常给每个并行分支分配独立的上下文副本,最后统一合并,避免竞争。
- 工具层并发:多个 Agent 同时调用同一个外部工具(比如数据库、API)。这一层要做限流和重试,否则容易把下游打挂。
实测下来,瓶颈往往不在模型调用,而在工具调用和状态同步。所以优化并发时,先看工具层的耗时分布,再决定要不要上更复杂的调度。
6.2 Agent 安全:那些不能忽视的边界
Agent 安全是个大话题,我只讲实操中最容易出问题的几点:
- 工具权限最小化。Agent 能调用的工具必须是白名单,且每个工具的权限要收窄。比如“读文件”和“写文件”要分开授权,不能让一个 Agent 既能读又能写还能删。
- 输入输出过滤。用户输入可能包含注入攻击,Agent 输出可能包含敏感信息。这两端都要有过滤层。
- 执行沙盒。涉及代码执行的 Agent,必须在沙盒里跑。热词里“agent沙盒”说的就是这个。沙盒要限制网络、文件系统、CPU 和内存。
- 审计日志。每个 Agent 的每个动作都要留痕,包括调用了什么工具、传了什么参数、返回了什么。出问题时能追溯。
注意:安全不是加一层就完事,它要贯穿整个 Agent 生命周期。我见过太多项目在 Demo 阶段完全不管安全,上线后才发现漏洞百出,返工成本极高。
6.3 可观测性:让 Agent 的行为可解释
Agent 最让人头疼的是“它为什么这么做”。没有可观测性,调试就是盲人摸象。我的做法是三个层次:
- Trace:记录每个任务的完整执行链路,包括每个节点的输入输出、耗时、状态。这是最基础的。
- Metrics:统计成功率、平均轮数、工具调用分布、错误类型分布。这些指标能帮你发现系统性问题。
- Replay:能重放某个失败任务,逐步查看每步的决策依据。这对定位偶发 bug 极其有用。
这三样东西,我建议在项目早期就搭起来,别等到出问题才补。因为 Agent 的行为不确定性太高,没有可观测性,你连“它是不是真的坏了”都判断不了。
7. 常见问题与排查技巧实录
7.1 Agent 卡死或循环不停怎么办
这是最高频的问题。排查顺序我一般是这样的:
- 看最大轮数有没有设。没设的话先加上,这是底线。
- 看重复动作检测有没有生效。连续两次相同工具+相同参数,应该强制中断。
- 看终止条件是不是太模糊。如果 Prompt 里写的是“当你觉得完成了就结束”,模型很可能永远觉得没完成。改成明确的 finish 工具调用。
- 看上下文里有没有矛盾信息。有时候模型循环是因为它收到了互相冲突的指令,只能反复尝试。
7.2 工具调用失败怎么优雅处理
工具失败是常态,关键是别让失败直接崩掉整个 Agent。我的处理策略是分级:
| 失败类型 | 处理方式 |
|---|---|
| 参数错误 | 把错误信息返回给模型,让它修正参数重试 |
| 超时 | 重试 1-2 次,仍失败则降级或跳过 |
| 权限不足 | 直接终止该分支,返回明确错误 |
| 下游不可用 | 触发熔断,切换到备用方案或告知用户 |
核心原则是:失败信息要结构化地返回给模型,而不是抛一个异常就完事。模型看到“参数 X 格式错误,应为整数”,它下次就能改对。
7.3 多 Agent 之间互相甩锅怎么办
这个坑很隐蔽。表现是:Agent A 说“这个该 B 做”,Agent B 说“这个该 A 做”,任务永远完不成。根因通常是职责边界没定义清楚。
解决办法是在系统 Prompt 里明确每个 Agent 的职责范围和禁止事项,并且在 Supervisor 层做兜底:如果检测到任务在两个 Agent 之间来回传递超过 N 次,就强制由 Supervisor 裁决。另外,任务分派时要把“这个任务属于谁”写清楚,别让 Agent 自己猜。
7.4 上下文爆炸的应急处理
上下文爆炸的典型症状是:延迟飙升、成本暴涨、模型开始胡言乱语。应急处理分三步:
- 立即压缩:把历史观察结果做摘要,只保留关键字段。
- 清理冗余:检查有没有重复的工具返回、有没有可以丢弃的中间过程。
- 调整预算:给 working memory 设硬性 token 上限,超了强制淘汰。
长期方案还是回到 Context Engineering:设计好记忆分层,别让所有东西都堆在 prompt 里。
8. 框架选型与学习路线的一点个人看法
8.1 框架不是必需品
热词里“agent框架”被反复提及,但我想泼盆冷水:框架解决的是编排和抽象问题,解决不了你的业务逻辑问题。我见过太多人一上来就选框架,结果被框架的抽象层绕晕,连基本的调试都做不了。
我的建议是:先用最朴素的方式手写一遍。用 Python 写个循环、写个状态机、写个简单的图,把 Agent 的核心机制摸清楚。这个过程会让你理解框架在帮你做什么,之后选框架时才有判断力。
选框架时看三点:是否支持你要的拓扑结构、是否可观测、是否容易扩展。别只看 star 数,要看它的抽象是否符合你的心智模型。
8.2 学习路线的个人建议
如果让我给一条学习路线,我会这么排:
- 先写单轮调用,理解 Prompt 和工具 schema 的作用。
- 再加循环,理解 Loop Engineering 的终止条件和上下文管理。
- 然后做上下文分层,理解 Context Engineering 的记忆设计。
- 接着画图,理解 Graph Engineering 的节点和边。
- 最后拆多 Agent,理解协作拓扑和通信协议。
每一步都要动手写,别只看文章。Agent 这东西,看十篇不如写一个。写的过程中踩的坑,才是真正属于你的经验。
8.3 面试里常被问到的几个点
如果你在准备 Agent 相关的面试,这几个问题几乎必问:单 Agent 和多 Agent 的取舍、上下文管理策略、循环终止条件、并发处理、安全边界。回答时别背概念,讲你实际做过的项目,讲你踩过的坑和怎么解决的。面试官想听的是你的判断力,不是你对框架 API 的熟悉度。
我在实际项目里最大的体会是:Agent 的难点从来不在模型,而在工程。模型能力是给定的,但你怎么组织 Prompt、怎么管理上下文、怎么设计循环和图、怎么拆多 Agent、怎么保证并发和安全——这些才是决定成败的地方。把这些想清楚,你做的 Agent 才不是玩具,而是能真正扛事的系统。