0%

Claude Code 源码解析 (3):文件搜索的底层原理

架构师点评:文件搜索是 AI Coding 的“眼睛”。模型能否理解大型仓库,往往不取决于一次塞多少 token,而取决于搜索、过滤、排序和上下文裁剪是否足够工程化。

导读: 这是 Claude Code 20 个功能特性源码解析系列的第 3 篇,深入分析文件搜索工具 (Glob/Grep) 的底层原理。

问题引入:为什么需要高效的文件搜索?

痛点场景

场景 1:大海捞针

1
2
3
4
5
6
7
8
用户:"帮我找一下项目中所有用到 axios 的地方"

AI 手动遍历:
for file in project/**/*:
if file.contains("axios"):
print(file)

10 万行代码 → 30 秒 → 用户等待...

场景 2:模糊记忆

1
2
3
4
5
用户:"我记得有个文件叫 user_ 什么_controller.py"

AI:精确匹配失败
→ "没有找到 user__controller.py"
→ 但实际文件是:user_auth_controller.py

场景 3:内容搜索慢

1
2
3
用户:"搜索所有包含 'TODO' 的 Python 文件"

AI 逐个文件读取 → 打开 1000 个文件 → 5 秒后返回结果

核心问题

设计 AI 助手的文件搜索工具时,面临以下挑战:

  1. 性能问题

    • 大型项目 (10 万 + 文件) 如何快速搜索?
    • 如何避免遍历整个文件系统?
  2. 准确性问题

    • 通配符匹配的规则是什么?
    • 如何处理边界情况?
  3. 灵活性问题

    • 支持多种匹配模式 (通配符、正则)
    • 支持内容搜索 + 文件名搜索
  4. 用户体验问题

    • 搜索结果如何呈现?
    • 如何支持分页、过滤?

Claude Code 用 GlobTool + GrepTool 解决了这些问题。

设计思想:为什么这样设计

思想 1:工具分离,各司其职

问题: 为什么不用一个工具完成所有搜索?

解决: Glob 和 Grep 分离。

工具 职责 优势
GlobTool 文件名匹配 快速过滤,不读取内容
GrepTool 内容匹配 精确搜索,支持正则

设计智慧:

分离关注点,每个工具做好一件事。

使用方式:

1
2
3
4
5
6
7
8
仅文件名搜索:
→ GlobTool ("找所有 *.py 文件")

仅内容搜索:
→ GrepTool ("找所有包含 TODO 的文件")

组合搜索:
→ GlobTool + GrepTool ("找所有 *.py 文件中包含 TODO 的行")

思想 2:性能优先,多层优化

优化层次:

1
2
3
4
5
6
7
8
9
10
11
第 1 层:排除无关目录
→ node_modules, .git, .venv

第 2 层:文件名预过滤
→ 先用 Glob 缩小范围

第 3 层:并行处理
→ 多文件同时搜索

第 4 层:原生工具
→ 使用 ripgrep (Rust 实现)

性能提升:

1
2
3
4
5
6
7
8
9
原始实现:10 万文件 → 10 秒
↓ 排除优化
5 秒
↓ 并行处理
2 秒
↓ ripgrep
0.5 秒

总提升:20 倍

思想 3:灵活匹配,适应用户习惯

问题: 用户的搜索习惯不同。

解决: 支持多种匹配模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 模式 1:精确匹配
search("TODO")
→ 匹配 "TODO"

// 模式 2:正则匹配
search("TODO\\w+")
→ 匹配 "TODO123", "TODO_FIX"

// 模式 3:忽略大小写
search("todo", { caseSensitive: false })
→ 匹配 "TODO", "todo", "Todo"

// 模式 4:全词匹配
search("\\bTODO\\b")
→ 匹配 "TODO", 不匹配 "TODOLIST"

思想 4:结果友好,便于理解

问题: 搜索结果如何呈现?

解决: 结构化输出 + 上下文。

1
2
3
4
5
6
7
8
interface SearchResult {
file: string; // 文件路径
line: number; // 行号
content: string; // 完整行内容
match: string; // 匹配的部分
before: string[]; // 前 N 行 (上下文)
after: string[]; // 后 N 行 (上下文)
}

输出示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
找到 3 个匹配:

1. src/utils/helper.py:25
# TODO: 优化性能
───────────────────────
def process_data(data):
result = []
→ # TODO: 优化性能
for item in data:
result.append(transform(item))

2. src/api/user.py:48
# TODO: 添加缓存
...

思想 5:智能排除,减少干扰

默认排除:

1
2
3
4
5
6
7
8
9
10
11
const defaultExclude = [
'node_modules/**',
'.git/**',
'.svn/**',
'.hg/**',
'__pycache__/**',
'*.pyc',
'*.pyo',
'.DS_Store',
'Thumbs.db',
];

为什么排除这些?

目录/文件 原因
node_modules 依赖库,通常不需要搜索
.git 版本控制元数据
pycache Python 字节码缓存
*.pyc 编译后的文件

用户可覆盖:

1
2
3
4
5
6
7
8
9
// 搜索时指定包含
search("TODO", {
exclude: [], // 不排除任何目录
})

// 或添加自定义排除
search("TODO", {
exclude: ['dist/**', 'build/**'],
})

OpenClaw 最佳实践

实践 1:创建搜索工具插件

目录结构:

1
2
3
4
5
6
7
~/.openclaw/extensions/file-search/
├── index.ts
├── tools/
│ ├── GlobTool.ts
│ ├── GrepTool.ts
│ └── SearchTool.ts
└── config.yaml

插件入口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { GlobTool } from './tools/GlobTool';
import { GrepTool } from './tools/GrepTool';
import { SearchTool } from './tools/SearchTool';

export const plugin = {
name: 'file-search',
version: '1.0.0',

async init(gateway: any) {
gateway.registerTool('glob', new GlobTool());
gateway.registerTool('grep', new GrepTool());
gateway.registerTool('search', new SearchTool());

console.log('[file-search] 3 tools registered');
},
};

实践 2:常用搜索命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 搜索所有 Python 文件
openclaw run glob --pattern "**/*.py"

# 搜索包含 TODO 的文件
openclaw run grep --pattern "TODO" --context 2

# 组合搜索:Python 文件中的 TODO
openclaw run search \
--file-pattern "**/*.py" \
--content-pattern "TODO"

# 排除 node_modules
openclaw run grep \
--pattern "axios" \
--exclude "node_modules/**"

# 忽略大小写
openclaw run grep \
--pattern "todo" \
--flags "i"

实践 3:Agent 对话集成

1
2
3
4
用户:"帮我找一下项目中所有用到 axios 的地方"

AI:```bash
grep -r "axios" --include="*.py" --include="*.js"

找到 5 个匹配:

  1. src/api/client.js:3
    import axios from ‘axios’;

  2. src/api/user.js:5
    const response = await axios.get(‘/api/users’);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

### 实践 4:性能调优

**配置文件:**

```yaml
# ~/.openclaw/config/search.yaml

# 默认排除
default_exclude:
- node_modules/**
- .git/**
- dist/**
- build/**
- "*.pyc"
- "*.pyo"

# 性能设置
performance:
# 并行度
parallelism: 4

# 单个文件最大大小 (MB)
max_file_size: 10

# 最大结果数
max_results: 1000

# 使用 ripgrep
use_ripgrep: true

实践 5:结果格式化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function formatResults(results: SearchResult[]): string {
if (results.length === 0) {
return '没有找到匹配的结果';
}

let output = `找到 ${results.length} 个匹配:\n\n`;

for (const result of results.slice(0, 10)) {
output += `${result.file}:${result.line}\n`;
output += `${result.content}\n`;

if (result.before.length > 0) {
output += `前:${result.before.join('\\n')}\n`;
}
if (result.after.length > 0) {
output += `后:${result.after.join('\\n')}\n`;
}
output += '\n';
}

if (results.length > 10) {
output += `\n... 还有 ${results.length - 10} 个结果`;
}

return output;
}

系列文章:

  • [1] Bash 命令执行的安全艺术 (已发布)
  • [2] 差异编辑的设计艺术 (已发布)
  • [3] 文件搜索的底层原理 (本文)
  • [4] 多 Agent 协作的架构设计 (待发布)

上一篇: Claude Code 源码解析 (2):差异编辑的设计艺术

企业落地建议

这篇源码解析对应的是企业 AI Coding 平台里的基础治理能力,建议落地时重点关注:

  1. 为大型仓库建立排除规则:默认跳过 node_modules、构建产物、日志和二进制文件。
  2. 把 Glob/Grep 搜索结果转成任务级证据:避免模型重复扫描整个仓库。
  3. 对架构文档、接口定义、测试文件建立优先级:让 Agent 优先读取高价值上下文。
  4. 将搜索命中率、误命中和耗时纳入 AI Coding 质量度量。:将搜索命中率、误命中和耗时纳入 AI Coding 质量度量。

相关入口:

Claude Code 系列的价值,不只是学习一个工具,而是拆解企业级 Coding Agent 应该具备的工程能力:上下文、权限、任务、工具、安全和可观测。