25. 프라이빗 RAG, 우리 인프라에서 검색하고 답변하기
복습 음성 · 51분 10초 · 프라이빗 RAG 개념과 구현 흐름 · 21장 · 파일에 1.1배속 적용
사내 문서로 질문에 답하는 시스템을 만들 때는 문서를 어디에 저장하는지와 어디에서 계산하는지를 함께 봐야 한다. 파일을 회사 컴퓨터에 보관해도 임베딩이나 답변 생성에 외부 서비스를 쓰면 문서 일부가 그 서비스로 전달된다.
Private RAG(프라이빗 RAG)에서는 검색과 답변 생성 흐름을 내부 환경에 배치한다. 문서 분할, 로컬 임베딩, 벡터 저장소, 생성 모델이 연결되는 위치와 각 단계에서 오가는 데이터를 확인한다.
위 음성은 개념과 기본 구현 흐름, 다섯 가지 개선 실험을 다루는 21장 구성이다. 파일에 1.1배속을 적용했으므로 플레이어는 1.0배속으로 두면 된다. 음성에서는 가상의 회사 복무 규정을, 본문에서는 따로 만든 장비 대여 규정을 예로 든다. 두 사례 모두 설명용 자료다.
1. 임베딩·저장·생성에서 데이터가 어디로 가는가
RAG(Retrieval-Augmented Generation, 검색 증강 생성)는 질문과 관련된 문서를 검색한 뒤 그 내용을 LLM(Large Language Model, 대규모 언어모델)의 입력에 넣어 답변을 만든다. 데이터가 이동하는 경로를 그리면 내부에 둘 부품이 드러난다.
| 단계 | 외부 서비스를 사용할 때 전달·저장되는 내용 | 내부 처리 구성 |
|---|---|---|
| 임베딩 | 문서 조각과 검색 질문 | 내부 환경에서 실행하는 임베딩 모델 |
| 벡터 저장 | 벡터, 연결된 원문과 메타데이터 | 조직이 관리하는 벡터 저장소 |
| 답변 생성 | 질문과 검색된 원문 | 내부 환경에서 실행하는 생성 모델 |
Embedding(임베딩)은 텍스트를 숫자 묶음인 벡터로 표현하는 과정이다. 벡터 검색 결과를 답변에 쓰려면 어떤 원문에서 나온 벡터인지 연결해야 한다. 저장소에 벡터만 있는지, 원문과 제목·날짜까지 함께 있는지 확인할 필요가 있다. 문서에서 추출한 벡터도 데이터 관리 범위에 포함해 살펴본다.
로컬이라는 말의 기준도 확인한다. Colab 런타임에서 모델을 직접 실행하면 계산 장소는 Colab 서버다. 회사 내부에서만 처리해야 하는 요구가 있다면 실제 서버와 저장소의 위치, 접근 권한과 통신 경로까지 맞춰야 한다.
모델을 처음 내려받는 준비 단계와 내부 문서를 처리하는 운영 단계는 나누어 구성할 수 있다. 필요한 파일을 미리 확보하고 운영 환경에서는 내부 파일을 읽게 하는 식이다. 비용을 비교할 때도 호출 요금과 함께 장비·전력·운영 작업을 고려한다.
2. 문서 등록과 질문 응답을 나누어 읽는다
문서 등록은 검색할 자료를 준비하는 과정이고, 질문 응답은 준비된 자료에서 근거를 찾아 답하는 과정이다.
문서 등록
문서 읽기 → 청킹 → 문서 임베딩 → 벡터·원문·출처 저장
질문 응답
질문 임베딩 → 관련 청크 검색 → 근거와 질문을 프롬프트에 결합
→ 답변 생성 → 근거·조건·숫자 확인
Chunk(청크)는 검색할 문서 조각이다. 모든 질문마다 전체 문서를 다시 임베딩할 필요는 없다. 등록해 둔 문서 벡터와 새 질문 벡터를 비교한다. 문서 내용이나 임베딩 모델을 바꾸면 그 변경에 맞춰 검색 데이터를 갱신한다.
이 글의 예시 규정에는 “장비의 기본 대여 기간은 7일”과 “다음 예약이 없고 담당자 승인을 받으면 3일 연장할 수 있다”는 내용이 있다. 사용자가 “노트북을 조금 더 써도 될까요?”라고 물으면 대여 기간과 연장 조건이 함께 담긴 근거를 찾아야 한다.
작업 순서를 읽을 때는 각 단계의 입력과 결과를 적어보면 도움이 된다. 임베딩의 결과는 벡터이고, 검색의 결과는 그 벡터에 연결된 문서다. 생성 모델이 답변에 사용하는 근거는 검색해서 가져온 원문이다.
3. 청킹은 의미와 입력 길이를 함께 맞추는 일
Chunking(청킹)은 긴 문서를 검색 가능한 조각으로 나눈다. 대여 안내 전체를 하나로 묶으면 신청·연장·분실 처리 내용이 한 벡터에 섞인다. 작게 나누면 질문과 직접 맞는 부분을 찾기 쉬워질 수 있지만 승인 조건이 다른 청크로 떨어질 수 있다.
Overlap(중첩)은 인접 청크에 일부 문장을 겹쳐두는 방법이다. 경계의 문맥을 보존하는 데 도움이 되며, 겹치는 만큼 저장량과 중복 검색 결과도 늘어난다. 조항 하나가 온전한 의미 단위라면 중첩 없이 나누는 조건도 비교할 수 있다.
| 접근 | 나누거나 연결하는 기준 | 함께 살펴볼 점 |
|---|---|---|
| Fixed-size Chunking(고정 크기 청킹) | 글자·토큰 수 | 문장 중간이 잘리는지 |
| Recursive Chunking(재귀적 청킹) | 문단부터 더 작은 경계로 내려감 | 크기 상한과 문맥 보존 |
| Structure-aware Chunking(구조 기반 청킹) | 제목·장·조항 | 제목 정보와 본문의 연결 |
| Semantic Chunking(의미 기반 청킹) | 문장 사이 의미 변화 | 경계 판단에 쓰는 임베딩과 추가 계산 |
| Parent-Child Chunking(부모·자식 청킹) | 작은 검색 단위와 큰 문맥을 연결 | 부모 확장과 중복 제거 |
처음에는 같은 문서에 크기 300자, 중첩 50자 같은 조건을 적용하고 분할 결과를 읽어볼 수 있다. 이 숫자는 비교용 설정이다. 청크 수와 평균 길이만 보지 말고 가장 긴 청크와 조건이 끊긴 위치도 확인한다.
모델 입력 상한은 Token(토큰) 단위다. 글자 수와 토큰 수의 비율은 Tokenizer(토크나이저)에 따라 달라진다. 실제 모델의 토크나이저로 길이를 재고, 접두어와 특수 토큰이 들어갈 여유도 고려한다.
의미 기반 청킹은 분할 단계에서 이미 임베딩을 사용한다. 내부 처리가 필요하다면 이때 호출하는 모델도 내부에 있어야 한다. 마지막 생성 모델만 내부로 옮겨서는 앞 단계의 데이터 이동까지 통제할 수 없다.
4. BGE-M3와 E5는 입력 규칙부터 비교한다
로컬 임베딩을 사용할 때는 Model Card(모델 카드)에서 입력 형식, 최대 길이, 벡터 차원을 확인한다. 공부에 사용한 두 후보의 조건은 다음과 같다.
| 모델 | 입력 상한 | 벡터 차원 | 검색 입력 형식 |
|---|---|---|---|
| BGE-M3 | 8192 토큰 | 1024 | E5 방식의 접두어를 붙이지 않음 |
| multilingual-e5-base | 512 토큰 | 768 | 질문 query: , 문서 passage: |
BGE-M3는 여러 언어와 입력 길이, 밀집·희소·다중 벡터 검색 표현을 지원한다. 여기서 정리하는 기본 흐름은 문서마다 하나의 Dense Vector(밀집 벡터)를 만드는 방식이다. 지원 기능 전체가 래퍼의 기본 호출에서 모두 사용된다고 가정하지 않는다. BGE-M3 모델 카드
E5의 검색 질문에는 query: , 문서에는 passage: 를 붙인다. 한국어에도 같은 규칙을 적용한다. 문장끼리 대칭적인 의미 유사도를 비교하는 작업은 모델 카드에서 query: 사용을 안내한다. 검색과 문장 유사도 작업의 입력을 구분해서 읽는다. E5 모델 카드
900토큰인 조항을 E5에 넣으면 상한 뒤의 내용이 잘릴 수 있다. 입력을 수용하는 모델을 고르는 방법과 조항을 더 작은 청크로 나누는 방법을 비교할 수 있다. 긴 입력을 처리한다는 조건과 실제로 정답 문서를 잘 찾는지는 별도로 확인한다.
MTEB(Massive Text Embedding Benchmark, 임베딩 평가 벤치마크)를 볼 때도 사용할 언어와 검색 작업의 점수를 살펴본다. 후보를 추린 다음에는 자기 문서와 질문으로 비교한다. 모델 크기와 입력 길이, 처리 시간도 선택 조건에 포함된다.
5. 벡터 정규화와 코사인 유사도를 연결한다
Cosine Similarity(코사인 유사도)는 벡터 방향의 가까움을 비교한다. Dot Product(내적)는 두 벡터의 대응하는 값을 곱해 더하는 계산이며, 방향과 벡터 길이가 함께 영향을 준다.
문서와 질문 벡터를 각각 길이 1로 Normalization(정규화)하면 내적을 코사인 유사도로 사용할 수 있다. 같은 방향을 가진 문서 벡터라도 길이가 두 배라면 같은 질문과의 내적은 두 배가 된다. 정규화는 이런 길이의 영향을 제거한다.
벡터 길이는 문서의 글자 수와 다른 개념이다. 문서가 길수록 벡터 길이가 반드시 커진다고 일반화할 수 없다. 직접 내적으로 순위를 계산한다면 사용한 벡터가 실제로 정규화됐는지 확인한다.
모델 내부에 정규화 단계가 있으면 바깥의 옵션 하나를 꺼도 단위 벡터가 나올 수 있다. 옵션 이름과 함께 결과 벡터의 norm(길이)을 재야 실험 조건을 설명할 수 있다.
Qdrant의 Cosine 설정은 업로드된 벡터를 정규화해 비교한다. 직접 배열의 내적을 계산하는 코드와 데이터베이스가 거리 계산을 처리하는 코드는 구분해서 읽는다. Qdrant 컬렉션 문서
6. Qdrant에는 벡터와 근거를 연결해 저장한다
Vector Database(벡터 데이터베이스)는 벡터와 관련 정보를 저장하고 유사한 벡터를 찾는다. Qdrant의 Collection(컬렉션)에는 벡터 크기와 거리 방식을 정한다. BGE-M3의 1024차원 벡터를 사용한다면 그 차원에 맞춰 구성한다.
Point(포인트)는 식별자, 벡터, Payload(페이로드)를 묶은 저장 단위다. 페이로드에는 본문과 문서 이름, 절 제목, 시행일 같은 정보를 담을 수 있다. 예시 장비 안내라면 “대여”, “연장”, “분실”이라는 절 정보를 연결한다.
Upsert(업서트)는 같은 식별자의 포인트를 갱신하고 새 식별자는 추가하는 동작이다. 모델을 바꿀 때는 벡터 차원과 표현 공간이 달라질 수 있으므로 새 모델로 문서를 다시 임베딩하고 검색 구성을 맞춘다. 차원이 같아도 서로 다른 모델의 벡터를 같은 표현 공간으로 취급하면 비교가 어긋날 수 있다.
Metadata Filter(메타데이터 필터)는 검색 범위를 좁힌다. 장비 대여 안내 중 “연장” 절만 후보로 삼고, 그 안에서 질문에 가까운 문서를 정렬할 수 있다. 필터는 후보 자격을, 유사도는 후보 안의 순서를 정한다. 잘못된 조건으로 정답을 제외하면 뒤의 순위 계산으로 복구하기 어렵다.
저장소를 배치하는 방식도 나누어 본다. Embedded Mode(임베디드 모드)는 프로그램 안에서 라이브러리를 사용하고 로컬 폴더에 데이터를 둔다. 여러 서비스가 같은 인덱스를 공유한다면 조직이 운영하는 서버에 접속하는 Self-hosting(셀프 호스팅)을 검토할 수 있다. 데이터가 커지면 여러 서버의 클러스터 구성과 운영 비용을 함께 살펴본다.
연습 코드에서 기존 컬렉션을 지우고 다시 만드는 부분은 저장 데이터가 사라지는 동작이다. 실제 데이터에 적용할 때는 대상과 보존 방법을 먼저 확인한다. 서버로 옮길 때도 데이터 이동·권한·백업 조건을 함께 준비한다.
7. 로컬 LLM의 메모리는 가중치와 실행 공간을 더해 본다
생성 모델로 다룬 Qwen2.5-7B-Instruct는 지시와 대화에 맞춰 응답하도록 조정된 모델이다. 로컬에서 실행하려면 GPU(그래픽 처리 장치)의 VRAM(전용 메모리)에 무엇이 올라오는지 계산해야 한다.
약 70억 개의 가중치를 하나당 2바이트로 저장한다고 단순 계산하면 약 14GB다. 여기에 임베딩 모델, 계산 중간값과 KV Cache(키·값 캐시)가 더해진다. 캐시는 앞서 계산한 어텐션 정보를 다음 토큰 생성에 재사용하는 공간이다. 입력·생성 길이와 동시 요청량에 따라 필요한 크기가 달라진다.
Quantization(양자화)은 가중치를 더 적은 비트로 표현하는 방법이다. 4비트 구성의 NF4(NormalFloat 4)는 정규분포형 가중치에 맞춘 표현이고, Double Quantization(이중 양자화)은 양자화에 필요한 상수도 다시 양자화해 공간을 줄인다. Transformers의 bitsandbytes 문서
일부 계층과 추가 정보는 더 높은 정밀도로 남는다. 그래서 모든 가중치 수에 0.5바이트를 곱한 값만으로 실제 VRAM 사용량을 정할 수 없다. 적재 직후와 긴 답변을 생성하는 동안의 사용량을 나눠 본다.
학습 자료의 T4 구성은 4비트 적재와 fp16(16비트 부동소수점) 계산을 사용한다. bf16(브레인 부동소수점 16비트)과 FlashAttention-2(어텐션 최적화 구현)를 검토할 때는 GPU·라이브러리의 지원 조건을 확인한다. 더 높은 정밀도로 적재하는 예시로 바꾸면 가중치 메모리도 다시 계산한다.
OOM(메모리 부족 오류)이 생겼다면 현재 올라온 모델 사본과 입력 길이, 동시 처리량을 함께 확인한다. 모델 파일 하나가 들어가는 것과 실제 요청을 처리할 여유가 있는 것은 각각 확인해야 할 조건이다.
8. 검색 결과를 생성 모델의 대화 형식에 넣는다
생성 단계에서는 System Prompt(시스템 프롬프트)에 답변 역할과 규칙을 적고 사용자 메시지에 질문과 검색한 원문을 담는다. Chat Template(채팅 템플릿)은 이 메시지들을 해당 모델이 학습한 대화 형식에 맞게 배열한다.
예시 규칙은 제공한 문서에 근거하기, 근거 절을 표시하기, 문서에 답이 없으면 확인되지 않는다고 알리기다. 출력 언어가 필요하다면 한국어로 답하도록 명시한다. 모델이 생성한 토큰에서 입력에 해당하는 부분을 제외하고 새 답변만 반환한다.
새로 생성할 토큰의 상한은 답변 길이 설정이다. 임베딩 모델의 입력 상한과는 적용 대상이 다르다. 두 설정에 우연히 같은 512라는 값이 들어 있어도 서로 다른 단계의 조건이다.
Temperature(온도)를 낮추면 샘플링에서 후보 선택이 상대적으로 덜 퍼지게 조절할 수 있다. 그래도 답변의 사실성은 확인해야 한다. 샘플링을 켰다면 같은 질문의 표현이 실행마다 달라질 수 있으므로 평가 조건도 기록한다.
기본 RAG에서는 이미 학습된 모델로 추론한다. 질문마다 검색한 문서를 입력에 넣는 과정이 모델 가중치를 새로 학습시키는 과정과 같지는 않다. 문서 갱신과 모델 학습을 구분해두면 무엇을 다시 준비할지 판단하기 쉽다.
9. 검색 성공과 답변 정확성은 따로 확인한다
장비 연장 안내가 검색됐다고 해보자. 답변이 “3일 연장할 수 있다”고만 쓰면 다음 예약이 없어야 한다는 조건과 담당자 승인이 빠졌다. 근거 절을 찾았는지와 그 내용을 충실히 사용했는지를 따로 본다.
문서에 반납 가능한 요일만 있고 담당자의 점심시간은 없을 수도 있다. 비슷한 업무 안내가 검색돼도 그 안에 답이 없다면 구체적인 시간을 채워 넣을 근거가 부족하다. 정답이 없는 질문도 평가에 포함할 이유다.
검색 결과가 없거나 최고 유사도가 Threshold(임계값)보다 낮을 때 생성을 건너뛰는 방법도 있다. 0.4 같은 값은 실험 출발값이다. 모델과 문서의 점수 분포가 달라지므로 정답이 있는 질문과 없는 질문을 함께 사용해 조정한다.
높은 유사도와 답변 가능성도 구분한다. 관련성이 높은 안내문에 필요한 조건이 빠져 있을 수 있다. 검색 점수로 거르는 단계 뒤에도 답변의 숫자·예외·출처를 원문과 대조한다. 프롬프트에 규칙을 적은 뒤에는 실제 답변에서 그 규칙이 지켜졌는지 확인한다.
10. 오프라인 검증은 시작 시점과 통제 범위가 중요하다
모델 파일을 내부에 갖춘 뒤에는 Offline(오프라인) 실행을 확인할 수 있다. 필요한 파일에는 생성 모델과 임베딩 모델·토크나이저·검색 데이터, 추가로 사용하는 리랭커가 포함된다.
Hugging Face의 HF_HUB_OFFLINE=1은 Hub에 HTTP 요청을 보내지 않고 캐시를 사용하도록 한다. 환경변수는 라이브러리를 불러오기 전에 설정해야 한다. 이미 import한 다음 값을 바꾼 상태로 답변을 받았다고 해서 새 설정이 적용됐다고 가정하기는 어렵다. Hugging Face 환경변수 문서
이 설정은 라이브러리의 Hub 요청에 적용된다. 다른 프로그램과 서비스의 통신까지 통제해야 한다면 네트워크 차단과 관찰도 함께 필요하다. “오프라인 설정을 켰다”, “새 프로세스가 내부 파일을 읽어 동작했다”, “외부 통신이 차단된 환경에서 동작했다”를 구분해 기록한다.
문서 등록과 질문 응답 경로도 각각 확인한다. 기존 인덱스에서 답변을 생성한 결과는 그 경로의 실행 증거다. 새 문서를 임베딩하고 저장하는 과정까지 확인하려면 해당 단계를 실행한 기록이 있어야 한다.
11. 평가 질문과 Hit@k로 기준선을 만든다
Baseline(기준선)은 변경 전 결과를 비교할 출발점이다. 문서와 평가 질문, 정답 근거를 정한 뒤 같은 조건에서 개선 전후를 비교한다.
Hit@k(상위 k개 정답 포함률)는 정답 청크가 검색 상위 k개에 들어간 질문의 비율이다. 설명용으로 질문 10개 중 7개에서 정답이 상위 3개 안에 있었다면 Hit@3은 7/10 = 70%다. 첫 번째 결과만 보는 Hit@1도 함께 보면 정답의 순위를 더 구체적으로 살펴볼 수 있다.
평가 질문에는 의미를 바꿔 말한 질문과 식별자 질문을 섞는다. “노트북을 더 써도 될까요?”와 “장비 EQ-017 안내를 찾아주세요”는 필요한 검색 능력이 다르다. 전체 점수만 보지 말고 질문 유형별로 나누어 비교한다.
청킹을 바꿔 청크 번호가 달라지면 정답 매핑도 다시 맞춘다. 조항 번호와 배열 인덱스도 구분한다. 번호가 한 칸 어긋나면 올바르게 찾은 결과를 실패로 계산할 수 있다.
Hit@3은 검색된 후보의 정답 포함 여부를 확인한다. 생성 모델이 조건을 잘 읽었는지까지 포함하는 지표는 아니다. 같은 평가 질문으로 검색 결과와 최종 답변을 나누어 기록하면 어느 단계가 개선됐는지 보인다.
12. 배치 처리량과 세 가지 조용한 실패를 진단한다
Batch(배치)는 여러 입력을 묶어 처리하는 단위다. 배치를 키우면 GPU를 더 효율적으로 사용할 수 있지만 메모리도 더 필요하다. 처리량은 처리한 청크 수를 걸린 시간으로 나눈 값이다.
배치 1·8·32·64를 비교한다면 시간과 초당 청크 수, 최대 메모리를 함께 기록한다. 입력 길이 분포도 유지해야 비교하기 좋다. 배치를 두 배로 늘려도 이미 다른 단계가 병목이라면 속도 개선이 작을 수 있다.
가정한 처리량이 초당 40청크라면 10만 청크의 임베딩 시간은 100000 / 40 = 2500초, 약 41분 40초다. 실제 일정에는 문서 읽기·저장·준비 시간도 더한다. 같은 문서를 복제해 만든 부하용 데이터와 다양한 질문에 답하는 검색 평가 데이터는 역할이 다르다.
프로그램이 오류 없이 끝나도 검색은 실패할 수 있다. 다음 세 조건은 입력과 출력에서 직접 확인한다.
| 원인 | 확인할 값 | 영향의 예 |
|---|---|---|
| Truncation(입력 절단) | 토큰 수와 잘린 위치 | 긴 조항 끝의 연장 조건이 임베딩에서 빠짐 |
| E5 접두어 누락 | 실제 질문·문서 입력 | 학습 때 사용한 입력 형식과 달라짐 |
| 정규화 조건 불일치 | 벡터 길이와 거리 설정 | 내적에 문서 벡터 길이가 섞임 |
긴 입력을 받는 모델을 썼다는 이유만으로 정답이 항상 첫 순위가 되지는 않는다. 접두어·정규화 조건을 바꿔도 작은 평가 세트에서는 순위가 같을 수 있다. 설정이 실제로 달라졌는지와 검색 결과가 어떻게 달라졌는지를 함께 확인한다.
13. BM25와 RRF로 식별자 검색을 보완한다
BM25는 단어의 빈도와 희소성, 문서 길이를 고려하는 키워드 검색 방식이다. Hybrid Search(하이브리드 검색)는 의미를 비교하는 밀집 검색과 이런 키워드 검색을 함께 활용한다.
먼저 검색할 텍스트에 식별자가 들어 있는지 확인한다. 제목을 메타데이터로 분리했다면 본문만 검색하는 BM25에는 장비 번호가 없을 수 있다. 제목·식별자를 검색용 텍스트에 포함하고 비교하는 밀집 검색에도 같은 텍스트를 사용한다.
한국어 조사가 붙었을 때 토큰이 맞는지도 본다. “제7조”와 “제7조가”를 서로 다른 단어로만 처리하면 식별자 일치가 깨질 수 있다. 번호를 별도 토큰으로 추출하거나 적절한 토크나이저를 적용하고 질문과 문서에 같은 규칙을 쓴다.
RRF(Reciprocal Rank Fusion, 순위 역수 융합)는 검색기의 원점수 대신 순위를 결합한다. 각 검색기에서 문서의 기여도를 1 / (상수 + 순위)로 계산하고 문서별로 더한다. 순위는 1부터 센다.
완충 상수 60일 때 한 문서가 두 검색기에서 모두 2위라면 1/62 + 1/62, 약 0.03226이다. 다른 문서가 한 검색기에서만 1위라면 1/61, 약 0.01639다. 이 예에서는 두 검색기가 함께 앞에 놓은 문서가 더 높은 합산 점수를 얻는다. 두 값은 계산 방식을 보여주기 위해 만든 가상 예다.
상수 60은 순위 차이를 완만하게 반영하는 설정이고, 최종 몇 개를 반환할지 정하는 top-k는 별도 설정이다. 검색기별 후보 수와 가중치도 비교할 수 있다. 의미 질문과 식별자 질문의 결과를 나누어 보면 어떤 쪽이 개선됐는지 알 수 있다.
14. 부모·자식 청킹과 리랭킹은 서로 다른 부분을 바꾼다
부모·자식 청킹은 작은 문장을 검색한 뒤 연결된 큰 조항을 가져온다. 자식에 부모 식별자와 본문을 연결해두면 “담당자 승인” 문장을 찾았을 때 기본 대여 기간과 다음 예약 조건까지 함께 전달할 수 있다.
같은 부모에서 자식 여러 개가 검색될 수 있으므로 부모 식별자로 중복을 제거한다. 처음 검색된 부모를 남겨 순서를 보존한다. 자식 6개에서 부모를 최대 3개로 추려도, 자식들이 한 조항에 속하면 최종 부모는 하나일 수 있다.
어떤 자식 문장이 검색을 일으켰는지도 남기면 원인을 추적하기 쉽다. 자식의 유사도 점수가 더 높다는 사실만으로 전체 품질 개선을 결론내리지 않고 정답 포함 여부와 최종 답변의 조건 보존을 비교한다.
Reranking(리랭킹)은 이미 찾은 후보의 순서를 다시 평가한다. Bi-encoder(바이 인코더)는 질문과 문서를 각각 벡터로 만들어 후보를 찾는다. Cross-encoder(크로스 인코더)는 질문과 문서를 한 쌍으로 함께 받아 관련성 점수를 계산한다. 후보를 먼저 줄이고 그 안에서 정밀하게 평가하는 두 단계로 구성할 수 있다. Sentence Transformers의 검색·리랭킹 설명
후보 20개를 찾아 리랭커로 평가한 뒤 상위 3개를 반환한다고 해보자. 첫 검색에서 정답이 빠졌다면 리랭커가 정렬하는 후보에도 정답이 없다. 후보 수를 늘리면 실제로 재평가한 문서가 얼마나 늘었는지와 지연 시간을 함께 본다. 문서가 11개뿐인 자료에서는 제한값 20과 50이 같은 후보 수를 만들 수 있다.
리랭커의 입력 상한도 따로 확인한다. 임베딩 단계에서 긴 문서를 수용했어도 질문·문서 쌍을 처리하는 리랭커 설정에서 잘릴 수 있다. 최초 유사도와 리랭커 점수는 서로 다른 계산이므로 같은 임계값을 곧바로 적용하기 전에 분포를 확인한다.
15. 실패한 질문을 기준으로 다음 실험을 고른다
개선할 때는 어떤 질문에서 어느 단계가 실패했는지를 먼저 적는다. 원인을 추정하고 관련된 조건을 한 번에 하나씩 바꾸면 결과를 해석하기 쉽다.
| 관찰한 문제 | 비교할 변경 | 함께 기록할 결과 |
|---|---|---|
| 승인 조건이 빠진다. | 청킹 경계·부모 문맥 확장 | 정답 포함과 답변의 조건 보존 |
| 정확한 장비 번호를 못 찾는다. | 식별자 입력·BM25·RRF | 식별자 질문의 점수 |
| 정답이 후보에는 있지만 뒤에 있다. | 리랭커·후보 수 | Hit@3와 질문당 지연 |
| 긴 조항에서만 실패한다. | 토큰 상한·재분할 | 절단 위치와 정답 순위 |
| 문서 등록이 오래 걸린다. | 배치 크기·계산 정밀도 | 처리량·최대 메모리·검색 품질 |
자기 문서로 평가 질문을 10개 이상 만들고 의미 질문과 식별자 질문을 섞어볼 수 있다. 기준선을 기록한 뒤 두 가지 이상 변경을 비교한다. 청킹·모델·질문을 한꺼번에 바꾸면 무엇이 점수를 바꿨는지 구분하기 어려우므로 변경 조건을 남긴다.
효과가 없거나 나빠진 시도도 기록한다. 평가 질문이 너무 쉬웠는지, 후보 수가 실제로 같았는지, 입력 형식이 맞지 않았는지 살펴본다. 확인한 원인과 아직 확인하지 못한 가설을 구분하면 다음 실험을 정하기 좋다.
프라이빗 RAG를 읽을 때는 데이터 이동 경로와 검색·생성 품질을 함께 확인한다. 내부에서 실행되는지, 필요한 근거를 찾는지, 답변이 그 근거를 제대로 사용하는지를 각각 설명할 수 있어야 한다.
AIFFEL의 프라이빗 RAG 학습 자료와 개인 복습 대본을 참고해 개념과 구현 흐름을 자기 말로 다시 정리했다. 본문의 장비 대여 규정과 산술 예시는 설명용이다. 이 글을 작성하면서 실제 GPU에 모델을 올리거나 검색 품질을 측정한 결과는 없다. 모델별 사용 조건은 본문에 연결한 공식 문서와 모델 카드에서 확인할 수 있다.