Skip to content

AI CLI 工具 Q&A

Q1: Claude Code 等 CLI 工具的"插件市场"是如何实现的?

结论先行

Claude Code 等终端 AI 编程工具没有传统意义上的"插件市场"(如 VS Code Marketplace 那种集中式商店)。它们的扩展能力主要通过 MCP(Model Context Protocol)协议 实现——这是一个去中心化的、基于协议的插件体系。

核心区别在于:

  • IDE 插件市场(如 VS Code):集中式商店 → 下载安装 → 加载到 IDE 运行时
  • CLI 工具扩展(如 Claude Code):标准协议 → 自行启动/连接 MCP Server → 工具注入到 Agent 上下文

1. 传统 IDE 插件市场的实现方式(参照对比)

以 VS Code 为代表的传统插件市场,架构如下:

开发者 → 打包 .vsix → 上传 Marketplace → 用户搜索/安装 → 宿主进程加载插件 DLL/JS

核心组件:

  • 注册表/商店:集中式服务器,托管插件元数据、版本、下载包
  • CLI 安装命令code --install-extension <id>
  • 运行时加载:宿主进程在启动时扫描已安装插件目录,加载到扩展宿主进程中
  • API Surface:插件通过宿主提供的 API(vscode.*)与编辑器交互

这种模式的前提是:有一个长期运行的 GUI 宿主进程,插件作为宿主的子模块运行。

2. CLI 工具为什么不走传统插件市场路线?

CLI 工具的运行模型与 IDE 有本质区别:

维度IDE(VS Code)CLI(Claude Code)
生命周期长期运行,常驻后台按需启动,用完退出
运行时有完整的 Extension Host 进程一个简单的 Node.js/Python 进程
UI 扩展需要与编辑器 UI 深度集成无 GUI,不需要 UI 扩展点
核心能力语法高亮、智能提示、调试器工具调用(文件/Shell/搜索)
扩展诉求添加语言支持、UI 面板、调试器添加外部数据源和操作能力

CLI 工具的扩展需求非常聚焦:给 AI 模型增加新的"工具"——能调用的 API、能查询的数据源、能执行的操作。不需要 UI 渲染、不需要语法高亮、不需要调试协议。

这种轻量的需求,用一个标准通信协议就能解决,不需要复杂的插件加载机制。

3. MCP:CLI 工具的"插件协议"

3.1 MCP 是什么

MCP(Model Context Protocol)是 Anthropic 于 2024 年底推出的开放协议,定位是 "AI 应用的 USB-C 接口"——一个连接 AI 模型与外部工具/数据的标准化协议。

┌─────────────┐                        ┌─────────────────┐
│  AI CLI 工具  │    MCP 协议             │  MCP Server      │
│  (MCP Client)│◄═══════════════════►  │  (插件/工具提供者) │
│              │  JSON-RPC over         │                  │
│  Claude Code │  stdio / HTTP          │  GitHub / Jira   │
│  Cursor      │                        │  DB / Sentry     │
│  Windsurf    │                        │  自定义服务...     │
└─────────────┘                        └─────────────────┘

3.2 MCP 的"插件"发现与安装

MCP 没有集中式商店,但有多种发现机制:

"插件发现"途径:

1. MCP 注册表网站
   ├── mcp.so                    ← 社区驱动的 MCP Server 目录
   ├── smithery.ai               ← MCP 工具注册表
   ├── glama.ai/mcp/servers      ← 另一个聚合目录
   └── github.com/modelcontextprotocol/servers  ← 官方参考实现

2. npm / PyPI 包
   └── npm install @modelcontextprotocol/server-github
   └── pip install mcp-server-sqlite

3. Docker 镜像
   └── docker run mcp/server-postgres

4. 自建 MCP Server
   └── 用任意语言实现 MCP 协议即可

3.3 "安装插件" = 配置 MCP Server

在 Claude Code 中,"安装一个插件"本质上就是在配置文件中声明一个 MCP Server:

json
// .claude/settings.json 或 ~/.claude/settings.json
{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_TOKEN": "ghp_xxx"
      }
    },
    "postgres": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-postgres"],
      "env": {
        "DATABASE_URL": "postgresql://..."
      }
    },
    "my-company-api": {
      "type": "http",
      "url": "https://mcp.mycompany.com/sse"
    }
  }
}

启动时,Claude Code 会:

  1. 读取配置,启动/连接每个 MCP Server 进程
  2. 通过 MCP 协议发现该 Server 提供的工具列表(tools/list
  3. 将这些工具的 JSON Schema 注入到系统提示中
  4. 模型即可像使用内建工具一样调用这些外部工具

3.4 MCP 协议核心交互

CLI 启动

  ├── 1. initialize(握手)
  │     Client → Server: 协议版本、能力声明
  │     Server → Client: 服务器能力、支持的功能

  ├── 2. tools/list(发现工具)
  │     Server → Client: [
  │       { name: "create_issue", description: "...", inputSchema: {...} },
  │       { name: "search_code", description: "...", inputSchema: {...} }
  │     ]

  ├── 3. tools/call(调用工具)——由模型决策触发
  │     Client → Server: { name: "create_issue", arguments: { title: "Bug fix", body: "..." } }
  │     Server → Client: { content: [{ type: "text", text: "Issue #42 created" }] }

  └── 4. resources/list + resources/read(可选:数据资源)
        Server → Client: 提供可读取的数据资源列表

通信方式:

  • stdio:CLI 启动子进程,通过标准输入/输出通信(本地插件首选)
  • HTTP + SSE:通过网络连接远程 MCP Server(远程服务首选)

4. 对比:三种"扩展"实现模式

模式一:传统插件市场(VS Code)
┌────────┐    下载     ┌────────┐    加载      ┌──────────┐
│ 商店    │ ────────► │ 本地包  │ ────────►  │ 宿主进程  │
│ .vsix  │           │ 文件    │            │ 扩展 API  │
└────────┘           └────────┘            └──────────┘

模式二:MCP 协议(Claude Code)
┌──────────┐   配置     ┌────────────┐   协议通信    ┌──────────┐
│ 注册表    │ ───────► │ 配置文件    │ ──────────► │ MCP      │
│ npm/目录  │          │ settings   │  JSON-RPC   │ Server   │
└──────────┘          └────────────┘             └──────────┘

模式三:内建 SDK 扩展(Claude Agent SDK)
┌──────────┐   npm      ┌────────────┐   编程调用    ┌──────────┐
│ npm      │ ───────► │ 项目代码    │ ──────────► │ 自定义    │
│ 发布     │          │ import SDK  │  tool()     │ Agent    │
└──────────┘          └────────────┘             └──────────┘

5. 不同 CLI 工具的扩展策略对比

工具主要扩展机制是否有集中商店扩展入口
Claude CodeMCP + Hooks + SDK无(依赖 MCP 注册表网站).claude/settings.json
CursorMCP + VS Code 扩展市场是(继承 VS Code Marketplace)Settings → MCP
GitHub Copilot CLI内建能力为主极少扩展点
Aider配置文件 + LiteLLM.aider.conf.yml
Continue.devMCP + 自定义 Provider社区 Hubconfig.json

6. 为什么 MCP 模式更适合 AI CLI 工具?

1. 语言无关 传统插件必须用宿主语言(VS Code 用 JS/TS)。MCP Server 可以用任何语言写——Python、Go、Rust、Java 都行,只要实现 JSON-RPC 协议。

2. 进程隔离 每个 MCP Server 是独立进程,崩溃不会影响主 CLI。传统插件跑在宿主进程里,一个插件 crash 可能拖垮整个编辑器。

3. 复用性高 一个 MCP Server 可以同时被 Claude Code、Cursor、Windsurf 等多个客户端使用。传统插件只能用于特定 IDE。

4. 部署灵活

  • stdio 模式:本地进程,零网络延迟
  • HTTP 模式:远程部署,团队共享,支持认证

5. 安全边界清晰 MCP Server 只能通过协议暴露的工具/资源与 CLI 交互,无法直接访问 CLI 内部状态。相比之下,传统插件可以调用宿主的任意 API。

7. MCP 生态的当前状态

截至 2025 年末,MCP 生态:

  • 3000+ MCP Server 已发布(官方统计)
  • 主要客户端:Claude Code、Claude Desktop、Cursor、Windsurf、Cline、Continue 等
  • 官方参考实现:GitHub、GitLab、Slack、Google Drive、PostgreSQL、SQLite、Sentry、Puppeteer 等
  • 社区注册表:mcp.so、smithery.ai、glama.ai 等聚合站点(类似非官方"插件市场")
  • 标准化进程:MCP 协议已开源,由 Anthropic 主导,多家公司参与

8. 总结

传统 IDE 插件市场                    AI CLI 工具扩展
═══════════════                    ═══════════════

集中式商店                          去中心化注册表
  ↓                                  ↓
下载 .vsix 插件包                   npm install / docker pull / 远程 URL
  ↓                                  ↓
宿主进程加载                        启动独立 MCP Server 进程
  ↓                                  ↓
调用宿主 API                        通过 JSON-RPC 协议通信
  ↓                                  ↓
仅限特定 IDE                        跨客户端复用

核心差异:从"代码加载"变成"协议通信"

AI CLI 工具的"插件市场"本质上是从代码级集成演变为协议级集成。MCP 协议扮演了"USB-C 接口"的角色——定义了标准的通信格式,让任何工具/服务都能以统一的方式被 AI 模型调用。这种模式更轻量、更安全、更具互操作性,非常契合 CLI 工具"轻量启动、按需连接"的运行特点。

Q2: Claude Code 输入 / 后弹出的所有斜杠命令详解

在 Claude Code 交互模式中,输入 / 会弹出命令列表,输入更多字母可过滤。截至 2026 年初,Claude Code 内建 40+ 个斜杠命令,按功能分为以下几大类。


1. 会话管理(Session Management)

控制对话的生命周期——新建、恢复、分叉、压缩、回退。

命令别名作用
/clear/reset, /new清空对话历史,从零开始新会话
/compact [指令]压缩对话以释放上下文空间。可指定保留重点:/compact 保留 API 设计决策
/resume [会话]/continue恢复之前的会话(按 ID/名称),不带参数则弹出会话选择器
/fork [名称]从当前对话分叉出一个新会话(保留当前历史,继续独立发展)
/rename [名称]重命名当前会话。不带参数时自动根据对话内容生成名称
/rewind/checkpoint回退代码和对话到之前的某个检查点(撤销 Claude 的操作)
/exit/quit退出 Claude Code

使用策略

  • /compact:上下文用量超过 80% 但还在同一个任务时使用,加焦点指令控制保留内容
  • /clear:彻底切换到不同任务时使用,旧上下文只会干扰
  • /resume:隔天继续昨天的工作时使用

2. 信息与诊断(Information & Diagnostics)

查看当前会话的状态——费用、上下文、健康度。

命令作用
/cost显示当前会话的 token 用量和费用
/usage显示订阅计划的用量限制和速率限制状态
/context以彩色方格图可视化上下文窗口使用情况
/status显示版本号、当前模型、账户信息、连接状态
/doctor运行诊断检查(环境、依赖、配置是否正常)
/help列出所有可用命令
/stats可视化每日使用量、会话历史、连续使用天数
/diff打开交互式 diff 查看器,显示未提交的更改和每轮 diff
/export [文件名]将对话导出为纯文本文件或复制到剪贴板
/copy复制上一条 Claude 回复到剪贴板(有代码块时弹出选择器)
/release-notes查看完整的版本更新日志
/insights生成分析报告,总结你的 Claude Code 使用模式

实用技巧:每 15-20 分钟跑一次 /cost,长会话中 token 消耗可能比预期快很多。


3. 模型与模式控制(Model & Mode Control)

切换模型、切换模式、调整 Claude 的行为方式。

命令作用
/model [模型名]切换模型(sonnet/opus 或完整模型名),用左右方向键调整 effort 级别
/fast [on|off]切换快速模式(同一模型,更快输出)
/plan进入 Plan 模式——Claude 先提出方案,确认后才执行
/vim切换 Vim 编辑模式和普通编辑模式
/output-style [风格]切换输出风格:Default / Explanatory / Learning
/theme更换颜色主题(浅色、深色、色盲友好等选项)

模型切换策略:大多数任务用 Sonnet(便宜快速),遇到复杂架构/疑难 bug 时切 Opus,难题解决后切回 Sonnet。


4. 配置与权限(Configuration & Permissions)

管理设置、权限、认证、项目初始化。

命令别名作用
/config/settings打开设置界面
/permissions/allowed-tools查看/修改工具权限(允许/拒绝列表)
/init初始化项目,创建 CLAUDE.md 文件
/memory编辑 CLAUDE.md 记忆文件,开关自动记忆功能
/login登录 Anthropic 账户
/logout登出 Anthropic 账户
/hooks配置和管理生命周期 Hooks
/agents管理子智能体配置
/skills列出可用的 Skills(自定义命令)
/mcp管理 MCP Server 连接
/plugin管理 Claude Code 插件
/terminal-setup配置终端快捷键绑定(如 Shift+Enter)
/keybindings打开或创建键绑定配置
/sandbox开关沙箱模式(隔离执行环境)
/extra-usage配置速率限制触发时的额外用量
/privacy-settings查看/更新隐私设置(Pro/Max 用户)
/statusline配置终端状态栏显示内容

5. 代码审查与 PR 工作流(Code Review & PR)

与 GitHub PR 交互,需要安装并认证 gh CLI。

命令作用
/review [PR号]审查 Pull Request 的质量、正确性和安全性。不带参数列出所有 open PR
/pr-comments [PR]获取并展示 GitHub PR 的评论,自动检测当前分支的 PR
/security-review对未提交的更改进行安全漏洞分析
/install-github-app安装 Claude GitHub App,启用自动 PR 审查

6. 工作目录与集成(Working Directories & Integration)

命令别名作用
/add-dir <路径>向当前会话添加额外的工作目录
/ide管理 IDE 集成,查看连接状态
/chrome配置 Chrome 浏览器集成
/remote-control/rc让当前会话可从 claude.ai 网页远程控制
/desktop/app将当前会话转移到 Claude Code Desktop 应用继续
/tasks列出和管理后台任务
/feedback [内容]/bug提交反馈或 Bug 报告

7. 快速参考卡片

日常高频命令 Top 10

/cost             ← 查费用(养成习惯,每 15 分钟看一次)
/context          ← 查上下文剩余空间
/compact          ← 压缩上下文(比 /clear 更温和)
/clear            ← 彻底清空,开新会话
/model sonnet     ← 切便宜模型
/model opus       ← 切强模型(处理难题时)
/resume           ← 恢复昨天的会话
/diff             ← 看 Claude 改了什么
/doctor           ← 出问题时先跑诊断
/help             ← 忘了命令时看这个

输入前缀快捷方式(不需要输入 /):

前缀作用示例
/斜杠命令或 Skill/compact 保留错误处理模式
!直接执行 Shell 命令(输出加入对话上下文)! git status
@文件路径自动补全@src/main.ts

! 前缀非常实用——直接执行命令并把输出注入到对话上下文中,不需要 Claude 审批:

bash
! npm test                # 跑测试,结果进入上下文
! git log --oneline -5    # 查最近 5 次提交
! cat .env.example        # 查看文件内容

8. 键盘快捷键

除了斜杠命令,Claude Code 还有一套键盘快捷键:

必记快捷键

快捷键作用
Escape取消当前生成(Claude 走错方向时立即打断)
Escape × 2回退/撤销 Claude 的上一步操作
Ctrl+C × 2退出会话
Ctrl+R反向搜索历史输入
Ctrl+T显示/隐藏任务列表
Shift+Tab循环切换权限模式(Auto-Accept / Plan / 默认)
Ctrl+O切换详细输出(查看完整工具调用过程)
Ctrl+B将当前运行的命令放到后台
Alt+P不清空输入框直接切换模型
Alt+T开关扩展思考(Extended Thinking)
Ctrl+G用外部编辑器打开当前输入(写长提示时很有用)

多行输入方式(因为 Enter 默认是发送):

方法快捷键说明
反斜杠转义\ + Enter所有终端通用
macOS 默认Option+EntermacOS 默认方式
Shift+EnterShift+EnteriTerm2/WezTerm/Ghostty/Kitty 原生支持
换行符Ctrl+J所有终端通用
粘贴直接粘贴多行文本适合粘贴代码块和日志

Q3: 详细介绍一下 Claude Code 的 /plugin 命令与插件系统

1. 概述

/plugin 是 Claude Code v1.0.33+ 引入的插件管理命令。插件(Plugin)是 Claude Code 的一等扩展单元,它把 Skills、Agents、Hooks、MCP Server、LSP Server 打包成一个可安装、可分享、可版本化的独立目录。

与 Q1 中介绍的"MCP 协议级扩展"不同,Plugin 是更上层的打包和分发机制——一个 Plugin 内部可以同时包含 MCP Server、自定义命令、子智能体、生命周期钩子等多种组件。

截至 2026 年初,已有 9000+ 个插件可用。


2. Plugin vs 独立配置(何时用插件 vs .claude/ 目录)

维度独立配置(.claude/ 目录)插件(Plugin)
命令命名/hello/plugin-name:hello(有命名空间)
适用场景单项目个人定制、快速实验团队分享、社区发布、跨项目复用
版本管理语义化版本(semver)
分发方式手动复制通过 Marketplace 安装
冲突风险有(同名覆盖)无(命名空间隔离)

建议路径:先在 .claude/ 中快速迭代,成熟后转为 Plugin 分发。


3. 插件目录结构

一个完整的 Plugin 长这样:

my-plugin/
├── .claude-plugin/           ← 元数据目录(仅放 plugin.json)
│   └── plugin.json             ← 插件清单(名称、版本、描述)
├── commands/                 ← 斜杠命令(Markdown 文件)
│   ├── status.md
│   └── logs.md
├── skills/                   ← Agent Skills(SKILL.md 结构)
│   ├── code-reviewer/
│   │   └── SKILL.md
│   └── pdf-processor/
│       ├── SKILL.md
│       └── scripts/
├── agents/                   ← 自定义子智能体
│   ├── security-reviewer.md
│   └── performance-tester.md
├── hooks/                    ← 生命周期钩子
│   └── hooks.json
├── output-styles/            ← 输出风格定义
│   └── terse.md
├── settings.json             ← 启用时应用的默认设置
├── .mcp.json                 ← MCP Server 配置
├── .lsp.json                 ← LSP Server 配置(代码智能)
├── scripts/                  ← 钩子脚本
│   ├── security-scan.sh
│   └── format-code.py
├── LICENSE
└── CHANGELOG.md

关键规则commands/agents/skills/hooks/ 等目录必须放在插件根目录,不要放进 .claude-plugin/ 内部.claude-plugin/ 里只放 plugin.json


4. plugin.json 清单文件

json
{
  "name": "my-plugin",
  "version": "1.2.0",
  "description": "我的自定义插件",
  "author": {
    "name": "Your Name",
    "email": "you@example.com"
  },
  "homepage": "https://docs.example.com/plugin",
  "repository": "https://github.com/user/plugin",
  "license": "MIT",
  "keywords": ["deployment", "ci-cd"],
  "commands": ["./custom/commands/"],
  "agents": "./custom/agents/",
  "skills": "./custom/skills/",
  "hooks": "./config/hooks.json",
  "mcpServers": "./mcp-config.json",
  "lspServers": "./.lsp.json",
  "userConfig": {
    "api_endpoint": {
      "description": "你的 API 端点",
      "sensitive": false
    },
    "api_token": {
      "description": "API 认证令牌",
      "sensitive": true
    }
  }
}
字段必选说明
name唯一标识符(kebab-case),同时作为命名空间前缀
version语义化版本号(MAJOR.MINOR.PATCH)
description插件说明,在插件管理器中显示
author作者信息
userConfig用户配置项,启用插件时提示用户输入(如 API Key)
commands/agents/skills组件路径,不指定则使用默认目录

userConfig 中的值可以通过 ${user_config.KEY} 在 MCP/LSP/Hook 配置中引用,敏感值存入系统钥匙链。


5. 插件的六大组件

5.1 Skills(技能)

放在 skills/ 目录下,每个 Skill 是一个含 SKILL.md 的文件夹:

markdown
---
name: code-review
description: 审查代码质量和潜在问题。当用户提到 review、审查、检查代码时触发。
---

审查代码时检查以下方面:
1. 代码组织与结构
2. 错误处理
3. 安全隐患
4. 测试覆盖率

安装后通过 /plugin-name:code-review 手动调用,或由 Claude 根据任务上下文自动触发。

5.2 Agents(子智能体)

放在 agents/ 目录下,Markdown 文件定义:

markdown
---
name: security-reviewer
description: 安全审查专家,分析代码中的安全漏洞
model: opus
effort: medium
maxTurns: 20
disallowedTools: Write, Edit
---

你是一名安全审查专家。分析代码中的注入、认证、数据暴露风险。

支持的 frontmatter 字段:namedescriptionmodeleffortmaxTurnstoolsdisallowedToolsskillsmemorybackgroundisolation(仅支持 "worktree")。

5.3 Hooks(生命周期钩子)

放在 hooks/hooks.json,响应 Claude Code 的 25+ 个生命周期事件

json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [{
          "type": "command",
          "command": "${CLAUDE_PLUGIN_ROOT}/scripts/format-code.sh"
        }]
      }
    ]
  }
}

主要事件包括:

事件触发时机
SessionStart会话开始或恢复
UserPromptSubmit用户提交提示词时
PreToolUse工具调用前(可阻止)
PostToolUse工具调用成功后
SubagentStart/Stop子智能体启停时
FileChanged文件变更时
PreCompact/PostCompact上下文压缩前后
WorktreeCreate/RemoveWorktree 创建/移除时
SessionEnd会话结束时

Hook 类型:command(执行脚本)、http(发 POST 请求)、prompt(LLM 评估)、agent(启动子智能体验证)。

5.4 MCP Server

.mcp.json 中配置:

json
{
  "mcpServers": {
    "plugin-database": {
      "command": "${CLAUDE_PLUGIN_ROOT}/servers/db-server",
      "args": ["--config", "${CLAUDE_PLUGIN_ROOT}/config.json"]
    }
  }
}

插件启用时 MCP Server 自动启动,工具无缝集成到 Claude 的工具列表中。

5.5 LSP Server(代码智能)

.lsp.json 中配置,提供实时诊断、跳转定义、查找引用等能力:

json
{
  "go": {
    "command": "gopls",
    "args": ["serve"],
    "extensionToLanguage": { ".go": "go" }
  }
}

官方已提供的 LSP 插件:pyright-lsp(Python)、typescript-lsp(TypeScript)、rust-lsp(Rust)。

5.6 Settings(默认设置)

settings.json 可在启用时激活插件的自定义 Agent 作为主线程:

json
{
  "agent": "security-reviewer"
}

6. /plugin 交互式命令

在 Claude Code 交互模式中输入 /plugin,会打开插件管理界面,包含以下功能 Tab:

Tab功能
Discover浏览已配置的 Marketplace 中的可用插件
Installed查看已安装的插件列表,启用/禁用/卸载
Errors查看插件加载错误(如 LSP 找不到二进制文件)
Validate验证插件的 plugin.json、frontmatter、hooks 语法

7. CLI 命令行管理

除了交互式 /plugin,还可以在命令行中非交互式管理插件:

bash
# 安装插件(从 Marketplace)
claude plugin install formatter@my-marketplace

# 安装到项目作用域(团队共享,写入 .claude/settings.json)
claude plugin install formatter@my-marketplace --scope project

# 安装到本地作用域(gitignore,仅自己可见)
claude plugin install formatter@my-marketplace --scope local

# 卸载插件
claude plugin uninstall formatter@my-marketplace

# 卸载但保留持久数据
claude plugin uninstall formatter@my-marketplace --keep-data

# 启用/禁用(不卸载)
claude plugin enable my-plugin
claude plugin disable my-plugin

# 更新到最新版本
claude plugin update my-plugin

安装作用域

作用域配置文件用途
user~/.claude/settings.json个人全局插件(默认)
project.claude/settings.json团队共享(提交到 Git)
local.claude/settings.local.json项目级私有(gitignore)
managed托管配置企业 IT 强制(只读,仅可更新)

8. 开发与调试插件

本地测试

--plugin-dir 加载本地插件,无需安装:

bash
# 加载单个插件
claude --plugin-dir ./my-plugin

# 加载多个插件
claude --plugin-dir ./plugin-a --plugin-dir ./plugin-b

修改插件后,在会话内运行 /reload-plugins 即可热重载,无需重启。

调试

bash
# 查看插件加载详情
claude --debug

# 在交互模式中验证插件
/plugin validate

常见问题排查

问题原因解决
插件不加载plugin.json 语法错误运行 /plugin validate 检查
命令不显示目录结构错误确保 commands/ 在插件根目录,不在 .claude-plugin/
Hook 不触发脚本没有执行权限chmod +x script.sh
MCP Server 启动失败路径用了绝对路径改用 ${CLAUDE_PLUGIN_ROOT} 变量
LSP 报 Executable not found语言服务器未安装先安装二进制(如 npm install -g typescript-language-server

9. 环境变量

插件中可以使用两个内建变量:

变量说明
${CLAUDE_PLUGIN_ROOT}插件安装目录的绝对路径(插件更新后会变)
${CLAUDE_PLUGIN_DATA}插件持久数据目录(~/.claude/plugins/data/{id}/),跨版本存活

典型用法——在 SessionStart 时自动安装依赖:

json
{
  "hooks": {
    "SessionStart": [{
      "hooks": [{
        "type": "command",
        "command": "diff -q \"${CLAUDE_PLUGIN_ROOT}/package.json\" \"${CLAUDE_PLUGIN_DATA}/package.json\" >/dev/null 2>&1 || (cd \"${CLAUDE_PLUGIN_DATA}\" && cp \"${CLAUDE_PLUGIN_ROOT}/package.json\" . && npm install)"
      }]
    }]
  }
}

10. 插件分发与 Marketplace

插件通过 Marketplace(插件市场)分发。Marketplace 本身也是一个目录结构(可以是 Git 仓库、本地目录或远程 URL),包含一个 marketplace.json 索引文件。

发布到官方市场

安装流程

用户 /plugin → Discover Tab → 浏览 Marketplace → 选择插件 → Install

claude plugin install formatter@official

插件被复制到 ~/.claude/plugins/cache/(安全隔离)

组件自动注册(Skills、Agents、Hooks、MCP/LSP Server)

用户可通过 /plugin-name:skill-name 调用

安全机制:安装的插件会被复制到本地缓存(~/.claude/plugins/cache/),而非直接引用原路径,防止插件源被篡改。


11. 总结:Plugin 在 Claude Code 扩展体系中的位置

Claude Code 扩展能力全景:

┌─────────────────────────────────────────────────────┐
│  Plugin(插件)—— 一等打包单元                        │
│  ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌───────────┐ │
│  │ Skills  │ │ Agents  │ │  Hooks  │ │ MCP/LSP   │ │
│  │ 自定义   │ │ 子智能体 │ │ 生命周期 │ │ Server    │ │
│  │ 命令     │ │         │ │ 钩子    │ │ 外部工具   │ │
│  └─────────┘ └─────────┘ └─────────┘ └───────────┘ │
├─────────────────────────────────────────────────────┤
│  通过 /plugin 或 CLI 管理                             │
│  通过 Marketplace 分发                                │
│  通过 --plugin-dir 本地开发测试                        │
└─────────────────────────────────────────────────────┘

Plugin 是 Claude Code 的最高层扩展抽象——它不是某一种能力的扩展,而是把所有扩展能力(Skills + Agents + Hooks + MCP + LSP + Settings)打包成一个可安装、可分享、可版本化的标准单元。这使得 Claude Code 的扩展生态从"手动配置若干 MCP Server"演进为"一键安装一个功能完整的插件包"。

Q4: Plugin 中定义的 Agent,是如何与 MCP、Skill、Hook 等组件交互的?

1. 先理解 Agent 的本质

Agent(子智能体)本质上是一个带独立上下文窗口、独立系统提示、独立工具集的 Claude 实例。它不是一段代码,而是一个配置文件——用 YAML frontmatter 声明"我能用什么工具、什么模型、什么权限",用 Markdown 正文定义"我的系统提示是什么"。

markdown
---
name: browser-tester
description: 使用浏览器测试前端功能
model: sonnet
tools: Read, Bash, Grep
mcpServers:
  - playwright:
      type: stdio
      command: npx
      args: ["-y", "@playwright/mcp@latest"]
  - github
skills:
  - api-conventions
  - error-handling-patterns
hooks:
  PreToolUse:
    - matcher: "Bash"
      hooks:
        - type: command
          command: "./scripts/validate-command.sh"
memory: project
isolation: worktree
background: false
maxTurns: 20
---

你是一名前端测试专家。使用 Playwright 工具对页面进行导航、截图和交互测试。

上面这个 Agent 定义同时涉及了 MCP、Skills、Hooks、Memory、Worktree 隔离——下面逐一解释它们之间的交互关系。


2. 全景交互架构

主对话(Main Conversation)

  │  ① Claude 根据 description 决定委派任务


┌─────────────────────────────────────────────────────────────┐
│  Agent: browser-tester                                       │
│  ┌──────────────────────────────────────────────────────┐   │
│  │ 独立上下文窗口                                         │   │
│  │                                                      │   │
│  │  系统提示 = frontmatter 定义 + Markdown 正文           │   │
│  │  (不继承主对话的系统提示)                                │   │
│  │                                                      │   │
│  │  ② skills 内容被注入                                   │   │
│  │  ┌─────────────────┐  ┌─────────────────────────┐    │   │
│  │  │ api-conventions │  │ error-handling-patterns  │    │   │
│  │  │ (全文注入上下文)  │  │ (全文注入上下文)          │    │   │
│  │  └─────────────────┘  └─────────────────────────┘    │   │
│  └──────────────────────────────────────────────────────┘   │
│                                                              │
│  ③ 可用工具集                                                │
│  ┌──────────────────────────────────────────────────────┐   │
│  │ 内建工具: Read, Bash, Grep (由 tools 字段指定)         │   │
│  │ MCP 工具: playwright.navigate, playwright.screenshot   │   │
│  │           github.create_issue, github.list_prs        │   │
│  │ (由 mcpServers 字段控制)                                │   │
│  └──────────────────────────────────────────────────────┘   │
│                                                              │
│  ④ Hooks 守卫                                                │
│  ┌──────────────────────────────────────────────────────┐   │
│  │ PreToolUse("Bash") → validate-command.sh              │   │
│  │ PostToolUse("Edit|Write") → run-linter.sh             │   │
│  │ (仅在此 Agent 活跃时运行,Agent 结束后清理)              │   │
│  └──────────────────────────────────────────────────────┘   │
│                                                              │
│  ⑤ 持久记忆                                                  │
│  ┌──────────────────────────────────────────────────────┐   │
│  │ .claude/agent-memory/browser-tester/MEMORY.md         │   │
│  │ (跨会话累积知识,启动时前 200 行注入上下文)              │   │
│  └──────────────────────────────────────────────────────┘   │
│                                                              │
│  ⑥ Worktree 隔离                                             │
│  ┌──────────────────────────────────────────────────────┐   │
│  │ 独立 git worktree → 独立文件目录 → 不影响主工作区       │   │
│  │ Agent 结束无修改时自动清理                               │   │
│  └──────────────────────────────────────────────────────┘   │
│                                                              │
│  ⑦ 结果摘要返回主对话                                         │
└─────────────────────────────────────────────────────────────┘

3. 各组件与 Agent 的交互机制详解

3.1 Agent 与 MCP Server 的交互

通过 mcpServers frontmatter 字段配置。有两种方式:

方式一:内联定义(Agent 独占)

yaml
mcpServers:
  - playwright:
      type: stdio
      command: npx
      args: ["-y", "@playwright/mcp@latest"]
  • MCP Server 在 Agent 启动时连接Agent 结束时断开
  • 主对话看不到这些工具(不占用主对话的上下文)
  • 适合:Agent 专用的外部工具

方式二:引用已有 Server(共享连接)

yaml
mcpServers:
  - github
  • 复用主会话已配置的 MCP Server 连接
  • Agent 和主对话共享同一个连接实例

交互流程

Agent 启动

  ├── 连接内联定义的 MCP Server(playwright)
  ├── 共享已有的 MCP Server 连接(github)

  ├── MCP tools/list → 发现工具列表
  │   → playwright.navigate, playwright.screenshot...
  │   → github.create_issue, github.list_prs...

  ├── 工具的 JSON Schema 注入 Agent 的上下文

  ├── Agent(Claude 模型)决策调用某工具
  │   → tools/call → MCP Server 执行 → 返回结果

  └── Agent 结束
      → 断开内联 MCP Server
      → 保留共享 Server 连接

关键设计:把 MCP Server 定义在 Agent 的 mcpServers 而非全局 .mcp.json 中,可以避免工具描述污染主对话上下文。如果你有一个工具很重要但描述很长(如 Playwright 有几十个工具),放在 Agent 里可以节省主对话的 token。

3.2 Agent 与 Skills 的交互

通过 skills frontmatter 字段配置:

yaml
skills:
  - api-conventions
  - error-handling-patterns

关键行为

特性说明
注入方式Skill 的完整内容被注入到 Agent 的系统提示中
不是"可调用"不是"Agent 可以调用这个 Skill",而是"Skill 内容直接成为 Agent 知识的一部分"
不继承Agent 不会继承主对话的 Skills,必须显式列出
命名空间插件 Skill 需要用完整名称:plugin-name:skill-name

这意味着 Skill 对 Agent 来说是编译时注入,不是运行时调用:

Agent 启动

  ├── 加载 skills/api-conventions/SKILL.md 全文
  ├── 加载 skills/error-handling-patterns/SKILL.md 全文

  ├── 拼接到 Agent 的系统提示中:
  │   [Agent 自身的 Markdown 正文]
  │   [== api-conventions Skill 全文 ==]
  │   [== error-handling-patterns Skill 全文 ==]

  └── Agent 开始工作,自然地运用这些知识

3.3 Agent 与 Hooks 的交互

Hooks 有两个层面:

层面一:Agent 自身定义的 Hooks(仅在 Agent 活跃时运行)

yaml
hooks:
  PreToolUse:
    - matcher: "Bash"
      hooks:
        - type: command
          command: "./scripts/validate-command.sh"
  PostToolUse:
    - matcher: "Edit|Write"
      hooks:
        - type: command
          command: "./scripts/run-linter.sh"
  • 这些 Hook 仅在此 Agent 运行期间生效
  • Agent 结束后自动清理,不影响主对话
  • 支持所有 Hook 事件(PreToolUse、PostToolUse 等)
  • Agent 中定义的 Stop 事件会被自动转换为 SubagentStop

层面二:主会话对 Agent 生命周期的 Hooks

settings.json 中可以监听 Agent 的启停:

json
{
  "hooks": {
    "SubagentStart": [{
      "matcher": "browser-tester",
      "hooks": [{ "type": "command", "command": "./scripts/start-browser.sh" }]
    }],
    "SubagentStop": [{
      "matcher": "browser-tester",
      "hooks": [{ "type": "command", "command": "./scripts/cleanup.sh" }]
    }]
  }
}

两层 Hooks 的关系

主会话 settings.json Hooks          Agent 自身 Hooks
─────────────────────               ──────────────────
SubagentStart("browser-tester")     (Agent 启动)
  → start-browser.sh                  │
                                      ├── PreToolUse("Bash")
                                      │     → validate-command.sh

                                      ├── [Agent 执行工具...]

                                      ├── PostToolUse("Edit")
                                      │     → run-linter.sh

                                      └── Stop (转为 SubagentStop)
SubagentStop("browser-tester")
  → cleanup.sh

3.4 Agent 与 Memory 的交互

yaml
memory: project
作用域存储路径适用场景
user~/.claude/agent-memory/<name>/跨项目通用知识
project.claude/agent-memory/<name>/项目级知识(可提交 Git)
local.claude/agent-memory-local/<name>/项目级私有(gitignore)

启用 memory 后:

  • Agent 的系统提示中会注入 MEMORY.md 的前 200 行(或 25KB)
  • 自动启用 Read/Write/Edit 工具(即使 tools 没有列出)
  • Agent 可以在执行过程中主动更新 MEMORY.md,积累跨会话知识

3.5 Agent 与 Worktree 隔离的交互

yaml
isolation: worktree
  • Agent 在独立的 git worktree 目录中运行
  • 有自己的文件系统和分支,不影响主工作区
  • Agent 结束后若无任何修改,worktree 自动清理
  • 适合并行任务——多个 Agent 同时工作互不干扰

4. 插件 Agent 的安全限制

重要:插件(Plugin)中定义的 Agent 有安全限制——以下三个字段会被忽略

被禁止的字段原因
hooks防止插件通过 Hook 执行任意脚本
mcpServers防止插件连接未经审查的外部服务
permissionMode防止插件绕过权限控制

如果你需要这些能力,有两种方式:

  1. 复制到本地:把 Agent 文件从插件复制到 .claude/agents/~/.claude/agents/,本地 Agent 没有这些限制
  2. 全局权限规则:在 settings.jsonpermissions.allow 中添加规则(但会影响整个会话)

各来源 Agent 的优先级

来源优先级限制
--agents CLI 参数最高无(仅当前会话)
.claude/agents/(项目级)
~/.claude/agents/(用户级)
Plugin agents/ 目录最低禁用 hooks/mcpServers/permissionMode

5. 完整交互流程图

以一个实际场景说明所有组件是如何协作的:

场景:用户说"测试登录页面的表单验证功能"

用户: "测试登录页面的表单验证功能"

  │ ① Claude 匹配到 browser-tester 的 description


Claude 决定委派给 browser-tester Agent

  │ ② 触发 SubagentStart Hook → start-browser.sh
  │ ③ 创建 worktree(isolation: worktree)
  │ ④ 连接内联 MCP Server(playwright)
  │ ⑤ 加载 Skills 全文注入上下文
  │ ⑥ 加载 MEMORY.md 前 200 行注入上下文


Agent 开始在独立上下文中工作

  ├── 调用 playwright.navigate("http://localhost:3000/login")
  │     └── PreToolUse Hook → 无匹配(仅匹配 Bash)→ 放行

  ├── 调用 playwright.screenshot()
  │     └── 返回页面截图

  ├── 调用 Bash("npm test -- --grep login")
  │     └── PreToolUse Hook → matcher="Bash" → validate-command.sh → 放行
  │     └── 返回测试结果

  ├── 调用 Edit("src/login.test.ts", ...)
  │     └── PostToolUse Hook → matcher="Edit" → run-linter.sh → lint 修复

  ├── Agent 更新 MEMORY.md:记录登录页的表单结构和测试模式

  └── Agent 生成结果摘要


Agent 结束

  ├── ⑦ 触发 SubagentStop Hook → cleanup.sh
  ├── ⑧ 断开 playwright MCP Server
  ├── ⑨ 检查 worktree 是否有修改 → 有 → 保留
  └── ⑩ 摘要结果返回主对话(不含中间过程的详细输出)

6. 总结

Agent 与各组件的交互本质上是声明式组合——Agent 不需要写代码去"调用" MCP 或"触发" Hook,而是通过 YAML frontmatter 声明"我需要什么",Claude Code 运行时负责编排:

组件Agent 如何声明运行时行为
MCPmcpServers 字段Agent 启动时连接,结束时断开;工具自动可用
Skillsskills 字段全文注入 Agent 的系统提示,成为领域知识
Hookshooks 字段仅在 Agent 活跃期间生效,结束后清理
Memorymemory 字段MEMORY.md 注入上下文,Agent 可读写积累知识
Worktreeisolation: worktreeAgent 在独立目录工作,不污染主工作区
Modelmodel 字段Agent 可以用不同于主对话的模型(如 haiku 降低成本)
Toolstools / disallowedTools控制 Agent 可用的内建工具子集

核心设计哲学:Agent 是一个声明式的能力组合体,Plugin 是它的分发载体。 Plugin 把 Agent 连同它依赖的 Skills、Hooks、MCP Server 一起打包分发,用户安装一个 Plugin 就获得了一整套协作能力。

Q5: Claude Code 哪些设计体现了 Harness(驾驭)的思想?

1. 什么是 Harness 思想

Harness 原意是马具/缰绳——不是把马关起来,而是在不削弱马力的前提下控制方向

在 AI Agent 语境下,Harness 思想是指:

不限制 AI 的能力上限,但在每个关键节点设置可控的约束和检查点,确保 AI 的行为始终在人类可预期、可审计、可中断的范围内运行。

它与"限制"的区别在于:限制是做减法(禁止 AI 做某事),Harness 是加缰绳(让 AI 能做但可控地做)。Claude Code 的整个架构可以说是 Harness 思想的教科书级实现。


2. 架构层面:模型无手无脚,CLI 是它的身体

Claude Code 最根本的 Harness 设计是双系统架构

┌──────────────────────┐              ┌──────────────────────┐
│  Claude 模型(大脑)    │              │  Claude Code CLI(身体)│
│                      │   请求/响应    │                      │
│  • 能推理            │ ◄──────────► │  • 能执行             │
│  • 能决策            │              │  • 有权限系统          │
│  • 能生成工具调用     │              │  • 有沙箱隔离          │
│  • 不能直接执行任何事  │              │  • 有 Hook 守卫       │
└──────────────────────┘              └──────────────────────┘

模型永远不直接执行任何操作。它只能返回一个结构化的"工具调用请求":

json
{ "type": "tool_use", "name": "Bash", "input": { "command": "rm -rf /" } }

这个请求必须经过 CLI 侧的完整管控链路才可能被执行。模型对真实世界零直接访问权——它的所有能力都经由 CLI 这个"驾驭层"中介。

这是最底层的 Harness:能力与执行权的分离


3. 权限层面:分层递进的缰绳

3.1 Deny > Ask > Allow 三级权限

用户请求 "删除这个文件"


Claude 模型返回: tool_use(Bash, "rm important.txt")


┌─────────────────────┐
│ ① Deny 规则检查      │  ← Bash(rm -rf *) 在黑名单?→ 直接阻止
└─────────┬───────────┘
          │ 未命中
┌─────────▼───────────┐
│ ② Ask 规则检查       │  ← 需要用户确认?→ 弹出审批提示
└─────────┬───────────┘
          │ 用户批准
┌─────────▼───────────┐
│ ③ Allow 规则检查     │  ← 在白名单中?→ 自动放行
└─────────┬───────────┘


        执行操作

Deny 规则不可覆盖——即使用户在 Allow 中配置了同样的规则,Deny 仍然优先。这是一个硬性缰绳,防止任何绕过。

3.2 六种权限模式——渐进式放权

模式缰绳松紧类比
Plan最紧只许看,不许动——马只能走路线
Default每步都问——骑手时刻拉缰
Accept Edits中等信任文件编辑,Shell 命令仍审批——松一半缰绳
Auto较松半自主执行,后台安全分类器检查——缰绳换成了围栏
Don't Ask反向未预批准的操作直接拒绝——CI/CD 场景的硬边界
Bypass最松跳过所有提示,但仍保护 .git/.claude——卸掉缰绳但保留笼头

核心思想:不是二元的"全开/全关",而是提供一个连续的信任光谱,用户根据场景选择合适的松紧度。


4. 执行层面:每一步都有检查点

4.1 Hook 系统——确定性守卫

CLAUDE.md 中的指令遵循率约 80%(模型可能忽略)。Hooks 的执行率是 100%——这就是 Harness 思想的精髓:关键约束不能依赖模型的"自觉",必须由确定性机制保证

工具调用请求

  ├── PreToolUse Hook ──── 100% 执行
  │     ├── command: 脚本校验 → exit 2 可阻止
  │     ├── prompt: LLM 评估 → 是/否判断
  │     └── agent: 子智能体验证 → 复杂审查

  ├── [执行工具操作]

  └── PostToolUse Hook ──── 100% 执行
        └── command: 自动 lint/format/安全扫描

对比:

机制执行率本质
CLAUDE.md 指令~80%建议(Advisory)—— "请不要做 X"
Hook100%强制(Enforcement)—— "不允许做 X"

这正是 Harness 与"口头约定"的区别:缰绳在手里,不是挂在马脖子上的牌子

4.2 沙箱隔离——操作系统级围栏

┌────────────────────────────────────────────┐
│  操作系统沙箱                                │
│  macOS: Seatbelt  /  Linux: bubblewrap      │
│                                            │
│  ┌──────────────────────────────────────┐  │
│  │ 文件系统                              │  │
│  │  项目目录 → 可读写                     │  │
│  │  父目录   → 只读                      │  │
│  │  系统目录 → 禁止                      │  │
│  └──────────────────────────────────────┘  │
│                                            │
│  ┌──────────────────────────────────────┐  │
│  │ 网络                                  │  │
│  │  已批准域名 → 允许                     │  │
│  │  其他域名   → 代理拦截,阻止            │  │
│  └──────────────────────────────────────┘  │
│                                            │
│  即使模型+CLI+Hooks 全部"同意"执行,         │
│  沙箱仍然是最后一道物理围栏。                  │
└────────────────────────────────────────────┘

这是纵深防御的 Harness 设计——不依赖任何单一机制,而是层层嵌套:

权限系统(Deny) → Hook(PreToolUse) → CLI 执行检查 → OS 沙箱
     ↑              ↑                   ↑             ↑
   第一道缰绳      第二道缰绳          第三道缰绳     围栏(最后防线)

5. 资源层面:防止失控

5.1 上下文压缩——防止记忆膨胀

Agent 的上下文窗口是有限的(200K tokens)。无限制地累积工具输出会导致上下文溢出、任务失忆。压缩系统就是"缰绳":

机制触发作用
微压缩单次工具输出过大把大型结果卸载到磁盘,上下文只留引用
自动压缩上下文达 95%自动总结历史,释放空间
手动压缩/compact用户主动控制保留什么

5.2 maxTurns / maxBudget——防止无限循环和费用失控

bash
claude -p --max-turns 5 --max-budget-usd 2.00 "重构 auth 模块"
约束作用
--max-turns限制 Agent 循环的最大轮次,防止死循环
--max-budget-usd限制单次任务的最大花费,防止费用失控
maxTurns(Agent frontmatter)限制子智能体的最大轮次

这是资源维度的缰绳——即使模型的推理完全正确,也要在预算和时间上设限。

5.3 子智能体深度限制

主对话 → 可以创建子智能体

  └── 子智能体 → 不可以再创建子智能体(深度 = 1)

防止 Agent 自我复制导致递归爆炸。这是拓扑层面的缰绳


6. 工具层面:最小权限原则

6.1 只读工具不需权限,写入工具必须审批

操作类型代表工具权限
只读(无副作用)Read, Glob, Grep, TodoWrite自动放行
有副作用Write, Edit, Bash, WebFetch需要审批

6.2 Agent 的工具约束

yaml
# 白名单模式:只允许指定工具
tools: Read, Grep, Glob

# 黑名单模式:禁止指定工具
disallowedTools: Write, Edit

# 限制可生成的子智能体类型
tools: Agent(worker, researcher), Read, Bash

6.3 命令黑名单

CLI 内建了危险命令黑名单(curlwget 等),即使 Allow 规则没有显式排除,也默认阻止。


7. 可观测层面:一切可审计

Harness 不仅是"控制",还包括"可见"——你必须看得到马往哪跑。

可观测机制作用
/cost实时查看 token 用量和费用
/context可视化上下文窗口使用状况
/diff查看 Claude 做了哪些文件修改
events.jsonlWorktree 生命周期事件的 append-only 日志
Hook 事件流25+ 个生命周期事件可被监听
Ctrl+O切换详细输出,查看完整工具调用过程
Escape × 2随时回退/撤销 Claude 的操作
Agent Memory子智能体的 MEMORY.md 可被人类审查
子智能体 transcript~/.claude/projects/{project}/{session}/subagents/ 下保存完整对话记录

8. 插件层面:第三方代码的信任边界

插件是外部来源的代码,Harness 思想在这里体现为默认不信任

约束说明
插件 Agent 禁用 hooks防止插件通过 Hook 执行任意脚本
插件 Agent 禁用 mcpServers防止插件连接未审查的外部服务
插件 Agent 禁用 permissionMode防止插件绕过权限控制
插件缓存隔离安装的插件被复制到 ~/.claude/plugins/cache/,不直接引用原路径
路径不能外逸插件不能引用自身目录之外的文件(../ 无效)
命名空间隔离插件 Skill 命名为 /plugin-name:skill,防止命名冲突

9. 总结:Harness 设计的六层纵深

┌─────────────────────────────────────────────────────┐
│  第一层:架构分离                                      │
│  模型无执行权,所有操作经由 CLI 中介                     │
├─────────────────────────────────────────────────────┤
│  第二层:权限系统                                      │
│  Deny > Ask > Allow,六种权限模式渐进放权               │
├─────────────────────────────────────────────────────┤
│  第三层:Hook 守卫                                     │
│  100% 确定性执行,PreToolUse 可阻止/修改操作            │
├─────────────────────────────────────────────────────┤
│  第四层:资源约束                                      │
│  maxTurns、maxBudget、子智能体深度=1、上下文压缩        │
├─────────────────────────────────────────────────────┤
│  第五层:沙箱隔离                                      │
│  OS 级文件系统/网络隔离 + Worktree 工作区隔离            │
├─────────────────────────────────────────────────────┤
│  第六层:可观测性                                      │
│  费用/上下文/diff/事件流/transcript 全方位可审计         │
└─────────────────────────────────────────────────────┘

Claude Code 的设计哲学可以用一句话概括:

给 AI 尽可能强大的能力,但在每一层都系上缰绳——不是为了限制它跑多快,而是为了确保它跑在正确的方向上,并且骑手随时可以勒停。

这就是 Harness 思想的完整体现:能力最大化、风险可控化、行为可审计化。

Q6: Claude Code 的 diff 能力是如何实现的?

Claude Code 的"diff 能力"涉及三个层面:Edit 工具如何精准修改文件修改结果如何生成 diff 展示/diff 命令如何提供交互式 diff 查看器。下面从底层到上层逐一拆解。


1. Edit 工具:精确字符串替换(而非传统 diff/patch)

Claude Code 的文件修改不使用传统的 diff + patch 流程,而是采用**精确字符串替换(Exact String Replacement)**方式。

1.1 工具参数

json
{
  "type": "tool_use",
  "name": "Edit",
  "input": {
    "file_path": "src/auth.ts",
    "old_string": "function login(user) {",
    "new_string": "function login(user: User): Promise<Session> {",
    "replace_all": false
  }
}

模型需要精确提供"要被替换的原文"和"替换后的新文"——不是行号,不是正则,而是文本本身

1.2 Edit 算法流程

输入: old_string, new_string, file_path


┌──────────────────────────────┐
│ Step 1: 精确匹配              │
│ 在文件中搜索 old_string 的     │
│ 字面量完全匹配                 │
└─────────────┬────────────────┘

     找到?────┴────没找到?
      │                │
      ▼                ▼
 ┌──────────┐   ┌─────────────────────┐
 │ 检查唯一性│   │ Step 2: 模糊匹配     │
 │          │   │ • 尾部空格标准化      │
 │ 唯一 → 替换│   │ • 缩进标准化         │
 │ 多处 → 报错│   │ • CRLF/LF 标准化     │
 └──────────┘   │ • 上下文行扩展匹配    │
                └──────────┬──────────┘

                  找到?────┴────仍然没找到?
                   │                   │
                   ▼                   ▼
              ┌──────────┐      ┌──────────────┐
              │ 替换+警告 │      │ 返回错误      │
              └──────────┘      │ 要求模型重新   │
                                │ 读取文件后重试  │
                                └──────────────┘

1.3 替换前的校验

校验项目的
文件存在防止通过 Edit 创建新文件(应该用 Write)
old_string 找到确保编辑目标正确
唯一匹配old_string 必须在文件中唯一(否则用 replace_all
内容有变化防止 old_string == new_string 的空操作

1.4 失败时的自愈循环

错误原因模型的自动应对
"old_string not found"文件在上次 Read 后被修改了重新 Read 文件,用最新内容重试
"Multiple matches"old_string 不够唯一增加上下文行数使其唯一
"File not found"路径错误用 Glob 搜索正确路径

这是 Agent Loop 的核心优势——失败不是终点,而是下一轮循环的输入。模型根据错误信息调整策略,自动重试。

1.5 为什么不用行号?

传统 diff/patch 使用行号定位,但在 AI Agent 场景下行号有致命缺陷:

方式问题
行号定位前面的编辑会导致后续行号偏移,多步编辑时极易出错
正则替换过于灵活,容易误匹配,LLM 生成正则不够可靠
精确字符串不依赖位置,不依赖格式——只要文本在文件中存在且唯一即可

精确字符串替换是对 LLM 最友好的编辑原语——模型只需要"看到什么就写什么",无需计算行号偏移。


2. Diff 生成:从替换操作到 Unified Diff

Edit 工具执行替换后,需要生成人类可读的 diff 展示。这涉及两个关键技术。

2.1 Marker-Based 位置追踪

多次替换时,前面的替换会改变后面的行号位置。Claude Code 用**标记法(Marker)**解决这个问题:

原始文件: "line 1\nfoo bar\nline 3\nfoo baz"

第一步:替换 "foo" → "[MARKER_1]replaced"
        → "line 1\n[MARKER_1]replaced bar\nline 3\n[MARKER_1]replaced baz"

第二步:替换 "line 3" → "[MARKER_2]LINE THREE"
        → "line 1\n[MARKER_1]replaced bar\n[MARKER_2]LINE THREE\n..."

定位阶段:找到每个 MARKER 的位置,计算其前面有多少个换行符
        → MARKER_1 在第 2 行,MARKER_2 在第 3 行

清理阶段:移除所有 MARKER,返回干净的结果文件
        → "line 1\nreplaced bar\nLINE THREE\nreplaced baz"
        → 修改行号: [2, 3]

Marker 格式:__REPLACE_MARKER_<9位随机串>_<计数器>__,确保不会与文件内容冲突。

2.2 Unified Diff 生成

替换完成后,用 diff 库对比替换前后的文件内容,生成标准 Unified Diff 格式:

diff
--- a/src/auth.ts
+++ b/src/auth.ts
@@ -1,3 +1,3 @@
 import { db } from './database';

-function login(user) {
+function login(user: User): Promise<Session> {
   const session = await db.createSession(user);

这就是用户在终端中看到的 diff 输出。

2.3 IDE 集成时的 ACP Diff 格式

当 Claude Code 通过 ACP(Agent Client Protocol)与 IDE(如 Cursor、Zed)集成时,diff 被转换为结构化的 ACP 格式:

json
{
  "type": "diff",
  "filePath": "src/auth.ts",
  "oldText": "function login(user) {",
  "newText": "function login(user: User): Promise<Session> {",
  "startLine": 3
}

IDE 客户端收到这个结构后,可以渲染为:

  • 侧边栏的高亮 diff 视图
  • 内联的添加/删除标记
  • 可点击的文件导航链接

2.4 Hunk 解析算法

从 Unified Diff 解析出 ACP 格式时,需要对每个 hunk 进行行分类:

Unified Diff 行前缀:
  "-" → 旧内容中被删除的行 → 放入 oldText
  "+" → 新内容中被添加的行 → 放入 newText
  " " → 上下文行(未变化)  → 同时放入 oldText 和 newText

3. /diff 交互式查看器

/diff 是 Claude Code 内建的交互式 diff 查看器,让用户在提交前审查所有修改。

3.1 功能

输入 /diff 后:

┌─────────────────────────────────────────────────┐
│  /diff - Interactive Diff Viewer                 │
│                                                  │
│  [← →] 切换视图:                                  │
│    • Git Diff(当前所有未提交的修改)                │
│    • Turn 1 Diff(第一轮 Claude 做的修改)          │
│    • Turn 2 Diff(第二轮 Claude 做的修改)          │
│    • Turn N Diff(第 N 轮...)                     │
│                                                  │
│  [↑ ↓] 浏览不同文件                                │
│                                                  │
│  显示格式: 标准 Unified Diff                       │
│    - 红色: 删除的行                                │
│    + 绿色: 新增的行                                │
│      白色: 上下文行                                │
└─────────────────────────────────────────────────┘

3.2 两种 diff 视图

视图数据来源用途
Git Diffgit diff(未提交的修改总和)查看 Claude 和你总共做了什么修改
Per-Turn Diff每轮 Agent Loop 中 Edit/Write 操作的记录查看 Claude 每一步具体改了什么

Per-Turn Diff 是 /diff 最有价值的能力——大规模重构时,你可以逐轮审查 Claude 的修改,而不是面对一个巨大的 git diff 无从下手。

3.3 实现原理

Claude Code 内部维护每轮操作的 diff 记录:

Turn 1: Edit("src/auth.ts", old1, new1) → diff_1
Turn 2: Edit("src/auth.ts", old2, new2) → diff_2
Turn 3: Write("src/new-file.ts", content) → diff_3
  ...

/diff 命令:
  ├── 读取 git diff HEAD(总 diff)
  ├── 读取每轮的 diff 记录
  └── 提供交互式 UI 切换浏览

4. Write 工具的 diff 处理

与 Edit(局部替换)不同,Write 是整个文件覆写。diff 处理方式也不同:

场景diff 行为
创建新文件diff 展示所有行为新增(全绿)
覆写已有文件diff 对比旧文件 vs 新内容(标准 diff)

在 ACP 格式中,Write 操作用 oldText: null 表示"这是整个文件的写入,不是局部编辑"。


5. 跨平台兼容

diff 系统需要处理不同操作系统的行尾差异:

系统行尾处理
Unix/macOS\n标准
Windows\r\n标准化为 \n 后匹配
经典 Mac\r标准化为 \n 后匹配
混合混合用 `/\r\n

已知问题:Windows/SSH 场景下 CRLF 行尾可能导致多行编辑的 diff 预览匹配失败(old_string 中的 \n 与文件中的 \r\n 不匹配)。


6. 总结:三层 diff 能力

┌─────────────────────────────────────────────────────┐
│  第一层:Edit 工具 —— 精确字符串替换                    │
│  • 不用行号、不用正则,用文本本身定位                    │
│  • 精确匹配 → 模糊匹配 → 报错自愈 三级策略              │
│  • Marker-Based 位置追踪解决多步编辑行号偏移             │
├─────────────────────────────────────────────────────┤
│  第二层:Diff 生成 —— 从替换结果到可读 diff             │
│  • 替换前后文件对比 → Unified Diff 格式                 │
│  • Hunk 解析 → ACP 结构化格式(供 IDE 渲染)            │
│  • 每轮操作独立记录 diff(Per-Turn Diff)               │
├─────────────────────────────────────────────────────┤
│  第三层:/diff 查看器 —— 交互式审查                     │
│  • Git Diff 视图(总修改)+ Per-Turn 视图(逐轮修改)   │
│  • 方向键切换文件和轮次                                 │
│  • 提交前审查,防止大规模重构中的意外修改                 │
└─────────────────────────────────────────────────────┘

核心设计选择:用"精确字符串替换"代替"行号+diff/patch",是 Claude Code 对 LLM 能力特点的精准适配——LLM 擅长理解和生成文本内容,但不擅长维护精确的数值位置(行号)。这个选择让 Edit 工具的成功率大幅高于基于行号的方案,也使得错误自愈变得简单(重新读文件即可,不需要重新计算偏移量)。

附:Unified Diff 与 ACP Diff 格式详解

一、Unified Diff 格式

Unified Diff 是 git diffdiff -u 和大多数代码审查工具使用的标准 diff 格式。Claude Code 的 Edit/Write 工具在内部生成的 diff 也采用这个格式。

1. 完整结构
diff
diff --git a/src/auth.ts b/src/auth.ts
index 3a4b5c6..7d8e9f0 100644
--- a/src/auth.ts
+++ b/src/auth.ts
@@ -10,7 +10,9 @@ import { db } from './database';

 export class AuthService {
   private tokenSecret: string;
-  private sessionTimeout = 3600;
+  private sessionTimeout: number;
+  private maxRetries = 3;

-  constructor(secret: string) {
+  constructor(secret: string, timeout = 3600) {
     this.tokenSecret = secret;
+    this.sessionTimeout = timeout;
   }
2. 逐行解读

文件头(Header)

--- a/src/auth.ts            ← 原文件(修改前)
+++ b/src/auth.ts            ← 新文件(修改后)
  • --- 标记原文件路径(a/ 前缀是 git 约定,表示 "before")
  • +++ 标记新文件路径(b/ 前缀表示 "after")
  • 实际的 diff -u 命令还会在路径后附带时间戳

Hunk 头(Hunk Header)

@@ -10,7 +10,9 @@ import { db } from './database';

解读:

部分含义
@@Hunk 的开始标记
-10,7原文件从第 10 行开始,展示 7 行
+10,9新文件从第 10 行开始,展示 9 行(因为新增了 2 行)
@@Hunk 头结束标记
import { db }...最近的函数/类签名(可选,由 git 自动添加,便于定位上下文)

行内容标记

前缀含义颜色(终端)
(空格)上下文行——两个文件中都有的未修改行白色/灰色
-删除行——只存在于原文件中红色
+新增行——只存在于新文件中绿色

"修改"的表示:没有专门的"修改"标记。一行被修改表示为先删后增

diff
-  private sessionTimeout = 3600;        ← 删除旧行
+  private sessionTimeout: number;        ← 新增新行
3. 单行号 vs start,count 格式

Hunk 头中的行号有两种写法:

写法含义示例
-10,7从第 10 行开始,共 7 行标准多行 hunk
-10等价于 -10,1,只有 1 行单行变更
-0,0空文件(新增文件时原文件为空)+++ b/newfile.ts
4. 上下文行的作用

默认展示变更行上下各 3 行的上下文。上下文行的目的:

  1. 定位:帮助读者理解变更发生在代码的什么位置
  2. 应用 patchgit apply 靠上下文行来定位 patch 的应用位置(即使行号因其他修改而偏移)
  3. 冲突检测:上下文不匹配说明文件已被修改,patch 可能不适用
5. 多个 Hunk

一个文件中如果有多处不连续的修改,会产生多个 Hunk,每个都有独立的 @@ ... @@ 头:

diff
--- a/config.ts
+++ b/config.ts
@@ -5,3 +5,4 @@
 const PORT = 3000;
+const HOST = '0.0.0.0';
 const DEBUG = false;
@@ -20,4 +21,4 @@
 export function getConfig() {
-  return { port: PORT, debug: DEBUG };
+  return { port: PORT, host: HOST, debug: DEBUG };
 }

二、ACP Diff 格式

ACP(Agent Client Protocol)是 Claude Code 与 IDE(如 Cursor、Zed)集成时使用的通信协议。Unified Diff 是文本格式,适合终端显示;ACP Diff 是结构化 JSON 格式,适合 IDE 渲染。

1. ACP Diff 结构
json
{
  "type": "diff",
  "filePath": "src/auth.ts",
  "oldText": "  private sessionTimeout = 3600;\n\n  constructor(secret: string) {\n    this.tokenSecret = secret;\n  }",
  "newText": "  private sessionTimeout: number;\n  private maxRetries = 3;\n\n  constructor(secret: string, timeout = 3600) {\n    this.tokenSecret = secret;\n    this.sessionTimeout = timeout;\n  }",
  "startLine": 12
}
字段类型含义
type"diff"标识这是一个 diff 类型的内容块
filePathstring被修改的文件路径
oldTextstring | null被替换的原文本(null 表示整个文件写入,非局部编辑)
newTextstring替换后的新文本
startLinenumber变更起始的行号
2. 从 Unified Diff 到 ACP Diff 的转换

Claude Code 的 Edit 工具执行后,会生成 Unified Diff。当通过 ACP 协议发送给 IDE 时,需要做 Hunk 解析转换:

Unified Diff (文本)              ACP Diff (结构化)
─────────────────               ──────────────────
@@ -12,5 +12,7 @@              {
-  timeout = 3600;                 "filePath": "src/auth.ts",
+  timeout: number;                "oldText": "  timeout = 3600;...",
+  maxRetries = 3;                 "newText": "  timeout: number;...",
                                   "startLine": 12
                    ───────→    }

Hunk 解析算法

遍历 Hunk 中的每一行,根据前缀分流:

对于 Hunk 中的每一行:
  前缀 = 行的第一个字符
  内容 = 行去掉前缀后的文本

  if 前缀 == "-":
      追加到 oldText
  elif 前缀 == "+":
      追加到 newText
  elif 前缀 == " " (空格):
      同时追加到 oldText 和 newText    ← 上下文行两边都要
3. Edit vs Write 的 ACP Diff 区别
操作oldTextnewText含义
Edit(局部替换)被替换的原文片段替换后的新文片段IDE 展示局部 diff
Write(整个文件创建)null整个文件内容IDE 展示为新增文件
Write(整个文件覆写)原文件完整内容新文件完整内容IDE 展示全文件 diff

oldText: null 是一个特殊信号——告诉 IDE 客户端"这不是局部编辑,而是整个文件的写入",IDE 可以据此选择不同的展示方式(如用"新增文件"视图而非 diff 视图)。

4. IDE 客户端如何渲染 ACP Diff

IDE 收到 ACP Diff 后,可以渲染为多种视图:

ACP Diff JSON

  ├── 侧边栏 Side-by-Side 视图
  │     左侧: oldText(红色背景高亮删除部分)
  │     右侧: newText(绿色背景高亮新增部分)

  ├── 内联 Inline 视图
  │     同一列中交替显示删除行和新增行

  ├── 审批弹窗
  │     显示 diff + [Accept] [Reject] 按钮

  └── 文件导航
        点击 filePath 跳转到对应文件的 startLine 位置

三、两种格式对比

维度Unified DiffACP Diff
格式纯文本结构化 JSON
使用场景终端、git、代码审查IDE 集成、Agent-IDE 通信
信息粒度行级(每行标记 +/-/空格)块级(oldText/newText 整块)
位置信息Hunk 头 @@ -start,count +start,count @@startLine 字段
多处修改多个 Hunk 拼接多个独立的 diff 对象
是否标准是(POSIX/GNU 标准)否(ACP 协议专用)
生成方diff 库 / git diffClaude Code CLI 从 Unified Diff 转换
消费方人类阅读 / git applyIDE 客户端渲染

四、在 Claude Code 数据流中的位置

Edit 工具执行替换

  ├── 替换前文件内容 + 替换后文件内容


diff 库对比 → 生成 Unified Diff

  ├── 终端模式 → 直接渲染(红/绿高亮)→ 用户在终端中看到

  └── IDE 模式 → Hunk 解析 → 转为 ACP Diff JSON

                                └── 通过 ACP 协议发送给 IDE

                                      └── IDE 渲染为侧边栏/内联/审批视图

Q7: Claude Code 在检索方面的原理和逻辑(索引?grep?正则?)

1. 核心理念:"Search, Don't Index"

Claude Code 的检索哲学可以用一句话概括:

不建索引,不用 RAG,不用 Embedding——全靠 grep 暴力搜索 + 模型自主决策搜索策略。

这个设计选择有明确的历史背景:Claude Code 早期版本曾尝试用 Voyage Embeddings 做 RAG 式的语义代码搜索,但 Anthropic 内部基准测试显示,基于 ripgrep 的 Agent 式搜索性能更优,且运维复杂度更低——不需要索引同步,不需要外部 Embedding 服务,没有安全隐患。

来源:Latent Space Podcast - Claude Code(2025年5月)


2. 两个内建检索工具

Claude Code 只有两个内建检索工具,都极其简单:

2.1 Grep —— 内容搜索(基于 ripgrep)

参数:
  - pattern (必选): 正则表达式模式
  - path (可选): 搜索路径(默认项目根目录)
  - glob (可选): 文件过滤模式(如 "*.ts")
  - type (可选): 文件类型(如 "js", "py")
  - output_mode (可选): content / files_with_matches / count
  - multiline (可选): 是否跨行匹配
  - -A/-B/-C (可选): 上下文行数

底层: ripgrep (rg)

核心特性

特性说明
基于 ripgrepRust 实现,极快,支持并行搜索
正则表达式完整正则语法(非简单字符串匹配)
结果上限输出上限为数千行,防止撑爆上下文
不需要索引每次搜索都是实时遍历文件系统
自动忽略遵循 .gitignore 规则,自动跳过 node_modules

典型调用

json
{
  "name": "Grep",
  "input": {
    "pattern": "function\\s+authenticate",
    "glob": "*.ts",
    "output_mode": "content",
    "-C": 3
  }
}

2.2 Glob —— 文件名搜索(基于文件系统)

参数:
  - glob_pattern (必选): 匹配模式(如 "**/*.test.ts")
  - target_directory (可选): 搜索目录

底层: 文件系统 glob 匹配

核心特性

特性说明
路径模式匹配**/*.tssrc/**/test_*.py 等标准 glob 语法
按修改时间排序最近修改的文件排在前面(更可能是相关文件)
自动递归**/ 开头的模式自动添加 **/ 前缀
不搜内容只匹配文件名/路径,不读取文件内容

3. 没有索引,搜索是怎么工作的?

这是最关键的问题。传统 IDE(如 VS Code)和 RAG 系统都依赖预建索引来加速搜索。Claude Code 完全不建索引——那它如何在大型代码库中高效检索?

答案是:靠模型的推理能力驱动多轮渐进式搜索

3.1 Agent 式搜索流程

用户: "找到用户认证的实现代码"

  │ Claude 不知道代码在哪,开始推理搜索策略


Turn 1: Glob("**/*auth*")
  → 发现: src/auth/login.ts, src/auth/middleware.ts, tests/auth.test.ts

  │ Claude 分析文件名,决定先看哪个


Turn 2: Read("src/auth/login.ts")
  → 看到 import { validateToken } from '../utils/token'

  │ Claude 发现还有相关文件需要看


Turn 3: Grep("validateToken", glob="*.ts")
  → 找到 src/utils/token.ts 中的实现

  │ Claude 找到了完整的认证链路


Turn 4: Read("src/utils/token.ts")
  → 理解了完整实现

关键洞察:搜索不是一次性操作,而是多轮推理驱动的渐进式探索。模型根据每一步的结果动态调整搜索策略——这就是"Agentic Search"的含义。

3.2 对比:索引搜索 vs Agent 搜索

维度传统索引搜索(RAG/Embedding)Claude Code Agent 搜索
前置准备需要建索引(耗时、需同步)零准备,开箱即用
存储开销Embedding 索引占用磁盘/内存无额外存储
同步问题文件修改后索引可能过期每次搜索都是实时结果
安全性Embedding 可能泄露代码语义不外传任何数据
语义理解依赖 Embedding 质量依赖 LLM 推理能力
搜索成本低(已索引)高(每次都遍历 + 多轮 LLM 调用)
搜索精度Embedding 有"语义漂移"风险精确匹配 + 模型判断

权衡:Claude Code 用更多的 token 和延迟换取了简洁性、实时性和安全性


4. 模型如何决定搜索策略?

没有硬编码的搜索路由——模型自己判断该用什么工具、什么参数。这个能力来自训练数据和系统提示中的指引。

4.1 系统提示中的搜索指引

Claude Code 的系统提示包含类似以下的指引(基于行为推断):

- 优先使用 Grep 做内容搜索(基于 ripgrep,速度最快)
- 使用 Glob 查找文件(按文件名/路径模式匹配)
- 不要用 Bash 的 find/grep 命令——用内建的 Grep/Glob 工具
- 搜索前先想想:文件可能叫什么名字?代码中可能用什么变量名?
- 搜索结果太多时,缩小范围(加 glob 过滤、限制路径)
- 搜索结果太少时,放宽条件(去掉过滤、换关键词)

4.2 模型的搜索决策树

任务: 找到某段代码

  ├── 知道文件名?─── 是 ──→ Read(文件路径)

  ├── 知道文件名模式?── 是 ──→ Glob("**/pattern*")
  │                              → 从结果中选择 → Read

  ├── 知道代码内容关键词?── 是 ──→ Grep("关键词")
  │                                → 定位文件和行号 → Read

  ├── 只有模糊描述?── 是 ──→ Glob 找可能的文件
  │                          → Read 几个最可能的
  │                          → 根据 import/引用线索 Grep 跟踪

  └── 完全不知道?── 是 ──→ Glob("**/*") 看项目结构
                           → 逐目录探索
                           → 或委派给 Explore 子智能体

4.3 Explore 子智能体——专用搜索 Agent

对于大规模代码探索,Claude 会委派给 Explore 子智能体

特性说明
模型Haiku(快速、低成本)
工具只读(Read, Glob, Grep),不可修改文件
独立上下文搜索结果不占用主对话的上下文空间
返回摘要只把关键发现返回主对话

三个彻底程度:

级别行为
quick定向搜索,已知位置的快速查找
medium跨多个目录搜索
very thorough全面分析,跨多个位置和命名约定

5. MCP Tool Search:MCP 工具的"懒加载"检索

这不是代码检索,而是工具定义的检索——但它是 Claude Code 检索能力的重要组成部分。

问题:安装大量 MCP Server 后,所有工具的 JSON Schema 都被加载到上下文中,可能消耗 50K+ token(有开发者报告 66,000+ token),还没开始干活上下文就满了。

解决方案:MCP Tool Search(懒加载)

无 Tool Search(急加载):
  启动时加载所有 100+ 工具定义 → 上下文占用 ~55K tokens

有 Tool Search(懒加载):
  启动时只加载 1 个 search 工具 → 占用 ~500 tokens
  模型需要某能力时 → search 工具用 BM25/正则 找到匹配工具
  按需加载匹配到的工具定义 → 每个 ~600 tokens

效果(Anthropic 官方基准):

  • 上下文占用减少最高 95%
  • 工具调用准确率提升 15%(噪声更少,模型更容易选对工具)
  • 配合 Token 缓存,成本降低 最高 90%

配置:

bash
ENABLE_TOOL_SEARCH=auto      # 默认(10% 上下文阈值时启用)
ENABLE_TOOL_SEARCH=auto:5    # 激进(5% 阈值)
ENABLE_TOOL_SEARCH=auto:20   # 保守(20% 阈值)
ENABLE_TOOL_SEARCH=true      # 始终启用
ENABLE_TOOL_SEARCH=false     # 禁用(全量加载)

6. 社区扩展:当 Grep 不够用时

Claude Code 内建的 Grep + Glob 覆盖了约 90% 的搜索需求。剩下 10% 的场景可以通过社区工具补充:

工具类型能力速度适用场景
Grep(内建)ripgrep精确文本/正则匹配~20ms90% 的日常搜索
Glob(内建)文件系统文件名模式匹配极快找文件
ast-grepPluginAST 结构化代码搜索~200ms大规模重构/迁移
SerenaMCP符号级导航(函数/类定义、引用)~100ms重构、符号跳转
grepaiMCP语义搜索 + 调用链分析~500ms不知道确切文本时的模糊搜索

决策原则:先用 Grep(最快),不够再升级到专用工具。

搜索决策流:

"我知道确切的文本" ────→ Grep(正则匹配)

                            ├── 找到 → 完成

                            └── 没找到

"我知道函数/类名" ─────→ Serena(符号导航)

"我知道代码结构模式" ──→ ast-grep(AST 匹配)

"我只有模糊描述" ─────→ grepai(语义搜索)

7. 为什么这种"笨"方法反而更好?

从工程角度看,Claude Code 的"每次都暴力搜索"方法看起来很"笨"——但实际效果超过了精心设计的索引系统。原因有三:

1. 实时性

索引有同步延迟——文件修改后索引可能过期。Agent 在编辑过程中频繁搜索(编辑→搜索→编辑→搜索),如果搜索结果是过期的索引,会导致连锁错误。ripgrep 每次都读实际文件,永远不会过期。

2. ripgrep 本身就够快

ripgrep 用 Rust 写的,利用 SIMD 指令和内存映射,在中大型代码库(10 万行级别)上单次搜索通常在 20-50ms 内完成。即使多轮搜索,总延迟也远小于一次 LLM API 调用的延迟。搜索本身不是瓶颈。

3. 模型推理弥补了"无索引"的劣势

传统搜索的索引解决的是"不知道在哪"的问题。但 LLM 可以通过推理来缩小搜索范围——它会根据项目结构、命名约定、import 路径来猜测代码位置,然后用 Grep 验证。这相当于用 LLM 的推理能力替代了索引的定位能力


8. 总结

Claude Code 检索架构:

┌────────────────────────────────────────────────────────┐
│  不建索引、不用 RAG、不用 Embedding                      │
│                                                        │
│  ┌─────────────────────────────────────────────────┐   │
│  │ Layer 1: 内建工具                                │   │
│  │  Grep(ripgrep)—— 内容搜索,正则匹配             │   │
│  │  Glob            —— 文件名搜索,路径模式匹配       │   │
│  └─────────────────────────────────────────────────┘   │
│                       ▲                                │
│                       │ 模型自主决策何时调用、用什么参数  │
│                       │                                │
│  ┌─────────────────────────────────────────────────┐   │
│  │ Layer 2: Agent 推理                              │   │
│  │  多轮渐进式搜索:Glob→Read→Grep→Read→...         │   │
│  │  根据每步结果动态调整策略                          │   │
│  │  失败自愈:换关键词、缩放范围、换工具               │   │
│  └─────────────────────────────────────────────────┘   │
│                       ▲                                │
│                       │ 大规模探索时委派                 │
│                       │                                │
│  ┌─────────────────────────────────────────────────┐   │
│  │ Layer 3: Explore 子智能体                        │   │
│  │  独立上下文,不占主对话空间                        │   │
│  │  Haiku 模型,快速低成本                           │   │
│  │  只返回摘要                                      │   │
│  └─────────────────────────────────────────────────┘   │
│                       ▲                                │
│                       │ 可选扩展                        │
│                       │                                │
│  ┌─────────────────────────────────────────────────┐   │
│  │ Layer 4: 社区扩展(MCP/Plugin)                   │   │
│  │  ast-grep(AST 搜索)、Serena(符号导航)          │   │
│  │  grepai(语义搜索)                               │   │
│  └─────────────────────────────────────────────────┘   │
└────────────────────────────────────────────────────────┘

核心哲学: 用模型的推理能力替代索引的定位能力
         用 ripgrep 的原始速度保证搜索效率
         用 Agent Loop 的多轮机制实现渐进式精确定位

Q8: Claude Code 是如何实现"用户选中内容"而非输出内容的能力的?

这个问题可以拆解为:用户在编辑器中选中一段代码,Claude 是怎么"看到"的? 这个过程涉及三层机制:IDE 捕获选中 → 注入到消息上下文 → 模型感知并响应


1. 两种运行模式的差异

Claude Code 运行在两种模式下,获取用户选中内容的方式不同:

模式能否看到用户选中?机制
纯 CLI 模式(终端中运行)不能自动看到需要用户手动 @ 引用文件,或用管道传入
IDE 集成模式(VS Code/Cursor 扩展)能自动看到IDE 扩展捕获选中内容,自动注入到上下文

2. IDE 模式:选中内容是如何到达模型的

2.1 整体数据流

用户在编辑器中选中一段代码


IDE 扩展捕获选中信息
  ├── 文件路径: src/auth.ts
  ├── 选中行范围: 第 15-25 行
  ├── 选中文本内容: "function login(user) { ... }"
  ├── 光标位置: 第 20 行


打包为结构化元数据,注入到发送给模型的消息中

  ├── <open_and_recently_viewed_files> 块
  │     → 当前打开的文件、最近查看的文件列表

  ├── <attached_files> 块
  │     └── <code_selection path="src/auth.ts" lines="15-25">
  │            → 选中的具体代码内容

  └── 用户的实际输入文本


Claude 模型收到的完整消息结构:
  [系统提示]
  + [open_and_recently_viewed_files 元数据]
  + [attached_files / code_selection 内容]
  + [用户文本输入]

2.2 注入的元数据格式

IDE 扩展自动在用户消息前附加以下结构化信息:

打开和最近查看的文件列表

xml
<open_and_recently_viewed_files>
Recently viewed files (recent at the top, oldest at the bottom):
- /data/workspace/src/auth.ts (total lines: 150)
- /data/workspace/src/config.ts (total lines: 80)
- /data/workspace/tests/auth.test.ts (total lines: 200)

Files that are currently open and visible in the user's IDE:
- /data/workspace/src/auth.ts (currently focused file, cursor is on line 20, total lines: 150)
</open_and_recently_viewed_files>

选中的代码内容

xml
<attached_files>
<code_selection path="/data/workspace/src/auth.ts" lines="15-25">
   15|export async function login(user: string, password: string) {
   16|  const hash = await bcrypt.hash(password, 10);
   17|  const record = await db.users.findOne({ user });
   18|  if (!record) {
   19|    throw new AuthError('User not found');
   20|  }
   21|  if (!await bcrypt.compare(password, record.hash)) {
   22|    throw new AuthError('Invalid password');
   23|  }
   24|  return createSession(record.id);
   25|}
</code_selection>
</attached_files>

注意:代码内容包含行号前缀15|),这让模型知道代码在文件中的精确位置。

2.3 模型如何理解这些元数据

模型的系统提示中包含类似以下的指引:

用户消息可能附带以下自动上下文:
- <open_and_recently_viewed_files>: 用户当前打开的文件和最近查看的文件
- <attached_files> 中的 <code_selection>: 用户在编辑器中选中的代码片段

这些信息可能与当前任务相关,也可能不相关。如果需要获取文件内容,请使用 Read 工具。

所以模型能够区分"用户输入的文本"和"IDE 自动注入的上下文",并据此判断用户意图。例如:

用户选中了 login 函数 + 输入 "这个函数有什么安全问题?"
  → 模型看到 code_selection 中的 login 函数代码
  → 结合用户问题,分析该函数的安全隐患

3. CLI 模式:用户如何引用内容

纯 CLI 模式没有 IDE 扩展帮忙自动捕获选中,用户需要主动引用:

3.1 @ 引用文件

> 解释 @src/auth.ts 中的 login 函数

  ↓ Claude Code 内部处理:

  1. 解析 @src/auth.ts 为文件引用
  2. 自动 Read 该文件内容
  3. 将内容注入到对话上下文中
  4. 模型基于文件内容回答问题

@ 引用支持:

引用方式示例说明
文件名@auth.ts模糊匹配,自动补全
路径@src/auth/login.ts精确路径
带行号@auth.ts#15-25指定行范围(IDE 模式中 Option+K 自动生成)
文件夹@src/components/引用整个目录(尾部加 /
子智能体@"code-reviewer (agent)"指定子智能体执行

3.2 管道模式

bash
# 将命令输出作为上下文
git diff | claude "审查这些变更"

# 将文件内容作为上下文
cat error.log | claude "分析这个错误"

# 将文件列表作为上下文
find . -name "*.ts" | head -20 | claude "这个项目的结构是什么"

管道内容被注入为用户消息的一部分,模型可以直接看到。

3.3 ! 前缀执行命令

! git status

→ 直接执行命令,输出注入到对话上下文
→ 不需要 Claude 审批
→ 相当于"让 Claude 看到这个命令的输出"

3.4 粘贴图片

Ctrl+V  →  粘贴剪贴板中的图片
         →  显示为 [Image #N] 标记
         →  图片以 base64 编码注入到消息中
         →  模型可以看到和分析图片内容

4. IDE 扩展的关键快捷键

快捷键平台功能
Option+K / Alt+KMac / Win·Linux将当前选中的代码插入为 @file.ts#5-10 引用
Cmd+Esc / Ctrl+EscMac / Win·Linux在编辑器和 Claude 输入框之间切换焦点
Shift+拖拽通用从文件资源管理器拖拽文件到输入框作为附件
选中代码后直接提问通用Claude 自动看到选中的文本(无需额外操作)

输入框底部有一个选中指示器,显示当前选中了多少行。点击可以切换是否让 Claude 看到选中内容(眼睛/禁眼图标)。


5. 技术实现原理

5.1 IDE 扩展 → CLI 的通信

VS Code 扩展本质上是 CLI 的 GUI 封装。扩展通过以下方式将上下文传递给底层 CLI 进程:

VS Code 扩展

  ├── 监听编辑器事件:
  │     • onDidChangeTextEditorSelection  → 捕获选中变化
  │     • onDidChangeActiveTextEditor     → 捕获当前文件切换
  │     • onDidOpenTextDocument           → 捕获文件打开

  ├── 收集上下文信息:
  │     • 当前文件路径 + 光标位置
  │     • 选中的文本范围和内容
  │     • 最近打开/查看的文件列表
  │     • 当前打开的所有编辑器标签

  └── 注入到发送给 CLI/API 的消息中:
        • 作为用户消息的前缀元数据
        • 模型看到的是一条包含代码上下文的完整消息

5.2 为什么是"注入元数据"而非"调用工具"

一个关键的设计选择:选中内容是作为消息上下文注入的,而不是让模型先调用 Read 工具去读文件。

方式优点缺点
注入元数据(实际方案)零延迟,模型第一轮就能看到选中内容即使不相关也占用上下文
工具调用按需读取,不浪费上下文需要额外一轮 API 调用,增加延迟

系统提示中有提醒:"这些文件可能与当前对话相关,也可能不相关"——让模型自己判断是否需要关注这些上下文。

5.3 行号的双重作用

注入的代码包含行号前缀(如 15|export async function login...),这有两个作用:

  1. 模型定位:模型知道代码在文件中的精确位置,如果需要编辑可以用 Edit 工具精准定位
  2. 系统提示指引:系统提示中说明"行号是元数据,不是代码的一部分",防止模型在生成代码时也带上行号
<inline_line_numbers>
代码块中可能包含 LINE_NUMBER|LINE_CONTENT 格式的行号。
将 LINE_NUMBER| 前缀视为元数据,不要视为实际代码的一部分。
</inline_line_numbers>

6. 总结

"用户选中内容"的实现本质:

不是模型主动"看到"用户屏幕

而是 IDE 扩展监听编辑器事件

将选中信息打包为结构化元数据

作为用户消息的前缀注入到 API 请求中

模型从消息中解析出选中内容

结合用户的文本问题进行响应

核心机制非常简单——IDE 扩展充当了用户视觉状态到文本上下文的翻译器。模型本身没有"看屏幕"的能力,一切都是通过文本注入实现的。

这也是为什么纯 CLI 模式下需要用 @ 手动引用——没有 IDE 扩展帮你自动捕获,用户必须自己把上下文"告诉" Claude。