# Let's build Claude Code's harness (step-by-step)
**作者**: Akshay
**日期**: 2026-07-15T18:10:23.000Z
**来源**: [https://x.com/akshay_pachaar/status/2077455755066868098](https://x.com/akshay_pachaar/status/2077455755066868098)
---

We will cover everything that goes into building a coding harness, the agent loop, planning, subagents, sandboxing, memory, and checkpointing, built step by step.
If you've ever tried building your own coding agent, you know how this goes. You wire a model up to file tools and a shell, point it at a real codebase, and it breaks down within a dozen tool calls.
It reads the wrong files, loses the goal halfway through, and fills its context with output it no longer needs.
Then the same task goes through Claude Code and finishes cleanly. The easy conclusion is that Anthropic simply has a better model, and that conclusion misses where the work actually happens.
The difference is the harness. A harness is the ordinary code wrapped around the model, and it handles planning, tool execution, memory, and safety while the model only decides the next step.
Here's what a fully harnessed agent looks like when you draw it out:

The picture looks busy, but it breaks into four groups:
- Memory feeds the model its working context plus facts it has learned across sessions.
- Skills encode how the agent should operate, meaning the procedures, constraints, and heuristics it follows.
- Protocols connect the agent to users, tools, and other agents.
- The harness core ties it all together with sub-agent orchestration, a sandbox, an evaluator, an approval loop, observability, and context compression.
Anthropic describes this split as the brain and the hands. The model is the brain that picks each action, and the harness is the hands that carry it out and keep the run on track.
So the gap between your agent and Claude Code isn't the model, it's the machinery around the model.
Claude Code is one of the most capable harnesses in production today, and it's built from a surprisingly small set of the layers in that illustration. To see how much of that machinery you'd have to build yourself, I rebuilt it in CrewAI, an open-source framework for orchestrating agents.
More of it maps onto built-in features than I expected, and the part that doesn't is where the real engineering lives.
Let's build it layer by layer, starting with the core loop and stacking planning, subagents, sandboxing, and memory on top. At each step we'll mark where the framework ends and where your job begins.
# How Claude Code's harness works
At the center of Claude Code is a plain agent loop. You send it a message, the model decides what to do next, and it either responds directly or requests a tool. If it requests one, the tool runs, the result goes back into the conversation, and the model decides again.
This repeats until the model returns a final answer with no further tool calls.
Inside that loop, the model reads files, edits code, runs shell commands, and executes tests. These aren't separate modes. They're just different tool calls inside the same loop.
The loop alone isn't enough for a reliable coding agent, though. Claude Code adds planning, file tools, subagents, memory, and a permission and sandbox system around it. These layers don't replace the loop, they make it safe and reliable enough for real work.

That's the architecture we'll rebuild, the core loop first, then each layer on top, mapping every layer to the CrewAI feature that handles it.
# The core agent loop
The loop runs the same sequence until the task is done:
1. Ask the model to perform the task.
2. The model responds directly or requests one or more tools.
3. If tools are requested, run them and return the results to the model.
4. Repeat with the updated conversation.
5. When the model responds without requesting any tools, the task is complete.

```
while True:
reply = model(messages, tools)
calls = [b for b in reply if b.type == "tool_use"]
if not calls: # plain text, no tool call: the job is done
return reply.text
messages += [reply, run_all(calls)]
```
Each tool call completes one step, gives the model new information, and feeds into the next decision. A simple question might finish in one iteration, while fixing a complex bug or refactoring a large codebase can take dozens of iterations before the model has enough to produce a final answer.
CrewAI provides this execution loop automatically as soon as you create an agent. You don't implement the while loop yourself, you define the agent and assign it a task.
# Building the first agent
Let's create a simple Bug Fixer agent.
```
from crewai import LLM, Agent, Crew, Task
bug_fixer = Agent(
role="Bug Fixer",
goal="Find and describe the fix for the reported bug in the codebase.",
backstory="You read directories and files to build an accurate picture of the code.",
llm="claude-sonnet-4-6",
)
task = Task(
description="Find the fix for {objective}.",
expected_output="A short description of the fix and which file it belongs in.",
)
result = Crew(agents=[bug_fixer], tasks=[task]).kickoff(
inputs={"objective": "the overdraft bug in account.py"}
)
```
Three concepts to understand here:
- An Agent defines who does the work, through its role, goal, LLM, and tools.
- A Task describes the assignment.
- A Crew brings agents and tasks together. Calling kickoff() runs the same execution loop described above, regardless of whether the underlying model is Anthropic, OpenAI, Google, or something else.
# Giving the agent tools
Tools are what let a model that only generates text actually work on a codebase. They read files, write them, run shell commands, and call external APIs.
CrewAI ships file system tools out of the box:
- FileReadTool reads files.
- DirectoryReadTool lists directories.
- FileWriterTool writes files.
```
from crewai_tools import DirectoryReadTool, FileReadTool, FileWriterTool
read_file = FileReadTool()
write_file = FileWriterTool()
list_dir = DirectoryReadTool()
filesystem_tools = [read_file, write_file, list_dir]
```
These also double as external memory. Instead of holding a large search result in the model's context window, the agent can write it to a file, keep only the filename, and read it back when needed.
This keeps the context window smaller and the model more focused, which is what Anthropic calls context engineering.

Built-in tools cover common workflows only. For anything more specific, you expose a Python function as a tool with the @tool decorator.
The docstring acts as the instruction manual, telling the model what the tool does, when to use it, and what it expects as input.
```
from crewai.tools import tool
import subprocess
@tool("run_tests")
def run_tests(path: str = "tests/") -> str:
"""Run the pytest suite at the given path and return the result."""
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
```
# Planning long-running tasks
As tasks get more complex, a plain execution loop starts losing track of the original goal. After enough tool calls, file reads, and intermediate results, the context fills up and the objective gets crowded out by everything that came after it.
This slow degradation is what people call context rot.
Planning addresses it directly. The agent builds a step-by-step plan before doing any work and keeps that plan in context throughout execution.
The plan doesn't do the work. It's a roadmap that keeps the model connected to the original goal, which is the same job Claude Code's to-do list does.

CrewAI adds this at the crew level with planning=True. It generates a plan before execution and keeps it available as the task progresses.
```
from crewai import Crew, LLM
crew = Crew(
agents=self.agents,
tasks=self.tasks,
planning=True,
planning_llm=LLM(model="gpt-4o-mini"),
)
```
> Note: By default, CrewAI uses gpt-4o-mini for planning, and you can swap in any LLM you prefer for that step.
Individual agents can also reason about their own work with reasoning=True:
```
from crewai import Agent
bug_fixer = Agent(
role="Bug Fixer",
goal="Find and describe the fix for the reported bug in the codebase.",
backstory="You read directories and files to build an accurate picture of the code.",
tools=[FileReadTool()],
reasoning=True,
max_reasoning_attempts=3 # Optional: Set a maximum number of reasoning attempts
)
```
Planning and reasoning solve different problems. Planning builds a high-level roadmap for the overall task, while reasoning gives one agent time to think through its own approach before acting.
When reasoning is enabled, the agent:
1. Reflects on the task and drafts an execution plan.
2. Evaluates whether the plan is ready.
3. Refines the plan if necessary, until it's satisfied or hits max_reasoning_attempts.
4. Injects the finalized reasoning plan into the task before execution.

Together, they keep the agent anchored on long-running tasks and reduce drift from the original goal.
# Delegating with subagents
Planning keeps the agent focused, but it doesn't reduce how much information the model has to hold. On a large codebase, even a well-planned task can exceed a single context window.
Finding one bug may require reading dozens of files, and the main agent doesn't need to keep all of them in memory.
Subagents solve this through delegation. The main agent hands a specific task to a helper agent, which works in its own context and returns a short summary. The main agent sees the conclusion, not the intermediate steps.

CrewAI supports this through hierarchical workflows, where a manager agent delegates to specialist agents and combines their results.
In our earlier setup, one Bug Fixer agent did all the heavy lifting. Let's split the work across a manager and three specialists:
- Codebase Explorer explores the code and maps the repository.
- Software Engineer implements the requested change.
- Test Runner runs the tests in the sandbox and reports pass or fail.
- Engineering Lead oversees the three specialists.

```
from crewai import Crew, Agent, Task, Process
explorer = Agent(
role="Codebase Explorer",
goal="Map the repository and surface the files relevant to the task.",
backstory="You read directories and files to build a picture of the code.",
tools=[read_file, list_dir],
llm=llm,
) # Same for other two specialist agents
manager = Agent(
role="Engineering Lead",
goal="Break the request into steps and delegate each to the right specialist.",
backstory="You decide who does what, review tests, finish once change is done.",
llm=llm,
allow_delegation=True,
)
crew = Crew(
agents=[explorer, coder, tester],
tasks=[task],
manager_agent=manager,
process=Process.hierarchical,
)
```
One thing to note is that allow_delegation is disabled by default, so it must be explicitly enabled on the manager.
# Sandboxing: Securing agent execution
An agent with shell access can run a destructive command, and telling the model not to do something isn't a safeguard.
Real protection comes from two layers:
1. A permission system that requires approval for sensitive actions.
2. A sandbox that isolates execution, so even approved commands can't touch the host machine.
Anthropic uses the same approach. Moving code execution into a sandbox cuts down how often a user needs to approve actions while still protecting the host system.

# Sandboxing in CrewAI
Executing code inside a sandbox rather than on the host machine applies that second layer. In this setup, code runs inside E2B, which spins up a fresh VM per session and destroys it afterward.
Shell commands and Python run entirely inside that isolated environment.

```
from crewai_tools import E2BExecTool, E2BPythonTool
sandbox_tools = [E2BExecTool(), E2BPythonTool()] # run tests / run code
```
# Human-in-the-loop approval
Setting human_input=True on a Task pauses the crew after it generates an answer. You review the output, then approve it or send it back for another iteration.
When execution reaches that task, CrewAI waits for your feedback through standard input.
```
from crewai import Task
task = Task(
description=(
"In the working directory ./workspace, {objective}. "
"Explore the code first, make the change, then run the tests and report."
),
expected_output="A summary of the files changed and the final test output.",
human_input=True,
)
```
If your crew runs behind a web app or chat interface rather than a terminal, CrewAI's webhook-based human-in-the-loop system handles the same review step.
# Memory and checkpointing
By default, an agent forgets everything once a run ends. Come back tomorrow to fix another bug in the same project, and it starts from zero.
Two mechanisms let an agent carry information across runs, and each serves a different purpose:
- Checkpointing saves the agent's state during a run, so it can resume after an interruption or continue from the same point along a different path.
- Persistent memory stores facts across separate conversations, including project preferences like "always format the final code before finishing."

## Memory in CrewAI
CrewAI provides a unified Memory interface rather than separate short-term, long-term, entity, and external memory types. When saving, it uses an LLM to identify important details, organize them, and make them retrievable later.
Setting memory=True on the crew gives it memory across runs. After each task, CrewAI extracts useful facts from the output and stores them, and in future runs it retrieves relevant memories and adds them to the task prompt.

```
from crewai import Crew
crew = Crew(
agents=[explorer, coder, tester],
tasks=[task],
memory=True,
)
```
All agents in a crew share its memory unless an agent is given its own.
## Checkpointing in CrewAI
A checkpoint is a snapshot of an agent's progress, including its configuration, task state, memory, intermediate results, inputs, and execution history.
By default, CrewAI creates a checkpoint whenever a task finishes, allowing the workflow to resume from that point if it's interrupted.
Checkpoints can live in one of two built-in stores:
- JsonProvider saves each checkpoint as a separate JSON file, which is easy to read and inspect manually.
- SqliteProvider stores all checkpoints in a single SQLite database, which holds up better under frequent checkpointing and larger workloads.

```
from crewai import Crew
crew = Crew(
agents=[explorer, coder, tester],
tasks=[task],
checkpoint=True,
)
```
Crew, Flow, and Agent all accept a checkpoint argument, and children inherit from their parent unless they set their own value.
# Putting it all together
Here's the full harness on one task, with the execution loop, tools, planning, subagents, sandboxing, and memory working together:
```
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:
"""Sync ./workspace into the sandbox, then run pytest there."""
return E2BExecTool().run(command=sync_and_test_command(path))
explorer = Agent(role="Codebase Explorer", goal="Map repo, surface relevant files.",
tools=[read_file, list_dir], llm=llm)
coder = Agent(role="Software Engineer", goal="Implement requested change.",
tools=filesystem_tools, reasoning=True, llm=llm)
tester = Agent(role="Test Runner", goal="Run tests in sandbox, report pass/fail.",
tools=sandbox_tools + [read_file] + [run_tests], llm=llm)
manager = Agent(role="Engineering Lead", goal="Delegate steps, finish once tests pass.",
allow_delegation=True, llm=llm)
task = Task(
description="In ./workspace, {objective}. Explore, edit, test, report.",
expected_output="Summary of changes and test 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": "fix failing tests in account.py"})
```
Agent harnesses are easiest to evaluate when success can be checked automatically. A test suite gives the agent a concrete objective, so it can plan, edit, test, and repeat until everything passes.
So this was tested against a small codebase, a BankAccount class with two real bugs and five tests, three of which failed. The rule was to fix only the implementation, not the tests.
This mirrors how Anthropic evaluates coding agents internally. One published example has Claude rebuilding a clone of the claude.ai interface against a large suite of failing tests.
Here, the harness took the project from 3 failing and 2 passing to all 5 passing, with the implementation-only rule closing off the shortcut of editing or removing the failing tests.

# What's still your job
Some parts of the system aren't things the framework builds for you:
- The prompts. Each agent's behavior comes from its role, goal, and backstory. Getting those right takes testing and iteration, and no configuration flag substitutes for it.
- The execution environment. The sandbox, whether E2B or a self-managed VM, has to be set up and wired in.
- Tool selection. Which tools each agent gets, and which agent should have access to what, is a design decision the framework doesn't make.
There's also a cost to the harness itself. Planning, subagents, and looping all add API calls, so a complex agent setup can end up more expensive than a task a single model call would have solved directly.
And there's a longer-term limitation worth keeping in mind. As models improve, some of the scaffolding stops being necessary, because some of what gets built into a harness today is a workaround for today's model limits rather than a permanent requirement.
Anthropic originally used context resets to keep Claude Sonnet 4.5 from ending tasks too early, and they weren't needed anymore with the more capable Claude Opus 4.5.

# Wrapping up
That's the whole finding. A coding agent's capability lives mostly in the harness, and an orchestration framework hands you more of that harness than you'd guess.
The loop, planning, delegation, sandboxing, and memory all arrive as configuration, while the prompts, the execution environment, and the tool choices remain yours.
If you want to run this against your own codebase, the CrewAI docs cover every feature used here, and the framework is fully open source.
Check out CrewAI Docs →
Find all the code here →
Thanks for reading!
Cheers! :)
## 相关链接
- [Akshay](https://x.com/akshay_pachaar)
- [@akshay_pachaar](https://x.com/akshay_pachaar)
- [44K](https://x.com/akshay_pachaar/status/2077455755066868098/analytics)
- [@tool](https://x.com/@tool)
- [claude.ai](https://claude.ai/)
- [Check out CrewAI Docs →](https://docs.crewai.com/)
- [Find all the code here →](https://github.com/patchy631/ai-engineering-hub/tree/main/build-code-harness)
- [Upgrade to Premium](https://x.com/i/premium_sign_up)
- [2:10 AM · Jul 16, 2026](https://x.com/akshay_pachaar/status/2077455755066868098)
- [44.1K Views](https://x.com/akshay_pachaar/status/2077455755066868098/analytics)
- [View quotes](https://x.com/akshay_pachaar/status/2077455755066868098/quotes)
---
*导出时间: 2026/7/16 13:38:47*
---
## 中文翻译
# 让我们构建 Claude Code 的 harness(分步指南)
**作者**: Akshay
**日期**: 2026-07-15T18:10:23.000Z
**来源**: [https://x.com/akshay_pachaar/status/2077455755066868098](https://x.com/akshay_pachaar/status/2077455755066868098)
---

我们将涵盖构建编码 harness 的所有要素,包括代理循环、规划、子代理、沙箱、内存和检查点,一步步进行构建。
如果你曾经尝试构建自己的编码代理,你就知道这其中的过程。你将模型连接到文件工具和 shell,将其指向一个真实的代码库,结果它在十几次工具调用内就崩溃了。
它读取错误的文件,中途丢失目标,并用不再需要的输出填满了上下文。
然后同样的任务在 Claude Code 中运行并顺利完成。简单的结论是 Anthropic 只是拥有更好的模型,但这个结论忽略了实际工作发生的地方。
差异在于 harness。Harness 是包裹在模型周围的普通代码,它处理规划、工具执行、内存和安全,而模型仅决定下一步。
当你画出来时,一个完全 equipped 的代理看起来是这样的:

这张图看起来很复杂,但可以分成四组:
- **Memory(内存)** 为模型提供其工作上下文以及它在会话中学到的事实。
- **Skills(技能)** 编码了代理应该如何运作,这意味着它遵循的程序、约束和启发式方法。
- **Protocols(协议)** 将代理连接到用户、工具和其他代理。
- **Harness core(核心)** 通过子代理编排、沙箱、评估器、审批循环、可观察性和上下文压缩将所有内容联系在一起。
Anthropic 将这种分工描述为大脑和双手。模型是选择每个动作的大脑,而 harness 是执行该动作并保持运行正轨的双手。
因此,你的代理与 Claude Code 之间的差距不在于模型,而在于模型周围的机制。
Claude Code 是当今生产环境中能力最强的 harness 之一,而且它是由图中 surprisingly 少的一层构建而成的。为了看看你需要自己构建多少这样的机制,我用 CrewAI 重新构建了它,这是一个用于编排代理的开源框架。
其中比我预期更多的部分映射到了内置功能,而没有映射的部分才是真正的工程所在。
让我们一层一层地构建它,从核心循环开始,然后堆叠规划、子代理、沙箱和内存。在每一步,我们将标记框架结束的地方以及你工作开始的地方。
# Claude Code 的 harness 如何工作
Claude Code 的中心是一个普通的代理循环。你向它发送一条消息,模型决定下一步做什么,然后它要么直接响应,要么请求工具。如果它请求工具,工具就会运行,结果返回到对话中,模型再次决定。
这会重复,直到模型返回最终答案且不再有工具调用。
在该循环内部,模型读取文件、编辑代码、运行 shell 命令和执行测试。这些不是单独的模式。它们只是同一个循环中不同的工具调用。
然而,仅靠循环对于可靠的编码代理来说是不够的。Claude Code 在其周围添加了规划、文件工具、子代理、内存以及权限和沙箱系统。这些层不会取代循环,它们使循环对于实际工作来说足够安全和可靠。

这就是我们将要重建的架构,首先是核心循环,然后是上面的每一层,将每一层映射到处理它的 CrewAI 功能。
# 核心代理循环
循环运行相同的序列,直到任务完成:
1. 要求模型执行任务。
2. 模型直接响应或请求一个或多个工具。
3. 如果请求了工具,运行它们并将结果返回给模型。
4. 使用更新的对话重复。
5. 当模型响应且未请求任何工具时,任务完成。

```
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 在你创建代理后自动提供这个执行循环。你不需要自己实现 while 循环,你定义代理并分配给它一个任务。
# 构建第一个代理
让我们创建一个简单的 Bug Fixer(错误修复)代理。
```
from crewai import LLM, Agent, Crew, Task
bug_fixer = Agent(
role="Bug Fixer",
goal="Find and describe the fix for the reported bug in the codebase.",
backstory="You read directories and files to build an accurate picture of the code.",
llm="claude-sonnet-4-6",
)
task = Task(
description="Find the fix for {objective}.",
expected_output="A short description of the fix and which file it belongs in.",
)
result = Crew(agents=[bug_fixer], tasks=[task]).kickoff(
inputs={"objective": "the overdraft bug in account.py"}
)
```
这里需要理解三个概念:
- **Agent(代理)** 通过其角色、目标、LLM 和工具定义谁来完成工作。
- **Task(任务)** 描述作业。
- **Crew(团队)** 将代理和任务聚集在一起。调用 kickoff() 会运行上述相同的执行循环,无论底层模型是 Anthropic、OpenAI、Google 还是其他。
# 为代理提供工具
工具让一个只生成文本的模型能够真正处理代码库。它们读取文件、写入文件、运行 shell 命令和调用外部 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]
```
这些工具也充当外部内存。代理可以将大型搜索结果写入文件,只保留文件名,并在需要时将其读回,而不是将大量搜索结果保留在模型的上下文窗口中。
这使上下文窗口更小,模型更专注,这就是 Anthropic 所称的上下文工程。

内置工具仅涵盖常见工作流。对于更具体的内容,你可以使用 @tool 装饰器将 Python 函数作为工具公开。
文档字符串充当指令手册,告诉模型工具的作用、何时使用它以及期望什么输入。
```
from crewai.tools import tool
import subprocess
@tool("run_tests")
def run_tests(path: str = "tests/") -> str:
"""Run the pytest suite at the given path and return the result."""
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 的待办事项列表所做的工作相同。

CrewAI 在 crew 级别通过 planning=True 添加了此功能。它在执行之前生成一个计划,并在任务进展时保持其可用。
```
from crewai import Crew, LLM
crew = Crew(
agents=self.agents,
tasks=self.tasks,
planning=True,
planning_llm=LLM(model="gpt-4o-mini"),
)
```
> 注意:默认情况下,CrewAI 使用 gpt-4o-mini 进行规划,你可以为该步骤交换任何你喜欢的 LLM。
单个代理也可以通过 reasoning=True 对自己的工作进行推理:
```
from crewai import Agent
bug_fixer = Agent(
role="Bug Fixer",
goal="Find and describe the fix for the reported bug in the codebase.",
backstory="You read directories and files to build an accurate picture of the code.",
tools=[FileReadTool()],
reasoning=True,
max_reasoning_attempts=3 # 可选:设置最大推理尝试次数
)
```
规划和推理解决不同的问题。规划为整个任务构建高层路线图,而推理让一个代理在行动之前有时间思考自己的方法。
当启用推理时,代理:
1. 反思任务并起草执行计划。
2. 评估计划是否准备就绪。
3. 如有必要,完善计划,直到满意或达到 max_reasoning_attempts。
4. 在执行之前将最终确定的推理计划注入任务。

它们共同使代理在长时间运行的任务中保持专注,并减少偏离原始目标。
# 使用子代理进行委派
规划使代理保持专注,但它不会减少模型必须持有的信息量。在大型代码库上,即使是计划良好的任务也可能超出单个上下文窗口。
查找一个 bug 可能需要读取几十个文件,而主代理不需要将所有文件都保存在内存中。
子代理通过委派解决这个问题。主代理将特定任务交给辅助代理,辅助代理在自己的上下文中工作并返回简短的摘要。主代理看到的是结论,而不是中间步骤。

CrewAI 通过分层工作流支持这一点,其中管理器代理委派给专家代理并合并他们的结果。
在我们之前的设置中,一个 Bug Fixer 代理完成了所有繁重的工作。让我们将工作分配给一个管理器和三个专家:
- **Codebase Explorer(代码库探索者)** 探索代码并映射存储库。
- **Software Engineer(软件工程师)** 实现请求的更改。
- **Test Runner(测试运行者)** 在沙箱中运行测试并报告通过或失败。
- **Engineering Lead(工程主管)** 监督这三个专家。

```
from crewai import Crew, Agent, Task, Process
explorer = Agent(
role="Codebase Explorer",
goal="Map the repository and surface the files relevant to the task.",
backstory="You read directories and files to build a picture of the code.",
tools=[read_file, list_dir],
llm=llm,
) # 其他两个专家代理也是如此
manager = Agent(
role="Engineering Lead",
goal="Break the request into steps and delegate each to the right specialist.",
backstory="You decide who does what, review tests, finish once change is done.",
llm=llm,
allow_delegation=True,
)
crew = Crew(
agents=[explorer, coder, tester],
tasks=[task],
manager_agent=manager,
process=Process.hierarchical,
)
```
需要注意的一点是,allow_delegation 默认是禁用的,因此必须在管理器上显式启用。
# 沙箱:保护代理执行
具有 shell 访问权限的代理可以运行破坏性命令,告诉模型不要做某事并不是一种保障。
真正的保护来自两层:
1. 一个权限系统,要求对敏感操作进行批准。
2. 一个沙箱,隔离执行,因此即使是批准的命令也无法触及主机。
Anthropic 使用相同的方法。将代码执行移动到沙箱中减少了用户需要批准操作的频率,同时仍然保护主机系统。

# CrewAI 中的沙箱
在沙箱内而不是在主机上执行代码应用了第二层。在此设置中,代码在 E2B 内运行,它为每个会话启动一个新 VM 并在事后销毁它。
Shell 命令和 Python 完全在该隔离环境中运行。

```
from crewai_tools import E2BExecTool, E2BPythonTool
sandbox_tools = [E2BExecTool(), E2BPythonTool()] # 运行测试 / 运行代码
```
# 人在回路审批
在任务上设置 human_input=True 会在 crew 生成答案后暂停它。你审查输出,然后批准它或将其发回进行另一次迭代。
当执行到达该任务时,CrewAI 通过标准输入等待你的反馈。
```
from crewai import Task
task = Task(
description=(
"In the working directory ./workspace, {objective}. "
"Explore the code first, make the change, then run the tests and report."
),
expected_output="A summary of the files changed and the final test output.",
human_input=True,
)
```
如果你的 crew 在 Web 应用或聊天界面后面而不是终端上运行,CrewAI 基于 webhook 的人在回路系统处理相同的审查步骤。
# 内存和检查点
默认情况下,代理在运行结束后会忘记所有内容。明天回来修复同一项目中的另一个 bug,它将从零开始。
两种机制允许代理在运行之间携带信息,每种机制服务于不同的目的:
- **Checkpointing(检查点)** 在运行期间保存代理的状态,因此它可以在中断后恢复或沿着不同的路径从同一点继续。
- **Persistent memory(持久内存)** 在单独的对话中存储事实,包括项目偏好,如“在完成之前始终格式化最终代码”。

## CrewAI 中的内存
CrewAI 提供统一的内存接口,而不是单独的短期、长期、实体和外部内存类型。保存时,它使用 LLM 识别重要细节,组织它们,并在以后检索它们。
在 crew 上设置 memory=True 使其在运行之间具有内存。在每个任务之后,CrewAI 从输出中提取有用的事实并存储它们,在未来的运行中,它检索相关记忆并将它们添加到任务提示中。

```
from crewai import Crew
crew = Crew(
agents=[explorer, coder, tester],
tasks=[task],
memory=True,
)
```
crew 中的所有代理共享其内存,除非某个代理拥有自己的内存。
## CrewAI 中的检查点
检查点是代理进度的快照,包括其配置、任务状态、内存、中间结果、输入和执行历史。
默认情况下,CrewAI 在任务完成时创建检查点,允许工作流从中断时恢复。
检查点可以存在于两个内置存储之一中:
- **JsonProvider** 将每个检查点保存为单独的 JSON 文件,易于阅读和手动检查。
- **SqliteProvider** 将所有检查点存储在单个 SQLite 数据库中,在频繁检查点和更大工作负载下表现更好。

```
from crewai import Crew
crew = Crew(
agents=[explorer, coder, tester],
tasks=[task],
checkpoint=True,
)
```
Crew、Flow 和 Agent 都接受 checkpoint 参数,而子级 i