花一小时搭好 Claude Code Harness,AI 写代码质量立竿见影
你装了 Claude Code,切了 Opus 4.7,同一个项目、同一套代码,为什么同事的产出比你高一个级别?答案大概率不在模型上。
模型决定上限,Harness 决定你实际能拿到多少
Claude Code 跑起来之后,实际表现由两样东西决定:模型本身的能力,和包裹在模型外面的那层配置——CLAUDE.md、Hooks、Skills、LSP、MCP、Sub-agents,统称 Harness。
打个比方:模型是一台 V8 发动机,Harness 是底盘、变速箱、方向盘和导航。发动机再好,装在没有方向盘的车上也跑不起来。现实是,绝大多数人花了几千块钱切到最强模型,然后把它架在一个空壳子里裸跑——Harness 什么都没配,上下文里除了 prompt 什么都没有。
半年实操下来,最深的一个体会是:花一小时把 Harness 搭好,回报率远高于换一个更好的模型。因为模型升级带来的收益是线性的——换好一点的模型,代码质量好一点。但 Harness 带来的收益是指数级的——每加一层配置(CLAUDE.md、Hook、Skill、LSP),每个回合的产出都在叠乘。更关键的是,Harness 会复利。你今天写的一条 CLAUDE.md 规则,会在未来每一次对话中生效;你今天写的一个 Skill,会在未来每一个相关任务中被精准激活。
Harness 的五个核心组件
一个配好的 Harness 包含五层,从近到远分别解决不同的问题:
- CLAUDE.md:常驻上下文的全局规则。AI 每次回复都看得见,所以必须精炼——只写”AI 需要在你开口之前就知道的事”。
- Hooks:生命周期自动触发器。SessionStart 把 git 状态自动注入上下文,Stop 在对话结束后自动跑审查。
- Skills:延迟加载的工作流模板。按需激活,把”新建 API 路由要跑 8 个步骤”这类流程写成可复用的指令。
- LSP:语言服务器协议。让 AI 的代码搜索从字符串匹配升级到符号级——同一个变量名,LSP 给出的是定义和引用,而不是几百条 grep 结果。
- Sub-agents:拥有独立上下文窗口的隔离实例。派去探路、调研、审计——不污染主会话的上下文。
这五层不是并列关系,而是递进关系。CLAUDE.md 建立基线规则,Hooks 自动化环境感知,Skills 封装可复用流程,LSP 提升代码理解精度,Sub-agents 拓展上下文边界。按这个顺序一层一层搭,每一步都在放大前面几步的价值。
什么场景下该搭 Harness
如果你的日常是写两三个文件的独立脚本,Harness 的回报不明显——一个 prompt 就能搞定的事,不需要 CLADE.md。
但如果你在以下任何一种场景,Harness 就是刚需:
- 维护 5 万行以上的代码库:AI 需要在大量文件之间导航、理解模块依赖,没有 Harness 的引导,每个回合都要从头探索。
- 团队共用 Claude Code:不同成员对代码规范的理解不一致,CLAUDE.md 和 Skills 把约定固化下来,避免每一次都靠聊天口述。
- 频繁执行固定工作流:比如新建 API 路由、写数据库迁移、发版流程——做一次的事情不需要 Skill,每周都在做的事情需要。
- 跨模块重构:改动涉及多个不熟悉的目录。没有 Sub-agent 探路,主会话的上下文一大半都浪费在读文件理解”这代码在干什么”上。
反过来说,如果你只是在做原型验证,需求每个小时都在变,Harness 的维护成本会超过收益。Harness 适合的是一个相对稳定的项目,你会在上面持续工作至少几个月——这种场景下,每一分钟花在配置上的时间,未来都会数十倍地折回。
搭 Harness 常踩的三个坑
第一个坑:把 CLAUDE.md 当项目文档写。 刚用的时候很容易往里塞技术选型理由、团队名单、历史决策记录,洋洋洒洒三百行。结果是 AI 每个回合都在为这些废话付费——上下文窗口一大半被吃掉,留给真正的代码理解和生成空间反而少了。后来砍到 20 行,效果好了不止一点。记住:写得对比写得多重要十倍。只写规则,不写理由。
第二个坑:过早搭 MCP Server。 MCP 让 Claude 连接数据库、GitHub、内部文档等外部系统,看起来很酷。但如果你连 CLAUDE.md 都没写好,Skills 都没建,LSP 都没装,就花一个下午搭一套 MCP——这就是典型的过度工程。把前四样调顺了再说 MCP。等你明确知道”如果 AI 能直接查这个,每次能省掉大量来回”的时候再动手。
第三个坑:忘了 Harness 需要维护。 Harness 不是一次性工程。模型大版本更新后,部分规则可能不再需要——比如旧版本模型不会自己读 tsconfig 的路径别名,新版本已经会了,你还写一条”注意路径别名”就是浪费 token。每三个月检查一次,每次模型发布后看一眼。
五步实操,从零搭一套
开工前确认:Claude Code 版本不低于 2.1.100(终端跑 claude —version),找一个你日常工作的真实项目(别用 demo),预留 15 分钟按顺序来。
第一步:写好 CLAUDE.md(回报率最高的 5 分钟)
CLAUDE.md 是 Claude Code 启动时自动加载的文件。之后每个对话回合它都在上下文里——你写的每一个字,AI 每次回复都在为它买单。
核心原则只有一条:规则放 CLAUDE.md,工作流放 Skills。
比如「金额单位永远是分,禁止浮点数」是规则,放 CLAUDE.md。「新建 API 路由要先检查已有路由、写 zod schema、写 handler、补测试、跑测试」是工作流,放 Skill。
在项目根目录创建 CLAUDE.md,结构如下:
# 项目概要
技术栈:Next.js 15 + tRPC + PostgreSQL
跑测试:pnpm test
# 全局约定
- 禁止用 em dash,中文用全角破折号
- 金额相关计算一律用"分",禁止浮点数
- 组件文件用 named export,不用 default export
如果你的项目有子包(比如 monorepo 里的 packages/api),也在子包目录放一个 CLADE.md,只写跟这个子包相关的约定。Claude Code 会从你初始化的目录往上走,自动加载沿途所有 CLAUDE.md。这样全局规则放根目录,包级规则放包目录,互不干扰。
第二步:配 SessionStart Hook(别每次都从头自我介绍)
Hooks 是在 Claude Code 生命周期事件上自动触发的脚本。最有用的两类:SessionStart——对话开始时自动执行;Stop——对话结束后自动执行。
如果你每次开 Claude Code 都要先回答”项目长什么样""现在在哪个分支""最近改了什么”,这些信息就应该自动化。在项目根目录创建 .claude/settings.json:
{
"hooks": {
"SessionStart": [
{
"type": "command",
"command": "node .claude/hooks/session-context.mjs"
}
]
}
}
SessionStart 脚本本身很简单——拼一段 git status + git log + 当前分支名,打印到 stdout。Claude Code 会把 Hook 的输出直接注入对话开头,AI 一上来就知道当前在哪、周围是什么状态。
进阶玩法:如果你的团队 CLAUDE.md 经常需要更新,用 Stop Hook 加一个 headless Claude 在每次会话结束后审查改动,自动生成 CLAUDE.md 的修改建议——让 Harness 越用越聪明,而不用你手动维护。
第三步:把重复工作流写成 Skill
Skill 最精妙的设计在于延迟加载:你可以写 50 个 Skill,但每个对话回合只为一个 Skill 的描述付 token 费。Skill 的正文只在描述匹配当前任务时才会被加载进上下文。
比如团队每次新建 API 路由都要跑一套固定流程,写成 Skill:
---
name: api-add-route
description: 新建 tRPC 路由,含 zod 校验和测试
paths: packages/api/**
---
# 新建 API 路由
1. 检查 src/routes/ 下已有路由,避免重复
2. 用 zod 写输入 schema
3. 写路由 handler
4. 补测试,用项目统一的 test helper
5. 跑 pnpm --filter api test 确认通过
注意 frontmatter 里的 paths 字段——它让这个 Skill 只在 api 包目录下工作时才会被激活。路径作用域是 Skill 最被低估的功能:在几百人的 monorepo 里,每个人只看到跟自己相关的 Skill,不会被别人的工作流干扰。
第四步:装上 LSP(别让 grep 在十万行代码里大海捞针)
Claude Code 不用向量数据库、不预建索引。它像人一样导航代码——读文件、grep 搜索、跟踪 import。在 5000 行项目里毫无感觉,到了 10 万行以上,一个简单的 grep 能炸出几百条结果——注释里的、日志里的、测试 fixture 里的、字符串拼接里的。
LSP 解决的就是这个问题:它理解作用域、类型和导入关系,做的是符号级搜索,不是字符串匹配。同一个变量名,LSP 给出的是一处定义和两处引用,而不是几百个无意义的字符串匹配。
装法不复杂:需要两样东西——一个 Claude Code 的 LSP 插件(code intelligence plugin),加上你的语言对应的 language server 二进制。TypeScript 项目装 typescript-language-server,Python 项目装 pyright。装完后 Claude 的每一次代码搜索都精准一个数量级。
第五步:拆 Sub-agent 探路(主会话别被上下文撑爆)
Sub-agent 是一个拥有独立上下文窗口的隔离 Claude 实例。你派它去探路,它在干干净净的上下文里执行,返回结果后退出。你的主会话永远看不到它的对话过程——只收最终答案。
最实用的模式:探索和编辑拆开。
比如你要重构 billing 模块但不熟悉它——别在主会话里一边 grep 一边读文件一边改代码,上下文很快就爆了。正确做法:
- 派一个只读 Sub-agent 去摸清 billing 目录的结构、入口点和数据流,把发现写到 .claude/discovery/billing.md
- Sub-agent 返回摘要,文件写入磁盘
- 主会话读 billing.md,在干净的上下文里开始重构
代码库越大,这个模式越值钱。10 万行以上的 monorepo 不拆 Sub-agent,单个会话的上下文一大半都喂给了探索过程,留给正经改代码的空间没多少。
一个真实的对比:配前 vs 配后
拿一个典型的 Next.js monorepo 项目举例。配 Harness 之前,每次开 Claude Code 的对话大概是这样的:
- 你:“帮我在 billing 模块加一个根据用户类型打折的功能”
- AI:“好的,让我先了解一下项目结构……请确认一下:你是用 tRPC 还是 REST?”
- 你:“tRPC”
- AI:“明白了。billing 模块在哪?”
- 你:“packages/api/src/routes/billing/”
- AI 开始读文件、grep 搜索——花掉 8000 token 的上下文,还没开始写代码
配完 Harness 之后,同样的需求:
- 你:“帮我在 billing 模块加一个根据用户类型打折的功能”
- AI 已经知道技术栈是 tRPC、金额单位是分、billing 的结构已经由 Sub-agent 缓存到 discovery 文件里。直接从「在哪个文件加什么逻辑」开始。
差距不是模型造成的——是上下文里缺少的那层信息造成的。Harness 把它补上了。
下一步:从个人配置到团队共享
如果你已经在个人项目里配好了 Harness,下一步该考虑的是 Plugin——把 CLAUDE.md、Hooks、Skills 打包成一个可安装的单元。团队 40 个人一句命令就全装上,而不是每个人花半小时手动拷贝。每个人的配置保持一致,新人入职第一天就有完整的 AI 编程环境。
模型会一直变强,这不用你操心。但模型下面那层 Harness——那是你的,而且它会复利。
同一个模型,同一个项目,配和不配 Harness,产出完全两个档次。花一小时试试。