Cerebras 公开公司知识库搭法:不需要任何人改使用习惯,资料留在原处AI自己去取
- Cerebras 的三名工程师公开了公司内部知识库 Cerebras Knowledge 的搭法,上线三个月,每天被提问超过 15000 次,提问方包括员工、自动化脚本和 Agent。
- 它不要求任何人改变习惯:资料继续留在 Slack、代码仓库、文档系统里,系统去每个平台取数,全部落进同一张 Postgres 表。
- Slack 是最难啃的源。同一批候选会被四种打分方式同时检索:全文匹配、向量匹配、稀有词权重、时间衰减,各补各的盲区。
- 聊天记录不直接拿去做向量。先由模型蒸馏成「一句话问题+摘要+结论+涉及的系统和代码符号」的统一结构,再嵌入。
- 六个检索器各出一份排名,用倒数排名融合合成一份,常数取 60,效果是「几份榜单都靠前」的文档赢过「一份榜单第一」的文档。
每天 15000 个问题,问的人里还有 Agent
Cerebras 的三名工程师最近在公司博客上把内部知识库 Cerebras Knowledge 的搭法整个拆开公开,从数据怎么采集一路讲到答案怎么排序,每一层用了什么参数都写在里面。
这套系统上线三个月,每天被提问超过 15000 次。提问的不只是员工,还有自动化脚本和 Agent。
触发它的场景很多公司都熟悉:Cerebras 的团队分布在机房运维、芯片设计、硬件、训练、推理、云平台等方向,每年进来几百个新人,沟通频道里被同样的三类问题填满:某个东西在哪里、某件事谁最懂、这个名词是什么意思。
① SOURCES 数据源,Slack、Wiki、代码、事故记录 ② DISTILLATION 蒸馏,模型把原始内容抽成统一结构 ③ EMBEDDINGS 向量,存进 Postgres 的 pgvector,3072 维,用 HNSW 这种索引结构快速找到最接近的向量 ④ RETRIEVAL 检索,六份排名并行 ⑤ FUSION + RERANK 融合与重排,先用倒数排名融合再交给小模型重排 ⑥ SYNTHESIS 合成,给出带引用的答案。来源:Cerebras
把资料全搬到一个地方,为什么从来没成过
在公司内部找资料难,难点不在搜索本身,在于资料天生就是散的。每过一个季度,总有人提出同一个「绝妙方案」:以后所有信息都记录到一个平台上,做成唯一的真相源。这个设想在实践中几乎没有成功过。
原因是信息在哪里产生方便,就会在哪里产生:文档里的建议修改、Slack 里的讨论串、GitHub 里的代码引用、Jira 里的状态字段。这些平台各自为自己的场景打磨了很多年。在 Google Docs 里讨论一个 pull request 会是很糟糕的体验。
所以 Cerebras 的第一个决定,是设计一套几乎不要求人改变现有习惯的系统。采集这一侧的做法,就是直接去每个平台把数据抽出来。
- 要求大家以后都写到统一平台
- 每个人多一道工序,执行几周后回潮
- 历史资料还留在原处,两边都不全
- 人继续在原来最顺手的地方写
- 每个源写一个连接器去取数
- 取回来的东西统一格式,落进同一张表
整套东西的核心,是一张表
这个知识库对外提供三件事:一个存内部数据的地方、一个查这些数据的地方,以及一层做身份认证、权限控制、审计和统计的东西。
核心是一张 Postgres 表,里面同时放着向量、原始摘要和元数据,来源不限。系统持续把公司各处的数据灌进来,维持一个随时可查的状态。数据接口被刻意做得简单:不管来自 Slack 讨论串还是芯片设计用的网表文件,都落进同一张向量表,进了这张表就能被同一套接口查到。
每个数据源只需要定义三件事:这是什么数据、怎么连上去、多久取一次。取回来生成的每一行,字段结构完全一样。
这个设计还留了一个口子给别的团队。有些团队本来就有自己的数据库,不想为了进知识库把数据挪到 Slack 或文档系统里。做法是把这类源当成插件脚本:团队提一个 pull request,交一个小的 Python 模块,负责从自己的系统读数据、吐出符合那张表结构的行,再配一条数据源记录。只要写进同一个库、用同一套字段,剩下的整条链路一行都不用动。
Slack 里最难的事,是分不清哪句话有用
Slack 是他们最看重、也最难处理的数据源,公司里最新的工程讨论都发生在那里。一开始他们试过最省事的办法:把原始文本直接做成向量去搜。很快发现单靠向量捞不全。
问题出在聊天记录本身的三个特点。第一,信息密度差得极远,「好的收到,谢谢」和一整段讲内核实现的解释,在系统眼里都只是一条消息。第二,消息长短不一,而短消息在向量相似度上经常压过更长、更详细的那条。第三,一条消息的含义往往取决于它前后的对话。
你在群里搜「恢复卡在清单加载之后」。真正解决过这个问题的那条回复,可能一个原词都没用,说的是「checkpoint 在 NFS 挂载点上卡住了」;而字面最像的那条,是八个月前一个已经作废的老办法;至于「好的收到,谢谢」,它和几乎所有问句都有点像。
他们的解法是让同一条内容同时被几种检索方式覆盖,每一种补上别的短板。同一个查询、同一批四条候选,切换打分方式,谁被抬起来、谁被压下去就变一次(数据取自原文示例)。
(恢复卡在清单加载之后)
CKPT_PREFETCH 设成 4。ERR_MANIFEST_TIMEOUT:恢复卡在清单加载之后。LEGACY_FETCHER=1。切一遍四个标签会发现,被抬起来和被压下去的从来不是同一批。那条两周前、换了一套说法、带着罕见参数名的讨论串,恰好是真正解决问题的答案,可它在全文匹配这一栏里一个词都没对上,只有向量匹配、稀有词权重、时间衰减这三栏认它。反过来,八个月前那条字面几乎一模一样的旧答案,在全文匹配里被抬得最高,到时间衰减这一栏才被打折。任何一种打分单独用,要么漏掉真答案,要么把过期答案排到前面。所以四种打分各自出一份排名,到查询时再合并。
聊天记录不直接拿去做向量
原始 Slack 文本一落地就能被关键词搜到,因为他们在 Postgres 里对原文维护了一个全文索引。但要让向量检索真的好用,还得多做一步加工。
这一步叫蒸馏:让模型读完整条讨论串,抽出四样东西,一句工程师真的会拿去搜的问题、一段简短摘要、最后的结论,以及过程中提到的系统和代码符号。被做成向量的是这四样东西,原始的逐字聊天记录不进向量库。实测把讨论串归一成统一格式之后,准确率有明显提升,多出来的元数据也给语义匹配提供了更多线索。
CKPT_PREFETCH=4 就跑完了,默认值对 NFS 挂载点来说太高。CKPT_PREFETCH=4。数据怎么实时进来:Socket Mode 与整串重拉
采集侧他们在工作区装了一个 Slack 机器人,跑在 Socket Mode 下:Slack 通过一条长连接把每条消息事件推过来,不用反复轮询 Web API,也就不会撞上接口调用频率限制。
事件一到就立刻回执,用稳定的事件 ID 去重,然后标记给入库程序。入库这一步有个容易忽略的设计:它不会孤零零地存下这条新消息,而是先定位这条消息属于哪条讨论串,再从 Slack 接口把整条对话重新拉一遍,包括最初那条和后面所有回复,然后把整串当作一行写回去。所以一条新回复会触发父消息和所有同级回复被重新拉取,存下来的正文、参与者名单和最后活跃时间,永远反映的是完整对话的当前状态。
另外,每个 Slack 频道在系统里都是一个独立的数据源,采集频率可以分别调。某个团队可以让自己那个高频的故障频道更新得更勤一些。
长讨论串里,被摘要漏掉的那条消息
做到这一步,Slack 的检索已经可用,但他们反复撞上同一个毛病:长讨论串里那些重要的消息,不一定能在整串的摘要里留下痕迹。
补救办法叫 bursting。一段 burst 指的是同一个人连着发的几条消息。他们把每段 burst 单独做成向量,并且在前面拼上整串的话题当上下文。这样一来,那种答案藏在某条跑题分支里、用词从没进过整串摘要的情况,也能被单独搜到。
为了不让低价值内容涌进数据库,每段 burst 要过一道加权打分,达标才做向量,判据有三条:含有一个在全公司语料里足够罕见的词,稀有度门槛是 IDF 大于等于 4.0;合并之后至少 200 个字符;这段里有消息被人加了表情回应,算一份来自同事的认可。
40GB 的代码库,怎么保持是最新的
要不要给代码库做向量,他们一开始是犹豫的。有了 Claude Code 这类命令行工具之后,给代码建向量索引看着有点反直觉,感觉有 grep 就够了。跟同行聊过、又读了 Cursor 关于大型代码库语义检索的结论之后,他们决定试一试。
真正的顾虑其实是另一件事:公司内部仓库很多,有的单个超过 40GB,怎么用可接受的代价让索引一直是最新的。
最后落在 CocoIndex 上,一个专门做代码库向量化的开源框架。切块规则按语言的语法边界来,从粗到细依次尝试:先试类这种高层边界,如果切出来的块还是太大,退到方法边界,再不行退到更小的代码块。同一个文件因此会产出好几个粒度的向量,比如文件级一条、函数级各一条。
- 切点落在哪儿全看长度
- 一个函数可能被从中间劈开
- 切出来的块常常缺头少尾,读不出在干嘛
- 先按类切,太大退到方法,再退到条件块和循环块
- 每一块都是一个完整的语法单位
- 同一个文件产出多个粒度,粗细都能被搜到
更新这一侧是这套方案真正省力的地方。CocoIndex 把同步用的元数据也存在 Postgres 里,每次提交只重新做变动过的那些代码块,不用整个仓库重算。同步状态和向量存在同一个库里,这一点对他们来说尤其合适。
随着接入的代码库变多,仓库接入这件事被改成了配置文件,各团队自己提交,可以按文件路径设白名单和黑名单。
一个问题进来之后,系统做了什么
每次收到提问,系统先跑一轮很短的规划:由模型判断这个问题可能需要用到哪些工具、哪些数据源,然后把选中的工具并行发出去。
MCP 那一侧,把工具直接交给 Agent 自己编排
网页界面里,上面这条链路是端到端跑完的:规划选工具,执行层并行取证并归一成带分数、时间和来源提示的统一证据,最后一轮模型据此产出带引用和注意事项的答案。对用户来说就是提个问题拿到一个答案。
MCP 集成走的是相反的路子:把检索能力拆成一个个直接可调的工具暴露出去,而不是藏在一个「帮我回答这个问题」的大接口后面。这些工具被刻意做得简单,尽量不掺模型,客户端因此可以查得又快又便宜。每个 MCP 工具对应一件最底层的检索动作,输入输出窄、有结构、稳定,任何客户端或 Agent 都能直接调。大部分工具只跑一条查询链路,按意思检索、按字面检索或者直接跑 ripgrep,套一层轻量打分,然后把原始证据行返回。
这样一来,编排的活儿交给 Claude Code 这类支持 MCP 的 Agent:调哪些工具、按什么顺序调、结果怎么拼成最终答案或者一次代码修改,都由它自己决定。检索层不依赖这些模型判断也能照常提供服务。
六份排名,怎么合成一份
一份检索结果排在最前面,可能只是因为它和提问用词撞了,实际回答的是另一件事。更麻烦的是,六个检索器给出的分数互相不能直接比:向量的余弦相似度和全文检索的相关度,根本不是同一把尺子。
他们的做法是先不看分数,只看名次。这个算法叫倒数排名融合,缩写 RRF:一份文档每上一份榜单,就给它加一次分,加的量是 1 除以(60 加上它在这份榜单里的名次),权重默认取 1.0,那个 60 是平滑常数。
拿总榜第一名算一遍就能看清这个机制。CLASS: CheckpointLoader 在六份榜单里进了五份,名次分别是第 1、2、3、5、5:
把 60 加在名次前面,第 1 名和第 5 名的得分只差 6%(0.01639 对 0.01538)。如果不加这个常数、直接取名次的倒数,第 5 名就只剩第 1 名的五分之一。所以 60 的作用是把「名次靠前」这件事的收益压平,让共识压过单一强票:一份在好几个检索器里都排在前列的文档,赢过只在其中一个里拿第一的文档。上面那个 INC-82 就是例子,它一次第一都没拿过,靠稳定进入五份榜单排到了第二。
合并之后还有三道工序
融合出来的榜单不会直接送去写答案。接下来先把同一个来源被切散的重复块合并回一条,再限制单个文件最多能贡献几条结果,得到一批更分散的前二十名。这二十条连同原始问题一起交给一个小的重排模型,它给每条打一个 0 到 10 分,留下前十条。
排名定下来之后,再把上下文补回来。比如命中的是 wiki 里的某一段,就顺带把它前后两段也拉进来,避免切块时被切散的标题、前置条件和注意事项丢掉。这样交到读者手上的是一段完整的引用,而不是一段缺前提的孤立文字。
走完这一整条,检索这一步交出去的是一包证据:来自不同检索器、经过融合,在来源这一级去过重,针对真正的问题重排过,最后才补上周边上下文。
搜全公司,反而搜不到有用的
语料越攒越大之后,「什么都搜」这件事很快就不好用了。做编译器的工程师不想在结果里看到机房运维手册,反过来也一样。
他们引入了 project 这一层来解决。一个 project 就是一组数据源的命名包:指定的几个 Slack 频道、几个代码仓库、几个内部数据库和几个文档空间,围绕某个团队或某个项目组织。project 被刻意做得很轻:同一个数据源,比如公司共用的故障频道或者中心平台仓库,可以被多个 project 同时引用,不需要复制一份。
新人入职时会被引导选一个或者建一个跟自己工作最贴近的默认 project,比如训练基础设施、编译器、机房运维。这个默认值存在用户档案里,之后所有查询自动限定在这个范围内。效果是新来的工程师不用先花时间搞清楚哪些 Slack 频道、哪些仓库、哪些文档空间跟自己有关,一上手拿到的结果就已经是相关的。
- 编译器的问题,结果里混进机房运维手册
- 相关度靠算法硬扛
- 新人不知道该信哪一条
- 只在这个团队关心的源里检索
- 相关性由范围先兜住一层
- 入职选一次,之后自动生效
这套知识库之所以跑得起来,是因为它在信息本来待着的地方就把人接住了,而没有把一切塞进一个僵硬的系统里。 Cerebras,《How we built our knowledge base》
全文出现过的具体数字
公司内部找资料:从「先把东西搬到一起」 → 改成「东西留在原地,系统自己去取」
Cerebras 三名工程师公开了内部知识库的完整搭法,一页带图讲完
↓ 一页读完 · 有一张会动的图
公司里每天都有人在问三类问题:某个东西在哪、这事谁最懂、这个名词什么意思。答案其实都在,散在 Slack 聊天、代码仓库、内部文档、故障记录里。所谓「内部知识库」,就是一个能替你把这些地方一起翻一遍、然后直接给答案的东西。
过去公司的标准做法是搜索框加统一平台:每过一个季度,总有人提议「以后所有信息都记到一个地方」。这件事几乎没成过。
Cerebras 的第一个决定是掉头:不要求任何人改写东西的习惯,改成给每个平台写一个取数程序,把取回来的东西统一成同一种格式,全部落进同一张数据表。上线三个月,每天被提问超过 15000 次,提问方除了员工还有自动化脚本和 Agent。
结果:每人多一道工序
几周后回潮,老资料还在原处
每个源一个取数程序
全部落进同一张表:
正文 + 3072 维向量 + 来源 + 时间
落进同一张表的好处是加一个新数据源,查询那一侧一行都不用改。但新麻烦立刻来了:Slack 是最难啃的源,「好的收到,谢谢」和一整段讲内核实现的解释,在系统眼里都只是一条消息。怎么分清哪句话有用?
他们的答案是不指望任何一把尺子:同一批候选,让四种打分方式同时量一遍,各补各的盲区。下图跟着小互的一次真实提问走一遍,他在群里问「恢复卡在清单加载之后」,库里有四条候选,看四把尺子各自把谁抬起来、把谁压下去。
「检索准确率提升」这种说法没有体感。换成同一件具体的事:一个新人接手训练集群,遇到恢复卡住,要找到「设 CKPT_PREFETCH=4」这个答案,三种做法各要多久。
日志停在清单加载。
啥也搜不到!
库里那条真答案写的是
「checkpoint 在 NFS
挂载点上停住」。
那就别搬了?
看见了另一条路。
资料留在 Slack、代码库、
文档里,系统自己去取。
不只是人
和一整段内核解释
凭什么一样重?
四把一起量。
看词有多罕见、按时间打折。
每把补别人的盲区。
另外三把都抬它。
几乎一模一样,时间
这把把它打折。
每上一份榜单加一次分。
只比第 5 名多 6%。
答案带引用。
CKPT_PREFETCH 设成 4。
- × 产品不对外,没有公开入口
- × 15000 次/天是 Cerebras 自己公布,第三方没核过
- × 原文没给「省多少分钟」的对照数据
