제가 제 로컬 RAG 스택에 저의 파일들에 대해 질문했더니, 그게 환각(hallucinated)을 일으켰습니다. 매우 자신감 있게 말이죠. 코퍼스(corpus)는 제가 제공한 것이었고, 검색(retrieval)도 정확했습니다. 그런데 작은 모델이 그 얇은 컨텍스트를 가지고 유창한 영어로 답변을 '만들어냈'습니다.
해결책은 더 큰 모델을 사용하는 것이 아니었습니다. 그것은 프롬프트에 명시하고 테스트를 통해 입증된, 일종의 행동 계약(behavioral contract)이었습니다:
text
You are a grounded assistant. Use ONLY the context below. Rules:
(1) If the context does not contain the answer, reply exactly: NOT IN CORPUS.
(2) Cite which source title each fact came from.
(3) Do not use outside knowledge.
The result: 이전에 환각을 일으켰던 동일한 질문이 이제 "NOT IN CORPUS"라고 답합니다. — 그리고 컨텍스트가 있을 때는, 출처 인용과 함께 답변이 나옵니다.
이것을 구축하면서 제가 배운 두 가지가 있습니다:
검색(retrieval) 자체가 문제가 아니었습니다. 벡터 검색(Vector search)은 항상 올바른 영역(neighborhood)을 찾아냈습니다. 문제는 '입'이었습니다. 즉, 작은 모델이 얇은 컨텍스트를 상상력을 발휘할 기회로 여긴 것이죠. 사람들은 실제로는 생성 계층(generation-layer)의 실패인 환각에 대해 RAG가 원인이라고 비난합니다.
'모름'(Abstention)은 테스트해야 하는 기능입니다. 저는 이제 코퍼스에 존재하지 않는 질문들을 의도적으로 던져서 모델이 거부하는지 확인합니다. 테스트하지 않은 '모름'은 곧 가지고 있지 않은 '모름'과 같습니다.
사용 스택: Ollama + nomic-embed-text + ChromaDB + granite 3B로, 8GB MacBook Air에서 완전히 로컬로 구동됩니다. 제 아카이브의 30만 개 이상의 청크(chunks)를 사용하며, 클라우드도, API도, 계정도 없습니다. 오케스트레이터가 각 질문을 네 개의 워커에게 분배하고, EvidenceFlow 가드가 화면에 표시되기 전에 검증합니다.
이제 제 전체 아카이브는 저의 목소리로 질문에 답하며 — 그리고 "모르겠습니다"라고 말할 권한까지 갖게 되었습니다. 그 허락이 바로 이 기능입니다.
도구(tooling)는 공개되었습니다: ask-mirror — '모름' 가드와 함께 하이브리드 검색(vector + SQL)을 구현했습니다. AGPL 라이선스입니다. 검증 규율은 EvidenceFlow에서 가져왔으며 — Ship 5에서는 실패 시 폐쇄(fail-closed) 원칙을 적용했고, 이제 전체 스택을 보호하고 있습니다.