共享状态与黑板协作
共享状态是一份供多个 Agent 或执行节点读取、更新的任务数据。它记录当前目标、已收到的证据、各子任务的进度和产物版本,使后续执行者能够知道任务已经推进到哪里。
黑板协作在共享状态之上增加了调度机制:各执行者把发现写入“黑板”,控制器检查哪些工作的前提已经满足,再选择下一项工作。例如两份资料核实结果通过验收后启动比较,发现价格过期后重新查价,草稿就绪后启动审查。黑板可以用数据库或内存对象实现,不要求模型之间轮流聊天。
哪些数据需要共享
以多个 Agent 共同生成选型报告为例,可以把信息分成三种范围。
| 数据范围 | 保存内容 | 谁需要读取 |
|---|---|---|
| Agent 私有上下文 | 自己的指令、工作中的工具交互、尚未提交的草稿 | 当前 Agent 的下一次调用 |
| 共享任务状态 | 当前选型条件、候选证据、子任务状态、报告版本、审查意见 | 参与这项任务的相关执行者 |
| 长期记忆 | 允许跨任务复用的偏好、经过整理的历史经验 | 后续有权限且确实需要这些信息的任务 |
共享状态保存到数据库后可以跨进程恢复,但它仍然属于这一项任务。数据库的持久化方式不会把一次采购的预算变成用户永久偏好。关于记忆的范围划分,见记忆机制。
“共享”也不要求每个 Agent 每轮读取全部字段。研究 Agent 需要选型条件和待查对象,审查 Agent 需要草稿、证据和验收要求。应用应按角色和当前步骤组装输入;共享存储中的数据只有经过读取,才会进入模型的上下文。
先约定字段由谁更新
下面是一份报告任务的状态布局:
{
"task_id": "selection-report",
"revision": 0,
"requirements_revision": 1,
"requirements": {"max_price_cny": 6000, "max_weight_kg": 1.5},
"subtasks": {
"verify-a": {"status": "running", "acceptance": "pending"},
"verify-c": {"status": "running", "acceptance": "pending"}
},
"evidence_by_id": {},
"draft": null,
"review_notes_by_id": {},
"status": "researching"
}字段所有权决定谁可以提出哪些更新。它可以由一个 Agent 持有,也可以由运行时或确定性的处理函数负责。
| 字段 | 更新方式 |
|---|---|
requirements | 协调者根据用户的新要求更新,同时增加条件版本 |
subtasks | 执行程序更新运行状态,验收流程另行记录交付结果是否可用;两者都关联对应子任务 |
evidence_by_id | 研究 Agent 提交证据,合并函数按稳定 ID 去重追加 |
draft | 写作 Agent 提交新产物引用及版本,审查 Agent 对指定版本提意见 |
review_notes_by_id | 审查结果按意见 ID 合并,解决状态由约定的处理者更新 |
整体 status | 控制器根据就绪条件和验收结果推进 |
Agent 在提示词里看到“只改自己的字段”,有助于减少误操作;共享服务仍需校验实际写入。否则任何能调用通用写入工具的执行者,都可能覆盖其他字段。
跟着两次并发完成,看更新怎样合并
研究 Agent A 和 C 都读到了上面的 revision: 0。A 查完自己的资料时,C 还在执行。如果它们各自把整份状态写回,后写入的一方就可能把另一方的新结果覆盖成旧值。
让执行者提交局部更新,可以缩小冲突范围。本次两个子任务都要求核实指定配置的总价与重量,证据需保留型号、配置、来源定位和查询时间。每次更新带有提交 ID 和所属子任务,数据交由状态服务处理:
更新 U-A
submission_id = result-a-1
task_id = selection-report
subtask_id = verify-a
requirements_revision = 1
新增证据 = {a-price-01: A 的价格记录, a-weight-01: A 的重量记录}
执行方报告状态 = completed
更新 U-C
submission_id = result-c-1
task_id = selection-report
subtask_id = verify-c
requirements_revision = 1
新增证据 = {c-price-01: C 的价格记录, c-weight-01: C 的重量记录}
执行方报告状态 = completed状态服务收到结果后,先检查归属与条件版本,再按任务契约检查交付字段,核对价格、重量对应的配置及其来源。执行方报告 completed 只表示本次执行已经结束;验收通过后,服务才记录 acceptance: accepted,供下游判断是否就绪。核实到价格超出预算也是有效结果,验收检查的是资料能否支持比较。
两份交付都满足要求时,状态服务按以下顺序更新:
| 步骤 | 状态变化 | 控制器的决定 |
|---|---|---|
| 验收 U-A | 保存 A 的证据,记录 verify-a 已完成且验收通过,整体版本变为 1 | C 尚未通过验收,继续等待 |
| 验收 U-C | 在当前状态上合并 C 的证据,记录 verify-c 已完成且验收通过,整体版本变为 2 | 两份结果均可用,创建比较任务 |
| 再次收到 U-A | 发现相同提交已经处理,不再写入或重复触发 | 比较任务继续原来的执行 |
若 U-C 缺少重量来源,就记录验收未通过并要求补查,比较任务继续等待。通过验收的证据、验收状态和提交 ID 应在一个原子更新中保存。否则可能先出现“验收通过”,证据却还没写入,控制器据此启动比较时仍会读到不完整材料。
两次新增证据可以合并,是因为它们写入不同的证据 ID。对这类字段,可以定义 reducer,即根据旧值与局部更新计算新值的函数:
def merge_evidence(current, incoming):
merged = dict(current)
for evidence_id, record in incoming.items():
if evidence_id in merged and merged[evidence_id] != record:
raise ValueError(f"同一证据 ID 对应不同内容: {evidence_id}")
merged[evidence_id] = record
return merged这段函数将重复的相同记录视为同一次数据,而对相同 ID、不同内容报错。它只负责数据合并;并发事务、权限和持久化仍由存储层负责。若先读后写之间缺少事务或版本检查,使用 reducer 也可能发生覆盖。
当输入合法且证据不可变时,这种合并满足重复提交不改变结果,也不依赖两份更新的到达顺序。简单的列表拼接则会留下重复项,而且排列顺序可能随调度变化。若后续要按时间或优先级读取,应保存相应字段,再显式排序。
哪些更新不能直接相加
证据追加容易合并,修改同一个草稿或改变任务状态需要更严格的规则。
假设两个写作 Agent 都读到 draft_version: 3,分别生成新版。如果系统无条件接受两次写入,后一次就会覆盖前一次。可以要求提交带 expected_draft_version: 3,由存储层原子比较:第一次成功后版本变为 4,第二次因版本不符被拒绝,再由执行者读取版本 4,决定重新生成或交给合并步骤。
这称为乐观并发控制。版本比较与写入必须是一个原子操作;在应用中先查版本、隔一会儿再写,仍然留有竞争窗口。也可以使用短事务或锁串行更新某些字段,不需要每个字段都采用同一种策略。
任务状态还应限制允许的转移。例如收到重复完成事件不应再次启动写作,已取消任务的迟到结果不能把任务重新改成运行中。具体状态转移放在生命周期篇讨论。
数据合并也不能代替内容判断。两份证据使用不同 ID,分别记着同一配置价格为 5999 和 6299,reducer 可以同时保留;当前报告采用哪条,还需要比较来源、查询时间和价格口径。将它们取平均值或随意保留后到的一条,都会改变证据含义。
如果用户此时将预算改为 5500 元,requirements_revision 应增加。依据旧条件生成的“符合预算”判断需要重算,已经核实的型号和重量则可能继续使用。把事实记录与派生结论分开,才能有选择地更新结果。
黑板怎样启动下一项工作
有了可靠的共享状态,控制器可以把调度条件写成规则,也可以让模型从当前符合条件的任务中选择。无论谁选择,运行时都需要记录任务是否已经派发,防止同一状态反复触发。
收到子任务结果
↓
校验归属、条件版本与提交 ID
↓
按任务契约检查交付字段、配置和来源
↓
原子保存证据、执行状态及验收结果
↓
检查就绪条件
├─ A、C 的结果均通过验收,且比较任务尚未创建 → 创建比较任务
├─ 验收发现缺少来源或同配置报价 → 创建补查任务
└─ 草稿已提交,且该版本尚未审查 → 创建审查任务创建任务时,可以用“任务类型 + 输入版本”生成稳定的调度键,并通过唯一约束防止重复创建。例如草稿版本 4 已经有审查任务,重复到达的通知就不应再启动一遍审查。通知负责促使控制器检查状态;当前状态和调度记录决定实际执行什么。
黑板与群聊的区别也在这里。群聊围绕共享消息记录,由调度器选择下一位发言者;黑板围绕可更新的问题状态,由控制器选择当前能推进任务的工作。两者可以结合:群聊产生一条修改建议,程序将其转换为待办;黑板任务完成后,再把结果发给参与讨论的 Agent。聊天记录本身不会自动提供字段所有权、版本检查或任务就绪条件。
对于步骤固定的研究、写作、审查,用明确的依赖图通常更容易调试。黑板适合线索逐渐出现、后续任务需要随状态增补的工作,同时也需要限制待办数量和重复调度。不同任务的执行依赖见流水线与任务依赖图。
参考资料
- LangGraph:Graph API — State 与 Reducers:状态更新及 reducer 的工作方式。
- LangGraph:Memory:任务状态与跨会话记忆。
- Microsoft AutoGen:Selector Group Chat:群聊的参与者选择与结束条件。