99% 的人以为「模型更强」就能写出好用的编码代理,结果一上真实代码库就崩。你可能也遇到过:工具乱调、文件乱改、上下文越跑越偏,最后连最初目标都忘了。问题往往不在模型,而在你给它配的「身体」——也就是执行框架。只要把这层搭对,开源栈也能跑出接近 Claude Code 的体验。
Claude Code 的关键差异,其实是那套围绕模型的普通代码:谁来规划、谁来执行、怎么记忆、怎么防事故。模型只是「下一步干啥」的决策器。下面就用 CrewAI,从一个最简单的循环开始,一层层把规划、子代理、沙箱和记忆叠上去,做出一个能在真实仓库里修 Bug 的执行框架。
Claude Code 的整体执行思路
代理循环背后的真实差距
Claude Code 内部的代理循环逻辑非常朴素:发任务给模型,模型要么直接回答,要么请求工具;工具跑完把结果塞回对话,再让模型决定下一步。循环反复,直到模型不再调用工具,给出最终答案。听上去,任何人都能用几十行代码写出来。

问题是,这个「玩具循环」一旦丢进真实代码库,很快就会出事:读错文件、误删内容、在上下文里堆满无用输出,甚至中途忘记自己要修哪个 Bug。有用户反馈,在一个中型仓库上跑简单代理,十几轮之后模型已经完全偏题,只能强行重开会话。
据一份 2024 年的内部测试数据,在没有规划和记忆的情况下,长于 20 轮的代码任务中,约有 60% 会出现目标偏移或重复劳动。
Claude Code 的优势不只是「模型更聪明」,而是执行框架更完整:规划层帮它记住目标,工具层帮它安全操作代码,记忆层帮它跨会话学习,安全层帮它不把你的机器炸掉。模型只是大脑,执行框架是双手和神经系统。

六层结构:从循环到记忆
可以把 Claude Code 的执行框架粗略拆成六层:
- 核心代理循环:负责「问模型—调工具—再问模型」的闭环。
- 工具层:文件读写、目录遍历、shell 命令、测试执行等。
- 规划层:生成并维持任务计划,减缓上下文衰减。
- 子代理与层级协作:把复杂任务拆给不同专家,主代理只看总结。
- 沙箱与审批:把危险操作关进安全环境,并加上人工确认。
- 记忆与检查点:跨会话记住事实,并能从中断处继续跑。
我用 CrewAI 重建时,惊讶地发现:大部分能力都能直接映射到框架内置功能,真正需要你动手的,是提示设计、工具选择和执行环境。换句话说,工程难点不在「有没有能力」,而在「怎么组合和约束这些能力」。
从零开始:核心循环与第一个代理
核心代理循环长什么样
代理循环的基本流程可以概括为:

- 把当前对话和可用工具列表发给模型。
- 模型要么直接给出文本回复,要么返回一个或多个工具调用。
- 如果有工具调用,就按顺序执行这些工具,拿到结果。
- 把模型回复和工具结果一起追加到对话历史中。
- 回到第 1 步,直到模型只返回文本、不再请求工具。

while True:
reply = model(messages, tools)
calls = [b for b in reply if b.type == "tool_use"]
if not calls: # 纯文本,无工具调用:任务完成
return reply.text
messages += [reply, run_all(calls)]
每次工具调用都是一次「行动」,把新信息塞回给模型,供下一步决策。修一个小 Bug 可能一两轮就搞定,大规模重构可能要几十轮。CrewAI 在你创建 Agent 和 Task 之后,会自动帮你跑这个 while 循环,你只需要关心「谁来干什么」。
用 CrewAI 写出第一个 Bug 修复代理
下面是一个最小可用的 Bug 修复代理示例:

from crewai import LLM, Agent, Crew, Task
bug_fixer = Agent(
role="Bug Fixer",
goal="查找并描述代码库中报告的缺陷修复方案。",
backstory="你通过读取目录和文件构建代码的准确图景。",
llm="claude-sonnet-4-6",
)
task = Task(
description="查找 {objective} 的修复方案。",
expected_output="简短描述修复内容及对应文件。",
)
result = Crew(agents=[bug_fixer], tasks=[task]).kickoff(
inputs={"objective": "account.py 中的透支缺陷"}
)
这里有三个核心概念:Agent、Task 和 Crew。Agent 是执行者,定义角色、目标、使用的 LLM 和工具;Task 是要做的事;Crew 把两者绑在一起,调用 kickoff() 就会触发完整的代理循环。CrewAI 支持 Anthropic、OpenAI、Google 等多家模型,你可以按成本和效果自由切换。
工具、规划与推理:让代理不再迷路
给代理装上操作代码库的「手」
如果模型只能输出文本,它就永远改不了你的代码。工具层的作用,就是把「读写文件、跑测试、调 API」这些能力暴露给模型,让它能真正动手。CrewAI 默认内置了常用的文件系统工具:
- FileReadTool:读取文件内容。
- DirectoryReadTool:列出目录结构。
- FileWriterTool:写入或覆盖文件。
from crewai_tools import DirectoryReadTool, FileReadTool, FileWriterTool
read_file = FileReadTool()
write_file = FileWriterTool()
list_dir = DirectoryReadTool()
filesystem_tools = [read_file, write_file, list_dir]
这些工具还有一个隐藏用法:当成「外部记忆」。代理可以把大段搜索结果写进文件,只在上下文里保留文件名,等需要时再读回来。这样上下文窗口不会被无关细节塞满,模型更容易专注在当前步骤。


当内置工具不够用时,可以用 @tool 装饰器把任意 Python 函数暴露给模型,函数的 docstring 就是给模型看的「使用说明」。
from crewai.tools import tool
import subprocess
@tool("run_tests")
def run_tests(path: str = "tests/") -> str:
"""运行指定路径下的 pytest 测试套件并返回结果。"""
result = subprocess.run(
["pytest", path, "-q"], capture_output=True, text=True, timeout=120
)
output = result.stdout + result.stderr
return output[-4000:] if len(output) > 4000 else output
规划:对抗上下文衰减的「路线图」
长任务里最常见的失败模式,是代理忘了自己一开始要干嘛。多轮工具调用、文件阅读和中间输出之后,原始目标被淹没在对话历史里,这就是所谓的上下文衰减。单靠「在提示里多强调几遍」很难解决。

规划层的作用,是在执行前先生成一份分步计划,并在执行过程中持续把这份计划挂在上下文里。它本身不执行任何操作,只是一个「待办清单」,类似 Claude Code 里那个始终可见的 TODO 面板。

CrewAI 在 Crew 级别提供了开关式规划:

from crewai import Crew, LLM
crew = Crew(
agents=self.agents,
tasks=self.tasks,
planning=True,
planning_llm=LLM(model="gpt-4o-mini"),
)
据官方文档说明,默认规划使用 gpt-4o-mini,你也可以换成任意你更信任的 LLM。
如果你想让单个代理在行动前「自我思考」几轮,可以打开 reasoning=True:
from crewai import Agent
bug_fixer = Agent(
role="Bug Fixer",
goal="查找并描述代码库中报告的缺陷修复方案。",
backstory="你通过读取目录和文件构建代码的准确图景。",
tools=[FileReadTool()],
reasoning=True,
max_reasoning_attempts=3 # 最大推理次数
)
启用推理后,代理会先反思任务、草拟方案、检查是否合理,再决定真正要执行的步骤。规划解决的是「整体路线」,推理解决的是「当前这一步要不要换个做法」。两者叠加,可以显著降低长任务中跑偏的概率。

子代理、沙箱与记忆:把复杂任务拆开又守住安全线
子代理:让主代理只记住「结论」
在大代码库里,哪怕有了规划,单个代理的上下文也很容易爆掉。比如排查一个复杂缺陷,可能要读几十个文件、跑多轮测试,主代理没必要记住所有细节。更好的做法,是把具体子任务委派给子代理,让它们在自己的上下文里折腾,最后只把总结结果交给主代理。

CrewAI 用层级流程(hierarchical process)来实现这一点:你定义几个专家代理,再定义一个管理者代理,允许它进行委派。一个常见拆分方式是:

- 代码库探索者:负责读目录、扫文件,画出「地图」。
- 软件工程师:根据地图和需求改代码。
- 测试执行者:在沙箱里跑测试,汇报结果。
- 工程主管:拆分任务、分配给专家、审核结果。

from crewai import Crew, Agent, Task, Process
explorer = Agent(
role="Codebase Explorer",
goal="映射仓库并找出相关文件。",
backstory="你通过读取目录和文件构建代码图景。",
tools=[read_file, list_dir],
llm=llm,
) # 其他两个专家代理类似
manager = Agent(
role="Engineering Lead",
goal="拆分请求并委派给合适专家。",
backstory="你决定分工,审核测试,变更完成即结束。",
llm=llm,
allow_delegation=True,
)
crew = Crew(
agents=[explorer, coder, tester],
tasks=[task],
manager_agent=manager,
process=Process.hierarchical,
)
这里有个容易忽略的细节:allow_delegation 默认是关闭的,你必须在管理者代理上显式打开,否则它不会主动调用子代理。这一点很多人第一次用都会踩坑。
沙箱与审批:真正的安全不靠「别乱来」四个字
给代理开 shell 权限,是一件风险极高的事。单靠在提示里写「不要删除文件」「不要访问网络」这种软约束,挡不住模型在极端情况下做出危险操作。更稳妥的做法,是把安全拆成两层:

- 权限系统:对敏感操作加审批,比如删除文件、重置数据库等。
- 沙箱环境:把所有代码执行都放进隔离环境,即便命令被批准,也不会伤到主机。
Anthropic 的做法也是类似:把代码执行迁移到沙箱里,既减少了用户需要点「同意」的次数,又把最危险的部分关在一个可控空间里。

CrewAI 这边,常见做法是用 E2B 提供的沙箱:每个会话启动一台干净的虚拟机,会话结束就销毁。所有 shell 命令和 Python 代码都在里面跑。


from crewai_tools import E2BExecTool, E2BPythonTool
sandbox_tools = [E2BExecTool(), E2BPythonTool()] # 运行测试 / 运行代码
说实话,沙箱本身也不是银弹:你还得考虑网络访问、持久化存储、镜像更新这些运维问题。不过相比直接在宿主机上跑 rm -rf,已经安全太多。
人类参与:关键节点拉一把刹车
有些场景,你会希望在代理给出结果后,先过一眼再让它继续。CrewAI 在 Task 上提供了一个简单的人类参与开关:

from crewai import Task
task = Task(
description=(
"在工作目录 ./workspace 中,{objective}。"
"先探索代码,修改,然后运行测试并报告。"
),
expected_output="变更摘要和最终测试输出。",
human_input=True,
)
执行到这个任务时,CrewAI 会停下来,通过标准输入等方式等你确认。你可以选择接受当前输出、要求重试,或者给出额外指示。如果你是通过 Web 或聊天界面跑代理,可以用 CrewAI 的 webhook 人类参与系统,把这一步接到自己的前端里。
记忆与检查点:让代理「越用越懂你」
统一记忆:不再区分短期、长期那么复杂
默认情况下,代理每次跑完就「失忆」。第二天再修同一个项目的新 Bug,它又从头摸索一遍目录结构和业务规则,既浪费 token,也浪费时间。CrewAI 提供了一个统一的 Memory 接口,帮你把有用信息跨会话保存下来。


你只需要在 Crew 上打开 memory=True:
from crewai import Crew
crew = Crew(
agents=[explorer, coder, tester],
tasks=[task],
memory=True,
)
每个任务结束后,CrewAI 会用 LLM 从输出中提取「值得记住的事实」,比如「这个项目要求所有函数都有类型注解」「合并前必须跑 e2e 测试」之类,下次再跑相关任务时,会自动把这些记忆检索出来,拼进提示里。除非你单独配置,否则一个 Crew 里的所有代理共享同一份记忆库。
检查点:从中断处继续,而不是重来一遍
检查点可以理解为「代理当前进度的快照」,里面包含:配置、任务状态、记忆、中间结果、输入和执行历史等。这样一来,如果中途出错或你想尝试不同分支,就可以从某个检查点继续,而不是从头再跑。


CrewAI 内置了两种检查点存储方式:
- JsonProvider:每个检查点一个 JSON 文件,方便手动查看和调试。
- SqliteProvider:所有检查点放在一个 SQLite 数据库里,更适合高频检查点和大规模任务。
开启检查点也很简单:

from crewai import Crew
crew = Crew(
agents=[explorer, coder, tester],
tasks=[task],
checkpoint=True,
)
Crew、Flow 和 Agent 都接受 checkpoint 参数,子对象会继承父对象的设置,除非你在子级上显式覆盖。我也不太确定这个继承策略在所有复杂场景下是不是最优,但在常见用法里足够顺手。
六层框架落地:一次真实的缺陷修复
把所有层叠在一起的完整示例
下面这个例子,把前面提到的六层全部串起来:核心循环、工具、规划、子代理、沙箱和记忆。
from crewai import Agent, Crew, LLM, Process, Task
from crewai.tools import tool
from crewai_tools import (DirectoryReadTool, FileReadTool, FileWriterTool,
E2BExecTool, E2BPythonTool)
llm = LLM(model="anthropic/claude-sonnet-4.6")
list_dir = DirectoryReadTool(directory="./workspace")
filesystem_tools = [FileReadTool(), FileWriterTool(), list_dir]
sandbox_tools = [exec_tool, E2BPythonTool()]
@tool("run_tests")
def run_tests(path: str = "tests/") -> str:
"""同步 ./workspace 到沙箱,然后运行 pytest。"""
return E2BExecTool().run(command=sync_and_test_command(path))
explorer = Agent(role="Codebase Explorer", goal="映射仓库,找出相关文件.",
tools=[read_file, list_dir], llm=llm)
coder = Agent(role="Software Engineer", goal="实现请求的变更.",
tools=filesystem_tools, reasoning=True, llm=llm)
tester = Agent(role="Test Runner", goal="在沙箱运行测试,报告结果.",
tools=sandbox_tools + [read_file] + [run_tests], llm=llm)
manager = Agent(role="Engineering Lead", goal="委派步骤,测试通过即完成.",
allow_delegation=True, llm=llm)
task = Task(
description="在 ./workspace 中,{objective}。探索、编辑、测试、报告.",
expected_output="变更摘要和测试输出.", human_input=True,
)
crew = Crew(
agents=[explorer, coder, tester], tasks=[task],
manager_agent=manager, process=Process.hierarchical,
planning=True, memory=True, checkpoint=True,
)
result = crew.kickoff(inputs={"objective": "修复 account.py 中的测试失败"})
我在一个小型示例仓库上跑过这套框架:仓库里有一个 BankAccount 类,埋了两个真实缺陷,配了 5 个测试,其中 3 个一开始是失败的。规则是「只能改实现,不许改测试」。代理会先探索目录,再定位相关文件,修改实现,最后在沙箱里跑 pytest,直到所有测试通过。


从 3 个失败测试到 5 个全绿,整个过程完全由代理驱动,且没有通过「删测试」这种方式作弊。和 Anthropic 内部用大规模测试套件评估 Claude Code 的思路类似,这种可自动验证成功与否的任务,是评估执行框架质量的最好试金石之一。
模型之外:提示、环境和工具选择才是你的护城河
哪些事框架帮不了你
CrewAI 这种框架能帮你搭好循环、规划、记忆和协作,但有三块内容,它刻意不替你做决定:
- 提示设计:每个代理的角色、目标和背景,决定了它的行为风格和偏好。要让代理真正贴合你的团队习惯,需要大量迭代和 A/B 测试,没有一个「万能配置」能一劳永逸。
- 执行环境:无论你用 E2B 还是自建虚拟机集群,镜像内容、依赖安装、网络策略都要自己设计。框架只负责「怎么调用」,不负责「环境长什么样」。
- 工具选择与权限:哪些代理能写文件,谁能跑 shell,谁只能读代码,这些都是架构决策。设计不当会带来安全风险,也会让代理行为变得不可控。
还有一点容易被忽略:执行框架本身是有成本的。规划、子代理、循环都会增加 API 调用次数,复杂代理配置的 token 消耗,可能远高于一次性的大模型调用。数据显示,在某些简单任务上,过度工程化的代理反而更慢、更贵。
模型进化后,哪些结构会被淘汰
随着模型能力提升,一部分今天看起来「必需」的结构,未来可能会变成累赘。比如 Anthropic 早期在 Claude Sonnet 4.5 上用过一种「上下文重置」技巧,防止模型在长任务中提前结束;等到 Claude Opus 4.5 出来,这个技巧就基本退役了,因为模型本身已经能更好地管理长上下文。

这也是为什么,搭执行框架时要留一点弹性:把容易过时的逻辑(比如某些很 hack 的上下文压缩策略)封装在可替换模块里,而不是写死在主流程中。否则模型一升级,你就得大动干戈重构整套系统。
写在后面:把这套判断方法留在手边
如果你正在考虑「要不要上一个 AI 编码代理」,与其纠结选哪个模型,不如先想清楚:你的执行框架准备做到哪几层。上面这套从循环到记忆的分层方法,在不同项目里反复验证过有效,尤其适合用来评估现有工具的上限和你自己要补的短板。
当你下次再遇到「代理在真实仓库里跑着跑着就迷路」的情况,可以回头对照这六层,看看问题到底卡在规划、子代理、安全,还是记忆。很多时候,一两个小改动,就能让整体体验从「玩具」变成「能上生产」。
常见问题
Q:如果我只想做一个简单的代码助手,有必要上这么完整的执行框架吗?
A:不一定要一口气上满六层,但至少要有核心循环和基础工具。原因在于,哪怕是「简单助手」,一旦涉及真实仓库,就会频繁读写文件和跑测试,没有工具层就只能靠人工复制粘贴,体验会非常差。建议做法是:先用单代理 + 文件工具 + 基础规划起步,等发现上下文吃紧或任务变复杂,再逐步引入子代理和记忆,这样成本和收益更匹配。
Q:如何判断什么时候该引入子代理,而不是继续堆一个大代理?
A:一个实用标准是「单代理上下文是否经常爆掉,或者提示是否已经复杂到难以维护」。当你发现需要在一个代理提示里同时描述多种角色(比如既要它当架构师,又要当测试工程师),而且任务经常超过几十轮对话时,就该考虑拆分。建议先把最容易独立的职责(如测试执行、代码探索)抽成子代理,再用管理者代理做协调,这样既能减轻上下文压力,也方便单独调优每个角色的提示。
Q:沙箱一定要用 E2B 吗?自己搭容器环境可行吗?
A:完全可以自己搭容器或虚拟机环境,只是工程成本会高一些。关键判断点在于:你是否有能力长期维护镜像、处理安全更新、监控资源使用。E2B 的好处是开箱即用、和 CrewAI 集成好,但会引入外部依赖和额外费用。如果你所在公司对数据安全要求极高,建议用自建沙箱,把 CrewAI 的工具层改成调用内部的执行服务,同时在网络和权限上做更细粒度的控制。
Q:记忆会不会让代理「记错东西」,导致后面行为反而变差?
A:有这个风险,尤其是在没有过滤的情况下把所有输出都写进记忆库。CrewAI 用 LLM 做了一层筛选,只提取「重要事实」,但仍然可能把过时信息保留下来。建议做法是:定期审查记忆内容,对项目规则类信息设置版本号或生效时间,并在提示里明确「以最新规则为准」。另外,可以为关键项目单独维护一份「权威配置文件」,让代理在执行前优先读取,降低记忆漂移带来的影响。
Q:执行框架会不会让成本失控,有没有简单的控制办法?
A:如果不加约束,复杂代理确实容易在 token 和时间上失控。控制成本的关键,是给每一层加上「刹车」:比如限制最大推理轮数、限制子代理可用的工具数量、为每个任务设置最大迭代次数。你还可以在日志里记录每次执行的 token 消耗和调用次数,按周做一次回顾,找出最费钱的路径,针对性地简化流程或改用更便宜的模型做规划层,这样既不牺牲效果,又能把账算清楚。


