이 요약은 Cerebras가 사내 지식 베이스를 어떻게 구축했는지 상세히 설명합니다. 이 시스템은 3개월 만에 사내에서 가장 널리 사용되는 도구 중 하나가 되었으며, 직원들이 매일 15,000개 이상의 질문에 답하며 정보의 분산을 해결하고 있습니다. 특히, 기존 행동 변화를 최소화하며 다양한 데이터 소스를 통합하고, Slack 대화와 코드 저장소와 같은 비정형 데이터를 효율적으로 처리하는 하이브리드 검색 접근 방식과 랭킹 재조정(reranking) 기법을 활용하여 정확하고 유용한 정보를 제공하는 데 중점을 두었습니다.
1. 사내 지식 베이스 구축 배경 💬
Cerebras는 데이터 센터 운영, 칩 설계, 하드웨어, 교육, 추론, 클라우드 플랫폼 등 다양한 분야에서 활동하는 회사인데요. 매년 수백 명의 신입 직원이 합류하면서, "X는 어디서 찾을 수 있나요?", "Y 분야 전문가는 누구인가요?", "Z가 무엇인가요?" 같은 질문들이 내부 커뮤니케이션 채널에 넘쳐나기 시작했어요. 😮 이런 문제들을 해결하기 위해 Cerebras Knowledge라는 지식 베이스를 구축하게 되었답니다. 이 시스템은 사람들이 정보와 시스템에 쉽게 연결되도록 돕는 것을 목표로 삼았어요.
2. 분산된 데이터의 통합 🌐
정보를 찾는 것이 조직 내부에서는 정말 어려운 일이에요. 데이터가 여러 도구에 흩어져 있기 때문이죠. 주기적으로 "모든 정보를 한 플랫폼에 기록하자!"는 제안이 나오곤 하지만, 현실에서는 '단일 정보원(Single Source of Truth)'이라는 꿈이 잘 실현되지 않아요. 정보는 슬랙 스레드, 깃허브 코드, 지라 상태 메타데이터처럼 각자의 목적에 최적화된 플랫폼에서 생성되기 때문인데요. 이런 점을 고려해 Cerebras는 기존 행동 변화를 최소화하는 시스템을 설계하는 데 주력했어요. 즉, 각 플랫폼에서 직접 데이터를 추출하는 방식을 선택한 거죠.
3. 지식 베이스의 구조 🏗️
Cerebras Knowledge는 크게 세 가지 기능을 제공해요.
- 내부 데이터를 수집하고 저장하는 플랫폼
- 데이터를 쿼리하는 플랫폼
- 인증 및 권한 부여를 적용하고 감사 및 분석을 제공하는 계층
이 시스템의 핵심은 임베딩, 원시 요약, 그리고 다양한 소스에서 오는 메타데이터를 담고 있는 단일 Postgres 테이블이에요. 이 테이블은 회사 전체의 데이터를 지속적으로 수집하며 쿼리 가능한 데이터 저장소를 유지합니다.
데이터 인터페이스는 간단하면서도 대부분의 데이터 형식에서 작동하도록 설계되었어요. 또한, 다른 개발자들이 쉽게 맞춤형 커넥터를 만들 수 있도록 했죠. 결과적으로 모든 소스(Slack 스레드부터 넷리스트까지)가 동일한 임베딩 테이블에 저장되고, 이 테이블의 모든 데이터는 동일한 인터페이스를 통해 즉시 쿼리할 수 있게 되었습니다.

각 데이터 소스는 데이터의 종류, 연결 방법, 그리고 얼마나 자주 데이터를 가져와야 하는지를 정의해요. 이렇게 생성된 임베딩 행은 Slack, 코드 저장소, 문서 시스템, 맞춤형 데이터베이스 등 어디서 왔든 동일한 인터페이스를 따릅니다.
4. Slack 데이터 처리 방법 🗣️
Slack은 가장 중요한 데이터 소스 중 하나였어요. 회사 내에서 가장 활발하고 최신 엔지니어링 논의가 이루어지는 곳이기 때문이죠.
4.1. 비정형 Slack 대화 처리 과정 📝
처음에는 단순히 원시 텍스트에 대한 임베딩만으로 충분한지 테스트했지만, 곧 벡터 검색만으로는 모든 관련 데이터를 일치시키기 어렵다는 것을 깨달았어요. Slack 메시지에는 다음과 같은 문제들이 있었거든요.
- 정보 밀도가 매우 다양해요. "응, 알았어 마이크"와 자세한 커널 설명이 모두 메시지죠.
- 메시지 길이가 다양하고, 짧은 메시지가 긴 메시지보다 코사인 유사도에서 더 높은 순위를 차지하는 경우가 많았어요.
- 메시지의 의미가 주변 대화에 따라 달라지는 경우가 많았습니다.
그래서 하이브리드 접근 방식이 필요하다고 판단했어요. 모든 스레드가 여러 검색 기법을 통해 동시에 검색될 수 있도록 Slack 수집 시스템을 구축했는데, 각 기법은 다른 기법의 약점을 보완해줍니다.
- 전문 검색 (Full-text search): 임베딩이 흐리게 처리할 수 있는 정확한 토큰(오류 문자열, 플래그 이름, 호스트 이름)을 찾아냅니다. 엔지니어가 문자 그대로의 오류 메시지를 붙여넣을 때, 정확한 어휘 일치는 거의 항상 최고의 증거가 됩니다.
- 임베딩 검색 (Embedding search): 의역을 찾아냅니다. "매니페스트 로드 후 복원이 멈춘다"고 묻는 사람과 "체크포인트가 NFS 마운트에서 멈춘다"고 답하는 사람이 같은 어휘를 사용하지 않을 수 있죠. 벡터 유사도가 질문과 다른 단어로 쓰인 답변을 연결해주는 역할을 합니다.
- 역문서 빈도 (Inverse document frequency, IDF): 신호와 불필요한 정보를 분리합니다. 드문 토큰(예: 잘 알려지지 않은 설정 플래그)으로 구성된 짧은 메시지는 순위가 높아져야 합니다. "좋아요, 감사합니다!"는 임베딩 공간에서 많은 쿼리와 가깝지만, 용어 희귀도를 고려하면 점수가 거의 0이 됩니다.
- 수명 감쇠 (Age decay): Slack 답변이 시간이 지남에 따라 만료됨을 인코딩합니다. 두 스레드가 같은 질문에 답할 수 있지만, 6개월 전의 스레드는 더 이상 존재하지 않는 인프라를 설명할 수도 있죠. 관련성이 동일하다면 최신 스레드가 더 높은 순위를 차지합니다.

어떤 단일 스코어도 단독으로 신뢰할 수 없어요. 각 기법은 동일한 코퍼스에 대한 자체 순위 뷰를 생성하고, 이 뷰들은 쿼리 시점에 병합됩니다.
4.2. Socket Mode를 이용한 실시간 데이터 수집 ⚡
실시간으로 데이터를 수집하기 위해, Slack 봇을 작업 공간에 설치하고 Socket Mode로 실행했어요. Slack은 모든 메시지 이벤트를 영구 WebSocket을 통해 Cerebras로 푸시하여, API를 폴링하고 속도 제한에 걸리지 않고도 실시간 업데이트를 받을 수 있게 했습니다.
이벤트가 도착하면 즉시 이를 확인하고, 안정적인 이벤트 ID를 사용하여 중복을 제거하며, 메시지를 수집 소비자가 처리하도록 표시합니다.
수집 소비자는 새 메시지를 단독으로 저장하지 않아요. 대신, 메시지가 속한 스레드를 확인하고, 부모 메시지와 모든 답글을 포함한 전체 대화를 Slack API에서 다시 가져옵니다. 그리고 전체 스레드를 하나의 행으로 다시 작성하죠. 따라서 기존 스레드에 대한 답글은 부모와 모든 형제 메시지를 다시 가져와 저장된 콘텐츠, 참여자 목록, 그리고 마지막 활동 타임스탬프가 항상 완전한 대화를 반영하도록 합니다.
시스템 내의 모든 Slack 채널은 자체 데이터 소스를 가지고 있어요. 이를 통해 데이터 신선도에 대한 세밀한 조정이 가능합니다. 예를 들어, 팀은 자주 사용되는 인시던트 채널을 더 자주 수집하도록 선택할 수 있습니다.
4.3. 스레드 및 메시지 처리 🔄
원시 Slack 텍스트는 내용에 대한 Postgres 전문(GIN) 인덱스를 유지하므로 도착하는 즉시 키워드 검색이 가능해요. 하지만 유용한 벡터 검색을 가능하게 하려면 몇 가지 추가적인 처리가 필요했습니다.
증류(distillation) 과정에서 LLM이 전체 스레드에서 구조화된 데이터를 추출해요.
- 엔지니어가 실제로 검색할 만한 한 줄 질문
- 짧은 요약
- 해결책 (resolution)
- 언급된 시스템 및 코드 참조

이 데이터 포인트들을 임베딩하고 공유 임베딩 테이블에 작성합니다. 원본 대화록은 직접 임베딩되지 않아요. Cerebras의 실험에서는 스레드가 일관된 형식으로 정규화되었을 때 정확도가 크게 향상되었다고 합니다. 추가 메타데이터는 의미론적 일치에 더 유용한 신호를 제공하죠.
4.4. 버스팅 (Bursting) 💥
이 시점에서 Slack 검색은 괜찮았지만, 긴 스레드 내의 중요한 메시지가 스레드 수준 요약에 항상 잘 표현되지 않는다는 문제가 계속 발생했어요.
개별 메시지의 신호를 강화하기 위해 버스팅을 사용합니다. 버스트는 동일한 작성자가 연속적으로 보낸 메시지 묶음이에요. 스레드 주제를 컨텍스트로 추가하여 개별 버스트를 임베딩합니다. 때로는 답변이 스레드 요약에 포함되지 않는 한두 개의 벗어난 메시지에 있을 수 있기 때문이죠. 버스트 임베딩은 해당 메시지를 단독으로 찾을 수 있게 해줍니다.
신호가 낮은 데이터가 데이터베이스에 도달하는 것을 방지하기 위해, 각 버스트는 신호의 가중 조합에 따라 점수를 매기고, 임베딩되기 전에 특정 임계값을 넘어야 합니다.
- 코퍼스 전체에서 상대적으로 드문 토큰을 포함하며, IDF가 4.0 이상이어야 합니다.
- 결합된 버스트가 최소 200자 이상이어야 합니다.
- 버스트 내의 하나 이상의 메시지에 반응(reactions)이 포함되어 사회적 부스트를 제공합니다.

증류 후, 자격을 갖춘 버스트는 임베딩되어 스레드 수준 기록과 함께 임베딩 테이블에 저장됩니다.
5. 코드 저장소 처리 💻
처음에는 코드 저장소를 임베딩하는 것이 필요한지에 대해 논의했어요. Claude Code와 다른 명령줄 도구의 등장으로, "grep이면 충분하다"고 생각될 때 코드 임베딩을 만드는 것이 직관적이지 않게 느껴졌죠. 하지만 업계의 다른 사람들과 이야기하고 대규모 코드베이스에서의 의미론적 검색에 대한 Cursor의 연구 결과를 읽은 후, 시도해 보기로 결정했습니다.
Cerebras에는 40GB가 넘는 대규모 내부 저장소가 많이 있었고, 이를 효율적으로 최신 상태로 유지하는 것이 주요 관심사였습니다.
5.1. CocoIndex를 이용한 코드 임베딩 유지 관리 🚀
여러 실험 끝에, 코드베이스 벡터화에 특화된 오픈소스 문서 임베딩 프레임워크인 CocoIndex를 사용하게 되었습니다.
각 저장소에 대해, 언어별 정규 표현식 경계를 사용하여 코드를 분할했습니다. 이 분할기는 먼저 클래스와 같은 높은 수준의 경계를 시도하고, 결과 청크가 여전히 너무 크면 메서드 경계, 그리고 더 작은 블록으로 나눕니다. 이렇게 생성된 청크를 임베딩하고 벡터를 Postgres에 씁니다. 단일 파일이 파일 수준 및 함수 수준 기록과 같이 다양한 세부 수준에서 여러 임베딩을 생성할 수 있습니다.

CocoIndex는 Postgres에서 동기화 메타데이터를 추적해요. 각 커밋마다 전체 저장소를 다시 계산하는 대신, 변경된 코드 청크만 다시 임베딩하고 내보냅니다. 이는 동기화 상태와 임베딩 저장소가 동일한 데이터베이스에 있기 때문에 특히 효과적이었습니다.
코드베이스 수가 증가함에 따라, 저장소 온보딩을 팀이 직접 제출할 수 있는 구성 파일로 옮겼는데, 여기에는 파일 경로 수준에서의 허용 목록(allowlists)과 거부 목록(denylists)이 포함됩니다.
6. 맞춤형 데이터 소스 🗄️
일부 팀은 이미 자체 데이터베이스를 가지고 있었고, 지식 베이스에 참여하기 위해 데이터를 Slack이나 문서 시스템으로 옮기고 싶어 하지 않았어요. 그들은 기존 테이블에 동일한 쿼리 인터페이스를 원했죠.
이를 지원하기 위해 맞춤형 소스를 플러그인 스크립트로 취급했습니다. 팀은 자신의 시스템에서 읽어와 임베딩 테이블과 같은 형태로 행을 내보내고, 일치하는 데이터 소스 항목을 추가하는 작은 Python 모듈로 풀 리퀘스트를 엽니다.
스크립트가 다른 모든 임베딩 행과 동일한 스키마를 사용하여 공유 데이터베이스에 기록하는 한, 나머지 스택은 변경 없이 작동합니다. 데이터는 Slack, 코드, 문서와 함께 쿼리 가능해지며, 시스템의 다른 곳에서는 특별한 처리가 필요하지 않습니다.
7. 계획 수립 및 도구 분산 🗺️
모든 쿼리에 대해, 먼저 LLM이 어떤 도구와 데이터 소스가 중요할지 결정하는 짧은 계획 수립(planning) 단계를 거칩니다. 주요 도구들은 다음과 같아요.
subsystem_index: 파일별 LLM 요약search: Slack, 위키, 코드 및 기타 인덱싱된 소스를 통합하고 내부적으로 재랭킹하는 통합 벡터 파이프라인search_slack: 직접적인 Slack 검색search_code: 소스 저장소에 대한 ripgreprecent_prs: 질문과 관련된 최근 풀 리퀘스트who_knows: 특정 주제에 대한 전문성을 입증한 사람
플래너는 인덱싱된 내용에 대한 간결한 설명을 기반으로 작동합니다. 여기에는 어떤 프로젝트가 존재하고, 각 프로젝트에서 어떤 소스를 사용할 수 있으며, 각 소스가 어떤 질문에 답하는 데 유용한지 등이 포함됩니다. 사용자의 쿼리와 현재 범위를 고려하여, 플래너는 실행자가 병렬로 분산하여 실행하고, 공통 증거 형식으로 정규화한 다음 최종 합성 LLM에 전달하는 도구 선택을 내보냅니다.

8. 랭킹 재조정 (Reranking) ⚖️
문서가 다른 질문에 답하면서도 쿼리와 어휘를 공유한다는 이유만으로 상위권에 나타날 수 있어요. 그래서 재랭킹 전에, 검색기들의 호환되지 않는 결과 목록을 상호 순위 융합(Reciprocal Rank Fusion, RRF)으로 결합합니다. 각 문서에 대해, 나타나는 각 목록에 가중치 / (60 + 순위)를 추가하며, 기본 가중치는 1.0, 평활화 상수(smoothing constant)는 60입니다.

평활화 상수는 단일 강력한 투표보다 합의가 더 중요하도록 만들어요. 여러 검색기에서 상위권에 나타나는 문서는 하나의 검색기에서 1위를 차지하는 문서보다 더 높은 점수를 받을 수 있습니다. 그런 다음 중복 청크를 다시 하나의 소스로 병합하고, 각 파일이 기여할 수 있는 결과 수를 제한하여 더 다양하고 상위 20개의 결과를 얻습니다.
원본 쿼리와 이러한 후보들을 작은 재랭커 모델에 보냅니다. 이 모델은 각 문서에 0에서 10까지의 점수를 부여하고, 상위 10개를 유지합니다.
순위가 최종 결정되면, 우승자들에게 다시 컨텍스트를 추가합니다. 예를 들어, 위키 섹션을 일치시키면 인접한 두 섹션을 가져와서 청크 분할로 인해 손실되었던 제목, 전제 조건 및 주의 사항이 손실되지 않도록 합니다. 이는 독자들에게 중요한 컨텍스트가 누락된 외로운 단락 대신 완전한 스니펫을 제공합니다.
따라서 검색의 결과는 풍부한 증거 패킷입니다. 즉, 서로 다른 검색기에서 융합되고, 소스 수준에서 중복 제거되며, 실제 질문에 대해 재랭킹된 다음 주변 컨텍스트로 확장된 결과들이죠.
9. MCP (Micro Code Platform) 🛠️
MCP 통합에서는 검색 구성 요소를 "이 질문에 답하세요"라는 단일 엔드포인트 뒤에 숨기지 않고 직접적인 도구로 노출합니다. 이러한 도구들은 의도적으로 간단하며 LLM에 최대한 의존하지 않아, 클라이언트가 빠르고 저렴하게 쿼리할 수 있도록 합니다.
각 MCP 도구는 search_slack, search_code, search, 또는 who_knows와 같은 하나의 기본 검색 원시(primitive)에 해당합니다. 도구의 입력과 출력은 제한적이고, 구조화되어 있으며, 안정적이어서 도구 자체 내에 추가 오케스트레이션 로직을 임베딩하지 않고도 어떤 클라이언트나 에이전트에서도 쉽게 호출할 수 있습니다.
대부분의 도구는 벡터 검색, 어휘 검색 또는 ripgrep과 같은 하나의 쿼리 파이프라인을 실행하고, 경량 점수 휴리스틱을 적용하며, 원시 증거 행을 반환합니다.
Claude Code 또는 MCP 호환 에이전트는 오케스트레이션 엔진이 됩니다. 어떤 도구를 어떤 순서로 호출할지, 그리고 결과를 최종 답변이나 코드 편집으로 어떻게 조합할지 결정하죠. 검색 계층 자체는 요청을 처리하기 위해 이러한 LLM 결정에 의존하지 않습니다.
10. 웹 UI 💻
웹 UI에서는 동일한 도구들이 존재하지만, 모든 사용자 질문에 대해 처음부터 끝까지 실행되는 완전한 쿼리 파이프라인에 연결되어 있어요. UI 에이전트는 플래너 및 실행자 단계를 소유합니다.
- 플래너 (Planner): 경량 LLM 패스가 쿼리와 활성 프로젝트를 검사한 다음,
search,search_slack,subsystem_index와 같은 어떤 검색 도구를 호출할지 선택합니다. - 실행자 (Executor): 시스템은 이러한 도구 호출을 병렬로 분산하고, 결과를 수집하며, 점수, 최근성, 소스 힌트가 포함된 공유 증거 스키마로 정규화합니다.
- 합성 (Synthesis): 최종 LLM 패스가 유형화된 증거 번들과 원본 질문을 받아 UI에 표시될 답변을 생성합니다. 여기에는 인용, 주의 사항 및 교차 소스 합성이 포함됩니다.
사용자 관점에서 웹 UI는 단순히 "질문하고 답변을 얻는" 것으로 보이지만, 내부적으로는 MCP 클라이언트가 명시적으로 재현할 수 있는 동일한 플래너 → 실행자 → 합성기 패턴을 실행합니다.

11. 조직화 📊
코퍼스가 커지면서 "모든 것을 어디서든 검색"하는 것은 빠르게 쓸모없게 되었어요. 컴파일러 팀 엔지니어들은 검색 결과에 인프라 런북이 포함되는 것을 원치 않았고, 그 반대도 마찬가지였죠. 그래서 프로젝트는 검색을 기본적으로 관련성 있게 만드는 주요 방법이 되었습니다.
11.1. 프로젝트 및 범위 지정 검색 🔎
Cerebras는 쿼리가 실행되는 작업 공간을 조직화하는 주요 방법으로 프로젝트를 도입했어요. 프로젝트는 특정 Slack 채널, 코드 저장소, 내부 데이터베이스, 그리고 팀이나 이니셔티브와 관련된 문서 공간과 같은 데이터 소스의 명명된 묶음입니다.
프로젝트는 의도적으로 경량으로 설계되었어요. 공유 인시던트 채널이나 중앙 플랫폼 저장소와 같은 동일한 데이터 소스는 중복되지 않고 여러 프로젝트에서 참조될 수 있습니다.

11.2. 온보딩 및 기본값 설정 🤝
온보딩 중에 사용자들은 ML 훈련 인프라, 컴파일러, 데이터 센터 운영과 같이 자신의 작업 방식과 일치하는 기본 프로젝트를 선택하거나 생성하도록 안내받습니다.
이 기본 프로젝트는 사용자 프로필에 저장되어 쿼리 범위를 자동으로 지정합니다. 새로운 엔지니어는 어떤 Slack 채널, 저장소, 또는 문서 공간이 중요한지 먼저 배울 필요 없이 높은 신호의 답변을 얻을 수 있게 됩니다.
12. 마치며 ✨
결론적으로, Cerebras의 지식 베이스는 모든 것을 하나의 경직된 시스템에 강요하는 대신, 정보가 이미 존재하는 곳에서 사람들을 만남으로써 작동합니다. 다양한 검색 기법을 결합함으로써 증거를 빠르게 찾아낼 수 있죠. 그 결과, 실제 회사 데이터에 충분히 유연하면서도, Cerebras가 계속 성장함에 따라 유용성을 유지할 만큼 구조화된 검색 경험을 제공하게 되었습니다.
