芥末
发布于 2026-03-31 / 0 阅读
0
0

Claude Code Agent 架构拆解:启动流程、工具执行、权限管道与上下文管理

Claude Code 是 Anthropic 做的命令行编程 Agent。它不是简单地把大模型接到一个终端输入框上,而是把模型、工具系统、权限控制、上下文管理、插件扩展、多 Agent 协作和终端 UI 组合成了一个完整的工程系统。

npm(Node.js 包管理与分发生态)包曾经携带过 Source Map。Source Map 的作用是把打包、压缩、混淆后的 JavaScript 文件映射回更接近开发阶段的源文件,方便调试。对一个打包后的 CLI(Command Line Interface,命令行界面)工具来说,如果发布产物里留下了完整 Source Map,就可能暴露大量 TypeScript 源码结构。

从工程视角看,真正有学习价值的不是“源码在哪里”,而是一个生产级 AI Agent 到底怎么组织:

  • 模型流式返回时,工具要不要立刻执行?
  • 多个工具调用同时出现时,哪些能并发,哪些必须排队?
  • 自动执行命令时,权限应该怎么判断?
  • 上下文窗口越来越满时,哪些信息保留,哪些信息压缩?
  • MCP(Model Context Protocol,模型上下文协议)工具很多时,怎么避免 prompt 被工具 schema 撑爆?
  • 多 Agent 协作一定要多进程吗?

这些问题没有一个能靠“调一次 API(Application Programming Interface,应用程序编程接口)”解决。Claude Code 的架构可以拆成几个核心模块来看。

整体架构:一个带权限系统的流式工具执行循环

Claude Code 用 TypeScript 编写,运行在 Node.js 上。终端 UI(User Interface,用户界面)基于 React + Ink,Ink 可以把 React 组件渲染到命令行里,所以 Claude Code 的交互界面并不是传统的纯字符串拼接,而是一个真正的组件化 TUI(Text-based User Interface,文本用户界面)。

核心目录可以按职责分成几组:

目录主要职责
entrypoints/程序入口、启动初始化
query/Agent 主循环、模型请求、工具调用调度
tools/文件读写、搜索、命令执行、MCP 工具等工具实现
commands/斜杠命令,例如 /help/loop
services/API 调用、MCP 连接、分析、远程配置等业务逻辑
components/ / hooks/React/Ink 终端 UI 层
state/应用状态管理
utils/swarm/多 Agent 协作
skills/Skill 扩展系统

工具系统是整个 Agent 的骨架。模型负责推理和生成工具调用,工具负责接触真实世界,例如读文件、改代码、执行 shell 命令、访问 MCP 服务。

Claude Code 工具系统架构

图里的关键关系是:入口初始化运行环境,query 模块驱动 Agent 循环,模型输出被转换成工具调用,工具调用再进入权限和调度系统。UI 层只负责展示交互状态,不直接决定工具是否执行;真正的执行权在工具系统和权限管道里。

可以把它抽象成一句话:

Claude Code 是一个运行在终端里的、带权限系统的、支持流式工具执行的 AI Agent 运行时。

这句话里有三个关键词:流式工具权限。后面的设计几乎都围绕这三点展开。

启动流程:信任边界之前只做安全初始化

一个 CLI Agent 启动时容易犯两个错误:要么启动阶段加载太多东西,导致首屏慢;要么在用户确认信任之前就读取项目、连接服务、启动语言服务器,扩大安全风险。

Claude Code 把启动拆成多个阶段,并在中间放了一条“信任边界”。

Claude Code 启动流程

启动流程可以按阶段理解:

阶段做什么设计目的
Stage 1并行预取 MDM 配置和 Keychain 信息能并行的尽早开始,减少等待
Stage 2校验 settings.jsonCLAUDE.md 等配置配置错误尽早失败,不进入复杂 UI
Stage 3只应用 CA 证书和网络相关环境变量在第一次 TLS 握手前准备证书环境
信任边界用户确认当前项目是否可信把安全敏感操作放到确认之后
Stage 4展示信任对话框明确用户授权点
Stage 5初始化 OAuth、Git、LSP、遥测等模块信任确认后再接触项目和外部服务
Stage 6延迟加载用户上下文、文件统计、提示信息首屏出来后再做非关键工作

几个缩写需要先讲清楚:

  • MDM(Mobile Device Management,移动设备管理)常用于企业策略下发。
  • Keychain 是 macOS 的密钥存储服务。
  • CA(Certificate Authority,证书颁发机构)用于证书链校验。
  • TLS(Transport Layer Security,传输层安全协议)是 HTTPS 等安全连接的基础。
  • OAuth(开放授权)用于用户授权登录。
  • LSP(Language Server Protocol,语言服务器协议)用于编辑器和语言服务交互。

这里的思路很清晰:

  1. 配置校验要早,失败要快。
  2. 网络证书类环境要早,因为运行时可能在第一次 TLS 请求时缓存证书状态。
  3. Git 仓库扫描、LSP 启动、遥测等操作要放在信任确认之后。
  4. 远程配置、上下文统计这类不影响首屏的任务要延迟。

用流程图表示就是:

flowchart TD
    A[启动 CLI] --> B[并行读取企业配置与密钥]
    B --> C[校验本地配置]
    C --> D[应用安全网络环境变量]
    D --> E{项目是否已信任}
    E -- 否 --> F[展示信任确认]
    F --> G[完整初始化]
    E -- 是 --> G
    G --> H[渲染 REPL 首屏]
    H --> I[延迟加载上下文与提示信息]

REPL(Read-Eval-Print Loop,读取-求值-输出循环)在这里指命令行交互主界面。启动优化不是简单地“少做事”,而是把任务按安全性和首屏必要性重新排序。

Agent 主循环:模型边返回,工具边执行

Claude Code 的 Agent 循环主要由 query.tsQueryEngine 驱动。Agent 的职责不是只调用一次模型,而是在“用户输入—模型响应—工具执行—结果回填—继续推理”之间反复循环。

Claude Code Agent 循环核心流程

图中的流程可以拆成四步:

  1. 准备请求上下文
    把用户消息、系统提示、项目上下文、历史消息、Git 状态、Memory 信息、可用工具等内容组合成请求。

  2. 调用 Claude API 并开启流式响应
    模型不是一次性返回完整结果,而是通过流式事件不断吐出文本块、工具调用块等内容。

  3. 工具调用到达后立即执行
    如果流里出现一个完整的工具调用,执行器会尽快启动对应工具,而不是等模型整段消息结束。

  4. 根据停止原因决定是否继续
    如果模型还需要工具结果,就把结果写回消息列表,再跑下一轮;如果模型已经完成回答,循环结束。

核心结构可以写成伪代码:

type QueryEngineOptions = {
  maxTurns?: number;      // 最大循环轮次,防止 Agent 无限自我循环
  maxBudgetUsd?: number;  // 费用上限,防止长任务失控
};

async function runAgentLoop(input: UserMessage, options: QueryEngineOptions) {
  const messages = buildInitialMessages(input);

  for (let turn = 0; turn < options.maxTurns; turn++) {
    const stream = callClaudeApi({
      messages,
      tools: getVisibleTools(),
    });

    const toolResults = await consumeStreamAndRunTools(stream);

    messages.push(...toolResults);

    if (stream.stopReason === "end_turn") {
      return buildFinalAnswer(messages);
    }

    if (isBudgetExceeded(options.maxBudgetUsd)) {
      throw new Error("Budget exceeded");
    }
  }

  throw new Error("Max turns exceeded");
}

这里最重要的不是循环本身,而是流式工具执行。传统做法通常是等模型完整返回,再解析里面有哪些工具调用,然后执行。Claude Code 更激进:工具调用块一旦完整,就进入执行队列。

这样做能减少端到端延迟。比如模型先决定读取 package.json,后面还在生成别的内容时,文件读取已经开始了。等模型需要下一个推理步骤时,工具结果可能已经准备好。

流式工具执行:并发安全由工具自己声明

Agent 经常会一次触发多个工具调用。假设模型同时要做三件事:

  • package.json
  • 搜索 src/**/*.ts
  • 修改 src/index.ts

读文件和搜索通常可以并发执行,但写文件不能随便和其他修改操作并发,否则可能产生文件冲突。Claude Code 用 StreamingToolExecutor 做调度,并要求每个工具声明自己是否并发安全。

工具接口可以抽象成这样:

type Tool<Input, Output> = {
  name: string;
  inputSchema: ZodSchema<Input>;

  // 根据输入判断本次调用是否能并发执行
  isConcurrencySafe(input: Input): boolean;

  validateInput(
    input: unknown,
    context: ToolContext
  ): ValidationResult<Input>;

  call(
    input: Input,
    context: ToolContext
  ): Promise<{ data: Output }>;

  isEnabled(context: ToolContext): boolean;
};

这里有一个细节:并发安全不是工具的固定属性,而是可以根据输入动态判断。例如同一个文件编辑工具,修改不同文件时也许可以并行,修改同一个文件就必须串行。把判断权交给工具,比框架写死规则更灵活。

调度逻辑可以画成这样:

flowchart LR
    A[流式工具调用到达] --> B{是否通过输入校验}
    B -- 否 --> E[返回校验错误]
    B -- 是 --> C{是否并发安全}
    C -- 是 --> D[进入并发执行池]
    C -- 否 --> F[进入串行队列]
    D --> G[收集工具结果]
    F --> G
    G --> H[写回 Agent 消息]

这种设计同时解决两个问题:

问题解决方式
全部串行太慢只读工具进入并发池
全部并发容易冲突写文件、执行 Bash 等状态变更操作进入串行队列

每个工具调用还会绑定独立的 AbortController。用户按 Esc 取消时,正在运行的子进程或异步任务可以被精确终止,而不是粗暴杀掉整个 CLI 进程。

权限系统:权限是管道,不是一个开关

AI 编程 Agent 最大的风险来自工具执行,尤其是 Bash 命令、文件写入、Git 操作、部署命令。一个成熟的权限系统不能只有“允许”和“拒绝”两个按钮,它需要多层决策。

Claude Code 支持多种权限模式:

模式行为适合场景
default每次敏感操作都询问交互式开发
bypassPermissions默认放行CI(Continuous Integration,持续集成)或受控自动化环境
dontAsk默认拒绝高限制环境
acceptEdits自动接受编辑类操作只希望自动改文件,不希望自动执行命令
auto分类器自动判断需要减少打扰,同时保留安全边界

auto 模式最有参考价值。它不是一次判断完成,而是四层递进。

Claude Code 四层权限决策管道

图里的四层可以理解成从快到慢、从确定规则到模型判断的管道:

层级判断方式特点
规则匹配用户配置的 allow/deny 字符串规则极快,适合明确命中
Bash 分类器对 shell 命令做模式识别能拦截 rm -rf、force push、生产部署等高风险命令
Transcript 分类器基于整段对话上下文判断能识别单条命令看不出的风险
独立模型分类调用独立 Claude Sonnet 做安全分类最慢,但能处理复杂边界情况

权限管道的价值在于“快路径”和“慢路径”分离。常见明确场景在前两层解决,少数复杂场景才进入上下文分类或模型分类。这样既不会每次都调用模型增加延迟,也不会把所有命令都交给正则表达式硬判。

一个权限请求可以抽象成:

async function decidePermission(request: ToolRequest): Promise<PermissionResult> {
  const ruleResult = matchUserRules(request);
  if (ruleResult.isFinal) return ruleResult;

  if (request.tool === "Bash") {
    const bashResult = classifyBashCommand(request.input.command);
    if (bashResult.isFinal) return bashResult;
  }

  const transcriptResult = classifyWithConversationContext(request);
  if (transcriptResult.isFinal) return transcriptResult;

  return classifyWithDedicatedModel({
    request,
    temperature: 0,
  });
}

温度设为 0 的独立模型调用用于降低输出随机性。安全分类需要稳定结果,而不是创造性回答。

上下文管理:固定大小窗口里的信息取舍

Agent 的上下文窗口可以理解成一个容量固定的背包。背包里要放系统提示、用户消息、历史对话、工具结果、项目说明、Memory、Git 状态、MCP 指令、工具 schema。装得太少,模型不知道项目背景;装得太多,费用上升,还可能超过上下文限制。

Claude Code 的上下文由多个来源拼装。

Claude Code 上下文管理

图里的设计重点是:不同信息使用不同加载策略。

上下文来源加载策略原因
CLAUDE.md / .claude.md从项目目录向上收集支持项目、目录、用户级说明
MEMORY.md索引常驻,topic 按需加载避免把全部记忆塞进 prompt
Git 状态memoized 缓存避免重复执行 Git 命令
MCP 服务器 instructions增量注入连接变化时只通知变化部分
工具列表名字先可见,schema 按需加载减少 token 消耗
历史消息接近上限时压缩支持长会话

Memory 系统可以分成索引和正文两层。索引文件限制行数和大小,只记录有哪些 topic;真正的 topic 文件在需要时才加载。Memory 类型大致包括:

  • user:用户偏好和画像。
  • feedback:历史交互反馈。
  • project:项目背景。
  • reference:外部资源指针。

当 token 使用量接近上下文窗口上限时,会触发 Message Compaction,也就是消息压缩。早期工具调用和结果会被压缩成摘要消息,保留关键事实,删除冗余细节。压缩过的内容后续还可以再次压缩。

flowchart TD
    A[历史消息持续增长] --> B{是否接近上下文上限}
    B -- 否 --> C[继续正常对话]
    B -- 是 --> D[选择早期消息与工具结果]
    D --> E[生成摘要消息]
    E --> F[替换原始长消息]
    F --> G[继续 Agent 循环]

还有一个容易忽略的优化:默认输出 token 上限不需要一开始就开到很大。很多请求的 p99(第 99 百分位)输出长度并没有那么夸张,所以默认可以给较小的 max output tokens。如果不够,再 retry 并提高上限。这样可以减少模型服务端的计算资源预留。

渐进式工具披露:工具很多时,先给名字,不给完整 schema

Agent 工具越多,prompt 越容易膨胀。尤其接入多个 MCP 服务器后,上百个工具的 JSON Schema(JavaScript Object Notation Schema,JSON 结构定义)可能占掉大量上下文窗口。

Claude Code 用“渐进式披露”解决这个问题。

Claude Code 渐进式工具披露流程

工具来源分成三类:

工具来源加载方式
内置工具核心工具直接加载,部分工具只披露名称
MCP 工具默认延迟加载
Skill 工具只加载 frontmatter,完整内容调用时再读

模型一开始只知道有一些工具名字和简短描述。如果需要完整定义,就调用 ToolSearch 获取工具 schema。查询方式可以支持精确选择、关键词搜索和排序:

select:Read,Edit,Grep
notebook jupyter
+slack send

含义分别是:

查询含义
select:Read,Edit,Grep按工具名精确取回
notebook jupyter用关键词搜索并排序
+slack send必须包含 slack,再按 send 排序

这套机制的收益很直接:工具数量从几十个增长到几百个时,prompt 不会线性膨胀。模型只在需要调用某个工具时才拿完整 schema。

Skill 系统:Markdown 变成可调用能力

Skill 是 Claude Code 的扩展机制,本质上是带 frontmatter 的 Markdown 文件。frontmatter 是 Markdown 文件开头用 YAML 写的元数据区域,用来描述 Skill 的名字、用途、触发条件、允许使用的工具等信息。

Claude Code Skill 加载架构

Skill 可以来自多个层级,加载时存在优先级:

  1. 内置 Skill。
  2. 企业策略下发的 Skill。
  3. 项目级 Skill,例如 .claude/skills/
  4. 插件提供的 Skill。
  5. 用户级 Skill,例如 ~/.claude/skills/

一个 Skill 的配置可以长这样:

---
name: database-migration-helper
description: Help inspect and edit database migration files
when_to_use: Use when changing schema migration scripts
arguments:
  - migration_name
allowed-tools:
  - Bash
  - Read
  - Edit
model: sonnet
user-invocable: true
context: inline
effort: medium
paths: "migrations/**"
hooks:
  PreToolUse:
    - check-migration-safety
---

其中 paths 很关键。假设某个 Skill 只适合数据库迁移文件,那么配置 paths: "migrations/**" 后,只有当用户编辑迁移目录里的文件时,它才出现在候选能力里。

参数替换也很实用:

写法含义
$ARGUMENTS全部参数
$0第一个参数
$filename命名参数

Skill 系统的核心不是“多一种命令格式”,而是让 Agent 能按场景加载能力。frontmatter 只需要几百 tokens,完整说明在真正调用时再进入上下文,因此可以注册很多 Skill 而不立即撑爆 prompt。

MCP 集成:外部工具的一等入口

MCP 是 Claude Code 连接外部工具和数据源的重要机制。它可以把本地服务、远程服务、企业内部系统暴露成 Agent 可调用工具。

Claude Code MCP 集成生命周期

MCP 集成要解决五类问题:

问题设计
配置从哪里来全局配置、项目配置、企业策略、插件市场
怎么连接Stdio、SSE、WebSocket、HTTP
怎么认证PKCE OAuth、Session Token、Token Refresh
工具怎么进入 prompt先拿列表,schema 按需加载
服务器变化怎么通知模型用 system reminder 增量通知

几个协议和认证名词需要拆开看:

  • Stdio 是标准输入输出,适合本地子进程 MCP 服务。
  • SSE(Server-Sent Events,服务器发送事件)适合服务端向客户端持续推送事件。
  • WebSocket 是双向长连接协议。
  • HTTP(HyperText Transfer Protocol,超文本传输协议)可以用于可流式传输。
  • PKCE(Proof Key for Code Exchange,授权码交换证明密钥)是 OAuth 的安全增强机制。
  • Session Token 用于维持会话。
  • Token Refresh 用于访问令牌过期后的刷新。

MCP 工具默认延迟加载很重要。连接建立后,系统可以知道服务器有哪些工具,但不会把所有 schema 全部塞进 prompt。模型需要用某个工具时,再通过 ToolSearch 获取完整定义。

多个 MCP 服务器同时认证失败时,还要处理竞态。例如多个连接同时返回 401,认证缓存如果并发读写,很容易出现重复弹窗或 token 覆盖。序列化写入链可以保证认证状态按顺序更新。

多 Agent 协作:上下文隔离比物理隔离更关键

Claude Code 的多 Agent 系统叫 Swarm。它支持三种后端:

后端运行方式适合场景
InProcess同一个 Node.js 进程内运行默认模式,开销低
Tmux使用 tmux pane需要可见终端隔离
iTerm2使用 iTerm2 tabmacOS 图形终端协作

Claude Code 多 Agent 协作架构

InProcess 模式最有意思。它没有为每个 Agent 启动独立进程,而是用 Node.js 的 AsyncLocalStorage 做上下文隔离。

runWithTeammateContext(
  {
    agentId,
    teamName,
    role: "member",
  },
  async () => {
    // 这里读取到的是当前 teammate 的上下文
    const sessionId = getSessionId();
    const permissions = getPermissionContext();

    await runAgentTask({ sessionId, permissions });
  }
);

AsyncLocalStorage 可以在同一个进程的异步调用链里保存独立上下文。这样多个 Agent 共享进程和内存,但每个 Agent 看到自己的 session、权限、任务状态。

权限同步通过 mailbox 机制完成:

sequenceDiagram
    participant Member as Member Agent
    participant Mailbox as Mailbox
    participant Lead as Lead Agent
    participant UI as Terminal UI

    Member->>Mailbox: 发起权限请求
    Mailbox->>Lead: 通知 lead 审批
    Lead->>UI: 展示 worker 权限请求
    UI-->>Lead: 用户批准或拒绝
    Lead->>Mailbox: 写入审批结果
    Mailbox-->>Member: 返回权限结果

每个 Team 有一个 lead agent 和多个 member agent。lead 负责分配任务和审批权限,member 负责执行子任务。配置可以存放在类似 ~/.claude/teams/{team-name}/config.json 的位置。

这个设计的重点是:多 Agent 不一定等于多进程。很多场景只需要上下文隔离,不需要操作系统级隔离。同进程方案可以减少进程间通信成本,也更容易共享缓存和工具注册表。

/loop 与定时任务:把 Agent 变成周期性执行器

Claude Code 还有定时任务能力,/loop 可以周期性执行某个命令或自然语言任务,底层对应 CronCreateTool。

Claude Code 定时任务体系

常见用法如下:

/loop 5m /babysit-prs
/loop 30m check the deploy
/loop check status every 20m
/loop

时间间隔会转换成 cron 表达式:

输入cron 表达式含义
5m*/5 * * * *每 5 分钟
2h0 */2 * * *每 2 小时
1d0 0 * * *每天 0 点

cron 的最小粒度通常是分钟,所以秒级间隔会向上取整。不均匀间隔也需要处理,例如 7 分钟无法均匀划分一小时,系统可以提示并取整到更合适的值。

任务有两种存储方式:

类型存储位置生命周期
会话级任务内存CLI 退出后消失
持久任务.claude/scheduled_tasks.json重启后仍存在

定时任务创建后会立即执行一次,而不是等待第一个 cron 触发点。这对“每隔一段时间检查一次”的任务更符合直觉,因为用户创建任务时往往希望马上得到第一次结果。

更重的远程触发器可以在云端调度远程 Agent。远程任务通常使用 UTC(Coordinated Universal Time,协调世界时),最小间隔可能比本地任务更长,并且可以挂接 Slack、Datadog 等外部连接器。

Hook 系统:扩展能力不应该写死在核心流程里

Claude Code 的可扩展性很大程度来自 Hook。Hook 是生命周期事件上的插入点,用户或插件可以在这些点执行自定义逻辑。

常见事件包括:

Hook触发时机
PreToolUse工具执行前
PostToolUse工具执行后
UserPromptSubmit用户提交消息时
SessionStart会话开始
FileChanged文件变化
PermissionRequest请求权限
PermissionDenied权限被拒
SubagentStart子 Agent 启动
ElicitationURL 唤起协议相关事件

Hook 可以来自两种地方:

  1. SDK(Software Development Kit,软件开发工具包)回调函数。
  2. settings 配置里的 shell 命令。

例如,一个团队可以在 PreToolUse 里禁止修改某些敏感文件,在 FileChanged 后自动运行格式化或测试,在 UserPromptSubmit 时注入内部规范检查。

可扩展系统的关键是预留稳定事件,而不是把所有需求都写进核心代码。核心只定义生命周期,扩展逻辑交给用户、插件和企业策略。

状态管理:基础设施状态和 UI 状态分开

Claude Code 的状态大致分两层。

状态层示例生命周期
全局基础设施状态session ID、项目根目录、模型配置、token 统计、team 上下文、hook 注册表整个进程
React 应用状态对话消息、任务列表、UI 展开状态、当前权限弹窗UI 渲染周期

全局状态适合放跨 Agent、跨组件共享的基础信息。React 状态适合放跟渲染强相关的数据,例如消息列表和输入框状态。

边界清晰很重要。如果所有东西都塞进全局变量,UI 更新会混乱;如果所有基础设施状态都放进 React,非 UI 模块又会被迫依赖渲染层。Agent 运行时需要明确区分“系统状态”和“界面状态”。

费用追踪:Agent 失控时要能止损

长任务 Agent 可能连续调用模型、搜索网页、执行工具。如果没有费用追踪,用户很难知道一次会话已经消耗多少。

Claude Code 按模型统计 token:

模型维度统计项
Opusinput、output、cache tokens
Sonnetinput、output、cache tokens
Haikuinput、output、cache tokens

缓存命中的 token 通常按较低价格计算。Web 搜索这类工具可能按次数固定收费。会话费用可以通过 session ID 关联到项目配置,恢复旧会话时继续累计,新建会话则从零开始。

费用系统还需要处理未知模型价格。如果遇到无法识别的模型,应该明确标记费用可能不准确,而不是假装统计无误。

对 Agent 产品来说,费用上限和最大轮次一样,都是安全阀:

type AgentSafetyLimits = {
  maxTurns: number;
  maxBudgetUsd: number;
  maxToolCalls: number;
  timeoutMs: number;
};

没有这些限制,Agent 在错误循环里可能持续消耗 API 费用和本机资源。

快捷键系统:终端 Agent 也需要可定制交互

Claude Code 的快捷键配置可以放在 ~/.claude/keybindings.json

{
  "bindings": [
    {
      "context": "main",
      "bindings": {
        "ctrl+shift+k": "/help",
        "cmd+enter": "submit",
        "ctrl+k ctrl+s": "toggleSearch"
      }
    }
  ]
}

它支持 chord binding,也就是组合键序列。例如 ctrl+k ctrl+s 表示先按 ctrl+k,再按 ctrl+s

快捷键解析器需要支持别名:

别名统一含义
ctrl / controlControl
alt / opt / optionOption / Alt
cmd / command / super / winCommand / Super

快捷键还要区分上下文。mainsearchtextareainput 等场景下,同一个按键可以有不同含义。Esc 这类保留按键通常不能随意重绑,否则会破坏基础交互。

配置热重载也很实用,用户修改配置后不必重启 CLI。

Source Map 发布风险:打包产物需要检查调试文件

从工程发布角度看,Source Map 事故给所有 TypeScript / JavaScript CLI 项目提了一个醒:构建产物不只要能运行,还要确认不携带不该发布的调试材料。

常见检查项包括:

检查项风险
.map 文件可能包含源码、路径、注释
内嵌 sourceMappingURL可能指向可下载 Source Map
base64 内联 Source Map可能把源码塞进打包文件
绝对路径暴露内部目录结构和用户名
未清理测试文件暴露内部接口或测试数据
feature flag 名称暴露未发布功能线索

发布前可以加一道脚本:

#!/usr/bin/env bash
set -euo pipefail

DIST_DIR="${1:-dist}"

echo "Checking source maps..."
if find "$DIST_DIR" -name "*.map" | grep -q .; then
  echo "Error: source map files found in $DIST_DIR"
  exit 1
fi

echo "Checking sourceMappingURL..."
if grep -R "sourceMappingURL" "$DIST_DIR"; then
  echo "Error: sourceMappingURL found in bundle"
  exit 1
fi

echo "Checking inline base64 maps..."
if grep -R "data:application/json;base64" "$DIST_DIR"; then
  echo "Error: inline source map found"
  exit 1
fi

echo "Release artifact check passed."

如果确实需要 Source Map,也应该上传到受控的错误追踪平台,而不是随 npm 包公开分发。

可复用的 Agent 架构原则

从 Claude Code 的设计里,可以抽象出一组通用原则,适合用在自己的 AI Agent 框架里。

原则具体做法
流式优先模型工具调用一旦完整就开始执行,不必等待整段回复结束
工具声明并发安全性读操作并发,写操作串行,判断逻辑由工具自己提供
权限做成管道规则、命令分类、上下文分类、独立模型判断逐层递进
上下文按需装载Memory、MCP 指令、工具 schema、Skill 内容都不要一次性全塞进 prompt
长会话自动压缩接近窗口上限时,把早期工具调用和结果压缩成摘要
多 Agent 重视上下文隔离不必默认多进程,同进程隔离也能覆盖很多协作场景
扩展点靠 Hook在生命周期事件上开放插入点,避免把插件逻辑写死
成本和轮次必须设上限防止 Agent 自循环导致费用和资源失控
启动流程分阶段信任边界前只做安全初始化,重模块延迟加载

做 Agent 产品时,模型能力只是起点。真正决定可用性的,是模型之外的工程系统:工具如何调度,权限如何判断,上下文如何裁剪,扩展如何接入,错误如何恢复,费用如何止损。

Claude Code 的架构说明了一件事:生产级 AI Agent 不是一个“大模型调用器”,而是一个围绕模型构建的运行时。模型负责推理,运行时负责把推理安全、稳定、可控地落到真实环境里。


评论