문제 상황: 도메인 확장

  • 단일 도메인(Driver)으로 시작
  • 기존에는 일반적인 RDB 테이블에 embedding column을 추가하여 벡터 정보를 저장하도록 설계
    • 테이블을 특정하여 검색한다면, 정확도가 매우 높은 정보를 제공할 수 있음
    • 데이터 동기화 과정에서도 하나의 테이블을 사용하기 때문에 관리하기 편함
  • 도메인이 확장되면서 DB 테이블을 어떻게 구성할 것인지에 대한 문제
  • 만약 기존 방식을 유지한다면, 도메인이 확장되었을 때, 쿼리마다 어떤 테이블에서 검색해야 하는지를 선택하는 로직이 추가적으로 필요
    • 만약 잘못 판단한다면 원하는 정보를 영영 못 찾게 됨

전통적인 해결 방법: 단일 통합 벡터 테이블

  • SpringAI, Langchain 등이 일반적으로 사용하는 방식
  • Driver, Circuit, Result 등 모든 도메인의 텍스트 데이터를 vector_store라는 하나의 거대한 지식 창고 테이블에 몰아넣는 방법
  • 테이블 구조가 다른데 어떻게 한번에 저장?
    • 데이터베이스 구조 예시 (PostgreSQL + pgvector)
      • id (UUID): 벡터 문서 고유 ID
      • content (TEXT): LLM이 읽을 실제 문장 (예: “찰스 르클레르는 페라리 소속 드라이버이며…“)
      • embedding (VECTOR): content의 벡터값
      • metadata (JSONB): {"type": "driver", "ref_id": 16, "team": "Ferrari"}
    • 사용자가 “2023년 모나코 서킷 우승자가 누구야?”라고 물었다면, 시스템은 서킷 정보에 대한 content 1개와, 경기 결과 정보 content 하나, 총 두 개를 알아서 섞어서 가져옴
    • 이 결과를 보고 LLM은 정답을 생성

개발 방향

  • 단일 통합 벡터 테이블을 사용하는 것으로 결정
  • vector_store 테이블을 생성
  • 데이터를 OpenF1 API에서 가져와서 RDB에 저장할 때, metadatatype=driver를 달아서 테이블에 꽂아 넣기
  • 쿼리 벡터에 따라서 데이터를 가져오는 개수를 topK 값을 통하여 조절 가능
    • 보통의 RAG 시스템에서는 3~5 정도로 설정
  • 가끔 topK=N으로 설정했더니, 쿼리랑 별로 관계없는 엉뚱한 데이터까지 가져오는 경우 발생 가능
    • .similarityThreshhold(0.8) 처럼 최소 유사도 컷오프를 설정할 수 있음
    • 이러면 유사도가 80% 미만인 엉뚱한 데이터는 버려짐
    • Hallucination을 강력하게 방지

동작 테스트

  • 사용자의 질문 쿼리가 정상적으로 Adapter로 전달되는가?
  • 질문 쿼리에 대한 유사도 검색이 정상적으로 이루어지는가?
  • Assistant의 답변이 DB에 존재하는 객관적 사실을 반환하는가?
  • 존재하지 않는 데이터에 대하여 답을 지어내지 않고 정해진 프롬프트 규칙을 따르는가? (현재 DB에는 국적 정보가 null로 처리되어 있음)