多 Agent 架构与通信¶
多 Agent 系统的核心优势与适用场景¶
🌟 1. 多 Agent 系统相比单 Agent 有哪些核心优势?¶
单 Agent 就像一个全能但只有一个大脑的工人。多 Agent 则是一个分工明确、各有所长的团队。这个结构差异带来了四个核心优势:

- 优势一:专业分工,深度优于广度
一个 Agent 的上下文窗口和注意力都是稀缺资源。把所有工具和指令塞给一个模型,它会变得“什么都会、什么都不精”。多 Agent 让每个 Agent 只掌握一个领域的工具和 System Prompt,比如财务分析 Agent 深谙会计准则,法律审查 Agent 专攻合同条款。这种认知负载的降低直接提升了输出的专业性和准确率。
- 优势二:并行处理,突破单线程瓶颈
单 Agent 执行复杂任务必须串行:先查数据,再分析,再写报告。多 Agent 可以把无依赖的子任务同时派发出去——竞品分析、用户调研、趋势预测三个 Agent 并行工作,总耗时由最慢的那个决定,而不是三者之和。这在长链路任务中能带来数倍的效率提升。
- 优势三:独立记忆与上下文隔离
单 Agent 在多轮工具调用后,上下文窗口会被大量中间结果污染,导致“迷失中间”问题。多 Agent 各自拥有独立的短期记忆,每个 Agent 的上下文只包含自己领域的相关历史,不会被其他子任务的噪音干扰。这对于需要严格逻辑链的推理任务尤为关键。
- 优势四:鲁棒性与可维护性
单 Agent 一个模块出错,整个流程崩溃。多 Agent 可以实现“某个 Worker 挂了,Orchestrator 换另一个备选”的容错机制。同时,各 Agent 可以独立迭代、独立部署,修改竞品分析 Agent 的 prompt 不会影响用户调研 Agent 的稳定性,降低了系统的耦合度和变更风险。
⚖️ 2. 什么情况下该用多 Agent,什么情况下反而不该用?¶
用还是不用,核心看一个比值:分工带来的增益 能否覆盖 引入的协调成本。我们可以用一个决策矩阵来判断。

你的任务是否满足以下条件?
├─ 任务可以自然分解为多个相互独立的子任务? (是 → +1)
├─ 子任务需要不同的专业工具或知识领域? (是 → +1)
├─ 子任务之间存在可并行执行的无依赖环节? (是 → +1)
├─ 单个 Agent 的上下文窗口已经不足以承载全流程?(是 → +1)
└─ 系统未来需要独立迭代或分配给不同团队维护? (是 → +1)
总分 ≥ 3? → 用多 Agent,收益大于成本。
总分 1~2? → 单 Agent + 良好设计的工具组合即可,无需引入多 Agent 的调度复杂度。
总分 = 0? → 绝对不要用多 Agent。协调开销(状态传递、错误处理、协议开销)远大于收益。
具体来说,这些场景适合单 Agent:
-
单一类型的任务:如“帮我总结这篇 PDF”,没有分解的必要。
-
对延迟敏感:多 Agent 通信、序列化、网络往返会增加秒级延迟,实时对话场景可能不适用。
-
工具集有限:如果所有操作都围绕同一个数据库和一组 API,一个精心设计的 Agent 足够覆盖。
-
团队规模小、维护成本敏感:多 Agent 意味着多套 prompt、多个部署单元、更复杂的监控,小团队容易被拖垮。
这些场景必须上多 Agent:
-
企业级复杂工作流:如“入职流程”,涉及 HR 系统、IT 权限开通、财务报税,需要不同领域的专业 Agent。
-
需要异构能力的组合:一个 Agent 擅长逻辑推理,另一个擅长创意写作,第三个专门做代码生成,Orchestrator 根据子任务类型动态路由。
-
高可用性要求:关键任务不能单点故障,需要 Agent 冗余和故障转移。
一个反例: 你的需求是“根据用户输入的描述,搜索产品库并返回价格”,这个任务明确、步骤少,用单 Agent + Function Calling 就很好。如果你硬拆成“意图识别 Agent + 搜索 Agent + 格式化 Agent”,不仅增加延迟,还引入序列化开销,完全是过度设计。
🏗️ 3. 一个市场分析任务同时需要分析竞品、用户、趋势,你会怎么设计 Agent 架构?¶
这个需求天然适合多 Agent:三个分析维度相互独立,各自需要不同的数据源和专业知识。我采用 Orchestrator + Worker 模式,结合 MCP 和 A2A 协议思想,实现一个松耦合、可扩展的架构。
架构蓝图:

各 Agent 的具体设计:
- 竞品分析 Agent
- System Prompt:你是一位竞争情报分析师,擅长识别竞品的功能优劣势、定价策略和市场定位。
- 工具:
web_scraper(抓取官网)、product_db_query(查询产品参数)、pricing_api(获取定价信息)。 -
输出:结构化 JSON,包含竞品对比矩阵和 SWOT 要点。
-
用户洞察 Agent
- System Prompt:你是用户研究专家,能从海量用户反馈中提炼出痛点、需求和满意度。
- 工具:
survey_api(调研数据)、review_scraper(电商/社媒评论)、sentiment_analyzer(情感分析)。 -
输出:用户画像摘要、核心需求列表、满意度评分。
-
趋势分析 Agent
- System Prompt:你是行业趋势分析师,擅长从专利、论文、投融资事件中捕捉技术走向和市场机会。
- 工具:
patent_search(专利检索)、news_api(行业新闻)、report_db(券商研报)。 -
输出:关键技术趋势、市场规模预测、风险因素。
-
Orchestrator Agent
- 负责解析用户指令,判断需要启动哪几个 Worker,设定并行/串行顺序(这里三个分析 Agent 完全独立,可以并行)。它同时也处理异常:如果竞品分析 Agent 返回“数据不足”,它可以指示该 Agent 缩小范围或使用备份数据源。
- 所有 Worker 完成后,激活 Reporter Agent 汇总。
示例代码:用 Python asyncio 模拟编排
import asyncio, json
class MarketAnalysisOrchestrator:
def __init__(self, agents):
self.agents = agents # {"competitor": agent_obj, ...}
async def run(self, topic):
# 并行派发三个独立分析任务
tasks = [
self.agents["competitor"].analyze(topic),
self.agents["user_insight"].analyze(topic),
self.agents["trend"].analyze(topic)
]
# 并行执行,return_exceptions=True 防止某一个失败阻塞全部
competitor, user_insight, trend = await asyncio.gather(*tasks, return_exceptions=True)
# 结果后处理:异常降级
results = {"competitor": self._safe_result(competitor),
"user_insight": self._safe_result(user_insight),
"trend": self._safe_result(trend)}
# 启动汇总 Agent,整合三份分析
report = await self.agents["reporter"].synthesize(results)
return report
def _safe_result(self, result):
if isinstance(result, Exception):
return {"error": str(result), "partial_data": None}
return result
通信机制选择:
如果这三个 Worker 是同一进程内的对象,直接调用即可。如果是分布式部署,可以让 Orchestrator 通过 HTTP + JSON 异步调用各个 Worker 的 /analyze 端点,遵循类似 A2A 的 Task 模型:提交任务 → 返回 task_id → 轮询或 Webhook 获取结果。这样架构上更具扩展性。
关键设计考量:
-
数据流与依赖:三个分析任务互不依赖,所以并行。如果趋势分析需要先知道竞品名单,那么趋势 Agent 就必须等竞品 Agent 完成后再启动,这时就是串行+并行的混合 DAG 编排。
-
去重与冲突解决:汇总 Agent 不能简单拼接三份报告。它要识别出“用户洞察说价格是痛点,竞品分析显示头部竞品在降价”这种交叉信号,提炼出有战略意义的洞察。这需要汇总 Agent 的 System Prompt 里明确其“综合分析”的职责。
-
容错与弹性:如果趋势分析 Agent 超时(比如第三方研报 API 挂了),汇总 Agent 应能基于竞品和用户洞察给出“有限但仍有价值”的报告,而不是整体失败。
最终效果:
用户只需要输入“分析智能手表市场”,数分钟内就能得到一份涵盖竞争格局、用户需求、技术趋势的结构化报告,且每个板块都有专业 depth,而非单 Agent 那种“大而全但浅”的泛泛之谈。这种架构可以轻松复用到任何多维度分析场景:比如“新产品可行性评估”、“企业数字化转型方案”等。
Supervisor 模式 vs Peer-to-Peer 模式的架构对比¶
🧬 1. 什么是 Supervisor 模式和 Peer-to-Peer 模式?¶
这两种模式的核心区别在于控制流的集中程度:谁来决定下一步做什么?是有一个总指挥,还是大家地位平等、互相商量?
Supervisor 模式(监督者模式)

-
决策权:完全集中在上方的 Supervisor。它理解全局目标,将复杂任务拆解为子任务,根据各个 Worker 的能力进行分派,并监控执行结果。Worker 之间不直接通信,所有信息和结果都回流到 Supervisor。
-
角色:Supervisor 是“大脑”,Workers 是“手脚”。典型的星型拓扑。
Peer-to-Peer 模式(对等模式/去中心化模式)

-
决策权:分散在各个 Agent 之间。没有固定的全局指挥官。每个 Agent 拥有自己独立的视角和局部目标,当它认为需要其他 Agent 的信息或能力时,可以主动发起对话、传递任务或请求协助。
-
角色:所有 Agent 地位平等,是一个“团队协作”模式。可以是全连接拓扑,也可以是基于某种共识机制的动态链接。
最直观的类比:
-
Supervisor 模式:交响乐团,指挥(Supervisor)统一控制节奏、声部进出,乐手(Worker)只看指挥,互相之间不需要交流。
-
Peer-to-Peer 模式:爵士乐队即兴演奏,乐手们互相听、互相给信号,谁都可以发起一段新的旋律,没有固定的指挥。
⚖️ 2. Supervisor 模式和 Peer-to-Peer 模式各有什么优缺点?生产中如何选型?¶
Supervisor 模式的优缺点:
| 优点 | 缺点 |
|---|---|
| 可控性强:执行路径完全由你设计的 Supervisor 逻辑(状态图或 LLM 路由)决定,易于调试和理解。 | 单点瓶颈:Supervisor 成为性能和可靠性的集中瓶颈。它挂了,全系统停止。 |
| 简单高效:Worker 之间无需复杂的发现和协商协议,架构清晰,开发成本低。 | 灵活性受限:当任务需要 Worker 之间大量互动时(如共同创作),Supervisor 的串行分派效率低下。 |
| 易于监控和干预:所有任务流经 Supervisor,你可以在一个节点上记录所有状态、做人工审批。 | Supervisor 上下文爆炸:所有 Worker 的结果都回流到 Supervisor,其上下文窗口很快会超限,需要压缩策略。 |
Peer-to-Peer 模式的优缺点:
| 优点 | 缺点 |
|---|---|
| 鲁棒性高:没有单点故障。一个 Agent 离线,其他 Agent 可以尝试寻找替代路径或动态重组。 | 协调复杂:需要设计复杂的通信协议(消息路由、冲突解决、共识机制),开发成本高。 |
| 灵活、涌现能力强:Agent 之间自发、多向的交互可能产生 Supervisor 模式下无法预见的创新解。 | 行为难预测:系统整体行为是多个 Agent 交互的涌现结果,调试、审计、复现问题极其困难。 |
| 去中心化扩展:新 Agent 加入只需遵循通信协议,不必修改中心节点,理论上扩展性更好。 | 消息风暴与死锁风险:无节制的对等通信会消耗大量 token,且容易出现循环依赖导致死锁。 |
生产环境选型决策树:
你的任务是否具备这些特征?
├─ 任务的步骤、依赖关系清晰,可以事先规划?
│ └─ 是 → Supervisor 模式。用 DAG 定义流程,稳定可控。
├─ 需要严格遵循合规流程、每一步都可审计?
│ └─ 是 → Supervisor 模式。集中控制 = 天然审计点。
├─ 对延迟敏感,希望尽可能并行处理?
│ └─ 是 → 优先 Supervisor,它可以轻易调度并行 Worker。
├─ 任务高度动态、需要 Agent 之间频繁交换信息或共同创作?
│ └─ 是 → 考虑 Peer-to-Peer,但要做好状态管理和冲突解决。
└─ 团队规模较小、希望快速落地第一个版本?
└─ 是 → Supervisor 模式。它是上手最快、最易维护的选择。
实战经验法则:
90% 的生产级多 Agent 系统应该从 Supervisor 模式起步。 原因很简单:它为你提供了最清晰的掌控力。先用 Supervisor 跑通业务,在运行中识别出哪些 Worker 之间有极强的实时协作需求后,再把那部分局部重构为 Peer-to-Peer 的“微协作域”,形成一种“联邦式”的混合架构。不要一上来就追求完全去中心化——那往往会导致一个不可调试的黑洞。
🏗️ 3. 用 LangGraph 实现 Supervisor 模式,关键代码怎么写?¶
LangGraph 的状态图机制天生适合 Supervisor 模式。关键在于:
-
定义一个包含
next_worker的共享状态。 -
Supervisor 节点是一个 LLM 调用,它根据当前状态决定下一个应该执行的 Worker。
-
用条件边根据
next_worker的值动态路由。
架构图(LangGraph 视角):

关键代码实现:
from typing import TypedDict, Literal, List
from langgraph.graph import StateGraph, END
from langgraph.checkpoint.memory import MemorySaver
from langchain_core.messages import HumanMessage, AIMessage
# 1. 定义全局状态
class SupervisorState(TypedDict):
messages: List[dict] # 全局对话历史
next_worker: str # Supervisor 决策:下一个 Worker 名称
task: str # 初始任务
# 2. 定义各 Worker 节点(示例简化,实际每个都是完整的 LLM 调用)
def analyst_worker(state: SupervisorState):
# 这里实际会用 LLM + 工具执行分析任务
result = f"[Analyst] 完成市场分析,发现3个关键趋势"
state["messages"].append({"role": "assistant", "name": "Analyst", "content": result})
return state
def writer_worker(state: SupervisorState):
result = f"[Writer] 根据分析报告,生成初稿..."
state["messages"].append({"role": "assistant", "name": "Writer", "content": result})
return state
def reviewer_worker(state: SupervisorState):
result = f"[Reviewer] 审核完成,通过。"
state["messages"].append({"role": "assistant", "name": "Reviewer", "content": result})
return state
# 3. 定义 Supervisor 节点(LLM 决策大脑)
def supervisor(state: SupervisorState):
# 构造决策 prompt,让 LLM 根据历史消息决定下一步
system_prompt = """
你是项目 Supervisor。根据当前任务和对话历史,决定下一步应该激活哪个 Worker。
可选 Worker:
- Analyst: 负责分析研究
- Writer: 负责撰写内容
- Reviewer: 负责审核内容
- FINISH: 任务完成,结束
请只输出下一个 Worker 的名字,不要输出其他内容。
"""
messages = [{"role": "system", "content": system_prompt}] + state["messages"]
response = llm.invoke(messages) # 假设 llm 已定义
next_action = response.content.strip()
state["next_worker"] = next_action
return state
# 4. 定义路由函数(条件边)
def route_to_worker(state: SupervisorState) -> str:
next_worker = state["next_worker"]
if next_worker == "FINISH":
return END
return next_worker
# 5. 构建图
builder = StateGraph(SupervisorState)
# 添加节点
builder.add_node("Supervisor", supervisor)
builder.add_node("Analyst", analyst_worker)
builder.add_node("Writer", writer_worker)
builder.add_node("Reviewer", reviewer_worker)
# 设置入口
builder.set_entry_point("Supervisor")
# 添加条件边:Supervisor 决策后路由到具体 Worker
builder.add_conditional_edges(
"Supervisor",
route_to_worker,
{
"Analyst": "Analyst",
"Writer": "Writer",
"Reviewer": "Reviewer",
END: END
}
)
# 添加普通边:每个 Worker 执行完后,无条件返回 Supervisor
for worker_name in ["Analyst", "Writer", "Reviewer"]:
builder.add_edge(worker_name, "Supervisor")
# 编译(可加 checkpointer 做持久化/人工介入)
app = builder.compile(checkpointer=MemorySaver())
# 6. 运行示例
config = {"configurable": {"thread_id": "1"}}
initial_state = {
"messages": [{"role": "user", "content": "分析新能源汽车市场并生成报告"}],
"next_worker": "",
"task": "分析新能源汽车市场并生成报告"
}
result = app.invoke(initial_state, config)
关键设计解读:
-
路由归 Supervisor 独有:只有 Supervisor 节点后有条件边,Worker 节点后都是无条件的回到 Supervisor。这严格保证了控制流不失控。
-
状态透明:
SupervisorState是整个图的全局状态,任何节点都能读写它。这让你可以在任何时候(如人工介入)暂停并检查当前所有信息。 -
持久化与暂停:通过
checkpointer,你可以序列化整个状态到数据库。这意味着如果 Supervisor 在等待人工审批,系统可以暂停,几小时后从断点完美恢复。 -
易于扩展:新增一个 Worker 只需 1) 实现它的节点函数,2) 在 Supervisor 的决策 prompt 里加上它的名字和能力描述,3) 在条件边的映射字典里加上它的路由,4) 添加一条它返回 Supervisor 的边。完全不需要改动其他 Worker 的逻辑。
混合扩展:在 Worker 内部使用自己的工具
每个 Worker 节点内可以有自己的工具调用循环。比如 Analyst 内部可以用 ReAct 模式调用数据库和搜索引擎,而 Supervisor 完全不关心这些细节。Supervisor 只看 Analyst 最终输出的摘要。这种分层设计是真正生产可用的架构。
多 Agent 的任务分解与动态分配策略¶
🧩 1. 静态预规划和动态分配分别是什么?¶
在多 Agent 系统中,这二者是任务分配策略的两种极端,本质区别在于 “分配决策的时间点”。
静态预规划:执行前,一次性画好整张地图。
动态分配: 执行中,一步一步问路。
静态:Plan → Assign → Execute (不可变)
动态:Sense → Decide → Act → Sense → ... (循环)
① 静态预规划¶
定义:在 Agent 开始执行之前,由一个 Planner(可以是 LLM,也可以是固定算法)根据任务描述,分解出完整的任务依赖图(DAG),并明确每个子任务由哪个 Agent 负责。执行阶段按图索骥,不再修改分配。
特点:
-
确定性:执行路径固定,易于审计、调试、复现。
-
高效:无运行时决策开销,可直接并行化无依赖的子任务。
-
僵硬:无法应对执行中出现的意外(如某个 Worker 返回异常结果,需要插入新步骤)。
代码示例:静态 DAG 定义与并行执行
import asyncio
# 任务 DAG 定义:每个任务有 id,依赖列表,执行函数
dag = {
"T1": {"deps": [], "func": agent_a_work},
"T2": {"deps": [], "func": agent_b_work},
"T3": {"deps": ["T1", "T2"], "func": agent_c_work},
"T4": {"deps": ["T3"], "func": agent_d_work},
}
async def execute_dag(dag):
results = {}
# 简单的拓扑执行:每次找出所有依赖已满足的任务并行执行
while len(results) < len(dag):
ready = [tid for tid, t in dag.items() if tid not in results
and all(d in results for d in t["deps"])]
tasks = [asyncio.create_task(dag[t]["func"](results)) for t in ready]
completed = await asyncio.gather(*tasks)
for tid, res in zip(ready, completed):
results[tid] = res
return results
这种模式非常适合流程标准化、变化极少的场景,比如金融行业的合规审批流。
② 动态分配¶
定义:由一个运行时的 Supervisor 或协商机制,在每一步根据“当前全局状态”来决定“下一个激活哪个 Agent”。它没有事先写死的完整计划,而是每一步都重新评估。
特点:
-
灵活:能处理异常、中间结果不符合预期时及时调整策略(如增加验证步骤、换 Agent)。
-
强适应性:对开放式、探索型任务效果极好,能走出设计者未预料到的路径。
-
成本与风险:每步都需要 LLM 决策(烧 Token),且可能陷入循环或做出错误路由。
动态分配的伪代码(Supervisor 循环):
while True:
next_agent = supervisor_llm(state) # 根据当前状态,LLM 决定下一步
if next_agent == "FINISH":
break
result = agents[next_agent].run(state) # 交给该 Agent 执行
state.update(result) # 更新全局状态
这适用于用户意图多变、任务结构不固定的场景,如“帮我深入研究一下这家公司,看值不值得投资”。
搭配使用:实际生产中,通常是“宏观静态,微观动态”。顶层用 DAG 划定大阶段,每个阶段内部再用动态 Supervisor 处理具体执行的不确定性。
📝 2. Supervisor 的路由 Prompt 应该怎么设计?有哪些关键要素?¶
Supervisor 的路由 Prompt 是动态分配模式的大脑。它需要具备准确理解现状 + 严格遵循输出协议的能力。设计不佳的 Prompt 会导致 Supervisor 乱路由、过早结束或死循环。
一个好的路由 Prompt 包含五个关键要素:


## 1. 角色与目标
你是任务调度 Supervisor。你的职责是根据对话历史和当前进度,决定下一步应该激活哪个专家 Agent,或者工作是否已经完成。
## 2. 可调度的 Agent 清单
你可以将任务路由给以下 Agent,请严格选择其中之一:
- **Analyst**:负责数据收集、竞品分析、信息检索。当你需要获取新信息或分析原始数据时选它。
- **Writer**:负责撰写文档、报告、邮件。当已有信息充足,需要生成文本时选它。
- **Reviewer**:负责审核内容、检查事实、提出修改意见。当有草稿需要质量把控时选它。
- **FINISH**:当任务完全达成,可以输出最终结果给用户时选择此项。
## 3. 当前任务进度
用户原始需求:{user_request}
已经完成的步骤和结果:
{completed_steps_summary}
最近一次 Agent 的输出:{last_agent_output}
## 4. 路由逻辑与约束
- 如果缺少必要信息,应优先激活 Analyst。
- 如果已有草稿但未被审核,必须激活 Reviewer。
- 如果 Reviewer 提出修改意见,应激活 Writer 进行修改。
- 只有在 Review 通过且用户需求完全满足时,才选择 FINISH。
- 禁止连续两次激活同一个 Agent,除非有明确的新信息注入。
## 5. 强制输出格式
请只输出下一个要激活的 Agent 名称,不要输出任何其他内容:
Analyst | Writer | Reviewer | FINISH
为什么这样设计?
-
角色与目标:设定了 Supervisor 的思考边界——它是个调度员,不是执行者。
-
Agent 清单:必须清晰、无歧义。每一项都要有明确的触发条件(“当你需要……时选它”),而不是简单的功能描述。这直接决定了路由准确性。
-
当前任务进度:这是 Supervisor 的“眼睛”。不要只喂原始的用户请求,必须包含已完成步骤的压缩摘要和上一步的输出,否则 Supervisor 会丧失对进度的感知。
-
路由逻辑与约束:这是硬编码的业务规则,用来纠正 LLM 的常见错误(如过早结束、喜欢重复调用同一个工具)。这些规则是你的“方向盘”。
-
强制输出格式:越简洁越好。不要让它输出 JSON 或解释,一个单词的决策最不容易出错。在代码里用
response.content.strip()直接拿。
迭代优化技巧:
-
如果发现 Supervisor 频繁过早 FINISH,在约束中加一句“在给出 FINISH 前,请确认 {checklist}”。
-
如果总在 A 和 B 之间来回震荡,增加“禁止连续激活同一 Agent”的硬约束,或在状态中记录
last_agent,在代码层拦截。
一个好的路由 Prompt,50% 在写 Agent 描述,30% 在制定约束,20% 在维护状态摘要。把这三块做扎实,你就能用最小的 Token 开销换来最稳的路由表现。
⚙️ 3. 一个复杂分析任务有 4 个子任务,其中 T1、T2 无依赖,T3 依赖 T1/T2,T4 依赖 T3,如何调度?¶
这是一个典型的 DAG 调度场景,任务依赖关系是:
最优策略是用静态预规划定义依赖,用动态执行器并行调度。
为什么不用纯 Supervisor 动态路由?
这种依赖关系明确、不会变化的任务,用 Supervisor 动态决策是一种浪费。每一轮让 LLM 判断“现在该跑 T3 了吗”既贵又不稳定。最好的方式是:代码层管理依赖图,LLM 只负责每个节点内部的“做事”。
实现方案:基于 asyncio 的 DAG 执行器
import asyncio
from dataclasses import dataclass, field
from typing import List, Dict, Any, Callable
@dataclass
class Task:
name: str
deps: List[str]
executor: Callable # 实际执行该任务的函数,内部可以调用 LLM
args: Dict[str, Any] = field(default_factory=dict)
class DAGExecutor:
def __init__(self, tasks: List[Task]):
self.tasks = {t.name: t for t in tasks}
self.results = {}
self._validate_dag()
def _validate_dag(self):
# 检查所有依赖是否已定义
for t in self.tasks.values():
for dep in t.deps:
if dep not in self.tasks:
raise ValueError(f"Task {t.name} 依赖的 {dep} 不存在")
async def run(self):
pending = set(self.tasks.keys())
while pending:
# 找出所有依赖已满足的任务
ready = [name for name in pending
if all(dep in self.results for dep in self.tasks[name].deps)]
if not ready:
# 理论上不会发生,除非有循环依赖或死锁
raise RuntimeError("调度死锁,无就绪任务")
# 并行执行所有就绪任务
tasks_coro = []
for name in ready:
task = self.tasks[name]
# 将前置任务的结果注入参数
deps_results = {dep: self.results[dep] for dep in task.deps}
tasks_coro.append(task.executor(deps_results=deps_results, **task.args))
ready_results = await asyncio.gather(*tasks_coro, return_exceptions=True)
# 保存结果
for name, res in zip(ready, ready_results):
if isinstance(res, Exception):
print(f"Task {name} 失败: {res}")
# 可以在此决定是重试、降级还是整体失败
self.results[name] = None # 标记失败
else:
self.results[name] = res
pending.remove(name)
return self.results
# --- 具体任务函数示例 ---
async def execute_T1(deps_results, **kwargs):
# 实际调用 Analyst Agent
return await analyst_agent.run("分析竞品A的市场份额")
async def execute_T2(deps_results, **kwargs):
return await analyst_agent.run("分析竞品B的市场份额")
async def execute_T3(deps_results, **kwargs):
t1_result = deps_results["T1"]
t2_result = deps_results["T2"]
# 将T1/T2结果汇总,交给 Writer Agent
return await writer_agent.run(f"基于以下数据生成对比分析:\nT1: {t1_result}\nT2: {t2_result}")
async def execute_T4(deps_results, **kwargs):
t3_result = deps_results["T3"]
return await reviewer_agent.run(f"审核以下报告:\n{t3_result}")
# --- 构建并运行 ---
dag = [
Task(name="T1", deps=[], executor=execute_T1),
Task(name="T2", deps=[], executor=execute_T2),
Task(name="T3", deps=["T1", "T2"], executor=execute_T3),
Task(name="T4", deps=["T3"], executor=execute_T4),
]
executor = DAGExecutor(dag)
final_results = asyncio.run(executor.run())
这段代码如何体现最优调度?
-
最大化并行:
T1和T2在第一轮循环中同时被asyncio.gather并行执行。如果每个耗时 10 秒,总等待只有 10 秒,而不是 20 秒。 -
自动依赖管理:
T3只有等到T1和T2都完成(它们的 key 都在self.results中)才会被加入ready队列。这完全由代码保证,不需要 LLM 参与。 -
异常隔离:如果
T1失败,self.results["T1"]被设为None,T3依然会被执行(因为它只检查依赖的 key 是否存在)。你可以在execute_T3内部检测输入是否为None来决定是降级输出还是跳过。
如果其中一步需要动态决策怎么办?
假设 T3 执行完后,发现数据冲突,需要额外增加一个 T3.5(重新核实)才能继续 T4。这时 DAG 是静态的,无法自动插入新节点。解决方案是:在 DAGExecutor 中增加一个动态回调钩子。当任务完成时,允许该任务的执行器返回一个“指令”,比如 INSERT_TASK,然后 DAG 执行器动态修改任务图后再继续调度。
这种“核心静态 + 钩子动态”的混合策略,是生产环境中最实用的模式:在可预见的范围内享受确定性的高效,在不可预见的地方为灵活性开一个口子。
6.2 结果处理与质量优化¶
多 Agent 的结果聚合:投票、加权、LLM 综合¶
1、基础题:多 Agent 系统中常见的结果聚合方式有哪些?¶
难度级别:⭐⭐(结果聚合策略、多数投票、LLM 综合)
常见的结果聚合策略有三种:多数投票(让多个 Agent 独立给出答案,取出现次数最多的)、加权聚合(根据每个 Agent 的历史表现或模型能力动态分配权重)、LLM 综合(用一个聚合 Agent 读取所有子 Agent 的输出,综合生成最终答案)。三种方式在准确性、成本、延迟上的权衡差异很大,需要根据任务类型选择。
2、进阶题:多数投票、加权聚合、LLM 综合各自适用什么场景?Mixture of Agents 的思路是什么?¶
难度级别:⭐⭐⭐(聚合策略适用边界、MoA 架构、成本与质量权衡)
1️⃣ Common Answer
多数投票就是取最多的答案,适合有对错的问题。加权就是不同 Agent 权重不一样。LLM 综合是用一个 LLM 来汇总,适合生成文本。Mixture of Agents 就是多个模型先给答案,再用一个模型来综合,效果会更好。
2️⃣ Impressive Answer
我会从三种策略的适用边界和 MoA 的系统化思路来分析:
-
多数投票适合答案空间有限的任务。比如分类、是/否判断、数学推理等有确定性答案的场景。Self-Consistency 就是这个思路的典型应用——同一问题让 LLM 推理多次,投票过滤偶发错误。但对于开放式生成任务,多个答案语义等价但形式各异,简单投票根本无法工作。
-
LLM 综合最灵活但成本最高。能处理矛盾输出、给出有理有据的综合判断,但会额外增加一次 LLM 调用,且输入 Token 随子 Agent 数量线性增长,成本要仔细评估。
-
MoA 是 LLM 综合的系统化版本。来自 Together AI 的研究,分两层:Proposer 层用多个不同 LLM(GPT-4、Claude、Llama)并行生成多样化候选答案,Aggregator 层用强模型综合输出。核心洞见是不同模型有不同偏差,输出具有多样性,综合后能互补,实验表明 MoA 在多个 Benchmark 上能超越单个最强模型。工程上 Proposer 层可以并行化以优化延迟,但总成本仍然较高,要评估 ROI。
3️⃣ Key Differences
| 维度 | Common Answer | Impressive Answer |
|---|---|---|
| 技术深度 | 描述三种方法是什么 | 深入分析每种方法的适用条件、局限性和成本影响 |
| 实践经验 | 无具体工程取舍 | 点出 Token 随子 Agent 线性增长、MoA 延迟优化等实际问题 |
| 思考维度 | 把三种方法并列对比 | 梳理方法演进逻辑,引入 MoA 研究成果 |
| 给面试官的印象 | 知道有这三种方法 | 理解背后的设计权衡,了解前沿研究,有成本意识 |
3、场景题:同一问题让 3 个 Agent 给出了不同的分析结论,如何做聚合?¶
难度级别:⭐⭐(聚合策略选择、矛盾输出处理)
1️⃣ Common Answer
可以用投票的方式,选出两个 Agent 都同意的答案,或者直接让一个更强的模型来做最终判断。
2️⃣ Impressive Answer
3 个 Agent 给出不同结论,说明任务是开放式分析类,不能用简单多数投票(答案形式各异,没有"多数"可言)。正确做法是 LLM 综合:把三个 Agent 的输出都传给 Aggregator Agent,让它理解各方论点、识别分歧点、综合给出有依据的最终结论。
Aggregator 的 Prompt 设计很关键:要求它显式指出各 Agent 的共识部分和分歧部分,并说明最终结论采纳哪方观点及原因。这样输出不只是"综合结果",还有可追踪的推理链路,便于后续审核。
3️⃣ Key Differences
| 维度 | Common Answer | Impressive Answer |
|---|---|---|
| 技术深度 | 提到投票或用强模型判断,无进一步分析 | 解释为何开放式任务不适合投票,LLM 综合更合适 |
| 实践经验 | 无 Aggregator Prompt 设计思考 | 提出 Aggregator Prompt 需要显式处理分歧、保留推理链路 |
| 思考维度 | 选一种方法 | 先分析任务类型,再选择适合的聚合策略 |
| 给面试官的印象 | 有基本思路 | 有聚合策略的工程设计经验 |
4、容易一起考的题¶
| 关联题 | 和本题的关系 |
|---|---|
| Self-Consistency 是什么?和多数投票有什么关系? | Self-Consistency 是多数投票在 LLM 推理场景下的具体应用 |
| 如何评估多 Agent 系统的输出质量? | 结果聚合之后需要有质量评估手段,两个问题紧密关联 |
| MoA 和 Ensemble 方法有什么关系? | MoA 是机器学习 Ensemble 思想在 LLM 多 Agent 场景下的延伸 |
多 Agent 通信的消息格式设计¶
1、基础题:Agent 之间通信的消息格式应该包含哪些基本字段?¶
难度级别:⭐⭐(结构化消息设计、链路追踪、task_id 的作用)
一个生产可用的 Agent 消息结构至少应包含:task_id(贯穿整个任务生命周期,用于追踪完整链路)、message_id(消息唯一标识)、sender/receiver(发送方/接收方 Agent 名称)、content(结构化消息内容)、message_type(request/response/error/status)、timestamp(用于排序和超时检测)。其中 task_id 是最关键的字段——所有相关 Agent 调用都携带同一个 task_id,才能在监控系统中把散落各处的调用聚合成一条完整链路。
2、进阶题:LangGraph SharedState 共享内存模式和消息队列解耦模式各有什么优劣?如何选型?¶
难度级别:⭐⭐(LangGraph SharedState、消息队列、架构选型)
1️⃣ Common Answer
LangGraph 用一个共享的 State 对象来传递信息,所有 Agent 都可以读写。消息队列是把消息发到队列里,其他 Agent 从队列取,可以解耦。两种方式都可以用,看具体情况选择。
2️⃣ Impressive Answer
我会从两种模式的核心机制和适用边界来分析:
-
LangGraph SharedState 模式:所有 Agent 节点共享同一个 State 对象,每个节点读取 State、执行逻辑、返回增量更新,通过 reducer 合并。优势是简单直接、内置持久化(checkpointer)、天然支持 human-in-the-loop。但有两个工程限制:所有 Agent 都能读到全部数据,无法做权限隔离;State 对象随任务进行持续膨胀,可能触发 Token 超限问题。
-
消息队列解耦模式:适合 Agent 跨服务、跨进程通信的场景,用 Redis Stream 或 Kafka 作为消息总线,Agent 订阅自己的 topic。优势是松耦合、独立扩缩容、消息持久化支持断点续处理;代价是引入外部依赖、运维复杂度上升、调试时需要跨多个系统查日志。
-
选型建议:单进程、任务流程固定的多 Agent 用 LangGraph SharedState;跨服务、需要高可用、Agent 独立部署的场景用消息队列。
3️⃣ Key Differences
| 维度 | Common Answer | Impressive Answer |
|---|---|---|
| 技术深度 | 描述两种方式是什么 | 深入分析 SharedState 的权限问题和 Token 膨胀风险 |
| 实践经验 | 知道两种方式存在 | 给出具体的工程局限和对应解决思路 |
| 思考维度 | 描述"是什么" | 给出"什么场景选什么"的具体决策标准 |
| 给面试官的印象 | 了解基本概念 | 有消息系统设计经验,考虑到权限、追踪、运维等多个维度 |
3、场景题:多 Agent 系统上线后,发现某个任务链路出错但很难定位是哪个 Agent 出了问题,怎么解决?¶
难度级别:⭐⭐(链路追踪、task_id 设计、可观测性)
1️⃣ Common Answer
可以在每个 Agent 里面加日志,出错的时候打印出来,然后去查日志就能找到是哪里出了问题。
2️⃣ Impressive Answer
这是多 Agent 系统可观测性的核心问题,根本原因是消息设计时没有统一的追踪 ID。解决方案分两层:
第一层是消息设计层面,强制每条 Agent 消息携带 task_id 和 parent_message_id,形成完整的消息追踪树。所有 Agent 的日志都带上 task_id,这样在日志系统里只需过滤 task_id 就能聚合出一条任务的完整执行链路。
第二层是工具层面,接入 LangSmith 或 OpenTelemetry,自动采集每个 Agent 节点的输入输出、耗时、Token 用量,在 UI 上直接可视化完整的调用链路,定位问题从"查日志"变成"看 Trace 图"。
3️⃣ Key Differences
| 维度 | Common Answer | Impressive Answer |
|---|---|---|
| 技术深度 | 只想到加日志 | 从消息设计和工具接入两个层面系统解决 |
| 实践经验 | 无具体追踪设计 | 提出 task_id + parent_message_id 的消息追踪树设计 |
| 思考维度 | 事后排查 | 事前设计可观测性基础设施,从根本上解决问题 |
| 给面试官的印象 | 有基本调试思路 | 有多 Agent 系统可观测性的工程经验 |
4、容易一起考的题¶
| 关联题 | 和本题的关系 |
|---|---|
| LangSmith 是什么?在多 Agent 系统中如何使用? | LangSmith 是 LangGraph 系统的官方可观测性工具,与消息追踪紧密关联 |
| OpenTelemetry 如何接入 AI Agent 系统? | 消息队列模式下跨服务追踪的标准解决方案 |
| 多 Agent 系统如何做权限隔离? | SharedState 模式的核心局限之一,延伸考察安全设计 |
多 Agent 系统的成本控制:防止 LLM 调用爆炸¶
1、基础题:多 Agent 系统为什么比单 Agent 更容易出现成本失控?¶
难度级别:⭐(成本构成理解、Token 计费原理)
多 Agent 系统每个 Agent 调用都独立消耗 Token,成本是单 Agent 的 N 倍起步。如果 Agent 之间形成循环调用或无限重试,Token 消耗会指数级增长。没有预算上限的系统,一个复杂任务可能烧掉几十美元。
2、进阶题:多 Agent 系统的 LLM 调用成本如何控制?有哪些防止调用次数爆炸的有效机制?¶
难度级别:⭐⭐⭐(Agent 调用轮次上限、上下文复用、早停机制、模型分级)
1️⃣ Common Answer
每个 Agent 都要消耗 Token,所以要控制成本。可以设置最大调用次数,防止死循环。对不变的内容做缓存。结果差不多了就提前停。另外可以用便宜的模型,不是每个 Agent 都要用最强的。
2️⃣ Impressive Answer
我会从调用次数、Token 量、模型选择、监控告警四个维度来回答这个问题:
-
调用轮次上限。LangGraph 通过
recursion_limit设置硬上限,但仅靠这个不够——还需要在 Supervisor 的路由 Prompt 里加规则:同一个 Agent 连续调用超过 3 次仍无进展,就返回当前最优结果,防止无效循环。 -
早停机制。不等跑完所有轮次就提前结束。判断信号有三类:置信度达阈值(Supervisor 评判"已足够好")、任务完成信号(Agent 返回
{"status": "complete"})、结果收敛(连续两轮输出相似度 > 0.95)。 -
上下文复用。这是最容易被忽视但效果显著的点。子 Agent 不应每次独立做 RAG 检索,应该复用父 Agent 已检索的上下文。对不变的系统规则、API 文档,用 OpenAI Prompt Caching 或 Anthropic Cache Control 缓存 System Prompt,可以节省 50-90% 的 Token 成本。
-
模型分级。简单路由和格式转换用 GPT-4o-mini / Claude Haiku;核心推理和代码生成用 GPT-4o / Claude Sonnet;复杂分析和最终综合用 o1 / Claude Opus。在生产中还需设置每次任务的 Token 预算上限,超出时自动降级或停止执行。
3️⃣ Key Differences
| 维度 | Common Answer | Impressive Answer |
|---|---|---|
| 技术深度 | 说了三个方向,无实现细节 | 给出每个机制的具体触发条件和实现思路 |
| 实践经验 | 未提上下文复用这一关键优化 | 明确指出子 Agent 重复检索是常见浪费点并给出方案 |
| 思考维度 | 只考虑了调用次数和模型选择 | 从次数、Token 量、模型选择、监控告警四个维度系统性覆盖 |
| 给面试官的印象 | 知道有成本问题 | 有生产级成本控制经验,考虑周全 |
3、场景题:生产环境中一个多 Agent 任务突然 Token 消耗飙升,如何快速定位并止损?¶
难度级别:⭐⭐⭐(成本监控、告警机制、预算守卫)
1️⃣ Common Answer
可以去日志里看一下哪个 Agent 调用次数多,然后降低调用频率。或者换个便宜的模型。如果还是太贵就直接停掉任务。
2️⃣ Impressive Answer
定位和止损需要分两个层次应对:
实时止损:在每次 LLM 调用后更新累计消耗,设置预算守卫(CostGuard),一旦超出阈值立即抛出异常中断任务,防止继续烧钱。同时配置告警(接飞书或 PagerDuty)让人工第一时间感知。
事后定位:通过 LangSmith 或自建 Trace 系统,按 Trace ID 聚合每次任务的各 Agent Token 消耗明细,对比历史基线找出异常 Agent。常见原因是某个 Agent 陷入了重试循环,或上下文窗口被无关内容撑大,定位后针对性修复(加早停、清理上下文)。
class CostGuard:
def __init__(self, budget_usd: float):
self.budget = budget_usd
self.consumed = 0.0
def check(self, tokens: int, model: str):
cost = tokens / 1_000_000 * MODEL_PRICE[model]
self.consumed += cost
if self.consumed > self.budget:
raise BudgetExceededError(f"已消耗 ${self.consumed:.2f},超出预算")
3️⃣ Key Differences
| 维度 | Common Answer | Impressive Answer |
|---|---|---|
| 应对策略 | 被动事后处理 | 实时止损 + 事后溯因双层应对 |
| 工具使用 | 只提到看日志 | 明确用 Trace ID 聚合 + LangSmith 定位异常 Agent |
| 思考深度 | 只知道停或换模型 | 能分析根因(重试循环、上下文膨胀)并给出修复方向 |
4、容易一起考的题¶
| 关联题 | 和本题的关系 |
|---|---|
| LangGraph 的 recursion_limit 是什么?如何配置? | 防止调用爆炸的基础机制,是成本控制的第一道防线 |
| OpenAI Prompt Caching 的原理和使用条件? | 上下文复用的核心实现手段,直接影响 Token 成本 |
| 多 Agent 系统的可观测性如何建设? | 成本监控依赖完善的 Trace 体系,两者深度绑定 |
6.3 可观测性、应用与安全¶
多 Agent 的可观测性:跨 Agent 调用链追踪¶
1、基础题:什么是 Trace ID?在多 Agent 系统中为什么必须有统一的 Trace ID?¶
难度级别:⭐(可观测性基础概念、分布式追踪原理)
Trace ID 是一次任务从开始到结束的唯一标识,所有 Agent 调用都携带同一个 Trace ID,形成完整的调用链。多 Agent 系统一次任务可能触发十几次 LLM 调用分散在不同 Agent,没有统一 Trace ID 就无法把它们关联起来,出了问题根本没法定位。
2、进阶题:多 Agent 系统的可观测性如何建设?如何实现跨 Agent 的调用链追踪和 Token 消耗汇总?LangSmith 在其中扮演什么角色?¶
难度级别:⭐⭐⭐(统一 Trace ID 传播、LangSmith 父子 Trace 结构、跨 Agent Token 消耗汇总)
1️⃣ Common Answer
多 Agent 的调用链很复杂,要给每个任务生成一个唯一 ID,所有 Agent 调用时都带上这个 ID,这样就能在日志里找到同一个任务的所有调用。LangSmith 是 LangChain 的可观测性工具,能记录 LLM 的输入输出和 Token 消耗。
2️⃣ Impressive Answer
我会从 Trace ID 传播、LangSmith 父子结构、Token 汇总三个层次来回答:
-
Trace ID 的正确传播方式。Trace ID 必须在任务入口统一生成,通过 Python 的
ContextVar在异步环境中安全透传,不能让每个 Agent 自己生成——否则就失去了关联能力。每个子调用生成自己的 Span ID 并记录父 Span ID,整体形成树状结构。 -
LangSmith 的父子 Trace 结构。给函数加
@traceable装饰器,LangSmith 会自动识别调用栈中的嵌套关系,构建父子 Trace 树。在 UI 上可以直接看到完整调用链、每个节点的输入输出、耗时和 Token 消耗,调试效率远高于翻日志。 -
跨 Agent Token 消耗汇总。通过 LangSmith Client 的
list_runsAPI,按 Trace ID 聚合所有 run 的usage_metadata,可以得到每个 Agent 的分项消耗和全局总量,用于成本分析和异常告警。生产中还需要把指标推送到 Prometheus + Grafana,并对失败 Trace 做 100% 采样、成功 Trace 做 10% 采样,平衡存储成本和调试需求。
3️⃣ Key Differences
| 维度 | Common Answer | Impressive Answer |
|---|---|---|
| 技术深度 | 知道需要 Trace ID 和 LangSmith | 详细说明 ContextVar 传播机制和父子 Trace 树状结构 |
| 实践经验 | 无具体实现 | 给出 LangSmith API 聚合 Token 的完整思路和采样策略 |
| 思考维度 | 只提了追踪 | 涵盖追踪、汇总、告警、采样策略等完整监控体系 |
| 给面试官的印象 | 了解工具名称 | 有生产级可观测性建设经验,懂得在成本和完整性间取舍 |
3、场景题:线上某个多 Agent 任务失败率突然升高,如何用可观测性手段快速定位问题?¶
难度级别:⭐⭐⭐(Trace 分析、错误采样、调用链定位)
1️⃣ Common Answer
去看日志,找报错信息。如果有 LangSmith 就去上面看一下哪个 Agent 出错了,然后针对那个 Agent 去排查。
2️⃣ Impressive Answer
利用完善的 Trace 体系,定位分三步走:
首先,通过 Grafana 看板定位失败率上升的时间窗口,对比该窗口内的 Trace 和正常时段的基线——找出失败 Trace 集中在哪个 Agent 节点(Span)。
其次,对失败的 Trace 做 100% 采样,在 LangSmith 里打开具体 Trace,查看每个 Span 的输入输出,重点看异常 Span 的 LLM 输入是否有上下文截断、工具调用参数是否异常、返回的 JSON 是否解析失败。
最后,如果是偶发性问题,检查是否有外部依赖(如检索服务、工具 API)的延迟或错误率同步升高,排除上下游影响,确认问题根因后修复并验证 Trace 恢复正常。
3️⃣ Key Differences
| 维度 | Common Answer | Impressive Answer |
|---|---|---|
| 定位路径 | 直接看日志,无系统方法 | 从指标 → Trace → Span 逐层收窄,有方法论 |
| 工具运用 | 只提到 LangSmith 看错误 | 结合 Grafana 时序分析 + LangSmith Span 输入输出检查 |
| 思考广度 | 只看 Agent 本身 | 考虑到外部依赖(工具 API、检索服务)的联动影响 |
4、容易一起考的题¶
| 关联题 | 和本题的关系 |
|---|---|
| Python ContextVar 的原理和使用场景? | Trace ID 在异步环境中安全传播的核心机制 |
| LangSmith 的 @traceable 装饰器是如何工作的? | 构建父子 Trace 结构的关键工具,直接影响调用链的可视化 |
| 多 Agent 系统的成本控制如何实现? | Token 消耗汇总是成本控制的前提,两者共享同一套 Trace 基础设施 |
Debate 模式:多 Agent 辩论提升推理质量¶
1、基础题:什么是多 Agent 的 Debate 模式?它的核心思想来自哪里?¶
难度级别:⭐(Debate 模式概念、Society of Mind 理论)
Debate 模式是让多个 Agent 对同一问题独立给出答案,再相互批判对方观点,最后由仲裁者综合所有意见给出最终答案。思想来自 Marvin Minsky 的 Society of Mind 理论——复杂智能从简单个体的竞争与协作中涌现,多个 LLM 作为独立思维者,通过竞争性对话提升集体推理质量。
2、进阶题:Debate 模式是如何提升推理质量的?最终仲裁者应该如何设计?¶
难度级别:⭐⭐⭐(Society of Mind 思想、独立生成防止羊群效应、仲裁者角色设计)
1️⃣ Common Answer
让多个 Agent 各自回答同一个问题,然后互相批评,最后一个 Agent 综合大家的意见给出最终答案。这样可以发现单个 Agent 的错误和盲点,提升准确性。最终的仲裁者就是汇总意见的那个 Agent。
2️⃣ Impressive Answer
我会从有效性原理、流程关键设计点、仲裁者设计三个角度来回答:
-
为什么有效。单个 LLM 生成时容易陷入"自洽的错误"——它会对自己的错误推理产生高置信度。多个独立 Agent 的答案具有多样性,交叉批判能暴露单个 Agent 忽视的反例和漏洞。MIT 等机构的研究证明这在数学推理、常识问答上显著优于单次生成。
-
流程关键设计点。Round 1 必须独立生成,绝对不能让 Agent 看到其他 Agent 的答案——否则会产生羊群效应,第一个 Agent 的答案会主导后续所有人,失去多样性,整个 Debate 的价值就归零了。Round 2 开始才进行交叉批判,每个 Agent 读取其他所有 Agent 的答案并发表批评意见,可以并行执行提升效率。
-
仲裁者的设计。仲裁者不能简单"取多数票",而是要做论据质量评估——优先支持论证更充分、逻辑更严密的立场,识别并排除明显错误的论点,并说明为什么采纳某方观点(可解释性)。仲裁者是整个流程认知要求最高的环节,必须用能力最强的模型。此外,Debate 的成本是参与 Agent 数量 × 轮次的倍数,不适合简单问题,最适合高风险决策和容易出现幻觉的复杂推理场景。
3️⃣ Key Differences
| 维度 | Common Answer | Impressive Answer |
|---|---|---|
| 技术深度 | 描述了流程,未解释为何有效 | 从 Society of Mind 理论出发解释多样性和竞争带来的质量提升原理 |
| 实践经验 | 无实现细节 | 强调 Round 1 必须独立生成这一关键设计点,并解释违反后的后果 |
| 思考维度 | 未提适用边界和成本 | 分析仲裁者设计要点,给出成本-收益的适用边界 |
| 给面试官的印象 | 了解概念 | 理解论文背景,有工程化落地经验,知道细节陷阱 |
3、场景题:一个法律合同审查系统想引入 Debate 模式,但预算有限,如何在控制成本的前提下实现?¶
难度级别:⭐⭐⭐(Debate 成本优化、适用边界、模型分级)
1️⃣ Common Answer
可以少用几个 Agent,比如只用两个 Agent 辩论一轮,不用辩论太多轮。便宜的地方用便宜的模型,最后汇总的时候用好一点的模型。
2️⃣ Impressive Answer
在预算有限的场景下,Debate 模式的成本优化有三个方向:
选择性触发:不是所有合同条款都需要辩论。对风险评分低的标准条款直接用单 Agent 处理,只对高风险条款(如违约责任、排他协议)触发 Debate,减少 70% 以上的无效辩论成本。
模型分级:辩论 Agent 用中等能力模型(如 Claude Sonnet)保证多样性,仲裁者用最强模型(如 Claude Opus)做最终判断。这样在质量和成本之间取得平衡。
轮次控制:法律场景通常一轮批判已经足够暴露主要分歧,不需要像数学推理那样多轮迭代。固定为"1 轮独立生成 + 1 轮交叉批判 + 仲裁"的三步结构,并设置单次任务的 Token 预算上限,超出时降级为单 Agent 处理。
3️⃣ Key Differences
| 维度 | Common Answer | Impressive Answer |
|---|---|---|
| 成本控制策略 | 只想到减少 Agent 数量和轮次 | 从选择性触发、模型分级、轮次控制三个维度系统优化 |
| 业务理解 | 没有结合法律场景的特点 | 识别高/低风险条款的差异,做差异化处理 |
| 兜底设计 | 无 | 超出预算时自动降级为单 Agent,保证服务可用性 |
4、容易一起考的题¶
| 关联题 | 和本题的关系 |
|---|---|
| Society of Mind 理论是什么?对 AI Agent 设计有什么启发? | Debate 模式的理论基础,理解原理才能设计好系统 |
| 多 Agent 系统的成本控制有哪些手段? | Debate 模式成本是普通多 Agent 的倍数,成本控制是必配能力 |
| LangGraph 如何实现并行节点执行? | Debate Round 1 独立生成需要并行执行,LangGraph 的 Send API 是关键实现手段 |
多 Agent 软件开发系统设计(需求/设计/编码/测试)¶
1、基础题:MetaGPT 模拟软件公司是什么思路?和普通的单 Agent 代码生成有什么本质区别?¶
难度级别:⭐(MetaGPT 概念、角色化 Agent 设计)
MetaGPT 的思路是把软件公司的角色(PM、架构师、程序员、QA)映射为不同的 Agent,每个 Agent 有明确的职责,输出结构化文档(PRD、架构设计、代码、测试报告),上游的输出是下游的输入,形成类流水线的工作流。和单 Agent 代码生成的本质区别是:职责分离 + 结构化中间产物,每个环节有专门的 Agent 把关,减少单点失败的影响。
2、进阶题:如果用多 Agent 系统来模拟软件开发流程,如何设计各个角色 Agent 的职责划分?人工审查节点应该在哪些关键环节介入?¶
难度级别:⭐⭐⭐(各角色 Agent 职责划分、MetaGPT/AutoGen 实现参考、人工审查节点介入时机)
1️⃣ Common Answer
可以设计产品经理 Agent 理解需求、架构师 Agent 做设计、程序员 Agent 写代码、测试 Agent 写测试用例,按顺序依次执行,前一个的输出是后一个的输入。MetaGPT 就是这个思路。人工可以在需求确认和代码审查环节介入。
2️⃣ Impressive Answer
我会从角色职责划分、工作流设计、人工介入节点三个角度来回答:
-
角色职责划分。六个角色各有明确的输入输出:PM Agent(用户需求 → 结构化 PRD)、Architect Agent(PRD → 架构设计文档 + API Schema)、Developer Agent(架构文档 + 模块分配 → 代码,可按模块并行)、Code Reviewer Agent(代码 → Review 意见,检查规范和安全)、QA Agent(代码 + PRD → 测试报告,基于验收标准生成用例)、DevOps Agent(代码 + 架构文档 → 部署配置)。
-
人工审查节点。有四个必须介入的关键节点——需求确认(PRD 完成后,AI 对需求的理解可能有偏差,是整个开发的起点,理解错了后面全错,成本最高);架构评审(架构决策影响深远,技术债往往源于此);代码合并前(不用每行都看,但对安全和核心业务逻辑做抽样审查);上线审批(最后的门控)。人工介入太多失去自动化价值,太少风险不可控,四个关键节点是经验上的合理平衡点。
-
LangGraph 实现 Human-in-the-Loop。用
interrupt_before在指定节点前自动暂停,配合MemorySaver做状态持久化,人工审查期间任务挂起。审查完成后调用update_state写入反馈,再用invoke(None, thread_id)从断点继续,整个流程对任务透明。关于框架选型,MetaGPT 更像"刚性流水线",输出格式有严格约束,适合流程标准化场景;AutoGen 更像"动态协商",灵活但难预测。生产中我倾向于 MetaGPT 的思路——强制结构化输出,减少因格式混乱导致的链路中断。
3️⃣ Key Differences
| 维度 | Common Answer | Impressive Answer |
|---|---|---|
| 技术深度 | 列举角色,描述串行流程 | 给出完整工作流图,分析哪些环节可并行,以及 interrupt_before 的具体用法 |
| 实践经验 | 人工节点只笼统提了一下 | 明确给出 4 个必须介入的关键节点,并解释每个节点介入的风险理由 |
| 思考维度 | 未对比 MetaGPT 和 AutoGen | 对比两种框架设计哲学,给出选型建议 |
| 给面试官的印象 | 知道有这个应用方向 | 有系统设计能力,能权衡自动化和人工把关的边界 |
3、场景题:多 Agent 软件开发系统上线后,代码质量仍然不稳定,如何改进?¶
难度级别:⭐⭐⭐(质量保证机制、反馈循环、Agent 能力边界)
1️⃣ Common Answer
可以让测试 Agent 多写几个测试用例,或者再加一个 Agent 专门做代码审查。如果代码质量不行就让程序员 Agent 重新生成。
2️⃣ Impressive Answer
代码质量不稳定通常有三个根因,对应三个改进方向:
结构化输出约束:如果 Developer Agent 的输出格式不一致(如有时输出 Markdown 有时是纯文本),下游 Code Reviewer 解析失败就会跳过审查。解决方案是强制 Developer Agent 输出符合预定义 Schema 的结构化格式,任何不合规的输出在进入下游前就被拦截并要求重生成。
反馈循环设计:Code Reviewer → Developer 的修复循环需要设置退出条件,否则会无限循环。建议设置最大修复轮次(如 3 轮),超出后人工介入判断是 Agent 能力边界还是需求本身有歧义。
质量基准测试:定期用标准化的测试用例集评估各 Agent 的输出质量,监控 QA Agent 的测试通过率趋势。一旦通过率下滑,及时排查是上游 Architect Agent 的设计文档质量下降还是 LLM 版本更新导致输出风格变化。
3️⃣ Key Differences
| 维度 | Common Answer | Impressive Answer |
|---|---|---|
| 问题诊断 | 直接加 Agent 或重新生成,未分析根因 | 识别三类根因并对应三个改进方向 |
| 反馈机制 | 只提到重新生成,无退出条件 | 明确修复循环的退出条件和人工介入触发点 |
| 系统思维 | 局部修补 | 从结构化约束、循环设计、质量监控三层系统改进 |
4、容易一起考的题¶
| 关联题 | 和本题的关系 |
|---|---|
| LangGraph 的 interrupt_before 和 Human-in-the-Loop 如何实现? | 人工审查节点的核心实现机制,是软件开发系统中质量把关的关键 |
| MetaGPT 和 AutoGen 的设计哲学有什么区别? | 两种框架代表不同的多 Agent 协作范式,直接影响软件开发系统的架构选型 |
| 多 Agent 系统如何控制 LLM 调用成本? | 软件开发系统角色多、链路长,成本控制是绕不开的工程问题 |
多 Agent 的安全边界:防止 Agent 间数据越权¶
1、基础题:什么是最小权限原则?在多 Agent 系统中如何理解它?¶
难度级别:⭐(最小权限原则概念、Agent 工具白名单)
最小权限原则是指每个 Agent 只能访问完成其任务所必需的工具和数据,不能多给。在多 Agent 系统中,不同 Agent 的职责不同,不能给所有 Agent 一个通用工具包,而应该为每个 Agent 配置工具白名单。一个专门做格式转换的 Agent 不应该有数据库读写权限,哪怕它从不主动使用。
2、进阶题:多 Agent 系统中如何防止 Agent 间的数据越权访问?工具权限隔离和敏感数据保护应该如何设计?¶
难度级别:⭐⭐⭐(Agent 权限隔离设计、工具访问白名单、敏感数据不传递给不可信 Agent、输出内容安全过滤)
1️⃣ Common Answer
可以给每个 Agent 设置工具白名单,只让它用被授权的工具。对于敏感数据,在传递前做脱敏处理,比如把手机号、身份证号替换掉。不同 Agent 应该按最小权限原则配置。
2️⃣ Impressive Answer
我会用"防御纵深"框架来组织这个问题,从外到内分四层设防:
-
权限控制层(最外层)。用集中式工具注册中心管理所有 Agent 的工具权限,通过
grant_tools为每个 Agent 显式授权,get_tools_for_agent在运行时动态返回该 Agent 的合法工具列表。任何不在白名单内的工具调用直接拒绝,不给任何 Agent 通用工具包。 -
数据脱敏层(传递层)。对数据做三级分类:L0 公开数据可以传给任何 Agent;L1 内部数据只给受信 Agent;L2 敏感数据(PII、API Key、密码)只在专属 Agent 内部处理,绝不作为消息内容传递。消息传递前经过 DataSanitizer 自动正则脱敏。关键原则是 Sensitive by Design——在架构层阻止敏感数据流动,而不是靠运行时过滤兜底。
-
工具调用层(工具层)。能执行代码、写文件、发网络请求的工具是最大风险点。安全工具执行器需要做两道检查:先验证 Agent 是否有该工具的执行权限,再做静态代码分析拦截危险导入(如
os、subprocess)。最终在沙箱(RestrictedPython 或 Docker 容器)中执行,防止注入攻击。 -
输出过滤层(输出层)。Agent 输出在传递给用户或下游 Agent 前,要做 Prompt Injection 检测——恶意网页内容被检索后可能含有"ignore previous instructions"等注入指令,影响后续 Agent 行为,需要提前拦截。任何一层失效都有其他层兜底,这是防御纵深的核心价值。
3️⃣ Key Differences
| 维度 | Common Answer | Impressive Answer |
|---|---|---|
| 技术深度 | 说了白名单和脱敏,无实现细节 | 给出工具注册中心、数据分级、静态代码分析等具体实现方案 |
| 实践经验 | 未考虑 Prompt Injection 等攻击面 | 提出 Prompt Injection 防护、第三方 Agent 沙箱隔离等高级威胁模型 |
| 思考维度 | 平面式列举几个措施 | 用"防御纵深"框架组织,形成层次化的安全体系 |
| 给面试官的印象 | 有基本安全意识 | 有系统性安全设计思维,考虑到攻击链路的每个环节 |
3、场景题:系统引入了第三方插件 Agent,如何防止它访问内部数据和工具?¶
难度级别:⭐⭐⭐(第三方 Agent 隔离、沙箱设计、API 网关审计)
1️⃣ Common Answer
可以不给第三方 Agent 访问内部数据库的权限,只给它公开的接口。对它的行为做一些日志记录,方便事后审计。
2️⃣ Impressive Answer
第三方 Agent 是多 Agent 系统中最高风险的接入点,需要从三个层次做隔离:
运行环境隔离:第三方 Agent 必须在独立的沙箱(Docker 容器或专用进程)中运行,不能和内部 Agent 共享进程空间和内存,防止通过内存读取或文件系统访问绕过权限控制。
访问路径隔离:第三方 Agent 不能直接调用内部工具,所有交互都必须通过受控的 API 网关进行。网关做身份认证(JWT)+ 权限校验(只开放公开接口)+ 请求参数校验(防止 SQL 注入、路径穿越),并对所有请求做 100% 日志记录。
数据传递控制:传递给第三方 Agent 的数据在出境前必须经过最严格的脱敏处理(L2 数据绝对不出境),返回数据在入境前经过 OutputGuard 做 Prompt Injection 检测,防止恶意插件通过输出内容影响内部 Agent 行为。
3️⃣ Key Differences
| 维度 | Common Answer | Impressive Answer |
|---|---|---|
| 隔离深度 | 只做权限控制,未考虑运行环境 | 从运行环境、访问路径、数据传递三层做纵深隔离 |
| 攻击面认知 | 只考虑正向访问控制 | 考虑到内存读取、文件系统访问、Prompt Injection 等多种攻击手段 |
| 审计能力 | 只提到日志记录 | 明确通过 API 网关做 100% 请求审计,具备事后溯源能力 |
4、容易一起考的题¶
| 关联题 | 和本题的关系 |
|---|---|
| Prompt Injection 攻击是什么?如何防御? | Agent 输出过滤的核心威胁模型,是多 Agent 安全中独有的攻击面 |
| 多 Agent 系统的可观测性如何建设? | 安全审计依赖完善的 Trace 和日志体系,两者共用同一套基础设施 |
| Docker 容器沙箱和 RestrictedPython 各自的适用场景? | 工具层和第三方 Agent 运行环境隔离的具体实现方案,需要根据场景选型 |
多 Agent 冲突解决与共识¶
冲突检测与协商机制¶
1、进阶题:多个 Agent 对同一问题给出矛盾的答案,系统应该如何处理?⭐⭐⭐¶
难度级别:⭐⭐⭐(冲突检测、投票机制、仲裁 Agent、置信度评分)
1️⃣ Common Answer
可以搞个投票,少数服从多数。或者找个更高级的 Agent 来仲裁。也可以把矛盾的答案都告诉用户,让用户自己选。
2️⃣ Impressive Answer
冲突处理需要检测、协商、仲裁三步机制:
- 冲突检测
- 比较多个 Agent 的输出:关键事实不一致、结论互斥、推荐方案冲突
- 用 LLM 或规则检测矛盾(如"答案 A 说 X>0,答案 B 说 X<0")
-
检测到冲突后,标记冲突点,进入协商流程
-
协商机制
- 投票机制:多个 Agent 对冲突点投票,少数服从多数(适合 Agent 数量≥3)
- 置信度评分:每个 Agent 输出时附带置信度,选置信度最高的(需 Agent 有校准能力)
-
辩论轮次:让持不同观点的 Agent 进行 1-2 轮辩论,尝试达成共识
-
仲裁机制
- 设一个 Arbitrator Agent(通常用更强的模型),接收各方观点和证据,做出最终裁决
- 仲裁结果附带理由,便于追溯和审计
- 如果仲裁也无法确定,降级为"多选一"让用户决定
3️⃣ Key Differences
| 维度 | Common Answer | Impressive Answer |
|---|---|---|
| 结构性 | 只说了投票和仲裁,无检测 | 检测→协商→仲裁三层完整流程 |
| 技术深度 | 不了解置信度评分和辩论机制 | 覆盖三种协商策略和仲裁理由追溯 |
| 实践经验 | 无冲突检测实现细节 | 提到用 LLM 或规则检测矛盾 |
| 面试官印象 | 方案简单粗暴 | 有系统的冲突处理框架 |
2、场景题:在多 Agent 协作中,如何设计一个"共识机制"来确保最终输出的质量?⭐⭐⭐⭐¶
1️⃣ Common Answer
可以让多个 Agent 各自生成答案,然后对比一下,选最好的。或者搞个审核 Agent,检查输出质量。
2️⃣ Impressive Answer
共识机制的设计需要分层校验、多维度评分、渐进收敛:
- 分层校验架构
- L1 - 独立生成:3-5 个 Agent 并行生成答案,互不干扰
- L2 - 交叉评审:每个 Agent 评审其他 Agent 的答案,指出问题和改进建议
- L3 - 融合优化:一个 Fusion Agent 综合所有答案和评审意见,生成最终版本
-
L4 - 终审:一个高级别 Judge Agent 做最终质量把关,不通过则打回 L1 重新生成
-
多维度评分
- 准确性(事实核查)、完整性(覆盖所有要点)、一致性(无自相矛盾)、可读性(表达清晰)
-
每个维度 1-5 分,设置最低门槛(如准确性≥4 分才能进入终审)
-
渐进收敛
- 设置最大迭代次数(如 3 次),避免无限循环
- 每次迭代后对比与上一版的差异,差异过小说明陷入局部最优,触发终止
3️⃣ Key Differences
| 维度 | Common Answer | Impressive Answer |
|---|---|---|
| 结构性 | 只有"生成 - 对比"两层 | L1 独立→L2 评审→L3 融合→L4 终审四层 |
| 技术深度 | 无评分维度和收敛机制 | 4 个评分维度 + 迭代终止条件 |
| 实践经验 | 无质量门槛意识 | 设置最低门槛和最大迭代次数 |
| 面试官印象 | 方案粗糙,质量无保障 | 有完整的质量保障框架,适合生产环境 |
多 Agent 安全与权限控制¶
多 Agent 系统的安全防护¶
基础题:多 Agent 系统中有哪些常见的安全风险?¶
⭐⭐ 考察要点:Prompt 注入传播、权限越界、工具滥用、数据泄露
1️⃣ Common Answer
主要有 Prompt 注入,Agent 可能被攻击。权限越界,Agent 可能访问不该访问的东西。工具滥用,Agent 可能用工具做坏事。数据泄露,敏感信息可能被泄露。还有就是 Agent 之间互相欺骗。
2️⃣ Impressive Answer
多 Agent 系统的安全风险可以分为输入层、执行层、协作层、输出层四个维度:
- 输入层 - Prompt 注入传播
- 直接注入:用户输入恶意 Prompt,让 Agent 执行非预期操作
- 间接注入:通过外部数据源(网页、文件、数据库)注入恶意内容
- 链式传播:一个 Agent 被注入后,将恶意内容传递给其他 Agent,形成攻击链
-
多模态注入:通过图片、音频等多模态数据注入隐藏指令
-
执行层 - 权限越界与工具滥用
- 权限越界:Agent 访问超出其职责范围的资源(如客服 Agent 访问财务数据)
- 工具滥用:Agent 使用工具进行非预期操作(如代码执行 Agent 删除系统文件)
- 资源耗尽:恶意 Agent 无限循环调用工具,导致系统资源耗尽
-
沙箱逃逸:Agent 通过工具组合突破沙箱限制
-
协作层 - 数据泄露与欺骗
- 数据泄露:Agent 在协作中泄露敏感信息(如 Agent A 将用户密码传给 Agent B)
- 信息欺骗:恶意 Agent 提供虚假信息,误导其他 Agent 做出错误决策
- 身份冒充:一个 Agent 冒充另一个 Agent,获取更高权限
-
拒绝服务:恶意 Agent 拒绝响应,阻塞整个工作流
-
输出层 - 内容安全与隐私
- 输出注入:Agent 输出中包含恶意内容,影响用户或其他系统
- 隐私泄露:Agent 在输出中无意泄露用户隐私信息
- 内容合规:Agent 输出违反法律法规或平台规范的内容
风险等级评估:Prompt 注入传播是最高风险,因为它可以触发其他所有风险。其次是权限越界,因为它可能导致直接的数据泄露或系统破坏。
3️⃣ Key Differences
| 维度 | Common Answer | Impressive Answer |
|---|---|---|
| 结构性 | 零散列举风险点 | 输入层、执行层、协作层、输出层四层结构 |
| 风险深度 | 只是"访问不该访问的" | 具体到沙箱逃逸、链式传播、身份冒充 |
| 风险评估 | 无 | 强调 Prompt 注入是最高风险 |
| 面试官印象 | 基本了解风险,但缺乏系统性 | 有完整的风险分类和评估框架 |
进阶题:如何设计 Agent 的权限隔离机制,防止工具滥用?¶
⭐⭐⭐ 考察要点:最小权限原则、沙箱执行、工具白名单、审批流
1️⃣ Common Answer
给每个 Agent 设置权限,只能用需要的工具。用沙箱隔离,不让 Agent 直接访问系统。用白名单,只允许用指定的工具。重要的操作需要审批,用户同意才能执行。
2️⃣ Impressive Answer
Agent 权限隔离需要最小权限原则、沙箱执行、工具白名单、审批流四层防护:
- 最小权限原则
- 角色定义:每个 Agent 有明确的角色和职责,只分配完成职责所需的最小权限
- 权限矩阵:建立 Agent-工具-操作的权限矩阵,细粒度控制
PERMISSION_MATRIX = {
"客服Agent": {
"allowed_tools": ["query_user", "update_order"],
"allowed_operations": ["read", "update"],
"data_scope": ["user_id", "order_id"]
},
"财务Agent": {
"allowed_tools": ["refund", "audit"],
"allowed_operations": ["read", "write", "delete"],
"data_scope": ["transaction_id", "account_id"]
}
}
-
动态授权:根据任务上下文动态调整权限(如客服 Agent 处理退款时临时授予财务权限)
-
沙箱执行
- 进程隔离:每个 Agent 在独立的容器或进程中运行,资源隔离
- 网络隔离:限制 Agent 的网络访问,只允许访问白名单域名
- 文件系统隔离:Agent 只能访问指定的沙箱目录,无法访问宿主机文件
-
资源限制:限制 CPU、内存、执行时间,防止资源耗尽攻击
-
工具白名单与参数校验
- 工具白名单:每个 Agent 只能调用白名单中的工具
- 参数校验:在工具执行前校验参数的合法性(如防止 SQL 注入)
- 敏感操作标记:标记高风险工具(如
delete_file、execute_code),强制走审批流 -
工具调用审计:记录所有工具调用日志,包括 Agent、工具、参数、结果
-
审批流机制
- 分级审批:根据操作风险等级设置不同的审批流程
- 低风险(如查询):自动执行
- 中风险(如更新):需要用户确认
- 高风险(如删除):需要多人审批
- 审批超时:设置审批超时时间,超时自动拒绝
-
审批历史:记录审批决策人、时间、理由,便于追溯
-
动态权限调整
- 信任评分:根据 Agent 的历史行为动态调整权限(如频繁违规则降权)
- 异常检测:监控 Agent 行为,检测异常模式(如突然调用未授权工具)
- 紧急熔断:检测到攻击行为时,立即撤销所有权限
实践要点:权限隔离是纵深防御,任何一层都不应该单独依赖。建议结合使用:白名单 + 沙箱 + 审批流,形成完整的防护体系。
3️⃣ Key Differences
| 维度 | Common Answer | Impressive Answer |
|---|---|---|
| 技术深度 | 只是"设置权限" | 权限矩阵、参数校验、信任评分等具体技术 |
| 架构设计 | 无 | 四层防护体系,纵深防御思想 |
| 代码示例 | 无 | 提供完整的权限矩阵示例 |
| 动态性 | 无 | 信任评分、异常检测、紧急熔断 |
| 面试官印象 | 基本思路正确,但缺乏工程细节 | 有完整的生产级权限隔离方案 |
高级题:在多 Agent 协作中,如何防止 Prompt 注入的链式传播?¶
⭐⭐⭐⭐ 考察要点:输入消毒、上下文隔离、信任边界、输出校验
1️⃣ Common Answer
过滤用户的输入,去掉恶意内容。每个 Agent 只能看到需要的信息,不要共享太多。检查 Agent 的输出,如果有问题就阻止。还有就是限制 Agent 之间的通信。
2️⃣ Impressive Answer
防止 Prompt 注入链式传播需要输入消毒、上下文隔离、信任边界、输出校验四层防护:
- 输入消毒
- 内容过滤:使用 LLM 或规则引擎检测输入中的恶意指令
def sanitize_input(user_input: str) -> str:
# 检测常见的注入模式
injection_patterns = [
r"忽略之前的指令",
r"作为.*,你需要",
r"请.*不要.*告诉任何人"
]
for pattern in injection_patterns:
if re.search(pattern, user_input):
raise SecurityException("检测到潜在的注入攻击")
return user_input
-
输入截断:限制输入长度,防止通过超长输入绕过检测
-
格式验证:对结构化输入(JSON、XML)进行严格验证
-
敏感词过滤:过滤掉系统指令、管理员权限等敏感词汇
-
上下文隔离
- 最小上下文原则:每个 Agent 只接收完成任务所需的最小上下文
- 上下文脱敏:在传递给 Agent 之前,对敏感信息进行脱敏(如手机号、身份证号)
- 上下文签名:对上下文进行数字签名,Agent 验证签名后才执行
-
上下文审计:记录所有上下文传递,便于事后追溯
-
信任边界
- 信任等级划分:将 Agent 划分为不同信任等级(如用户 Agent < 业务 Agent < 管理员 Agent)
- 边界守卫:在信任边界之间设置守卫 Agent,过滤和校验所有跨边界的消息
class BoundaryGuard:
def check_message(self, message: Message, source_level: int, target_level: int):
if source_level > target_level:
raise SecurityException("低信任 Agent 不能向高信任 Agent 发送指令")
if self.detect_injection(message.content):
raise SecurityException("检测到注入攻击")
-
单向通信:高信任 Agent 向低信任 Agent 传递信息,但不接受低信任 Agent 的指令
-
隔离执行:不同信任等级的 Agent 在不同的沙箱中运行
-
输出校验
- 内容安全检测:使用 LLM 或规则引擎检测 Agent 输出中的恶意内容
- 指令阻断:检测到 Agent 输出中包含指令性内容时,将其转换为描述性内容
def sanitize_output(agent_output: str) -> str:
# 将 "请删除文件 X" 转换为 "用户请求删除文件 X"
if re.match(r"请.*删除", agent_output):
return f"用户请求:{agent_output}"
return agent_output
-
输出截断:限制输出长度,防止通过超长输出绕过检测
-
输出审计:记录所有 Agent 输出,便于事后分析
-
链式传播阻断
- 传播链追踪:为每个消息分配唯一 ID,追踪传播链路
- 传播深度限制:限制消息在 Agent 之间的传播深度(如最多 3 跳)
- 传播时间限制:限制消息的有效期,过期自动失效
- 异常熔断:检测到注入攻击时,立即阻断整个传播链
实践要点:Prompt 注入的链式传播是最高风险,需要多层防护。建议的防御优先级:输入消毒 > 信任边界 > 输出校验 > 上下文隔离。同时,要建立应急响应机制,一旦检测到攻击,立即熔断。
3️⃣ Key Differences
| 维度 | Common Answer | Impressive Answer |
|---|---|---|
| 技术深度 | 只是"过滤输入" | 输入消毒、上下文签名、传播链追踪等具体技术 |
| 架构设计 | 无 | 信任等级划分、边界守卫、传播深度限制 |
| 代码示例 | 无 | 提供完整的输入消毒、边界守卫示例 |
| 防御优先级 | 无 | 明确给出防御优先级和应急响应机制 |
| 面试官印象 | 基本思路正确,但缺乏系统性 | 有完整的链式传播防护体系 |
多 Agent 编排模式对比与选型¶
主流编排框架的对比与技术选型¶
1、进阶题:LangGraph、CrewAI、AutoGen 三大框架的核心差异是什么?如何选型?⭐⭐⭐¶
-
问题类型:对比分析
-
难度级别:⭐⭐⭐
-
考察要点:编排模型(状态图 vs 角色任务 vs 对话)、灵活性、学习曲线、适用场景
1️⃣ Common Answer
这三个框架都是做多 Agent 的,但不太一样。LangGraph 是用图的方式编排 Agent,状态可以在节点间传递,比较灵活。CrewAI 是基于角色和任务的,每个 Agent 有自己的角色,然后分配任务。AutoGen 是通过对话的方式,Agent 之间互相聊天来完成任务。选型的话,如果需要复杂的流程控制用 LangGraph,如果是明确的任务分工用 CrewAI,如果是对话式的场景用 AutoGen。
2️⃣ Impressive Answer
三大框架的核心差异在于编排模型和适用场景,需要根据需求特性进行选型:
- LangGraph:状态图编排模式
- 核心模型:基于有向状态图(State Graph),节点是 Agent 或工具,边定义状态转换条件
- 状态管理:全局状态在节点间传递和更新,支持复杂的状态流转和条件分支
- 灵活性优势:支持循环、条件分支、并行执行等复杂控制流,适合需要精确编排的场景
- 适用场景:复杂工作流、需要精确控制执行顺序、状态依赖强的任务(如代码生成、数据分析流水线)
-
学习曲线:中等,需要理解状态图概念
-
CrewAI:角色-任务协作模式
- 核心模型:基于角色(Role)和任务(Task)的协作,每个 Agent 扮演特定角色,分配特定任务
- 任务驱动:任务有明确的输入、输出、依赖关系,Agent 按任务依赖顺序执行
- 团队协作:支持多 Agent 团队协作,可以定义 Manager 协调者角色
- 适用场景:分工明确的任务(如内容创作团队:研究员、写手、编辑)、结构化工作流
-
学习曲线:较低,概念直观,类似项目管理中的任务分配
-
AutoGen:对话式协作模式
- 核心模型:基于多轮对话的协作,Agent 通过自然语言对话协商和完成任务
- 自然交互:Agent 可以互相提问、澄清需求、协商分工,更接近人类协作方式
- 动态编排:执行路径由对话动态决定,适合不确定性和探索性强的任务
- 适用场景:需要探索和协商的任务(如需求分析、创意头脑风暴)、开放性问题求解
-
学习曲线:中等偏上,需要设计对话协议和提示词
-
技术选型决策树
- 需要精确控制执行流程 → LangGraph
- 任务分工明确,结构化工作流 → CrewAI
- 需要探索和协商,开放性强 → AutoGen
- 团队经验:Python 基础强选 AutoGen/LangGraph,快速上手选 CrewAI
- 生态集成:LangChain 生态选 LangGraph,微软生态选 AutoGen
3️⃣ Key Differences
| 维度 | Common Answer | Impressive Answer |
|---|---|---|
| 结构性 | 简单对比三个框架 | 每个框架单独分析,最后给出选型决策树 |
| 技术深度 | 描述基本特性 | 深入编排模型、状态管理、适用场景分析 |
| 实践经验 | 缺乏选型依据 | 提供决策树和实际场景建议 |
| 面试官印象 | 了解框架差异 | 具备技术选型能力,有架构设计思维 |
2、进阶题:Orchestrator(中心化编排)和 Choreography(去中心化协作)两种模式有什么区别?¶
难度级别:⭐⭐⭐(中心协调者 vs 事件驱动、耦合度、可扩展性、故障传播)
1️⃣ Common Answer
Orchestrator 是中心化的,有一个主 Agent 负责协调其他 Agent,所有决策都由它来做。Choreography 是去中心化的,Agent 之间直接协作,没有中心控制。Orchestrator 比较��易控制,但中心节点可能成为瓶颈。Choreography 比较灵活,但可能难以理解和调试。
2️⃣ Impressive Answer
两种编排模式在控制流、耦合度、可扩展性等方面有本质差异:
- 控制流与决策机制
- Orchestrator(中心化编排):由中心协调者统一决策,负责任务分解、Agent 选择、执行顺序控制
- 优势:全局视角优化,容易实现复杂的业务逻辑和约束
- 劣势:中心节点成为单点,决策逻辑复杂时难以维护
-
Choreography(去中心化协作):Agent 之间通过事件或消息直接协作,每个 Agent 自主决策
- 优势:去中心化,单个 Agent 故障不影响整体,系统更健壮
- 劣势:缺乏全局优化,可能出现死锁或循环依赖
-
耦合度与依赖关系
- Orchestrator:Agent 与 Orchestrator 强耦合,Agent 之间解耦。新增 Agent 只需在 Orchestrator 中注册
-
Choreography:Agent 之间可能存在强耦合,依赖事件协议。新增 Agent 需要修改现有 Agent 的事件监听逻辑
-
可扩展性与性能
- Orchestrator:中心节点可能成为性能瓶颈,适合中小规模系统
-
Choreography:天然支持横向扩展,每个 Agent 独立部署,适合大规模分布式系统
-
可观测性与调试
- Orchestrator:执行路径明确,Trace 链路清晰,容易追踪和调试
-
Choreography:执行路径动态,需要完善的分布式 Trace 和事件日志系统,调试复杂度高
-
选型建议
- 需要精确控制、全局优化 → Orchestrator(如工作流引擎、审批流程)
- 高并发、大规模分布式 → Choreography(如微服务架构、事件驱动系统)
3️⃣ Key Differences
| 维度 | Common Answer | Impressive Answer |
|---|---|---|
| 结构性 | 简单对比优缺点 | 五个维度系统分析,最后给出选型建议 |
| 技术深度 | 描述基本概念 | 深入控制流、耦合度、可扩展性等架构层面 |
| 实践经验 | 缺乏场景分析 | 结合实际场景给出选型依据 |
| 面试官印象 | 了解两种模式 | 具备架构设计能力,能从多维度分析 |
3、场景题:如果让你从零设计一个多 Agent 系统,你会如何做技术选型和架构设计?¶
⭐⭐⭐⭐

1️⃣ Common Answer
从零设计多 Agent 系统的话,首先要把需求搞清楚,看要做什么任务。然后选择框架,如果简单点就用 CrewAI,复杂点就用 LangGraph。通信协议可以用 HTTP 或者消息队列。容错设计很重要,要考虑重试、降级、熔断。可观测性也要做好,日志、监控、Trace 都要有。还要考虑成本,Token 用量要控制。
2️⃣ Impressive Answer
从零设计多 Agent 系统需要系统化的方法论,从需求分析到落地实施全链路设计:
- 需求分析与场景拆解
- 任务特性分析:确定任务的确定性(探索性 vs 结构化)、并发性(可并行 vs 顺序依赖)、实时性要求
- 复杂度评估:评估状态空间大小、Agent 数量、交互复杂度,决定编排模式(Orchestrator vs Choreography)
-
非功能需求:明确性能要求(延迟、吞吐量)、可用性目标(SLA)、成本预算、合规要求
-
框架选型决策
- 精确控制需求 → LangGraph(状态图编排)
- 分工明确任务 → CrewAI(角色-任务协作)
- 探索性任务 → AutoGen(对话式协作)
- 考虑团队技能(Python/Java)、生态集成(LangChain/OpenAI)、可维护性
-
简单需求直接用开源,复杂需求可能需要基于开源二次开发或自研核心编排引擎
-
分层架构设计
- 编排层:负责任务分解、Agent 调度、状态管理
- Agent 层:实现具体 Agent 逻辑,封装 LLM 调用
- 工具层:提供外部工具接口(API、数据库、文件系统)
-
可观测性层:日志、监控、Trace、告警
-
通信协议设计
- 同步通信:HTTP/gRPC,适合低延迟、强一致性场景
- 异步通信:消息队列(Kafka/RabbitMQ),适合高吞吐、解耦场景
-
协议标准化:定义统一的 Agent 通信协议(输入输出格式、错误码)
-
容错与高可用设计
- Agent 级容错:重试策略(指数退避)、超时控制、降级逻辑(规则引擎替代)
- 系统级容错:熔断机制、限流保护、多可用区部署
-
数据一致性:幂等设计、事务补偿、最终一致性保证
-
可观测性体系
- Trace 传播:全局 Trace ID,Span 层次化设计
- 日志管理:结构化日志(JSON),集中收集(ELK/Loki),敏感信息脱敏
-
监控告警:核心指标(延迟、成功率、Token 成本),分级告警,基线对比
-
渐进式落地策略
- MVP 验证:先实现核心功能,小流量验证,快速迭代
- 金丝雀发布:新功能先小流量测试,监控指标正常后全量
- 持续优化:基于监控数据和用户反馈,持续优化性能和成本
3️⃣ Key Differences
| 维度 | Common Answer | Impressive Answer |
|---|---|---|
| 结构性 | 简单罗列步骤 | 七个阶段系统设计,从需求到落地 |
| 技术深度 | 提到基本概念 | 深入架构设计、容错机制、成本优化等细节 |
| 实践经验 | 缺乏具体方案 | 包含分层架构、通信协议、渐进式落地等实战经验 |
| 面试官印象 | 了解设计流程 | 具备系统架构能力,有完整的落地方法论 |