백여 줄 파이썬으로 AI 상담 '사람 연결' 고가용성 구현
9월 1일 시행된 고객상담 인공지능 협업 국가표준 요구사항을 참고해, 한 개발자가 백여 줄 파이썬으로 AI 상담에서 사람 상담원 연결을 안정적으로 처리하는 구조를 소개했다.
중국어 원문을 AI로 번역했습니다. 고유명사와 수치는 원문 표기를 우선하며, 중요한 판단에는 아래 출처 원문을 함께 확인하세요.
9월 1일, GB/T 47746—2026 《고객 연락 서비스 인공지능과 인적 고객 서비스의 협동 요구사항》이 정식으로 시행되었다. 이는 국내 최초로 인간-기계 고객 서비스 협동을 전담 규율하는 국가 표준으로, 몇 가지 강제 요구사항은 요구사항 문서에 그대로 옮겨 적을 만하다:
- 명확하고 눈에 띄는 인적 서비스 진입점을 반드시 마련해야 하며, 인적 채널을 숨기는 것은 엄격히 금지한다;
- 사용자가 명확히 인적 상담원 연결을 요구할 때는 반드시 신속히 전환해야 한다;
- 전환 후에는 고객 정보를 동기화해야 하며, 같은 내용을 반복해서 묻지 않아야 한다;
- 가격, 할인, 환불, 배상, 계약 변경 등의 사항과 관련해서는 AI가 규칙을 설명하고, 자료를 수집하고, 작업 티켓을 생성할 수 있지만, 최종 확인은 인적 상담원 또는 규범화된 절차를 통해 완료해야 한다.
현실은 시행 두 달이 지나도 언론의 실측에서 여전히 실패가 나온다: 어떤 티켓 구매 플랫폼에서는 기자가 세 번 「인적 상담원 연결」이라고 말해야 연결되었고, 어떤 이는 40분을 기다려도 연결되지 않아 표가 무효 처리되었으며, 심지어 로봇이 사용자의 고집을 인식한 후 바로 전화를 끊어버리는 경우도 있었다. 중국소비자협회 상반기 데이터에서 애프터서비스 관련 불만이 26.79%를 차지했으며, 「AI 고객 서비스의 허위 약속, 인적 연결의 어려움」이 새롭게 부상하는 이슈이다.
이러한 문제들은 모델 능력과는 거의 무관하며, 대부분이 세션 상태 관리 와 제품 전략 의 문제이다. 아래에 바로 실행할 수 있는 골격을 제시한다.
기술 요점
네 가지 제약이 구현 방식을 결정한다:
1. 인적 상담원 연결은 반드시 규칙이어야지, 모델 판단이어서는 안 된다. 의도 인식은 사람에게, 최후의 보루는 규칙 엔진에게 맡긴다. 표준에 쓰인 「명확한 요구」「시간 초과」「고위험 시나리오」는 본질적으로 모두 열거 가능한 트리거 조건이다. 2. 세션에는 상태 카운트가 있어야 한다. 최후의 보루 대응 문구가 연속으로 몇 번 적중했는지, 총 몇 라운드에 도달했는지를 모두 기록해야 한다. 대부분의 온라인 사고는 바로 이 두 개의 카운터가 없어서 발생한다. 3. 위험 키워드가 업무 키워드보다 우선한다. 「불만」「규제」「12315」「인신 안전」 같은 단어는 반드시 독립 채널로 가야 하며, 「셀프서비스 절차」에 먹혀서는 안 된다. 4. 전환은 맥락을 함께 가져가야 한다. 전환된 후 인적 상담원이 이전 기록을 볼 수 없다면, 사용자가 억울한 사연을 다시 이야기해야 하는 것과 같아 경험이 두 번째로 무너진다.
실행 가능한 예시
순수 표준 라이브러리이며, 복사하면 바로 실행된다. 핵심은 「계속 대화」인지 「인적 상담원 연결」인지를 반환하는 route() 함수 하나이다.
세 개의 코드 블록을 순서대로 이어 붙이면 바로 실행 가능 from dataclasses import dataclass from typing import Callable
--- 명확한 상담원 연결 요구: 매칭 즉시 전환(국가 표준의 강제 요구 사항) --- ESCALATE_PATTERNS = ("转人工", "人工客服", "找真人", "真人客服", "我要人工")
--- 위험 단어: 매칭 즉시 에스컬레이션, 우선순위 최고 --- RISK_PATTERNS = ("投诉", "监管", "12315", "起诉", "曝光", "受伤", "人身", "诈骗")
MAX_TURNS = 4 MAX_REPEAT = 2
--- 세션 상태: 세 개의 카운터가 모두 여기에 --- @dataclass class Session: sid: str turns: int = 0 fail_hits: int = 0 # 폴백 화법 연속 매칭 횟수 last_reply: str = "" history: list = None
def __post_init__(self): self.history = []
def route(session: Session, text: str, reply_fn: Callable[[str], str]): """(action, 응답 텍스트)를 반환한다.""" text = (text or "").strip() session.turns += 1 session.history.append(("user", text))
1. 위험 단어가 최우선, 어떤 셀프서비스 만류도 하지 않음 if any(k in text for k in RISK_PATTERNS): return "escalate", "상담원으로 연결해 드리겠습니다. 잠시만 기다려 주세요."
2. 명확한 상담원 연결 요구: 한 번이면 전환, 만류 없음 if any(k in text for k in ESCALATE_PATTERNS): return "escalate", "상담원 연결 중입니다."
3. 정상 업무 경로: 모델은 응답 생성만 담당, 전환 여부는 절대 결정하지 않음 reply = reply_fn(text) if reply == session.last_reply: session.fail_hits += 1 else: session.fail_hits = 0 session.last_reply = reply session.history.append(("bot", reply))
4. 폴백: 반복 화법 / 턴 수 초과, 두 카운터 중 하나라도 매칭되면 if session.fail_hits >= MAX_REPEAT: return "escalate", "이 문제는 제가 처리할 수 없어 상담원으로 연결해 드렸습니다." if session.turns >= MAX_TURNS: return "escalate", "여러 번 시도해 드렸으니 상담원 연결로 계속 처리하겠습니다."
return "reply", reply
if __name__ == "__main__": FALLBACK = "고객님의 심정 충분히 이해합니다. 조금만 기다려 주세요."
def fake_llm(text: str) -> str: # 질문과 동떨어진 답만 하는 로봇을 흉내 냄 if "退" in text: return FALLBACK return "주문 상세 페이지에서 셀프로 조작하실 수 있습니다."
def run(cases, label): s = Session(sid=label) print(f"--- {label} ---") for i, q in enumerate(cases, 1): action, msg = route(s, q, fake_llm) print(f"[{i}] {q} -> {action}: {msg}") if action == "escalate": print(f" 업그레이드 시 {len(s.history)}개의 컨텍스트를 함께 전달") break
세 가지 경로를 각각 실행 run(["我要退两张电影票", "退不了", "还是退不了", "算了"], "폴백 화법 반복 트리거") run(["这个优惠券怎么用不了", "我要投诉"], "위험 단어 트리거") run(["这个优惠券怎么用不了", "转人工"], "명확한 요구 트리거")
세 가지 경로를 각각 실행해 보면: 폴백 반복 경로는 3번째 발화에서 업그레이드되고, 위험 단어와 명확한 요구 경로는 모두 2번째 발화에서 업그레이드된다. 즉, 모델이 얼마나 똑똑할 필요는 없고, 세 개의 카운터 중 하나만 울리면 사람이 구출될 수 있다.
대형 모델을 붙여 폴백 화법을 생성하려면 「생성」과 「결정」을 분리할 것을 권한다. 모델은 reply_fn만 담당하고, 업그레이드 여부는 항상 route()가 결정한다. 나는 평소 이런 다중 모델 폴백을 조율할 때 통합 진입점으로 전환해서, 각 업체별로 SDK를 따로 연동하는 수고를 덜고, 몇 개의 예비 모델을 위해 각각 구독을 열 필요도 없다. 핵심 경로에서는 단일 공급업체를 쓰지 말 것, 이 점이 어느 모델을 고르느냐보다 중요하다.
함정 경고
함정 1: 「人工」「0」을 트리거 단어로 삼는 것. 많은 시스템이 단일 글자 인식이 정확하지 않아, 「人工」 두 글자가 자주 업무 단어로 흡수되어 버린다. 완전한 구문을 매칭하든지, 동의어 표를 유지하든지 해야지, 퍼지 매칭에 기대지 말 것.
함정 2: 감정 인식을 반대로 한 것. 어떤 팀은 감정 점수를 도입했는데, 사용자가 급할수록 「위로 화법」 분기가 더 잘 트리거되어, 결과적으로 사람을 더 오래 가둬 버렸다. 올바른 방법은 감정이 격해지면 셀프서비스 절차의 가중치를 직접 낮추고 상담원 전환의 가중치를 높이는 것이지, 위로 문구를 한 단락 더하는 것이 아니다.
함정 3: 「상담원 전환율」을 부정적 KPI로 삼는 것. 이것이 근본적인 문제다. 일단 상담원 전환율이 높으면 성과 평가에서 감점을 당하게 되면, 제품 매니저는 입구를 숨기게 되고, 엔지니어링 측이 아무리 훌륭해도 소용없다. 지표는 최소한 짝을 지어 봐야 한다: 상담원 전환율 + 전환 후 1회 해결률 + 평균 연결 소요 시간.
함정 4: 전환 시 컨텍스트를 잃는 것. 국가 표준은 전환 후 고객 정보를 동기화하고 다시 묻지 않을 것을 명확히 요구한다. 구현상으로는 티켓에 구조화된 요약 한 단락을 붙이면 충분하다: 사용자 요구 사항, 이미 시도한 동작, 주문 번호, 감정 표시. 전체 대화를 그대로 넘기지 말 것, 사람이 다 읽지 못한다.
함정 다섯: AI의 약속이 기록으로 남지 않는다. 중국소비자협회의 태도는 명확하다. 사업자가 직접 배포한 AI 고객센터의 출력 내용은 서비스의 일부이며, 「AI가 한 말은 무효」라는 식의 책임 회피는 성립하지 않는다. 대화 기록은 검색과 내보내기가 가능해야 하며, 환불·배상 관련 약속은 반드시 업무 티켓(공식 접수 기록)으로 남겨야 한다.
정리
상담원 연결이 어려운 문제의 해법은 8할이 엔지니어링이고 2할이 결단이다. 엔지니어링 부분은 이 몇 개의 카운터와 하나의 우선순위 큐로, 하루 오후면 다 작성할 수 있다. 결단 부분은 「인력 절감」과 「사람을 미치게 하지 않기」 사이에서 후자를 선택할 의향이 있느냐다. 표준이 이미 잣대를 제시했으니, 이제 누가 먼저 바꾸느냐에 따라 누가 욕을 덜 먹느냐가 갈릴 것이다.