챗봇을 붙이는 이유는 대체로 하나입니다. 상담 문의를 줄이려고. 그런데 붙이고 나서도 상담 인력이 그대로인 경우가 많습니다.
원인은 대개 기술이 아닙니다.
사람들은 답을 몰라서 묻는 게 아닙니다
고객센터에 들어오는 문의를 실제로 나눠보면 상당수가 이렇습니다.
- 내 주문이 지금 어디까지 갔는지
- 어제 결제한 게 왜 취소됐는지
- 내가 신청한 게 승인됐는지
전부 그 사람 데이터를 봐야 답할 수 있는 질문입니다. 안내 문서를 아무리 잘 넣어도 못 답합니다.
이런 문의가 60%인데 FAQ 챗봇을 붙이면, 나머지 40% 중 일부만 줄고 대부분은 그대로 넘어옵니다. 게다가 챗봇에서 한 번 헛돌고 오기 때문에 기분은 더 나빠져 있습니다.
그래서 먼저 문의를 나눠봐야 합니다
만들기 전에 최근 문의 몇백 건을 놓고 나눠보세요.
| 유형 | 챗봇이 답할 수 있나 | 필요한 것 |
|---|---|---|
| 안내 문서에 있는 내용 | 됨 | 문서 검색 |
| 내 데이터 조회 | 시스템과 연결하면 됨 | 조회 API + 권한 |
| 상태 변경(취소·환불 신청) | 되지만 위험함 | 쓰기 API + 확인 절차 |
| 예외 처리·협의 | 안 됨 | — |
이 비율만 보면 얼마나 줄일 수 있는지 바로 나옵니다. 15%라면 그 15%를 위해 얼마를 쓸지 판단하면 됩니다. 이걸 안 하고 시작하는 게 실패의 대부분입니다.
분류 자체를 모델에게 시켜도 됩니다. 최근 문의 500건을 넣고 유형을 붙이게 하면 몇 분이면 끝납니다. 작은 모델로 충분하고 값도 몇백 원입니다.
const response = await client.messages.create({
model: "claude-haiku-4-5",
max_tokens: 256,
system: "문의를 다음 넷 중 하나로 분류한다: 안내, 조회, 변경, 예외",
messages: [{ role: "user", content: inquiry }],
});
조회형을 처리하려면 연결이 필요합니다
"내 주문 어디까지 갔어요"에 답하려면 챗봇이 주문 시스템을 조회할 수 있어야 합니다. 요즘 LLM은 도구 호출로 이걸 처리합니다. 모델이 필요한 함수를 알아서 고르고, 그 결과로 답을 만듭니다.
const tools: Anthropic.Tool[] = [
{
name: "get_order_status",
description: "로그인한 사용자의 주문 상태를 조회한다. 주문번호를 모르면 최근 주문을 반환한다.",
input_schema: {
type: "object",
properties: {
orderNo: { type: "string", description: "주문번호. 없으면 최근 건" },
},
required: [],
additionalProperties: false,
},
strict: true, // 입력이 스키마에 정확히 맞도록 보장한다
},
];
여기서 실제 작업량은 모델 쪽이 아니라 API 쪽에 있습니다.
권한이 제일 중요합니다
도구에 userId를 파라미터로 넣으면 안 됩니다. 모델이 그 값을 만들어낼 수 있으니까요. 사용자를 특정하는 값은 모델이 볼 수 없는 데서 넣어야 합니다.
// 위험 — 모델이 남의 userId를 넣을 수 있다
async function getOrderStatus({ userId, orderNo }) { /* ... */ }
// 이렇게 — 세션에서 꺼내 쓴다. 모델은 orderNo만 정한다
async function getOrderStatus({ orderNo }, ctx: { userId: string }) {
return db.orders.findFirst({
where: { userId: ctx.userId, ...(orderNo ? { orderNo } : {}) },
orderBy: { createdAt: "desc" },
});
}
이 원칙 하나가 사고를 막습니다. 도구 스키마에는 모델이 정해도 되는 것만 넣으세요.
쓰기 도구는 확인을 거쳐야 합니다
조회는 틀려도 다시 물으면 되지만 취소나 환불은 되돌리기 어렵습니다. 상태를 바꾸는 도구는 바로 실행하지 말고 사용자 확인을 한 번 받으세요.
모델: cancel_order(orderNo: "A-1024") 호출 요청
서버: 실행 안 하고 화면에 확인 카드 표시
"A-1024 주문을 취소할까요? [취소하기] [아니요]"
사용자가 눌렀을 때만 실제 실행
모델이 도구를 부르는 것과 그 도구가 실행되는 건 분리돼 있어야 합니다.
모르면 모른다고 하게 하세요
LLM은 모르는 것도 그럴듯하게 답합니다. 고객 응대에서는 이게 제일 위험합니다. 환불 규정을 지어내면 그 답이 곧 분쟁이 됩니다.
근거를 못 찾으면 답하지 않게 합니다. 문서 검색 점수가 기준 아래면 답변 생성으로 넘어가지 않고 상담원 연결로 보냅니다.
틀리면 곤란한 값은 만들게 두지 않습니다. 금액·기간·조항은 모델이 문장을 쓰게 하지 말고, 시스템이 조회한 값을 그대로 화면에 뿌립니다. 모델은 그 값을 감싸는 문장만 씁니다.
형식을 못 박습니다. 답변과 근거를 나눠 받으면 근거가 비었을 때 자동으로 거를 수 있습니다.
const response = await client.messages.create({
model: "claude-opus-5",
max_tokens: 1024,
output_config: {
format: {
type: "json_schema",
schema: {
type: "object",
properties: {
answer: { type: "string" },
sources: { type: "array", items: { type: "string" } },
confident: { type: "boolean" },
},
required: ["answer", "sources", "confident"],
additionalProperties: false,
},
},
},
messages: [{ role: "user", content: question }],
});
// confident가 false거나 sources가 비면 사람에게 넘긴다
로그를 안 보면 반년 뒤에 아무도 안 씁니다
챗봇은 한 번 만들고 끝나는 물건이 아닙니다. 로그 보고 계속 고쳐야 하는 물건입니다.
최소한 이건 남기세요.
- 질문 원문과 최종 답변
- 어떤 문서·어떤 도구를 근거로 썼는지
- 사용자가 그 뒤에 상담원 연결을 눌렀는지
마지막이 사실상의 정답지입니다. 챗봇이 답했는데도 상담원을 찾았다면 그 답은 실패한 겁니다. 이 비율을 주 단위로 보면 어디를 고쳐야 할지 그대로 나옵니다.
운영 계획 없이 도입하면 반년 뒤엔 아무도 안 봅니다.
상담원을 없애는 대신 빠르게 만드세요
효과가 확실한 쓰임이 하나 더 있습니다. 고객이 아니라 상담원이 쓰는 챗봇입니다.
문의가 들어오면 관련 문서와 그 고객의 최근 이력을 모아 초안을 만들어 상담원에게 보여줍니다. 상담원은 고쳐서 보냅니다.
| 고객에게 바로 | 상담원 보조 | |
|---|---|---|
| 틀렸을 때 | 고객에게 그대로 갑니다 | 사람이 거릅니다 |
| 도입 난이도 | 권한·확인 절차 전부 필요 | 조회만 붙이면 됩니다 |
| 효과 | 문의 수 감소 | 응답 시간 감소 |
위험은 훨씬 작으면서 효과는 먼저 나옵니다. 여기서 쌓인 상담원의 수정 이력이 나중에 고객에게 직접 열 때의 평가 세트가 되기도 하고요. 첫 단계로 이쪽을 권하는 경우가 많습니다.
정리
챗봇 도입이 실패하는 건 모델이 나빠서가 아니라 문의 종류를 안 보고 시작해서입니다.
최근 문의 몇백 건을 나눠보는 데 하루면 충분하고, 그 하루가 전체 투자 규모를 정합니다. 조회형이 많다면 그건 챗봇 프로젝트가 아니라 API 프로젝트입니다. 견적도 일정도 거기 맞춰야 하고요.
