检索栈
文件名、标题、短语、正文 Token 与中文二元组。
页面分块 embedding,LanceDB 返回语义近邻。
依据各自排名合并,不要求分数同量纲。
为强结果补充链接邻居,并保留结构来源。
Rust 后端的 search_project_inner() 是桌面 UI、HTTP API 和 MCP 共享的检索核心;这减少了入口之间的实现分叉,但最终答案仍会受调用参数、上下文组装和模型生成影响。
词法检索:简单但针对知识库优化
系统递归扫描 wiki/ 的 Markdown,先从 frontmatter 或一级标题提取标题,再计算:
- 文件 stem 与完整查询完全相等的高奖励。
- 完整短语出现在标题或正文中的奖励。
- 查询 Token 命中标题和正文的不同权重。
- 从第一个命中附近生成片段,并提取页面中的图片引用。
中文为什么要特殊处理
空格分词对中文无效。tokenize_query() 会对长度大于 2 的 CJK Token 同时产生二元组、单字和原词,并过滤中英文停用词。
“知识图谱检索”
→ “知识” “识图” “图谱” “谱检” “检索”
→ “知” “识” “图” “谱” “检” “索”
→ “知识图谱检索”
这不是完整中文分词器,但不依赖词典、跨平台稳定。二元组和单字会扩大候选集,也会引入大量弱命中;是否真正提升专有词召回、是否损害 Precision@k,必须在中文查询集上测量。
向量检索:可选的增强层
Embedding 默认可关闭,端点、Key 和模型独立配置,支持 OpenAI 兼容接口。开启后,页面先按标题和语义边界切块,再写入 Rust 的 LanceDB。
分块而非整页
长页面的局部语义不会被整页平均;搜索结果可回到具体标题路径和片段。
增量更新
摄入后只处理新写页面;删除页面时同步清理向量;批量重建有进度与取消状态。
失败可降级
向量端点失败时打印诊断并继续返回关键词结果,不让可选能力拖垮基础检索。
维护动作
增量写入达到阈值后优化表;支持旧页面级表计数、迁移和删除。
为什么使用 RRF
关键词分数可能是“命中次数 + 奖励”,向量分数可能是余弦相似度。直接加权相加会受量纲和模型分布影响。RRF 只看名次:
某页同时排在词法和向量前列会自然升高;只被一种信号发现也仍能进入结果。代码还保留原始 vector_score 用于解释,
最终 mode 会标注 token / vector / graph 的实际参与情况。
RRF 解决不同分数不可直接比较的问题,不会自动保证相关性。固定 K=60、候选窗口、信号权重和图扩展比例都是待调参数,应通过离线消融和分查询类型评测确定,而不是沿用经验常数。
图扩展:找到“没有同词”的相关页
初始搜索结果成为种子,系统为一跳图邻居预留最终窗口的 15%–30%。向量结果越稀疏,图扩展份额越大。
邻居并非简单塞入:它会记录 graph_related_to,告诉上层“这页是通过哪一个种子关系发现的”。
查询“注意力机制”命中 Transformer 概念页后,图扩展可能带回没有出现同一关键词、但通过 wikilink 相连的 BERT、GPT 或序列建模历史页。
四信号关联度模型
| 信号 | 权重 | 含义 |
|---|---|---|
| 直接链接 | 3.0 × 方向数 | A 链到 B 或 B 链到 A;双向链接得到两份分数。 |
| 来源重叠 | 4.0 × 共同来源数 | 两个页面由同一原始材料贡献,作为同源先验;是否相关仍需验证。 |
| 共同邻居 | 1.5 × Adamic–Adar | 共享稀有邻居比共享超级 Hub 更有信息量,贡献为 1 / log(degree)。 |
| 类型亲和 | 1.0 × 矩阵值 | entity↔concept、concept↔synthesis 等组合有不同先验。 |
来源重叠可以作为关联先验,但不是相关性的证据:一份长报告可能同时支撑互不相关的页面,LLM 自动生成的 Wikilink 也会把摄入偏差反馈进图。直接链接、共同来源和类型亲和都应被标注为启发式信号,并接受消融验证。
可视化图谱
buildWikiGraph() 读取 Markdown、frontmatter、wikilink 与来源字段,生成节点、边和社区。为了规模化,它设置了清晰边界:
- 并发读取 Wiki 文件,缓存最近两个项目的图。
- 3,000 节点以内计算完整加权图;更大规模限制代价高的全对关联。
- 3,000 节点以上把社区分析送入 Worker,避免阻塞 UI。
- Sigma.js 渲染,节点大小按链接数平方根缩放,位置缓存防止数据更新后跳动。
- 支持按页面类型或 Louvain 社区着色,ForceAtlas2 负责结构布局。
图谱不是海报,而是诊断工具
惊奇连接
跨社区、跨类型、边缘节点连接 Hub、低权重但在图中存在的边会获得惊奇分。结构页 index/log/overview 被排除,避免它们制造伪洞察。
知识空白
- 孤立页:度 ≤ 1,说明几乎没有进入知识网络。
- 稀疏社区:至少 3 页且内聚度 < 0.15,说明同一领域内部链接不足。
- 桥接节点:连接 3 个以上社区,是跨领域关键枢纽,需要重点维护。
洞察卡可直接高亮图中节点,并把空白转成 Deep Research 主题,形成反馈闭环。但孤立、稀疏、桥接和“惊奇”描述的是当前图结构,不等于知识真的缺失、重要或正确;把这些标签直接展示为事实会制造算法权威感。
图谱同时服务检索、可视化和治理,这是有价值的复用;图算法输出应被称为“调查线索”,需要证据或人工判断后才能升级为知识结论。
没有评测,就没有“提升”
README 声称组合检索将召回率从 58.2% 提升到 71.4%,但本次在固定提交中未找到配套数据集、查询标注、评测脚本或运行报告。因此该数字只能列为项目声明,不能当作已复现结论。
| 评测层 | 最小做法 | 关键指标 |
|---|---|---|
| 检索相关性 | 冻结语料与 query→relevant chunk 标注,按中文、英文、实体、概念、时间问题分层。 | Recall@k、nDCG@k、MRR、Precision@k |
| 信号消融 | 分别运行词法、向量、图、词法+向量、全部信号,固定候选窗口。 | 增益、退化查询数、延迟与成本 |
| 答案忠实度 | 要求句子级引用,验证回答是否由召回证据支持。 | citation precision、support rate、unsupported claim rate |
| 回归与漂移 | 模型、Prompt、Schema、Embedding 或图规则升级时重跑固定集。 | 版本差异、语言切片、失败集修复率 |
| 线上反馈 | 记录无结果、改写查询、用户打开来源与纠错,不记录敏感原文。 | zero-result rate、reformulation rate、source-open rate |
最有价值的产物不是一个总分,而是一组长期保留的失败查询。每次调权重、换模型或改图规则,都必须证明旧失败减少且没有在关键切片制造新退化。
src-tauri/src/commands/search.rs、src/lib/embedding.ts、wiki-graph.ts、graph-relevance.ts、graph-insights.ts 与 README 的检索指标声明。评测矩阵是本次审计提出的生产要求。