저자: @hi_im_isaac_, @learnwdaniel, @gaozenghao
참고: 전체 기술 블로그의 인터랙티브 버전은 다음에서 확인할 수 있습니다: https://www.cerebras.ai/blog/how-we-built-our-knowledge-base
직원들은 매일 내부 지식 베이스에 15,000개 이상의 질문을 합니다. 3개월 전 출시 이후, 이 도구는 회사에서 가장 널리 채택된 내부 도구 중 하나가 되었습니다. 사람, 자동화 시스템, 그리고 에이전트가 함께 사용하고 있습니다.
Cerebras 에서는 팀들이 데이터 센터 운영, 칩 설계, 하드웨어, 트레이닝, 추론, 클라우드 플랫폼 등 다양한 분야에서 작업합니다. 매년 수백 명의 신규 직원이 합류하면서, 커뮤니케이션 채널은 동일한 질문으로 가득 차기 시작했습니다.
- "X 는 어디서 찾을 수 있나요?"
- "Y 분야의 전문가는 누구인가요?"
- "Z 가 무엇인가요?"

우리는 사람과 시스템을 유용한 정보에 연결해주는 Cerebras Knowledge 를 구축했습니다.
데이터가 있는 곳에서 만나기
조직 내에서 정보를 찾는 것은 어렵습니다. 데이터는 여러 도구에 흩어져 있고, 분기마다 누군가는 똑같은 훌륭한 해결책을 제안합니다: 모든 정보를 한 곳에 기록하기 위해 하나의 플랫폼에 모든 것을 저장하자는 것입니다. 물론, 단일 진실 공급원이라는 꿈은 현실에서 거의 작동하지 않습니다.
정보는 사용하기 편리하고 인체공학적인 곳에서 생성됩니다: 문서의 제안된 편집, Slack 의 스레드, GitHub 의 코드 참조, Jira 의 상태 메타데이터 등이 그 예입니다. 이러한 플랫폼은 각자의 특정 영역에 맞춰 제작되었으며, 수년간의 제품 엔지니어링과 분석을 통해 최적화되었습니다. Google Docs 에서 풀 리퀘스트를 논의하는 것은 끔찍한 경험이 될 것입니다.
따라서 우리는 기존 행동 방식을 최소한으로 변경하는 시스템을 설계하기로 했습니다. 데이터 수집 측면에서는 각 플랫폼에서 직접 데이터를 추출하는 방식을 의미했습니다.
지식 베이스의 구조
우리의 지식 베이스는 세 가지를 제공합니다:
- 내부 데이터를 수집하고 저장하는 플랫폼.
- 해당 데이터를 쿼리하는 플랫폼.
- 감사 및 분석 기능과 함께 인증 및 권한 부여를 적용하는 계층.
핵심에는 다양한 소스의 임베딩, 원본 요약, 메타데이터를 보유하는 단일 Postgres 테이블이 있습니다. 시스템은 회사 전체의 데이터를 지속적으로 수집하고 쿼리 가능한 데이터 저장소를 유지 관리합니다.
우리는 간단하면서도 대부분의 데이터 형태와 호환되는 데이터 인터페이스를 원했습니다. 또한 Cerebras 의 다른 개발자들이 맞춤형 커넥터를 구축할 수 있기를 바랐습니다. 그 결과는 의도적으로 단순합니다: Slack 스레드부터 넷리스트까지 모든 소스는 동일한 임베딩 테이블에 저장되며, 해당 테이블의 모든 데이터는 동일한 인터페이스를 통해 즉시 쿼리 가능합니다:

각 데이터 소스는 데이터가 무엇인지, 어떻게 연결하는지, 얼마나 자주 가져와야 하는지를 정의합니다. 결과 임베딩 행은 Slack, 코드 저장소, 문서 시스템 또는 맞춤형 데이터베이스 중 어디에서 왔든 동일한 인터페이스를 따릅니다.
비정형 Slack 대화 처리 방법
Slack 은 우리가 설계해야 했던 가장 중요한 데이터 소스였습니다. 회사 전반에서 가장 최신의 엔지니어링 논의가 이루어지는 곳이기 때문입니다.

처음에는 원시 텍스트에 대한 단순 임베딩만으로도 충분히 잘 작동하는지 테스트했습니다. 그러나 벡터 검색만으로는 관련 데이터를 모두 매칭하기에 충분하지 않다는 것을 곧 깨달았습니다.
Slack 메시지는 몇 가지 문제점을 제시합니다:
- 정보 밀도가 매우 다양합니다: "어 그래 좋아 마이크"와 상세한 커널 설명은 모두 메시지입니다.
- 메시지 길이가 다양하며, 짧은 메시지가 코사인 유사도에서 더 길고 상세한 메시지를 자주 이깁니다.
- 메시지의 의미는 종종 주변 대화에 따라 달라집니다.
우리는 하이브리드 접근 방식이 필요했습니다. 모든 스레드가 여러 검색 기술을 통해 동시에 검색 가능하도록 Slack 수집을 구축했으며, 각 기술은 다른 기술의 약점을 보완합니다:
- 전문 검색은 임베딩이 흐릿하게 만드는 정확한 토큰을 포착합니다: 오류 문자열, 플래그 이름, 호스트 이름. 엔지니어가 문자 그대로의 오류 메시지를 붙여넣을 때, 정확한 어휘 일치가 거의 항상 최고의 증거이며, 어떤 의미적 유사성도 이를 능가할 수 없습니다.
- 임베딩 검색은 의역을 포착합니다. "매니페스트 로드 후 복원이 중단됨"이라고 묻는 사람과 "NFS 마운트에서 체크포인트가 멈춤"이라고 답한 사람은 어휘를 공유하지 않을 수 있습니다. 벡터 유사성은 질문을 다른 단어로 작성된 답변에 연결하는 것입니다.(1)
- 역문서 빈도는 신호와 잡음을 분리합니다. 희귀 토큰(예: 모호한 설정 플래그)을 중심으로 구성된 짧은 메시지는 순위가 매겨져야 합니다. "좋아요, 감사합니다!"는 임베딩 공간에서 많은 쿼리와 가깝지만, 용어 희소성을 고려하면 점수가 거의 0에 가깝습니다.
- 시간 감쇠는 Slack 답변의 유효 기간을 인코딩합니다. 두 개의 스레드가 동일한 질문에 답할 수 있으며, 6개월 전의 스레드는 더 이상 존재하지 않는 인프라를 설명할 수 있습니다. 관련성이 동일할 경우, 최신 스레드가 승리합니다.

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

이러한 데이터 포인트를 임베딩하고 공유 임베딩 테이블에 씁니다. 원본 대본은 직접 임베딩되지 않습니다. 실험 결과, 스레드가 일관된 형식으로 정규화될 때 정확도가 크게 향상되었습니다.(7,9) 추가 메타데이터는 의미적 일치에 더 유용한 신호를 제공합니다.
버스팅
이 시점에서 Slack 검색은 훌륭했지만, 동일한 문제에 계속 직면했습니다: 긴 스레드 내의 중요한 메시지가 항상 스레드 수준 요약에 표현되지는 않는다는 점이었습니다.
개별 메시지의 신호를 강화하기 위해 버스팅을 사용합니다. 버스트는 동일한 작성자의 연속적인 메시지 실행입니다. 스레드 주제를 컨텍스트로 추가하여 개별 버스트를 임베딩합니다(2). 때로는 답변이 스레드 요약에 절대 포함되지 않는 어휘를 가진 하나의 엉뚱한 메시지에 있기 때문입니다. 버스트 임베딩은 해당 메시지를 자체적으로 찾을 수 있게 만듭니다.
낮은 신호 데이터가 데이터베이스에 도달하는 것을 방지하기 위해, 각 버스트는 가중치가 적용된 신호 조합에 대해 점수가 매겨지고 임베딩되기 전에 임계값을 통과해야 합니다:
- 코퍼스 전체에서 상대적으로 희귀한 토큰을 포함하며, IDF 가 4.0 이상입니다.
- 결합된 버스트는 최소 200자입니다.
- 버스트 내 하나 이상의 메시지에 반응이 있어 사회적 부스트를 제공합니다.

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

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

재순위화
문서는 다른 질문에 답변하면서 쿼리와 어휘를 공유하기 때문에 상위에 표시될 수 있습니다. 재순위화 전에, 상호 순위 융합(RRF)을 사용하여 검색기의 호환되지 않는 결과 목록을 결합합니다. 각 문서에 대해, 문서가 나타나는 각 목록에 대해 가중치 / (60 + 순위)를 추가하며, 기본 가중치는 1.0이고 평활 상수는 60입니다.

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

조직화
코퍼스가 성장함에 따라 "모든 것을 모든 곳에서 검색"하는 것은 빠르게 유용하지 않게 되었습니다. 컴파일러 팀의 엔지니어는 인프라 런북을 결과에서 원하지 않았으며, 그 반대도 마찬가지였습니다. 프로젝트는 검색을 기본적으로 관련성 있게 만드는 방법입니다.
프로젝트 및 범위 검색
우리는 쿼리가 실행되는 작업 공간을 구성하는 기본 방법으로 프로젝트를 도입했습니다. 프로젝트는 팀이나 이니셔티브와 관련된 특정 Slack 채널, 코드 저장소, 내부 데이터베이스 및 문서 공간과 같은 데이터 소스의 명명된 번들입니다.
프로젝트는 의도적으로 가볍습니다. 공유 인시던트 채널이나 중앙 플랫폼 저장소와 같은 동일한 데이터 소스는 복제되는 대신 여러 프로젝트에서 참조될 수 있습니다.

온보딩 및 기본값
온보딩 중에 사용자는 자신의 작업 방식(예: ML 트레이닝 인프라, 컴파일러 또는 데이터 센터 운영)과 일치하는 기본 프로젝트를 선택하거나 생성하라는 메시지가 표시됩니다.
해당 기본 프로젝트는 사용자 프로필에 저장되며 쿼리 범위를 자동으로 지정합니다. 신입 엔지니어는 어떤 Slack 채널, 저장소 또는 문서 공간이 중요한지 먼저 배울 필요 없이 높은 신호의 답변을 얻을 수 있습니다.
마무리 생각
결국, 지식 베이스가 작동하는 이유는 모든 것을 하나의 경직된 시스템에 강제로 집어넣는 대신, 정보가 이미 있는 곳에서 사람들을 만나기 때문입니다. 다양한 검색 기술을 결합하여 증거를 신속하게 표면화할 수 있습니다. 그 결과는 실제 회사 데이터에 충분히 유연하면서도 Cerebras 가 계속 성장함에 따라 유용성을 유지할 수 있을 만큼 구조화된 검색 경험입니다.
여기까지 읽으셨고 이 내용이 흥미로우시다면, ai/growth 팀에서 채용 중입니다. 관심이 있으시면 @learnwdaniel 로 연락주세요.
참고 문헌
- Malkov 및 Yashunin, Efficient and Robust Approximate Nearest Neighbor Search Using Hierarchical Navigable Small World Graphs, arXiv:1603.09320 / IEEE TPAMI 2018.
- Anthropic, Introducing Contextual Retrieval, 2024.
- Cormack, Clarke 및 Büttcher, Reciprocal Rank Fusion Outperforms Condorcet and Individual Rank Learning Methods, SIGIR 2009.
- Li et al., Search-o1: Agentic Search-Enhanced Large Reasoning Models, arXiv:2501.05366, 2025.
- Anthropic, Code Execution with MCP, 2025.
- Liu et al., Lost in the Middle: How Language Models Use Long Contexts, arXiv:2307.03172, 2023.
- Anthropic, Use XML Tags.
- Salesforce/Slack Engineering, How Slack AI Processes Billions of Messages, 2024.
- Improving Agents, Best Nested Data Format, 2025.
- Cursor, Improving Agent with Semantic Search, 2025.





