NeoHorse-Jev-4B는 TokenRhythm이 공개한 4B 파라미터 규모의 구조화된 의사결정 모델이다. 일반적인 채팅 모델처럼 문장을 생성하는 것이 아니라, 애플리케이션 측에서 정의한 질문에 대해 선택지나 점수, 참/거짓 판별에 가까운 값을 반환한다.
이 글에서는 NeoHorse-Jev-4B를 네이티브 런타임으로 구동하고, Python을 통해 의사결정을 실행하는 과정까지 테스트해 본다.
NeoHorse-Jev-4B는 입력으로 state와 questions를 받는다. state에는 판단 대상의 상황을 작성하고, questions에는 모델에 판별시키고 싶은 질문을 정의한다.
Hugging Face의 모델 카드에서는 용도로 요청 라우팅(request routing), 툴 선택(tool selection), 조건 판별(condition judgment), 결과 평가(result evaluation) 등이 언급되어 있다. 출력에는 후보별 확률이 포함되므로, 단순히 '그럴듯한 답변'을 문장으로 반환하는 것이 아니라 애플리케이션 측에서 임계값이나 분기 조건에 사용하기 용이하다.
모델 카드에서는 추론 방식(inference method)으로 vLLM, SGLang, 네이티브 런타임이 안내되어 있다. 이번에는 Python 프로젝트에서 빠르게 테스트하기 위해 네이티브 런타임을 사용한다.
Python 프로젝트를 생성하는 데는 mise와 uv를 사용한다.
프로젝트 내에서 가상 환경을 사용할 경우, mise.toml에 uv의 가상 환경을 등록해 둔다.
$ cat << EOF >> mise.toml
[env]
_.python.venv = ".venv"
...
CUDA 버전 PyTorch를 사용하는 경우, pyproject.toml에 PyTorch의 인덱스를 추가한다. 여기서는 CUDA 12.8용 예시를 보여준다.
[tool.uv.sources]
torch = { index = "pytorch-cu128" }
[[tool.uv.index]]
...
CPU 환경이나 다른 CUDA 버전을 사용하는 경우에는 이 부분을 사용 환경에 맞게 변경한다.
NeoHorse-Jev-4B의 모델 카드에서는 네이티브 런타임에 필요한 일체로 backbone/, tokenizer/, pointer_head.safetensors, model_manifest.json, dist/, package/ 등이 제시되어 있다. 개별 파일뿐만 아니라 리포지토리 전체를 가져오는 것이 이해하기 쉽다.
이 글에서는 프로젝트 직하의 model 디렉토리에 다운로드한다.
$ mkdir model
$ uvx hf download TokenRhythm/NeoHorse-Jev-4B --local-dir ./model
다음으로, 다운로드한 추론 패키지를 의존성에 추가한다. 이미지 입력도 테스트할 경우, vision 익스트라를 붙여서 설치해야 한다.
$ uv add "./model/package"
이상으로 준비가 완료되었다.
어떤 판단을 시키고 싶어서 Gemma4에게 적절한 문제를 만들어 달라고 했다.
회의에서의 오해와 시간 관리의 딜레마
당신은 A사와 B사가 공동 개최하는 매우 중요한 회의의 진행을 담당하고 있다.
회의는 시작 후 30분이 지났고, 당초 안건을 이야기해야 할 B사 담당자로부터 '기술적인 문제로 늦습니다'라는 연락이 왔다.
남은 참가자들 사이에서는 '이대로 진행해 달라'는 분위기가 감돌지만, 당신만이 이 상황이 불안하다.
당신(진행자)이 취해야 할 선택지는?
"늦고 있는 분이 도착할 때까지 일단 모두가 대기합시다. 서둘러 진행하는 것은 좋지 않습니다."라고 전하고, 구체적인 시간(예: 앞으로 15분)을 제안한다. - B: 핵심 멤버를 위한 안건 재편성.
B사 멤버 외의 참가자들에게 오늘 논의할 수 있는 안건(초기 진척 보고나 공유 사항 등)에 한정하여 회의를 강제로 진행한다. - C: 아젠다 재구성 및 후속 조치 제시.
"B사 담당자님께서 부재하시므로, 오늘의 안건을 '전반부: 진척 공유(현재 가능)'와 '후반부: 기술적인 우려 사항 논의(B사 도착 후)'의 두 부분 구성으로 전환하는 것이 좋을 것 같습니다. 또한, 후반부는 다시 별도의 일정으로 시간을 요청드리는 것은 어떨까요?"라고 제안한다.
이 회의 진행 판단을 예로 들어, 여러 선택지 중 하나를 고르게 한다. choice
그때는 criteria에 후보 이름과 설명을 전달한다.
from neohorse_decision import DecisionEngine
engine = DecisionEngine("./model")
state = """당신은 A사와 B사가 공동으로 개최하는 매우 중요한 회의의 진행을 맡고 있습니다。
...
실행 결과는 다음과 같았다.
$ uv run main.py
아젠다 재구성 및 후속 조치 제시하기
{'시간 경과를 기다리기': 0.37104377150535583, '핵심 멤버 대상 안건 재편성하기': 0.22577781975269318, '아젠다 재구성 및 후속 조치 제시하기': 0.4031783938407898}
가장 높은 확률은 '아젠다 재구성 및 후속 조치 제시하기'이지만, 확률 차이가 크지 않다. 이 결과는 모델이 세 번째 선택지를 골랐다는 것뿐만 아니라, 판단에 망설임이 있다는 것도 보여준다.
애플리케이션에 통합할 경우, 최대 확률의 후보를 그대로 채택하는 것만으로는 부족한 경우가 있다. 예를 들어, 최대 확률이 특정 값보다 낮으면 사람에게 확인을 요청하거나, 상위 두 후보 간 차이가 작으면 추가 정보를 수집하는 등의 제어를 넣을 수 있다.
NeoHorse-Jev-4B는 문장 생성(text generation)이 아니라 구조화된 의사결정(structured decision-making)을 반환하는 모델이다. 네이티브 런타임을 사용하면 Hugging Face에서 모델 세트를 가져와 Python의 DecisionEngine으로부터 비교적 적은 코드로 테스트해 볼 수 있다.