[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"product:kaas":3},{"id":4,"name":5,"slug":6,"slogan":7,"description":8,"organization":9,"images":10,"avatar_url":11,"image_url":12,"website_url":13,"access_links":14,"status":19,"build_by":20,"categories":21,"stats":31,"upload_relationship":36,"create_at":37,"update_at":38,"owner_id":39,"upload_user_id":40,"reviewer_id":41,"video_info":42,"prerelease_cover_url":43,"scheduled_publish_at":42,"user_scheduled":44,"emoji_reacts":45,"tag":42,"pass_review":42,"user_star":44},2771,"KaaS","kaas","先编译再检索：一个不用 embedding 的开源知识库","{\"type\": \"doc\", \"content\": [{\"type\": \"paragraph\", \"content\": [{\"text\": \"Github 仓库\", \"type\": \"text\", \"marks\": [{\"type\": \"link\", \"attrs\": {\"rel\": \"noopener noreferrer nofollow\", \"href\": \"https:\u002F\u002Fgithub.com\u002Fbybit-exchange\u002Fkaas\", \"class\": null, \"target\": \"_blank\"}}]}, {\"text\": \" | \", \"type\": \"text\"}, {\"text\": \"文档站\", \"type\": \"text\", \"marks\": [{\"type\": \"link\", \"attrs\": {\"rel\": \"noopener noreferrer nofollow\", \"href\": \"https:\u002F\u002Fbybit-exchange.github.io\u002Fkaas-doc\u002F\", \"class\": null, \"target\": \"_blank\"}}]}, {\"type\": \"hardBreak\"}, {\"type\": \"hardBreak\"}, {\"text\": \"市面上大多数「知识库问答」工具的套路都差不多：把文档切块、算 embedding、塞进向量库，提问时按相似度召回若干碎片喂给 LLM。KaaS 做了两个和主流不太一样的选择：\", \"type\": \"text\"}]}, {\"type\": \"orderedList\", \"attrs\": {\"type\": null, \"start\": 1}, \"content\": [{\"type\": \"listItem\", \"content\": [{\"type\": \"paragraph\", \"content\": [{\"text\": \"先编译再检索\", \"type\": \"text\", \"marks\": [{\"type\": \"bold\"}]}, {\"text\": \"：不直接对原始碎片做 RAG。先用 LLM 把散乱内容编译成一篇篇结构化的 Markdown Wiki 文章，再在这上面做检索。\", \"type\": \"text\"}]}]}, {\"type\": \"listItem\", \"content\": [{\"type\": \"paragraph\", \"content\": [{\"text\": \"不用 embedding\", \"type\": \"text\", \"marks\": [{\"type\": \"bold\"}]}, {\"text\": \"：检索阶段没有向量库、没有相似度计算。文章目录直接喂给 LLM，让它像人翻目录一样选页、读全文。\", \"type\": \"text\"}]}]}]}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"这篇讲整套系统的骨架和几个关键取舍，单个机制的细节留给后续文章。\", \"type\": \"text\"}]}, {\"type\": \"horizontalRule\"}, {\"type\": \"heading\", \"attrs\": {\"level\": 2}, \"content\": [{\"text\": \"解决什么问题\", \"type\": \"text\"}]}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"KaaS 最初是我们内部的一个工具。知识散落在文档、会议、邮件里，每当有人转岗或离开，他积累的上下文就跟着走了，接手的人要花几周重新拼凑。\", \"type\": \"text\"}]}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"我们要的是让散乱输入沉淀成可读、可编辑、能长期演进的知识资产：人能直接打开读的 Markdown，而不是黑盒向量库。这也定了架构的走向：编译质量第一，检索只是编译产物之上的一层薄导航。\", \"type\": \"text\"}]}, {\"type\": \"horizontalRule\"}, {\"type\": \"heading\", \"attrs\": {\"level\": 2}, \"content\": [{\"text\": \"带来了什么价值\", \"type\": \"text\"}]}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"对使用者来说，KaaS 的价值集中在几点：\", \"type\": \"text\"}]}, {\"type\": \"bulletList\", \"content\": [{\"type\": \"listItem\", \"content\": [{\"type\": \"paragraph\", \"content\": [{\"text\": \"散乱的笔记、文档、会议记录被编译成分类清晰、带溯源的 Markdown Wiki，能长期积累复用。\", \"type\": \"text\"}]}]}, {\"type\": \"listItem\", \"content\": [{\"type\": \"paragraph\", \"content\": [{\"text\": \"用自然语言提问，拿到带引用的回答，同一个问题不用反复被问第二遍。\", \"type\": \"text\"}]}]}, {\"type\": \"listItem\", \"content\": [{\"type\": \"paragraph\", \"content\": [{\"text\": \"产出是纯 Markdown：可读、可 git 版本管理、可手改。AI 生成、人工校准，质量随用随涨。\", \"type\": \"text\"}]}]}, {\"type\": \"listItem\", \"content\": [{\"type\": \"paragraph\", \"content\": [{\"text\": \"部署轻，\", \"type\": \"text\"}, {\"text\": \"docker compose up\", \"type\": \"text\", \"marks\": [{\"type\": \"code\"}]}, {\"text\": \" 或 CLI 一键启动，默认 SQLite，无向量库重依赖。\", \"type\": \"text\"}]}]}, {\"type\": \"listItem\", \"content\": [{\"type\": \"paragraph\", \"content\": [{\"text\": \"通过 MCP 的 \", \"type\": \"text\"}, {\"text\": \"ask\", \"type\": \"text\", \"marks\": [{\"type\": \"code\"}]}, {\"text\": \" 工具，Claude Code、Codex、openclaw 都能把这套 Wiki 当知识源直接查询。\", \"type\": \"text\"}]}]}, {\"type\": \"listItem\", \"content\": [{\"type\": \"paragraph\", \"content\": [{\"text\": \"对接任意 OpenAI-compatible 端点，可以全本地跑，数据不出境。\", \"type\": \"text\"}]}]}]}, {\"type\": \"horizontalRule\"}, {\"type\": \"heading\", \"attrs\": {\"level\": 2}, \"content\": [{\"text\": \"怎么做到的\", \"type\": \"text\"}]}, {\"type\": \"heading\", \"attrs\": {\"level\": 3}, \"content\": [{\"text\": \"整体架构\", \"type\": \"text\"}]}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"三层分工：\", \"type\": \"text\"}]}, {\"type\": \"bulletList\", \"content\": [{\"type\": \"listItem\", \"content\": [{\"type\": \"paragraph\", \"content\": [{\"text\": \"Web UI，React + Vite + shadcn\u002Fui，Chat \u002F Submit \u002F Wiki \u002F Status 四个页面\", \"type\": \"text\"}]}]}, {\"type\": \"listItem\", \"content\": [{\"type\": \"paragraph\", \"content\": [{\"text\": \"Go Backend，标准库 \", \"type\": \"text\"}, {\"text\": \"net\u002Fhttp\", \"type\": \"text\", \"marks\": [{\"type\": \"code\"}]}, {\"text\": \"，REST\u002FSSE API、Worker Pool、任务队列、MCP 端点\", \"type\": \"text\"}]}]}, {\"type\": \"listItem\", \"content\": [{\"type\": \"paragraph\", \"content\": [{\"text\": \"Python AI 引擎，kb-ai（uv），编译流水线、LLM 迭代检索、Chat\", \"type\": \"text\"}]}]}]}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"存储默认 SQLite（零依赖，\", \"type\": \"text\"}, {\"text\": \"go run\", \"type\": \"text\", \"marks\": [{\"type\": \"code\"}]}, {\"text\": \" 即可跑），存任务队列和编译状态；检索走纯 LLM 迭代，不依赖任何嵌入模型。\", \"type\": \"text\"}]}, {\"type\": \"heading\", \"attrs\": {\"level\": 3}, \"content\": [{\"text\": \"检索不用 embedding，让 LLM 直接翻目录\", \"type\": \"text\"}]}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"这是 KaaS 和 naive RAG 的关键差别。编译阶段会维护一份 \", \"type\": \"text\"}, {\"text\": \"master-index.md\", \"type\": \"text\", \"marks\": [{\"type\": \"code\"}]}, {\"text\": \" 作为全量文章目录（标题加摘要）。检索时不做向量召回，而是把这份目录连同问题一起喂给 LLM，让它选出最相关的文章路径，再把选中的文章整篇读进来做回答上下文。\", \"type\": \"text\"}]}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"核心就三步：\", \"type\": \"text\"}]}, {\"type\": \"codeBlock\", \"attrs\": {\"language\": \"python\"}, \"content\": [{\"text\": \"store = KBStore(kb_dir, read_only=True)\\n\u002F\u002F 1. 读 master-index 目录\\ncatalog = store.existing_articles()\\nif not catalog:\\n    return []\\nmeta_by_path = {a.path: a for a in catalog}\\n\u002F\u002F 2. LLM 选页\\nselected = _select_relevant(catalog, query, model, max_select=max_articles)  \\n\u002F\u002F 3. 读全文\\nreturn _read_selected(store, meta_by_path, selected)                       \", \"type\": \"text\"}]}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"LLM 选页就是一段结构化 prompt，让模型从目录里挑路径并返回 \", \"type\": \"text\"}, {\"text\": \"{\\\"paths\\\": [...]}\", \"type\": \"text\", \"marks\": [{\"type\": \"code\"}]}, {\"text\": \"；选中的页整篇读入，仅按 \", \"type\": \"text\"}, {\"text\": \"MAX_ARTICLE_CHARS = 12_000\", \"type\": \"text\", \"marks\": [{\"type\": \"code\"}]}, {\"text\": \" 截断以控制 prompt 预算。全流程没有一处 import 向量库或 embedding 模型。\", \"type\": \"text\"}]}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"这套方案能成立，是因为编译已经把噪声去掉了：进入检索的是结构化、去重后的文章，目录里的标题和摘要足以让 LLM 导航。好处是部署轻，不需要 chromadb \u002F sentence-transformers \u002F pytorch 这些重依赖，镜像小、无需预下载模型。代价是：只靠目录摘要导航，一篇文章如果只在正文深处才和问题相关、标题摘要里没体现，就可能选不到。这个「body-depth 召回」缺口是否值得引入向量检索，留待后续按价值评估。\", \"type\": \"text\"}]}, {\"type\": \"heading\", \"attrs\": {\"level\": 3}, \"content\": [{\"text\": \"编译流水线：Extract → Classify → Write → Index\", \"type\": \"text\"}]}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"检索能这么轻，前提是编译够重。KaaS 的编译是一条 4 阶段流水线：\", \"type\": \"text\"}]}, {\"type\": \"bulletList\", \"content\": [{\"type\": \"listItem\", \"content\": [{\"type\": \"paragraph\", \"content\": [{\"text\": \"Extract\", \"type\": \"text\", \"marks\": [{\"type\": \"bold\"}]}, {\"text\": \"：从原始文本提取概念、实体、决策、行动项\", \"type\": \"text\"}]}]}, {\"type\": \"listItem\", \"content\": [{\"type\": \"paragraph\", \"content\": [{\"text\": \"Classify\", \"type\": \"text\", \"marks\": [{\"type\": \"bold\"}]}, {\"text\": \"：把提取结果映射到已有文章，或标记为新建\", \"type\": \"text\"}]}]}, {\"type\": \"listItem\", \"content\": [{\"type\": \"paragraph\", \"content\": [{\"text\": \"Write\", \"type\": \"text\", \"marks\": [{\"type\": \"bold\"}]}, {\"text\": \"：创建\u002F合并 Markdown 文章\", \"type\": \"text\"}]}]}, {\"type\": \"listItem\", \"content\": [{\"type\": \"paragraph\", \"content\": [{\"text\": \"Index\", \"type\": \"text\", \"marks\": [{\"type\": \"bold\"}]}, {\"text\": \"：更新 Markdown 索引（master-index \u002F topic-index）\", \"type\": \"text\"}]}]}]}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"服务端把 Classify→Write→Index 编排成一条带并发和 SSE 进度的流水线。工程难点在去噪（单个 LLM 调用卡死不能拖垮整批）和去重（并行分组不能各自「发明」同名文章），这两点在系列首篇《4 阶段编译流水线：先编译再检索，如何去噪去重》里已经展开，这里不再重复。\", \"type\": \"text\"}]}, {\"type\": \"heading\", \"attrs\": {\"level\": 3}, \"content\": [{\"text\": \"接进任意 AI agent：一个 \", \"type\": \"text\"}, {\"text\": \"ask\", \"type\": \"text\", \"marks\": [{\"type\": \"code\"}]}, {\"text\": \" tool 的 MCP 设计\", \"type\": \"text\"}]}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"编译好的 Wiki 不只给自家 Web UI 用，还能被任意支持 Model Context Protocol 的 agent（Claude Code \u002F Codex \u002F openclaw）当成可问答的知识源。KaaS 的 MCP server 只暴露一个 \", \"type\": \"text\"}, {\"text\": \"ask\", \"type\": \"text\", \"marks\": [{\"type\": \"code\"}]}, {\"text\": \" 工具：\", \"type\": \"text\"}]}, {\"type\": \"codeBlock\", \"attrs\": {\"language\": \"python\"}, \"content\": [{\"text\": \"@mcp.tool()\\ndef ask(query: str, paths: list[str] | None = None, model: str | None = None) -> dict:\", \"type\": \"text\"}]}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"它不重写检索逻辑，而是直接复用 chat core（\", \"type\": \"text\"}, {\"text\": \"run_server_chat_http\", \"type\": \"text\", \"marks\": [{\"type\": \"code\"}]}, {\"text\": \"）：跑一遍 LLM 迭代检索，再让 LLM 生成带内联 \", \"type\": \"text\"}, {\"text\": \"[Title](path)\", \"type\": \"text\", \"marks\": [{\"type\": \"code\"}]}, {\"text\": \" 引用的答案。MCP 的 \", \"type\": \"text\"}, {\"text\": \"tools\u002Fcall\", \"type\": \"text\", \"marks\": [{\"type\": \"code\"}]}, {\"text\": \" 是请求\u002F响应式的，chat core 是流式的，所以 \", \"type\": \"text\"}, {\"text\": \"ask\", \"type\": \"text\", \"marks\": [{\"type\": \"code\"}]}, {\"text\": \" 用一个 collector 把流事件收集成完整答案再返回。\", \"type\": \"text\"}]}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"两种传输：stdio（默认，agent 本地 spawn \", \"type\": \"text\"}, {\"text\": \"kb-ai mcp\", \"type\": \"text\", \"marks\": [{\"type\": \"code\"}]}, {\"text\": \"，自包含）和 streamable-http。远程场景由 Go 后端在 \", \"type\": \"text\"}, {\"text\": \"\u002Fmcp\", \"type\": \"text\", \"marks\": [{\"type\": \"code\"}]}, {\"text\": \" 原生服务，保持 \", \"type\": \"text\"}, {\"text\": \":8080\", \"type\": \"text\", \"marks\": [{\"type\": \"code\"}]}, {\"text\": \" 单一对外 origin。\", \"type\": \"text\"}]}, {\"type\": \"horizontalRule\"}, {\"type\": \"heading\", \"attrs\": {\"level\": 2}, \"content\": [{\"text\": \"小结\", \"type\": \"text\"}]}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"KaaS 的核心做法是把成本压在编译阶段：编译时做足去噪、去重、结构化，检索就能很轻，不用 embedding，只让 LLM 翻目录读全文。也因为产出是 Markdown，它能用 git 管理、手动编辑、被任意 agent 当知识源接入。\", \"type\": \"text\"}]}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"几个实现选择也遵循同一个思路：能简单就不复杂。检索用 LLM 迭代翻目录，没有额外堆一个向量库。\", \"type\": \"text\"}]}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"后续每篇会钻进一个机制：编译流水线的去噪去重（已发布）、为什么敢不用 embedding、Worker 并发与故障恢复、\", \"type\": \"text\"}, {\"text\": \"ask\", \"type\": \"text\", \"marks\": [{\"type\": \"code\"}]}, {\"text\": \" tool 的 MCP 设计。代码都是公开的，感兴趣可以直接翻 \", \"type\": \"text\"}, {\"text\": \"GitHub 仓库\", \"type\": \"text\", \"marks\": [{\"type\": \"link\", \"attrs\": {\"rel\": \"noopener noreferrer nofollow\", \"href\": \"https:\u002F\u002Fgithub.com\u002Fbybit-exchange\u002Fkaas\", \"class\": null, \"target\": \"_blank\"}}]}, {\"text\": \"。\", \"type\": \"text\"}]}, {\"type\": \"horizontalRule\"}, {\"type\": \"heading\", \"attrs\": {\"level\": 2}, \"content\": [{\"text\": \"致谢\", \"type\": \"text\"}]}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"KaaS 的核心思路受 Andrej Karpathy 的 \", \"type\": \"text\"}, {\"text\": \"「LLM Wiki」gist\", \"type\": \"text\", \"marks\": [{\"type\": \"link\", \"attrs\": {\"rel\": \"noopener noreferrer nofollow\", \"href\": \"https:\u002F\u002Fgist.github.com\u002Fkarpathy\u002F442a6bf555914893e9891c11519de94f\", \"class\": null, \"target\": \"_blank\"}}]}, {\"text\": \" 启发：把知识编译成一个持续演进、相互链接的 Wiki，随时间沉淀复用，省掉每次提问都对原始数据重跑 RAG 的老路。感谢他把这个模式讲清楚了。\", \"type\": \"text\"}]}, {\"type\": \"horizontalRule\"}, {\"type\": \"heading\", \"attrs\": {\"level\": 2}, \"content\": [{\"text\": \"项目地址\", \"type\": \"text\"}]}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"https:\u002F\u002Fgithub.com\u002Fbybit-exchange\u002Fkaas\", \"type\": \"text\", \"marks\": [{\"type\": \"link\", \"attrs\": {\"rel\": \"noopener noreferrer nofollow\", \"href\": \"https:\u002F\u002Fgithub.com\u002Fbybit-exchange\u002Fkaas\", \"class\": null, \"target\": \"_blank\"}}]}]}, {\"type\": \"paragraph\", \"content\": [{\"text\": \"欢迎使用 KaaS 并 star 支持我们！\", \"type\": \"text\"}]}]}","bybit","https:\u002F\u002Fwatcha.tos-cn-beijing.volces.com\u002Fprod\u002Fuser\u002Fuploads\u002F10088572_1785596069_c3daf277-f480-4226-937b-993c8c722356.png;https:\u002F\u002Fwatcha.tos-cn-beijing.volces.com\u002Fprod\u002Fuser\u002Fuploads\u002F10088572_1785596076_4a88b28c-d444-4787-8dbb-62f01a4da338.png;https:\u002F\u002Fwatcha.tos-cn-beijing.volces.com\u002Fprod\u002Fuser\u002Fuploads\u002F10088572_1785596079_00eaafdb-6ef8-48ef-9a16-d8f97b72e6db.png","https:\u002F\u002Fwatcha.tos-cn-beijing.volces.com\u002Fprod\u002Fuser\u002Fuploads\u002F10088572_1785595846_fb29399d-d85e-41d8-a866-a0870f3a8f9b.png","https:\u002F\u002Fwatcha.tos-cn-beijing.volces.com\u002Fprod\u002Fuser\u002Fuploads\u002F10088572_1785596566_3f468140-e6b3-4928-b2ee-db9619056efd.png","https:\u002F\u002Fgithub.com\u002Fbybit-exchange\u002Fkaas",{"items":15},[16],{"platform":17,"url":13,"is_primary":18},"WEB",true,"PUBLISHED",[],[22,25,28],{"id":23,"name":24},6,"编程开发",{"id":26,"name":27},7,"智能搜索",{"id":29,"name":30},8,"知识管理",{"upvotes":32,"stars":33,"review_count":32,"reply_count":32,"score":34,"post_count":32,"update_at":35},1,5,2.0654329147389294,"2026-09-16T12:08:02.575Z","THIRD_PARTY","2026-08-01T14:54:53.394Z","2026-08-01T15:39:15.454Z",0,10088572,10004627,null,"",false,{"reacts":46},[]]