第 5 章:代码搜索与发现

为什么搜索是一等 Agent 技能

在 Agent 编辑代码之前,它必须先找到正确的代码。搜索是模糊请求和具体文件之间的桥梁。糟糕的搜索会导致糟糕的编辑,因为模型会基于不完整证据推理。

有用的 Agent 通常需要几种搜索模式:

  • 文件发现:“有哪些文件?”
  • 文本搜索:“这个 symbol 或字符串在哪里使用?”
  • 语义收窄:“哪些结果看起来与用户任务相关?”
  • 代码智能:“定义、引用或实现在哪里?”
  • 工具发现:“这种搜索应该使用哪个工具?”

通用搜索策略

模型通常先从宽处开始,然后收窄:

def investigate_request(request, tools):
    query_terms = extract_terms(request)

    candidate_files = tools.find_files(
        patterns=guess_file_patterns(request),
        limit=200,
    )

    matches = []
    for term in query_terms:
        matches.extend(
            tools.search_text(term, files=candidate_files, limit=100)
        )

    ranked = rank_by_path_and_match_quality(matches, request)
    return ranked[:20]

好的搜索工具不只是返回字节。它们会应用 ignore rules,检测二进制文件,限制输出,在有用时包含行号,并明确说明截断。

Codex:Shell-Native Search

Codex 非常依赖 shell-native search。模型被期望使用快速开发者工具,例如 rgrg --filesgit grepfind 和语言测试命令。这符合 Codex 更宽的哲学:提供强大的 shell,然后用策略和沙箱约束执行。

这带来什么能力

Shell-native search 很灵活:

def codex_search_plan(question):
    if question.looks_like_exact_symbol:
        return "rg -n 'symbol_name'"
    if question.looks_like_file_discovery:
        return "rg --files | rg 'pattern'"
    if question.needs_git_history:
        return "git log --oneline -- path"
    return "rg -n 'best guess terms'"

模型可以串联命令,用 pipes 过滤,检查 Git 状态,并把搜索与项目特定脚本结合。

运行时仍然提供什么

Codex 仍有与搜索相关的基础设施:

  • 交互式 UI 使用 file-search helpers 做快速文件选择。
  • Shell tool handling 捕获输出、退出状态、耗时和错误。
  • Tool orchestration 在命令执行前应用审批和沙箱策略。
  • Tool-search surfaces 可以帮助发现可供模型使用的工具。

换句话说,Codex 不需要专用 grep_search 工具,模型也能有效搜索;但运行时仍控制 shell 命令如何执行。

Claw:具名搜索工具

Claw 暴露具名搜索工具,例如 glob-style file discovery 和 grep-style text search。这些工具与文件 read/write/edit 操作并列,返回结构化输出。模型可以请求搜索,而不需要构造 shell pipeline。

Claw 搜索形状

def claw_glob_search(pattern, workspace):
    files = walk_workspace(workspace)
    files = apply_ignore_rules(files)
    files = [f for f in files if glob_match(pattern, f.relative_path)]
    return {"filenames": sort_paths(files)}


def claw_grep_search(pattern, path, workspace):
    results = []
    for file in safe_files_under(path, workspace):
        if is_binary(file):
            continue
        for line_no, line in enumerate(read_lines(file), start=1):
            if regex_search(pattern, line):
                results.append({
                    "path": file.relative_path,
                    "line": line_no,
                    "text": trim(line),
                })
    return truncate_results(results)

优势是输出可预测。模型不需要记住命令行 flags,也不需要解析嘈杂 shell 输出。运行时可以一致地执行 workspace boundaries 和 token budgets。

搜索结果设计

搜索结果应该回答四个问题:

问题 示例字段
匹配在哪里? path, line
匹配了什么? text, captures
输出是否完整? truncated, total_matches
模型下一步该做什么? hint, next_offset, narrowing_suggestion

有界格式化伪代码:

def format_search_results(matches, max_items=80, max_chars=20_000):
    output = []
    used_chars = 0

    for match in matches:
        item = f"{match.path}:{match.line}: {match.text}"
        if len(output) >= max_items or used_chars + len(item) > max_chars:
            return {
                "truncated": True,
                "matches": output,
                "hint": "Narrow the query or inspect a specific file.",
            }
        output.append(item)
        used_chars += len(item)

    return {"truncated": False, "matches": output}

Ignore Rules 与 Workspace Boundaries

搜索必须尊重项目边界。否则模型会浪费时间扫描 dependencies、generated outputs、caches、home directories 或不相关的父目录。

重要过滤器:

  • 除非明确允许,否则留在 active workspace 下。
  • 遵守 .gitignore 和常见 ignore directories。
  • 默认跳过二进制文件。
  • 避免巨大文件,除非用户特别要求。
  • 返回给模型前规范化路径。
def safe_search_roots(requested_path, workspace):
    root = canonicalize(requested_path or workspace.root)
    if not root.is_relative_to(workspace.root):
        raise PermissionError("search path escapes workspace")
    return root

代码智能作为搜索

文本搜索并不适合所有任务。Agent 也会受益于语言服务器功能,例如 go-to-definition、references、symbols 和 diagnostics。Claw 在更宽的工具注册表中暴露了 LSP-style surfaces。Codex 通常可以通过项目命令、语言工具或 IDE/app integration 获得类似信息,具体取决于活动界面。

概念上:

def find_implementation(symbol, tools):
    text_hits = tools.search_text(symbol)
    if len(text_hits) == 1:
        return text_hits[0]

    if tools.has("lsp_definition"):
        return tools.lsp_definition(symbol)

    return rank_likely_definitions(text_hits)

取舍

维度 Shell-Native Search 具名搜索工具
灵活性 很高 中等
输出一致性 取决于命令
Token 效率 取决于模型命令 运行时控制
可移植性 取决于已安装工具 运行时提供
安全分类 更难,因为命令是任意字符串 更容易,因为参数结构化
模型学习曲线 使用常见 CLI 模式 使用工具特定 schema

Codex 受益于 shell fluency。Claw 受益于可预测的工具契约。两种方法都需要截断、路径安全和好的结果格式化。

源码锚点

对 Codex,有用的文件名是 shell.rstool_search.rs 和 file-search helper modules。对 Claw,有用的文件名是 file_ops.rstools/lib.rs,以及工具注册表中与 LSP/tool-search 相关的部分。