步骤并行化

969 字
5 分钟
步骤并行化

在 Agent 系统中并行化步骤时,错误隔离是关键设计。我们在从 Prompt 到 Harness:企业级 Agent 工程的完整演进之路中讨论了两种错误隔离机制,针对两种不同的并发混乱:

错误隔离是关键设计:Promise.allSettled 保证一个子任务失败不中断其他子任务,失败结果标记 __error 传递给下游步骤由 LLM 自行判断处理。Context 并发写入安全通过浅拷贝 sharedContext 确保只读前序数据。SSE 事件乱序问题通过 taskId 分组渲染解决。

下面我们来简单介绍一下原文提到的两种隔离机制,以及它们对应的并发混乱问题。

1. Context 并发写入#

假设我们有两个步骤并行执行,并且共享同一个 sharedContext:

sharedContext = {
userInput: "分析销售数据",
previousResult: "……"
}

如果两个并行步骤都直接修改它的话:

步骤 A:写入 sharedContext.result = A
步骤 B:写入 sharedContext.result = B

显然,由于 A,B 的执行顺序不固定,所以可能出现:

  • A 覆盖 B;
  • B 覆盖 A;
  • 一个步骤读到了另一个步骤写到一半的数据。

这就是 Context 的并发写入问题。

1.1 解决方案:软拷贝#

文中的解决方式是软拷贝。也就是说,在启动每个并行步骤前,我们给它一份独立的浅拷贝:

const contextA = { ...sharedContext };
const contextB = { ...sharedContext };

于是:

步骤 A 只读取 contextA,并把结果单独返回
步骤 B 只读取 contextB,并把结果单独返回

可见,二者都不直接修改原始的 sharedContext,而是等它们都完成后,再由调度器统一合并结果:

const [resultA, resultB] = await Promise.all([
runStepA(contextA),
runStepB(contextB)
]);
sharedContext.stepAResult = resultA;
sharedContext.stepBResult = resultB;

浅拷贝的局限性在于,它只复制最外层。

换句话说,如果 Context 里有嵌套对象,比如:

const sharedContext = {
user: {
name: "Alice"
}
};
const contextA = { ...sharedContext };

那么 contextA.user 和 sharedContext.user 仍指向同一个对象,不同的步骤依然会修改共享数据。

因此,这种方案真正安全的前提是:

  • 并行步骤严格只读前序数据;
  • 或冻结对象,禁止修改;
  • 或使用深拷贝;
  • 或采用不可变数据结构。

在原文中,浅拷贝恰恰是配合只读约束,防止并行步骤直接争抢同一个顶层 Context.

2. SSE 事件乱序问题#

同样,假设两个 Agent 任务同时执行:

任务 A:生成报告
任务 B:分析代码

它们产生的 SSE 事件就可能交错到达前端:

A:开始搜索
B:开始读取代码
B:读取完成
A:搜索完成
B:开始分析

如果前端把所有事件直接放进同一个列表,用户看到的就是两个任务混在一起,容易误以为事件顺序错乱。

2.1 解决方案:分组#

原文中的解决办法是:每条 SSE 事件都携带所属任务的 ID,按照 taskID 进行分组:

{
"taskId": "task-A",
"type": "search_started",
"message": "开始搜索"
}
{
"taskId": "task-B",
"type": "code_read",
"message": "读取代码完成"
}

这样,前端收到事件后,不是全部追加到同一个列表,而是根据 taskId 分组:

eventsByTask[event.taskId].push(event);

最终就会分别显示:

任务 A
1. 开始搜索
2. 搜索完成
任务 B
1. 开始读取代码
2. 读取完成
3. 开始分析

这样,即使不同任务的事件在网络中交错到达,界面也不会把它们混在一起。


需要注意的是,taskID 并不能解决同一任务内部的乱序,因为它们的 taskID 完全相同。此时,我们还需要给事件增加递增序号:

{
"taskId": "task-A",
"sequence": 3,
"message": "工具完成"
}

然后前端就按下面的规则处理:

先按 taskId 分组
再按 sequence 排序

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

步骤并行化
https://hac.he.cn/posts/agent-01/
作者
HAC
发布于
2026-08-28
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
HAC
观之非易,行且克难
Greetings
欢迎来到我的博客!这里主要分享我的学习笔记与兴趣爱好。
音乐
封面

音乐

暂未播放

0:000:00
暂无歌词
分类
标签
站点统计
文章
46
分类
8
标签
19
总字数
116,126
运行时长
0 天
最后活动
0 天前
文章目录