글 목록으로
2026년 9월 19일
13분 소요

Jev를 API 키 받아 직접 돌려봤다 — 계산은 코드가 하고 판단만 맡기면 29/36이 36/36이 된다

TypeSafe AI의 Jev를 직접 호출해 잰 결과다. 질문을 10개로 늘려도 지연은 그대로였고 서버 처리는 90밀리초 안팎이었다. 세기와 날짜 간격은 정확했지만 기한을 계산해 오늘과 비교하는 판단에서 무너졌고, 전액 환불된 주문을 승인 확률 0.8로 틀리기도 했다. 날짜와 잔액을 코드가 계산해 넘기자 36건이 전부 맞았다. 한국어는 정확도는 같고 confidence만 낮았다. 시리즈 3편.

에이전트나 워크플로 안에는 글을 쓸 필요가 없는 LLM 호출이 섞여 있다. 이 문의가 환불 건인지 고르고, 이 도구 호출이 위험한지 판정하고, 검색 결과 30개 중 관련 있는 것을 추리고, 이 답변을 사람에게 보내도 되는지 검사하는 일이다. 결과는 열거형 하나거나 0과 1 사이 숫자 하나인데, 그걸 얻으려고 문장을 생성하는 모델을 부른다. 그래서 몇 초를 기다리고, JSON 파싱이 가끔 깨지고, 무엇보다 모델이 얼마나 확신하는지 알 수 없다. 로그에 남는 건 "refund"라는 문자열뿐이고 그게 0.98짜리 판단인지 0.51짜리 판단인지 구분되지 않는다.

TypeSafe AI가 2026년 9월 15일 공개한 Jev는 그 호출만 떼어내겠다는 모델이다. 누가 왜 만들었고 지금 어떤 반응인지는 1편에, 같은 방식을 오픈 모델로 직접 만들어 본 결과는 2편에, 실제로 무언가를 만들어 붙여 본 결과는 4편에 적었다. 가장 많이 돌아다닌 데모는 Doom이다. The Register가 전한 수치로는 같은 판단을 GPT-5.6 Terra가 8.566초에, Jev가 0.114초에 끝냈다.

이 글은 그 모델을 API 키를 받아 직접 돌려본 기록이다. 아래 수치는 2026년 9월 19일에 jev-1.13.0을 HTTP API로 호출해 얻은 것이고, 별다른 말이 없으면 연결을 유지한 상태의 측정이다. 머신 한 대에서 잰 값이라 절대치보다 항목 사이의 차이를 보는 게 맞다. 다른 모델과 나란히 돌린 벤치마크는 아니다. 실험에 쓴 정답은 코드로 계산하거나 두 번 검수했다. 처음에 손으로 붙인 라벨 하나가 틀려 있었고, 틀린 쪽은 Jev가 아니라 나였다.

문장 대신 세 가지 답만 돌려준다

Jev에 보내는 것은 프롬프트가 아니라 상태(state)와 질문 묶음이다. 질문 타입은 셋뿐이다.

타입 하는 일 돌아오는 값
Choice 정해진 목록에서 하나 고르기 선택지, 전체 확률 분포, confidence
Score 정해진 등급 위에서 위치 매기기 점수, 등급 범례, 확률 분포, confidence
Noul 문장이 참일 확률 0에서 1 사이 값 하나

고객 문의 한 건에 세 가지를 한꺼번에 물으면 이렇게 돌아온다.

{
  "model": "jev-1.13.0",
  "answers": {
    "billing": { "type": "noul", "noul": 0.99 },
    "tone": { "type": "choice", "choice": "angry", "confidence": 1.0,
              "probabilities": { "calm": 0.0, "neutral": 0.0, "angry": 1.0 } },
    "urgency": { "type": "score", "score": 1.99, "confidence": 0.99,
                 "legend": { "0": "low", "1": "medium", "2": "high" },
                 "probabilities": { "0": 0.0, "1": 0.01, "2": 0.99 } }
  },
  "usage": { "input_tokens": 369, "output_tokens": 72 }
}

Score가 1.99로 나오는 게 눈에 띈다. 등급 위의 위치를 확률로 가중해 돌려주기 때문에 정수로 떨어지지 않는다. 두 등급 사이의 0.5는 중간값이 아니라 둘 사이에서 갈렸다는 뜻이다.

질문을 늘려도 지연이 늘지 않는다

문서는 같은 상태에 대한 질문이라면 하나 더 붙이는 비용이 거의 없다고 설명한다. 같은 티켓에 질문 개수만 바꿔 가며 12번씩 호출해 봤다.

질문 개수 지연 중앙값 p10 p90 입력 토큰 출력 토큰
1 246ms 228ms 293ms 362 20
3 276ms 225ms 296ms 394 53
5 258ms 208ms 305ms 425 85
10 263ms 213ms 297ms 599 189

질문을 열 배로 늘려도 지연은 그대로다. 대신 입력 토큰은 362에서 599로 늘었다. 질문 텍스트가 입력에 들어가기 때문이다. 공짜는 아니지만 100만 토큰에 $0.042이라 실질적으로는 공짜에 가깝다.

여기서 더 중요한 건 저 246밀리초의 구성이다. 같은 엔드포인트로 연결만 새로 맺어 보면 TCP 연결에 160에서 210밀리초가 걸린다. TypeSafe는 미국 서부에서 서비스하고, 왕복 지연이 그만큼 깔린다는 뜻이다. 연결을 유지한 호출의 246밀리초에서 그 왕복을 빼면 서버 쪽 처리는 80밀리초 안팎이다. 응답 헤더로도 확인된다. x-envoy-upstream-service-time에 92가 찍혀 있었고, 회사가 밝힌 하한 70밀리초와 맞는 값이다. 연결을 새로 맺는 첫 호출은 0.60초에서 0.73초가 걸렸다.

실무에 주는 함의가 분명하다. 한국에서 쓰면 모델이 아무리 빨라도 왕복 지연이 바닥으로 깔린다. 그래서 질문을 여러 번 나눠 보내는 설계는 손해가 크고, 같은 상태에 대한 질문은 한 번에 묶어 보내야 한다. 연결 재사용도 선택이 아니라 기본이다.

state를 키워도 크게 달라지지 않는다. 같은 티켓을 무관한 문장 사이에 묻어 두고 크기만 바꿔 여섯 번씩 호출했다.

입력 토큰 지연 중앙값 p90
568 266ms 286ms
2,484 257ms 296ms
8,872 300ms 541ms
21,644 422ms 562ms

3만 2천 토큰까지 올려도 동작했고, 그 안에 묻힌 “갱신하지 않겠다”는 신호를 묻는 Noul은 크기와 무관하게 0.98을 유지했다. 문서가 경고한 “무관한 내용이 많으면 정확도가 떨어진다”는 이 실험에서는 재현되지 않았다.

숫자에 약하다는 경고가 어디까지 맞나

공식 문서는 Jev 1.13이 취약한 작업을 아홉 가지로 적어 두고, 계산과 세기와 날짜 연산은 코드가 하라고 권한다. 정답이 분명한 17문항을 만들어 세 번씩 물어봤다.

유형 문항 정답률
세기 파란 물건 개수, 백엔드 인원수 100%
계산 실패한 요청 수, 인보이스 합계 100%
날짜 계산 두 날짜 사이 일수, 장애 지속 시간 100%
기재된 날짜 비교 사건 선후, 가장 최근 사건 100%
임계값 비교 열 개보다 많은가, 절반을 넘었는가 73%
파생 날짜와 오늘 비교 만료됐는가, 기한이 지났는가 25%

세기와 계산은 생각보다 잘했다. $120.00 + $45.50 + $89.25 + $15.00을 $269.75로 맞혔고, 2025-01-15와 2025-02-28 사이를 44일로 맞혔고, 21시 40분부터 다음 날 2시 15분까지를 4시간 35분으로 맞혔다. 문서의 경고를 곧이곧대로 받으면 놓치는 부분이다.

무너진 곳은 따로 있었다. 날짜를 직접 계산해 오늘과 비교해야 하는 네 문항 중 세 개를 틀렸다. “1월 31일에 시작해 45일 동안 유지되는 체험판이 3월 10일 기준으로 만료됐는가”에는 만료됐다고 답했다. 실제 종료일은 3월 17일이다. “7월 20일에 발행한 net-30 인보이스가 8월 12일 기준으로 연체인가”에도 연체라고 답했다. 기한은 8월 19일이다. 임계값 비교도 흔들렸다. Choice로 “파란 물건이 몇 개인가”를 물으면 12를 confidence 0.89로 맞히는데, 같은 상태에서 Noul로 “열 개보다 많은가”를 물으면 0.49, 0.51, 0.53으로 갈팡질팡했다. 개수는 아는데 그 개수를 기준선과 비교하지 못한다.

여기서 이 모델의 성격이 드러난다. 틀린 다섯 문항의 평균 confidence는 0.33이었고, 맞힌 열두 문항은 0.88이었다. 이 문항들에서는 모르면 모른다고 신호를 줬다. confidence 0.6을 바닥으로 깔면 틀린 다섯 문항이 전부 걸러지고, 자동으로 처리되는 열한 문항은 전부 정답이다. 대신 맞힐 수 있었던 한 문항도 같이 사람에게 넘어간다. 다만 이게 항상 성립하지는 않는다. 바로 아래 실험에서 확신을 갖고 틀리는 경우가 나왔다.

참고로 Noul에는 confidence 필드가 따로 없다. 위 숫자에서 Noul의 confidence는 확률이 0.5에서 얼마나 떨어져 있는지로 내가 환산한 값이다. 2편에서 역산한 Jev의 confidence 공식에 선택지 두 개를 넣은 것과 같은 계산이다.

문서가 경고한 것 중 재현되지 않은 것도 있다. 같은 명제를 긍정과 부정 Noul로 나눠 물었을 때 확률 합이 1이 아니라는 항목인데, 문서의 예시는 1.19였다. 다섯 개 명제로 확인해 보니 합이 0.99에서 1.06 사이였다. 어긋나긴 하지만 문서의 예시만큼 크지는 않았다. 그래도 서로 다른 타입의 확률을 같은 자로 비교하지 말라는 원칙은 지키는 게 안전하다.

계산을 코드로 옮기면 29/36이 36/36이 된다

위 약점이 실제 업무 판단에서 어떻게 나타나는지 보려고 가상의 환불 정책을 만들었다. 네 조건을 모두 만족해야 승인이다. 고객이 지금 환불을 요청하고 있을 것, 수령 후 30일 이내일 것(정확히 30일은 허용), 미개봉일 것, 결제액에서 이미 환불된 금액을 뺀 잔액이 0보다 클 것. 경계에 걸친 사례를 포함해 12건을 만들고, 정답은 손으로 붙이지 않고 같은 조건을 코드로 계산했다. 사례마다 세 번씩 호출했다.

입력 방식을 셋으로 나눴다.

입력 방식 정답
주문·결제·환불 기록을 그대로 주고 최종 승인 여부를 묻는다 29/36
네 조건을 Noul로 따로 묻고 코드에서 조합한다 30/36
경과 일수와 잔액을 코드가 계산해 state에 넣고 최종 승인 여부를 묻는다 36/36

원본 기록을 그대로 줬을 때 틀린 사례는 세 건이다. 정확히 30일째인 주문은 세 번 다 거절했고, 31일째인 주문은 세 번 중 한 번 승인했다. 두 경우 모두 승인 확률이 0.48에서 0.50 사이였고 confidence는 0.03과 0.01이었다. 사실상 동전을 던진 셈이고, 임계값을 걸어 두면 사람에게 넘어간다.

세 번째가 문제다. 129,900원을 결제하고 69,900원과 60,000원이 이미 환불된 주문은 잔액이 0원이라 거절해야 한다. Jev는 세 번 모두 승인을 골랐고 승인 확률은 0.73, 0.81, 0.72였다. confidence로는 0.5 안팎이다. 0.6에 건 임계값은 간신히 걸러내지만 0.5였다면 통과한다. 확신을 갖고 틀리는 경우가 실제로 있다.

질문을 쪼개는 것은 해결책이 아니었다. 조건 네 개를 따로 물어 코드에서 조합해도 30/36으로 거의 그대로였다. 모델이 뺄셈을 틀리면 잘게 나눈 질문에서도 같은 자리에서 틀린다. 달라진 건 계산을 코드로 옮겼을 때다. 경과 일수와 잔액을 코드가 계산해 “30일 이내: 예, 잔액: 0원”이라는 사실로 넘기자 36건이 전부 맞았고 confidence도 0.99 이상으로 올라왔다.

역할을 나누는 기준이 여기서 나온다. 날짜 차이와 금액 합산처럼 코드가 정확히 계산할 수 있는 것은 코드가 하고, 고객의 말이 환불 신청인지 절차 문의인지처럼 뜻을 읽어야 하는 것만 Jev에 맡긴다. 이 실험에서도 “환불 절차만 알려 달라”와 “금요일까지 안 오면 그때 환불을 요청하겠다”는 세 방식 모두에서 전부 맞혔다.

한국어에서는 정확도는 같고 confidence만 낮다

문서는 영어가 가장 정확하고 CJK도 지원한다고만 적어 놨다. 한국어로 얼마나 되는지는 나와 있지 않아서 직접 쟀다. 고객 문의 45건을 만들어 각각 영어와 한국어로 같은 내용을 쓰고, 다섯 가지 분류 중 하나를 고르게 했다. 쉬운 문항 25건과, 의도를 돌려 말하거나 구어체와 오타가 섞인 어려운 문항 20건을 섞었다. 문항마다 세 번씩, 두 언어로 270번 호출했다.

정확도 평균 confidence 중앙값
영어 100% 0.9948 1.0
한국어 100% 0.9750 1.0

정확도는 차이가 없었다. “컴플레인은 아니고요, 이번 달이 왜 평소보다 많이 나왔는지만 좀 알고 싶습니다” 같은 완곡한 문장도, “또 결제됐네요?? 부가서비스는 지난 분기에 뺀 걸로 아는데요” 같은 구어체도 전부 맞혔다. 이중부정을 쓴 여섯 문항도 양쪽 다 전부 맞혔다.

차이는 확률 분포에 있었다. 같은 문항을 두 언어로 짝지어 비교하면 45건 중 17건에서 한국어 confidence가 낮았고, 23건은 같았고, 5건만 한국어가 높았다. 가장 크게 벌어진 문항은 영어 0.997에서 한국어 0.617로 0.38이 떨어졌다.

이게 실무에서 문제가 되는 이유는 임계값 때문이다.

임계값 영어 통과 한국어 통과 추가로 사람에게 가는 건수
0.80 100% 98% 1
0.90 100% 96% 2
0.95 98% 84% 6
0.99 80% 69% 5

0.95를 자동 처리 기준으로 잡으면 영어 문의는 45건 중 44건이 자동으로 처리되는데 한국어는 38건만 처리된다. 답은 똑같이 맞았는데 여섯 건이 더 사람에게 간다. 한국어 서비스에 붙일 생각이라면 임계값을 영어 기준으로 베껴 오면 안 되고, 한국어 데이터로 따로 잡아야 한다.

낮은 confidence가 항상 모른다는 뜻은 아니다

실험 중에 헷갈리기 쉬운 결과가 하나 나왔다. 결제 문제와 SSO 장애가 같이 적힌 티켓에 “어느 팀이 먼저 맡아야 하는가”를 물었더니 confidence가 0.33 언저리에서 계속 낮게 나왔고, 여덟 번 중 한두 번은 답이 바뀌었다. state 크기를 키워도 그대로였으니 무관한 내용 때문이 아니었다.

이유는 단순하다. 그 티켓에는 실제로 두 팀의 일이 섞여 있고, 모델은 확률을 둘로 나눠 준 것이다. 문서도 받아들일 만한 대안이 여럿이면 확률이 퍼질 수 있고, 그게 곧 답이 틀렸다는 뜻은 아니라고 적어 놨다. 낮은 confidence를 무조건 사람에게 넘기는 규칙을 만들면 진짜로 모르는 경우와 둘 다 맞는 경우가 같은 취급을 받는다. 둘을 나누려면 확률 분포를 봐야 한다. 한 선택지에 0.33이 몰려 있고 나머지가 고르게 퍼진 것과, 두 선택지에 0.45씩 갈린 것은 다른 상황이다.

확률을 받았으면 분기를 코드로 옮긴다

Jev를 LLM 자리에 그대로 끼워 넣는 것으로는 얻을 게 별로 없다. 실제 변화는 판단과 실행을 나누는 데서 나온다. 답은 무엇을 할지 알려주고, confidence는 지금 그걸 실행해도 되는지 알려준다. 공식 패턴 문서가 드는 예는 전화 은행 업무다.

if action.confidence < 0.6:
    route_to_support_agent(account_id)          # 확신 자체가 없을 때
elif action.choice == "check_balance":
    read_balance(account_id)                    # 틀려도 손해가 작은 작업
elif action.choice == "approve_transfer":
    if action.confidence > 0.85:
        approve_transfer(account_id)            # 위험한 작업은 높은 기준
    else:
        ask_user_to_confirm(account_id)

임계값을 작업의 위험도에 따라 다르게 잡는다. 잔액 조회가 틀리면 사용자가 잘못된 숫자를 한 번 듣고 만다. 이체 승인이 틀리면 돈이 움직인다. LLM 분류기로는 이 구분을 하기 어렵다. 모델이 얼마나 망설였는지가 출력에 안 남기 때문이다. 앞의 숫자 실험에서는 정답률 75%짜리 문제 묶음이 임계값 하나로 자동 처리분 100%가 됐다. 환불 실험이 보여 주듯 임계값만으로 모든 오답이 걸러지지는 않으니, 계산을 코드로 옮기는 일과 같이 가야 한다.

같은 구조가 가드레일에도 쓰인다. LLM 가드레일 쿡북은 입력용과 출력용 질문 묶음을 만들어 탈옥 시도, 유해 요청, 의료 조언 요청, 자해 신호, 전체 심각도를 한 번의 호출로 평가한다. 그다음 확률을 두 개의 임계값에 걸어 통과, 검토, 차단, 상담 연결로 나눈다. 문서의 예시 값은 검토 0.35, 조치 0.70에서 0.85 사이다. 여기서 중요한 설계 선택은 평가와 정책을 분리한 것이다. 위험을 재는 일은 모델이 하고, 어느 선에서 막을지는 애플리케이션이 정한다.

에이전트 하네스에 붙인 사례도 나왔다. LangChain은 langchain-typesafe 통합을 내면서 미들웨어 두 개를 보여 줬다. 하나는 요청을 보고 빠른 모델과 강한 모델 중에서 고르는 라우팅이고, 다른 하나는 도구 호출을 실행 전에 분류해 위험한 호출을 막는 것이다.

from langchain_typesafe import Noul, TypeSafeClassifier

classifier = TypeSafeClassifier()
response = classifier.invoke(
    state="배포가 두 번 실패했고 고객에게 500 에러가 보인다.",
    questions={"urgent": Noul(instructions="지금 바로 봐야 하는 일인가?")},
)

도구 호출 차단은 하네스 엔지니어링 글에서 정리한 구분과 맞닿는다. 반복되는 판단을 프롬프트에 적어 두는 대신 호스트 코드가 집행하게 옮기는 일인데, 그동안 이 자리를 막아 온 것이 비용과 지연이었다. 모든 도구 호출 앞에 LLM 한 번을 더 부르면 턴마다 몇 초와 몇 센트가 붙는다. 모델 이름부터 제번스 역설(Jevons Paradox)에서 따온 것이고, 판정이 싸지면 판정을 더 많이 부르게 된다는 쪽에 회사가 걸고 있다. 다만 한국에서 붙일 때는 앞서 본 왕복 지연 160밀리초가 매 판정에 붙는다는 점을 같이 계산해야 한다.

함수 호출 쪽은 기대를 낮춰 잡는 게 맞다. 쿡북의 방식은 함수 이름과 닫힌 목록으로 된 인자만 질문으로 바꾼다. 자유 텍스트, 숫자, 날짜 인자는 질문하지 않고 기본값으로 넘긴다. 전체 신뢰도는 평균이 아니라 인자별 확률의 최솟값으로 잡는다. 인자 하나만 틀려도 호출 전체가 틀리기 때문이다.

지금 쓰는 구성에서 뭘 고칠까

이미 LLM으로 분류와 판정을 돌리고 있다면 다음 순서로 시험해 볼 만하다.

  1. 후보를 고른다. 출력이 열거형이나 0과 1 사이 숫자 하나이고, state가 32k 토큰 안에 들어가는 호출을 찾는다. 라우팅, 관련성 판정, 가드레일, 재순위가 여기 해당한다. 만료일이나 기한, 잔액처럼 값을 계산해 기준과 비교하는 판단은 모델에 맡기지 말고 코드에서 계산해 그 결과를 state에 넣는다. 환불 실험에서 29/36을 36/36으로 바꾼 것이 이 한 가지였다.
  2. 기준선을 기록한다. 그 호출의 현재 모델과 프롬프트, 정확도, 지연, 호출당 비용, 사람이 개입한 비율을 적어 둔다. 라벨이 붙은 사례가 없다면 먼저 200개쯤 만든다.
  3. 질문으로 옮긴다. 프롬프트를 그대로 번역하지 말고 Choice, Score, Noul 중 어디에 맞는지부터 고른다. 같은 상태에 대한 질문은 전부 한 번의 호출에 묶는다. 목록 밖의 입력이 들어올 수 있으면 other 선택지를 넣는다.
  4. 임계값을 자기 데이터로 정한다. confidence 값을 구간으로 나눠 구간별 정확도를 재고, 자동으로 처리할 범위와 사람에게 넘길 범위를 가른다. 한국어 트래픽이 있으면 언어별로 따로 잡는다. 정확도가 같아도 분포가 다르면 같은 임계값이 다른 결과를 낸다.
  5. 낮은 confidence의 이유를 나눈다. 확률이 한 곳에 몰리지 않은 게 모르기 때문인지, 여러 답이 다 맞기 때문인지 분포를 보고 구분한다. 후자라면 질문을 쪼개는 쪽이 낫다.
  6. 되돌릴 조건을 미리 정한다. 기준선 대비 정확도가 얼마나 떨어지면 원래 모델로 돌아갈지, 사람 개입이 얼마나 늘면 실패로 볼지 적어 둔다.

비용은 걱정할 수준이 아니다. 위 실험 중 가장 큰 것이 24번 호출에 입력 20만 토큰이었고 요금은 $0.0085였다. 이 글을 쓰며 돌린 실험 전부를 합쳐도 몇 센트다.

Claude Code를 쓴다면 TypeSafe가 에이전트 스킬을 마켓플레이스로 배포하고 있다.

claude plugin marketplace add typesafe-ai/skills
claude plugin install typesafe@typesafe-ai

스킬 문서가 스스로 밝히는 한계가 솔직하다. 에이전트는 질문을 잘 쓰지 못하니 같이 고쳐 쓸 각오를 하라고 적혀 있다.

정리

직접 돌려 보고 바뀐 판단이 두 가지 있다. 하나는 숫자에 약하다는 경고가 생각보다 좁다는 것이다. 세기와 사칙연산과 날짜 간격은 잘했고, 무너진 건 값을 계산해 기준선이나 오늘과 비교하는 단계였고, 거기서는 확신을 갖고 틀리기도 했다. 그 단계를 코드로 옮기자 전부 맞았다. 다른 하나는 한국어다. 정확도는 영어와 같았지만 확률 분포가 덜 날카로워서, 영어 기준으로 만든 임계값을 그대로 쓰면 한국어 문의만 더 많이 사람에게 넘어간다.

여전히 남는 한계도 분명하다. 문장을 만들어야 하는 일은 그대로 LLM이 해야 하고, state는 32k 토큰까지이고, 한국에서 호출하면 왕복 지연 160밀리초가 바닥으로 깔린다. 그리고 회사가 내건 “40배에서 200배 빠르고 최대 444배 싸다”는 값은 전부 자기가 만든 워크플로 eval에서 나온 것이고, 나도 다른 모델과 같은 조건으로 붙여 본 것은 아니다. 그 수치를 믿고 옮기기 전에 자기 과제에서 기준선을 재는 절차가 여전히 필요하다.