“환각도 타입 에러도 0%”라는 Jev 를 써도 오답을 잡는 검증은 그대로 남습니다
- 공유 링크 만들기
- X
- 이메일
- 기타 앱
환각이 0% 라는 말과 정확도가 최고 76.0% 라는 말은 같이 성립합니다. 둘 다 TypeSafe AI(타입세이프)가 Jev(제브)를 공개하면서 내놓은 값이고, 서로 모순되지 않습니다. 모순처럼 보인다면 앞의 0% 가 무엇에 대한 0% 인지를 잘못 읽은 겁니다.
이 발표문을 열어본 건 지금까지 본 0% 주장하고는 종류가 좀 달라 보여서였습니다. 대부분의 0% 는 재서 나온 값입니다. 몇 건 중 몇 건이었다는 분모와 분자가 뒤에 붙어 있고, 그래서 다음 달에 다시 재면 0.3% 가 될 수도 있는 값이죠. 그런데 이 0% 는 재기 전에 이미 참인 문장이었습니다. 출력이 미리 정의한 스키마 밖으로 나갈 수 없게 만들어 놓았으니, 스키마를 벗어나는 사건의 빈도는 세어 볼 필요가 없습니다.
읽고 나서 달라진 게 있습니다. 「0% 가 사실이냐」를 따지는 자리에서 내려왔습니다. 사실입니다. 대신 무엇에 대해 사실인지가 문제로 남습니다. 보장되는 것은 돌아온 값의 형식이고, 그 값이 맞느냐는 보장 밖에 그대로 있습니다.
범위를 먼저 그어 두겠습니다. 저는 Jev 를 호출해 본 적이 없습니다. 신청해서 받아 돌려본 적이 없으니 지연이 실제로 얼마나 나오는지, 확률이 얼마나 잘 맞는지를 제 눈으로 본 문장은 이 글에 하나도 없습니다. 제가 읽은 건 TypeSafe 의 공식 발표문, 그 발표문이 넘긴 평가 사이트, Wikipedia 항목, 그리고 이 모델을 두고 쓴 외부 글 두 편입니다. 전부 2026년 9월 22일에 확인했습니다.

Jev 가 돌려주는 것은 문장이 아니라 스키마에 맞는 값입니다
Jev 는 문장을 생성하지 않습니다. 미리 정의한 스키마에 맞는 타입이 붙은 값과 확률, 그리고 신뢰도를 돌려줍니다. 질문의 형태는 세 가지입니다. 정의된 집합에서 하나를 고르는 Choice, 정렬된 수준에 대해 상태를 평가하는 Score, 예 또는 아니오 명제를 0에서 1 사이 확률로 평가하는 Noul 입니다. Choice 는 고른 값만 주는 게 아니라 옵션별 확률까지 같이 줍니다.
TypeSafe 는 이 부류를 System One 모델이라고 부릅니다. 발표문 제목에도 모델 이름보다 이 분류가 먼저 나옵니다. 오래 생각해서 답을 만들어 내는 쪽이 아니라 보자마자 판단이 떨어지는 쪽을 맡기겠다는 이름인데, 세 질문 유형이 실제로 전부 그런 모양입니다. 셋 다 답을 서술하지 않고 미리 정해 둔 눈금 위의 한 자리를 가리킵니다.
구조도 흔한 쪽이 아닙니다. 토큰을 하나씩 이어 붙이는 자기회귀 방식이 아니라, 상태 하나를 받아 거기 붙은 질문을 전부 병렬로 평가하고 한 번의 패스로 답을 다 돌려줍니다. 훈련은 RLCD(Reinforcement Learning for Calibrated Decisions)라고 부르는 방식으로 했고, 사람의 선호가 아니라 결과에 대한 확률을 최적화했다고 밝혔습니다.
여기까지 보면 0% 가 어디서 나온 말인지가 그냥 보입니다. Wikipedia 의 Jev 항목은 이렇게 적습니다.
답변이 미리 정의되어 있기 때문에, 모델은 제공된 스키마 외의 값을 반환할 수 없다. TypeSafe 는 이를 '할루시네이션과 타입 에러를 제거'하는 것으로 제시한다.
반환할 수 없다는 건 측정해 보니 그런 적이 없었다는 말이 아닙니다. 구조상 그 사건이 일어날 자리가 없다는 말입니다. 그래서 이 문장은 참입니다. 저는 이걸 과장이라고 보지 않습니다. 모델이 뱉은 응답에서 JSON 블록을 찾다가 따옴표가 하나 깨져서 밤에 알림을 받아 본 사람이라면, 그 종류의 사고를 구조로 없앴다는 게 실제로 얻어지는 것이라는 걸 압니다.
문제는 이 참인 문장이 어디까지를 덮느냐입니다.
같은 발표가 가리킨 evals.typesafe.ai 에 정확도 수치가 있습니다
공식 발표문에는 정확도 수치가 없습니다. 자사 워크플로우 평가에서 193.6배 빠르고 444.6배 저렴하다는 비교는 적혀 있는데, 얼마나 맞혔는지는 「See our workflow evals site for all the details」로 넘깁니다. 그 사이트가 evals.typesafe.ai 입니다.
들어가면 점이 흩뿌려진 차트가 나오는데, 화면에 보이는 숫자만으로는 어느 점이 무엇인지 알 수가 없습니다. 그래서 페이지를 내려받아 차트 SVG 안의 <title> 툴팁을 그대로 뽑았습니다. 요약된 설명이 아니라 사이트가 각 점에 직접 붙여 둔 문자열입니다. 기준은 이렇게 적혀 있습니다.
Each point averages one model configuration's accuracy, cost and time over the four workflows with equal weight, against the consensus labels.
네 개의 워크플로우는 Security Incidents, Agent Trace Observability, Invoice Processing, Customer Service 이고, 정답 기준은 여러 모델이 합의한 라벨입니다.
Jev 는 구성이 다섯 개 올라가 있습니다. 그중 정확도가 가장 높은 구성이 76.0% 이고, 그때 비용이 $0.0001 에 시간이 0.4초입니다.
여기서 방향을 하나 끊어 두겠습니다. 이걸 「Jev 는 정확도가 낮다」로 읽으면 표를 잘못 읽은 겁니다. 같은 평가에서 sol 의 최고 구성이 79.1% 인데 $0.2152 에 34.3초가 걸렸고, opus 5 는 78.4% 에 $0.4856 과 92.1초입니다. 정확도 3점을 더 얻으려고 비용을 2000배 넘게, 시간을 여든 배 넘게 쓴 자리입니다. 파레토 프런티어를 차지한다는 TypeSafe 의 주장은 이 숫자들과 어긋나지 않습니다.
그리고 이 평가는 TypeSafe 가 만들고 TypeSafe 가 돌렸습니다. 워크플로우를 자사 모델 능력 팀 구성원이 제작했고 편향 가능성이 있으며 보고된 수치가 실제 결과의 상한에 가까울 수 있다고, 같은 문서에 본인들이 적어 두었습니다. 정답 기준도 사람이 붙인 라벨이 아니라 모델들이 합의한 라벨입니다. 가장 유리하게 측정했을 조건에서 나온 값이 76.0% 라는 뜻입니다.
제가 여기서 보는 건 76.0% 라는 값의 높낮이가 아닙니다. 0% 와 76.0% 가 같은 회사의 같은 발표 안에 나란히 있다는 배치입니다. 앞의 것은 출력 형식이 스키마를 벗어나지 않는다는 보장이고, 뒤의 것은 그 스키마 안에서 고른 값이 맞았는지의 비율입니다. 층위가 다르고, 앞의 것이 뒤의 것을 끌어올리지 않습니다.
Choice 로 고른 값이 틀리면 타입 검사에도 파서에도 안 걸립니다
시나리오를 하나 끝까지 펴 보겠습니다. 보안 알림을 분류하는 자리라고 하겠습니다. 심각도 집합을 critical, high, medium, low 넷으로 정해 두고, 알림 하나를 상태로 넣어 Choice 로 물어봅니다. 돌아온 값이 low 입니다.
이 응답에서 잘못된 곳을 찾아보면 형식 쪽에는 없습니다. 역직렬화가 성공하고, 타입 검사가 통과하고, low 를 받는 분기가 정상 동작합니다. enum 에도 들어 있고 철자도 맞고 대소문자도 맞습니다. 로그에는 요청 하나가 정상 처리됐다고 남습니다.
그런데 그 알림이 실제로는 자격 증명이 밖으로 나간 사건이었다면, 이 응답은 완전히 틀린 응답입니다. 틀렸는데 아무 데서도 안 걸립니다.
low 로 분류된 알림이 그 뒤에 어디로 가는지를 따라가 보면 더 분명해집니다. 즉시 호출은 안 걸리고, 대시보드의 낮은 우선순위 목록으로 들어가고, 주간 리포트에서 건수로만 집계됩니다. 사람이 그 알림을 다시 볼 확률이 분류 결과에 따라 결정되는 구조인데, 분류를 한 쪽은 자기가 맞았는지 틀렸는지를 모릅니다. 확률과 신뢰도를 같이 돌려주기는 하지만, 그건 이 값이 맞다고 모델이 얼마나 믿는지이지 맞았는지가 아닙니다.
이게 두 종류의 실패가 갈리는 지점입니다. 깨진 JSON 이 돌아오면 파서가 예외를 던지고 스택 트레이스가 남습니다. 어디서 터졌는지 보이고, 알림이 울리고, 누군가 고칩니다. 스키마에 맞는 틀린 값은 아무것도 남기지 않습니다. 실패가 예외로 터지지 않고 정상 응답의 얼굴을 하고 지나갑니다. 나중에 사고가 나서 거슬러 올라가 보면, 그 시점의 로그에는 처리 완료만 적혀 있습니다.
Sean Goedecke 가 이 대목을 semantic dodge 라고 불렀습니다. 환각을 할 수 없다는 주장이 말을 돌린 것에 가깝다는 뜻인데, 그가 든 이유가 정확히 이겁니다. Jev 도 선택지 안에서 틀린 것을 고를 수 있습니다.
같은 모양을 전에 한 번 본 적이 있습니다. 워크플로우 액션 일곱 개 중 셋이 로그만 남기고 성공을 반환하던 코드였는데, 그때도 반환값의 형식은 성공이었습니다. 호출한 쪽에서 보면 아무 문제가 없었고, 문제는 그 함수가 실제로 한 일이 없다는 데 있었습니다. 형식이 성공을 말하고 내용이 실패를 말할 때, 검사기가 읽는 건 형식 쪽입니다.
걷어내도 되는 코드는 JSON 파싱이고 남겨야 하는 코드는 값을 보는 검증입니다
구조화 출력을 보장하는 모델로 갈아타면 지워도 되는 코드가 실제로 생깁니다. 응답 문자열에서 JSON 블록을 찾아내는 부분, 깨진 따옴표를 고쳐서 다시 파싱해 보는 부분, enum 에 없는 라벨이 돌아왔을 때 기본값으로 떨어뜨리는 부분, 파싱에 세 번 실패하면 프롬프트를 바꿔서 재시도하는 부분. 이런 코드를 모델 응답 근처에 쌓아 두고 사는 게 지금까지의 기본값이었고, 그게 없어지는 건 작은 변화가 아닙니다. 걷어내겠다는 판단은 맞습니다.
남겨야 하는 건 고른 값이 상황에 맞는지 보는 쪽입니다. 이건 형식 검사가 아니라 내용 검사라서, 스키마가 아무리 단단해도 대신해 주지 않습니다.
문제는 이 둘이 코드에서 붙어 있다는 겁니다.
응답을 받아서 파싱하고, 검사하고, 분기하는 흐름은 대체로 한 함수 안에 들어 있습니다. 위쪽 절반이 형식을 보고 아래쪽 절반이 내용을 봅니다. 그리고 「이제 파싱 실패가 안 나니까 이 방어 코드는 필요 없다」는 판단으로 함수를 열면, 눈에 들어오는 건 함수 전체입니다. 위쪽을 지우는 김에 아래쪽도 같이 정리되는 일이 생각보다 자주 일어납니다. 둘 다 「모델이 이상한 걸 주면 어떡하지」에서 출발해 붙은 코드라, 쓴 사람 머릿속에서도 한 덩어리로 묶여 있기 때문입니다.
앞의 보안 알림 자리를 코드로 적으면 대략 이런 모양이 됩니다.
resp = call_model(state)
try:
data = json.loads(extract_json(resp))
except JSONDecodeError:
return fallback(state)
if data["severity"] not in SEVERITIES:
data["severity"] = "medium"
if data["severity"] == "low" and state.has_credential_signal:
escalate(state, reason="모델 분류와 수집된 신호가 어긋남")
위쪽은 Jev 로 갈아타면 지워도 되는 줄이고, 마지막 두 줄은 남아야 하는 줄입니다. 같은 함수 안에 붙어 있고 들여쓰기도 같습니다. 파싱 방어를 걷어내라는 작업을 들고 이 함수를 열었을 때, 마지막 두 줄이 다른 종류라는 걸 알아보려면 그 줄이 무엇을 보고 있는지를 읽어야 합니다. 지워야 할 줄과 남겨야 할 줄이 생김새로 구분되지 않는다는 게 이 작업의 실제 난이도입니다.
이건 Jev 만의 이야기는 아닙니다. 다른 곳의 구조화 출력으로 옮길 때도 모양이 같습니다. 형식 보장이 강해질수록 형식 방어 코드가 줄어드는 건 정상이고, 그때 내용 검증까지 같이 줄어드는지를 보는 게 이 갈아타기에서 실제로 할 일입니다. 커밋 diff 에서 지워진 줄을 볼 때 두 종류가 섞여 있는지만 봐도 절반은 걸러집니다.
선택지 집합에 정답이 없으면 Jev 는 스키마를 지키면서 틀립니다
0% 는 집합 안에서만 성립하는 문장입니다. 그러면 그 집합은 누가 정하는가가 남습니다. 사람이 정합니다.
앞의 보안 알림으로 돌아가면, 심각도 넷을 정한 것도 사람이고 어떤 상태를 넣어 줄지 고른 것도 사람입니다. 그 상태 안에 판단에 필요한 정보가 안 들어 있으면 모델은 들어 있는 것만 보고 고릅니다. 집합에 맞는 값이 아예 없는 경우도 있습니다. 「이건 우리 시스템 얘기가 아니다」가 정답인데 집합에 그런 항목이 없으면, 모델은 넷 중 하나를 고릅니다. 고를 수밖에 없습니다. 그게 이 모델이 하는 일이니까요.
그렇게 나온 응답도 스키마를 지킨 응답입니다. 0% 는 그대로 유지됩니다.
여기에 설계상의 제약이 하나 더 겹칩니다. TypeSafe 는 선택지 카디널리티가 256 을 넘으면 2단계 구성이 필요해져서 속도가 떨어진다고 적어 두었습니다. 그러니 담기지 않는 상황을 줄이려고 선택지를 넉넉하게 열어 두는 방향으로 설계를 밀면, 이 모델을 쓰는 가장 큰 이유였던 속도가 깎입니다. 집합을 좁히면 안 담기는 상황이 늘고, 넓히면 느려집니다. 이 저울질은 모델이 해 주지 않습니다.
그리고 집합을 정한 시점과 그 집합이 틀어지는 시점은 보통 몇 달씩 떨어져 있습니다. 처음 넷으로 자를 때는 그 넷으로 충분했고, 그 판단이 낡았다는 건 넷에 안 담기는 알림이 들어오기 시작할 때 드러납니다. 그때도 모델은 계속 넷 중 하나를 돌려주고 있고, 스키마도 계속 지켜지고 있습니다. 집합이 상황을 못 담게 됐다는 신호는 응답 어디에도 실려 오지 않습니다.
규격이 문서에 적혀 있는데 그걸 세는 방법이 없어서 같은 글의 길이가 두 가지로 나온 적이 있었습니다. 그때 걸린 것도 규격 자체가 아니라 규격 바깥이었고, 지금 이 경우도 걸리는 자리가 모델 밖입니다. 「이 집합으로 이 상황이 담기는가」를 보는 코드는 모델이 대신 두지 못하고, 안 두면 아무 데서도 안 걸립니다.
RLCD 로 맞췄다는 확률을 임계값으로 쓸 근거는 아직 밖에 없습니다
Jev 는 고른 값만 주는 게 아니라 옵션별 확률과 신뢰도를 같이 줍니다. 그리고 TypeSafe 는 훈련 목표가 사람의 선호가 아니라 결과에 대한 확률이었다고 밝혔습니다. 캘리브레이션을 겨냥해서 훈련했다는 뜻이고, 이게 맞는다면 신뢰도 0.9 는 실제로 열 번 중 아홉 번 맞는 값이 됩니다.
이걸 코드에서 어떻게 쓰게 되는지는 뻔합니다. 임계값을 하나 정하고, 그 위는 자동으로 흘려보내고 아래는 사람에게 올립니다. 구조화 출력에 확률까지 붙어서 오면 이 분기를 안 만들기가 더 어렵습니다.
그런데 그 임계값이 서 있는 바닥이 무엇인지는 봐야 합니다. 캘리브레이션이 실제로 맞는다는 걸 TypeSafe 밖에서 확인한 자료를 저는 찾지 못했습니다. 찾아봤는데 안 보이더라는 것이지, 없다고 단정하는 건 아닙니다. 공개 벤치마크 성적도 안 나와 있습니다. 공식 FAQ 에 「How does Jev perform against public benchmarks?」라는 항목이 있는데, 질문만 있고 답이 비어 있습니다.
그러니 지금 임계값을 0.9 로 두든 0.95 로 두든, 그 숫자는 제조사가 그렇게 훈련했다고 밝힌 말 위에 서 있습니다. 알고 두는 것과 모르고 두는 것은 코드에서 똑같이 생겼지만 다른 물건입니다. 모르고 두면 나중에 그 분기가 틀렸을 때 어디를 열어야 하는지가 없습니다.
확인해 보지 않은 쪽이 더 단정적으로 말한다고 얼마 전에 썼는데, 그건 남 얘기로만 두기에는 쉬운 자리입니다. 제조사가 밝힌 값을 그대로 읽어서 코드에 옮기는 것도 확인하지 않고 안전을 말하는 방식 중 하나입니다.
Goedecke 는 제약 디코딩으로도 같은 타입 보장이 나온다고 적었습니다
Goedecke 의 지적 중에 기술 쪽으로 가장 뾰족한 건 이겁니다. 타입 보장 자체는 새 모델 없이도 얻을 수 있다는 것. 기존의 제약 디코딩은 자기회귀 생성을 그대로 둔 채 로짓 샘플러가 문법에 맞지 않는 토큰을 버리는 방식인데, 출력이 스키마를 벗어나지 않는다는 결과는 여기서도 똑같이 나옵니다. 오픈 모델에 제약 디코딩을 걸어도 같습니다. 그러니 0% 는 Jev 의 고유한 성과라기보다, 구조화 출력이라는 접근이 원래 주는 것에 가깝습니다.
그가 Jev 를 깎기만 한 건 아닙니다. RLCD 가 새로운 스케일링 축은 아니라고 보면서도, 구조화 출력에만 맞춰 파인튜닝한 모델이라는 점은 의미 있는 이점일 수 있다고 인정했습니다. 그리고 그가 가장 중요한 변화로 꼽은 건 정확도도 타입 안전성도 아니고 속도였습니다.
이 대목은 저도 같은 쪽으로 봅니다. 발표문이 밝힌 지연이 실제로 70에서 500밀리초 사이라면, 같은 일을 빨리 하는 수준이 아니라 지금까지 모델을 못 넣던 자리에 넣을 수 있게 되는 쪽입니다. 요청마다 판단이 한 번씩 들어가야 하는 경로, 사용자가 화면에서 기다리고 있는 경로, 초당 수천 건이 지나가는 경로. 여기에 수십 초짜리 모델 호출을 넣는 건 비싼 게 아니라 불가능한 일이었습니다. 입력 100만 토큰당 $0.042 에 출력 토큰이 무료라는 가격도 같은 방향으로 붙습니다.
그래서 이 글은 Jev 를 쓰지 말자는 글이 아닙니다. 형식 보장과 속도는 실제로 얻어집니다. 다만 얻어지는 것과 얻어지지 않는 것이 하나의 문구에 같이 묶여 있어서, 문구를 그대로 믿고 코드를 고치면 안 얻어진 쪽에서 구멍이 납니다.
TypeSafe 는 자기 한계를 적었고 아키텍처는 적지 않았습니다
발표문을 읽으면서 좀 의외였던 건 TypeSafe 가 자기 수치에 붙여 둔 단서들이었습니다. 비교 대상 LLM 을 OpenRouter 를 거쳐 돌렸기 때문에 라우팅 편향이 있을 수 있다고 적었고, 워크플로우를 자사 모델 능력 팀이 만들었으니 편향 가능성이 있고 보고된 수치가 실제 결과의 상한에 가까울 수 있다고도 적었습니다. Doom 을 플레이하는 봇 데모도 화면을 보고 움직인 게 아니라 텍스트로 정리된 게임 상태를 받아 돌아간 것이고, 이미지 입력은 아직 안 된다고 괄호까지 쳐서 밝혔습니다. 카디널리티 256 제약도 같은 문서에 있습니다.
제품을 알리려고 쓴 문서에서 이만큼 적어 두는 건 흔하지 않습니다. 읽는 사람이 어디를 의심해야 하는지를 본인들이 먼저 짚어 준 셈이라, 이 부분은 값이 있습니다.
적지 않은 것도 있습니다. 정확한 아키텍처, 가중치, 기술 논문이 없습니다.
그래서 붙은 논란이 하나 있습니다. ConvAI Innovations 쪽의 Nandakishor Mukkunnoth 가 같은 개념을 1년 앞서 공개했다고 주장합니다. 2025년 3월에 낸 SalesRLAgent 를 근거로 들면서, 자기 쪽은 가중치와 공개 데이터셋과 패키지를 같이 냈는데 TypeSafe 는 논문도 가중치도 학습 데이터도 없이 같은 비자기회귀 판단 개념을 새로운 돌파처럼 제시했다고 적었습니다.
어느 쪽이 먼저인지 저는 판정하지 않습니다. 양쪽 다 비교 가능한 형태로 기술 세부를 내놓지 않았고, 그 상태에서 내리는 판정은 판정이 아니라 편들기입니다.
다만 판정이 안 되는 이유가 이 글의 앞부분과 같은 자리에서 나옵니다. 귀속을 못 가리는 것도, 0% 가 정확히 어디까지를 덮는 보장인지를 밖에서 재지 못하는 것도, 같은 미공개에서 나옵니다. 발표문과 평가 사이트가 말해 주는 만큼이 지금 확인 가능한 전부입니다.
사람들은 자기가 볼 수 있는 위험은 다들 스스로 먼저 적는다고 얼마 전에 썼습니다. TypeSafe 도 그렇게 했다고 보고 있습니다. 편향 가능성과 카디널리티 제약은 자기들이 만들면서 마주친 것이라 적혔고, 0% 라는 문구가 어디까지를 덮는지는 만드는 쪽에서 마주칠 일이 없으니 안 적혔습니다. 그 문구를 읽고 코드를 고치는 건 만든 쪽이 아니라 쓰는 쪽에서 하는 일이니까요.
지금은 이렇게 보고 있습니다. 파싱 실패에 대비하던 코드는 걷어내도 되고, 그 손이 오판에 대비하던 검증까지 가지 않게 하는 게 이번 갈아타기에서 실제로 할 일입니다. 역직렬화 오류는 스택 트레이스를 남기지만 스키마에 맞는 틀린 값은 아무것도 안 남긴다는 게, 이 둘을 다르게 다뤄야 하는 이유의 전부입니다.
- 공유 링크 만들기
- X
- 이메일
- 기타 앱
댓글
댓글 쓰기