作者:@hi_im_isaac_、@learnwdaniel、@gaozenghao 注:完整技术博客的交互式版本见:https://www.cerebras.ai/blog/how-we-built-our-knowledge-base
员工每天向我们的内部知识库提问超过 15,000 次。它在三个月前上线后,已成为公司内部采用最广的工具之一,供人类、自动化流程和智能体使用。
在 Cerebras,我们的团队覆盖数据中心运维、芯片设计、硬件、训练、推理、云平台等多个领域。随着每年数百名新员工加入,沟通渠道里不断涌现同样的问题:
- “去哪里能找到 X?”
- “谁是 Y 领域的专家?”
- “Z 是什么?”

我们构建 Cerebras Knowledge,是为了帮助人和系统接入有用的信息。
让数据留在它原本所在之处
在组织内部寻找信息很难。数据分散在各类工具中,每隔几个月,总会有人提出同一个看似绝妙的解决方案:把所有内容都记录到一个平台,让全部信息集中在同一个地方。当然,“唯一事实来源”的梦想在实践中很少奏效。
信息会在最方便、最符合使用习惯的地方产生:文档中的修订建议、Slack 里的讨论串、GitHub 中的代码引用,以及 Jira 的状态元数据。这些平台都为各自的领域量身打造,经过多年产品工程和分析优化。拿 Google Docs 来讨论一个拉取请求,体验一定很糟。
因此,我们着手设计一个尽量不改变既有工作方式的系统。在数据采集侧,这意味着直接从各个平台提取数据。
知识库的构成
我们的知识库提供三项能力:
- 用于收集和存储内部数据的平台。
- 用于查询这些数据的平台。
- 负责身份验证和授权的层,并提供审计与分析能力。
其核心是一张 Postgres 表,保存来自多个来源的嵌入向量、原始摘要和元数据。系统持续摄入公司各处的数据,并维护一个随时可查询的数据存储。
我们希望数据接口足够简单,同时又能适用于大多数数据形态;也希望 Cerebras 的其他开发者能够构建自定义连接器。最终方案刻意保持简单:从 Slack 讨论串到网表(netlist),每个来源都写入同一张嵌入表;而表中的任何内容都可立即通过同一接口查询:

每个数据源都定义数据是什么、如何连接,以及多久抓取一次。无论一行嵌入数据来自 Slack、代码仓库、文档系统还是自定义数据库,它都遵循同一套接口。
我们如何处理非结构化的 Slack 对话
Slack 是我们最需要优先设计的数据源:公司中最新的工程讨论大多发生在那里。

起初,我们测试了对原始文本直接生成简单嵌入是否足够好。很快就发现,仅靠向量检索不足以匹配所有相关数据。
Slack 消息带来了几个挑战:
- 信息密度差异极大:“hey yeah sure mike”和一段详细的内核说明都是消息。
- 消息长度各异;在余弦相似度上,更短的消息经常会胜过更长、更详细的消息。
- 一条消息的含义往往取决于周围的对话。
我们需要混合式方案。我们构建 Slack 摄入流程,让每个讨论串能够同时通过多种检索技术找到;每种技术都用来弥补其他技术的弱点:
全文检索能捕捉嵌入会模糊掉的精确词元:错误字符串、标志名、主机名。当工程师粘贴一条字面量错误信息时,精确的词法匹配几乎总是最佳证据;再高的语义相似度也不该盖过它。
嵌入检索能够捕捉改述。提问“manifest 加载后恢复过程卡住了”的人与回答“checkpoint 停在 NFS 挂载点”的人,可能从未使用相同词汇。正是向量相似度把用不同措辞写下的问题与答案关联起来。(1)
逆文档频率(IDF)能把信号与填充内容区分开。围绕罕见词元(例如冷门配置标志)写成的短消息,理应获得较高排名。“sounds good, thanks!” 在嵌入空间中可能接近许多查询,但一旦考虑词项稀有度,它的得分几乎为零。
时间衰减刻画了 Slack 答案会过期这一事实。两个讨论串可能都回答同一个问题,但六个月前的那个可能描述的是已经不存在的基础设施。在其他相关性相同的情况下,更新的讨论串胜出。

我们不会单独信任任何一个评分器。每种技术都会为同一语料库产生自己的排序视图,查询时再将这些视图融合(见“重排序”)。
Socket Mode
为了实时采集数据,我们在工作区中安装了一个 Slack 机器人,并以 Socket Mode 运行。Slack 通过持久 WebSocket 将每个消息事件推送给我们,因此无需轮询 Web API、耗尽其速率限制,就能获得实时更新。
事件到达后,我们会立刻确认,使用稳定的事件 ID 去重,并标记该消息交给摄入消费者处理。
摄入消费者不会孤立地保存一条新消息。它会解析消息所属的讨论串,并从 Slack API 重新拉取完整对话,包括父消息和每条回复;随后将整个讨论串作为一行写回。因此,只要现有讨论串新增回复,就会重新拉取父消息和所有同级回复,确保已存储的内容、参与者列表和最后活跃时间戳始终反映完整对话。
系统中的每个 Slack 频道都有自己的数据源。这使我们能够细粒度地调节数据新鲜度;例如,一个团队可以选择更频繁地摄入繁忙的事故响应频道。
讨论串与消息
原始 Slack 文本一落库就可以进行关键词检索,因为我们在原始内容上维护了 Postgres 全文(GIN)索引。不过,要实现有用的向量检索,我们还会做一些额外处理。(8)
在蒸馏阶段,LLM 会从完整讨论串中提取结构化数据:
- 一句工程师实际会搜索的问题。
- 一段简短摘要。
- 问题的解决方案。
- 被提及的系统和代码引用。

我们对这些数据点生成嵌入,并将其写入共享嵌入表。原始转录文本并不直接嵌入。在实验中,将讨论串归一化为一致格式后,准确率显著提升。(7,9) 额外的元数据也为语义匹配提供了更有用的信号。
突发段(Burst)
此时 Slack 检索已经不错,但我们仍不断遇到同一问题:长讨论串中的重要消息,并不总能被讨论串级摘要所代表。
为了增强单条消息的信号,我们使用 burst。一个 burst 是同一作者连续发送的一段消息。我们会为单个 burst 生成嵌入,并在前面附上讨论串主题作为上下文(2),因为有时答案藏在某条岔开的消息里,它的词汇根本不会出现在讨论串摘要中。Burst 嵌入使这条消息本身也可以被找到。
为了防止低信号数据进入数据库,每个 burst 都会根据加权信号组合评分,只有超过阈值才会生成嵌入:
- 它包含在整个语料库中相对罕见的词元,IDF 至少为 4.0。
- 合并后的 burst 至少有 200 个字符。
- 该 burst 中的一条或多条消息包含表情回应,从而获得社交信号加成。

蒸馏后,符合条件的 burst 会生成嵌入,并与讨论串级记录一同存入嵌入表。
代码仓库
最初,我们曾讨论是否有必要为代码仓库生成嵌入。随着 Claude Code 和其他命令行工具的兴起,在“grep 就够了”的氛围下,构建代码嵌入似乎有些反直觉。但在与业内同行交流、并阅读 Cursor 关于大型代码库语义检索的发现后,我们决定尝试。
我们有许多内部仓库,其中一些超过 40 GB。最主要的顾虑是如何高效地保持它们最新。
使用 @cocoindex_io 维护代码嵌入
经过数次实验,我们选择了 CocoIndex——一个专注于代码库向量化的开源文档嵌入框架。
对于每个仓库,我们使用按由粗到细顺序排列、与语言相关的正则表达式边界来切分代码。切分器会先尝试类等较高层级的边界;如果得到的块仍然过大,就会退回到方法边界,再到更小的代码块。我们对得到的块生成嵌入,并将向量写入 Postgres。单个文件可能生成多个不同具体层次的嵌入,例如文件级和函数级记录。

CocoIndex 会在 Postgres 中跟踪同步元数据。每次提交时,它只会重新生成嵌入并重新导出变更过的代码块,而不是重新计算整个仓库。这一点对我们尤其有效,因为同步状态和嵌入存储位于同一个数据库。
随着代码库数量增加,我们将仓库接入迁移到由团队自行提交的配置文件中,其中包括文件路径级别的允许列表和拒绝列表。
自定义数据源
有些团队本来就有自己的数据库,并不希望为了接入知识库而把数据迁到 Slack 或文档系统。他们希望能够在现有表上使用同样的查询界面。
为此,我们把自定义来源视为插件脚本。团队提交一个拉取请求,其中包含一个小型 Python 模块:它知道如何从自己的系统读取数据,并输出符合我们嵌入表形状的行,以及对应的数据源条目。
只要脚本使用与其他每一行嵌入相同的模式写入共享数据库,栈中的其他部分就无需改变。这些数据就能与 Slack、代码和文档一起查询,系统其他地方无需特殊处理。
规划与工具并发扇出
对于每个查询,我们首先运行一次简短的规划:LLM 决定哪些工具和数据源可能重要。主要工具包括:
- subsystem_index:按文件生成的 LLM 摘要。
- search:覆盖 Slack、wiki、代码和其他已索引来源的统一向量管线,内部会合并并重排序。
- search_slack:直接检索 Slack。
- search_code:对源代码仓库运行 ripgrep。
- recent_prs:与问题相关的近期拉取请求。
- who_knows:在某个主题上已展现专业能力的人。
规划器面对的是我们已建立索引内容的紧凑描述:有哪些项目、每个项目有哪些来源,以及各来源适合回答什么问题。给定用户查询和当前作用域后,它会产出工具选择;执行器将这些选择并发扇出,规范化为统一证据格式,再交给最终的综合 LLM。(4)

重排序
一份文档可能仅因与查询共享词汇,就出现在靠前位置,却回答的是另一个问题。重排序前,我们先使用倒数排名融合(reciprocal rank fusion,RRF)将各检索器彼此不兼容的结果列表合并。对每一份文档,只要它出现在某个列表中,我们就加上 weight / (60 + rank),默认权重为 1.0,平滑常数为 60。

平滑常数使多方共识比一次强投票更重要:一份在多个检索器中都靠前出现的文档,可以胜过只在其中一个检索器排名第一的文档。然后,我们把重复的数据块合并回一个来源,限制每个文件可贡献的结果数,最终得到更多样的前二十个结果。
我们将原始查询和这些候选项发送给一个小型重排序模型。它为每份文档打 0 到 10 分,我们保留前十项。(6)
排名确定后,我们会把上下文补回胜出的结果。例如,若匹配到 wiki 的一个章节,就会拉入相邻的两个章节,避免因切块而拆散的标题、前置条件和注意事项丢失。这样读者拿到的是完整片段,而不是缺少重要上下文的孤立段落。
因此,检索的输出是一个丰富的证据包:来自不同检索器并经融合的结果,在来源级别去重,针对实际问题重排序,最后再扩展周边上下文。
MCP
在 MCP 集成中,我们将检索构件直接暴露为工具,而不是把它们隐藏在一个“回答这个问题”的端点后面。这些工具有意保持简单,并尽可能不依赖 LLM,从而让客户端能够快速、低成本地查询。(5)
每个 MCP 工具都对应一个底层检索原语,例如 search_slack、search_code、search 或 who_knows。工具的输入和输出范围窄、结构化且稳定,因此任何客户端或智能体都能轻松调用,而无需把额外的编排逻辑塞进工具本身。
大多数工具运行一条查询管线,例如向量检索、词法检索或 ripgrep,应用轻量评分启发式规则,并返回原始证据行。
Claude Code 或任何兼容 MCP 的智能体,会成为编排引擎:决定调用哪些工具、以什么顺序调用,以及如何将结果组装为最终答案或代码改动。检索层本身无需依赖这些 LLM 决策,也能提供请求服务。
Web UI
Web UI 中也存在同样的工具,但它们被连接到一个完整的查询管线,会对每个用户问题端到端运行。UI 智能体负责规划器和执行器两个步骤。
规划器:轻量 LLM 会检查查询和当前项目,然后选择要调用的检索工具,例如 search、search_slack 和 subsystem_index。
执行器:系统将这些工具调用并发扇出,收集结果,并将它们规范化为共享证据模式,其中包含分数、时效性和来源提示。
综合:最终的 LLM 会接收带类型的证据包和原始问题,然后生成 UI 中展示的答案,包括引文、注意事项和跨来源综合。
从用户角度,Web UI 只是“提出一个问题,然后得到答案”。但在底层,它运行的是同一种 planner → executor → synthesizer 模式,MCP 客户端可以显式地复现它。

组织
随着语料库增长,“搜索所有地方的一切内容”很快就不再有用。编译器团队的工程师不希望在结果中看到基础设施运行手册,反之亦然。项目是我们让检索默认保持相关性的方式。
项目与作用域检索
我们将项目引入为组织查询工作区的主要方式。一个项目是具名的数据源组合:与某团队或计划相关的特定 Slack 频道、代码仓库、内部数据库和文档空间。
项目有意保持轻量。共享事故频道或中央平台仓库等同一个数据源,可以被多个项目引用,而无需重复。

接入与默认设置
在接入过程中,用户会被引导选择或创建一个符合其工作方式的默认项目,例如机器学习训练基础设施、编译器或数据中心运维。
默认项目会存储在用户档案中,并自动限定查询范围。新工程师无需先了解哪些 Slack 频道、仓库或文档空间重要,也能获得高信噪比的答案。
最后的思考
归根结底,这个知识库之所以有效,是因为它让信息留在人们本来就在使用的地方,而不是强迫所有内容进入一个僵化系统。通过组合多种检索技术,我们能够迅速呈现证据。最终得到的检索体验,既足够灵活以适应真实的公司数据,也足够结构化,能在 Cerebras 持续成长时保持有用。
如果你读到了这里,并且对此感兴趣,AI/Growth 团队正在招聘;有兴趣请联系 @learnwdaniel。
参考资料
- Malkov 和 Yashunin,使用分层可导航小世界图进行高效且稳健的近似最近邻搜索,arXiv:1603.09320 / IEEE TPAMI 2018。
- Anthropic,介绍上下文检索,2024。
- Cormack、Clarke 和 Büttcher,倒数排名融合优于 Condorcet 与单项排序学习方法,SIGIR 2009。
- Li 等,Search-o1:由智能体驱动、以检索增强的大型推理模型,arXiv:2501.05366,2025。
- Anthropic,使用 MCP 执行代码,2025。
- Liu 等,迷失在中间:语言模型如何使用长上下文,arXiv:2307.03172,2023。
- Anthropic,使用 XML 标签。
- Salesforce/Slack Engineering,如何让 Slack AI 处理数十亿条消息。
- Improving Agents,最佳嵌套数据格式。
- Cursor,用语义检索改进智能体,2025。