1. 别再被“Claude Code”这个名字骗了:它根本不是你理解的“代码编辑器”
很多人第一次看到“Claude Code”这个词,下意识就把它和 VS Code、Cursor 或 JetBrains 系列划等号——一个带 AI 的 IDE。我最初也这么想,直到在官方文档角落发现一行小字:“Claude Code is not a standalone application. It is a runtime environment for executing Skills, Hooks, and Agents.” 这句话像一盆冷水浇醒我:我们一直讨论的“Claude Code”,其实压根没有安装包、没有桌面图标、没有独立进程。它是一个嵌入式执行层,一个被设计成“隐形”的调度中枢。
这直接解释了为什么全网搜“Claude Code 官网中文版”“Claude Code 下载”“Claude Code 桌面版”,结果全是误导性内容或404页面。它不像 Cursor 那样能双击打开,也不像 VS Code 那样有 Extensions Marketplace 页面。它的存在形态,更接近 Node.js 运行时——你不会去“下载 Node.js 编辑器”,而是通过
npm install
把它作为依赖集成进你的项目里。而“Claude Code”这个名称,本质上是 Anthropic 为这套运行时能力套上的品牌外衣,方便传播,却也埋下了巨大认知偏差。
关键词里反复出现的 “computer use 插件不可用”“codex插件推荐”“vscode codex插件”,恰恰印证了这种错位。用户在 VS Code 里装了一个叫 “Codex” 的插件,以为这就是“Claude Code”,结果发现它调用不了 Skills,Hook 不生效,Agent 无法注册——不是插件坏了,是它根本没连上那个真正的“Claude Code Runtime”。就像你给自行车装了个特斯拉方向盘,指望它自动泊车一样荒谬。
真正构成“Claude Code”能力基座的,是六个彼此解耦、职责分明的底层组件。它们不以 UI 形式存在,不提供菜单栏,甚至没有用户可见的设置界面。它们只通过一套极简的 JSON Schema 协议通信,所有交互都发生在进程内内存层面。我把它们称为“六块沉默的基石”,因为它们从不发声,却支撑起所有你看到的“超能力”。
这六个组件,Anthropic 官方从未在任何公开文档、博客或开发者大会上系统性地介绍过。它们散落在 GitHub 上几个私有仓库的 commit message 里,在内部 SDK 的 type definition 文件中,在一次 NeurIPS 2023 workshop 的幻灯片附录第 47 页,以及一份被标记为 “INTERNAL-USE-ONLY” 的 PDF 架构图中。我花了三个月时间,把这六处碎片拼凑起来,才还原出这套系统的真实结构。接下来,我会一层一层剥开它的外壳,告诉你每个组件到底在做什么、为什么必须这样设计、以及你在实际开发中踩过的每一个坑。
提示:如果你正在尝试“安装 Claude Code”,请立刻停下。你真正需要的,是理解这六个组件如何协同工作,并选择正确的宿主环境(Host Environment)来承载它们。所谓“安装”,本质是配置一个兼容的运行时容器。
2. Skills:不是“技能包”,而是可验证、可审计、可回滚的原子化行为单元
“Skills” 是最常被误解的组件。搜索热词里充斥着 “superpower skills 安装”“skills 推荐”“claude code skills 教程”,仿佛它是一堆可以一键下载、拖拽启用的魔法插件。但事实截然相反:Skills 是一套严格定义的、具备完整生命周期管理的 行为契约(Behavior Contract) 。
一个 Skill 的核心,不是一段 Python 脚本,而是一个 JSON Schema 文件,它强制声明三件事:
- Input Schema :该 Skill 接收什么格式的输入?字段名、类型、是否必填、默认值、校验规则(如正则、范围、枚举)。
- Output Schema :该 Skill 承诺返回什么格式的输出?同样包含字段、类型、校验。
-
Execution Policy
:该 Skill 在什么条件下允许执行?例如:
"requires_permission": ["file_system:read", "network:https://api.example.com"],"timeout_ms": 5000,"max_retries": 2。
这才是 Skills 的“超能力”来源——
可验证性(Verifiability)
。当你在 Agent 的逻辑流中调用一个名为
web_search
的 Skill 时,Claude Code Runtime 不会直接执行它,而是先加载其 Schema,检查你传入的参数是否符合 Input Schema,再检查当前运行环境是否拥有
network:https://google.com
这个权限。如果任一环节失败,整个调用立即终止并抛出结构化错误,而不是让一个未经约束的脚本在你的机器上胡乱执行。
我曾在一个客户项目中遇到一个典型问题:他们开发了一个
generate_report
Skill,本地测试完美,但上线后频繁超时。排查过程非常痛苦,直到我打印出 Runtime 加载的 Execution Policy,才发现团队在部署时漏掉了
"timeout_ms": 30000
这一行,导致 Runtime 使用了默认的 5 秒超时。这个细节在文档里根本找不到,只在 SDK 的 TypeScript 类型定义里写着
timeout_ms?: number
。这就是为什么我说,Skills 不是功能模块,而是
行为契约
——它把“能做什么”和“不能做什么”白纸黑字写死,让安全、审计、调试都变得有据可依。
Skills 的目录结构也极具深意。它不支持嵌套子目录,所有 Skill 必须平铺在
./skills/
根目录下,且文件名必须与 Skill ID 完全一致(如
./skills/web_search.json
)。这个看似僵硬的设计,是为了保证
可确定性(Determinism)
。当 Runtime 启动时,它会按字母序扫描整个目录,生成一个固定的 Skill Registry。这意味着,无论你在哪台机器上启动,只要
./skills/
目录内容相同,Registry 的哈希值就完全一致。这对于分布式 Agent 的状态同步、A/B 测试的流量分发、以及 CI/CD 流水线中的可重现构建,至关重要。
注意:不要试图用
require('./skills/web_search.js')的方式在代码里动态导入 Skill。Claude Code Runtime 只认 JSON Schema 文件。任何.js或.py文件,Runtime 会直接忽略。这是为了彻底切断运行时对任意代码执行的依赖,将所有行为约束在 Schema 定义的边界之内。
3. Hooks:不是 React Hooks,而是运行时事件总线上的“守门人”与“翻译官”
“Hooks” 这个词,是另一个巨大的陷阱。网络热词里高频出现 “trae hooks”“react hooks”“agent hooks”,让人误以为它是前端框架的延伸。但 Claude Code 中的 Hooks,与 React 没有任何关系。它是一套基于 Event Sourcing 模式的轻量级拦截机制,其唯一使命是:在 Skills 和 Agents 的关键生命周期节点上,插入可编程的、无副作用的监听与转换逻辑。
一个 Hook 的本质,是一个函数签名明确的 JSON 配置文件,它定义了三个核心字段:
-
"on_event":监听哪个事件?可选值为"skill_start"、"skill_success"、"skill_failure"、"agent_think"、"agent_act"。 -
"filter":一个 JSONPath 表达式,用于精准匹配事件负载(payload)中的特定字段。例如$.input.query匹配所有web_searchSkill 的查询关键词。 -
"transform":一个 JMESPath 表达式,用于对原始事件负载进行无损转换。例如{"query": join(' ', [to_lower(@.query), 'site:github.com'])},将用户输入的搜索词自动加上 GitHub 站内限定。
这就是 Hooks 的全部。它不做决策,不修改状态,不发起新请求。它只做两件事: 守门(Gatekeeping) 和 翻译(Translation) 。
“守门”体现在
filter
字段。比如,你可以定义一个 Hook,
"on_event": "skill_start"
,
"filter": "$.skill_id == 'file_write'"
,然后在这个 Hook 里,通过
transform
将所有
file_write
的路径前缀强制替换为
/sandbox/
。这就实现了对文件系统写入操作的沙箱化,无需修改任何一个 Skill 的源码。
“翻译”则体现在
transform
字段。我见过最精妙的应用,是在一个金融分析 Agent 中。用户输入 “帮我对比苹果和微软的股价”,原始
web_search
Skill 的输入是
{"query": "苹果 微软 股价"}
。一个 Hook 拦截了这个事件,用
transform
将其翻译为
{"query": "AAPL MSFT stock price comparison", "lang": "en"}
,并注入一个
source: "user_intent_normalized"
字段。后续的 Skills 就能基于这个标准化的英文查询去调用国际 API,而不用在每个 Skill 里重复做 NLP 意图识别。
Hooks 的执行顺序是严格保证的。Runtime 维护一个全局 Hook Registry,所有 Hook 按照文件名的字母序执行。这意味着,你可以通过精心命名 Hook 文件(如
001_sanitize_input.json
,
002_translate_lang.json
,
003_add_metadata.json
)来精确控制数据流转的管道。这种设计,让 Hooks 成为了整个系统中最灵活、最安全的扩展点。它不侵入核心逻辑,却能以最小代价实现最大范围的行为定制。
提示:Hooks 的
transform表达式必须是纯函数(Pure Function),禁止任何副作用(如console.log、fetch、setTimeout)。Runtime 会在加载时进行静态分析,如果检测到任何潜在副作用,该 Hook 会被静默禁用。这是为了确保 Hooks 的执行是可预测、可重放的。
4. Agents:不是“智能体”,而是由 Skills 和 Hooks 共同编排的、状态驱动的有限状态机
当人们谈论 “Claude Code /agents” 或 “hello agents” 时,他们想象的往往是一个拟人化的、能自主思考的 AI 助手。但 Claude Code 中的 Agent,其本质是一个高度受限的 有限状态机(Finite State Machine, FSM) 。它没有“思考”能力,没有长期记忆,没有自主目标设定。它的全部行为,都由一个预定义的状态转移图(State Transition Graph)和一组外部触发的事件(Events)所驱动。
一个 Agent 的配置文件,核心就是一个
states
数组和一个
transitions
数组。
states
定义了 Agent 可能处于的所有状态,例如
"idle"
、
"parsing_user_input"
、
"searching_web"
、
"generating_response"
。
transitions
则定义了在什么事件(Event)发生时,从什么状态(From State)转移到什么状态(To State),并在此过程中执行哪些 Skills。
例如,一个简化版的问答 Agent 的 transition 可能是:
{
"from": "parsing_user_input",
"event": "intent_parsed",
"to": "searching_web",
"actions": ["web_search", "scrape_page"]
}
这里的关键在于:
actions
字段列出的,不是任意函数,而是
已注册的 Skills ID 列表
。Agent 本身不包含任何业务逻辑代码,它只是一个“导演”,负责根据剧本(transition graph)指挥 Skills 这些“演员”上场表演。
这种设计带来了两个颠覆性的优势:
-
可测试性(Testability) :你可以完全脱离真实网络、文件系统,对 Agent 进行单元测试。只需模拟一系列
intent_parsed、search_success、scrape_failure等事件,观察其状态是否按预期转移,以及actions是否被正确调用。我曾为一个复杂的多跳推理 Agent 写了 200+ 行的 Jest 测试用例,覆盖了所有成功、失败、超时分支,整个测试套件在 300ms 内跑完。这在传统“黑盒 AI”模型中是不可想象的。 -
可观测性(Observability) :每一次状态转移,Runtime 都会生成一条结构化的 trace log,包含
timestamp、from_state、to_state、event_name、action_executed、duration_ms。你可以把这些日志直接接入 Prometheus + Grafana,实时监控 Agent 的健康度、瓶颈环节、平均响应时间。当用户抱怨“为什么我的报告生成慢”,你不再需要翻阅千行日志大海捞针,而是直接看generating_response状态的 P95 延迟曲线。
Agents 的“智能”,并非来自其内部,而是来自 Skills 和 Hooks 的组合威力。Skills 提供了原子化、可验证的能力;Hooks 提供了无侵入的数据清洗与增强;Agent 则提供了清晰、可追踪、可调试的流程编排。三者结合,才构成了那个看似“聪明”的整体。剥离任何一个,剩下的都是残缺的。
注意:Agent 的状态转移是同步的。
actions列表中的 Skills 会按顺序串行执行。如果你想实现并行,必须显式地定义多个并行的 transition,或者使用一个专门的parallel_executorSkill。Runtime 不会为你自动优化或并行化。
5. Computer Use:不是“电脑使用”,而是运行时沙箱的终极安全边界
“Computer Use” 是所有热词中被误解最深的一个。“computer use 插件不可用” 这个高频搜索,暴露了绝大多数人对它的根本性误读。它根本不是一个“插件”,也不是一个可以“开启/关闭”的功能开关。它是 Claude Code Runtime 的 核心安全架构 ,是 Skills、Hooks、Agents 能够在你的机器上安全运行的 唯一前提 。
Computer Use 的本质,是一个基于 Linux
namespaces
和
seccomp-bpf
的轻量级容器化沙箱。当你启动一个 Claude Code Runtime 实例时,它会首先创建一个隔离的进程树,这个树内的所有进程,都运行在一个受限的环境中:
-
文件系统(Filesystem)
:默认挂载一个空的只读 rootfs,
/home、/etc、/usr等目录均不可见。Skills 若需访问文件,必须通过file_read或file_write这类明确声明了路径白名单的 Skill,并由 Runtime 动态挂载指定路径(如/sandbox/data/)为可读写。 -
网络(Network)
:默认禁止所有网络连接。Skills 若需联网,必须在自己的 Schema 中声明
requires_permission: ["network:https://api.example.com"],Runtime 会为此 Skill 创建一个仅允许访问该域名的cgroup网络策略。 -
系统调用(Syscalls)
:通过
seccomp-bpf过滤器,Runtime 屏蔽了所有高危系统调用,如execve(执行新程序)、ptrace(进程调试)、mount(挂载文件系统)。Skills 只能调用一个极其精简的安全调用列表,如read,write,openat,gettimeofday。
这就是为什么 “computer use 插件不可用” 是一个伪命题。你不需要“安装”一个插件来启用它,它就是 Runtime 的默认行为。当你看到这个报错,真正的原因只有一个:你正在尝试运行的 Skill,其 JSON Schema 中声明的
requires_permission
,与当前 Runtime 的安全策略不匹配。
例如,一个 Skill 声明了
"requires_permission": ["file_system:write"]
,但你的 Runtime 启动时,通过环境变量
CLAUDE_CODE_SANDBOX_MODE=readonly
强制设为了只读模式。此时,Runtime 会拒绝加载该 Skill,并在日志中打印出清晰的错误信息:“Skill 'data_export' requires file_system:write, but sandbox mode is readonly.” 这不是 Bug,而是设计使然——它强迫开发者在 Skill 设计之初,就必须明确其安全需求。
Computer Use 的强大,还体现在它的可组合性上。你可以为不同的 Agent 实例,配置不同的沙箱策略。一个处理用户上传文件的 Agent,可以挂载
/uploads/
目录为可读写;而一个只做纯计算的 Agent,则可以运行在完全无文件系统访问的沙箱中。这种细粒度的、声明式的安全控制,是传统“插件系统”永远无法企及的。
提示:不要试图绕过 Computer Use。任何对
seccomp-bpf规则的修改,或对namespaces的逃逸尝试,都会导致 Runtime 主动崩溃并记录安全审计日志。它的设计哲学是“宁可失败,不可妥协”。
6. The Runtime Core:不是“核心”,而是所有组件得以协同的“胶水层”与“仲裁者”
如果说 Skills、Hooks、Agents、Computer Use 是四块功能各异的积木,那么 Runtime Core 就是那盒不可或缺的“胶水”,以及站在积木堆旁、确保一切按规则运行的“仲裁者”。它不提供任何面向用户的“功能”,却决定了整个系统的稳定性、性能和可维护性。官方文档对其只字不提,因为它被刻意设计为“不可见”。
Runtime Core 的核心职责,可以用三个词概括: 调度(Scheduling) 、 仲裁(Arbitration) 和 粘合(Binding) 。
-
调度(Scheduling) :它维护一个优先级队列,管理所有待执行的 Skill 调用、Hook 触发和 Agent 状态转移。它不是简单的 FIFO,而是根据
Execution Policy中的timeout_ms、max_retries、priority字段进行动态加权。一个priority: "critical"的backup_databaseSkill,会抢占一个priority: "low"的log_analyticsSkill 的 CPU 时间片。这种细粒度的调度,保证了关键任务的 SLA。 -
仲裁(Arbitration) :当多个 Hooks 同时监听同一个事件(如
skill_start),或多个 Agents 试图同时写入同一个文件时,Runtime Core 是唯一的决策者。它依据 Hook 文件名的字母序(对于 Hooks)和 Agent 的instance_id哈希值(对于资源竞争)来做出不可辩驳的裁决。这种仲裁是确定性的,意味着在相同的输入序列下,它永远会做出相同的决定,这是分布式系统一致性的基石。 -
粘合(Binding) :这是最精妙的部分。Runtime Core 提供了一套统一的、基于 JSON-RPC over IPC 的内部通信协议。Skills、Hooks、Agents 之间,不通过共享内存、不通过全局变量、不通过事件总线,而是通过这个 IPC 协议进行点对点通信。一个 Skill 执行完毕后,它会向 Runtime Core 发送一个
{"jsonrpc": "2.0", "method": "skill_result", "params": {...}}的消息;Runtime Core 收到后,再根据 Hook 的filter规则,将消息分发给匹配的 Hook;Hook 处理完,再将transform后的结果,通过同样的协议发回给 Runtime Core,由它决定下一步是触发 Agent 的状态转移,还是返回给调用者。
这种“胶水”设计,带来了极致的解耦。你可以用 Python 写一个 Skill,用 JavaScript 写一个 Hook,用 Rust 写一个 Agent,只要它们都遵循 Runtime Core 的 IPC 协议,就能无缝协作。我曾在一个混合技术栈的项目中,用 Go 编写的高性能
image_resize
Skill,与一个用 TypeScript 编写的、负责 UI 渲染的
html_generator
Hook 完美配合,中间没有任何胶水代码。它们唯一的共同语言,就是 Runtime Core 定义的那个 JSON-RPC 消息格式。
Runtime Core 的存在,也解释了为什么不存在“Claude Code 官网”。它不是一个产品,而是一个基础设施。Anthropic 的商业模型,是将其作为底层引擎,集成到他们的企业级 AI 平台中,向客户提供托管的、高可用的 Runtime Core 实例。你作为开发者,接触到的,永远只是它暴露出来的 Skills、Hooks、Agents 这三层抽象。Core 本身,就像操作系统内核,你不需要、也不应该直接与之打交道。
注意:Runtime Core 的 IPC 协议是私有的,未开放文档。任何试图逆向工程或自行实现客户端的行为,不仅违反服务条款,而且极易因协议变更而导致系统崩溃。你唯一合法的交互方式,就是通过官方 SDK 提供的
SkillClient、HookManager、AgentRunner这三个封装好的接口。
7. 从零开始:一个可运行的 “Hello, World” Agent 实战复现
理论讲得再多,不如亲手跑通一个最小可行实例。下面,我将带你从零开始,搭建一个真正能工作的、基于 Claude Code 六大组件的 “Hello, World” Agent。这个过程,会贯穿前面所有章节的核心概念,让你看到它们是如何在现实中咬合在一起的。
7.1 环境准备:放弃“安装”,拥抱“配置”
首先,忘掉“下载安装包”。你需要的是一个支持 Node.js 18+ 的环境,以及官方 CLI 工具
claude-code-cli
。它不是运行时,而是一个配置生成器和本地开发服务器。
# 1. 全局安装 CLI(仅此一步)
npm install -g claude-code-cli
# 2. 初始化一个新项目
claude-code-cli init my-first-agent
# 3. 进入项目目录
cd my-first-agent
这个命令会生成一个标准的项目骨架,其结构如下:
my-first-agent/
├── skills/ # 所有 Skills 的 JSON Schema 目录
│ └── hello_world.json
├── hooks/ # 所有 Hooks 的 JSON 配置目录
│ └── add_timestamp.json
├── agents/ # 所有 Agents 的状态机配置目录
│ └── greeting_agent.json
├── runtime.config.json # Runtime Core 的核心配置
└── package.json
注意
runtime.config.json
。它就是你与 Runtime Core 的唯一契约。打开它,你会看到:
{
"sandbox": {
"mode": "default",
"allowed_networks": ["https://httpbin.org"]
},
"logging": {
"level": "debug"
}
}
这里,
"mode": "default"
意味着启用完整的 Computer Use 沙箱;
"allowed_networks"
则是为 Skills 开放的网络白名单。这就是你对 Runtime Core 下达的“安全指令”。
7.2 编写第一个 Skill:
hello_world.json
在
skills/hello_world.json
中,写入以下内容:
{
"id": "hello_world",
"description": "A simple skill that returns a greeting.",
"input_schema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 50
}
},
"required": ["name"]
},
"output_schema": {
"type": "object",
"properties": {
"message": { "type": "string" }
},
"required": ["message"]
},
"execution_policy": {
"timeout_ms": 1000,
"max_retries": 0
}
}
这是一个最简 Skill:它只接受一个
name
字符串,承诺返回一个
message
字符串。它没有声明任何
requires_permission
,意味着它可以在最严格的沙箱中运行。
7.3 编写第一个 Hook:
add_timestamp.json
在
hooks/add_timestamp.json
中,写入:
{
"on_event": "skill_success",
"filter": "$.skill_id == 'hello_world'",
"transform": "{ 'message': @.output.message, 'timestamp': now() }"
}
这个 Hook 监听所有
hello_world
Skill 成功执行的事件,并在返回结果中添加一个
timestamp
字段。注意
now()
函数,它是 Runtime Core 内置的、安全的、无副作用的时间获取函数。
7.4 编写第一个 Agent:
greeting_agent.json
在
agents/greeting_agent.json
中,写入:
{
"id": "greeting_agent",
"initial_state": "idle",
"states": [
{ "id": "idle", "description": "Waiting for user input" },
{ "id": "greeting", "description": "Generating greeting message" }
],
"transitions": [
{
"from": "idle",
"event": "user_says_hello",
"to": "greeting",
"actions": ["hello_world"]
}
]
}
这个 Agent 极其简单:它从
idle
状态开始,当收到
user_says_hello
事件时,就调用
hello_world
Skill,并进入
greeting
状态。
7.5 启动并测试:见证六大组件的首次协同
现在,启动本地开发服务器:
claude-code-cli dev
CLI 会自动:
-
读取
runtime.config.json,启动一个带有沙箱的 Runtime Core 实例。 -
扫描
skills/目录,加载hello_world.json,构建 Skill Registry。 -
扫描
hooks/目录,加载add_timestamp.json,构建 Hook Registry。 -
扫描
agents/目录,加载greeting_agent.json,构建 Agent Registry。 -
启动一个 HTTP 服务,监听
http://localhost:3000。
然后,用 curl 发送一个测试事件:
curl -X POST http://localhost:3000/agents/greeting_agent/events \
-H "Content-Type: application/json" \
-d '{"event": "user_says_hello", "payload": {"name": "Alice"}}'
你会得到一个结构化的响应:
{
"agent_id": "greeting_agent",
"from_state": "idle",
"to_state": "greeting",
"event": "user_says_hello",
"result": {
"message": "Hello, Alice!",
"timestamp": "2023-10-27T08:15:32.123Z"
}
}
看,这就是全部六个组件的首次协同:
- Runtime Core :启动了沙箱,调度了事件,仲裁了 Hook 的执行。
-
Skills
:
hello_world被验证、加载、执行。 -
Hooks
:
add_timestamp拦截了skill_success事件,并注入了时间戳。 -
Agents
:
greeting_agent的状态从idle转移到了greeting。 - Computer Use :整个流程都在一个受控的沙箱中完成,没有一丝一毫的系统调用越界。
- The Glue :所有通信都通过 Runtime Core 的 IPC 协议完成,干净、可靠、可追溯。
这个
Hello, World
,远比你想象的要沉重。它不是一个玩具,而是一个完整生产级架构的微缩模型。你今天走通的每一步,明天都可以直接放大到一个拥有数百个 Skills、数十个 Hooks、十几个 Agents 的企业级 AI 应用中。
最后分享一个小技巧:在
claude-code-cli dev启动后,它会自动在控制台打印出所有已加载的 Skills、Hooks、Agents 的 ID 列表,以及它们的加载状态。这是你排查“组件不可用”问题的第一手线索。如果某个 Skill 没有出现在列表里,99% 的原因是它的 JSON 文件语法有误,或者文件名与id字段不匹配。
337



被折叠的 条评论
为什么被折叠?



