做 crewAI 项目一年多,我最大的体会是:很多人把精力全花在 Agent 定义上,角色、目标、背景故事写得花团锦簇,但 Task 一个个写得极其潦草——description 一句话带过,expected_output 随便填两个字,context 完全不串。结果就是整个 Crew 跑起来像一盘散沙,Agent 各说各话,拿到的下游结果永远是"一段文字",而不是"你需要的数据"。
Task 才是 crewAI 系统里真正决定上限的东西。它不只是"让 agent 干一件事"的指令,更像是一个数据流转的契约:上游任务输出什么、下游任务消费什么、什么时候并行、什么时候等待,全靠 Task 的字段和依赖关系来驱动。这篇文章我把 crewAI 里 Task 设计和上下文传递的核心逻辑拆开讲,包括 expected_output 怎么写、context 怎么串、异步任务怎么排,以及我在实践中踩过的坑。
1. Task 在 crewAI 中的定位:先搞清楚它解决什么问题
1.1 Task 不是"提示词",而是工作流的最小单元
crewAI 的整体结构是 Crew 包含多个 Agent,Agent 之间通过 Task 来协作。如果你只是把 Task 理解成"丢给大模型的提示词",那方向就错了。Task 在 crewAI 里承担了三层职责:第一,它是一个执行单元,描述要做什么、谁来做、做成什么样;第二,它是一个数据接口,它的输出会被其他 Task 消费,形成依赖关系;第三,它是流程控制的基础,通过 async_execution 和 context 可以编排并行、串行、条件等待这些复杂逻辑。
所以设计 Task 的时候,你其实是在设计整个 Crew 的数据流图。先想清楚信息的来源、流向、消费方式,再回头写 Task 的具体描述,这个顺序不能反。我见过不少反着来的项目,Task 写好了才发现数据传不下去,最后只能靠拼字符串硬塞,整个工程就废了。
1.2 Task 的核心属性拆解
一个 Task 常见字段如下,每个字段都有它的作用和坑:
- description:要完成的具体任务说明。这个字段不是随便写,需要包含背景、输入、约束,否则 Agent 发挥空间太大。
- expected_output:任务的输出验收标准。它决定了 Agent 认为"做到什么程度算完成",是整个 Task 的灵魂。
- agent:负责执行该任务的 Agent。不指定则默认由 Crew 的 process 分配合适的 Agent。
- context:依赖的上游 Task 列表。这些 Task 的输出会作为上下文输入给当前任务。
- async_execution:是否异步执行。异步任务不会阻塞主流程,但必须被其他 Task 引用,否则永远不会执行。
- output_pydantic / output_json:将输出解析为结构化数据(Pydantic 模型或 JSON 对象)。
- callback:任务完成后的回调函数,可以用于日志、通知、存储。
理解每个字段的关键,在于理解它背后对应的执行机制。比如 context 不只是"拼接文本",它控制的是任务依赖和调度顺序;expected_output 也不只是"给模型一个要求",它还影响到 crewAI 内部对输出质量的判断和后续任务的输入格式。
1.3 一句话概括 Task 设计与数据流的关系
Task 的数据流关系可以通过两种方式建立:一种是显式的 context=[task_a, task_b],另一种是隐式的 agent 记忆和 Crew 共享状态。显式依赖是推荐做法,因为它的执行顺序是确定的、可预期的;隐式依赖则依赖 LLM 的记忆能力,在任务链变长之后非常不可靠,容易丢信息。
后面几节我会展开讲这些机制,并给出一套可以直接抄的实践模板。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 输出期待(expected_output)设计:决定下游能拿到什么
2.1 expected_output 的本质:给 LLM 一个验收标准
很多初学者把 expected_output 写成"一份报告"或"一个列表",这等于没写。LLM 在生成时非常依赖字面约束,你给它多大的自由,它就会给你多大的混乱。expected_output 的实质,是让模型在生成前就明确"我的输出会被谁、以什么方式消费",这样模型才会主动控制格式和结构化程度。
我的一般写法是:expected_output 里包含三个要素——结构(输出是什么类型,列表/JSON/报告)、字段(逐项列出关键信息)、长度或边界(大约多少字、不超过几条)。举个例子:
text复制任务:调研 2025 年 AI Agent 赛道融资动态。
错误 expected_output:融资信息列表。
正确 expected_output:包含 5 条融资动态的清单,每条动态必须包含以下字段:
公司名、融资金额、投资方、公布时间、一句话业务说明。
总长度不超过 500 字。输出格式为 markdown 有序列表。
你可能会觉得这样写很啰嗦,但实测下来,写得越具体的 expected_output,模型一次成型率越高。尤其是下游要用代码解析数据时,含糊的输出格式意味着你要写更多清洗逻辑,成本远高于多写几十个字。
2.2 三个层面的 expected_output 设计
第一层是自然语言描述,也就是上面那种写法,适合下游还是给人看的场景。第二层是配合 output_pydantic 或 output_json,要求模型输出结构化 JOSN 甚至直接校验模型,这是让数据可编程的关键。第三层是配合 callback,在任务完成后自动做校验和入库,形成闭环。
实际项目里,我通常这样组合:
python复制from crewai import Task
from pydantic import BaseModel
class FundingNews(BaseModel):
company: str
amount: str
investor: str
date: str
comment: str
research_task = Task(
desc
