v1: 模型即智能体
约 200 行代码。4 个工具。每个编码智能体的本质。
Claude Code 的秘密?没有秘密。
剥去 CLI 美化、进度条、权限系统。剩下的东西出奇地简单:一个让模型调用工具直到任务完成的循环。
核心洞察
传统助手:
用户 → 模型 → 文本响应
智能体系统:
用户 → 模型 → [工具 → 结果]* → 响应
^________|
星号很重要。模型重复调用工具,直到它决定任务完成。这将聊天机器人转变为自主智能体。
关键洞察:模型是决策者。代码只提供工具并运行循环。
四个基本工具
Claude Code 有大约 20 个工具。但 4 个覆盖了 90% 的用例:
| 工具 | 目的 | 示例 |
|---|---|---|
bash | 运行命令 | npm install, git status |
read_file | 读取内容 | 查看 src/index.ts |
write_file | 创建/覆盖 | 创建 README.md |
edit_file | 精确更改 | 替换一个函数 |
有了这 4 个工具,模型可以:
- 探索代码库(
bash: find, grep, ls) - 理解代码(
read_file) - 进行更改(
write_file,edit_file) - 运行任何东西(
bash: python, npm, make)
智能体循环
整个智能体在一个函数中:
def agent_loop(messages):
while True:
# 1. 询问模型
response = client.messages.create(
model=MODEL, system=SYSTEM,
messages=messages, tools=TOOLS
)
# 2. 打印文本输出
for block in response.content:
if hasattr(block, "text"):
print(block.text)
# 3. 如果没有工具调用,完成
if response.stop_reason != "tool_use":
return messages
# 4. 执行工具,继续
results = []
for tc in response.tool_calls:
output = execute_tool(tc.name, tc.input)
results.append({"type": "tool_result", "tool_use_id": tc.id, "content": output})
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": results})
为什么这有效:
- 模型控制循环(持续调用工具直到
stop_reason != "tool_use") - 结果成为上下文(作为"用户"消息反馈)
- 记忆是自动的(消息列表累积历史)
系统提示
唯一需要的"配置":
SYSTEM = f"""你是位于 {WORKDIR} 的编码智能体。
循环:简要思考 → 使用工具 → 报告结果。
规则:
- 优先使用工具而非文字。行动,不要只是解释。
- 永远不要编造文件路径。如果不确定,先使用 ls/find。
- 进行最小的更改。不要过度工程。
- 完成后,摘要更改了什么。"""
没有复杂的逻辑。只有清晰的指令。
为什么这个设计有效
1. 简单 没有状态机。没有规划模块。没有框架。
2. 模型做思考 模型决定使用哪些工具、按什么顺序、何时停止。
3. 透明 每个工具调用都可见。每个结果都在对话中。
4. 可扩展 添加一个工具 = 一个函数 + 一个 JSON 模式。
缺少什么
| 功能 | 为什么省略 | 添加于 |
|---|---|---|
| 待办事项跟踪 | 不必要 | v2 |
| 子智能体 | 复杂度 | v3 |
| 权限 | 用于学习的信任模型 | 生产环境 |
重点是:核心很小。其他一切都是细化。
更大的图景
Claude Code、Cursor Agent、Codex CLI、Devin——都共享这个模式:
while not done:
response = model(conversation, tools)
results = execute(response.tool_calls)
conversation.append(results)
差异在于工具、显示、安全。但本质总是:给模型工具,让它工作。
学习笔记
核心思想:智能体不是魔法。智能体基本上是 LLM + 工具 + 循环。
1. 从聊天机器人到智能体
传统助手:
用户 → 模型 → 响应
智能体:
用户 → 模型 → [工具 → 结果]* → 响应
^______________|
关键区别:
- 聊天机器人只能回应。
- 智能体可以采取行动。
- 模型可以重复调用工具直到任务完成。
2. 智能体的本质
while True:
response = model(messages, tools)
if no_tool_calls:
break
result = execute_tool(...)
messages.append(result)
核心循环:
观察
↓
思考
↓
行动
↓
观察
或者:
模型
↓
工具
↓
结果
↓
模型
3. 四个基本工具
bash
运行任意命令。
ls
find .
git status
pytest
npm install
目的:
- 探索项目
- 运行测试
- 使用 Git
- 使用 Docker
- 执行脚本
在许多情况下:
智能体 ≈ LLM + Bash
readfile
读取文件内容。
read_file("src/main.py")
目的:
- 理解代码
- 检查配置
- 读取日志
writefile
创建或覆盖文件。
write_file("README.md", content)
目的:
- 生成代码
- 创建文档
- 创建配置文件
editfile
进行精确编辑。
edit_file(
path="app.py",
old_text="foo",
new_text="bar"
)
目的:
- 小修改
- 错误修复
- 重构
4. 模型就是智能体
传统软件:
if A:
tool1()
if B:
tool2()
程序员控制工作流。
智能体系统:
模型控制工作流。
应用程序只提供工具。
示例:
用户:
修复失败的测试。
可能的执行计划:
1. ls
2. pytest
3. 读取错误输出
4. 读取文件
5. 编辑文件
6. pytest
7. 重复...
模型动态创建这个工作流。
5. 为什么记忆有效
大多数简单的智能体使用:
messages = []
不断追加:
messages.append(user)
messages.append(model)
messages.append(tool_result)
产生的历史:
用户
助手
工具结果
助手
工具结果
...
整个历史被发送回模型。
那个历史变成了记忆。
6. 反应式智能体
最简单的智能体没有长期规划。
观察
↓
行动
↓
观察
↓
行动
示例:
ls
↓
read_file
↓
edit_file
↓
pytest
↓
edit_file
↓
pytest
Claude Code 的很大一部分以这种方式表现。
7. 规划
复杂的任务受益于规划。
示例:
将 Flask 应用程序迁移到 FastAPI。
生成待办事项列表:
1. 分析项目结构
2. 理解 API
3. 创建 FastAPI 骨架
4. 迁移路由
5. 更新测试
6. 验证执行
执行流程:
规划
↓
执行
↓
更新规划
↓
执行
概念上:
todo_list = [...]
8. 观察至关重要
智能体不是:
思考 → 行动
它们是:
思考
↓
行动
↓
观察
↓
再思考
示例:
pytest
输出:
FAILED: test_login
那个失败是一个观察。
下一个行动取决于它。
9. 工具结果必须返回上下文
错误的:
result = tool()
print(result)
正确的:
messages.append(tool_result)
否则模型不知道发生了什么。
智能体需要一个封闭的反馈循环:
模型
↓
工具
↓
结果
↓
模型
10. 自我修复来自哪里
模型写道:
foo()
运行测试:
pytest
错误:
NameError: foo not defined
错误进入上下文。
模型看到:
foo 不存在
它更新代码:
bar()
再次运行测试。
成功。
自我修复基本上是:
观察
↓
下一个预测
11. 为什么 Bash 如此强大
一个 Bash 工具覆盖:
Git
Docker
Python
Node
搜索
构建
部署
示例:
搜索代码:
grep -r login .
查找文件:
find . -name "*.py"
运行测试:
pytest
提交更改:
git commit
因此:
bash ≈ 许多专用工具的组合
12. 智能体框架的现实
许多框架看起来像:
规划智能体
↓
研究智能体
↓
编码智能体
↓
审查智能体
↓
测试智能体
但在底层:
while not done:
think()
use_tools()
observe()
包装不同。
核心模式保持不变。
13. 演进路径
V1
LLM
+ 工具
+ 循环
V2
LLM
+ 工具
+ 循环
+ 规划
V3
LLM
+ 工具
+ 循环
+ 子智能体
Claude Code / Codex CLI
LLM
+ 工具
+ 循环
+ 规划
+ 记忆
+ 权限
+ Git 集成
+ 并行智能体
核心永远不变:
观察
→ 思考
→ 行动
→ 观察
接下来学什么
下一个主要主题是:
上下文工程
关键问题:
为什么智能体系统中最大的瓶颈
往往是上下文窗口而不是模型?
推荐路线图:
- 上下文窗口
- 上下文工程
- RAG
- 工具使用
- 规划系统
- 记忆系统
- 多智能体架构
- Claude Code 架构
- OpenHands 架构
- Codex CLI 架构
- 长期运行的智能体
一句话摘要
智能体 = LLM + 工具 + 循环
高级智能体 = LLM + 工具 + 循环 + 上下文工程
模型就是智能体。
完整源码
#!/usr/bin/env python3
"""
v1_basic_agent.py - Mini Claude Code:模型即智能体(约 200 行)
核心理念:"模型就是智能体"
=========================================
Claude Code、Cursor Agent、Codex CLI 的秘密?没有秘密。
剥去 CLI 美化、进度条、权限系统。剩下的
出奇地简单:一个让模型调用工具直到完成的循环。
传统助手:
用户 → 模型 → 文本响应
智能体系统:
用户 → 模型 → [工具 → 结果]* → 响应
^________|
星号(*)很重要!模型重复调用工具,直到它决定
任务完成。这将聊天机器人转变为自主智能体。
关键洞察:模型是决策者。代码只提供工具并
运行循环。模型决定:
- 调用哪些工具
- 按什么顺序
- 何时停止
四个基本工具:
------------------------
Claude Code 有大约 20 个工具。但这 4 个覆盖了 90% 的用例:
| 工具 | 目的 | 示例 |
|--------------|----------------------|----------------------------|
| bash | 运行任何命令 | npm install, git status |
| read_file | 读取文件内容 | 查看 src/index.ts |
| write_file | 创建/覆盖 | 创建 README.md |
| edit_file | 精确更改 | 替换一个函数 |
只用这 4 个工具,模型就可以:
- 探索代码库(bash: find, grep, ls)
- 理解代码(read_file)
- 进行更改(write_file, edit_file)
- 运行任何东西(bash: python, npm, make)
用法:
python v1_basic_agent.py
"""
import os
import subprocess
import sys
from pathlib import Path
from dotenv import load_dotenv
# 从 .env 文件加载配置
load_dotenv()
try:
from anthropic import Anthropic
except ImportError:
sys.exit("请安装:pip install anthropic python-dotenv")
# =============================================================================
# 配置
# =============================================================================
API_KEY = os.getenv("ANTHROPIC_API_KEY")
BASE_URL = os.getenv("ANTHROPIC_BASE_URL")
MODEL = os.getenv("MODEL_NAME", "claude-sonnet-4-20250514")
WORKDIR = Path.cwd()
# 初始化客户端 - 处理直接 Anthropic 和兼容的 API
client = Anthropic(api_key=API_KEY, base_url=BASE_URL) if BASE_URL else Anthropic(api_key=API_KEY)
# =============================================================================
# 系统提示 - 模型需要的唯一"配置"
# =============================================================================
SYSTEM = f"""你是位于 {WORKDIR} 的编码智能体。
循环:简要思考 → 使用工具 → 报告结果。
规则:
- 优先使用工具而非文字。行动,不要只是解释。
- 永远不要编造文件路径。如果不确定,先使用 bash ls/find。
- 进行最小的更改。不要过度工程。
- 完成后,摘要更改了什么。"""
# =============================================================================
# 工具定义 - 4 个工具覆盖 90% 的编码任务
# =============================================================================
TOOLS = [
# 工具 1:Bash - 通往一切的网关
# 可以运行任何命令:git, npm, python, curl 等
{
"name": "bash",
"description": "运行 shell 命令。用于:ls, find, grep, git, npm, python 等",
"input_schema": {
"type": "object",
"properties": {
"command": {
"type": "string",
"description": "要执行的 shell 命令"
}
},
"required": ["command"],
},
},
# 工具 2:读取文件 - 用于理解现有代码
# 返回文件内容,对大文件可选行限制
{
"name": "read_file",
"description": "读取文件内容。返回 UTF-8 文本。",
"input_schema": {
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "文件的相对路径"
},
"limit": {
"type": "integer",
"description": "最大读取行数(默认:全部)"
},
},
"required": ["path"],
},
},
# 工具 3:写入文件 - 用于创建新文件或完全覆盖
# 自动创建父目录
{
"name": "write_file",
"description": "写入内容到文件。如果需要,创建父目录。",
"input_schema": {
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "文件的相对路径"
},
"content": {
"type": "string",
"description": "要写入的内容"
},
},
"required": ["path", "content"],
},
},
# 工具 4:编辑文件 - 用于对现有代码进行精确编辑
# 使用精确字符串匹配进行手术式编辑
{
"name": "edit_file",
"description": "替换文件中的精确文本。用于手术式编辑。",
"input_schema": {
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "文件的相对路径"
},
"old_text": {
"type": "string",
"description": "要查找的精确文本(必须完全匹配)"
},
"new_text": {
"type": "string",
"description": "替换文本"
},
},
"required": ["path", "old_text", "new_text"],
},
},
]
# =============================================================================
# 工具实现
# =============================================================================
def safe_path(p: str) -> Path:
"""
确保路径保持在工作区内(安全措施)。
防止模型访问项目目录之外的文件。
解析相对路径并检查它们不会通过 '../' 逃脱。
"""
path = (WORKDIR / p).resolve()
if not path.is_relative_to(WORKDIR):
raise ValueError(f"路径逃离工作区:{p}")
return path
def run_bash(command: str) -> str:
"""
执行带有安全检查的 shell 命令。
安全性:阻止明显危险的命令。
超时:60 秒以防止挂起。
输出:截断到 50KB 以防止上下文溢出。
"""
# 基本安全 - 阻止危险的模式
dangerous = ["rm -rf /", "sudo", "shutdown", "reboot", "> /dev/"]
if any(d in command for d in dangerous):
return "错误:危险命令已阻止"
try:
result = subprocess.run(
command,
shell=True,
cwd=WORKDIR,
capture_output=True,
text=True,
timeout=60
)
output = (result.stdout + result.stderr).strip()
return output[:50000] if output else "(无输出)"
except subprocess.TimeoutExpired:
return "错误:命令超时(60秒)"
except Exception as e:
return f"错误:{e}"
def run_read(path: str, limit: int = None) -> str:
"""
读取文件内容,可选行限制。
对于大文件,使用 limit 只读取前 N 行。
输出截断到 50KB 以防止上下文溢出。
"""
try:
text = safe_path(path).read_text()
lines = text.splitlines()
if limit and limit < len(lines):
lines = lines[:limit]
lines.append(f"...(还有 {len(text.splitlines()) - limit} 行)")
return "\n".join(lines)[:50000]
except Exception as e:
return f"错误:{e}"
def run_write(path: str, content: str) -> str:
"""
写入内容到文件,需要时创建父目录。
这用于完全创建/覆盖文件。
对于部分编辑,改用 edit_file。
"""
try:
fp = safe_path(path)
fp.parent.mkdir(parents=True, exist_ok=True)
fp.write_text(content)
return f"写入 {len(content)} 字节到 {path}"
except Exception as e:
return f"错误:{e}"
def run_edit(path: str, old_text: str, new_text: str) -> str:
"""
替换文件中的精确文本(手术式编辑)。
使用精确字符串匹配 - old_text 必须逐字出现。
只替换第一次出现,以防止意外的批量更改。
"""
try:
fp = safe_path(path)
content = fp.read_text()
if old_text not in content:
return f"错误:在 {path} 中未找到文本"
# 为安全起见只替换第一次出现
new_content = content.replace(old_text, new_text, 1)
fp.write_text(new_content)
return f"已编辑 {path}"
except Exception as e:
return f"错误:{e}"
def execute_tool(name: str, args: dict) -> str:
"""
将工具调用分派到适当的实现。
这是模型工具调用和实际执行之间的桥梁。
每个工具返回一个字符串结果,返回给模型。
"""
if name == "bash":
return run_bash(args["command"])
if name == "read_file":
return run_read(args["path"], args.get("limit"))
if name == "write_file":
return run_write(args["path"], args["content"])
if name == "edit_file":
return run_edit(args["path"], args["old_text"], args["new_text"])
return f"未知工具:{name}"
# =============================================================================
# 智能体循环 - 这是一切的核心
# =============================================================================
def agent_loop(messages: list) -> list:
"""
完整的智能体在一个函数中。
这是所有编码智能体共享的模式:
while True:
response = model(messages, tools)
if no tool calls: return
execute tools, append results, continue
模型控制循环:
- 持续调用工具直到 stop_reason != "tool_use"
- 结果成为上下文(作为"用户"消息反馈)
- 记忆是自动的(消息列表累积历史)
为什么这有效:
1. 模型决定使用哪些工具、按什么顺序、何时停止
2. 工具结果为下一个决策提供反馈
3. 对话历史跨轮次保持上下文
"""
while True:
# 步骤 1:调用模型
response = client.messages.create(
model=MODEL,
system=SYSTEM,
messages=messages,
tools=TOOLS,
max_tokens=8000,
)
# 步骤 2:收集任何工具调用并打印文本输出
tool_calls = []
for block in response.content:
if hasattr(block, "text"):
print(block.text)
if block.type == "tool_use":
tool_calls.append(block)
# 步骤 3:如果没有工具调用,任务完成
if response.stop_reason != "tool_use":
messages.append({"role": "assistant", "content": response.content})
return messages
# 步骤 4:执行每个工具并收集结果
results = []
for tc in tool_calls:
# 显示正在执行什么
print(f"\n> {tc.name}: {tc.input}")
# 执行并显示结果预览
output = execute_tool(tc.name, tc.input)
preview = output[:200] + "..." if len(output) > 200 else output
print(f" {preview}")
# 为模型收集结果
results.append({
"type": "tool_result",
"tool_use_id": tc.id,
"content": output,
})
# 步骤 5:追加到对话并继续
# 注意:我们追加助手的响应,然后是用户的工具结果
# 这保持了交替的用户/助手模式
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": results})
# =============================================================================
# 主 REPL
# =============================================================================
def main():
"""
用于交互使用的简单读取-求值-打印循环。
历史列表跨轮次保持对话上下文,
允许具有记忆的多轮对话。
"""
print(f"Mini Claude Code v1 - {WORKDIR}")
print("键入 'exit' 退出。\n")
history = []
while True:
try:
user_input = input("你:").strip()
except (EOFError, KeyboardInterrupt):
break
if not user_input or user_input.lower() in ("exit", "quit", "q"):
break
# 将用户消息添加到历史
history.append({"role": "user", "content": user_input})
try:
# 运行智能体循环
agent_loop(history)
except Exception as e:
print(f"错误:{e}")
print() # 轮次之间的空行
if __name__ == "__main__":
main()
模型即智能体。这就是全部秘密。