OpenRouter 모델로 RP 캐릭터 카드를 A/B 테스트하기.
같은 카드가 Claude Opus 에서는 생생하게 살아 있다가 GLM 에서는 고객센터 봇이 된다. 어떻게 체계적으로 테스트하고, 진짜 어울리는 모델을 고를까 — 방법, 비용, 그리고 당신을 속이려 드는 함정까지.
같은 카드, 다른 영혼
카드를 한 번이라도 공유해 본 창작자라면 안다. 4,000 토큰짜리 페르소나를 쓰고, 자주 쓰는 모델에서 그리팅을 마음에 들 때까지 다듬고, 친구가 다른 host 에 같은 카드를 올렸더니 「예의는 바른데 평면적」, 「자꾸 제4의 벽을 깬다」, 「뭐든 거절한다」고 보고하는 그 느낌.
카드는 그대로다. 바뀐 건 모델이다. 정렬(alignment) 학습, 기본 샘플러 동작, 지시 추종 편향, 긴 컨텍스트 감쇠 — 이 모든 것이 캐릭터를 다른 방향으로 민다. 정직한 태도는 「어떤 모델이 제일이냐」를 논쟁하는 게 아니라 「이 카드에 어떤 모델이 제일이냐」를 측정하는 것.
감 평가로는 부족한 이유
대화 한 번은 표본 크기 1. RP 카드 평가에서 감만으로는 적극적으로 잘못된 결론으로 이끄는 세 가지 문제가 있다.
- 페르소나 드리프트. 어떤 모델은 20 턴을 버티고, 어떤 모델은 6 턴 만에 RLHF 기본값으로 미끄러진다. 긴 시나리오를 실제로 돌려야 보인다.
- 거절 경계(refusal surface). 갈등, NSFW, 신체 공포, 회색 윤리 선택 — provider 마다 선이 다르고 system prompt 표현에 따라 움직인다. 「한 번 거절당했다」는 관찰은 정보량이 거의 없다.
- 톤 지문. 두 모델 모두 인물을 유지하면서도 한 쪽은 장황하고 진지하게, 한 쪽은 짧고 건조하게 응답할 수 있다. 어느 쪽이 어울리는지는 취향 판단이지만, 비교 가능한 출력이 있어야 결정할 수 있다.
해법은 벤치마크가 아니다 — 벤치마크는 추론을 잰다, RP 가 아니다. 해법은 작고 고정된 시나리오 세트를 여러 모델에서 재생하고, 더 강한 심사 모델로 채점하는 것.
최소 실용 방법
쓸 만한 평가에는 네 가지 부품이 필요하다. 시나리오, 실행 횟수, 심사 모델, 평가 기준(rubric). 화려할 필요 없다. 핵심은 일관성.
- 시나리오 5~8 개. 카드의 표면을 덮도록 고르자: 일상 상호작용, 위기/감정 폭발, NSFW 나 경계 상황(해당 시), 가치관 충돌, 장기 기억 테스트(20 턴 전 떡밥을 회상시키기).
- 시나리오당 5~10 회. 생성은 확률적이라 표본 1 은 잡음. 5 가 「모델을 구분할 수 있다」의 하한선, 10 이 안정적.
- 피험 모델보다 강한 심사 모델. self-eval 은 편향된다 — 모델은 자기 출력을 체계적으로 높게 평가한다. 별도의 강한 심사 모델(Claude Sonnet, GPT 급, Gemini Pro 급)에 카드 · 시나리오 · 후보 응답을 함께 넘긴다.
- 평가 차원 4~6 개. 인물 일관성, 거절/탈선, 톤 일치, callback 정확도, 문체, 종합 점수. 각 1~5 점 + 한 문장 이유. 실제로 읽는 건 이유 쪽.
이건 벤치마크라기보다 시식에 가깝고, 그게 요점이다. 리더보드를 발표하려는 게 아니라, 카드 공개 페이지에 「추천 모델」 뱃지로 claude-opus 를 적을지 gemini-flash 를 적을지 결정하려는 것.
왜 OpenRouter 가 맞는 하네스인가
SDK 다섯 개 붙이고, 키 다섯 개 관리하고, 메시지 포맷 다섯 종을 직접 정규화할 수도 있다. 한 번 해 본 사람은 보통 두 번은 안 한다. OpenRouter 는 모델 동물원 앞에 OpenAI 형태 API 한 겹을 씌우고, 응답에 모델별 가격을 실어 주며, provider 교체는 문자열 하나 변경.
구현은 이만큼 줄어든다:
POST https://openrouter.ai/api/v1/chat/completions
Authorization: Bearer $OPENROUTER_API_KEY
{
"model": "anthropic/claude-3.5-sonnet",
"messages": [
{ "role": "system", "content": cardToSystemPrompt(card) },
{ "role": "user", "content": scenario.opening }
],
"temperature": 0.85,
"max_tokens": 600
}model 을 google/gemini-2.0-flash, deepseek/deepseek-chat, x-ai/grok-2, z-ai/glm-4.6 로 바꾸기만 하면 나머지 코드는 그대로. 호출당 토큰 비용은 usage 블록에 들어와서 별도 billing API 없이 합계를 낼 수 있다.
봉투 뒤편의 비용 추정
구체적인 예. 시나리오 입력 평균 3,000 토큰(카드 + system + 시나리오 이력), 출력 500 토큰. 시나리오 8 × 시도 5 × 후보 6 × 생성당 심사 1 회:
- 생성: 8 × 5 × 6 = 240 회. 혼합 평균으로 입력 $1~$3/M 토큰, 출력 $3~$15/M 토큰 가정 시, 일반적인 중간대 믹스로 대략 $2~$6.
- 심사: 240 회, 각 회에 카드(약 3k) + 응답(약 500)을 읽고 짧은 평(약 150) 반환. Haiku 급 심사로 추가 $1~$2.
- 합계: 매트릭스 평가 한 바퀴 $10 이하. 카드 수정할 때마다 다시 돌릴 수 있다.
이 숫자들은 설명용이지 견적이 아니다 — OpenRouter 가격은 매주 움직이고 provider 간 격차도 크다. 핵심은 자릿수: 진지한 평가가 커피 한 잔 값에 해결된다는 사실.
당신을 속이는 세 함정
거의 모든 사람이 첫 시도에서 밟는 지뢰:
- 심사 모델은 긴 글을 좋아한다. LLM 심사에 「품질」을 물어보면 체계적으로 더 길고 장황한 응답을 선호한다. 프롬프트에서 간결성을 명시적으로 요구하거나, 길이 정규화 단계(임계치 초과 시 감점)를 추가해야 한다. 그렇지 않으면 글자 수를 재는 셈.
- system prompt 해석이 다르다. 어떤 모델은 길고 구조화된 system prompt 를 충실히 따르고, 어떤 모델은 권고 정도로 취급한다. XML 태그를 적극적으로 쓰는 카드는 Claude 에서 완벽하게 보이고, 태그를 모르는 모델에서는 혼란스러워진다. 어떤 모델이 단순화된 시스템 렌더링을 필요로 하는지를 평가 결과의 일부로 기록할 것 — 잡음이 아니다.
- temperature 0 이 결정적인 게 아니다. provider 쪽 배칭, prompt cache, tie-break 가 temperature: 0 에서도 변동을 만든다. 표본은 어쨌든 여러 개 뽑자. 릴리스 블로커급 재현성이 필요하면 API 가 지원하는 곳에서 seed 를 고정하고, 「거의 같음」이 현실의 상한이라는 것을 받아들이자.
Studio 에서 만들고 있는 것
위 방법은 OpenRouter API 에 익숙한 사람이라면 오후 셋 분량의 글루 코드. 그 외의 사람들 — 그리고 미래의 글루 유지 보수를 피하고 싶은 자신 — 을 위해, 이게 바로 tavernai.cards Studio 가 다듬고 있는 모양.
- 시나리오 라이브러리 — 카드별 재사용 시나리오 템플릿, 일상/위기/충돌/장기 callback 같은 흔한 원형이 기본 탑재.
- 매트릭스 러너 — OpenRouter 카탈로그에서 후보 모델 선택, 시나리오당 실행 횟수 설정, 나머지는 알아서 돌아간다.
- 내장 심사 — Claude 급 심사 + 길이 정규화 평가 기준, 장황함을 최적화하지 않도록 보장.
- 자동 리포트 — 모델별 강점, 가장 약한 시나리오, 공개 페이지에 달 「추천 모델」 제안.
Studio 는 유료 티어(주당 커피 한 잔 정도)이며 매트릭스 러너가 중심. 무료 티어는 이전 폴리오에서 소개한 linter, converter, 멀티 host 내보내기를 계속 제공.
정말 아끼는 카드가 있다면 그게 어떤 모델에서 가장 빛나는지 알고 싶을 것이다. 두루마리에 이름을 남기면 Studio 는 차례로 열어 간다.