深入解析 Repowise 的代码库搜索命令:从全文检索到语义与符号搜索
深入解析 Repowise 的代码库搜索命令:从全文检索到语义与符号搜索
Repowise 是一个面向 AI 与开发者的代码库智能平台,其核心能力之一是将仓库中的代码结构、符号、决策记录与文档沉淀为可检索的 Wiki。本文围绕插件体系中的 repowise search 命令展开,完整讲解该命令在自然语言、全文与符号三种检索模式下的用法、参数语义、结果呈现方式,以及"搜索为空"时的诊断与恢复路径。读完本文,你将能像使用内置智能工具一样,在 CLI、Claude Code 与 Codex 插件中熟练调用 Repowise 搜索能力,并理解其底层检索原理(全文与向量融合、符号索引、查询形态路由),从而在真实代码库中精准定位函数、类、文件与文档页面。
一、repowise search 是什么:面向代码库 Wiki 的混合检索入口
repowise search 是 Repowise 的命令行检索命令,其定位在源码中有非常明确的表述——它是 MCP 工具 search_codebase 的 CLI 适配层(见 search_cmd.py 第 1 行注释)。这意味着命令行搜索与 MCP 工具搜索共享同一套检索实现:"a CLI answer and an MCP answer are the same answer"——无论你从终端还是通过 Agent 调用,得到的排序与结果完全一致。
该命令的适用场景非常广泛:
- 自然语言提问:例如"how does authentication work"(认证是怎么工作的)、"where is rate limiting handled"(限流在哪里处理);
- 关键词检索:直接查找包含某关键词的 Wiki 页面;
- 符号定位:按函数、类、变量名进行模糊匹配,快速跳到定义处;
- 路径检索:按文件路径解析出对应文档页面。
从搜索对象的来源看,repowise search 检索的是"代码库 Wiki"——Repowise 在 init / update 阶段将仓库的模块、文件、符号、决策记录等内容渲染成结构化页面并建立索引。所有已索引的仓库都有 Wiki 页面,因此搜索结果空通常不是"Wiki 不存在",而是索引状态问题(详见"空结果诊断"一节)。
二、基本用法与三种检索模式
2.1 命令行形态
repowise search 的命令行形态如下:
repowise search QUERY [PATH]
在插件(Claude Code 命令 /repowise:search、Codex prompt)与 CLI 中,调用方式统一为:
repowise search "$ARGUMENTS"
即把用户的输入原样作为查询串传入。如果 $ARGUMENTS 为空,插件会主动反问:"你在找什么?你可以用自然语言搜索,比如 'how does authentication work',或 'where is rate limiting handled'。"
2.2 三种核心模式与附加参数
根据 search.md 的说明,--mode 参数决定检索方式:
| 模式 | 含义 | 说明 |
|---|---|---|
--mode fulltext |
快速关键词搜索(默认) | 基于 SQLite FTS5 的全文索引,速度快,零成本 |
--mode semantic |
向量相似度搜索(语义搜索) | 依赖已配置的 embedding 提供商(embedder);若失败会提示先运行 reindex |
--mode symbol |
符号名称模糊匹配 | 匹配函数、类、变量等符号名 |
其他常用参数:
| 参数 | 含义 | 默认值 |
|---|---|---|
--limit N |
最大返回结果数 | 10 |
--repo <alias> |
工作区(workspace)模式下限定某个仓库别名 | — |
--all |
工作区模式下跨所有已索引仓库扇出并合并结果 | 关闭 |
--no-workspace |
即使在工作区内也强制单仓库模式 | 关闭 |
--format |
table(默认)或 json |
table |
--full |
输出工具的完整原始负载(JSON) | 关闭 |
2.3 从源码看模式解析的真实语义
值得注意的是,CLI 文档与插件文档中"fulltext 是默认模式"的说法,在源码层面有更精细的实现。查看 search_cmd.py 可以发现一个模式别名映射:
_MODE_ALIASES = {"fulltext": "concept", "semantic": "concept", "symbol": "symbol"}
_MODE_CHOICES = ["fulltext", "semantic", "symbol", "auto", "concept", "path", "hybrid"]
也就是说,CLI 的 fulltext 与 semantic 两种拼写,实际都会落到 MCP 工具内部的 concept 分支执行:该分支通过 RRF(Reciprocal Rank Fusion,倒数排名融合)把全文(FTS)与向量(vector)两条检索腿融合,而不是在二者之间二选一。在无 embedder 的 keyless 索引上,向量腿会自动脱落(_safe_vector 守卫),等效于回退到全文——这正是 --mode semantic 曾经手工回退全文的行为。这种设计保证了旧脚本继续可用:--mode fulltext 仍然是文档化的默认拼写,但底层执行的是更先进的融合检索。
而 --mode symbol 走符号索引;此外还支持工具自有的 auto、concept、path、hybrid 拼写(见 CLI_REFERENCE.md):
auto:按查询形态自动路由——标识符进符号索引、路径解析文件、自然语言走概念检索、混合查询走 hybrid;path:路径检索,按文件路径解析页面;hybrid:符号命中与页面命中交错返回,见下方"hybrid 交错"小节。
2.4 结果呈现:干净、可操作的列表
无论哪种模式,CLI 都会以"干净的列表"呈现结果,包含页面标题(title)、类型(type/page_type)与简短摘要(snippet)。源码中的投影逻辑(_project_result,见 search_cmd.py)为三种结果形态保留了不同的关键列:
- page(页面命中):
score、title、page_type、path、snippet; - symbol(符号命中):
score、name、qualified_name、kind、path、line、symbol_id——其中symbol_id正是repowise symbol命令后续定位符号所需的标识; - file(文件命中,路径检索产生):
score、title、path。
此外,search_codebase 的返回还会附带 candidates 字段:命中页面所解析到的、可直接打开阅读的去重文件列表("Files to read"),这是 CLI 结果中一个非常实用的"下一步"指引。
三、实用示例
以下示例覆盖了四种典型查询形态(来自 CLI_REFERENCE.md):
# 关键词全文搜索(默认模式)
repowise search "rate limiting"
# 语义搜索:自然语言提问
repowise search "how are errors handled" --mode semantic
# 符号搜索:按名称模糊匹配
repowise search "AuthService" --mode symbol
# 路径检索:解析特定文件
repowise search "cli/output.py" --mode path
# auto 模式:按查询形态自动路由
repowise search "where is resolve_console_width called" --mode auto
# 工作区(workspace)内限定仓库
repowise search "rate limit" --repo backend
# 工作区跨仓库扇出
repowise search "rate limit" --all
从插件角度,search 是"索引纯本地"命令——不需要 LLM key 即可运行(见 CLI_REFERENCE.md 关于"哪些命令需要 key"的说明)。全文与符号检索完全离线可用,只有语义检索依赖 embedder 配置。
四、空结果诊断:先分清"没有 Wiki"与"没有匹配"
插件文档专门给出了空结果时的处理思路。Repowise 的每个已索引仓库都会生成 Wiki 页面:即便没有任何 LLM key,init 也会从仓库结构渲染出整个 Wiki(keyless run renders the whole wiki from structure)。因此搜索为空通常不是"没有 Wiki",而是以下两种情况之一:
4.1 "完全没有页面"——索引未完成或使用 fast 模式
- 索引从未跑完:建议运行
repowise init --yes(无需 API key),或在上次运行被中断时使用repowise init --resume续跑; - 仓库用
--mode fast索引:这是唯一一种不写 Wiki 的索引模式(fast 模式只建结构索引、不生成文档页面)。建议改为完整初始化。
4.2 "页面存在,但语义搜索找不到任何东西"——mock embedder 所致
如果仓库是用 mock embedder 嵌入的,向量索引无法用于排序,语义检索自然无结果。此时:
- 全文搜索(
--mode fulltext)与符号搜索(--mode symbol)不受影响,可继续使用; - 建议配置一个真实的 embedder 后执行
repowise reindex重建语义索引。
具体命令(见 CLI_REFERENCE.md):
# 默认 embedder(auto 自动检测)重建向量索引
repowise reindex
# 显式指定 embedder 与批大小
repowise reindex --embedder gemini --batch-size 50
reindex 支持 gemini、openai、openrouter、ollama、edenai、mock 或 auto(默认 auto)。用真实 embedder 完整重建后,索引 scope 会标记语义搜索恢复可用,--mode semantic 即能正常工作。
五、源码级原理:检索是如何组织的
5.1 单一入口与查询形态路由
search_codebase(定义于 tool_search.py)是全部检索的统一入口。它的核心思路是"按查询形态路由"(见 tool_search.py 的 docstring):
- 标识符形态(identifier)→ 符号索引,返回
symbol_id、路径与行号边界,可直接衔接get_symbol; - 路径形态(path)→ 解析文件,供
get_context使用; - 自然语言形态(prose)→ Wiki 语义搜索;
- 混合形态(mixed)→ hybrid:符号命中打头、仅返回有文件支撑的页面。
路由启发式位于 _query_shape.py,例如 _is_path 会判断查询是否以问号结尾、以疑问词开头、包含多个词或含 /、\ 等路径特征(见 _query_shape.py);_embedded_identifiers 则从自然语言中抽取蛇形命名(snake_case)或驼峰(CamelCase)标识符,用于符号腿的精确匹配(见 _query_shape.py)。
5.2 全文腿:SQLite FTS5
全文检索由 FullTextSearch 类封装,底层是 SQLite FTS5 虚拟表 page_fts(见 search.py)。其建表结构为:
CREATE VIRTUAL TABLE IF NOT EXISTS page_fts
USING fts5(page_id UNINDEXED, title, content, summary, target_path, vocabulary, digest)
FTS5 没有 ALTER TABLE,当索引列结构变化时会重建整表(见 search.py);写入采用"删除+插入"策略(FTS5 无 UPDATE,见该文件第 511 行)。查询词在进入 FTS5 之前会经过 build_fts5_query 的规范化处理(停用词、前缀规则、文档频率上限等),避免 FTS5 无法解析的词导致整体失败(见 search.py)。
5.3 向量腿:语义搜索的前提与降级
向量检索(_safe_vector)依赖语义向量存储(store_has_semantic_vectors)。在无 embedder 的 keyless 索引上,mock 产生的向量无法参与排序,向量腿会被守卫直接短路返回空(见 tool_search.py),随后全文腿兜底出结果。这正是插件文档所说"mock embedder 保持全文搜索可用、语义搜索留待以后构建"的源码实现。向量搜索还有超时预算(环境变量 _VECTOR_TIMEOUT_ENV 可调,默认见 vector_search_timeout_s()),超时只丢弃语义腿并记录告警,绝不阻塞全文结果。
5.4 融合与排序:RRF + 多级修正
在概念(concept)模式下,_fused_retrieve 并行执行 FTS 与向量两条腿,通过 RRF 融合:命中得分是 1/(rank + k) 在命中的各检索腿上的累加,并缩放到 BM25 量纲(见 tool_search.py)。每个结果携带 sources 列表("fts"、"vector" 或两者兼有)——被两条腿同时命中的页面是更强的匹配。这种设计让 CLI 与 get_answer 的混合检索共享同一语料排序。
排序之后还有一系列针对检索噪音的修正(见 tool_search.py):
- 决策记录降权(decision down-weight):除非查询是 why 形态("为什么……"),否则
decision_record页面的相关性会被乘以下降系数——因为决策标题短而密集,很容易在余弦相似度上虚高; - 测试页面降权(test page down-weight):除非查询明确提到 test、pytest、fixture 等词,测试文件页面的相关性会被打 0.6 折——测试通常不是实现问题的最佳首个阅读对象;
- 决策去重(decision dedup):按归一化标题折叠近乎重复的决策记录,只保留得分最高的变体。
此外还有新鲜度加分(_apply_freshness_boost):近期活跃(30 天内提交)的文件获得微小加分,作为同分排序的决胜项(见 tool_search.py);路径直命中保护(_protect_exact_paths)与符号精确匹配保护(_protect_exact_symbols)则把精确命中稳定地排到模糊邻居之前。
5.5 符号与路径检索:结构索引
--mode symbol 与 --mode path 走 _structured_search,其底层是 Repowise 自持的结构化索引:WikiSymbol(规范符号行)与 GraphNode(pagerank、betweenness、入口点、测试状态等排序信号),见 tool_search_symbols.py。符号打分采用分层策略(tool_search_symbols.py):
- 精确名称 / 规范化限定名匹配(+100);
- 查询叶标识符点名该符号(+40);
- 查询 token 覆盖率(按比例最高 +30,全覆盖再 +20);
- 子串回退(+10);
- 图中心性(pagerank、betweenness)与入口点加分——但被钳制在很小的范围内,避免高 pagerank 文件压过真正的名称匹配。
token 化会同时按非字母数字与驼峰峰点切分(GitIndexer.index_repo → {git, indexer, index, repo}),并丢弃短于两个字符的 token(见 tool_search_symbols.py)。
5.6 hybrid:符号与页面的交错窗口
hybrid 模式将不可比的两种分数(符号 SQL 得分约 43 量级、概念相关性 0-1 量级)按块交错(_interleave_hybrid,见 tool_search.py):默认符号打头、为概念页面保留窗口一半;当查询以自然语言为主、且没有符号精确命中时,概念页面打头、模糊符号落尾。随后 _collapse_by_file 将同文件的多个命中折叠为一行(每个文件只占一个结果槽,同一文件的其它符号以 name:line 形式并入 symbols 字段,超出上限记 +N more),确保 --limit N 买到的是 N 个不同文件(见 tool_search.py)。
5.7 工作区:扇出与融合
在工作区(workspace)模式下,--repo <alias> 限定单个仓库,--all 则跨所有已索引仓库扇出:每个仓库各执行一次检索,然后以 RRF(k=60)对排名列表融合,_worst_freshness 取各仓库中最差的索引新鲜度来报告陈旧告警(见 search_cmd.py)。单仓库的失败不会拖垮整个扇出——出错仓库被记为 failure,其余仓库照常返回。
六、与相邻命令的协作
repowise search 在 Repowise 的检索/问答工具族中承担"原始命中列表"的角色,与以下命令协同使用:
repowise ask QUESTION:需要的是"带引用的综合答案"而非关键词列表时使用。get_answer内部会先运行与本命令相同的混合检索,再交给 LLM 合成答案——因此ask是唯一消耗 LLM 调用的查询类命令(见 CLI_REFERENCE.md);repowise symbol SYMBOL_ID:search --mode symbol返回的symbol_id正是该命令的输入,可继续获取符号的完整定义与字节级上下文;repowise context:路径/符号命中可通过get_context扩展为文件级上下文;repowise why:why 形态的查询(如"为什么这里用 JWT?")建议交给get_why,search_codebase对 why 查询会天然保留决策记录排序(见 why.md 的模式说明)。
此外,search 命令也是插件钩子(hooks)中的常用检索入口:Claude Code 的 lens 钩子即用它做搜索结果增强与陈旧 Wiki 检查(见 CLI_REFERENCE.md 关于 hook 引擎的说明)。
七、常见问题速查
| 现象 | 原因 | 处理 |
|---|---|---|
| 搜索提示无索引可搜 | 仓库尚未初始化 | 运行 repowise init --yes(无需 key) |
| 完全没有页面 | 索引未完成,或使用 --mode fast 初始化 |
repowise init --resume;fast 模式请改为完整初始化 |
| 语义搜索无结果、全文/符号正常 | 使用 mock embedder 嵌入 | 配置 embedder 后 repowise reindex |
| 语义搜索提示无 embedder | keyless 索引 | 设置 embedder key 并 reindex;此时已自动回退全文结果 |
| 期望精确符号却返回模糊邻居 | 拼写/大小写不符或符号未索引 | 按提示检查拼写,用 --mode symbol 单独检索标识符 |
| 结果里出现无关的决策/测试页面 | 非 why 查询被决策降权、非测试查询被测试降权后仍命中 | 属正常排序行为,可结合 --mode、kind 过滤收窄 |
八、小结
repowise search 用一条命令覆盖了代码库检索的绝大多数形态:默认全文、可选语义、符号与路径检索、hybrid 交错、工作区扇出。其背后是 FTS5 与向量检索的 RRF 融合、基于查询形态的自动路由、决策/测试降权与新鲜度排序等一系列精心设计的检索策略,且与 search_codebase MCP 工具共享同一实现,保证 CLI 与 Agent 的回答完全一致。掌握本命令的参数与空结果诊断路径,你就拥有了在大型代码库中快速定位代码、符号与文档的第一把钥匙。