使用 Bun作为运行时而非node
Bun: JS 运行时,比Node快,自带包管理,feature flag死码(分支不运行代码)编译时消除成为可能
QuerryEngine
每个对话都有一个QuerryEngine实例,包括:
完整对话历史——-Message[]列表
FileStateCache—-缓存,记录已经读取文件内容
totalUsage———-累计token使用(成本、上下文压缩触发)
对外函数方法:
1 | async *submitMessage(input: string): AsyncGenerator<SDKMessage> |
1 | submitMessage() |
预处理,判断input 是否为
/命令或者!指令如果是,就不llm而是走命令
上下文组装,调用
fetchSystemPromptParts()同时获取git状态、Claude.md、工具prompt、用户画像【并发】记忆注入:
读取memory.md,追加到系统提示词末尾
API调用,query()
工具调用循环:如果有
tool_use,进入循环,调用工具,返回结果,结果加入上下文对话,继续llm querry每一轮结束检查累计token,超过就进行上下文压缩
上下文压缩策略
【触发】: 1.token超出阈值 2./compact指令触发 3.可以选择功能,每一轮轻量摘要
4.插入标记snipCompact,按需回复原始片段
Claude.md记忆系统
存储在memory.md里面,组装上下文加入记忆的时候,会向上遍历父目录,收集每一层的claude.md(最上层:工具偏好、代码风格,向下通常是项目级记忆)
【这个还要细致学习】
工具
1 | interface Tool<Input, Output> { |
工具权限
工具级别:`checkPermissions(input, context)【检查危险指令rm -rf】
全局permission 自动检测风险(llm根据操作计划,决定是否授权),ui提示用户,用户自己选择
工具分类
文件: 读、写、展示列表、追加、..
代码:bash、 computer
Agent协作:
Agent : 启动一个子queryEngine,Agent嵌套,每个有独立上下文
TeamCreate : 创建agent团队:代码team = 前端agent+后端agent+审查agent
Teamdelete
Sendmessage: Agent之间相互传递信息(广播、单播)
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
211. 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("博客开发组") 释放资源这个解释的很清晰了
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协作
- 进程隔离
每个agent一个独立的claude-code进程
2.Mailboc
进程间不共享内存,依靠文件系统mailbox,消息JSON,发送方写入,接收方轮训标记已读,SendMessagetool本质上就是写入json文件
3.任务状态机
Swarm创建task,调度器把任务分给某个agent,agent维护任务状态【完成、失败、终止】,最终任务结束
claude-code 亮点
Feature flag 用bun:bundle编译门控,死码可以不编译进去
cli启动延迟
快速退出路径:
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()动态加载大模块,只在使用时时候触发
IO与模块加载并发
startMdmRawRead()startKeychainPrefetch(),cli 需要数据的时候io已经加载完成
面试
1. 为什么claude-code 不用rag
- 历史,早期Anthropic早期使用过rag ,本地向量数据库,embedding检索,但是效果差,换成了agentic search :模型调用grep, glob,find 去搜索代码。表现突出但是评价主要是靠体感。
2. embedding对于函数名、变量名语义匹配不够敏感,不如精确匹配
3. rag环节太多,精度损耗:切分,embedding,向量检索,sort,生成每一个都不能100%,调试的时候环节很多,也容易出错
4. grep实时搜索,rag要持续更新(embedding,检索消耗资源)或者放弃时效性
5. 但是token消耗很大,找搜索什么,搜索匹配,获取结果