01要搭的是什么GOAL
一个最小 Agent = 一个循环:观察当前状态 → 决策下一步 → 执行(调工具)→ 更新状态,不断重复,直到任务完成、或撞上停止条件。它不需要大模型也能跑——先用一个"确定性决策器"(写死规则)把骨架跑通,理解了再换成真模型。这正是 P-006 的做法:默认零依赖、无需 key。
先跑通这个,你就亲手理解了 Agent 的本质;后面换模型、加工具都是在这个骨架上长出来的。
02目录骨架STRUCTURE
先想清楚每个文件负责什么,再动手。最小拆法:
my-agent/ ├── run.py # 入口:读任务、建隔离工作区、跑循环、写结果 ├── agent.py # 核心:观察→决策→执行→更新的循环 + 停止条件 ├── decider.py # 决策器:给状态、返回下一步动作(先写死规则版) ├── tools.py # 工具:Agent 能调的动作(读/写文件),每个都带边界校验 ├── trace.py # 留痕:把每一步逐条写进 JSONL └── verify.py # 验证:独立核对结果对不对(不信 Agent 自己报"我做完了")
为什么这么分?因为每个文件对应 Agent 的一个"关节":循环、决策、能力、留痕、验证。分开写,后面想换决策器或加工具,只动一个文件。
03写循环(agent.py)THE LOOP
循环是心脏。关键不在"跑起来",在三个显式的停止条件——不然它会空转或失控烧钱:
def run(task, decider, tools, max_steps=12):
state = {"task": task, "history": [], "done": False}
fails = 0
for step in range(max_steps): # 停止条件①:步数上限
obs = observe(state) # 观察:整理现状给决策器
action = decider.decide(obs) # 决策:返回 {"type": ..., ...}
if action["type"] == "finish": # 停止条件②:任务完成
state["done"] = True
break
try:
result = execute(action, tools)
fails = 0
except ToolError as e: # 只接可预期的工具错误
result = {"error": str(e)}
fails += 1
if fails >= 3: # 停止条件③:连续失败熔断
break
state["history"].append({"step": step, "action": action, "result": result})
return state
注意两点:一是"决策"和"执行"分开(决策器只决定做什么、不碰真实世界,方便测试和替换);二是只捕获可预期的 ToolError,别用一个 except 吞掉所有异常——那会把真正的 bug 藏起来。
04加工具 = 加边界(tools.py)TOOLS = LIMITS
工具就是 Agent 的"手"。给它一个工具,就是给它一份权力——所以每个工具都要自带边界,别让它能碰不该碰的:
from pathlib import Path
WORKSPACE = Path("runs/current").resolve() # 只允许在这个目录内活动
def _resolve(name):
p = (WORKSPACE / name).resolve()
if not p.is_relative_to(WORKSPACE): # 防"../../etc/passwd"越界
raise ToolError(f"越界访问被拒: {name}")
return p
def read_file(name):
return _resolve(name).read_text(encoding="utf-8")
def write_file(name, content):
_resolve(name).write_text(content, encoding="utf-8")
return "ok"
TOOLS = {"read_file": read_file, "write_file": write_file} # 只注册这两个
这就是 F-012 讲的"工具即权力边界"落到代码:白名单注册(只有 TOOLS 里的能被调)、路径校验(is_relative_to 挡越界)。想让它更安全,就把边界收得更紧,而不是加更多工具。
05留 trace + 独立验证TRACE & VERIFY
这两步是"判断/证据"意识落到动手,也是这个站反复强调、但别处教程常省掉的:
- trace:把每步的动作和结果逐条追加进一个 JSONL 文件。一份带失败记录的 trace,就是你项目"经得起追问"的证据(见 N-007)。
- 独立验证:任务完成后,不要信 Agent 自己说的"我做完了"。用一段独立代码去实际核对结果(比如它说写了 5 行,你就真去数那个文件有没有 5 行)。verify 和 agent 分开,才防得住"自报家门"。
# verify.py — 独立核对,不信自报
def verify(task, workspace):
# 例:任务要求生成的文件至少 5 行
out = (workspace / task["output"]).read_text()
actual = len(out.splitlines())
return actual >= task["done_when"]["min_lines"] # 逐项核对,返回真假
06跑通 + 三级改动练习RUN & PRACTICE
跑通的标志:python3 run.py 后,你能看到 trace 里一步步的动作、最后 verify 返回"通过",且结果文件真的在。到这里,你就有了一个能追问的最小项目。
然后别停在"跑通"——按 F-008 的三级台阶,在这个骨架上练"改",每次改完记一行台账(动了什么、崩在哪、为什么):
- 改输入:换一个不同的任务,看它在循环里怎么走、哪一步变了。
- 改约束:把
max_steps调到 3,或给工具加一条新限制,看它怎么被逼着变聪明或失败。 - 改失败:故意让某个工具抛错,验证熔断真的触发、trace 里能看到失败——把报错从噪音读成信息。
能改三处、每处说得清为什么,这个项目才真正属于你——这正是 F-010 说的"经得起追问"。
- 你的循环有三个显式停止条件(完成/步数/熔断)吗?
- 你的工具有边界校验、只注册了该有的吗?
- 你有一份能打开的 trace,和一段不信自报的独立验证吗?
完整可跑的参考实现:P-006 Minimal Agent Loop(克隆即跑)。想要逐行讲解、三级改动的完整练习和陪跑答疑 → 教学包。