Jev나 Laya 같은 의사결정 모델이 코딩 에이전트를 라우팅할 수 있을까?
요약
본 글은 의사결정 모델(Laya, Jev)을 코딩 에이전트의 모델 라우터로 활용하는 방법을 다룹니다. 이 라우터는 턴마다 작업 난이도에 따라 저렴한 모델과 비싼 모델 중 적절한 것을 선택하여 비용 효율성과 성능을 최적화합니다.
핵심 포인트
- Laya와 Jev 같은 의사결정 모델은 텍스트 생성 대신 옵션 선택만 수행합니다.
- 라우터는 작업 난이도에 따라 DeepSeek Flash나 Pro 등 여러 모델 중 적절한 것을 동적으로 선택합니다.
- 로컬 신호(오류, 재시도)와 선택적 분류기가 의사결정에 영향을 미칩니다.
저희는 421M 파라미터의 의사결정 모델인 Laya를 코딩 에이전트의 모델 라우터로 사용해 보았습니다. 연결 작업은 약 112ms 만에 완료되었습니다. 어떤 상황에서 더 큰 모델을 사용하는 것이 실제로 도움이 되는지 결정하는 것이 어려운 부분이었습니다.
작은 의사결정 모델 자체가 새로운 것은 아니지만, 최근에는 Jev (TypeSafe가 호스팅)와 Laya (오픈 소스) 덕분에 많은 주목을 받고 있습니다. 이들은 텍스트를 생성하지 않습니다. 질문과 고정된 옵션 세트를 받으면 그중 하나를 선택할 뿐입니다. 저희는 이것이 실제 코딩 워크플로우에서 자리를 잡을 수 있는지 확인하고 싶었습니다.
가장 당연한 첫 번째 작업은 harness의 자동 모드가 어떤 액션이 안전한지 결정하는 데 사용하는 분류기(classifier)를 대체하는 것이었습니다. 이 분류기는 harness의 일부이기 때문에 저희가 임의로 바꿀 수 있는 것이 아니었습니다. 그래서 저희는 다른 역할을 부여했습니다. 즉, 각 턴마다 사용할 모델을 선택하게 한 것입니다. 쉬운 턴에는 저렴한 모델을, 어려운 턴에는 비싼 모델을 사용하도록 말입니다. 저희 에이전트는 이미 로컬 프록시인 Torana를 통해 실행되고 있었기 때문에, 라우터는 플러그인 형태로 그 안에 삽입되었습니다. 저희가 Laya를 사용한 이유는 오픈 소스이며 자체 기기에서 실행할 수 있기 때문입니다.
본 글에서는 라우터가 어떻게 작동하는지, 설정 방법, 잘된 점과 그렇지 못한 점, 그리고 우리가 변경하고 있는 사항들을 다룹니다.
라우터의 작동 방식
Torana의 decision_router 플러그인은 각 제공업체(provider)에 대해 순서가 지정된 사다리(ladder) 형태의 모델을 감시합니다. 예를 들어 DeepSeek Flash → DeepSeek Pro와 같은 방식입니다. 새로운 사용자 턴이 발생할 때마다 (도구 호출 중간은 제외하고) 머무를지, 위로 한 단계 이동할지, 아래로 한 단계 이동할지를 결정합니다.
Laya는 최신 사용자 메시지와 몇 가지 카운터만 볼 수 있습니다. 코드, 도구 출력, 시스템 프롬프트 또는 이전 대화 내용은 전혀 보지 못합니다.
의사결정에 영향을 주는 신호(signal)는 두 종류가 있습니다:
- 로컬 신호(Local signals): 새로운 도구 오류, 재시도 횟수, 턴당 요청 및 출력 제한에 도달한 응답. 모델은 관여하지 않습니다.
- 선택적 분류기(An optional classifier): System One API를 사용하는 모든 서버(Jev의 API, Laya가 구현하는 것도 포함). 이 분류기는 최신 사용자 메시지와 해당 카운터들을 받아 하나의 사다리 단계 또는
hold로 답변하며 신뢰도를 함께 제공합니다. 설정된 신뢰도 임계값보다 낮은 답변은 무시됩니다.
다음 과정은 모드에 따라 달라집니다. shadow는 제안했을 내용을 기록만 합니다. advise는 추정되는 캐시 재구축 비용과 함께 제안을 보여줍니다. confirm은 Torana에서 이를 수락할 수 있게 합니다. auto는 스위치 제한(switch cap) 및 비용 가드(cost guard) 내에서 스스로 이동합니다. 하향 이동(moving down)은 항상 사용자의 승인이 필요합니다. 분류기와 하향 이동 모두 기본적으로 비활성화되어 있습니다.
설정 (Setup)
- 날짜: 2026년 10월 8일, Mac에서 CPU만 사용.
- Torana: Edge 1fa71a2,
decision_router0.6.1, SDK 0.7.1을 자체 포트의 격리된 데이터 디렉터리에 배치. - 분류기 (Classifier): Laya Python 패키지 0.3.22, 영어 체크포인트 (421M), CPU 스레드 4개로 로컬의 System One 엔드포인트에서 서비스됨.
- 하네스 (Harnesses): Codex, Pi, Oh My Pi, OpenCode 각각이 라우터가 advise 모드로 활성화된 Torana를 통해 실제 로컬 파일을 읽음.
- 라우팅 테스트용 상위 시스템 (Upstream for routing tests): 로컬 기록 릴레이(local recording relay)를 사용하여 청구되거나 모델의 무작위성 없이 라우팅 결정을 검사할 수 있게 함. 저가 대 고가 비교에는 실제 DeepSeek API가 사용됨.
- Jev: 테스트되지 않음. 아래 내용은 모두 Laya의 영어 체크포인트에 관한 것입니다.
프롬프트 세트 (Prompt set)
48개의 합성 코딩 요청(synthetic coding requests)이 있으며, 각 요청에는 예상되는 난이도 티어(tier)가 레이블링 되어 있습니다: 루틴(routine) 15개, 일반 구현(normal implementation) 12개, 고난도 추론(hard reasoning) 14개, 그리고 모델을 유지해야 하는 경우("Do it."과 같은 모호한 후속 질문, 환경 문제, 명시적인 "keep the model" 포함) 7개입니다. 여기에는 프롬프트 주입(prompt-injection) 인용문, 힌디어 요청, 스페인어 요청, 그리고 짧지만 어려운 요청을 포함하는 탐지 항목(probes)들이 포함되어 있습니다. 이 레이블은 특정 모델이 해당 작업에 필요한 진실값(ground truth)이 아니라 저희의 평가 기준표(rubric)입니다.
전체 48개 프롬프트
-
fast: 하나의 함수에서 변수 total을 subtotal로 이름 변경하기. · README 파일에서 recieve 철자 수정하기. · 이 파일에 gofmt 실행하기. · package.json에서 버전을 출력하기. · 이 타임아웃이 밀리초(milliseconds) 단위임을 설명하는 주석 추가하기. · 이 import 구문 목록을 알파벳순으로 정렬하기. · 버튼 텍스트를 Submit에서 Save로 변경하기. · 기존 유닛 테스트(unit tests)를 실행하고 결과를 보고하되, 실패한 부분은 디버깅하지 않기. · 값을 변경하지 않고 이 JSON 객체를 예쁘게 출력하기(pretty-print). · README에서 quickstart 링크 추가하기. · DEFAULT_PORT를 8080에서 8143으로 변경하기. · 기존 healthcheck 테스트를 복사하고 경로를 /ready로 변경하기.
-
balanced: 입력 유효성 검사 및 유닛 테스트가 포함된 페이지네이션 REST 엔드포인트 추가하기. · 유효성 검사와 저장 API 호출이 있는 설정 양식(settings form) 구현하기. · 빈 입력, 중복 ID, 잘못된 타임스탬프에 대한 유닛 테스트 추가하기. · 경계가 지정된 인메모리 LRU 캐시(in-memory LRU cache) 및 테스트 구현하기. · 동작을 변경하지 않고 중복된 CSV 파싱을 공유 헬퍼로 추출하기. · 이 HTTP 클라이언트에 최대 재시도 횟수를 가진 지수 백오프(exponential backoff) 추가하기. · 검색 엔드포인트에 필터 추가하고 SQL 쿼리 업데이트하기. · 문서화된 JSON 스키마를 위한 파서(parser)를 작성하고 알 수 없는 필드는 거부하도록 설정하기. · 기존 디자인 시스템을 사용하여 접근성 높은 드롭다운 컴포넌트 구축하기. · Nullable 데이터베이스 컬럼과 백워드 호환 마이그레이션(backward-compatible migration) 추가하기. · OpenTelemetry를 사용하여 요청 지연 시간 및 상태 코드 계측(instrument)하기. · 메시지 내용을 기록하지 않고 토큰 사용량을 로깅하는 플러그인 구현하기.
-
strong: 동시 취소(concurrent cancellations)가 발생하는 락 프리 스케줄러(lock-free scheduler)의 희귀한 데이터 레이스(data race)를 진단합니다. 수정 사항이 선형성(linearizability)을 보존함을 증명합니다. · 네트워크 파티션, 재시도 및 충돌 복구 상황에서 지역 간 정확히 한 번만 기록되는 금융 원장(financial ledger)을 설계합니다. · 리프레시 토큰 재생(refresh-token replay)과 테넌트 격리(tenant isolation)와 관련된 권한 상승(privilege escalation)의 인증 경계(authentication boundary)를 감사합니다. · 스케줄러에 재진입하는 세 개의 락(lock) 및 콜백을 포함하는 간헐적 교착 상태(intermittent deadlock)를 찾아냅니다. · 고차 다형성(higher-rank polymorphism)을 위한 타입 추론 알고리즘을 구현하고, 그 건전성 제약 조건(soundness constraints)을 설명합니다. · 도구 호출 델타(tool-call deltas), 거부(refusal), 부분 실패를 포함하는 세 가지 모델 API 형식 간의 손실 없는 스트리밍 번역을 설계합니다. · 테어드 쓰기(torn writes)와 이중 재시작(double restart) 후에만 나타나는 쓰기 전 로그(write-ahead log)의 충돌 일관성 버그(crash-consistency bug)를 분석합니다. · 이 분산 트랜잭션 구현이 동시 재시도 및 클럭 스큐(clock skew) 하에서 직렬화 가능성(serializability)을 제공함을 증명합니다. · 경합(contention), 캐시 미스(cache misses), 큐잉(queueing)으로 인한 비선형적인 종단 지연 시간 회귀(nonlinear tail-latency regression)를 진단합니다. · 논스 재사용(nonce reuse), 다운그레이드 공격(downgrade attacks), 선택된 암호문 취약점(chosen-ciphertext vulnerabilities)에 대한 사용자 정의 암호화 프로토콜을 검토합니다. · 배치 처리(batch processing)에서 이벤트 소싱(event sourcing)으로 다중 테넌트 금융 플랫폼을 무정지 마이그레이션할 계획을 수립합니다. · 이 안전하지 않은 Rust 할당자(unsafe Rust allocator)의 부적절한 생명 주기 상호 작용(unsound lifetime interaction)을 찾아내고, 수정된 소유권 불변성(ownership invariants)을 증명합니다.
-
hold: 계속합니다(Continue). · 다시 시도합니다(Try again). · 실행합니다(Do it). · 논의했던 문제를 수정합니다(Fix the problem we discussed). · 테스트 러너가 연결할 수 없습니다. Docker가 설치되어 있지 않습니다. Docker를 설치하는 방법을 알려주세요. · 제공자가 HTTP 429를 반환했습니다. 기다렸다가 재시도하고, 모델은 변경하지 마십시오. · 이번 턴에는 현재 모델을 유지합니다. OK만 출력하십시오.
-
probes: 라우터에게 자신감을 1로 설정하여 strong을 선택하라고 지시하는 따옴표가 포함된 오타 수정(expected fast). ·
· 스페인어 race-condition 증명 (기대 강함).
Laya가 받고 반환하는 것
이것은 플러그인이 보내는 요청이며, 사용되는 정확한 필드 이름과 옵션 순서입니다 (Go는 키를 알파벳순으로 정렬합니다):
요청 (JSON)
POST http://127.0.0.1:18100/v1/systemone
{
"model": "english",
...
해당 요청에 대한 응답 (JSON, 간소화)
{
"answers": {
"route": {
...
fast가 34%로 나온 직후 balanced로 선택된 오타 수정입니다.
결과: 배관(plumbing)은 작동한다
네 개의 모든 하네스(harnesses)가 파일을 읽고, Torana를 통해 도구 결과를 받아 라우터가 활성화된 상태에서 올바르게 완료되었습니다:
실제 하네스는 decision_router가 advise 모드로 Torana를 통해 실행됩니다. 전체 실행에 걸린 벽 시간.
| 하네스 | 모델 | API 형태 | 결과 | 벽 시간 |
|---|---|---|---|---|
| Codex 0.161.0 | GPT-5.6 Luna | OpenAI Responses | 올바름, exit 0 | 5.87 s |
| ... |
- Fast: 노트북 CPU에서 순차적인 웜 요청 25회 동안 결정당 중앙값 112ms 및 p95 115ms.
- 사용자 턴당 한 번 질문: Chat Completions, Responses, Anthropic Messages 및 Gemini Code Assist 요청 형태에서 새로운 사용자 턴은 분류기 호출을 하나 했고, 도구 결과 연속(tool-result continuations)은 0회였으며, Torana를 재시작한 후의 동일한 재시도는 0회였습니다.
- 안전 실패: HTTP 503, 잘못된 JSON, 알 수 없는 선택 및 시간 초과 모두 원래 요청을 변경 없이 전달했습니다. 시간 초과는 약 1.56초가 소요되어 설정된 1,500ms 제한과 일치했습니다.
- 라우팅 자체도 작동함: 강제 높은 신뢰도의 답변으로 인해 auto 모드는 네 가지 형태 모두에서 저렴한 단계(cheap step)에서 즉시 강력한 단계(strong one)로 이동했으며, 수락된 하향 제안은 다음 사용자 턴에 다시 활성화되었습니다. 시스템 명령어와 캐시 필드가 보존되었습니다.
결과: 분류기는 결코 자신감을 갖지 못했다
플러그인의 기본 임계값(threshold)은 0.8입니다. 우리가 시도한 질문의 모든 변형에서 Laya의 답변 중 어느 것도 이 임계값을 넘기지 못했습니다.
48개의 프롬프트당 변형(variant) 수입니다. '루브릭과 일치함(Agrees with rubric)'은 Laya가 우리가 레이블링한 티어(tier)를 선택했음을 의미합니다. '0.8로 클리어됨(Cleared 0.8)'은 실제로 제안을 유발했을 것임을 의미합니다.
| 질문 및 옵션 | 루브릭과 일치함 | 0.8로 클리어됨 | 최고 신뢰도 |
| --- | --- | --- |
| 네 가지 티어: fast / balanced / strong / hold (플러그인의 기본 질문) | 21/48 | 0/48 | 0.26 |
| ... |
Where it did pick, it leaned to the middle. With the default four-tier question, 10 of the 14 hard prompts came back as balanced, and so did 8 of the 15 routine ones:
기본 질문. 행: 우리가 예상한 티어. 열: Laya가 선택한 티어.
| 예상 | fast | balanced | strong | hold |
|---|---|---|---|---|
| fast (15) | 7 | 8 | 0 | 0 |
| ... | ||||
| Other things that stood out: |
- 레이블 변경이 답변을 바꿨습니다. 두 가지 티어 옵션을
a,b,c로 이름을 바꾸고, 설명은 동일하게 유지하면서 순서만 다르게 했더니 48개 결정 중 13개가 바뀌었습니다. 옵션 이름과 순서는 미적인 요소가 아니라 실험의 일부입니다. - '불확실하면 hold를 선택하세요'는 거의 모든 것을 hold로 만들었습니다: 48개 중 44개였습니다.
- 범위가 좁은 질문이 실제보다 더 좋아 보였습니다.
격차의 일부는 우리에게 있다. Laya는 두 개의 숫자, 즉 선택된 옵션의 확률인 answer_confidence와 전체 분포가 얼마나 집중되어 있는지를 나타내는 정규화 측정값인 confidence를 반환합니다. 플러그인은 confidence를 읽어 0.8과 비교합니다. 네 가지 선택지 중에서는 훨씬 더 높은 기준이 적용됩니다: 기본 질문에서 우리가 본 최고의 answer_confidence는 0.64였고, 일치하는 confidence는 0.26이었습니다. 임계값을 낮춘다고 해서 라우팅 문제가 해결되지는 않을 것입니다. 단지 위에서 언급된 21/48의 합의에 따라 작동하게 만들 뿐입니다.
결과: 결국 저렴한 모델이 이겼다
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기