基于Claude Code与Codex构建AI Agent循环:从提示词到自主编程实践

Agent循环Claude Code提示词工程
于 2026-07-07 15:52:33 修改
·本内容遵循CC 4.0 BY-SA版权协议

1. 从“提示词”到“Agent循环”,到底解决了什么问题

如果你还在用“一次性提示词”去驱动大模型写代码,那很可能已经过时了。现在更值得关注的,是让模型自己思考、自己执行、自己修正的 Agent 循环。这不仅仅是换个说法,而是从“你问它答”的单次交互,升级为“你定目标,它来执行”的持续协作。

这篇文章要聊的,就是如何用 Claude CodeCodex 这类具备代码执行能力的模型,来构建一个能真正跑起来的 Agent 循环。核心价值在于:把复杂的、多步骤的编程任务,拆解成模型能自主推进的迭代过程。比如,你不是直接问“给我写个爬虫”,而是告诉它“目标是获取某网站前十页的标题,请分步执行,遇到错误自己尝试修复”。

这适合两类人看:一是已经熟悉基础提示词,但感觉模型输出不稳定、复杂任务一步到位很难的开发者;二是想探索如何将大模型更深度地集成到自动化工作流或工具链里的工程师。最关键的能力,是让模型具备了“执行-观察-调整”的闭环思维,而不仅仅是“生成”。

2. 理解 Agent 循环:不只是聊天,而是赋予“行动”与“反思”能力

在传统的提示词工程里,我们和模型的对话往往是线性的:用户输入问题 -> 模型输出答案 -> 结束。这种模式对于定义清晰、一步就能完成的任务(例如“将这段 Python 代码转换成 Java”)很有效。但面对“开发一个具有某某功能的脚本”这类开放任务时,单次生成的结果质量波动很大,且难以处理过程中的意外(如依赖缺失、API变化、解析错误)。

Agent 循环的核心思想,是引入 工具(Tools)记忆(Memory)规划(Planning) 能力,让模型成为一个可以自主行动的智能体。一个典型的循环包含以下几个阶段:

2.1 规划阶段:拆解任务与制定策略

模型接收到一个高层目标(例如:“分析这个 GitHub 仓库最近一个月 issue 的情绪倾向”)。它不会直接生成最终代码,而是先进行规划:

  • 任务拆解:将大目标分解为可执行的子任务。例如:1. 克隆仓库;2. 获取 issue 列表;3. 提取 issue 文本;4. 调用情感分析 API;5. 汇总结果并可视化。
  • 工具选择:决定每个子任务需要使用什么工具(如:git 命令、requests 库、textblob 库、matplotlib)。
  • 策略制定:考虑可能的风险和备用方案(如:GitHub API 有速率限制,需要分页或添加延迟)。

2.2 执行与观察阶段:运行代码并获取反馈

这是与传统提示词最大的不同。模型生成代码(或命令)后,系统会真正执行这段代码

  • 执行:在一个安全的沙箱环境(如 Docker 容器、受限的本地环境)中运行模型生成的代码。
  • 观察:捕获执行的所有输出:标准输出(stdout)、标准错误(stderr)、返回值、生成的文件等。这些反馈是模型进行下一步决策的关键输入。

2.3 评估与调整阶段:基于结果进行迭代

模型收到执行反馈后,对其进行评估:

  • 成功判断:子任务是否完成?输出是否符合预期?
  • 错误诊断:如果执行失败(抛出异常、返回错误码、输出为空),模型需要分析错误信息(stderr),判断原因(是语法错误、逻辑错误、依赖问题还是环境问题?)。
  • 计划调整:根据评估结果,决定下一步行动。是重试当前步骤(可能修改代码),还是跳过,或者调整后续计划?

这个“规划 -> 执行 -> 观察 -> 评估 -> 再规划”的循环会持续进行,直到最终目标达成或达到迭代上限。

2.4 为什么 Claude Code 和 Codex 适合做这件事?

  • 代码生成与理解能力强:它们专精于代码,生成的代码片段可执行性高,也能很好地理解执行错误信息。
  • 支持长上下文:Agent 循环会产生大量的交互历史(用户指令、模型生成的代码、执行输出)。Claude 等模型的长上下文窗口能很好地记住整个对话和任务状态。
  • 对工具使用的描述能力:它们能理解如何调用系统命令、Python 库、REST API 等常见“工具”。

3. 实战环境搭建:安全第一,隔离执行环境

在开始写循环之前,最要紧的不是设计多么精巧的提示词,而是搭建一个安全、可控、可观察的执行环境。让模型生成的代码随意在你的主机上运行是极其危险的。我建议的路径是:先本地模拟,再用容器隔离。

3.1 基础环境准备

你需要一个 Python 环境(建议 3.8+)和必要的库。核心是能与大模型 API 交互,并能执行子进程。

BASH
# 基础库
pip install openai anthropic requests
# 用于更优雅地处理子进程(可选但推荐)
pip install pexpect

3.2 创建安全的代码执行器

这是 Agent 循环的“手”和“眼睛”。你需要一个函数,它接受一段代码(字符串),在受控环境中运行,并返回结果、错误和状态。

方案一:使用 subprocess 运行独立脚本(简单,适用于命令行任务)

PYTHON
import subprocess
import tempfile
import os
 
def execute_code_safely(code_str, language=“python”, timeout=30):
"""
在一个临时文件中执行代码,并捕获输出。
警告:此方法仍有一定风险,仅用于可信代码或严格隔离的环境。
"""
with tempfile.NamedTemporaryFile(mode=‘w’, suffix=‘.py’, delete=False) as f:
f.write(code_str)
temp_file_path = f.name
 
try:
# 使用 subprocess 运行,捕获输出和错误
result = subprocess.run(
[‘python’, temp_file_path],
capture_output=True,
text=True,
timeout=timeout,
shell=False
)
stdout = result.stdout
stderr = result.stderr
return_code = result.returncode
except subprocess.TimeoutExpired:
stdout = “”
stderr = f“Execution timed out after {timeout} seconds.”
return_code = -1
finally:
# 清理临时文件
os.unlink(temp_file_path)
 
return {
“stdout”: stdout,
“stderr”: stderr,
“returncode”: return_code,
“success”: return_code == 0
}

方案二:使用 Docker 容器(推荐,更安全) 为每个任务启动一个干净的、无网络(或受限网络)的 Docker 容器,代码在容器内执行。这能有效防止对宿主机的破坏。

PYTHON
import docker
import tempfile
 
client = docker.from_env()
 
def execute_in_docker(code_str, image=“python:3.9-slim”, timeout=30):
"""
在 Docker 容器中执行代码。
"""
# 1. 创建临时目录和文件(通过卷挂载)
with tempfile.TemporaryDirectory() as tmpdir:
code_path = os.path.join(tmpdir, ‘script.py’)
with open(code_path, ‘w’) as f:
f.write(code_str)
 
# 2. 运行容器
container = client.containers.run(
image,
command=f“python /tmp/script.py”,
volumes={tmpdir: {‘bind’: ‘/tmp’, ‘mode’: ‘ro’}}, # 只读挂载
working_dir=‘/tmp’,
stdout=True,
stderr=True,
detach=False, # 等待执行完成
remove=True, # 执行后自动删除容器
mem_limit=“100m”, # 限制内存
network_mode=“none”, # 禁用网络(除非任务需要)
timeout=timeout
)
# 容器输出通常是 bytes,需要解码
if isinstance(container, bytes):
output = container.decode(‘utf-8’)
# 通常 Docker SDK 将 stdout 和 stderr 合并返回
# 更精细的处理可以分别获取 logs(stdout=True) 和 logs(stderr=True)
stdout = output
stderr = “”
success = True # 需要根据返回码判断,这里简化了
else:
# 处理可能的异常
stdout = “”
stderr = str(container)
success = False
 
return {“stdout”: stdout, “stderr”: stderr, “success”: success}

注意:生产环境务必考虑更完善的沙箱方案,如使用 gVisorFirecracker 或专门的代码执行服务。这里 Docker 方案是向安全迈进了一大步。

3.3 初始化你的 AI 助手

以 Claude 和 OpenAI Codex 为例,你需要设置 API 密钥和客户端。

PYTHON
import anthropic
import openai
import os
 
# 从环境变量读取密钥
ANTHROPIC_API_KEY = os.getenv(“ANTHROPIC_API_KEY”)
OPENAI_API_KEY = os.getenv(“OPENAI_API_KEY”)
 
claude_client = anthropic.Anthropic(api_key=ANTHROPIC_API_KEY)
openai.api_key = OPENAI_API_KEY # 旧版写法,新版请用 `openai.OpenAI()`

4. 构建核心 Agent 循环:一个可运行的 Python 示例

现在,我们把环境、执行器和模型组合起来。下面是一个简化但完整的 Agent 循环实现,它尝试完成一个具体任务:“获取 Python 官方博客(https://blog.python.org/)的最新文章标题”。

4.1 定义系统提示词(设定 Agent 角色与规则)

系统提示词是 Agent 的“宪法”,它定义了行为准则、可用工具和输出格式。这是成功的关键。

PYTHON
SYSTEM_PROMPT = “””
你是一个智能编程助手,可以编写并执行代码来完成用户的任务。你必须遵循以下规则:
1. 你只能通过编写代码来与外界交互。代码必须用 ```python ... ``` 的格式包裹。
2. 代码将在一个安全的、有网络访问权限的 Python 环境中执行。你可以使用 requests, BeautifulSoup, pandas 等常见库。如果缺少库,请在代码中尝试安装(使用 pip)。
3. 执行后,你会看到代码的 **标准输出(stdout)** 和 **标准错误(stderr)**。
4. 基于执行结果,你必须进行分析:
- 如果任务成功完成,输出最终答案,并以 `[任务完成]` 结束。
- 如果代码有错误(抛出异常),分析错误,修改代码,然后重新尝试。
- 如果输出不符合预期,调整你的策略或代码逻辑。
5. 每次回应都必须包含:对当前情况的分析、下一步计划、以及要执行的代码。
6. 保持思考过程简洁。
当前任务:{user_task}
这是你第一次运行,请开始你的规划与执行。
“””

4.2 实现主循环函数

这个函数负责维护对话历史,调用模型,执行代码,并将结果反馈给模型。

PYTHON
def run_agent_loop(task_description, model_type=“claude”, max_iterations=10):
"""
运行一个简单的 Agent 循环。
model_type: ‘claude’ 或 ‘codex’
"""
messages = [
{“role”: “user”, “content”: SYSTEM_PROMPT.format(user_task=task_description)}
]
iteration = 0
 
while iteration < max_iterations:
iteration += 1
print(f“\n=== 迭代 {iteration} ===")
 
# 1. 调用大模型,获取响应
if model_type == “claude”:
response = claude_client.messages.create(
model=“claude-3-sonnet-20240229”, # 根据实际情况选择模型
max_tokens=2000,
messages=messages
)
assistant_reply = response.content[0].text
elif model_type == “codex”:
# 注意:OpenAI 的 ChatCompletion 格式
response = openai.ChatCompletion.create(
model=“gpt-4”, # Codex 已不推荐,可用 GPT-4 或 GPT-3.5-Turbo 替代
messages=messages,
max_tokens=1500
)
assistant_reply = response.choices[0].message.content
else:
raise ValueError(“不支持的模型类型”)
 
print(f“助手回复:\n{assistant_reply}”)
 
# 2. 从回复中提取代码块
code_to_execute = None
if “```python” in assistant_reply:
# 简单提取第一个代码块
parts = assistant_reply.split(“```python”)
if len(parts) > 1:
code_part = parts[1].split(“```”)[0]
code_to_execute = code_part.strip()
# 如果没有 python 代码块,检查是否任务已完成
elif “[任务完成]” in assistant_reply:
print(“任务已由助手标记完成。”)
print(“最终答案:”, assistant_reply.split(“[任务完成]”)[0])
break
 
if not code_to_execute:
print(“未找到可执行的 Python 代码。助手可能在进行纯文本分析。将回复加入历史继续对话。”)
messages.append({“role”: “assistant”, “content”: assistant_reply})
# 可以添加一个用户提示,催促其提供代码
messages.append({“role”: “user”, “content”: “请根据你的分析,提供下一步要执行的 Python 代码。”})
continue
 
print(f“提取到代码:\n{code_to_execute}”)
 
# 3. 安全执行代码
print(“正在执行代码...”)
# 这里使用前面定义的 execute_code_safely 或 execute_in_docker
execution_result = execute_code_safely(code_to_execute) # 请替换为你的安全执行函数
 
print(f“执行结果 - 成功: {execution_result[‘success’]}”)
print(f“标准输出:\n{execution_result[‘stdout’][:500]}...”) # 只打印前500字符
if execution_result[‘stderr’]:
print(f“标准错误:\n{execution_result[‘stderr’]}”)
 
# 4. 构建反馈信息,加入对话历史
feedback = f“””
上一次你生成的代码已执行。
执行状态: {‘成功’ if execution_result[‘success’] else ‘失败’}
标准输出(stdout):
{execution_result[‘stdout’][:2000]} # 限制长度,防止上下文爆炸
标准错误(stderr):
{execution_result[‘stderr’]}
请分析以上结果,并决定下一步行动。
“””
# 将助手上次的回复和执行结果加入历史
messages.append({“role”: “assistant”, “content”: assistant_reply})
messages.append({“role”: “user”, “content”: feedback})
 
# 5. 判断是否成功完成
# 这里可以加入更复杂的成功条件判断,例如输出中是否包含目标信息
if execution_result[‘success’] and “Python” in execution_result[‘stdout’]: # 简单示例条件
print(“\n任务似乎已成功完成!检查输出是否符合预期。”)
# 可以主动询问模型是否完成
messages.append({“role”: “user”, “content”: “输出看起来包含了目标信息。请确认任务是否完成?如果完成,请输出最终答案并以 [任务完成] 标记。”})
# 或者直接跳出循环
# break
 
if iteration >= max_iterations:
print(f“达到最大迭代次数 {max_iterations},任务未完成。”)
return messages

4.3 运行并观察

调用这个函数,看看 Agent 如何工作。

PYTHON
if __name__ == “__main__”:
task = “获取 Python 官方博客 (https://blog.python.org/) 的最新5篇文章标题。”
history = run_agent_loop(task, model_type=“claude”, max_iterations=6)

运行后,你会在控制台看到类似这样的迭代过程:

TEXT
=== 迭代 1 ===
助手回复:
我需要获取一个网页的内容并解析。我将使用 requests 和 BeautifulSoup。首先,我需要安装必要的库...
```python
import requests
from bs4 import BeautifulSoup
url = “https://blog.python.org/“
response = requests.get(url)
soup = BeautifulSoup(response.text, ‘html.parser’)
# 尝试找到文章标题的HTML元素,这需要探索
print(soup.prettify()[:1000]) # 先打印部分HTML结构看看

提取到代码... 执行结果 - 成功: True 标准输出: (打印出网页的HTML结构) === 迭代 2 === 助手回复: 我看到了HTML结构。文章标题似乎位于 <h2 class=‘entry-title’> 标签内的 <a> 链接里。我来调整代码提取标题。

PYTHON
import requests
from bs4 import BeautifulSoup
url = “https://blog.python.org/“
response = requests.get(url)
soup = BeautifulSoup(response.text, ‘html.parser’)
titles = soup.find_all(‘h2’, class_=‘entry-title’)
for i, title_tag in enumerate(titles[:5]):
link = title_tag.find(‘a’)
if link:
print(f“{i+1}. {link.text.strip()}”)

...

TEXT
最终,在几次迭代内,Agent 应该能输出最新的文章标题列表,并以 `[任务完成]` 结束。
 
## 5. 关键细节与避坑指南:让循环真正可靠
 
看到这里,你可能已经跑通了一个基础循环。但要让它在真实项目中可靠工作,还需要处理大量细节。下面是我在实际搭建中总结的几个关键点和常见坑。
 
### 5.1 上下文管理:别让历史对话撑爆 Token
Agent 循环的每次迭代都会在对话历史中追加模型回复和执行结果。几次迭代后,上下文会变得非常长,导致:
1. **API 调用成本激增**(按 Token 收费)。
2. **模型可能无法关注最早的任务指令**。
3. **达到模型上下文长度上限**。
 
**解决方案:摘要与选择性记忆**
不要无脑地把所有历史都塞进去。可以采用以下策略:
* **系统指令持久化**:确保系统提示词在每次请求中都被包含(某些 API 支持单独的 `system` 角色)。
* **总结长输出**:对于很长的 `stdout`(比如打印了整个网页 HTML),不要全量发送。可以只发送前 N 个字符,或者让模型自己生成一个摘要。在我们的示例中,我们只发送了前 2000 个字符。
* **只保留最近几轮对话**:实现一个滑动窗口,只保留最近 3-5 轮完整的“思考-代码-结果”循环。
* **关键信息提取**:让模型在完成一个子任务后,主动提取关键结果(如“已成功获取到标题列表:[‘标题1’, ‘标题2’]”),然后将这个简洁的结果而非原始日志加入历史。
 
### 5.2 错误处理与超时控制:防止无限循环
模型可能会陷入死循环,比如不断重试一个无法修复的错误。
* **设置最大迭代次数**:就像示例中的 `max_iterations`,这是最后的安全网。
* **识别重复错误**:在代码中检查最近几次的 `stderr` 是否相似。如果模型连续多次生成几乎相同的错误代码,可以中断循环,并提示“检测到重复错误,请重新评估任务可行性”。
* **执行超时**:务必为代码执行设置超时(如 `timeout=30` 秒),防止恶意或 bug 代码无限运行。
* **资源限制**:在使用 Docker 时,严格限制 CPU、内存和网络。
 
### 5.3 工具能力的定义与扩展
我们的示例只允许模型写 Python 代码。但一个强大的 Agent 应该能调用更丰富的工具。
* **封装常用操作为“函数”**:对于文件读写、数据库查询、调用特定 API 等操作,与其让模型生成原始代码,不如你预先定义好一系列安全的函数,并告诉模型这些函数的描述和调用方式(类似于 OpenAI 的 Function Calling)。这更安全、更高效。
```python
# 提供给模型的工具描述
tools = [
{
“name”: “read_file”,
“description”: “读取指定路径文件的内容”,
“parameters”: {“type”: “object”, “properties”: {“filepath”: {“type”: “string”}}}
},
{
“name”: “web_search”,
“description”: “在互联网上搜索信息”,
“parameters”: {“type”: “object”, “properties”: {“query”: {“type”: “string”}}}
}
]
```
然后在循环中,解析模型想要调用哪个工具,由你的主程序去安全地执行对应的函数,再将结果返回给模型。
* **限制危险操作**:在系统提示词中明确禁止 `os.system`、`subprocess` 调用任意命令、`eval`、`exec`、访问特定路径等。更好的做法是在执行沙箱中直接屏蔽这些模块。
 
### 5.4 成功条件判断:何时停止循环?
不能完全依赖模型说 `[任务完成]`。它可能误判。
* **定义明确的可验证目标**:对于“获取标题”任务,成功条件可以是“stdout 中包含至少一个非空的标题文本,且格式符合预期”。你可以在主循环中编写逻辑来检查这个条件。
* **多条件判断**:结合执行成功(returncode==0)、输出非空、输出包含关键词、模型主动声明完成等多个信号来判断。
* **用户确认**:对于重要任务,可以在循环中加入“检查点”,将关键中间结果呈现给用户,询问是否继续。
 
### 5.5 提示词工程:引导模型更好地规划与反思
系统提示词的质量决定了 Agent 的“性格”和能力。除了基本规则,还可以加入:
* **思维链(Chain-of-Thought)鼓励**:“请逐步思考,先规划,再写代码。”
* **错误分析模板**:“如果遇到错误,请先解释你认为的错误原因,再提供修改后的代码。”
* **鼓励使用已知信息**:“你之前已经成功执行了 X,其结果是 Y,这可以作为下一步的输入。”
* **限制代码范围**:“尽量使用标准库和 requests, BeautifulSoup 库。如需其他库,请先尝试 `pip install`。”
 
## 6. 从 Demo 到生产:架构思考与优化方向
 
一个能跑起来的 Demo 和一个能在生产环境处理复杂任务的 Agent 系统之间,还有很大距离。如果你打算深入,可以从以下几个方向优化:
 
### 6.1 架构分层
将系统清晰地分层:
1. **Orchestrator(协调层)**:负责管理整个工作流,调用大模型,维护任务状态和记忆。这是我们主循环的核心。
2. **Execution Layer(执行层)**:提供安全、可监控、资源受限的代码/工具执行环境。Docker 沙箱是基础,可以考虑 Kubernetes Jobs 或 AWS Lambda 等无服务器函数进行弹性执行。
3. **Tool Registry(工具注册中心)**:集中管理所有可用的工具(函数),包括它们的描述、参数 schema 和安全策略。模型通过查询这里来决定使用什么工具。
4. **Memory/State Store(状态存储)**:将对话历史、中间结果、任务状态持久化到数据库(如 Redis、PostgreSQL),而不是只放在内存里。这支持长时任务和断点续跑。
 
### 6.2 更强大的规划与反思
* **Tree of Thoughts**:让模型在每一步考虑多个可能的计划(分支),然后通过快速模拟或评估选择最有希望的一条。
* **ReAct 模式**:显式地将模型的输出结构化为 `Thought:`、`Action:`、`Observation:` 三部分。这能极大地提升模型推理的条理性和可靠性。
* **外部验证器**:对于关键步骤的结果(如下载的文件、计算的数据),使用另一个简单的程序或规则进行校验,再将结果反馈给 Agent。
 
### 6.3 成本与性能优化
* **选择性价比模型**:对于规划步骤,可以使用更便宜、速度更快的模型(如 Claude Haiku, GPT-3.5-Turbo);对于复杂的代码生成步骤,再用更强的模型(如 Claude Sonnet, GPT-4)。
* **缓存**:对相同的工具调用或子任务结果进行缓存,避免重复计算和 API 调用。
* **异步执行**:如果任务可并行化(如处理多个文件),可以同时发起多个执行请求,但要注意 API 的速率限制。
 
### 6.4 监控与可观测性
生产系统必须可观测。你需要记录:
* **每次 API 调用的请求和响应**(可脱敏)。
* **每次工具/代码执行的输入、输出、耗时和资源使用**。
* **整个任务的生命周期事件**(开始、每个迭代、成功、失败)。
* **关键指标**:任务成功率、平均迭代次数、平均耗时、Token 消耗成本。
 
这些日志是调试、优化和计费的基础。
 
## 7. 总结:从“写提示词”到“设计智能体”
 
回到最初的问题:提示词过时了吗?并没有。好的提示词(特别是系统提示词)仍然是 Agent 的灵魂。但我们的焦点变了——从精心雕琢一个能“猜中”我们心思的魔法咒语,转向**设计一个能自主利用工具、从反馈中学习、并持续向目标推进的系统**。
 
Claude Code、GPT-4 等模型是强大的大脑,而 Agent 循环是我们为它打造的身体和反馈神经系统。实战的第一步,不是追求最复杂的架构,而是像本文所做的那样:**先搭建一个最小可运行的闭环,亲眼看看模型是如何在“执行-反馈”中迭代的**。在这个过程中,你会更深刻地理解到,真正的挑战往往不在于模型本身的能力,而在于如何构建安全可靠的执行环境、如何管理有限的上下文、如何定义清晰的任务边界和成功标准。
 
我建议你先用本地脚本和简单的任务(如网页抓取、数据处理)跑通整个流程,感受每个环节的痛点。然后,再逐步引入沙箱隔离、工具封装、状态管理等进阶概念。记住,一个能稳定处理十个小任务的 Agent,远比一个设计华丽但一跑就崩的复杂系统更有价值。