• 请不要在回答技术问题时复制粘贴 AI 生成的内容
tybot2025
V2EX  ›  程序员

先编译再检索:一个不用 embedding 的开源知识库

  •  
  •   tybot2025 · Aug 28 · 3696 views

    市面上大多数「知识库问答」工具的套路都差不多:把文档切块、算 embedding 、塞进向量库,提问时按相似度召回若干碎片喂给 LLM 。KaaS 做了两个和主流不太一样的选择:

    1. 先编译再检索:不直接对原始碎片做 RAG 。先用 LLM 把散乱内容编译成一篇篇结构化的 Markdown Wiki 文章,再在这上面做检索。
    2. 不用 embedding:检索阶段没有向量库、没有相似度计算。文章目录直接喂给 LLM ,让它像人翻目录一样选页、读全文。

    这篇讲整套系统的骨架和几个关键取舍,单个机制的细节留给后续文章。


    解决什么问题

    KaaS 最初是我们内部的一个工具。知识散落在文档、会议、邮件里,每当有人转岗或离开,他积累的上下文就跟着走了,接手的人要花几周重新拼凑。

    我们要的是让散乱输入沉淀成可读、可编辑、能长期演进的知识资产:人能直接打开读的 Markdown ,而不是黑盒向量库。这也定了架构的走向:编译质量第一,检索只是编译产物之上的一层薄导航。

    把散乱笔记蒸馏成结构化、可读的 Wiki ,再检索


    带来了什么价值

    对使用者来说,KaaS 的价值集中在几点:

    先编译成结构化 Wiki 再检索,而不是切块加向量召回

    • 散乱的笔记、文档、会议记录被编译成分类清晰、带溯源的 Markdown Wiki ,能长期积累复用。
    • 用自然语言提问,拿到带引用的回答,同一个问题不用反复被问第二遍。
    • 产出是纯 Markdown:可读、可 git 版本管理、可手改。AI 生成、人工校准,质量随用随涨。
    • 部署轻,docker compose up 或 CLI 一键启动,默认 SQLite ,无向量库重依赖。
    • 通过 MCP 的 ask 工具,Claude Code 、Codex 、openclaw 都能把这套 Wiki 当知识源直接查询。
    • 对接任意 OpenAI-compatible 端点,可以全本地跑,数据不出境。

    怎么做到的

    整体架构

    KaaS 系统架构

    三层分工:

    技术 职责
    Web UI React + Vite + shadcn/ui Chat / Submit / Wiki / Status 四个页面
    Go Backend 标准库 net/http REST/SSE API 、Worker Pool 、任务队列、MCP 端点
    Python AI 引擎 kb-ai ( uv ) 编译流水线、LLM 迭代检索、Chat

    存储默认 SQLite (零依赖,go run 即可跑),存任务队列和编译状态;检索走纯 LLM 迭代,不依赖任何嵌入模型。

    检索不用 embedding ,让 LLM 直接翻目录

    这是 KaaS 和 naive RAG 的关键差别。编译阶段会维护一份 master-index.md 作为全量文章目录(标题加摘要)。检索时不做向量召回,而是把这份目录连同问题一起喂给 LLM ,让它选出最相关的文章路径,再把选中的文章整篇读进来做回答上下文。

    核心就三步:

    store = KBStore(kb_dir, read_only=True)
    catalog = store.existing_articles()          # 1. 读 master-index 目录
    if not catalog:
        return []
    meta_by_path = {a.path: a for a in catalog}
    selected = _select_relevant(catalog, query, model, max_select=max_articles)  # 2. LLM 选页
    return _read_selected(store, meta_by_path, selected)                         # 3. 读全文
    

    LLM 选页就是一段结构化 prompt ,让模型从目录里挑路径并返回 {"paths": [...]};选中的页整篇读入,仅按 MAX_ARTICLE_CHARS = 12_000 截断以控制 prompt 预算。全流程没有一处 import 向量库或 embedding 模型。

    这套方案能成立,是因为编译已经把噪声去掉了:进入检索的是结构化、去重后的文章,目录里的标题和摘要足以让 LLM 导航。好处是部署轻,不需要 chromadb / sentence-transformers / pytorch 这些重依赖,镜像小、无需预下载模型。代价是:只靠目录摘要导航,一篇文章如果只在正文深处才和问题相关、标题摘要里没体现,就可能选不到。这个「 body-depth 召回」缺口是否值得引入向量检索,留待后续按价值评估。

    编译流水线:Extract → Classify → Write → Index

    检索能这么轻,前提是编译够重。KaaS 的编译是一条 4 阶段流水线:

    • Extract:从原始文本提取概念、实体、决策、行动项
    • Classify:把提取结果映射到已有文章,或标记为新建
    • Write:创建/合并 Markdown 文章
    • Index:更新 Markdown 索引( master-index / topic-index )

    服务端把 Classify→Write→Index 编排成一条带并发和 SSE 进度的流水线。工程难点在去噪(单个 LLM 调用卡死不能拖垮整批)和去重(并行分组不能各自「发明」同名文章),这两点在系列首篇《 4 阶段编译流水线:先编译再检索,如何去噪去重》里已经展开,这里不再重复。

    接进任意 AI agent:一个 ask tool 的 MCP 设计

    编译好的 Wiki 不只给自家 Web UI 用,还能被任意支持 Model Context Protocol 的 agent ( Claude Code / Codex / openclaw )当成可问答的知识源。KaaS 的 MCP server 只暴露一个 ask 工具:

    @mcp.tool()
    def ask(query: str, paths: list[str] | None = None, model: str | None = None) -> dict:
    

    它不重写检索逻辑,而是直接复用 chat core (run_server_chat_http):跑一遍 LLM 迭代检索,再让 LLM 生成带内联 [Title](path) 引用的答案。MCP 的 tools/call 是请求/响应式的,chat core 是流式的,所以 ask 用一个 collector 把流事件收集成完整答案再返回。

    两种传输:stdio (默认,agent 本地 spawn kb-ai mcp,自包含)和 streamable-http 。远程场景由 Go 后端在 /mcp 原生服务,保持 :8080 单一对外 origin 。


    小结

    KaaS 的核心做法是把成本压在编译阶段:编译时做足去噪、去重、结构化,检索就能很轻,不用 embedding ,只让 LLM 翻目录读全文。也因为产出是 Markdown ,它能用 git 管理、手动编辑、被任意 agent 当知识源接入。

    几个实现选择也遵循同一个思路:能简单就不复杂。检索用 LLM 迭代翻目录,没有额外堆一个向量库。

    后续每篇会钻进一个机制:编译流水线的去噪去重(已发布)、为什么敢不用 embedding 、Worker 并发与故障恢复、ask tool 的 MCP 设计。代码都是公开的,感兴趣可以直接翻 GitHub 仓库


    致谢

    KaaS 的核心思路受 Andrej Karpathy 的 「 LLM Wiki 」 gist 启发:把知识编译成一个持续演进、相互链接的 Wiki ,随时间沉淀复用,省掉每次提问都对原始数据重跑 RAG 的老路。感谢他把这个模式讲清楚了。


    项目地址

    https://github.com/bybit-exchange/kaas

    欢迎使用 KaaS 并 star 支持我们!

    13 replies    2026-09-11 15:58:51 +08:00
    robinxplorer
        1
    robinxplorer  
       Aug 28
    不会巨慢吗?
    zthxxx
        2
    zthxxx  
       Aug 28
    @tybot2025 顺便问下架构图用什么 skill 画的?挺干净的
    langsfang
        3
    langsfang  
       Aug 28
    有没有 benchmark, 能够证明这种方法的优势呢?
    zuokanyunqishi
        4
    zuokanyunqishi  
       Aug 29
    我用 python vb 了个简单知识库..
    MAzrael
        5
    MAzrael  
       Aug 29   ❤️ 1
    想问一下,这个架构有在超大规模知识库上测试过没,对比传统的 rag 和基于 KG 的增强,有进行过对比没
    1 、百万级文档的 Markdown 索引有多大,上下文会爆吗?
    2 、这个和 KG 有本质区别吗?按文中的 4 阶段流水线,感觉 KG 也可以做这个事,也能绕过 embedding 。
    3 、rag 情况下,不太理解这个绕过 embedding 的目的是啥,还是说你们这个思路在召回上能够有巨大提升?
    4 、感觉这个 4 个阶段跑下来,每个阶段之间都有“精度损失”,并且按照 LLM WIKI 的思路,索引——聚合知识——原信息,LLM 在回答时需要跑好几轮,token 成本应该高不少
    cobiao
        6
    cobiao  
       Aug 30
    我也做了一个 ai 知识库,只不过要轻量很多,面向个人的,打开浏览器即可使用. 也是不要 embedding,但我没有那么多的 pipeline,而是几个基础工具,然后交给 skills
    imdoge
        7
    imdoge  
       2 days ago
    index.md 选页和_select_relevant 是固定流程的一步,不太看好
    文档到达几千上万甚至更多后,index.md 的 metadata 信息迅速臃肿,简略了查不到,丰富了非常大
    然后_select_relevant 是固定的更固化了 agent 的流程,可以看目录查但不应该仅依赖目录而且让它成为管道固定的一步
    xiaomushen
        8
    xiaomushen  
       2 days ago
    是的,这才是更合理的架构方向

    我们要解决的唯一问题是:用户查询一个问题,等答案时候,睡着了怎么办?
    vishun
        9
    vishun  
       13h 4m ago
    `编译阶段会维护一份 master-index.md 作为全量文章目录(标题加摘要)`
    文档少可以,文档多了怎么办?
    DefoliationM
        10
    DefoliationM  
       6h 50m ago
    这没法用,还是得向量数据库。
    gitlight
        11
    gitlight  
       5h 52m ago
    yet another pageindex

    不可否认的是 agenticRAG 是趋势
    vcmq
        12
    vcmq  
       5h 49m ago
    试用了一下,感觉一个文件的处理速度有点慢,另外我上传的是中文的内容,处理出来的是全英文的。。。
    Tywin
        13
    Tywin  
       5h 43m ago
    可能先抽个图谱,再基于图谱去向量数据库 rag 要更快更精准一点
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   2867 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 78ms · UTC 13:42 · PVG 21:42 · LAX 06:42 · JFK 09:42
    ♥ Do have faith in what you're doing.