- Published on
사내 지식베이스를 LLM으로 만든다는 것 — 권한 인지 검색, 신선도, 그리고 출시 전 평가셋
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 들어가며 — 하루 만오천 건을 받는 사내 검색이 실제로 푸는 문제
- Cerebras가 공개한 구조 — 좁은 허리 하나와 여섯 개의 도구
- 권한 인지 검색 — HR 문서를 흘리는 검색은 없는 것만 못하다
- 신선도와 삭제 전파 — 인덱스는 진실을 늦게 배운다
- 위키와 티켓이 서로 다른 말을 할 때
- 출시 전에 만들어야 하는 평가셋
- 조직 단위로 스코프를 자르는 일
- 마치며 — 검색 품질은 나중에 고칠 수 있지만 유출은 되돌릴 수 없다
들어가며 — 하루 만오천 건을 받는 사내 검색이 실제로 푸는 문제
2026년 7월 15일, Cerebras가 사내 지식베이스를 어떻게 만들었는지 공개했습니다(How we built our knowledge base, GeekNews 정리). 출시 석 달 만에 하루 15,000건 넘는 질문을 받는 사내 도구가 됐고, 인당으로 환산하면 하루 약 15건입니다. 눈에 띄는 대목은 질의자의 구성입니다 — 사람만이 아니라 자동화 스크립트와 에이전트가 같은 엔드포인트에 묻습니다.
먼저 이 시스템이 실제로 푸는 문제를 정확히 짚고 넘어가는 게 좋겠습니다. 사내 검색의 문제는 "문서가 없다"가 아닙니다. 대개는 이렇습니다 — 답은 6개월 전 어느 Slack 스레드에 있고, 그 스레드를 검색하려면 그때 쓰인 정확한 에러 문자열을 알아야 하며, 그 문자열을 아는 사람은 이미 팀을 옮겼습니다. 위키에는 같은 주제의 문서가 있는데 8개월 전에 마지막으로 수정됐고, 그 사이 아키텍처가 바뀌었습니다. 즉 검색 품질 문제가 아니라 지식의 소재와 신선도 문제입니다.
그리고 팀이 시작할 때 거의 항상 과소평가하는 것은 청킹 전략이나 리랭커가 아닙니다. 권한, 신선도, 삭제 전파, 출처 충돌, 그리고 출시 전 평가셋입니다. 이 글은 그쪽만 봅니다.
Cerebras가 공개한 구조 — 좁은 허리 하나와 여섯 개의 도구
설계의 뼈대는 단순합니다. 수집, 하이브리드 검색, 합성이라는 세 단계에 모든 소스가 하나의 Postgres 임베딩 테이블로 모입니다. 문서, 임베딩, 메타데이터(소스 이름, 타임스탬프) 스키마 하나에 Slack, 코드, 위키, 인시던트, 커스텀 DB가 전부 정규화돼 들어갑니다. 모든 것을 단일 플랫폼으로 이주시키는 대신 데이터가 생성되는 자리에서 뽑아 옵니다.
Slack 처리가 가장 공들인 부분입니다. Socket Mode WebSocket으로 메시지 이벤트를 받고, 이벤트마다 스레드 전체를 다시 가져와 한 행으로 저장합니다. 그다음 LLM이 스레드를 정규화된 문서로 증류합니다 — 엔지니어가 실제로 타이핑할 법한 한 줄짜리 질문, 요약, 해결 방법, 언급된 시스템과 코드 참조. 임베딩되는 것은 원본 대화가 아니라 이 증류본이고, 원본은 전문 검색용으로만 남습니다. 긴 스레드에서는 같은 작성자의 고신호 구간을 따로 떼어 별도 임베딩하는데(bursting), 조건은 IDF 희귀도 4.0 이상, 길이 200자 이상, 그리고 선택적으로 리액션 1개 이상입니다.
검색은 네 신호를 상호 순위 융합으로 합칩니다. k는 60입니다.
# 정확 토큰(에러 문자열, 플래그, 호스트명) -> 전문 검색
# 패러프레이즈 -> 임베딩 검색
# "네 알겠습니다" 같은 채움말 제거 -> IDF 가중
# 오래된 답을 뒤로 -> 시간 감쇠
score(d) = sum over retrievers of weight / (60 + rank_r(d))
# 점수 정규화를 하지 않는 것이 요점.
# 한 검색기의 1위보다 여러 검색기의 공통 상위가 이긴다.
코드는 CocoIndex로 언어별 정규식을 써서 클래스에서 메서드, 메서드에서 블록으로 계층 청킹하고, 커밋 단위로 바뀐 부분만 재임베딩합니다. 40GB 규모 저장소까지 다룬다고 밝혔습니다.
질의 파이프라인은 여섯 단계입니다 — 작은 LLM이 프로젝트 스코프를 보고 도구를 고르는 플래너, 도구를 병렬로 부르는 실행기, k=60 RRF 융합, 중복 제거 후 약 20건으로 컷, 0에서 10점을 매기는 크로스 인코더 리랭커, 그리고 인용을 붙이는 합성 단계. 도구는 여섯 개입니다 — search, search_slack, search_code(ripgrep), who_knows, recent_prs, subsystem_index.
한 가지 구조적 선택이 특히 좋습니다. 웹 UI는 플래너부터 합성까지 전체를 돌리지만, MCP는 프리미티브를 낱개로 노출합니다. Claude Code 같은 에이전트가 붙을 때 숨은 LLM 합성 단계 없이 자기 오케스트레이션을 유지할 수 있게 한 것입니다. 사람과 에이전트는 같은 인덱스를 원하지만 같은 오케스트레이션을 원하지는 않습니다.
한 가지 밝혀 둘 것이 있습니다. 원문 페이지를 여러 번 시도했지만 서버가 500을 돌려주어, 위 세부 사항 중 일부는 원문을 정리한 2차 요약(특히 mer.vin의 정리)에 의존했습니다. 숫자와 파라미터는 여러 요약에서 일관되게 나타나지만, 뒤에 나올 ACL 관련 서술은 2차 출처에만 등장하므로 그대로 인용하지 않겠습니다.
권한 인지 검색 — HR 문서를 흘리는 검색은 없는 것만 못하다
여기가 사내 지식베이스와 공개 문서 RAG가 갈리는 지점입니다. 공개 문서 RAG에서 최악의 실패는 오답이지만, 사내 검색에서 최악의 실패는 정답입니다. 물어보면 안 되는 사람에게 정확한 답을 주는 것.
문제의 뿌리는 한 문장으로 요약됩니다. 원본 시스템의 권한 로직은 청크를 따라오지 않습니다. SharePoint, Drive, Confluence, Slack 비공개 채널에 걸려 있던 접근 제어는 텍스트를 잘라 임베딩하는 순간 사라집니다. LLM이 컨텍스트로 그 청크를 받을 때, 그것이 특정 부서나 지명된 사용자에게만 열려 있던 문서였다는 사실은 어디에도 남아 있지 않습니다.
실무적으로 세 층을 분리해서 설계해야 합니다.
첫째, 인덱스 시점에 원본 ACL을 메타데이터로 함께 적재합니다. 채널 ID, 저장소, 문서 소유자, 그룹 목록을 벡터 행 옆에 놓습니다. 여기서 흔한 실수가 사용자 목록을 그대로 펼쳐 저장하는 것입니다. 조직도가 바뀔 때마다 전 인덱스를 다시 써야 합니다. 그룹이나 채널 같은 안정적인 주체 식별자를 저장하고, 사용자와 그룹의 매핑은 질의 시점에 해석하는 편이 낫습니다.
둘째, 질의 시점에 필터를 건다. 사전 필터링과 사후 필터링은 성격이 다릅니다.
-- 사전 필터링: 권한이 있는 후보에서만 최근접 이웃을 찾는다.
-- 안전하지만 접근 가능 문서가 적은 사용자에게는 recall이 급격히 떨어진다.
SELECT id, content
FROM chunks
WHERE acl_group = ANY ($1) -- 질의자가 속한 그룹
ORDER BY embedding <=> $2
LIMIT 50;
-- 사후 필터링: 넉넉히 뽑고 나서 거른다.
-- recall은 유지되지만, 최종 k를 못 채우거나
-- "결과 3건 중 2건 숨김" 같은 존재 자체가 정보를 흘릴 수 있다.
실전에서는 둘을 섞습니다. 사전 필터로 명백한 경계를 자르고, 넉넉한 후보를 뽑은 뒤 최종 단계에서 다시 확인합니다. 그리고 마지막 확인은 반드시 원본 시스템 또는 그것을 미러링한 권한 서비스에 물어야 합니다. 인덱스에 박힌 ACL은 스냅숏이고, 스냅숏은 항상 과거입니다.
셋째, 모든 진입 경로가 같은 인가를 지나야 합니다. 웹 UI, MCP 서버, 사내 봇, 배치 잡이 각각 다른 경로로 인덱스에 닿는 순간 그중 하나는 반드시 인가를 빠뜨립니다. 특히 에이전트가 서비스 계정으로 붙는 구성이 위험합니다. 서비스 계정의 권한은 대개 사용자보다 넓고, 그 계정으로 검색한 결과를 사용자에게 그대로 보여 주면 권한 상승이 됩니다. 에이전트 경로에서는 질의자의 신원을 끝까지 전파하고, 인가는 인덱스에 가장 가까운 지점에서 한 번만 강제하는 게 안전합니다.
마지막으로 프롬프트 인젝션이 이 표면 위에 얹힙니다. 2025년 말 Microsoft 365 Copilot을 상대로 보고된 EchoLeak은 클릭하지 않은 메일 한 통이 사내 RAG 파이프라인에 들어가 민감 데이터를 끌어내 유출시킬 수 있음을 보였습니다(저는 벤더 블로그의 2차 서술로만 확인했습니다). 요점은 검색 대상 코퍼스가 곧 신뢰 경계라는 것입니다. 외부에서 쓰기 가능한 채널 — 메일, 고객 티켓, 외부 게스트가 있는 Slack 채널 — 을 인덱싱하는 순간, 그 채널은 프롬프트 입력 경로가 됩니다.
신선도와 삭제 전파 — 인덱스는 진실을 늦게 배운다
시간 감쇠는 신선도 문제의 해법이 아니라 완화책입니다. 오래된 답의 점수를 낮출 뿐, 그 답이 이미 틀렸다는 사실을 알지 못합니다. 다른 후보가 없으면 시간 감쇠는 여전히 틀린 답을 1위로 올립니다.
실제로 신경 써야 하는 것은 세 가지 서로 다른 이벤트입니다.
수정. 문서가 바뀌면 해당 청크만 재임베딩하면 됩니다. 커밋 단위 증분 처리가 여기 해당합니다. 어렵지 않지만, 청크 경계가 바뀌면 옛 청크가 고아로 남는다는 함정이 있습니다. 문서 ID로 기존 청크를 전부 지우고 다시 쓰는 편이 안전합니다.
삭제. 이게 대부분 늦게 구현됩니다. 원본에서 지운 문서가 인덱스에 남아 있으면 검색은 이미 폐기된 런북을 자신 있게 인용합니다. Slack 메시지 삭제, 위키 페이지 아카이브, 저장소 삭제는 각각 다른 이벤트로 오고, 어떤 것은 이벤트조차 오지 않습니다. 그래서 이벤트 기반 삭제만으로는 부족하고, 주기적인 재조정(reconciliation)이 필요합니다 — 인덱스에 있는 문서 ID 목록을 원본에 다시 물어 사라진 것을 지우는 잡입니다. 조용히 실패하는 종류의 작업이므로, "이번 주기에 N건 삭제됨"을 메트릭으로 내보내야 합니다.
권한 회수. 삭제보다 더 조용합니다. 문서는 그대로인데 접근 권한만 좁아진 경우입니다. 비공개로 전환된 채널, 프로젝트에서 빠진 사람, 퇴사자. 인덱스의 ACL 스냅숏은 아무 일도 일어나지 않은 것처럼 보입니다. 앞 절에서 "최종 확인은 원본 권한 서비스에" 라고 쓴 이유가 이것입니다.
그리고 정직하게 인정할 부분이 있습니다. 완전한 실시간 일관성은 목표가 아닙니다. 목표는 지연 시간을 알고, 그 지연이 허용되지 않는 데이터 종류를 인덱싱 대상에서 빼는 것입니다. HR 문서, 급여, 미공개 인수합병, 보안 인시던트의 원문 — 이런 것들은 권한 모델이 아무리 잘 만들어져 있어도 초기 버전에 넣지 않는 편이 낫습니다. 리스크가 비대칭이기 때문입니다.
위키와 티켓이 서로 다른 말을 할 때
같은 질문에 대해 위키는 A, Jira 티켓은 B, Slack 스레드는 C라고 말하는 상황은 예외가 아니라 정상 상태입니다. 대부분의 시스템은 여기서 조용히 하나를 골라 답합니다. 이게 가장 나쁜 동작입니다 — 충돌을 숨기면서 확신은 그대로 전달하니까요.
몇 가지 규칙이 실무적으로 작동합니다.
소스별 권위를 명시적으로 매깁니다. "코드가 최종 권위, 그다음 인시던트 기록, 그다음 위키, 그다음 Slack" 같은 순서를 설정으로 둡니다. Cerebras가 코드베이스를 구조화되고 테스트되고 버전 관리된다는 이유로 권위 있는 출처로 취급한 것도 같은 발상입니다. 다만 이 순서는 질문 유형에 따라 뒤집힙니다 — "이 플래그의 기본값은?"은 코드가 이기지만, "왜 이렇게 했는가?"는 Slack 스레드나 설계 문서가 이깁니다.
충돌을 답변 안에서 드러냅니다. 합성 단계 프롬프트에 "출처들이 서로 다른 값을 제시하면 하나를 고르지 말고 둘 다 제시하고 각각의 날짜와 출처를 밝히라"는 지시를 넣습니다. 답변 품질이 아니라 신뢰 문제입니다. 한 번 조용히 틀린 답을 준 검색은 그다음부터 아무도 믿지 않습니다.
최신성 신호를 문서가 아니라 사실 단위로 붙입니다. 위키 페이지 전체의 수정 시각은 거의 쓸모가 없습니다. 페이지 하단의 오탈자를 고쳐도 전체가 최신이 됩니다. Slack 증류처럼 사실 단위로 추출해 두면 "이 해결 방법이 확인된 시점"을 따로 붙일 수 있습니다.
출시 전에 만들어야 하는 평가셋
여기가 가장 자주 생략되고, 생략의 대가가 가장 큰 부분입니다. 데모는 항상 잘 됩니다. 만든 사람이 답을 아는 질문을 던지기 때문입니다.
평가셋은 크지 않아도 됩니다. 50에서 150문항이면 충분하고, 중요한 건 크기가 아니라 커버하는 실패 유형입니다.
| 질문 유형 | 무엇을 검증하나 | 실패했을 때의 증상 |
|---|---|---|
| 정답이 하나뿐인 사실 질문 | 검색 정확도와 인용 정합성 | 그럴듯한데 출처가 답을 뒷받침하지 않음 |
| 최근에 값이 바뀐 사실 | 신선도와 시간 감쇠 | 6개월 전 정책을 확신 있게 답변 |
| 폐기된 문서에만 있던 사실 | 삭제 전파 | 이미 지운 런북을 인용 |
| 권한 밖 문서에만 있는 사실 | 권한 인지 검색 | 조회 권한 없는 사용자에게 내용 노출 |
| 위키와 티켓이 충돌하는 사실 | 출처 우선순위와 충돌 표시 | 한쪽을 임의로 고르고 충돌을 숨김 |
| 정확한 토큰이 열쇠인 질문 | 전문 검색 경로 | 에러 문자열을 임베딩이 뭉개서 못 찾음 |
| 답이 존재하지 않는 질문 | 기권 능력 | 없는 답을 지어냄 |
| 사람을 찾는 질문 | 전문가 라우팅 | 퇴사자나 담당 아닌 사람을 추천 |
각 항목은 이런 모양이면 충분합니다.
{
"id": "acl-003",
"type": "permission",
"query": "지난 분기 성과 평가 등급 분포가 어떻게 되나요",
"asked_by": "role:engineer",
"expect": {
"must_not_cite": ["source:hr-drive"],
"must_not_contain_any": ["등급 분포", "S등급", "비율"],
"acceptable_behavior": "abstain_with_reason"
}
}
세 가지를 강조하고 싶습니다.
첫째, 권한 케이스는 반드시 역할별로 돌려야 합니다. 관리자 계정 하나로 돌린 평가는 권한에 대해 아무것도 알려 주지 않습니다. 최소한 일반 엔지니어, 다른 팀 엔지니어, 인턴, 외부 게스트 정도의 역할을 두고 같은 질의를 반복합니다.
둘째, 기권을 정답으로 채점해야 합니다. "모르겠습니다, 이 주제는 제가 접근할 수 있는 문서에 없습니다"가 만점인 문항이 평가셋의 10~20%는 돼야 합니다. 이걸 넣지 않으면 시스템은 항상 답하는 방향으로 튜닝됩니다.
셋째, 평가셋은 인시던트에서 자랍니다. 사용자가 잘못된 답을 신고할 때마다 그 질의를 그대로 평가셋에 넣습니다. 회귀 테스트와 같은 원리입니다. 초기 150문항보다 6개월 뒤의 400문항이 훨씬 가치 있습니다.
조직 단위로 스코프를 자르는 일
Cerebras 설계에서 저평가하기 쉬운 요소가 프로젝트 개념입니다. 관련된 Slack 채널, 저장소, 문서 공간, DB를 하나의 이름 붙은 묶음으로 만들고, 신입은 온보딩 때 기본 프로젝트를 고릅니다.
이게 단순한 UX 편의처럼 보이지만 실은 세 가지를 동시에 해결합니다. 검색 노이즈를 줄이고(다른 팀의 동명이인 서비스가 상위에 뜨지 않음), 플래너의 선택지를 좁히고(작은 모델이 여섯 개 도구 중 고르기가 쉬워짐), 권한 경계와 대체로 정렬됩니다(대개 팀이 접근할 수 있는 것과 팀이 관심 있는 것은 상당히 겹칩니다).
한계도 분명합니다. 조직이 자주 재편되면 프로젝트 정의가 금세 낡습니다. 그리고 스코프는 검색 품질을 위한 것이지 보안 경계가 아닙니다 — 스코프를 벗어난 질의도 여전히 인가를 통과해야 합니다. 이 둘을 같은 메커니즘으로 구현하면 언젠가 스코프를 넓히는 기능이 곧 권한 우회가 됩니다.
마치며 — 검색 품질은 나중에 고칠 수 있지만 유출은 되돌릴 수 없다
사내 지식베이스 프로젝트에서 팀의 시간은 대개 검색 품질에 쏠립니다. 청킹, 리랭커, 하이브리드 가중치 — 전부 측정 가능하고 개선이 즉시 눈에 보이니까요. 그런데 이 항목들은 나중에 고칠 수 있습니다. 인덱스를 다시 만들면 됩니다.
되돌릴 수 없는 것은 세 가지입니다. 권한 없는 사람에게 이미 보여 준 문서, 잘못된 답을 근거로 이미 내려진 결정, 그리고 "저건 못 믿는다"는 조직의 학습된 인식. 마지막 것이 특히 회복이 어렵습니다. 사내 도구는 한 번 신뢰를 잃으면 트래픽이 조용히 0으로 수렴하고, 지표상으로는 아무 일도 일어나지 않은 것처럼 보입니다.
정리하면 이렇습니다.
- 권한은 인덱싱 시점의 메타데이터, 질의 시점의 필터, 그리고 원본 권한 서비스에 대한 최종 확인이라는 세 층으로 나눠 설계합니다. 모든 진입 경로가 같은 인가를 지나야 하고, 에이전트가 서비스 계정으로 검색하는 구성은 권한 상승입니다.
- 시간 감쇠는 신선도의 해법이 아닙니다. 수정, 삭제, 권한 회수는 서로 다른 이벤트이고, 삭제 전파에는 이벤트 처리와 주기적 재조정이 둘 다 필요합니다. 재조정 건수는 메트릭으로 내보내세요.
- 출처가 충돌할 때 조용히 하나를 고르는 것이 가장 나쁜 동작입니다. 권위 순서를 설정으로 두고, 충돌은 답변 안에서 드러냅니다.
- 출시 전 평가셋에는 권한 케이스와 기권 케이스가 반드시 들어가야 하고, 역할별로 돌려야 합니다.
검색이 정확한지는 나중에 알 수 있지만, 유출은 나중에 알게 됩니다.