Claude Code 源码拆解 harness 设计 - 工具设计
🍊

Claude Code 源码拆解 harness 设计 - 工具设计

Tags
Date
Created
Apr 16, 2026 07:51 AM
Blocking
Blocked by
  • 总结
    • 万能工具是兜底而不是首选
      • 模型的工作偏向用最少的工具做最多的事,一定要显示的写出哪些常见用哪些工具,而不靠模型判断
    • 工具按需发现
    • 工具输出限制预算
      • 不要假设输出是可控的
      • 不要直接截断,会导致信息丢失,给模型一个摘要+指针,让模型按需/分段去读
    • 好的工具设计,不只是给模型更多能力,也要给模型更清晰的边界
    • 完美符合之前提到的 上下文窗口中的内容不是 装不装的下 而不是 该不该装进去
 
  • Claude Code 工具系统包含
    • 输入校验
    • 权限检查
    • 输出格式化
    • 并发安全
    • 分类器摘要
    • 搜索关键词
 
  • 核心设计
    • 万能工具是兜底而不是首选
      • 最反直觉的设计,在 bash 工具中 的使用说明有一个规则,不要这个工具来执行读文件、搜索、列目录等,除非已经确认专用工具无法完成任务
      • 列出文件工具偏好
        • 文库搜索用 Glob 工具而不是首选 find 命令
        • 内容搜索用 Grep 工具而不是首选 grep 命令
        • 读文件用 Read 而不是 cat 命令
        • 编辑用 Edit 而不是 sed / awk 命令
      • 为什么要这样设计
        • 万能工具是安全和可控性的敌人
          • 所有操作都走同一个工具入口,权限系统、输出管理等等设计都会被绕过
      • 专用工具的价值
        • 专用工具的价值不在于能做什么,而是它把操作限制在一个可控的范围内
        • Read 工具只能读文件,因此能精确控制它的输出格式、做去重、token预算检查
        • Edit 工具只能编辑文件,所以能做 diff 展示,路径保护,权限细分等等
      • 模型的工作偏向用最少的工具做最多的事,因此必须在万能工具的说明中引导
    • 工具的按需发现
      • 加载的工具太多浪费上下文空间,并且影响模型的性能
      • Claude Code 的做法不是拆分多个Agent 而是将工具分类
        • 核心工具:启动时直接加载,模型直接可用
          • 命令行、文件读取、文件编辑、文件写入、搜索、Agent
        • 延迟工具:模型只知道工具的名字,不知道参数格式,无法直接调用(按需发现)
          • 网页搜索、网页抓取、任务管理等
      • tool search
        • 当需要使用延迟工具时,先调用 tool search 工具传入关键词,tool search 返回匹配的工具的完整定义,包括参数格式,拿到定义后就能直接调用
        • searchHint
          • 每个工具除了完整的名字和使用说明之外还有一个 searchHint 3~10词的能力摘要
          • 专门为关键词匹配优化,用的词不和工具名重复,覆盖可能搜索的各种表述
          • 例如文件读取工具的 searchHint 是 “读取文件、图片、PDF、文本”
        • 优先级规则
          • 工具名精确匹配权重最高、searchHint匹配次优先、完整工具说明中的关键词匹配最低
        • 这样就能保证模型用简短的关键词就能找到工具
      • 缓存稳定性(排序策略)
        • 源码中将内置工具排在前面作为连续前缀,外部插件排在后面,这样即使插件工具发生变化,内置工具前缀不变,服务端的工具提示词缓存不会失效(关键在于缓存断点 见详解)
          • 详解
            • LLM处理文本是自回归 从左到右的,当模型读取到第 n 个 token 时 它的状态依赖于前面的所有token
            • 只要前 n 个 token 的序列 完全一模一样,模型前面计算出的数学矩阵就可以直接复用
            • 当第 n+1 个 token 发生变化,会导致第 n+1 个位置开始重新计算前n个token
            • Prompt 的结构通常是 Tools → System Prompt → Messages
            • Claude Code 为了最大化缓存命中率,采用 严格分组排序 + 插入缓存断点 的策略
              • 将工具进行严格物理排序,静态→按需发现→动态/插件
              • Claude Code 会在 最后一个静态内置工具 的结尾处,打上缓存断点
              • 最后的prompt 结构为
                • 缓存区(命中缓存)
                  • [内置工具A] -> [内置工具B] -> [Tool Search(按需发现)] -> [内部工具C + 缓存断点]
                • 计算区(正常计算)
                  • [新增插件工具] -> [系统提示词中的动态变量 (如当前工作目录、时间)] -> [用户的提问]
        • 如果混合排序,每次插件工具变化都会打乱前缀,缓存直接废了
      • 总结:如果工具太多就应该考虑 分级加载,核心工具直接给,其余按需发现
        • 最简单的实现:直接给出所有工具的名字+简短的描述 按需发现(skills)
    • 工具输出预算限制
      • 大部分工具是 10w 字符,文件读取 无限大
      • 当工具输出超过上限,系统不会直接阶段,丢失信息,而是将输出放在磁盘文件,给模型一个 预览(文件的前几千字节内容)+ 文件路径,让模型用工具按需去读取
      • 文件读取为什么是无限大
        • 如果设置一个阈值,存放到本地后,Read读回来,又会触发阈值,陷入死循环
        • 它有自己的 token预算检查,超过预算直接报错,模型会尝试使用偏移和行数参数分段读取
      • 搜索工具
        • 搜索结果默认限制在 250条,总共2w字符
        • 如果模型需要更多信息,可以申请适当解除限制