第 4 章 · Task 深入:任务定义与上下文
本章目标:掌握 description 与 expected_output 的写法如何决定输出质量,学会用 context 构建任务依赖链、用 async_execution 并行加速、用 human_input 与 callback 增加人工与程序控制点。
4.1 Task 的核心属性全景
如果说 Agent 是"员工",Task 就是交给他的"工单"。官方属性表中,日常最常用的是这些:
| 属性 | 类型 | 说明 |
|---|---|---|
description | str(必填) | 任务内容描述,支持 {占位符} 插值 |
expected_output | str(必填) | 完成标准的详细描述,直接写入提示词 |
agent | Agent | 执行该任务的 agent |
context | List[Task] | 额外注入哪些任务的输出作为上下文 |
tools | List[BaseTool] | 本任务专属工具(覆盖 agent 默认工具集) |
human_input | bool | 完成前是否需要人工确认 |
output_file | str | 把输出写入指定文件 |
callback | Callable | 任务完成后的回调函数 |
async_execution | bool | 是否异步并行执行 |
output_pydantic / output_json | BaseModel | 结构化输出(第 12 章详解) |
注意 description 和 expected_output 是必填项——CrewAI 用它们构造提示词,缺一不可。
4.2 description 与 expected_output 的写法
任务文本的质量 = 输出质量的上限。对比:
python
from crewai import Task
# ❌ 反例:描述含糊,标准缺失
vague_task = Task(
description="分析一下这份销售数据。",
expected_output="一份分析报告。",
agent=analyst,
)
# ✅ 正例:背景 + 动作 + 约束 + 标准
precise_task = Task(
description=(
"分析附件中的 2024 年 Q3 销售数据({region} 区域)。"
"1) 计算环比增长率最高的三个品类;"
"2) 找出退货率超过 8% 的SKU并列出可能原因;"
"3) 所有结论必须给出数据出处(表格行号)。"
),
expected_output=(
"Markdown 报告,包含:"
"## 增长Top3(表格:品类/环比/依据行号)、"
"## 高退货SKU(表格+原因假设)、"
"## 建议行动(不超过3条)。"
"总长度不超过 600 字。"
),
agent=analyst,
)三条实战原则:
- 把"怎么算完成"写进 expected_output:格式(表格/列表/Markdown)、长度、必须包含的段落;
- description 里给动作分解(1/2/3 步骤),模型对编号指令的遵循度远高于一段散文;
- 占位符插值:
{region}等变量在kickoff(inputs={...})时替换,让任务定义可复用。
4.3 用 context 构建任务依赖链
默认情况下,顺序流程中前一个任务的输出自动流入下一个任务。但当依赖关系不是"紧邻上一个"时,就要显式声明 context:
python
research_ai = Task(description="调研 AI Agent 赛道动态。", expected_output="5 条要点", agent=researcher)
research_ops = Task(description="调研运维自动化市场。", expected_output="5 条要点", agent=researcher)
# 汇总任务同时依赖两个上游任务
merge_task = Task(
description="合并两份调研,输出统一的赛道综述。",
expected_output="不超过 500 字的综述,分'共性趋势'与'差异机会'两节。",
agent=writer,
context=[research_ai, research_ops], # 显式声明双依赖
)context=[...] 的语义是:这些任务全部完成后,它们的输出会被拼接注入本任务的上下文。这是构建 DAG(有向无环图)式工作流的基础工具。
4.4 并行任务:async_execution
互相独立的上游任务可以并行执行,显著缩短总时长:
python
t1 = Task(
description="调研 A 方向",
expected_output="3 条要点",
agent=researcher,
async_execution=True, # 异步执行,不阻塞主链
)
t2 = Task(
description="调研 B 方向",
expected_output="3 条要点",
agent=researcher,
async_execution=True, # 与 t1 并行
)
summary = Task(
description="汇总 A、B 两方向调研结果。",
expected_output="200 字综述",
agent=writer,
context=[t1, t2], # 汇总任务会等待两个异步任务完成
)规则:异步任务由"下一个引用它的同步任务"触发等待。上面 summary 是同步任务且 context 引用了 t1、t2,所以它成为汇合点。若所有任务都异步且无人汇合,Crew 会在收尾时等待全部完成。
4.5 人工确认与回调控制点
python
import json
def notify(result):
"""任务完成回调:拿到 TaskOutput 对象"""
print(f"[回调] 任务完成,输出长度 {len(result.raw)} 字符")
with open("task_log.jsonl", "a") as f:
f.write(json.dumps({"raw": result.raw[:200]}, ensure_ascii=False) + "\n")
review_task = Task(
description="起草给客户的道歉信。",
expected_output="150 字以内的正式信函,语气诚恳,不承诺具体赔偿金额。",
agent=writer,
human_input=True, # 输出后暂停,等人在终端确认或给修改意见
callback=notify, # 完成后执行回调(含人工修改后的最终版)
output_file="output/apology_letter.md", # 最终输出落盘(目录自动创建)
)human_input=True:任务给出答案后在终端暂停,你可以输入修改意见让 agent 迭代,回车确认后继续。适合对外发布前的最后一道人工闸门;callback:拿到TaskOutput,适合做日志、指标上报、下游触发;output_file:自动创建目录并写入,是"留档审计"的最简单实现。
4.6 本章小结
- description 与 expected_output 是必填项且直接进入提示词:动作分步骤编号、完成标准写清格式与长度;
- 默认输出自动流向下一任务;非紧邻依赖用
context=[...]显式声明,可构建 DAG; async_execution=True让独立任务并行,由引用它们的同步任务充当汇合点;human_input是人工闸门,callback是程序钩子,output_file负责留档。
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. Task 的哪两个属性是必填的?
2. 汇总任务需要等待两个互不相关的调研任务完成,正确做法是?
3. human_input=True 的行为是?
4. 关于 callback 参数,正确的说法是?
🛠️ 动手实践
- 把第 2 章的 crew 改造为三路并行:同时调研"技术趋势/商业模式的竞品/用户社区反馈",再汇总成简报。
- 给你的最终输出任务加上
human_input=True和output_file="output/report.md",体验人工修改闭环。 - 写一个 callback:把每次任务的 token 用量(TaskOutput 中可获取)追加写入
usage.csv。
任务会写了。下一章我们把 agents 和 tasks 组装成 Crew,玩转批量执行与结果解读。