본문으로 건너뛰기
10. 질의 생성과 라우팅

10. 질의 생성과 라우팅

✎ 이 문서 수정하기 GitHub에서 수정

모든 정보를 벡터 검색으로 찾는 것은 비효율적일 수 있다. 지역 소개는 문서 검색에 맞지만 특정 날짜의 빈 객실 수와 가격은 구조화된 DB에서 확인하는 편이 정확하다. 이 장은 자연어 질문을 저장소가 이해하는 질의로 바꾸고, 결과를 정리해 생성 모델에 전달한다.

10.1 자료 저장소에 맞는 질의

벡터 저장소는 유사한 자료를 찾고 관계형 DB는 명시적인 조건과 관계로 행을 조회한다. 그래프 DB는 개체 사이의 관계를 따라간다.

같은 “숙박 정보”라도 주변 분위기와 관광지 접근성은 문서에, 현재 가격과 재고는 거래 시스템에 있을 수 있다. 질문의 어떤 부분에 어떤 데이터가 필요한지 나눠야 한다.

Dense retrieval은 임베딩 공간의 유사성을, sparse 또는 lexical retrieval은 단어의 출현과 가중치를 활용한다. 메타데이터 필터는 그와 별도로 지역, 날짜, 문서 유형 같은 조건을 적용한다.

10.2 Self-querying

Self-querying은 자연어 요청에서 의미 검색용 문장과 구조화된 필터를 함께 만드는 방식이다. “Cornwall에 있는 박물관”에서 박물관을 검색할 표현과 지역 필터를 나눌 수 있다.

10.2.1 메타데이터 보강

검색할 기록에 지역, 출처, 자료 유형과 날짜 같은 필드를 준비한다. 필터는 실제 저장한 값과 타입이 맞아야 동작한다.

메타데이터를 모델로 생성한다면 틀린 태그도 들어갈 수 있다. 문서의 실제 지역과 모델이 추정한 지역을 구분하고, 허용 값과 누락 규칙을 정해야 한다.

TF-IDF나 BM25는 주로 단어 기반 검색의 가중치와 순위를 위한 기법이다. 지역 같은 명시적 메타데이터와 같은 것으로 취급하면 안 된다.

10.2.2 메타데이터가 있는 자료에 질의

직접 필터를 지정하거나 SelfQueryRetriever처럼 모델이 필터를 생성하는 구성 요소를 사용할 수 있다. 필드 설명과 허용 연산을 제공해야 잘못된 열 이름과 조건을 줄일 수 있다.

예를 들어 region = "Cornwall"과 의미 검색 문장 “가족이 방문할 박물관”을 함께 적용할 수 있다. 검색 대상이 줄어 정밀도가 좋아질 수 있지만 잘못 만든 필터는 필요한 자료를 완전히 제외한다.

사용자 권한은 모델이 생성하는 필터에 맡기지 않는다. 애플리케이션이 허용한 범위를 별도로 강제한 뒤 그 안에서 검색 조건을 적용해야 한다.

10.3 구조화된 SQL 생성

자연어를 SQL로 바꾸려면 모델이 실제 테이블과 열, 관계를 알아야 한다. 문법이 맞는 SQL도 존재하지 않는 열을 쓰거나 조인을 잘못하면 답이 틀린다.

10.3.1 SQLite

SQLite는 하나의 파일을 DB로 사용하고 별도 서버 없이 접근할 수 있어 실습에 편하다. 하지만 이 실행 방식이 외부 서비스와 같은 운영 조건을 제공한다는 뜻은 아니다.

SQL 문법과 사용 가능한 기능도 DB별로 다르다. SQLite 예제를 그대로 PostgreSQL에 적용할 수 있다고 가정하면 안 된다.

10.3.2 DB 구성과 연결

여행 예제는 목적지, 숙소와 특가 정보를 테이블로 나누고 키로 연결한다. 모델에는 실제 스키마와 필요한 관계를 제공한다.

숙소와 특가를 잘못 조인하면 같은 제안이 여러 번 나오거나 다른 지역의 가격이 붙을 수 있다. 테이블을 읽을 수 있는 것과 올바른 관계로 답하는 것은 다르다.

10.3.3 자연어에서 SQL 생성

질문, 스키마, DB 종류와 필요한 예시를 이용해 SQL을 만든다. 생성 결과에 코드 블록이나 설명이 붙을 수 있으므로 SQL 추출과 구문 검사도 필요하다.

“이번 주말”처럼 상대적인 날짜를 사용하면 기준 날짜와 시간대가 명확해야 한다. “가장 저렴한”이라는 표현도 총액인지 1박 요금인지, 객실 수와 세금이 포함되는지에 따라 질의가 달라진다.

10.3.4 SQL 실행

생성과 실행은 분리된 책임이다. 모델이 SQL을 만들 수 있다는 이유로 운영 DB에 임의 실행 권한을 줘서는 안 된다.

읽기 전용 자격 증명, 허용 테이블과 연산, 행 수와 실행 시간 제한을 적용한다. 사용자별 접근 범위도 DB 또는 애플리케이션에서 강제해야 한다.

문자열에서 코드 블록 표시만 제거하는 것은 검증이 아니다. 쓰기나 관리 명령을 막고 의도한 조회만 허용하는 통제가 필요하다. 파라미터가 있는 값은 DB 드라이버의 바인딩을 사용하는 편이 안전하다.

10.4 의미 기반 SQL

10.4.1 일반 SQL

일반 SQL의 조건과 조인은 정확한 필드와 규칙을 기준으로 한다. 문자열의 일치나 패턴 검색, 수치 비교와 집계를 수행할 수 있다.

의미적 유사성과 다르지만 표현력이 단순한 정확 일치에만 한정되는 것은 아니다. 전문 검색이나 DB별 확장 기능을 조합할 수도 있다.

10.4.2 의미 검색과 결합

pgvector 같은 확장을 사용하면 관계형 데이터와 벡터를 함께 두고 유사도 검색을 수행할 수 있다. “조용하고 해변 가까운 숙소”는 설명의 의미로 찾고 지역과 가격은 정확한 필터로 제한하는 식이다.

이름의 유사성만으로 같은 사람이나 같은 지역을 확정하면 안 된다. 벡터는 후보 검색에 도움이 될 수 있지만 식별과 권한 판정은 별도 기준이 필요하다.

10.4.3 임베딩 생성

검색할 설명 필드 등을 임베딩해 행과 연결한다. 모델, 차원과 입력 구성 방식을 고정하고 원문 변경 때 갱신해야 한다.

값이 바뀐 행에 오래된 임베딩이 남으면 질의 결과의 순위와 현재 내용이 어긋난다. 데이터 갱신과 재임베딩의 일관성이 필요하다.

10.4.4 의미 검색 실행

질문 벡터와 저장 벡터의 거리를 계산해 순서를 정한다. 사용하는 연산자가 유클리드 거리인지 코사인 거리인지 확인해야 한다.

SQL에서 벡터 값을 문자열로 임의 연결하기보다 지원되는 드라이버와 파라미터 타입을 사용한다. 의미 순위가 높아도 가격이나 날짜 조건을 충족한다는 뜻은 아니므로 필터와 함께 해석해야 한다.

10.4.5 자동 생성

의미 검색까지 포함한 질의를 모델이 생성하도록 할 수 있다. 그 경우에도 모델이 벡터 숫자를 상상해서 만드는 것이 아니라 임베딩 함수가 계산한 값을 쿼리에 전달해야 한다.

모델이 선택할 수 있는 필드와 연산을 제한하고 실행 전에 검증한다. 일반 SQL과 벡터 SQL 모두 실행 계층의 통제를 유지해야 한다.

10.4.6 얻을 수 있는 이점

관계형 필터와 조인, 유사도 검색을 같은 데이터 구조에 결합할 수 있다. 원문과 임베딩이 다른 시스템에 흩어질 때보다 일관성 관리가 단순해질 수 있다.

반면 인덱스 크기와 갱신 비용, 근사 검색의 성능을 확인해야 한다. 기존 DB에 넣는다는 사실만으로 모든 규모에서 효율적인 것은 아니다.

10.5 그래프 DB 질의

그래프는 개체를 노드로, 관계를 엣지로 표현한다. 특정 장소가 어느 지역에 속하고 어떤 교통수단으로 이어지는지처럼 관계를 따라가는 질문에 유용할 수 있다.

RDF의 주어, 술어, 목적어와 SPARQL, 속성 그래프의 노드와 관계 및 Cypher 등은 서로 다른 표현과 질의 체계다. 실제 저장소의 스키마와 문법을 제공해야 한다.

모델이 관계를 추론했다고 그 관계가 DB에 있는 사실은 아니다. 생성한 질의의 결과와 모델의 추가 추론을 구분해야 한다. 그래프를 사용한다고 근거의 정확성이나 설명가능성이 자동으로 보장되지는 않는다.

10.6 체인 라우팅

질문을 보고 적절한 검색 경로를 선택한다. 일반 관광 정보는 문서 검색, 실제 숙박 제안은 관계형 DB처럼 나눌 수 있다.

10.6.1 검색 경로 준비

각 경로는 같은 입력을 받아 최종 생성에 사용할 근거를 반환하도록 구성할 수 있다. 다만 문서와 SQL 행의 의미가 다르므로 공통으로 보낼 때 구조를 명시해야 한다.

필드 이름이 없는 튜플의 숫자를 모델이 알아서 해석하게 두면 가격과 할인율을 혼동할 수 있다. 열 이름과 단위를 포함한 구조가 더 명확하다.

10.6.2 Router

모델이 허용된 경로 중 하나를 선택하도록 구조화된 출력을 사용한다. 선택할 범주는 열거형 등으로 제한한다.

복합 질문을 한 경로로 강제로 보내면 일부 요구를 놓친다. 두 저장소가 모두 필요한지, 추가 확인이 필요한지 판단하는 정책도 필요하다. 모든 입력을 한 경로에 우겨 넣는 기본값은 실패를 감출 수 있다.

10.6.3 전체 RAG 연결

라우터의 선택에 따라 실제 검색을 수행하고 결과와 원래 질문으로 답을 만든다. 어떤 경로를 골랐는지와 검색한 근거를 함께 추적해야 한다.

라우팅이 잘못되면 이후 검색과 생성이 정상 실행돼도 답은 틀릴 수 있다. 최종 문장 평가 외에 경로 선택 자체의 정답도 비교할 필요가 있다.

10.7 검색 결과 후처리

10.7.1 유사도 기준

관련도가 낮은 후보를 제외하거나 거리가 큰 후보를 제거한다. 저장소별 점수의 의미와 범위를 확인해야 하며 같은 임계값을 모든 모델에 적용하면 안 된다.

필터 뒤 결과가 없으면 근거 부족으로 다룬다. 무조건 한 개를 남기면 관계없는 자료가 최종 답의 근거가 될 수 있다.

10.7.2 키워드 기준

필수 단어와 제외 단어로 자료를 거를 수 있다. 명시적인 제약을 반영하기 쉽지만 동의어와 부정 표현, 언어 차이에 취약하다.

“무료가 아니다”에도 “무료”라는 단어는 있다. 단어 존재만으로 문서의 의미를 판단할 수 없다는 한계를 기억해야 한다.

10.7.3 시간 가중치

최근 접근한 자료나 최근 생성된 자료에 가중치를 줄 수 있다. 다만 최근 접근은 자료의 실제 최신성과 다르다.

오래된 문서를 자주 검색하면 접근 시간이 계속 갱신될 수 있다. 규정과 요금처럼 현재 유효한 자료가 중요한 경우에는 발행일, 적용일과 원천 버전을 기준으로 판단해야 한다.

10.7.4 RRF

Reciprocal Rank Fusion은 여러 검색 결과의 순위를 결합한다. 서로 척도가 다른 점수를 직접 더하지 않고 각 목록에서의 순위를 사용한다.

개념적인 점수는 다음과 같다.

RRF(d) = 각 검색 목록에서 d가 나온 경우의 1 / (c + rank(d)) 합

c는 순위 차이의 영향을 완화하는 상수다. 실제 구현에서는 순위가 0부터인지 1부터인지와 상수 설정을 일관되게 해야 한다. 반환 문서 수 k와 혼동하지 않는다.

두 목록에서 문서 A가 각각 1위와 4위, B가 2위와 2위라면 두 문서의 각 역순위 점수를 합쳐 비교한다. 한 목록에만 등장한 문서는 그 목록의 기여만 가진다.

합치려면 동일 문서를 식별할 ID가 필요하다. 제목이 같다는 이유로 다른 버전의 문서를 하나로 보거나 같은 청크를 별개로 세면 순위가 왜곡된다. RRF도 근거가 틀렸는지 판단하는 사실 검증은 아니다.

질문

유효한 SQL이면 바로 실행해도 될까?

문법과 업무상 허용 범위는 다르다. 읽기 전용 권한과 실행 제한, 스키마와 조건 검증이 필요하다.

가장 최근에 검색된 자료가 가장 최신 자료일까?

접근 시점과 내용의 유효 시점은 다르다. 시간 가중치가 무엇을 측정하는지 확인해야 한다.

이전: 질문 변환 / 시리즈 목차 / 다음: 도구 기반 에이전트

마지막 수정 일자