Claude Code 工具调用架构深度解析:六层防御与渐进式加载

本文基于 Claude Code v2.1.88 源码,深入分析其工具调用(Tool Use)的准确性保障与可控制性机制,并探讨如何在自己的项目中实现类似能力。


一、问题背景

大语言模型(LLM)通过 function calling(工具调用)与外部系统交互。但模型的输出并不总是可靠的------参数类型可能错误,调用的命令可能危险,执行顺序可能引发竞态条件。

Claude Code 作为 Anthropic 官方的 CLI 工具,拥有一套多层防御体系来保证工具调用的准确性与安全性。下面逐层拆解。


二、工具调用流水线:从模型输出到实际执行

当模型生成一个 tool_use 块时,数据会经过以下六层检查:

复制代码
模型输出 tool_use(工具名 + 参数)
    │
    ▼
[1] 工具查找 ---→ 不存在?返回 "No such tool" 错误
    │
    ▼
[2] Zod Schema 校验 ---→ 类型不匹配?返回 "InputValidationError" 错误
    │
    ▼
[3] validateInput 业务校验 ---→ 不合法?返回工具特定的错误信息
    │
    ▼
[4] Pre-Tool Hooks ---→ Hook 拦截?停止执行
    │
    ▼
[5] 权限决策(规则 → Hook → AI分类器 → 用户弹窗)---→ deny?返回错误
    │
    ▼
[6] 执行工具(并发/串行取决于安全性)
    │
    ▼
返回结果给模型

每一层都能独立拦截不安全的操作。核心设计哲学是 fail-closed(不确定就拒绝)defense-in-depth(纵深防御)


三、六层防御机制详解

3.1 第一层:Zod Schema 强类型校验

每个工具都定义了严格的 Zod Schema。模型输出是松散的 JSON,必须在执行前通过类型校验。

源码中有一段耐人寻味的注释:

typescript 复制代码
// toolExecution.ts:614
// "surprisingly, the model is not great at generating valid input"
// (令人惊讶的是,模型并不擅长生成合法的输入)
const parsedInput = tool.inputSchema.safeParse(input)
if (!parsedInput.success) {
  return [{
    message: createUserMessage({
      content: [{
        type: 'tool_result',
        content: `<tool_use_error>InputValidationError: ${errorContent}</tool_use_error>`,
        is_error: true,
        tool_use_id: toolUseID,
      }],
      toolUseResult: `InputValidationError: ${parsedInput.error.message}`,
    }),
  }]
}

模型可能把数组输出为字符串,把数字输出为布尔值------这些错误在 Schema 层就被拦截,永远不会到达工具执行逻辑。

3.2 第二层:业务逻辑校验(validateInput)

Schema 只能检查类型。业务规则由每个工具自定义实现:

typescript 复制代码
// toolExecution.ts:683-733
const isValidCall = await tool.validateInput?.(parsedInput.data, toolUseContext)
if (isValidCall?.result === false) {
  // 返回工具特定的错误信息
  return [{
    message: createUserMessage({
      content: [{
        type: 'tool_result',
        content: `<tool_use_error>${isValidCall.message}</tool_use_error>`,
        is_error: true,
      }],
    }),
  }]
}

例如:

  • Bash 工具 会解析命令的 AST 结构,检查是否包含危险操作
  • 文件工具 会校验路径是否在工作目录范围内
  • Agent 工具 会检查嵌套深度是否超限

3.3 第三层:Hook 系统 --- 可插拔的前/后置拦截

每个工具执行前都会运行用户自定义的 PreToolUse hooks

typescript 复制代码
// toolExecution.ts:800-862
for await (const result of runPreToolUseHooks(toolUseContext, tool, ...)) {
  switch (result.type) {
    case 'message':              // Hook 返回附加消息
    case 'hookPermissionResult': // Hook 直接做出 allow/deny 决定
    case 'hookUpdatedInput':     // Hook 修改了工具输入参数
    case 'preventContinuation':  // Hook 阻止后续工具执行
    case 'stop':                 // Hook 强制停止,立即返回
  }
}

Hook 可以拦截执行、修改参数、注入上下文、甚至阻止后续所有工具的执行。这是用户自定义安全策略的核心入口。

3.4 第四层:权限系统 --- 多层决策

这是控制力的核心。权限决策的结果有三种:

typescript 复制代码
// types/permissions.ts
type PermissionBehavior = 'allow' | 'deny' | 'ask'

决策来源是多层级的,按优先级依次尝试:

复制代码
① 用户配置的规则(alwaysAllow / alwaysDeny)
   ↓ 没有匹配的规则
② Hook 的权限决策
   ↓ Hook 没有决策
③ AI 分类器自动判断(仅 auto 模式)
   ↓ 分类器不确定
④ 弹窗让用户手动确认(最终兜底)

权限来源的定义覆盖了所有可能的场景:

typescript 复制代码
// types/permissions.ts
type PermissionDecisionReason =
  | { type: 'rule',              rule: PermissionRule }           // 用户配置的规则
  | { type: 'mode',              mode: PermissionMode }           // 权限模式决定
  | { type: 'hook',              hookName: string }               // Hook 拦截
  | { type: 'classifier',        classifier: string, reason }     // AI 分类器
  | { type: 'permissionPromptTool', ... }                         // 用户弹窗
  | { type: 'safetyCheck',       reason: string }                 // 安全检查
  | { type: 'sandboxOverride',   reason: string }                 // 沙盒覆盖
  | { type: 'workingDir',        reason: string }                 // 工作目录限制
  | ...

3.5 第五层:并发控制 --- 安全的工具并行,危险的操作串行

模型可能在一次响应中输出多个工具调用。系统会根据工具的安全性自动分组:

typescript 复制代码
// toolOrchestration.ts:91-116
function partitionToolCalls(toolUseMessages, toolUseContext): Batch[] {
  return toolUseMessages.reduce((acc, toolUse) => {
    const tool = findToolByName(toolUseContext.options.tools, toolUse.name)
    const isConcurrencySafe = /* 校验后 */ false

    if (isConcurrencySafe && acc[acc.length - 1]?.isConcurrencySafe) {
      acc[acc.length - 1].blocks.push(toolUse)  // 合并到并发批次
    } else {
      acc.push({ isConcurrencySafe, blocks: [toolUse] })  // 新建串行批次
    }
    return acc
  }, [])
}

执行策略:

typescript 复制代码
// toolOrchestration.ts:26-81
for (const { isConcurrencySafe, blocks } of partitionedBatches) {
  if (isConcurrencySafe) {
    // 读操作(Read、Glob、Grep)→ 并行执行,上限 10 个
    yield* runToolsConcurrently(blocks, ...)
  } else {
    // 写操作(Write、Edit、Bash)→ 串行执行,避免竞态
    yield* runToolsSerially(blocks, ...)
  }
}

默认策略是 fail-closed:不确定时假设不安全。

typescript 复制代码
// Tool.ts 中的默认值 --- 安全优先
const TOOL_DEFAULTS = {
  isConcurrencySafe: () => false,   // 默认不可并发
  isReadOnly:        () => false,   // 默认假设是写操作
  isDestructive:     () => false,
}

3.6 第六层:Bash 工具的特殊安全机制

Bash 工具是最危险的工具,拥有额外的安全层:

  • AST 解析:通过 shell-quote 解析器将命令拆解为子命令,逐一检查
  • AI Classifier :在 auto 模式下使用独立的小模型判断命令安全性
  • 沙盒隔离 :需要 dangerouslyDisableSandbox 显式授权
  • 内部字段保护:防止模型伪造内部状态(后文详述)

四、六个巧妙的设计

在校验和权限的基础之上,Claude Code 还有几个让人眼前一亮的设计。

4.1 投机式分类器(Speculative Classifier)--- 安全与速度兼得

AI 分类器需要几百毫秒才能判断命令安全性,但权限弹窗也需要时间。系统的做法是在 Hook 执行的同时就悄悄启动分类器

typescript 复制代码
// bashPermissions.ts:1482-1541
const speculativeChecks = new Map<string, Promise<ClassifierResult>>()

// 在工具校验阶段就提前启动分类
export function startSpeculativeClassifierCheck(command, ...): boolean {
  const promise = classifyBashCommand(command, cwd, allowDescriptions, ...)
  promise.catch(() => {})  // 防止 unhandled rejection
  speculativeChecks.set(command, promise)
  return true
}

// 在权限决策阶段消费结果
export function consumeSpeculativeClassifierCheck(command): Promise | undefined {
  const promise = speculativeChecks.get(command)
  if (promise) speculativeChecks.delete(command)
  return promise
}

时间线对比:

复制代码
没有投机:                          有投机:
[Hook 运行 200ms]                  [Hook 运行 200ms]
[分类器 300ms]                      [分类器 300ms] ← 同时进行
[弹窗等待...]                      [弹窗等待...]  ← 分类结果已就绪
总计:500ms + 弹窗                  总计:300ms + 弹窗(节省200ms)

这是一个经典的**时间重叠(time-overlap)**设计,让安全检查几乎零额外延迟。

4.2 Sed 编辑模拟 --- 把 Bash 命令"翻译"成用户友好的 Diff

当模型用 sed -i 's/foo/bar/g' file.txt 修改文件时,系统不会直接弹出一个冷冰冰的 shell 命令让用户确认,而是:

  1. 解析 sed 命令,提取出 {pattern, replacement, flags, filePath}
  2. 读取目标文件的当前内容
  3. 在内存中用 JavaScript 正则引擎模拟 sed 替换
  4. 显示和 Edit 工具一样的可视化 diff 预览
typescript 复制代码
// sedEditParser.ts --- 解析 sed 命令
export type SedEditInfo = {
  filePath: string        // 文件路径
  pattern: string         // 搜索模式(正则)
  replacement: string     // 替换内容
  flags: string           // 标志位(g、i 等)
  extendedRegex: boolean  // 是否使用扩展正则
}

export function parseSedEditCommand(command: string): SedEditInfo | null {
  // 解析 sed -i 's/pattern/replacement/flags' file 形式的命令
  // 使用 AST 解析器确保安全性
  const parseResult = tryParseShellCommand(withoutSed)
  // ... 校验 flags 只允许安全字符
  const validFlags = /^[gpimIM1-9]*$/
  // ...
}
typescript 复制代码
// sedEditParser.ts --- 在 JS 中模拟 sed 替换
export function applySedSubstitution(content: string, sedInfo: SedEditInfo): string {
  let regexFlags = ''
  if (sedInfo.flags.includes('g')) regexFlags += 'g'
  if (sedInfo.flags.includes('i')) regexFlags += 'i'
  // ... 将 sed 正则语法转换为 JavaScript 正则
}

这是一个语义提升的设计:把底层的 shell 操作翻译成用户可以直观审查的 diff 视图。

4.3 Denial Tracking --- 自适应降级

auto 模式下,AI 分类器自动决定是否允许命令执行。但如果分类器连续出错怎么办?系统设计了自适应降级:

typescript 复制代码
// denialTracking.ts
export type DenialTrackingState = {
  consecutiveDenials: number   // 连续拒绝次数
  totalDenials: number         // 总拒绝次数
}

export const DENIAL_LIMITS = {
  maxConsecutive: 3,   // 连续被拒 3 次 → 降级
  maxTotal: 20,        // 总共被拒 20 次 → 降级
}

export function recordDenial(state: DenialTrackingState): DenialTrackingState {
  return {
    ...state,
    consecutiveDenials: state.consecutiveDenials + 1,
    totalDenials: state.totalDenials + 1,
  }
}

export function recordSuccess(state: DenialTrackingState): DenialTrackingState {
  if (state.consecutiveDenials === 0) return state
  return { ...state, consecutiveDenials: 0 }
}

export function shouldFallbackToPrompting(state: DenialTrackingState): boolean {
  return (
    state.consecutiveDenials >= DENIAL_LIMITS.maxConsecutive ||
    state.totalDenials >= DENIAL_LIMITS.maxTotal
  )
}

当分类器连续拒绝 3 次后,系统会自动降级到弹窗询问用户。这是一个自省机制------系统监控自己的决策质量,连续出错意味着"我的判断可能有误",主动把决策权交还给人类。

4.4 内部字段防护 --- 防止模型越权

Bash 工具的 sed 编辑预览功能依赖一个内部字段 _simulatedSedEdit。这个字段由权限系统在用户批准后注入,告诉 Bash 工具"这次 sed 已经预演过了,直接执行"。

问题来了:如果模型自己伪造这个字段呢?系统做了纵深防御:

typescript 复制代码
// toolExecution.ts:756-773
// Defense-in-depth: strip _simulatedSedEdit from model-provided Bash input.
// This field is internal-only --- it must only be injected by the permission system.
if (
  tool.name === BASH_TOOL_NAME &&
  processedInput &&
  typeof processedInput === 'object' &&
  '_simulatedSedEdit' in processedInput
) {
  const { _simulatedSedEdit: _, ...rest } = processedInput
  processedInput = rest  // 剥离掉!
}

即使 Schema 的 strictObject 将来有 bug,这层防线依然在。

4.5 backfillObservableInput --- 同一份数据,两条路径

Hook 和权限系统可能需要扩展后的字段 (如把相对路径展开为绝对路径),但 tool.call() 必须用原始字段(工具结果会嵌入路径,改了会破坏 transcript 一致性和 VCR 录制哈希)。

解决方案:一份浅拷贝,两条路径:

typescript 复制代码
// toolExecution.ts:775-793
// Backfill legacy/derived fields on a shallow clone so hooks/canUseTool see
// them without affecting tool.call(). SendMessageTool adds fields; file
// tools overwrite file_path with expandPath --- that mutation must not reach
// call() because tool results embed the input path verbatim.
let callInput = processedInput
const backfilledClone =
  tool.backfillObservableInput &&
  typeof processedInput === 'object' &&
  processedInput !== null
    ? ({ ...processedInput } as typeof processedInput)  // 浅拷贝
    : null

if (backfilledClone) {
  tool.backfillObservableInput!(backfilledClone)  // 扩展字段
  processedInput = backfilledClone                // 给 hooks 和权限系统用
}
// callInput 仍然是原始值,给 tool.call() 用

4.6 Schema 缺失时的自修复提示

当模型在未加载 Schema 的情况下硬调延迟工具,Zod 校验必然失败。系统不是简单地返回错误,而是在错误消息中附带修复指令:

typescript 复制代码
// toolExecution.ts:577-597
export function buildSchemaNotSentHint(tool, messages, tools): string | null {
  if (!isDeferredTool(tool)) return null
  if (discovered.has(tool.name)) return null  // 已发现 → 不是这个问题
  return (
    `This tool's schema was not sent to the API --- it was not in the ` +
    `discovered-tool set derived from message history. Without the schema ` +
    `in your prompt, typed parameters get emitted as strings and the ` +
    `client-side parser rejects them. Load the tool first: call ` +
    `ToolSearch with query "select:${tool.name}", then retry this call.`
  )
}

错误消息本身就是修复指令------告诉模型"你需要先调用 ToolSearch 加载这个工具,然后再重试"。这让系统能从错误中自我恢复,而不是陷入死循环。


五、MCP 工具的渐进式加载

5.1 问题:上下文窗口的 Token 竞争

MCP(Model Context Protocol)工具由外部服务注册,数量可能很多。如果把所有 MCP 工具的完整 Schema(名称 + 描述 + 参数定义)都塞进 prompt,会占用大量上下文窗口:

复制代码
50 个 MCP 工具 × 平均 500 token/schema = 25,000 token 始终占用

在 200K 上下文窗口下,这已经占了 12.5%。工具更多时问题更严重。

5.2 方案:defer_loading --- 注册空壳,按需注入

Claude Code 利用了 Anthropic API 的 defer_loading 特性:

typescript 复制代码
// api.ts:119-226
export async function toolToAPISchema(tool, options) {
  // ... 构建基础 schema
  const schema = {
    name: base.name,
    description: base.description,
    input_schema: base.input_schema,
  }

  // 关键:标记为延迟加载
  if (options.deferLoading) {
    schema.defer_loading = true  // API 只知道名字,不注入完整 schema
  }

  return schema
}

效果对比:

复制代码
普通工具: { name, description, input_schema: {完整参数定义} }
                                             → 模型完全可见,可直接调用

延迟工具: { name, defer_loading: true }      → 模型只知道名字存在
                                             → 不知道参数格式,无法调用

5.3 ToolSearchTool --- 按需搜索和加载

模型通过 ToolSearchTool 搜索并加载需要的工具:

typescript 复制代码
// ToolSearchTool.ts --- 搜索接口定义
export const inputSchema = lazySchema(() =>
  z.object({
    query: z.string().describe(
      'Query to find deferred tools. ' +
      'Use "select:<tool_name>" for direct selection, or keywords to search.'
    ),
    max_results: z.number().optional().default(5),
  }),
)

搜索支持三种查询方式:

复制代码
① 精确选择:  query="select:mcp__slack__send_message"
② 关键词搜索: query="slack send message"
③ 必须包含:  query="+slack send"  (+前缀表示必须包含该词)

关键词搜索的实现:

typescript 复制代码
// ToolSearchTool.ts:186-230
async function searchToolsWithKeywords(query, deferredTools, tools, maxResults) {
  // 快速路径:精确名称匹配
  const exactMatch = deferredTools.find(t => t.name.toLowerCase() === queryLower)
  if (exactMatch) return [exactMatch.name]

  // MCP 前缀匹配:query 以 mcp__ 开头
  if (queryLower.startsWith('mcp__')) {
    return deferredTools
      .filter(t => t.name.toLowerCase().startsWith(queryLower))
      .slice(0, maxResults).map(t => t.name)
  }

  // 分词搜索:required(+前缀)和 optional
  const requiredTerms = []
  const optionalTerms = []
  for (const term of queryTerms) {
    if (term.startsWith('+')) requiredTerms.push(term.slice(1))
    else optionalTerms.push(term)
  }
  // ... 评分排序
}

5.4 发现集的持久化 --- 跨轮次记忆

工具一旦被发现,就不需要重复加载。extractDiscoveredToolNames 扫描整个消息历史:

typescript 复制代码
// toolSearch.ts:545-592
export function extractDiscoveredToolNames(messages: Message[]): Set<string> {
  const discoveredTools = new Set<string>()

  for (const msg of messages) {
    // 从上下文压缩的 boundary 恢复(压缩后不丢失发现记录)
    if (msg.type === 'system' && msg.subtype === 'compact_boundary') {
      const carried = msg.compactMetadata?.preCompactDiscoveredTools
      if (carried) {
        for (const name of carried) discoveredTools.add(name)
      }
      continue
    }

    // 从 tool_result 中的 tool_reference 块提取
    if (msg.type !== 'user') continue
    const content = msg.message?.content
    for (const block of content) {
      if (isToolResultBlockWithContent(block)) {
        for (const item of block.content) {
          if (isToolReferenceWithName(item)) {
            discoveredTools.add(item.tool_name)
          }
        }
      }
    }
  }
  return discoveredTools
}

每次构建 API 请求时,根据已发现的工具决定注册哪些:

typescript 复制代码
// claude.ts:1154-1167
if (useToolSearch) {
  const discoveredToolNames = extractDiscoveredToolNames(messages)
  filteredTools = tools.filter(tool => {
    if (!deferredToolNames.has(tool.name)) return true       // 内置工具:始终包含
    if (tool.name === 'ToolSearch')           return true     // 搜索工具:始终包含
    return discoveredToolNames.has(tool.name)                 // MCP 工具:仅包含已发现的
  })
}

5.5 Token 预算自适应

系统会根据 MCP 工具的 token 占比自动决定是否启用延迟加载:

typescript 复制代码
// toolSearch.ts:96-117
const DEFAULT_AUTO_TOOL_SEARCH_PERCENTAGE = 10  // 上下文窗口的 10%

function getAutoToolSearchTokenThreshold(model: string): number {
  const contextWindow = getContextWindowForModel(model, betas)
  const percentage = getAutoToolSearchPercentage() / 100
  return Math.floor(contextWindow * percentage)
}

三种模式:

模式 行为
tst(默认) 所有 MCP 工具始终延迟加载
tst-auto MCP 工具总量超过 10% 上下文窗口时才启用
standard 全部内联,不做延迟加载

5.6 完整生命周期图

复制代码
┌──────────────────────────────────────────────────────┐
│  MCP Server 连接                                      │
│  工具注册到 tools[] 数组,标记 isMcp=true              │
│  → isDeferredTool() 返回 true                         │
└───────────────────┬──────────────────────────────────┘
                    ▼
┌──────────────────────────────────────────────────────┐
│  构建 API 请求                                         │
│  MCP 工具以 defer_loading=true 注册(只有名字)         │
│  ToolSearchTool 始终包含                               │
└───────────────────┬──────────────────────────────────┘
                    ▼
┌──────────────────────────────────────────────────────┐
│  模型看到工具名字列表,决定需要用某个 MCP 工具           │
│  → 调用 ToolSearch(query="select:mcp__slack__send")   │
└───────────────────┬──────────────────────────────────┘
                    ▼
┌──────────────────────────────────────────────────────┐
│  ToolSearchTool 返回 tool_reference 块                 │
│  API 自动将该工具的完整 Schema 注入模型上下文            │
└───────────────────┬──────────────────────────────────┘
                    ▼
┌──────────────────────────────────────────────────────┐
│  下一轮 API 请求                                       │
│  extractDiscoveredToolNames() 扫描消息历史              │
│  发现已引用的工具 → 完整 Schema 包含在后续请求中          │
│  模型此刻能正确构造参数并调用                            │
└──────────────────────────────────────────────────────┘

六、如何自己实现类似机制

Claude Code 的 defer_loading + tool_reference 依赖于 Anthropic API 的原生特性。如果你使用其他 API(OpenAI、Gemini 等),需要自己实现替代方案。

6.1 核心限制

复制代码
所有主流 LLM API 的硬规则:
  模型只能调用 tools[] 数组里声明过的工具
  模型输出的 tool_use 的 name 必须匹配已注册的工具名
  否则 API 直接返回错误

模型不是"看到描述就能调用",它受限于 API 的 tools 数组。你在工具描述里写再详细的说明,对 API 来说只是普通文本。

6.2 路线 A:动态注册(多轮注入)

最接近 Claude Code 原理的方案:

python 复制代码
# 伪代码
discovered = set()

def build_tools_list(all_tools, messages):
    """每次 API 调用前,扫描消息历史,动态决定注册哪些工具"""
    for msg in messages:
        if "tool_discovered:" in msg.content:
            discovered.add(extract_tool_name(msg))

    return [
        tool for tool in all_tools
        if tool.builtin
        or tool.name == "ToolSearch"
        or tool.name in discovered
    ]

# 第 1 轮:只有内置工具 + ToolSearch
response = api.chat(tools=build_tools_list(all_tools, messages))

# 第 2 轮:模型调了 ToolSearch 后,被搜索的工具加入 tools
response = api.chat(tools=build_tools_list(all_tools, messages))

优缺点

  • Token 效率最好,无工具数量上限
  • 但需要多一轮交互,实现复杂度高

6.3 路线 B:万能调度工具

不注册 10 个独立工具,注册一个通用执行器:

json 复制代码
{
  "tools": [
    {
      "name": "execute_tool",
      "description": "执行指定工具。先调用 list_tools 查看可用工具和参数说明。",
      "parameters": {
        "type": "object",
        "properties": {
          "tool_name": { "type": "string" },
          "arguments": { "type": "object" }
        },
        "required": ["tool_name", "arguments"]
      }
    },
    {
      "name": "list_tools",
      "description": "列出所有可用的 MCP 工具及其参数说明",
      "parameters": { "type": "object", "properties": {} }
    }
  ]
}
python 复制代码
async def handle_execute_tool(tool_name, arguments):
    # 在客户端路由到实际的 MCP 调用
    actual_tool = mcp_tools[tool_name]
    result = await actual_tool.call(arguments)
    return result

优缺点

  • 实现最简单,一轮就能工作
  • 但参数没有类型校验,准确率较低

6.4 路线 C:全部注册 + 精简描述

不做延迟加载,压缩每个工具的描述长度:

json 复制代码
{
  "name": "mcp__slack__send_message",
  "description": "发送 Slack 消息(调用 tool_help 获取详细参数说明)",
  "parameters": {
    "type": "object",
    "properties": {
      "channel": { "type": "string" },
      "text": { "type": "string" }
    }
  }
}

10 个精简工具约 1500 token,可以接受。需要详细信息时通过 help 工具获取。

优缺点

  • 工具始终可调用,不需要多轮
  • 适合工具数量不超过 20-50 个的场景

6.5 三种路线对比

维度 路线 A: 动态注册 路线 B: 万能调度 路线 C: 精简注册
Token 效率 最好 一般
参数类型校验
调用延迟 多一轮 可能多一轮
实现复杂度
工具数量上限 无上限 无上限 ~50 个
需要 API 特殊支持 不需要 不需要 不需要

建议:工具不超过 20 个选路线 C,工具很多或频繁增减选路线 A,快速原型选路线 B。


七、总结

Claude Code 的工具调用架构体现了三个核心设计原则:

  1. 不信任模型输出,但也不阻塞模型工作。每层校验都是非阻塞的------能自动处理的自动处理,不能处理的才交给用户。

  2. 纵深防御(Defense in Depth)。任何单一防线都可能出 bug,但六层防线同时失效的概率极低。

  3. 按需加载(Lazy Loading)。不是预先把所有信息塞给模型,而是在需要时才提供。这既节省了 token,也降低了模型出错的可能性。

这些设计思路不仅适用于 Claude Code,对任何需要 LLM 与外部工具交互的系统都有参考价值。

相关推荐
每天吃饭的羊2 小时前
nest架构
架构
孤独的小丑2 小时前
OpenClaw 架构深度剖析:从设计哲学到技术实现
架构·openclaw·tokens使用优化·大模型云api
达不溜的日记3 小时前
AutoSAR通信概述-通信服务架构
架构
小超同学你好3 小时前
Transformer 23. Qwen 3.5 架构介绍:混合线性/全注意力、MoE 与相对 Qwen 1 / 2 / 3 的演进
人工智能·深度学习·语言模型·架构·transformer
自信不孤单3 小时前
UniAda核心代码详解
python·ai·大模型·tta·狄利克雷理论·证据感知
ofoxcoding3 小时前
GPT-5.4 vs Claude Opus 4.6 实测对比:2026 年该选哪个模型写代码?
网络·gpt·ai
Ulyanov4 小时前
Streamlit基础入门与快速原型开发
python·架构·系统仿真
Agent产品评测局4 小时前
企业发票管理自动化落地,验真归档全流程实现方法:2026企业级智能体选型与实测指南
运维·网络·人工智能·ai·chatgpt·自动化
复园电子4 小时前
KVM与Hyper-V虚拟化环境:彻底解决USB外设映射掉线的底层架构优化
开发语言·架构·php