개요

비가 오면 농부는 싫어할까?

보통은 그렇다고 답한다. 그럼 표에 농부 × 비 = 불만이라고 한 줄 쓰면 끝이다. 그런데 그 농부가 사실 일하기 싫은 농부라면? 비가 오면 오늘은 쉴 수 있으니 오히려 좋아한다. 같은 비, 같은 농부인데 반응이 뒤집혔다.

이 프로젝트를 시작할 때부터 붙잡고 있던 질문이 있다.

이거, Jev 말고 if 분기로도 되지 않나?

앞 글에서 게임을 구름 캐는 천사로 피봇하면서 이 질문이 다시 나왔다. 게임에 판정 모델이나 LLM을 넣을지 고민 중이라면, 그 판단을 “반응이 맥락 때문에 뒤집히는가” 하나로 줄인 과정과 그걸 검증할 케이스 설계를 볼 수 있을 거다. 적어보겠다!

Jev는 고르기만 한다

Jev는 TypeSafe AI가 2026년 9월에 공개한 판정 전용 모델이다. 문장을 생성하지 않고, 상태와 질문을 보내면 정해진 형태의 판단만 돌려준다.

질의 형태 하는 일
choice 주어진 선택지 중 하나를 고른다
score 순서 있는 등급으로 평가한다
noul 예/아니오일 확률(0~1)을 낸다

원래 기획에서 Jev 자리는 시청자였다. 성향이 다른 AI 시청자 여럿이 한 사건을 동시에 보고 각자 반응하는 구조라, 시청자 성향 × 게임 사건 조합을 사람이 손으로 다 채울 수 없다는 게 근거였다. 피봇하면서 시청자 레이어는 보류됐고, 새 컨셉에서 Jev가 설 자리가 있는지 다시 따져야 했다.

출력이 정해져 있으면 if로 되지 않나?

처음엔 이렇게 생각했다. 출력이 정해져 있고, 그 출력을 고르는 데 필요한 값을 우리가 다 알고 있다면 단순 if 분기로도 구현할 수 있다. AI 시청자의 반응도 결국 우리가 만든 점수나 문장 풀에서 나오니까.

틀린 말은 아니다. 다만 기준이 조금 어긋나 있었다. Jev가 대신하는 건 출력(점수, 문장)이 아니다. 출력은 원래 디자이너가 만든 풀에서 나온다. Jev가 대신하는 건 그 사이의 매핑, “이 상황에서 이 사람이라면 어떤 출력을 고를까”다.

개념도: 상황과 인물이 들어가 매핑을 거쳐 디자이너가 만든 출력 풀에서 하나를 고른다. Jev가 맡는 건 가운데 매핑 칸이다

그래서 물어야 할 건 “출력을 누가 만드나”가 아니라 “출력을 고르는 매핑을 if로 다 쓸 수 있나”다.

반대 방향으로 틀리기도 쉽다. 입력이 자유 텍스트처럼 보이면 판정 모델이 필요할 것 같지만, 그 입력이 디자이너가 정한 목록에서 나온 거라면 표로 채점된다. 겉모양이 아니라 매핑이 열거되는지가 기준이다.

반응 태그는 표 하나로 끝난다

매핑을 if로 쓸 수 있는지는 결국 하나로 압축된다.

같은 사건에 대한 반응이 맥락 때문에 뒤집히는가?

뒤집힘이 없으면 태그 점수표로 충분하다. 지상 주민에게 “비 좋아함”, “우박 싫어함” 같은 반응 태그를 붙이면 이렇게 된다.

  맑음 비 우박
비 좋아함 - 🔺 🔻
우박 싫어함 - - 🔻

날씨 × 태그 표로 끝난다. 반전을 넣고 싶어서 “농부는 원래 비를 싫어하지만 이 농부는 좋아한다”를 규칙으로 직접 써 두면? 그건 우선순위 높은 if 한 줄일 뿐이다. 처음 생각이 이 경우엔 맞았다.

동기 태그는 표를 터뜨린다

태그를 반응이 아니라 동기로 쓰면 이야기가 달라진다. “일하기 싫음”, “돈이 급함” 같은 태그는 날씨를 직접 언급하지 않아서, 상황마다 해석이 필요하다.

개요의 농부[일하기 싫음]를 따라가 보면,

  • 비 → 오늘은 쉰다 🔺
  • 우박 → 작물이 망한다 🔻
  • 비 + 장날 → 쉬고 싶은데 장사도 해야 한다. 애매
  • 비 + 하늘에서 천사가 떨어짐 → 구경거리다 🔺

동기 10개 × 날씨 5개 × 행사 6개면 벌써 300칸이다. 여기에 떨어지는 천사 같은 부수 사건까지 붙으면 표로 쓰기 어렵다. 이때부터 매핑을 Jev에게 맡길 이유가 생긴다.

정리하면 판정 모델의 가치는 세 조건이 겹칠 때 나온다.

  • 관찰자가 많다. 한 사건을 여럿이 동시에 판정한다(1:N).
  • 맥락이 결과를 뒤집는다. 같은 사건, 같은 직업인데 동기와 상황에 따라 방향이 바뀐다.
  • 세계의 사실은 코드가 쥔다. 무슨 일이 일어났는지는 코드가 정하고, 모델은 그 일이 누구에게 무슨 의미인지만 해석한다.

새 컨셉에서 Jev 자리: 불만족도 게이지

이 조건을 새 컨셉에 대 보니 자리가 하나 보였다. 천사들이 떨어뜨린 날씨를 맞는 지상 주민이다. 아직 검토 중인 제안이고, 당장 1주 프로토타입에는 Jev가 들어가지 않는다.

  • 성향이 다른 랜덤 주민 NPC(직업 + 동기 태그 1~2개 + 오늘 사정)가 투하·추락 같은 사건마다 각자 판정해서 불만족도 게이지를 올리고 내린다.
  • 게이지 구간에 따라 다음 주문의 난이도(바람, 사건 확률, 필요한 섞기 수, 보상 배율)가 바뀐다.
  • 판정은 Jev가, 게이지 증감·상한·감쇠와 난이도 반영은 코드가 쥔다.

화면에 보이는 정보는 시점마다 다르다.

  • 플레이 중에는 주민의 직업과 누가 🔺/🔻 했는지만 보인다. 직업이라는 기준선이 있어야 난이도 변화가 자의적으로 느껴지지 않는다.
  • 결과 화면에서 숨겨 둔 동기 태그를 공개한다. “뭐야 이 이상한 NPC는?” 하는 순간을 노린다.

여기서 공개가 두 갈래로 갈린다. 공개 후 “아 그래서 비를 좋아했구나” 납득되면 좋은 이상함이고, 공개 후에도 “왜?”면 나쁜 이상함이다. 나쁜 이상함은 판정 오류를 전시하는 꼴이 된다.

규칙 판정과 나란히 세운다

1편에서 시청자 레이어를 만들 때 다시 꺼내자고 적어 둔 원칙 중에 “규칙 기반 판정과 Jev 판정이 같은 출력 형식을 쓰게 하자”가 있었다. 이번에 그 원칙이 쓸 데가 생겼다.

// 예시: 실행해 본 코드가 아니라 구조 설명용이다
public interface IJudge
{
    // 주민 한 명이 사건 하나를 보고 내리는 판정
    Verdict Judge(Resident resident, WorldEvent worldEvent);
}

public struct Verdict
{
    public int Direction;     // -1 불만 증가, 0 무관, +1 만족
    public float Confidence;  // 판정 확신도 (0~1)
    public string Reason;     // 디자이너가 정한 이유 라벨 중 하나
}

// RuleJudge: 직업만 보고 표에서 찾는다
// JevJudge: 직업 + 동기 + 오늘 사정을 보내고 Jev가 고르게 한다

둘이 같은 인터페이스 뒤에 있으니 같은 사건을 양쪽에 넣고 비교할 수 있다. 뒤집힘이 안 나오면, 즉 둘의 답이 늘 같으면 Jev를 빼고 RuleJudge만 쓰면 된다. 네트워크가 끊겼을 때 바꿔 끼울 대체 판정이기도 하다.

이 비교를 연출로도 재활용한다. 직업만 본 판정(RuleJudge)과 동기까지 본 판정(Jev)의 차이가 가장 큰 주민을 결과 화면에서 “오늘의 이상한 주민”으로 띄운다. 검증용 비교가 그대로 한 판의 하이라이트가 된다.

알려진 위험도 적어 뒀다.

  • 악순환: 실패 → 불만 → 난이도 상승 → 또 실패
  • 반전 과다: 뒤집힘이 너무 잦으면 예측이 안 된다. 한 판에 1/4 이하로 두는 걸 권장값으로 잡았다.
  • 판정 비일관성: 같은 사건에 다른 판정이 나오면 안 되니 판 단위로 캐시한다.
  • 공급자 종속: Jev 하나에 묶이지 않도록 IJudge 뒤에 둬서 공급자를 갈아 끼울 수 있게 한다.

뒤집히면 안 되는 케이스도 넣는다

Jev가 정말 맥락을 읽는지 보려면 반전이 일어나야 할 케이스만으로는 부족하다. 동기 태그만 보면 무조건 뒤집는 모델도 그 케이스는 다 맞히니까. 그래서 20개를 세 묶음으로 나눴다.

묶음 개수 보는 것
A. 반전 기대 12 방향이 기대대로 뒤집히나
B. 반전 안 됨 5 A와 인물이 같고 날씨만 다른 대조군. 여기서 뒤집으면 오반전
C. 애매 3 방향이 아니라 확신도가 A 평균보다 낮게 나오나

B가 핵심이다. 농부[일하기 싫음]가 비에 🔺인 건 A에, 같은 농부가 우박에 🔻인 건 B에 넣는다. 짝으로 비교하면 모델이 “일하기 싫음”이라는 단어만 보고 뒤집는지, 날씨까지 읽는지가 드러난다.

통과 기준은 이렇게 제안해 뒀다.

  • A 방향 일치 10/12 이상
  • B 오반전 1개 이하
  • 같은 케이스를 5회 반복했을 때 방향이 흔들리는 비율 10% 이하
  • 이유 라벨 하나가 절반을 넘지 않음 (라벨 쏠림 방지)

마지막으로 랜덤 조합 10개를 사람이 직접 읽고 “납득 / 왜?”만 표시한다. 숫자 기준을 다 통과해도 결과 화면에 띄웠을 때 나쁜 이상함이면 소용없으니까.

소감

출력이 정해져 있으면 if로 다 될 거라 생각했는데, 반응 태그에서는 그 생각이 맞았고 동기 태그에서 처음으로 표가 감당이 안 되는 지점이 보였다. “이거 if로 되지 않나?”를 프로젝트 내내 붙잡고 있었던 게 결국 Jev 자리를 찾는 기준이 됐다.