Chapter 4 · Task Deep Dive: Task Definition and Context
Chapter goals: Master how description and expected_output writing determines output quality, learn to build task dependency chains with context, accelerate with async_execution, and add human and program control points with human_input and callback.
4.1 Task Core Attributes Overview
If Agent is the "employee", Task is the "work order" assigned to them. From the official attribute table, these are the most commonly used in daily work:
| Attribute | Type | Description |
|---|---|---|
description | str (required) | Task content description, supports {placeholder} interpolation |
expected_output | str (required) | Detailed completion standard, written directly into the prompt |
agent | Agent | The agent that executes this task |
context | List[Task] | Which other tasks' outputs to inject as additional context |
tools | List[BaseTool] | Task-specific tools (overrides the agent's default toolset) |
human_input | bool | Whether human confirmation is needed before completion |
output_file | str | Write the output to a specified file |
callback | Callable | Callback function after task completion |
async_execution | bool | Whether to execute asynchronously in parallel |
output_pydantic / output_json | BaseModel | Structured output (covered in detail in Chapter 12) |
Note that description and expected_output are required — CrewAI uses them to construct the prompt; neither can be omitted.
4.2 Writing description and expected_output
Task text quality = the ceiling of output quality. Comparison:
from crewai import Task
# ❌ Anti-pattern: vague description, no standard
vague_task = Task(
description="Analyze this sales data.",
expected_output="An analysis report.",
agent=analyst,
)
# ✅ Best practice: background + action + constraints + standard
precise_task = Task(
description=(
"Analyze the Q3 2024 sales data in the attachment (region: {region}). "
"1) Calculate the three categories with the highest quarter-over-quarter growth rate; "
"2) Identify SKUs with return rates exceeding 8% and list possible causes; "
"3) All conclusions must cite data sources (table row numbers)."
),
expected_output=(
"Markdown report containing: "
"## Top 3 Growth (table: category/QoQ/row number reference), "
"## High-Return SKUs (table + cause hypotheses), "
"## Recommended Actions (no more than 3 items). "
"Total length no more than 600 characters."
),
agent=analyst,
)Three practical principles:
- Write "how to judge completion" into expected_output: format (table/list/Markdown), length, required sections;
- Decompose actions in description (steps 1/2/3) — models follow numbered instructions far better than prose;
- Placeholder interpolation:
{region}and other variables are replaced atkickoff(inputs={...}), making task definitions reusable.
4.3 Building Task Dependency Chains with context
By default, in sequential flow the previous task's output automatically flows into the next task. But when the dependency is not "the immediately preceding task", you must explicitly declare context:
research_ai = Task(description="Research the AI Agent market landscape.", expected_output="5 key points", agent=researcher)
research_ops = Task(description="Research the ops automation market.", expected_output="5 key points", agent=researcher)
# Merge task depends on both upstream tasks
merge_task = Task(
description="Merge both research findings into a unified landscape overview.",
expected_output="A review under 500 characters, divided into 'Common Trends' and 'Divergent Opportunities' sections.",
agent=writer,
context=[research_ai, research_ops], # Explicitly declare dual dependency
)The semantics of context=[...] are: after all these tasks complete, their outputs are concatenated and injected into this task's context. This is the foundational tool for building DAG (directed acyclic graph) style workflows.
4.4 Parallel Tasks: async_execution
Independent upstream tasks can execute in parallel, significantly reducing total duration:
t1 = Task(
description="Research direction A",
expected_output="3 key points",
agent=researcher,
async_execution=True, # Async execution, does not block the main chain
)
t2 = Task(
description="Research direction B",
expected_output="3 key points",
agent=researcher,
async_execution=True, # Parallel with t1
)
summary = Task(
description="Summarize research results from directions A and B.",
expected_output="200-character overview",
agent=writer,
context=[t1, t2], # Summary task waits for both async tasks to complete
)Rule: async tasks are waited on by "the next synchronous task that references them". In the example above, summary is a synchronous task that references t1 and t2 via context, so it becomes the convergence point. If all tasks are async and no one converges, the Crew waits for all to complete at the end.
4.5 Human Confirmation and Callback Control Points
import json
def notify(result):
"""Task completion callback: receives a TaskOutput object"""
print(f"[callback] Task complete, output length {len(result.raw)} chars")
with open("task_log.jsonl", "a") as f:
f.write(json.dumps({"raw": result.raw[:200]}, ensure_ascii=False) + "\n")
review_task = Task(
description="Draft an apology letter to the customer.",
expected_output="A formal letter under 150 characters, sincere tone, do not promise a specific compensation amount.",
agent=writer,
human_input=True, # Pause after output; wait for human to confirm or provide revision suggestions
callback=notify, # Execute callback after completion (including the final version after human revision)
output_file="output/apology_letter.md", # Final output written to disk (directory auto-created)
)human_input=True: the task pauses in the terminal after producing an answer; you can input revision suggestions for the agent to iterate on, then press Enter to continue. Ideal as the final human gate before external release;callback: receives aTaskOutput; suitable for logging, metrics reporting, triggering downstream actions;output_file: auto-creates the directory and writes; the simplest implementation for "archival audit".
4.6 Chapter Summary
- description and expected_output are required and go directly into the prompt: decompose actions into numbered steps, write clear format and length in the completion standard;
- By default, output flows automatically to the next task; non-adjacent dependencies use
context=[...]for explicit declaration, enabling DAG construction; async_execution=Trueruns independent tasks in parallel, with a referencing synchronous task acting as the convergence point;human_inputis the human gate,callbackis the program hook,output_filehandles archival.
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. Which two Task attributes are required?
2. A summary task needs to wait for two unrelated research tasks to complete. What is the correct approach?
3. What does human_input=True do?
4. Regarding the callback parameter, which statement is correct?
🛠️ Hands-On Exercises
- Refactor the Chapter 2 crew into a three-way parallel: simultaneously research "technology trends / business model competitors / user community feedback", then merge into a briefing.
- Add
human_input=Trueandoutput_file="output/report.md"to your final output task; experience the human-in-the-loop revision cycle. - Write a callback that appends each task's token usage (available in TaskOutput) to
usage.csv.
You can now write tasks. Next, assemble agents and tasks into a Crew and master batch execution and result interpretation.