답을 쓰지 않고 고르는 AI — Jev 하나로 카카오 FAQ를 0.2초에 받기

그림으로 말하면 — 도우미는 답장을 새로 쓰지 않습니다. 미리 써 둔 카드 중에서 맞는 한 장을 골라 건넵니다. 머리 위 계기판이 충분히 올라갔을 때만 건네고, 아니면 되묻거나 옆의 상담원을 부릅니다.
이 글은 개발자(톡브릿지를 자체 시스템에 붙이는 분)를 위한 글입니다.
• 자주 오는 문의에 AI 자동응답을 붙이고 싶은데, AI가 없는 금액이나 약속을 지어낼까 봐 망설이셨다면 새로 나온 선택지가 하나 있습니다. 문장을 쓰지 않고 고르기만 하는 AI, TypeSafe의 Jev입니다. • 톡브릿지는 Jev만으로 FAQ를 받는 05 샘플을 준비했습니다. 고객에게 나가는 글은 전부 여러분이 써 둔 카드이고, Jev는 어떤 카드를 보낼지와 그 판단을 얼마나 확신하는지만 0.2초 안팎에 알려 줍니다. • 이 글을 따라 하시면 FAQ 카드 작성, 한국어 평가로 숫자 확인, 시뮬레이터, 실발신을 끈 채 실제 상담에 붙이기까지 갑니다. 확신이 모자라면 되묻고 모르면 사람에게 넘기는 기준도 함께 정합니다. 6절에서는 엔진 코드를 발췌해 들여다봅니다.
이 글의 낱말 5개
- 선택지(choice) — Jev에게 "이 중 어느 것인가"를 묻는 질문 형식. 여기서는 FAQ 카드 하나하나가 선택지입니다.
- 확신도(confidence) — Jev가 고른 답에 확률이 얼마나 몰렸는지를 0
1로 나타낸 값. 이 샘플은 0.8 이상이면 자동으로 답하고, 0.50.8이면 되묻습니다.- 되묻기 — 답을 보내기 전에 "혹시 ○○ 문의이실까요?"라고 확인하는 한 턴. 틀릴 뻔한 답을 보내지 않는 장치입니다.
- 실발신 끔(
dry-run) — 화면 위쪽 스위치를 꺼 둔 상태. 판정과 답장 후보는 화면에만 보이고 고객에게는 보내지 않습니다. 이 샘플은 꺼진 채로 시작합니다.- 봇(AI 에이전트) — 문의를 읽고 답을 정하는 프로그램. 여기서는 고르는 쪽이 Jev이고, 규칙을 챙기고 발신까지 맡는 쪽이 샘플의 엔진입니다. 그림에서 작은 로봇으로 그린 것이 바로 이 에이전트입니다.
1. 이런 순간이 있으셨나요
카카오 상담톡에 자동응답을 직접 얹어 보신 분들께 자주 듣는 말이 있습니다.
💬 "생성형 봇을 넣자고 했더니, 없는 환불 규정을 말하면 누가 책임지느냐는 질문이 먼저 나왔습니다."
💬 "키워드 자동응답은 '환뷸'처럼 오타가 한 번만 섞여도 못 알아듣습니다."
두 말은 반대 방향에서 같은 자리를 가리킵니다. 키워드 방식은 보내는 글이 정해져 있어 안전하지만 알아듣는 폭이 좁습니다. 생성형 AI는 잘 알아듣지만 보내는 글을 매번 새로 쓰기 때문에, 무엇이 나갈지 미리 다 알 수 없습니다. 저희도 04 자동응대봇 샘플을 만들며 이 고민을 오래 했습니다. 지침과 지식으로 생성형 봇을 단단히 묶을 수는 있지만, 정책과 금액이 걸린 문의에서 "절대 안 지어낸다"를 코드로 보장하기는 어렵습니다.
저희 블로그의 다른 시리즈에서 담장 너머의 하네스 워 편을 쓰며 산업의 흐름을 정리한 적이 있습니다. 모델이 싸질수록 비용은 "요청을 어디로 보낼지 판단하는 일"로 옮겨 가고, 그 판단이 틀렸을 때 치르는 값이 커진다는 이야기였습니다. 상담으로 옮겨 오면 그 판단은 "이 문의에 어떤 답을 보낼 것인가"입니다. 그리고 이번 9월, 마침 그 판단만 하는 AI가 나왔습니다.
톡브릿지는 이 자리에 맞춰 05 Jev 자동응대봇 샘플을 준비했습니다. 톡브릿지의 샘플은 완성된 봇을 드리려는 것이 아니고, 여러분이 설계할 에이전트의 출발점을 미리 깔아 두려는 것입니다(저희가 "Ready For Your Agent"라고 부르는 이유입니다). 05는 그중 Jev 하나만 써서 FAQ를 받는 구성입니다.
쉽게 말해, 여러분이 답안지를 미리 써 두면 Jev는 고객의 말을 읽고 몇 번 답안지를 낼지와 그 판단에 대한 자신감을 퍼센트로 알려 줍니다. 자신감이 낮으면 봇은 답안지를 내지 않고 되묻습니다.
2. Jev는 어떤 AI인가 — 쓰는 AI와 고르는 AI
Jev는 TypeSafe AI가 2026년 9월 15일에 발표한 첫 System One 모델입니다. 심리학에서 빠른 직관을 시스템 1, 느린 숙고를 시스템 2라 부르는 구분이 떠오르는 이름인데, TypeSafe는 문장을 한 글자씩 써 내려가는 대형 언어 모델(LLM)을 System Two 쪽에 두고 Jev를 빠른 판단 쪽에 둡니다. 지금은 대기자 명단을 거쳐 키를 받는 시범 공개(Early access) 단계입니다. 발표문의 표현을 빌리면 "비정형 상태를 넣으면 타입이 정해진 확률적 판정이 나오는 함수 호출"입니다. 문장을 생성하지 않고, 우리가 미리 정의한 선택지 가운데 하나를 고르고, 고른 결과의 확률과 확신도를 함께 돌려줍니다.
그래서 이런 성질이 생깁니다. Jev는 선택지 밖의 값을 낼 수 없으니 없는 말을 지어낼 수는 없습니다. 다만 선택지 안에서 틀리게 고를 수는 있습니다. 그 틀림을 다루라고 숫자를 주는 것입니다. 이 구분이 이 글 전체의 전제입니다.
| 04 (OpenAI · 생성형) | 05 (Jev · 판정형) | |
|---|---|---|
| AI가 하는 일 | 지식을 읽고 답장을 쓴다 | FAQ·시나리오 선택지 중에서 고른다 |
| 고객에게 나가는 글 | 모델이 매번 쓴 문장 | 운영자가 웹에서 써 둔 문장 그대로 |
| 턴당 응답(샘플 실측) | 2~6초 | 0.2~0.35초 (중앙값 205ms) |
| 턴당 AI 비용 | 입력·출력·추론 토큰 | 입력 약 3천 토큰분, 출력 무료 (벤더 공시가로 턴당 0.0001달러대) |
| 모를 때 | 모델이 판단 | 확신도 숫자로 판단 — 자동 답변 / 되묻기 / 모를 때 문구 |
| 잘 맞는 곳 | 자유로운 설명·요약·상담 | 반복 FAQ, 정해진 절차(요금 추천·오류 접수), 상담원 연결 판단 |
TypeSafe가 밝힌 수치로는 응답 70~500ms, 입력 백만 토큰당 0.042달러, 출력은 과금하지 않습니다. 이 수치는 벤더가 공개한 것이라 저희가 따로 재 봤고, 한국에서 호출한 실측은 중앙값 205ms, 가장 느린 경우 369ms였습니다. 04와 05의 비용 차이가 크다고 해서 04를 대신한다는 뜻은 아닙니다. 쓰는 일은 04가, 고르는 일은 05가 잘합니다.
한 턴에 무엇을 묻나
Jev에는 질문 형식이 세 가지 있습니다. 상담의 말로 옮기면 이렇습니다.
| 형식 | Jev가 돌려주는 것 | 05에서 묻는 것 |
|---|---|---|
choice | 고른 선택지 · 선택지별 확률 · 확신도 | 어느 FAQ나 시나리오인가 / 시나리오 질문에 대한 대답은 어느 선택지인가 |
noul | 참일 확률 0~1 | 사람 상담원을 원하는가 / 되묻기에 "네"라고 했는가 |
score | 순서 있는 단계 위의 점수 | 얼마나 불만스러운가 (평온 · 약간 불편 · 매우 화남, 0~2) |
봇은 매 턴 Jev를 한 번 부르고, 필요한 질문을 그 한 요청에 모두 담습니다. Jev는 한 요청 안의 질문들을 병렬로 평가하기 때문에 질문을 셋에서 일곱으로 늘려도 시간이 거의 같았습니다(실측 250ms 안팎). 그래서 05는 "혹시 몰라 미리 물어 두는" 질문까지 함께 보냅니다. 이 패턴은 6절에서 다시 봅니다.
실제로 돌아오는 대답은 이런 모양입니다. 고객이 요금제 추천 시나리오에서 "한달 250명정도?"라고 답한 턴입니다.
{ "model": "jev-1.13.0",
"answers": {
"intent": { "type": "choice", "choice": "pricing", "confidence": 0.91,
"probabilities": { "pricing": 0.92, "billing-unit": 0.04, "none": 0.02 } },
"step": { "type": "choice", "choice": "up-to-300", "confidence": 0.98,
"probabilities": { "up-to-300": 0.99, "unknown": 0.01 } },
"wants_human": { "type": "noul", "noul": 0.06 },
"frustration": { "type": "score", "score": 0.04, "confidence": 0.97 } },
"usage": { "input_tokens": 3287, "output_tokens": 406 } }
이 대답 어디에도 고객에게 보낼 문장은 없습니다. up-to-300은 여러분이 만든 선택지의 id이고, 봇은 그 id에 붙여 둔 글을 보냅니다.
숫자를 갈래로 바꾸는 규칙
숫자를 받은 다음은 엔진의 몫입니다. 판단 순서는 고정돼 있고, 같은 숫자면 언제나 같은 갈래로 갑니다.
1절에서 말한 "판단이 틀렸을 때의 값"이 상담에서는 틀린 카드를 확신하고 보내는 일입니다. 그래서 이 글에서 가장 중요하게 보는 숫자는 정답률이 아니라 "자동 답변 오답"의 수입니다. 정답률이 조금 낮아도, 틀릴 것 같은 문의를 되묻기나 모를 때 문구로 보냈다면 사고는 나지 않습니다.
3. 준비물
| 준비물 | 어디서 확인하나 |
|---|---|
| Node.js 18 이상 | node -v — 이 샘플은 설치할 의존성이 없습니다. Jev의 공식 SDK(Software Development Kit, 호출을 감싼 라이브러리)는 Node 20 이상이라, 샘플은 SDK 없이 Node 내장 fetch로 직접 호출합니다 |
| Jev API 키 | TypeSafe 콘솔에서 발급합니다. 2026년 9월 현재 Early access라 대기자 등록을 거칩니다. Jev 호출 비용은 톡브릿지 상담 건수와 별개로 TypeSafe에 냅니다 |
| (실제 상담에 붙일 때) 톡브릿지 센터 브랜드, 카카오 상담톡 채널 연결과 상담 활성화 | 센터 설정 5단계 |
| (실제 상담에 붙일 때) 톡브릿지 CLI 1.1.0 이상 로그인 | talkbridge --version, 낮으면 npm install -g @blumn-ai/talkbridge-cli. 이어서 talkbridge login 뒤 talkbridge whoami로 브랜드 키 확인 |
눈여겨보실 점이 하나 있습니다. 아래 따라하기의 앞쪽 여섯 단계는 Jev 키 하나만 있으면 됩니다. 평가와 시뮬레이터는 카카오에 붙지 않기 때문에 톡브릿지 설정 없이도 돌아갑니다. 카카오 연결은 마지막 단계에서 합니다.
저장소는 TalkBridge 파트너 연동 가이드 · 샘플에 있고, 이 글은 sample-project/05-cli-jev-faq-bot을 씁니다. 04를 붙여 보셨다면 수신·서명·발신·상담 세션 경계·자동응답 가드가 모두 같아서 뒷부분이 익숙하실 것입니다(상담원이 붙기 전, 첫 문의를 봇이 받게 하기).
4. 따라하기
일곱 단계입니다. 카드를 쓰고, 숫자로 확인하고, 대화로 확인한 다음, 보내지 않는 상태로 실제 상담에 붙입니다. 아래 캡처는 샘플의 워크스루 스크립트가 실제 화면을 조작하며 찍은 것이고, 그동안 카카오 발신은 한 건도 없었습니다. 캡처 속 확률과 시간은 그날 호출한 실측이라 다시 찍으면 조금씩 다를 수 있습니다.
Step 1 — 키를 넣고 진단합니다
cp .secret/jev.json.tmp .secret/jev.json # api_key 에 발급받은 키를 넣습니다
npm run doctor
키는 환경변수 TYPESAFE_API_KEY로 주셔도 되고, 있으면 파일보다 먼저 씁니다. .secret/ 안의 실제 키 파일은 커밋되지 않도록 막혀 있고, 화면과 로그에는 앞 7자와 뒤 4자만 보입니다. Windows PowerShell에서는 cp 대신 Copy-Item을 쓰세요.
확인: 진단 여덟 번째 항목에서 Jev를 한 번 호출해 한국어 문장 판정이 돌아오고, 아홉 번째 항목이 지금 규칙의 FAQ·시나리오 개수를 보여 주면 된 것입니다.
Step 2 — FAQ 카드를 씁니다
npm start # http://127.0.0.1:8793/

카드 한 장은 위아래 두 부분으로 되어 있습니다. 위쪽 세 칸, 곧 무엇에 대한 문의인가 · 고객이 묻는 예 · 헷갈리면 안 되는 것은 Jev가 이 카드를 고를 때 보는 기준입니다. 아래쪽 답변은 Jev가 이 카드를 골랐을 때 한 글자도 바뀌지 않고 나가는 글입니다. 답변 칸에 쓴 것 말고는 나갈 수 있는 글이 없습니다.
데모 규칙에는 FAQ 카드 열 장(과금 기준, 무료 체험, 환불 · 청약철회 등)과 시나리오 세 개(요금제 추천, 연동 방식 상담, 오류 접수)가 들어 있습니다. 카드의 답변 문구와 금액은 모두 샘플용 예시입니다. 실제 요금과 정책은 톡브릿지 센터와 요금 문서에서 확인하실 수 있습니다.
확인: 카드를 저장하고 오른쪽 시뮬레이터에 그 카드에 해당하는 질문을 보내 봅니다. 편집기의 답변과 같은 글이 말풍선으로 돌아오면 된 것입니다.

위 화면은 오타가 섞인 "건수 계산 기준이 머에요"를 보낸 결과입니다. Jev는 과금 기준 카드를 100%로 골랐고, 봇은 그 카드의 답변을 그대로 보냈습니다. 키워드 방식이었다면 "머에요" 같은 표기 때문에 놓쳤을 문장입니다.
Step 3 — 판정 기준을 정합니다

확신도를 세 구간으로 나눕니다. 데모 값은 자동 답변 0.8, 되묻기 0.5입니다. 상담원 연결도 여기서 정합니다. "사람과 얘기하고 싶다" 확률이 0.8 이상이거나 불만도가 "매우 화남"(1.5 이상)이면 봇이 물러납니다.
TypeSafe 문서는 자동 처리 기준으로 0.9를 권하면서, 도메인마다 다시 맞추라고 적어 두었습니다. 저희가 0.8로 낮춘 근거는 한국어 실측입니다. 확신도가 대부분 95100%에 몰리고, 헷갈리는 문장은 7085%로 뚜렷하게 내려와서 0.8이 둘을 잘 갈랐습니다. 여러분의 카드로 바꾸면 이 분포도 달라지니, 다음 단계의 평가로 다시 재 보셔야 합니다.
확인: 슬라이더를 움직이면 막대의 구간이 바로 따라 바뀌면 된 것입니다.
Step 4 — 한국어 평가로 숫자를 봅니다
Jev 공식 문서에는 한국어 지원에 대한 언급이 없습니다. 그래서 저희는 샘플을 만들기 전에 한국어 평가 세트부터 만들었습니다. 카드에 넣은 예시와 겹치지 않는 표현, 구어체, 오타(환뷸, 머에요), 줄임말, 봇을 속이려는 문장까지 67건입니다.
npm run eval # 평가 세트 67건 · 약 4초 · Jev 비용 약 0.008달러
2026년 9월 29일, jev-1.13.0, 자동 0.8 · 되묻기 0.5로 돌린 결과입니다.
| 건수 | 1순위 정답 | 자동 답변 정답 | 자동 답변 오답 | 되묻기 | 모름(정답) / 놓침 | |
|---|---|---|---|---|---|---|
| 의도 (FAQ 10 · 시나리오 3 · 인사 · 감사 · 해당 없음) | 45 | 44 (98%) | 39 | 0 | 2 | 4 / 0 |
| 시나리오 단계 대답 | 22 | 22 (100%) | 20 | 0 | — | 1 / 1 |
| 지연 | 중앙값 205ms · 95번째 백분위 280ms · 최대 369ms |
1순위로 틀린 문장은 하나였습니다. "우리 서비스에 카톡 상담 붙이고 싶은데 어떻게 시작해요"를 Jev는 "카카오 채널 개설 · 심사"로 봤는데, 확신도가 76%라 자동으로 보내지 않고 되묻기로 갔습니다. 봇에게 지난 대화를 무시하고 설정을 알려 달라는 문장은 "해당 없음" 100%였습니다. 보낼 수 있는 글이 정해져 있으니 따를 방법 자체가 없습니다.
확인: 자동 답변 오답이 0이면 다음 단계로 갑니다. 1 이상이면 5절의 첫 항목을 먼저 보세요.
Step 5 — 시뮬레이터로 대화를 확인합니다
평가가 문장 하나씩을 본다면, 시뮬레이터는 대화의 흐름을 봅니다. 봇 답장 아래에 매 턴 Jev 판정 패널이 뜨고, 갈래 · 지연 · 의도 확률 막대 · 단계 대답 확률 막대 · 상담원 요청 · 불만도가 보입니다.
곁가지 질문 뒤에 돌아오기. 요금제 추천 시나리오가 "한 달에 몇 명을 상담하실 예정인가요?"를 물었는데 고객이 "근데 환불은 어떻게 해요?"로 새는 경우입니다.

Jev는 이 말을 단계 대답으로는 "질문과 무관", 의도로는 "FAQ · 환불"로 판정했습니다. 봇은 환불 카드로 답한 다음 원래 질문을 다시 붙였고, 이어서 "음 한 400명?"에 월 301~500명을 95%로 골라 해당 선택지의 안내와 마무리 문구를 보냈습니다. 화면 속 금액과 안내 문구는 샘플 규칙에 넣어 둔 예시입니다.
첫 메시지에 답이 있으면 건너뛰기. "웹훅이 계속 401이 떠요 ㅠ"라는 한 마디에는 문의 종류(오류 접수)와 첫 질문의 답(증상)이 함께 들어 있습니다.

같은 요청 안에서 의도(오류 접수 100%)와 미리 물어 둔 첫 단계(증상 = 웹훅이 401, 99%)가 함께 돌아와서, 봇은 "어떤 증상이세요?"를 건너뛰고 두 번째 질문으로 갔습니다. 미리 판정한 값은 추측이므로 자동 답변 기준(0.8)을 넘을 때만 씁니다.
되묻기. 평가에서 틀릴 뻔했던 그 문장을 대화로 보내 봅니다.

"카톡"과 "시작"이라는 말 때문에 Jev는 카카오 채널 개설을 77%로 봤습니다. 자동 기준에 못 미쳐 봇은 답 대신 확인을 구했습니다. 고객이 "아뇨 채널 말고 연동 방법이요"라고 하면, 확인 확률이 4%로 나오고 봇은 되물었던 카드를 버린 뒤 이번 말을 새로 판정해 연동 방식 상담(96%)을 시작합니다.
화난 고객은 사람에게. "아 진짜 몇번을 말해요 짜증나네"에는 불만도 1.92/2가 나왔고, 봇은 상담원 연결 안내를 보낸 뒤 멈췄습니다. 이후 "여보세요?"에는 답하지 않습니다.

확인: 여러분이 기대한 갈래와 패널의 갈래가 같으면 된 것입니다. 다르면 확률 막대에서 어느 카드와 헷갈렸는지 보입니다.
Step 6 — 카드 한 줄로 고칩니다
되묻기가 났던 문장을 다시 보겠습니다. 고객은 연동을 물었는데 Jev가 채널 개설과 헷갈렸습니다. 이때 할 일은 모델을 다시 학습시키는 것이 아닙니다. 연동 방식 상담 시나리오의 "고객이 묻는 예"에 그 표현을 한 줄 넣고 저장합니다.


같은 문장이 77% 되묻기에서 98% 자동으로 바뀌었습니다. 바뀐 것은 Jev에게 건네는 선택지 설명 한 줄입니다. 규칙을 고칠 때마다 이 효과가 다른 카드에도 번질 수 있으니, 고친 뒤에는 반드시 평가를 다시 돌립니다.
확인: npm run eval의 자동 답변 오답이 여전히 0이면 된 것입니다.
Step 7 — 실제 상담에 붙이되, 보내지는 않습니다
여기서부터 톡브릿지 설정이 필요합니다.
cp .env.example .env # TB_BRAND(whoami 의 브랜드 키), TB_WEBHOOK_SECRET(아무 긴 임의 문자열)
npm run gateway:setup # 게이트웨이가 넘겨줄 주소를 http://127.0.0.1:8793/webhook 으로
npm run gateway:start
웹 상단에서 [봇 켜기]를 켜고 [실발신]은 꺼 둔 채로 둡니다. 실제 고객 문의가 들어오면 봇은 Jev로 판정만 하고, 답장 후보는 [실시간 모니터]에 점선 말풍선으로만 보여 줍니다. 모니터 오른쪽 봇 활동에는 턴마다 갈래와 Jev 지연, 의도와 대답 판정이 쌓입니다. 며칠 이렇게 두고 실제 문의에서 어떤 판정이 나오는지 보신 뒤, 괜찮으면 확인창을 거쳐 [실발신]을 켭니다.
확인: 실제 문의에 대해 모니터에 점선 말풍선과 판정 요약이 보이고 고객 카카오톡에는 아무것도 가지 않았으면 된 것입니다.
발신을 켜면 톡브릿지 상담 건수를 씁니다. 상담 건수는 메시지 수가 아니라 상담방이 열린 날 기준(한국 표준시)으로 세고, 같은 고객과 같은 날 여러 번 주고받아도 1건입니다. 한 가지 솔직히 적어 둡니다. 05는 시뮬레이터, 한국어 평가, 서명 웹훅 수신(실발신 끔)까지 확인했고, 실채널에서 실제로 보내는 것은 저희도 아직 확인하는 중입니다. 발신 경로는 실채널 발신까지 확인한 04와 같은 코드이지만, 확인을 마치면 저장소의 검증 기록에 먼저 올리겠습니다.
5. 이렇게 하면 막힙니다
- 엉뚱한 FAQ를 확신하고 보낸다. 카드가 늘면 비슷한 카드끼리 섞입니다. 과금 기준과 요금제 추천, 환불과 결제 문의처처럼 짝이 되는 카드는 서로를 "헷갈리면 안 되는 것"에 적어 경계를 줍니다. 그래도 섞이면 자동 답변 기준을 올립니다. 어느 경우든 평가 세트에 그 문장을 넣어 재현한 뒤 고치세요.
- 되묻기가 너무 많다. 대개 "고객이 묻는 예"가 빈약한 카드입니다. 데모에서도 예가 한두 개뿐인 카드는 79~82%로 경계에 걸렸습니다. 고객 말투로 두세 개를 더 넣고 평가를 다시 보세요. 기준을 낮추는 것은 마지막 수단이고, 낮춰도 자동 답변 오답 0은 지켜야 합니다.
- "해당 없음"이 없으면 모든 문의가 어딘가로 끌려간다. Jev는 선택지 중 하나를 반드시 고르기 때문에, 해당 없음 선택지가 없으면 "점심 뭐 드셨어요?"도 가장 가까운 카드로 배정됩니다. 05의 엔진은 인사 · 감사 · 해당 없음을 자동으로 붙이니, 요청을 직접 조립하실 때도 이 셋을 빼지 마세요.
- 키 오류.
Jev 401은 키가 틀렸거나 폐기된 경우라 콘솔에서 확인합니다.Jev 422는 요청 형식 문제라 화면의 [Jev 요청 보기]로 본문을 봅니다. 키는 서버에서만 쓰고 브라우저로 내려보내지 않습니다. - 상담 내용이 외부로 나간다. 매 턴 고객 메시지, 직전 봇 질문, 최근 몇 턴의 대화가 TypeSafe API로 전송됩니다. [판정 기준]의 "최근 대화 턴 수"로 범위를 줄일 수 있고, 0으로 두면 직전 봇 질문과 이번 메시지만 갑니다. 개인정보가 오가는 채널이라면 TypeSafe의 데이터 보관 정책을 확인하고 고객 고지에 넣어 주세요.
- 고객이 보냈는데 봇이 조용하다. 게이트웨이는 한 곳으로만 넘겨줍니다. 04를 쓰다 오셨다면
npm run gateway:setup을 다시 돌려 8793으로 바꿔야 합니다(npm run doctor네 번째 항목이 포트 불일치를 알려 줍니다). 그다음 [봇 켜기], 봇 활동의건너뜀사유, 방이 정지 상태인지 순으로 봅니다. - 비용이 두 갈래다. 카카오로 나가는 답장은 톡브릿지 상담 건수(상담방이 열린 날 기준, 같은 고객·같은 날은 1건)를, Jev 판정은 TypeSafe 비용을 씁니다. 실발신을 꺼 두어도 Jev 판정 비용은 듭니다.
6. 코드로 들여다보기
04에서 05로 오며 바뀐 파일은 셋입니다. server/jev.js(Jev 호출), server/bot.js의 요청 조립과 판정, server/rules.js의 규칙 형식입니다. 수신·서명·발신·게이트웨이 코드는 04와 바이트 단위로 같습니다. 아래 발췌는 저장소 태그 v0.3.0 기준입니다.
6.1 요청 조립 — buildRequest()
매 턴 Jev에 보낼 { state, questions }를 만듭니다. 라이브 상담, 시뮬레이터, 한국어 평가, [Jev 요청 보기]가 모두 이 함수 하나를 씁니다. 평가와 실전이 같은 요청으로 판정되기 때문에, 평가 숫자를 실전에 그대로 믿을 수 있습니다.
// server/bot.js — buildRequest() (발췌·축약)
const jstate = { customer_message: pendingTexts.join('\n') };
if (lastBot) jstate.bot_last_message = lastBot; // 직전 봇 질문 — 짧은 대답을 알아듣는 열쇠
if (recent.length) jstate.recent_conversation = recent; // 최근 N턴 (판정 기준에서 조절)
const intentCriteria = {};
for (const f of rules.faqs) if (f.enabled) intentCriteria[f.id] = describe(f, f.title); // { what, examples, not_for }
for (const f of rules.flows) if (f.enabled && f.steps.length) intentCriteria[f.id] = describe(f, f.name);
Object.assign(intentCriteria, BUILTIN); // 인사 · 감사 · 해당 없음
const questions = {
intent: { type: 'choice', instructions: '고객의 최근 메시지(customer_message)는 어떤 문의인가? …', criteria: intentCriteria },
wants_human: { type: 'noul', instructions: '고객이 자동응대봇이 아닌 사람 상담원과 직접 이야기하길 원한다' },
frustration: { type: 'score', instructions: '고객의 최근 메시지는 얼마나 불만스러운가?', criteria: ['평온함', '약간 불편함', '매우 화남'] },
};
if (step) questions.step = { type: 'choice', instructions: `봇이 "${step.ask}" 라고 물었다. …`, criteria: optionCriteria(step, …) };
// Speculative fan-out — 다른 시나리오의 첫 질문을 미리 묻는다. 질문이 늘어도 Jev 는 병렬로 평가한다
for (const f of rules.flows) {
if (!f.enabled || !f.steps.length || f.id === flow?.id) continue;
questions[`pre:${f.id}`] = { type: 'choice', instructions: `고객 메시지에 "${f.steps[0].ask}" 에 대한 답이 이미 들어 있다면 …`, criteria: optionCriteria(f.steps[0], …) };
}
if (state.pendingConfirm) questions.confirm = { type: 'noul', instructions: `봇이 "${state.pendingConfirm.title}" 문의가 맞는지 물었고, 고객이 그렇다고 확인했다` };
카드의 세 칸이 여기서 { what, examples, not_for } 객체가 됩니다. Step 6에서 예시 한 줄이 확신도를 크게 움직인 것은 이 examples가 바뀌었기 때문입니다. 화면의 [Jev 요청 보기]를 누르면 이 함수가 만든 본문을 그대로 볼 수 있습니다.

6.2 숫자 → 갈래 — decide()
Jev의 대답을 받아 무엇을 보낼지 정하는 함수입니다. 2절 흐름도의 순서가 그대로 코드 순서입니다.
// server/bot.js — decide() (발췌·축약)
// 1) 사람을 원함 · 매우 화남 → 상담원 (키워드는 Jev 호출 전에 이미 걸렀다)
const human = A.wants_human?.noul ?? 0;
if (human >= S.humanThreshold) return handoff(rules.handoff.message, 'human', `상담원 요청 ${pct(human)}`);
if (S.angryHandoff && frus && frus.score >= 1.5) return handoff(rules.handoff.message, 'angry', …);
// 2) 되묻기 직후 — "네" 확률 0.7 이상이면 되물었던 카드로. 아니면 이번 메시지를 새 문의로 판정
// 3) 시나리오 진행 중 — 직전 질문의 대답인가? 아니면 곁가지 FAQ · 시나리오 전환 · 다시 묻기(2회 넘으면 상담원)
// 4) 새 문의
const t = intent.choice === 'none' ? null : findTarget(intent.choice);
if (!t || intent.confidence < S.confirmThreshold) {
return { action: 'reply', reply: P.fallback, route: 'fallback', … }; // 모를 때 문구
}
if (intent.confidence < S.autoThreshold) {
state.pendingConfirm = { kind: t.kind, id: t.id, title: t.title };
return { action: 'reply', reply: P.clarify.replace('{title}', t.title), route: 'clarify', … }; // 되묻기
}
return enter(t, t.kind === 'faq' ? 'faq' : 'flow-start', …); // 카드 답변 그대로 / 시나리오 시작
모든 reply가 P.fallback, P.clarify, 카드의 answer, 선택지의 say, 시나리오의 done처럼 규칙에 적힌 글에서 옵니다. Jev가 준 값은 어느 갈래로 갈지를 정하는 데만 쓰이고, 문장 조립에는 들어가지 않습니다.
6.3 "지어내지 않음"을 지키는 곳 — findTarget()
// server/bot.js — decide() 안 (발췌)
const findTarget = (id) => {
const faq = rules.faqs.find((f) => f.enabled && f.id === id);
if (faq) return { kind: 'faq', id, title: faq.title, faq };
const fl = rules.flows.find((f) => f.enabled && f.steps.length && f.id === id);
if (fl) return { kind: 'flow', id, title: fl.name, flow: fl };
return null; // 없거나 꺼진 카드 → 모를 때 문구로
};
Jev가 고른 id가 규칙에 존재하고 켜져 있을 때만 그 카드의 글을 보냅니다. 운영자가 카드를 꺼 두었는데 Jev가 그 id를 골랐다면, 봇은 모를 때 문구를 보냅니다. 카드의 답변 칸이 비어 있어도 마찬가지입니다. 지어낸 말이 나갈 경로를 코드에서 막아 두는 곳이 여기입니다.
6.4 호출과 재시도 — jev.js
// server/jev.js — systemOne() (발췌·축약)
const body = JSON.stringify({ state, model: config.jev.model, questions }); // model 기본값 'jev-latest'
for (let attempt = 0; attempt <= config.jev.maxRetries; attempt++) { // 기본 2회 재시도
const res = await fetch(`${config.jev.baseUrl}/v1/systemone`, {
method: 'POST',
headers: { authorization: `Bearer ${config.jev.apiKey}`, 'content-type': 'application/json' },
body, signal: ctrl.signal, // 타임아웃 10초
});
if (res.ok) return { answers: json.answers, usage: json.usage, model: json.model, ms: Date.now() - t0, … };
// 401(키) · 422(형식)는 즉시 실패. 429 · 529 · 5xx 만 지수 백오프(500ms → 5s, Retry-After 존중)
if (!RETRYABLE.has(res.status) || attempt >= config.jev.maxRetries) throw lastErr;
}
재시도 기본값은 TypeSafe 공식 SDK의 기본값을 그대로 옮겼습니다. 재시도를 다 써도 실패하면 봇은 조용히 멈추지 않고, [상담원 연결]에 적어 둔 오류 안내를 보낸 뒤 사람에게 넘깁니다. 응답의 model에는 jev-latest 대신 실제 버전(오늘은 jev-1.13.0)이 오는데, 모니터에 이 값을 남겨 둡니다. 버전이 바뀌면 평가를 다시 돌려야 하기 때문입니다.
6.5 직접 붙일 때 바꾸는 곳
| 하고 싶은 것 | 바꾸는 곳 |
|---|---|
| 우리 회사 FAQ·시나리오로 교체 | 웹에서 편집한 뒤 data/bot.json. 또는 data/demo-bot.json을 복사해 초기 규칙으로 |
| 평가 세트를 우리 문의로 | data/eval-set.json. 실제 상담 로그에서 뽑은 문장과 사람이 붙인 정답이 가장 좋습니다 |
| 판정 질문 추가 (예: 개인정보가 들었는가) | buildRequest()의 questions에 noul 하나 추가 → decide() 맨 앞에서 읽기. 병렬 평가라 지연은 거의 그대로입니다 |
| 카드별 기준 | 지금은 전역 기준 하나. 환불·계약처럼 되돌릴 수 없는 안내는 카드에 더 높은 기준을 두는 식으로 decide()의 비교를 바꿉니다 |
| 카드가 255장을 넘을 때 | choice 선택지 상한이 255입니다. 카테고리 시나리오로 나누세요(npm run doctor 아홉 번째 항목이 개수를 보여 줍니다) |
7. 다음 단계
먼저 평가 세트를 여러분 고객의 실제 문의로 바꾸세요. 데모 세트의 67문장은 저희가 손으로 만든 것이라, 여러분 채널의 말투와 오타를 다 담지 못합니다. 과거 상담에서 자주 온 문의를 뽑아 정답을 붙이면 됩니다. 같은 저장소의 03 샘플(상담지식 그래프)이 과거 문의를 꺼내는 데 도움이 됩니다.
그다음은 04와 합치는 일입니다. 05는 모를 때 "정확히 답드리기 어려워요"로 끝나는데, 그 턴만 04의 생성형 봇에게 넘길 수 있습니다. 반복 문의는 싸고 빠른 판정으로 끝내고, 설명이 필요한 문의에만 비싼 생성을 쓰는 구성입니다. 1절에서 말한 "어디로 보낼지 판단하는 일"을 상담 규모에서 직접 해 보는 셈이기도 합니다.
운영으로 옮기실 때는 몇 가지를 더 챙기셔야 합니다. 규칙을 저장하기 전에 평가를 자동으로 돌려 자동 답변 오답이 늘면 저장을 막는 장치를 두시길 권합니다. 규칙 편집 화면에는 로그인이 없고 상담방 상태는 메모리에만 있어 재시작하면 사라지므로, 화면에 권한을 걸고 상태를 데이터베이스로 옮기셔야 합니다. Jev는 아직 Early access라서, 응답의 모델 버전이 바뀔 때마다 평가를 다시 돌려 기준을 맞추는 일도 운영 절차에 넣어 두세요.
이 샘플도 그대로 가져다 쓰시기보다 여러분 상담 방식에 맞게 고쳐 쓰시라고 드리는 출발점입니다. 카드를 쓰고, 숫자를 보고, 한 줄씩 고치는 순서만 가져가셔도 충분합니다.
톡브릿지 센터에서 카드 등록 없이 시작하실 수 있습니다 — 30일 동안 상담 3건이 무료입니다. https://console.talkbridge.io/ · 문서 https://api.talkbridge.io/ · 막히시면 help@talkbridge.io
참고
- TalkBridge 파트너 연동 가이드 · 샘플 (GitHub) — 이 글의 저장소. 05 Jev 자동응대봇 README에 데모 캡처 14장 전부와 설정·문제 해결 표가 있습니다
- TypeSafe — Introducing System One Models and Jev (2026-09-15) — Jev 발표문. 응답 시간·가격 수치의 출처
- Jev 문서 — API · Choice · Confidence · Patterns · 콘솔 · 키 발급
- 톡브릿지 공식 문서 — 게이트웨이 · 센터 설정 · 요금
- 상담원이 붙기 전, 첫 문의를 봇이 받게 하기 — 04 생성형 자동응대봇. 수신과 자동응답 가드는 이 편과 같습니다
- 담장 너머의 하네스 워 — 판단(라우팅)이 비용의 자리가 된다는 산업 흐름
- TalkBridge Discord 커뮤니티