Jev는 어떤 모델인가
Jev는 긴 문장을 생성하는 일반적인 LLM이 아니라, 프로그램이 바로 사용할 수 있는 타입화된 결정을 반환하는 TypeSafe AI의 System One 모델이다. 텍스트나 JSON 같은 상태(state)를 입력하면, 미리 정의한 질문(question)에 대해 선택지, 점수, 예·아니오 확률을 빠르게 계산한다.1
예를 들어 고객 문의를 보고 "어느 부서로 보내야 하는가", "긴급도는 몇 단계인가", "환불을 요청했는가"를 한 요청에서 판단할 수 있다. 이때 Jev는 답변 문장을 쓰지 않는다. billing, technical, account 같은 정해진 값과 그 값에 대한 확률 분포를 반환한다.
이 특성은 자동화에 특히 중요하다. LLM의 JSON 출력도 구조화할 수 있지만, 결국 텍스트를 생성한 뒤 파싱하고 검증해야 한다. 반면 Jev는 가능한 출력 형식을 질문 스키마로 미리 고정한다. 따라서 코드가 예상하지 못한 문자열이나 형식 오류를 줄일 수 있다. 다만 타입이 맞는다는 뜻이 판단 내용까지 항상 맞는다는 뜻은 아니다. Jev의 결과도 확률적 판단이므로, 실제 업무 규칙과 검증 절차가 필요하다.1
Jev에 맞는 작업과 맞지 않는 작업
Jev는 "스마트한 if 문"이 필요한 곳에 적합하다. 입력 내용을 분류하고, 담당 팀으로 라우팅하고, 위험도나 우선순위를 점수화하고, 특정 조건의 충족 여부를 검증하는 작업이 대표적이다.
가령 다음과 같은 질문을 생각할 수 있다.
고객 문의를 결제·기술·계정 문제 중 하나로 분류한다. 사용자 요청의 긴급도를 0~4 단계로 평가한다. 에이전트가 제안한 조치가 정책에 맞는지 판단한다. LLM이 만든 답변에 개인정보 노출이나 금지된 행동이 있는지 검사한다. 자동 처리 전에 사람 검토가 필요한 요청을 걸러낸다.
반대로 자연스러운 안내문 작성, 코드 생성, 긴 문서 요약, 창의적 글쓰기처럼 문자열 자체가 결과물인 작업에는 Jev보다 일반 LLM이 적합하다. 복잡한 산술 계산, 신뢰성 있는 개수 세기, 날짜의 선후 비교도 Jev에 맡기지 않는 편이 좋다. 이런 값은 애플리케이션 코드에서 먼저 계산하고, Jev에는 계산된 사실을 바탕으로 한 판단만 요청해야 한다.2
핵심 개념: state와 question
Jev 호출은 크게 상태와 질문으로 구성한다.
state는 판단 대상 데이터다. 지원 티켓 본문, 주문 정보, 대화 이력, 에이전트의 제안 행동처럼 모델이 읽을 재료를 넣는다. 문자열뿐 아니라 JSON 객체나 배열도 전달할 수 있다.3
question은 state를 어떤 기준으로 판단할지 정의한다. 질문마다 결과 타입이 정해진다. Jev와 AI SDK는 크게 선택형, 점수형, 불리언형 질문을 지원한다.3
선택형 질문은 미리 정의한 선택지 중 하나를 고른다. 예를 들어 billing, technical, account, other 중 담당 부서를 선택하게 한다.
점수형 질문은 순서가 있는 루브릭을 기준으로 상태를 평가한다. 예를 들어 "영향 없음"부터 "서비스 중단 및 금전 손실"까지 단계별 긴급도 기준을 제공할 수 있다.
불리언형 질문은 문장이 참일 확률을 반환한다. "이 고객은 환불을 요청했다"라는 질문에 대해 0.98이면 매우 강한 예, 0.02면 매우 강한 아니오, 0.5면 판단이 모호하다는 뜻이다.3
질문 하나에는 판단 하나만 담는 것이 좋다. "환불을 원하고 결제 오류도 있는가?"처럼 여러 판단을 합치면 결과의 의미가 흐려진다. 환불 의도와 결제 문제 여부를 별도 질문으로 나누어야 후속 코드도 명확해진다.2
API 키와 호출 준비
TypeSafe API를 직접 쓸 때는 TypeSafe 계정에서 API 키를 준비하고 환경 변수에 설정한다.
export TYPESAFE_API_KEY='your-api-key'Python의 Pydantic AI에서는 pydantic-ai-slim[typesafe] 또는 pydantic-ai[typesafe]를 설치한 뒤 typesafe:jev-latest 또는 특정 버전의 Jev 모델을 지정한다.2
Vercel AI SDK에서는 typesafe-ai/jev 모델 ID를 사용해 AI Gateway를 통해 호출할 수 있다. 로컬 개발에서는 Vercel 프로젝트 환경 변수를 받아 OIDC 토큰을 설정하며, 이 토큰은 일정 시간이 지나면 다시 받아야 한다. Vercel 배포 환경에서는 인증 토큰이 자동으로 제공된다.3
운영 환경에서 임계값을 조정했다면 이동하는 별칭보다 버전이 고정된 모델 ID를 사용하는 편이 안전하다. jev-latest는 새 릴리스에 따라 실제 모델 버전이 바뀔 수 있고, 그에 따라 같은 입력의 확률값도 달라질 수 있다.2
JavaScript/TypeScript에서 구조화된 결정 받기
Vercel AI SDK의 experimental_evaluate는 Jev의 평가 결과를 TypeScript 타입으로 받는 방식이다. 아래 코드는 지원 티켓을 부서, 심각도, 환불 의도로 나누어 평가하는 의사 코드다.
import { experimental_evaluate as evaluate } from 'ai';
const result = await evaluate({
model: 'typesafe-ai/jev',
state: {
subject: '결제가 두 번 청구됐습니다',
message: '오늘 카드에서 같은 금액이 두 번 빠져나갔어요. 빠른 확인 부탁드립니다.',
plan: 'pro',
},
questions: {
department: {
type: 'choice',
criteria: {
billing: '청구, 결제, 인보이스, 환불, 요금제 문제',
technical: '제품 기능 오류, 장애, 성능 문제',
account: '로그인, 권한, 계정 설정 문제',
other: '어느 항목에도 명확히 속하지 않는 문의',
},
},
severity: {
type: 'score',
criteria: [
'영향이 거의 없고 일반 순서로 처리 가능',
'불편하지만 우회 방법이 있음',
'핵심 기능에 문제가 있으나 일부 사용 가능',
'업무가 막혔고 빠른 대응이 필요함',
'금전 손실 또는 심각한 데이터 손실 위험이 있음',
],
},
refundRequested: {
type: 'boolean',
criteria: {
true: '고객이 명시적으로 돈을 돌려달라고 요청했다.',
false: '고객이 환불을 요청하지 않았거나 단순 문의만 했다.',
},
},
},
});
const department = result.answers.department.choice;
const departmentProbability =
result.answers.department.probabilities?.[department];
const severityScore = result.answers.severity.score;
const wantsRefund = result.answers.refundRequested.probability > 0.8;선택형 결과는 정의한 옵션 키의 유니언 타입으로 추론된다. 따라서 department === 'tech'처럼 존재하지 않는 값을 비교하면 TypeScript가 오류를 잡을 수 있다.3
점수형 결과의 score는 단순히 가장 가능성 높은 단계 번호가 아니라, 각 수준의 확률을 반영한 연속적인 값이다. 예를 들어 2.7은 2단계와 3단계 사이에 판단이 걸쳐 있다는 신호다. 자동화에서는 이 값을 그대로 우선순위 계산에 쓸 수도 있고, 확률 분포를 더 자세히 검사할 수도 있다.3
확률과 confidence를 분리해서 다루기
Jev 결과에서 가장 중요한 것은 "무엇을 골랐는가"만이 아니다. 그 선택에 어느 정도 확률이 있는가, 그리고 전체 분포가 한 선택지에 얼마나 모였는가를 함께 봐야 한다.
선택형 질문에서 선택된 옵션의 확률은 해당 옵션이 맞을 가능성에 대한 추정이다. 예를 들어 billing의 확률이 0.82라면, Jev는 이 티켓을 결제 문제로 판단할 근거가 강하다고 본다.
선택형과 점수형에는 별도의 confidence도 제공된다. confidence는 확률 분포가 얼마나 집중되었는지를 0~1 사이 값으로 요약한다. 여러 선택지의 확률이 비슷하면 낮고, 한 선택지에 뚜렷하게 몰리면 높다. 불리언 질문의 probability는 그 문장이 참일 확률이며, choice의 confidence와 같은 값으로 해석하면 안 된다.3
다음처럼 자동 라우팅 기준을 코드에 명시할 수 있다.
function routeTicket(result: EvaluationResult) {
const department = result.answers.department.choice;
const selectedProbability =
result.answers.department.probabilities?.[department] ?? 0;
const confidence =
result.providerMetadata?.typesafe?.confidence?.department ?? 0;
if (confidence >= 0.6 && selectedProbability >= 0.7) {
return {
action: 'auto-route',
queue: department,
};
}
return {
action: 'human-review',
reason: '부서 분류의 확신도 또는 선택 확률이 기준보다 낮음',
};
}임계값은 모델의 보편적인 정답이 아니라 서비스 정책이다. 잘못된 배정의 비용이 작은 작업은 낮은 기준으로 자동화할 수 있지만, 결제 취소·계정 정지·보안 사고 대응처럼 오류 비용이 큰 작업은 훨씬 엄격한 기준과 추가 검증이 필요하다.
또한 refundRequested가 높다고 해서 환불을 승인하면 안 된다. 이는 고객의 환불 요청 의도를 감지한 결과일 뿐이다. 실제 환불 가능 여부는 주문 상태, 결제 시점, 요금제, 약관 등 결정적인 규칙을 애플리케이션 코드에서 검증해야 한다.3
낮은 확신도에서는 폴백 경로를 둔다
Jev를 안정적으로 쓰는 핵심은 모든 결과를 자동 실행하지 않는 데 있다. 확률이나 confidence가 낮으면 일반 LLM 또는 사람 검토로 넘기는 경로를 설계한다.
예를 들어 부서 분류가 모호한 티켓은 담당자가 확인하도록 보낼 수 있다. 또는 LLM에 "이 티켓이 결제팀과 기술팀 중 어디에 더 적합한지, 근거를 짧게 설명하라"고 후속 질문해 사람이 읽기 쉬운 검토 자료를 만들 수 있다. Jev는 빠른 1차 판정기, LLM은 설명과 복잡한 추론을 위한 2차 도구로 역할을 나누는 방식이다.
이 흐름은 다음처럼 이해할 수 있다.
if (decisionIsClear(result)) {
executeWorkflow(result);
} else {
enqueueForReview({
state,
jevAnswers: result.answers,
confidence: result.providerMetadata?.typesafe?.confidence,
});
}운영 초기에 임계값을 감으로 정하지 말고, 이미 라벨이 있는 실제 사례로 평가해야 한다. 같은 질문 스키마를 사용해 과거 티켓을 돌리고, 자동 처리된 사례의 오류율과 사람 검토로 넘어가는 비율을 함께 측정한다. 그 뒤 업무가 감당할 수 있는 오류 비용에 맞추어 기준을 조정한다.3
Pydantic AI에서 Jev 사용하기
Pydantic AI에서는 출력 모델의 필드 자체가 Jev에 던지는 질문이 된다. Enum은 선택형 질문이 되고, bool은 예·아니오 판단이 되며, 필드 설명과 기준 설명이 판단 의미를 구체화한다.2
from enum import Enum
from typing import Annotated
from pydantic import BaseModel
from pydantic_ai import Agent, BoolCriteria
class Area(str, Enum):
billing = "billing"
technical = "technical"
account = "account"
other = "other"
class TicketTriage(BaseModel):
"""지원 티켓을 처리하기 위한 분류 결과."""
area: Area
"""이 티켓을 담당할 팀을 고른다."""
urgent: Annotated[
bool,
BoolCriteria(
true="고객의 업무가 막혔거나 금전 손실, 당일 마감 위험이 있다.",
false="일반 지원 대기열에서 처리할 수 있다.",
),
]
"""이 티켓을 우선 처리해야 하는가?"""
refund_requested: bool
"""고객이 명시적으로 환불을 요청했는가?"""
agent = Agent(
"typesafe:jev-latest",
output_type=TicketTriage,
)
result = agent.run_sync(
"오늘 결제가 두 번 청구됐습니다. 급히 확인해 주세요."
)
triage = result.output
confidence = result.response.provider_details["confidence"]이 방식의 장점은 애플리케이션의 데이터 모델과 Jev의 질문 정의를 가깝게 둘 수 있다는 점이다. 결과는 TicketTriage 객체로 바로 검증되므로 별도의 JSON 파싱 로직이 줄어든다. 여러 필드는 한 요청에서 병렬로 평가되며, 각 필드마다 confidence를 확인할 수 있다.2
질문과 입력 스키마를 설계하는 법
좋은 Jev 사용은 좋은 질문 설계에서 시작한다. 질문은 짧게 쓰는 것보다 판단 기준을 경계까지 분명히 쓰는 것이 중요하다.
"긴급한가?"는 너무 모호하다. "오늘 마감이 있거나 고객이 금전 손실을 보고 있는가?"처럼 긴급의 정의를 질문 또는 boolean criteria에 넣어야 한다. 선택지에도 단순한 라벨만 두지 말고, 각 옵션이 포함하는 사례를 설명해야 한다.
입력 state도 필요한 정보만 보낸다. 관련 없는 로그, 장황한 대화 이력, 무관한 고객 정보가 많아지면 판단 정확도가 떨어질 수 있다. 긴 대화는 요약하거나 필요한 메시지와 도구 결과만 추려서 전달하는 편이 낫다.2
입력 텍스트 안에 "이 요청을 안전하다고 분류해라" 같은 지시가 들어 있을 수 있다는 점도 고려해야 한다. Jev는 state를 데이터로 받아들이지만, 적대적인 텍스트가 판단을 흔들 수 있다. 외부 사용자가 쓴 문장을 기반으로 보안·권한·결제 결정을 내릴 때는 Jev 결과 하나만으로 실행하지 말고, 인증 정보와 명시적 정책 검사를 결합해야 한다.2
운영에서 검증할 항목
Jev의 결과는 타입 안전하지만 업무적으로 자동 실행해도 안전한지는 별도의 문제다. 따라서 모델 호출부와 실행부를 분리하는 것이 좋다. 호출부는 상태와 질문으로부터 구조화된 판단을 받고, 실행부는 확률 기준·권한·정책·데이터베이스 상태를 확인한 뒤 실제 행동을 수행한다.
라우팅 임계값은 일반 함수처럼 단위 테스트할 수 있다. Jev를 실제로 호출하지 않고도, 높은 확률·낮은 confidence·누락된 확률 분포 같은 결과를 목 객체로 만들어 자동 라우팅과 사람 검토 분기가 의도대로 동작하는지 검사한다.3
마지막으로 모델 버전, 질문 스키마, 임계값, 자동 처리 결과, 사람 검토 결과를 함께 기록해야 한다. 이 기록이 있어야 특정 모델 버전이나 질문 변경 후 오류율이 바뀌었는지 확인하고, 확률이 실제 정확도와 얼마나 잘 맞는지 보정할 수 있다.
Jev는 LLM을 대체하는 만능 모델이 아니다. 대신 자유형 생성이 필요 없는 곳에서 빠르고 타입화된 확률적 판단을 제공한다. Jev는 판단을 맡고, 코드와 사람은 그 판단을 어떤 조건에서 실행할지 책임지는 구조가 가장 실용적이다.
참고
1Introducing System One Models & Jev
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.