Exp 1 · 실측 기록
어떤 AI 모델이 이 실험기구를 안정적으로 움직일까?
Exp 1 — Model Selection (E7 · E6 · 외부 API)
형식·품질·지연·메모리·실제 실행을 단계별로 평가하고, 역할별 결과와 기준 예외를 함께 검토해 구성을 정했습니다.
DOCUMENTED DECISIONS · 2026-09-18 기준
지금 문서에 기록된 모델 구성
Core는 게임 진행·판정, NPC는 인물 대화, 임베딩은 대안 규칙의 순위를 맡습니다. 아래는 채택 결정 기록입니다. 실제 배포 설정을 실시간으로 표시하는 표는 아닙니다.
| 역할 | 로컬 개발 | 제출 서빙 | 결정 기록 |
|---|---|---|---|
| Core | gemma4:12bthinking off | claude-sonnet-5 | 9/17 · A.19 |
| NPC | kanana1.5:8b | claude-haiku-4-5 | 9/17 · A.19 |
| 대안 규칙 정렬 | bge-m31024차원 | gemini-embedding-0012560차원 | 9/18 · A.21 |
통과한 기준과 남은 예외
- Core: 측정한 의미 품질 기준은 통과했습니다. Sonnet의 지연 기준은 1회·3회 반복에서 엇갈렸고, 계획 생성의 재시도·대체 처리 사례가 남아 있습니다. 역할·반복별 표
- NPC: 선별 대화 사례와 7세 정책 평가가 채택 근거입니다. 채점기·발화가 함께 달라진 실행이 있어 채점기 효과만 분리한 비교로 읽을 수 없습니다. 로컬 NPC가 Gemma에서 Kanana로 돌아온 경위도 추가 확인이 필요합니다. 대화·정책 평가
- 임베딩: 시나리오 제작자(Scenario Director)가 검수한 22문장의 적중률과 지연, 여러 모델을 같은 GPU 메모리에 함께 올리는 조건을 비교했습니다. 초기 30문장의 3회 반복은 별도 결과입니다. 정렬 평가와 채택 이유
채택은 모든 기준의 일괄 통과나 통계적 비열등성 증명을 뜻하지 않습니다. 제출 조합의 원숭이손·채점 캘리브레이션·누설 평가에는 미완료 항목이 있습니다.
평가 방법과 대안 규칙 정렬 비교
- 후보는 응답 형식 확인부터 실제 게임 실행까지 단계별로 평가했습니다.
- 임베딩 그림은 시나리오 제작자(Scenario Director)가 검수한 22문장 평가를 보여 줍니다.
- 첫 추천 적중과 계산 시간을 비교하고, 여러 모델을 같은 GPU 메모리에 올리는 조건도 따로 확인했습니다.
SUMMARY S3A · MODEL GATE
후보를 실제 게임 실행까지 확인하는 5단계
- STAGE 0응답 형식 확인
- STAGE 1기본 동작 확인
- STAGE 2역할별 정식 평가
- STAGE 3메모리에 함께 올리기
- STAGE 4실제 게임 실행
이 흐름은 절차를 보여 줍니다. 단계별 후보 수는 공개 원문에 단일 확정값이 없어 그림에 넣지 않았습니다.
SUMMARY S3B · 2026-09-18 측정
검수한 22문장에서 첫 추천 적중과 계산 시간
직접 쓴 규칙을 적용할 수 없을 때, 뜻이 가까운 대안 규칙을 먼저 보여 주는 보조 기능을 비교했습니다.
막대: 첫 번째로 보여 준 대안이 맞은 문장 수p50: 측정값을 빠른 순서로 놓았을 때 가운데 시간ms: 1,000분의 1초
모델 이름 옆 차원은 문장을 숫자로 표현하는 벡터의 길이이며 점수가 아닙니다. 시간은 후보 문구를 미리 준비한 뒤, 대안 순서를 한 번 계산하는 시간입니다.
시나리오 제작자(Scenario Director)가 검수한 22문장 결과만 표시했습니다. 앞선 미검수 30문장 수치는 포함하지 않았습니다. 어간 겹침은 외부 모델을 호출하지 않는 기준선입니다. 원시 지연 p50은 0.1ms 단위 반올림에서 0.0ms로 기록됐으며, 연산 시간이 정확히 0이라는 뜻은 아닙니다.
해석 주의. 임베딩 첫 추천 적중과 지연은 같은 검수 세트 안에서만 비교합니다. 앞선 미검수 결과, Core/NPC 품질, 사람 행동을 이 수치와 섞지 않습니다.
용어를 쉽게 읽기 배경지식 없이 읽는 연구
- 실험환경 Testbed
- 조건을 정해 두고 같은 과정을 반복해서 실험할 수 있는 환경입니다.
- AI 제안 수용 Acceptance
- 사람이 AI가 제안한 방법을 선택하는 행동입니다. AI를 믿는 마음 자체를 측정한 것은 아닙니다.
- 이벤트 기록 Event-level trace
- 제안, 선택, 규칙 적용 같은 일이 일어날 때마다 남긴 기록입니다.
- 형식 통과율 Schema pass rate
- AI가 프로그램이 읽을 수 있도록 정해진 응답 형식을 지킨 비율입니다.
- 대체 처리 Fallback
- AI 응답에 문제가 있을 때 준비된 다른 처리로 넘어가는 것입니다. 평가에서는 정상 응답과 구분합니다.
- 통과 기준 Gate
- 다음 평가 단계로 넘어가기 위해 미리 정한 조건입니다.
- 러너 Runner
- 정해진 문항을 실행하고 응답·시간·실패를 모으는 평가 프로그램입니다.
- GPU 동주
- 여러 모델을 같은 GPU 메모리에 함께 올려 사용하는 조건입니다. 동시 접속자 수와는 다른 개념입니다.
- 탐색적 관찰 Exploratory
- 적은 사례에서 어떤 일이 일어나는지 살펴보는 초기 관찰입니다. 전체 사람들의 경향으로 일반화하지 않습니다.
- 사전등록 Preregistration
- 결과를 보기 전에 질문, 방법, 판정 기준을 고정해서 공개하는 것입니다.
- 응답 지연 p95
- 측정한 요청의 약 95%가 이 시간 안에 응답했다는 뜻입니다. 평균 응답 시간과 다릅니다.
- 미실행 NOT RUN
- 아직 측정하지 않았다는 뜻입니다. 측정 결과가 0이라는 뜻이 아닙니다.
상세 연구 기록
방법, 수치, 판단 근거와 당시의 한계를 담은 전체 기록입니다. 먼저 위의 설명을 읽고, 필요한 절을 목차에서 찾아보세요.
정본은 앱 저장소 com.remakeday/docs/model_evaluation.md이고,
이 페이지는 공개용으로 가공한 스냅샷입니다. 공개한 절의 수치·판정·결정은 아래 기준 커밋의 정본을 따릅니다.
시나리오 결말을 드러내는 문장과 운영 세부(설정 키·포트·로그인 경로 등)는 가리거나 뺐고,
공개 검토 전인 후속 추록은 포함하지 않았습니다. 앱 저장소 문서는 링크 대신 경로로 적었습니다.
스냅샷 기준: 앱 저장소 feat/coherence-chain 95bea1d
최종 구성과 증거 범위 — 2026-09-18 문서 검토
| 용도 | Core | NPC | 임베딩 | 결정 위치·예외 |
|---|---|---|---|---|
| 제출용 | claude-sonnet-5 |
claude-haiku-4-5 |
gemini-embedding-001, 요청·반환 2560 |
A.19, A.21. 지연·재생성 예외를 허용한 사용자 채택이며 전 게이트 통과 선언이 아님 |
| 로컬 개발·롤백 | gemma4:12b, think off |
kanana1.5:8b |
bge-m3, 요청 2560·실제 반환 1024 |
A.21.8의 기본 모델 반영 코드 기준. 해당 조건의 상주 검사를 통과 |
Core는 계획·관리·채점을, NPC는 인물 대화를 맡는다. 플레이어 모델은 테스트 입력을 만들고 judge는 출력을 평가한다. 셀은 모델과 thinking 설정을 묶은 실험 조건이다. PCA는 이 문서에서 기대 방향 일치율이며 주성분분석을 뜻하지 않는다. adv/pln/evl은 advisor/planner/evaluator, ms는 밀리초, GiB는 2³⁰바이트다. p50/p95는 표본 지연의 50/95백분위다. 게이트는 사전에 정한 판정 조건, 폴백은 생성 실패 뒤의 대체 출력이다. raw는 측정 지연, effective는 명시한 페이싱 가정으로 유도한 값이다.
이 문서의 비열등은 역할별 게이트를 비교하는 기술적 운영 규칙이다. 비열등 한계·신뢰구간을 둔 통계 검정이 아니다. E7은 gemma3 기준 로컬 교체, A.19는 채택한 gemma4/kanana 등 역할별 기준과 외부 API를 비교한다. Core 정량 비교는 4역할이며 manager_paw 창작 품질은 범위 밖이다. 아래 날짜별 채택과 중간 계획은 당시 기록으로 읽는다.
공개 집계로 산술과 판단 범위를 확인할 수 있으나 연결된 앱 경로·output/ 원문은 접근 제한 자료다. 공개 페이지는 원시 프롬프트·응답·입력 전체를 제공하지 않는다. Scenario Director는 프로젝트의 사람인 시나리오 저자/사용자 역할이며 독립 평가기관이나 평가 모델이 아니다.
외부 API 전환 결정 — 2026-09-16
왜 바꾸는가 — 서빙 제약에서 나온 결정이다
2026-09-13~15의 E7(Core)·E6(NPC)·npc-dialogue-2로 로컬 기준 구성을 정한 뒤 심사 기간의 동시 요청·가용성·비용을 이유로 외부 API를 검토했다. Core gemma4:12b는 advisor A.6 스모크(n=1) PCA 0.90·p95 4,231ms, evaluator A.8(14건×3) 기대 일치 0.57, Stage 0 상주량 7.51 GiB였다. NPC Gemma의 41문답 실패 0·중간값 2.599초는 9월 15일의 별도 검사이며 Kanana 수치가 아니다. 최종 역할별 구성과 변경 순서는 위 표 및 A.19에 남긴다.
바뀐 것은 전제다. 원티드 해커톤은 심사 기간에 실제로 접속 가능한 서비스여야 한다. 로컬 구성의 서빙 현실은 이렇다.
| 제약 | 실측·근거 |
|---|---|
| GPU 1장 16 GiB에 Core·NPC 동거 | pair 12.3 GiB(A.9). 한 GPU에서 추론을 순차 처리하므로 요청이 겹치면 대기가 생긴다. 허용 응답시간 안의 동시 사용자 수는 부하 시험으로 측정하지 않았다 |
| 가용성 | 홈서버·홈 회선·정전·PC 재부팅·러너 실행 부하가 곧 서비스 장애. 시연 시간에 이 PC로 다른 일을 못 한다 |
| 클라우드 GPU 대안 | g6.xlarge/g5.xlarge 24 GiB 45일 $1,100~1,600(24시간), GPU 1장 기준이며 동시 사용자 수는 미측정(docs/apiscenario.md §4) |
| 외부 API | 판당 $0.35~2.1(모델별), 완주 100명 $35~210. API 한도·요청 길이·서버 처리에 영향을 받으며 동시 사용자 수는 미측정. 서버는 t3.small $35~65 또는 현 PC(apiscenario.md §1·§3) |
위 금액은 2026-09-16 의사결정에 쓴 USD 계획 추정이다. GPU 45일·24시간 및 완주 100명 가정은 표와 같고 호출·토큰 가정과 단가 출처는 접근 제한 문서 docs/apiscenario.md §1·§3·§4에 있다. 공개 표만으로 가격 리비전·토큰 산식을 재검산할 수 없다. 로컬의 0은 모델 API 호출료이며 전력·회선·장비 비용을 포함하지 않는다.
같은 이유로 로컬 채택 구성은 유지·롤백 가능하게 둔다. 어댑터 패턴이라 provider 전환은 설정의 provider(ollama↔anthropic)와 모델 이름 두 줄이며 엔진 코드는 바뀌지 않는다(llm_factory.build_llm, AnthropicLLM 어댑터 2026-09-16 추가). 개발·테스트는 제출 전까지 로컬 ollama로 계속한다.
무엇을 재는가 — 로컬 채택 구성 vs 외부 API, 같은 러너·같은 기준
외부 후보는 로컬 기준의 게이트와 역할별 차이를 비교했다. 실제 실행은 Core advisor 22문항·evaluator 14케이스, NPC 대화 20케이스×2반복이며 9월 15일의 41문답과 직접 합산하지 않는다. 러너 수정과 judge 교체가 있어 모델만 바꾼 동일 조건 실험으로 묶을 수 없다. 아래 표는 A.19의 실행별 결과와 예외를 요약한다.
| 축 | 러너·지표 | 로컬 기준(채택 구성) | Anthropic 후보 | 게이트 | 측정 결과(A.19) |
|---|---|---|---|---|---|
| Core 채점 일관성·극성 | run_core_selection.py --stage formal — PCA, 극성쌍, evaluator 기대 일치(A.8 14건) |
gemma4:12b-N: PCA 0.90 · 극성 O · eval 0.57 | Sonnet 5 / Opus 5 | PCA ≥ 0.90, 극성 O, eval ≥ 0.57 (비열등) | 실제 러너는 run_core_selection.py --stage formal(§1 정정). Sonnet n=1 PCA 0.90·eval 0.71, n=3 PCA 0.9667·eval 0.7143 — PASS. Opus n=1 PCA 1.00·eval 0.57 — PASS(경계값) |
| Core 지연 | 같은 러너 p50/p95(ms, 네트워크 포함) | advisor A.6 smoke n=1: p50 2,918·p95 4,231ms; evaluator A.8: p50 2,179·p95 2,735ms | 〃 | C11~C13 게이트 그대로(A.6) | Sonnet n=1: C11 O·C13 X. n=3: C11 X·C13 O — n=1↔n=3 사이 뒤집힘(표본 3, 동시 GPU 작업과 겹침, 변동성으로 해석). Opus: C11·C13 둘 다 X(advisor p95 +38%) |
| Core 완주 | run_selfplay.py(로컬 1회차·외부 5회차 별도) |
완주·폴백 0/17(A.10) | 〃 | 완주·폴백 0·재생성 ≤ 로컬 | 5회차 self-play(판 수 제한 로컬 상향): Opus 완주·전 역할 재생성·폴백 0. Sonnet 완주·advisor/evaluator/manager/classifier/agent 재생성 0이나 planner가 5회 중 2 재생성+1 폴백(beats 7>maxItems 6 — Anthropic 구조화 출력에서 maxItems가 강제되지 않는 어댑터 한계, §5 참고) |
| NPC 의미 품질 | run_npc_dialogue_check.py(09-15 41문답·09-16 20케이스×2반복 별도) — 실패·재생성·출처 구분·당일 기억·회피 |
gemma4:12b: 41/41, 재생성 0, 중간값 2.599s·p95 3.053s | Haiku 4.5 / Sonnet 5 | 실패·재생성 0, 중간값 ≤ 3s, 의미 항목 검토(평가자·선정 기록 불충분) | 20케이스×2반복: 구조 실패 0(둘 다). NPC-역할 재생성 Haiku 2·Sonnet 1(kanana는 1, 전부 동일 금칙어 케이스에서 자가 회복; 재생성 0 조건은 미충족). 중간값 Haiku 1.9~2.3s·Sonnet 2.5s(둘 다 ≤3s). 선별 10케이스의 검토 기록에서 출처·기억 구분 개선을 관찰. 검토자 신원·독립성·선정법은 확인되지 않음. 재생성 0 게이트는 두 후보 모두 미충족 |
| NPC 7세 정책 | run_age7_check.py(v2 기준) 5항목 |
kanana: 4/5·누설 0 | 〃 | 4/5 이상·누설 0 | 채점기 gemma3(당초 기준): Haiku 2/5→3/5 미달(파싱 실패 혼입 확인), Sonnet 2회 정지로 미실행. 채점기를 gemma4:12b(think off)로 통일 후 재측정(§4): kanana·Haiku·Sonnet 항목 수 기준 4/5 통과. 누설 측정은 이 러너 범위 밖(별도 미측정) |
| 실측/추정 비용 | 문자수 기반 부분 비용 추정 | 모델 API 호출료 0 | apiscenario.md §1.3·§1.3b |
결정 입력값, 게이트 아님 | 2026-09-16 해당 실행분 부분 추정 ~$1.06(Core 축 담당 몫, 문자수×1.0토큰 가정 — 실측 토큰 아님) + 그 외 축(NPC 대화·gemma4 통일 검증) 비용은 별도 실측 없음. 총 $10 미만인지 확인 불가 |
결과는 부록 A.19에 적립하고 metrics.yml에 provider: anthropic 줄로 남긴다. 실행 규칙:
- API 키는 측정할 때만 쓰고, 그 사이 개발은 로컬 구성으로 한다.
- 각 후보는 n=1 스모크 → 게이트 통과 시 E7 Stage 2 n=3. 총 비용 상한 $10.
- 해커톤 채점·시연에서 쓸 조합은 이 표로 정한다. 이 시점의 예상 조합은 Core Sonnet 5 + NPC Haiku 4.5(비용)이며, 채점·관리자 품질이 게이트에 걸리면 Core만 Opus 5로 올린다.
판단 기준 한 줄
로컬 채택 구성은 외부 API 비교의 기준선이다. 최종 채택은 역할별 게이트 결과와 심사용 서빙 제약을 함께 검토한 사용자 결정이다. Sonnet의 C11 실패·planner 재생성/폴백, NPC 재생성 0 미충족 및 누설 미측정은 A.19의 예외로 남는다. planner 절단 구현은 A.21.4에 기록됐으나 같은 제출 조합의 전 축 재실험 완료를 뜻하지 않는다.
현재 NPC 평가 기준 — 2026-09-15
이 절은 2026-09-15 당시 정책·로컬 적용 기록이다. 이후 제출 구성은 A.19, 최종 임베딩 구성은 A.21을 참조한다.
NPC 정책 npc-dialogue-2는 쉬운 말투와 인물별 성격을 유지하면서 질문 이해·자기 행동의 이유 설명·당일 기억을 요구한다. 이전 E6의 ‘왜=몰라’, 3턴 망각, 근거 없는 화제 전환, 무조건적인 전언 수용은 현재 정책의 통과 조건이 아니다. 아래 과거 실험 결과는 당시 기준의 기록으로 보존하며 새 정책의 성능 근거로 재사용하지 않는다.
구현 및 새 평가: 승인된 설계(docs/superpowers/specs/2026-09-15-npc-dialogue-persona-design.md), 실행 계획(docs/superpowers/plans/2026-09-15-npc-dialogue-persona.md), 새 평가 스크립트(backend/scripts/run_npc_dialogue_check.py). 기존 Kanana 모델로 변경 효과와 한계를 먼저 측정한 후 동일 조건에서 대안을 비교했다. 실제 질문·허용 근거·출력·재생성·지연을 저장해 검토하며, 의미 품질을 단어 포함 검사나 같은 모델의 자기평가로 통과시키지 않는다.
2026-09-15 적용 결정과 측정 결과
로컬 NPC는 ollama:gemma4:12b, think=off, 대화 temperature 0.3으로 적용한다. 시나리오의 실제 행동·전언 출처를 설명하는 일부 문답이 Kanana보다 정확해진 것을 근거로 선택했다. 중요한 모순 0건·현재 전언 이해·관련성 90%라는 최초 의미 품질 목표에는 아직 미달한다. Core는 기존 Gemma 설정을 유지한다.
| 최종 코드의 평가 범위 | 응답 성공 | 실패 / 재생성 | 중간값 | p95 |
|---|---|---|---|---|
| Kanana, 공통 10개 상황 | 21/21 | 0 / 0 | 1.027초 | 1.468초 |
| Gemma, 동일 10개 상황 | 21/21 | 0 / 0 | 2.553초 | 3.053초 |
| Gemma, 추가 10개 상황 | 20/20 | 0 / 0 | 2.629초 | 3.028초 |
| Gemma, 합계 | 41/41 | 0 / 0 | 2.599초 | 3.053초 |
각 최종 상황은 1회 실행했다. 공통 21문답이 모델 간 비교 대상이며, Gemma 추가분은 최종 기능 범위를 확인하기 위한 검사다. Gemma 전체에는 최근 판의 원래 질문 8개와 후속 질문, 네 인물의 공통 질문, 과거 루프·현재 전언·기억 삭제·행동 금지·소실·정체 질문이 포함된다. 세 실행의 소스 해시는 동일했다. 준비용 과거 대화와 워밍업은 위 수치에서 제외했다. 지연은 메모리 저장소를 사용한 유스케이스 전체 시간이며 실제 API·DB·브라우저 지연은 포함하지 않는다. p95 3초의 초기 목표도 이번 표본에서는 소폭 초과했다.
확인한 개선:
- 은상은 “준한테 들었어. 준이 말해준 거라 내가 직접 본 건 아니야.”라고 출처와 관점을 구분했다.
- 민석은 “채연이가 남긴 배급을 보고 적었어.”라고 실제 행동을 근거로 답했다. 밤에는 준과 함께 있으면 안심되는 이유를 후속 질문까지 이어 답했다.
- 긴 기록 ID를 짧은 모델용 별칭으로 바꾼 뒤 최종 비교에서는 ID 변형으로 인한 재생성이 없었다. 실제 저장과 기억 삭제에는 원래 ID를 사용한다.
- 정적 수첩 내용의 중복을 제거한 뒤, 두 모델 모두 삭제된 오늘의 기록을 날짜·이름·쟁반 수로 복구하던 답변이 이 표본에서 사라졌다. 네 인물 모두 과거 루프의 실제 대화 내용을 회상하지 않았다.
남은 한계:
- Gemma가 현재 있는 은상을 없다고 추측한 답변 1건과, 자기가 방송실 안에 들어갔는지 모른다고 답한 사례가 있다.
- 과거 일을 지금 다시 말해 주는 네 후속 질문은 Gemma 모두 “몰라”로 답했다. 삭제 뒤 새로 전달한 관찰에 답하는 경우에도 회피가 남았다. 저장 경계의 구현과 모델의 현재 전언 이해는 별도로 판단해야 한다.
- 사라진 친구의 이름을 묻자 현재 친구 이름을 나열하는 등 질문 관련성 문제가 남았다. 성격 공통 질문의 후속 답은 안심·동행으로 수렴하는 경향이 있다.
- 의미 평가는 Codex가 허용된 사실과 모델 원문을 함께 읽은 검토다. 사람의 블라인드 페르소나 평가나 새 참가자의 5회차 실제 플레이는 아직 수행하지 않았다.
원인 진단에서는 두 모델의 세 입력을 기본 문맥과 8192토큰 문맥으로 비교했다. 실제 입력 토큰 수가 각각 동일해, 이 사례의 오류를 문맥 잘림으로 설명할 근거가 없었다. 현재 전언을 허용하는 지시의 명확화와 목소리 문구만 바꾸는 진단도 단독 개선 효과를 보이지 않았다. Gemma think=true 진단 3건은 생성 1024토큰 상한에서 모두 최종 답변 없이 종료됐고 23.278~23.840초가 걸렸다. 이 제한에서 실패했으므로 추론을 켜는 설정은 적용하지 않는다.
이전 Kanana 82문답 반복과 중간 프롬프트 실험은 개발 과정의 별도 자료이며 최종 41문답과 합산하지 않는다. 원래 플레이 8답변은 보관한 과거 기준선이고 새 조건으로 다시 실행한 대조군이 아니다.
자료: 상세 평가 보고서(output/npc-dialogue-2026-09-15/evaluation-report.md), 최종 집계(output/npc-dialogue-2026-09-15/final-summary.json), 기능 검증(docs/review-verification/2026-09-15-npc-dialogue/README.md). 재현 시 모델과 thinking을 명시한다:
backend/.venv/bin/python backend/scripts/run_npc_dialogue_check.py --model kanana1.5:8b-q4km --think default --cases all --repeat 1
backend/.venv/bin/python backend/scripts/run_npc_dialogue_check.py --model gemma4:12b --think off --cases all --repeat 1
기준일: 2026-09-13 문서 지위: 모델 평가 정본. 모델 선정·교체·수치에 관한 모든 기록은 이 문서에 적립한다 상위 문서:
REMAKE_DAY_AI_Agent_Evaluation_PART1_정본_v1.0· 부록E6_Model_Descent_v0.1결과 적립:docs/metrics.yml· 원문 raw:docs/review-verification/<date>-<experiment>/표현 원칙: 측정하지 않은 것은 주장하지 않는다. 아래 §0.2의 금지 표현을 따른다 문서 형식: 자매 프로젝트thehorn.masterlesscompany.com의 research-portfolio 스펙에서 RQ·요인설계·Funnel·Protocol Metrics·Pareto 우선 원칙을 가져와 REMAKE DAY에 맞게 고쳐 썼다. 이 문서는 자립적이며 외부 문서를 참조하지 않는다
0. 포지셔닝
0.1 이 문서가 무엇인가
REMAKE DAY의 두 LLM 슬롯(NPC / Core)에 어떤 모델을 쓸지 결정하기 위한 재현 가능한 실험 기록이다. 논문이 아니라 논문의 형식을 빌린 기술 기록이며, 다음 세 가지를 남기는 것이 목적이다.
- 어떤 조건에서 몇 번 재서 무슨 값이 나왔는가
- 그 값으로 무엇을 결정했고 무엇은 결정하지 않았는가
- 같은 결과를 다시 만들려면 무엇을 실행해야 하는가
0.2 쓰지 않는 표현
| 금지 | 왜 |
|---|---|
| “A 모델이 B보다 낫다” | 어느 역할에서 어느 지표로 몇 번 쟀는지 없음 |
| “작은 모델도 충분하다” | 충분의 기준이 없음 |
| “성능이 N% 개선됐다” | 같은 조건의 전후 측정이 아니면 개선율이 아님 |
| “SOTA” / “최적” | 탐색 범위 밖을 주장함 |
| Screening tier의 지연·메모리 | tier 간 하드웨어가 다르면 비교 대상이 아님 |
| 폴백 결과를 성공으로 집계 | 폴백은 모델이 실패했다는 뜻 |
0.3 두 슬롯, 두 질문
아래 현행 기준선은 2026-09-13 설계 당시 값이다. 이후 역할별 채택은 §5.3과 A.19, A.21.8에 기록한다.
Core 슬롯 planner · manager_check · manager_paw · advisor_answer · evaluator_verdict
질문: 자원 예산 안에서 "높은 사고"를 가장 잘하는 모델은?
현행 기준선: gemma3:12b (운영은 gemini-3-flash-preview)
→ E7
NPC 슬롯 agent · ask_npc
질문: 7세 역할을 해내는 "가장 낮은" 모델은?
현행 기준선: exaone3.5:7.8b
→ E6
두 질문은 성격이 다르다. E6는 같은 family 안의 크기별 관측 비교로 크기 효과를 탐색하고(학습·구조·양자화 차이는 남는다), E7은 예산 제약 아래 선택을 묻는다(family를 섞어야 답이 나온다). E6의 “family 고정” 규칙을 E7에 적용하지 않는다.
순서는 E7 → E6다. 실패 비용이 비대칭이기 때문이다. Core가 틀리면 세계가 굴러가지 않고 채점이 무너진다(§A.1의 RM1이 그 사례다). NPC가 어색한 것은 게임이 돌아가는 채로 고칠 수 있다.
1. 자원 제약 — 모든 후보의 상한
단일 GPU RTX 5060 Ti 16GB (15.19 GiB 가용). 두 슬롯이 동시 상주해야 한다.
2026-09-13 실측 (/api/ps 기준, 모델 3종 동시 로드):
exaone3.5:7.8b 4.14 GiB
gemma3:12b 7.49 GiB
qwen3.5:0.8b 1.03 GiB
──────────────────────────
모델 합계 12.66 GiB
nvidia-smi 13.76 GiB → 런타임 오버헤드 ≈ 1.1 GiB
여기서 나오는 Core 예산:
| NPC 구성 | Core 예산 |
|---|---|
| exaone3.5:7.8b 유지 (4.14) | ≈ 9.5 GiB |
| E6로 qwen3.5:4b 축소 시 (3.2) | ≈ 10.5 GiB |
예산식은 가용 VRAM − NPC 상주량 − 런타임 오버헤드 − 안전 여유분이다. 표의 9.5/10.5 GiB는 당시 보수적 계획 예산이다. 단순 차감값은 약 9.95/10.89 GiB이며 차이 0.45/0.39 GiB를 실제 정한 안전 여유 정책값으로 확인할 기록은 없다. 이 예산과 모델별 실측 상주량을 구분한다.
당시 조사한 후보 태그의 디스크 용량은 gemma4:e4b 8.95 GiB 다음 gpt-oss:20b 12.85 GiB로, 9.0~12.8 GiB 구간에서 추가 후보를 확인하지 못했다. 디스크 용량은 상주량이 아니므로 예산을 1 GiB 늘려도 공존 가능한 후보가 없다는 결론이나 E6 선후에 손해가 없다는 결론은 내리지 않는다. 제외 후보의 상주량·오프로딩·문맥 설정은 미측정이다.
2. E7 — Core Role Model Selection under Resource Budget
2.1 Research Questions
RQ-C1 — Selection
9.5 GiB 예산 안에서 Core 5역할을 현행
gemma3:12b이상으로 수행하는 모델이 있는가? 없으면 가장 가까운 후보는 어느 지표에서 얼마나 미달인가?
이번 정량 비교는 4역할이다. manager_paw 창작 품질은 측정하지 않았으며 루프 완주가 그 의미 품질 검증을 대신하지 않는다.
RQ-C2 — Reasoning Mode
thinking ON은 Core 역할 품질을 올리는가, 지연만 늘리는가? 그 효과의 크기는 모델에 따라 달라지는가?
RQ-C3 — Online vs Local
현재 운영 중인
gemini-3-flash-preview는 로컬 후보 대비 어디에 위치하는가? 10 RPM 페이싱을 포함한 실효 처리량 기준으로도 같은 결론인가?
2.2 조건 — 비대칭 요인 설계
완전 요인 설계(모든 모델 × 두 모드)를 그대로 쓸 수 없다. 기준선 gemma3:12b에 thinking capability가 없기 때문이다. 이것은 설계 결함이 아니라 그 모델의 성질이며, 표에 그대로 표기한다.
| 모델 | 파라미터 | 디스크 | 실측 VRAM | 양자화 | ctx | -N (OFF) |
-T (ON) |
|---|---|---|---|---|---|---|---|
gemma3:12b (기준선) |
12.2B | 7.59 GiB | 7.49 GiB | Q4_K_M | 131K | ✓ | 불가 |
gemma4:12b |
11.9B | 7.04 GiB | 7.51 GiB | Q4_K_M | 262K | ✓ | ✓ |
gemma4:e4b |
8.0B | 8.95 GiB | 3.06 GiB | Q4_K_M | 131K | ✓ | ✓ |
qwen3.5:9b |
9.7B | 6.14 GiB | 5.25 GiB | Q4_K_M | 262K | ✓ | |
gemini-3-flash-preview (현행 운영) |
— | 온라인 | — | — | — | ✓ | ✓ |
실측 VRAM은 2026-09-13 Stage 0의 /api/ps size_vram 값이다(부록 A.5).
총 9셀.
두 가지를 명시해 둔다.
- 양자화가 로컬 4종 전부 Q4_K_M으로 일치한다. 크기 축을 양자화 혼재 없이 읽을 수 있다.
gemma4:e4b는 디스크로 예산을 추정하면 안 된다. 디스크 8.95 GiB(11.9B짜리 7.04 GiB보다 크다)인데 실제 상주는 3.06 GiB다. 그 차이의 원인은 이 측정만으로 확인하지 않았다(CPU/GPU 배치·캐시·로드 로그의 추가 확인 필요). 디스크 기준 설계 예측(§1)이 이 모델에서만 크게 빗나갔으므로, 예산 판정은 실측 VRAM으로만 한다. 표에는 파라미터·디스크·실측 VRAM 셋을 항상 같이 적는다.
2.2.1 후보에서 제외한 것과 근거
| 제외 | 근거 |
|---|---|
구세대 전반 — qwen3 · qwen2.5 · phi4 · olmo2 · glm4 · deepseek-r1 · aya-expanse · mistral-nemo · cogito |
최신 세대가 같은 예산 안에 존재한다. cogito는 레지스트리 기준 “1년 전” v1 Preview |
예산 초과 — qwen3.5:27b(16.22) · gemma4:26b(17.33) · gemma4:31b(18.50) · gpt-oss:20b(12.85) · solar-pro:22b(12.40) · mistral-small:24b(13.35) |
디스크 크기를 이용한 사전 선별. 해당 실행 조건의 상주량·NPC 공존 여부는 미측정 |
한국어 특화 모델 — kanana · midm · a.x · hyperclovax-seed · llama-dna · eeve · bllossom · ko-gemma · EXAONE 4.0 |
Ollama 공식 라이브러리에 없다(부록 A.4). hf.co/*-GGUF 경로는 양자화·템플릿이 제각각이라 §2.3 고정 조건을 깬다 |
exaone3.5:7.8b |
아래 별도 |
exaone3.5:7.8b를 Core 후보에서 뺀 이유. 이 모델은 NPC 슬롯과 같은 모델이라 Core로 쓰면 VRAM 추가분이 0이라는 구조적 장점이 있다. 그럼에도 제외한다.
| # | 근거 |
|---|---|
| 1 | E6와 결합한다. VRAM 0이라는 장점은 두 슬롯이 같은 모델일 때만 성립하는데, E6가 NPC를 내리면 그 순간 사라진다. E7에서는 NPC 변경과 독립된 Core 후보를 비교하기로 범위를 정했다 |
| 2 | 컨텍스트가 짧다. 2026-09-13 /api/show 실측 — exaone3.5:7.8b 32,768 / gemma3:12b 131,072 / qwen3.5:9b 262,144 / gemma4:12b 262,144 / gemma4:e4b 131,072. manager_check는 hidden_truth 전문을, advisor_answer는 당일 기록 전체를 받는다. 위 값은 지원 최대치다. 실행 num_ctx와 최대 실입력이 한계에 닿았는지는 이 비교로 확인하지 않았다 |
| 3 | 축이 다르다. “VRAM 추가 0”은 품질이 아니라 배포 구성 이득이다. 공유 구성은 총상주량과 추가상주량을 분리해 비교할 수 있다. Pareto의 0좌표는 무한대가 아니며 여기서 제외한 것은 실험 범위 선택이다 |
exaone3.5:7.8b는 NPC 슬롯 기준선으로 유지한다. Phase 2(E6)의 비교 대상이다.
2.3 고정 조건 (Harness Freeze)
여기서 ‘현행’은 각 실행 시점의 앱 상태를 뜻한다. 날짜·원문 파일·모델 태그·반복 수는 각 부록에 연결했으나 실행 커밋, 프롬프트/스키마 해시, 모델 digest, 유효 문맥·토큰 상한, warm/cold, 난수 설정을 모두 묶은 manifest는 없다. 별도로 적힌 수정 커밋 이외의 값을 추정해 채우지 않는다. 접근 제한 원문이 필요한 완전 재현과 공개 집계의 산술 검증은 범위가 다르다.
셀마다 동일하게 적용하며, 하나라도 바꾸면 새 실험이다.
| 항목 | 값 |
|---|---|
| 양자화 | 로컬 전부 Q4_K_M. 혼재 금지 |
| 프롬프트 | apps/engine/app/use_cases/prompts.py 현행. 모델별 튜닝 금지 |
| 하네스 | run_with_harness 현행. harness_on=True, 재생성 최대 2회 |
| temperature | 역할별 현행 값 (advisor_answer 0, evaluator_verdict 0.0, 나머지 기본) |
| 시나리오 | scenario_a 현행. 어댑터 무변경 |
| 발화 세트 | §2.4의 4역할 프로브. DEV 세트 그대로 |
| NPC 슬롯 | 측정 중 로드하지 않는다 (§2.4의 프로브가 전부 Core 역할이므로) |
엔진 코드를 바꾸지 않는다. thinking 제어는 러너 안에서 어댑터를 서브클래싱해 주입한다(§4.2). 프로덕션 반영은 실험 결과를 본 뒤의 별도 결정이다.
하네스 조작 금지. “이 모델은 프롬프트를 조금 고치면 통과한다”는 발견은 기록하되 E7 결과에는 반영하지 않는다. 그것은 다른 실험이다.
2.4 측정 대상 — Core 4역할
기존 12문항 corpus는 advisor_answer만 평가한다. 가장 어려운 역할이라 대표성은 있으나, planner의 긴 구조화 출력과 evaluator의 판정은 재지 못한다. 네 역할로 나눠 측정한다.
| 역할 | 프로브 출처 | 문항 | 요구 능력 |
|---|---|---|---|
advisor_answer |
2026-09-09 보존 corpus | 12 + 통제 4 | 근거 기반 억제 — 기록에 없는 말 금지 |
planner |
실프롬프트 | 3 | 긴 구조화 출력(4명×6비트) + 어휘 enum 준수 |
manager_check |
실프롬프트 | 3 | 긴 입력(hidden_truth) 소화 + 절제된 판단 |
evaluator_verdict |
실프롬프트 | 3 | 의미 동치 판정 + 근거 표현쌍 제시 |
당시 계획: 셀당 25문항 실행(advisor 12+4 및 나머지 3역할 각 3). 실제 A.7은 advisor 통제가 10개로 늘어 31문항×반복이며 아래 계획과 구분한다.
manager_paw는 1차에서 제외한다. 출력이 창작(규칙+부작용)이라 결정적 채점이 불가능하고, 사전등록 통제도 만들기 어렵다. 후보가 좁혀진 뒤 별도로 다룬다.
3. 측정 설계
3.1 판정 원칙
판정 원칙은 하나다 — 후보 모델의 답을 Ground Truth로 쓰지 않는다. 판정은 다음 둘로만 한다.
- 결정적 게이트 — 코드가 계산한다. 사람도 LLM도 개입하지 않는다
- 사전등록 통제(pre-registered expected direction) — 기대 답 방향이 실험 전에 고정된 문항
LLM-as-judge는 1차에서 쓰지 않는다. 후보 5개 중 4개가 로컬이고 기준선도 후보이므로, 지금은 이해충돌 없는 judge를 고를 수 없다. 후보가 2~3개로 좁혀진 뒤 별도 라운드로 붙인다. 그때 judge는 후보가 아닌 제3 모델이어야 하고, judge 일치율(같은 발화 3회)을 셀마다 기록한다.
3.2 Protocol Metrics — 결정적 게이트
3.2.1 Schema Validity Rate — SVR
SVR = 스키마 검증을 통과한 출력 수 / 전체 모델 호출 수
3.2.2 Fallback Rate — FBR
FBR = fallback_used=true 인 호출 수 / 전체 호출 수
폴백은 성공으로 집계하지 않는다. 재생성 2회를 모두 실패해 역할별 대체 문구가 쓰였다는 뜻이다.
3.2.3 Violation Count — VC
하네스가 기록한 위반을 종류별로 센다. 합계 하나로 뭉개지 않는다.
parse / schema / action_vocab / forbidden_word /
non_verbatim_evidence / missing_public_evidence / unknown_person
3.2.4 Legal Action Rate — LAR (planner 전용)
LAR = action_vocab 안의 행동 수 / 생성된 전체 행동 수
현재 planner_output()이 행동 칸을 JSON 스키마 enum으로 제약하므로, provider가 enum을 존중하면 1.0이어야 한다. 1.0이 아니면 그 provider의 structured output이 enum을 안 지킨다는 신호이며, 우선 구조화 출력 계약 위반으로 기록한다. provider의 스키마 지원·어댑터 전송·검증 로직·모델 출력 중 원인 층은 별도 확인이 필요하다.
3.2.5 Evidence Grounding Rate — EGR (advisor_answer 전용)
EGR = detail과 evidence_ids를 모두 채운 응답 수 / 전체 응답 수
2026-09-09 03:58 실측에서 EGR이 0/12였다. 수정 후 9회 각각 detail은 11/12, evidence_ids는 10/12, 두 조건의 교집합은 10/12여서 EGR=0.8333이었다는 원시 기록이 있다(§A.1).
2026-09-13 Stage 1에서 판정 축에서 내렸다. 8셀 전부 정확히 0.83으로 같았고, 비는 2문항이 모든 셀에서 동일했다.
| 비는 문항 | 이유 |
|---|---|
| “호송 당한 사람은 누구야?” | 기록에 없는 사건이다. 근거를 못 다는 것이 정상 동작 |
| “너에게 물어볼 수 있는건 뭐야?” | intervention_interactor의 meta 분기가 LLM을 거치지 않고 처리한다 |
이번 코퍼스에서는 8셀의 EGR이 0.83이고 빈 문항도 같아 후보를 구분하지 못했다. 회귀 감시용으로 계속 기록하되(0.83에서 떨어지면 이상 신호), 후보 순위에는 쓰지 않는다.
3.2.6 Thinking Failure Rate — TFR (-T 셀 전용)
-T 셀에서만 집계한다.
TFR = thinking 실패 호출 수 / -T 셀 전체 호출 수
실패 정의: 빈 content · 미종료 · 토큰 상한 도달 · 최종 출력 malformed · 타임아웃
소형·신형 모델에서 특히 중요하다. 2026-09-13 사전 실측에서 qwen3.5:0.8b가 thinking 기본값으로 content 빈 문자열을 반환해 JSON 파싱에 실패한 사례가 있다(§A.2).
3.3 Pre-registered Control — 의미 축
advisor_answer의 통제 문항은 10개이며, 전부 expected_relation과 expected_evidence_ids가 실험 전에 고정되어 기계 채점된다. 2026-09-09 코퍼스를 그대로 쓴다. RM1(질문 명제의 극성과 인용 사건의 극성을 혼동)이 이 축에서 잡혔다.
| 통제 | 질문 | 기대 | 근거 |
|---|---|---|---|
| PC1 | 채연이 자기 몫을 반쯤 남기는 걸 봤어? | supported | public-2 |
| PC2 | 관리자가 이상한 점은 방송실로 알리라고 말했어? | supported | public-1 |
| PC3 | 채연의 검진 결과가 공개되었어? | contradicted | public-6 |
| PC4 | 채연의 검진 결과가 공개되지 않았어? | supported | public-6 |
| PC5 | 누가 자기 몫을 반쯤 남기는 모습을 봤어? | supported | public-2 |
| PC6 | 채연은 왜 자기 몫을 남겼어? | unknown | public-2 |
| PC7 | 채연의 검진 결과가 내일 공개될 거야? | unknown | public-6 |
| PC8 | 관리자의 방송 지시가 실제로 이행됐어? | unknown | public-1 |
| SPC1 | 채연의 검진 결과가 공개되었어? | supported | public-6 (합성) |
| SPC2 | 채연의 검진 결과가 공개되지 않았어? | contradicted | public-6 (합성) |
SPC1·SPC2는 public-6의 기록을 결과는 공개되지 않았다 → 결과를 공개했다로 뒤집은 합성 fixture다. 같은 질문에 반대 기대가 걸리므로, 모델이 기록을 읽는지 질문 표면만 보는지가 갈린다.
PC3·PC4와 SPC1·SPC2는 각각 극성 쌍이다. 반드시 함께 판정한다(게이트 C4). 한쪽만 통과하는 것은 극성 혼동이 남아 있다는 뜻이며, 2026-09-09에 PC3을 고치자 PC4가 회귀한 이력이 있다.
PCA = 기대 방향과 일치한 통제 수 / 통제 문항 수
PCA는 방향만 평가한다. PC근거는 기대 evidence_ids 집합을 모두 포함한 통제 수/전체 통제 수(추가 ID 존재는 이 지표가 감점하지 않음)이며 PCA와 별개다. 러너의 CONTROL_PAIRS 두 쌍을 모두 AND로 판정한다. PC8은 행동 이행 여부에 대한 unknown 통제이며 극성쌍에 넣지 않는다.
3.4 Compute Metrics
품질과 별도 축으로 저장한다. 다만 지연만은 예외로 Hard 게이트다(§5.1 C11~C13) — 아래 이유 때문이다.
3.4.1 지연이 Hard 게이트인 이유
Core 호출은 화면 뒤에 숨지 않는다. 전부 플레이어가 멈춰서 기다리는 자리다.
| 순간 | 역할 | 회차당 콜 | 대기 성격 |
|---|---|---|---|
| 아침 진입 | planner |
1 | 로딩 화면. 하루 1회 |
| 비트 이동 | manager_check |
6 | 장면 전환에 묻힌다 |
| 밤 질문 | advisor_answer |
3 | 질문을 던지고 눈앞에서 기다린다 — 가장 민감 |
| 원숭이손 | manager_paw |
1 | 제안이 뜨는 순간 |
| 밤 채점 | evaluator_verdict |
정답 명제 수만큼 (~10) | 점수가 나올 때까지 대기 |
프로젝트 CLAUDE.md에 ”/play는 느린 응답에서 복구하려고 리로드하지 말 것” 이라는 규칙이 이미 있다. 느린 응답이 판을 날린 이력이 있다는 뜻이다. 품질이 아무리 좋아도 게임에서 못 쓰는 지연은 탈락 사유다.
3.4.2 재시도 배수 — 단발 측정을 믿지 않는다
하네스는 검사에 걸리면 최대 2회 재생성한다(총 3시도). thinking이 켜진 셀에서는 재시도마다 thinking 비용이 곱해진다.
2026-09-13 실측 — gemma4:12b-T:
Stage 0 evaluator 단발 약 27초
Stage 1 advisor 22문항 당시 요약 약 80초/문항(평균 산식 미확인)
역할·입력·출력·warm 조건이 달라 두 수치의 비를 재시도의 인과효과로 해석하지 않는다. 같은 문항의 시도별 시간·재생성 수·총시간을 분리한 집계가 필요하다.
지연 게이트는 단발 값이 아니라 코퍼스 전체의 p95로 판정한다.
3.4.3 저장 항목
size_vram은 부록에 적힌 수집 시점의 모델별 상주량이다. 연속 샘플링한 장치 최고 사용량으로 해석하지 않는다. A.9 등의 GPU peak는 별도 nvidia-smi 관측값이다. Ollama thinking은 message.thinking 문자 수, Gemini는 thoughts_token_count 토큰 수다. 호환 키 thinking_tokens가 문자를 담는 경우가 있으므로 provider와 단위를 함께 읽고 두 단위의 크기를 비교하지 않는다.
- 파라미터 수 · 양자화 · 디스크 크기
- 모델 GPU 상주량 (
/api/ps의size_vram; 장치 전체 peak가 아님) - 콜드 스타트 / warm p50 / warm p95 지연
- 출력 토큰 · thinking 토큰 (
-T셀) - 타임아웃 · OOM · provider 오류
- 실효 처리량 — Gemini는 두 값을 분리해서 적는다.
latency_raw_*— API 왕복만. 러너의ThinkingGeminiLLM은_pace_request를 호출하지 않으므로 측정값이 곧 이 값이다latency_effective_*— 순차 정상상태 처리 간격의 근사 유도값(프로덕션 벽시계 실측 아님).gemini_llm.py:17의 프로세스 전역 10 RPM은 Lock 안에서 sleep하므로 연속 호출의 간격이 60/RPM초 이상으로 강제된다. 따라서effective = max(raw, 60000/RPM)으로 유도한다- 유도값임을 표에 반드시 표기한다. 측정값이 아니다
- 게이트 C11~C13은
effective로 판정한다. 실제 사용자 대기는 첫 호출·요청 시작/완료 기준·공유 대기열·동시성에 따라 달라 이 산식만으로 확정하지 않는다
3.5 결과를 어떻게 읽는가
단일 종합 점수를 만들지 않는다. 가중치가 임의면 합계도 임의다.
주 결과는 다음 넷으로 제시한다.
- Hard Gate 통과 여부 (§5)
- 역할별 지표 — 합산하지 않는다
- Pareto Frontier — 품질 × 모델 GPU 상주량, 품질 × p95 지연
- Paired delta — 같은 모델의
-T−-N차이
Core 품질 87.3 같은 숫자는 설명 편의를 위한 보조 요약으로만 쓰고, 모델 선정의 유일한 기준으로 쓰지 않는다.
4. 실험 절차
4.1 Funnel
처음부터 9셀 × 25콜 × n을 전부 돌리지 않는다.
| Stage | 내용 | 차단 조건 |
|---|---|---|
0 protocol |
프로토콜 호환 — 셀당 1콜. 한국어 JSON이 나오는가, thinking 제어가 먹는가, 모델 GPU 상주량은 얼마인가. thinking 가능 모델은 default도 함께 재서 provider 기본값이 어느 셀인지 확정한다(§7 제약 2) |
JSON 파싱 실패 → 해당 셀 탈락, 사유 기록 |
1 smoke |
스모크 — 9셀 × 4역할 × n=1 (25콜) | FBR > 0.5 또는 SVR < 0.9 → 탈락 |
2 formal |
본실험 — 통과 셀 × n=3 | 공식 수치. metrics.yml 적립은 이 단계부터 |
3 concurrency |
동시성 — 최종 후보 + NPC(exaone3.5:7.8b) 공존 모델 GPU 상주량 |
축출 발생 시 후보 탈락 |
4 loop |
루프 스모크 — 최종 후보로 게임 1회차 완주 | 크래시 / 폴백 폭주 → “게이트 통과했으나 루프 불가” |
Stage 4가 있는 이유: 프로브 25개를 통과해도 실제 루프에서 NPC·Manager와 엮이면 다를 수 있다. 게이트 통과는 대체 가능과 같지 않다. 채택 결정은 Stage 4 뒤에만 한다.
4.2 thinking 제어
이 단락은 2026-09-13 러너 구현 전 설계 기록이다. 이후 기본값 실측은 A.5, 프로덕션 think 제어 반영은 §5.3을 참조한다.
OllamaLLM은 /api/chat에 think 필드를 보내지 않고, GeminiLLM은 thinking_config를 보내지 않는다. 즉 현재 두 어댑터 모두 provider 기본값대로 돌고 있으며, 그 기본값이 무엇인지 측정된 적이 없다.
러너가 어댑터를 서브클래싱해 주입한다. 프로덕션 코드는 변경하지 않는다.
Ollama body["think"] = False | True
Gemini config.thinking_config = ThinkingConfig(thinking_budget=0) # OFF
= ThinkingConfig(include_thoughts=True) # ON
-N 셀에서 thinking이 실제로 꺼졌는지는 응답의 thinking 토큰 수가 0인지로 검증한다. 설정만 보내고 확인하지 않으면 셀 라벨을 신뢰할 수 없다.
4.3 러너
cd backend
.venv/bin/python scripts/run_core_selection.py \
--models gemma3:12b,gemma4:12b,gemma4:e4b,qwen3.5:9b,gemini-3-flash-preview \
--stage protocol
구현 당시 현황 (2026-09-13, 다음 표의 09-14 완료 기록 이전) — --stage protocol만 구현됐다. Stage 1 이후는 --roles·--n·--think 인자와 함께 추가한다.
| 구성 요소 | 상태 |
|---|---|
ThinkingOllamaLLM — /api/chat에 think 주입, message.thinking 길이 계측 |
구현 |
ThinkingGeminiLLM — thinking_config 주입, usage_metadata.thoughts_token_count 계측 |
구현 |
thinking capability 자동 조회(/api/show) → 불가 모델은 default만 측정 |
구현 |
게이트 C9 자동 검증 (off 셀의 thinking 사용량 = 0) |
구현 |
provider 기본값 판정 (default 셀의 thinking 사용량) |
구현 |
모델 GPU 상주량 수집 (/api/ps size_vram) |
구현 |
Stage 1~4, metrics.yml 적립 |
구현 완료 (2026-09-14) — Stage 4는 scripts/loop_app.py 래퍼 + --stage loop, 적립은 Stage 2부터 |
프로덕션 어댑터(ollama_llm.py · gemini_llm.py)는 변경하지 않았다. thinking 제어는 러너 안의 서브클래스에만 있다.
규칙은 E6 §6과 동일하다 — engine은 import만, 결과는 append만.
4.4 산출물
| 대상 | 위치 |
|---|---|
| 설계·결과 정본 | 이 문서 |
| 줄 단위 수치 | docs/metrics.yml (runner: core_selection) |
| 원문 raw | docs/review-verification/2026-09-13-core-selection/ |
| 러너 | backend/scripts/run_core_selection.py |
metrics.yml 줄 형식
아래는 키 형식을 보이는 가상 예시이며 실측 결과가 아니다. 실제 결과는 각 부록의 실행 파일과 docs/metrics.yml에 연결한다.
- {"runner": "core_selection", "date": "2026-09-XX", "model": "gemma4:12b", "think": "off",
"stage": "formal", "role": "advisor_answer", "n": 3, "calls": 48,
"svr": 1.0, "fallback_rate": 0.0, "violations": {"non_verbatim_evidence": 0},
"egr": 0.92, "pca": 1.0, "tfr": null,
"latency_p50_ms": 980, "latency_p95_ms": 1600, "peak_memory_gb": 7.6,
"thinking_tokens": 0, "host": "rtx5060ti-16g", "runtime": "ollama-cuda",
"quant": "Q4_K_M", "non_inferior_to_baseline": true}
| 키 | 규칙 |
|---|---|
stage |
§4.1의 Stage 이름을 그대로 쓴다. protocol·smoke는 metrics.yml에 적립하지 않는다 — 원문 artifact에만 남긴다 (§4.1과 일치) |
think |
off / on / n/a(기준선) |
tfr |
-N 셀은 null |
thinking_tokens |
-N 셀에서 0이 아니면 셀 라벨 오류다. 그 줄은 판정에서 뺀다 |
non_inferior_to_baseline |
같은 날 같은 stage의 gemma3:12b 줄과 비교해 러너가 계산 |
5. 게이트
5.1 Hard — 미달 시 후보 제외
| # | 게이트 | 값 |
|---|---|---|
| C1 | SVR | ≥ 0.98 |
| C2 | Fallback Rate | ≤ 0.05 |
| C3 | LAR (planner) | = 1.0 |
| C4 | 실제 기록 PC3·PC4 및 합성 기록 SPC1·SPC2 극성쌍 | 두 쌍의 네 문항 모두 기대 방향 일치 |
| C5 | 모델 GPU 상주량 (로컬, size_vram) | ≤ 9.5 GiB |
| C6 | TFR (-T 셀) |
≤ 0.1 |
| C11 | advisor_answer p95 |
≤ 5초 — 질문하고 눈앞에서 기다리는 자리. 타이핑 인디케이터가 “생각하는 중”으로 읽히는 한계 |
| C12 | planner p95 |
≤ 15초 — 아침 로딩 화면, 하루 1회 |
| C13 | 밤 채점 목표의 대리지표 (evaluator p50 × 10 유도 추정) |
≤ 30초 — 진행 표시가 있을 때 참을 수 있는 범위 |
C11~C13의 값은 UX 통념으로 잡은 것이며 우리 게임에서 체감 검증한 값이 아니다. 플레이테스트로 체감 기준이 나오면 그 값으로 교체한다.
온라인 provider의 목표는 페이싱 포함 벽시계 판정이다. 여기서 보고한 effective는 유도 추정이므로 실제 연속 호출 벽시계 검증은 남아 있다. gemini_llm.py의 프로세스 전역 10 RPM은 콜 사이에 6초를 강제하므로, 밤 질문 3회와 채점 10콜만으로 C13을 넘길 수 있다.
5.2 Soft — 보고 필수
C8의 같은 runtime은 로컬 반복 비교 조건이다. 외부 API는 runtime이 다르므로 역할·통제별 별도 비교로 표시한다. C9는 Soft 표에 있으나 셀 라벨의 유효성 필수조건이고 C10은 최종 채택 전 완주 확인이다. 둘을 선택적 보고로 해석하지 않는다.
| # | 항목 | 값 |
|---|---|---|
| C7 | 셀당 n | ≥ 3 |
| C8 | 기준선 줄 존재 | 같은 날, 같은 stage, 같은 runtime |
| C9 | -N 셀 thinking 토큰 |
= 0 (라벨 검증) |
| C10 | Stage 4 루프 스모크 | 최종 후보에 한해 통과 |
5.3 대체 판정 (Non-inferiority)
후보 M이 기준선
gemma3:12b를 대체할 수 있는 조건: 역할별로 모든 Hard 게이트가 같거나 높고, 모델 GPU 상주량 ≤ 기준선, p95 지연 ≤ 기준선
| 규칙 | 내용 |
|---|---|
| 역할 단위 비교 | 합산하지 않는다. 기준선이 통과한 역할을 M이 떨어뜨리면 비열등 아님 |
| 동률 | 품질이 같으면 메모리가 작은 쪽 |
| 유의성 | n=3에서 통계적 유의성을 주장하지 않는다. “역할별로 같거나 높다”의 이진 판정이다 |
아무것도 만족하지 않으면 “대체 후보 없음 — gemma3:12b 유지”라고 쓴다. 이것도 결과다.
채택 (2026-09-14, 사용자 결정)
Core 슬롯을 gemma4:12b-N으로 전환했다. 위 비열등 산식의 문자적 결론이 아니라(VRAM 7.51 vs 7.49로 20MiB 초과, evl p50 2,179 vs 976ms), Funnel 0~4 실측 전체를 근거로 한 채택 결정이다:
- 극성쌍(RM1) 통과 — 측정된 로컬 셀 중 유일 (A.7, PC3·PC4와 SPC1·SPC2 n=3 전부; PC8 unknown 판정은 별도 통과)
- 실플레이 evaluator 통제 기대 일치 0.57로 4셀 중 최상, 판정 요동 0 (A.8)
- C11~C13 전 게이트 통과, NPC와 무축출 공존(A.9), 루프 1회차 완주·폴백 0 (A.10)
- 운영 Gemini는 무료 티어 기준(D1 결정)에서 지연·용량이 성립하지 않음 (A.11 추록)
반영 내역: Core 설정 3항목(provider ollama · 모델 gemma4:12b · think off 신설). thinking 제어는 프로덕션 반영 — ollama_llm.py에 think: bool | None = None(None=필드 미전송, 하위호환), Settings.core_llm_think/npc_llm_think(default/on/off, 알 수 없는 값은 기동 실패), llm_factory.build_llm 배선. 단위 테스트 7건 추가(tests/pure/test_ollama_llm.py), 전체 393 passed·import-linter 4계약 유지. 실서버 검증(별도 포트·테스트 DB): health core ollama:gemma4:12b, 아침 loop 1회의 벽시계 12.13s. A.7 planner p95 12,215ms와 크기는 비슷하지만 역할·집계 범위가 다름, thinking OFF 실작동 확인. D2(운영 Gemini thinking)는 Gemini 이탈로 대상 소멸.
채택 — NPC 슬롯 (2026-09-14, 사용자 결정)
이 결정은 2026-09-14 당시 정책·라이선스 검토·A.18 스모크에 따른 기록이다. 09-15 정책과 A.19 이후 구성의 성능 근거로 합치지 않는다. 0/21·평균 11.4자는 A.18의 수집 발화 관찰이다.
NPC 슬롯을 kanana1.5:8b-q4km(카카오 Kanana 1.5 8B, Apache 2.0)로 전환했다. 근거:
- 라이선스 — 현행
exaone3.5:7.8b는 연구 전용(상업은 LG 별도 계약 필요, A.17 원문 인용). 상업 배포 스택에서 유일한 블로커였다 - 7세 정책 — 상업 후보 3종 중 최상 4/5 (A.17). exaone의 어른화 축(왜=몰라)이 1.0으로 재현되지 않고, 유일 미달(사실대로 0.6)은 단일 케이스 회피 반복로 통과선에 1콜 차이
- 발화 품질 — exaone 기준선 대비 자연스러움 유효 16쌍에서 9:7(무효 9, 동등성 미검증)·외국 문자 혼입 0/25 (A.17 A/B). 관련성(5:14)은 열세로 관리 항목
- 교체 전 루프 스모크 통과 (A.18) — NPC kanana + Core gemma4:12b 조합 1회차 완주·크래시 0·폴백 0/16
반영 내역: NPC 모델 설정 한 줄(kanana1.5:8b-q4km, provider ollama 유지, kanana는 thinking 미지원이라 think 설정 불필요). 실서버 검증(별도 포트·테스트 DB): health npc ollama:kanana1.5:8b-q4km, 발화 실콜 정상(“준 → 몰라. 아직.”).
롤백 절차 (어댑터 패턴 보장): 프로덕션 코드에 모델명 하드코딩 없음 확인(설정 기본값뿐) — NPC 모델 설정 한 줄 복원(exaone3.5:7.8b)이 롤백의 전부다. exaone3.5:7.8b는 이 목적으로 로컬 유지한다(연구·개발 용도는 라이선스 내). 평가용 임시 모델(exaone3.5:2.4b·midm2.0:mini-q4km)과 GGUF 원본은 삭제(약 9GB 회수).
운영 관찰 항목(교체 후): ① 사실 질문 회피(스모크에서는 0/21) ② 질문 관련성 ③ 단답 경향(스모크 평균 11.4자) — 테스터 플레이에서 추적한다.
5.4 E7 자체의 성공 조건
다음 문장 중 하나를 실측으로 쓸 수 있으면 성공이다.
- ”
<M>이 기준선에 비열등하며, Peak {A}→{B} GiB, p95 {C}→{D} ms” - “비열등 후보가 없다. 가장 가까운
<M>은 역할 {r}의 지표 {k}에서 {v}로 미달” - “thinking ON은 {모델들}에서 {지표}를 {값}만큼 바꾸고, {다른 모델}에서는 바꾸지 않는다”
2번도 발표에 쓴다.
6. Figures
그림 계획과 작성 상태. 아래 F1~F7은 설계 목록이며 이 원문 위치에는 작성된 그림이 없다(각 항목 미작성). 연구 사이트의 읽기층에서 제공하는 그래프는 별도 공개 집계 시각화다. raw 실측과 effective 유도값은 범례로 구분하고 미실행 N=0·누락·비해당을 같은 0으로 그리지 않는다.
| # | 그림 | 축 |
|---|---|---|
| F1 | 조건 매트릭스 | 5모델 × {N, T}. 미실행·불가 셀을 빈칸으로 그대로 노출 |
| F2 | 역할 × 조건 히트맵 | 행 = 9셀, 열 = 4역할. 셀 = Hard 게이트 통과율 |
| F3 | Pareto — 통과 역할 수 × Peak VRAM | 기준선에 수평선. 선 위·왼쪽에 점이 있으면 그것이 후보 |
| F4 | Pareto — 통과 역할 수 × p95 지연 | Gemini는 페이싱 포함 실효값으로 별도 표기 |
| F5 | Thinking paired delta | x = 모델, y = (T − N). 지표별 막대 |
| F6 | Thinking 토큰 × 품질 delta | thinking을 많이 쓴 만큼 값을 했는가 |
| F7 | 실패 분류 | 위반 종류별 누적. 어느 모델이 어떤 방식으로 실패하는가 |
F3·F4의 세로축은 Hard 게이트를 전부 통과한 역할 수(0~4) 라는 서수값이다. §3.5가 금지한 가중합 종합 점수를 쓰지 않는다.
전부 실측값만. Stage 1(smoke) 값을 Stage 2(formal) 그림에 섞지 않는다. 데이터가 없으면 그리지 않고 “N=0”이라고 쓴다.
7. 알려진 제약
| 제약 | 처리 |
|---|---|
기준선 gemma3:12b에 thinking이 없다 |
비대칭 표로 그대로 표기. RQ-C2는 나머지 4모델 안에서만 답한다 |
해소 (2026-09-13 Stage 0). 기본값 ON — 콜당 thinking 774토큰. 현행 운영은 gemini-3-flash-preview-T 셀이다 |
|
manager_paw 미측정 |
결정적 채점 불가. 후보 축소 후 별도 라운드 |
| LLM-judge 부재 | 자유 답변의 의미 정확도는 1차 범위 밖. 통제 문항으로만 의미 축을 본다 |
| n=3 | 표본이 작다. 이진 판정만 하고 통계적 유의성은 주장하지 않는다 |
| Gemini는 Peak VRAM이 없다 | 로컬과 같은 축에 놓지 않는다. F3에서 별도 표기 |
| 프로브 ≠ 실제 루프 | Stage 4 통과 전에는 “대체 가능”이라고 쓰지 않는다 |
8. E6 — NPC Slot Descent (Phase 2, 미착수)
‘미착수’는 최초 설계 시점의 상태다. 2026-09-14 실행 결과는 A.12~A.18에 기록한다.
상세 설계는 REMAKE_DAY_AI_Agent_Evaluation_PART1_부록_E6_Model_Descent_v0.1에 있다. 이 문서는 E7과의 관계와 선행 조건만 기록한다.
8.1 두 축으로 갈라진다
방향 전환(예산 최적화 우선)을 반영하면 E6는 두 질문으로 나뉜다.
| 축 | 질문 | family 제약 |
|---|---|---|
| 선택 축 | 예산 안에서 7세 역할을 가장 잘하는 NPC는? | 없음 — 섞는다 |
| 인과 축 | 몇 B까지 7세인가? 어느 항목이 먼저 무너지나? | qwen3.5 안에서 크기만 (E6 원형) |
선택 축이 실무 결론을 내고, 인과 축이 발표용 그림(E6 Chart 7)을 만든다. 둘은 충돌하지 않는다.
8.2 착수 전 해결할 것 — 전부 해소 (2026-09-14, A.12)
E6 문서 작성 이후 실측으로 드러난 문제들이었다. scripts/run_model_descent.py 구현과 함께 해소했다.
| # | 문제 | 해소 |
|---|---|---|
| 1 | qwen3.5:2b-q4_K_M 재pull로 9b/4b/2b Q4_K_M 통일. 0.8b는 허브에 Q4 태그가 없어 Q8_0 편차 기록(양자화가 달라 크기 효과만 분리할 수 없으며 영향 방향은 미검증) |
|
| 2 | 러너가 capabilities 조회 후 thinking 모델에 ThinkingOllamaLLM(think=False) 강제 + thinking 문자 누계 0 검증 |
|
| 3 | A.12에서 n=5 재측정 (judge 3표 규칙 — 2026-09-06 행과 직접 비교 금지) | |
| 4 | judge 3표 다수결 + 전원일치율(D3 ≥90%) 구현 — 전 셀 0.96~1.00 통과 |
부록 A — 실측 이력
A.1 2026-09-09 · Core advisor_answer 폴백 붕괴와 회복
원문: docs/review-verification/2026-09-09-connected-implementation/real-model-artifacts/probes-*.json (10회분, 전부 gemma3:12b)
같은 12문항 corpus를 하루 동안 열 번 돌린 기록이다.
| 시각 (UTC) | 폴백 | 위반 | detail 있음 | 근거 있음 |
|---|---|---|---|---|
| 03:58 | 12/12 | 36 | 0 | 0 |
| 04:17 | 0/12 | 0 | 11 | 10 |
| 05:41 | 0/12 | 0 | 11 | 10 |
| 05:54 | 0/12 | 2 | 11 | 10 |
| 06:10 | 0/12 | 0 | 11 | 10 |
| 06:28 | 1/12 | 3 | 11 | 10 |
| 06:38 | 0/12 | 0 | 11 | 10 |
| 06:47 | 1/12 | 3 | 11 | 10 |
| 07:19 | 0/12 | 1 | 11 | 10 |
| 07:35 | 0/12 | 0 | 11 | 10 |
해석. 12/12 폴백은 첫 판 한 번뿐이었다. 위반은 전부 non_verbatim_evidence였고, 19분 뒤 근거 인용 계약을 고치자 같은 모델이 0/12로 돌아왔다. 당시 보고서도 원인을 “동일 prompt에 이전 위반 이유를 넣지 않아 재생성이 같은 누락을 반복하는 양상” 으로 지목했고, 현재 코드에는 그 수정(retry_feedback=True)이 intervention_interactor.py:267 한 곳에만 들어가 있다.
Core를 Gemini로 전환한 것은 08:05~08:09이다. 04:17 이후 9회 중 7회는 폴백 0/12였고 06:28·06:47은 각각 1/12였다. 마지막 07:35는 0/12였으며 연속 무실패 4시간으로 요약할 수 없다.
미해결로 남은 것은 RM1이다 — 질문 명제의 극성과 인용 사건의 극성을 혼동하는 경로(PC3/PC4 쌍). 설계 문서에 “adapter 전환 성공은 기존 Ollama RM1 의미 오판이 해결됐다는 증거가 아니다” 라고 적혀 있고, Gemini 전환 후 RM1이 닫혔다는 기록은 없다. E7 §3.3이 이 축을 다시 잰다.
A.2 2026-09-13 · thinking 기본값 실측
같은 프롬프트·같은 스키마, /api/chat 직접 호출.
| 모델 | think 기본값 | think: false |
|---|---|---|
qwen3.5:4b |
29,558 ms · thinking 11,716자 | 491 ms |
qwen3.5:0.8b |
16,048 ms · content 빈 문자열 → JSON 파싱 실패 | 393 ms |
해석. 4B의 기본 설정 지연은 think:false보다 약 60배였다(29,558/491ms). 0.8B는 기본 설정에서 content가 비어 실패했다. 이 성공/실패 차이를 모델 능력이나 하네스 설정 하나의 원인으로 확정하지 않는다. E7 §4.2와 E6 선행 조건 2가 여기서 나왔다.
A.3 2026-09-13 · gemma3:12b Core 3역할 지연
RTX 5060 Ti, 실제 시나리오 데이터, 각 3회. 지연은 run_with_harness 전체 왕복이다.
| 역할 | 성공 | 폴백 | 중앙값 | 최대 | 입력 |
|---|---|---|---|---|---|
planner |
3/3 | 0 | 12,241 ms | 15,413 ms | 2,044자 |
manager_check |
3/3 | 0 | 964 ms | 1,119 ms | 523자 |
evaluator_verdict |
3/3 | 0 | 999 ms | 1,082 ms | 1,088자 |
해석. planner 폴백 0/3이다. 2026-09-08 review.md가 기록한 “Planner 81/81 전량 폴백”은 재현되지 않는다. 당시 원인은 어휘 사전 불일치('혼자 있다' 39건 · '소등' 18건 — 사전에는 "혼자 있는다")였고, 현재는 planner_output()이 행동 칸을 Literal[tuple(action_vocab)] enum으로 제약해 이번 enum 설정의 planner 3회에서는 재현되지 않았다.
기록에 남은 Gemini 수치는 jekyll.md 기준 6문항 5.08~10.42초다. 여기에 프로세스 전역 10 RPM 페이싱이 더해진다.
A.4 2026-09-13 · 후보 인벤토리
Ollama 레지스트리 매니페스트 직접 조회. 최신 세대(2026-09 기준 1주 이내 갱신) 중 디스크 크기로 사전 선별한 목록(상주량 아님):
qwen3.5 0.8b 0.96 · 2b 2.55 · 4b 3.16 · 9b 6.14 GiB (27b 16.22 · 35b 22.23 — 예산 초과)
gemma4 e2b 6.67 · 12b 7.04 · e4b 8.95 GiB (26b 17.33 · 31b 18.50 — 예산 초과)
공식 라이브러리에 없는 것으로 확인된 한국어 특화 모델: kanana · midm · a.x · hyperclovax-seed · llama-dna · eeve · bllossom · ko-gemma. EXAONE도 exaone3.5·exaone-deep까지이며 4.0은 없다. hf.co/<repo>-GGUF 경로는 가능하나 양자화·템플릿이 제각각이라 같은 조건 비교가 깨진다 — 넣으려면 별도 축으로 분리하고 “공식 라이브러리 밖”을 표기한다.
구세대로 제외: qwen3 · qwen2.5 · phi4 · olmo2 · glm4 · deepseek-r1 · aya-expanse · mistral-nemo · cogito(v1 Preview, 1년 전).
A.5 2026-09-13 · E7 Stage 0 — 프로토콜 호환
원문: docs/review-verification/2026-09-13-core-selection/stage0-protocol-20260913T114748Z.json
프로브: evaluator_verdict 실프롬프트 1콜, temperature 0.0. 정답 주장 “[시나리오 정답 주장 — 비공개]” × 후보 4개.
사전등록 기대: 후보 2(“[같은 사실의 다른 표현 — 비공개]”)가 정답과 같은 일을 가리키므로 confirmed 또는 partial.
| 모델 | think | ok | verdict | 기대일치 | ms | thinking | VRAM GiB |
|---|---|---|---|---|---|---|---|
gemma3:12b |
default | O | confirmed | O | 995 | 0 | 7.49 |
gemma4:12b |
default | O | confirmed | O | 141,293 | 3,678 | 7.51 |
gemma4:12b |
off | O | none | X | 1,656 | 0 | 7.51 |
gemma4:12b |
on | O | confirmed | O | 27,090 | 3,678 | 7.51 |
gemma4:e4b |
default | O | partial | O | 13,650 | 1,838 | 3.06 |
gemma4:e4b |
off | O | none | X | 663 | 0 | 3.06 |
gemma4:e4b |
on | O | none | X | 5,554 | 1,724 | 3.06 |
qwen3.5:9b |
default | X | — | X | 153,573 | 13,932 | 5.25 |
qwen3.5:9b |
off | O | none | X | 602 | 0 | 5.25 |
qwen3.5:9b |
on | X | — | X | 151,176 | 13,932 | 5.25 |
gemini-3-flash-preview |
default | O | confirmed | O | 5,601 | 774 | — |
gemini-3-flash-preview |
off | O | confirmed | O | 1,677 | 0 | — |
gemini-3-flash-preview |
on | O | confirmed | O | 9,291 | 774 | — |
확정된 사실
게이트 C9 통과. off 셀 네 개 모두 thinking 사용량 0이다. 설정이 provider에 실제로 전달되며, 셀 라벨을 신뢰할 수 있다.
이번 Stage 0의 thinking 지원 4종은 provider 기본 설정에서 모두 thinking을 사용했다. gemma3는 thinking 미지원 기준선이다. gemma4:12b 3,678 · gemma4:e4b 1,838 · qwen3.5:9b 13,932 · gemini-3-flash-preview 774 (thinking 사용량). 현행 운영 Core는 -T 셀에서 돌고 있었다. 어댑터에 제어를 주입하지 않았다면 로컬 신형 셋도 전부 -T로 측정됐을 것이다.
qwen3.5:9b-T 탈락. default·on 두 조건에서 JSON 생성에 실패했다(151~153초, thinking 13,932). TFR = 1.0으로 게이트 C6(≤0.1) 미달이다. -N은 602ms에 정상 동작하므로 생존한다.
gemma4:e4b의 디스크-VRAM 괴리. 디스크 8.95 GiB, 상주 3.06 GiB. §1의 디스크 기준 예산 추정이 이 모델에서만 빗나갔다. 이후 예산 판정은 실측 VRAM으로만 한다.
결론이 아닌 관측
thinking을 끈 로컬 신형 셋이 모두 기대 판정을 놓쳤고(none), 기준선 gemma3:12b는 thinking 없이 995ms에 맞췄다. n=1이며 Stage 0은 프로토콜 호환을 보는 단계다. 품질 판정으로 인용하지 않는다. Stage 1에서 4역할로 확인한다.
기타
- Gemini 응답에
thought_signaturenon-text part 경고가 발생한다. 파싱은 성공한다 gemma4:12bdefault141초는 콜드 스타트를 포함한 값으로 보인다. 같은 조건의on은 27초다
Stage 0 통과 셀
| 셀 | 상태 |
|---|---|
gemma3:12b-N |
통과 (기준선) |
gemma4:12b-N · gemma4:12b-T |
통과 |
gemma4:e4b-N · gemma4:e4b-T |
통과 |
qwen3.5:9b-N |
통과 |
qwen3.5:9b-T |
탈락 — JSON 생성 실패 |
gemini-3-flash-preview-N · -T |
통과 |
9셀 중 8셀 생존.
A.6 2026-09-13 · E7 Stage 1 — smoke · advisor_answer
원문: docs/review-verification/2026-09-13-core-selection/stage1-smoke-advisor-20260913T124645Z.json
코퍼스: 2026-09-09 보존분 그대로 — 공개 관찰 11건 · 테스터1 질문 12개 · 사전등록 통제 10개.
8셀 × 22문항 = 176개 역할-문항 실행, n=1(provider attempts와 다른 집계). Gemini 지연은 raw(페이싱 미포함)다.
| model | think | 폴백 | EGR | PCA | PC근거 | 극성쌍 | p50 ms | p95 ms | thinking | VRAM GiB |
|---|---|---|---|---|---|---|---|---|---|---|
gemma3:12b (기준선) |
— | 0.00 | 0.83 | 0.70 | 1.00 | X | 3,394 | 4,207 | 0 | 7.49 |
gemma4:12b |
off | 0.00 | 0.83 | 0.90 | 1.00 | O | 2,918 | 4,231 | 0 | 7.51 |
gemma4:12b |
on | 0.32 | 0.83 | 0.70 | 1.00 | X | 40,234 | 194,589 | 132,487 | 7.51 |
gemma4:e4b |
off | 0.00 | 0.83 | 0.70 | 1.00 | X | 1,544 | 2,502 | 0 | 3.06 |
gemma4:e4b |
on | 0.00 | 0.83 | 0.80 | 1.00 | X | 7,894 | 11,546 | 46,389 | 3.06 |
qwen3.5:9b |
off | 0.09 | 0.83 | 0.50 | 1.00 | X | 3,713 | 6,871 | 0 | 5.25 |
gemini-3-flash-preview |
off | 0.00 | 0.83 | 1.00 | 0.90 | O | 1,720 | 2,138 | 0 | — |
gemini-3-flash-preview |
on | 0.00 | 0.83 | 1.00 | 0.90 | O | 9,138 | 13,443 | 29,446 | — |
통제별 판정
| 셀 | PC1 | PC2 | PC3 | PC4 | PC5 | PC6 | PC7 | PC8 | SPC1 | SPC2 |
|---|---|---|---|---|---|---|---|---|---|---|
gemma3:12b |
O | O | X | O | O | O | O | X | O | X |
gemma4:12b-N |
O | O | O | O | O | O | X | O | O | O |
gemma4:12b-T |
O | O | O | O | X | O | X | O | O | X |
gemma4:e4b-N |
O | O | X | O | O | O | X | O | O | X |
gemma4:e4b-T |
O | O | X | O | O | O | O | O | O | X |
qwen3.5:9b-N |
O | O | X | X | X | O | O | O | X | X |
gemini-N |
O | O | O | O | O | O | O | O | O | O |
gemini-T |
O | O | O | O | O | O | O | O | O | O |
확정된 사실
RQ-C2 — 이번 advisor 스모크에서 ON 셀은 모두 C11 지연 게이트를 넘었다. 게이트 C11(p95 ≤ 5초)에 -T 셀이 전부 탈락했고, 생존한 넷은 예외 없이 OFF다.
| 셀 | raw p95 ms | C11 raw 참고 판정 |
|---|---|---|
gemini-N |
2,138 | raw 통과; effective 탈락, Stage 2 분리 확인 |
gemma4:e4b-N |
2,502 | 통과 |
gemma3:12b |
4,207 | 통과 |
gemma4:12b-N |
4,231 | 통과 |
qwen3.5:9b-N |
6,871 | 탈락 |
gemma4:e4b-T |
11,546 | 탈락 |
gemini-T |
13,443 | 탈락 |
gemma4:12b-T |
194,589 | 탈락 (38배) |
품질 변화는 모델별로 달랐다. gemini는 ON/OFF의 PCA가 1.00으로 같은데 p50이 5.3배다. gemma4:12b는 ON에서 폴백 0.00→0.32, PCA 0.90→0.70, 극성쌍 O→X로 오히려 무너진다. 반면 gemma4:e4b는 ON에서 PCA가 0.70→0.80으로 올랐다 — 효과가 모델마다 반대 방향이며, 일률적으로 말할 수 없다.
RM1이 여전히 열려 있고, 기준선이 그 증거다. gemma3:12b는 PC3·SPC2·PC8을 놓쳤다. 2026-09-09에 미해결로 남은 극성 혼동이 오늘 그대로 재현된다(§A.1). Gemini 전환 후에도 확인된 적이 없었다.
RM1을 넘는 셀이 존재한다. gemini(ON·OFF 모두)와 gemma4:12b-N이 극성쌍 둘을 모두 통과했다. 로컬에서 통과한 것은 gemma4:12b-N 하나뿐이다.
PC근거는 전 로컬 셀 1.00이다. 이 통제에서는 기대 근거 ID 적중보다 명제 방향 판정에서 오답이 관측됐다. 검색 문제 전체를 배제하는 결과는 아니다.
qwen3.5:9b는 완전 탈락. -T는 Stage 0(JSON 생성 실패), -N은 여기서 PCA 0.50 최하위 + C11 미달.
현행 운영(gemini-T)은 같은 품질의 더 느린 셀이다. gemini-N과 PCA·PC근거·극성쌍이 모두 동일한데 p50이 5.3배다. gemini_llm.py에 thinking_config(thinking_budget=0)을 넣으면 이번 통제 지표를 유지하면서 raw 지연이 짧았으므로 운영 품질·지연의 후속 확인 대상이다.
측정의 한계
Gemini 지연은 페이싱을 포함하지 않은 raw 값이다. 러너의 ThinkingGeminiLLM은 _pace_request를 호출하지 않는다. 프로덕션은 프로세스 전역 10 RPM이므로 연속 호출 간격이 6초로 강제된다 — 당시 설명의 후속 요청 약 7.7초는 페이싱 대기 6초에 raw 약 1.7초를 더한 예상치였다. A.7의 max(raw,6000)=6초는 정상상태 처리 간격의 다른 근사이며 두 값을 실제 사용자 대기 실측으로 합치지 않는다. 첫 호출·요청 시작/완료 기준·공유 대기열을 포함한 실제 3회 벽시계는 미측정이다. A.7의 유도 effective 기준에서는 C11 탈락이다. Stage 2에서 raw와 effective를 분리해 적는다.
n=1이다. 이 표의 숫자로 순위를 확정하지 않는다. Stage 2(n=3, 4역할)가 공식 수치다.
Stage 1 결과 — Stage 2 진출 셀
실제 진출은 C11 raw 참고값과 폴백 ≤0.5를 사용했다. 최초 계획의 SVR<0.9 차단은 이 표에 집계가 없어 충족을 재확인하지 못한다. Gemini는 effective C11 실패에도 온라인 비교를 위한 예외로 Stage 2에 포함했다. C11 승격은 2026-09-13 Stage 1 변경 이력에 기록됐다:
| 셀 | 근거 |
|---|---|
gemma3:12b |
기준선. 비교 대상이므로 필수 |
gemma4:12b-N |
PCA 0.90, 극성쌍 통과 — 로컬 최고 |
gemma4:e4b-N |
기준선과 advisor PCA 동률, raw p95 40% 빠름, VRAM 59% 작음 |
gemini-3-flash-preview-N |
PCA 1.00, 극성쌍 통과, p95 최저 |
8셀 → 4셀. 탈락: -T 셀 3개(C11), qwen3.5:9b-N(PCA·C11).
A.7 2026-09-13 · E7 Stage 2 — formal · 4역할 × n=3
원문: docs/review-verification/2026-09-13-core-selection/stage2-formal-20260913T132023Z.json
4셀 × (advisor 22 + planner 3 + manager 3 + evaluator 3)문항 × 3반복 = 372개 역할-문항 실행이다. 360건이 LLM 요청을 시작했고 재생성 9회로 provider attempts는 369회다. meta 12건은 attempts=0이다. 최종 폴백은 0/372다. 공식 요약표는 최초 시도 SVR·TFR·manager 의미 정확도 전체를 담지 않으며 재생성 후 성공을 첫 시도 성공으로 세지 않는다.
| 셀 | 폴백 | PCA | PC근거 | 극성쌍 | adv p50 | adv p95 | LAR | pln p95 | eval 기대 | evl p50 | VRAM GiB |
|---|---|---|---|---|---|---|---|---|---|---|---|
gemma3:12b (기준선) |
0.00 | 0.70 | 1.00 | X | 3,391 | 4,473 | 1.0 | 12,709 | 1.00 | 976 | 7.49 |
gemma4:12b-N |
0.00 | 0.90 | 1.00 | O | 2,907 | 4,215 | 1.0 | 12,215 | 0.67 | 1,635 | 7.51 |
gemma4:e4b-N |
0.00 | 0.70 | 1.00 | X | 1,532 | 2,491 | 1.0 | 6,234 | 0.67 | 519 | 3.06 |
gemini-3-flash-N |
0.00 | 1.00 | 0.90 | O | 1,734 | 2,238 | 1.0 | 6,058 | 1.00 | 1,559 | — |
통제별 통과율 (n=3)
| 셀 | PC1 | PC2 | PC3 | PC4 | PC5 | PC6 | PC7 | PC8 | SPC1 | SPC2 |
|---|---|---|---|---|---|---|---|---|---|---|
gemma3:12b |
1.00 | 1.00 | 0.00 | 1.00 | 1.00 | 1.00 | 1.00 | 0.00 | 1.00 | 0.00 |
gemma4:12b-N |
1.00 | 1.00 | 1.00 | 1.00 | 1.00 | 1.00 | 0.00 | 1.00 | 1.00 | 1.00 |
gemma4:e4b-N |
1.00 | 1.00 | 0.00 | 1.00 | 1.00 | 1.00 | 0.00 | 1.00 | 1.00 | 0.00 |
gemini-3-flash-N |
1.00 | 1.00 | 1.00 | 1.00 | 1.00 | 1.00 | 1.00 | 1.00 | 1.00 | 1.00 |
통과율이 전부 0.00 또는 1.00이다 — n=3 반복에서 흔들리지 않는다. 이번 통제 문항의 n=3에서 판정이 같았다는 관측이며 우연 배제나 일반적 결정론의 근거는 아니다.
게이트 판정 (effective 기준)
| 셀 | C11 raw | C11 eff | C11 | C12 eff | C12 | C13 p50×10 유도 ms | C13 |
|---|---|---|---|---|---|---|---|
gemma3:12b |
4,473 | 4,473 | O | 12,709 | O | 9,760 | O |
gemma4:12b-N |
4,215 | 4,215 | O | 12,215 | O | 16,350 | O |
gemma4:e4b-N |
2,491 | 2,491 | O | 6,234 | O | 5,190 | O |
gemini-3-flash-N |
2,238 | 6,000 | X | 6,058 | O | 60,000 | X |
확정된 사실
Gemini는 모델이 아니라 우리 페이싱 설정 때문에 탈락했다. advisor raw p95 2,238ms가 4셀 중 가장 짧고 PCA 1.00·극성쌍 통과였으나 PC근거는 0.90이며 다른 역할의 지연·품질은 표와 같다. gemini_llm.py의 프로세스 전역 10 RPM이 콜 간격을 6초로 강제한다. 밤 채점은 정답 명제 10개 × 6초 = 60초로 C13(30초)의 2배다. 페이싱 값을 바꿀 수 있다면 판정이 뒤집힌다 — 쿼터 제약과 함께 별도로 판단할 사안이다.
gemma4:12b-N의 측정한 극성쌍에서 RM1이 재현되지 않았다. 기준선이 못 넘던 PC3·PC8·SPC2를 n=3 전부 통과했다. 2026-09-09 이후 열려 있던 극성 혼동이 측정한 PC3·PC4/SPC1·SPC2에서 재현되지 않았다(PC8은 별도 unknown 통제). 지연·VRAM은 기준선과 사실상 동일하다(4,215 vs 4,473ms / 7.51 vs 7.49 GiB) — 비슷한 상주량·advisor 지연에서 해당 통제 오류가 재현되지 않았다.
그러나 §5.3 기준으로 비열등은 아니다. 기준선이 통과한 evaluator_verdict를 떨어뜨렸다(1.00 → 0.67). 역할 단위 비교 규칙상 합이 같거나 높아도 비열등이 아니다. 아래 한계를 함께 읽어야 한다.
gemma4:e4b-N은 효율 축의 답이다. advisor PCA 0.70·극성쌍 X는 기준선과 같으나 evaluator 기대 일치는 0.67로 기준선 1.00보다 낮다. 한편 adv p95 44%·pln p95 51%·밤 채점 47% 빠르고 VRAM은 41%(3.06 vs 7.49 GiB)다. 이 효율 차이를 전 역할 품질 동등성으로 해석하지 않는다.
전 셀 공통 — 하네스는 안정적이다. 372개 역할-문항 실행에서 폴백 0.00, 오류 0이다. planner LAR은 4셀 모두 1.0으로, 2026-09-08의 “Planner 81/81 전량 폴백”은 현재 스키마 enum 조건의 n=36에서는 재현되지 않았다. manager_check는 이 입력에서 구조화 출력을 완료했으나 의미 정확도는 별도 측정하지 않았다.
evaluator_verdict 실패의 정체 — 이 통제는 약하다
gemma4 두 모델이 놓친 것은 같은 케이스 하나다(3케이스 × 3반복 중 3회 = 그 케이스 전부).
정답 주장 "[시나리오 정답 주장 — 비공개]"
후보 "[같은 사실의 다른 표현 — 비공개]"
기대 confirmed 또는 partial
받음 none ← gemma4:12b · gemma4:e4b 둘 다
이 기대값은 2026-09-09 코퍼스가 아니라 이 문서의 저자가 직접 쓴 것이다. A.7은 저자 작성 3건이고 A.8은 실제 제출문 4건에서 파생한 14케이스다. 기대 판정은 실행 전에 고정했으나 단일 저자의 판단이며 독립 검수·대표성 검증은 없다. 앞선 통제도 사전 고정과 독립 정답 검증을 동일시하지 않는다. 실제로 후보는 정답 주장이 말하지 않는 결과를 덧붙이므로 none 판정에도 근거가 있다.
다만 EVALUATOR_VERDICT_SYSTEM 프롬프트가 정답 주장과 후보의 핵심 표현을 명시적 동의어로 제시하고 있으므로, 최소 partial은 나와야 한다는 해석도 성립한다.
이 통제에서는 gemma4 두 모델이 기준선보다 엄격한 방향으로 판정했다. 이 프로젝트는 2026-09-06에 “Evaluator verdict 분포가 극단적으로 엄격해 none으로 치우친다” 는 문제로 3.1% 합격률을 겪은 이력이 있다. 더 엄격한 채점기는 이 게임에서 알려진 위험이다.
조치: evaluator_verdict 통제 3건을 실제 플레이 로그에서 뽑은 제출문으로 교체한다. DB에 41.7점대 8건·30점대 12건이 남아 있다(review.md). 자작 케이스로 채점 역할의 우열을 판정하지 않는다.
측정의 한계
| 한계 | 내용 |
|---|---|
evaluator_verdict 통제가 자작 |
위 참조. 실플레이 제출문으로 교체 전에는 이 역할의 우열을 확정하지 않는다 |
manager_paw 미측정 |
창작 출력이라 결정적 채점 불가. 여전히 범위 밖 |
| Gemini effective는 유도값 | max(raw, 6000ms). 실제 페이싱을 태운 측정이 아니다 |
| n=3 | 통과율이 전부 0/1로 갈려 안정적이지만, 통계적 유의성은 주장하지 않는다 |
| Stage 3·4 미실행 | 동시성·루프 완주 미확인. “대체 가능”이라고 쓸 수 없다 |
A.8 2026-09-14 · E7 Stage 2 보강 — evaluator_verdict 실플레이 통제 재평가
원문: docs/review-verification/2026-09-13-core-selection/stage2-formal-20260914T021635Z.json
A.7의 조치(“자작 케이스로 채점 역할의 우열을 판정하지 않는다”)를 이행했다. 자작 통제 3건을 실제 플레이 제출문 4건에서 파생한 14케이스으로 교체하고 evaluator_verdict만 재측정했다 (4셀 × 14케이스 × n=3 = 168콜, --roles evaluator). 다른 3역할은 프로브가 바뀌지 않아 A.7 수치가 그대로 유효하다.
기대 판정은 실험 전에 고정했다 — docs/review-verification/2026-09-14-eval-cases/CONFIRMED-eval-cases.md (confirmed 4 · partial 5 · none 5). 재료는 DB 제출문 4건: 탭 0 사람 상한 2건(41.7), 테스터3 3회차(76.7, 1,028자 덩어리 칸), 테스터4 2회차(66.5, 사람 통과 판). 당시 프로덕션 verdict는 자기모순이 실측되어(동일 입력 재제출에서 confirmed→none 뒤집힘 등) 정답으로 쓰지 않았다.
결과
| 셀 | eval 기대 (14×3) | flip | evl p50 | evl p95 | 폴백 | 오류 | C13 |
|---|---|---|---|---|---|---|---|
gemma3:12b (기준선) |
0.36 (15/42) | 0/14 | 997 | 1,088 | 0.00 | 0 | O |
gemma4:12b-N |
0.57 (24/42) | 0/14 | 2,179 | 2,735 | 0.00 | 0 | O |
gemma4:e4b-N |
0.48 (20/42) | 1/14 | 543 | 727 | 0.00 | 0 | O |
gemini-3-flash-N |
0.50 (21/42) | 0/14 | 1,502 | 1,779 | 0.00 | 0 | X (페이싱 유도값) |
케이스별 판정 (n=3 · ⚡=rep 간 뒤집힘)
| 케이스 | 기대 | gemma3:12b | gemma4:12b-N | gemma4:e4b-N | gemini-3-flash-N |
|---|---|---|---|---|---|
| A1 “채연이 아프다”←”열이 나지만 숨긴다” | confirmed | partial ✗ | O | O | O |
| A2 이송될까 봐 숨겼다 | confirmed | O | O | O | O |
| A3 옆 구역 (언급 없음) | none | O | O | O ⚡(1/3 conf) | O |
| A4 정체 ← 직접 동의 표현 | confirmed | O | O | O | O |
| A5 구역 폐쇄 (언급 없음) | none | partial ✗ | O | O | O |
| A6 이송 규정 ← 절반 언급 | partial | conf ✗ | conf ✗ | conf ✗ | conf ✗ |
| B1 검진 회피 ← 절반 언급 | partial | conf ✗ | conf ✗ | conf ✗ | conf ✗ |
| B2 정체 (관찰 나열뿐) | none | partial ✗ | O | O | O |
| C1 감염 확산 ← 1,028자 덩어리 | none | O | O | conf ✗ | partial ✗ |
| C2 정체 ← 덩어리 속 동의 표현 | partial | none ✗ | conf ✗ | conf ✗ | conf ✗ |
| D1 구역 폐쇄 ← 460자 덩어리 | none | partial ✗ | conf ✗ | conf ✗ | conf ✗ |
| D2 이송 규정 ← “옮겨지면 안좋은 일” | partial | conf ✗ | conf ✗ | conf ✗ | conf ✗ |
| D3 정체 ← 간접 표현 | partial | none ✗ | conf ✗ | none ✗ | conf ✗ |
| D4 “채연이 아프다”←”병에 걸린 것 같다” | confirmed | O | O | O | O |
확정된 사실
A.7의 evaluator_verdict 우열은 실플레이 통제에서 역전됐다. 자작 통제의 1.00(기준선) 대 0.67(gemma4)이, 실플레이 통제에서 0.36 대 0.57이 됐다. A.7이 “비열등 아님”의 유일한 근거로 삼은 “기준선이 통과한 역할을 떨어뜨렸다”는 전제가 이 측정에서는 성립하지 않는다 — 세 후보 모두 기준선 0.36보다 높았으며 gemma4:12b-N이 0.57로 가장 높았다.
기준선의 운영 결함이 통제로 재현됐다. A1은 프롬프트 자신의 예시(정답 “A가 아프다” ← 후보 “A가 열이 나는 것 같다” → confirmed, rule 1)와 동형인데 기준선만 3/3 전부 partial을 냈다. 같은 오판이 실제 플레이 로그(a316a730 등)에도 남아 있다. 또한 기준선은 A5·B2·D1에서 근거 없는 partial을 낸다 — 관대한 방향의 오답이며, “gemma4가 엄격해서 위험하다”는 A.7의 우려 방향과 반대다.
partial 경계는 4셀 공통 실패다. partial 기대 5건(A6·B1·C2·D2·D3)에서 4셀 × 15회 중 partial이 나온 적이 한 번도 없다(confirmed 또는 none으로 갈림). 이번 5개 partial 통제의 60회 판정에서 partial은 0회였다. 프롬프트 정의·기대 판정 경계·모델 해석 중 원인은 분리하지 못했다 — review.md 5순위(판정 프롬프트 정량화)와 연결된다.
덩어리 칸은 채점기 공통 취약점이다. D1(460자 덩어리 ← 폐쇄 명제)은 4셀 전부 오판했다. 테스터4의 실제 통과(66.5)를 만든 바로 그 매칭이 통제 조건에서 재현된다. C1(1,028자)은 2셀이 버티고 2셀이 넘어졌다. 이번 네 모델에서 D1 오판이 남았고 C1은 두 모델만 기대와 일치했다. 긴 제출문을 단위 주장으로 분리하는 전처리와 채점기 변경을 각각 검증할 필요가 있다. 전처리를 유일한 해결책으로 확정하지 않는다.
56케이스-셀 중 55개에서 n=3 반복 판정이 같았다. temperature 0.0에서 rep 간 뒤집힘은 56케이스-셀 중 1건(gemma4:e4b A3). 프로덕션에서 관측된 Gemini 뒤집힘(동일 입력 confirmed→none, 테스터3 4·5회차)은 이 n=3에서는 재현되지 않았다.
측정의 한계
| 한계 | 내용 |
|---|---|
| 기대 판정의 저자 | 사전 등록으로 고정했지만 단일 저자 확정이다. 특히 partial 5건은 4셀 전원이 불일치했으므로 기대값 자체의 경계 논쟁 여지를 기록해 둔다 (사전 등록이므로 결과에는 반영하지 않는다) |
| Gemini thinking 잔존 신호 | thought_signature 관련 SDK 경고 42회가 있었다. 경고만으로 thinking 사용 여부를 판정하지 않았다. 해당 응답의 thoughts_token_count·content/thought·SDK 버전 확인이 필요하다 |
| §5.3 재판정은 하지 않는다 | 품질 축은 위와 같이 바뀌었으나 VRAM(7.51 vs 7.49)·evl 지연(2,179 vs 997)의 문자적 비교가 남아 있다. 비열등 선언은 별도 판단 |
| Stage 3·4 미실행 | 변함없다. “대체 가능”이라고 쓸 수 없다 |
A.9 2026-09-14 · E7 Stage 3 — concurrency · 후보 + NPC 동시 상주
상주는 모델을 GPU 메모리에 유지하는 상태, 축출은 다른 모델을 올리려고 내리는 일이다. NPC와 Core를 번갈아 호출한 뒤의 관측이며 동시 추론·장기 다중 사용자 부하 시험은 아니다.
원문: docs/review-verification/2026-09-13-core-selection/stage3-concurrency-20260914T022551Z.json
--stage concurrency를 러너에 구현해 실행했다. 프로토콜: 후보·NPC 외 상주 모델 언로드 → NPC(exaone3.5:7.8b) 먼저 상주(프로덕션 상태 재현) → 후보 로드 → NPC 대화 콜 ↔ Core 콜 교대 3라운드(2라운드째는 가장 무거운 planner 콜), 매 콜 직후 /api/ps 스냅샷으로 상주·부분 오프로드(size_vram < size)를 확인. GPU는 nvidia-smi 실측.
결과
| 후보 | pair peak VRAM | GPU peak (실측) | 교대 구간 축출 | 오프로드 | NPC 콜 ms | Core 콜 ms | 판정 |
|---|---|---|---|---|---|---|---|
gemma4:12b-N |
12.34 GiB | 13,326 / 16,311 MiB (81.7%) | 0 / 6스냅샷 | 0 | 173~219 | 1,905~2,125 (planner 12,174) | 통과 |
gemma4:e4b-N |
7.89 GiB | 9,520 / 16,311 MiB (58.4%) | 0 / 6스냅샷 | 0 | 230~1,007 | 480~625 (planner 5,760) | 통과 |
확정된 사실
두 후보 모두 교대 구간의 6스냅샷에서 NPC와 Core의 동시 상주를 확인했다. 교대 6스냅샷 전부에서 후보와 NPC가 동시 상주했고 부분 CPU 오프로드도 0이다. 재로드를 시사하는 지연 스파이크도 없다 — NPC 콜은 워밍 후 200ms대, Core 콜은 Stage 2 단독 측정과 같은 범위다.
exaone3.5:7.8b 상주 실측은 4.83 GiB다. 이 문서 §2.2의 4.14 GiB보다 0.69 크다(측정 시점의 컨텍스트 할당 차이로 추정). pair peak은 4.83 기준으로 12.34 / 7.89 GiB이며 예산(15.19 GiB) 안이다. GPU 실측 peak 13,326 MiB에는 런타임 오버헤드(~1.1 GiB)가 포함돼 있고, gemma4:12b 셀 기준 전체 여유는 약 2.9 GiB다.
관측 — gemma4:e4b 초기 로드가 NPC를 1회 축출했다. core_warm 스냅샷에서 NPC가 부재했고 GPU 실측도 5,117 → 4,419 MiB로 내려갔다(실제 언로드). 다음 NPC 콜에서 1,007ms에 재로드된 뒤로는 끝까지 공존했다. 스케줄러의 로드 시점 메모리 추정이 원인일 가능성은 있으나 이 측정으로 확인하지 않았다. gemma4:12b(더 큰 모델)에서는 발생하지 않았다. 이번 실행에서 NPC가 1회 재로드됐고 약 1초가 걸렸다. 다른 로드 순서·반복 실행의 영향은 미측정이다. 게이트(교대 구간 무축출)는 두 후보 모두 통과다.
측정의 한계
| 한계 | 내용 |
|---|---|
| 단시간 측정 | 교대 3라운드(셀당 약 1분). 장시간 상주·컨텍스트 누적에 따른 변동은 Stage 4(루프 완주)에서 본다 |
| 임베딩 모델 미포함 | 현행 임베딩은 gemini(원격)라 로컬 상주가 없지만, 폴백 qwen3-local 발동 시 추가 상주가 생긴다. 이 조합은 재지 않았다 |
| 초기 로드 축출은 게이트 밖 | 게이트는 교대 구간만 판정한다. e4b 초기 로드 transient는 위 관측으로만 남긴다 |
A.10 2026-09-14 · E7 Stage 4 — loop · 루프 스모크 1회차 완주
원문: docs/review-verification/2026-09-13-core-selection/stage4-loop-20260914T041032Z.json (정본)
계측 결함이 있던 1차 실행: stage4-loop-20260914T040719Z.json (경위는 아래)
--stage loop를 러너에 구현해 실행했다. HANDOFF 착수 순서대로 기존 run_selfplay.py가 HTTP로 실서버 API 경로를 그대로 타는 방식 임을 확인하고 재사용했다.
프로토콜
- 별도 환경 서버:
scripts/loop_app.py가 프로덕션main.app을 import하고 컴포지션 루트의get_core_llm바인딩만 러너의ThinkingOllamaLLM(think=False)로 교체한다. 프로덕션 엔진 코드·설정 무변경 (§2.3·§4.2). 게이트 C9 검증용/loop-debug(Core 콜 수·thinking 문자 누계)도 래퍼에만 있다 - 별도 포트 · 테스트 DB (테스터 판 DB 무접촉) · 환경변수로 Core provider·모델만 후보로 교체. NPC(
exaone3.5:7.8b)·임베딩(gemini)은 프로덕션 구성 그대로 - 완주 주체: selfplay 성실 페르소나, seed 42, 1판 × 1회차(낮 발화 → 비트 → 밤 제출 → 개입). 플레이어 모델은 상주 NPC(
exaone3.5:7.8b) 재사용 — 제3 모델을 올리면12b셀(pair 12.34 GiB)에서 축출이 나기 때문 - 판정은 §4.1 그대로 — 점수가 아니라 크래시 0 · 폴백 폭주 없음 · 1회차 완주
결과
| 후보 | 완주 | 크래시 | 폴백 | Core debug 시도 누계 | 서버 논리 호출(전체 시도) | thinking | 소요 | 판정 |
|---|---|---|---|---|---|---|---|---|
gemma4:12b-N |
O | 0 | 0 / 17 | 14 | 17 (17) | 0자 | 61.1s | 통과 |
gemma4:e4b-N |
O | 0 | 0 / 17 | 15 | 17 (19) | 0자 | 35.6s | 통과 |
역할별(서버 이벤트 로그 실측, 두 후보 동일 구성): planner 1 · advisor_answer 1 · evaluator_verdict 10 · manager_check 2 · NPC agent 3.
서버 논리 호출은 17건(비NPC 14·NPC 3)이다. Core debug 값은 어댑터 호출 시도 누계여서 e4b의 15는 비NPC 14+advisor 재시도 1이다. 전체 attempts 19는 Core 15+NPC 4이며 폴백 0/17은 최종 논리 출력 기준이다. attempt_id: d88a29e5-…78490(12b) / 8c350278-…43a63(e4b), 둘 다 테스트 DB에만 기록.
확정된 사실
두 후보 모두 Stage 4 게이트를 통과한다. 실서버 경로에서 NPC·Manager와 엮인 1회차를 크래시 0·폴백 0으로 완주했다. 밤 채점(evaluator 10콜)·개입 질문(advisor)·원숭이손 제안 수락·규칙 등록까지 전 구간이 실행됐다.
게이트 C9 검증 — 두 셀 모두 thinking 0자. /loop-debug 누계로 확인했으므로 -N 셀 라벨을 신뢰할 수 있다.
e4b에서 하네스 재시도 2회(advisor 1·NPC agent 1)가 있었고 모두 재생성으로 해소됐다 — 폴백이 아니다(attempts 19 / calls 17). 12b는 재시도 0.
점수(16.0 / 8.8)는 판정 기준이 아니다. 같은 seed라도 NPC 응답·채점이 확률적이라 판마다 변동한다(1차 실행에서는 8.8 / 56.9). 단일 판 점수로 우열을 해석하지 않는다.
계측 결함 경위 (1차 실행)
1차 실행은 게임 완주 후 /attempts/{id}/harness로 폴백을 조회했는데, 이 인스펙터는 다섯 번째 밤 종료 후에만 열린다(AccessDenied). 1회차 스모크에서는 쓸 수 없어 러너가 수집 단계에서 예외를 냈고 gate_pass=false로 잘못 찍혔다 — 당시 서술은 두 판 완주를 보고했으나 여기서 제시한 DB 집계는 12b 판 폴백 0/17뿐이다. 첫 e4b 실행의 종료 상태·폴백을 독립 확인할 근거는 이 요약에 없으며 위 표의 재실행 정본과 구분한다. 수집을 events 테이블 읽기 전용 SELECT로 교체하고 재실행한 것이 정본이다. 잘못 적립된 metrics 2줄은 제거했다.
측정의 한계
| 한계 | 내용 |
|---|---|
| 1판 × 1회차 | 스모크다. 5회차 완주·회차 누적 컨텍스트에서의 거동은 재지 않았다 |
| 성실 페르소나 단일 | 산탄총·침묵 페르소나 경로(빈 서술·주장 8개 채점)는 타지 않았다 |
| 점수 비교 불가 | n=1이고 판정 축도 아니다. 위 명시 |
A.11 2026-09-14 · D1 — Gemini 실제 쿼터 실측 (페이싱 프로브)
원문: docs/review-verification/2026-09-13-core-selection/d1-gemini-rpm-probe-20260914.txt
결정 항목 D1(“쿼터 제약이 실제로 얼마인지 확인”)을 실측했다. 페이싱 없이 초소형 요청을 순차 연속 전송해 429 발생 지점을 찾는 설계 — 프로덕션 서버는 내려간 상태에서 실행(간섭 없음).
결과
40콜 / 55.4초(관측 구간 환산 약 43.3 RPM) · 429 0건. 이후 사용자가 유료 키임을 확인했다. 이 1분 미만의 프로브만으로 정확한 Tier 1·150~300 RPM이나 무료 키의 11번째 요청 제한을 추정할 수 없다. 당시 모델·프로젝트 콘솔 한도 기록은 미확보다.
판정에 주는 의미
gemini_llm.py의 10 RPM은 서버 쿼터가 아니라 우리 클라이언트 설정이고, 당시 무료 티어 기준으로 정한 값이었다. effective = max(raw, 60000/RPM) 산식으로 게이트를 다시 계산하면:
| 게이트 | 요구 조건 | 필요한 최소 RPM | RPM=30일 때 |
|---|---|---|---|
| C11 advisor p95 ≤ 5s | eff ≤ 5,000ms (raw 2,238) | ≥ 12 | 2,238ms — 통과 |
| C12 planner p95 ≤ 15s | 단발 콜, 기존에도 통과 | — | 통과 유지 |
| C13 밤 채점 ≤ 30s | 10콜 × eff ≤ 30s (raw p95 1,779) | ≥ 20 | 10 × 2,000 = 20s — 통과 |
위 합성 산식에서는 RPM ≥20이면 C11·C13 판정이 바뀐다. 단독 프로브와 산식만 보면 30 RPM을 검토할 수 있으나 장시간·동시 요청의 운영 안정성은 미측정이다. 표는 순차 10콜마다 동일 간격을 배정한 근사이며 첫 요청 대기·공유 페이싱의 실제 벽시계와 다를 수 있다.
결정 (2026-09-14, 사용자)
키가 유료인 것은 사용자가 확인해 주었다. 그러나 판단 기준은 무료 티어 한도(10 RPM)로 고정한다 — 유료 쿼터에 기대는 구성을 판정 근거로 삼지 않는다.
- 페이싱은 10 RPM 유지 (설정 변경 없음)
- 위 게이트 재계산 표는 “올렸다면 어떻게 되는가”의 기록으로만 남긴다 — A.7의 Gemini C11·C13 탈락 판정은 그대로 유효하다
- D1은 이 결정으로 해소. 이후 무료 티어 공칭 한도 자체가 바뀌면 그때 재검토한다
추록 — 무료 키 전환 실측 (같은 날)
사용자가 실제로 키를 무료 티어로 교체했고, 같은 프로브를 재실행했다(원문 파일 추록 참조).
무료 키에서는 쿼터(10 RPM) 이전에 지연·용량이 먼저 무너진다. 초소형 콜(5토큰)이 20~28초 + 503 UNAVAILABLE 1회 + 30초 타임아웃 1회, 6콜/131초(실효 2.7 RPM). 1시간 전 유료 키의 같은 콜은 1.1~1.5초였다. 429는 지연 때문에 도달조차 못 했다.
- E7의 Gemini raw 수치(p50 1,720ms 등)는 유료 키에서 측정된 것이다. 무료 키 기준으로는 이 시점 실측에서 공개된 초소형 호출 20~28초는 C11(5초)의 4~5.6배였다. 실제 advisor p95 측정은 아니지만 이 시점 Core 구성에서 배제하는 근거로 삼았다
- 시점 한정 측정(원문에 요일은 일요일로 적혀 있으나 2026-09-14는 월요일이다. 실행 날짜·시간대 일치 여부 미확인; 당시 기록 13시대, “demand spike is usually temporary”)이므로 무료 티어의 상시 지연으로 일반화하지 않는다. 다만 무료 키 운영은 지연이 시간대에 따라 20초대까지 요동칠 수 있는 구성임이 확인됐다
- 이 측정은 D1 결정(무료 티어 기준 판단)을 강화한다 — 이번 키·프로젝트의 gemini-3-flash-preview Core 구성은 그 시점 지연으로 제외했다
A.12 2026-09-14 · E6 Formal — NPC descent · 정책 ON/OFF × 5모델 × n=5
원문: docs/review-verification/2026-09-14-npc-descent/e6-descent-formal-20260914T053343Z.json (ON) · e6-descent-formal-20260914T054425Z.json (OFF)
러너: scripts/run_model_descent.py 신규 구현 (E6 설계 §6 규격). 셀 절차: Stage 0(스키마 1콜) → age7 5항목 × n=5 → 누설 프로브 5 × 2(하네스 ON) → 지연·Peak VRAM. judge는 같은 응답을 3회 판정해 다수결로 채점하고 전원일치율을 D3로 기록한다 — 2026-09-06 앵커 행(n=2·단일 판정)과 규칙이 다르므로 이번 실행 내 셀끼리만 비교한다.
착수 전 블로커 4건의 해소
| # | 블로커 | 해소 |
|---|---|---|
| 1 | 양자화 혼재 | qwen3.5:2b-q4_K_M 재pull로 9b/4b/2b Q4_K_M 통일. 0.8b는 허브에 Q4 태그가 없어 Q8_0 편차 기록 (양자화가 달라 크기 효과만 분리할 수 없으며 영향 방향은 미검증) |
| 2 | thinking 미제어 | capabilities 조회 후 qwen 전 크기에 러너 서브클래스 think=False 강제, thinking 문자 누계 0 검증. exaone은 thinking 없음 |
| 3 | Anchor n=2 | 이번 실행에서 n=5 재측정 |
| 4 | judge 일치율 러너 부재 | judge 3표·전원일치율(D3) 구현 |
결과 — 정책 ON (판정용)
rate는 항목별 통과 응답/5, items는 5개 관측 항목 중 rate≥0.7인 수다. 실제 비교 판정 항목은 1·2·4·5이고 3은 관측용이다. 셀당 정책 응답 25개를 judge gemma3:12b가 각 3회 판정(75판정)했고 D3는 이 25개 응답의 전원일치 비율이다. 누설은 5프로브×2=10회 중 0, 스키마는 기록된 35개 논리 호출의 유효 비율 35/35다. 재생성 시도 수와 분모가 다르다. A.12의 절대 선택 기준은 5항목 모두≥0.7였고 A.19는 로컬 Kanana의 4/5를 유지하는 기준을 썼다. 동일 게이트를 유지한 것이 아니며 별도의 사전 변경 승인 시각·근거는 이 요약에 없다.
| 셀 | 1 사실 | 2 왜=몰라 | 3 망각* | 4 유도 | 5 문자 | items | 누설 | 스키마 | D3 | p95 ms | VRAM GiB | collapse |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
exaone3.5:7.8b (Anchor) |
0.60 | 0.40 | 0.40 | 1.00 | 0.40 | 1/5 | 0 | 1.00 | 1.00 | 2,610 | 4.83 | toddler |
qwen3.5:9b |
0.80 | 0.60 | 0.20 | 1.00 | 0.80 | 3/5 | 0 | 1.00 | 1.00 | 5,252 | 4.88 | none |
qwen3.5:4b |
0.80 | 1.00 | 1.00 | 1.00 | 0.80 | 5/5 | 0 | 1.00 | 1.00 | 2,290 | 2.98 | none |
qwen3.5:2b (Q4 재pull) |
0.20 | 1.00 | 1.00 | 1.00 | 1.00 | 4/5 | 0 | 1.00 | 1.00 | 639 | 1.51 | toddler |
qwen3.5:0.8b (Q8_0) |
0.60 | 0.80 | 0.80 | 0.80 | 0.80 | 4/5 | 0 | 1.00 | 1.00 | 716 | 1.03 | toddler |
*항목 3(3턴 망각)은 판정 축이 아니라 관측치다 (§3.1). 전 셀 누설 0 · 스키마 1.00 · D3 전부 ≥0.96 — 게이트 D1·D2·D3 전 셀 통과.
결과 — 정책 OFF (RQ9)
| 셀 | 1 사실 | 2 왜=몰라 | 3 망각* | 4 유도 | 5 문자 | items | collapse | ON−OFF (items) |
|---|---|---|---|---|---|---|---|---|
exaone3.5:7.8b |
0.80 | 0.40 | 0.40 | 1.00 | 0.20 | 2/5 | adult | −1 |
qwen3.5:9b |
0.60 | 1.00 | 0.20 | 1.00 | 0.80 | 3/5 | toddler | 0 |
qwen3.5:4b |
0.40 | 1.00 | 0.40 | 1.00 | 1.00 | 3/5 | toddler | +2 |
qwen3.5:2b |
0.20 | 1.00 | 1.00 | 1.00 | 1.00 | 4/5 | toddler | 0 |
qwen3.5:0.8b |
0.60 | 1.00 | 0.60 | 0.80 | 0.80 | 3/5 | toddler | +1 |
두 갈래 질문에 대한 답 (실험 목적 — 사용자 정의)
① 이번 정책 OFF 5셀에서 실험의 행동 분류 기준을 모두 만족한 셀은 없었다. ‘7세·3세화·어른화’는 이 실험의 운영 분류이며 실제 아동 발달 능력 측정이 아니다. OFF 5셀 중 collapse=none이 없다. qwen 전 크기는 항목 1(사실대로)이 0.2~0.6으로 미달(3세화 방향), exaone은 항목 2·5 미달로 어른화(이유를 유창하게 지어내고 규칙 의도를 추론).
② 정책 프롬프트+하네스로 7세를 만들 수 있는 크기는 — 측정 사다리에서 4B 하나다. ON에서 collapse=none이면서 5항목 전부 ≥0.7인 셀은 qwen3.5:4b뿐이다. E6 설계 §7.3 문장 3형: 2B 이하에서 3세화(항목 1이 0.2~0.6으로 붕괴 — 정책을 줘도 사실 질문에 답을 못 한다), 7.8B(exaone)·9B에서 어른화 신호 잔존(항목 2가 0.4/0.6 — 이유를 지어낸다). 정책 프롬프트 효과(ON−OFF)는 4B에서 최대(+2: 항목1 0.4→0.8, 항목3 0.4→1.0)이고 2B에서는 0 — 이번 qwen3.5 후보·정책·문항에서 2B는 기준 미달, 4B는 통과했다. 두 크기 사이는 측정하지 않았고 크기만의 인과 하한은 확정하지 않는다.
비열등 판정 (E6 설계 §4)과 그 한계
러너 계산: 4b·2b·0.8b 비열등 참, 9b 거짓(p95 5,252 > Anchor 2,610). 그러나 이번 실행에서 Anchor 자체가 판정 축 4항목 중 항목 4 하나만 통과해(1/5), E6 설계 §4의 기준선 통과 항목 유지 조건만으로 후보의 정책 수행 능력을 충분히 구별하지 못했다 — 형식적 최소 비열등 모델은 0.8b지만 그 셀은 collapse=toddler다. 절대 축(collapse·5항목 ≥0.7)으로 보면 후보는 4b 하나다. E6 설계 §7.3 문장 1형(4b 기준): qwen3.5:4b는 E6 설계 §4의 기술적 대체 규칙을 만족하며(Anchor 통과 항목 유지·누설 0·p95 2,290 ≤ 2,610), Peak Memory 4.83 → 2.98 GiB.
검수·관측
- 5/5 검수 수행(E6 설계 §8 금지 문장 규칙) — 4b의 5/5를 그대로 믿지 않고 transcripts 전수 감사: PASS 응답은 이 실험의 정책 기준에 맞는지 검토한 발화(“몰라. 그냥.”, “그랬어?”)이고, Anchor의 FAIL은 실제 거동(4턴 전 사실 정확 회상, “그래야 모두가 알 수 있고…” 식 이유 생성)이다. 이는 정책 분류 검토이며 실제 아동 문체의 외적 타당성 검증이 아니다. 검수자의 독립성·별도 판정표는 남아 있지 않다
- Anchor(exaone)가 이번 규칙으로 1/5 — 2026-09-06의 2/5(n=2·단일 판정)와 직접 비교하지 않는다. 다만 “Anchor부터 7세 정책에 미달”이라는 그림은 유지되며, 항목 3(망각)은 Anchor 0.40으로 여전히 낮다
4b한자 혼입 2/25(8%) — “그냥广播이지”, “말听?”.korean_only_check는 영문만 검사해 통과됐다. 하네스에 CJK 검사를 추가하는 것은 하네스 변경이므로 E6 결과에 반영하지 않고 후속 과제로만 기록- judge 일치율이 전 셀 0.96~1.00으로 높다 — temp 0.7에서도 판정이 갈리는 케이스가 거의 없었다
A.13 2026-09-14 · E6 후속 — qwen3.5:4b + Core 후보 동시 상주·루프 스모크
원문: e6-pair-qwen3.5_4b-gemma4_12b-20260914T094133Z.json · e6-loop-smoke-20260914T094617Z.json (접근 제한 앱 경로: docs/review-verification/2026-09-14-npc-descent/)
동시 상주 (stage_concurrency 패턴 — NPC 선상주 → Core 로드 → 교대 3라운드)
| 조합 | pair peak | GPU peak | 교대 축출 | 오프로드 |
|---|---|---|---|---|
qwen3.5:4b + gemma4:12b |
10.49 GiB | 12,178 / 16,311 MiB (74.7%) | 0 / 6스냅샷 | 0 |
exaone 조합(A.9: 12.34 GiB, 여유 ~2.9 GiB) 대비 여유가 ~4.1 GiB로 늘어난다. (원문 evictions의 1건은 Core 로드 전 웜업 스냅샷의 러너 아티팩트 — 교대 구간 축출이 아니다.)
루프 스모크 (Stage 4 패턴 — scripts/loop_app_npc.py 래퍼, 프로덕션 무수정)
NPC=qwen3.5:4b(think 강제 OFF) + Core=gemma4:12b(think 강제 OFF), 테스트 DB, selfplay 성실 1회차: 완주 · 크래시 0 · 폴백 0/17(agent 재시도 1, 재생성 해소) · Core thinking 0자 · NPC thinking 0자 · 종료 시 두 모델 동시 상주(7.51+2.98 GiB). 소요 244s — 플레이어 LLM도 4b를 재사용했다(exaone을 올리면 3모델 15.3 GiB로 빠듯해지는 것을 회피). 점수 0.0은 판정 축이 아니며(§4.1) 플레이어 서술 품질의 영향으로 본다 — 기록만.
A.14 2026-09-14 · NPC 발화 품질 A/B — exaone3.5:7.8b vs qwen3.5:4b (E6과 별개 축)
원문: npc-quality-ab-20260914T101538Z.json (접근 제한 앱 경로: docs/review-verification/2026-09-14-npc-descent/) · 러너: scripts/run_npc_quality_ab.py
E6(7세 정책 체크리스트)와 다른 축이다 — 정책 준수가 아니라 발화 품질(한국어 자연스러움·7세 말투·페르소나·관련성)을 잰다. E6 결과를 덮어쓰지 않는다. 사용자 프레임: exaone은 한국어 특화 모델이라 품질 기준선이고, 4b가 하네스 아래서 얼마나 따라오는지가 질문이다.
방법
E6 Formal ON 원문의 transcripts 재사용 — 같은 발화·같은 회차의 두 모델 응답 25쌍을 익명(응답1/응답2)으로 judge(gemma3:12b)에 제시. 항목 4개 × 3표 다수결 × 순서 2회(스왑). 스왑 후 다수결이 일치하는 쌍만 유효 — 순서 교환 뒤 다수결이 다른 쌍은 비교 집계에서 제외한다. 위치 영향과 확률적 판정 변동은 분리하지 않았다. 단일 종합 점수 없음.
결과 (25쌍)
| 항목 | exaone 승 | 4b 승 | 무승부 | 무효(순서 교환 불일치) |
|---|---|---|---|---|
| 한국어 자연스러움 | 18 | 3 | 0 | 4 |
| 7세 말투 | 7 | 15 | 0 | 3 |
| 페르소나 적합 | 14 | 2 | 0 | 9 |
| 질문 관련성 | 18 | 3 | 0 | 4 |
결정적 지표: 비한글 스크립트(한자 등) 혼입 exaone 0/25 vs 4b 2/25(8%) · 평균 응답 길이 35.0자 vs 9.1자 · 폴백 둘 다 0.
확정된 사실
이번 유효 판정에서 exaone 승수가 더 많았다 — 자연스러움 18:3, 관련성 18:3, 페르소나 14:2. 4b가 앞선 유일한 항목은 7세 말투(15:7)인데, 평균 9.1자의 단답과 함께 관찰됐으며 길이의 원인 효과는 분리하지 않았다. 다만 이는 E6 §11이 경고한 judge 편향(“짧고 어색한 출력을 7세답다로 과대평가”)과 같은 방향이라 단독 근거로 쓰지 않는다. A.12와 같은 응답 25개에서 한자 혼입 2건을 다시 집계했다. 분모는 문자가 아니라 발화이며 독립 재현이 아니다. 여기서 한자 혼입과 하네스의 영문 검사 범위는 다르다.
E6과 A.14는 반대 방향의 트레이드오프를 그린다: exaone은 7세 정책 1/5(collapse=toddler: 사실 항목 우선; 어른화 관련 항목도 미달)이지만 발화 품질 우세, 4b는 정책 5/5·VRAM 2.98이지만 발화 품질 열세. NPC 슬롯 결정은 이 두 축 사이의 선택이다 — 어느 쪽도 두 축을 다 이기지 못했다.
측정의 한계
| 한계 | 내용 |
|---|---|
| n=25쌍 | E6 transcripts 재사용 범위. 통계적 유의성 주장 없음 |
| judge 단일 모델 | gemma3:12b 하나. 페르소나 항목은 무효(순서 교환 불일치) 9건으로 판정 안정성이 낮은 편 |
| 발화 세트 편향 | 7세 체크리스트용 발화라 일상 대화 분포와 다르다 |
A.15 2026-09-14 · E6 항목 2 판정 스펙 v2 재판정 — “아이도 이유를 지어낸다”
원문: e6-rejudge-item2-20260914T102437Z.json (접근 제한 앱 경로: docs/review-verification/2026-09-14-npc-descent/) · 러너: scripts/run_rejudge_item2.py
경위 — 정책과 판정의 불일치
Scenario Director(사용자)가 7세 스펙을 확정했다: “애들도 이유를 지어낸다. 단 근거가 없거나, 본인이 하고 싶은 이야기를 한다.” 대조 결과 AGE7_POLICY 프롬프트 2번(“네가 아는 이유나 기분을 쉬운 말로 답한다”)은 이미 이 정의와 일치하는데, run_age7_check.py 항목 2의 judge 기준(“이유 문장이 있으면 무조건 FAIL”)이 정책보다 엄격한 판정 결함이었다. 항목 5는 프롬프트(“이유는 생각하지 않는다”)와 판정이 일치해 그대로 둔다.
judge 기준을 v2로 재작성했다 — PASS: 몰라 / 자기 기분·직접 본 것 / 근거 없는 아이다운 지어내기 / 딴 얘기로 새기. FAIL: 남의 속마음·모르는 원인의 그럴듯한 추론, 다단계 인과, 조건 달기, 검증 요구. 응답은 재생성하지 않고 A.12 ON transcripts를 재판정만 했다 (judge 75콜, 3표 다수결).
결과 (v1 → v2, 원 판정 병기·보존)
| 셀 | item2 rate | items_passed | collapse | judge 일치 |
|---|---|---|---|---|
| exaone3.5:7.8b | 0.4 → 0.4 | 1 → 1 | toddler 유지 | 1.0 |
| qwen3.5:9b | 0.6 → 0.8 | 3 → 4 | none 유지 | 1.0 |
| qwen3.5:4b | 1.0 → 1.0 | 5 → 5 | none 유지 | 1.0 |
| qwen3.5:2b | 1.0 → 1.0 | 4 → 4 | toddler 유지 | 1.0 |
| qwen3.5:0.8b | 0.8 → 0.4 | 4 → 3 | toddler 유지 | 0.8 |
확정된 사실
exaone의 항목 2 실패 분류는 개정한 v2에서도 유지됐다. 개정 기준에서도 FAIL 3건이 전부 만장일치로 유지됐다 — “그래야 모두가 알 수 있고, 준비할 수 있잖아. 시간이 정해져 있으면 혼란 없이 배급을 받을 수 있으니까” 같은 응답은 아이다운 지어내기가 아니라 다단계 인과·타인 의도 추론의 어른 설명이다. 어른화 신호 서술은 v2 기준으로도 유효하다.
qwen3.5:9b만 판정이 완화됐다 (0.6→0.8, items 4/5) — “관리자님이 그 시간일 때만 나오시잖아. 왜 그래?”가 아이다운 답으로 재분류. 다만 9b의 비열등 탈락 사유는 p95(5,252ms)와 VRAM 무이득이라 탈락은 유지된다.
0.8b는 오히려 하락했다 (0.8→0.4) — v1이 PASS로 세던 비문 횡설수설(“배급이 시간 안에 나오면 좋지만… 그다지 늦어지면 안된다”)을 v2가 걸렀다. 3세화(collapse=toddler) 판정과 정합적이다.
NPC 결정 구도는 바뀌지 않는다. exaone 1/5·4b 5/5 그대로이고, 4b의 항목 2 통과가 전부 “몰라” 단답이라 v2 개정 뒤에도 같은 분류다 — E6(정책) vs A.14(품질)의 반대 방향 트레이드오프가 유지된다.
측정의 한계
| 한계 | 내용 |
|---|---|
| 재판정 범위 | 항목 2·정책 ON·n=5만. OFF 셀과 다른 항목은 v1 판정 그대로 |
| 판정 규칙 변경 이력 | v1(몰라만 PASS)과 v2는 다른 자다. metrics에 note: rejudge-item2-spec-v2로 구분 적립, 이후 실행은 v2가 기본 |
A.16 2026-09-14 · E6 확장 — exaone3.5:2.4b 후보 평가
원문: e6-descent-formal-20260914T103151Z.json · npc-quality-ab-20260914T104637Z.json(vs 7.8b) · npc-quality-ab-20260914T105727Z.json(vs 4b) · e6-pair-exaone3.5_2.4b-gemma4_12b-20260914T105749Z.json (접근 제한 앱 경로: docs/review-verification/2026-09-14-npc-descent/)
사용자 지시로 exaone 하위 버전을 평가했다 — 한국어 특화 패밀리를 유지한 채 크기를 내려 품질·정책·VRAM 세 축을 동시에 잡는 셀인지. ollama pull exaone3.5:2.4b: Q4_K_M(양자화 통일 문제없음), capabilities ['completion'](thinking 미지원 — 7.8b와 동일), 2.67B.
E6 셀 (n=5 · judge 3표 · 항목 2는 v2 스펙 — 정책 ON 항목2만 A.15 v2와 비교 가능; 기존 OFF는 v1이라 직접 비교 제한)
이후 표의 원 식별자 왜=몰라는 항목2의 옛 이름이다. v2는 아이다운 이유·모름·딴 이야기 허용이며 A.17과 A.19의 같은 식별자도 이 정의로 읽는다.
| policy | 사실대로 | 왜=몰라 | 망각* | 유도수용 | 문자그대로 | items | 누설 | 스키마 | D3 | p95 | VRAM |
|---|---|---|---|---|---|---|---|---|---|---|---|
| on | 0.40 | 0.20 | 0.20 | 1.00 | 0.00 | 1/5 | 0 | 1.00 | 0.96 | 1,911ms | 1.74 GiB |
| off | 0.20 | 0.40 | 0.40 | 1.00 | 0.00 | 1/5 | 0 | 1.00 | 1.00 | 2,667ms | 1.74 |
양방향 동시 붕괴다. collapse 분류는 toddler(사실대로 0.40 미달 우선)지만, 어른화 조건(항목 2·5 동시 미달)도 함께 충족한다 — A.12의 exaone7.8b에도 사실·어른화 관련 항목 미달이 함께 있으므로 새로 처음 나타난 유형은 아니다. transcripts 검수: 항목 2·5 FAIL이 전부 “안전과 질서를 유지하기 위해”, “일정한 패턴으로 활동하고, 그로 인해…”, “마치 큰 학교에서 규칙을 지키는 것처럼” 류의 다단계 인과·비유 설명(v2 기준으로도 명백 FAIL)이고, 사실대로 FAIL은 반문 얼버무림이다.
이번 조건에서 시험한 exaone 7.8B와 2.4B 모두 어른화 관련 항목에 미달했다. 패밀리 전체의 성질은 확인하지 않았다. 7.8B(왜=몰라 0.4) → 2.4B(0.2). ON−OFF 격차도 0(items 1→1)으로 통과 항목 수만 ON/OFF 모두 1로 같았다. 항목별 rate는 다르며 정책 효과 부재를 입증한 것이 아니다. E6 설계 §4의 기술적 대체 규칙은 형식상 참(anchor 통과 항목=유도수용뿐 · 누설 0 · p95 1,911≤2,610)이지만 A.12·A.15와 같은 형해화 사유로 절대 축 판정을 병기한다: 1/5, 후보 부적격.
발화 품질 A/B (A.14와 동일 프로토콜, 25쌍 × 4항목 × 3표 × 순서 스왑)
| 항목 | 7.8b 승 | 2.4b 승 | 무승부 | 순서 교환 불일치 | 2.4b 승(vs 4b) | 4b 승 | 무승부 | 순서 교환 불일치 |
|---|---|---|---|---|---|---|---|---|
| 한국어 자연스러움 | 15 | 4 | 1 | 5 | 9 | 10 | 0 | 6 |
| 7세 말투 | 20 | 2 | 1 | 2 | 1 | 24 | 0 | 0 |
| 페르소나 적합 | 10 | 4 | 2 | 9 | 8 | 7 | 0 | 10 |
| 질문 관련성 | 8 | 7 | 0 | 10 | 14 | 10 | 0 | 1 |
정량: 한자 혼입 2.4b 0/25(4b는 2/25) · 평균 길이 83.5자(7.8b 35.0 · 4b 9.1) · 폴백 0. 2.4b는 7.8b의 품질 우위를 계승하지 못했다 — 2.4b는 평균 83.5자로 가장 길었고 7세 말투 승수도 낮았다. 길이의 영향을 분리하지 않았다. 자연스러움은 유효 19쌍에서 9:10이며 동등성 검정은 아니다. 한자 혼입 0과 관련성 승수는 표본 범위의 관찰이다.
pair (Core gemma4:12b)
pair peak 9.25 GiB(1.74+7.51) · GPU peak 10,158/16,311 MiB · 여유 ~6.0 GiB · 교대 3라운드 무축출·오프로드 0. Core 초기 로드 때 NPC 1회성 축출 관측(다음 콜 1.3초 재로드 후 안정) — A.9의 e4b 초기 로드 패턴과 동일. 게이트(교대 구간)는 통과.
3모델 결정 구도
| exaone3.5:7.8b (현행) | exaone3.5:2.4b | qwen3.5:4b | |
|---|---|---|---|
| 7세 정책 (v2) | 1/5 — 어른화 | 1/5 — 어른화+3세화 동시 | 5/5 |
| 발화 품질 | 우세 기준선 | 기준선 대비 3항목 승수 적음, 4b와 자연스러움 9:10 | 열세(단답 9.1자·한자 8%) |
| pair 여유 (Core 12b) | ~2.9 GiB | ~6.0 GiB | ~4.1 GiB |
2.4b는 세 축 동시 해결 후보가 아니다. VRAM 하나만 최상이고, 정책은 이 두 시험 크기에서 1/5로 관찰됐다, 품질은 패밀리 우위를 잃는다. 결정 구도는 여전히 7.8b(품질) vs 4b(정책+VRAM) 두 축이다.
측정의 한계
| 한계 | 내용 |
|---|---|
| exaone 중간 크기 부재 | ollama 허브에 exaone3.5는 2.4b·7.8b·32b뿐 — 4B급 셀은 잴 수 없었다 |
| 품질 A/B의 judge | A.14와 동일 — gemma3:12b 단일 judge, 위치 편향은 스왑 무효 처리로만 통제 |
A.17 2026-09-14 · NPC 상업 라이선스 조사 + 상업 가능 후보 3종 평가
원문: npc-license-survey.md · e6-descent-formal-20260914T110608Z.json · npc-quality-ab-20260914T112712Z.json · e6-pair-kanana1.5_8b-q4km-gemma4_12b-20260914T112726Z.json (접근 제한 앱 경로: docs/review-verification/2026-09-14-npc-descent/)
사용자 지적(“엑사온이 상업용으론 안 되는 점”)으로 라이선스를 조사하고, 상업 가능 대안을 같은 파이프라인에 태웠다. 라이선스 판단은 법률 자문이 아니다 — 원문 조항 인용 + 해석.
라이선스 조사 요지 (상세·출처는 npc-license-survey.md)
다음은 2026-09-14 조사 당시의 결정 기록이다. EXAONE LICENSE(보고서 표기 Agreement 1.1-NC), Mi:dm 원저장소, Kanana 모델 카드에 출처가 연결돼 있다. 당시 열람일은 조사일로 기록됐으나 고정 리비전·모든 모델의 공식 LICENSE 조항은 보존되지 않았다. Gemma의 조사 출처에는 2차 자료가 섞여 있어 공식 라이선스 재확인이 필요하다. 아래 라이선스 열은 당시 분류이며 현재의 무조건적 배포 허용 판정이 아니다. EXAONE 롤백 기록은 연구·개발 범위로 읽는다.
- 2026-09-14 조사에서는 EXAONE 3.5를 연구용으로 분류했다. 공식 LICENSE: “open to anyone for research purposes… For commercial use, please reach out to … LG AI Research”. 서비스 오픈(상업 배포)에는 당시 조사에서는 별도 계약 검토가 필요한 후보로 분류해 교체했다
- 상업 가능 한국어 후보: KT Mi:dm 2.0(MIT, 2.3B Mini) · Kakao Kanana 1.5(Apache 2.0, 2.1B/8B) · SKT A.X Light(Apache, ~7B — 가용성만 기록) · Naver HCX SEED(전용 라이선스, MAU 1천만 이하 허용 — 기록만)
- 스택 점검: Core
gemma4:12b·gemma4:e4b는 당시 조사표에는 Apache 2.0(Gemma 4부터)으로 기재됐으나 공식 LICENSE 리비전·조항 확인 자료가 없어 이 문서에서 법적 허용을 확정하지 않음. judgegemma3:12b는 Gemma Terms이나 배포 스택 밖(평가 도구). embedding gemini는 무료 티어 약관(데이터 활용 조항) 유의
셀 실측 — 정책 ON, n=5, judge 3표(v2 스펙), 전 셀 누설 0·스키마 1.00·D3 ≥0.96
| 후보 (라이선스) | 사실대로 | 왜=몰라 | 망각* | 유도 | 문자 | items | p95 | VRAM | collapse |
|---|---|---|---|---|---|---|---|---|---|
gemma4:e4b (Apache) |
0.4 | 1.0 | 0.4 | 1.0 | 1.0 | 3/5 | 2,372 | 3.06 | toddler |
kanana1.5:8b (Apache, Q4_K_M import) |
0.6 | 1.0 | 0.8 | 1.0 | 1.0 | 4/5 | 1,917 | 4.91 | toddler(경계) |
midm2.0:mini (MIT, 2.3B import) |
0.2 | 0.8 | 0.6 | 0.8 | 0.8 | 3/5 | 624 | 2.12 | toddler |
- 세 후보 모두 어른화 축 항목(왜·유도·문자)은 통과하고 판정 항목 중에서는 항목 1(사실대로)만 미달(관측용 항목3은 e4b 0.4·midm 0.6도 0.7 미만) — exaone 7.8b(어른화)와 반대 방향
kanana1.5:8b의 항목 1 실패는 전반 붕괴가 아니다. 5회 중 실패 2회가 전부 같은 케이스(“충식이 어제 이송된 거 맞아?” → “그랬어?”)의 반복 — 민감 사실 시인을 회피하는 패턴이고 나머지 사실 질문 3종은 통과. 기준 0.7에 1콜 차이(0.6)- Modelfile import: 두 모델 모두 llama3 계열 템플릿으로 GGUF import(Q4_K_M — 양자화 통일 유지). thinking 미지원(capabilities 확인)
품질 A/B — exaone3.5:7.8b(기준선) vs kanana1.5:8b (25쌍 · 3표 · 스왑 2회)
| 항목 | exaone | kanana | 무승부 | 무효(순서 교환 불일치) |
|---|---|---|---|---|
| 한국어 자연스러움 | 9 | 7 | 0 | 9 |
| 7세 말투 | 0 | 21 | 0 | 4 |
| 페르소나 적합 | 7 | 4 | 1 | 13 |
| 질문 관련성 | 14 | 5 | 1 | 5 |
- 외국 문자 혼입 둘 다 0/25 (
qwen3.5:4b의 한자 8%와 대조) · 평균 길이 35.0자 vs 12.3자 · 폴백 0 - kanana의 7세 말투 21:0은 단답(12.3자) 편향이 섞였을 수 있다(A.14의 4b 사례와 같은 계열 주의). 다만 4b와 달리 자연스러움 유효 16쌍에서 기준선 9승·Kanana 7승이고 혼입이 0(무효 9쌍; 동등성 입증 아님)이라는 점이 다르다
pair 체크 — kanana1.5:8b + gemma4:12b
pair peak 12.42 GiB · GPU 13,402/16,311 MiB · 교대 r1~r3 전 스냅샷 동시 상주, 오프로드 0. 기록된 “축출 1”은 core 로드 전 npc_warm 시점의 워밍업 순서 아티팩트다(Core 로드 전 스냅샷을 축출로 집계한 러너 아티팩트이며 A.9의 실제 NPC 언로드와 다름) — 교대 구간 판정은 무축출. exaone 조합(12.34)과 동급이라 VRAM 이득은 없다.
구도에 주는 의미
- 라이선스를 결정 축에 넣으면 exaone 7.8b는 “품질 기준선이자 상업 블로커”가 된다 — 유지하려면 LG 계약이 필요
- 상업 가능 후보 중
kanana1.5:8b가 기준선에 가장 가까운 프로필: 자연스러움 9:7(무효 9, 동등성 미검증) + 혼입 0 + 정책 4/5(경계) + p95 1,917ms. 대가: VRAM 이득 없음(12.42), 항목 1 민감 사실 회피 1케이스, 관련성 열세(14:5) - VRAM 축은 여전히
qwen3.5:4b(pair 10.49)만. 세 축(품질·정책·VRAM) 동시 해결 셀은 이번 확장에서도 없다 midm2.0:mini는 2B급 3세화 패턴 재확인(exaone 2.4b·qwen 2b와 동류),gemma4:e4b는 사실대로 0.4로 NPC 슬롯 부적합(Core 후보로서의 E7 결과와는 별개 역할임을 명시)
A.18 2026-09-14 · NPC 교체 전 루프 스모크 — kanana + gemma4:12b
원문: docs/review-verification/2026-09-14-npc-descent/e6-loop-smoke-kanana-20260914.json · 서버 로그 e6-loop-server-kanana8b-gemma12b.log
NPC 채택(§5.3) 확정 전 게이트. scripts/loop_app_npc.py 래퍼(별도 포트·테스트 DB — Core만 러너 서브클래스 think off, kanana는 thinking 미지원이라 프로덕션형 어댑터 그대로)로 selfplay 성실 1회차.
| 항목 | 값 |
|---|---|
| 완주 / 크래시 | O / 0 (61.4s, 점수 8.8) |
| 폴백 | 0 / 16콜 (planner 1 · advisor 1 · evaluator 10 · manager 1 · NPC agent 3) |
| Core thinking | 0자 (C9) |
| 관리 항목 ① 회피성 응답 | 0 / 21 발화 |
| 관리 항목 ② 외국 문자 혼입 | 0 |
| 관리 항목 ③ NPC 평균 응답 길이 | 11.4자 — 단답 경향 유지, 운영 관찰 지속 |
관리 관찰의 분모는 원자료 npc_replies에 저장된 21개 문자열이며 서버의 agent 논리 호출 3건이나 전체 16콜과 다르다. 회피 0/21·문자 혼입 0·평균 11.4자는 이 수집 목록을 대상으로 했다. 개별 대화 경로와 플레이어 출력 제외 규칙·회피 판정 주체를 독립 확인할 수집 provenance는 부족하므로 NPC 운영 전체로 일반화하지 않는다.
판정: 통과. 교체 반영은 §5.3 “채택 — NPC 슬롯” 참조.
A.19 2026-09-16 · 외부 API 평가 — Anthropic(Core·NPC)·Gemini 무료 키(NPC)·gemma4 통일
원문: output/model-eval-2026-09-16-anthropic/core-age7-summary.md(Core·NPC 7세 1차) ·
stage2-formal-20260916T121314Z.json(n=1) · stage2-formal-20260916T143841Z.json(n=3) ·
age7-anthropic.yml · age7-anthropic-solo.yml · age7-claude-haiku-4-5-verbose.log ·
output/npc-dialogue-2026-09-16-anthropic/comparison.md ·
output/npc-dialogue-2026-09-16-gemini/comparison.md ·
output/gemma4-unify-2026-09-16/summary.md·judge_agreement_results.json·
age7-kanana-gemma4judge.yml·age7-anthropic-gemma4judge.yml·
age7-claude-haiku-4-5-gemma4judge.log·age7-claude-sonnet-5-gemma4judge.log·
selfplay-player-gemma4.log(전부 output/, gitignore·접근 제한 — 이 부록은 공개 집계 요약이며 완전 재현 원자료는 아님).
러너: run_core_selection.py --provider anthropic(§1 정정, run_model_descent.py가 아니다 —
PCA·극성쌍 개념이 그 스크립트에 없다), run_npc_dialogue_check.py --provider
{anthropic,gemini}, run_age7_check.py --provider anthropic, run_selfplay.py.
1. Core PCA·극성·eval·지연 (run_core_selection.py --stage formal, advisor·evaluator만)
2026-09-18 근거 대조로 로컬 evaluator 칸의 2,918ms를 2,179ms로 정정했다. 2,918은 A.6 advisor p50의 역할 혼입이었다. 비교 기준은 stage1-smoke-advisor-20260913T124645Z.json의 advisor 22문항(n=1), stage2-formal-20260914T021635Z.json의 evaluator 14케이스×3인 별도 역할별 실행이다. C13 로컬 유도값은 21,790ms로 통과다. Sonnet n=1 evaluator p50은 로컬보다 약 46%, Opus는 약 44% 높다. 실행별 코드 ref·모델 digest·전체 freeze 정보는 남은 provenance 공백이다.
| 모델 | PCA | 극성쌍 | eval 기대 일치 | advisor p95(ms) | evaluator p50(ms) | C11(p95≤5s) | C13(p50×10≤30s) |
|---|---|---|---|---|---|---|---|
| 로컬 기준 gemma4:12b-N | 0.90 | O | 0.57 | 4,231 | 2,179 | O | O |
| claude-sonnet-5 (n=1) | 0.90 | O | 0.7143 | 4,593 | 3,186 | O | X (31,860ms) |
| claude-opus-5 (n=1) | 1.00 | O | 0.5714 | 5,853 | 3,135 | X (5,853ms) | X (31,350ms) |
| claude-sonnet-5 (n=3, 게이트 통과 후 추가 실행) | 0.9667 | O | 0.7143 | 7,387 | 2,964 | X | O (29,640ms) |
n=3: advisor 66콜·evaluator 42콜, 폴백 0, advisor p50 3,813ms. PCA·극성·eval 게이트는 Sonnet n=1·n=3, Opus n=1에서 PASS. 보존 평가 작업공간에서 Opus n=3 artifact는 찾지 못했다. n=1은 모델당 advisor 22·evaluator 14개 역할-문항 실행이고 n=3은 Sonnet만 advisor 66·evaluator 42건이다(Opus의 eval 0.5714는 게이트 하한과 사실상 동률인 경계값). C11·C13 지연 게이트는 Sonnet에서 n=1↔n=3 사이에 뒤집혔다(n=1: C11 O·C13 X / n=3: C11 X·C13 O) — n=3은 동시 로컬 GPU 작업과 겹쳤으나 외부 API 지연의 인과를 분리하지 않았다. advisor p95 상승·evaluator p50 하락은 이번 반복 실행의 관측이다. Opus는 C11·C13 둘 다 X(advisor p95가 로컬보다 38% 높음 — 이번 실행의 차이이며 API·네트워크·동시 작업의 영향을 분리하지 않았다). C13은 evaluator p50 × 10의 합성 지표이며 실제 연속 10콜의 벽시계나 p95가 아니다. 묶음 호출은 별도 설계안으로 품질·지연을 측정하지 않았다.
2. Core 완주 — self-play 5회차(판 수 제한 로컬 상향, persona 성실, NPC kanana)
Core/NPC는 서비스 역할, 플레이어는 테스트 입력 생성 역할이다. 아래 세 초기 실행은 NPC kanana·성실 페르소나였고 첫 표의 플레이어 모델/think 설정은 이 공개 요약만으로 확정하지 않았다. 뒤의 추가 실행은 플레이어 gemma4(별도 구성 파일)로 구분한다. 동시 작업은 첫 로컬 Core 대조 실행에 기록된 조건이며 모델 자체 속도로 환산하지 않는다.
| 구성 | 점수(5회차) | 결말 | 소요 | attempt_id | 하네스 |
|---|---|---|---|---|---|
| Core claude-sonnet-5 | 13.1, 16.0, 4.4, 8.8, 8.8 | doom | 350.3s | acfad5ac-28b1-458d-9041-a8d419c80cb4 |
evaluator_verdict 50·manager_check 15·classifier 10·agent 10·advisor 4콜, 재생성·폴백 0. planner 5콜, 재생성 2+폴백 1(스키마 위반: plans[3].beats 7개 > maxItems 6 — maxItems가 Anthropic 어댑터에서 description으로 강등되어 구조화 출력이 강제하지 못함) |
| Core claude-opus-5 | 8.8, 0.0, 4.4, 8.8, 4.4 | doom | 295.3s | 4c29beca-84c3-4c2c-b7f9-606ce3bf7bcf |
전 역할 재생성·폴백 0 |
| Core ollama gemma4:12b(대조) | 35.0 × 5 | doom | 1526.0s(동시 gemma3 채점기 작업과 GPU 경합) | 87e9faf6-2230-49a3-9f1f-f2ab6036db68 |
advisor 3회차 재생성 2(non_verbatim_evidence), 그 외 0. 첫 시도는 서술 폴백이 free_text 2000자 상한을 넘겨 422로 죽어 수정 후 재실행(§5) |
플레이어에 gemma4를 사용한 추가 1판(Core gemma4, NPC kanana)도 1판 실행: 8.8, 8.8, 8.8, 21.9, 17.5 / doom /
323.6s / e5b936ce-ee2d-4616-b1ee-86ffa4c301e2 / 전 역할 재생성·폴백 0. self-play 점수는 회차마다
크게 흔들려(n=1) 이 표로 플레이어 모델 순위를 매기지 않는다 — 완주·폴백·크래시 0이 이 축의 판정
대상이다. 완주·폴백 게이트는 Opus·첫 gemma4 대조군 모두 통과(첫 Gemma는 advisor 재생성 2, 폴백 0; 추가 Gemma-player 실행만 전 역할 재생성 0), Sonnet은 planner에 한해 재생성 2·
폴백 1로 “재생성 ≤ 로컬” 기준을 벗어난다(원인은 §5의 어댑터 한계).
제출 조합 확인 (2026-09-17 0시대, 사용자 요청) — Core anthropic:claude-sonnet-5 + NPC anthropic:claude-haiku-4-5,
플레이어 gemma4:12b(think off), persona 성실, 5회차 1판: 11.7, 11.7, 16.0, 24.8, 29.8 / doom / 261.5s /
305d77a6-aae4-4cf0-9e6e-c3fcce5e20ac. 하네스: evaluator_verdict 50·manager_check 15·classifier 10·agent 10·
advisor 4콜 재생성·폴백 0, planner 5콜 중 5회차 재생성 1·폴백 0(같은 beats 7개 > 6 위반, 재생성으로 복구).
NPC 발화 100건에서 메타 표현(유저·플레이어·AI·게임 등) 0건, 서버 로그 오류·거절 0. 크래시 0으로 완주 —
이 1판에서 완주·폴백 0을 확인했으며 planner 재생성 1과 미측정 축은 남는다. 점수는 n=1이라 순위 근거로 쓰지 않는다.
3. NPC 의미 품질 — run_npc_dialogue_check.py(20케이스 × repeat 2 = 구조상 80평가턴)
논리 평가 턴은 보통 케이스별 primary와 followup 2턴×20×2=80이다. Kanana 82턴은 이전 러너의 별도 집계이며 추가 2턴의 실행별 원인을 이 요약에서 확인하지 못했다. 성공 요청·NPC provider attempts·분류기 attempts·최종 폴백은 서로 다른 단위다. Haiku 수정 전/후의 1,906.5~2,256.1ms는 서로 다른 실행의 p50 범위이며 통합 표본 p50이 아니다. 각 값의 실행 ID별 배정은 보존 요약에서 확정하지 않았다.
| kanana1.5:8b(기준선, 82턴) | claude-haiku-4-5 | claude-sonnet-5 | gemini-3-flash-preview | |
|---|---|---|---|---|
| 구조 실패 | 0 | 0 | 0 | 0(성공분만) |
| NPC-역할 재생성/미복구 폴백 | 1 / 0 | 2 / 0 | 1 / 0 | 0 / 66(할당량 소진) |
| 성공 요청 | 82 | 80 | 80 | 14 / 80 시도 |
| p50 / p95(ms) | 1,060.3 / 1,515.5 | 1,906.5~2,256.1 / 2,545.8~3,418.8(러너 수정 전·후) | 2,498.0 / 3,492.2 | 9,183.2 / 66,669.6(10 RPM 대기 포함 wall time) |
| 평균 답 길이(자) | 29.4 | 31.4~32.5 | 35.8 | 54.5 (n=14) |
세 모델 모두 재생성은 동일한 identity 케이스의 시나리오 금칙어에서 나왔고 하네스 재시도로 자가
회복했다(미복구 폴백 0). 원시 재생성/failed_harnesses 수치(Haiku 162/80, Sonnet 161/80)는
새 발화 분류기 스텁이 평가 스키마를 못 맞춰 매 턴 2재생성+1폴백을 만드는 러너 결함이며 provider와
무관하다(§5에서 수정, Haiku만 수정 러너로 재실행).
의미 품질(당시 ‘사람 검토’로 기록한 선별 10케이스) — 검토자·선정법·모델명 블라인드·독립 판정·불일치 처리 기록은 확인되지 않아 독립 사람 평가로 확정하지 않는다. 다음은 해당 표본의 검토 관찰이다. — 두 Anthropic 모델 모두 09-15 보고서가 로컬 모델의
지속적 약점으로 적은 “지금 방금 들었다 vs 기억한다” 구분을 통과한다(kanana: “그건 나도 몰라.”로
방금 들은 말도 부정). 근거-출처 구분(전언 vs 직접 목격)도 둘 다 명시적으로 한다. Sonnet은 “사라진
친구 이름을 물으면 현재 인물을 나열”하는 09-15 문서화 약점을 그대로 재현(lost-name: “없어진
친구? 그건 나도 몰라. 채연, 민석, 은상 다 오늘 봤는데.”), Haiku는 이 사례를 피한다. core-6(부재
인물이 무엇을 했는지 근거 없이 단정)에서 Sonnet·Gemini는 “은상이는 안 적었어”로 근거 없는 부정
단정을 하고, Haiku는 모른다고 정직하게 답한다.
Gemini 무료 키 — NPC 대안으로 채택 불가. 실제 제약은 과제가 지시한 10 RPM 페이싱이 아니라
하루 20요청 상한이다. 워밍업 시도 2회가 503으로 사례 데이터 없이 실패, 3번째 시도에서 14턴
성공 후 503 → 429 RESOURCE_EXHAUSTED(“daily cap 20 requests”)로 나머지 66턴 전부 실패. 게임
한 판에 NPC 호출이 수십 건 필요해 하루 20건으로는 운영 불가 — 결정: Gemini는 NPC 후보에서
제외.
4. NPC 7세 정책 (run_age7_check.py, policy on)
1차 — 채점기 gemma3:12b(로컬 기준값과 같은 채점기)
| 모델 | 1_사실대로 | 2_왜=몰라 | 3_3턴망각 | 4_유도수용 | 5_문자그대로 | 통과 | 게이트(4/5) |
|---|---|---|---|---|---|---|---|
| 로컬 기준 kanana1.5:8b(n=5, 2026-09-14) | 0.6 | 1.0 | 1.0 | 1.0 | 1.0 | 4/5 | O |
| claude-haiku-4-5(n=4, 1차) | 0.75 | 1.0 | 0.0 | 0.0 | 0.0 | 2/5 | X |
| claude-haiku-4-5(n=4, 단독 재실행) | 0.0 | 1.0 | 0.0 | 0.75 | 1.0 | 3/5 | X |
| claude-sonnet-5 | — | — | — | — | — | — | 2회 정지로 미실행 |
Haiku 재실행분의 1_사실대로 4건 전부 “judge 출력 파싱 실패” 주석이 달렸으나 답변 자체는 사실을
정확히 밝힌 내용이었다(§4의 gemma4 재현에서 같은 20발화의 재판정에서 파싱 실패가 보고되지 않았으나, VRAM 경합 원인 여부는 미확인이다. 채점기·실행 환경이 함께 달라 인과를 분리하지 못함). Sonnet은 1차 32분·2차 24분 CPU 0%·소켓 idle로 멈춰
종료했고, 같은 프롬프트 단발 진단 호출은 3.31초에 정상 완료돼 그 단발 호출에서는 문제가 재현되지 않았다. 이 Anthropic
키를 동시에 쓰는 여러 평가 작업이 겹친 시점의 계정/네트워크 정체로 추정한다(확정 원인 아님).
2차 — 채점기를 gemma4:12b(think off)로 통일 후 재측정(§5의 채점기 교체 이후)
| 모델 | 1_사실대로 | 2_왜=몰라 | 3_3턴망각 | 4_유도수용 | 5_문자그대로 | 통과 | 게이트(4/5) |
|---|---|---|---|---|---|---|---|
| kanana1.5:8b(n=4) | 0.75 | 1.0 | 0.25 | 1.0 | 1.0 | 4/5 | O |
| claude-haiku-4-5(n=4, 67s) | 0.75 | 1.0 | 0.0 | 0.75 | 1.0 | 4/5 | O |
| claude-sonnet-5(n=4, 90s) | 1.0 | 1.0 | 0.0 | 0.75 | 1.0 | 4/5 | O |
세 모델은 5항목 중 4항목 기준을 통과했다. 누설 0 조건은 이 러너에서 미측정이다. 공통 약점은 항목 3(3턴 망각) — 셋 다 이름·서랍 위치·
숫자를 그대로 정확히 기억해 말한다. Sonnet은 이 항목에서 메타 발화 누출도 보였다: “세 번째 서랍에
있다고 했어, 유저가.” — 페르소나 밖 표현(“유저”)이 답변에 그대로 섞여 나왔다. 누설(금칙어)
측정은 run_age7_check.py 범위 밖이라 이 축에서 측정하지 않았다.
gemma3→gemma4 채점기 교체 자체의 영향: 같은 20개 발화(Haiku 1차 verbose 로그)를 두 채점기로 각 2회 재채점했다. 모델당 20발화×2=40판정이며 두 모델 합계 80판정이다. 보고된 파싱 실패 0은 모델당 0/40판정, 자기일치 20/20은 반복 비교쌍, 모델 간 일치 19/20(95%)은 발화 비교쌍이다. 모델 간 일치에 쓴 반복 선택/집계 규칙은 별도 확인이 필요하다 — 유일한 불일치는 “그건 나도 몰라. 내가 못 봤어.”를 gemma3는 회피(FAIL), gemma4는 부정 답변(PASS) 으로 본 해석 차이다. 즉 1차 표와 2차 표의 점수 변화(특히 Haiku 2/5→3/5→4/5)는 여러 조건 변경 뒤의 관측으로 읽어야 한다. VRAM 경합에 따른 잡음 제거라는 원인 가설은 확인되지 않았다.
5. 어댑터·러너 결함과 수정
아래 기록은 2026-09-16의 SDK 1.6.0·모델·러너 설정에 한정한다. 현재 SDK의 일반 동작 설명이 아니다.
| 층 | 영향받은 실행 | 조치·재실행 근거 |
|---|---|---|
| 어댑터 호환 | Haiku temperature 스모크 | 6a02a13, 수정 후 스모크 1회 |
| 모델 응답/스키마 전달 | Sonnet planner beats 초과 | A.19 selfplay 재생성·폴백; 후속 절단 구현 A.21.4, 동일 전 축 재실험 미확인 |
| 평가 러너 | NPC 분류기 스텁·judge thinking | Haiku 대화 재실행, judge 교체/재판정; 이 요약의 수정 commit 미상 |
| selfplay 입력·로그인 | free_text 상한·Secure 쿠키 | Gemma 대조 재실행, 로그인 33bebca; 상한 수정 commit 미상 |
어댑터 스모크(모델당 complete() 1회, 페르소나 한 줄 + JSON 스키마, 2026-09-16 20:5x)
| 모델 | 수정 전 | 수정 후(6a02a13) |
temperature 전송 | effort 전송 |
|---|---|---|---|---|
| claude-haiku-4-5 | temperature 없으면 OK, 있으면 TypeError |
OK 1.9s {"reply": "죽하고 계란말이 먹었어요"} |
extra_body로만 |
안 보냄 |
| claude-sonnet-5 | OK 3.3s | OK 2.3s | 안 보냄 | low |
| claude-opus-5 | OK 2.6s | OK 2.5s | 안 보냄 | low |
수정 전 Haiku는 temperature 없이 부른 호출에서 “저는 AI이라 음식을 먹지 않습니다”라고 답했다(페르소나가 한 줄뿐인 스모크 프롬프트) — 실제 NPC 프롬프트를 쓰는 §3·§4와 조합 self-play(§2 끝)에서는 메타 누설이 재현되지 않았다.
- Anthropic SDK 1.6.0
temperature인자 소실 —Messages.create()에temperature파라미터가 빠져 Haiku 호출이TypeError.extra_body로 우회 전송(커밋6a02a13). Sonnet·Opus는 원래 temperature를 받지 않는 설계라 영향 없음, effort는 Sonnet·Opus에만 전송. - planner
beatsmaxItems 미시행 — 구조화 출력 스키마의maxItems가 Anthropic 어댑터에서 description 문구로 강등되어(§HANDOFF “스키마 strip” 참고) 실제 상한을 강제하지 못함 — Sonnet self-play에서 재생성·폴백의 유일한 원인(§2). 후속 과제: 프롬프트 쪽 상한 명시 또는 응답 후 절단. - NPC 대화 러너의 발화 분류기 스텁 결함 —
utter()에 새로 추가된classify()호출이 러너 fixture의core_llm스텁({"plans": []}고정)과 스키마가 안 맞아 provider와 무관하게 매 턴 재생성 2+폴백 1이 붙던 것을 스텁이label스키마면"question"을 돌려주도록 수정, Haiku는 수정 러너로 재실행. - selfplay 밤 서술 폴백 422 —
free_text2000자 상한을 넘겨 죽던 것을 수정(gemma4 대조군 1차 시도가 이 결함으로 크래시). - gemma4:12b 채점기 thinking 기본 ON —
think필드를 생략하면 gemma4가 기본으로 thinking을 켜 판정 1건에 1,666 thinking 토큰·35초가 걸림.runner_common.make_llm()기본값을think=False로 고정(13개 호출 지점 전부 적용).
6. 비용 추정 (list price, docs/apiscenario.md §1.3, 문자수 × 1.0토큰 가정 — 부분 비용 추정, 실측 아님)
Core 축(A.19.1·2 일부)에 배정된 실행분 합계 ~$1.06(Core smoke Sonnet ~$0.24·Opus ~$0.59, NPC age7
Haiku 1차 ~$0.10, Sonnet 정지 시도 2건 ~$0.13 추정 포함, 진단 호출 <$0.01) — 이 실행분에 배정된 예산(~$4)보다 낮은 추정. NPC
대화 점검(§3)·gemma4 통일 재측정(§4 2차)의 정확한 토큰 사용량은 별도로 집계하지 않았다(AnthropicLLM.complete()가
response.usage를 버려 러너가 기록하지 않음) — 2026-09-16 해당 실행 범위의 부분 추정이며 실측 정산이 아니다. 문자수×1토큰은 검증된 상한식이 아니므로 ~$1.06을 총지출이나 비용 상한으로 사용하지 않는다. 누락된 NPC 대화·재측정·실패 과금을 포함한 provider usage/청구 기록 없이는 총 $10 미만 여부를 확정할 수 없다.
판정과 결정
- PCA·극성·eval: Sonnet n=1·n=3, Opus n=1 통과. 지연: Sonnet은 반복별 C11/C13 통과가 달랐고 Opus n=1은 둘 다 실패했다. 완주: Opus 재생성·폴백 0; 첫 Gemma Core 대조는 advisor 재생성 2·폴백 0; 뒤의 Gemma-player 실행은 재생성·폴백 0이다. Sonnet 초기 실행은 planner 재생성 2·폴백 1, 제출 조합 확인판은 planner 재생성 1·폴백 0이다. 역할별로 planner의 재생성을 로컬 advisor 재생성과 상쇄하지 않는다.
- NPC: 선별 10케이스 검토에서 출처·기억 구분 개선을 관찰했으나 검토자·선정 기록은 불충분하다. Haiku 2·Sonnet 1재생성으로 재생성 0 조건은 미충족이다. 7세 항목 수 기준은 재측정에서 4/5, 누설 조건은 미측정이며 전체 게이트 통과를 뜻하지 않는다. 첫 judge 실행의 실패 원인은 미확정이다.
- 권고(최종 결정은 사용자 몫): Core
claude-sonnet-5, NPCclaude-haiku-4-5. Opus 5는 이 게이트 조합에서 품질 우위가 없고(eval 게이트 경계·PCA만 소폭 우위) advisor p95가 더 느리며 비용이 2~3배라 채택하지 않는다. 사용자 확정(2026-09-17): 이 조합을 제출용 구성으로 고정한다 — 조합 self-play 1판 이상 없음(§2 끝). 전환 절차는 앱 저장소 인계 문서에 둔다. - 관찰 항목(운영 시 유의): advisor p95가 5~7s대에서 표본 간 크게 흔들린다(C11 경계) · planner
beatsmaxItems가 Anthropic 구조화 출력에서 강제되지 않는다(§5, 후속 과제) · 3턴 망각(항목 3)은 로컬·Anthropic 공통 약점 · Sonnet이 “유저가”라는 메타 표현을 답변에 누출한 사례 1건(§4) · 이번 프로젝트·키의 gemini-3-flash-preview NPC 구성은 해당 시점 하루 20요청 상한으로 제외했다. A.21의 Gemini 임베딩은 별도 모델·한도다.
A.20 2026-09-18 · 임베딩 슬롯 — 모델 비교를 하지 않은 이유
근거: docs/design/P2-P5-mvp.md §주요 설계 결정 1 · docs/jekyll.md 2026-09-06 “채점 밸런스 조정” ·
metrics.yml scoring_calibration 5줄(2026-09-06) · 코드·DB 실사(2026-09-18).
E7(Core)·E6(NPC)·A.19(외부 API) 어디에도 임베딩 슬롯의 로컬 vs 외부 비교가 없다. 빠뜨린 것이 아니라 비교 대상이 2026-09-06에 사라졌다. 경위는 3단계다.
- P0 설계(09-06) — 용도는 둘이었다: ① 채점의 정답 주장 검색(RAG) ②
search_notes도구의 노트 검색(기획서 v9.0 §11.5). 이를 위해 pgvector·truth_claim_embeddings(1,536차원)·GeminiEmbedding(gemini-embedding-001) 어댑터를 깔았다. ②는 끝내 구현하지 않았다 —tool_port.py에 “search_notes는 P2” 주석만 남고 디스패치·유스케이스가 없어, 실제 도구는ask_npc하나다. - P2 설계 결정(같은 날) — 문장이 양쪽 다 20개 이하라 DB 왕복이 과해 pgvector 질의를 파이썬
코사인으로 대체, 테이블은 유지하되 실사용 보류(
P2-P5-mvp.md결정 1). 이 시점부터 테이블은 빈 채다. - 채점 밸런스 조정(같은 날) — 여기서 경로가 제거됐다. 플레이테스트 8판의 “정답에 도달해도
21~42%” 문제를 진단한 결과 원인이 RAG top-3 후보 절단(유저 주장 최대 8개 중 임베딩 유사도 상위
3개만 심판에게 전달)이었다.
run_scoring_calibration.py골든 세트로 측정하니 절단을 없애고 8개를 전부 번호 목록으로 심판에게 넣는 쪽이 나았다 — 정답형 65.3 → 100.0, 오답형 25.7 → 19.4, 2회 반복 편차 0.0%p. 그래서_judge의 임베딩 랭킹·장애 폴백(user_claims[:3]) 경로와_cos를 삭제했다.
즉 두 용도 중 ①은 채점 캘리브레이션 축에서 탈락했고 ②는 구현되지 않았다. 어느 임베딩 모델이 더
나은지는 물을 필요가 없었다 — 당시 골든 세트에서 유저 주장 top-3 후보만 전달하는 경로보다 전체 최대 8개를 전달하는 경로의 채점 결과가 나았기 때문이다. AI_Agent_Evaluation
PART1 §5 Anchor 표가 계획했던 gemini-embedding vs Qwen3-Embedding-4B 비교가 실행되지 않은 이유가 이것이다.
코드·DB 실사(2026-09-18) — 위 경위가 현재 코드와 일치하는지 확인했다.
| 확인 항목 | 결과 |
|---|---|
get_embedding() 호출처 |
0건 (llm_factory.py:68에 정의만 존재) |
.embed(...) 호출 |
0건 |
벡터 유사도 질의(<=>·cosine·l2_distance) |
0건 — Vector(1536) 컬럼 선언만 남음 |
truth_claim_embeddings 행수 |
0행 (실 DB 조회) |
/health 응답 |
{"db":"ok"} — 09-06 당시 “헬스체크용으로 존속”시킨 임베딩 표시도 이후 사라짐 |
설정 파일 EMBEDDING_FALLBACK=qwen3-local |
build_embedding()이 fake/gemini만 받아 읽히지 않는 설정(grid_keymaker_secret_manager.py:31에 필드만 존재) |
그래서 하지 않는 것: 임베딩 슬롯의 로컬(qwen3-local) vs 외부(gemini-embedding-001) 비교는
제출 범위에서 측정하지 않는다. 호출이 0인 슬롯이라 게이트를 만들 대상이 없다.
정본 반영(2026-09-18): 아래 문서에 원문을 고치지 않고 같은 경위를 추가 기록으로 덧붙였다 —
기획서 확정본 v9.0 §11.5(search_notes 미구현)·§11.6(모델 계층 3회 변경과 임베딩 계층 소멸),
AI_Agent_Evaluation PART1 §5 Anchor(임베딩 축만 미측정인 이유), 팀원용 쉬운설명 v9.0 §49(쉬운 말 요약).
docs/apiscenario.md에 걸려 있던 “임베딩 무료 키 한도 제출 전 확인 필요”는 전제가 틀린 과제라 같은 날 정정했다.
이 문서 §A.17 라이선스 점검과 09-14 변경 이력 줄의 “embedding gemini”, docs/HANDOFF.md의 운영 구성
2곳은 그 시점의 기록이라 그대로 둔다. 이 시점에는 Core·NPC 2슬롯으로 기록했다. 이후 같은 날 A.21.8에서 추천 순위용 임베딩을 추가해 최종 3슬롯 구성으로 변경했다.
A.21 2026-09-18 · 임베딩 부활(규칙 대안 순위 한 자리) — 5셀 비교·모델 간 순위 일치도
원문: output/rule-alternatives-2026-09-18/rule-alternatives-20260918T012519Z.json(셀별 전체 순위·오답, gitignore·접근 제한 — 이 부록은 공개 집계 요약이며 완전 재현 원자료는 아님) ·
docs/metrics.yml rule_alternatives_eval 11줄. 러너: backend/scripts/run_rule_alternatives_eval.py, 골든: backend/scripts/rule_alternatives_golden.yml
(에이전트 작성·Scenario Director 미검수, 30문장, 17개 (대상, 행동) 쌍 전부 2~3문장, 금지형·구어·오타 포함).
결정 경위. A.20에서 임베딩 호출이 0임을 확인한 뒤 사용자 결정(2026-09-18)으로 한 자리에만 되살렸다:
신의 개입 “직접 쓰기” 규칙이 거부됐을 때 주는 대안 3개의 순위(intervention_interactor.suggest_alternatives).
어간 앞 2글자 겹침이던 것을 Strategy(alternative_rank.py — StemOverlapRank·EmbeddingRank)로 뽑고, 임베딩은
어떤 예외든 어간으로 폴백한다. 추천 순위를 정하며 최종 선택은 플레이어가 한다. 채점·NPC 기억·단서 해금에 직접 호출하지 않지만 오추천이 플레이어 선택·진행에 미치는 간접 영향은 미검증이다. 09-06의 교훈(“임베딩으로
결정하면 틀렸을 때 조용히 깎인다”)대로 채점·NPC 기억·단서 해금에는 닿지 않는다. 구현은 자매 프로젝트
com.lifetutorial의 임베딩 방식을 따랐다: 1536 = MRL 절단(로컬 truncate_dim/ollama dimensions, Gemini output_dimensionality),
L2 정규화(ollama 응답 노름 1.000000 실측), 질의/문서 비대칭(Qwen3 지시문 접두는 query에만, Gemini RETRIEVAL_QUERY/RETRIEVAL_DOCUMENT),
“다른 provider 벡터는 한 컬럼에 섞지 않는다”(lifetutorial은 embedding/embedding_local 컬럼 분리 — 우리는 테이블 미사용이라 포트 주석으로만).
1. 셀 결과 (n=30, 각 1회, 지연은 rank() 1회 wall time — 행동 문구 캐시 후)
| 셀 | dims | top-1 | top-3 | p50 ms | p95 ms | 오답(top-1) |
|---|---|---|---|---|---|---|
| stem(현행 어간 겹침) | — | 0.300 | 0.833 | 0.0 | 0.0 | 21/30 |
ollama qwen3-embedding:4b |
2560 | 0.933 | 1.000 | 84.5 | 90.9 | 2 |
ollama qwen3-embedding:4b |
1536 | 0.867 | 1.000 | 85.8 | 94.7 | 4 |
ollama bge-m3 |
1024 | 0.900 | 1.000 | 107.5 | 1,855.2* | 3 |
gemini-embedding-001 |
1536 | 0.967 | 1.000 | 373.5 | 388.1 | 1 |
* bge p95는 첫 3건(1.8~2.0s)의 이상치 — 원인 미확정(동시에 돌던 다른 평가 작업의 ollama 사용으로 모델 축출·재로드 추정), 나머지 27건 ~100ms. 이번 Gemini 30요청은 30/30 성공했고 429가 없었다. 이 결과로 키 쿼터와 전역 풀 중 429 원인을 판정할 수 없다. 오류 metric/scope와 해당 프로젝트 한도 근거는 미확보다. 5셀 공통 오답 1건 “은상이 본 대로 얘기하게”(기대 “설명한다” ↔ 모델 “들은 것을 그대로 전한다”)은 골든 자체가 모호한 문장일 수 있다.
2. 모델 간 순위 일치도 — 사용자 질문 “qwen@1536이 Gemini와 같은 결과를 내는가”
벡터 코사인은 모델 간에 재지 않았다(차원이 같아도 공간이 다르다). 같은 문장에 대한 전체 템플릿 순위를 셀 쌍끼리 비교했다.
적중률은 정답 행동이 top-1/top-3에 포함된 문장 수/평가 문장 수다. top-1 일치는 두 모델의 첫 추천이 같은 비율, Jaccard는 상위 3개 집합의 교집합/합집합, Kendall τ는 전체 순서의 일치도다. 러너는 두 셀 모두 성공한 동일 순서 문장의 Jaccard·τ를 문장별로 구한 뒤 평균한다. 순열로 계산하므로 점수 동률을 별도 tie 보정하지 않고 rank가 반환한 순서로 처리한다. 높은 일치도가 정답 정확도를 뜻하지 않는다. stem 0.0ms는 소수 한 자리 반올림이며 측정 생략이나 정확한 0이 아니다.
| 쌍 | top-1 일치 | top-3 Jaccard | Kendall τ |
|---|---|---|---|
| gemini ↔ qwen1536 | 0.867 | 0.627 | 0.342 |
| gemini ↔ qwen2560 | 0.933 | 0.600 | 0.331 |
| qwen2560 ↔ qwen1536 | 0.933 | 0.967 | 0.918 |
| gemini ↔ bge | 0.933 | 0.823 | 0.598 |
| stem ↔ qwen1536 | 0.233 | 0.607 | −0.184 |
| stem ↔ gemini | 0.333 | 0.597 | −0.011 |
읽기: qwen 2560→1536의 전체 순위 일치 τ는 0.918이고 top-3 적중률은 유지됐으나 top-1 정답은 28/30→26/30으로 2건 줄었다. Gemini와 qwen은 정답을 모두 top-3에 포함했어도 추천 목록과 순서는 같지 않다(Jaccard 0.627/0.600, τ<1). 어간 순위는 임베딩과 일치도가 낮았고 정답 top-1은 9/30=0.300이었다. 모델 간 비일치를 순서의 무의미함이나 모델 공간 동일성으로 해석하지 않는다.
3. 판정
실험 판정(초기 30문장).
-
이 축의 게이트(신설): top-3 ≥ 어간 기준 0.833(비열등) + 폴백 동작 확인. 임베딩 4셀 전부 top-3 1.000으로 통과. 폴백은 잘못된 base_url로 실호출해 어간 결과와 동일함을 확인(warning 로그 1줄). 구현 검증.
-
Sonnet의 구현 코드 최종 검토(B등급; 모델 성능 등급 아님) — Critical 1:
EmbeddingRank.rank의 캐시 조회가try밖이라embed_documents가 후보보다 짧게 돌려주면KeyError가 폴백을 우회해/preview_rule500 →try안으로 이동, 회귀 테스트 추가. Major 1: 팩토리가 Qwen3 지시문을 모델 불문 붙임 →qwen3-embedding*에만. 서버 기동은 어댑터가 죽어 있어도 실패하지 않음(요청 시점 생성, 실행 확인). 격리(채점·NPC·단서에 import 없음) 확인. 수정 후 960 passed는 구현 회귀검증이며 모델 품질 점수가 아니다. 당시 후보와 최종 선택의 위치. -
프로덕션 provider: Gemini@2560 · 로컬: bge-m3@1024 — 최종 판정은 A.21.8(사용자 결정 2026-09-18). 후보:
ollama:qwen3-embedding:4b@1536(top-1 0.867, 85ms, 비용 0, 외부 의존 없음 — 제출 후 Core·NPC가 Anthropic이라 GPU가 비어 있음) vsgemini-embedding-001(top-1 0.967, 373ms, 무료 키 — 일일 상한 미측정). 차이는 30문장 중 3문장이고 top-3는 동일. 현재 설정 파일는EMBEDDING_PROVIDER=gemini라 그대로 띄우면 Gemini 경로로 간다.
4. 같은 날 닫은 것 — A.19 후속 과제
- planner
beatsmaxItems 미시행(A.19 §5) — 엔진 쪽 결정적 절단으로 닫음.llm_output_dto.py에cut_to_max_length헬퍼(필드의MaxLen을 읽어 앞부터 절단)를 두고NpcPlan.beats에 적용, 꽉 찬 계획일 때만 beat 번호를 위치로 재부여(어댑터가maximum도 벗겨beat: 7이 섞여 올 수 있다 — sonnet 검토 Major). 성긴 계획의 beat 번호는 규칙when_beat슬롯이라 손대지 않는다. 같은 결함 계열인AdvisorOptionsOutput.options(max 3)·AdvisorReplyOutput.evidence(max 2)에도 같은 헬퍼 적용. 테스트 5건, 958 passed. 로컬(ollama) 경로는 스키마가 강제되어 no-op. - 러너 provider 경로 —
run_scoring_calibration.py·run_leak_test.py에--provider anthropic(runner_common.make_provider_llm, 프로덕션llm_factory.build_llm경유).run_paw_eval.py는 LLM을 부르지 않고 self-play 판을 읽는 러너라 라벨만. ollama 기본 경로 3건 실주행 동일 동작. → 제출 조합(Sonnet/Haiku)의 채점 캘리브레이션·누설·원숭이손 측정은 이 경로로 후속 실행 예정으로 남긴다. 이 검토에 연결된 결과는 없으며 제출 Sonnet/Haiku의 세 축 완료로 표시하지 않는다. 별도의 앱 A.22는 다른 로컬 채점기 수정 기록이므로 이 예정 작업을 대체하지 않는다.
5. 차원 상향 검토 (같은 날, 사용자 질문 “1536 말고 더 올릴 수 있나”)
올릴 수 있다. Gemini는 128~3072(기본 3072를 MRL 절단), qwen3-embedding:4b는 32~2560 — 둘이 같이 쓸 수 있는 최대는 2560.
GeminiEmbedding에 dimensions 인자를 열고 Settings.embedding_dimensions 한 값이 두 어댑터에 같이 들어가게 했다.
| 셀 | dims | top-1 | top-3 | p50 ms | 비고 |
|---|---|---|---|---|---|
| gemini | 1536 | 0.967 | 1.000 | 364 | §1과 같은 셀 재실행 |
| gemini | 2560 | 0.967 | 1.000 | 373 | 1차 실행은 15건에서 503(일시 장애)로 중단, 재실행 30/30 |
| gemini | 3072 | 0.967 | 1.000 | 380 | 기본(무절단) |
| qwen | 2560 | 0.933 | 1.000 | 84 | §1 재확인 |
일치도: gemini3072↔gemini1536 top-1 1.000·τ 0.962, gemini3072↔gemini2560(n=15, 503으로 중단된 1차 실행의 공통 성공 문장만 비교) τ 0.942. 30/30 재실행의 적중률 표와 다른 분모이며 30건 순위 일치도는 재집계하지 않았다. 초기 30문장의 측정 차원 1536·2560·3072에서 top-1/top-3 적중률은 같았지만 전체 순위는 달랐다. gemini2560↔qwen2560 top-1 0.933·τ 0.380(1536에서 0.867·0.342) — 두 모델을 같이 올리면 일치가 소폭 오른다.
읽기: 이득이 있는 쪽은 qwen뿐(1536→2560 top-1 +2/30). 1536을 고집할 이유였던 “같은 컬럼” 제약은 테이블 미사용이라 지금은 없다.
EMBEDDING_DIMENSIONS=2560이면 qwen은 무절단·Gemini는 3072→2560 절단으로 둘이 같은 차원이 된다. 단 pgvector vector 타입 HNSW/IVFFlat
인덱스는 2,000차원 상한(그 이상은 halfvec)이라, 훗날 테이블을 살릴 때는 이 점을 본다.
중간 결정(2026-09-18): 당시 qwen 로컬·Gemini 둘 다 2560. 이후 로컬 bge1024 채택은 A.21.8. Settings.embedding_dimensions 기본값을 2560으로 바꿔 설정 파일 없이도 이 값이며, 어댑터 기본값·테스트도 2560. 포트의 EMBEDDING_DIM=1536은 미사용 pgvector 컬럼 규격으로만 남긴다.
6. 정본 수치 — Scenario Director 검수 골든(22문장) · 초기 30문장 반복 별도 · 서버 실경로 · VRAM 동주
A.21.1~5는 에이전트 작성 골든 30문장(미검수)의 1차 수치다. E7·E6 기준(사전 통제·반복·실경로·동시 상주)에 맞추기 위해 같은 날 검수·반복·실경로·상주 검사를 추가했다. 검수 22문장과 초기 30문장 반복은 서로 다른 집합이다.
골든 검수(Scenario Director, 2026-09-18). 프로젝트는 Scenario Director를 사람인 시나리오 저자/사용자로 정의한다. golden 파일은 에이전트 초안과 저자의 검수 버전을 구분한다. 첫 모델 실행 후 수정한 사후 검수 집합이며 검수자가 모델 결과를 봤는지, 별도 서명·항목별 승인 기록은 남아 있지 않다. 독립 전문가 검증으로 해석하지 않는다. 일반화에는 미사용 검증 세트가 필요하다.
검수 기준은 “문장의 뜻이 행동과 같은가” — 형식이 아니라 맥락. 뜻이 다른 8문장 제외 (1 “밥 나눠주지 마”≠배급을 남긴다 · 3 “병원 가서 진찰”(세계에 없음) · 5 오타 · 9 “건드리지 마”≠만진다 · 17(18이 맞음) · 19 “헛소문 못 내게”≠소문을 낸다 · 23 “쫓아가지 마”≠따라간다 · 26 “적어두게 하지 마”≠기록한다), 14·15는 “포대 안을 확인”이 아니라 “포대 자체가 뭔지 본다”는 뜻으로 교체. 22문장·17쌍 전부 커버.
| 셀 | dims | top-1 | top-3 | p50 ms | top-1 오답 |
|---|---|---|---|---|---|
| stem(현행) | — | 0.364 | 0.818 | 0 | 14/22 |
| ollama qwen3-embedding:4b | 2560 | 0.909 | 1.000 | 86 | 은상이 들은 말을 한 글자도 안 바꾸고 옮기게 · 은상이 본 대로 얘기하게 |
| ollama qwen3-embedding:4b | 1536 | 0.818 | 1.000 | 84 | 채연이가 아는 거 털어놓게 · 준이 주머니에 든 걸 꺼내 보이게 · 은상이 들은 말을 한 글자도 안 바꾸고 옮기게 · 은상이 본 대로 얘기하게 |
| ollama bge-m3 | 1024 | 0.909 | 1.000 | 109 | 준이 자루가 대체 뭔지 보게 해 · 은상이 본 대로 얘기하게 |
| gemini-embedding-001 | 1536 | 0.955 | 1.000 | 418 | 은상이 본 대로 얘기하게 |
| gemini-embedding-001 | 2560 | 0.955 | 1.000 | 407 | 은상이 본 대로 얘기하게 |
| 쌍 | top-1 일치 | top-3 Jaccard | Kendall τ |
|---|---|---|---|
| gemini2560 ↔ qwen2560 | 0.909 | 0.582 | 0.358 |
| gemini1536 ↔ qwen1536 | 0.818 | 0.641 | 0.312 |
| qwen2560 ↔ qwen1536 | 0.909 | 0.909 | 0.900 |
| gemini ↔ bge | 0.955 | 0.804 | 0.630 |
| stem ↔ gemini | 0.409 | 0.577 | 0.051 |
검수 22문장에서 qwen2560과 bge의 top-1은 20/22=0.909로 동률이고 qwen의 단독 실행 p50은 더 짧았다. Gemini1536·2560은 21/22=0.955, qwen1536은 18/22=0.818, stem은 8/22=0.364였다. 임베딩 top-3는 모두 22/22이나 추천 목록·순서의 동일성은 아니다. Gemini2560↔qwen2560 top-1 일치는 20/22=0.909다. 동시 상주 선택은 A.21.8의 bge 채택과 구분한다. 정확도 출처는 단일 6셀 실행 rule-alternatives-20260918T021836Z.json이다. 후속 …022248Z.json은 4셀 부분 재실행으로 지연 표본이 다르다. docs/metrics.yml의 기존 지연 소수값이 어느 한 JSON과 모두 정확히 일치하지 않아 수치를 임의 교체하지 않았으며 지연 provenance 대조가 남는다.
초기 30문장 반복 n=3(검수 22문장과 별도, 조용한 GPU). 6셀 전부 top-1·top-3·오답 문장 집합이 3회 같았다 — 이 관측은 정확도와 오답 집합의 반복 일치이며 전체 순위 결정론을 검증한 것은 아니다. 검수 22문장의 n=3 artifact 세트는 찾지 못했다. 초기 반복의 지연 변동은 로컬 ±3ms·Gemini ±40ms였다. bge p95 1.8s 이상치는 3회 모두 117~119ms로 재현 안 됨 — 1차 실행 때의 1회성 모델 로드로 판단(확정 아님).
원문 rule-alternatives-rep{1,2,3}.json.
서버 실경로. 설정 파일 무수정, 환경변수 오버라이드로 별도 포트·테스트 DB에 기동해 실제 HTTP POST /nights/{id}/rule/preview로 확인
(판은 실제 /sessions로 생성, 회차·밤 행은 fixture 방식으로 삽입 — 하루 플레이는 생략). “민석이 스피커실로 가게 해” → 임베딩 방송실에 간다 1위(어간은 3위),
응답 233ms(행동 문구 17개 캐시 채움 포함) → 이후 85ms. ollama 차단(OLLAMA_BASE_URL=127.0.0.1:9) 상태에서 같은 문장 4회 전부 200 + 어간 순위 + warning 1줄
(임베딩 실패, 어간 겹침으로 폴백: [Errno 111] Connection refused), 6ms. 500 없음.
VRAM 동주 — 이번 gemma4+kanana 설정에서 qwen 호출 2/2에 두 모델이 축출됐다. RTX 5060 Ti 16GB: 모델 상주 gemma4:12b 약 7.51 GiB(원표기 8.06GB) + kanana1.5:8b 약 4.91 GiB(원표기 5.27GB), 장치 전체 13,402 MiB(약 13.09 GiB) 상태에서 임베딩 1회 호출 →
ollama가 model predicted to exceed available memory, evicting을 두 번 내며 Core·NPC를 모두 축출, qwen3-embedding:4b(실상주 4.37GB — 파일 2.5GB보다 크다)만 남음(2/2 재현).
다음 gemma4 호출 3.8s 재로드, kanana 6.5s. 세 모델 상주 합은 약 16.48 GiB(원표기 십진 17.7GB)로 장치 가용량보다 큼. 프로덕션(Core·NPC Anthropic)은 해당 없음. 로컬 개발·롤백 구성에서 임베딩을 켜려면
① EMBEDDING_PROVIDER=gemini(lifetutorial이 같은 이유로 08-27에 택한 길) 또는 ② 로컬 bge-m3(실상주 약 0.615 GiB(원표기 0.66GB), top-1 0.909로 qwen@2560과 동률, 당시 예산 추정 후보; 장치 총량 13,402 MiB와 모델 상주 0.66GB는 단위·포함 범위가 달라 단순 합산하지 않음, 동주 실측은 A.21.8)
중 하나다. 결정은 사용자.
7. 로컬 VRAM 예산 구성의 쌍 — bge-m3 ↔ Gemini를 같은 차원(1024)으로 (사용자 요청)
임베딩이 Gemini로 간 원래 이유가 로컬 VRAM 예산이고(§6), 이번 시험 후보 중 예산에 들어갈 것으로 추정한 모델은 bge-m3(실상주 약 0.66GB)였다. 이 절 시점에는 동주 미측정이며 A.21.8에서 확인했다. bge-m3는 1024 고정
(MRL 없음 — 1536을 요청해도 1024로 온다)이라 Gemini를 1024로 내려 같은 차원으로 쌍 비교했다. 검수 골든 22문장.
| 셀 | dims | top-1 | top-3 | p50 ms |
|---|---|---|---|---|
| ollama bge-m3 | 1024 | 0.909 | 1.000 | 106 |
| gemini-embedding-001 | 1024 | 0.955 | 1.000 | 367 |
| gemini-embedding-001 | 1536 (참고) | 0.955 | 1.000 | 363 |
| 쌍 | top-1 일치 | top-3 Jaccard | Kendall τ |
|---|---|---|---|
| gemini1024 ↔ bge-m3 | 0.955 | 0.782 | 0.594 |
| gemini1536 ↔ bge-m3 | 0.955 | 0.804 | 0.630 |
| gemini1024 ↔ gemini1536 | 1.000 | 0.955 | 0.964 |
| gemini1024 ↔ qwen2560 | 0.909 | 0.554 | 0.291 |
읽기: 검수 22문장에서 측정한 Gemini 1024·1536·2560의 top-1 적중률은 21/22=0.955로 같았다. 1024↔1536 top-1 추천 일치는 1.000이지만 Jaccard 0.955·τ 0.964여서 전체 추천 순서는 같지 않다. 초기 30문장의 1536·2560·3072 비교는 A.21.5의 별도 조건이다. 지원 범위 128~3072 전 구간을 측정하지 않았고 128 조건은 없다. 이 22문장·행동 목록에서 bge의 Gemini 순위 일치 τ(0.594~0.630)가 qwen(0.291~0.358)보다 높았지만 벡터 공간이 더 가깝다거나 유사성이 두 배라는 의미는 아니다. 로컬 bge 채택과 제출 Gemini2560 최종 구성은 A.21.8을 참조한다. 원문 rule-alternatives-20260918T022248Z.json.
8. bge-m3 채택 전 실측 — qwen과 같은 두 축(서버 실경로·VRAM 동주)
A.21.6~7의 bge 동주 가능성은 예산 추정이었다. 서로 다른 메모리 단위의 합산 추정을 아래 장치 MiB 실측과 구분한다. qwen에 했던 절차 그대로 실측했다.
서버 실경로(별도 포트·테스트 DB, 임베딩 provider=ollama·모델=bge-m3, 차원 기본 2560):
다음 사례는 HTTP 배선·순위 반환 일치 검사이며 추천의 의미 정답 판정이 아니다. A.21.6에서 뜻이 다르다고 제외한 유형도 포함하므로 검수 22문장 정확도 시험과 별개로 읽는다. 요청 차원 기본 2560과 실제 반환 1024를 구분한다.
“채연이한테 밥 나눠주지 말라고 해” → 배급을 남긴다·검진을 받는다·관찰을 설명한다, “민석이 스피커실로 가게 해” → 방송실에 간다·기록한다·가진 것을 보여준다 —
서버 응답 = 로컬 EmbeddingRank 계산 완전 일치. 첫 호출 247/257ms(행동 문구 17개 캐시 채움), 이후 126/121ms. 서버→ollama 실제 본문을 로깅 프록시로 캡처:
{"model": "bge-m3", "input": ["채연이한테 밥 나눠주지 말라고 해"], "keep_alive": "2h", "dimensions": 2560} → Qwen3 지시문 없이 원문 그대로, 응답 1024차원
(2560 요청을 bge가 무시, 오류 없음, L2 노름 1.000000). ollama 차단 시 4호출 전부 200 + 어간 순위 + warning 1줄, 6~21ms.
VRAM 동주(모델 상주 gemma4 약 7.51 GiB + kanana 약 4.91 GiB, 장치 전체 13,402 MiB → bge-m3 1회):
| 단계 | nvidia-smi | 상주 | 지연 |
|---|---|---|---|
| bge-m3 1회 | 14,187 MiB | gemma4·kanana·bge-m3 셋 다 온칩 100% | 1,163ms(첫 로드) |
| gemma4 재호출 | 14,187 | 유지 — 재로드 없음 | 355ms |
| kanana 재호출 | 14,187 | 유지 | 170ms |
| 임베딩 재호출 | 14,187 | 유지 | 109ms |
2회 반복 동일, journal evict 0줄, 장치 여유 2,124 MiB(16,311−14,187, 약 2.07 GiB). qwen(4.37GB, 둘 다 축출)과 대비된다.
판정 — bge-m3 채택. 검수 골든 top-1 0.909(qwen@2560과 동률)·top-3 1.000·Gemini와 τ 0.6·초기 30문장의 3회 정확도/오답 집합 반복 일치·실경로·폴백·해당 조건 동주를 확인.
Settings.embedding_model 기본값을 bge-m3로 바꿈(사용자 결정 2026-09-18). 최종 구성: 프로덕션 = Core·NPC Anthropic + 임베딩 Gemini@2560,
로컬 개발·롤백 = gemma4 + kanana + bge-m3@1024(설정 파일 EMBEDDING_PROVIDER=ollama 한 줄). qwen3-embedding:4b는 이번 비교에서 bge와 top-1 동률이고 단독 p50이 짧았던 후보로 남긴다. provider 한 줄 변경은 bge 기본 모델이 반영된 당시 코드 기준이다.
측정의 한계
- 골든 1차 30문장은 에이전트 작성·미검수(§1~§5), 정본은 Scenario Director 검수 22문장(§6). bge p95 이상치는 재현되지 않았고 원인은 미확정.
- ollama GGUF
qwen3-embedding:4b(2.5GB)는 lifetutorial의 sentence_transformers fp16(~8GB)과 같은 모델·다른 양자화·서빙이라 벡터가 동일하지 않다 — lifetutorial의 로컬 수치를 여기 옮겨 쓰지 않는다. - 일치도는 순위 기준이며 유사도 값의 분포(마진)는 비교하지 않았다.
9. 변경 이력
아래는 기록 추가순이며 날짜가 완전히 정렬된 표는 아니다. 당시 결정과 수치는 보존하되 2026-09-18 사후 감사에서 확인한 산술·분모·주장 범위는 본문과 함께 바로잡았다.
| 날짜 | 내용 |
|---|---|
| 2026-09-13 | 문서 생성. E7 설계 확정, E6를 Phase 2로 재배치, 부록 A 실측 3건 이관 |
| 2026-09-13 | E7 Stage 0 실행. 부록 A.5 추가. §2.2에 실측 VRAM 열 추가, §7 제약 2 해소, qwen3.5:9b-T 탈락 반영 |
| 2026-09-13 | E7 Stage 1 실행. 부록 A.6 추가. 지연을 Hard 게이트로 승격(C11~C13), 통제 4→10개 정정, EGR을 판정 축에서 강등, Gemini raw/effective 지연 분리. 8셀 → 4셀 |
| 2026-09-13 | E7 Stage 2 실행 — 공식 수치. 부록 A.7 추가. Gemini는 페이싱으로 C11·C13 탈락, gemma4:12b-N에서 측정 극성쌍에서 RM1이 재현되지 않았으나 evaluator_verdict 손실로 비열등 아님. 자작 evaluator 통제의 한계 기록. Stage 3·4 미실행 |
| 2026-09-14 | E7 Stage 2 보강 — evaluator 통제 교체 재측정. 부록 A.8 추가. 자작 3건 → 실플레이 14건(사전 등록), --roles evaluator 168콜. 기준선 1.00→0.36, gemma4:12b-N 0.67→0.57로 우열 역전. partial 경계 4셀 공통 실패, 덩어리 칸 취약점 확인 |
| 2026-09-14 | E7 Stage 3 실행 — concurrency. 부록 A.9 추가. --stage concurrency 러너 구현. 두 후보 모두 NPC와 교대 구간 무축출 공존(12.34 / 7.89 GiB). e4b 초기 로드의 NPC 1회성 축출 관측 기록. exaone 상주 실측 4.83 GiB로 §2.2 갱신 필요 |
| 2026-09-14 | E7 Stage 4 실행 — loop. 부록 A.10 추가. --stage loop 러너 구현(scripts/loop_app.py 래퍼로 프로덕션 무수정 thinking 주입, 테스트 DB). 두 후보 모두 1회차 완주·크래시 0·폴백 0/17·thinking 0자로 게이트 통과. metrics.yml stage: loop 2줄 적립. Funnel Stage 0~4 전부 완료 — 남은 것은 결정 항목(D1~D3)과 §5.3 비열등 선언 여부 |
| 2026-09-14 | D1 실측·해소 — Gemini 쿼터 프로브. 부록 A.11 추가. 페이싱 없이 40콜/55.4s(약 43 RPM) 429 0건 — 키는 유료(사용자 확인). 합성 산식의 RPM ≥20에서 C11·C13 판정 변화 계산했으나, 사용자 결정으로 판단 기준을 무료 티어 한도(10 RPM)로 고정 — 페이싱 유지, Gemini 탈락 판정 유효, D1 해소. 이후 키를 실제 무료 티어로 교체, 재프로브에서 초소형 콜 20~28s·503 관측(A.11 추록) |
| 2026-09-14 | Core 채택 방향 기록 (교체는 보류). 게임 관점 권고 = gemma4:12b-N — 이번 극성쌍을 모두 통과한 유일한 로컬 셀, 실플레이 evaluator 기대 일치 최상(0.57)·판정 요동 0, 지연은 전 게이트 안. 사용자 결정: 기록만 하고 교체하지 않는다. E6(NPC descent) 결과를 먼저 본다 — NPC를 exaone(4.83 GiB)보다 작은 모델로 내릴 수 있으면 12b Core의 VRAM 여유 문제(pair 12.34 GiB, 여유 ~2.9 GiB)가 구조적으로 해소되기 때문. D2(운영 Gemini thinking OFF)는 Core를 Gemini로 유지할 경우에만 유효한 결정으로 보류 |
| 2026-09-14 | E6 Formal 실행 — NPC descent. 부록 A.12·A.13 추가, §8.2 블로커 4건 해소. run_model_descent.py 구현(judge 3표·D3, thinking 강제 OFF). ON/OFF 10셀 × n=5: 이번 OFF 5셀에서 행동 분류 기준 통과 없음(OFF 전 셀 collapse≠none), 정책 ON에서 7세 구간은 측정 사다리상 4B 하나(2B 이하 3세화·7.8B/9B 어른화 신호). qwen3.5:4b — 5항목 전부 ≥0.7·누설 0·p95 2,290·2.98 GiB. 후속: 4b+gemma4:12b pair 10.49 GiB 무축출(여유 ~4.1 GiB), 루프 스모크 완주·폴백 0/17. 4b 한자 혼입 2/25 관측(후속 과제) |
| 2026-09-14 | Core 채택 반영 — gemma4:12b-N 전환. §5.3에 채택 선언. Core 설정 3항목 전환(think off 신설), 프로덕션 think 제어 반영(ollama_llm.py think 인자 · Settings 2필드 · llm_factory 배선, 테스트 7건, 전체 393 passed). 실서버 검증: health ollama:gemma4:12b, 아침 loop 벽시계 12.13s(planner p95와 측정 범위 다름). D2는 Gemini 이탈로 대상 소멸 |
| 2026-09-14 | NPC 발화 품질 A/B — 부록 A.14. run_npc_quality_ab.py(쌍대 블라인드, 3표×스왑 2회). exaone 우세: 자연스러움 18:3·관련성 18:3·페르소나 14:2. 4b 우세는 7세 말투 15:7뿐(9.1자 단답과 함께 관측, 원인 미분리 — judge 편향 방향이라 단독 근거 배제). 4b 같은 응답의 한자 혼입 2/25 재집계. E6(정책)과 A.14(품질)는 반대 방향 트레이드오프 — NPC 결정은 이 두 축의 선택 |
| 2026-09-14 | 항목 2 판정 스펙 v2 재판정 — 부록 A.15. Scenario Director 스펙 확정(“아이도 근거 없는 지어내기·딴 얘기는 한다”)으로 judge 기준이 정책보다 엄격했던 결함을 수정, A.12 ON transcripts 재판정(재생성 없음). exaone 0.4 유지(FAIL 전건이 다단계 인과 어른 설명 — 해당 응답의 v2 분류 유지), 9b 0.6→0.8(탈락 사유는 p95라 유지), 0.8b 0.8→0.4(비문 필터). NPC 결정 구도 불변. 이후 age7 실행은 v2 기준 |
| 2026-09-14 | E6 확장 — exaone3.5:2.4b 평가 (부록 A.16). 사용자 지시. 셀(1/5, 양방향 동시 붕괴 — 시험한 두 크기에서 관련 항목 미달, ON−OFF 격차 0) + 품질 A/B 2건(7.8b에 3항목 열세·83.5자 장광설, 4b와 자연스러움 승수 9:10, 동등성 미검증) + pair 9.25 GiB(여유 ~6.0). 세 축 동시 해결 후보 아님 — 결정 구도는 7.8b(품질) vs 4b(정책+VRAM) 유지. AB 러너에 --raw-b 추가(서로 다른 원문 간 쌍대) |
| 2026-09-14 | NPC 상업 라이선스 조사 + 상업 후보 3종 평가 (부록 A.17). 사용자 지적(“엑사온 상업 불가”)으로 조사 — EXAONE 3.5는 당시 조사에서 연구용으로 분류(공식 조항·리비전 확인 한계는 A.17), 현행 NPC는 라이선스 블로커. 상업 가능 후보 실측: kanana1.5:8b(Apache) 4/5·자연스러움 승수 9:7, 무효 9·혼입 0·pair 12.42(VRAM 이득 없음, 항목1 0.6은 민감 사실 회피 1케이스), gemma4:e4b 3/5(사실 0.4), midm2.0:mini 3/5(2B급 3세화 동류). Gemma 4는 당시 Apache 2.0으로 분류했으나 공식 LICENSE 근거 보완 필요. 결정 구도가 “exaone(연구 한정) vs 상업 후보(kanana=품질축 근접 / qwen4b=VRAM축)”로 재편 |
| 2026-09-14 | NPC 채택 반영 — kanana1.5:8b-q4km (사용자 결정, §5.3·A.18). 교체 전 루프 스모크 통과(완주·폴백 0/16·관리 항목 3건 깨끗) 후 NPC 모델 설정 한 줄 전환, 실서버 health·실콜 검증. 어댑터 패턴 확인(프로덕션 하드코딩 없음) — 롤백은 설정 한 줄, exaone3.5:7.8b는 롤백용 유지. 평가용 임시 모델 2종·GGUF 원본 삭제(~9GB 회수). 모델 선정(E7 Core + E6/확장 NPC) 전부 완료 — 운영 구성: Core gemma4:12b-N · NPC kanana1.5:8b · embedding gemini |
| 2026-09-14 | 채점 파이프라인 변경 — 이후 evaluator 측정은 새 기준. 실판 da38de28에서 동일 제출 70.8→59.6 요동(identity-1) 진단 → ① evaluator temp 0은 기배선 확인(그럼에도 요동 — 회귀 테스트 고정) ② scoring_rules.apply_ratchet 단조 잠금(같은 문장 유지 시 하향 금지, ratcheted 표시) ③ judge_candidates 문장 단위 후보(8칸 UI 유지, 덩어리 칸 해소 — A.8의 측정 모델에서 남은 덩어리 칸 문제에 대응한 변경이며 일반적 해결 검증과 구분). 실판 오프라인 검산: 요동 케이스 차단(59.6→70.8 유지). 402 passed |
| 2026-09-16 | 외부 API 전환 결정 기록. 로컬 채택 구성(Core gemma4:12b-N·NPC kanana/gemma4)은 유효하며, 전환 사유는 해커톤 심사용 서빙 제약(GPU 1장 순차 추론·동시 사용자 수 미측정·홈서버 가용성·클라우드 GPU 비용)이다. 로컬 vs Anthropic 비교 프로토콜(실행별 러너 변경·게이트 예외를 구분한 기술적 비교)을 문서 상단에 정의, 결과는 부록 A.19 예정. 키는 측정 당일만 투입. 비용 근거 docs/apiscenario.md. Anthropic 어댑터 추가(AnthropicLLM, provider anthropic) |
| 2026-09-14 | AGE7 프롬프트 v2 + 대화 메모리 창 — 이후 7세 계열 측정은 새 기준. 사용자 확정 7세 정의(“몰라로 끝내지 않는다 — 본 것·하고 싶은 말을 붙인다”)를 정책 2·3·7에 반영하고 인물별 “직접 본 것”을 페르소나에 추가. 1차 문구는 망각을 0.8→0.0으로 붕괴시켜(4턴 전 사실 회상) 문구 정밀화 + 정책 ON 시 메모리 마지막 두 교환만 제공하는 구조 절단으로 교정. kanana 재측정(n=5·judge 1표): [0.6·1.0·1.0·1.0·1.0] 4/5 통과·누설 0. 신의 질문 해금(advisor_leads 10건)·dormant 탐사 행동 5종·직접쓰기 대안 제시도 이 회차 — 이전 E6 수치와 직접 비교 금지 |
| 2026-09-16 | 외부 API 평가 실행 — 부록 A.19. Core PCA·극성·eval 게이트는 Sonnet 5는 n=1·n=3, Opus 5는 n=1에서 통과, 지연 게이트는 Sonnet이 n=1↔n=3 사이 경계에서 뒤집히고 Opus는 미달. self-play 완주·폴백은 Opus·gemma4 대조군 0, Sonnet은 planner의 maxItems 미시행 1건. NPC 대화 의미 품질은 선별 10케이스의 개선 관찰과 재생성 0 미충족. NPC 7세 정책은 채점기를 gemma4:12b(think off)로 통일해 재측정한 뒤 kanana·Haiku·Sonnet 전부 4/5 통과(1차 gemma3 채점기 결과는 원인 미확정, VRAM 경합 가설). Gemini 무료 키는 하루 20요청 상한으로 NPC 후보에서 제외. 권고: Core claude-sonnet-5 · NPC claude-haiku-4-5(최종 결정은 사용자 몫). 어댑터 결함 2건 수정(Haiku temperature extra_body 전송, gemma4 채점기 think 기본값 off). 상단 “측정 결과” 열·metrics.yml 병행 기록 |
| 2026-09-17 | 제출 조합 확정. Core claude-sonnet-5 + NPC claude-haiku-4-5 조합 self-play 5회차 1판 확인(11.7→29.8, 261.5s, 폴백 0, planner 재생성 1, 메타 누설 0) — A.19 §2 끝. 사용자 지시로 이 조합을 제출용 구성으로 고정. A.19 §2 gemma4 대조군 advisor 재생성 수 정정(3→2) |
| 2026-09-18 | 임베딩 슬롯 — 부록 A.20 추가. 로컬 vs 외부 비교를 하지 않은 이유를 기록: 채점 RAG용 임베딩 랭킹 경로가 2026-09-06 채점 밸런스 조정에서 제거됐다(top-3 절단이 저득점 원인 — 골든 세트 정답형 65.3→100.0). 코드·DB 실사로 확인 — get_embedding() 호출 0·.embed() 0·유사도 질의 0·truth_claim_embeddings 0행·/health에 표시 없음, EMBEDDING_FALLBACK은 읽히지 않는 설정. 두 번째 용도였던 search_notes는 미구현(ask_npc만 실재). 이 시점 운영 구성은 Core·NPC 2슬롯(같은 날 후속 A.21.8에서 3슬롯 확정)으로 정본화하고, 기획서 확정본 v9.0(§11.5·§11.6)·AI_Agent_Evaluation PART1(§5 Anchor)·팀원용 쉬운설명(§49)에 원문 수정 없이 같은 경위를 추가 기록. apiscenario.md의 “임베딩 무료 키 한도 제출 전 확인 필요” 과제는 전제가 틀려 정정 |
| 2026-09-18 | 임베딩 부활 한 자리 + 5셀 비교 — 부록 A.21. 사용자 결정으로 직접 쓰기 규칙 대안 순위에만 임베딩(Strategy·예외 시 어간 폴백, lifetutorial 방식). n=30 골든(에이전트 작성): stem top-1 0.300 / qwen@2560 0.933 / qwen@1536 0.867 / bge 0.900 / gemini 0.967, top-3 임베딩 전 셀 1.000. 순위 일치도 qwen2560↔1536 τ 0.92(top-1 28/30→26/30 감소), gemini↔qwen top-1 0.87~0.93·τ 0.33(정답 top-3 적중률과 추천 목록 동일성은 다름). 당시 provider 결정 대기; 같은 날 최종 bge/Gemini 확정은 A.21.8. 같은 날 A.19 후속 구현 완료 — planner beats 절단(+advisor 2필드, 958 passed), 러너 3종 --provider anthropic. 제출 Sonnet/Haiku의 채점 캘리브레이션·누설·원숭이손 세 축 실험 결과는 미확인 |
| 2026-09-18 | 전수 감사 정정. 역할별 evaluator 기준 2,918→2,179ms, A.17 무승부 및 A.16 A/B 범주 복원, 372문항 실행/369 provider attempts 구분. 검수 22문장과 초기 30문장 n=3 분리. 최종 제출 Sonnet/Haiku+Gemini2560, 로컬 gemma4/kanana+bge1024 확정(A.19·A.21.8); 제출 세 축 후속 실험은 미확인으로 유지. |
| 2026-09-18 | 제출 조합 세 축 실측 — 부록 A.23. Sonnet 5 채점 캘리브레이션 정답형 100·절반형 54.8·오답형 0(오답 무득점, 절반형만 밴드 +4.8, n=1). Haiku 4.5 누설 on 0%/off 50%(적발 14). self-play 5회차 4.4→8.8→0.0, 오류·폴백 0, 이벤트로 claude 모델 사용 확인. paw_eval 수락 11 Δ −0.8. 러너 --model 미지정 시 Anthropic 404가 0점으로 집계되던 것 가드 추가(4a8519a), 가짜 metrics 2줄 되돌림. F11은 미결(사람 판 필요) |
측정하지 않은 것은 주장하지 않는다. 재현성은 정확도가 아니다. 그리고 선택 실험에 하나 더 — 이기는 모델을 찾는 것이 목적이 아니라, 지금 쓰는 모델을 바꿀 이유가 있는지 아는 것이 목적이다.
A.23 2026-09-18 · 제출 조합(Sonnet 5 / Haiku 4.5) 세 축 실측 — 채점 캘리브레이션·정체 누설·self-play(원숭이손)
A.19·A.21.8에서 “제출 조합의 채점·누설·원숭이손 세 축은 미확인”으로 남겨둔 것을, 최종 시나리오·오늘 변경(신의 질문 3회 고정·점진 공개, 준 손목띠 설명, 밤 기록 파편 정리)까지 반영한 서버에서 키를 넣고 한 번에 실측했다. 서버 구성은 get_settings() 실측 anthropic claude-sonnet-5 | anthropic claude-haiku-4-5 | gemini. 러너는 A.21의 --provider anthropic 경로.
실수 1건(먼저 기록). run_scoring_calibration·run_leak_test의 --model 기본값이 ollama 모델명이라, --provider anthropic만 주면 Anthropic에 gemma4:12b/exaone3.5:7.8b로 호출해 404가 러너 안에서 0점으로 집계되고 metrics.yml에 가짜 줄 2개가 붙었다(20:08, 되돌림). 이후 runner_common.make_provider_llm이 claude-가 아닌 모델명을 받으면 즉시 중단하게 고쳤다(4a8519a). 아래 수치는 모델명을 맞춘 재실행.
1. 채점 캘리브레이션 — Core claude-sonnet-5, 골든 3세트, n=1, 후보 전수
| 세트 | 원인 | 동기 | 정체 | 총점 | 게이트 |
|---|---|---|---|---|---|
| 정답형 | 100 | 100 | 100 | 100.0 | ≥60 통과 |
| 절반형 | 50 | 21 | 100 | 54.8 | 30~50 → 4.8 초과 |
| 오답형 | 0 | 0 | 0 | 0.0 | <30 통과 |
편차 게이트(≤10%p) 통과. gate_pass=false는 절반형 상한 초과 하나 때문이다. 대조: 09-06 gemma3:12b 캘리브레이션은 정답형 65~100·절반형 57~83·오답형 19~45로 어느 실행도 게이트를 통과하지 못했다. Sonnet 5는 오답형 0(잘못된 문장에 점수를 주지 않음)·정답형 100으로 지금까지 가장 좋은 프로파일이고, 절반형이 밴드 위로 4.8 넘는 것은 정체 칸을 100으로 준 한 판정 때문이다(n=1이라 단일 표본). 채점 기준(가중치·판정)은 손대지 않는다 — 사용자 결정(2026-09-18, “점수 오르면 안 바꿔도 됨”).
2. 정체 누설 — NPC claude-haiku-4-5, n=2/발화, 탐침 5, 7세 정책 on
| 조건 | 발화 | 누설 | 누설률 | 하네스 적발 |
|---|---|---|---|---|
| SYSTEM_HARNESS on | 10 | 0 | 0.00 | 14 |
| SYSTEM_HARNESS off | 10 | 5 | 0.50 | 0 |
하네스 off일 때 Haiku 4.5는 50%로 kanana(09-14, 20%)·exaone(09-06, 40%)보다 더 흘리지만, on이면 0%로 완전히 막힌다(적발 14 = 한 발화에 여러 번 걸린 경우 포함). 제출 구성은 항상 on.
3. self-play 1판(5회차) + 원숭이손 — 서버 Sonnet 5/Haiku 4.5, 플레이어 로컬 gemma4:12b(성실)
판 5548a458(20:08~20:12, 274.0s). 회차 점수 4.4 → 4.4 → 8.8 → 0.0 → 0.0, 폴백 0·재생성 0·오류 0. harness_event 모델 표기 claude-sonnet-5 ×37·claude-haiku-4-5 ×6(서버가 실제로 Anthropic을 썼음을 이벤트로 확인). 원숭이손 1회차 제안 수락. 대조: 09-17 같은 서버 조합 self-play 11.7→29.8(A.19).
run_paw_eval(attempt 12개, 이벤트 11건): 수락 11 / 거부 0, Δ 계산 가능 7, 다음 회차 평균 Δ −0.8 — 원숭이손을 받아도 다음 회차 점수가 오르지 않는다(설계상 대가가 숨어 있고 소원은 단서 방향을 살짝 틀어 주는 것이라, 점수 이득이 없는 것 자체는 설계와 어긋나지 않는다).
읽기. 이 self-play는 F11(목표 점수대) 판단 재료가 못 된다 — 로컬 플레이어가 신의 질문을 한 번도 쓰지 않아(로그에 질문 0) 오늘 넣은 “물을수록 더 드러냄”(F5)이 전혀 작동하지 않았고, 4~5회차 0.0은 확인된 문장을 지우고 새로 쓴 결과(테스터10 F1과 같은 궤적, 플레이어가 accepted_claims를 읽지 않음). 사람 판(테스터12, F5 이전) 27.5와 비교 불가. 테스터12가 3회차 이후 질문을 안 쓴 것도 환급으로 횟수가 안 줄어 표시가 헷갈렸기 때문(사용자 보충 20:20)이라, 질문 기능 자체의 문제로 읽지 않는다. 크래시·폴백·누설 축에서는 제출 구성에 이상이 없다.
4. 결론
| 축 | 결과 | 판정 |
|---|---|---|
| 채점(Sonnet 5) | 정답형 100 · 절반형 54.8 · 오답형 0 | 오답 무득점·정답 만점, 절반형만 밴드 +4.8(n=1). 기준 유지 |
| 누설(Haiku 4.5) | on 0% / off 50% | 하네스 필수, on이면 완전 차단 |
| self-play | 4.4→8.8→0.0, 오류 0 | 안정성 OK, 난이도 판단 재료 아님 |
| 원숭이손 | 수락 후 Δ −0.8 | 설계와 모순 없음 |
F11 목표 점수대는 오늘 결정하지 않는다(사용자 결정: F5 반영 뒤 점수를 보고 판단 — 사람 판이 한 번 더 필요). 제출 구성 그대로 간다.
5. 같은 코드에서 로컬 조합과 나란히 (사용자 요청, 20:17~20:19)
로컬은 gemma4:12b(Core/채점)·kanana1.5:8b-q4km(NPC), 서버 코드는 §1~3과 같은 1e42e78 이후. 채점·누설 러너는 모델을 직접 부르므로 서버(Anthropic 유지) 재기동 없이 측정. self-play 로컬 판은 같은 코드로 19:57에 돌린 ccdf041b.
| 축 | 로컬 gemma4 / kanana | Anthropic Sonnet 5 / Haiku 4.5 | 읽기 |
|---|---|---|---|
| 채점 정답형 | 100.0 | 100.0 | 동일 |
| 채점 절반형 | 54.8 (원인 50·동기 21·정체 100) | 54.8 (원인 50·동기 21·정체 100) | 칸별까지 동일 — 판정이 같고 밴드 +4.8도 같은 원인(정체 100) |
| 채점 오답형 | 14.6 (동기 42) | 0.0 | gemma는 틀린 문장의 동기 칸에 점수를 줌, Sonnet은 무득점 |
| 채점 소요 | 67s | 150s | |
| 누설 하네스 on | 0.00% (적발 9) | 0.00% (적발 14) | 둘 다 완전 차단 |
| 누설 하네스 off | 30% | 50% | Haiku가 맨몸으로는 더 흘림 — 하네스 의존도 높음 |
| 누설 소요 | 26s | 58s | |
| self-play 5회차 | 0.0·0.0·0.0·8.8·0.0 | 4.4·4.4·8.8·0.0·0.0 | 둘 다 로컬 gemma 플레이어·신의 질문 0 — 난이도 재료 아님 |
| self-play 소요 | 306s | 274s | Anthropic이 더 빠름(로컬은 3모델 동주) |
| 오류·폴백 | 0 | 0 | |
| 원숭이손 Δ(1→2회차) | 0 | 0 | run_paw_eval은 로그 12판 합산(−0.8)이라 판별 분리 불가, 로그에서 직접 읽음 |
읽기. 제출 조합의 이점은 딱 하나에 집중된다 — 오답 무득점(오답형 0 vs 14.6). 정답·절반형은 칸별로 동일해 채점기 차이가 아니라 골든 세트·매칭 규칙이 결정한 값이다. 누설은 하네스가 켜져 있는 한 차이가 없고, 끄면 Haiku가 더 위험하다(제출은 항상 on). 지연은 온라인이 빠르다. 로컬 롤백(A.21.8 설정 파일 한 줄)은 오답형 관대함을 감수하는 선택이 된다.
7. 2차 실측 — 원숭이손 ① 아침 억제(155a15b) 반영 뒤 두 조합 재측정 (사용자 요청, 20:42~20:55)
같은 절차를 최신 코드로 다시. self-play는 서버를 조합별로 재기동해 각 1판(플레이어는 둘 다 로컬 gemma4:12b, 로컬 판은 환경변수 덮어쓰기로 ollama 구성, 끝난 뒤 Anthropic 복귀 확인).
| 축 | 로컬 1차 → 2차 | Anthropic 1차 → 2차 | 읽기 |
|---|---|---|---|
| 채점 정답형 | 100 → 100 | 100 → 100 | 동일 |
| 채점 절반형 | 54.8 → 54.8 | 54.8 → 59.2 (원인 50→62) | Sonnet이 원인 칸 부분 인정을 한 번 더 줌 — n=1 변동, 밴드(30~50) 상한 초과는 그대로 |
| 채점 오답형 | 14.6 → 14.6 | 0 → 0 | 로컬만 틀린 문장의 동기 칸에 점수 |
| 누설 on / off | 0% / 30% → 0% / 20% | 0% / 50% → 0% / 60% | on은 둘 다 0. off는 n=2·발화 10이라 ±10%p 노이즈 |
| self-play 5회차 | 0·0·0·8.8·0 → 0·0·0·0·0 (303.9s) | 4.4·4.4·8.8·0·0 → 4.4·0·4.4·0·8.8 (289.5s) | 둘 다 로컬 플레이어·신의 질문 0 — 난이도 재료 아님. 오류·폴백 0 |
| 원숭이손 ① | 수락 | 수락 | 2회차 1비트 관찰이 억제 서술 “채연은 오늘 쟁반을 옆으로 밀지 않는다…”로 남고 R2 obeyed(DB 이벤트 확인, Anthropic 판 58e0173f) |
읽기. 1차와 같은 그림이다: 제출 조합의 이점은 오답 무득점, 누설은 하네스 on이면 둘 다 0, 온라인이 조금 빠르다. ① 아침 억제는 채점·누설과 무관한 시나리오 데이터라 그 축에는 변화가 없고, DB 관찰 이벤트에서 2회차 1비트가 “채연은 오늘 쟁반을 옆으로 밀지 않는다”로 남고 채연이 “나 배급 안 밀어냈는데. 오늘도 먹었어.”라고 답하는 것으로 반영을 확인했다(self-play 러너 로그에는 장면 서술이 남지 않는다).
6. 판당 비용 실측 (사용자 질문 “한 판 비용은?”, 20:25)
self-play 판 5548a458의 harness_event 92건에 남은 실제 프롬프트·출력을 messages.count_tokens로 세고 정가(Sonnet 5 $2/$10, Haiku 4.5 $1/$5 per MTok, 캐시 없음)를 곱했다.
| 모델 | 역할 | 호출 | 입력 tok | 출력 tok | $ |
|---|---|---|---|---|---|
| Sonnet 5 | evaluator_verdict(밤 채점) | 50 | 62,575 | 4,675 | 0.172 |
| Sonnet 5 | manager_check | 12 | 64,398 | 1,255 | 0.141 |
| Sonnet 5 | planner | 5 | 10,063 | 4,761 | 0.068 |
| Sonnet 5 | advisor_answer | 4 | 8,228 | 1,422 | 0.031 |
| Sonnet 5 | classifier·manager_paw | 11 | 3,861 | 210 | 0.009 |
| Haiku 4.5 | agent(NPC) | 10 | 33,730 | 1,091 | 0.039 |
| 합계(기록분) | 92 | 0.461 |
NPC 발화는 하네스에 10건만 남았지만 실제 발화 95건이 각각 Haiku 호출(입력 ~3.4k tok)이라고 보면, 테스터12 규모(발화 113·질문 6·밤 5)로 환산 시 판당 약 $0.9~1.2 — NPC $0.44(최대 항목), 밤 채점 $0.17, 관리자 검사 $0.14~0.35(장면마다 도는지에 따라), 분류 $0.08, 계획 $0.07, 질문 $0.05. 09-17 HANDOFF 추정 ~$0.8과 같은 자릿수.
첫 최적화 후보(제출 후). 프롬프트 캐시를 전혀 쓰지 않는다(cache_read 0). NPC 시스템 프롬프트 ~3k tok이 발화마다 반복되므로 캐시하면 Haiku 입력 $1→$0.10/MTok, NPC $0.44→약 $0.08, 판당 약 $1.0→$0.6. 어댑터 cache_control 한 줄.