多 Agent 协作系统设计:任务拆分、状态共享、冲突处理与失败恢复
我做 AgentInvest 多智能体股票分析系统时,把任务交给了四个角色。研究员先完成基础研究,技术分析师和舆情分析师并行补充,投资经理最后汇总。每个角色内部使用 ReAct 调用工具,外层流水线控制顺序、并发和失败条件。
角色跑起来以后,工程问题才真正出现。子任务拆到什么程度,两个 Worker 能不能同时写状态,一个分支超时后是否继续,都会直接影响最终结果。面试官追问多 Agent 项目时,通常也会沿着这些问题往下问。
AI Agent 核心概念 已经介绍过 Orchestrator-Subagent、Peer-to-Peer 等协作模式。这篇文章继续讨论这些模式落到项目以后,任务、状态、冲突和失败恢复应该怎样处理。
除了 AgentInvest,后面还会穿插技术调研、客服转接和代码审查等例子。
多 Agent 和多阶段 Prompt Chain 有什么区别?
Prompt Chain 管理处理步骤,多 Agent 管理能够独立完成子目标的执行者。两者可以同时出现,外层使用固定 Workflow,节点内部再运行各自的 Agent。
| 对比项 | 多阶段 Prompt Chain | 多 Agent |
|---|---|---|
| 执行单元 | 一条 Workflow 中的处理节点 | 可以独立完成子目标的 Agent |
| 工作方式 | 节点按照既定逻辑处理输入并产生输出 | Agent 可以选工具、观察结果并迭代执行 |
| 控制方式 | Workflow 负责节点跳转和状态流转 | 可以用固定编排,也可以由模型动态委派 |
| 上下文 | 上下文通常沿处理链路传递 | 可以按 Agent 隔离上下文、工具、权限和状态 |
| 运行状态 | 主要关注整条链路执行到了哪个节点 | 还要区分各 Agent 的任务、状态、尝试次数和交付结果 |
| 结果交付 | 节点输出通常作为后续节点的输入 | 可以传消息、共享状态,也可以交付结构化结果 |
| 失败处理 | 重试失败节点或从 Workflow 检查点恢复 | 还要决定哪个角色可以降级、跳过或重新执行 |

如果 AgentInvest 的四个角色只是四次固定模型调用,共用一份上下文和一组工具,每一步只加工上一步的文本,那它仍然是一条多阶段 Prompt Chain。
项目里的每个角色都是独立的 ReActAgent,有自己的 Prompt、Toolkit、maxIters 和上下文。研究员能查行情、研报、新闻和财务数据,技术分析师关注 K 线与技术指标,舆情分析师查询市场情绪,投资经理读取上游 <core_thesis> 做综合判断。外层 Workflow 管理的是四个独立 Agent 的执行过程,不只是在四段文本之间传递结果。
是否并行只说明任务怎么执行。多 Agent 可以顺序交接,Prompt Chain 也可以出现并行分支。
如果让你设计一个多 Agent 系统,你会先考虑哪些问题?
先确定每个角色负责什么、谁控制下一步、结果怎样交付以及失败后怎么处理,最后再决定需要几个 Agent、哪些任务可以并行。
| 需要确定的问题 | 具体要考虑什么 | 一个常见例子 |
|---|---|---|
| 每个 Agent 负责什么? | 子目标、Prompt、工具、上下文和权限是否需要隔离 | 技术调研可以拆成资料检索、数据分析和结论复核 |
| 谁决定下一步做什么? | 流程由代码控制,还是由模型动态选择角色和任务 | 固定审核流程交给代码,不确定的检索方向交给模型 |
| 多个 Agent 怎么配合? | 顺序、并行、路由、交接、循环,还是几种方式组合 | 多路资料检索并行执行,Reviewer 在资料齐全后统一复核 |
| Agent 之间交付什么? | 传完整对话、共享状态,还是只传结构化结果和必要证据 | 检索 Agent 交付结论、引用和 Artifact URI,而非完整对话 |
| 怎么记录状态和处理失败? | 是否单独记录角色状态,超时后重试、跳过、降级还是终止 | 某一路检索超时后保留其他结果,并明确标记缺失项 |
| Agent 部署在哪里? | 同一进程内运行,还是通过网络跨服务、跨团队或跨安全域 | 本地角色可以直接调用,远程角色需要身份、协议和任务状态 |
以技术调研为例,外层 Workflow 可以固定为制定计划、检索资料、分析和复核四个阶段。资料检索阶段再由 Agent 选择搜索词、数据源和工具,必要时增加新的检索方向。角色之间只交付结论、引用和 Artifact,不需要共享完整聊天记录。
什么时候值得用多 Agent?
我一般会先用单 Agent 或固定 Workflow 实现。一个 Agent 已经能在可接受的成本和延迟内完成任务时,拆出多个角色只会增加调度、上下文交付、结果归并和故障恢复的工作量。
如果不同环节需要明显不同的工具、权限和上下文,或者任务可以分成几个相对独立的方向,再考虑多 Agent。每个角色还要有明确的交付结果,例如结构化结论、引用、代码 Diff 或测试报告。如果角色之间只交付几段自由文本,后面通常会把大量精力花在结果拼接和问题定位上。
Anthropic 公开的 Multi-Agent Research 系统采用了这种设计。主 Agent 根据问题分出多个研究方向,Subagent 分别搜索,完成后再由主 Agent 汇总。各分支可以独立探索,适合并行;如果分支频繁依赖彼此的中间结论,或者必须反复同步完整上下文,通信成本很快就会抵消拆分带来的收益。

代码变更任务里,编码 Agent 负责修改文件并交付 Diff,测试 Agent 运行测试并返回失败信息,Reviewer 根据需求和 Diff 做检查。三个角色使用的工具和验收标准都不一样,拆开后各自有清楚的任务。如果它们只是读取同一份 Diff,再分别写一段相似的总结,增加的只是模型调用次数。
上线前还要用同一批任务做对照测试,比较单 Agent 和多 Agent 的质量、耗时、Token 与失败率。没有可衡量的收益,就先保留较简单的实现。
AgentInvest 中的四个 Agent 是怎么协作的?
以 AgentInvest 的股票分析任务为例,用户提出这样一个问题:
帮我分析一下贵州茅台现在还能不能买。
模型常识回答不了这个问题。系统需要查询实时行情、研报、财务指标、K 线和市场舆情,还要结合用户是否持仓给出建议。AgentInvest 按下面的顺序执行。
- 研究员先查询行情、研报、新闻和财务数据,交付基础研究结论与
<core_thesis>。 - 研究员完成后,技术分析师查询 K 线与技术指标,舆情分析师查询市场情绪,两个角色并行执行。
- 投资经理读取上游论点,再结合用户持仓、历史记忆和策略 Skill 生成最终建议。
外层由项目自定义的 AgentScopePipelineExecutionStrategy 控制阶段顺序。角色内部才交给 ReAct,让模型判断当前缺什么数据、调用哪个工具、拿到结果后是否继续分析。这样既能保证投研流程稳定,又没有把每个角色写成完全固定的 Prompt Chain。
进入投资经理阶段需要满足两个条件。研究员已经产出核心论点,技术分析师和舆情分析师中至少有一个成功。上游结果明显不足时,系统直接跳过投资经理,并通过 SSE 返回错误,避免模型硬凑一份看起来完整的投资建议。
研究员必须先完成,投资经理必须最后执行,只有中间两个互不依赖的角色并行。角色由多 Agent 设计确定,执行先后则由 DAG 控制。
多 Agent 应该选择哪种编排模式?
编排模式取决于谁拥有控制权、角色何时确定、任务之间有什么依赖,以及谁直接面对用户。
| 模式 | 控制方式 | 适用场景 | 主要代价 |
|---|---|---|---|
| Sequential | 代码按固定顺序调用多个 Agent | 步骤确定,前后依赖清楚 | 时延累加,灵活性有限 |
| Parallel | 代码把独立任务并行分发后归并 | 多维评审、独立检索、投票 | 状态归并和部分失败处理 |
| Router | 规则或模型把请求路由给一个或多个专家 | 业务域清楚、分类相对稳定 | 路由错误会直接选错专家 |
| Supervisor/Manager | 主 Agent 动态调用子 Agent,并保留最终控制权 | 子任务无法提前完全确定,需要统一答案 | 主 Agent 成为瓶颈,协调成本高 |
| Handoff | 当前 Agent 把控制权和必要上下文交给另一个 Agent | 客服分流、分阶段对话、专家直接服务用户 | 会话历史和权限边界更难维护 |
| Evaluator-Optimizer | 生成者和评审者循环迭代 | 有明确评分标准,迭代确实能改善结果 | 容易无限打磨,需要停止条件 |
| Group Chat | Agent 共享会话,按轮转或 Selector 依次发言 | 讨论、评审、需要相互修正的任务 | 上下文容易膨胀,停止条件难定 |
| Debate | 多个 Agent 多轮提出、质疑并修正观点 | 结论可由证据检验,分歧本身有价值 | 容易重复争论,不保证更正确 |
| Event-driven/Peer | Agent 根据消息和本地规则决定后续动作 | 跨服务、跨团队的开放协作 | 一致性、安全和调试最复杂 |
AgentInvest 使用的是 Sequential + Parallel + Aggregator 混合编排。研究员与投资经理构成前后顺序,中间的技术分析师和舆情分析师并行执行,投资经理承担最终归并。
这里没有使用 Supervisor 动态决定角色数量,也没有让某个 Agent 把用户会话 Handoff 给另一个 Agent。股票分析的主要阶段相对固定,用代码控制外层流程更稳定;需要动态判断的部分,留在各角色内部的 ReAct Loop 中处理。
这些模式可以组合。Router 选中专家后,专家仍可调用 Subagent;Supervisor 也可以先完成准备任务,再并行派发 Worker。
角色应该静态拆分还是动态委派?
静态拆分是指角色、依赖和执行路径在运行前已经确定。固定顺序、固定并行分支和条件 DAG 都属于这一类。它的好处是容易测试,超时、预算和部分失败也更好控制,适合业务步骤相对稳定的任务。
动态委派是由 Manager 或 Supervisor 根据当前问题和中间结果决定调用哪些 Agent、拆出多少子任务以及是否继续探索。Anthropic 的 Multi-Agent Research 就属于这种模式,不同问题需要调查的方向不一样,很难提前画出一张固定 DAG。
两种方式可以混用。例如,外层固定收集资料、分析和审核三个阶段,资料收集阶段再由主 Agent 根据问题创建 Subagent。审核和发布门槛仍由固定流程控制,调研方向可以在运行时扩展。

如何把子任务拆成可执行契约?
只在 Prompt 里写“你是技术调研 Agent”,角色仍然不知道要查什么、能用哪些数据,以及怎样才算完成。协调器派发任务时,要同时给出子目标和成功标准,标明依赖的上游任务、可用工具与权限,并约定输出 Schema 和产物位置。时间、Token、调用次数和失败策略也属于任务契约,由运行时负责执行。
下面的 AgentTask 展示了一个简化的任务契约。
public record AgentTask(
String taskId,
String parentTaskId,
AgentRole role,
TaskContext context,
List<AgentRole> dependencies,
Set<String> allowedTools,
String outputSchema,
int maxIters,
Duration modelTimeout,
Duration toolTimeout,
Instant deadline,
int maxAttempts) {
}实际项目还要补任务版本、幂等键、成功标准和产物位置。allowedTools、超时和重试次数由运行时强制执行,模型只接收完成当前子任务所需的信息。
怎么判断子任务能否并行?
以技术调研为例,研究问题确定后,“查官方文档”和“查论文与基准测试”可以同时开始。两边输入已经具备,也不需要读取对方的中间结论。对比结论必须等待两边材料到齐,只能放在后面。
并行分支还要避开同一权威字段。两个检索 Agent 分别提交自己的材料清单,由归并节点写最终报告;发送通知、修改代码等外部副作用也交给单一执行者,或者使用幂等与唯一约束保护。某个分支失败后是等待、降级还是终止,同样要在运行前写进成功标准。
Java 中可以用线程池、虚拟线程或结构化并发执行这些分支,具体选哪一种不是重点。编排层仍要限制最大并发数,为整个阶段设置共同的 Deadline;到期后取消未完成任务、保留已经完成的结果,再根据成功门槛决定是否进入下游。
怎么防止角色重复工作和结果缺失?
职责范围要同时落到任务和工具上。代码变更场景中,编码 Agent 负责修改工作区,测试 Agent 只运行测试并返回失败信息,Reviewer 读取 Diff、测试结果和规则清单。只改角色名称,不限制工具和输出,几个 Agent 还是会重复检查同一件事。
每个角色还要有明确的交付协议。一个通用的 RoleResult 至少可以包含结论、证据、数据时间、Artifact URI、缺失项和置信边界,协调层负责校验必填字段。结构化输出解决的是格式和交接问题,结论是否可信仍然要结合证据与时效判断。
AgentInvest 目前使用 <core_thesis> 传递核心论点,这是轻量做法,改造成本低;它也可能丢掉数据时间和关键依据。需要更强审计能力时,应该逐步替换成结构化结果,而不是继续扩大自然语言兜底规则。
Agent 之间应该怎样通信和共享状态?
Agent 之间不需要默认使用聊天消息。进程内的固定 Workflow 通常通过参数、返回值和共享状态交付结果,跨服务或需要持续协商时才增加消息和远程协议。
| 通信方式 | 怎么做 | 适合什么情况 |
|---|---|---|
| 参数和返回值 | 编排器调用 Agent,把上游结果作为下游输入 | 进程内固定 Workflow,关系简单 |
| 共享状态/黑板 | Agent 读取公共状态,只更新自己负责的字段 | 图式编排,需要多个节点逐步补全结果 |
| 消息 | 点对点发送或向 Group Chat 广播 | Agent 需要持续协商、交接或异步协作 |
| 结构化结果 | 交付带 Schema 的结论、报告或文件引用 | 结果较大,需要校验、复用和审计 |
| 远程 Agent 协议 | 通过 A2A 等协议发现 Agent、传递任务和产物 | 跨进程、跨团队或跨安全域 |
通信机制越复杂,鉴权、重试、消息顺序和重复投递也越难处理。能在方法调用中完成的交付,没有必要先改成消息系统。
共享状态只保存跨角色协作需要的信息。每个 Agent 的工具记录和 ReAct 进度留在自己的会话状态中;用户请求、租户、资源和权限通过显式的 TaskContext 传入。角色完成后,把结论、证据摘要、缺失项和 Artifact URI 写入自己负责的状态槽,编排器另外记录角色是否开始、完成、失败或超时。
网页、日志、查询结果和代码 Diff 等原始材料可以留在当前角色上下文或 Artifact 库。SSE、WebSocket 只负责把进度推给前端,不承担任务状态保存。
业务上下文最好显式传给 Agent 和工具。依赖 ThreadLocal 隐式读取任务数据,在并发、异步或远程调用后更容易串任务,也很难从方法签名看出工具用了哪些信息。
如何控制共享状态的大小?
技术调研任务中,检索 Agent 没有必要把抓取的所有网页、搜索过程和工具日志复制给 Reviewer。它可以把原始材料存入 Artifact 库,只交付结论摘要、引用、数据时间和 Artifact URI。Reviewer 需要核查某条结论时,再按 URI 读取原始证据。
摘要不能脱离证据。涉及价格、版本、法规、实验数据等时效性较强的信息,还要保留采集时间、来源和关键字段。
多 Agent 的运行状态应该记录什么?
public record AgentRunState(
String runId,
String subject,
RunStatus status,
Map<AgentRole, RoleState> roles,
Map<AgentRole, ArtifactRef> artifacts,
BudgetUsage usage,
List<FailureRecord> failures,
ArtifactRef finalOutput) {
}
public record RoleState(
AgentRole role,
TaskStatus status,
int attempt,
String providerId,
String modelName,
Instant startedAt,
Instant updatedAt,
FailureRecord lastFailure) {
}这份结构是简化示意。单 Agent 的会话状态、共享业务状态和外层 Workflow 状态要分别保存。会话存储记录模型对话与工具进度,Workflow 状态记录任务执行到了哪个角色,两者不能相互替代。
角色拆成独立服务后,再补 ownerAgentId、version、leaseUntil 和 fencingToken 处理任务接管。这些字段属于分布式 Worker 层,进程内多 Agent 不需要提前照搬。
A2A 能解决什么,不能解决什么?
多个角色运行在同一个进程时,直接方法调用通常更简单。Agent 独立部署、由不同团队维护或需要跨安全域调用后,再考虑用 A2A 统一能力发现、任务状态和产物交付。
一次 A2A 协作通常从读取 Agent Card 开始,调用方先确认远程 Agent 的能力、接口和认证要求,再创建带唯一 ID 的 Task。双方通过 Message 补充任务信息,完成后用 Artifact 交付文档或结构化结果,Context 则把相关任务和消息关联起来。
A2A 1.0 的 TaskState 除了表示未知或未指定状态的 TASK_STATE_UNSPECIFIED,还包括 TASK_STATE_SUBMITTED、TASK_STATE_WORKING、TASK_STATE_INPUT_REQUIRED、TASK_STATE_AUTH_REQUIRED、TASK_STATE_COMPLETED、TASK_STATE_FAILED、TASK_STATE_CANCELED 和 TASK_STATE_REJECTED。其中,INPUT_REQUIRED 和 AUTH_REQUIRED 都属于等待外部补充信息后继续执行的中断状态。这比只返回“成功/失败”更适合长任务。
A2A 解决的是远程 Agent 如何发现彼此、交换消息、跟踪任务和交付产物。它不会替应用决定执行拓扑,也不会自动解决内部一致性。两个 Worker 同时更新任务怎么办,超时后由谁接管,通知或退款如何避免重复执行,仍然需要业务系统自己处理。
多 Agent 的并发冲突怎么处理?
看到两个 Agent 给出了不同结果,不能简单地选一个覆盖另一个。先分清楚它们遇到的是哪一种冲突。
| 冲突类型 | 一个常见例子 | 处理方式 |
|---|---|---|
| 状态写冲突 | 两个检索 Agent 同时覆盖报告的 sources 字段 | 按角色分槽、追加集合或使用版本/CAS |
| 观点冲突 | 两个 Agent 基于不同来源得出相反结论 | 保留来源、时间和适用范围,交给归并器判断 |
| 所有权冲突 | 旧 Worker 和接管 Worker 同时提交结果 | Lease + 版本/CAS + Fencing Token |
| 副作用冲突 | 两个 Agent 重复发送通知或发起退款 | 单一执行者、幂等键和业务唯一约束 |
并行状态怎么归并?
并行 Agent 各自更新自己的状态槽,结果和错误也按角色保存。Token、耗时等计数可以通过事件汇总,最终报告和 finalOutput 只允许归并节点写入。前端事件携带角色和序号用于展示,不能反过来修改任务状态。
如果两个 Agent 都能覆盖同一个 finalOutput,线程安全容器只能保证写入过程不损坏,无法判断哪个业务结果应该保留。按角色保存结果,再由单一归并节点写权威字段,才能避免最后写入者直接覆盖其他分支。
多个 Agent 的事实结论冲突怎么办?
股票分析里的“冲突”不一定是谁算错了。技术分析师可能根据 60 日 K 线判断趋势偏多,舆情分析师却发现短期负面情绪正在升高。两条结论关注的时间范围和数据来源不同,不能简单投票决定听谁的。
如果后续要提高结论的可审计性,可以把关键观点进一步结构化。
{
"claimId": "claim-17",
"subject": "600519",
"predicate": "technical-trend",
"value": "BULLISH",
"scope": "daily-kline-60d",
"sourceTool": "get_technical_indicators",
"observedAt": "2026-08-15T10:00:00+08:00",
"evidenceStatus": "VERIFIED"
}scope 用来说明结论来自日线还是分钟线,observedAt 记录数据时间,sourceTool 说明依据来自哪个工具。投资经理归并时就能区分“中期技术趋势偏多”和“短期舆情偏空”,不必强行把它们改成同一个方向。
数据不足时,最终建议应该降低确定性。ReAct 能减少不查数据就回答的情况,但不能保证投资结论一定正确。
为什么 Lease 还要配 Fencing Token?
进程内任务能够直接取消和等待时,通常不需要分布式 Lease。角色拆成远程 Worker 后,才会遇到旧实例和接管实例同时返回的问题。
假设 Worker A 获得任务租约后发生长时间停顿。租约到期,系统把任务交给 Worker B。此时 A 恢复并继续写入,如果下游只检查“A 曾经拿到过锁”,旧结果仍可能覆盖 B 的新结果。
假设 A 获得的 fencingToken 是 7,B 接管后拿到 8。状态库把 8 记为当前可接受版本;A 恢复后仍携带 7 提交,写入条件不成立,迟到结果被拒绝。
Lease 主要解决失联 Owner 不会永久占有任务,Fencing Token 或版本校验解决旧 Owner 恢复后的迟到写入。只给锁设置 TTL,并不能自动保护所有外部资源。
任务生命周期应该如何建模?
角色状态应该由外层执行策略管理,不能让模型自由填写。编排器需要根据依赖任务的状态决定下游能否运行,下面是一种通用的状态机设计。
| 状态 | 含义 | 允许的主要后继状态 |
|---|---|---|
PENDING | 已创建,依赖尚未满足 | READY、CANCELED |
READY | 可以被调度 | RUNNING、CANCELED |
RUNNING | 角色正在执行 | WAITING_INPUT、SUCCEEDED、RETRYABLE_FAILED、FINAL_FAILED |
WAITING_INPUT | 等待用户输入、审批或授权 | READY、CANCELED、FINAL_FAILED |
RETRYABLE_FAILED | 确认是可重试故障 | READY、FINAL_FAILED |
SUCCEEDED | 输出已保存并通过基本校验 | 终态 |
FINAL_FAILED | 无法自动恢复 | 终态或人工重新开启 |
CANCELED | 被用户或系统取消 | 终态 |
在当前单进程流水线里,按角色保存状态就能完成主要控制。以后拆成分布式服务,提交结果时还要同时校验任务状态、Owner、Fencing Token 和版本:
UPDATE agent_task
SET status = 'SUCCEEDED',
artifact_uri = :artifactUri,
version = version + 1
WHERE task_id = :taskId
AND status = 'RUNNING'
AND owner_agent_id = :ownerAgentId
AND fencing_token = :fencingToken
AND version = :expectedVersion;受影响行数为 0,说明任务已经被接管、取消或发生并发更新。Worker 不能继续“补写”,而应重新读取状态。
多 Agent 失败后如何恢复?
多 Agent 系统里,失败之后不能直接统一重试。输入不完整、权限不足、搜索接口超时和结果缺少证据,处理方式完全不同:
| 失败类型 | 一个常见例子 | 建议动作 |
|---|---|---|
| 输入/契约错误 | 缺少目标资源,输出不符合 Schema | 修正一次或立即失败,不做网络重试 |
| 权限/业务拒绝 | 客服 Agent 没有退款权限 | 终止调用或等待用户、管理员授权 |
| 瞬时故障 | 搜索接口连接重置、短期限流、临时 5xx | 有界重试,退避加抖动 |
| 调用超时 | 模型或工具超过本次调用预算 | 终止当前调用,按角色策略处理 |
| 角色超时 | 一个调研分支超过整个阶段的 Deadline | 取消未完成任务,保留成功结果 |
| 质量不达标 | 报告缺少引用、必填字段或可核验的产物 | 修正一次、降级或跳过下游 |
| 确定性代码错误 | 事件解析失败、状态迁移非法 | 快速失败、告警、修代码 |
恢复动作要落到真正负责的层级。角色执行器处理模型、工具和迭代次数,编排器决定是否取消其他分支、保留哪些结果以及下游能否继续。角色拆成远程 Worker 后,调度器再处理 Lease、任务接管和迟到结果。
如何限制重试次数和总预算?
预算至少要分成四层:单次模型调用、单次工具调用、单个 Agent 的迭代次数,以及整条任务或某个阶段的 Deadline。前几层限制局部消耗,最外层负责保证任务不会因为内部重试一直拖延。
重试预算还应该只有一个主要持有者。模型 SDK、角色执行器和网关如果各自独立重试,同一次故障会放大成多轮嵌套调用。编排层需要记录已经消耗的尝试次数、Token、时间和成本,重试前先检查剩余预算。
Checkpoint 应该保存在哪里?
检查点适合放在结果已经落盘、可以独立校验的位置。技术调研中的检索阶段完成后,先保存带引用的材料清单;数据分析完成后,保存可以复用的 Artifact;Reviewer 结束后,再记录通过或退回原因。需要用户授权的动作也要在暂停前保存待执行参数。
单个 Agent 的会话状态和外层 Workflow 检查点是两类数据。前者记录对话、工具调用和角色内部进度;后者还要保存当前阶段、已完成角色、产物引用、模型与 Prompt 版本、预算使用和失败记录。
只把聊天记录写入 Redis,并不能自动从“两个检索任务已经完成、一个数据分析任务超时”的位置继续整条 Workflow。恢复时必须能重建依赖关系和已确认产物。
恢复通常也不是从某一行 Java 代码继续。一个角色可能从 ReAct 节点或工具调用前重新执行,因此读取类工具可以有界重试;如果以后加入交易、通知等外部副作用,还要额外保证幂等。
Agent 超时后怎么处理?
进程内并发可以在阶段 Deadline 到期后取消未完成的 Future,记录超时状态并保留已经完成的产物。取消只是向本地任务发出停止信号;如果 Agent 已经调用远程模型或外部工具,还要确认远端请求是否结束,以及是否产生了副作用。
角色拆成独立服务后,调用方超时也不能证明原 Worker 已经停止。调度器发现 Lease 过期时,先查询 Worker 和下游系统是否已经产生可用 Artifact。产物存在且校验通过,任务可以直接推进;结果仍然未知时,调度器递增 attempt 和 fencingToken,把任务放回 READY,新 Worker 再从最近的 Checkpoint 和 Artifact URI 恢复。
原 Worker 随后恢复并提交结果时,旧 Fencing Token 或版本会让这次写入失败。任务超过最大尝试次数后进入 FINAL_FAILED,现场和已完成产物继续保留,等待人工处理。
除了 lastHeartbeatAt,还要记录 lastProgressAt,用于识别“进程活着但 ReAct 一直兜圈子”的情况。
部分失败时,是等、降级还是返回部分结果?
处理方式由任务的成功标准决定,并在运行前写进任务契约。必答维度缺失时可以让整体失败;多个同类分支只需达到规定数量时,可以按 Quorum 归并;允许部分完成的任务则返回已有结果并标出缺失项。备用工具、模型和数据源属于 Fallback,高价值或高风险任务可以转人工补齐。
AgentInvest 采用的是带门槛的 Best-effort:研究员必须成功,技术分析师和舆情分析师至少成功一个,投资经理才会继续。技术面失败但舆情结果可用时,投资经理可以根据已有结果继续,运行状态仍要保留技术面失败,不能把这次分析当成全量成功。两个补充角色都失败时,则不让投资经理硬凑结论。

外部副作用怎么补偿?
退款、发邮件、创建工单、修改代码和发布版本都会改变外部状态。这些操作可以统一交给 Action Executor,负责分析和生成文本的 Agent 只提交动作意图与参数。
Action Executor 收到请求后先校验身份、权限和风险等级,高风险动作等待人工确认。执行时使用幂等键和业务唯一约束,并按业务需要记录事务、Outbox 或 Saga。动作完成后再核验外部状态,保存审计记录;失败时根据已执行步骤选择补偿或转人工。
补偿不是数据库回滚的同义词。“发送了一封邮件”通常无法真正撤回,只能再发送更正通知。补偿本身也可能失败,需要幂等、重试和人工入口。详细原理可以看 分布式事务。
Human-in-the-loop 如何暂停和恢复任务?
HITL 发生在任务执行过程中。客服 Agent 判断订单可以退款,但金额超过自动审批阈值时,编排器把任务转为 WAITING_INPUT,保存 Checkpoint、退款依据和待执行参数,然后释放当前计算资源。
审批人稍后打开任务,可以批准、拒绝或修改参数。系统使用原来的 runId/taskId 恢复运行,同时重新校验审批人权限、订单状态、退款金额和任务版本。等待期间任何一个条件发生变化,都要让原审批失效。
发布代码、删除数据等高风险操作可以使用同样的暂停方式。Agent 缺少必要输入、需要补充工具授权,或者多个角色的结论无法自动归并时,也可以进入等待状态。框架负责中断和恢复机制,业务代码决定谁能审批、审批范围和有效期。
如何观测一次多 Agent 运行?
多 Agent 的一次运行会派生出多个任务,Trace 也应该保留这种父子关系:
ResearchRun
├── Planner
│ └── ModelCall
├── SourceResearchStage
│ ├── OfficialDocsResearcher
│ │ └── WebSearchTool
│ └── PaperResearcher
│ └── PaperSearchTool
├── Analyst
│ └── DataAnalysisTool
└── Reviewer
└── ModelCall根 Span 表示整次 ResearchRun,记录 runId、租户、任务主题以及代码和 Prompt 版本。每个角色任务作为子 Span,带上父任务 ID、Agent 角色、模型、Skill、状态、attempt 和停止原因。模型与工具调用继续挂在角色 Span 下面,记录参数与结果摘要、错误类型、Token、成本和耗时。
有了父子关系,排查时才能看出时间花在队列等待、角色执行还是最终归并,也能确认下游为什么被跳过。一次重试要继续挂在原角色任务下,并保留新的 attempt,不能伪装成一次全新的正常调用。
前端可以通过 SSE 或 WebSocket 展示角色开始、工具调用、结果摘要和最终输出,但事件流不应该成为任务状态的事实源。断线恢复、重放和审计仍然要读取持久化的 Agent 状态、Workflow 运行记录和 Artifact。
模型路由结果也要在调用开始时写入 Trace,记录实际使用的 provider 和 model。执行结束后重新做一次路由再补日志,可能把观测数据记到另一个模型上。
OpenTelemetry 的 GenAI 语义约定列出了 invoke_agent、invoke_workflow、execute_tool 等操作名,以及 Agent、Conversation、Tool 和 Token 使用量相关属性,这些约定目前仍处于 Development 状态。工具参数和结果可能包含敏感数据,规范也明确提醒这类字段不宜默认记录。生产系统应使用摘要、脱敏、采样和分级访问。
多 Agent 需要哪些特有指标?
任务失败时,先看各角色的成功率、超时率和重试分布,再下钻到模型与工具的错误率和 P95。某个角色频繁触发 ReAct 迭代上限,通常会同时拉高 Token、成本和阶段耗时。
并行阶段还要记录队列等待、实际并发数和最慢分支耗时。角色交付后,继续统计结构化结果校验失败、返工和下游跳过的次数。最终再看整次任务的 Token、成本、P95,以及首条前端事件和完整结果分别等待了多久。
多个分支已经并行,端到端耗时仍然由最慢分支和最终归并决定。并行后没有变快时,可以检查并发限额、外部接口限流、最长工具调用和归并节点耗时。
多 Agent 怎么评测?
ReAct 每次选择的工具顺序可能不同,评测不需要逐节点匹配一条固定轨迹。先检查子 Agent 是否按任务契约交付结果,再检查编排器是否遵守依赖、并发和跳过条件,最后确认归并节点有没有遗漏上游冲突、引用或失败信息。
技术调研任务可以用代码检查引用格式、来源时间、必要字段和 Artifact 是否存在,再判断结论能否被材料支持。工具注册、角色权限、任务依赖、并发上限、超时取消和上下文隔离都有确定规则,使用单元测试与集成测试验证即可。
恢复能力要靠故障注入验证。让一个检索 Agent 超时,观察其他产物是否保留,Reviewer 是否按照成功门槛继续;让工具返回错误数据,检查角色会不会错误地标记成功;提供相互冲突的来源,确认归并节点保留分歧和证据。
最终建议的质量仍然需要固定任务集、多次运行和人工校准。Task、Trial、Grader、Outcome 与 Transcript 的定义见 AI 应用评测体系。
多 Agent 还需要对照组。可以让单 Agent 使用全部工具,再分别运行固定多 Agent Workflow 和 Supervisor 动态编排,三组使用相同输入快照、模型和预算。比较必要工具调用率、证据覆盖、事实错误、P95、Token 和成本后,才能判断拆分是否真的带来收益。
角色消融也很直接。去掉一个检索 Agent、Reviewer 或归并节点重新评测,如果质量、覆盖率和失败恢复几乎不变,这个角色很可能只增加了调用次数。
多 Agent 设计最容易犯哪些错误?
角色名称能直接作为 Agent 边界吗?
研究员、分析师和 Reviewer 这些名称只说明了角色分工,不能证明系统已经拆成多个 Agent。假如它们共用一份上下文和一组工具,每一步都只是改写上一步的文本,这套实现仍然更接近 Prompt Chain。
判断是否真的拆出了多个 Agent,可以看每个角色能否独立接收子目标,在受限的上下文和工具中完成任务,并按约定的 Schema 交付结果。缺少这些差异,只换一段 System Prompt,系统并没有获得清晰的 Agent 边界。
多 Agent 一定要并行吗?
AgentInvest 中,研究员必须先产出基础结论,投资经理也必须等上游任务完成后才能汇总,只有互不依赖的技术分析和舆情分析适合并行。并行由任务依赖决定,与系统里有几个 Agent 没有必然关系。Reviewer、Handoff 以及按回合讨论的 Group Chat 都可能顺序执行。没有确认输入是否就绪、结果如何归并就强行并发,只会增加状态和失败处理的复杂度。
确定性步骤需要交给 Agent 吗?
输入是否合法、当前角色能调用哪些工具、上游产物是否齐全,这些判断都有明确规则,交给代码和状态机处理即可。超时取消、重试次数和阶段顺序也应该由运行时强制执行。Agent 更适合处理需要语义判断的工作,例如选择检索方向、解释相互矛盾的材料,以及综合多个角色的结论。确定性规则塞进 Prompt,不但更慢,还会把原本可以稳定复现的行为变成概率问题。
所有 Agent 都应该共享完整上下文吗?
技术调研中,检索 Agent 可能抓取几十个页面,还会产生多轮工具记录。Reviewer 通常只需要结论、引用、缺失项和 Artifact URI,没有必要重放整个检索过程。确实需要核对原始材料时,再按 URI 读取。每个 Agent 只接收自己的任务、可用工具和必要的上游结果,这样既能控制 Token,也能减少无关信息对当前角色的干扰。
并行写冲突能交给模型商量吗?
需要先区分结论冲突和写入冲突。两个分析 Agent 对同一份数据给出不同判断,可以保留各自的证据,再由 Reviewer 或归并节点判断。两个分支同时覆盖 finalOutput,则是并发写入问题,模型讨论解决不了。
并行结果应当按角色分开保存,最终报告只允许归并节点写入。发送通知、创建工单、退款等操作还会产生外部副作用,需要继续用幂等键、唯一约束和事务保护,不能指望 Prompt 避免重复执行。
只设计正常执行路径会有什么问题?
假设两个检索 Agent 并行执行,一个成功,另一个超时。此时是保留已有材料继续交给 Reviewer,还是判定资料不全并结束任务,取决于事先约定的成功门槛。任务设计时要覆盖部分成功、超时、取消、缺少必要产物和下游跳过等状态。否则,流程图上的正常链路虽然能跑通,线上遇到第一个异常时,编排器却不知道该继续、降级还是停止。
为什么不能只验收最终回复?
即使某个检索工具调用失败,模型仍然可能生成一份语句流畅、结构完整的报告。只看最终文字,很容易把这种结果当成成功。
验收时还要核对必要工具是否调用成功、每个角色是否按契约交付、引用能否回到原始材料,以及归并节点有没有隐瞒失败分支。最终回复只是其中一项产物,不能代替对执行过程的验收。

