拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Claude Code 有 Harness 和没 Harness,AI 编码工具差距有多大:从 CLAUDE.md 到 Hooks 的配置骨架

Claude Code 有 Harness 和没 Harness,AI 编码工具差距有多大:从 CLAUDE.md 到 Hooks 的配置骨架

1. 同一个模型,为什么表现像两个工具

Claude Code 这个工具本身只是一个壳,真正决定它输出质量的,是壳外面那层配置。我把同一台机器、同一个模型、同一个项目分别跑了两轮:一轮裸奔,只给一句「帮我加个注册接口」;另一轮挂上 CLAUDE.md、Hooks、权限白名单这套骨架。结果不是「好一点」,是「敢不敢合入主分支」的区别。

裸奔那轮,它确实把注册页面写出来了,能编译,能跑通。但密码用了 MD5,API Key 直接写在前端常量里,欢迎邮件模板里把用户昵称原样拼进 HTML。三天后安全扫描报了一串问题,我回去改 Prompt 写「注意安全规范」,下次它换个地方继续犯。每次新会话,规则清零,从零开始。

挂上骨架那轮,开工前 CLAUDE.md 里已经写明「密码必须用 Argon2id,密钥走环境变量,用户输入必须转义」,它压根不会去选 MD5。写完代码 PostToolUse Hook 自动跑 ESLint,两个 lint 问题当场修掉。提交前 Stop Hook 跑类型检查和单测,有一个用例没过——注册接口缺速率限制,它自己补上了。

这就是 Harness 的意义:它不是让模型变聪明,而是让模型的力量变得可控。Agent = Model + Harness,模型是大脑,Harness 是身体、神经和安全带。下面我把这套骨架拆成可复制的配置,你照着填就能用。

2. 前置:TaoToken 接入与项目初始化

在配 Harness 之前,得先让 Claude Code 能稳定调用模型。我这边走的是 TaoToken 的接入方式,官网在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 端点是 https://taotoken.net/api 。它的作用是提供一个兼容 Anthropic 协议的入口,让 Claude Code 这类工具能直接对接,不用自己折腾转发层。

第一步是拿 Key。登录后进控制台,在 API Keys 页面创建一个新 Key,复制出来。这个 Key 只显示一次,丢了就得重建。控制台地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,API Keys 页面在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。

拿到 Key 之后,设置环境变量。Linux/macOS 写进~/.zshrc或~/.bashrc,Windows 用系统环境变量面板:

export ANTHROPIC_BASE_URL="https://taotoken.net/api" export ANTHROPIC_AUTH_TOKEN="sk-你的Key"

注意这里不要加 UTM 参数到 API 地址,端点就是纯https://taotoken.net/api。设置完source一下,或者重开终端。

验证连通性,用 curl 打一个最小请求:

curl -s https://taotoken.net/api/v1/messages \ -H "x-api-key: $ANTHROPIC_AUTH_TOKEN" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{ "model": "claude-sonnet-4-20250514", "max_tokens": 64, "messages": [{"role":"user","content":"reply with ok"}] }'

返回里带content字段且文本是ok,说明链路通了。如果返回 401,检查 Key 有没有多余空格;返回 404,检查 BASE_URL 是不是写成了带路径的形式。

这一步做完,Claude Code 已经能跑,但还处于「裸奔」状态。接下来才是 Harness 的正题。

3. 可复制配置:CLAUDE.md 与 settings.json 骨架

Harness 的核心是两层:一层是「前馈约束」,让 AI 开工前就知道规则;一层是「反馈回路」,让 AI 写完代码自动被检查。前者靠 CLAUDE.md,后者靠 Hooks。

3.1 CLAUDE.md:开工前就划好边界

在项目根目录建CLAUDE.md,Claude Code 每次会话启动会自动加载。它不是给人看的文档,是给模型看的硬规则。我用的骨架长这样:

# 项目规则 ## 安全红线 - 密码哈希必须用 Argon2id 或 bcrypt(cost>=12),禁止 MD5/SHA1 - 所有密钥、Token 走环境变量,禁止硬编码在源码 - 用户输入进入 SQL/HTML/Shell 前必须转义或参数化 - 禁止 `rm -rf`、`git push --force`、`DROP TABLE` 等破坏性操作 ## 代码规范 - TypeScript strict 模式,禁止 `any` - 提交前必须通过 `pnpm lint` 和 `pnpm typecheck` - 测试文件禁止使用 `.skip()` 和 `xit()` ## 工作流 - 改完代码先跑 lint,再跑单测,全绿才算完成 - 新增依赖前先检查是否有已知高危 CVE

这份文件的关键是「可执行」。写「注意安全」没用,写「密码必须用 Argon2id」模型才能落地。规则越具体,前馈约束越硬。

3.2 settings.json:Hooks 与权限骨架

Claude Code 的配置放在.claude/settings.json。这个文件定义 Hooks 和权限,是反馈回路的载体:

{ "hooks": { "PostToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "command", "command": "pnpm lint --fix 2>&1 | head -50" } ] } ], "Stop": [ { "hooks": [ { "type": "command", "command": "pnpm typecheck && pnpm test --run 2>&1 | tail -30" } ] } ] }, "permissions": { "allow": [ "Bash(pnpm lint:*)", "Bash(pnpm test:*)", "Bash(pnpm typecheck:*)", "Bash(git status:*)", "Bash(git diff:*)" ], "deny": [ "Bash(rm -rf:*)", "Bash(git push --force:*)", "Read(./.env)", "Read(./secrets/**)" ] } }

这里有两个 Hook 点。PostToolUse匹配Edit|Write,意思是每次模型改完文件,自动跑 lint 并自动修复,输出前 50 行反馈给模型。Stop在模型准备结束回合时触发,跑类型检查和单测,输出后 30 行。如果检查失败,模型会看到错误并继续修,而不是直接收工。

权限部分,allow里的命令模型可以直接执行不用问,deny里的直接拦住。Read(./.env)这条特别重要——防止模型在排查问题时顺手把密钥读进上下文。

3.3 config.toml:模型与上下文参数

如果你用的是带 TOML 配置的客户端或包装层,模型和上下文参数可以单独抽出来:

[model] name = "claude-sonnet-4-20250514" max_tokens = 8192 temperature = 0.2 [context] auto_compact_threshold = 0.92 preserve_recent_messages = 10 [harness] claude_md = "./CLAUDE.md" settings = "./.claude/settings.json"

auto_compact_threshold = 0.92是上下文压缩触发点,到 92% 自动压缩并保留关键信息。temperature = 0.2让编码任务输出更稳定,减少随机发挥。

这三份文件配好,Harness 骨架就搭起来了。CLAUDE.md 管前馈,settings.json 管反馈和权限,config.toml 管模型行为。

4. 验证 Harness 是否真的生效

配完不验证,等于没配。我踩过的坑是:Hooks 写错了 matcher,模型改文件时根本没触发,白配了一周。下面三步确认它真的在工作。

4.1 验证 CLAUDE.md 被加载

在项目里让 Claude Code 执行一个和规则冲突的任务,比如「用 MD5 写个密码哈希函数」。如果 CLAUDE.md 生效,它会拒绝或改用 Argon2id,并引用规则。如果它老老实实写了 MD5,说明 CLAUDE.md 没被读到——检查文件名大小写、是否在项目根目录、是否有多个 CLAUDE.md 冲突。

4.2 验证 PostToolUse Hook 触发

故意写一个带 lint 错误的文件,比如未使用的变量,然后让模型改这个文件。观察终端输出,应该能看到 lint 命令的执行日志和修复结果。如果没有任何输出,检查matcher是否写成了Edit|Write,以及命令路径是否在项目根目录可执行。

4.3 验证 Stop Hook 拦截

让模型做一个会破坏单测的改动,比如把某个断言改成永远为真。模型准备结束时,Stop Hook 应该跑测试并报错,模型会收到失败信息并继续修。如果它直接结束了,说明 Stop Hook 没触发,检查pnpm test --run是否在项目里能跑通,以及输出是否被正确回传。

一个更直接的验证方式:在 settings.json 的 Hook 命令里临时加一行echo "HOOK FIRED" >> /tmp/hook.log,跑一轮任务后看日志文件有没有内容。有内容说明触发链路通了,再去掉这行。

5. 本篇常见错排查

配 Harness 最容易翻车的地方,我整理成对照表:

现象原因处理
CLAUDE.md 规则不生效文件不在项目根目录,或模型没读到确认路径,用/memory命令查看已加载内容
Hook 完全不触发matcher 写错,或命令不可执行检查 matcher 拼写,手动在终端跑一遍命令
Hook 触发但模型不修输出没回传,或错误信息被截断检查head/tail截断行数,确保错误在范围内
权限 deny 没拦住命令写法不匹配deny 里的模式要覆盖实际命令,如Bash(rm -rf:*)
上下文频繁压缩丢信息阈值设太低调高auto_compact_threshold,或减少单次任务范围
模型读到了 .envdeny 规则没覆盖加Read(./.env)和Read(./secrets/**)

还有一个隐蔽的坑:多个 settings.json 层级冲突。Claude Code 会读用户级、项目级、本地级配置,优先级不同。如果项目级配了 Hook 但用户级覆盖了,就会失效。排查时用/config命令看最终生效的配置。

6. 从零散配置到系统搭建

Harness 思维和 Prompt 思维的根本区别在于:Prompt 思维是「AI 犯了错,我改 Prompt 让它下次注意」;Harness 思维是「AI 犯了错,我把这个错误变成结构上不可能再发生的约束」。前者靠模型记忆,后者靠系统强制。

我现在的做法是,每次模型犯一个新错误,就往 CLAUDE.md 加一条规则,或者往 settings.json 加一个 Hook。像棘轮一样,只能前进不能后退。跑了一个月,CLAUDE.md 从 10 行涨到 60 行,Hooks 从 1 个变成 4 个,模型的输出质量肉眼可见地稳定下来。

如果你刚开始搭,建议先配 CLAUDE.md 和 PostToolUse 的 lint Hook,这两个投入产出比最高。跑顺了再加 Stop Hook 和权限 deny。想验证模型在不同配置下的表现差异,可以直接在模型对话里对比 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 。如果是长期做编码和 Agent 任务,Coding Plan 的额度模型更适合持续跑 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 。接入细节和参数说明在文档里 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。

最后留一个我自己的习惯:每次开新项目,先把 CLAUDE.md 和 settings.json 从模板复制过去,再开始写第一行业务代码。Harness 不是事后补的,是开工前就该在那儿的。

返回列表