CrewAI 协作模式:顺序、层级、异步
一、概念速查
Process 类型速查
| 模式 | 调度方式 | 依赖表达 | 输出聚合 |
|---|---|---|---|
| Process.sequential | Task 列表按声明顺序逐个串行执行 | context 参数声明前驱 |
自动拼接各 Task 输出 |
| Process.hierarchical | Manager Agent 运行时动态分配 Task 并协调 | Manager 自行决策 | Manager 收集后统一返回 |
| Process.consensual (已弃用) | 基于讨论的共识路由,0.30+ 移除 | --- | --- |
CrewAI 在 0.30+ 中重构了 Process 层,废弃 consensual 模式,保留三种活跃模式:sequential、hierarchical 和新引入的 async。
Sequential 模式
顺序模式是最简单可靠的协作方式。Task 按照在列表中声明的顺序逐个执行,前一个 Task 的输出可以通过 context 参数传递给后一个 Task。这种模式适合具有明确流水线关系的任务场景,比如先调研后撰写,或者先采集数据后分析。每个 Task 的 Agent 执行结果会按序写入共享结果池,后续 Task 可以引用。
python
from crewai import Agent, Task, Crew, Process # crewai>=0.30.0
researcher = Agent(role="研究员", goal="收集数据", backstory="资深研究背景")
writer = Agent(role="写手", goal="撰写报告", backstory="技术写作背景")
research = Task(
description="调研 CrewAI 三种 Process 的性能差异",
expected_output="性能对比表格",
agent=researcher,
)
write = Task(
description="基于调研结果撰写分析报告",
expected_output="1500 字报告文档",
agent=writer,
context=[research], # 声明依赖:write 依赖 research 的输出
)
crew = Crew(
agents=[researcher, writer],
tasks=[research, write],
process=Process.sequential,
)
result = crew.kickoff()
Hierarchical 模式
层级模式引入一个 Manager Agent 来分配任务。Manager 负责理解任务描述、评估各 Agent 的能力与角色匹配度,然后动态分配任务并监控执行过程。这种方式适合任务分配方案不确定或者需要根据中间结果动态调整的场景。Manager Agent 可以自动生成也可以由用户自定义注入。
python
from crewai import Agent, Task, Crew, Process # crewai>=0.30.0
manager = Agent(
role="项目经理",
goal="协调团队高效完成任务",
backstory="你是一位资深技术经理,擅长分配任务和把控质量",
allow_delegation=True,
)
coder = Agent(role="工程师", goal="编写代码", backstory="全栈开发背景")
tester = Agent(role="测试", goal="验证代码质量", backstory="QA 背景")
dev_task = Task(
description="实现用户登录功能",
expected_output="可运行的登录模块代码",
agent=coder,
)
test_task = Task(
description="为登录功能编写单元测试",
expected_output="测试用例通过的报告",
agent=tester,
)
crew = Crew(
agents=[coder, tester],
tasks=[dev_task, test_task],
process=Process.hierarchical,
manager_agent=manager, # 显式指定 Manager
# 不传 manager_agent 时由框架自动生成
)
Async 模式
异步模式并非独立的 Process 类型,而是 Task 级别的并行执行开关。当多个 Task 没有依赖关系时,将它们标记为 async_execution=True 可以让这些 Task 同时执行,缩短总耗时。注意 async Task 之间不能相互依赖,否则会触发依赖环检测异常。这个模式特别适合数据采集场景------同时从多个数据源抓取信息,最后统一合并。
python
from crewai import Agent, Task, Crew, Process # crewai>=0.30.0
scraper = Agent(role="爬虫", goal="抓取数据", backstory="数据采集专家")
analyzer = Agent(role="分析师", goal="分析趋势", backstory="数据分析师")
fetch_a = Task(
description="抓取电商平台 A 的手机价格",
expected_output="价格表",
agent=scraper,
async_execution=True,
)
fetch_b = Task(
description="抓取电商平台 B 的手机价格",
expected_output="价格表",
agent=scraper,
async_execution=True,
)
merge = Task(
description="合并两份价格数据并输出比价报告",
expected_output="比价报告",
agent=analyzer,
context=[fetch_a, fetch_b],
)
crew = Crew(
agents=[scraper, analyzer],
tasks=[fetch_a, fetch_b, merge],
process=Process.sequential, # async 由 Task 级 async_execution 控制
)
二、底层原理
Process 引擎调度流程
三种 Process 共享同一内核引擎,区别在于 Task 出队策略 和 分配决策主体。
#mermaid-svg-DNbPfyKlGVjK6wzI{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-DNbPfyKlGVjK6wzI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DNbPfyKlGVjK6wzI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DNbPfyKlGVjK6wzI .error-icon{fill:#552222;}#mermaid-svg-DNbPfyKlGVjK6wzI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DNbPfyKlGVjK6wzI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DNbPfyKlGVjK6wzI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DNbPfyKlGVjK6wzI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DNbPfyKlGVjK6wzI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DNbPfyKlGVjK6wzI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DNbPfyKlGVjK6wzI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DNbPfyKlGVjK6wzI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DNbPfyKlGVjK6wzI .marker.cross{stroke:#333333;}#mermaid-svg-DNbPfyKlGVjK6wzI svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DNbPfyKlGVjK6wzI p{margin:0;}#mermaid-svg-DNbPfyKlGVjK6wzI .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-DNbPfyKlGVjK6wzI .cluster-label text{fill:#333;}#mermaid-svg-DNbPfyKlGVjK6wzI .cluster-label span{color:#333;}#mermaid-svg-DNbPfyKlGVjK6wzI .cluster-label span p{background-color:transparent;}#mermaid-svg-DNbPfyKlGVjK6wzI .label text,#mermaid-svg-DNbPfyKlGVjK6wzI span{fill:#333;color:#333;}#mermaid-svg-DNbPfyKlGVjK6wzI .node rect,#mermaid-svg-DNbPfyKlGVjK6wzI .node circle,#mermaid-svg-DNbPfyKlGVjK6wzI .node ellipse,#mermaid-svg-DNbPfyKlGVjK6wzI .node polygon,#mermaid-svg-DNbPfyKlGVjK6wzI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-DNbPfyKlGVjK6wzI .rough-node .label text,#mermaid-svg-DNbPfyKlGVjK6wzI .node .label text,#mermaid-svg-DNbPfyKlGVjK6wzI .image-shape .label,#mermaid-svg-DNbPfyKlGVjK6wzI .icon-shape .label{text-anchor:middle;}#mermaid-svg-DNbPfyKlGVjK6wzI .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-DNbPfyKlGVjK6wzI .rough-node .label,#mermaid-svg-DNbPfyKlGVjK6wzI .node .label,#mermaid-svg-DNbPfyKlGVjK6wzI .image-shape .label,#mermaid-svg-DNbPfyKlGVjK6wzI .icon-shape .label{text-align:center;}#mermaid-svg-DNbPfyKlGVjK6wzI .node.clickable{cursor:pointer;}#mermaid-svg-DNbPfyKlGVjK6wzI .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-DNbPfyKlGVjK6wzI .arrowheadPath{fill:#333333;}#mermaid-svg-DNbPfyKlGVjK6wzI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-DNbPfyKlGVjK6wzI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-DNbPfyKlGVjK6wzI .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DNbPfyKlGVjK6wzI .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-DNbPfyKlGVjK6wzI .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DNbPfyKlGVjK6wzI .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-DNbPfyKlGVjK6wzI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-DNbPfyKlGVjK6wzI .cluster text{fill:#333;}#mermaid-svg-DNbPfyKlGVjK6wzI .cluster span{color:#333;}#mermaid-svg-DNbPfyKlGVjK6wzI div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-DNbPfyKlGVjK6wzI .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-DNbPfyKlGVjK6wzI rect.text{fill:none;stroke-width:0;}#mermaid-svg-DNbPfyKlGVjK6wzI .icon-shape,#mermaid-svg-DNbPfyKlGVjK6wzI .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DNbPfyKlGVjK6wzI .icon-shape p,#mermaid-svg-DNbPfyKlGVjK6wzI .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-DNbPfyKlGVjK6wzI .icon-shape .label rect,#mermaid-svg-DNbPfyKlGVjK6wzI .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DNbPfyKlGVjK6wzI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-DNbPfyKlGVjK6wzI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-DNbPfyKlGVjK6wzI :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} sequential
hierarchical
async
否
是
是
否
crew.kickoff()
ProcessEngine
解析 Task 列表
Process 类型
按声明顺序
单线程出队
Manager Agent
生成分配计划
标记 async_execution
的 Task 同时出队
检查 Task.context
所有前驱已完成?
Manager 指派
Agent + 下发指令
await asyncio.gather()
并行执行
阻塞等待
前驱完成
Agent 执行
ReAct 循环
写入 TaskOutput
到共享结果池
还有未完成的
Task?
聚合 TaskOutput
返回 CrewOutput
Sequential 的 Task 依赖链
sequential 模式的核心机制是 Task.context 参数组成的 有向无环图(DAG)。ProcessEngine 在 kickoff 时遍历所有 Task:
- 收集每个 Task 的
context引用的前驱 Task 列表 - 构建依赖图,拓扑排序后按序执行
- 当 Task 的
context为空或所有前驱已完成,该 Task 立即出队执行 - 当前驱未完成时,引擎挂起当前 Task,切换到下一个就绪 Task
这意味着 sequential 并非死板的列表顺序 ------ 只要依赖允许,引擎会 跳跃执行 可并行的小批量。例如 TaskA, B, C 中 B 依赖 A 但 C 无依赖,实际执行顺序是 A→C→B 或 A→(B|C) 并发。
Hierarchical 的 Manager Agent
hierarchical 模式引入一个 调度 Agent(Manager),其职责不是执行具体 Task,而是:
- 解读当前 Crew 目标和 Task 描述
- 为每个 Task 选择最合适的 Agent(基于 Agent 的
role、goal、backstoryembedding) - 将 Task 描述改写为具体指令下发给目标 Agent
- 检查 Agent 输出质量,不合格时要求重做或换人
Manager Agent 有两种来源:
| 方式 | 代码 | 行为 |
|---|---|---|
| 自动生成 | Crew(process=Process.hierarchical) |
框架创建一个默认 Manager,使用与 Crew 相同的 LLM,goal 和 backstory 硬编码 |
| 自定义注入 | Crew(process=Process.hierarchical, manager_agent=my_manager) |
传入自定义 Agent 实例,自由控制 Manager 的 LLM、工具、人格 |
自定义 Manager 的意义在于:生产环境中 Manager 应使用更强模型(如 GPT-4 而非 GPT-4o-mini),因为分配决策的质量直接决定整个 Crew 的产出上限。Manager 的 allow_delegation 必须为 True,否则 hierarchical 模式无法工作。
Async 的并行与聚合
async 模式不是独立的 Process 类型,而是 Task 级别的执行开关 。在 sequential 或 hierarchical 框架下,将单个 Task 的 async_execution=True 即可触发并行:
- 引擎使用
asyncio.gather()/ 线程池同时启动所有标记为async_execution=True的 Task - 这些 Task 通常分配给同一个 Agent(如爬虫同时抓取多个 URL),或分配给不同 Agent
- 所有 async Task 完成后,引擎将它们的结果写入共享结果池,后续依赖这些结果的其他 Task 读取聚合
关键约束:async Task 之间不能互相依赖 。所有 async Task 在出队时必须是互不依赖的叶子节点。引擎在 kickoff 阶段会做依赖环检测,如果检测到 async Task 之间存在 context 交叉引用,抛出 CrewDependencyError。
输出聚合机制
无论哪种 Process,最终产物都是 CrewOutput 对象:
CrewOutput
├── tasks_output: List[TaskOutput] # 每个 Task 的原始输出
│ ├── task_output.description # Task 描述
│ ├── task_output.raw # Agent 生成的原始文本
│ ├── task_output.agent # 执行 Agent 名称
│ └── task_output.summary # LLM 生成的摘要
├── raw: str # 全部 TaskOutput.raw 的拼接
└── json_dict: dict # 结构化输出(若 Task 指定了 JSON)
sequential 模式下自动拼接所有 Task 的输出字符串;hierarchical 模式下 Manager 可能对最终输出做额外整合;async 模式下引擎按 Task 声明顺序排列输出而非执行完成顺序。
三、架构设计原则
Process 选择指南
| 条件 | 推荐模式 | 理由 |
|---|---|---|
| Task 数量少(<10)且依赖关系明确 | sequential | 调度开销接近零,行为可预测 |
| Task 数量多且执行时间不均 | sequential + async_execution | 让长 Task 与短 Task 重叠执行 |
| Task 分配策略需要动态决策 | hierarchical | Manager 根据中间结果调整人力分配 |
| Agent 能力差异大,需质检把关 | hierarchical + 自定义 Manager | Manager 可要求重做或重新分配 |
| 纯数据并行(同一 Agent 处理多来源) | sequential + 多 async Task | 爬虫/扫描类场景最适用 |
| 首次搭建,不确定怎么选 | sequential | 下限最高,refactor 到 hierarchical 成本低 |
性能对比
| 维度 | sequential | hierarchical | async |
|---|---|---|---|
| 额外 LLM 调用次数 | 0 | 每个 Task 至少 1 次(Manager 决策) | 0 |
| 端到端延迟 | ∑(各 Task 耗时) | ∑(各 Task 耗时) + Manager 开销 | max(async Task 耗时) + 后续 Task 耗时 |
| 确定性 | 完全确定 | 依赖 Manager 模型,可能不稳定 | 完全确定(给定输入和种子) |
| Task 间数据传递 | context 显式传递 | Manager 上下文隐式携带 | context 显式传递 |
| 调试难度 | 低 | 中(需追踪 Manager 决策链) | 中(并发日志交错) |
| 错误恢复 | Task 级重试 | Manager 可重新分配 | Task 级重试 |
混杂模式的最佳实践
顺序 + 异步的组合是实际项目中最常见的模式:主体流程用 sequential 保证依赖正确性,将 I/O 密集型子任务标记为 async 并行执行。
python
from crewai import Agent, Task, Crew, Process # crewai>=0.30.0
# 爬虫 Agent 并行抓取多个源
scraper = Agent(role="爬虫", goal="抓取网页内容", backstory="数据采集专家")
writer = Agent(role="编辑", goal="整合信息产出报告", backstory="资深编辑")
tasks = []
sources = ["https://source-a.com", "https://source-b.com", "https://source-c.com"]
for i, url in enumerate(sources):
tasks.append(Task(
description=f"抓取 {url} 的内容",
expected_output="网页正文",
agent=scraper,
async_execution=True,
))
tasks.append(Task(
description="整合所有来源的内容,撰写一份综合报告",
expected_output="综合报告",
agent=writer,
context=tasks[:3], # 依赖所有爬虫任务完成
))
crew = Crew(
agents=[scraper, writer],
tasks=tasks,
process=Process.sequential, # 主体串行,子任务并行
)
避坑清单
- Manager 与 Worker 用同一个 LLM:Manager 决策失误会拖累所有 Worker。自定义 Manager 时至少比 Worker 用一个级别的模型。
- 不设 max_iter:Agent 在复杂 Task 上可能陷入无限推理循环,导致 Token 消耗失控。
- async Task 过多:并发数受 LLM API 速率限制和 Agent 的 tool 调用瓶颈约束,建议上限 5-8 个并行 Task。
- 忽略 context 顺序 :多个
context中 Task 的执行结果按tasks_output中的索引顺序排列,不是按完成时间。 - hierarchical 模式下使用 memory=False:Manager 需要跨 Task 记忆上下文才能做出连贯分配决策,关闭 memory 会严重降低分配质量。