crewAI Task 设计与上下文传递:输出期待与任务链依赖
本文基于 crewAI v1.11.0 官方文档,系统拆解 Task 的五大要素、上下文传递机制和结构化输出校验实践。
一、Task 是什么:工作单元还是数据总线
很多人把 crewAI 的 Task 简单理解为"给 Agent 的一段指令"。这个认知让你只用了 Task 20% 的能力。
Task 的真正定位有两个维度:
第一维:工作单元——定义一件事"要做什么"和"做成什么样",是驱动 Agent 行动的核心输入。
第二维:数据总线——Task 的输出不只是最终结果,它会通过context机制自动流入下游 Task 的上下文,成为整个多智能体协作系统中信息流转的数据通道。
理解了这两个维度,你才能设计出真正高效的多智能体工作流。
二、Task 五要素详解
2.1 description:驱动推理的"任务说明书"
fromcrewaiimportTask research_task=Task(description=""" 你需要对 {topic} 这个技术方向进行深度研究。 研究维度要求: 1. 技术成熟度评估(TRL 1-9 标准) 2. 主要开源实现(GitHub 星数 > 5000 优先) 3. 商业化落地案例(至少 3 个) 4. 核心技术挑战和未解决的问题 研究时间范围:过去 12 个月内的信息为主,基础理论部分不限时间。 避免:不要引用未经验证的消息源,不要做过度乐观的技术预测。 """,...)description 的编写原则:
- 具体化任务边界:告诉 Agent 做什么,不做什么(避免领域)
- 提供评估标准:如"技术成熟度 TRL 标准",让 Agent 用明确的框架思考
- 注入偏好约束:如"避免过度乐观的预测",直接影响输出质量
- 善用 Jinja2 变量:
{topic}这类占位符让 Task 模板化、可复用
2.2 expected_output:定义"完成"的标准
expected_output 是 crewAI Guardrails 机制的基础——Agent 会持续工作直到输出符合这里描述的预期。
research_task=Task(description="...",expected_output=""" 一份结构化的技术调研报告,必须包含以下部分: ## 技术概述(200-300字) ## 成熟度评估(TRL 等级 + 判断依据) ## 主要实现方案(表格格式,含GitHub链接) ## 商业案例(3-5个,含公司名、应用场景、规模估算) ## 核心挑战(bullet list,每条给出技术解释) ## 作者结论(100字以内,给出明确的"值得/不值得深入"判断) 输出格式:Markdown。不需要引言和致谢。 """,agent=researcher)关键点:expected_output 越具体,输出质量越稳定。含糊的"写一份报告"和具体的"包含6个指定章节、字数范围、格式要求的报告",最终质量差异显著。
2.3 agent:执行者的精确指派
research_task=Task(description="...",expected_output="...",agent=researcher# 明确指派给 researcher Agent)在 Sequential 流程中,agent 是必填的。在 Hierarchical 流程中,Manager Agent 会根据任务内容和 Agent 能力自动分配,此时可以省略 agent 参数。
2.4 context:任务链的数据传递
context是 crewAI 中最精妙的设计之一。它建立了 Task 之间的显式依赖关系,并自动完成数据传递:
# Task 1:研究research_task=Task(description="研究 {topic} 的技术现状",expected_output="包含技术要点的调研报告",agent=researcher)# Task 2:分析(依赖 Task 1 的输出)analyze_task=Task(description=""" 基于已有的调研报告,从投资价值角度进行深度分析。 重点评估:技术壁垒、市场规模、竞争格局。 """,expected_output="投资分析报告(含明确的投资建议)",agent=analyst,context=[research_task]# research_task 的输出自动注入此处)# Task 3:撰写(同时依赖 Task 1 和 Task 2)write_task=Task(description="将技术调研和投资分析整合为面向高管的简报",expected_output="500字以内的执行摘要",agent=writer,context=[research_task,analyze_task]# 同时使用两个上游任务的输出)context 的数据流机制:
research_task.output │ ▼ [自动追加到 analyze_task 的 Prompt 中] "以下是已完成任务的输出,请基于此继续工作: [research_task 的输出内容]" │ ▼ analyze_task 执行 → analyze_task.output │ ▼ [自动追加到 write_task 的 Prompt 中] "以下是已完成任务的输出: [research_task 输出] [analyze_task 输出]"这个机制让信息传递完全声明式——你只需要声明依赖关系,crewAI 负责数据流转,无需任何手动拼接 Prompt 的代码。
2.5 tools:Task 级别的工具覆盖
Task 级别的 tools 配置优先级高于Agent 级别的默认工具:
# Agent 有通用工具集researcher=Agent(role="研究员",tools=[SerperDevTool(),ScrapeWebsiteTool(),FileReadTool()])# 特定 Task 只允许用搜索工具(安全限制)public_research_task=Task(description="只从公开互联网搜索信息",expected_output="...",agent=researcher,tools=[SerperDevTool()]# 覆盖 Agent 的工具集,只允许搜索)# 另一个 Task 需要额外工具internal_research_task=Task(description="结合内部数据库和互联网信息",expected_output="...",agent=researcher,tools=[SerperDevTool(),internal_db_tool]# 扩展工具集)三、结构化输出:Pydantic 校验与类型安全
当你需要 Task 的输出具有严格的结构(如要存入数据库、传给下游 API),可以使用 Pydantic 模型定义输出格式:
fromcrewaiimportTaskfrompydanticimportBaseModel,FieldfromtypingimportListclassTechTrend(BaseModel):name:str=Field(description="技术名称")trl_level:int=Field(description="技术成熟度等级 1-9",ge=1,le=9)github_url:str=Field(description="主要 GitHub 仓库地址")summary:str=Field(description="100字以内的技术概述")classResearchReport(BaseModel):topic:strtrends:List[TechTrend]=Field(description="识别到的技术趋势列表")conclusion:str=Field(description="作者的综合结论")confidence_level:float=Field(description="作者对结论的信心度 0-1",ge=0,le=1)research_task=Task(description="研究 {topic} 的技术趋势",expected_output="结构化的技术趋势分析报告",agent=researcher,output_pydantic=ResearchReport# 指定 Pydantic 输出模型)# 执行后,result 是 ResearchReport 实例,类型安全result=crew.kickoff(inputs={"topic":"具身智能"})report:ResearchReport=research_task.output.pydanticprint(report.trends[0].trl_level)# 有类型提示,IDE 可自动补全四、任务模板化:Jinja2 变量注入
crewAI 的 Task description 和 expected_output 都支持 Jinja2 模板语法,这让同一套 Task 定义可以服务于不同的运行时参数:
# 可复用的研究 Task 模板research_task=Task(description=""" 对 {{topic}} 进行深度技术研究,时间范围:过去 {{months}} 个月。 重点关注 {{focus_area}} 方向,输出语言:{{language}}。 """,expected_output="结构化调研报告",agent=researcher)# 不同参数运行同一个 Taskcrew.kickoff(inputs={"topic":"大语言模型量化压缩","months":6,"focus_area":"边缘设备部署","language":"中文"})crew.kickoff(inputs={"topic":"具身智能","months":12,"focus_area":"工业机器人应用","language":"英文"})五、回调机制:任务完成后的处理链
Task 支持 callback 函数,在任务完成后异步触发:
importjsonfromdatetimeimportdatetimedefsave_research_result(task_output):"""任务完成后自动保存结果到文件"""timestamp=datetime.now().strftime("%Y%m%d_%H%M%S")filename=f"research_{timestamp}.json"withopen(filename,'w',encoding='utf-8')asf:json.dump({"task":"research","output":task_output.raw,"timestamp":timestamp},f,ensure_ascii=False,indent=2)print(f"研究结果已保存到{filename}")defnotify_slack(task_output):"""任务完成后发送 Slack 通知"""slack_client.post_message(channel="#ai-research",text=f"研究任务完成!\n摘要:{task_output.raw[:200]}...")research_task=Task(description="...",expected_output="...",agent=researcher,callback=save_research_result# 任务完成后自动调用)六、YAML 配置方式
与 Agent 类似,Task 的生产环境推荐使用 YAML 声明:
# config/tasks.yamlresearch_task:description:>对 {topic} 进行深度技术研究,时间范围:过去 {months} 个月。 重点关注技术成熟度、主要实现和商业案例。expected_output:>包含以下章节的 Markdown 报告: 技术概述、成熟度评估、主要实现方案(表格)、 商业案例(3-5个)、核心挑战、作者结论agent:researcheranalyze_task:description:>基于已有的调研报告,从投资价值角度进行深度分析。 重点评估:技术壁垒、市场规模、竞争格局。expected_output:>投资分析报告,含明确的"值得/不值得"投资建议agent:analystcontext:[research_task]@CrewBaseclassTechResearchCrew:tasks_config='config/tasks.yaml'@taskdefresearch_task(self)->Task:returnTask(config=self.tasks_config['research_task']# callback 等代码逻辑仍在 Python 中配置)@taskdefanalyze_task(self)->Task:returnTask(config=self.tasks_config['analyze_task'],callback=notify_slack)七、任务链设计的常见模式
7.1 线性管道(最常见)
Task A → Task B → Task C(顺序依赖)适合:研究→分析→写作、采集→处理→报告
7.2 扇出-汇聚(并行后合并)
→ Task B(分支1)─┐ Task A ─── → Task C(分支2)─┼→ Task E(汇聚) → Task D(分支3)─┘实现方式:Task E 的 context 中同时引用 B、C、D:
Task(context=[task_b,task_c,task_d])7.3 条件分支(结合 Flows)
对于需要条件判断的复杂流程,推荐结合 Flows 的@router装饰器,在 Flow 层面做分支控制,Crew 负责每个分支内的具体执行。
八、常见设计错误
错误一:expected_output 过于模糊
# ❌ LLM 不知道"完成"的标准是什么expected_output="关于这个话题的分析"# ✅ 具体到格式、字数、必含要素expected_output=""" 包含5个以上论点的分析报告(800-1200字)。 每个论点必须有数据支撑。格式:Markdown,使用 H2 标题分隔论点。 """错误二:不必要的 context 依赖
# ❌ Task C 其实不需要 Task A 的输出,但却添加了依赖task_c=Task(context=[task_a,task_b])# task_a 是无关的# ✅ 只声明真正需要的依赖task_c=Task(context=[task_b])不必要的 context 会使 LLM 的上下文窗口被无关信息占据,增加 Token 消耗且可能干扰输出质量。
错误三:单个 Task 承担太多职责
# ❌ 一个 Task 同时做研究+分析+写作,质量难以保证mega_task=Task(description="研究、分析并写出一篇完整的技术报告")# ✅ 拆分为多个专注的 Taskresearch_task=Task(description="研究阶段:收集原始信息")analyze_task=Task(description="分析阶段:提炼洞见")write_task=Task(description="写作阶段:组织为可读文章")九、小结
Task 是 crewAI 中连接"Agent 能力"和"业务需求"的桥梁。设计好 Task 意味着:
- description 精准:给 Agent 明确的任务边界和评估框架
- expected_output 具体:把"完成"的标准描述清楚,而非留给 Agent 自己判断
- context 按需声明:显式建立依赖关系,让信息流转零代码
- 适当使用 Pydantic:需要结构化输出时,类型安全比自由文本可靠得多
Task 设计能力是区分 crewAI 初级用户和高级用户的核心差异。一个精心设计的 Task 链,可以让能力普通的模型产出超出预期的质量;而设计糟糕的 Task,就算用最强的模型也会返回混乱的输出。
系列导航
- 上一篇:crewAI Agent 深度定义:角色、目标、背景故事与工具绑定
- 下一篇:crewAI 顺序与层级流程:Sequential vs Hierarchical 决策矩阵
基于 crewAI v1.11.0 官方文档,撰写于 2026 年 3 月