Claude‑Code Agent Harness,核心架构与源码洞察

一、整体架构分层

整体是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。

执行周期:

  1. 拼装完整消息上下文(history+system prompt+工具定义)
  2. 请求Anthropic API,流式接收模型输出
  3. 识别tool_call,把工具请求交给权限层校验
  4. 执行工具,捕获stdout/stderr、错误,包装工具结果消息
  5. 工具结果追加进对话上下文,回到第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框架)

  1. Agent逻辑最小化:循环框架不做业务判断;推理全部交给LLM;框架专注执行、安全、编排、上下文管理。开源框架经常把大量业务逻辑写死在代码。
  2. 多Agent不复制完整上下文:依靠API侧Prompt Cache继承,子Agent只回传摘要,控制上下文膨胀。
  3. 安全不是事后补丁:工具层原生权限门控;bash多层校验,高危操作强制审批。
  4. 会话记忆≠全部历史:KAIROS后台Agent提炼事实,而不是简单存储对话片段。
  5. 一套核心Agent Loop到处复用:主会话、子worker、后台记忆Agent全部复用同一个queryLoop,只改参数配置,代码复用极高。

四、暴露出来的有趣细节

  1. ANTI_DISTILLATION_CC flag:开启会注入虚假工具schema,用于对抗第三方蒸馏采集Claude Code行为数据。
  2. 四层上下文压缩策略,会话token超限自动执行降级压缩。
  3. 遥测埋点非常细,每个工具耗时、token消耗、审批次数全部采集。
  4. 支持模型自动路由:简单任务Haiku,复杂架构任务自动切Opus,普通编码Sonnet。

五、局限与可借鉴边界

  1. Claude‑Code深度绑定Anthropic Claude API;很多机制依赖Anthropic Prompt Cache特性,直接迁移OpenAI等其他模型会失效。
  2. 泄露版本是生产内部代码,没有面向外部开发者的注释;很多内部feature flag,部分功能未对外发布。
  3. MCP协议是其扩展基石,外部工具全部走MCP,不做自定义插件协议。

六、开源复刻启示

做自研企业Agent系统,可以直接借鉴这套模式:

  1. 实现极简通用Agent queryLoop;
  2. 区分Coordinator‑Worker子Agent,摘要回传,避免上下文爆炸;
  3. 静态/动态分割system prompt,充分利用LLM厂商缓存;
  4. 两级自然语言配置文件;
  5. 独立权限引擎,工具调用前置校验;
  6. 后台独立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>`;
}

关键设计要点(对应源码行为)

  1. 循环不做业务逻辑:queryLoop 本身不解析工具语义、不做任务规划;规划、判断何时调用工具全部交给 Claude LLM。代码只负责消息流转、权限、循环控制。
  2. 错误不击穿Agent循环:bash报错、文件读写异常、MCP服务挂掉,全部包装为 tool_result 返回模型,Agent继续跑,不直接抛异常退出循环。
  3. 权限引擎前置拦截:工具调用先校验,后执行,校验失败直接构造拒绝结果回传给模型,工具根本不会运行。
  4. 多Agent复用同一个Loop函数 :Coordinator、Worker、后台Kairos记忆Agent,全部调用 queryLoop;通过入参控制:可用工具集、systemPrompt、权限、最大turn数。
    • Coordinator:工具只有 spawn_worker_agent,不能读写文件/bash
    • Worker:全套工具可用,独立消息历史
    • Kairos记忆Agent:极小工具集,后台低权限,空闲自动运行
  5. 消息原地累加messageHistory:引用传递,每一轮把模型消息、工具结果不断追加,构成完整上下文。
  6. 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);
相关推荐
回眸&啤酒鸭1 天前
【回眸】Minicart 电商购物车核心功能落地指南
人工智能
一隅论数智1 天前
给AI一张“业务概念地图“:本体如何从哲学走向企业智能
大数据·人工智能·经验分享·笔记·学习·学习方法·政务
AI的探索之旅1 天前
97 个 OpenCV 实例(三十):双目立体,从标定到点云
人工智能·opencv·计算机视觉
AlbertZein1 天前
Step-5-Preview 上手实测:3D 游戏、金融分析、网页设计一次跑完
人工智能·aigc
LaughingZhu1 天前
Product Hunt 每日热榜 | 2026-09-19
人工智能·深度学习·神经网络·搜索引擎·百度
美狐美颜SDK开放平台1 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
wukangjupingbb1 天前
智能网联汽车安全能力框架
人工智能
龙亘川1 天前
明月照湾区,智启新赛道:从顶流文旅IP盛会看智慧文旅升级路径
人工智能·智慧城市·开源软件·数据可视化
飞猫的边缘AI1 天前
边缘AI应用:家用AI摄像头怎么做数据训练?
人工智能·边缘计算·ai算法·边缘ai