```html 文档机器人不编造:Ask OLake 的诚实设计 文档机器人不编造:Ask OLake 的诚实设计

问一个文档机器人某个根本不存在的功能,然后看它编。参数名、配置块,一整套给你造出来。你复制粘贴进项目,跑挂了,从此对这份文档的信任又少了一分。

打开网易新闻 查看精彩图片

有人受不了这个,做了个反着来的。它叫 Ask OLake,服务的是开源数据工具 OLake——把数据库和 Kafka 里的数据搬进 Apache Iceberg,同门的 OLake Fusion 负责管 Iceberg 表。这个机器人只守两条规矩:每个回答都标出处;找不到答案,就直接说找不到。

它拿了 OLake x Hacktoberfest 黑客松一等奖。作者把做法和踩过的坑都摊开了讲。

先读渲染后的页面,不读仓库

第一个决定是抓渲染后的文档站,而不是文档仓库。文档是 Docusaurus 的 MDX 格式,其中大约一百个文件是共享片段,只在构建时才被内联进去。读原始文件,会漏掉用户真正在页面上看到的文字。渲染后的页面什么都有,还带稳定的锚点可以链过去。

除了文档,它还读博客和对比页、四个 OLake 的 GitHub 仓库(README、Helm values、连接器配置规范、许可证),以及几百个有实质讨论的已关闭 issue。这些来源有信任顺序:文档优先,然后是仓库内容和 issue,最后是博客。两个来源打架时,文档赢。

没用向量数据库,也没出事

没有向量数据库,也没用 LangChain。整个索引就几千个 chunk,塞进内存还有富余。组件越少,冷启动越快,要调的东西也越少。

检索是混合式的。用户会敲 pgoutput、max_threads 这种精确标识符,也会说“合并小文件”而文档里写的是“compaction”。BM25 擅长前者,嵌入模型帮后者。活动给的 key 只允许聊天模型,所以嵌入是本地跑的,用的是 model2vec,一个响应不到一毫秒的小型静态嵌入器。

意外在这儿。作者原以为加上嵌入只会更好。在 36 道可回答的测试题上,单靠 BM25 有 94% 的概率把正确页面找进前八。等权重加上嵌入后,掉到了 86%——在这个语料上,嵌入器就是比 BM25 弱。把它的权重降到一半,命中率回到 100%。要是没有这套评测集,他会把更差的版本发出去,还自我感觉良好。

诚实不能只写在提示词里

在提示词里写“别编”,那只是个请求。他要的是保证。

检索结果太弱时,应用根本不调用模型,直接返回一句“我在 OLake 文档里没找到这个”,成本为零。来源列表是从回答里真实出现的 [1]、[2] 标记反查回检索到的页面,模型没法引用一个没给过它的 URL。

评测还抓到另一种情况。有人问“OLake 支持 Snowflake 作为数据源吗”,机器人答了支持,因为它找到一个请求该功能的 GitHub issue。于是加了一条规则:issue 是讨论,永远不能当作某功能存在的证据。这一行修掉了一整类错误答案。

让跑分来选模型

主办方先推荐了 GLM-4.7-Flash,同时说三个允许的模型可以自由排序。于是作者把完整的 42 题评测对每个模型各跑一遍,轮流当主模型。

GLM 引用正确页面的比例是 97%,还行。但在一个提示词注入题上,它泄露了自己的系统提示词,最慢的回答超过 17 秒。DeepSeek V4.1 Flash 每次都引到预期页面,六道不可回答和注入题全部处理正确,中位数 2.3 秒,95 分位 4.0 秒。

于是排序是 DeepSeek 第一、Gemini 第二、GLM 第三。第一个模型六秒内没答完,同一个请求转给 Gemini,谁先答完算谁的。代码里还加了一道检查,拒绝任何包含系统提示词文本的回答——光靠提示词规则显然不够。

他笃定的那个想法,错了

他原本确信查询改写会有用。计划是让一个快模型先把“我怎么在本地装这个”翻译成文档自己的词汇,再去检索。做出来一测,准确率从 97% 掉到 89%。改写出来的句子一直在复读自己提示词里的词,而不是用户的意图。

代码还留在仓库里,挂在一个关掉的开关后面。他宁可留个死功能,也不想装作没试过。

真正有用的是些无聊的东西:一份简短的近义词表,在用户词旁边补上文档的词(搜“写数据到”时也搜“destinations”);以及喂给模型 20 个 chunk 而不是 10 个,因为不少正确答案就卡在第 11 到 20 名。

文档保鲜,一次回答不到十分之一美分

文档会变,烤死进去的快照会过期。一个后台任务每小时检查文档仓库和代码仓库有没有动过。有变化就只重抓那一部分,只重嵌不同的 chunk,然后原子替换新索引。旧索引一直服务到新的就绪。每晚一个 GitHub Action 从头全量重建,并把检索评测当作提交前的闸门。

一次典型回答大约 0.0006 美元。活动给了 3 美元额度,他全部开发和评测、大约一千次模型调用,用掉约 55 美分。有“我不知道”这条捷径、一个回答缓存和每日模型调用上限,他不担心额度耗尽。

它还会在哪儿翻车

已知的漏网之鱼,是需要从两个不同页面各取一个事实的问题,比如 Kafka upsert 行为叠加某个具体的 Databricks 配置。要修就得把问题拆成几段,也就意味着第二次模型调用,他判断一题一次调用比覆盖这个场景更值。

嵌入器也偏小。换成嵌入 API,更强的模型能扛起更多排序工作。限流器和缓存都放在进程内存里,要扩到多实例就得上 Redis。

作者留了话:去问它点真问题,或者它不该知道的问题,看它怎么反应。答得不好就开 issue——评测集最初就是这么变好的。