Chapter 2 · Environment Setup and First Crew
Chapter goals: Install CrewAI and configure model access, write a minimal sequential crew with two roles (researcher → writer), run it with
kickoff(), and learn to read the execution log.
2.1 Installing CrewAI
CrewAI officially recommends managing environments with uv. First confirm Python version meets >=3.10 and ❤️.14:
python3 --version # Must be between 3.10 and 3.13
# Install uv (macOS / Linux)
curl -LsSf https://astral.sh/uv/install.sh | sh
# Install the crewai CLI (includes the framework itself)
uv tool install crewai
# Verify
crewai versionIf you prefer the traditional approach, you can also install CrewAI directly into a project virtual environment with pip:
python3 -m venv .venv && source .venv/bin/activate
pip install crewaiVersion note
The official docs require underlying openai >= 1.13.3 (even if you're using a third-party compatible endpoint, CrewAI internally reuses the openai SDK's transport layer). If you hit dependency conflicts, upgrade pip first, then install.
2.2 Configuring Third-Party Model Credentials
Per course conventions, we use DeepSeek (any OpenAI-compatible service works the same way). Create a .env file in the project root:
# .env — never commit real credentials to git
DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxxThen read them in code via environment variables. Using python-dotenv to auto-load .env is the easiest approach:
# config.py — unified credential loading entry point
from dotenv import load_dotenv # pip install python-dotenv
import os
load_dotenv() # Reads .env from current directory and injects into environment
DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY")
assert DEEPSEEK_API_KEY, "Please configure DEEPSEEK_API_KEY in .env first".env can also be loaded manually via export; the scaffold project in Chapter 17 auto-generates a .env template.
2.3 Verify Model Connectivity First
Before writing a crew, confirm the third-party endpoint actually works with 20 lines of code — ruling out key and network issues first makes debugging far more efficient than jumping straight into the framework:
# ping_llm.py — minimal connectivity test (no agent involved)
import os
from crewai import LLM
llm = LLM(
model="openai/deepseek-chat",
base_url="https://api.deepseek.com/v1",
api_key=os.getenv("DEEPSEEK_API_KEY"),
)
print(llm.call("Introduce yourself in one sentence"))
# If it prints a response, all three — key, base_url, and model id — are correctllm.call(prompt) is CrewAI's lowest-level LLM interface, bypassing the agent loop entirely for a direct conversation. It is also the standard approach for diagnosing model-layer issues.
2.4 First Two-Role Crew
Create my_first_crew.py: a "researcher" who summarizes key points, and a "writer" who expands those points into a short essay.
import os
from crewai import Agent, Task, Crew, Process, LLM
# 1) Third-party OpenAI-compatible model
deepseek = LLM(
model="openai/deepseek-chat",
base_url="https://api.deepseek.com/v1",
api_key=os.getenv("DEEPSEEK_API_KEY"),
temperature=0.7,
)
# 2) Role 1: Researcher
researcher = Agent(
role="Senior Industry Researcher",
goal="Summarize 5 most valuable key points about the given topic",
backstory="You have ten years of tech industry research experience, "
"skilled at quickly grasping trend essences, "
"and your output is always concise, structured, and free of fluff.",
llm=deepseek,
verbose=True, # Prints execution trace — strongly recommended during learning
)
# 3) Role 2: Technical Writer
writer = Agent(
role="Science Writer",
goal="Rewrite the researcher's points into an accessible short essay",
backstory="You are a well-known tech column author who excels at explaining "
"complex concepts to零基础 readers.",
llm=deepseek,
verbose=True,
)
# 4) Task list: executed in definition order under sequential mode
research_task = Task(
description="Research topic '{topic}' and summarize 5 key points.",
expected_output="A numbered list of 5 key points, each no more than 30 characters.",
agent=researcher,
)
write_task = Task(
description="Based on the researcher's points, write a ~300-word introductory essay.",
expected_output="A Markdown-formatted essay with a title and three subsections.",
agent=writer,
)
# 5) Assemble and execute
crew = Crew(
agents=[researcher, writer],
tasks=[research_task, write_task],
process=Process.sequential, # Default value; written explicitly for clarity
verbose=True,
)
result = crew.kickoff(inputs={"topic": "AI Agents"})
print("===== Final Result =====")
print(result.raw)Run:
export DEEPSEEK_API_KEY=sk-xxx
python my_first_crew.py2.5 Understanding the kickoff Execution Process
With verbose=True enabled, the terminal prints sequentially:
- Task assignment:
Working Agent: Senior Industry Researcher— which agent is currently executing; - Raw output: the full LLM reply for that task;
- Task handoff: the first task's output is automatically injected into the second task's context;
- Token stats: after completion, token usage for this run is printed.
Key observations:
- The
{topic}placeholder was replaced by the value frominputs={"topic": ...}— this is CrewAI's template interpolation mechanism; bothdescriptionandexpected_outputsupport it; expected_outputis not just decorative: it gets written into the prompt and directly affects output format and quality — write it carefully;- The last task's output becomes the entire Crew's output (
result.raw).
Since definition and data are separated, the same crew can be reused with a different topic:
# One definition, multiple runs: only swap inputs
for topic in ["Large Model Inference Optimization", "Multi-Agent Collaboration"]:
result = crew.kickoff(inputs={"topic": topic})
print(f"[{topic}] -> {result.raw[:50]}...")2.6 Common First-Run Error Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
401 Unauthorized | API key not loaded or incorrect | Check that the env var name is DEEPSEEK_API_KEY |
404 model not found | Missing /v1 in base_url or wrong model id | DeepSeek endpoint must be https://api.deepseek.com/v1 |
| Stuck with no output | Verbose not enabled, looks frozen | Add verbose=True to both Agent and Crew |
| Messy output format | expected_output too vague | Specify hard format requirements like "numbered list" or "Markdown" |
2.7 Chapter Summary
- Installation: official recommendation is
uv tool install crewai, Python must be in the 3.10–3.13 range; - Third-party model integration: three件套 —
model="openai/deepseek-chat"+base_url+api_keyfrom environment variables; - Minimal crew five steps: define LLM → define Agents → define Tasks → assemble Crew →
kickoff(inputs=...); verbose=Trueis your eyes during learning;expected_outputdetermines output quality; previous task output flows automatically into the next task.
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. In sequential flow, can the second Task see the first Task's output?
2. How does topic in inputs={"topic": "AI"} take effect?
3. When accessing OpenAI-compatible endpoints like DeepSeek, what should base_url be?
4. Regarding expected_output, which statement is correct?
🛠️ Hands-On Exercises
- Change this chapter's crew topic to something you're interested in, and change the writer's
backstoryto "rigorous academic editor" — compare the output style difference. - Add a third role reviewer (proofreader) with a new review task: receive the article and produce a list of suggested revisions.
- Intentionally set
DEEPSEEK_API_KEYto an empty value and run once; record the error message. Then intentionally omit/v1from base_url and observe the behavior. Write both errors into your notes.
Your first Crew is running. Next, we deep dive into Agent role design methodology to make every角色 truly "stand out."