Skip to content

第 21 章 解剖 Claude Code,动手造一个编码智能体 ​

本章收尾不引任何新机制,只挑一个真实在跑的工业级编码智能体来拆,看前二十章讲过的每样东西在真实产品里各自对应什么、做到多大,拆完之后,再把书里的零件装配起来,得到一个能跑的迷你编码智能体;拆的对象是 Claude Code,Anthropic 出品的编码智能体,挑它有两个原因,本书讲过的机制它几乎占全了,回路、工具、记忆、权限和压缩一样不缺,对照起来每样都能找到位置,另一个原因是官方文档把架构口径讲得很直白,这一章里所有机制描述都以它为准(截至 2026-09),版本容易变的地方正文里都做了标注,以后回查原文能对得上。全章七节,21.1 看回路,比工业级产品的核心与第 2 章手写的循环是不是同一种东西;21.2 与 21.3 看工具层和环境记忆,这两样书里都造过一遍,做成产品后长什么样;21.4 讲权限与钩子,概率系统里确定的那一半;21.5 讲上下文压缩,读一张“什么该幸存”的设计表,顺带看技能的按需加载与长任务的外循环;21.6 动手装配,21.7 实测,再把教学版和真实产品的差距盘一遍。七节各自独立成篇,读完一节可以放下书歇口气,章末有本章小结,之后是全书尾声。

21.1 同一个回路,三阶段与五步 ​

工业级编码智能体的核心回路,究竟要写多复杂?第 2 章出现过几十行 while 循环,模型决策、代码执行、结果回填,然后进下一轮,刚跑通时只够查个快递、改个地址,离在真实代码仓库里工作的智能体还差着一截不小的距离。

官方文档的口径简单得出乎意料,一共三步,收集上下文(gather context)、采取行动(take action)和验证结果(verify results)。先摸清现状再动手,改完还要确认改对。这套循环本质就是 2.3 节写过的那个,只是每一步底下垫的设施厚了许多。

三步里最容易被跳过的是最后一步。写过 demo 的人都熟悉那股诱惑,模型改完代码就报一句“已修复”,没人真去跑一遍测试。这股诱惑我也没能免俗,模型一说“已修复”,下一个任务就已经排到眼前了。三阶段口径把“验证结果”写进回路的定义,等于把话挑明,不验证的循环算不上完整的循环。2.4 节讲过 demo 与生产的差距,会掉进那道沟的智能体,多数不是第二步做不了动作,而是第三步从头到尾没人做。21.6 节装配的迷你版会带上一个 run_tests 工具,把第三步变成工具,验证就成了模型自己可发起的动作。

官方 Agent SDK 文档把这三步落到代码上,展开的说法是五步。第一步模型跑起来先收提示,系统提示、工具定义和历史消息一起进场;第二步随后评估这一轮的响应,模型要么只回文字,要么发一次工具调用,也可能两个都做;第三步接着执行工具,这一步里钩子能拦截、改写和阻止调用,细说在 21.4;第四步再进入循环,把工具结果回填进历史、开下一轮,官方把一次完整循环称作一个 turn;第五步最后出结果,模型不再要工具就给出最终答复,SDK 层还会附一个结果对象,装着 token 用量、成本与会话标识。对照三步看五步,收集上下文不占单独一步,第一步收历史是它,第四步回填工具结果也是它,读工具结果本来就是在收集上下文;采取行动摊在第二到第四步,回填结果一边续上下文,一边把行动循环下去;验证结果没有专属的步位,它做成了工具,藏在第三步里。

这五步要是拿去量第 2 章的 run_agent,能一步不差地对上号。收提示认的是开头那两条消息(system 一条、user 一条),再加上 tools 参数里的工具定义;评估响应认的是读 response.choices[0].message,检查里面有没有 tool_calls;执行工具认的则是 tool_map 那次查表调用。

循环那一段,做的就是包消息,把结果裹成 role 为 tool 的消息塞回列表,随后进入下一轮;出结果则由 if not message.tool_calls: return message.content 收尾。第 2 章那几十行,骨架跟工业产品其实是同一条。

五步落到数据结构上很直观。回路从头到尾只操作一张不断变长的消息列表,开场两条,一条 system 一条 user;模型每做一轮决定列表就涨一条,每个工具结果回填再涨一条。按这个规律数,从开场两条数起,单工具、不带旁白的四个回合走完,列表长度依次走到 4、6、8、10。每轮正好多两条,模型的决定一条、工具的结果一条;模型一轮发多个调用时,一次就多出好几条。所谓回路,落到内存里就是这张表外加一个 while;所谓上下文管理,管的正是这张表长到多大、长了怎么办。21.5 整节都在答这后半问。

官方文档里还有一句定位性质的话,直译大致是,Claude Code 是套在模型外面的一层 harness(外壳),负责提供工具、上下文管理和执行环境,把一个语言模型变成能实际干活的编码智能体。这句话把职责切得很干净,智能不在外壳里,而在模型里。外壳只干三件事,备好工具、管住上下文和给出执行环境。文档另有一个更简洁的说法,整个回路由两件东西驱动,一件是会推理的模型,一件是会行动的工具。前二十章写下的机制,套用的正是这层外壳。

顺着这句定位会冒出一个自然的问题,模型一代强过一代,这层外壳会不会越缩越小。回答倒是很干脆,外壳不会缩水,只会搬家。模型升级确实会让一批脚手架退役,有位工程师在新模型上线那一周,删掉了自己搭的两个上下文监控智能体,新模型在窗口快满时不再提前潦草收尾,这两个助手非但没了用处,反而添乱。

说搬家,指的是另外一半。升级同时把原本够不着的任务拉到了够得着的地方,要跑几天的长任务、好几个智能体接力的大型任务,这些新任务照样需要新的脚手架。分工也就清楚了,外壳缺什么自己补,模型缺什么写在厂商的路线图上。前二十章手写的零件,补的恰是外壳那部分缺口。只等模型,等不来一个能实际工作的系统。

评估响应这一步还藏着一个容易漏掉的分支。官方口径里,模型这一轮的输出有纯文本、工具调用、两者都有三种形态。第三种并不稀奇,边解说边动手本来就是智能体工作的自然方式,第 2 章的消息结构早就装得下它,一条 assistant 消息可以同时带文字和工具调用。第 2 章的 while 循环拿 if not message.tool_calls 一个判断就把三种形态都覆盖到,写法虽然简单,往后读评测轨迹时,全靠它分辨模型是在推进任务还是光说不做。

那差距到底在哪儿?SDK 这一层看不出分别,回路只有两个旋钮,max_turns 只数工具轮,纯文字对话的一轮不算数,max_budget_usd 按花费设顶,到了线就停。两个旋钮背后的思想,第 10 章 10.4 节讲过,属于预算型终止,只是那一章用消息数、token 用量和超时来算,这里多出按美元算的一档。真正的差距在回路以外的工程,细说有三点。模型决策与工具执行之间没有权限闸,规则散落在系统提示里而不是写进环境,上下文满了也没有应对预案。这三点恰好是 21.3 到 21.5 三节的主题。

21.2 工具层,五类内置与一层扩展 ​

Claude Code 的工具层,按官方文档的分类,内置工具归成五类。文件操作负责读写代码仓库里的文件,搜索在文件内容与目录结构里找东西,执行跑命令,Web 取网页,还有一类代码智能提供代码层面的理解能力,这类要装插件才有。五类之外另有一组编排类工具,像是派一个子智能体去做专项任务、向用户请求一句澄清,它们不对文件系统下手,管的是回路自己的组织。

这五类工具背后,用的还是第 4 章那套工具设计法,描述怎么写、参数怎么切、幂等怎么保,都有现成的方法可循,那一章最后的结论,是把工具当产品来做。

核心为什么在文件、搜索和执行这三件上,拿三阶段口径一对就明白。读文件加搜索负责收集上下文,改文件加跑命令负责采取行动,验证结果还得靠跑命令。编码任务的动作本来就能收进这几件。迷你版最终也只靠四件工具就能运行,跟工业产品的工具箱比,件数差在两倍以上,量级仍属相当。

那两件编排工具,书里都出现过同样的机制。派生子智能体是把一段专项任务连同干净上下文一起交出去,隔离的做法来自第 8 章 8.4 节,派任务的模式是第 10 章监督者那一套。向用户提问,指卡在缺信息时主动要澄清而不是硬猜,对应第 15 章 15.1 节四种形态里的澄清请求。编排工具在官方口径里也算工具,模型调用它们与调用读文件走同一条回路。组织智能体和操作文件共享同一套基础设施,着眼的不只是简便,而是让回路始终只有一条。

五类工具里,前三类书里已经讲过同样的机制。文件操作与搜索这类,工具怎么定义、描述怎么写、参数怎么切,直接套第 4 章的方法就行;执行类的参数校验、超时控制和错误回填,正是 4.4 节执行器约定所讲的内容。Web 类和代码智能类本书没有展开,前者至少要知道它存在,取外部网页是编码智能体常有的需求,后者等装插件时自然会遇上。

图 21-1 改文件三路线,行号一改就漂,现行产品收敛到按内容定位

改文件这一件还藏着一个选型问题,模型怎么表达改哪里、怎么改,工具层有几条路线。最简便的是整文件重写,模型把改好的文件从头到尾吐一遍,21.6 迷你版的 write_file 走的就是这条路。教学版影响不大,文件只有几十行;真实仓库里这样做的成本就过高了,改动只有几行,输出却按整份文件计,没动过的行也靠模型重新生成一遍,哪一行生成了不同的内容,diff 里还分不清哪处是有意改的、哪处是抄走了样。另一条路是行号定位,让模型报第几行改成什么。听着精确,实际经不起连续编辑,一轮里改完一处,后面的行号整体挪位,模型记忆里的号码即刻作废,下一处改动就会改到错的行上。现行产品收敛到的是按内容定位,具体分两种形态,OpenAI Codex 的 apply_patch 让模型给带上下文行的补丁块,上下文行与文件内容对上了才写入,对不上就报错重来;Cursor 的应用模型更直接,生成改动时就产出查找替换对。Claude Code 的 Edit 工具是这条路线最直白的版本,模型引一段文件里已有的原文,再给一段要换成的新文,两段一并交上来。官方文档写得很死,不用正则、不做模糊匹配,原文必须与文件逐字符一致,差一个空白字符都对不上;同一段原文在文件里出现不止一次也不行,要么多带些上下文把这一处圈出来,要么明说整批替换。

差一个字符就对不上,听着苛刻,恰恰是这条路线成立的原因。失配是显性的,要么换上,要么干脆报错,不存在差不多对上了、换了个错的地方这种静默出错,模型收到报错,重读文件再试一次就恢复。引原文还顺带验证了读文件这一步,没真读过,原文就引不出来,凭印象编的一段引文在匹配这关直接暴露。几条路线摆在一起,收敛的道理就清楚了,行号跟着编辑历史走,一改就漂;原文跟着文件走,只要那段文字还在,拿内容当地址就一直有效。第 4 章 4.4 节讲执行器时说过,结果要可校验、错了要可恢复,落到改文件这一件上,就是这个逐字符一致的旧串换新串。

官方文档还给整套工具箱画过一张整体的图,技能、MCP、钩子和子智能体全叠在核心回路上,构成一层扩展。核心回路只认得那五类工具,其余能力名字再新,也要以扩展层的身份挂上来。MCP 第 5 章学过,它把工具接入从点对点集成抬成了标准协议;Claude Code 拿它当扩展层里接外部工具的通道,跟书里的用法是一回事。这样的分层恰好印证第 9 章的最简可行架构,先要一个窄核心,什么都能往上挂,再按需把能力挂上去,而不是把每种功能都写死在回路里。

扩展层的四件东西,书里都能找到出处,逐个对号就行,MCP 有第 5 章一整章,子智能体的隔离机制在 8.4 节,技能按需加载背后的思想,是第 4 章 4.6 节的渐进披露,21.5 还要算它的成本,钩子则留给 21.4。这些能力做出来之后怎么分发、怎么让团队放心采纳,第 18 章的登记库与市场讲的正是这一层。所以读官方文档碰见这些词,不用补新知识,要用的章节索引书里都有。

技能的格式还超出了 Claude Code 自己的范围。一个技能就是一个装着 SKILL.md 的文件夹,名称、描述和用法说明都写在这份文件里。这套格式是 Anthropic 在 2025 年 10 月随 Agent Skills 一起推出来的,两个月后就开放成了标准。

截至 2026 年 8 月,能读这个格式的工具已经超过二十种,Gemini CLI、OpenAI Codex、Cursor 和 GitHub Copilot 都算在内。每家工具发现技能的方式不一样,格式本身却通用。技能写一次就能给多个工具用,扩展层里数它的可移植性最好。

搜索凭什么独占一类?仓库一大,文件挨个读不过来,窗口会先超限;搜索把找相关内容折成一次工具调用,一条 grep 顶得上十个文件的全文。这一类的存在说明,工具箱的设计同样要算上下文的预算,到 21.5 讲压缩时,会在另一头再见到这条约束。

这份清单顺带回答了一个新手常问的问题,要不要一上来就给智能体配几十个工具。工业产品的答案就在结构里,核心工具箱收在五类,长尾能力全放到扩展层,按需挂载。收着有什么好处,两笔开销都算得清。一笔是模型的决策空间会随工具数量变大,挑错工具的概率跟着涨;另一笔是每件工具的描述都占上下文,细目留到 21.5。迷你版只配四件工具就能运行,不是舍不得,而是照着真实产品的做法来的。

工具多不多不是关键,关键是每次调用的结果怎么喂进下一轮决策。这一点官方文档着墨不多,但第 4 章的两条原则可以直接拿过来检验。4.2 节要求工具结果回填,回填的是结构化内容,模型下一轮读的就是它;4.4 节要求错误信息也可读,因为错误正是模型下一步的输入。工业产品敢放开手让模型一连串自主调用工具,靠的不只是模型变聪明,更是每次调用返回时,结果都收拾成了模型读得懂、读得完的样子。

21.6 要装配的迷你版里有这样一个工具定义。

python
{
    "type": "function",
    "function": {
        "name": "list_files",
        "description": "列出工作目录下的全部文件,先用它了解项目结构。",
        "parameters": {"type": "object", "properties": {}},
    },
}

拿 4.5 节的标准看这条描述,它做了两件事。既讲清工具干什么,也讲清什么时候该用它(“先用它了解项目结构”)。放到真实产品几十件工具里,模型选谁凭的就是这句里的时机信息。工具设计法不是书里的空谈,它在真实产品里直接决定模型能不能选对。

模型的行动能力到工具箱就到头了。因此给智能体配工具,其实也是在画它能做什么的边界。第 13 章从安全的一侧讲过最小权限,同一件事换个说法就是,配给智能体哪些工具,等于授权它做哪些事。扩展层的价值也在这里,能力不够挂个新工具,比改回路便宜一个量级。工具决定智能体能做什么,环境里的记忆则决定它带着什么状态去做。

21.3 环境即记忆,规则文件的两条读写路径 ​

Claude Code 的环境记忆是一套分层系统,跨会话留下的机制只有两个,由人写的 CLAUDE.md 与智能体自写的 auto memory,前者是规矩,后者是经验。第 4 章 4.6 节提到过 2026 年工程界攒下的几条环境实践,有一条是在仓库根目录放 AGENTS.md,把智能体在这个仓库里该怎么工作写清楚,OpenAI Codex、Gemini CLI 和 Cursor 都认这个约定,这条实践落进具体产品,长成的就是这套形态。

官方文档讲得很干脆,每个会话面对的都是一个全新的上下文窗口,上一个会话什么都不会留下来。这与第 8 章 8.1 节讲的短期记忆一致,会话内就是全部,会话外就是零。跨会话想留下些什么,只能靠那两个机制,官方界线划得很清楚。

CLAUDE.md 按层加载,官方给出明确顺序。托管策略层排在最前,接着是用户主目录下的 ~/.claude/CLAUDE.md,然后是项目目录下的 ./CLAUDE.md 或 ./.claude/CLAUDE.md,最后轮到 CLAUDE.local.md。四层从组织走向个人,粒度一层比一层细。第 1 章 1.9 节写过系统提示,这里能看到同一思想的放大,规则文件分层,公司级、个人级、项目级和本机级,各管一段。

托管策略层守着组织底线,安全规范、合规要求这些事不容商量;用户层收个人偏好,语言习惯、常用约定都算在内;项目层管这个仓库怎么构建,依赖怎么装、测试怎么跑、代码风格守什么;local 那层光看名字就明白,只放这台机器上才生效的条目。多团队协作时自然会有把规矩分层的需求,官方不过把次序定死,谁覆盖谁就不再有悬念。

auto memory 走的是另一个方向,它是智能体边工作边自己攒的记忆。官方口径给过几条边界,按仓库组织、随仓库走;跨 worktree 共享,换了工作树一样有效;每个会话只载入它的前 200 行或 25KB。第 8 章 8.2 节讲长期记忆的文件载体时强调过三点,明文可读、能进版本库和零额外设施,auto memory 正是那个判断的产品化,连读多少都划成了明确预算。

CLAUDE.md 出自人手,写的是规矩,规矩求稳,改动要谨慎。auto memory 出自智能体自己,记的是经验,这个仓库哪类改动容易出问题、哪些约定总被忘掉,这类不值得每次由人叮嘱的事,它自己默默攒下。第 8 章 8.3 节讲记忆的写入、遗忘与压缩,给出应用管理与智能体自管理两条路线,两个机制正好各占一路,CLAUDE.md 走应用管理,auto memory 走自管理。

官方还给了篇幅上的建议,单个 CLAUDE.md 别超过 200 行,多步流程移出去做成技能,这么安排的用意放到 21.5 讲。这条建议跟 4.6 节那条反面教训同源,巨型规则文件挤占任务上下文,腐化又快,所以规则要短、要目录式。注意这里有两个 200,别弄混。auto memory 的前 200 行或 25KB 是加载预算,规定一个会话最多读多少,CLAUDE.md 的 200 行以内是篇幅建议,约束写的时候克制到什么程度,两个数字碰在一起纯属巧合。

篇幅真压不下去时,还有一条结构上的出路,主文件当索引,专题内容拆出去另放,正文里用 @ 加路径引入。CLAUDE.md 里写一行 @docs/architecture.md,架构文档就并了进来;接口约定、代码规范各归各的文件,主文件里只留一行引入。引入的内容该占的上下文一点不少,换来的是主文件一眼读得完,每个专题能单独维护、单独评审。规矩怎么分文件,跟代码怎么分模块,道理是同一个。

加载方式与篇幅都定了,最难的落到往文件里写什么。这里有一条硬纪律,每一行都得拿一次真实的失败换回来。智能体这周把不稳定的测试注释掉,文件里就该添一行“不许注释测试,要么删掉要么修好”;照这个方式长,文件天然就短,没有新失败就不会有新行。

自动生成的文件是负资产。跑一次初始化命令,智能体扫一遍代码库,产出一份漂亮的规则文件,里面写的目录结构与技术栈,本来读代码就能推出来。同一份信息喂两遍,添的只有噪声,没有信号。

两篇研究把这套文件的价值算得更细。一篇拿 124 个真实 PR 做配对实验,测的是效率,有上下文文件时,中位耗时下降约 29%,输出 token 减少约 17%。另一篇看成功率,结论是几乎不升,人写的文件平均只带来约 2.4% 的改善,智能体自动生成的文件反而把成功率拉低 0.5% 到 2%,推理成本还多耗约两成。至于文件写多长,论文消融下来,长短对成功率没有明显影响,写得太长不过是白付 token。归结成一句话,文件不靠写得长、写得全取胜,而靠真实失败一行行攒出来。它能省下耗时和啰嗦,但提升不了成功率。

写法上还有一条反直觉的经验,禁令不如正向指令管用。写“用 tRPC”胜过写“别用 REST”,每出现一个“别”字,就把那件不该做的事推进模型注意力一次,提得越频繁它越挂念。真要写警告,得写到具体动作为止。“别对预发环境跑 migrate:reset”值得占一行,“小心迁移”则不值得。

想写进文件的规矩越攒越多时,还有一类按需加载的规则文件能装下。.claude/rules/ 目录专门收这类文件,文件头部用 paths 字段声明路径匹配,平时它们只躺在盘上待命,一个 token 都不占;等模型碰到匹配路径的文件,才把规则注入上下文。全仓库通用的规矩进 CLAUDE.md,只跟某类文件相关的规矩,比如测试怎么写、样式怎么守,就走条件规则,哪种文件配哪种规则。

规则文件收的是静态规矩,环境里还有易变的一半,当前在哪个分支、哪些文件改了没提交、工作目录在哪。这些状态模型同样得知道,否则它连自己在哪个仓库、哪条分支上工作都说不清。产品把这一半做成动态注入,官方文档把 git 状态列为模型直接可访问的内容,当前分支、未提交改动和最近提交都算在内;注入走系统提醒(system-reminder)这条通道,消息不算用户输入、也不算工具结果,模型读得到,也认得出这是系统给的环境事实,钩子回喂上下文走的也是这条通道(钩子 21.4 细讲)。刷新时机本身就是产品打磨的对象,官方 changelog 里有一条修复说得很直白,压缩之后重新告诉模型的 git 状态,原先给的是会话开始时的旧状态,修过之后才换成当前状态(截至 2026-09)。这里有一对现成的取舍,易变状态写进常驻文件,一个会话只占一次预算,代价是内容会过期;跟着轮次刷新,状态永远新鲜,代价是每轮都重新占预算。产品的做法是把两类东西分开,求稳的规矩常驻,求新的状态刷新。这对取舍是上下文工程的老问题,第 7 章 7.2 节那五条策略,回答的正是信息住在哪、什么时候取。

这套记忆系统却有一条最重要的边界,官方原话直译是,Claude 把这些文件当作上下文,而非强制配置。换句话说,CLAUDE.md 里写的规则,模型会尽力去守,可没有任何机制担保它一定照办。真要硬性保证,比如“绝不允许改这个文件”,官方给的方案不是把规则写得更严厉,而是用一个 PreToolUse 钩子在工具执行前把调用拦下(21.4 细讲)。软约束负责管方向,硬约束负责守底线,两层各干各的。第 13 章 13.3 节讲纵深防御时点过,安全组件必须装在确定性的代码这一侧,这里是同一条原则落到真实产品的位置。

子智能体也可以维护自己的 auto memory,各自的经验各自记。不过产品内置的两类子智能体是例外。Explore 负责只读的代码搜索与探索,Plan 在规划模式里先做调研,把出计划要用的上下文收集齐,这两类连 CLAUDE.md 都不加载,官方的解释是让调研又快又省,读上下文这项权利同样按需分配。第 8 章 8.4 节说子智能体是上下文隔离的极致,隔离的不只是干活的上下文,连记忆的进出都成了设计项。

子智能体还能以文件的形式定义。在项目里建 .claude/agents/ 目录,放进一个 Markdown 文件,开头几行写名字、描述和允许使用的工具,正文当系统提示,一个子智能体就定义好了。把它放到用户主目录下的 ~/.claude/agents/,就能跨项目使用。

派任务仍取决于描述。主智能体看到哪个子智能体的描述更匹配,就把任务交给哪个,这与第 4 章把工具描述写给模型读是同一套方法,只是这回选的不再是工具,而是执行任务的帮手。官方还建议把项目的 agents 目录提交进版本库,让团队共用同一套子智能体,谁把评审智能体改好了,提交后所有人就都拿到这份改进。走到这里,智能体的定义本身也成了环境里的普通文件,git 能追踪、能 diff、能评审,这套思路第 18 章讲能力分发时出现过。

能进版本库的也不止 agents 目录。项目级的设置、权限规则和斜杠命令,提交进 git 就是团队标准,新成员克隆仓库那一下,配置跟代码一起到位,省去挨个口头交代。共享也带来新问题,谁都能改自己那份配置,改着改着就分岔,同一个仓库里跑出几套不一样的智能体,写出来的代码跟着分岔。治理按规模分层。团队规模把配置当代码管,进版本库、走评审、随发布走,人人装出同一套基线;部门规模在团队之上加一层共享的知识索引,文档和规范集中维护,各团队的智能体都来查;企业规模再立一个治理平面,业务规则、架构约束和权限边界统一从这一层下发。第 18 章讲登记库与市场时说过能力要能分发,配置和规矩的分发是同一件事的另一半。

迷你版给了它一个最小实现,在工作目录放一份规则文件,文件存在就并入系统提示。

python
RULES_FILE = ROOT / "MINI_CLAUDE.md"         # 迷你的 CLAUDE.md:规则写在环境里

def system_prompt() -> str:
    """规则文件存在就并入系统提示:环境可读性的最小实现。"""
    base = (
        "你是工作在 workspace/ 目录里的编码智能体,用中文答复。"
        "修代码的流程:先 list_files 和 read_file 了解现状,"
        "再 write_file 修改,最后 run_tests 验证,通过后向用户汇报。"
    )
    if RULES_FILE.is_file():
        rules = RULES_FILE.read_text(encoding="utf-8").strip()
        return base + "\n\n# 工作目录规则(MINI_CLAUDE.md)\n" + rules
    return base

十几行代码就把人来写规矩这一半机制落了地,加载时机仍是会话开始,位置仍是系统提示,差别只在分层。auto memory 那一半,则让智能体在任务收尾时自己往记忆文件追加经验,下次会话开头再并进来,代码量也在同一个量级。真正要弄清楚的不是怎么写,而是记什么、不记什么、何时清空,第 8 章 8.3 节关于遗忘与压缩的讨论全能直接搬来用,这项改造收进了 21.7 末尾的练习清单。

记忆系统把智能体该怎么工作从代码搬进了环境,改一份文件,下一会话的行为就跟着变,一行代码都不用动。然而环境里写下的都是软约束,模型读得到,也可能忘得掉,一碰就出事的操作要靠下一节的硬闸。

21.4 权限与钩子,确定性的那一半 ​

第 13 章 13.2 节讲过最小权限,第 15 章 15.1 节给人机协同定下四种形态,里面最常用的是工具审批。两章合到一处,说的是同一个结论:模型做出的判断是概率性的,批准不能再丢给概率。

官方文档把工具按类型分了级。

工具类型要不要批准细则
只读工具免批限工作目录内
Bash要批内置的一批只读命令除外
文件修改要批批准只到会话结束,下次重新问
WebFetch要批预先批准过的文档域名除外
WebSearch要批"是,且不再询问"按仓库永久保存

这张表的轮廓一眼能认出来,正是 13.2 节最小权限的实操形态:一个动作能造成多大的不可逆伤害,决定它要不要跨过批准这道门槛。

审批环节本身还藏着产品层面的细节。在权限提示里勾一下“是,且不再询问”,这个选择就会变成一条规则,写进 .claude/settings.local.json(截至 2026-08,自 v2.1.211 起这个文件按仓库根解析),往后同类调用直接放行。auto 权限模式则换了一个分类器来代为审批,低风险放行,高风险当场拦下,官方的措辞是拦下危险动作,而不是拿它来问人。第 15 章 15.3 节的渐进自主从高度受限起步、随信任放权,这里正是那条曲线的产品化,一条条“不再询问”的规则,就是信任增长的书面记录。

单次点“是”得来的批准只维持到会话结束。长会话里点了十次放行攒下的信任,为什么不能带进下一个会话?拿 15.3 节的渐进自主一对照就清楚了,单次授权只管当前会话,会话一结束立刻失效,下次得重新挣。显式勾“不再询问”则另当别论,那条规则写进文件、跨会话生效,表格里 WebSearch 那一行按仓库永久保存,说的就是这个。这套设计把默认放权压到最小的时间粒度。要跨会话,非走显式那步不可,影响面多大由自己拍板。

图 21-2 权限规则三段求值,deny、ask、allow 依次过,次序写死

攒下的这些规则,求值语义也定得很死。一次工具调用进来,规则按 deny、ask、allow 的次序评估,deny 段里匹配上就拦,ask 段里匹配上就问人,allow 段里匹配上就放行,三段全空才落回该工具的默认行为;规则写得具体不具体,改变不了这个次序。后果值得掂量,一条宽泛的 deny 会拦下所有沾边的调用,哪怕另有一条更具体的 allow 盖在同一调用上也不放行,allow 在 deny 上开不出例外。写规则的纪律照这个道理反推,deny 写窄,只圈真正要禁的那一小段;allow 写宽,放心的整类放过去。Bash 规则还认前缀,写 Bash(git commit:) 就放行所有以 git commit 起头的命令,冒号星号只认结尾位置;写成 Bash(),整类命令全放开,这道闸等于拆了。拦的窄、放的宽、粒度切到子命令,自建权限系统时,这套求值语义可以直接照搬。

钩子的官方定义,直译过来是这样,用户定义的 shell 命令、HTTP 端点或 LLM 提示词,在 Claude Code 生命周期的特定节点自动执行。定义句列了三种形态,shell 命令是本地脚本,写起来最快;HTTP 端点把检查逻辑挪到远端服务,本地只留配置;LLM 提示词干脆拿模型当检查器。定义句之后,处理器类型又添了 MCP 工具和智能体两种,三加二共五种处理器,可官方的定义句没跟着改,仍写着三种。三种形态共用同一套 matcher,处理器给出的决策也沿同一条路送回回路;至于检查逻辑安在哪一层,按维护成本挑。

触发的节奏分三档。每会话一次的有会话开始、会话结束;每轮一次的有用户提交提示、模型停止;每次工具调用都会触发的是执行前、执行后。另有权限请求、权限拒绝和子智能体启停这类节点,各归到相应的档位里。

底层机制其实就三段。matcher 先过滤这一回要不要触发,事件细节打包成 JSON 送进处理器(shell 命令形态经标准输入送入),处理器随后可以回一个决策。

能回什么决策,21.3 留的那道硬保证就在这儿兑现,答案是一个字段,permissionDecision: "deny"。有了这个字段,一次工具调用在动手之前就拦得下来。钩子能做的远不止拦截,21.1 读五步时官方提过,它能拦截、改写和阻止一次调用;放到自己的系统里,那种不拦不改、只把事件记进日志的钩子,正是第 14 章链路追踪最现成的数据来源。三种力度配上三档节奏和一整排事件节点,产品里全部确定性的自动化都挂在这套机制上。

图 21-3 PreToolUse 决策四值,一个字段定一次调用的去向

deny 只是决策词表里的一个值。permissionDecision 有四个值,allow 直接放行,deny 直接拦下,ask 把决定交回给人,defer 跳过这一环,让调用顺着常规权限流程往下走。

拦之外还能改,处理器回一个 updatedInput,工具实际拿到的参数就换成改写后的这份,重定向路径、补齐参数都从这儿做。

第三个字段 additionalContext 什么事件都认,内容会附进模型的上下文。这个字段把钩子从把关变成了反馈环,PostToolUse 钩子在每次改文件之后跑静态检查,查出来的问题经 additionalContext 回喂给模型,模型照着改,改完再查,检查、回喂、修正的闭环在外壳里自己转完,全程不需要人工介入。格式化这类守卫更安静,模型连项目用什么格式规范都不必知道,文件落盘时格式已经修好。

Stop 事件上这个环走到了头,模型宣布任务完成,钩子先把测试跑一遍,测试不过就回一个 block 决策并附上理由,会话不许收尾,模型只得接着修。这里有个词表要分清,Stop 回的 block 走的是另一个字段 decision,跟 PreToolUse 的 permissionDecision 不在同一张表上;官方现行口径是 decision 只收 block 一个值,放行就省略这个字段(截至 2026-09)。这个环自带死循环的风险,修完再宣布完成,钩子再拦,永远出不去,产品的出口是一个输入字段 stop_hook_active,钩子把收尾拦回去、模型修完再想停的这一回,它为真,钩子看到就知道放行。改完自动跑测试、不过不许收尾,这套纪律用的人很多,只有一处要留神,测试集一大,每改一行全量跑一遍,等待会比实际工作还长,纪律得配上一句:只跑与改动相关的那部分测试,按测试运行器提供的关联机制挑,这套自动检查才用得下去。

处理器怎么选,另有一根确定性阶梯,回答的是检查交给谁。命令在底,提示居中,智能体在顶,命令是本地脚本,规则写得死;提示让一个小模型做单次判断;智能体则开子智能体做多轮核查,能翻文件也能搜索。选型原则是能低则低,命令能拦下就不上提示,提示能判了就不动智能体,每抬高一档,就多一分不确定性和成本。另外两种处理器,HTTP 端点与 MCP 工具,是接外部检查的接口,不在这根阶梯上。

给事件分类时,第一维看的不是何时触发,而是能否阻断。拦得下动作的事件才配谈把关,拦不下的只能写日志、喂观测。同一种检查,想阻断就挂在工具执行前,想留痕就挂在工具执行后,位置先由这一维定下。

命令型处理器还守着一份退出码契约,分成三档。0 表示正常,标准输出里的 JSON 就是决策;2 表示故意阻断,标准错误的内容会退回给模型当理由;其他值说明脚本自己出了问题,不阻断但必须显形,轨迹里会出现钩子报错的提示,完整错误进调试日志。把故意阻断与脚本故障拆开,出于很实际的考虑,检查脚本出 bug 时,不至于顺带把整个工作流也停掉。

同一个事件挂多个钩子很常见,有一条语义容易想错,官方写明,同一事件上全部匹配的钩子并行执行,不按配置排先后;同一个处理器在多处设置里都定义过,也只跑一次。想当然地指望先格式化、后检查的顺序,问题往往就出在这条上,两个检查真有先后依赖,写进同一个脚本里顺序执行,比挂两个钩子等运气可靠。钩子本身还能直接测,把一段有代表性的事件 JSON 从终端喂给脚本,看退出码、看输出,一行命令就把处理器验完,不必每次开一个会话去触发真实事件。这跟 21.7 要讲的桩测试是同一个道理,脚本化的输入喂给真实的部件,确定性部件的验证本来就该这么做。

排障有现成的四步链,每步带一次验证。先看装载,会话里列得出这条钩子,配置才算读到了。再看触发,往脚本里添一行写日志的语句,动一下工具,日志涨了才说明它跑了。再看成功,钩子跑了不等于完成了工作,去查它该格式化的文件、该写的记录动没动。最后直接拿 JSON 喂脚本,退出码和报错会把残留在脚本或环境里的问题暴露出来。真有一类问题就出在环境上,依赖一清理,检查工具没了,钩子本身没坏,跑的环境变了。四步走完,问题十有八九已经定位。

还有两件事得记牢。钩子脚本以当前用户的完整权限运行,没有沙箱,它能摸到的文件、网络和凭据,跟自己敲命令一模一样,所以钩子要当可执行代码来审查,输入校验、路径检查一样不能省;权限系统把模型拦在外面,钩子自己却站在信任边界之内。另一件是不必等结果的任务有专门的做法,钩子可以标成异步,扔到后台执行,会话照常往前走,写审计日志、发通知都用得上;异步钩子拦不了动作,同步钩子的执行则有超时封顶,到点取消、输出作废,调用放行继续走正常权限流程,指望一个挂死的检查脚本替人把关,靠不住。

权限和钩子本是两个概念,其实又是一体的,权限系统回答这类动作要不要人点头,钩子回答点头前后还想查点什么、记点什么。权限请求、权限拒绝都各有专属节点,接上第 14 章的链路追踪,一次拦截同样能成为可观测事件。第 13 章 13.3 节的三层纵深防御,在真实产品里直接对应的是工具授权层,权限系统就是它的产品化形态,PreToolUse 钩子为执行前的把关留好了挂点;输出验证那一层官方架构页没有对应机制,自己的应用得把这一环守住,别等产品来兜底。

迷你版的那道权限门,位置与 PreToolUse 钩子等价。

python
# 只读工具免批,写和执行要人点头,对应 Claude Code 的权限分级
NEEDS_APPROVAL = {"write_file", "run_tests"}

def permission_gate(tool_name: str, args: dict) -> tuple[bool, str]:
    if tool_name not in NEEDS_APPROVAL:
        return True, ""
    if AUTO_APPROVE:
        return True, ""
    preview = json.dumps(args, ensure_ascii=False)
    if len(preview) > 120:
        preview = preview[:120] + "……"
    answer = input(f"【权限】{tool_name} {preview} 允许?[y/N] ").strip().lower()
    if answer == "y":
        return True, ""
    return False, f"用户拒绝了 {tool_name} 调用"

有两处设计值得停下来看一眼。NEEDS_APPROVAL 做成了一个集合,而不是给每个工具各写一个 if,这才配得上分级这个词,权限的粒度在工具类型上。另一处在拒绝的处理方式,拒绝不是抛出异常了事,函数把是否放行与拒绝原因一并作为返回值带出来,怎么处置交由调用方决定,处置的办法下一段就能见到。AUTO_APPROVE 这个开关占的是 auto 权限模式的位置,教学版省了分类器,开关一开就是全放行;真实产品的 auto 会拦下高风险动作,这正是教学版与真实产品之间一处实实在在的差距。

处置的办法在调用方,指 21.6 印出的 run_agent 中那个 else 分支。一旦拒绝发生,run_agent 不抛异常、不终止,而是把拒绝原因包成 error 结果回填,交由模型自行消化。异常终止是工程师的视角,一出错就停;error 回填是智能体的视角,把用户不批准当作一条新事实递给模型,模型读到后可以改道,先说明打算怎么改,再请求批准。4.4 节讲过错误信息怎么回填给模型,拒绝这个特例这么好用,道理与错误回填那条通例一样,模型拿得到,就能照着改。

闸怎么添、何时拆,同样有纪律,而且两条并不对称。加约束遵循 21.3 讲过的那条纪律,只在真实失败之后才添;删约束则要等到更强的模型让它变得多余,模型升级并不自动免检。照这套纪律走,闸的形状就是这支团队失败史的形状。

顺着还能推出一个结论,好的规则库下载不来。起点完全相同的两支团队,半年后各自握着两套不同的规则,这算特性,不算缺陷。两边遇到的失败不一样,互相照抄,等于把别人家的失败教训整套搬进门。第 13 章说过安全属性不能建立在模型自觉上,这里同理,规则库不能建立在别人的失败上,得从自己的失败里一条条攒出来。

权限与钩子把住的是回路里做不做这一问,模型的每个动作,要么过人的闸,要么过检查的闸。可这两道闸管的全是新动作。窗口里早已攒下的那些,十几轮读过的文件、跑过的测试和回填的结果,没有任何闸在管。

21.5 上下文压缩,留什么 ​

第 7 章翻来覆去只讲一件事,上下文窗口是智能体最稀缺的资源。编码智能体在真实仓库里干活,读上十个文件、跑几轮测试、翻几回报错,窗口就满了。满了怎么办,把最早的聊天记录删掉?工业产品的答案远比这精细,官方文档把压缩(即第 7 章的压实)写成一张什么该幸存的设计表,这一节逐行来读。

图 21-4 技能加载生命周期,描述常驻,正文按需展开

按官方口径,会话一启动就要载入四件东西,CLAUDE.md、auto memory、MCP 工具名和技能的 description。注意最后一样只载描述,不载正文。技能正文要等实际使用才加载,CLAUDE.md 则从头到尾一直载着,21.3 留下的那个问题到这里有了答案,把多步流程移出 CLAUDE.md、做成技能,省的是每个会话的固定开销;偶尔才用到的流程,不值得每个会话整份载入。这正是渐进披露落到产品层的样子。第 4 章 4.6 节说仓库文档要目录式、按需取用,同一个思想在这里换了层实现。

只载描述这条设计还有下半段。所有技能的描述共同分一份字符预算,量级约为上下文窗口的百分之一。预算紧张时,先压短描述,再丢掉用得最少的那些技能描述,技能名字始终常驻,正文等用到才展开。降级按使用频率办,不按技能条目平分。这逼着写描述从第一天起就精炼措辞,降级第一步压的就是描述,写得越短,这一步越少误伤真正在用的技能。这份预算算的只是技能描述;每件工具的定义也随每一轮请求上行、同样占窗口,那笔开销 21.2 记过。

技能这层还有一件事要测,触发。触发失败分两种。漏触发是该用技能的请求没用上,过触发是不该用的请求抢着用。测法是挑 10 到 20 条真实请求组成任务集,该触发与不该触发的情形都备齐,跑一遍数比例。社区经验给出可用的线,该触发的触发率要过九成,不该触发的误触发压到半成。

漏触发修起来有个省力的办法,直接问模型为什么没触发。把技能描述和没触发的那句请求摆给它看,模型多半说得出所以然,描述里缺了哪类措辞、请求的说法跟描述哪里对不上。照它说的补描述,重启会话再试,几轮就收敛。猜描述为什么不灵,不如让靠这份描述工作的模型亲自诊断。

另有一类技能根本不该让模型自动触发。官方给了一个开关 disable-model-invocation,设成 true,技能就只听人的,描述不进上下文,模型不知道它存在,人敲斜杠命令才加载。这一下把技能分成两种用法,参考型走默认,描述常驻,模型按语境自己判断要不要用它;任务型走开关,提交代码、部署和发通知这类带副作用的操作,误触发一次就有实际代价,必须控制在人手里。分界拿一个最坏情形测试来画,想象模型自作主张执行了这个技能,最坏会发生什么。一想到自动提交没测过的代码、自动部署有问题的版本就有风险,就设开关;最坏也就是多显示一份参考文档的小事,就交给模型自动判断。副作用越大,控制握得越紧。

技能列表拉得越长,彼此竞争触发就越激烈,描述长得很像的技能最容易让模型选错。这份预算与 21.2 讲的工具数量是同一个问题,列表要勤清理,用不上的技能宁可下架,也别留在那里稀释其他技能的触发。

修剪之外,技能库还得不断积累。一个可循的闭环是先搜、后干、再沉淀,接到任务先查有没有现成技能,有就照着执行;没有就现学现解,干完把解法整理成一个新技能,留给下一次。技能库再进一步,每条技能还能带上执行记录和成功率,路由按实证表现挑技能,不按描述相似度挑,描述写得漂亮、实际老失败的技能,数据会把它压下去。第 8 章 8.2 节讲长期记忆时强调经验要写成明文文件,技能库正是落盘的一种,而且每条都留着用得怎样的记录,好不好由数据说了算。

SKILL.md 自身也有一个定位,它是路由器,不是仓库。常见的误区是把相关知识全塞进正文;正确做法是正文只放核心流程加一张指路表,写明哪类请求该读哪个参考文件,细节全部放进参考文件按需读取。

目录结构有现成约定,开放标准给技能文件夹画的常见布局是三个子目录,scripts 放可执行脚本,references 放参考文档,assets 放模板和素材,SKILL.md 居中当索引。这个布局跟路由器的定位正好接上,正文指路表指向的文件,多半就躺在这三个目录里。

还有一条停手线。一旦发现自己正往 SKILL.md 里写公式、让模型照着演算,就该停笔,把这段逻辑做成脚本交给模型调用。脚本执行比模型推导更准、更省 token、还可复现。知识放进技能,计算放进脚本,两边不要混在一起。

技能正文里还能嵌 shell 命令,做动态注入。正文里写上一对反引号夹一条命令,这份技能发给模型之前,产品先在 shell 里把命令跑了,输出原样内联进正文。场景很具体,做一个拉取请求技能,正文里嵌上 git diff 和最近几条提交,模型收到技能时改动清单已经摆在眼前,头一轮不必再花调用去摸现状。便利的背面是注入面,技能文里嵌了命令,抄来的技能就等于带了一段可执行代码;官方给设置留了策略开关,关掉技能的 shell 执行,嵌入的命令一律替换成一行禁用提示,管不住来源的技能可以整体关掉这条通道。

压缩怎么触发,又是什么形态?接近上限时它自动开始,也可以手动敲 /compact,用一个结构化摘要替换整个会话;/autocompact 500k 能调阈值,/rewind 能退回某个点重新来过。要紧的不是这些命令,而是替换发生那一刻,哪些东西不进摘要、可以原样幸存。官方文档给了一张幸存表,下面逐行看。

条目压缩之后
系统提示不变
项目根 CLAUDE.md、不带路径条件的规则重新注入
auto memory、计划重新注入
带路径条件的规则、嵌套的 CLAUDE.md按触发条件重新加载
最近修改的文件至多重读 5 个;单个超 5,000 token 不重读原文,只回一条路径引用
已用过的技能正文重注入;单技能上限 5,000 token,合计上限 25,000 token,超了最旧先丢
钩子注入的上下文不幸存,随会话一起进摘要

幸存表之外还有一条要记住,压缩发生时,会话开始钩子会重新跑一遍。

幸存表里最花心思的,是大文件那一行。为什么不直接把文件重读一遍,只回一条路径引用?上万 token 的文件读一次就是一大笔开销,下一轮还不一定用得上。只给路径,等模型判断需要时自己再读,成本就由压缩时预付改成按需支付。这一行用的就是 7.2 节的 Retrieval 策略,存放权和取用权分开,窗口里只有索引,内容留在盘上。

把幸存表逐行对照回第 7 章,谁该活下来只问两件事,下一次还用不用得上,重新算一遍贵不贵。规则和计划轻巧、常用,所以重新注入;文件重、又按需取用,就给路径让模型需要时再读;技能正文有明确的预算上限,5,000 和 25,000 这两个数字,正是 Reduce 策略按条目摊下去的配额。7.3 节讲压实与摘要,压实是把消化完的信息换成占位引用;这张表,就是那条判据的工程版本。

再往细处对,7.2 节的五种策略在这里能对上三条。规则与计划重新注入属于 Write,要紧的东西写成持久条目;大文件只回路径、要用再读属于 Retrieval,存放权与取用权拆开;技能正文设上限、最旧先丢属于 Reduce,预算硬顶。7.2 节教的是策略叫什么,这张表呈现的是策略落到条目后的具体配额,5 个、5,000、25,000,每个数字都是同一个权衡的答案,这一类内容该在窗口里占多少。

这套预算安排里也给子智能体留了位置。按官方口径,子智能体在独立上下文里运行,任务干完只回传一段摘要和一点元数据。第 8 章 8.4 节说子智能体把上下文隔离做到了极致,这份收益在这儿看得最清楚,主会话为一次专项调查要付出的上下文成本,从全程旁观缩成了一页摘要。

还有一处细节。自定义子智能体的 description 加起来超过 15,000 token,启动时就会告警,官方建议把细节挪进子智能体的系统提示,理由是全部 description 在启动那刻就要加载,而系统提示只在运行时才加载。连这条告警线都是照着加载时机算出来的。

技能和子智能体还能组合,组合有两个方向。一个方向是子智能体预载技能,子智能体的定义里声明要带的技能,开工时知识包先装进去,子智能体自己的正文当系统提示,技能全文当领域知识。另一个方向是技能派子智能体,技能触发时另开一个隔离子智能体去执行,技能正文换成任务指令,系统提示由子智能体自己带,中间过程不进主对话。两个方向机制相同,归属相反,分界就一条,前一方向的系统提示是写死的子智能体定义,技能当知识装进去;后一方向的系统提示是通用模板,技能正文当任务交下去。给流水线上的固定岗位配知识,走前一方向;让大型任务在隔离的上下文里一次干完,走后一方向。第 8 章 8.4 节的上下文隔离,在这里又用上了一回。

这套预算安排,迷你版只落了两处最小实现。read_file 最多返回 200 行,超长就截断、注明总行数;run_tests 只吐输出末尾的 2,000 字符,报错信息正好全聚在输出末尾。两处背后是同一个判断,工具结果每一行都占窗口,截断就是最小号的幸存表。真把 run_tests 接上大项目,几万行输出灌进来,这张表迟早要自己重新发明一遍,先截尾、再只留失败的测试、最后只留失败断言附近几行,用的都是 Reduce。

压缩不是出事了才处理,而是常规设计。第 7 章给的是原则,这张表则是原则落到条目后的样子,每个条目都有去处、有配额。哪天自己的系统压缩写不动了,把这张表抄一遍,给每个条目标出去处。

外循环立了一条铁律,窗口里什么都不多留。每轮只带盘上三件东西进场,干完写回去,队列清空才算收工。

图 21-5 长任务外循环,状态在盘上,窗口每轮全新

预算和压缩只能管到会话内。任务更长时还有更彻底的办法,每轮都开一个全新会话,状态一律存在盘上。计划文件就是需求书,进度文件就是实验记录,规则文件就是教训本。每轮开工先读这三份,干完把结果写回盘上,然后关窗退出。交接全靠盘上的状态,窗口里不保留历史。

这个循环有两点特别值得说。第一个是上下文永远干净,第 47 轮的模型就是第 1 轮的模型,是文件系统在学习,这话是先行者们的原话。另一个在退出条件,不是等模型宣布完成,而是等任务队列清空,队列里还有任务就不准停,防的正是那种干了八成就宣称胜利的智能体。

这套跑法出自社区实践,最开始的形态,是一个 shell 脚本把智能体包起来跑批。如今各家厂商的产品把循环的原语收进自家工具,不写脚本也能照这个模式安排长任务。教学版两百行装不下,先写进 21.7 的进阶清单。

21.6 装配两百行的迷你 Claude Code ​

回路的骨架借自第 2 章,工具定义与执行约定借自第 4 章,规则文件与权限门取自本章 21.3、21.4 两节,上下文两处截断取自 21.5。这一节负责把这些零件拼成一个能跑的命令行编码智能体,成品是单个约二百行的文件,能列目录、读文件、改代码和跑测试,写操作之前设一道人工确认的权限门。

迷你版的名字叫 mini_cc.py,它只在旁边的 workspace/ 目录里工作,不会越过这个边界。规则文件 MINI_CLAUDE.md 就放在工作目录,文件在就把内容并进系统提示。工具一共四件,list_files、read_file、write_file 和 run_tests,其中写文件和跑测试都要过权限门。模型接入还是第 2 章的老办法,走 DeepSeek 的 OpenAI 兼容端点。

代码按六段来讲。四段印在这一节,权限门和系统提示那两段 21.3、21.4 已经逐行印过,翻回前面接上,六段拼起来就是完整的二百行。RULES_FILE、NEEDS_APPROVAL 这两个常量前后都出现过,拼装时各自只留一处。

装配讲究次序,工具层先上,回路跟上,闸放到最后。先把四个工具函数调通,用几行脚本直接调用,确认读得进文件、跑得动测试。接着写 run_agent 把它们串进回路,循环本身可以先拿桩客户端验证,21.7 的实测就是这么做的。权限门和规则文件放到最后才上,动的只是回路上的一两处,骨架不动。次序一反过来就乱,回路没通先加权限,出错时根本分不清是回路断还是闸坏。先部件后整体,出问题才好定位,装配这类工作的道理都是相通的。

第一段先看头部与常量。

python
"""迷你 Claude Code:一个能读文件、改代码、跑测试的命令行编码智能体。

零件全部来自本书:第 2 章的 while 回路、第 4 章的工具定义与执行器约定。
再从 Claude Code 借三件设计:工具结果结构化回填、工作目录里的规则文件、
写操作前的权限门。教学简化版,不是复刻。
"""

import json
import os
import subprocess
import sys
from pathlib import Path

from openai import OpenAI

client = OpenAI(
    api_key=os.environ["DEEPSEEK_API_KEY"],
    base_url="<DeepSeek 兼容端点,见官方文档>",
)
MODEL = "deepseek-v4-flash"

ROOT = (Path(__file__).parent / "workspace").resolve()   # 智能体的工作目录
RULES_FILE = ROOT / "MINI_CLAUDE.md"         # 迷你的 CLAUDE.md:规则写在环境里
MAX_TURNS = 12
AUTO_APPROVE = os.environ.get("MINI_AUTO_APPROVE") == "1"  # 全放行;省掉分类器的 auto 模式

文件头那段 docstring 写清了各处零件出自哪里,别把它当客套话,日后重读这份代码,它就是索引。ROOT 把智能体圈在 workspace/ 里,再配 read_file、write_file 中的三行路径校验,13.2 节的沙箱思想就有了一份最小实现;AUTO_APPROVE 靠环境变量开启全放行,占的正是 21.4 里 auto 模式的位置。

第二段看四个工具函数,前两个只读。

python
def list_files() -> str:
    """列出工作目录下的全部文件。"""
    files = sorted(
        str(p.relative_to(ROOT)) for p in ROOT.rglob("*") if p.is_file()
    )
    return "\n".join(files) or "(空目录)"

def read_file(path: str) -> str:
    """读一个文本文件,最多返回 200 行。"""
    target = (ROOT / path).resolve()
    if not target.is_relative_to(ROOT):  # 防绝对路径与 .. 逃出工作目录
        return json.dumps({"error": f"路径越界:{path}"}, ensure_ascii=False)
    if not target.is_file():
        return json.dumps({"error": f"文件不存在:{path}"}, ensure_ascii=False)
    lines = target.read_text(encoding="utf-8").splitlines()
    if len(lines) > 200:
        head = "\n".join(lines[:200])
        return head + f"\n……(共 {len(lines)} 行,只返回前 200 行)"
    return "\n".join(lines)

这段代码有三处值得留意。先看 read_file 入口的三行路径校验,无论模型传绝对路径还是带 .. 的相对路径,resolve 之后都越不过 ROOT。安全不能建立在模型自觉上,13.2 节的原则落到代码上就是这三行。文件找不到时,它返回一段 JSON 错误文本而不是抛异常,这正是 4.4 节讲的错误回填,错误是喂给模型的输入,不是给人看的栈。200 行截断,讲的就是 21.5 的窗口预算。剩下两个函数在写的一侧。

python
def write_file(path: str, content: str) -> str:
    """把 content 整体写入文件(新建或覆盖)。"""
    target = (ROOT / path).resolve()
    if not target.is_relative_to(ROOT):  # 防绝对路径与 .. 逃出工作目录
        return json.dumps({"error": f"路径越界:{path}"}, ensure_ascii=False)
    target.parent.mkdir(parents=True, exist_ok=True)
    target.write_text(content, encoding="utf-8")
    return json.dumps(
        {"written": path, "chars": len(content)}, ensure_ascii=False
    )

def run_tests() -> str:
    """在工作目录里跑 pytest,返回退出码和输出。"""
    proc = subprocess.run(
        [sys.executable, "-m", "pytest", "-q"],
        cwd=ROOT,
        capture_output=True,
        text=True,
        timeout=120,
    )
    out = (proc.stdout + proc.stderr).strip()
    return f"exit={proc.returncode}\n{out[-2000:]}"  # 截尾,防止刷爆上下文

write_file 入口同样有三行校验,返回时带上字符数;run_tests 则配了 timeout=120,输出只留末尾 2,000 字符。四个函数返回的全是字符串,模型拿到的每份工具结果都一个形状,这正对应 21.2 那条判断,每次调用的结果怎么喂进下一轮,决定下一轮决策的质量。

第三段讲工具定义和权限集合。TOOLS_SPEC 里那四条定义照着第 4 章的写法来,第一条在 21.2 出现过,这里把四条全部印出来。

python
TOOLS_SPEC = [
    {
        "type": "function",
        "function": {
            "name": "list_files",
            "description": "列出工作目录下的全部文件,先用它了解项目结构。",
            "parameters": {"type": "object", "properties": {}},
        },
    },
    {
        "type": "function",
        "function": {
            "name": "read_file",
            "description": "读一个文本文件的内容,最多 200 行。",
            "parameters": {
                "type": "object",
                "properties": {
                    "path": {"type": "string", "description": "相对工作目录的路径"}
                },
                "required": ["path"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "write_file",
            "description": "把内容整体写入文件。修改代码用它。",
            "parameters": {
                "type": "object",
                "properties": {
                    "path": {"type": "string"},
                    "content": {"type": "string", "description": "文件的完整新内容"},
                },
                "required": ["path", "content"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "run_tests",
            "description": "在工作目录里跑 pytest,看改动是否通过测试。",
            "parameters": {"type": "object", "properties": {}},
        },
    },
]

tool_map = {
    "list_files": list_files,
    "read_file": read_file,
    "write_file": write_file,
    "run_tests": run_tests,
}

# 只读工具免批,写和执行要人点头,对应 Claude Code 的权限分级
NEEDS_APPROVAL = {"write_file", "run_tests"}

第四、第五两段前面都逐行读过了,system_prompt 怎么并入规则文件,permission_gate 怎么立闸。这两段不在这里重印,翻回 21.3、21.4 对照着看就行。

第六段看回路本体。

python
def run_agent(task: str) -> str:
    messages = [
        {"role": "system", "content": system_prompt()},
        {"role": "user", "content": task},
    ]

    for turn in range(1, MAX_TURNS + 1):
        response = client.chat.completions.create(
            model=MODEL,
            messages=messages,
            tools=TOOLS_SPEC,
        )
        message = response.choices[0].message
        messages.append(message)  # 模型的决定原样进历史

        if not message.tool_calls:
            return message.content  # 模型不再要工具,任务收尾

        for tool_call in message.tool_calls:
            name = tool_call.function.name
            args = json.loads(tool_call.function.arguments)
            print(f"[第 {turn} 轮] {name}({json.dumps(args, ensure_ascii=False)[:100]})")

            allowed, reason = permission_gate(name, args)
            if allowed:
                result = tool_map[name](**args)
            else:
                # 拒绝也回一条 tool 消息,模型看得见并调整做法
                result = json.dumps({"error": reason}, ensure_ascii=False)
                print(f"          ↳ 已拦截:{reason}")
            messages.append(
                {"role": "tool", "tool_call_id": tool_call.id, "content": result}
            )

    return "已达轮数上限,任务未收尾。"

if __name__ == "__main__":
    task = " ".join(sys.argv[1:]) or "workspace 里有个计算模块的测试在挂,找到并修好它。"
    print(run_agent(task))

把这段代码跟第 2 章的 run_agent 摆在一起比,回路本体比第 2 章多的只有两处,一处是 permission_gate 那三行,另一处是拒绝分支里 error 结果的回填;开场那条 system 消息里并入规则文件,21.3 已交代过,不算新增。21.1 讲的五步这里全都在,收提示、评估、执行、循环、出结果,过闸和拒绝回填不过是叠在第三步上的增量。轮数上限 12,修代码这类多步任务,余量给得够足。

workspace/ 下要提前备好三个文件。calc.py 这份计算模块里埋着 bug,add 的实现写成了减法;test_calc.py 备着两条断言,add 与 mul 各占一条;MINI_CLAUDE.md 写下三行规矩。

text
- 本项目是教学用小项目,只有一个 calc.py 和它的测试。
- 修改代码保持风格一致:函数不写类型注解,注释用中文。
- 汇报时给出修改了哪个函数、哪一行。

第三件正是 21.3 那套机制在起作用。规则进了系统提示,模型收尾时该讲清改了哪个函数,要求就来自这里。真跑起来照不照着办,得看模型的表现。

21.7 实测与进阶清单,教学版差在哪 ​

装出来的两百行能不能运行,得靠三个场景来验,一条正路、一条带闸的正路和一条被拦下来的路。桩测试的做法,是拿脚本化的假客户端回放固定回合,不打真实 API,跑出来的行为形状跟真跑一致,既确定又不花钱,还能随时回归。这个道理,第 12 章讲从便宜到贵的三层漏斗时已经说过一遍。

做桩并不费力,假客户端里放一份剧本,模型第一轮该回什么工具调用、第几轮该用什么收尾,全写死。每次 create 弹出一条,run_agent 并不知道对面是假的,照常解析、照常执行、照常回填。剧本放完,真实工具全都动过,回路完整走了一圈,一分钱 API 都没花。改了权限逻辑想回归,把剧本重放一遍即可。

三个场景这样安排。auto 模式下让模型自己一路跑到底,五回合内修好挂掉的测试;手动模式下由人两次放行,同样修好;再一个手动场景是第一次写操作就遭拒绝,看模型拿到 error 结果后怎样收场。前两个用来证明回路通畅,第三个专盯权限门与拒绝回填。第三个场景的控制台输出贴在下面。

text
[第 1 轮] list_files({})
[第 2 轮] read_file({"path": "calc.py"})
[第 3 轮] write_file({"path": "calc.py", "content": "def add(a, b):\n    return a + b\n\n\ndef mul(a, b):\n    return a *)
          ↳ 已拦截:用户拒绝了 write_file 调用

这段输出读下来,模型先列目录、再读文件,写操作在闸前停了步。输出没印出的后半段,桩剧本里也写好了,拒绝以 error 结果回填,模型收到后放弃修改、照实汇报,文件保持原样。第三行那半截 JSON 不是出错,是打印侧截断的结果,[:100] 只给一百字符的调用预览,截断点恰好切在 JSON 字符串中间,收尾引号都没带上,目的就是不让整份文件刷屏。

场景 1 设 MINI_AUTO_APPROVE=1,五个回合走完,列目录、读文件、改文件、跑测试、汇报一气贯通,收尾时 calc.py 里的 add 确实回到加法,测试一片绿。场景 2 换成手动模式,闸口问两次,一次放给 write_file、一次放给 run_tests,路径跟场景 1 一模一样,只是每一步多了人点头这一下。

三个场景拼起来恰好盖住权限门的三条路径,放行、拦截和自动。装配完的代码于是有了三个能讲清的预期,这是第 12 章养成的习惯,改动之后跑一遍,三条路径全绿才算没改坏。自己动手跑之前先备三件东西,装好 openai 和 pytest 两个包,把 DEEPSEEK_API_KEY 写进环境变量,在 workspace/ 里备好代码与 pytest 测试,然后执行 python3 mini_cc.py "测试在挂,修好它"。

这行命令本身就是编码智能体的非交互用法,任务从命令行进来,结果打到终端,跑完退出。手动模式下闸照样问人,开了 auto 就全程无人看管。真实产品把这种用法做成了正式形态,官方叫非交互模式(non-interactive mode),-p 后面跟任务就能启动,数据经管道喂入,结果以 JSON 吐出,退出码报成败,CI 里做代码评审、分诊构建日志都用它。教学版的 auto 模式加上 MAX_TURNS 上限,凑成了无人值守的最小配置,第 15 章分级表里人离得最远的那一档,说的就是它。只是真把它放进 CI,手动那道闸立刻暴露问题,流水线里没有人守着终端按 y,等批准的会话只会一直挂着。官方给无人值守场景的答案是授权前移到启动那一刻(截至 2026-09),--allowedTools 按权限规则语法在命令行上白名单式放行,哪几类调用免批,启动时就写死;--bare 则跳过环境加载,钩子、技能、规则文件、子智能体、插件、MCP 服务器和 auto memory 这些一概不读,跑在不信任的目录里时,防的正是项目里的钩子被篡改。21.4 那句批准不能再丢给概率,落到无人值守场景就是这条,现场问不出批准,就把授权在启动时用参数定死。

非交互模式的输出分三档,各服务各的场景。text 给人读,json 给机器读,stream-json 一次吐一行 JSON 对象,给管线消费。json 那一档回的是一个结果信封,会话标识、轮数、开销、token 用量连同最终答复装在一起,下游拿到就能记录用量,每个拉取请求的评审花了多少、跑了几轮,照信封里的字段统计就行,超了线的部门也按这个数字拦。stream-json 把过程也交出来,一次工具调用、一个结果和最终答复各占一行,中间夹着系统事件,会话启动有 init,压缩发生有 compact_boundary,管线里盯着后一个事件,就知道这趟任务的上下文压过一回,长任务里值得留意。三档输出配上 Unix 管道,一行命令就是一条流水线,cat 一份日志,接 claude -p 解析报错,输出再喂给下一个程序,CI 里串起来不费事。

这两百行是教学简化版,不是复刻,这一点得认清,差距要一项项数清楚。真实产品的内置工具有五类,上面还有扩展层,改文件按内容定位、旧串换新串,迷你版的 write_file 还是整文件重写;规则文件是四层加载,再加智能体自记的 auto memory;权限是按工具类型分级的完整系统;钩子有三种节奏和一整排事件节点;压缩自带一张逐条目的幸存表;长任务另有每轮换新会话、状态放盘上的外循环。差距列全就是进阶清单,而每一项都不是白手起家,回路的形状、工具的设计方法、软硬约束的分层和预算的思路,二十章里都讲过。照清单逐项补,补到哪一项,就回书里相应那一章再读一遍。

再往远处看,还有一个方向,把计划本身从文字换成图。第 6 章里的图装知识,这里的图装规划。微软团队做的仓库规划图 RPG,把能力、文件结构、数据流和函数整合进一张统一的图,用它替换自由文本计划,然后顺着图生成整个仓库。在 RepoCraft 基准上,配套的 ZeroRepo 生成的仓库平均约 3.6 万行代码,比最强基线大 3.9 倍,功能覆盖 81.5%。数字全是论文自己报的。

真正值得带走的是那条机制层面的判断。用自然语言写的计划,每迭代一轮就与现实偏开一点,计划同代码越走越远。把依赖写进图里,它们就成了显式的边,计划跟代码的一致性就有保障。图存储与本体这套基础设施,第 6 章 6.5 讲过了。

再回到 21.1 开头那个问题,工业级编码智能体的核心回路到底要写多复杂。眼下有了自己的答案,二百行代码,四件工具、一份规则文件、一道权限门和两处截断,能跑、能拦、能收尾。余下的差距,进阶清单都列好了。

本章小结 ​

  • 21.1 到 21.5 解剖五节对照下来,工业级编码智能体的机制跟书里教的没有两样。回路是三步循环(收集上下文、采取行动、验证结果),SDK 展开成五步,验证结果做成工具;工具是五类内置加扩展层(技能、MCP、钩子和子智能体);环境记忆两条读写路径,CLAUDE.md 四层加载是规矩,auto memory 是经验,每会话载前 200 行或 25KB;权限按工具类型分级,钩子给确定性把关,permissionDecision 四值、退出码 0/2/其他三分;压缩是一张幸存表,最近文件至多重读 5 个、单个超 5,000 token 只回路径、技能正文合计上限 25,000 token。
  • 两条硬纪律直接可抄。规则文件每一行都由真实失败换得,自动生成的文件是负资产(两篇研究的数字,有上下文文件时中位耗时降约 29%、输出 token 降约 17%,成功率几乎不升);禁令不如正向指令,警告要写到具体动作。
  • 21.6 装配次序固定,先工具层、再回路、最后才加闸,次序一反,出了问题分不清错在哪一层;两百行 mini_cc.py,四件工具、一份规则文件、一道权限门、两处截断。
  • 21.7 用桩测试三场景(放行、拦截和自动)回归,三条路径全绿才算没改坏;非交互模式是无人值守的正式形态,授权前移到启动那一刻;教学版与真实产品的差距摊开就是一张进阶清单,补到哪一项,就回书里对应那一章再读一遍。
  • 差距不在机制的形状,而在工程的厚度;四层加载、按类型分级和逐条目的预算,厚度可以一项一项补起来。

全书尾声 ​

第 21 章走完,这本书也该收笔了。回头看第 1 章,写了多年代码的工程师刚开始用大语言模型,最不适应的不是新框架,而是旧直觉集体失灵,同一个问题问模型两遍,两次答案不同,测试写不出断言。当时立下过一句话,智能体开发的本质,是把凡事都要对的执念,换成错了也能补救的系统设计。二十一章走下来,这句话长成了一整套手艺。

全书故事的骨架,是第 2 章那几十行 while 循环撑起来的,模型决策、代码执行、结果回填,一圈接一圈地转。后面十九章都在给这条回路做加法,一处一处加固,函数调用补足行动能力,上下文工程管住预算,记忆接上跨会话的状态,多智能体带来分工。

从能跑到可靠,是第五部分的主线,评测给依据,安全给底线,可观测性盯住生产,人机协同定下人站在回路哪个位置。再往外,第 16 到 18 章把镜头拉到多模态、SDK 格局与整个生态;第 19 章把这些收拢成一套做决策的方法;第 20 章拿一份周报需求把方法走通;第 21 章则把这套方法拿到真实工业产品上合练。贯穿全书的只有一条线索,概率输出装进回路,工程师在回路外一圈又一圈地补确定性工程。

旧技能在这一路上并没有作废,HTTP、数据库、队列、CI/CD、代码评审,每一章都在反复用。智能体系统里九成代码仍是工程师熟悉的那种,概率性的部分只有中间那个决策点,学会管住它,其余都是老本行。书名的本意也在此,从工程师到智能体开发者,跨过的不是技术鸿沟,而是工程习惯的延伸,把要求从每次都对,换成把系统设计成错了也能补救。

接下来的路同样有章可循。练手不必把书再重读一遍,不如挑一个真实的小需求,回路、工具、上下文、评测四样,照第 20 章的次序各搭一遍,跑通一个小系统,比抄十遍笔记都值。设计上一时拿不准,19.2 的映射表和 19.4 的审查清单就是现成的检查单,动手前先过一遍;书里没写到的边界情况,多半也能在这两处找到最接近的问法。检查单翻熟了,这本书才算真正变成了自己的东西。

版本号和价格写在书里,迟早会过期。正文凡是时效数字都标了截至口径,核实日期收在章末参考资料段,接手旧项目一切以官方页面为准,这正是第 19 章纪律一的最后一课。也有不会过期的东西留在了纸上,回路的形状、评测的问句、放权的档位、选型的三条纪律。合上书,下一套系统从哪儿起步,答案第 2 章就写下了,从一个能跑的 while 循环开始,先让它转起来,再一层层打磨到值得托付。

参考资料 ​

  • Claude Code 官方文档(智能体回路与 harness 定位,三阶段口径,auto 模式分类器拦下高风险动作;内置工具五类(文件操作、搜索、执行、Web、代码智能),及技能、MCP、钩子、子智能体构成的扩展层;git 状态(当前分支、未提交改动、最近提交)列为模型直接可访问的环境内容),https://code.claude.com/docs/en/how-claude-code-works ,核实于 2026-08-28,2026-09-02 复核补五类工具与扩展层口径,2026-09-16 复核补环境状态访问口径。
  • Claude Code Agent SDK 文档(回路五步展开、max_turns 与 max_budget_usd、harness 定位),https://code.claude.com/docs/en/agent-sdk/agent-loop 、https://code.claude.com/docs/en/agent-sdk/overview ,核实于 2026-08-28。
  • Claude Code 官方文档(记忆:CLAUDE.md 四层加载、auto memory 200 行或 25KB、"context, not enforced configuration"),https://code.claude.com/docs/en/memory ,核实于 2026-08-28。
  • Claude Code 官方文档(子智能体:独立上下文、description 15,000 token 告警线、Explore 与 Plan 不加载 CLAUDE.md;文件定义:项目级 .claude/agents/ 与用户级 ~/.claude/agents/,frontmatter 写 name 与 description、可选 tools 等字段,正文即系统提示,按描述自动委派,建议提交进版本库团队共享),https://code.claude.com/docs/en/sub-agents ,核实于 2026-08-31。
  • Claude Code 官方文档(技能:正文仅在使用时加载、四层位置;disable-model-invocation——描述不进上下文、仅人可经斜杠命令调用;!command 动态注入——发送前执行 shell 命令并把输出内联进正文、disableSkillShellExecution 策略开关整体禁用),https://code.claude.com/docs/en/skills ,核实于 2026-08-28,2026-09-16 复核补调用开关与动态注入口径。
  • Claude Code 官方文档(钩子:三形态、三种节奏、matcher 与决策回流、处理器类型已扩五种;同一事件全部匹配钩子并行执行、多处设置定义同一处理器只跑一次、超时即取消且放行调用走正常权限流程;决策字段两套——PreToolUse 走 hookSpecificOutput.permissionDecision(allow/deny/ask/defer),Stop 走顶层 decision(只收 block,放行省略该字段)),https://code.claude.com/docs/en/hooks ,核实于 2026-08-28,2026-09-16 复核补并行、去重与超时口径,2026-09-17 复核补决策字段两套与 Stop decision 口径。
  • Claude Code 官方文档(权限:工具类型分级五行表、settings.local.json 与 v2.1.211 仓库根解析、auto 模式分类器;规则求值按 deny、ask、allow 次序、首匹配生效、宽 deny 遮蔽窄 allow、Bash 前缀 😗 只认结尾位置、设置可进版本库团队共享),https://code.claude.com/docs/en/permissions ,核实于 2026-08-28,2026-09-16 复核补规则求值、前缀与共享口径。
  • Claude Code 官方文档(上下文窗口:开机即载清单、压缩幸存表、/autocompact 与 /rewind),https://code.claude.com/docs/en/context-window ,核实于 2026-08-28。
  • Claude Code changelog(v2.1.211:always allow 规则改存仓库根,跨 worktree 持久),https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md ,核实于 2026-08-28。
  • Claude Code 官方文档(非交互模式:-p 启动、stdin 管道输入、JSON 输出与退出码、CI 与评审脚本用例;无人值守授权:--allowedTools 按权限规则语法白名单式放行(结尾 * 前缀匹配)、--bare 跳过钩子/技能/自定义命令/子智能体/插件/MCP 服务器/auto memory/CLAUDE.md 的自动发现、不读则不跑,不信任目录下防项目钩子被动手脚、--permission-prompts none 无人应答时拒绝而非等待),https://code.claude.com/docs/en/headless ,核实于 2026-09-02,2026-09-17 复核补无人值守授权口径。
  • Claude Code 官方文档(工具参考:Edit 工具做精确字符串替换——old_string 与 new_string、不用正则与模糊匹配、先读后改与唯一性检查、replace_all),https://code.claude.com/docs/en/tools-reference ,核实于 2026-09-16。
  • Claude Code Agent SDK 文档(钩子输出协议:PreToolUse 的 permissionDecision 四值 allow/deny/ask/defer、updatedInput 改写工具入参、PostToolUse 的 additionalContext 附进工具结果),https://code.claude.com/docs/en/agent-sdk/hooks ,核实于 2026-09-16。
  • Claude Code 官方文档(钩子指南:additionalContext 回喂的内容以系统提醒(system-reminder)注入模型上下文),https://code.claude.com/docs/en/hooks-guide ,核实于 2026-09-16。
  • Claude Code 官方 changelog(v2.1.269:压缩之后重新告知模型的 git 状态,由会话开始时的旧状态改为当前状态),https://code.claude.com/docs/en/changelog ,核实于 2026-09-16。
  • OpenAI API 文档(apply_patch 工具:模型给带上下文行的 V4A 结构化差异、按内容匹配定位而非行号、失配返回 Invalid Context 由模型调整重试),https://developers.openai.com/api/docs/guides/tools-apply-patch ,核实于 2026-09-16。
  • Cursor 官方博客(Instant Apply:模型的编辑改动以查找替换块提出,不走标准 diff 格式),https://cursor.com/blog/instant-apply ,核实于 2026-09-16。
  • DeepSeek API 文档(OpenAI 兼容端点与模型名 deepseek-v4-flash,转引第 2 章已核实条目),https://api-docs.deepseek.com/ ,核实于 2026-08。
  • AGENTS.md 开放格式官网(仓库根目录规则文件的跨工具约定,转引第 4 章 4.6 节已核实条目),https://agents.md/ ,核实于 2026-08。
  • Agent Skills 开放标准官网(SKILL.md 跨工具格式:Anthropic 2025 年 10 月推出,2025 年 12 月开放标准化,截至 2026 年 8 月二十余种工具可读;技能目录 scripts/references/assets 常见布局),https://agentskills.io/ ,核实于 2026-08-31,2026-09-16 复核补目录布局。
  • Jane Luo 等《RPG: A Repository Planning Graph for Unified and Scalable Codebase Generation》(微软,ICLR 2026;ZeroRepo 沿图生成仓库,RepoCraft 基准上平均约 3.6 万行代码、比最强基线大 3.9 倍、功能覆盖 81.5%,数字为论文自报口径),https://arxiv.org/abs/2509.16198 ,核实于 2026-08-31。
  • Lulla 等(ICSE 2026 JAWs 论文):10 个仓库 124 个真实 PR 的配对实验,有上下文文件时中位耗时降 28.64%、输出 token 降 16.58%,测的是耗时与 token、不是任务成功率,arXiv:2601.20404,https://arxiv.org/abs/2601.20404 ,核实于 2026-09。
  • Gloaguen 等(ETH Zurich):任务成功率基本无提升,人写的上下文文件平均约 +2.4%、LLM 自动生成的两个基准分别约 −0.5% 与 −2% 且推理成本升约 20% 到 23%,文件长度与指令类别对表现无显著影响(消融结论),arXiv:2602.11988,https://arxiv.org/abs/2602.11988 ,核实于 2026-09。
  • 《Agentic Engineering》(Addy Osmani,O'Reilly Early Release,2026-08-28 首发,实体 5 章)第 3 章(harness 不会缩水只会搬家、新模型上线当周退役两个上下文监控智能体的亲历、外壳缺口归自己与模型缺口归厂商路线图的分工;Ralph 外循环——计划与进度落盘、每轮全新会话、队列有活不许停,社区实践口径,Geoffrey Huntley 与 Ryan Carson 推广;错误变规则的演化纪律——只在真实失败后加约束、只在更强模型使其冗余后删约束、两个团队握不同规则库是特性)、第 4 章(AGENTS.md 每行由真实失败换来、自动生成文件是负资产、两门学科研究的转述、粉红大象锚定与正向指令优于禁令式警告)。见 txt/agentic-engineering.txt(英文原文,第 3、4 章,行 1166-1168、1296-1348、1443-1459、1618-1715、1865-1889)。
  • 《The Claude Code Operating Model》(2026)第 2 章(.claude/rules/ 条件规则:文件头 paths 声明 Glob 模式、命中才注入上下文)、第 3 章(description 共享字符预算约上下文 1%、按使用频率降级、名字常驻;SKILL.md 是路由器不是仓库;公式该停手做成脚本;触发评测:10-20 例测试集、触发率 90% 与误触发 5% 的经验线,书中自定口径)、第 5 章(处理器三型确定性阶梯 command > prompt > agent、退出码 0/2/其他三分、故意阻断与脚本故障的区分)。见 txt/claude-code-operating-model.txt(英文原文,第 2、3、5 章,行 2225-2345、2945-2975、3095-3145、3230-3320、3865-3880、5682-5760、6456-6463)。
  • 《The Claude Code Operating Model》(2026)第 3 章(参考型对任务型技能两分、最坏情形测试;!command 动态上下文注入省下首轮信息收集调用——书中口径)、第 4 章(技能×子智能体组合两方向:子智能体经 skills 字段预载知识包、技能经 context: fork 派隔离子智能体,分界在系统提示归属)、第 5 章(permissionDecision 四值、updatedInput 参数改写、additionalContext 回喂 lint 结果促成自动修复、Stop 事件测试质量门与 stop_hook_active 防死循环)、第 7 章(非交互模式 JSON 结果信封 total_cost_usd/num_turns/session_id、stream-json 的 init 与 compact_boundary 事件)。见 txt/claude-code-operating-model.txt(英文原文,行 3142-3230、3456-3510、4774-4830、5475-5500、5772-5800、6036-6175、7430-7460、8028-8045)。
  • 《Claude Code Architect》(Andrei Media,2026)第 5 章(技能触发排障环:不触发时问模型原因、按回答补描述、重启再试)、第 7 章(技能目录 scripts/references/assets 布局)、第 15 章(钩子排障四步链:装载、触发、成功、生效,每步一次验证;终端直喂事件 JSON 验退出码)、第 16 章(改后自动跑测试的守卫:失败回 block 决策逼模型继续修、只跑与改动相关的测试防慢)。见 txt/claude-code-architect.txt(英文原文,行 1495-1530、1920-1935、4630-4665、4920-4945)。译文见 txt-ref-zh/claude-code-architect/。
  • 《Claude Code: The Definitive Guide to Agentic Development》(2026)第 2 章(权限规则三段求值 deny→ask→allow 与首匹配生效、deny 写窄 allow 写宽、Bash 前缀匹配语法;钩子以完整用户权限运行无沙箱;项目设置进版本库团队共享;异步钩子与超时)、第 3 章(CLAUDE.md 的 @ 路径引入:主文件当索引、专题内容拆出去)、第 6 章(非交互输出三档 text/json/stream-json、stream-json 供管线消费、cat 管道接 claude -p 的用法)。见 txt/claude-code-definitive.txt(英文原文,行 926-975、1066-1093、1129-1131、1398-1406、2907-2958)。译文见 txt-ref-zh/claude-code-definitive/。
  • 《Agentic GraphRAG》(Anthony Alcaraz,O'Reilly 2026)第 6 章(配置分岔的三层治理:配置即代码(团队规模)、共享知识层(部门规模)、治理控制平面(企业规模))、第 7 章(技能作为自改进对象:执行记录与成功率、按实证表现路由而非描述相似度)。译文见 txt-ref-zh/agentic-graphrag/。
  • 《The Bible of Agentic AI Systems》(Stefanos Drakos,自出版 2026)第 18 章(技能优先执行:先搜技能,没有就现学现解,干完把解法沉淀成新技能)。见 txt/agentic-ai-bible.txt(英文原文,行 6045-6075)。