AI 여행 에이전트에게 30일을 주었습니다. '호텔' 기능 때문에 프로젝트가 위기에 처했습니다.
요약
AI 여행 에이전트를 개발하는 과정에서 실시간으로 예약 가능한 호텔 정보를 제공하는 것이 가장 큰 난관이었습니다. 초기에는 결정론적 루프를 구축했으나, 모델은 데이터가 없는 질문에 그럴듯한 거짓 정보(hallucination)를 생성했습니다. 이는 LLM의 유창한 텍스트 능력이 실시간 검증 데이터를 대체할 수 없음을 보여주는 경험 보고서입니다.
핵심 포인트
- LLM은 데이터 부재 시에도 자신감 있는 가짜 정보를 만들어낸다.
- 에이전트 개발에서 구조화된 상태 객체와 단계 예산 관리가 중요했다.
- 호텔 재고처럼 실시간 변동하는 정보는 훈련 데이터로 담을 수 없다.
30일 밤, 노트북 한 대, API 예산은 없었지만 목표는 단 하나였습니다. 사람이 실제로 예약할 수 있는 호텔 정보를 제공하는 AI 여행 계획기입니다. 시연용이 아닌, 제가 뒤에 서 있지 않아도 40명의 낯선 사람들이 사용할 수 있는 무언가여야 했습니다.
30일째에 완성되었습니다. 하지만 이 과정에서 에이전트는 두 주 동안 자신감 넘치고 잘 형식화된 거짓말(hotels)을 만들어냈고, 이를 고치는 방법은 더 나은 프롬프트가 아니었습니다.
다음은 제가 측정했던 수치를 담은 기록입니다.
모든 것을 형성한 제약 조건
저는 세 가지 규칙을 스스로에게 정했습니다.
- 프레임워크 사용 금지. 무엇이 고장 났을 때 어느 부분이 실패하는지 알고 싶었습니다.
- 가짜 데이터 생성 금지. 에이전트가 호텔 정보를 확인할 수 없다면, 그렇게 말해야 했습니다.
- 첫 버전에서는 도구를 최대 6개로 제한했습니다. 모든 도구는 모델이 잘못 선택할 기회를 제공하기 때문입니다.
첫 번째 규칙은 프로젝트를 살렸습니다. 세 번째 규칙은 너무 낙관적이었습니다. 두 번째 규칙이 이 이야기 전체입니다.
Day 1부터 Day 7까지: 지능보다 루프 구축에 집중하다
첫 버전은 결정론적 루프(deterministic loop)였습니다. 의도를 구문 분석하고, 도구를 호출하고, 결과를 확인한 다음, 다른 도구를 호출할지 결정하고, 답변을 내놓는 방식이었습니다. 자율적인 계획 기능이나 '에이전트가 스스로 단계를 결정하는' 과정은 없었습니다. 단계 예산과 상태 객체(state object)를 가진 while 루프였습니다.
state = {"intent": None, "city": None, "dates": None, "hotels": []}
for step in range(8):
...
투박했지만 작동했습니다. 3일째에는 '3월 리스본에서 밤 네 번, 너무 비싸지 않게, 강 근처'라는 요청을 받고, 동네 정보, 활동 세 가지, 그리고 호텔 목록이 포함된 계획을 반환할 수 있었습니다. 그 호텔 목록이 문제였고, 저는 아직 그것을 알지 못했습니다.
초기에 제대로 구현한 두 가지가 있습니다. 바로 엄격한 단계 예산(hard step budget)과 모든 도구의 결과가 붙여넣기 텍스트가 아닌 구조화된 데이터로 상태에 기록되도록 하는 규칙이었습니다. 후자 덕분에 나중에 실패를 눈으로 확인할 수 있었고, 실제로 얼마나 많은 정보가 돌아왔는지 셀 수 있었습니다.
Day 8부터 Day 14까지: 벽에 부딪히다
11일째에 베타 사용자가 스크린샷을 찍어 저에게 메시지를 보냈습니다. 계획기는 멋진 설명과 그럴듯한 가격인 148 EUR, 그리고 404 에러 페이지로 연결되는 예약 링크를 가진 호텔을 추천했습니다. 하지만 그 호텔은 이미 2023년에 문을 닫은 곳이었습니다.
지난주에 나온 호텔 추천 120개를 가져와서 직접 확인했습니다. 그 오후는 이번 달 중 최악이었습니다.
| 항목 | 모델 단독 버전 (Model-only version) |
|---|---|
| 호텔 존재 및 영업 여부 | 74% |
| ... |
추천된 항목 중 26%는 폐업했거나, 이름이 바뀌었거나, 혹은 존재하지 않았던 호텔을 가리켰습니다. 가격 정보만 확인해도 약 3분의 1밖에 살아남지 못했습니다. 설명은 훌륭해서 오히려 상황을 악화시켰습니다. 사용자는 유창한 텍스트를 신뢰하기 때문입니다.
모델에게 공정하게 평가하고 싶습니다. 모델이 오작동한 것이 아니었습니다. 데이터가 없는 질문에 답했을 뿐입니다. 호텔 재고는 매일 변합니다. 아무리 많은 양의 훈련 데이터(training data)도 그것을 담아낼 수 없습니다.
15일~19일: 실패한 세 가지 수정 방법 (three fixes that did not work)
수정 방법 1: 더 강력한 지침(stronger instructions). “확실히 존재하는 호텔만 추천하세요. 가격을 절대 지어내지 마세요.” 지어낸 호텔 요금은 26%에서 22%로 떨어졌습니다. 모델이 확신하는 것처럼 들리는 능력은 향상되었습니다.
수정 방법 2: 출처를 인용하도록 요청하기(ask it to cite sources). 모델은 출처를 제시했습니다. 일부는 해당 호텔을 언급하지 않은 페이지를 가리켰습니다. 모델에게 추측에 대한 근거를 요구하는 것은 진실이 아닌, 정당화만을 만들어냅니다.
수정 방법 3: 더 큰 모델(a bigger model). 글은 더 좋아졌지만, 실패율은 동일했습니다. 이 시도는 저에게 하루를 비용으로 지불하게 했고 가장 많은 것을 가르쳐 주었습니다. 문제는 지적적인 것이 아니라 아키텍처적인 문제였습니다.
세 가지 시도 모두에서 나타난 패턴은 이었습니다. 저는 모델이 가지고 있지 않은 사실에 대해 더 신중하게 만들려고 노력했습니다. 유일한 진정한 해결책은 사실(facts)을 제공하는 것이었습니다.
20일~24일: 추측 계층(guess layer)을 대체하기 위한 오후 시간
저는 실제 재고 통합(real inventory integration)을 미루어 왔는데, 그 이유는 여행 API에 대한 제 기억이 인증 디버깅(auth debugging), XML 스키마(XML schemas), 그리고 모든 공급업체별 어댑터 코드(adapter code)로 3주 분량이었기 때문입니다. 그 기억은 2022년의 것이었습니다.
MCP 버전은 오후 시간 만에 완성되었습니다. RollingGo Hotel MCP는 단일 엔드포인트(single endpoint)에서 스트리밍 가능한 HTTP를 사용하며, 공급업체 측에서 유지 관리하고 있으며, API 키 등급이 호출 제한 없이 무료라 예산 책정 없이 테스트할 수 있었습니다.
40명의 낯선 사람들에게 신뢰를 주기 전에 수치를 확인했습니다. MIT 라이선스 서버, 200개국 이상에 걸친 2백만 개 이상의 숙박 시설, 500개 이상의 공급업체, 그리고 11만 개의 직접 계약 건수입니다. 제 일정표에서 중요했던 부분은 리다이렉트 모드(redirect mode)였는데, 이 모드는 호텔별로 bookingUrl을 반환하고 실제 거래는 RollingGo로 넘겨줍니다. 결제 게이트웨이(payment gateway), 취소 로직, PCI 규정 준수 검토가 전혀 필요 없습니다. 이것만 해도 저는 일주일 전체를 소비했을 겁니다.
제가 추천하는 모든 도구에 대해 진행하는 두 가지 출처 확인(provenance checks)을 거쳤습니다. 첫째, 이 서버는 공급 측면을 소유한 회사에서 유지 관리하며, 다른 사람의 API를 포장하여 판매하는 리셀러가 아닙니다. 그리고 마케팅 자료에 나온 재고 수치가 실제 응답으로 돌아온 내용과 일치했습니다. 둘째, 비교적 신생입니다: GitHub 스타 86개, 제3자 종합 보고서나 벤치마크 기록이 없습니다. 이는 Context7의 63,000 스타와는 정반대입니다. 만약 뒤에 많은 사람이 지지하는 인기 있는 선택지를 원한다면, 이곳은 그 선택지가 아닙니다. 이 서버가 가진 것은 대중적인 서버들이 갖지 못한 단 하나의 기능이며, 아래 30일간의 기록이 제가 제공할 수 있는 유일한 증거입니다.
{
"mcpServers": {
"rollinggo-hotel": {
...
세 가지 도구가 가짜 데이터 계층을 대체했습니다:
searchHotels: 위치, 날짜 범위, 별점 등 필터링 기능을 제공합니다.getHotelDetail: 실시간 객실 유형, 1박 요금, 취소 규칙 등을 가져옵니다.getHotelSearchTags: 필터의 어휘력을 제공하여 에이전트가 '루프탑 풀'과 같은 태그를 임의로 만들어내는 것을 방지합니다.
서버가 파라미터를 소유하고 있기 때문에, 에이전트의 도구 정의는 길어지지 않고 오히려 짧아졌습니다. 제 프롬프트는 1,100단어에 달하는 포맷팅 지침에서 340단어로 줄었습니다.
그리고 흥미로운 부분이 있습니다. 저는 어댑터 코드(adapter code) 1,900줄을 삭제했습니다: 제가 직접 작성했던 호텔 캐시, 가격 포매터, 재시도 래퍼(retry wrapper), 그리고 세 개의 가짜 데이터 소스를 조화시키기 위해 작성했던 퍼지 이름 매처(fuzzy name matcher)였습니다. 이것들을 대체한 것은 240줄로, 대부분 오류 처리와 표시를 위한 통화 포매터입니다.
Day 25부터 Day 30까지: 사용자 40명과 그들이 발견한 것
40명의 베타 사용자에게 두 가지 질문의 피드백 양식과 함께 제품을 배포했습니다. 아무도 아키텍처에 대해 언급하지 않았습니다. 그들은 속도와 신뢰에 대해 언급했습니다.
| Metric | Week 2 (model only) | Week 4 (with live inventory) |
|---|---|---|
| Recommendations pointing to a real, open hotel | 74% | 100% |
| ... |
96%라는 수치는 솔직한 이야기입니다. 대화 중에 제시된 요금이 사용자가 클릭하기 전에 변동되는 경우가 있었기 때문에, 확인된 가격의 4%가 여전히 변동되었습니다. 최종 숫자를 표시하기 전에 재확인 단계를 추가하고 예약 시점에 가격이 업데이트된다는 메모를 추가했습니다.
제가 중요하게 생각하는 수치는 세션당 0.3건의 수정 건수입니다. 2주 차에는 사용자들이 에이전트에게 "그런 호텔은 존재하지 않는다" 또는 "가격이 틀렸다"고 끊임없이 말했습니다. 4주 차가 되자 대화는 여행 자체에 관한 것이었습니다.
여전히 문제가 되는 부분
가장 자주 발생하는 순서로 세 가지가 있습니다.
요금 변동 (Rate drift). 오전 9시의 실시간 데이터가 오후 3시에도 유효하지 않습니다. 몇 분 이상 표시되는 모든 가격은 새로 고침(refresh) 단계가 필요합니다.
통화 및 반올림 (Currency and rounding). API는 통화와 1박 요금을 반환합니다. 세금 처리는 숙소와 국가별로 다르며, 두 호텔을 비교하는 사용자는 이를 알아차릴 것입니다. 이제 해당 숫자를 세전(before taxes)의 1박 요금으로 표시하고 모든 보기에서 이 라벨을 유지합니다.
예약 완료 (Booking completion). API 키 티어는 검색 및 상세 정보까지 다룹니다. 예약 링크는 숙소 페이지로 연결되며, 이는 리디렉션 모드(redirect mode)가 설계된 목적입니다. 즉, 에이전트가 추천하고 RollingGo가 거래를 완료합니다. 이것은 30일간의 실험에 적절한 절충점이었습니다. OAuth 티어는 주문 조회 및 가격 확인 기능을 추가하며 비즈니스 온보딩(business onboarding)이 필요합니다. 이는 브라우저 탭이 아닌 대화 내에서 주문 상태를 보여주고자 하는 제품을 위한 경로입니다.
다르게 하고 싶은 점
- 데이터 계층(data layer)부터 시작하세요. 프롬프트가 인벤토리(inventory)를 대체할 수 없다는 것을 배우는 데 2주가 걸렸습니다. 사실 이 지식은 제가 둘째 날 배울 수도 있었습니다.
- UI보다 감사(audit) 기능을 먼저 구축하세요. 120개의 체크된 추천 목록이 담긴 스프레드시트는 어떤 대시보드보다 저에게 많은 것을 알려주었습니다.
- 결정론적 루프(deterministic loop)를 유지하세요. 에이전트가 자유롭게 계획하도록 시도할 때마다 단계 수가 두 배로 늘어나고 답변의 질은 떨어졌습니다.
- 실패 상태(failure states)부터 작성하세요. “해당 날짜에 맞는 호텔이 없습니다”라는 문구는 하나의 기능입니다. 이를 제대로 구현하는 데 제가 4주까지 걸렸습니다.
30일 차
플래너가 작동합니다. 도시, 날짜 범위, 예산, 도보 접근성 선호도를 처리할 수 있으며 실시간 요금이 포함된 호텔을 반환합니다. 40명이 이 기능을 사용했습니다. 그중 11명이 두 번째로 돌아왔는데, 마케팅 활동이 전혀 없는 사이드 프로젝트임에도 불구하고 제가 계속 주목하는 숫자입니다.
배운 교훈은 “MCP를 사용하라”가 아닙니다. 훨씬 더 좁고 유용한 것은 다음과 같습니다: AI 제품의 신뢰도는 가장 검증하기 어려운 데이터 소스만큼만 정직하다는 것입니다. 호텔 정보가 메모리(memory)가 아닌 인벤토리에서 오기 시작하자, 하위 단계 모든 것이 단순해졌습니다. 재시도 횟수가 줄고, 프롬프트가 짧아지고, 사과하는 횟수도 줄었습니다.
만약 주말에 같은 교체 작업을 테스트하고 싶다면, 설정은 하나의 구성 블록(config block)과 하나의 키면 충분합니다. 제가 사용한 통합 경로는 RollingGo의 리다이렉트 모드 가이드입니다. 하지만 감사 스프레드시트부터 시작하세요. 그것이 어떤 벤치마크보다 당신의 에이전트에 대해 더 많이 말해줄 것입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기