多智能体对话框架这两年从论文里的概念一路卷到了工程落地,AgentChat 算是其中比较有代表性的一个方向。我最早接触这类框架是在做一个内部知识问答系统的重构,当时单 Agent 的方案已经撑不住复杂任务了——一个模型既要理解意图、又要查资料、还要做校验,prompt 越写越长,效果反而越来越差。后来拆成多个角色各管一摊,整体稳定性明显上来了。这篇就围绕 AgentChat 这类基于大语言模型的多智能体对话框架,把架构设计、核心机制、开发实操和部署细节完整讲一遍,适合已经有一定 LLM 应用基础、想往多智能体方向深入的同学参考,也适合正在做技术选型的团队拿去做对比。
1. 为什么单 Agent 撑不住复杂任务
1.1 单 Agent 的能力天花板在哪里
很多人刚开始做 LLM 应用时,习惯把所有逻辑塞进一个 Agent:系统提示词里写清楚"你是一个助手,要能回答问题、要能调用工具、要能记住上下文、要能判断什么时候该拒绝"。这种做法在 Demo 阶段没问题,但一旦任务复杂度上来,问题就集中爆发了。
最典型的是指令冲突。当系统提示词里同时要求"尽可能详细回答"和"回答要简洁"时,模型会随机偏向某一侧,导致输出风格不稳定。再比如"优先使用工具"和"不确定时不要乱调用工具"这两条,模型很难精确判断边界,经常出现该调用时不调用、不该调用时乱调的情况。
第二个问题是上下文污染。单 Agent 处理多轮任务时,所有中间结果都堆在同一个上下文窗口里。前面查到的无关信息、失败的尝试、用户的闲聊,全都会影响后续推理。我实测过一个场景:让单 Agent 先做数据清洗再做分析,结果它在分析阶段反复引用清洗阶段的中间日志,把噪声当成了有效信息。
第三个问题是难以调试。单 Agent 出错了,你很难定位是哪一步的推理出了问题。是意图理解错了?还是工具调用参数错了?还是最终生成时跑偏了?所有环节耦合在一起,排查成本极高。
1.2 多智能体拆分的核心逻辑
多智能体框架的本质思路是职责分离。把一个大而全的任务拆成若干子任务,每个子任务交给一个专门的 Agent,每个 Agent 只关心自己那一亩三分地。这样做的好处很直接:
- 提示词可以写得很聚焦。负责检索的 Agent 就专注检索,提示词里只讲检索策略和输出格式,不用操心最终回答的语气。
- 上下文隔离。每个 Agent 有自己的对话历史,互不干扰,避免了信息串味。
- 可独立替换和优化。检索效果不好,只改检索 Agent,不影响其他环节。
- 可并行执行。多个独立子任务可以同时跑,整体延迟反而可能比单 Agent 串行处理更低。
AgentChat 这类框架提供的正是这套拆分和协作的基础设施:Agent 的定义、消息的路由、对话的编排、状态的传递。你不需要从零实现消息总线,只需要关注每个 Agent 的具体逻辑。
1.3 AgentChat 在框架生态中的位置
市面上多智能体框架不少,AgentChat 的定位偏向对话驱动。它不像某些工作流框架那样强调 DAG(有向无环图)式的固定流程,而是更强调 Agent 之间通过自然语言消息进行协商和协作。这个设计取向决定了它更适合那些流程不完全确定、需要动态决策的场景,比如开放式问答、多轮协商、复杂任务分解。
如果你的任务是固定流水线(A 做完必然做 B,B 做完必然做 C),用工作流引擎可能更合适。但如果任务需要根据中间结果动态决定下一步找谁、问什么,AgentChat 这种对话驱动的模式优势就体现出来了。
2. AgentChat 的核心架构拆解
2.1 Agent 抽象层:每个角色到底封装了什么
AgentChat 里最基础的单元就是 Agent。一个 Agent 通常包含几个核心要素:
| 要素 | 作用 | 常见配置项 |
|---|---|---|
| 角色定义 | 决定 Agent 的身份和行为边界 | name、system_message |
| 模型配置 | 指定用哪个 LLM 及参数 | model、temperature、max_tokens |
| 工具集 | 该 Agent 可调用的外部能力 | tools 列表 |
| 记忆机制 | 保存对话历史和中间状态 | memory 类型、窗口大小 |
| 终止条件 | 判断该 Agent 何时结束发言 | 最大轮次、特定标记 |
角色定义是最关键的。我见过很多项目在这里偷懒,system_message 就写一句"你是一个助手",结果 Agent 行为完全不可控。正确的做法是把角色写到足够具体:它的职责是什么、不负责什么、输出格式要求、遇到不确定情况怎么处理。比如一个校验 Agent 的提示词里应该明确写"你只负责检查事实一致性,不负责补充新信息,如果发现矛盾就指出具体位置"。
模型配置上有个经验:不同角色的 Agent 可以用不同规模的模型。负责路由和简单判断的 Agent 用轻量模型就够了,负责最终生成和复杂推理的用大模型。这样能在成本和效果之间取得平衡。我实测过一个配置,路由 Agent 用 7B 级别的模型,生成 Agent 用 70B 级别,整体成本比全用大模型降了六成多,效果几乎没损失。
2.2 消息传递机制:Agent 之间怎么"说话"
AgentChat 的消息传递是理解整个框架的关键。Agent 之间不是直接函数调用,而是通过消息进行通信。一条消息通常包含发送者、接收者、内容、类型这几个字段。
消息类型一般分几类:
- 普通对话消息:Agent 之间的自然语言交流。
- 工具调用消息:Agent 请求调用某个工具,以及工具返回的结果。
- 控制消息:用于终止对话、切换轮次、请求人工介入等。
- 系统消息:框架层面的通知,比如超时、错误。
这种设计的精妙之处在于解耦。发送消息的 Agent 不需要知道接收方是谁、怎么处理,它只管把消息发出去。框架负责路由。这意味着你可以动态增删 Agent,而不影响已有 Agent 的代码。
实际开发中有一个容易踩的坑:消息格式不统一。如果 A Agent 输出的是 JSON,B Agent 期望的是纯文本,中间就需要一个转换层。我的做法是在框架层面约定一个统一的消息信封格式,内容字段可以是任意结构,但信封本身(发送者、接收者、类型、时间戳)必须标准化。这样每个 Agent 只需要解析自己关心的内容字段。
2.3 对话编排:谁来决定下一个发言者
对话编排是多智能体框架里最考验设计的部分。常见的有几种模式:
轮询模式:Agent 按固定顺序依次发言。实现简单,但灵活性差,适合流程固定的场景。
路由模式:有一个专门的路由 Agent 或路由函数,根据当前对话状态决定下一个发言者。灵活度高,但路由逻辑本身可能成为瓶颈。
竞价模式:每个 Agent 根据当前上下文判断自己是否应该发言,谁最相关谁先说。这个模式最接近人类开会的感觉,但实现复杂度最高,容易出现"抢话"或"冷场"。
混合模式:实际项目里最常见。比如先用路由决定大方向,再在候选 Agent 里用竞价决定具体谁发言。
AgentChat 通常支持自定义编排逻辑。我的建议是:先从轮询模式跑通,再逐步引入路由。一上来就搞竞价模式,调试会让你怀疑人生。路由逻辑最好用代码实现而不是让 LLM 来判断,因为 LLM 判断不稳定,而且每次判断都要消耗 token。只有当路由规则确实难以用代码表达时,才考虑用 LLM 做路由。
2.4 状态管理与共享上下文
多智能体系统里,状态管理是个绕不开的问题。哪些信息是全局共享的,哪些是 Agent 私有的,需要提前设计清楚。
全局共享的通常是:任务目标、已确认的事实、当前进度。这些信息放在一个共享的"黑板"(blackboard)里,所有 Agent 都能读写。私有的是每个 Agent 自己的对话历史和中间推理过程。
这里有个关键决策:共享上下文要不要全量同步给每个 Agent。全量同步的好处是信息完整,坏处是上下文窗口很快被撑爆,而且无关信息会干扰推理。我的做法是按需同步:共享黑板里存全量信息,但每个 Agent 在发言前只拉取与自己职责相关的部分。比如检索 Agent 只需要知道"要查什么",不需要知道"最终回答要什么语气"。
状态管理还有一个坑是并发写入冲突。多个 Agent 同时往黑板里写数据时,可能互相覆盖。解决办法是给黑板加版本号或锁机制,写入前先检查版本。这个在单机环境下用简单的乐观锁就能解决,分布式环境下需要引入更复杂的协调机制。
3. 从零搭建一个 AgentChat 应用
3.1 环境准备与依赖选型
搭建 AgentChat 应用,基础环境需要这些东西:
- Python 3.10+:大部分多智能体框架对 Python 版本有要求,3.10 是比较稳妥的选择。
- LLM 接入层:可以是本地部署的模型,也可以是 API 调用。本地部署推荐用 vLLM 或 Ollama 做推理服务,API 调用则看你的模型供应商。
- 向量数据库:如果涉及知识检索,需要 Milvus、Chroma 或 FAISS 这类向量库。
- 消息队列(可选):如果 Agent 数量多、需要异步通信,可以引入 Redis 或 RabbitMQ。
依赖管理我强烈建议用Poetry 或 Conda,不要用裸 pip。多智能体项目依赖复杂,版本冲突是家常便饭。用 Poetry 锁定版本,能省掉大量"在我机器上能跑"的问题。
一个容易忽略的点是模型服务的并发能力。多智能体系统里,多个 Agent 可能同时请求模型。如果你的模型服务只支持单并发,整个系统会被拖垮。本地部署时,vLLM 的--max-num-seqs参数要调够;API 调用时,要注意速率限制,做好重试和降级。
3.2 定义第一个 Agent:从最小可用开始
不要一上来就设计五个 Agent 的复杂系统。先用一个 Agent 跑通全流程,确认框架、模型、工具链都没问题,再逐步拆分。
一个最小 Agent 的定义大概长这样:
from agentchat import Agent, AgentConfig config = AgentConfig( name="assistant", system_message="你是一个知识问答助手。回答要基于事实,不确定时明确说不知道。", model="your-model-name", temperature=0.3, max_tokens=1024, ) agent = Agent(config) response = agent.chat("介绍一下多智能体系统的核心优势") print(response)这个阶段重点验证三件事:模型能不能正常调用、输出格式是否符合预期、异常情况(超时、限流)有没有被正确处理。我见过太多项目跳过这一步,直接上复杂架构,结果出问题时根本不知道是框架问题还是模型问题。
3.3 引入工具调用:让 Agent 能"动手"
Agent 只有对话能力是不够的,真正有用的是能调用外部工具。工具调用的核心是函数签名描述和结果解析。
def search_knowledge(query: str) -> str: """根据查询词检索知识库,返回最相关的三条内容。""" # 实际检索逻辑 return results agent.register_tool(search_knowledge)工具描述要写得足够清晰,包括:工具做什么、参数是什么含义、返回什么格式、什么情况下应该用。模型是根据这些描述来决定是否调用工具的,描述模糊就会导致误调用或漏调用。
实测经验:工具数量不要超过 10 个。工具太多,模型选择困难,准确率会下降。如果确实需要很多工具,考虑按场景分组,不同 Agent 挂载不同工具集。
另一个坑是工具返回结果太长。如果检索返回了 5000 字,直接塞进上下文会挤占空间。我的做法是在工具内部做截断和摘要,只返回最相关的片段,完整结果存到外部存储,需要时再取。
3.4 多 Agent 协作的第一次跑通
当单 Agent 验证没问题后,开始拆分。以一个"研究助手"场景为例,拆成三个 Agent:
- 规划 Agent:接收用户问题,拆解成若干子问题。
- 检索 Agent:针对每个子问题检索资料。
- 综合 Agent:汇总检索结果,生成最终回答。
编排逻辑用最简单的轮询:
agents = [planner, retriever, synthesizer] for agent in agents: message = agent.process(shared_context) shared_context.update(message)第一次跑通的目标不是效果多好,而是确认消息能在 Agent 之间正确传递、共享上下文能正确更新、整个流程能正常结束。这个阶段出问题最多的是消息格式和上下文更新时机,耐心调试。
4. 开发中那些文档不会告诉你的坑
4.1 提示词工程的"多 Agent 特有问题"
单 Agent 的提示词工程已经够麻烦了,多 Agent 场景下还有额外的坑。
角色边界模糊。两个 Agent 的职责如果有重叠,它们会互相推诿或者重复劳动。比如规划 Agent 和综合 Agent 都可能涉及"总结",如果不明确划分,规划 Agent 可能把总结也做了,综合 Agent 就没活干。解决办法是在提示词里明确写"你不负责 XX,那是 XX Agent 的职责"。
输出格式不一致。A Agent 输出的是自然语言,B Agent 期望的是结构化数据,中间就需要转换。我的做法是在每个 Agent 的提示词里强制约定输出格式,并且在框架层面做格式校验,不符合就重试。
提示词过长导致注意力分散。多 Agent 系统里,每个 Agent 的提示词可能包含角色定义、工具说明、格式要求、示例等,很容易超过 2000 字。提示词越长,模型对关键指令的注意力越弱。解决办法是分层提示词:核心指令放最前面,细节放后面,示例能少则少。
4.2 上下文窗口的精细化管理
上下文窗口是多智能体系统的稀缺资源。管理不好,要么信息丢失,要么 token 成本爆炸。
几个实用策略:
- 滑动窗口 + 摘要:保留最近 N 轮完整对话,更早的对话压缩成摘要。摘要由专门的 Agent 或规则生成。
- 按需检索:不把所有历史都塞进上下文,而是把历史存到外部,需要时用检索的方式取回相关片段。
- 角色隔离:每个 Agent 只保留与自己相关的历史,不相关的直接丢弃。
我实测过一个配置:三个 Agent 协作,如果不做上下文管理,跑十轮后 token 消耗是初始的 8 倍;做了滑动窗口和摘要后,稳定在 2 倍左右,效果几乎没差别。
4.3 死循环与无限对话的终止策略
多智能体系统最容易出的问题就是死循环。两个 Agent 互相觉得对方应该先行动,或者反复确认同一个信息,对话永远结束不了。
终止策略要设计多层:
- 最大轮次限制:硬性限制对话轮数,超过就强制结束。这是最后一道防线。
- 重复检测:如果连续几轮的消息内容高度相似,判定为死循环,触发终止。
- 目标达成检测:有一个专门的判断逻辑,检查任务目标是否已达成,达成则结束。
- 超时机制:整个对话设置总时长上限,超时强制结束。
我的经验是最大轮次限制一定要设,而且不要设太大。一般任务 10 到 15 轮足够了,超过这个数还没结果,多半是设计有问题,继续跑也是浪费 token。
4.4 模型输出不稳定性的应对
LLM 的输出天然有随机性,多智能体系统里这种随机性会被放大。A Agent 的一次随机偏差,可能导致 B Agent 完全跑偏。
应对手段:
- 降低 temperature:需要稳定输出的 Agent,temperature 设到 0.1 到 0.3。
- 结构化输出:强制模型输出 JSON 等结构化格式,减少自由发挥空间。
- 输出校验与重试:对关键输出做格式和内容校验,不符合就重试。
- 多轮投票:对关键决策,让模型跑多次,取多数结果。成本高但稳定性好。
这里有个反直觉的经验:不是所有 Agent 都需要稳定输出。负责创意生成的 Agent,适当提高 temperature 反而能带来更好的效果。关键是区分哪些环节需要稳定,哪些环节需要多样性。
5. 部署上线:从能跑到能用
5.1 本地部署与 API 调用的取舍
部署方式的选择直接影响成本、延迟和可控性。
| 维度 | 本地部署 | API 调用 |
|---|---|---|
| 成本 | 前期硬件投入高,长期边际成本低 | 按量付费,前期无投入 |
| 延迟 | 可控,取决于硬件 | 受网络和供应商影响 |
| 数据隐私 | 数据不出本地 | 数据需传给供应商 |
| 模型选择 | 受硬件限制 | 可选范围广 |
| 运维复杂度 | 高 | 低 |
我的建议是:原型阶段用 API,验证可行后再考虑本地部署。本地部署的硬件门槛不低,一个能跑 70B 模型的配置,显存需求在 80G 以上。如果只是跑 7B 到 13B 的模型,单张消费级显卡也能凑合,但并发能力有限。
本地部署时,推理框架的选择很关键。vLLM 的吞吐量表现好,适合并发场景;Ollama 部署简单,适合快速验证;TGI 在 HuggingFace 生态里集成度高。具体选哪个,看你的技术栈和团队熟悉度。
5.2 性能优化的几个关键点
多智能体系统的性能瓶颈通常不在模型推理本身,而在编排和通信。
并行化:独立的子任务并行执行。比如三个检索任务互不依赖,就同时发起,而不是串行等待。这一项优化往往能带来数倍的延迟下降。
缓存:相同或相似的请求结果缓存起来。检索结果、模型输出都可以缓存。缓存命中率在高频场景下能到 30% 以上。
批处理:多个请求合并成一个批次发给模型。vLLM 等框架支持连续批处理,能显著提升吞吐。
流式输出:最终回答用流式返回,用户感知延迟大幅降低。虽然总时间没变,但体验好很多。
5.3 监控与可观测性建设
多智能体系统上线后,没有监控就是盲人摸象。需要监控的指标包括:
- 每个 Agent 的调用次数、成功率、平均延迟
- token 消耗:按 Agent、按任务类型拆分
- 对话轮次分布:识别异常长的对话
- 工具调用成功率:哪些工具经常失败
- 错误类型分布:超时、限流、格式错误各占多少
日志要记录完整的对话链路,包括每个 Agent 的输入输出。出问题时能快速定位是哪个环节的问题。我习惯给每次对话分配一个 trace_id,所有相关日志都带上这个 id,排查时一搜就全出来了。
5.4 成本控制的实战手段
多智能体系统的 token 消耗是单 Agent 的数倍,成本控制必须提前规划。
模型分级:简单任务用轻量模型,复杂任务用大模型。路由、格式转换、简单判断这些环节,小模型完全够用。
上下文压缩:前面提到的滑动窗口和摘要,能显著减少 token 消耗。
结果缓存:高频重复的查询直接返回缓存结果。
提前终止:任务达成或判定为死循环时立即终止,不浪费 token。
预算控制:给每个任务设置 token 预算上限,超了就降级或终止。
我实测过一个优化组合:模型分级 + 上下文压缩 + 结果缓存,整体成本比优化前降了约 70%,效果损失在可接受范围内。
6. 几个值得深挖的进阶方向
6.1 Agent 之间的协商与冲突解决
当多个 Agent 对同一问题给出不同结论时,怎么解决冲突?简单的做法是投票,但投票忽略了 Agent 的专业性差异。更合理的做法是加权投票或引入仲裁 Agent。
仲裁 Agent 的职责是听取各方意见,根据证据强度做出判断。这个 Agent 的提示词要特别设计,强调"基于证据而非基于发言顺序或语气"。
6.2 动态 Agent 生成
固定 Agent 集合适合流程确定的场景。但有些任务需要根据情况动态创建 Agent。比如遇到一个需要特定领域知识的子问题,临时创建一个该领域的专家 Agent。
动态生成的关键是Agent 模板化。预定义好 Agent 的模板(角色、工具、模型配置),运行时填充具体参数即可。这样既灵活又可控。
6.3 与外部系统的深度集成
AgentChat 应用很少是孤立的,通常需要和外部系统集成:数据库、API、消息平台、工作流引擎。集成的关键是抽象好接口,让 Agent 通过统一的工具接口访问外部系统,而不是每个 Agent 都写一套集成代码。
我在实际项目里的体会是,多智能体系统的价值不在于 Agent 数量多,而在于每个 Agent 的职责是否清晰、协作是否顺畅。三个职责明确的 Agent,效果往往好过十个职责模糊的 Agent。另外,不要迷信框架,框架只是脚手架,真正决定效果的是你对业务的理解和对提示词的打磨。先把单 Agent 做到极致,再考虑拆分,这个顺序不要反。