1. 从一次误删事故说起:Agent Loop 与 Permission System 到底在防什么
很多人第一次用 Claude Code 这类编码 Agent,都会经历一个心理转折:前十分钟觉得它像个听话的实习生,半小时后开始担心它像个过于热心的同事——你只是让它「清理一下构建产物」,它可能顺手把dist、.cache甚至某个还没提交的临时目录一起删了。这不是模型笨,恰恰相反,是它太想帮你把任务做完。
Claude Code 的核心检索词就三个:Agent Loop(智能体循环)、Permission System(权限系统)、沙箱(Sandbox)。它们分别回答三个问题:Agent 怎么持续干活、谁来决定某个动作能不能执行、执行时怎么保证出错也不炸。适合谁看?适合已经用过 Claude Code、Cline、Codex 这类工具,想搞清楚「为什么它要这么设计」并且想把这套思路落地到自己项目里的开发者。
我试过把 Claude Code 的架构拆开看,会发现它和早期 LangChain 那种 Workflow 路线是两种哲学。Workflow 是「你画好流程图,模型在节点间流转」,好处是可控,坏处也是可控——所有可能性都被你框死了。而 Agent Loop 走的是 ReAct 模式,本质是一个 while 循环:模型推理出下一步动作,框架执行工具,把结果喂回模型,再推理,直到任务完成。它把「怎么走」的决定权交给了模型,人只负责给目标和边界。
但高自由度必然带来风险。于是 Claude Code 做了决策与执行分离:模型只负责「决定调用哪个工具、传什么参数」,真正执行的是框架本身,而执行前必须过 Permission System 这一关。这一关的优先级是 Deny > Ask > Allow,也就是 Deny-First。为什么不是 Allow-First?因为安全默认值必须是「不确定就拦」。Anthropic 有个统计很说明问题:当 Claude Code 弹窗问用户「是否允许执行这个工具调用」时,93% 的回答都是 Allow。这意味着人根本不会仔细看,弹窗形同虚设。所以真正靠得住的不是「问用户」,而是沙箱和 worktree 这种隔离机制——即使用户手滑点了允许,损失也被限制在可控范围内。
理解了这三点,你才能理解为什么 CLAUDE.md 是明文存储、为什么权限规则要写成 JSON、为什么沙箱不是可选项。下面我会从配置落地讲起,把 Agent Loop 的循环控制、Permission System 的规则写法、沙箱的边界验证一步步拆开,最后给你一份可以直接抄的 CLAUDE.md 和权限配置。
2. TaoToken 前置准备:把 Base URL、Key、Model ID 三件套配齐
在讲 Claude Code 的架构落地之前,得先有一个能稳定调用的模型入口。Claude Code 本身是个客户端框架,它需要一个兼容 Anthropic API 的服务端来跑 Agent Loop。这里我用 TaoToken 作为接入层,因为它同时提供模型对话、Coding Plan 和 API Keys 管理,适合做这种需要反复调试的 Agent 场景。
先说清楚三件套,这是后面所有配置的基础,缺一不可:
- Base URL:
https://taotoken.net/api,注意这个地址不带任何查询参数,是纯 API 根路径。 - API Key:在控制台的 API Keys 页面生成,格式通常是一串以
sk-开头的字符串。 - Model ID:比如
claude-sonnet-4-5这类具体模型标识,要和你在 Coding Plan 里选的模型一致。
为什么强调「三件套要写全」?因为 Claude Code、Cline、Codex 这些工具在配置时,任何一个缺失都会导致请求失败,而且报错信息往往很含糊。比如只填了 Base URL 没填 Model ID,可能报reading choices之类的解析错误;Key 写错则直接 401。把这三个当成一个整体来配,能省掉大量排查时间。
具体操作路径是这样的:先到控制台生成 API Key,然后确认你要用的 Model ID,最后把 Base URL 填进 Claude Code 的配置。如果你用的是 Claude Code 的 settings 文件,配置大概长这样:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_API_KEY": "sk-你的key", "ANTHROPIC_MODEL": "claude-sonnet-4-5" } }如果你用的是 Codex 的auth.json,结构会不一样,但三件套的逻辑相同:
{ "base_url": "https://taotoken.net/api", "api_key": "sk-你的key", "model": "claude-sonnet-4-5" }这里有个容易踩的坑:Base URL 末尾不要多加/v1或/chat/completions,不同客户端对路径拼接的处理不一样,多写反而会 404。TaoToken 的 API 根路径就是https://taotoken.net/api,客户端会自己拼后续路径。
配好之后,建议先用模型对话页面做一次最小验证,确认 Key 和 Model ID 是通的,再去跑 Claude Code 的 Agent Loop。因为 Agent Loop 会连续发多次请求,如果基础调用都不通,循环里会疯狂报错,很难定位是配置问题还是权限问题。
另外,如果你打算长期跑编码任务,Coding Plan 会比按次调用更划算,而且它和 API Keys 是打通的,切换时不用改 Base URL。对于要反复调试 Permission System 规则的场景,这点很重要——你会需要大量试错。
3. 可复制配置:CLAUDE.md 与权限规则 JSON 怎么写
这一节是全文的核心,直接给你能抄的配置。Claude Code 的 Agent Loop 之所以能安全地跑,靠的是两层约束:一层是 CLAUDE.md 里的行为约定,另一层是 Permission System 的规则文件。前者告诉模型「你应该怎么做」,后者告诉框架「什么能执行、什么要问、什么直接拒」。
先说 CLAUDE.md。它的定位是「项目级记忆」,明文存储,模型每次启动都会读。所以它既是给模型看的说明书,也是你审计 Agent 行为的入口。一份实用的 CLAUDE.md 应该包含:项目结构说明、常用命令、禁止事项、以及权限相关的约定。下面这份可以直接改:
# 项目约定 ## 项目结构 - 源码在 src/,测试在 tests/,构建产物在 dist/ - 配置文件在 config/,不要手动改 dist/ 下的任何文件 ## 常用命令 - 安装依赖:npm install - 跑测试:npm test - 构建:npm run build - 本地启动:npm run dev ## 禁止事项 - 不要执行 rm -rf,不要删除 dist/ 以外的目录 - 不要修改 .env、.git/config、package-lock.json - 不要执行 git push、git reset --hard - 不要访问项目目录以外的路径 ## 权限约定 - 读文件、跑测试:可以直接执行 - 写文件、装依赖:需要确认 - 删除文件、改 git 历史:默认拒绝这份文件的关键在于「禁止事项」和「权限约定」两段。它们不是硬约束,而是给模型的行为提示,真正兜底的是 Permission System。但两者配合起来效果最好:模型看到 CLAUDE.md 会主动避开危险操作,减少弹窗次数;而权限规则则在框架层拦截漏网之鱼。
接下来是权限规则。Claude Code 的权限配置通常放在 settings 文件里,用 JSON 描述。核心是三个数组:deny、ask、allow,优先级从高到低。写法如下:
{ "permissions": { "deny": [ "Bash(rm -rf:*)", "Bash(git push:*)", "Bash(git reset --hard:*)", "Write(.env)", "Write(.git/**)" ], "ask": [ "Bash(npm install:*)", "Bash(npm run build:*)", "Write(src/**)", "Edit(src/**)" ], "allow": [ "Read(**)", "Bash(npm test:*)", "Bash(git status:*)", "Bash(git diff:*)" ] } }这里的设计意图很明确:deny放最危险的操作,直接拒绝,连问都不问;ask放有副作用但常见的操作,弹窗让用户确认;allow放只读或低风险操作,全程放行。注意deny的优先级最高,即使某个操作同时匹配allow和deny,也会被拒绝。这就是 Deny-First 的落地方式。
有个细节值得说:Bash(rm -rf:*)这种写法里的:*是通配符,表示匹配以rm -rf开头的所有命令。如果你只写Bash(rm -rf),可能匹配不到带参数的变体。所以规则要写得稍微宽一点,宁可多拦不可漏拦。
再配合沙箱使用。Claude Code 支持在沙箱环境里执行工具调用,比如用 worktree 隔离出一个临时工作目录,Agent 在里面怎么折腾都不影响主仓库。配置沙箱通常需要在启动时加参数,或者在 settings 里指定:
{ "sandbox": { "enabled": true, "worktree": true, "allowedPaths": ["src", "tests", "config"], "deniedPaths": ["dist", ".git", "node_modules"] } }这样即使 Permission System 被绕过,或者用户手滑点了 Allow,Agent 也只能在allowedPaths里活动,deniedPaths里的东西碰不到。这就是「人也会犯错」的兜底。
把 CLAUDE.md、权限 JSON、沙箱配置三样配齐,你的 Agent Loop 才算真正可控。下面讲怎么验证这套配置生效。
4. 验证请求与成功结果:跑一次 Agent Loop 看权限拦截
配置写完不验证,等于没配。这一节给你一套可复现的验证步骤,确认 Agent Loop 在跑、Permission System 在拦、沙箱在隔离。
第一步,先确认基础调用通。用 curl 直接打 TaoToken 的 API,验证 Key 和 Model ID:
curl https://taotoken.net/api/v1/messages \ -H "x-api-key: sk-你的key" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{ "model": "claude-sonnet-4-5", "max_tokens": 128, "messages": [{"role": "user", "content": "回复 ok"}] }'如果返回里有正常的content字段,说明三件套没问题。如果报 401,检查 Key;如果报模型不存在,检查 Model ID;如果报路径错误,检查 Base URL 是不是多写了后缀。
第二步,启动 Claude Code,让它做一个「应该被拦截」的操作。比如在对话里输入:「帮我删除 dist 目录下的所有文件」。观察它的行为:
- 如果配置生效,它应该先尝试调用 Bash 工具,然后被 Permission System 拦截,弹出确认或直接拒绝。
- 如果它直接执行了,说明
deny规则没生效,检查 JSON 格式和路径匹配。
第三步,验证沙箱隔离。让 Agent 尝试写一个allowedPaths之外的文件,比如/tmp/test.txt或项目根目录的secret.txt。如果沙箱生效,这个操作应该失败,报错类似path not allowed或sandbox violation。
第四步,看 Agent Loop 的循环控制。给它一个多步任务,比如「跑测试,如果有失败就修复,然后重新跑」。观察它是否在循环:跑测试 → 读报错 → 改代码 → 再跑测试。如果它跑一次就停,说明循环没起来,可能是模型没返回工具调用,或者框架没把结果喂回去。
成功的结果应该长这样:Agent 连续执行多个工具调用,每次调用前权限系统按规则放行或拦截,沙箱限制路径,最终任务完成。你可以在会话记录里看到完整的调用链,因为 Claude Code 是明文存储会话的,每一步都可追溯。
这里有个实测经验:如果 Agent Loop 卡住不动,先看是不是某个工具调用在等用户确认(Ask 规则触发),而你没注意到弹窗。Claude Code 的弹窗有时候会被终端输出淹没,建议把终端窗口拉大一点。
验证通过后,你就有了一个可控的 Agent 环境。接下来讲常见报错怎么排查。
5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth
配置 Agent 环境时,报错信息往往比代码还难懂。这一节把最常见的几类错误和对应解法列出来,都是真实会遇到的。
401 Unauthorized。这是最直接的,Key 不对或没传。检查三处:API Key 是不是复制完整(有时候会漏掉末尾字符)、请求头字段名对不对(Anthropic 用x-api-key,OpenAI 兼容用Authorization: Bearer)、Key 有没有过期。如果用的是 TaoToken,到控制台的 API Keys 页面重新生成一个,替换掉配置里的旧值。
local proxy failed。这个报错通常出现在客户端尝试走本地代理但连不上时。先确认你的 Base URL 是不是写成了http://localhost:xxxx之类的本地地址。如果是,改成https://taotoken.net/api。另外检查环境变量里有没有残留的HTTP_PROXY、HTTPS_PROXY,这些会干扰请求。清掉再试。
reading choices 相关报错。这类错误一般出现在解析响应时,比如cannot read property 'choices' of undefined。原因是客户端按 OpenAI 格式解析,但服务端返回的是 Anthropic 格式,或者反过来。检查你的客户端配置里,API 格式选的是不是和 Base URL 匹配。TaoToken 同时支持两种格式,但路径不同,Anthropic 格式走/v1/messages,OpenAI 格式走/v1/chat/completions。配错了就会解析失败。
OAuth 相关报错。如果你用的是需要 OAuth 登录的客户端(比如某些版本的 Claude Code),可能会遇到 token 刷新失败。这种情况通常是 OAuth 配置和 API Key 混用了。建议统一用 API Key 方式,在 settings 里明确写ANTHROPIC_API_KEY,不要同时开 OAuth。如果必须用 OAuth,确认回调地址和客户端配置一致。
权限规则不生效。检查 JSON 格式:permissions下面的deny、ask、allow必须是数组,每项是字符串。路径匹配要注意大小写和通配符。比如Write(src/**)匹配src下所有文件,但Write(src/*)只匹配一层。改完规则后重启 Claude Code,因为权限配置通常在启动时加载。
沙箱报 path not allowed。说明 Agent 尝试访问allowedPaths之外的位置。要么把路径加进白名单,要么调整任务让它别越界。注意deniedPaths优先级高于allowedPaths,如果某个路径同时出现在两边,会被拒绝。
Agent Loop 不循环。如果 Agent 执行一次工具就停,检查模型返回里有没有tool_use类型的 content。如果没有,可能是模型没理解任务,或者 max_tokens 太小导致输出被截断。把 max_tokens 调大,或者在 CLAUDE.md 里明确要求「完成任务前不要停止」。
排查的核心思路是:先确认基础调用通(curl 验证),再确认权限规则加载(看启动日志),最后确认沙箱边界(故意越界测试)。一层层往下查,比瞎改配置快得多。
6. 把架构思路落地到自有项目:从 CLAUDE.md 到权限边界
理解了 Claude Code 的 Agent Loop、Permission System 和沙箱设计,最终目的是把这套思路用到自己的项目里。不管你是做内部工具、还是给团队搭编码 Agent,这几个原则可以直接迁移。
第一,决策与执行分离。你的 Agent 框架里,模型只负责输出「要调用什么工具、传什么参数」,真正执行工具的是框架代码,而且执行前必须过权限层。这样即使模型幻觉,也不会直接造成破坏。实现上就是一个中间件:模型返回 tool_use → 权限检查 → 通过则执行 → 结果回传。
第二,Deny-First 的权限默认值。不要设计成「默认允许,危险操作才拦」,而要设计成「默认拒绝,明确允许才放行」。因为用户对弹窗的注意力极低,93% 的 Allow 率说明「问用户」不是安全机制。真正的安全机制是默认拒绝加沙箱隔离。
第三,明文可审计。Claude Code 把会话和记忆明文存储,这个选择很关键。它让 Agent 的每一步都可追溯、可修改。你的项目里也可以用类似方式:把 Agent 的决策日志、工具调用记录写成明文文件,方便事后审计和调试。向量数据库适合检索,但不适合审计,两者可以并存。
第四,CLAUDE.md 作为行为契约。它不只是给模型看的提示词,更是团队约定的载体。把项目结构、常用命令、禁止事项写进去,新人和 Agent 都能快速上手。而且它是版本控制的,改动能被 review。
具体落地时,你可以先从一个最小闭环开始:一份 CLAUDE.md + 一份权限 JSON + 一个沙箱配置。跑通之后再逐步细化规则。比如先只配deny和allow,观察哪些操作需要ask,再补进去。规则不是一次写好的,是跑出来的。
如果你要长期跑编码任务,建议用 Coding Plan 配合 API Keys,这样调试权限规则时不用担心调用成本。模型对话页面可以用来快速验证单个工具调用的行为,接入文档里有完整的路径和参数说明。把这套配置跑顺之后,你会发现 Agent 不再是「不可控的黑盒」,而是一个边界清晰、可审计、可回滚的协作工具。