并行化(Parallelization):让 AI 同时处理互不依赖的事
有些 Agent 任务慢,不是因为模型不够聪明,而是因为它把所有事情排成了一条队。
比如做一份公司调研:
查新闻 -> 查财报 -> 查产品 -> 查用户评价 -> 汇总如果这四个查询互不依赖,就没必要一个等一个。它们可以同时开始,最后再汇总。
这就是并行化:
把互不依赖的任务同时执行,再把结果合并成最终输出。
并行化解决的是「怎么更快」的问题。但它不是把所有任务都同时跑。能不能并行,关键看依赖、共享状态、成本和失败影响。
说得再生活一点:并行化不是让一群人同时挤进厨房抢一口锅,而是让一个人洗菜、一个人备料、一个人查菜谱,最后再由主厨把东西收回来。真正省时间的前提,是大家做的事彼此不打架。
一、先看一个耗时对比
假设四个子任务各需要 10 秒:
| 做法 | 执行方式 | 理想耗时 | 风险 |
|---|---|---|---|
| 串行 | A -> B -> C -> D | 40 秒 | 稳,但慢 |
| 并行 | A/B/C/D 同时跑 | 10 秒左右 | 需要处理失败、限流、汇总 |
| 半并行 | A/B 同时跑,C 依赖 A,D 依赖 B | 20 秒左右 | 需要先画依赖关系 |
并行化的价值不只是快。它还让任务边界更清楚:每个子任务负责一种资料、一个维度或一批样本,最终汇总器再做取舍。
二、判断哪些任务能并行
先不要急着写代码。先问这几个问题:
| 判断项 | 可以并行 | 不适合并行 |
|---|---|---|
| 依赖关系 | 子任务不依赖彼此输出 | B 必须等 A 的结果 |
| 共享状态 | 只读资料,各自独立 | 多个任务写同一条记录 |
| 外部限制 | API 限流宽松 | 严格限流或有事务顺序 |
| 失败影响 | 单个失败可降级 | 一个失败会污染后续全部结果 |
| 上下文要求 | 每个任务只需局部上下文 | 必须保持连续推理 |
| 成本预算 | 多次调用可接受 | 并行会瞬间放大成本 |
一个简单判断:
如果任务之间只是在“收集不同材料”,通常可以并行。
如果任务之间是在“逐步推理和决策”,通常要谨慎。更稳一点,可以先把任务写成这种结构:
[
{ "name": "news", "depends_on": [] },
{ "name": "finance", "depends_on": [] },
{ "name": "product", "depends_on": [] },
{ "name": "summary", "depends_on": ["news", "finance", "product"] }
]depends_on 为空的任务,才是第一批可以同时启动的任务。summary 这种依赖前面结果的任务,应该放到最后。
三、并行化的四个动作:拆、判、跑、汇
1. 拆:把大任务拆成独立子任务
例如公司调研可以拆成:
1. 新闻动态
2. 经营数据
3. 产品信息
4. 用户口碑每个子任务都应该能独立完成,并且有明确输出格式。
这里的「独立」很重要。不是随便切成四段文字,而是每个子任务拿到自己的输入后,就能交出一份可被汇总器读取的结果。
2. 判:判断依赖关系
不是所有拆出来的任务都能并行。
可以并行:
新闻动态、经营数据、产品信息、用户口碑
不适合并行:
先总结报告,再基于总结写邮件前者是多路收集,后者是前后依赖。
如果一件事必须等另一件事完成后才能判断下一步,它更像提示词链;如果几件事只是从不同角度补充材料,它才适合并行。
3. 跑:限制并发地同时执行
并行不是无限同时跑。真实系统要考虑模型限流、API 配额、成本和超时。
最多同时跑 3 个子任务;
单个任务超过 30 秒就标记 timeout;
失败结果不阻塞其他任务;这里最好有一个并发上限,而不是来多少跑多少。并发上限既保护外部服务,也保护自己的预算。
4. 汇:合并结果
汇总器不能只是把结果拼起来。它要去重、排序、处理冲突,并说明哪些子任务失败或信息不足。
一个合格的汇总器至少要做四件事:
| 动作 | 要解决的问题 |
|---|---|
| 去重 | 多个子任务可能找到同一条信息 |
| 对齐 | 不同子任务的输出格式要能放在一起比较 |
| 取舍 | 结论冲突时,要说明采用哪条证据 |
| 暴露缺口 | 子任务失败、超时或信息不足时,不能假装完整 |
四、两种经典模式
1. Fan-out / Fan-in
Fan-out / Fan-in 适合「不同任务同时做,最后汇总」。
2. Map-Reduce
Map-Reduce 适合「同一种任务批量做,最后归纳」。
1000 条用户评论
↓
每 100 条一组并行分析
↓
汇总高频问题和代表性样例3. 怎么选
| 模式 | 输入特征 | Map 阶段做什么 | Reduce 阶段做什么 | 常见风险 |
|---|---|---|---|---|
| Fan-out / Fan-in | 多个不同维度 | 分别处理新闻、财务、产品、口碑 | 合成一个综合判断 | 汇总时维度权重不清 |
| Map-Reduce | 大量同类数据 | 对每批数据做同类分析 | 去重、聚类、归纳趋势 | 样本分组不均、重复结论多 |
Fan-out 更像「多个人查不同资料」。Map-Reduce 更像「很多人按同一规则处理一堆材料」。
如果你不知道该选哪个,可以先看输入:维度不同,用 Fan-out / Fan-in;材料很多但格式相似,用 Map-Reduce。
五、完整案例:公司调研助手
目标:
输入:公司名称
输出:一份调研摘要,包含新闻、经营、产品、用户口碑和风险提示可以拆成四个并行子任务:
| 子任务 | 输入 | 输出 |
|---|---|---|
| 新闻分析 | 公司名 + 最近新闻 | 3 条关键动态 |
| 经营数据 | 公司名 + 财务/公开资料 | 增长、收入、风险信号 |
| 产品信息 | 公司名 + 产品页 | 核心产品和定位 |
| 用户口碑 | 公司名 + 评论/社区内容 | 高频评价和抱怨 |
每个子任务都要求输出固定结构:
{
"topic": "news",
"findings": ["..."],
"risks": ["..."],
"sources": ["..."],
"status": "success"
}固定结构的好处,是汇总器不用猜「这段话到底是结论、风险还是来源」。子任务可以各做各的,但交卷格式要统一。
最后汇总提示词只看这些结构化结果:
你是一名公司研究分析师。
请根据下面四类并行调研结果,生成一份简明调研摘要。
要求:
1. 不要编造资料中没有的信息;
2. 合并重复结论;
3. 明确标出信息不足或子任务失败的部分;
4. 输出:核心判断、关键证据、风险、下一步建议。
并行结果:
{{parallel_results}}六、一个最小并行执行器
下面用 Python 伪代码展示关键结构:并发限制、部分失败、汇总输入。
import asyncio
async def run_one_task(task, semaphore):
async with semaphore:
try:
output = await ai_call(task["prompt"], timeout=30)
return {
"name": task["name"],
"status": "success",
"output": output,
}
except TimeoutError:
return {
"name": task["name"],
"status": "timeout",
"output": None,
}
except Exception as error:
return {
"name": task["name"],
"status": "failed",
"error": str(error),
"output": None,
}
async def run_parallel_research(company):
tasks = build_research_tasks(company)
semaphore = asyncio.Semaphore(3)
results = await asyncio.gather(
*(run_one_task(task, semaphore) for task in tasks)
)
successful = [item for item in results if item["status"] == "success"]
failed = [item for item in results if item["status"] != "success"]
return await ai_call(
prompt="请汇总调研结果,并说明失败或缺失的信息",
input={
"successful_results": successful,
"failed_tasks": failed,
},
)真实系统还会加重试、缓存、成本统计和日志,但核心结构就是:子任务独立跑,失败单独记录,汇总器看完整状态。
这段伪代码里最值得注意的不是 asyncio.gather,而是两点:
| 设计 | 作用 |
|---|---|
Semaphore(3) | 控制最多同时跑 3 个任务,避免并发失控 |
返回 status | 让汇总器知道哪些结果成功、失败或超时 |
也就是说,并行执行器不只负责「跑得快」,还要负责把每一路任务的状态说清楚。
七、生产化关键点
| 设计点 | 为什么重要 | 简单做法 |
|---|---|---|
| 先画依赖关系 | 防止把串行任务误并行 | 画 DAG 或列 depends_on |
| 限制并发数量 | 防止触发限流和成本尖峰 | semaphore / queue |
| 设置超时 | 防止一个任务拖住整体 | 单任务 timeout |
| 允许部分失败 | 不让一个失败毁掉全部结果 | success / failed / timeout |
| 汇总要去重 | 并行结果常有重复 | Reduce 阶段做排序和取舍 |
| 记录日志 | 方便定位慢任务和坏任务 | step、duration、status、cost |
并行化最容易犯的错误,是只看到速度,没有设计失败收敛。
还有一种常见错误,是把共享写操作也并行了。比如多个子任务同时改同一份报告、同一条数据库记录、同一个记忆区。这样虽然看起来快,但很容易互相覆盖。更稳的做法是:并行阶段只产出独立结果,最终写入动作交给汇总器统一完成。
八、什么时候不要并行
并行化不是默认选项。下面这些情况,更适合串行或小批量半并行:
| 场景 | 为什么不适合直接并行 | 更稳的做法 |
|---|---|---|
| 后一步依赖前一步判断 | 并行会拿不到必要上下文 | 用提示词链 |
| 多个任务写同一状态 | 容易覆盖、冲突或重复提交 | 先并行读,最后统一写 |
| 外部 API 严格限流 | 同时请求会触发失败 | 小批量队列 |
| 每个子任务成本很高 | 并行会放大瞬时成本 | 先路由筛选,再并行 |
| 需要连续推理 | 拆开后会丢上下文 | 保持单一路径 |
并行化的核心不是「多开几个模型调用」,而是先证明这些调用真的互不依赖。
九、和提示词链、路由的关系
| 模式 | 解决的问题 | 判断依据 | 可以怎么组合 |
|---|---|---|---|
| 提示词链 | 先后顺序 | 是否依赖上一步输出 | 链中的某一步可以并行 |
| 路由 | 走哪条分支 | 输入属于哪类意图 | 路由后进入不同并行流程 |
| 并行化 | 怎么更快 | 子任务能否同时启动 | 并行结果再交给汇总链 |
它们不是互斥选择。
一个公司调研 Agent 可能先路由判断「这是调研任务」,再并行收集新闻、财务和口碑,最后用提示词链生成摘要、审校和邮件。
可以把这三种模式放在同一条流程里看:
用户输入
↓
路由:判断任务类型
↓
并行化:同时收集互不依赖的材料
↓
提示词链:基于材料逐步生成、检查、改写十、一句话记住并行化
并行化不是让 AI 盲目同时干活,而是把互不依赖的任务同时启动,再用一个明确的汇总步骤收回来。
好的并行化系统要同时回答三个问题:
哪些任务真的互不依赖?
单个任务失败时怎么办?
多个结果回来后谁负责去重、取舍和汇总?