并行化(Parallelization):让 AI 同时处理互不依赖的事

有些 Agent 任务慢,不是因为模型不够聪明,而是因为它把所有事情排成了一条队。

比如做一份公司调研:

查新闻 -> 查财报 -> 查产品 -> 查用户评价 -> 汇总

如果这四个查询互不依赖,就没必要一个等一个。它们可以同时开始,最后再汇总。

这就是并行化:

把互不依赖的任务同时执行,再把结果合并成最终输出。

并行化解决的是「怎么更快」的问题。但它不是把所有任务都同时跑。能不能并行,关键看依赖、共享状态、成本和失败影响。

说得再生活一点:并行化不是让一群人同时挤进厨房抢一口锅,而是让一个人洗菜、一个人备料、一个人查菜谱,最后再由主厨把东西收回来。真正省时间的前提,是大家做的事彼此不打架。

一、先看一个耗时对比

假设四个子任务各需要 10 秒:

做法执行方式理想耗时风险
串行A -> B -> C -> D40 秒稳,但慢
并行A/B/C/D 同时跑10 秒左右需要处理失败、限流、汇总
半并行A/B 同时跑,C 依赖 A,D 依赖 B20 秒左右需要先画依赖关系

并行化的价值不只是快。它还让任务边界更清楚:每个子任务负责一种资料、一个维度或一批样本,最终汇总器再做取舍。

二、判断哪些任务能并行

先不要急着写代码。先问这几个问题:

判断项可以并行不适合并行
依赖关系子任务不依赖彼此输出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 适合「不同任务同时做,最后汇总」。

流程图画布 · 115%
Mermaid 流程图加载中...

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 盲目同时干活,而是把互不依赖的任务同时启动,再用一个明确的汇总步骤收回来。

好的并行化系统要同时回答三个问题:

哪些任务真的互不依赖?
单个任务失败时怎么办?
多个结果回来后谁负责去重、取舍和汇总?