네이버 톡톡 AI 상담
Architecture
Stack
- Python 3.12
- FastAPI
- LangGraph
- OpenAI GPT-4.1
- Supabase
- Langfuse
External integrations
- Apollo API
- Naver TalkTalk
- OpenAI API
- Supabase PostgreSQL
- Langfuse
- Slack Webhook
Highlights
- LangGraph ReAct + 3 LangChain tools
- Hexagonal Ports & Adapters
- Langfuse LLM observability
문제정의
네이버여행 채널의 톡톡 상담 인입은 24/7 들어오지만 상담사는 영업시간에만 응대할 수 있다. 야간·주말 큐에는 사람의 시간이 부족했다.
도입 전 고객 문의 452건을 15개 카테고리로 분류해 자동화 가능 영역의 비중을 정량 분석했다.
| 분류 | 비중 |
|---|---|
| 예약·확정 문의 | 19.9% |
| 상품 문의 | 16.4% |
| 취소 문의 | 12.8% |
| 미팅·픽업 / 바우처 / 유효기간 / 환불 등 | 21.2% |
| 자동화 가능 합계 | 약 70% |
상품·예약·환불 규정은 Apollo API와 네이버여행 API(CAS)에 단일 진실원(SSoT) 으로 이미 존재한다 — 사람이 매번 시스템을 조회해 옮겨 적는 비효율을 자동화할 여지가 명확했고, 이 분석을 근거로 도입을 제안해 승인받았다.
목표는 세 가지로 좁혔다.
- 1차 응대 자동화 — FAQ·예약 조회·상품 정보 질의를 24시간 즉시 응답.
- 불확실하면 환각 대신 핸드오버 — LLM이 추론으로 답을 만들지 않고 상담사에게 깔끔히 넘긴다.
- 단일 추적 채널 — 사고를 사후 재현할 수 있게 Langfuse + Supabase 대화 로그를 일원화.
구현
V1 → V3 진화
운영 8개월 동안 시스템은 단일 ReAct → Supervisor + Sub-Agent로 두 번의 큰 진화를 거쳤다.
| 시기 | 구조 | 동기 |
|---|---|---|
| V1 (2025-07~08) | 단일 ReAct + Clean/Hexagonal 4계층 | 외부 SDK 격리, 빠른 출시 |
| V2 (2025-09~11) | AgentSkill 분리 · DI 컨테이너 · 지연 초기화 | 도구 간 보이지 않는 의존성 제거, 커버리지 94% 확보 |
| V3 (2026-01~03) | Supervisor + 5 Sub-Agent + 7 인텐트 라우팅 | 시스템 프롬프트가 5만자 넘어 규칙 간섭 발생 |
V1의 단일 ReAct가 상품·예약·취소·환불을 모두 안았더니, 도메인 추가마다 프롬프트가 부풀고 상품 규칙이 예약 응대에 잘못 적용되는 '규칙 간섭' 이 잡히기 시작했다. V3 전환은 그래서 필연이었다 — Supervisor가 7가지 인텐트로 라우팅하고, 각 Sub-Agent(Product / Reservation / Cancel·Refund / Counselor / Vision)는 도메인 프롬프트와 전용 툴만 들고 동작한다.
Hexagonal + Ports & Adapters
V1부터 유지해 온 원칙은 그대로다. 핵심 도메인(에이전트 의사결정)을 외부 SDK에서 격리:
- 포트:
ChatRepository,ProductRepository,ReservationRepository,HandoverService - 어댑터:
ApolloRepository(HTTP),NaverTalkTalkService(API),ConversationLogger(Supabase) - 부수효과는 어댑터로만 빠져나가게 강제 → LLM provider·메신저 채널 교체에 영향받지 않음
환각 방지 가드
운영하면서 잡힌 LLM 실수 패턴을 프롬프트와 코드 양쪽에서 막았다. 프롬프트만으로는 충분하지 않다.
| 사고 패턴 | 가드 |
|---|---|
| 사용자 발화로 예약 일자를 덮어쓰는 sycophancy | 예약 사실 single-source-of-truth 규칙 + use_date 입력 가드 |
| LLM의 요일 오인 (예: 토요일 투어를 금요일로 안내) | 시스템 메시지에 60일 날짜-요일 참조표 + 검증 규칙을 표 앞에 배치 → 요일 오답 0% |
| 취소 수수료 산술 환각 | 백엔드에서 결론을 선계산해 LLM에는 결과만 주입 |
| 환불 가능성 추론 | refund_info / nonRefundable 기반 응답만 허용, 추론 금지 |
| 상품코드 누락 시 무한 추측 | 3단계 에스컬레이션 (부드러운 재요청 → 상세 링크 안내 → 상담사 인계) |
관측 가능성
- Langfuse — Chain-of-Thought·도구 호출·토큰·latency 추적. flush로 실시간 전송 보장.
- Supabase — 대화 영구 로그 (
async log, fire-and-forget). - Slack — 전역 예외 핸들러가 함수명·예외 클래스·사용자 컨텍스트를 묶어 즉시 발송. 폴링 헬스체크는 제거하고 웹훅 트래픽을 생존 신호로 전환해 인프라 비용을 줄였다.
- 로그 안정성 — KST 일별 회전, PID 기반 독립 파일로 멀티 인스턴스 쓰기 충돌 차단 → 데이터 유실률 0%.
출시
배포는 처음부터 전 트래픽으로 갔다. 사전 베타 없이, 발생하는 문제는 빠르게 잡고 잘못된 응답은 상담사가 재상담으로 커버하는 운영 모델이다. 이 선택 덕에 실제 사용 패턴 데이터가 첫 주부터 쌓였다.
초기 두 주는 인프라 사고 다발 구간이었다.
- 웹훅 URL이 계속 바뀌어 네이버톡톡 설정 페이지에서 5회 이상 수동 수정
- Gunicorn × uvicorn 워커 튜닝이 안 맞아 응답 지연 → 네이버톡톡의 5초 응답 제한을 초과 → 웹훅 중복 전송
- Containerfile 최적화 부재로 빌드 5분+
해결 순서대로:
- 멀티 스테이지 빌드 — 최종 이미지 절반 이하, 보안성 향상
- Gunicorn(uvicorn worker) 워커·타임아웃을 Cloud Run 리소스에 맞춰 고정
- 즉시 200 + BackgroundTask — 웹훅을 받자마자 200 OK 반환하고 무거운 AI 추론은 백그라운드로 분리
이후 Cloud Run에서 99.9%+ 가용성으로 안정화. cxdm CLI가 만든 GitHub Actions 워크플로우 + Secret Manager + 환경별 태그 트리거(both 모드)로 stage/prod 배포가 표준화됐다.
결과학습
운영 8개월 누적 수치:
| 항목 | 결과 |
|---|---|
| 월평균 자동 응대 건수 | 2,000건 이상 · 상담사 개입 0 |
| 단순 반복 문의 처리 효율 | 60% 개선 |
| 테스트 커버리지 | ReservationApplicationService 기준 94% |
| 멀티에이전트 도입 후 도메인별 응답 정확도 | 약 40% 향상 |
| 요일 오답률 | 60일 참조표 도입 후 0% |
| Cloud Run 인시던트 | 초기 2주 다발 → BackgroundTask·워커 튜닝 이후 정상화 |
기술적으로 가져간 학습은 네 가지다.
- ReAct 코어는 결국 도메인 레이어로 끌려 내려간다. 초기엔 단일 ReAct로 충분했지만, 도메인이 늘수록 프롬프트·툴 결합도가 올라 결국 V3 Supervisor + Sub-Agent로 도메인 컨텍스트를 분리해야 했다.
- LLM은 사용자 발화를 너무 잘 믿는다. sycophancy 류 사고는 프롬프트만으론 안 막혔고, 데이터 source-of-truth 규칙을 코드로 강제하는 가드가 필요했다.
- 결정적 계산은 LLM에서 분리한다. 취소 수수료처럼 정답이 정해진 산술은 백엔드에서 선계산하고 LLM은 자연어 렌더링만 맡기는 게 환각을 가장 확실히 줄였다.
- Langfuse는 디버깅보다 회귀 추적에서 빛난다. 사고가 났을 때 어떤 프롬프트 버전·어떤 도구 호출에서 어긋났는지 되짚는 용도로 가장 가치 있었다.
운영 8개월에서 가장 회수율 높은 엔지니어링 패턴 하나를 꼽자면 2단계 폴백 조회다.
번호 형식을 사용자가 고민할 필요 없이 두 시스템을 자동으로 가로지른다 — 같은 패턴을 환불 정보 조회·상품 매칭에도 재사용했다.
Next
Vision 정확도 개선을 다음 분기 단일 우선순위로 둔다.
영수증·예약 스크린샷의 false negative를 줄여, 현재 Vision Agent가 놓치고 상담사로 인계되는 케이스를 자동 처리로 끌어들이는 것이 목표다. V3 멀티에이전트 구조에서 Vision Agent는 이미 별도 노드로 분리되어 있으므로 모델·프롬프트·후처리 개선이 다른 에이전트에 부수효과 없이 격리된 채로 진행 가능하다.