【免费下载链接】Docufinder
Anything — 파일 이름이 아니라 본문으로 찾는 문서 검색기. HWP·PDF·Office 전문 검색을 오프라인으로. Windows·macOS | Offline content search across Korean documents (HWP, PDF, Office). Windows and macOS
Docufinder(产品名 Anything)是一款离线文档内容搜索器,能直接检索 HWP、PDF、Office 文件的正文,而不是只搜文件名。很多人好奇:面对上万份文档,它凭什么能做到关键词输入后近乎"1秒返回"?答案藏在三套机制里——两阶段索引、实时增量同步、HNSW 向量库。本文用尽量少的代码,带你读懂这套设计背后的工程取舍。
一、先说结论:搜索快的三个支柱
传统"边搜边读文件"的方式之所以慢,是因为每次查询都要把磁盘上的文档从头解析一遍。Docufinder 的做法相反:把耗时的解析工作提前做掉,存成索引;查询时只查索引,不碰正文。这三套支柱分别解决了三个问题:
- 两阶段索引:先让全文搜索立即可用,向量搜索后台慢慢补;
- 实时增量同步:文件一变索引就跟上,避免每次全量重建;
- HNSW 向量库:让"意思相近也能搜到"的语义搜索在本地跑起来。
二、两阶段索引:先让全文搜索立即可用
这是 Docufinder 最核心的设计。它把一个文档的索引过程拆成了两阶段,源码位于 pipeline/mod.rs。
阶段一:全文索引(FTS-only)扫描文件夹后,只把文档正文切成小块(chunk),写入 SQLite 的 FTS5 全文索引,不生成任何向量。这一步只依赖 SQLite,速度快、内存低。完成之后,你输入的每一个关键词已经能秒级命中了。
阶段二:向量嵌入(后台异步)全文索引跑完后,独立的后台线程 VectorWorker 才开始逐块做语义向量,把每个 chunk 编码成向量存入向量库。关键细节:
- 按32 个 chunk 一批做嵌入,而不是一整篇文档一批,这样小文件多的目录也能保持批量效率;
- 每处理1000 个 chunk或每30 秒就把向量库落盘一次,中途崩溃也不至于全丢;
- 嵌入用的是本地 ONNX 模型 KoSimCSE,输入截断到 128 token,并在子批次之间主动让出锁,让正在进行的搜索查询能插进来,不被后台索引卡住。
为什么非要拆成两阶段?因为全文搜索是用户感知最敏感的部分——它决定了"能不能马上搜到";而向量搜索锦上添花但计算贵。拆开之后,用户先享受快,语义能力在后台慢慢"追上",互不拖累。
全文索引阶段还有一处容易忽略的性能点:数据库写入不是"一个文件一次提交",而是每 200 个文件批量提交一次(常量TRANSACTION_BATCH_SIZE,见 pipeline/mod.rs),把 SQLite 的磁盘落盘开销摊薄,官方注释称这带来了 3~5 倍的性能提升。
三、实时增量同步:文件一变,索引就跟上
索引建好了,如果你又新建、修改、删除了文件怎么办?全量重建太浪费,Docufinder 用监听 + 差异计算来做到"只处理变动的部分"。
3.1 文件监听:谁动了就记谁
核心是 WatchManager,它基于notify库注册两个监听器:
- 本地文件夹用系统级事件(Windows 的
ReadDirectoryChangesW/ macOS 的inotify),文件一动立刻收到事件; - 网络文件夹(UNC 路径)因为拿不到可靠事件,改用30 秒轮询的
PollWatcher兜底。
事件进来后不会立刻逐条处理,而是先做防抖(debounce),把短时间内的多次变更合并成一批,再交给增量索引,避免"复制一个大文件夹"时事件风暴把数据库打爆。
3.2 差异计算:只挑出"变了的那部分"
真正重建索引的入口是 sync_folder_fts。它的巧妙之处在于差异(diff)不是用两个大 HashMap 在内存里比,而是交给 SQLite 算:
- 把当前磁盘上所有文件路径批量写进一张SQLite 临时表
_sync_fs(见 sync.rs); - 用SQL JOIN直接对比这张表和已有的索引记录,自动分出"新增 / 修改 / 删除 / 未变"四类;
- 只对新、改的文件重新解析入库。
这套做法把内存占用从原来的约 400MB 压到 40~80MB,让低配电脑也能跑全目录同步。
3.3 周期同步:给监听加一道保险
系统事件偶尔会丢(尤其是大目录)。为此 Docufinder 加了一个每 10 分钟 + 窗口重新聚焦时触发的周期同步任务 periodic_sync.rs,把磁盘和数据库再"对一遍账"。它还会自动判断此刻是否正在做批量索引,若是则跳过,避免多个同步任务争抢数据库锁。
四、HNSW 向量库:让"意思相近也能搜到"
关键词匹配解决"字面一样",但要搜"意思相近"就需要向量 + 相似度。Docufinder 的向量库封装在 search/vector.rs,底层是usearch(一个 HNSW 实现的 Rust 绑定)。
几个关键的工程参数(见 create_options):
| 参数 | 取值 | 作用 |
|---|---|---|
| 维度 | 768 | 与嵌入模型输出对齐 |
| 相似度 | 余弦(Cos) | 适合语义文本 |
| 量化 | F16 半精度 | 体积减半,显存/内存更省 |
connectivity(M) | 16 | HNSW 每层节点连接数,精度与体积的平衡点 |
efConstruction | 128 | 建图时的搜索宽度,越大图越"准" |
efSearch | 128 | 查询时的搜索宽度,从 64 提到 128 显著改善了大索引的召回率 |
内存上的"小心机"
向量库支持三种模式:空、mmap 视图(只读、映射到文件、几乎不占常驻内存)、全量加载(可读写)。默认用 mmap 视图模式——查询时内存占用极低,只有在需要新增/删除向量时才切到全量模式,写完再切回。这就是它能"本地跑、又不太吃内存"的关键。
它还对损坏文件做了防御:加载前先校验文件最小尺寸,小于 64 字节视为被截断(比如强关、磁盘满导致),直接清理并重建空索引,而不是让程序崩掉。
混合搜索:把两路结果揉成一个排名
最终返回给用户的结果,是全文(BM25)和向量(语义)两路结果用 RRF 融合出来的,逻辑在 hybrid.rs。RRF(Reciprocal Rank Fusion)不直接比分数,而是按"这一路里排第几名"来打分(1/(k+rank),k 常取 60),天然避开了两种分数量纲不一致的问题,让"关键词命中"和"语义相近"公平地同台竞争。
五、这些设计如何拼出"1 秒"体验
把上面三套机制串起来,就能理解 1 秒的来源:
- 查询只查索引、不碰正文→ 全文走 SQLite FTS5、语义走 HNSW 近邻检索,都是 O(log n) 级别的快;
- 两阶段把贵的计算挪到后台→ 用户感知的路径上永远只有轻量操作;
- 增量同步保证索引新鲜→ 不用全量重建,所以"刚保存的文件马上能搜到";
- RRF 融合 + 让锁机制→ 后台正在嵌入,也不妨碍前台查询插队返回。
想亲手验证搜索质量,项目内置了评测集,位于 harness/search-quality/,可跑批查询对比召回表现。
一句话总结:Docufinder 用"提前建索引"换"查询时不解析",用"两阶段"换"快与准的平衡",用"增量 + HNSW"换"既新又聪明"。这正是它能在本地、离线、面对上万文档时,依然给你 1 秒响应的全部秘密。
【免费下载链接】Docufinder
Anything — 파일 이름이 아니라 본문으로 찾는 문서 검색기. HWP·PDF·Office 전문 검색을 오프라인으로. Windows·macOS | Offline content search across Korean documents (HWP, PDF, Office). Windows and macOS
相关推荐
Buzz 本地转录:从音频文件到可导出字幕的 5 步流程
Buzz 本地转录:从音频文件到可导出字幕的 5 步流程 Buzz 是一款基于 OpenAI Whisper 的本地音频转录工具,在你自己的电脑上完成语音转文字
语音AI 应用本地部署桌面应用超图解!pgvector中HNSW索引如何实现毫秒级向量搜索?
超图解!pgvector中HNSW索引如何实现毫秒级向量搜索? 你是否在使用PostgreSQL进行向量搜索时遇到性能瓶颈?当向量数据量达到百万级时,线性扫描的
向量数据库数据库搜索引擎LatticeDB HNSW向量索引源码级解析:100万向量如何实现0.83ms、100%召回
LatticeDB HNSW向量索引源码级解析:100万向量如何实现0.83ms、100%召回 LatticeDB 是一款嵌入式单文件知识图谱数据库,可以把图遍
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考