Zettelkasten

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 20/Max5x20/Max5x 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자로 좁혀짐
    • 그런데 02347, 23474695 각각 단독은 200, 합쳐서 0~4695는 400
    • Hermes(02347) + 내가 쓴 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_PROMPT env로 게이팅, 기본 ON(억제). editable install이라 수정 후 hermes gateway restart 필요.
  • 결과: clientSystemPrompt=true를 유지한 채 페르소나·스킬 인덱스·도구를 전부 살리고 400 회피. 검증 — 실제 hermes chat PONG 성공 + bash 도구 2 calls 정상.

판단 (추론 — 가설 갱신):

  • 위 본문의 "부분 수정으론 안 되고 통째로 genuine CC로 갈아야 한다"는 부분 반증됐다: 누적 점수의 지배적 단일 기여자가 MEMORY_GUIDANCE 프로즈로 보이며, 그 한 블록만 제거해도 임계값 아래로 내려간다. 메모리 관리 지침문이 Claude Code 자체 메모리 프롬프트와 표면적으로 가장 닮은 것이 원인이라는 가설(미증명).
  • 단 "퍼지 누적 분류기"라는 본 가설 자체는 유지된다 — 임계값이 가깝고 세션마다 동적 콘텐츠(메모리 스냅샷·프로필·cwd의 AGENTS.md)가 달라지므로, 이는 안정적 fix가 아니라 회피다. 임계값/스냅샷이 커지면 재발 가능하고, 그때는 억제 범위를 넓혀야 한다.

참고