claude-code解读

使用 Bun作为运行时而非node

Bun: JS 运行时,比Node快,自带包管理,feature flag死码(分支不运行代码)编译时消除成为可能

QuerryEngine

每个对话都有一个QuerryEngine实例,包括:

完整对话历史——-Message[]列表

FileStateCache—-缓存,记录已经读取文件内容

totalUsage———-累计token使用(成本、上下文压缩触发)

对外函数方法:

1
async *submitMessage(input: string): AsyncGenerator<SDKMessage>
1
submitMessage()
  1. 预处理,判断input 是否为/命令或者!指令

    如果是,就不llm而是走命令

  2. 上下文组装,调用fetchSystemPromptParts()同时获取git状态、Claude.md、工具prompt、用户画像【并发】

  3. 记忆注入:

    读取memory.md,追加到系统提示词末尾

  4. API调用,query()

  5. 工具调用循环:如果有tool_use,进入循环,调用工具,返回结果,结果加入上下文对话,继续llm querry

  6. 每一轮结束检查累计token,超过就进行上下文压缩

上下文压缩策略

【触发】: 1.token超出阈值 2./compact指令触发 3.可以选择功能,每一轮轻量摘要

4.插入标记snipCompact,按需回复原始片段

Claude.md记忆系统

存储在memory.md里面,组装上下文加入记忆的时候,会向上遍历父目录,收集每一层的claude.md(最上层:工具偏好、代码风格,向下通常是项目级记忆)

【这个还要细致学习】

工具

1
2
3
4
5
6
7
8
9
10
11
interface Tool<Input, Output> {
name: string // 工具名,模型调用时使用
description: string // 告诉模型这个工具做什么
inputSchema: ZodSchema<Input> // 入参校验(Zod)
call(input, context): Promise<Output> // 实际执行逻辑
checkPermissions(input, context) // 工具级权限检查
isReadOnly(): boolean // 只读工具可跳过部分权限流程
isConcurrencySafe(): boolean // 是否可与其他工具并发执行
renderToolUseMessage(input) // 在 UI 中如何展示"模型在调用这个工具"
renderToolResultMessage(output) // 在 UI 中如何展示工具返回结果
}

工具权限

  1. 工具级别:`checkPermissions(input, context)【检查危险指令rm -rf】

  2. 全局permission 自动检测风险(llm根据操作计划,决定是否授权),ui提示用户,用户自己选择

工具分类

文件: 读、写、展示列表、追加、..

代码:bash、 computer

Agent协作:

  1. Agent : 启动一个子queryEngine,Agent嵌套,每个有独立上下文

  2. TeamCreate : 创建agent团队:代码team = 前端agent+后端agent+审查agent

  3. Teamdelete

  4. Sendmessage: Agent之间相互传递信息(广播、单播)

  5. TaskCreate、TaskUpdate、TaskList、TaskOutput、TaskStop

    感觉这个像plan-and-solve

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    1. TeamCreate("博客开发组")  
    → 成员:产品经理Agent、前端Agent、后端Agent

    2. TaskCreate("需求分析", 分配给产品经理Agent)
    → 产品经理Agent 分析完,SendMessage("需求文档") 给前端和后端

    3. TaskCreate("写前端代码", 分配给前端Agent)
    TaskCreate("写后端API", 分配给后端Agent)
    → 两者并行执行

    4. TaskList()
    → 查看进度:前端 70%,后端 50%

    5. 前端Agent 遇到接口问题,SendMessage("需要确认字段格式") 给后端Agent

    6. 后端Agent 完成,TaskUpdate("写后端API", status="已完成")
    → TaskOutput("写后端API") 获取代码

    7. 测试发现问题,TaskCreate("修复登录Bug", 分配给前端Agent)

    8. 项目上线后,TeamDelete("博客开发组") 释放资源

    这个解释的很清晰了

    1. MCP: 【Model Context Protocol】统一AI与外部工具、数据源的连接方式

      和function calling区别:

      ​ function calling格式固定,每次请求都得给llm所有工具描述,无法普世化集成

      ​ mcp server : 告诉外界有什么可用工具,便于集成

      和api区别:

      ​ api:面向人类,不需要写工具描述

      ​ mcp:加入LLM能发现理解的标准协议

      MCP对外暴露:1. tool 可以执行的函数 2. resources 只读的数据源(上下文,某数据库的schema) 3. prompt模板

      ​ 把已有的工具、方法、functioncalling统一起来,对外提供统一封装结构并且便于模型理解【动态的,根据连接后声明动态注册】

      7.web 搜索

多Agent协作

  1. 进程隔离

​ 每个agent一个独立的claude-code进程

​ 2.Mailboc

​ 进程间不共享内存,依靠文件系统mailbox,消息JSON,发送方写入,接收方轮训标记已读,SendMessagetool本质上就是写入json文件

​ 3.任务状态机

​ Swarm创建task,调度器把任务分给某个agent,agent维护任务状态【完成、失败、终止】,最终任务结束

claude-code 亮点

  1. Feature flag 用bun:bundle编译门控,死码可以不编译进去

  2. cli启动延迟

    1. 快速退出路径:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      你执行 claude --version

      1. 解析 cli.tsx 文件
      2. 遇到 import { something } from './core/engine'

      3. 读取 ./core/engine.ts(文件 I/O)
      4. 解析这个文件的 AST(语法树)
      5. 递归处理 engine.ts 里的所有 import(可能几十个)
      6. 执行这些模块的顶层代码(初始化连接池、加载配置、预热缓存...)
      7. 终于回到 cli.tsx,发现 args.version 为 true
      8. 打印 VERSION,退出
      1
      2
      3
      4
      5
      // main() 函数的最开头,任何业务模块加载之前
      if (args.version) {
      print(VERSION);
      process.exit(0) // ← 直接杀死进程
      }

      直到干什么之后,直接返回,不做多余操作,在用户角度延迟更少

​ 3.模块懒加载

dynamic import()动态加载大模块,只在使用时时候触发

  1. IO与模块加载并发

    startMdmRawRead() startKeychainPrefetch(),cli 需要数据的时候io已经加载完成

面试

1. 为什么claude-code 不用rag

  1. 历史,早期Anthropic早期使用过rag ,本地向量数据库,embedding检索,但是效果差,换成了agentic search :模型调用grep, glob,find 去搜索代码。表现突出但是评价主要是靠体感。

​ 2. embedding对于函数名、变量名语义匹配不够敏感,不如精确匹配

 3. rag环节太多,精度损耗:切分,embedding,向量检索,sort,生成每一个都不能100%,调试的时候环节很多,也容易出错
 4. grep实时搜索,rag要持续更新(embedding,检索消耗资源)或者放弃时效性
 5. 但是token消耗很大,找搜索什么,搜索匹配,获取结果