Skip to content

第 2 章 · 环境搭建与第一个 Crew

本章目标:装好 CrewAI 并完成模型接入,写出 researcher → writer 双角色的最小 sequential crew,用 kickoff() 跑通全流程并读懂执行日志。

2.1 安装 CrewAI

CrewAI 官方推荐使用 uv 管理环境。先确认 Python 版本满足 >=3.10 且 ❤️.14

bash
python3 --version        # 必须在 3.10 ~ 3.13 之间

# 安装 uv(macOS / Linux)
curl -LsSf https://astral.sh/uv/install.sh | sh

# 用 uv 安装 crewai CLI(含框架本体)
uv tool install crewai

# 验证
crewai version

如果你更习惯传统方式,也可以直接用 pip 把 CrewAI 装进项目虚拟环境:

bash
python3 -m venv .venv && source .venv/bin/activate
pip install crewai

版本提示

官方文档要求底层 openai >= 1.13.3(即使你用的是三方兼容端点,CrewAI 内部也复用 openai SDK 的传输层)。若遇到依赖冲突,先升级 pip 再安装。

2.2 配置三方模型密钥

按课程约定使用 DeepSeek(任何 OpenAI 兼容服务同理)。在项目根目录创建 .env 文件:

bash
# .env —— 永远不要把真实密钥提交到 git
DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxx

并在代码中通过环境变量读取。用 python-dotenv 自动加载 .env 是最省心的方式:

python
# config.py —— 统一的密钥加载入口
from dotenv import load_dotenv   # pip install python-dotenv
import os

load_dotenv()  # 读取当前目录 .env 并注入环境变量

DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY")
assert DEEPSEEK_API_KEY, "请先在 .env 中配置 DEEPSEEK_API_KEY"

.env 的加载也可以手动 export;第 17 章的脚手架项目会自动生成 .env 模板。

2.3 先验证模型连通性

在写 crew 之前,先用 20 行代码确认三方端点真的能通——排除密钥、网络问题后再上框架,排障效率高得多:

python
# ping_llm.py —— 最小连通性测试(不经过任何 agent)
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("用一句话介绍你自己"))
# 能打印出回复,说明密钥、base_url、模型 id 三要素全部正确

llm.call(prompt) 是 LLM 对象的最底层调用接口,跳过 agent 循环直接对话,也是排查模型层问题的标准手段。

2.4 第一个双角色 Crew

创建 my_first_crew.py:一个"研究员"负责整理要点,一个"作者"把要点扩写成短文。

python
import os
from crewai import Agent, Task, Crew, Process, LLM

# 1) 三方 OpenAI 兼容模型
deepseek = LLM(
    model="openai/deepseek-chat",
    base_url="https://api.deepseek.com/v1",
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    temperature=0.7,
)

# 2) 角色 1:研究员
researcher = Agent(
    role="资深行业研究员",
    goal="围绕给定主题整理出 5 条最有价值的核心要点",
    backstory="你有十年科技行业研究经验,擅长快速抓住趋势本质,"
              "输出永远精炼、结构化、不带废话。",
    llm=deepseek,
    verbose=True,            # 打印执行过程,学习阶段强烈建议开启
)

# 3) 角色 2:技术作者
writer = Agent(
    role="科普作者",
    goal="把研究员的要点改写成通俗易懂的短文",
    backstory="你是知名科技专栏作者,善于把复杂概念讲给零基础读者。",
    llm=deepseek,
    verbose=True,
)

# 4) 任务清单:sequential 模式下按定义顺序执行
research_task = Task(
    description="研究主题「{topic}」,总结 5 条核心要点。",
    expected_output="一个包含 5 条要点的编号列表,每条不超过 30 字。",
    agent=researcher,
)

write_task = Task(
    description="基于研究员的要点,写一篇 300 字左右的入门短文。",
    expected_output="一篇 Markdown 格式的短文,含标题和三个小节。",
    agent=writer,
)

# 5) 组队并执行
crew = Crew(
    agents=[researcher, writer],
    tasks=[research_task, write_task],
    process=Process.sequential,   # 默认值,显式写出更清晰
    verbose=True,
)

result = crew.kickoff(inputs={"topic": "AI Agent 智能体"})
print("===== 最终结果 =====")
print(result.raw)

运行:

bash
export DEEPSEEK_API_KEY=sk-xxx
python my_first_crew.py

2.5 读懂 kickoff 的执行过程

开启 verbose=True 后,终端会依次打印:

  1. 任务分配Working Agent: 资深行业研究员——当前执行哪个 agent;
  2. 原始输出:该任务的完整 LLM 回复;
  3. 任务流转:第一个 task 的输出被自动注入第二个 task 的上下文;
  4. Token 统计:结束后打印本次运行的 token 使用量。

几个关键观察点:

  • {topic} 占位符被 inputs={"topic": ...} 的值替换了——这是 CrewAI 的模板插值机制,descriptionexpected_output 里都能用;
  • expected_output 不是摆设:它会被写进提示词,直接影响输出的格式与质量,务必认真写;
  • 最后一个 task 的输出就是整个 Crew 的输出(result.raw)。

由于定义与数据已分离,同一套 crew 换个主题就能复用:

python
# 一套定义,多次运行:只换 inputs
for topic in ["大模型推理优化", "多智能体协作"]:
    result = crew.kickoff(inputs={"topic": topic})
    print(f"[{topic}] -> {result.raw[:50]}...")

2.6 常见首次运行错误排查

报错/现象原因解决
401 UnauthorizedAPI key 未读到或错误检查环境变量名是否为 DEEPSEEK_API_KEY
404 model not foundbase_url 少了 /v1 或模型 id 拼错DeepSeek 端点必须是 https://api.deepseek.com/v1
卡住不动无报错未开 verbose,看起来像死机给 Agent 和 Crew 都加 verbose=True
输出格式混乱expected_output 太含糊写明"编号列表""Markdown"等硬性格式要求

2.7 本章小结

  • 安装:官方推荐 uv tool install crewai,Python 需在 3.10–3.13 区间;
  • 三方模型接入三件套:model="openai/deepseek-chat" + base_url + 环境变量里的 api_key
  • 最小 crew 五步走:定义 LLM → 定义 Agents → 定义 Tasks → 组装 Crew → kickoff(inputs=...)
  • verbose=True 是学习期的眼睛;expected_output 决定输出质量;上一个任务输出自动流入下一个任务。

🧪 随堂测验

点击你认为正确的选项。答错时会展示正确答案与原因解析。

1. 在 sequential 流程中,第二个 Task 能否拿到第一个 Task 的输出?

2. inputs={"topic": "AI"} 中的 topic 会如何生效?

3. 访问 DeepSeek 这类 OpenAI 兼容端点时,base_url 应该是?

4. 关于 expected_output,下列说法正确的是?

🛠️ 动手实践

  1. 把本章 crew 的主题换成你自己感兴趣的方向,并把 writer 的 backstory 改成"严谨的学术编辑",对比输出风格变化。
  2. 增加第三个角色 reviewer(审稿人),新增一个 review 任务:接收文章并给出修改建议列表。
  3. 故意把 DEEPSEEK_API_KEY 设成空值跑一次,记录报错信息;再故意漏掉 base_url 的 /v1 观察现象,把两种错误写进你的笔记。

第一个 Crew 已经转起来了。下一章我们深入 Agent 的角色设计方法论,让每个角色真正"立得住"。