文档检索系统
低资源消耗库

一、问题背景
WEB4GPCODE 的目标并不是做一个大而全的知识平台,而是把一批结构稳定、引用要求明确、更新频率相对可控的工程建设规范整理成可浏览、可搜索、可公网部署的轻量书库应用。这个目标决定了它的技术路线:优先把文档整理成可靠语料,优先使用规则检索和稀疏检索,优先保证结果可定位、可解释、可复核,而不是一开始就引入向量数据库、全文搜索集群或大模型问答。
这类应用有很强的普适性。除了工程规范,企业制度、技术手册、产品说明、设备维护文档、合同模板、内部培训资料、地方标准、法规汇编等,都属于相似类型:文档总量不一定巨大,但结构性强、准确性要求高、使用场景集中。只要先把书库建设好,再用合适的组合查询手段,就可以在很小的服务器资源上提供足够可用的检索和阅读体验。
二、先建书库,而不是先建搜索
很多文档应用失败,不是因为搜索算法不够高级,而是因为语料本身不可控。WEB4GPCODE 的第一层经验是把书库当成一个工程对象来维护。
项目中把书库与服务拆成两个边界:
• engineering-standards/负责规范正文、目录、元数据、机器清单和整理脚本• web4gpcode-standards/负责索引生成、API 和前端展示
这个边界很重要。书库不是前端静态页面的附属物,也不是运行时代码的一部分。它有自己的目录规范、迁移脚本和机器清单。服务端只读取经过整理的书库,不在用户请求时临时理解一堆散乱文件。
书库建设的关键动作包括:
1. 统一目录层级 把文档整理成类似
系列 / 文献类型 / 规范的稳定层级。目录层级本身就是元数据,可以用于筛选、浏览和搜索缩圈。2. 统一文件命名 正文文件按章节、节、附录、术语等稳定命名,例如
ch03-sec3.1.md、appendix-A.md、terms.md。稳定文件名能让索引、阅读 URL、缓存和测试都更可靠。3. 保留机器清单 .web4gpcode/library-manifest.json是书库进入程序世界的入口。服务端不靠遍历目录猜测全部含义,而是以清单为准读取规范编号、标题、分类、标签和路径。4. 保持正文为可解析文本 Markdown 对这类书库很合适。它足够轻,便于人工维护,也便于程序解析标题、条号、章节路径和正文块。相比 PDF 或图片,Markdown 能显著降低检索和展示成本。
5. 让整理脚本可重复执行 书库整理不是一次性人工劳动。随着文档增加,必须有脚本检查、迁移、规范化和重建清单。脚本化的好处是结果可重复,错误可定位,后续维护成本低。
这套经验可以迁移到其他专有文档场景。无论原始资料来自 Word、PDF、网页还是扫描件,真正进入检索系统前,都应该先落到“结构化目录 + 元数据清单 + 可解析正文”的形式。搜索质量的上限,很大程度由这一步决定。
三、索引生成前置,把运行时负担降下来
WEB4GPCODE 没有在每次用户搜索时重新扫描全部 Markdown,也没有依赖外部搜索集群。它选择在构建阶段执行:
npm run build:index索引结果写入 web4gpcode-standards/data/library-index/,拆成几类资产:
• manifest.json:索引版本、生成时间、资产摘要• catalog.json:规范目录和元数据• content-manifest.json:正文文件、章节、条文等内容索引• search-structures.json:搜索所需的稀疏结构和打分数据
这种“构建期重活、运行期轻读”的方式非常适合小资源服务器。运行服务时,Node 进程读取已经生成好的 JSON 资产,搜索时直接使用内存中的结构,不需要数据库 join,不需要外部搜索节点,也不需要实时解析原始书库。
拆分索引资产还有两个好处:
• 启动和加载可以分层:目录浏览不必立刻加载完整搜索结构 • 故障可诊断:某个资产缺失或损坏时,可以明确报告是目录、正文还是搜索结构异常
对于中小规模书库,这种方式通常比引入 Elasticsearch 更划算。Elasticsearch 很强,但需要额外内存、JVM、磁盘维护、索引映射设计和运维经验。若书库规模只是几十本到几百本文档,且查询模式稳定,预生成 JSON 索引加内存检索就足以覆盖大量场景。
四、组合查询比单一算法更重要
规范类文档的查询并不是普通互联网搜索。用户输入往往带有强结构,例如:
• GB55001• 3.2.1 设计工作年限• 附录A 符号• 燃气 管道 防火间距• 建筑节能 围护结构
这类查询如果只靠一个模糊匹配算法,很容易出现看似相关但无法复核的结果。WEB4GPCODE 的经验是把查询拆成多个层次组合处理。
1. 结构化直达优先
规范编号、条号、章号、节号、附录号是最可靠的线索。系统应优先识别这些结构化目标,然后缩小搜索范围。
这类能力主要依赖:
• 规则解析 • 正则匹配 • 大小写、空格、标点归一化 • 槽位填充
例如 GB 55001 3.2.1 和 gb55001 3.2.1 应该归一到同一个规范编号和条号。结构化直达的价值不只是准确,还能降低资源消耗,因为它能快速减少候选范围。
2. 字段化检索,而不是把所有文本揉成一团
一份规范文档里,不同字段的重要性不同。规范编号、书名、章节标题、条号、关键词、正文文本不能同权处理。
WEB4GPCODE 采用字段化思路,把这些内容分别纳入索引和打分:
• 规范编号 • 标准号 • 规范标题 • 文献类型 • 领域标签 • 主题标签 • 文件标题 • 章节路径 • 条文编号 • 条文正文
这样做的好处是排序更符合用户直觉。标题命中通常比正文角落里的偶然命中更重要;条号精确命中应高于普通正文词命中;规范号命中应直接影响候选书籍选择。
3. 精确短语作为主路径
工程规范里有大量固定术语,例如“设计工作年限”“防火间距”“抗震设防烈度”。这些词组不能完全拆散处理。如果只按单字或单词散列,结果会被大量弱命中冲淡。
因此,精确短语匹配应作为高优先级路径。它能保证常见术语、专名和规范表达优先被找到,也能减少用户反复换关键词的成本。
4. jieba 辅助中文复合词召回
中文没有天然空格,复合词召回是检索质量的重要部分。项目中引入 nodejieba 作为辅助召回层,并配合工程领域词典增强分词结果。
这里的经验是:分词不要替代全部规则主路径,而应作为辅助层使用。对于规范编号、条号、固定短语,规则和精确匹配仍然更稳定;对于自然语言主题词,jieba 能补足中文词边界问题。
5. 2-gram 兜底,但不让它主导排序
2-gram 可以缓解分词失败和输入碎片化问题,但它也容易带来噪声。项目中把 2-gram 放在兜底层,而不是主排序层。这样可以兼顾召回和精度:找不到时能补一点,找得到时不干扰强命中。
6. BM25 / BM25F 适合这类文档
规范、制度、手册这类文本往往有稳定术语和明确字段。BM25 及其字段版 BM25F 很适合这类稀疏检索场景。它们的优点是:
• 不需要训练数据 • 不需要大模型推理 • 对术语、编号、标题词表现稳定 • 资源消耗低 • 命中原因相对可解释
这也是为什么本项目在“不引入向量和 LLM”的约束下仍能取得较好检索体验。对于专有文档应用,稀疏检索不是落后方案,而是准确、便宜、可控的主路径。
五、低资源消耗的工程策略
小资源消耗不是只靠“少装软件”,而是一系列架构选择共同形成的结果。
1. 单体 Node 服务承载 API 和前端
WEB4GPCODE 使用 Node + Express 提供 API,同时托管最小前端静态文件。对于中小型书库,这种单体结构非常实际:
• 部署简单 • 日志集中 • 反向代理配置简单 • 不需要前后端多服务编排 • 本地开发和公网部署一致性较高
前端是轻量 HTML/CSS/JS,不依赖大型前端构建链路。用户打开页面后,主要通过 /api/books、/api/content、/api/search 与服务交互。
2. 读多写少,尽量走内存结构
书库类应用的典型特点是读多写少。规范正文和索引不是每分钟变化的数据。因此可以把更新成本放到构建索引阶段,把查询成本放到内存读取阶段。
这比“每次请求查数据库再解析正文”更省资源,也更稳定。只要索引构建过程可重复,运行时就可以保持简单。
3. 原始书库不作为静态目录暴露
低资源不等于低边界。公网部署时,不应把 engineering-standards/、data/library-index/ 或原始 Markdown 直接作为 Nginx 静态目录暴露。正确方式是通过 API 返回必要的公开字段和正文内容,隐藏本地路径、索引资产细节和运维结构。
这样既降低泄露风险,也让未来调整书库目录时不破坏外部访问契约。
4. 软限流保护单进程服务
项目在公网模式下启用内存滑动窗口限流,对搜索、阅读、浏览等接口分开配置。对于小服务器,这比一开始上复杂网关更务实。
限流的目的不是精细计费,而是防止单个客户端把 CPU 打满。搜索接口通常是最贵的,应单独限制;阅读和浏览可以更宽松。
5. 反向代理承担 HTTPS 和静态连接
Nginx 或 Caddy 放在 Node 前面,可以处理 TLS、压缩、连接管理和访问日志。Node 只监听本地端口,例如 127.0.0.1:端口号,减少直接暴露面。
这种部署方式对小资源服务器友好:Nginx 负责擅长的网络层工作,Node 负责业务 API 和搜索逻辑。
6. 避免过早引入重型组件
对于几十本到几百本文档的书库,下面这些组件未必是第一阶段必需:
• Elasticsearch • 向量数据库 • 在线 LLM 推理服务 • 微服务拆分 • 分布式任务队列 • 独立前端构建平台
这些工具都有价值,但它们也会引入额外内存、磁盘、运维、监控和故障模式。项目早期更值得投入的是语料整理、索引质量、查询解析和结果解释。
六、小资源书库应用的普适架构
从 WEB4GPCODE 抽象出来,一个通用的小资源书库应用可以采用如下结构:
原始资料
↓ 清洗、切分、命名、补元数据
规范化书库
↓ 构建期索引
目录索引 + 内容索引 + 搜索索引
↓ Node/Express 或其他轻量 API 服务
浏览 / 阅读 / 搜索接口
↓ Nginx/Caddy 反向代理
公网访问关键不是具体语言一定要用 Node,而是这个分层思想:
• 书库和服务分离 • 构建期和运行期分离 • 原始资料和公开 API 分离 • 结构化直达和全文检索组合 • 强命中和弱召回分层 • 低频维护和高频查询分离
只要这些边界清楚,技术栈可以替换成 Python、Go、Rust 或其他运行时。真正决定系统质量的是书库工程化程度和检索链路设计。
七、适合复用的场景
这套路线尤其适合以下类型的资料:
• 工程规范、行业标准、地方标准 • 企业制度、流程手册、质量体系文件 • 产品手册、设备说明书、维修维护资料 • 法规条文、合同模板、审查清单 • 课程讲义、培训材料、内部知识库 • 项目档案、技术备忘录、设计说明汇编
这些资料通常有共同特征:
• 总量中等 • 结构稳定 • 更新频率不高 • 准确引用很重要 • 用户需要定位原文,而不是只要摘要 • 预算或服务器资源有限
在这种场景下,低资源文档应用往往比“大模型问答平台”更合适。它不会替代人工判断,但能显著降低定位资料、查找条文和复核依据的时间。
八、经验总结
WEB4GPCODE 的开发经验可以概括为几条原则:
1. 先把书库整理成工程资产,再谈搜索体验。 2. 用机器清单和稳定目录约束语料,而不是让运行时代码猜。 3. 把重计算放到索引构建阶段,把查询阶段做轻。 4. 结构化直达、字段化检索、短语匹配、中文分词和 2-gram 兜底要组合使用。 5. BM25/BM25F 这类稀疏检索非常适合规范、制度、手册类文档。 6. 小资源部署要控制组件数量,优先单体服务、预生成索引、内存查询和反向代理。 7. 当书库规模和访问量真正增长后,再考虑搜索集群、向量补召回或多进程扩展。
最重要的判断是:专有文档书库应用的核心价值不是“看起来像智能问答”,而是让用户稳定、快速、低成本地找到可信原文。只要这个目标明确,小资源服务器也可以承载一个实用的专业书库系统。
AI Research & Do
夜雨聆风