乐于分享
好东西不私藏

【图解】Claude Code 源码解析|顶级Agent架构、Tool、Skill(一)

【图解】Claude Code 源码解析|顶级Agent架构、Tool、Skill(一)

写在前面

前段时间Claude Code的源码泄漏了,最近也稍微用ai速读了一下整个架构,内容实在太多了,这篇文章就先捋一下Claude Code的整个 Agent 框架体系,看看今年最火的Harness架构具体是什么样的。本文主要内容聚焦于整理架构流程、Tool 和 Skill等等

架构

Claude Code的Agent架构图大概如下所示:

How Claude Code Works

和大多数Agent架构类似,CC的整体架构偏向ReAct的开发范式,主要由以下几个模块组成:

  1. 主查询循环,Query Loop + LLM Loop
  2. 统一的 Tool 协议
  3. 统一的 cmd/Skill 协议
  4. 统一的记忆管理和检索模块
  5. 顶级 Prompt(做过Agent的都知道调Prompt有多痛苦)
  6. 规整有序的上下文拼接和压缩恢复模块
  7. Master Agent/SubAgent/Task/Coordinator的调度系统
  8. 精准而不冗余的权限控制

关于这里面所有的Prompt,我们下篇文章再展开说说。下面我们从头开始讲一个query在CC中的处理流程。

Query 处理

Query Handler

这一步是把 用户的输入 转换成 内部消息对象 + 附件对象

  1. Query 如果是从非交互主入口进来,则会直接进入 QueryEngine 模块处理。
  2. 如果是从交互模式进来,则会判断是普通的prompt/bash模式/slash cmd,是否带图片、是否带附件等等。

此时也会进行相似记忆的检索动作,包括检索用户的编码喜好习惯等等,将记忆连同query一同注入到上下文中,做上下文增强。记忆具体是怎么存储,怎么检索的我们放到下篇文章。

上下文前缀

获取Query和相似记忆之后,在请求大模型之前,还需要加载一些上下文的前缀信息,包括SystemPrompt、System Ctx、UserCtx等等。

Context
  • SystemPrompt:工具使用说明、memory 机制说明、输出风格、环境信息、MCP 指令等
  • UserContext:CLAUDE.md 聚合内容、当前日期等
  • SystemContext:git status、cache breaker 等

CC在这些message的编排上有一个规则:

  • SystemContext追加到SystemPrompt后,例如 System Prompt = default/custom/agent/coordinator prompt + SystemContext
  • UserContext 包成 meta user message,插到消息最前面,例如 Messages = [meta UserContext, ...业务消息]

工具选取与上下文治理

在这一步中,CC会从工具池中筛选出当前轮次可用的工具,作为一个工具集合一起发送给模型,工具这里我们后面再详细说明。

Context Collapse

此时的上下文内容包括了 这一轮发给模型的MessageSystemPromptTool SchemaMemoryAttachments 等等… 如果当前的上下文太长、太多、太乱,CC会先进行上下文治理

计算当前的Context窗口大小的Token是否已超过某个阈值,超过则进行Context Collapse,如果进行 Context Collapse 之后还是超过这个阈值,则进行Autocompact,如果还不行就会请求模型做一次 full compact。

  • Context Collapse:不立刻重写整段历史,而是先把一部分旧上下文折叠成一个更短的投影视图。 比如工具的返回字段太多了,对某些字段做降级处理,也就是只保留精简字段等等。
  • Autocompact:真的把对话历史重构成一个新的、更短的消息序列。

⚠️ 注意:压缩之后的消息是有一个排序规则的:

exportfunctionbuildPostCompactMessages(result: CompactionResult): Message[] {return [    result.boundaryMarker,    ...result.summaryMessages,    ...(result.messagesToKeep ?? []),    ...result.attachments,    ...result.hookResults,  ]}
  • boundaryMarker: 边界标记,相当于一个分界线,告诉模型:从这里开始,前面的原始历史已经被压缩掉了,下面这些消息不是完整原始对话,而是压缩后的重建结果。
  • summaryMessages:这是压缩出来的摘要消息。
  • messagesToKeep:有些消息不能靠摘要代替,必须原样保留在 compact 后的上下文里
  • attachments:compact 之后,原先很多上下文附件已经丢失了,所以要重新注入。摘要本身通常不包含这些运行时约束信息。
  • hookResults:compact 后还会跑 SessionStart hooks,包括重新注入CLAUDE.md、自定义系统约束、环境补充信息等等

标准化信息与流式交互

在标准化信息的这个阶段会做的事情有: attachment 重排、去掉 progress/system 等不可发内容、合并连续 user messages、切分无效 tool references、修复 tool_use/tool_result 配对、删除过多媒体项等等

LLM Loop

请求发出之后,模型开始streaming流式输出,模型可能会产出 普通assistant文本、 thinking、 tool_use

  • 如果只是文本并且无工具调用,就会走后面的结束流程
  • 如果有ToolUse,则会调用ToolUse,将ToolUse的Result回传给LLM,让LLM自己判断是否需要结束本次循环。如果LLM已经没有tool use了,或者命中了其他的策略,比如账户没钱了,就会退出LLM的Loop,走后面的结束流程。

结束的流程会有一个 stop hooks ,内容包括:

  • Transcript 持久化:每一轮的中间态和最终态都会进行持久化,持久化会话状态。做会话状态恢复的时候,不止恢复message,还有 skill state、tool use、session metadata等等…
  • Extract Memory:这里会 fork 一个 memory extraction subagent,去检查这一轮有没有新的信息值得长期记忆。这里只会在 memory 目录下做读取,可以更新 topic files和MEMORY.md 文件。
  • Session Memory:这是当前会话摘要,不是长期记忆,可以理解为短期的session记忆。本轮结束后,如果 token 增长 / 工具调用数达到阈值,创建或读取 summary.md,fork 一个受限子 agent,只允许编辑这一个文件,做当前 session 的结构化摘要更新。

Tool

接下来我们展开聊一下Tool的处理细节。

Tool Use In Claude Code
  1. 当模型需要进行 ToolCall 的时候,会先去可用的工具池子找这个工具,如果找不到就返回error
  2. 进入统一执行器 streamedCheckPermissionsAndCallTool ,这一步会做几个校验动作。
checkPermissionAndCallTool 源码
  1. 类型校验: 先是工具自己的Schema Check做校验,看看模型生成的参数类型是否正确
  2. 语义校验: 做业务的 Input Validate 校验,检查路径是否合法、参数组合是否冲突、命令是否合理、skill是否存在等等..
  3. PreHook:进入预执行的hook,这一步比较像可插拔的前置干预器,可以改写输入的input、增加额外上下文、直接给权限决策等等
  4. Permission Decision:前置的hook结束之后,才会进入权限系统,会把hook给的权限决策、CanUseTool、全局权限模式、用户规则、Classifier 合并起来得到一个结果。allow、deny、ask。
  5. 如果没有放行,模型会收到一个失败的tool result,然后LLM自己决定换方法,请求别的工具。
  6. 如果通过了放行,那么就直接进行 tool call 操作并且根据Tool协议返回结果给LLM做新一轮的输入
  7. 在工具执行完成之后,还会跑一个Post Hooks,包括增加附属消息、额外上下文、提示用户等等
Post Hooks

Skill

⚠️ 注意:如果这次调用的是 SkillTool,执行路径还是同一条主线,但 tool.call() 的内部含义不同。它的tool.call() 做的事不是操作外部世界 而是:

  1. 找到一个 skill cmd,展开 SKILL.md / cmd prompt。
  2. 生成新的 meta user messages。
  3. 附加 command_permissions,返回 contextModifier

Skill其实就是一份标准的SOP,而我们常说的渐进式披露就是在这里体现的:一个skill里面可能会包含很多tool,此时我们会先把skill描述给到模型,模型去判断做选择用哪些skill,再去调用真正的tool。

Build Prompt In Skill

skill 执行的时候会把 skill 名称、来源、正文内容登记起来。此时 skill 已经不再只是一个命令,而是变成了 用户/系统消息、权限附加信息、可能的附件的一个组合,也就是真正的后续上下文素材

所以 SkillTool 的 tool_result 只是表层结果,真正重要的是:它往消息流里塞进了新的 prompt 片段,还改变了后续的权限和模型配置。也就是说,SkillTool 是一个用 tool 协议包装的上下文变换器

最后

Claude Code和大多数的Agent架构类似,不过里面有非常多的细节可以耐人寻味的,包括 多重压缩的上下文治理,消息的顺序与补充,以及文章这里没提到的多重权限管理 等等…

从我自己做Agent的角度来看,Prompt 和 记忆选取也是很重要的,调Prompt过程非常痛苦。下篇文章,我们就来讲讲Claude Code里面的Prompt和记忆管理模块。