learn-claude-code第1-5章开源学习笔记分享

s01 Agent Loop --- 一个循环就是全部

"One loop & Bash is all you need" --- 一个工具 + 一个循环 = 一个 Agent。

  • Harness 层:The Loop(模型与现实世界的第一座桥)
  • 前序:无(起点)

一、要解决的问题

模型能"输出"一条 bash 命令,但输出完就停了:它不会自己执行,也不会根据执行结果继续推理。于是人被迫当中间层------手动跑、手动回贴结果。本章就是把这段"人工中转"自动化。

二、核心机制

一个 while True:模型要工具就继续,不要就停。判据是响应里有没有 tool_use 内容块。

信号 含义 循环动作
含 tool_use 块 模型请求调工具 执行 → 回灌结果 → 继续
不含 tool_use 块 模型没调工具 退出循环

三、关键代码(约 30 行)

python 复制代码
def agent_loop(messages):
    while True:
        response = client.messages.create(
            model=MODEL, system=SYSTEM, messages=messages,
            tools=TOOLS, max_tokens=8000)
        messages.append({"role": "assistant", "content": response.content})

        tool_calls = [b for b in response.content if b.type == "tool_use"]
        if not tool_calls:          # 没有任何工具调用 → 本轮结束
            return

        results = []
        for block in tool_calls:
            output = run_bash(block.input["command"])
            results.append({"type": "tool_result",
                            "tool_use_id": block.id, "content": output})
        messages.append({"role": "user", "content": results})   # 结果回灌
  • 工具只有一个 bash:subprocess.run(shell=True, timeout=120),输出截断 50000 字符。
  • 危险命令直接拦:rm -rf /、sudo、shutdown、reboot、> /dev/。

四、关键洞察

  • 判据落在"内容块"上 :只看 tool_use 是否存在,因此循环永远不会追加一条空的 tool-result 消息。
  • 模型决定(调不调工具、调哪个),Harness 执行(调工具、把结果追加为新消息)。
  • 这 30 行不是智能本身,而是"让模型能持续行动"的最小运行框架。后面 16 章都在这个循环上加机制,循环本身永不变。

五、一句话总结

模型是司机,Harness 是车;s01 造出了车的底盘------一个让模型能"踩油门---看结果---再决策"的循环。


s02 Tool Use --- 加一个工具,只加一个 handler

"Add a tool, add just one handler" --- 循环不变,把新工具注册进调度表即可。

  • Harness 层:Tool Dispatch(扩展模型能触达的范围)
  • 前序:s01(循环不变)

一、要解决的问题

只有 bash 时,读文件要 cat、写文件要 echo >、改文件要 sed。模型想的是"读这个文件",却要自己翻译成命令------多一层翻译,既费 token 又易错。

二、核心机制

s01 循环一行不改 。唯一变化是工具执行那一行:从硬编码 run_bash() 换成调度查表 TOOL_HANDLERS[block.name](**block.input)。

加一个工具只需两步:

  1. 定义工具 :TOOLS 数组加一条(JSON schema,告诉模型"我能做什么")。
  2. 注册 handler :TOOL_HANDLERS 字典加一条映射。

三、关键代码

python 复制代码
TOOLS = [
    {"name": "bash",       "description": "Run a shell command.", ...},
    {"name": "read_file",  "description": "Read file contents.",  ...},
    {"name": "write_file", "description": "Write content to file.", ...},
    {"name": "edit_file",  "description": "Replace text in file once.", ...},
    {"name": "glob",       "description": "Find files by pattern.", ...},
]

TOOL_HANDLERS = {
    "bash": run_bash, "read_file": run_read, "write_file": run_write,
    "edit_file": run_edit, "glob": run_glob,
}

for block in tool_calls:
    handler = TOOL_HANDLERS[block.name]   # 查表
    output = handler(**block.input)        # 调用
  • 文件工具引入 safe_path(限制在工作目录内),bash 仍不受限。
  • 模型一次可能返回多个 tool_use------按 response.content 中的原始顺序逐个执行。

四、与 s01 的变化

组件 s01 s02
工具数 1(bash) 5(+read/write/edit/glob)
工具执行 硬编码 run_bash() TOOL_HANDLERS 查表
路径安全 无 safe_path(仅文件工具)
循环 while True + tool_use 与 s01 完全一致

五、关键洞察

  • "加工具 = 加一条 schema + 一条映射" 是整套 Harness 的扩展范式,后续所有章节都沿用。
  • 抽象点只有一处(调度表),没有为"未来可能的工具"做过度设计。
  • bash 未被约束:rm -rf / 仍能跑------这正好引出 s03。

六、一句话总结

用一张 名字 → 函数 的调度表,把"单工具 Agent"升级为"多工具 Agent",而主循环纹丝不动。


s03 Permission --- 执行前先过权限闸门

"Check permissions before executing" --- 权限管线决定哪些操作需要审批。

  • Harness 层:Permission(工具执行前的一道闸门)
  • 前序:s02(循环不变)

一、要解决的问题

s02 有 5 个工具:文件工具有 safe_path 保护,bash 完全不受限 。让它"清理下项目",它可能真的跑 rm -rf /。安全不能靠"信任模型",必须靠代码------每次工具执行前做一次检查。

二、核心机制

s02 循环完全保留,只在工具执行前插入 check_permission()。每次工具调用按固定顺序过三道闸:

闸门 作用 命中后
1. 拒绝清单 永久禁止的操作(rm -rf /、sudo) 直接拒绝,不执行
2. 规则匹配 依赖上下文(越界读写、rm 文件) 交给闸门 3
3. 用户审批 闸门 2 命中后暂停等确认 用户决定允许/拒绝

三道都没命中 → 直接执行(绝大多数常规操作走这条路)。

三、关键代码

python 复制代码
DENY_LIST = ["rm -rf /", "sudo", "shutdown", "reboot", "mkfs", "dd if=", "> /dev/sda"]

PERMISSION_RULES = [
    {"tools": ["read_file", "write_file", "edit_file"],
     "check": lambda args: not (WORKDIR / args.get("path","")).resolve()
                              .is_relative_to(WORKDIR),
     "message": "Access outside workspace"},
    {"tools": ["bash"],
     "check": lambda args: contains_destructive_command(args.get("command","")),
     "message": "Potentially destructive command"},
]

def check_permission(block) -> bool:
    if block.name == "bash" and check_deny_list(block.input.get("command","")):
        return False                          # 闸门 1
    reason = check_rules(block.name, block.input)
    if reason and ask_user(...) == "deny":    # 闸门 2 → 3
        return False
    return True
  • 破坏性命令用词边界正则 识别((?:^|[;&|()\n])\s*(?:rm|del)(?=\s|$|[;&|()])),所以 model、delimiter、echo del test.txt 不会误伤。

四、与 s02 的变化

组件 s02 s03
安全模型 无(信任模型) 三闸权限管线
新函数 --- check_deny_list / check_rules / ask_user / check_permission
循环 直接执行所有工具 执行前插入 check_permission()

五、关键洞察

  • 安全必须是代码,不能是信任:提示词约束挡不住模型。
  • 章节自陈边界:拒绝清单用简单字符串匹配 ,只用于演示权限闸门的位置,不是完整的安全边界。
  • 设计取"三闸固定顺序":硬拒绝优先,其次软询问,最后放行------越危险的判断越靠前。

六、一句话总结

在"模型说要执行"和"真的执行"之间插一道闸门;但演示级实现不等于生产级安全边界。


s04 Hooks --- 挂在循环上,别写进循环里

"Hang on the loop, don't write into it" --- 钩子在工具执行前后注入扩展逻辑。

  • Harness 层:Hooks(不侵入循环的扩展点)
  • 前序:s03(循环不变)

一、要解决的问题

s03 有了权限检查。但每加一个需求------"记录每次 bash 调用""写完后自动 git add"------都得改 agent_loop。循环会迅速膨胀成:

python 复制代码
log_to_file(block); check_permission(block); notify_slack(block)
output = execute(block); auto_git_add(block)   # 循环已面目全非

**想扩展的是 Agent 行为,改的却是循环本身。**循环应是稳定内核,扩展应挂在外面。

二、核心机制

s03 循环与权限逻辑完全保留,唯一变化:把 check_permission() 从循环体挪到一个钩子上。循环不再直接调任何检查函数,只调 trigger_hooks("PreToolUse", block),由注册表决定跑什么。

四个事件覆盖一个完整 Agent 周期:

事件 触发时机 典型用途
UserPromptSubmit 用户输入后、进 LLM 前 输入校验、上下文注入
PreToolUse 工具执行前 权限检查、日志
PostToolUse 工具执行后 副作用(自动 git add)、输出检查
Stop 循环即将退出 清理、决定是否继续

三、关键代码

python 复制代码
HOOKS = {"UserPromptSubmit": [], "PreToolUse": [], "PostToolUse": [], "Stop": []}

def register_hook(event, callback): HOOKS[event].append(callback)

def trigger_hooks(event, *args):
    for callback in HOOKS[event]:
        result = callback(*args)
        if result is not None:    # 返回值 ≠ None → 该钩子说"停"
            return result
    return None

控制流语义:

  • PreToolUse 返回非 None → 本次工具调用被拦。
  • Stop 返回非 None → 循环继续(返回值作为新消息注入)。
  • UserPromptSubmit / PostToolUse 的返回值不影响控制流。

循环里只改一行:

python 复制代码
blocked = trigger_hooks("PreToolUse", block)     # s03: if not check_permission(block)
if blocked:
    results.append({... "content": str(blocked)}); continue
handler = TOOL_HANDLERS.get(block.name)
output = handler(**block.input) if handler else f"Unknown: {block.name}"
trigger_hooks("PostToolUse", block, output)

四、与 s03 的变化

组件 s03 s04
扩展方式 check_permission() 硬编码在循环 HOOKS 注册表 + trigger_hooks()
循环 直接调 check_permission 调 trigger_hooks("PreToolUse", ...)
退出控制 无 Stop 钩子可阻止退出
输入拦截 无 UserPromptSubmit 可注入上下文

五、关键洞察

  • 观察者模式:循环只发事件,回调自己决定行为,实现"扩展而不侵入"。
  • 权限检查从"循环的一行"变成"钩子的一个回调"------职责归位:循环管调度,策略挂钩子。
  • 四个钩子点正好是 Agent 周期的四个关键节点:输入 → 执行前 → 执行后 → 退出。

六、一句话总结

给循环留 4 个扩展点,让所有"想加的行为"以回调形式挂在外面,循环始终干净。


s05 TodoWrite --- 没有计划的 Agent 会跑偏

"An agent without a plan goes wherever the wind blows" --- 先列步骤再执行,复杂任务不易漏步。

  • Harness 层:Planning(让 Agent 想清楚再动手)
  • 前序:s04(复用工具调度 + 权限 + 钩子)

一、要解决的问题

给一个复杂任务:"把所有 Python 文件改成 snake_case,跑测试,修失败。"Agent 改 3 个文件 → 跑测试 → 发现 2 个失败 → 开始修。修着修着就忘了原始目标是"改名",测试失败占满了全部注意力。

对话越长越糟:工具结果不断填充上下文,稀释系统提示。10 步重构,走到第 3 步就开始即兴发挥,因为第 4--10 步已被挤出注意力。

二、核心机制

保留 s04 的工具调度、权限、钩子,新增 todo_write 工具 + 一个提醒计数器。

  • todo_write 只更新计划状态,真正干活仍是已有工具。
  • 新工具走同一个 TOOL_HANDLERS[block.name] 调度路径。
  • 连续 3 轮 tool-use 没用 todo_write,Harness 在该轮结果里追加一条提醒。

三、关键代码

python 复制代码
class TodoManager:
    def __init__(self): self.items = []
    def update(self, todos):                          # 校验后整体替换
        validated = [...]                             # ≤20 项;content 非空;仅 1 项 in_progress
        self.items = validated
        return self.render()                          # [ ] pending / [>] in_progress / [x] completed

TODO = TodoManager()
def run_todo_write(todos): 
    out = TODO.update(todos); print(out); return out

TOOLS.append({"name": "todo_write", "description": "Create and manage a task list ..."})
TOOL_HANDLERS["todo_write"] = run_todo_write

提醒逻辑:

python 复制代码
rounds_since_todo = 0 if used_todo else rounds_since_todo + 1
if rounds_since_todo >= 3:
    results.append({"type": "text", "text": "<reminder>Update your todos.</reminder>"})
    rounds_since_todo = 0

典型流程:列全部步骤(pending)→ 挑一步设 in_progress → 做完设 completed → 看下一个 pending → 继续。

四、与 s04 的变化

组件 s04 s05
工具数 5 6(+todo_write)
计划 无 有状态 TODO 列表 + 提醒
循环 调度 + 钩子 同一调度路径,另加计数器与提醒注入

五、关键洞察

  • todo_write 没有给 Agent 任何新的"执行能力",它加的是"计划能力"。 这是本章最核心的一句。
  • 提醒是软约束(往结果里塞文本),不是硬拦截------把"该做计划了"作为一种上下文信号交给模型。
  • 计划驻留内存,随会话结束消失------这正好引出 s10 的持久化任务图。

六、一句话总结

用一个 todo_write 工具把"先计划"变成模型可调用的动作,再靠轻量提醒维持它,从而对抗长任务中的注意力漂移。

相关推荐
小奇不哭43 分钟前
ROS2 单节点,RGB-D 深度相机局部路径提取 + B 样条平滑路径 + YOLOv8 障碍物检测 + 深度测距,用于移动机器人视觉局部循迹。
python·yolov8·ros2·cv2·路径探索循迹
每天都要写算法(努力版)1 小时前
【行业前沿报告】DAgger:让智能体在自己走到的状态上学习
人工智能·学习·机器学习
liron711 小时前
智能实体演化系统的统一性概念
人工智能·深度学习·神经网络
小小龙学IT1 小时前
Python scikit-learn 机器学习库深度解析
python·机器学习·scikit-learn
呆萌很1 小时前
常用骨干网络预训练输入尺寸
人工智能
Yolanda_20221 小时前
19.神经网络-最大池化的使用
人工智能·深度学习·神经网络
W***25921 小时前
2026 企业 AI 办公平台选型指南:可完成全链路任务的 AI 工具评估
人工智能
2601_962071572 小时前
类变量和全局变量的查找路径有什么区别?
开发语言·python
RockHopper20252 小时前
面向工业现实的原生数字化工程框架概要说明
人工智能·智能体·世界模型·工业数字化