多个 Agent 并行执行时如何管理全局状态并避免竞争?
未标记
考查重点:并行写入与状态合并。并行分支各写独立结果,按字段 reducer 或版本校验合并;共享计数用原子操作,避免直接覆盖全局状态
本题目录
参考答案
核心回答
我会按任务隔离、写入归属、合并规则和原子提交管理状态,尽量减少多个Agent直接修改同一个字段。
- 全局只在当前任务内共享:用户和任务标识明确,子Agent保留自己的执行过程,公共状态只放协作必需的信息。
- 并行分支分别交结果:各自写对应结果槽或提交带ID的更新,最终结论由一个归并节点写入。
- 按字段定义合并语义:列表是否去重、消息怎样按ID更新、计数怎样累计,不能统一用覆盖或拼接。
- 共享修改用原子机制保护:进程内用短临界区或单写者;数据库用条件更新、版本或事务,冲突后重新判断。
- 处理重复、迟到和接管:结果带任务版本和归属,旧执行不能覆盖新状态;外部业务操作另有幂等保障。
例如两个检索Agent并行收集资料,应分别返回各自来源,再统一合并;不能都基于旧的 sources 列表生成完整新列表,谁晚返回就覆盖谁。
展开说明
1. 把共享State当作有职责的数据,而不是全局变量
可以保存目标、任务版本、分支状态、结果引用、预算和最终产物。对话、工具中间记录以及只供某角色使用的信息留在各自运行状态里。不同用户、会话和运行不能复用一份无范围的可变字典。
共享字段应明确谁能写:子Agent提交局部结果,调度者更新运行状态,归并者生成最终输出。线程安全Map只能避免容器结构损坏,无法判断“哪个Agent的结论应该成为最终结果”。把这些职责分开,往往比给整个State加一把锁更有效。
2. LangGraph中,局部更新也需要正确的reducer
节点通常返回变化的字段,由框架按字段应用reducer。同一super-step的并行节点若写入不支持多次更新的同一字段,可能触发 INVALID_CONCURRENT_GRAPH_UPDATE;不能假定框架会自动选择最后一条。
可独立的结果用不同字段保存;确需共同汇入一个列表或映射时,定义合并规则。例如资料按稳定来源ID去重,分支结果按任务与角色定位。普通列表相加只是拼接,不会自动去重;add_messages 则会根据消息ID追加或更新,语义不同。
并行完成顺序不应成为业务顺序。需要固定顺序时,结果附带章节号或其他排序键,归并后明确排序。自定义reducer要检查空值、重复更新、顺序和重放;若规则不能安全合并,就保留单写者,不必强求所有字段并发可写。
3. 数据库条件写要保护实际状态转换
假设两个执行者都读到任务版本5,随后都想推进任务:提交时要求“当前版本仍为5,状态仍允许本次转换”,并在同一个原子更新中写入新状态和版本。第一个成功后,第二个的条件不再成立,应重新读取并判断结果是否仍有效,不能把旧整份State再次覆盖回去。
跨多个字段的不变量可以用适当事务或锁保护。事务隔离级别有不同保证;发生版本冲突、序列化失败或接管变化时,要明确重试哪些逻辑。进程内锁只协调本进程,多个服务实例不能靠各自的一把锁保护同一数据库记录。
网络或模型调用可能很慢,不宜一直占着全局锁或长事务等待。可以先读取版本并执行局部工作,再用条件提交检查是否仍适用。被取消、目标已更新或已由新执行接管的结果,应拒绝发布到当前权威状态。
4. 重复投递、费用和外部副作用分别处理
结果提交带稳定ID和任务版本,可以避免同一结果重复归并。预算统计却需要记录每次实际模型或工具尝试:重试真的产生了费用,不能因为最终结果去重就把这部分消耗抹掉。
若任务分布在远程Worker,失联接管可使用租约,并让状态写入端校验递增的接管版本或fencing token,拒绝旧Worker恢复后的迟到写入。单进程能够直接取消和等待的任务,通常不必提前引入这套机制。
检查点负责保存执行状态,不能替代所有并发控制;数据库事务也不能自动让远程发单、通知等操作只发生一次。存在外部副作用时,还需要业务幂等标识、回执与状态查询,避免恢复或竞争后重复执行。
5. 用交错顺序验证不变量
至少检查两个分支同时提交、重复提交、旧版本迟到、取消后返回,以及一个分支失败时怎样归并。应确认资料不丢失、同一结果不重复、终态不会被旧结果改回运行中,最终产物来自规定的归并逻辑。
失败是否允许部分交付由任务定义;分支进入失败或超时终态,不代表它成功完成。观察记录应能关联任务、分支、尝试、输入版本与提交结果,才能定位是计算错误、合并错误还是过期写入。
常见追问
有checkpoint就不会竞争了吗?
不会。它保存执行进度和状态,但同一任务的并发推进、应用外部数据库的修改及旧Worker接管,仍需相应的单写或条件提交机制。还要遵循所用框架对同一线程并发运行的支持方式。
所有字段都用operator.add可以吗?
不可以。它对列表是拼接,对数值是相加,不能判断哪个结果有效,也不自动去重。最终结论、状态转换、消息更新和费用事件有不同语义,应分别设计。
需要给整个系统上分布式锁吗?
不一定。先通过任务隔离、分支分槽和单一归并减少争用。只有确实跨实例竞争同一资源时,再选择数据库条件写、事务或适当锁机制;锁的范围、超时和接管后的旧写入也要处理。
对应教程
- LangGraphAPI:图与状态:比较默认更新、
add_messages、列表累加及自定义reducer,理解状态字段的合并语义。同step并发更新的限制还需结合框架当前说明判断。
参考资料
- 国内:JavaGuide:共享状态与并发归并:借鉴按角色分槽、权威字段单写及远程接管的设计,说明正文中为何需要业务合并规则,以及何时才引入 lease 与 fencing。
- 国内:ranxi2001:共享状态读写职责:依据任务范围、读写分离与版本控制,说明正文中如何减少多个 Agent 共同修改同一字段。
- 国内:WeThinkIn:异步写入与版本一致性:依据预期版本、重复和迟到更新的处理,说明正文中状态条件提交及冲突后的重判策略。
- 国外:LangGraph:Graph API:依据 super-step、局部更新与逐字段 reducer 的约定,解释正文中框架如何合并并行更新与消息。
- 国外:PostgreSQL:Transaction Isolation:依据并发条件检查、隔离保证和事务重试,说明正文中数据库原子保护的范围及其对远程副作用的限制。

