CrewAI 用「角色 + 目标 + 背景」来定义 Agent 团队,很适合把一件活拆给几个人分工。本篇搭一个最小但完整的「写码 + 审码」双 Agent:一个负责按需求写函数,另一个负责挑毛病。但有一个重要变化必须先说清楚:CrewAI 内置的代码执行工具(CodeInterpreterTool)已经从 crewai-tools 中移除,所以生成的代码不会再被自动跑一遍——这篇教程的重点之一,就是教你在这种情况下怎么安全核验。
先搞清楚:为什么内置执行器没了
这不是「功能砍了」,而是安全上的主动收缩。CrewAI 的 CodeInterpreterTool(带 Docker 沙箱与受限 Python 回退)在 2026 年被移除,起因是安全研究者记录了可远程执行代码(RCE)与沙箱逃逸类漏洞,CERT/CC 的 VU#221883 也记录了厂商的说明。与此同时,Agent 上的 allow_code_execution、code_execution_mode 两个参数被标记为弃用,官方文档改为推荐接入第三方沙箱服务。
对你的影响:网上大量老教程还在教「用 CodeInterpreterTool 让 Agent 自己跑代码」,那套写法已经失效或不再被推荐。如果你需要「Agent 在循环里真的执行代码」,正确做法是接专门的沙箱服务(例如 E2B、Modal,或 crewai-tools 提供的 e2b_sandbox_tool / daytona_sandbox_tool 包装),而不是自己在本地拼一个 Docker 执行器。
Step 1:安装并锁定版本
mkdir crewai-codegen && cd crewai-codegen
python -m venv venv
# Windows: venv\Scripts\activate
source venv/bin/activate
# 钉一个明确的版本,避免「教程写的和装的不一样」
pip install crewai==1.15.20
# 验证(应输出你装的版本号)
python -c "import crewai; print(crewai.version)"
Step 2:定义两个角色
新建 main.py。CrewAI 的 Agent 靠三段文字定位自己:role(角色)、goal(目标)、backstory(背景)。写得越具体,行为越稳定:
# main.py
import os
from crewai import Agent, Task, Crew, Process
os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY", "")
coder = Agent(
role="Python 开发工程师",
goal="按需求写出可读、可测试、只依赖标准库的 Python 函数",
backstory=(
"你擅长写小而清晰的工具函数,坚持给每个函数写 docstring,"
"处理边界输入(空值、异常输入),不引入不必要的第三方依赖。"
),
verbose=True,
allow_delegation=False,
)
reviewer = Agent(
role="代码审查工程师",
goal="找出代码中的缺陷、边界问题与风格问题,并给出可执行的修改建议",
backstory=(
"你经验丰富、以严格著称。你逐条检查:输入校验、边界条件、"
"异常处理、命名与可读性,并明确指出问题所在行。"
),
verbose=True,
allow_delegation=False,
)
注意:这里没有给任何 Agent 配代码执行工具。老教程里的 allow_code_execution=True 已弃用,写了也不会生效。
Step 3:定义任务并串成 Crew
write_task = Task(
description=(
"写一个 Python 函数 parse_duration(text),把 '1h30m'、'45s'、'2h' "
"这类字符串解析成总秒数(int)。要求:只依赖标准库;"
"输入非法时抛出 ValueError 并给出可读信息;附 docstring 和 3 个示例。"
),
expected_output="一段完整的 Python 代码,包含函数定义、docstring 和示例用法。",
agent=coder,
)
review_task = Task(
description=(
"审查上一步产出的代码。逐条指出:① 是否有未处理的边界输入;"
"② 异常信息是否可读;③ 命名与结构是否清晰。"
"最后给出一版修改后的完整代码,并说明改了哪几处、为什么。"
),
expected_output="问题清单 + 修改后的完整代码 + 修改理由。",
agent=reviewer,
)
crew = Crew(
agents=[coder, reviewer],
tasks=[write_task, review_task],
process=Process.sequential, # 顺序执行:写 → 审
verbose=True,
)
if __name__ == "__main__":
result = crew.kickoff()
print("\n===== 最终交付 =====\n")
print(result)
运行:
python main.py
Step 4:读懂输出,别只看最终答案
打开 verbose=True 后,你能看到两个 Agent 各自在想什么、怎么交接。重点看三件事:
- coder 有没有偷懒:是不是真的处理了边界输入,还是只写了「happy path」;
- reviewer 挑出的问题是否成立:有的审查意见其实站不住脚,需要你判断;
- 最终代码是谁写的:reviewer 的修改版才是交付物,别把 coder 的初稿当结果。
Step 5:手动核验(这一步不能省)
因为框架不再替你执行,交付物到你手上时是「一段还没运行过的文本」。正确姿势:
# 1. 把生成的代码存成文件(不要直接粘进你的业务代码)
# parse_duration.py
# 2. 在隔离环境里跑,别在生产机、别在有真实数据的环境里跑
python -m venv .sandbox && source .sandbox/bin/activate
# (生产上更稳妥的是用容器或第三方沙箱)
# 3. 写最小测试,覆盖正常与异常输入
# test_parse.py
from parse_duration import parse_duration
import pytest
def test_basic():
assert parse_duration("45s") == 45
assert parse_duration("1h30m") == 5400
def test_invalid():
with pytest.raises(ValueError):
parse_duration("abc")
# 4. 跑测试
pytest -q
安全红线:① 绝不在生产服务器或存有真实数据的机器上直接运行 Agent 生成的代码;② 生成的代码要当「第三方代码」对待——先审、再隔离跑、再合入;③ 如果确实需要 Agent 在循环里执行代码,接专业沙箱服务,不要自己拼装执行器;④ 版本与 API 以 CrewAI 官方文档当前版本为准,别照搬旧教程。
Step 6:需要「真的执行」时的正确接法
如果你确实希望 Agent 能跑代码(例如让它自己调试),思路是把执行环境外包出去:
| 需求 | 推荐做法 | 说明 |
|---|---|---|
| 只想要代码,人工核验 | 本篇的双 Agent + 手动测试 | 最简单、风险最低,适合大多数场景 |
| Agent 需要边写边跑 | 接第三方沙箱(E2B / Modal / Daytona) | 执行隔离在对方环境,网络与文件权限可配置 |
| 想免运维托管 | CrewAI 的商业控制平面 | 由平台托管运行,不用自己管环境与密钥 |
常见问题速查
| 现象 | 大概率原因 & 解决 |
|---|---|
| Agent 想「执行」代码但失败 | 内置执行器已移除,改用第三方沙箱或人工核验 |
| 装 crewai 报编译错误 | Python 版本不在 3.10–3.13,或 pip 过旧;先升级 pip 再重试 |
| reviewer 提的意见很水 | review 任务描述太笼统,要求「定位到行 + 给完整修改版」 |
| 两个 Agent 反复推诿 | 开了 allow_delegation 却没给清晰的职责边界,按本篇关掉委派 |
| 跑一次很贵 | verbose 与多轮会放大消耗,收敛任务范围、限制轮次 |