솔라피
경력근거: 솔라피톡, 커뮤니티, CMS 등 자사 프로덕트의 백엔드와 프론트엔드를 개발하고 운영합니다
※ 실제 산출물이 아닌 AI 예측 시안입니다.
솔라피 커넥트팀은 솔라피톡을 직접 만들고 운영하며, 이 공고는 LLM 기반 AI 에이전트 기능을 설계하고 고도화하는 일을 주요업무로 적었습니다. 솔라피톡의 AI 응대는 고객사의 고객 앞에 서는 기능이라, 상담원을 찾는 고객을 계속 붙잡거나 도움말에 없는 환급 조건을 말하는 순간 고객사가 AI 응대를 끄게 됩니다. 연결 문구, 인용한 도움말 번호, 상담원 연결 시각, 같은 고객의 다음 대화가 한 기록 안에 있는 상담 채팅 제품이어야 이 판정이 섭니다. 이 포트폴리오는 그 기록으로 AI 응대를 판정하는 NestJS 규칙과 상담 매니저 화면을 보여줍니다. 상담 품질 관리나 고객 경험 설계에 대한 전문성은 주장하지 않습니다.
커넥트팀은 솔라피톡·커뮤니티·CMS 같은 자사 프로덕트의 백엔드와 프론트엔드를 기획부터 운영까지 맡습니다. 솔라피톡은 고객사가 자기 고객의 문의를 받는 상담 채팅이라, 그 안의 AI 응대가 언제 상담원에게 넘겨야 했는지는 솔라피톡 대화 기록에서만 나오는 질문입니다.
주요업무에 LLM 기반 AI 에이전트 기능의 설계·개발·고도화가 있고, 우대사항은 LLM API·RAG·AI 에이전트 개발 경험입니다. 에이전트를 고도화하려면 먼저 어느 대화에서 에이전트가 넘겨야 할 고객을 붙잡았는지, 어떤 답이 도움말 근거 없이 나갔는지를 세는 판정이 있어야 하고, 이 포트폴리오가 그 판정을 만듭니다.
공고는 TypeScript 제품 개발 경험과 NestJS·MongoDB·WebSocket·메시지 큐 경험을 찾습니다. 대화 기록은 MongoDB 에, 판정 API 는 NestJS 에 두고, 대화가 끝난 뒤 24시간 재문의 판정은 메시지 큐의 지연 작업으로, 새 메시지에 따른 화면 갱신은 WebSocket 으로 붙입니다.
솔라피톡은 고객사가 카카오 상담톡 같은 채널로 들어오는 자기 고객의 문의를 한곳에서 받는 상담 채팅이고, 고객사는 첫 응대를 LLM 기반 AI 에이전트에 맡길 수 있습니다. 이 관리자 페이지는 고객사 한 곳의 하루치 대화 메시지, AI 답변마다 인용한 도움말 번호, 고객사 도움말 목록, 상담원 연결 문구와 첫 응답 기준 시간을 적은 연결 규칙표를 받아 AI 가 응대한 대화마다 다섯 판정 중 하나를 붙입니다. 고객 메시지에서 연결 문구를 띄어쓰기와 상관없이 찾고, 그 뒤 AI 가 몇 번 더 답했는지와 상담원이 들어왔는지를 세며, AI 가 닫은 대화의 고객이 24시간 안에 새 대화를 열었는지 다음 날 기록까지 봅니다. 고객사 상담 매니저는 이 판정으로 상담원이 다시 연락할 대화를 골라 나눠 줍니다.
솔라피톡으로 자사 고객 문의를 받는 고객사 상담 매니저 — AI 에이전트가 처리한 대화 중 상담원이 다시 연락해야 할 것을 골라 상담원에게 나눠 준다
지금 상담 매니저는 AI 가 닫은 대화를 하루 수백 건씩 넘겨 보며 고객이 화를 낸 대화를 눈으로 찾습니다. 고객이 '상담원'·'직원연결' 처럼 연결을 요청한 문구는 대화 텍스트에 그대로 남고, AI 답변마다 인용한 도움말 번호와 상담원 연결 시각도 기록에 있습니다. 이 셋을 규칙으로 견주고 같은 고객이 다음 대화를 언제 열었는지 이어 보면, 상담원이 다시 연락해야 할 대화가 사유별로 추려지고 매니저는 그 목록만 나눠 주면 됩니다.
상담 매니저가 대화 메시지·AI 답변 인용·도움말 목록·연결 규칙표 네 파일을 올리면 AI 가 응대한 대화마다 다섯 판정 중 하나와 연결 요청 문구, 요청 뒤 AI 답변 수, 상담원 첫 응답까지 걸린 분, 재문의까지 걸린 시간이 서는 관리자 페이지입니다. 띄어쓰기를 무시한 연결 문구 찾기, 대화별 메시지 순서 세기, 대화 사이 24시간 창 계산, 판정 차례가 이 주에 들어갑니다. 입력은 파일 업로드로 받고 실시간 연결은 2주차로 미룹니다.
띄어쓰기 없는 요청과 다음 날 재문의를 늘린 대화 200건에 상담 매니저 입장의 판정을 먼저 달아 두고, 자동 판정과 갈린 대화를 사유별로 모읍니다. 대화 기록을 MongoDB 에 넣고, 대화 종료 이벤트를 메시지 큐에 24시간 지연 작업으로 걸어 재문의 판정을 확정하며, 새 메시지가 들어오면 WebSocket 으로 매니저 화면의 판정을 바로 고칩니다.
판정이 붙은 대화마다 Claude 가 고객의 원래 질문, AI 가 한 답, 그 주제의 도움말 제목을 받아 상담원 인계 메모를 씁니다. 메모 속 고객 인용은 대화 원문에 그대로 있어야 하고 도움말 제목은 도움말 목록에 있어야 하며, 어긋나면 그 대화는 판정 요약 문장만 보냅니다. 판정 작업은 채팅 API 와 다른 서비스로 띄워 큐로만 주고받습니다.
상담 매니저가 모아리빙의 10월 1일 대화를 올리면 R-55102 가 '직원연결 해주세요' 뒤에도 AI 가 세 번 더 답한 대화로 맨 왼쪽 열에 서고, R-55177 이 연결 뒤 36분 만에 상담원이 답한 대화로 그 옆 열에 섭니다.
매니저가 R-55131 을 펼쳐 고객의 관부가세 환급 질문과 인용한 도움말이 없는 AI 답을 읽고, R-55140 을 펼쳐 같은 고객이 1시간 45분 뒤 '아까 교환 배송비 물어봤는데' 로 새 대화를 연 기록을 확인한 뒤 상담원을 배정합니다.
띄어쓰기가 빠진 연결 요청, 도움말 목록에서 지워진 번호를 인용한 답, 25시간 뒤 돌아온 고객, 제때 상담원에게 넘어가 목록에서 빠지는 대화를 한 묶음으로 넣으면 대화마다 판정 하나가 붙습니다. 발신자 칸이 비거나 시각이 깨진 메시지 파일 10종은 그 메시지만 건너뛰고 건너뛴 수를 상단에 셉니다.
대화 200건은 상담 매니저 입장에서 제가 먼저 판정을 달아 두고, 자동 판정이 그와 다른 대화를 판정별로 셉니다. 대화도 정답도 제가 쓴 것이라 이 차이는 문구 찾기와 시각 계산이 문서대로 걸리는지를 보여줄 뿐이고, AI 답변 내용이 맞았는지는 따로 사람이 봐야 합니다.
위 수치는 더미데이터 자가채점이며, 판정이 상담원의 재연락 순서를 실제로 줄이는지는 솔라피톡 고객사의 실제 대화 기록과 상담 매니저의 배정 결과가 있어야 측정됩니다. 고객사 한 곳의 하루치 대화를 솔라피톡 메시지 내보내기 그대로 받도록 sender 네 값과 시스템 메시지 두 문구를 고정해 두어, 첫 실측은 그 하루로 시작합니다.
고객이 정말 상담원을 원했는지, AI 답에 근거가 없었는지에 대한 정답 200건을 상담 매니저가 아닌 제가 달았습니다. 일치율은 문구 찾기와 24시간 창이 제 규칙대로 걸린다는 증거일 뿐이고, 매니저가 같은 대화를 같은 순서로 나눠 줄지는 고객사의 실제 하루치 대화로 다시 봐야 합니다.
고객이 '상담원 연결해 주세요' 를 정확히 한 번 쓰는 대화만 있다면 판정은 거의 틀릴 수 없으니, 대화에는 '직원연결' 처럼 띄어쓰기가 빠진 요청, '환불' 이 아니라 '환급' 을 써서 연결 문구에 걸리지 않는 질문, 도움말 목록에서 지워진 A-099 를 인용한 답, 다음 날 25시간 뒤 돌아온 고객을 섞었습니다.
연결 문구 목록이 넓으면 '담당자분께 감사하다고 전해 주세요' 같은 인사도 연결 요청으로 잡힙니다. 지금은 문구 포함 여부만 보므로, 실제 대화에서 잘못 잡힌 비율을 보고 문구를 고치는 일은 도입 단계에서 고객사별로 합니다.
도움말을 인용했다고 답이 그 도움말과 맞는다는 보장은 없습니다. 이 판정은 인용이 비었거나 목록에 없는 경우만 잡고, 인용한 도움말과 답 내용이 어긋나는지는 상담원이 인계 메모를 보고 판단합니다.
이 포트폴리오는 대화 기록으로 AI 응대를 판정하는 백엔드 규칙과 상담 매니저 화면을 보여줍니다. 상담 품질 관리나 고객 경험 설계에 대한 전문성은 주장하지 않습니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
고객사 「모아리빙」의 솔라피톡 대화 메시지입니다. sender 는 고객·AI·상담원·시스템 넷이고, 상담원 연결과 대화 종료는 시스템 메시지 문구로 남습니다. 10월 1일 대화를 판정하고, 24시간 재문의를 보려고 10월 2일 대화 하나를 함께 담았습니다.
| message_id | room_id | customer_id | sender | text | sent_at |
|---|---|---|---|---|---|
| M-901001 | R-55102 | C-3011 | customer | 주문한 소파 배송이 언제 오나요? | 2026-10-01T10:02:00+09:00 |
| M-901002 | R-55102 | C-3011 | ai | 주문번호를 알려주시면 배송 일정을 확인해 드릴게요. | 2026-10-01T10:02:20+09:00 |
| M-901003 | R-55102 | C-3011 | customer | 직원연결 해주세요 | 2026-10-01T10:03:00+09:00 |
| M-901004 | R-55102 | C-3011 | ai | 배송 조회는 마이페이지 > 주문내역에서 확인하실 수 있어요. | 2026-10-01T10:03:15+09:00 |
| M-901005 | R-55102 | C-3011 | customer | 상담원 연결이요 | 2026-10-01T10:04:00+09:00 |
아래 JSON Schema 에 맞는 솔라피톡 대화 메시지 행을 더 만들어 주세요. {"type":"object","properties":{"message_id":{"type":"string"},"room_id":{"type":"string","description":"대화방 번호입니다."},"customer_id":{"type":"string","description":"고객사의 고객 번호입니다."},"sender":{"type":"string","enum":["customer","ai","counselor","system"]},"text":{"type":"string","description":"메시지 원문입니다. 시스템 메시지는 '고객 요청으로 상담원에게 연결합니다' 또는 '대화가 종료되었습니다' 둘 중 하나입니다."},"sent_at":{"type":"string","description":"+09:00 오프셋이 붙은 ISO 8601 시각입니다."}},"required":["message_id","room_id","customer_id","sender","text","sent_at"]} 대화 하나는 메시지 3~10줄이고, AI 가 닫는 대화는 시스템 메시지 '대화가 종료되었습니다' 로, 상담원에게 넘기는 대화는 '고객 요청으로 상담원에게 연결합니다' 로 이어집니다. 쇼핑몰·학원·병원 예약 가운데 한 업종의 고객 문의로 씁니다. 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) '직원연결'·'상담원연결' 처럼 띄어쓰기 없이 쓴 연결 요청, (2) 연결 요청 뒤에도 AI 가 두세 번 더 답하고 상담원이 들어오지 않는 대화, (3) AI 가 닫은 뒤 같은 고객이 몇 시간 안에 새 대화를 연 경우와 24시간을 조금 넘겨 연 경우, (4) 연결 문구와 비슷하지만 다른 낱말('환급' 과 '환불'), (5) 연결된 뒤 상담원 첫 응답이 30분 넘게 늦은 대화. 생성한 행은 위 스키마를 통과해야 하며, 형식을 깨뜨리는 시도는 별도로 3건만 표시해 두세요.{"type":"object","properties":{"message_id":{"type":"string"},"room_id":{"type":"string","description":"대화방 번호입니다."},"customer_id":{"type":"string","description":"고객사의 고객 번호입니다."},"sender":{"type":"string","enum":["customer","ai","counselor","system"]},"text":{"type":"string","description":"메시지 원문입니다. 시스템 메시지는 '고객 요청으로 상담원에게 연결합니다' 또는 '대화가 종료되었습니다' 둘 중 하나입니다."},"sent_at":{"type":"string","description":"+09:00 오프셋이 붙은 ISO 8601 시각입니다."}},"required":["message_id","room_id","customer_id","sender","text","sent_at"]}AI 에이전트가 보낸 답변 메시지마다 근거로 인용한 도움말 번호입니다. 인사나 되묻기처럼 도움말 없이 보낸 답은 cited_article_id 가 비어 있고, 도움말 목록에서 지워진 옛 번호를 인용한 답을 하나 넣었습니다.
| message_id | room_id | cited_article_id |
|---|---|---|
| M-901002 | R-55102 | A-101 |
| M-901004 | R-55102 | A-101 |
| M-901006 | R-55102 | — |
| M-901008 | R-55102 | A-101 |
| M-901012 | R-55131 | — |
아래 JSON Schema 에 맞는 AI 답변 인용 행을 더 만들어 주세요. {"type":"object","properties":{"message_id":{"type":"string","description":"chat_messages 에서 sender 가 ai 인 메시지 번호입니다."},"room_id":{"type":"string"},"cited_article_id":{"type":["string","null"],"description":"AI 가 답의 근거로 인용한 도움말 번호입니다. 인용 없이 답했으면 null 입니다."}},"required":["message_id","room_id","cited_article_id"]} chat_messages 에서 sender 가 ai 인 메시지마다 한 줄을 쓰고, 대부분은 help_articles 에 있는 번호를 인용합니다. 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) 구체적인 금액·기한을 말했는데 cited_article_id 가 null 인 답, (2) 도움말 목록에서 지워진 옛 번호를 인용한 답, (3) 되묻기라 인용이 null 인 답. 생성한 행은 위 스키마를 통과해야 하며, 형식을 깨뜨리는 시도는 별도로 3건만 표시해 두세요.{"type":"object","properties":{"message_id":{"type":"string","description":"chat_messages 에서 sender 가 ai 인 메시지 번호입니다."},"room_id":{"type":"string"},"cited_article_id":{"type":["string","null"],"description":"AI 가 답의 근거로 인용한 도움말 번호입니다. 인용 없이 답했으면 null 입니다."}},"required":["message_id","room_id","cited_article_id"]}고객사 「모아리빙」이 솔라피톡에 올린 도움말 목록입니다. 9월에 적립금 도움말을 새로 쓰면서 옛 번호 A-099 를 지워 이 목록에는 없습니다.
| article_id | title | updated_at |
|---|---|---|
| A-101 | 배송 조회 방법 | 2026-05-12 |
| A-102 | 주문 변경·취소 안내 | 2026-06-03 |
| A-103 | 반품 신청 방법 | 2026-06-03 |
| A-104 | 교환 배송비 안내 | 2026-07-21 |
| A-105 | 주말·공휴일 배송 안내 | 2026-08-08 |
아래 JSON Schema 에 맞는 고객사 도움말 목록 행을 더 만들어 주세요. {"type":"object","properties":{"article_id":{"type":"string"},"title":{"type":"string"},"updated_at":{"type":"string","description":"마지막으로 고친 날(YYYY-MM-DD)입니다."}},"required":["article_id","title","updated_at"]} 쇼핑몰 고객이 자주 묻는 배송·교환·반품·결제·적립금·회원 주제로 제목을 씁니다. 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) 같은 주제를 새로 써서 옛 번호가 목록에서 빠진 경우, (2) 제목은 비슷하지만 다루는 범위가 다른 두 도움말(해외배송 안내와 해외배송 반품). 생성한 행은 위 스키마를 통과해야 하며, 형식을 깨뜨리는 시도는 별도로 3건만 표시해 두세요.{"type":"object","properties":{"article_id":{"type":"string"},"title":{"type":"string"},"updated_at":{"type":"string","description":"마지막으로 고친 날(YYYY-MM-DD)입니다."}},"required":["article_id","title","updated_at"]}고객사가 정한 상담원 연결 규칙표입니다. 고객 메시지에 trigger_phrase 가 띄어쓰기와 상관없이 들어 있으면 AI 는 상담원에게 넘겨야 하고, 넘긴 뒤 상담원은 first_reply_min 분 안에 첫 응답을 해야 합니다.
| trigger_phrase | first_reply_min |
|---|---|
| 상담원 | 15 |
| 상담사 | 15 |
| 직원 연결 | 15 |
| 사람이랑 | 15 |
| 담당자 | 15 |
아래 JSON Schema 에 맞는 상담원 연결 규칙 행을 더 만들어 주세요. {"type":"object","properties":{"trigger_phrase":{"type":"string"},"first_reply_min":{"type":"integer","minimum":1,"description":"상담원 연결 시스템 메시지부터 첫 상담원 메시지까지 허용하는 분입니다."}},"required":["trigger_phrase","first_reply_min"]} 돈이 걸린 문구(환불·결제 오류)는 first_reply_min 을 짧게, 전화 요청처럼 즉답이 어려운 문구는 길게 씁니다. 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) 띄어쓰기가 있는 두 낱말 문구, (2) 다른 문구 안에 들어 있는 짧은 문구('상담' 과 '상담원'), (3) 인사말에도 흔히 나오는 문구('담당자'). 생성한 행은 위 스키마를 통과해야 하며, 형식을 깨뜨리는 시도는 별도로 3건만 표시해 두세요.{"type":"object","properties":{"trigger_phrase":{"type":"string"},"first_reply_min":{"type":"integer","minimum":1,"description":"상담원 연결 시스템 메시지부터 첫 상담원 메시지까지 허용하는 분입니다."}},"required":["trigger_phrase","first_reply_min"]}| TypeScript | 연결 문구 찾기, 대화별 순서 세기, 24시간 창 계산, 판정 차례를 담은 규칙 모듈과 화면 코드의 언어입니다. |
| NestJS | 업로드 API 와 판정 서비스, 2주차 지연 작업 처리기와 WebSocket 게이트웨이를 모듈로 나눠 둡니다. |
| MongoDB | 2주차부터 대화 메시지와 AI 답변 인용을 대화 단위 문서로 넣고 고객별 다음 대화를 찾습니다. |
| 메시지 큐 | 대화 종료 이벤트를 24시간 지연 작업으로 걸어 재문의 판정을 그 시각에 확정합니다. |
| WebSocket | 새 메시지가 들어와 판정이 바뀌면 상담 매니저 화면의 해당 대화 카드를 바로 옮깁니다. |
| MSA | 3주차에 판정 작업을 채팅 API 와 다른 서비스로 띄워 큐 메시지로만 주고받게 나눕니다. |
| LLM API·RAG | 3주차 인계 메모에서 대화 주제와 가까운 도움말을 찾아 제목을 붙이고 Claude 가 메모를 씁니다. |
| Claude Code·Codex | 규칙 모듈과 화면 초안을 맡기고, 띄어쓰기 없는 연결 문구·삭제된 도움말 번호·다음 날 재문의 테스트를 통과한 코드만 합칩니다. |
✗ fail → 연결 문구가 너무 넓게 잡히면 규칙표 문구를 다시 보고, 판정 차례가 매니저 판단과 어긋나면 상담원 요청 무시와 근거 없는 답변의 순서를 바꿔 봅니다.
✓ pass → 매니저가 이 보드만 보고 오늘 다시 연락할 대화를 나눠 줄 수 있으면 1주차 결과로 충분합니다. 2주차로 간다면 200건 정답 대조와 MongoDB·메시지 큐·WebSocket 연결을 붙입니다.
✗ fail → 지연 작업이 사라지면 재문의 판정은 하루 한 번 묶어 돌리는 방식으로 되돌리고 인계 메모 작업은 미룹니다.
✓ pass → 도움말 검색과 상담원 인계 메모로 3주차를 엽니다.
위 예측 UI 는 3주차까지 완주했을 때의 화면입니다. 1주차에 실제로 만드는 것은 아래 표시된 화면 하나입니다.
verdict사유 라벨 · room_id + 한 줄 요약(근거 수치 포함)화면·코드·면접에서 이 단어를 그대로 씁니다. 키는 출력 스키마의 enum 과 같습니다.
상담원 요청을 AI가 계속 받음상담원 요청 놓침넘긴 뒤 상담원 응답이 늦음연결 후 대기도움말에 없는 답변근거 없는 답변같은 문의가 다시 들어옴재문의AI 응대로 끝남AI 종료AI 에이전트가 응대한 10월 1일 대화 하나가 연결 문구 찾기, 요청 뒤 발화 세기, 인용 도움말 대조, 연결 대기 분 계산, 24시간 재문의 확인을 거쳐 어떤 판정을 받는지 남기는 형식입니다. 판정은 규칙이 정하고, 3주차부터 상담원 인계 메모만 모델이 씁니다.
{"type":"object","properties":{"room_id":{"type":"string"},"customer_id":{"type":"string"},"verdict":{"type":"string","enum":["AI 응대로 끝남","상담원 요청을 AI가 계속 받음","도움말에 없는 답변","같은 문의가 다시 들어옴","넘긴 뒤 상담원 응답이 늦음"]},"first_trigger_message_id":{"type":["string","null"],"description":"고객 메시지 중 띄어쓰기를 지운 text 에 띄어쓰기를 지운 trigger_phrase 가 처음 들어 있는 메시지입니다."},"matched_phrase":{"type":["string","null"]},"ai_after_trigger":{"type":"integer","minimum":0,"description":"first_trigger_message_id 뒤 같은 room_id 에서 sender 가 ai 인 메시지 수입니다."},"counselor_message_count":{"type":"integer","minimum":0},"uncited_message_ids":{"type":"array","items":{"type":"string"},"description":"cited_article_id 가 null 이거나 help_articles 에 없는 AI 답변 메시지입니다."},"handoff_at":{"type":["string","null"]},"first_counselor_at":{"type":["string","null"]},"reply_wait_min":{"type":["number","null"],"minimum":0,"description":"first_counselor_at 에서 handoff_at 을 뺀 분입니다."},"first_reply_min":{"type":["integer","null"],"minimum":1},"closed_at":{"type":["string","null"],"description":"AI 가 닫은 대화의 마지막 sent_at 입니다."},"next_room_id":{"type":["string","null"]},"reentry_hours":{"type":["number","null"],"minimum":0,"description":"next_room_id 의 첫 sent_at 에서 closed_at 을 뺀 시간을 소수 둘째 자리로 반올림한 값입니다."},"evidence":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"agg":{"type":["string","null"]},"value":{}},"required":["field","value"]}},"summary":{"type":"string"},"hedge":{"type":["string","null"]}},"required":["room_id","customer_id","verdict","ai_after_trigger","counselor_message_count","uncited_message_ids","evidence","summary"]}[
{
"room_id": "R-55102",
"customer_id": "C-3011",
"verdict": "상담원 요청을 AI가 계속 받음",
"first_trigger_message_id": "M-901003",
"matched_phrase": "직원 연결",
"ai_after_trigger": 3,
"counselor_message_count": 0,
"uncited_message_ids": [
"M-901006"
],
"handoff_at": null,
"first_counselor_at": null,
"reply_wait_min": null,
"first_reply_min": null,
"closed_at": null,
"next_room_id": null,
"reentry_hours": null,
"evidence": [
{
"field": "text",
"value": "직원연결 해주세요"
},
{
"field": "trigger_phrase",
"value": "직원 연결"
},
{
"field": "sender",
"agg": "count",
"value": 3
},
{
"field": "cited_article_id",
"value": null
}
],
"summary": "고객이 '직원연결 해주세요' 로 상담원을 찾은 뒤에도 AI 가 세 번 더 답했고 상담원은 들어오지 않았습니다.",
"hedge": "띄어쓰기를 지우지 않고 찾으면 첫 요청이 '상담원 연결이요' 로 밀리지만, 그 뒤 AI 답변도 두 번이라 판정은 같습니다. 인용 없는 답 M-901006 도 있지만 상담원 요청 무시가 먼저입니다."
},
{
"room_id": "R-55131",
"customer_id": "C-3027",
"verdict": "도움말에 없는 답변",
"first_trigger_message_id": null,
"matched_phrase": null,
"ai_after_trigger": 0,
"counselor_message_count": 0,
"uncited_message_ids": [
"M-901012"
],
"handoff_at": null,
"first_counselor_at": null,
"reply_wait_min": null,
"first_reply_min": null,
"closed_at": "2026-10-01T11:31:10+09:00",
"next_room_id": null,
"reentry_hours": null,
"evidence": [
{
"field": "message_id",
"value": "M-901012"
},
{
"field": "cited_article_id",
"value": null
},
{
"field": "text",
"value": "네, 반품이 완료되면 7일 안에 관부가세 전액이 자동으로 환급됩니다."
}
],
"summary": "AI 가 관부가세 환급 기한을 7일이라고 답했는데 인용한 도움말이 없습니다.",
"hedge": "고객이 쓴 '환급' 은 연결 문구 '환불' 과 한 글자 다른 낱말이라 상담원 연결 대상이 아니었습니다. 환급 문의를 연결 대상에 넣을지는 고객사가 규칙표에 문구를 더해 정합니다."
},
{
"room_id": "R-55165",
"customer_id": "C-3131",
"verdict": "도움말에 없는 답변",
"first_trigger_message_id": null,
"matched_phrase": null,
"ai_after_trigger": 0,
"counselor_message_count": 0,
"uncited_message_ids": [
"M-901082"
],
"handoff_at": null,
"first_counselor_at": null,
"reply_wait_min": null,
"first_reply_min": null,
"closed_at": "2026-10-01T12:47:00+09:00",
"next_room_id": null,
"reentry_hours": null,
"evidence": [
{
"field": "cited_article_id",
"value": "A-099"
},
{
"field": "article_id",
"agg": "count",
"value": 10
}
],
"summary": "AI 가 적립금 유효기간을 답하면서 인용한 A-099 가 지금 도움말 목록 10편 안에 없습니다.",
"hedge": "9월에 새로 쓴 A-107 적립금 사용 안내의 옛 번호로 보입니다. 기간이 바뀌었는지는 상담원이 A-107 을 열어 확인합니다."
},
{
"room_id": "R-55140",
"customer_id": "C-3044",
"verdict": "같은 문의가 다시 들어옴",
"first_trigger_message_id": null,
"matched_phrase": null,
"ai_after_trigger": 0,
"counselor_message_count": 0,
"uncited_message_ids": [],
"handoff_at": null,
"first_counselor_at": null,
"reply_wait_min": null,
"first_reply_min": null,
"closed_at": "2026-10-01T14:02:00+09:00",
"next_room_id": "R-55188",
"reentry_hours": 1.75,
"evidence": [
{
"field": "sent_at",
"value": "2026-10-01T14:02:00+09:00"
},
{
"field": "sent_at",
"value": "2026-10-01T15:47:00+09:00"
},
{
"field": "room_id",
"value": "R-55188"
},
{
"field": "customer_id",
"value": "C-3044"
}
],
"summary": "AI 가 교환 배송비를 답하고 닫은 지 1시간 45분 만에 같은 고객이 '아까 교환 배송비 물어봤는데' 로 새 대화를 열었습니다.",
"hedge": "새 대화 R-55188 은 7분 30초 만에 상담원이 받아 그 대화 자체는 목록에 올리지 않습니다."
},
{
"room_id": "R-55177",
"customer_id": "C-3090",
"verdict": "넘긴 뒤 상담원 응답이 늦음",
"first_trigger_message_id": "M-901071",
"matched_phrase": "상담사",
"ai_after_trigger": 0,
"counselor_message_count": 1,
"uncited_message_ids": [],
"handoff_at": "2026-10-01T11:05:00+09:00",
"first_counselor_at": "2026-10-01T11:41:00+09:00",
"reply_wait_min": 36,
"first_reply_min": 15,
"closed_at": null,
"next_room_id": null,
"reentry_hours": null,
"evidence": [
{
"field": "sent_at",
"value": "2026-10-01T11:05:00+09:00"
},
{
"field": "sent_at",
"value": "2026-10-01T11:41:00+09:00"
},
{
"field": "first_reply_min",
"value": 15
},
{
"field": "trigger_phrase",
"value": "상담사"
}
],
"summary": "교환 접수로 상담사를 찾은 고객이 상담원에게 넘어간 뒤 첫 응답까지 36분을 기다려 기준 15분을 넘었습니다.",
"hedge": null
},
{
"room_id": "R-55150",
"customer_id": "C-3102",
"verdict": "AI 응대로 끝남",
"first_trigger_message_id": null,
"matched_phrase": null,
"ai_after_trigger": 0,
"counselor_message_count": 0,
"uncited_message_ids": [],
"handoff_at": null,
"first_counselor_at": null,
"reply_wait_min": null,
"first_reply_min": null,
"closed_at": "2026-10-01T16:20:18+09:00",
"next_room_id": null,
"reentry_hours": null,
"evidence": [
{
"field": "cited_article_id",
"value": "A-105"
},
{
"field": "text",
"value": "대화가 종료되었습니다"
}
],
"summary": "AI 가 주말 배송 도움말을 인용해 답하고 대화를 닫았으며 고객은 다시 문의하지 않았습니다.",
"hedge": null
},
{
"room_id": "R-55160",
"customer_id": "C-3115",
"verdict": "AI 응대로 끝남",
"first_trigger_message_id": null,
"matched_phrase": null,
"ai_after_trigger": 0,
"counselor_message_count": 0,
"uncited_message_ids": [],
"handoff_at": null,
"first_counselor_at": null,
"reply_wait_min": null,
"first_reply_min": null,
"closed_at": "2026-10-01T09:20:00+09:00",
"next_room_id": "R-55199",
"reentry_hours": 25.08,
"evidence": [
{
"field": "cited_article_id",
"value": "A-103"
},
{
"field": "sent_at",
"value": "2026-10-01T09:20:00+09:00"
},
{
"field": "sent_at",
"value": "2026-10-02T10:25:00+09:00"
}
],
"summary": "AI 가 반품 신청 방법을 답하고 닫았고, 같은 고객은 25시간 넘게 지나 수거 일정을 물으러 왔습니다.",
"hedge": "다음 대화가 24시간을 1시간 5분 넘겨 열려 재문의로 세지 않았습니다. 묻는 내용도 신청 방법이 아니라 수거 일정이라 다른 문의입니다."
}
][자동 검증 · 코드가 판정합니다] 1. verdict 가 AI 응대로 끝남·상담원 요청을 AI가 계속 받음·도움말에 없는 답변·같은 문의가 다시 들어옴·넘긴 뒤 상담원 응답이 늦음 다섯 값 중 하나인지 확인합니다. 2. room_id·customer_id·verdict·ai_after_trigger·counselor_message_count·uncited_message_ids·evidence·summary 필드가 빠짐없이 존재하는지 확인합니다. 3. ai_after_trigger·counselor_message_count 가 0 이상이고 reply_wait_min·reentry_hours 가 0 이상인지 확인합니다. 4. reply_wait_min 을 first_counselor_at − handoff_at(분)으로, reentry_hours 를 next_room_id 첫 sent_at − closed_at(시간)으로 다시 계산합니다. 5. first_trigger_message_id 가 있고 counselor_message_count 가 0 이며 ai_after_trigger 가 2 이상이면 verdict 는 상담원 요청을 AI가 계속 받음입니다. 6. 앞 조건에 걸리지 않고 uncited_message_ids 가 하나라도 있으면 verdict 는 도움말에 없는 답변입니다. 7. 앞 조건에 걸리지 않고 reentry_hours 가 24 이하이면 verdict 는 같은 문의가 다시 들어옴입니다. 8. 앞 조건에 걸리지 않고 reply_wait_min 이 first_reply_min 을 넘으면 verdict 는 넘긴 뒤 상담원 응답이 늦음입니다. 어느 조건에도 걸리지 않고 AI 가 닫은 대화면 verdict 는 AI 응대로 끝남입니다. 9. evidence 의 field 이름이 네 데이터셋 중 하나에 있는 컬럼인지 대조합니다. 10. evidence 의 value 가 수록된 행의 원문 값과 같은지 확인합니다. 여러 줄을 센 수는 agg 를 적습니다. 11. summary 가 존댓말로 끝나는지 확인합니다. [판정 대상 · 무엇을 목록에 올리는가] 10월 1일에 열린 대화 중 AI 가 한 번이라도 답했거나 상담원 연결이 늦은 대화만 판정합니다. 연결 기준 시간 안에 상담원이 받은 R-55188 은 상담원이 이미 응대한 대화라 목록에 올리지 않고 화면 위 집계의 제때 연결로만 셉니다. 10월 2일에 열린 R-55199 는 판정 대상이 아니라 R-55160 의 재문의를 확인하는 데만 씁니다. [판정 차례 · 위 규칙이 둘 이상 걸릴 때] 대화 하나에는 판정을 하나만 남깁니다. ① 상담원 요청을 AI가 계속 받음 → ② 도움말에 없는 답변 → ③ 같은 문의가 다시 들어옴 → ④ 넘긴 뒤 상담원 응답이 늦음 → ⑤ AI 응대로 끝남. [하드 케이스 · 판정이 갈리는 이유] R-55102 의 '직원연결 해주세요' 는 규칙표의 '직원 연결' 과 띄어쓰기만 달라, 양쪽 띄어쓰기를 지운 뒤 찾아야 첫 요청으로 잡힙니다. M-901006 처럼 cited_article_id 가 null 인 답과 R-55165 의 A-099 처럼 도움말 목록 10편에 없는 번호는 모두 인용 없는 답으로 셉니다. R-55160 의 고객은 25.08시간 뒤에 돌아와 24시간 창 밖이라 재문의가 아닙니다. R-55131 의 고객 메시지 '관부가세 환급도 되나요' 는 연결 문구 '환불' 과 다른 낱말이라 연결 요청이 아닙니다. 고객 메시지 하나에 연결 문구가 둘 이상 걸리면 first_reply_min 이 가장 짧은 문구를 matched_phrase 로 씁니다. 더미에는 그런 메시지가 없고, 증강 데이터의 하드 케이스가 만듭니다. [평가 러너 · 2주차] 더미 대화 200건에 손으로 매긴 정답 판정과 자동 판정을 대조해 판정별 일치율을 내고, 어긋난 대화는 따로 모아 연결 문구 목록과 판정 차례를 다시 봅니다. [수동 검증 · 사람이 봅니다] 1. 인용이 있는 AI 답이 그 도움말 내용과 맞는지는 상담원이 봅니다. 2. 연결 문구에 걸린 메시지가 실제로 상담원을 원한 것인지는 상담 매니저가 펼쳐 보고 정합니다.
근거: 솔라피톡, 커뮤니티, CMS 등 자사 프로덕트의 백엔드와 프론트엔드를 개발하고 운영합니다