여행 데이터를 위한 대화형 AI 인터페이스 구축하기
요약
eSIMDB AI를 위해 자연어 입력을 구조화된 데이터베이스 쿼리로 변환하는 AI 채팅 레이어 구축 사례를 소개합니다. 기존 필터 UI의 한계를 극복하기 위해 미세 조정된 소형 LLM을 활용하여 저비용·고성능의 의도 추출 시스템을 구현했습니다.
핵심 포인트
- 자연어 입력을 구조화된 쿼리로 변환하는 NLP 추출 레이어 구축
- GPT-4 대신 미세 조정된 3B 파라미터 소형 LLM을 사용하여 지연 시간과 비용 최적화
- 10,000개 이상의 라벨링된 데이터를 통한 모델 학습 및 모호성 처리 전략
- 사용자 의도의 모호함을 투명하게 확인하는 인터랙티브 UX 적용
우리가 eSIMDB AI를 위한 AI 채팅 레이어를 구축했을 때, 핵심 과제는 말하기에는 매우 단순해 보였습니다. 바로 사용자의 자연어(Natural Language) 여행 설명을 가져와 15,000개 이상의 플랜에 걸친 구조화된 데이터베이스 쿼리(Database Query)로 변환하는 것이었습니다. 우리가 어떻게 접근했는지, 그리고 무엇을 배웠는지 소개합니다.
왜 단순히 필터만 사용하지 않았을까요?
eSIMDB의 첫 번째 버전은 목적지, 데이터 크기, 유효 기간, 예산을 선택하는 드롭다운 메뉴와 같은 전통적인 필터 기반 UI를 갖추고 있었습니다. 작동은 했지만 UX(사용자 경험) 측면에서 문제가 있었습니다. 여행자들은 데이터베이스 쿼리 파라미터(Parameter) 단위로 생각하지 않기 때문입니다. 그들은 "일본으로 10일 동안 여행을 가는데, 원격 근무를 해야 해서 20유로 미만의 핫스팟 사용이 가능한 플랜이 필요해"라고 생각합니다.
이러한 생각을 정확한 필터 선택으로 변환하려면, 사용자가 "일본"이 국가 코드 JP에 매핑된다는 점, "10일"은 유효 기간(Validity)으로 입력해야 한다는 점, "20유로 미만"은 price_max 파라미터가 필요하다는 점, 그리고 "핫스팟 사용 가능"이 tethering_allowed 불리언(Boolean) 값을 필터링해야 함을 알고 있어야 했습니다.
사용자들은 실수를 하고, 필터를 놓치며, 최적화되지 않은 결과를 얻었습니다. 사용자들이 필터를 올바르게 설정했다는 확신을 갖지 못했기 때문에 전환율(Conversion)도 낮았습니다.
NLP 추출 레이어
AI 시스템의 핵심은 비구조화된 입력(Unstructured Input)에서 구조화된 의도(Intent)를 추출하는 쿼리 이해 모델입니다:
class TripQuery:
countries: list[str] # ["JP"]
duration_days: int # 10
...
우리는 이 추출을 위해 여러 가지 접근 방식을 시도했습니다:
- 규칙 기반 정규 표현식 (Rule-based regex): 빠르지만 취약합니다. "10 days in Japan"은 작동했지만, "a week and a bit in JP"는 작동하지 않았습니다.
- 구조화된 출력을 사용하는 GPT-4 (GPT-4 with structured output): 정확도는 높지만, 요청당 비용이 비싸고 실시간 UI를 사용하기에는 지연 시간(Latency)이 너무 높습니다.
- 미세 조정된 소형 LLM (Fine-tuned small LLM, 현재 우리의 방식): 우리는 여행 의도 추출에 특화된 3B 파라미터 모델을 미세 조정(Fine-tuning)했습니다. 이 모델은 약 50ms 내에 실행되며, 모호성을 잘 처리하고 운영 비용이 저렴합니다.
학습 데이터(Training data)가 결정적인 투자였습니다. 우리는 실제 사용자의 표현 방식, 예외 케이스(Edge cases), 의도적인 모호성을 포괄하는 10,000개 이상의 예시 쿼리에 라벨을 붙였습니다.
모호성 처리하기
실제 사용자의 쿼리는 종종 모호합니다:
- "Paris" → 도시(프랑스, FR로 매핑)인가 아니면 사람의 이름인가?
- "10 days" → 유효 기간이 10일 이상(≥10)이어야 하는가, 아니면 정확히 10일인가?
- "unlimited" → 진정으로 무제한인가, 아니면 단순히 데이터 용량 제한이 매우 큰 것인가?
- "I might go to Switzerland too" → 필수 요구 사항인가, 아니면 선택 사항인가?
우리의 접근 방식은 다음과 같습니다: 신뢰도가 높은 속성(attributes)을 추출하고, 모호한 속성에는 합리적인 기본값(defaults)을 사용하며, 가정을 투명하게 드러내는 것입니다: "10일 이상의 유효 기간을 가진 프랑스 여행 계획을 검색 중입니다. 스위스 커버리지도 필요한지 알려주세요."
신뢰도가 낮은 추출의 경우, 추측하기보다는 명확한 질문을 던집니다. 관련 없는 결과를 반환하는 것보다 한 번 더 질문을 주고받는(round-trip) 것이 더 낫습니다.
계획 검색 아키텍처 (Plan Retrieval Architecture)
구조화된 여행 쿼리(TripQuery)가 확보되면, 검색은 2단계 프로세스로 진행됩니다:
1단계 — 거친 필터링 (Coarse filter): 필수 요구 사항(국가 커버리지, 최소 유효 기간, 지정된 경우 최대 가격, 필요한 경우 테더링 여부)과 일치하는 후보 계획을 가져오기 위한 SQL 쿼리를 실행합니다. 이 단계에서는 보통 100~500개의 후보 계획이 반환됩니다.
2단계 — 점수 산정 및 순위 지정 (Scoring and ranking): 사용자의 특정 컨텍스트를 바탕으로 복합 점수 모델(가격 효율성, 신뢰성, 커버리지 품질, 유연성, 기능, 활성화 속도)을 적용합니다. 정직한 트레이드오프(tradeoff) 설명을 곁들여 상위 3~5개를 보여줍니다.
트레이드오프 생성에는 LLM을 사용합니다. 점수가 가장 높은 상위 5개 계획과 원래의 쿼리를 LLM에 제공하고, 그 차이점을 쉬운 언어로 설명하도록 요청합니다. 이 부분이 사용자들이 가장 가치 있게 느끼는 지점입니다.
효과적이지 않았던 것
순수 벡터/임베딩 기반 검색 (Purely vector/embedding-based retrieval): 계획을 임베딩(embeddings)으로 인코딩하고 인코딩된 쿼리에 대해 유사도 검색(similarity search)을 수행하는 방식을 시도했습니다. 결과는 좋지 않았습니다. 가장 중요한 차원(데이터 크기, 국가 커버리지, 가격)은 임베딩이 잘 처리하지 못하는 구조화된 수치 속성(structured numerical attributes)이기 때문입니다. '필터링 후 순위 지정(filter-then-rank)'을 결합한 하이브리드 방식이 더 효과적입니다.
LLM이 모든 필터링을 수행하게 하는 경우: "모델이 알아서 판단하게 하자"는 방식은 유혹적이지만, 환각 (hallucination) 현상이 발생한 계획이나 가격을 제시하는 결과를 초래합니다. 데이터베이스로부터 구조화된 검색 (structured retrieval)을 수행하고, LLM은 이해와 설명 용도로만 사용해야 합니다.
현재 상태 및 미결 과제
저희의 평가 세트 (evaluation set)를 기준으로 볼 때, 시스템은 실제 사용자 쿼리의 95% 이상을 정확하게 처리합니다. 나머지 5%는 대부분 이례적인 엣지 케이스 (edge cases, 예: 매우 특이한 국가, 매우 구체적인 기술적 요구사항)입니다.
저희가 여전히 해결 중인 미결 과제들은 다음과 같습니다: "제공업체 X에 대해 더 자세히 알려줘"와 같은 쿼리를 효율적으로 처리하는 방법, 회원가입 없이 더 나은 개인화 (personalization)를 구현하는 방법, 그리고 더 긴 대화 과정에서의 멀티턴 컨텍스트 (multi-turn context) 유지 방법입니다.
전체 제품은 esimdb.ai에서 확인하실 수 있습니다. 댓글을 통한 아키텍처 (architecture) 논의는 언제나 환영합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기