anthropics/claude-code:Anthropic 官方的命令行 AI 编程助手¶
说明:Claude Code CLI 本体是闭源发布包,官方未公开完整源码。本文基于三类证据写成:① 对 Claude Code 公开行为的黑盒观察;② 官方博客与文档描述;③ Anthropic 开源的 Claude Agent SDK(
claude-agent-sdk-python、claude-agent-sdk-typescript)中可核对的类型定义与函数签名——该 SDK 与 Claude Code CLI 共享同一套 Hook 事件、权限回调和会话协议,是目前能拿到源码级证据的最佳代理(注:TypeScript 版仓库只发布编译产物和示例,不含可读源码;下文引用的具体文件路径均出自 Python SDK 仓库src/claude_agent_sdk/)。文中凡标注了具体文件路径/类型签名的内容,均已用 SDK 源码核对;凡是QueryEngine、Subagent Coordinator这类在公开代码里找不到对应符号的内部命名,均为行为观察的归纳称呼,不代表 Anthropic 官方内部实现的确切命名,面试转述时请说明这是观察推测而非源码事实。
先说人话:这是个什么东西¶
Claude Code 是 Anthropic 官方出的命令行 AI 编程助手。你在终端里跟它对话,它能读你的代码库、改代码、跑命令——本质上是把一个大模型接到你本地的文件系统和 Shell 上,让它像一个真正参与项目的工程师那样干活,而不是只在聊天框里贴代码片段给你抄。
举一个最简单的场景:你在终端里敲一句"帮我把 parseConfig 函数加上空指针判断",Claude Code 会先读这个函数所在的文件(可能还顺带看看谁调用了它),生成修改后的代码写回文件,再顺手跑一下测试命令确认没改坏。这个过程里"读文件""改文件""跑命令"都是它真刀真枪在你本地环境里执行的动作,不是纸上谈兵——这也是为什么它的运行时设计要比一个普通聊天机器人复杂得多。
在软件开发型智能体(Coding Agent)的系统建设中,大模型本身不等于应用,它仅仅是意图判定源。由 Anthropic 推出的 Claude Code 代表了当今最顶尖的交互式编程智能体设计之一,它的底层是一个高度工程化的本地任务运行时(Local Task Runtime)。
核心设计思路:怎么让 AI 安全地读写你的真实代码库¶
它要解决的核心问题是:怎么让一个大模型安全地读写你的真实代码库、执行真实的 Shell 命令,同时还不失控?如果什么都放行,一次误判的 rm -rf 或者一次跑飞的死循环就可能造成真实损失;但如果每一步都要人工确认,体验又会退化成"复制粘贴机器人",失去了智能体的意义。
Claude Code 的做法是把这个问题拆成三层来处理:一是怎么驱动一次任务从接到指令跑到交出结果(对应下面第 1 节的查询循环与转录记录);二是高危操作发生前怎么被拦下来交给人审批(第 2 节的权限确认与 Hook 拦截机制);三是任务变得很长、很复杂时怎么防止上下文被撑爆(第 3 节的自动压缩与子代理隔离)。以下依次展开,能用 Claude Agent SDK 源码验证的部分会标出文件路径和类型签名;无法验证的部分保留为黑盒观察,并加"据观察""社区分析认为"等限定语。
1. 一次任务是怎么跑起来的:Query 循环与转录记录¶
Claude Code 的核心是 QueryEngine(查询引擎)。它将一次复杂的编程任务抽象为一系列可恢复的状态转移过程,并在本地维持一个 Transcript(转录历史)循环。
[用户输入 / 斜杠命令]
│
v
┌────────────────────────────────────────────────────────┐
│ 1. Context Synthesis (上下文组装) │
│ - 装配 CLAUDE.md 项目规范 & User Settings │
│ - 提取 Git Diff 现场 & Memory 历史记录 │
│ - 按 Token 水位进行启发式剪枝或摘要引入 │
└───────────────────────┬────────────────────────────────┘
│
v
┌────────────────────────────────────────────────────────┐
│ 2. Unified Transcript Loop (转录推进循环) │
│ ┌──> 向 Claude API 发流式采样请求 │
│ ├──> 拦截 Assistant 返回的 tool_use (工具意图) │
│ ├──> 【安全阀】触发 Hook / TTY 拦截审批 │
│ ├──> 执行本地处理器 (Bash/File I/O) │
│ └──> 将工具执行结果 (tool_result) 追加回 Transcript │
└───────────────────────┬────────────────────────────────┘
│
│ 终止条件满足 (无后续工具调用 / 编译通过)
v
[输出最终 Patch 或总结]
上面这张图对应的正是前面"帮我改 parseConfig"那个例子背后发生的事:先把项目规范和历史记录装配进上下文,再进入一个"采样 → 判断要不要用工具 → 审批 → 执行 → 把结果写回历史"的循环,直到任务收尾。下面两个知识点分别对应图里"上下文怎么组装"和"转录怎么落盘恢复"这两处细节,可以用 SDK 源码核实:
- CLAUDE.md 规范约束:据观察与官方文档描述,项目根目录下的
CLAUDE.md会被注入到系统 Prompt 里,约束智能体生成代码时必须遵守的编译指令、测试命令与代码风格。这一点可以在 Claude Agent SDK 里找到旁证:ClaudeAgentOptions.setting_sources(src/claude_agent_sdk/types.py)的官方注释明确写着 "Must includeprojectto load CLAUDE.md files"——即 CLAUDE.md 的加载被绑定在"project"这一显式的 setting source 开关上,是可核实、可关闭的机制,而不是一个玄学式的"最高注意力优先级"。至于它在 System Prompt 里具体处于什么位置、权重如何实现,SDK 没有给出细节,这部分仍是推测。 - 状态回载与持久化:据观察,所有的 API 消息、工具参数及执行返回值会被序列化追加进本地转录文件,中途网络超时或进程中断后可以从该文件恢复到崩溃现场。SDK 里能验证的旁证是:每个 Hook 事件的
BaseHookInput(src/claude_agent_sdk/types.py)都带有session_id: str与transcript_path: str字段,ClaudeAgentOptions也暴露了resume(按 session id 恢复)、fork_session(从某个转录分叉出新会话)等选项——这证实了"存在一个可定位、可恢复、可分叉的转录文件"这件事本身是真实机制,但转录的具体 JSON 序列化格式、字段结构,SDK 并未完整公开,仍属推测。
2. 高危操作怎么被拦下来:权限确认与 Hook 拦截机制¶
当 Agent 具备在本地物理环境执行 Shell 命令(rm -rf 等)和修改磁盘文件的巨大权力时,安全绝不能仅依赖 Prompt 里的道德说教,而必须通过物理运行时拦截守住底线。这一节具体拆成两层来看:一层是面向人的交互式确认,一层是面向命令本身的静态拦截。
- 交互式权限确认与
can_use_tool回调: 据观察,Claude Code 在交互式终端下遇到高危操作(写文件、非只读 Bash 命令等)会挂起当前流程、接管终端输入输出,弹出形如? Approve command "npm run build"? [y/N]的确认提示——这层终端 UI 的具体实现(是否真的挂起了某个 JS 异步流、如何接管 Stdio)官方没有公开代码,仍是黑盒观察。 但这套"确认/拒绝"机制背后的协议形状,可以在 Claude Agent SDK 里源码级验证:SDK 把这个确认动作抽象成一个强类型回调CanUseTool(src/claude_agent_sdk/types.py):官方注释写明,CanUseTool = Callable[ [str, dict[str, Any], ToolPermissionContext], Awaitable[PermissionResult] ] # PermissionResult = PermissionResultAllow | PermissionResultDenycan_use_tool"is invoked when the CLI's permission rules evaluate to 'ask' for a tool call — it is the SDK replacement for the interactive permission prompt"。也就是说,CLI 里那个[y/N]终端提示和 SDK 里的can_use_tool回调,是同一条权限判定路径在两种宿主环境下的不同呈现——这一点可验证;返回PermissionResultDeny时,拒绝结果会作为工具的正常tool_result传回模型而不是抛异常中断整个任务,与前文 CLAUDE.md 一节引用的行为模式一致。SDK 文档还提到permission_mode存在一档"auto"——"A model classifier approves or denies each tool call",即除了人工确认外官方还提供了一层模型自动判定风险的机制,这是纯黑盒观察看不到的。 - PreToolUse Hook 与拦截决策:
据观察与社区分析,Claude Code 在执行 Bash 意图前会对命令做静态扫描(是否包含
sudo、破坏性删除、跨目录越权读写等),一旦命中就直接拦截——但具体是 AST 解析还是正则匹配,官方未公开,这属推测细节。 可以源码级验证的是这类拦截背后的事件与决策结构:SDK 的HookEvent(src/claude_agent_sdk/types.py)把PreToolUse列为独立的强类型 Hook 事件,PreToolUseHookInput携带tool_name、tool_input、tool_use_id;对应的PreToolUseHookSpecificOutput.permissionDecision字段类型是Literal["allow", "deny", "ask", "defer"]。也就是说"允许 / 拒绝 / 转人工确认 / 推迟"这四态决策,是 Hook 协议里明确定义、可验证的类型,而不是本文臆测出来的机制;只是黑名单具体命中哪些命令模式,没有公开源码可查。
3. 上下文快撑爆时怎么办:自动压缩与子代理隔离¶
长周期编程任务(如"修复一个包含 20 个文件的 Bug")会导致会话消息迅速堆叠,引发 Context 崩溃。Claude Code 采用了双重上下文压榨算法,一个管"历史消息太长",一个管"单次搜索/扫描太吵":
- 反应式压缩(Reactive Compaction):
据观察和社区分析,当 Transcript 累计 Token 数触及一定水位时,运行时会自动触发一次压缩,把已执行完的冗余输出(如很长的
npm install日志)替换成摘要,释放上下文空间;具体触发阈值(是否真是 \(120\text{K}\) 这类数字)、摘要生成的算法细节,官方没有公开,这里只是社区常见的推测性描述。 可以确认的是,"自动压缩"这个事件本身在 Claude Agent SDK 里是一个有名有类型的一等公民,而不是本文杜撰的概念:PreCompactHookInput(src/claude_agent_sdk/types.py)定义为class PreCompactHookInput(BaseHookInput): hook_event_name: Literal["PreCompact"] trigger: Literal["manual", "auto"] custom_instructions: str | Nonetrigger区分"manual"(用户手动触发压缩)和"auto"(运行时自动触发),说明"自动压缩"是被 CLI 显式建模、可以挂 Hook 拦截或注入自定义指令的真实事件——但压缩发生的具体算法(怎么摘要、保留哪些证据链)仍是黑盒。 - 子代理隔离机制(Subagent Isolation):
面对极其繁琐的横切扫描任务(如"在全库中 Grep 检索特定模块的所有调用点"),据观察主 Agent 不会直接把成千上万行的搜索噪声塞进主历史中,而是会派生一个独立的子代理去执行扫描,只把提炼后的结论带回主上下文——这部分整体架构印象来自黑盒行为观察,是否真的存在一个叫"Coordinator"的组件、内部具体怎么调度,无法用公开代码验证。
但"子代理拥有独立、可归因的执行上下文"这件事,在 Claude Agent SDK 里有直接的源码证据:
SubagentStopHookInput(src/claude_agent_sdk/types.py)为每个子代理暴露即每个子代理有自己独立的class SubagentStopHookInput(BaseHookInput): hook_event_name: Literal["SubagentStop"] stop_hook_active: bool agent_id: str agent_transcript_path: str agent_type: stragent_id与agent_transcript_path(单独的转录文件路径),与主会话的transcript_path是分开的;PreToolUseHookInput/PostToolUseHookInput也都混入了_SubagentContextMixin(携带agent_id/agent_type),官方注释说明这是"当多个子代理并行运行时,工具生命周期 Hook 在同一控制通道上交错到达,agent_id是唯一能把每次调用归属到正确子代理的方式"。子代理本身的画像(可用工具、模型、最大轮次、是否后台运行等)由AgentDefinitiondataclass 定义。这些足以证实"子代理确实拥有独立的转录/上下文,且是被显式建模的一等对象",但主 Agent 具体怎样决定何时派生子代理、怎样过滤回传结论,SDK 没有暴露这部分调度逻辑,仍属推测。
4. 资深系统架构师面试表达方案¶
面试提问:在开发像 Claude Code 这样可以直接在本地执行命令、修改文件的高危 Agent 应用时,你是如何从运行时层面保障系统安全与性能确定性的?
回答模版: 做一个能在本地执行命令、改文件的 Coding Agent,安全边界和上下文管理是两个绕不开的问题。
我参考 Claude Code 观察到的设计,落到自己的架构里大致是这么处理的:模型提出执行 Bash 命令或写文件这类有风险的动作时,不能只靠 Prompt 里"请谨慎操作"这种软性约束,而是在运行时层面直接挂起,把终端控制权抢过来等用户确认;用户拒绝的话,就把"用户拒绝执行"当作一个正常的工具返回值传回模型,让它换个思路,而不是直接抛异常中断掉整个任务。
上下文这块,我们吃过一次教训:早期让主 Agent 直接执行一次全库 Grep 扫描,几千行搜索结果一股脑塞进对话历史,后面几轮模型的注意力全被这堆噪声占满,决策质量明显下滑。后来改成把这类大范围扫描任务丢给一个独立的、只读的子上下文去跑,跑完只把结论摘要带回主流程——这个思路很大程度上是照着 Claude Code 的 Subagent 设计抄的。长会话本身也要做压缩,把历史消息里已经执行完、不再需要细节的部分(比如很长的构建日志)提炼成摘要,不然上下文迟早会顶满。
这套思路能不能百分百对应 Claude Code 的真实实现,我没有源码级的把握,更多是从它对外表现出来的行为反推的架构猜测,实际验证还是要看官方文档或自己动手测试。