1. harness 是什么?
在 Claude Code 语境里,harness 可以理解为"把模型变成可用工程代理的外部运行框架/脚手架/控制层"。
模型本身只是"脑子";harness 是让这个脑子能在真实代码库里工作的那套东西,包括:
- 它启动时读什么上下文;
- 能调用哪些工具;
- 哪些命令允许、哪些禁止;
- 什么时候自动跑 lint/test;
- 怎么连接内部文档、Jira、GitHub、数据库;
- 怎么通过 LSP 获取符号级代码理解;
- 怎么通过 hooks 强制执行规则。
Anthropic 那篇文章明确说,大型代码库里 Claude Code 的表现不只由模型决定,更大程度取决于模型周围的生态,也就是 harness;文章把 harness 拆成 CLAUDE.md、hooks、skills、plugins、MCP servers,并补充 LSP 和 subagents 两类能力。(Claude)
可以把它类比成:
模型 = 程序员本人
harness = IDE + 项目文档 + 权限系统 + CI 脚本 + 内部工具 + 工作流规范
没有 harness,Claude Code 只能像一个"聪明但刚入职的新员工"到处 grep、猜目录、问你问题。harness 做得好,它更像一个已经熟悉项目规范、构建流程和安全边界的工程师。
2. CLAUDE.md 自动加载:从哪里加载?应该怎么写?要写大量知识吗?
不建议把当前目录下的大量知识都塞进 CLAUDE.md。
CLAUDE.md 应该写"每次会话都值得让 Claude 知道的高价值规则",而不是项目百科全书。
Claude Code 官方文档说,CLAUDE.md 是你写给 Claude 的持久指令文件,会在每次会话开始时读取;它是上下文,不是强制配置,所以越具体、越简洁,遵循效果越稳定。(Claude)
它从哪里加载?
常见加载位置有几层:
| 范围 | 位置 | 适合写什么 |
|---|---|---|
| 组织级 | macOS: /Library/Application Support/ClaudeCode/CLAUDE.md;Linux/WSL: /etc/claude-code/CLAUDE.md;Windows: C:\Program Files\ClaudeCode\CLAUDE.md |
公司级安全、合规、代码审查规则 |
| 用户级 | ~/.claude/CLAUDE.md |
你个人所有项目通用偏好 |
| 项目级 | ./CLAUDE.md 或 ./.claude/CLAUDE.md |
当前项目架构、构建、测试、编码约定 |
| 本地项目级 | ./CLAUDE.local.md |
你个人的项目私有配置,比如本地 sandbox URL、测试账号;应加入 .gitignore |
官方文档说明,工作目录上方层级里的 CLAUDE.md 和 CLAUDE.local.md 会在启动时完整加载;子目录里的 CLAUDE.md 会在 Claude 读取那些目录时按需加载。(Claude)
大型 monorepo 里尤其重要:最好从相关子目录启动 Claude Code,而不是总在 repo 根目录启动 。Anthropic 的文章也建议这么做,因为 Claude 会向上遍历并加载沿途的 CLAUDE.md,所以根目录上下文不会丢,但工作范围会更聚焦。(Claude)
CLAUDE.md 应该写什么?
写这些:
md
# Project Overview
This repo is a TypeScript monorepo using pnpm workspaces.
The service in apps/payments owns payment orchestration and should not contain UI code.
# Build and Test
- Install dependencies: pnpm install
- Typecheck this package: pnpm --filter payments typecheck
- Run unit tests: pnpm --filter payments test
- Run one test file: pnpm vitest path/to/file.test.ts
# Architecture Rules
- API handlers live in src/api/handlers/
- Domain logic lives in src/domain/
- Do not call external payment providers directly from controllers.
- Use PaymentGateway interface in src/domain/gateways/
# Coding Conventions
- Use 2-space indentation.
- Prefer explicit return types for exported functions.
- Do not introduce new dependencies without checking package.json first.
# Safety
- Never modify migration files under db/migrations unless explicitly asked.
- Never run commands against production environment.
官方文档也给出了类似原则:CLAUDE.md 适合写 build commands、coding standards、project architecture、naming conventions、common workflows;推荐每个 CLAUDE.md 控制在约 200 行以内。(Claude)
不应该写什么?
不要写:
md
# Bad
这里是 src 目录下所有文件的详细说明......
A.ts 做......
B.ts 做......
C.ts 做......
也不要把所有设计文档、所有历史决策、所有接口说明都塞进去。原因是:CLAUDE.md 会占用上下文窗口,越大越拖慢,越容易稀释真正重要的规则。官方文档也提醒,CLAUDE.md 会进入上下文窗口;文件越长,越消耗上下文,遵循效果也会下降。(Claude)
更好的做法是:
- 根目录
CLAUDE.md:写项目总览、关键命令、危险区、导航入口。 - 子目录
CLAUDE.md:写该模块局部规则。 .claude/rules/:写只对特定文件或路径生效的规则。- Skills:写多步骤、专门任务流程,比如"安全审计流程""数据库迁移流程"。
- README / docs:保留长文档,CLAUDE.md 只放指针。
一句话:
CLAUDE.md 不是知识库;它是 Claude 每次工作前必须知道的"项目操作手册"。
3. Hooks 一般什么时候用合适?大型工程中常用哪些 hooks?
Hooks 适合用于 "不能只靠 Claude 记住,必须自动执行或强制检查"的事情。
官方文档说,hooks 是定义在 JSON settings 文件里的事件处理器,可以在 PreToolUse、PostToolUse、Stop 等事件点触发,并可按工具名匹配,比如只对 Bash、Edit、Write 生效。(Claude)
核心判断标准:
| 场景 | 用 CLAUDE.md | 用 Hook |
|---|---|---|
| "请遵守我们的编码风格" | 可以 | 不够 |
| "每次改 TS 文件后必须跑 prettier" | 不适合 | 适合 |
| "不要删除 secrets 目录" | 可以提醒 | 应该 hook/permissions 强制 |
| "启动时把当前 Jira issue 注入上下文" | 不适合 | 适合 |
| "结束前检查任务是否真的完成" | 不稳定 | 适合 Stop hook |
大型工程里常见 Hook 类型
A. SessionStart:启动时注入动态上下文
适合在会话开始时加载当前开发环境状态:
- 当前 git branch;
- 最近提交;
- 当前 worktree 状态;
- 当前目录对应的 owner/team;
- 当前 Jira/Linear issue;
- 本模块 build/test 命令;
- 当前服务的 deploy freeze 状态。
官方文档说明,SessionStart 在新会话或恢复会话时运行,适合加载开发上下文、最近变更或设置环境变量;如果只是静态上下文,应使用 CLAUDE.md。(Claude)
大型工程示例:
json
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/load-context.sh"
}
]
}
]
}
}
load-context.sh 可以输出:
txt
Current branch: feature/payment-retry
Changed files: src/payments/retry.ts, src/payments/retry.test.ts
Service owner: Payments Platform
Relevant test command: pnpm --filter payments test
这些内容会作为上下文给 Claude。
B. PreToolUse:工具执行前拦截危险操作
适合做安全、权限、边界控制。
常见用途:
- 禁止
rm -rf; - 禁止访问
.env.production; - 禁止运行生产数据库命令;
- 禁止改
generated/、vendor/、node_modules/; - 禁止直接 push 到 main;
- 对
curl、ssh、kubectl、terraform apply做额外确认。
官方文档说明,PreToolUse 在 Claude 生成工具参数之后、工具真正执行之前运行,可用于 allow、deny、ask 或 defer 工具调用。(Claude)
示例:阻止危险 shell 命令。
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/block-dangerous-bash.sh"
}
]
}
]
}
}
比如脚本发现命令包含:
bash
rm -rf /
kubectl delete
terraform destroy
psql $PROD_DATABASE_URL
就返回 deny。
C. PostToolUse:改文件后自动检查
适合做格式化、lint、类型检查、单测、安全扫描。
官方文档说明,PostToolUse 在工具成功执行后立刻运行,输入里包含工具参数和工具返回结果;它可以把检查结果反馈给 Claude。(Claude)
大型工程常见配置:
json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/check-touched-file.sh"
}
]
}
]
}
}
check-touched-file.sh 可以根据被改文件类型执行:
.ts/.tsx:pnpm eslint file && pnpm tsc --noEmit.py:ruff check file && pyright file.go:gofmt -w file && go test ./....rs:cargo fmt && cargo check
这比在 CLAUDE.md 里写"记得跑 lint"可靠得多。
D. Stop:Claude 想结束时做最终检查
适合做"收尾质量门禁":
- 是否所有 TODO 都完成;
- 是否有测试失败;
- 是否修改了代码但没运行测试;
- 是否只改了实现没改测试;
- 是否生成了迁移文件但没更新 schema snapshot;
- 是否还有临时 debug log。
官方 hooks guide 给出示例:Stop hook 可以让模型判断任务是否完成,如果返回 "ok": false,Claude 会继续工作。(Claude)
大型工程里常见的 Stop hook:
json
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Check whether all requested tasks are complete, tests were considered, and no obvious follow-up work remains. Return JSON {\"ok\": true} or {\"ok\": false, \"reason\": \"...\"}."
}
]
}
]
}
}
E. FileChanged:配置文件变更时刷新环境
适合监听:
.env.envrcpackage.jsonpyproject.tomlgo.modCargo.tomlpnpm-lock.yaml
官方文档说,FileChanged 在被监听文件变化时运行,常用于项目配置变化后重载环境变量。(Claude)
大型工程 Hook 设计原则
不要一开始就写很多 hooks。推荐顺序:
- 先写 CLAUDE.md:告诉 Claude 项目基本规则。
- 再写 PreToolUse 安全拦截:防止破坏性操作。
- 再写 PostToolUse 质量检查:格式化、lint、类型检查。
- 最后写 SessionStart / Stop:做上下文注入和收尾检查。
Hooks 最适合处理 确定性规则。如果规则可以用脚本判断,就不要让 Claude "记得做"。
4. LSP 集成如何与 Claude 协作?
这里应该理解为:LSP 让 Claude Code 拥有类似 IDE 的代码理解能力。
LSP 是 Language Server Protocol。VS Code、JetBrains 等 IDE 的"跳转定义""查找引用""类型提示""诊断错误"很多就是通过语言服务器实现的。Claude Code 的 LSP 集成,就是把这套代码智能能力暴露给 Claude 使用。
Anthropic 的文章说,LSP 让 Claude 获得和 IDE 类似的导航能力:跳转到定义、查找引用、跨文件追踪函数调用,并能区分不同语言或模块里同名函数;没有 LSP 时,Claude 更依赖文本匹配,容易找错符号。(Claude)
官方插件文档也说明,LSP 集成提供:
- 即时 diagnostics:Claude 编辑后马上看到错误和 warning;
- 代码导航:go to definition、find references、hover information;
- 语言感知:类型信息和符号文档。(Claude)
它和 Claude Code 的协作流程
假设你让 Claude 改一个 TypeScript 函数:
txt
把 PaymentService.createCharge 改成支持 idempotency key
没有 LSP 时,Claude 可能会:
- grep
createCharge; - 打开很多匹配文件;
- 猜哪个是定义;
- 再 grep 调用点;
- 可能误把 mock、test fixture、旧实现当成真实实现。
有 LSP 时,它可以:
- 通过符号导航找到
PaymentService.createCharge的真实定义; - 查找所有引用;
- 看函数签名、类型、接口定义;
- 修改实现;
- 修改调用点;
- LSP 自动报告类型错误、缺失 import、签名不匹配;
- Claude 在同一轮里修复这些 diagnostics。
官方插件发现页也说,代码智能插件开启后,Claude 可以在每次编辑后看到语言服务器报告的错误和 warning;如果 Claude 引入类型错误,它可以在同一轮注意到并修复。(Claude)
怎么启用?
一般是两步:
- 安装对应语言的 language server binary;
- 在 Claude Code 里安装对应 LSP plugin。
官方 marketplace 里有多种 code intelligence 插件,例如:
| 语言 | 插件 | 需要的 binary |
|---|---|---|
| C/C++ | clangd-lsp |
clangd |
| Go | gopls-lsp |
gopls |
| Java | jdtls-lsp |
jdtls |
| Python | pyright-lsp |
pyright-langserver |
| Rust | rust-analyzer-lsp |
rust-analyzer |
| TypeScript | typescript-lsp |
typescript-language-server |
官方文档说明,code intelligence plugins 会启用 Claude Code 的内置 LSP 工具,但语言服务器 binary 需要你自己安装;如果看到 Executable not found in $PATH,就说明缺少对应 binary。(Claude)
示例:
bash
npm install -g typescript typescript-language-server
然后在 Claude Code 里:
txt
/plugin install typescript-lsp@claude-plugins-official
Python 示例:
bash
npm install -g pyright
然后:
txt
/plugin install pyright-lsp@claude-plugins-official
大型工程里 LSP 的关键价值
LSP 对大型工程特别有价值,尤其是:
- C/C++:头文件、宏、重载、跨目录引用复杂;
- Java/C#:类层级、接口实现、依赖注入复杂;
- TypeScript:类型、泛型、路径 alias 多;
- Rust/Go:编译器/语言服务器可以精确给出符号关系;
- monorepo:同名函数、同名接口、同名模块非常多。
在大型代码库中,grep "validate" 可能有几千个结果,但 LSP 的 "find references" 可以只返回同一个符号的真实引用。这就是 文本搜索 和 语义导航 的区别。Anthropic 文章也指出,LSP 可以在 Claude 读取文件之前先过滤到同一符号的引用,避免 grep 带来的大量无关匹配。(Claude)
注意事项
LSP 不是魔法,工程配置要正确:
- TypeScript 要有正确的
tsconfig.json; - C/C++ 最好有
compile_commands.json; - Python 要配置好 venv、pyright 路径、import root;
- Java 要让
jdtls能识别 Maven/Gradle 项目; - Go 要保证
go.mod正常; - Rust 要保证
Cargo.toml正常。
否则 Claude 接入的也是一个"看不懂项目"的 language server。
最实用的落地组合
对大型工程,我建议这样搭:
txt
repo/
CLAUDE.md # 项目全局规则:少而关键
.claude/
settings.json # hooks、permissions
rules/
typescript.md # TS 文件专用规则
database.md # DB/migration 专用规则
hooks/
load-context.sh
block-dangerous-bash.sh
check-touched-file.sh
services/
payments/
CLAUDE.md # payments 模块局部规则
risk/
CLAUDE.md # risk 模块局部规则
优先级: