Big Project · AI's EYE 29
챗봇, 대략적인 답변이 아닌 '신뢰'를 말하기까지
고객 안내용 챗봇 수치가 Dashboard와 조금 달라도 대략적인 답변이면 괜찮아 보인다. 왜 fallback과 multi-turn history까지 같은 계약을 지켜야 하는지 의문이다.
근거 · 프로젝트 문서 · 코드 · 테스트
1장: 챗봇 정보, ‘대략적’이면 괜찮을까?
솔라는 거실 소파에 앉아 스마트폰을 들여다보며 미간을 살짝 찌푸렸다. 화면 속에서는 솔라가 가장 좋아하는 떡볶이 가게의 주문 현황이 떠 있었다. 막 샤워를 마치고 나온 루나가 솔라의 표정을 보고는 옆에 조용히 앉았다.
“무슨 안 좋은 일 있어?”
“아, 언니. 별건 아니고… 그냥 좀 답답해서. 이 앱 말이야. 챗봇한테 배달 얼마나 걸리는지 물어보니까 ‘지금 주문하면 약 30분 정도 걸려요’라고 하거든. 그래서 주문했는데, 여기 주문 현황 페이지에는 ‘예상 도착 시간 55분’이라고 뜨네. 대체 뭐가 맞는 거야?”
솔라는 짜증이 섞인 목소리로 투덜거리며 화면을 루나에게 보여주었다. 챗봇의 대화창과 주문 상세 페이지가 나란히 떠 있었다.
“음… 25분이나 차이가 나네.”
“그치? 이게 한두 번이 아니야. 그냥 ‘안내용’이니까 대략적인 값만 알려주는 거라고 생각은 하는데… 막상 겪으니까 기분 나빠. 차라리 둘 중 하나만 보여주든가. 어느 장단에 맞춰야 할지 모르겠잖아. 괜히 챗봇한테 물어봤나 싶고.”
솔라의 말에 루나는 잠시 생각에 잠겼다. 솔라는 그저 배고픔에 칭얼대는 것이 아니었다. 사용자가 동일한 서비스 안에서 완전히 다른 두 개의 정보를 받았을 때 느끼는 혼란, 그리고 그로 인해 발생하는 미세한 불신에 대해 이야기하고 있었다.
“솔라 네 말대로, 아마 셔츠를 입은 챗봇과 유니폼을 입은 직원이 서로 다른 말을 하는 것처럼 느껴졌을 거야.”
“어? 셔츠랑 유니폼?”
“응. 챗봇은 좀 더 친근하게 말을 거니까 사복을 입은 안내원 같고, 주문 현황 페이지는 딱딱한 정보를 주니까 유니폼을 입은 공식 창구 직원 같지. 하지만 고객 입장에서는 둘 다 같은 가게 직원이잖아. 그런데 한 명은 30분, 다른 한 명은 55분이라고 말하는 상황인 거지. 누구를 믿어야 할까?”
루나의 비유에 솔라는 고개를 끄덕였다. “완전 그거지! 같은 가게인데 말이 다르니까, 이 가게 전체를 못 믿게 되는 느낌. 그냥 챗봇은 친절하게 대답하는 데만 집중하고, 실제 중요한 정보는 따로 관리하나 봐.”
솔라는 ‘챗봇은 안내용이므로 대략적인 답변도 허용된다’는 자신의 처음 생각을 떠올렸다. 머리로는 그럴 수 있다고 이해했지만, 막상 배고픈 고객의 입장이 되니 그 ‘대략적’인 답변이 얼마나 쉽게 ‘틀린’ 정보가 되고 신뢰를 깎아내리는지 온몸으로 체감했다.
“만약에 말이야,” 루나가 입을 열었다. “그 셔츠 입은 안내원이랑 유니폼 입은 직원이 같은 포스기(POS) 화면을 보고 이야기한다면 어떨까? 주방에서 ‘지금 주문 5개 밀려있고, 55분 걸립니다!’라고 띄워놓은 그 화면 말이야.”
“그랬다면 둘 다 똑같이 55분이라고 말해줬겠지. ‘지금 주문하시면 55분 정도 걸릴 것 같아요!’ 하고.”
“맞아. 사용자가 보는 챗봇이든, 주문 현황 페이지(Dashboard)든, 혹은 가게 주인이 보는 관리자 페이지든, ‘예상 대기 시간’이라는 핵심적인 정보(KPI)는 단 하나의 진실된 출처(Single Source of Truth)에서 가져와야 해. 시스템 용어로는 이걸 ‘공통 API’를 사용한다고 말할 수 있겠지.”
루나의 설명에 솔라는 자신이 겪은 혼란의 원인이 명확해지는 것을 느꼈다. 챗봇과 주문 현황 페이지가 서로 다른 정보원을 참고했거나, 혹은 챗봇이 아예 자체적으로 만든 ‘대략적인’ 숫자를 말하고 있었던 것이다.
“그럼 처음부터 그렇게 만들면 되는 거 아니야? 왜 굳이 다르게 만들어서 사람 헷갈리게 하는 거지? 개발자들이 그걸 몰랐을까?”
“모르진 않았을 거야. 하지만 때로는 시스템에 예상치 못한 부하가 걸리기도 해. 예를 들어, 갑자기 주문이 폭주해서 AI 챗봇 모델을 돌리는 서버에 과부하가 걸렸다고 상상해 봐. 이럴 때 챗봇 기능을 완전히 꺼버릴 순 없으니, AI의 복잡한 기능은 잠시 멈추고 최소한의 답변만 하도록 하는 ‘간단 모드’로 전환할 수 있어.”
“아, 비상 상황 같은 거구나.”
“응. 여기서 솔라 네가 아까 한 질문이 중요해져. 이 ‘간단 모드’일 때, 챗봇은 ‘예상 대기 시간’을 어떻게 말해야 할까? AI가 없으니 계산을 못 하니까, 그냥 ‘20분에서 30분 사이’처럼 미리 저장해 둔 문구를 보여주는 게 가장 쉬운 방법일 거야.”
솔라는 잠시 생각에 잠겼다. 쉬운 방법이지만, 방금 전 자신이 겪었던 문제와 똑같은 상황이 발생할 수 있었다. 실제 대기 시간은 1시간이 넘을 수도, 10분밖에 안 될 수도 있는데 말이다.
”…아니야. 그러면 안 돼. 비상 상황일수록 더 정확한 정보를 줘야지. AI가 죽었더라도, 어떻게든 그 ‘공통 API’에 접속해서 진짜 대기 시간을 가져와서 말해줘야 해. ‘지금 시스템에 부하가 많아 자세한 안내는 어렵지만, 예상 대기 시간은 55분입니다.’ 라고 말하는 게 맞아.”
솔라는 스스로 답을 내놓고는 놀란 표정을 지었다. ‘대략적이면 괜찮다’던 처음의 생각은 온데간데없었다. 어떤 상황에서든, 특히 시스템이 불안정한 비상 상황에서조차 고객에게 전달되는 핵심 지표는 단 하나의 출처에서 나와야 한다는 것을 깨달은 것이다. 그것이 사소한 편의성이나 기술적 한계를 뛰어넘는, 서비스와 사용자 사이의 ‘신뢰’의 문제라는 것을.
“그래. 바로 그거야. 429 에러(서버 과부하 신호)가 떠서 ‘간단 모드’로 동작하더라도, 챗봇이 말하는 대기 주문 수나 예상 시간은 웹사이트의 대시보드와 동일한 KPI, 즉 공통 API 값을 사용해야 한다는 원칙. 이게 무너지면 사용자는 앱이 보여주는 어떤 숫자도 믿지 않게 되니까.”
솔라는 고개를 끄덕였다. 이제 왜 챗봇이 대략적인 숫자를 말하면 안 되는지 확실히 알 것 같았다. 하지만 한 가지 궁금증이 더 생겼다.
“알겠어. ‘대기 시간’ 같은 단발성 정보는 이제 이해가 가. 그런데 챗봇이랑은 계속 대화를 이어가잖아. 내가 전에 했던 말을 기억했다가 다음 답변에 활용하기도 하고. 그렇게 대화가 길어지거나, 아니면 아예 AI 모델이 완전히 고장 나서 아무 말도 못 하는 상황이 되면 어떡하지? 그럴 때도 이 ‘일관성’이라는 계약을 지킬 수 있는 거야?“
2장: 맥락을 잃지 않는 대화, 실패를 속이지 않는 안내
솔라의 질문이 끝나자, 루나는 잠시 말이 없었다. 그녀는 대답 대신, 솔라가 보고 있던 떡볶이 앱을 닫고 다른 쇼핑몰 앱을 열었다. 그리고는 챗봇 아이콘을 눌러 대화창을 띄웠다. 루나의 손가락이 화면 위에서 빠르게 움직였다.
나: 새로 나온 파란색 등산 재킷 재고 있어?
챗봇: 네, 고객님! 푸른빛이 감도는 방수 등산 재킷 말씀이시군요. 현재 M, L 사이즈 재고 있습니다.
거기까지는 괜찮았다. 루나는 곧바로 다음 질문을 입력했다.
나: 그럼 M 사이즈로 주문하고 싶은데, 제주도까지 배송은 얼마나 걸릴까?
챗봇: 어떤 상품을 말씀하시는지 모르겠어요. 상품명을 다시 알려주시겠어요?
“이것 봐.”
루나가 스마트폰을 솔라에게 건넸다. 솔라는 화면 속 황당한 대화를 보고 실소를 터뜨렸다.
“푸하. 방금 자기가 말해놓고 까먹었네. 금붕어야 뭐야.”
“흔한 일이지. 그리고 이건 어때?”
루나는 다시 스마트폰을 받아 들고는, 이번엔 아무런 맥락 없는 단어를 입력했다. Gemini 429. 그러자 챗봇은 잠시 멈칫하더니 전혀 다른 톤의 메시지를 툭 뱉어냈다.
챗봇: 죄송합니다. 내부 시스템 오류가 발생했습니다. (오류 코드: 500)
솔라는 미간을 찌푸렸다. “이번엔 그냥 고장 나 버렸네. ‘내부 시스템 오류’라니. 고객이 저 코드 번호를 알아서 뭐 하라고. 그냥 대화가 막혀버렸잖아.”
“방금 네가 본 두 가지 상황이, 네가 던진 질문에 대한 내 대답이야. 대화가 길어질 때, 그리고 AI 모델이 완전히 고장 났을 때.”
루나는 스마트폰 화면을 끈 채 테이블에 내려놓았다. 이제 그들의 시선은 허공에서 마주쳤다. 솔라는 방금 본 두 가지 실패 사례를 떠올렸다. 첫 번째는 맥락을 잃어버린 ‘기억상실’ 챗봇, 두 번째는 무책임하게 대화를 끊어버린 ‘시스템 오류’ 메시지였다.
“첫 번째는 그냥 개발 실수 아니야? 이전 대화 내용을 다음 질문에 같이 안 넘겨줘서 그런 거잖아. 두 번째는… AI 모델이 감당 못 하는 질문이 들어오니까 그냥 뻗어버린 거고. 어쩔 수 없는 거 아닌가?”
솔라는 여전히 대화의 맥락(history)을 유지하는 것과, 시스템이 멈췄을 때의 대처(fallback)는 앞서 이야기한 ‘KPI 일관성’과는 다른 문제라고 생각했다. 하나는 단순 기술 구현 문제, 다른 하나는 불가항력의 오류처럼 보였다.
“그렇게 분리해서 볼 수도 있어.” 루나가 조용히 입을 열었다. “하지만 사용자 입장에선 어떨까? 방금 ‘금붕어’ 챗봇이랑 대화하면서 어떤 기분이 들었어? 신뢰가 생겼어?”
“아니. 바보 같았지. 이 챗봇한테는 뭘 물어보면 안 되겠다는 생각이 들었어.”
“맞아. 사용자가 챗봇에게 말을 건넬 때마다, 프런트엔드는 이전 대화 history를 다음 /chat 요청에 전달해야 해. 이건 단순히 기술적인 데이터 전달이 아니야. 우리가 나누는 대화처럼, 상대방이 내 말을 ‘기억하고 있다’는 신호를 계속 보내는 거지. 그 신뢰의 끈이 끊어지는 순간, 사용자는 챗봇을 더 이상 대화 상대로 여기지 않고 그냥 고장 난 기계로 볼 뿐이야.”
루나의 설명에 솔라는 고개를 끄덕였다. 대화 기록 전달이 단순한 ‘구현’이 아니라, 사용자 경험의 핵심적인 ‘계약’이라는 사실을 깨달았다.
“그럼 두 번째, AI 모델이 고장 났을 때는? 그건 정말 어쩔 수 없는 거 아니야? ‘내부 시스템 오류’라고 알려주는 게 최선일 수도 있잖아.”
“최선일까? 우리가 만들던 떡볶이 앱으로 돌아가 보자. 사용자가 ‘대기 시간 알려줘’라고 물었는데, 순간적으로 AI 모델에 과부하가 걸려 답변을 생성하지 못했다고 상상해 봐. 그때 ‘내부 시스템 오류’라고 답하는 게 맞을까?”
솔라는 잠시 생각에 잠겼다. 상상만 해도 짜증이 났다. 사용자는 그저 대기 시간이 궁금했을 뿐인데, 뜬금없는 시스템 오류 메시지를 마주하면 얼마나 당황스러울까.
”…아니. 그러면 안 돼. 차라리… AI가 없어도, 지난번에 말한 ‘공통 API’에서 대기 시간 값이라도 가져와서 ‘지금 AI 답변은 어렵지만, 예상 대기 시간은 55분입니다.’ 라고 말해주는 게 훨씬 낫지.”
스스로 답을 내놓은 솔라의 눈이 커졌다. 아, 하고 작은 탄성이 터져 나왔다. 별개라고 생각했던 두 문제가 하나로 연결되는 순간이었다.
“바로 그거야. AI 모델이 죽었을 때, 우리는 임의의 숫자를 새로 만들거나 ‘오류’라며 대화를 포기하는 대신, keyword fallback과 공통 API라는 두 번째 안전장치를 사용해야 해. ‘대기 시간’이라는 키워드를 인식해서, 비록 화려한 문장은 아니더라도 시스템의 단 하나의 진실된 정보(공통 API)에 근거한 답변을 돌려주는 거지. 이것 역시 ‘일관성’이라는 계약을 지키는 거야. 어떤 상황에서도 사용자를 속이지 않고, 가능한 한 최선의 진실을 전달하겠다는 약속.”
솔라는 이제 완전히 이해했다. 대화의 맥락을 기억하는 것, 그리고 시스템이 실패했을 때조차 정직하고 유용한 정보를 제공하려는 노력. 이 모든 것이 결국 서비스와 사용자 사이의 신뢰를 만드는 벽돌 하나하나와 같았다. 처음에는 KPI만 맞으면 된다고 생각했지만, 이제는 대화의 모든 과정, 심지어 실패의 순간까지도 그 계약 안에 포함되어야 한다는 것을 알았다.
생각을 정리하던 솔라의 얼굴에 새로운 질문이 떠올랐다.
“좋아, 이제 알겠어. 우리 시스템 안에서는 어떻게든 일관성을 지키기 위해 노력해야 한다는 거. 그런데 만약 우리가 만든 이 챗봇을 카카오톡이나 다른 메신저 앱 안에서 쓰는 거라면 어떡하지? 만약 그쪽 플랫폼 정책이 ‘우리 플랫폼에서 발생하는 모든 서버 에러는 무조건 ‘시스템에 문제가 발생했습니다’라는 고정된 문구로만 보여줘야 합니다’라고 정해져 있다면? 우리가 아무리 정중한 fallback 메시지를 준비해도 소용없는 거 아니야?“
3장: 외부 채널의 제약 안에서, 챗봇은 어떻게 신뢰를 지킬까?
솔라는 자신의 질문에 스스로가 만든 벽을 느꼈다. 우리 서비스 안에서는 어떻게든 일관성을 지키고 사용자 경험을 보호할 수 있다. 하지만 우리가 통제할 수 없는 외부 채널, 이를테면 국민 메신저인 카카오톡 안에서는 어떨까? 그곳의 규칙이 우리의 원칙과 충돌한다면, 결국 그 규칙을 따를 수밖에 없는 것 아닐까?
생각을 증명이라도 하듯, 솔라는 소파에 기댄 채 스마트폰으로 은행 앱의 카카오톡 채널 챗봇에게 말을 걸었다. 자동이체 날짜를 변경하는 복잡한 시나리오를 일부러 물어봤다. 아니나 다를까, 몇 번의 대화가 오가다 챗봇은 멈춰 섰다. 그리고 화면에 나타난 것은 차가운 회색 말풍선이었다.
현재 서비스 이용이 원활하지 않습니다. 잠시 후 다시 시도해 주세요.
솔라는 허탈한 웃음을 지었다. “이것 봐. 결국 이렇게 되잖아.” 솔라는 화면을 루나에게 보여주었다. 우리가 아무리 정성껏 실패 안내 메시지를 만들어도, 카카오톡이라는 플랫폼이 그냥 ‘서비스 이용 불가’라고 막아버리면 끝이다. 이건 개발자가 게을러서가 아니라, 어쩔 수 없는 기술적 제약처럼 보였다. “이런 상황에서도 ‘일관된 계약’을 지키라는 건 너무 가혹한 거 아니냐고.”
루나는 솔라의 스마트폰 화면과, 그 너머 체념한 듯한 솔라의 표정을 번갈아 보았다. 그리고는 조용히 자신의 스마트폰을 들어 무언가를 그리기 시작했다. 간단한 다이어그램이었다.
“솔라, 네가 지금 본 상황은 아마 이럴 거야.”
루나가 보여준 화면에는 두 개의 상자와 화살표가 그려져 있었다.
[ 현재 상황 ]
사용자 → 카카오톡 서버 → 우리 챗봇 서버
- 우리 챗봇 서버의 AI Agent가 처리 중 실패함 (내부 오류 발생)
- 우리 서버가 카카오톡 서버에 ‘서버 에러(500 응답)‘를 보냄
- 카카오톡 서버는 ‘서버 에러’ 신호를 받으면, 정책에 따라
서비스 이용이 원활하지 않습니다라는 고정 메시지를 사용자에게 보여줌
“맞아. 정확히 내가 생각한 그대로야. 카카오톡의 정책이니까 우리가 어쩔 수 없는 거잖아.” 솔라는 자신의 생각이 틀리지 않았음을 확인받은 듯 말했다.
“그럼 이건 어때?”
루나는 다이어그램 아래에 새로운 흐름을 추가했다.
[ 우리가 해야 할 일 ]
사용자 → 카카오톡 서버 → 우리 챗봇 서버
- 우리 챗봇 서버의 AI Agent가 처리 중 실패함 (내부 오류 발생)
- 우리 서버가 이 실패를 스스로 감지하고, ‘죄송합니다. AI 답변에 오류가 발생하여…’ 와 같은 정중한 안내 문구를 만듦
- 우리 서버가 이 안내 문구를 ‘성공(200 응답)’ 신호에 담아 카카오톡 서버로 보냄
- 카카오톡 서버는 ‘성공’ 신호를 받았으므로, 우리가 보낸 안내 문구를 그대로 사용자에게 보여줌
솔라는 두 번째 다이어그램을 한참 동안 들여다보았다. 처음에는 이해가 되지 않았다. 실패했는데 어떻게 ‘성공’을 보낸다는 걸까. 하지만 곧 그 구조의 핵심을 깨닫고는 아, 하고 작은 소리를 냈다.
“속이는 거네?”
“사용자를? 아니면 카카오톡 서버를?” 루나가 미소를 지으며 물었다.
“카카오톡 서버를! 카카오톡은 우리 서버가 진짜 성공했는지, 아니면 성공한 척하는지는 모르잖아. 그냥 ‘성공’ 신호가 오면 메시지를 그대로 띄워주고, ‘실패’ 신호가 오면 자기들 마음대로 오류 메시지를 띄우는 거니까. 우리는 그 규칙을 역이용하는 거구나. 내부적으로는 실패했지만, 고객에게 무례한 경험을 주지 않기 위해 겉으로는 ‘성공적으로 안내 메시지를 전달했습니다’라고 말하는 거네.”
솔라의 눈이 반짝였다. 외부 채널의 제약은 피할 수 없는 벽이 아니었다. 그 제약의 규칙을 정확히 이해하고, 그 안에서 사용자 경험을 지킬 방법을 찾는 영리한 게임이었다. Kakao는 비-200 응답이면 고객에게 시스템 오류만 보여주므로 Agent가 실패해도 정중한 안내를 담은 200 스킬 응답으로 감싼다. 이 문장은 기술적 한계에 대한 포기가 아니라, 그 한계 속에서 신뢰를 지키려는 적극적인 전략이었던 것이다.
모든 퍼즐이 맞춰진 기분이었다. 처음에는 ‘대략적이면 어때’라고 생각했던 챗봇의 숫자가, 왜 시스템 전체의 KPI와 같아야 하는지. 대화의 맥락을 잃지 않는 것이 왜 중요한지. 그리고 이제는 우리가 통제할 수 없는 영역에서조차 어떻게 사용자와의 약속을 지켜야 하는지. 그 모든 것이 ‘일관성’이라는 하나의 원칙, 그리고 ‘신뢰’라는 궁극적인 목표를 향하고 있었다.
솔라는 다시 자신의 스마트폰을 들었다. 그리고는 새 메모장을 열어, 조금 전까지 이야기했던 내용들을 자신의 언어로 정리하기 시작했다. 만약 자신이 챗봇을 만드는 사람이 된다면, 절대 잊지 말아야 할 원칙들을.
- 1. 숫자는 하나의 목소리로: 챗봇이 말하는 모든 KPI(대기 시간, 재고 등)는 웹사이트, 앱과 동일한 공통 API를 사용한다. 비상 상황(간단 모드)에서도 예외는 없다.
- 2. 대화는 기억을 담아서: 모든 대화 요청에는 이전 대화 기록(history)을 포함하여, 사용자가 같은 말을 반복하지 않게 한다.
- 3. 실패는 솔직하고 친절하게: 시스템 내부 오류가 발생해도, 채널의 제약을 파악하여 무뚝뚝한 오류 코드 대신 우리가 만든 정중한 안내 메시지로 감싸서 전달한다.
세 번째 원칙 아래에, 솔라는 괄호를 치고 덧붙였다. (카카오톡의 경우: 실패해도 200 성공 코드로 응답!)
이 짧은 메모는 단순한 요약이 아니었다. ‘대략적인 답변이면 괜찮다’고 생각했던 사용자의 혼란에서 출발해, 시스템의 이면을 탐험하며 얻어낸 솔라 자신만의 ‘챗봇 설계 계약서’였다. 이제 솔라는 어떤 챗봇을 만나더라도, 그 챗봇이 사용자와 어떤 계약을 맺고 있는지, 그리고 그 계약을 얼마나 성실하게 지키고 있는지 판단할 수 있는 자신만의 기준을 갖게 된 것이다.