📑 本页目录(点开跳转)
01 · 第一天:10 分钟造出你的第一个 Agent
⏱ 10 分钟 | ⭐ 核心 | 🔨 必须动手 | 不需要 API Key
🎯 一句话
Agent = 大模型 + 工具 + 一个 while 循环。 就这三样。这一节你徒手把它写出来,看清里面没有任何魔法。
🧠 先看清那个循环
所有 Agent 产品——Claude Code、Cursor、Manus——剥掉皮之后都是这个循环:
- A:「我要调用工具 X,参数是 Y」
- B:「任务完成,答案是 Z」
- 执行工具
- 把结果塞回对话历史
- ↩︎ 回到第 ① 步,循环继续
- 退出循环,把答案返回给用户
💡 人话:模型自己决定「接下来干什么」,程序只负责跑腿(执行工具)和记账(维护对话历史)。决策权在模型,这就是 Agent 和普通程序的区别。
🔨 动手:完整的 Agent(能跑,不用 API Key)
下面的代码里有一个「假模型」——它按剧本演出一个真模型的行为,让你零成本看清循环的每一步。复制运行:
# -*- coding: utf-8 -*-
"""一个最小但完整的 Agent。MockModel 让你不用 API Key 就能跑通全部机制。"""
import json, os
# ============ 第 1 步:定义工具 ============
# 工具 = 一个函数 + 一段给模型看的说明书
def calculator(expression: str) -> str:
"""计算数学表达式"""
try:
return str(eval(expression, {"__builtins__": {}}, {}))
except Exception as e:
return f"计算出错:{e}。请检查表达式,只支持 + - * / ** ()"
def list_files(path: str = ".") -> str:
"""列出目录下的文件"""
try:
items = os.listdir(path)[:20]
return "\n".join(items) if items else "(空目录)"
except Exception as e:
return f"读取失败:{e}"
TOOLS = {"calculator": calculator, "list_files": list_files}
TOOL_DESCRIPTIONS = """你可以使用以下工具:
- calculator(expression): 计算数学表达式,如 "23*7+1"
- list_files(path): 列出目录下的文件
需要用工具时,只输出一行 JSON:{"tool": "工具名", "args": {参数}}
不需要工具时,直接输出答案。"""
# ============ 第 2 步:模型(先用假的) ============
class MockModel:
"""按剧本扮演一个真模型:先查文件、再算数、最后总结。
换成真模型时,只需要替换这个类(见文末)。"""
def __init__(self):
self.script = [
'{"tool": "list_files", "args": {"path": "."}}',
'{"tool": "calculator", "args": {"expression": "365 * 24"}}',
"根据工具结果:当前目录的文件已列出,一年有 8760 小时。任务完成。",
]
self.step = 0
def chat(self, messages):
reply = self.script[min(self.step, len(self.script) - 1)]
self.step += 1
return reply
# ============ 第 3 步:那个循环 ============
def run_agent(model, user_task, max_steps=10):
messages = [
{"role": "system", "content": TOOL_DESCRIPTIONS},
{"role": "user", "content": user_task},
]
for step in range(max_steps):
reply = model.chat(messages)
messages.append({"role": "assistant", "content": reply})
print(f"\n【第 {step+1} 轮】模型说:{reply}")
# 判断:是工具调用,还是最终答案?
try:
call = json.loads(reply)
assert "tool" in call
except (json.JSONDecodeError, AssertionError):
print("\n✅ 模型给出最终答案,循环结束。")
return reply
# 执行工具,把结果塞回对话
tool_fn = TOOLS.get(call["tool"])
result = tool_fn(**call["args"]) if tool_fn else f"错误:没有 {call['tool']} 这个工具"
print(f" 工具返回:{result[:80]}")
messages.append({"role": "user", "content": f"工具返回结果:\n{result}"})
return "达到最大步数,强制停止。" # ⭐ 护栏:永远要有最大步数
# ============ 跑起来 ============
run_agent(MockModel(), "看看当前目录有什么文件,然后告诉我一年有多少小时")
你会看到:模型(假的)第 1 轮要求列文件 → 程序执行并回填 → 第 2 轮要求算数 → 回填 → 第 3 轮给出答案退出。
恭喜,这就是一个 Agent 的全部骨架。 大约 60 行。
🔍 五个值得盯着看的细节
messages是唯一的状态。模型没有记忆,每轮都把完整历史发过去。这就是为什么「上下文」是一切的核心约束(第 4 节)- 工具结果以
user角色回填。模型看到的世界 = 对话历史里的文字。工具返回什么文字,模型就「感知」到什么 max_steps护栏。没有它,一个犯轴的模型能无限循环烧钱。真实系统还会加超时、预算上限- 工具的错误信息写给模型看。
calculator出错时返回的是「请检查表达式,只支持…」——这是在教模型怎么改,不是给人看的报错(第 7 节展开) TOOL_DESCRIPTIONS是提示词。工具说明书写得好不好,直接决定模型用不用得对(第 7 节的核心命题)
🔌 换成真模型(有 API Key 再做,没有就跳过)
只需替换 MockModel:
# pip install anthropic
import anthropic
class ClaudeModel:
def __init__(self):
self.client = anthropic.Anthropic() # 读环境变量 ANTHROPIC_API_KEY
def chat(self, messages):
system = next(m["content"] for m in messages if m["role"] == "system")
rest = [m for m in messages if m["role"] != "system"]
resp = self.client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
system=system,
messages=rest,
)
return resp.content[0].text
run_agent(ClaudeModel(), "当前目录里有几个 py 文件?")
📌 真实开发中你会用官方的 tool use API(模型原生输出结构化工具调用,不用自己解析 JSON)或 Claude Agent SDK(循环都帮你写好了)。但你现在徒手写过一遍,用那些工具时就知道皮下是什么。
🔗 这一章连到哪里
| 去哪 | 为什么 |
|---|---|
| ML基础 01 | 同样的「10 分钟先跑通再问为什么」,那边是监督学习版 |
| 全景导论 09 | ⭐ 你刚写的那个循环有名字 —— ReAct 就是它的理论原型(那一章第 ③ 节,而且会回指本章)。那一章还把 CoT / Self-Consistency / Reflexion / ToT / 长思考排在一起,告诉你什么时候值得让模型"多想" |
| 13 · 多 Agent 协作 | ⭐ 同一个循环的两个变体不在导论里,在本教程:Plan-and-Execute(开头一次性出完整计划)和 ReWOO(工具返回根本不回模型)。⚠️ 判据是「计划是谁做的、什么时候做的、还改不改」——附录 A 有一句话版 |
| 全景导论 02 | 循环里被反复调用的那个「模型」,内部到底在做什么 |
✅ 检查点
- Agent 的三要素是什么?
- 模型是怎么「记住」上一轮工具结果的?
- 为什么必须有
max_steps? - Agent 和普通程序的本质区别是什么?
👀 答案
- 大模型 + 工具 + 循环。
- 它根本没记住——程序把工具结果追加进
messages,每轮把完整历史重新发给它。 - 防止无限循环烧钱/卡死。这是最基本的护栏。
- 决策权在模型:下一步做什么由模型的输出决定,而不是程序里预写的分支。
🛑 可以停在这里
⚡ 走神救援
Agent = 模型 + 工具 + while 循环,就这三样,里面没有任何魔法。循环是这么转的:①把对话历史发给模型 → ②模型只回两种东西之一,A「我要调用工具 X,参数是 Y」或 B「任务完成,答案是 Z」 → ③-A 是工具调用就执行、把结果塞回历史、回到 ①;③-B 是最终答案就退出。⭐整个循环只有这一个出口——模型说"完成了",程序才停。这套骨架总共约 60 行,配一个按剧本演出的假模型(先列文件、再算 365×24、报出一年 8760 小时)就能零成本跑通,不用 API Key。五个值得盯的细节:⭐messages 是唯一的状态——模型根本没有记忆,是程序把工具结果追加进去、每轮再把完整历史重发一遍,这就是为什么上下文是一切的核心约束(第 4 节);工具结果以 user 角色回填,模型看到的世界就等于对话历史里的文字,工具返回什么它才感知到什么;⚠️max_steps 是最低限度的护栏(示例里是 10),没有它一个犯轴的模型能无限循环烧钱,真实系统还要加超时和预算上限;工具的错误信息是写给模型看的——出错时返回「请检查表达式,只支持…」是在教它怎么改,不是给人看的报错(第 7 节);TOOL_DESCRIPTIONS 本质就是提示词,说明书写得好不好直接决定模型用不用得对。⭐决策权在模型,这就是 Agent 和普通程序的本质区别:下一步做什么由模型的输出决定,不是代码里预写的分支。
下一节 👉 02-模型与effort-选对大脑.md