跳到主要内容

02 · Crew 编排(多 Agent 怎么协作)

本章讲什么: 一支 Crew 怎么把若干 Task 和 Agent 组织起来——顺序跑还是交给经理调度、上下文怎么在任务间传、Agent 怎么互相委派、输出怎么校验和聚合。

2.1 它要解决的小问题

单个 Agent(上一章)只能干一件事。真实项目要多步骤、多角色:研究员产出资料、写手据此成文、校对再润色。谁先谁后?后一步怎么拿到前一步的产物?谁来分配活?Crew 就是回答这些编排问题的层。

2.2 入口:kickoff → 选 process

Crew.kickoff(inputs)(crew.py:980)是同步入口。核心分支极简(crew.py:1037-1044):

# crew.py:1037-1044(摘)
if self.process == Process.sequential:
result = self._run_sequential_process()
elif self.process == Process.hierarchical:
result = self._run_hierarchical_process()
else:
raise NotImplementedError(...)

Process 只有两个成员:sequentialhierarchical(process.py,consensual 还是 TODO)。两种流程最终都调同一个 _execute_tasks(crew.py:1485-1492);层级模式只是先多造一个经理 Agent。

kickoff 还做了几件横切的事:开 OpenTelemetry baggage 追踪、进入事件总线运行时作用域、prepare_kickoff 插值 inputs、结束时排空记忆写入 + 清理文件(crew.py:1028-1069)。

2.3 顺序流程(sequential)

_run_sequential_process 直接把任务列表交给 _execute_tasks(self.tasks)(crew.py:1485-1487)。_execute_tasks 是真正的编排循环(crew.py:1529-1598),逐个任务做:

  1. prepare_task_execution 选出该任务的 Agent、工具、是否跳过(crew.py:1554)。
  2. 若是 ConditionalTask,先判条件,不满足就跳过(crew.py:1560-1566,见 2.7)。
  3. 算出上下文 context = self._get_context(task, task_outputs)——即把已完成任务的输出喂给当前任务(crew.py:1585)。
  4. task.execute_sync(agent, context, tools) 同步执行(crew.py:1586)。
  5. 输出存进 task_outputs,记执行日志。

异步任务穿插: 若任务标了 async_execution,用 task.execute_async 拿到 Future 攒着(crew.py:1568-1577);遇到下一个同步任务前,先 _process_async_tasks 把攒着的 future 全部 join(crew.py:1579-1583)。这让相邻的异步任务能并行,但同步任务作为屏障。

2.4 层级流程(hierarchical):自动造一个经理

_run_hierarchical_process_create_manager_agent() 再跑同一个 _execute_tasks(crew.py:1489-1492)。经理 Agent 的创建逻辑(crew.py:1494-1519):

# crew.py:1509-1517(摘)— 自动合成的经理
manager = Agent(
role=i18n.retrieve("hierarchical_manager_agent", "role"),
goal=i18n.retrieve("hierarchical_manager_agent", "goal"),
backstory=i18n.retrieve("hierarchical_manager_agent", "backstory"),
tools=AgentTools(agents=self.agents).tools(), # 关键:经理的工具 = 委派/提问
allow_delegation=True,
llm=self.manager_llm,
verbose=self.verbose,
)

两个真实约束:

  • 经理不能自带普通工具:若你传的 manager_agent 带了 tools,直接抛 Exception("Manager agent should not have tools")(crew.py:1498-1505)。经理的「工具」只能是「把活派给手下」。
  • 层级模式下,任务执行用的 Agent 永远是经理(_get_agent_to_use 在 hierarchical 时返回 self.manager_agent,crew.py:1685-1688)。经理通过委派工具把活分给真正的工人 Agent。

2.5 Agent 之间怎么协作:委派与提问

经理(以及任何 allow_delegation=True 的 Agent)拿到的特殊工具来自 AgentTools.tools()(tools/agent_tools/agent_tools.py:22),就两个:

工具作用文件
DelegateWorkTool把一个子任务整体派给某个「同事」Agent 去做tools/agent_tools/delegate_work_tool.py
AskQuestionTool向某个同事 Agent 提一个问题、拿回答tools/agent_tools/ask_question_tool.py

在顺序模式下,如果某个 Agent 开了 allow_delegation,_add_delegation_tools 会把「除自己外的其他 Agent」作为可委派对象注入委派工具(crew.py:1791-1801)。于是「委派」在实现上就是一个普通工具调用——复用了第 1 章的工具循环,优雅地把多 Agent 协作降维成了「Agent 调了个叫『派活给同事』的工具」。

_prepare_tools(crew.py:1616-1683)是工具装配的总闸:按 Agent 能力依次叠加委派、代码执行、多模态、平台(apps)、MCP、记忆、文件等工具。

2.6 上下文怎么传

任务间的「接力」靠 _get_context:把前序任务的 TaskOutput 拼成文本,作为后一任务提示词的一部分(crew.py:15851569-1571)。顺序模式默认拿到目前所有产出;异步分支只拿最近的同步输出。Task 自身也可显式声明 context=[task_a, task_b] 来精确控制依赖(见 task.py)。

2.7 条件任务(ConditionalTask)

_handle_conditional_task(crew.py:1600-1614)在执行前调 check_conditional_skip:根据前序输出判断该任务是否该跑。若跳过,产出一个「跳过」的占位 TaskOutput 并继续。注意它会先把攒着的异步 future 全部结清,因为条件判断可能依赖那些结果(crew.py:1608-1610)。

2.8 输出校验与聚合:guardrail → CrewOutput

单个 Task 的执行核心在 Task._execute_core(task.py:762):它调 agent.execute_task(...) 拿到原始结果(task.py:790),包成 TaskOutput(带 raw / pydantic / json_dict,task.py:816-826),然后:

  • guardrail:self._guardrailsself._guardrail 逐个校验/修正输出(task.py:828-844)。guardrail 是「输出守门员」——可以是函数或 LLM 判定(LLMGuardrail),不通过可触发重试。
  • 触发 task / crew 级回调,可选写文件(task.py:849-873)。
  • emit TaskCompletedEvent(task.py:874-877)。

所有 TaskOutput 最后由 _create_crew_output(task_outputs) 聚合成一个 CrewOutput(crew.py:1598),result.raw 通常取最后一个任务的输出。kickoff 收尾还会跑 after_kickoff_callbacks 和计算 token 用量(crew.py:1046-1051)。

2.9 其他入口

方法用途位置
kickoff_for_each(inputs: list)对一批输入各跑一次(每次 self.copy()),聚合 usagecrew.py:1074
kickoff_async把同步 kickoff 丢到线程跑;流式时返回 CrewStreamingOutputcrew.py:1110
akickoff原生异步:全程 async,不靠线程包装crew.py:1190
train / test / replay训练(带人类反馈迭代)、评测、从某任务重放crew.py:928 / 2114 / 1918

→ 下一章:另一套范式 Flow,它不靠「任务列表」而靠「方法间的事件触发」来编排。