从本地文档到 API 服务:给 AI 装一个知识检索外挂

最近在做一个国际快递知识库的时候,遇到了一个问题:AI 助手需要频繁查询一份专业的渠道规则文档,但这份文档有 80 多个文件、近千个段落。直接塞进上下文不现实,每次搜索又太慢。

于是花了一个下午,搭了一个本地 HTTP 服务,用 LanceDB 做向量存储,响应时间控制在 50ms 以内。效果还不错。

方案

思路很简单:

  1. 文档切块,生成 embedding
  2. 存到 LanceDB(本地、无服务、够快)
  3. 暴露一个 HTTP 接口 /search,返回 top-k 结果

用 Python 写了个 Flask 服务,大概 200 行代码。启动后监听 127.0.0.1:8765,agent 调用时发一个 GET 请求就能拿到检索结果。

重点是切块策略。一开始用固定 512 字符切,结果查"液体能走什么渠道"时,排在前面的是"内置电池"相关的内容——因为 chunk 太长,边界不清晰。后来改成按段落语义切,效果就好了。

效果

现在 agent 回答国际快递问题时,会先调用这个服务检索,命中后再回源读原文。比直接让模型"自己想"准确多了。

一个例子:

用户问:DHL、FedEx、UPS 有什么区别?
服务返回 FAQ 文档中的"三大国际快递对比"段落
agent 基于检索结果回答,并且会引用原文

几点体会

Hybrid 检索很重要。 纯向量检索对专有名词不太友好,加上关键词检索后,准确率提升。我的实现是 /search/hybrid,同时走向量索引和 BM25,最后用 RRF 融合。

重排比想象的有用。 初期检索 20 条,用 bge-reranker 重排后取前 5,比直接取前 5 效果好。代价是增加了 100ms 左右的延迟,但对对话场景来说可以接受。

知识库维护是长期工作。 索引建好了不是结束,而是开始。新的 FAQ 要加进去,旧的规则要更新,定期要做 full reindex。这个工作量比搭服务本身大得多。

后续

目前这个服务只支持国际快递一个领域。下一步准备做成通用框架,其他知识库也能复用。代码还没整理好,整理完会开源。