Meridian 경유 Hermes의 out of extra usage 400은 system prompt 특정 텍스트가 트리거다
·수정 2026.06.03·수정 2회
요약
- Claude 구독을 Meridian으로 프록시해 Hermes(third-party 하네스)를 쓸 때, 도구가 포함된 Hermes 실요청이
400 You're out of extra usage로 막힌다 - 실험으로 확인된 트리거는 키워드·도구개수·프롬프트 크기가 아니라 system prompt의 특정 텍스트였고, 워크어라운드는 하네스 프롬프트를 빼고 genuine Claude Code 프롬프트만 보내는 것(
clientSystemPrompt=false)이다 - (2026-06-03 갱신) 추가 bisection으로 트리거를 단일 블록
MEMORY_GUIDANCE로 국소화 → 그 한 블록만 제거하면clientSystemPrompt=true(페르소나·도구 유지)로도 통과한다("통째로 갈아야 한다"는 부분 반증). 아래 추가 검증 섹션 참조
본문
배경
- 2026-05 Anthropic 정책: programmatic/Agent SDK 사용분이 구독 plan 한도가 아니라 별도 월 credit pool에서 차감(6/15 시행). Pro 100/Max20x $200.
- 400 응답 메시지는
You're out of extra usage. Add more at claude.ai/settings/usage이며 Anthropic request_id가 붙어 있다(= Anthropic API가 낸 빌링 에러를 Meridian이 중계). 다만 "왜 이 요청만 그 경로로 분류돼 막히는가"의 인과는 관측 메시지에서 역추론한 것이지 정책 원문에 명시된 건 아니다. - Meridian은 번들된 genuine
claude바이너리에 위임하므로 원래 first-party로 통과해야 하는데, Hermes를 경유시키면 여전히 400이 났다. - 핵심 질문: Claude Code도 tools를 쓰는데 왜 Hermes만 막히나? 무엇이 트리거인가?
검증 방법
- Hermes가 보낸 실제 요청 본문(
~/.hermes/sessions/request_dump_*.json, system prompt 37,449자, tools=29)을 떠서curl로 Meridian/v1/messages에 직접 재현 → Hermes 안 돌리고도 200/400을 반복 측정.
결과 (실험으로 직접 관측한 사실)
- 트리거가 아닌 것:
MERIDIAN_BETA_POLICY=strip-all→ 변화 없음MERIDIAN_DEFER_TOOL_THRESHOLD를 도구 수 위(200)로 올림 → 변화 없음- base 모델 강제(
supports1mContext→false, opus[1m] 제거) → 변화 없음 - 정체성 키워드 58개(hermes·nousresearch·opencode) 전부 중립화 → 여전히 400
- 프롬프트 크기: 6KB generic 프롬프트 → 200
- 도구 개수: generic 도구 29개 → 200
- 트리거가 system prompt 텍스트에 있음. bisection 결과:
- Hermes 풀 프롬프트 + 29 tools → 400 / generic + 29 tools → 200
- 앞 절반(0~18779) → 400, 뒷 절반 → 200 → 앞 ~4695자로 좁혀짐
- 그런데 0
2347, 23474695 각각 단독은 200, 합쳐서 0~4695는 400 - Hermes(0
2347) + 내가 쓴 generic 하네스 텍스트 → 200 (즉 Hermes 고유의 23474695 구간이 함께 있어야 걸림) - "You are Claude Code..." 흉내 오프닝을 0~4695에서 제거 → 여전히 400
- Hermes와 단어를 전혀 안 겹치게 새로 쓴 generic "자율 에이전트 하네스" 프롬프트(6440자, 같은 의미) + 29 tools → 200
- 대조: generic helpful 프롬프트 → 200, genuine Claude Code 프리셋 → 200, Hermes 실제 프롬프트 → 400
판단 (추론 — 미증명)
- 위 관측으로 배제된 것: 키워드 매칭(키워드 중립화에도 400), 의미(semantic) 카테고리 분류(단어 다른 generic 하네스는 200). 이 두 배제는 실험 근거가 있다.
- 가장 잘 맞는 가설: Anthropic이 보유한 알려진 하네스 프롬프트 원문과의 퍼지 유사도를 누적 임계값으로 판정한다. "반쪽씩 통과·합치면 실패"가 유사도 누적 임계값의 전형적 신호.
- 단 구현체가 임베딩 유사도인지 / simhash·MinHash 지문인지 / 학습 분류기인지는 우리 실험으로 구분 불가. Anthropic 비공개라 단정 못 함. 400의 발생 주체가 Anthropic 서버 분류인지도 직접 분리 검증한 건 아니다(메시지·request_id로 미루어 Anthropic 측으로 추정).
- 이 가설은 왜
clientSystemPrompt=false만 듣는지도 설명: 부분 수정으론 유사도가 임계값 아래로 안 내려가고, 프롬프트를 통째로 genuine CC로 갈아야 안 걸림.
워크어라운드 (확인됨)
- Meridian SDK Features에서 어댑터의
clientSystemPrompt=false(+codeSystemPrompt=true) → 하네스 프롬프트를 빼고 Claude Code 프리셋만 전송 → tool 요청 200.~/.config/meridian/sdk-features.json에 저장되어 재시작 후 유지. - 트레이드오프: 하네스 고유 페르소나·룰이 빠짐(도구 스키마는 유지돼 기능은 동작). 프롬프트를 꼭 살려야 하면 extra usage 충전이 정공법.
- 근거 출처: Meridian #516·#495(OPEN), NousResearch/hermes-agent #15080(CLOSED) — 모두 tools-트리거를 교차 확인.
추가 검증 (2026-06-03): 트리거를 단일 블록으로 국소화 + clientSystemPrompt 유지 해법
같은 curl rig(request_dump의 system을 opencode 어댑터로 Meridian에 직접 전송, clientSystemPrompt=true)로 풀 프롬프트(37.5k)를 섹션 단위로 더 잘게 bisection.
실험으로 관측한 사실:
- 무죄(정체성+해당블록만 단독 전송 → 200): 스킬 인덱스
<available_skills>11k(11,711자),## Skills (mandatory)블록, help 블록, cli-note 블록 - 무죄(이미 통과하는 구성에 더해도 200): MEMORY 스냅샷+USER 프로필(통과 base에 추가 → 200), 프로젝트 컨텍스트 AGENTS.md(MEMORY_GUIDANCE만 뺀 풀 프롬프트에 그대로 포함된 채 200)
- 유죄:
MEMORY_GUIDANCE블록 1개(agent/prompt_builder.py:137, "You have persistent memory across sessions. Save durable facts using the memory tool…") → 정체성+이 블록만으로도 400. 통과 base(정체성+스킬인덱스 13k)에 이 블록만 더해도 400 - 크기 무관 재확인: skills index 11k 단독 → 200 / mem-guidance 포함 5.3k 조합 → 400 (작은 쪽이 실패)
- 블록 내부 bisection: "memory를 declarative facts로 써라"(L3) + "skill_manage(action='patch')로 고쳐라"(L4) 쌍이 함께 있을 때 400(3회 반복 모두, 결정적), L2/L3/L4 각 줄 단독·L2+L3·L2+L4는 200
- 2문장 리워딩(같은 의미·다른 표현)으로는 부족: 리워딩한 메모리 가이드를 스킬 인덱스와 함께 두면 여전히 400(누적 점수가 임계 위에 남음). 격리된 작은 조합에선 리워딩이 통과하기도 했음
- 블록 통째 제거: MEMORY_GUIDANCE만 빼면 AGENTS.md·스킬 인덱스·스냅샷 전부 포함한 풀 35.6k(35,603자) 프롬프트가 200(2회 반복)
- 정체성 보강 관측: Meridian opencode 어댑터가 dump에서 "Hermes Agent…Nous Research"를 "Claude Code…Anthropic"으로 자동 치환하고 있었음(오히려 CC와 더 닮게 만듦) → 위 본문의 "정체성 키워드 중립화에도 400"이 왜 그런지 설명됨
해법(확인됨 — clientSystemPrompt=false 안 써도 됨):
agent/system_prompt.py의 tool_guidance 주입(memory/session/skill 가이드 프로즈)을HERMES_LEAN_PROMPTenv로 게이팅, 기본 ON(억제). editable install이라 수정 후hermes gateway restart필요.- 결과:
clientSystemPrompt=true를 유지한 채 페르소나·스킬 인덱스·도구를 전부 살리고 400 회피. 검증 — 실제hermes chatPONG 성공 + bash 도구 2 calls 정상.
판단 (추론 — 가설 갱신):
- 위 본문의 "부분 수정으론 안 되고 통째로 genuine CC로 갈아야 한다"는 부분 반증됐다: 누적 점수의 지배적 단일 기여자가 MEMORY_GUIDANCE 프로즈로 보이며, 그 한 블록만 제거해도 임계값 아래로 내려간다. 메모리 관리 지침문이 Claude Code 자체 메모리 프롬프트와 표면적으로 가장 닮은 것이 원인이라는 가설(미증명).
- 단 "퍼지 누적 분류기"라는 본 가설 자체는 유지된다 — 임계값이 가깝고 세션마다 동적 콘텐츠(메모리 스냅샷·프로필·cwd의 AGENTS.md)가 달라지므로, 이는 안정적 fix가 아니라 회피다. 임계값/스냅샷이 커지면 재발 가능하고, 그때는 억제 범위를 넓혀야 한다.
참고
- Agent Platform은 manifest-first lazy-load와 skill 프롬프트로 source 검색 시스템을 구현한다 —
query()의systemPrompt: { preset, append }구조가 곧clientSystemPrompt=false로 "빼는" 그 하네스 append분 - Rabin–Karp string-matching algorithm — 해시 기반 텍스트 지문 매칭. 유력 가설(원문 유사도 지문)의 기초 개념