
一、整体架构分层
整体是Harness(Agent harness),不是大模型本身;是包裹 Claude LLM 的执行外壳,负责上下文装配、工具调度、权限、多Agent编排、会话记忆、终端交互,所有入口(CLI/IDE‑Plugin/Headless SDK)复用同一套Agent主循环。
用户交互层(CLI / IDE插件 / SDK Headless)
↓
Agent Core【核心】
├─ queryLoop 主Agent循环(88行核心逻辑)
├─ System‑Prompt组装 & Anthropic Prompt Cache
├─ Coordinator‑Worker 多Agent swarm编排
├─ Hook钩子系统
└─ 会话状态/记忆管理
↓
安全权限层(Permission Engine)
├─ 工具权限门控
├─ bash沙箱校验(23项安全检查)
└─ 用户审批、黑白名单规则
↓
工具层 Tools(40+内置工具,MCP外部工具)
├─ 文件读写/cell‑diff增量编辑
├─ bash执行、git操作、LSP对接
└─ web fetch、子Agent派生工具
↓
状态持久化层
├─ 两级配置:全局CLAUDE.md /项目CLAUDE.md
├─ memdir自动记忆存储
└─ 会话快照、遥测、成本统计
核心执行链路:用户输入 → Agent Loop → 权限校验 → 工具执行 → 结果回灌上下文 → LLM推理,循环直到无工具调用输出最终结果。
源码目录核心结构(泄露src)
src
├── agent/ # Agent核心,queryLoop、子Agent派生、Coordinator协调器
├── tools/ # 全部内置工具实现、工具schema定义
├── commands/ # /开头斜杠命令实现(50+命令)
├── services/ # API调用、流式处理、缓存、遥测、记忆提取服务
├── state/ # 双层状态管理 BootstrapState + AppState
├── constants/ # prompt.ts(最重要:system prompt模板)、常量、flag
├── hooks/ # Hook系统,工具调用拦截修改扩展点
├── memdir/ # KAIROS自动记忆后台守护逻辑
├── bridge/ # IPC进程桥接,子Agent通信mailbox队列
├── components/ # Ink终端React UI组件
├── types/ # 全套强类型定义
├── vim/ voice/ # Vim模式、语音模式
└── plugins/ # MCP插件加载
二、核心模块源码解析
1. Agent Loop(agent/queryLoop.ts)
整个系统心脏,仅88行核心循环,所有会话(主会话、子worker agent、后台记忆agent)全部复用该循环,仅传入不同配置、权限集、system‑prompt。
执行周期:
- 拼装完整消息上下文(history+system prompt+工具定义)
- 请求Anthropic API,流式接收模型输出
- 识别tool_call,把工具请求交给权限层校验
- 执行工具,捕获stdout/stderr、错误,包装工具结果消息
- 工具结果追加进对话上下文,回到第2步;直到模型不再输出工具调用,输出最终文本结束本轮turn。
关键设计:无硬编码业务逻辑,Loop本身不做业务判断;全部决策交给Claude模型;代码只负责执行、权限、消息流转。后台记忆提取也是fork一个权限受限Agent跑同一个queryLoop,做记忆碎片整理,不写独立逻辑。
2. Coordinator‑Worker 多Agent Swarm 架构(最精华工业多Agent设计)
不是简单多实例,父子代理共享Anthropic Prompt Cache,不复制完整上下文,大幅降低token开销。
- Coordinator协调者:禁止直接读写文件/执行bash;仅拥有3个工具:派生子Agent、消息发送、任务终止。职责是拆解大任务,切分子任务下发Worker。
- Worker工作子Agent:协调者fork出来,携带完整工具权限,在独立上下文窗口执行子任务。
IPC mailbox消息队列做进程间通信;原子claim锁防止抢任务。Worker只把提炼后的XML <task‑notification>摘要传回主Agent,不把完整对话历史返回主会话,避免主上下文爆炸。Worker进程销毁后中间全部丢弃,只保留结论。
解决行业痛点:长任务多Agent容易把主上下文撑爆。
3. Prompt缓存工程:constants/prompts.ts
export const SYSTEM_PROMPT_DYNAMIC_BOUNDARY = '__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__'
sections = [
// 静态部分:角色定义、工具说明、行为规范(全局缓存)
...
// === 分割标记 ===
SYSTEM_PROMPT_DYNAMIC_BOUNDARY,
// 动态部分:当前git状态、CLAUDE.md配置、MCP工具列表、项目信息
...resolvedDynamicSections
]
利用Anthropic Prompt Cache能力,分界线以上静态system prompt全局复用缓存;分界线以下是每个会话动态可变内容。极大降低高频会话token成本。这是工业级Prompt工程教科书实现。
4. 双层状态管理 state/
- BootstrapState(进程全局单例):模块级单例对象,80+字段;成本追踪、会话ID、遥测计数器、全局开关flag;所有模块可导入读取;只依赖基础库,无循环依赖。
- AppState Store UI状态:极简手写不可变Store,几十行实现getState/setState/subscribe,带引用相等优化;供Ink终端UI渲染使用,DeepImmutable类型约束。
区分:全局业务元数据放BootstrapState;UI视图会话状态放AppState,职责分离。
5. 工具系统 & 安全沙箱 tools/
共40+内置工具,每个工具都绑定权限标签;所有工具调用必须先过权限引擎。
- 文件编辑:cell‑level diff细粒度修改,不是整文件重写;减少模型输出噪声。
- bash工具安全层:23项安全校验;防御zsh特殊扩展、零宽字符注入、危险命令黑名单;每一条bash都做规则校验;高危操作触发用户交互审批。
- MCP作为扩展插件入口:外部MCP Server工具自动合并进工具schema,和内置工具统一调度。
- 工具调用支持完整Hook拦截:可以beforeToolCall修改参数、阻断调用、afterToolCall修改返回结果。
6. 记忆系统 KAIROS memdir/ 未公开后台守护能力
KAIROS是后台驻留Agent守护进程(autoDream):用户空闲时自动fork低权限Agent,扫描会话历史,合并碎片观察,消解矛盾幻觉,提炼事实存入memdir记忆目录。下次会话自动加载记忆,解决长会话上下文熵增,缓解Agent越跑越混乱。
不会把全部历史塞进上下文窗口;只保存提炼后的关键事实。
7. 配置体系:两级 CLAUDE.md
~/.claude/CLAUDE.md # 全局个人指令(本地,不提交git)
项目目录/.claude/CLAUDE.md # 项目团队规则,可以提交git共享
settings.json # MCP服务、权限策略
.env # API密钥,禁止提交
自然语言写配置文件,直接注入system prompt动态段;团队可以把编码规范、项目约束写进仓库CLAUDE.md,所有团队成员运行Claude Code自动继承。
三、值得学习的工程思想(对比开源Agent框架)
- Agent逻辑最小化:循环框架不做业务判断;推理全部交给LLM;框架专注执行、安全、编排、上下文管理。开源框架经常把大量业务逻辑写死在代码。
- 多Agent不复制完整上下文:依靠API侧Prompt Cache继承,子Agent只回传摘要,控制上下文膨胀。
- 安全不是事后补丁:工具层原生权限门控;bash多层校验,高危操作强制审批。
- 会话记忆≠全部历史:KAIROS后台Agent提炼事实,而不是简单存储对话片段。
- 一套核心Agent Loop到处复用:主会话、子worker、后台记忆Agent全部复用同一个queryLoop,只改参数配置,代码复用极高。
四、暴露出来的有趣细节
ANTI_DISTILLATION_CCflag:开启会注入虚假工具schema,用于对抗第三方蒸馏采集Claude Code行为数据。- 四层上下文压缩策略,会话token超限自动执行降级压缩。
- 遥测埋点非常细,每个工具耗时、token消耗、审批次数全部采集。
- 支持模型自动路由:简单任务Haiku,复杂架构任务自动切Opus,普通编码Sonnet。
五、局限与可借鉴边界
- Claude‑Code深度绑定Anthropic Claude API;很多机制依赖Anthropic Prompt Cache特性,直接迁移OpenAI等其他模型会失效。
- 泄露版本是生产内部代码,没有面向外部开发者的注释;很多内部feature flag,部分功能未对外发布。
- MCP协议是其扩展基石,外部工具全部走MCP,不做自定义插件协议。
六、开源复刻启示
做自研企业Agent系统,可以直接借鉴这套模式:
- 实现极简通用Agent queryLoop;
- 区分Coordinator‑Worker子Agent,摘要回传,避免上下文爆炸;
- 静态/动态分割system prompt,充分利用LLM厂商缓存;
- 两级自然语言配置文件;
- 独立权限引擎,工具调用前置校验;
- 后台独立Agent做记忆提炼,而不是把全部历史喂给大模型。
Claude‑Code queryLoop 伪代码实现
还原泄露源码 agent/queryLoop.ts 核心逻辑,省略 Ink UI、遥测、异常重试、MCP 埋点,保留真实业务控制流。
关键点:同一个 queryLoop 函数,供主Agent、Worker子Agent、Kairos记忆后台Agent复用;靠入参区分权限、systemPrompt、工具集,不写多套循环。
/**
* queryLoop 通用Agent主循环
* @param context 会话上下文状态
* @param systemPrompt 组装完成的system prompt(已处理静态/动态缓存分割)
* @param tools 可用工具列表schema
* @param permissionEngine 权限校验实例
* @param messageHistory 消息数组,会原地追加更新
* @param abortSignal 中断信号
* @param onToolOutput 工具执行回调,用于UI输出
* @returns 最终文本结果,循环结束
*/
async function queryLoop({
context,
systemPrompt,
tools,
permissionEngine,
messageHistory,
abortSignal,
onToolOutput,
}) {
let finalResponseText = "";
const MAX_TURNS = context.maxTurns ?? 50; // 最大轮次防死循环
let turnCount = 0;
while (turnCount < MAX_TURNS) {
turnCount += 1;
// 1. 调用Anthropic流式API,送入 system + history + tools
const stream = anthropic.stream({
system: systemPrompt,
messages: messageHistory,
tools: tools,
stream: true,
abortSignal,
});
let partialText = "";
let toolCalls = [];
// 2. 流式解析返回,收集文本片段 + 工具调用块
for await (const chunk of stream) {
switch (chunk.type) {
case "text_delta":
partialText += chunk.text;
break;
case "tool_call_start":
// 模型请求调用工具,收集全部tool_call
toolCalls.push(chunk.toolCall);
break;
}
}
// 本轮模型完整返回消息,加入会话历史
const modelMessage = {
role: "assistant",
content: partialText ? [{ type: "text", text: partialText }] : [...toolCalls]
};
messageHistory.push(modelMessage);
// ========== 分支A:没有工具调用 → Agent任务结束,返回最终文本 ==========
if (toolCalls.length === 0) {
finalResponseText = partialText;
break;
}
// ========== 分支B:存在工具调用,逐个执行工具 ==========
const toolResultMessages = [];
for (const toolCall of toolCalls) {
// --------------------------
// 安全前置:权限引擎校验(核心!所有工具不能绕过)
// --------------------------
const permit = await permissionEngine.check(toolCall, context);
if (!permit.allowed) {
// 拒绝执行,把拒绝原因作为工具结果回传给模型
toolResultMessages.push({
role: "user",
content: [{
type: "tool_result",
tool_use_id: toolCall.id,
content: `Permission denied: ${permit.reason}`
}]
});
continue;
}
// --------------------------
// 执行工具,捕获stdout/stderr/异常
// --------------------------
let toolOutput;
try {
toolOutput = await executeTool(toolCall.name, toolCall.input, context);
onToolOutput?.(toolCall, toolOutput); // UI回调打印终端输出
} catch (err) {
// 工具抛异常,错误原样返回给模型,不直接崩溃Agent循环
toolOutput = `Tool execute error: ${err.message}\n${err.stack}`;
}
// 工具执行结果封装成 tool_result 消息
toolResultMessages.push({
role: "user",
content: [{
type: "tool_result",
tool_use_id: toolCall.id,
content: toolOutput
}]
});
}
// 全部工具结果追加进对话历史,下一轮循环把全部上下文喂回LLM
messageHistory.push(...toolResultMessages);
// while循环回去 → 再次请求LLM,新一轮turn
}
// 循环退出:达到最大轮次,或者模型不再调用工具
if (turnCount >= MAX_TURNS) {
return `[Agent terminated: reached max turn limit ${MAX_TURNS}]\n${finalResponseText}`;
}
return finalResponseText;
}
配套辅助函数伪代码
executeTool 工具分发器
/**
* 工具分发:内置工具 + MCP外部工具统一入口
*/
async function executeTool(toolName, toolInput, context) {
// 内置工具map:readFile / writeFile / bash / git / spawn_worker_agent ...
const builtInTools = getBuiltInToolRegistry();
if (builtInTools.has(toolName)) {
return builtInTools.get(toolName).handler(toolInput, context);
}
// MCP外部工具转发
const mcpServer = context.mcpRegistry.getServerForTool(toolName);
return await mcpServer.callTool(toolName, toolInput);
}
Coordinator‑Worker 调用示例(如何fork子Agent)
spawn_worker_agent 是内置工具,内部复用同一个 queryLoop,新开子进程+独立消息mailbox,不拷贝完整messageHistory。
// Coordinator内部:派生子Worker
async function spawn_worker_agent_handler(input, context) {
const workerContext = createChildAgentContext(context);
// Worker使用裁剪后的systemPrompt、完整工具集,独立messageHistory
const workerResult = await forkProcessAndRunQueryLoop({
context: workerContext,
systemPrompt: workerSystemPrompt,
tools: workerTools,
// Worker拥有自己独立空的消息队列,不复制父完整history
messageHistory: [],
userTask: input.task,
});
// Worker只返回摘要结果,不把全部对话返回父Agent,控制token爆炸
return `<task‑notification>${workerResult}</task‑notification>`;
}
关键设计要点(对应源码行为)
- 循环不做业务逻辑:queryLoop 本身不解析工具语义、不做任务规划;规划、判断何时调用工具全部交给 Claude LLM。代码只负责消息流转、权限、循环控制。
- 错误不击穿Agent循环:bash报错、文件读写异常、MCP服务挂掉,全部包装为 tool_result 返回模型,Agent继续跑,不直接抛异常退出循环。
- 权限引擎前置拦截:工具调用先校验,后执行,校验失败直接构造拒绝结果回传给模型,工具根本不会运行。
- 多Agent复用同一个Loop函数 :Coordinator、Worker、后台Kairos记忆Agent,全部调用 queryLoop;通过入参控制:可用工具集、systemPrompt、权限、最大turn数。
- Coordinator:工具只有 spawn_worker_agent,不能读写文件/bash
- Worker:全套工具可用,独立消息历史
- Kairos记忆Agent:极小工具集,后台低权限,空闲自动运行
- 消息原地累加messageHistory:引用传递,每一轮把模型消息、工具结果不断追加,构成完整上下文。
- Prompt Cache靠上层组装:queryLoop不关心system prompt内部静态/动态分割;上层调用方组装好完整 systemPrompt 传入,利用Anthropic缓存能力。
和普通开源Agent的差异点
AutoGPT 等很多框架:代码里写大量if‑else做任务拆解、判断下一步动作;
Claude‑Code queryLoop:纯执行层,任务拆解、决策全部交给LLM,代码只做执行安全与消息编排。
极简调用示例
// 主会话入口调用
const history = [];
const result = await queryLoop({
context: mainContext,
systemPrompt: buildSystemPrompt(),
tools: getAllToolsSchema(),
permissionEngine: globalPermissionEngine,
messageHistory: history,
abortSignal: AbortSignal.timeout(1000 * 60 * 10),
onToolOutput: (tool, out) => console.log(`[Tool] ${tool.name}`),
});
console.log("Agent输出:", result);