블로그 목록

Claude Code 가 안 보는 description 1024자 상한

Claude Code 의 플러그인 검증은 SKILL.md description 1024자 상한을 안 본다. 같은 파일을 Codex 는 거부하고 Copilot CLI 는 조용히 뺀다. 설명문을 늘릴 때 상한을 함께 검사로 박아야 하는 이유.

공개 조회 1

TL;DR

  • 스킬 3개의 description 이 1197, 1241, 1324자였다. Agent Skills 스펙 상한은 1024자다
  • claude plugin validate 는 1100자도 통과다. Codex 는 거부하고 Copilot CLI 는 조용히 뺀다
  • 셋을 1017, 1015, 1019자로 줄이고 상한 검사를 넣었다. 상시 토큰 11,348 에서 10,654
  • 설명문을 늘리기 전에 상한부터 검사로 박는다. 상시 10,654 는 200K 예산의 다섯 배

증상

에이전트에게 주는 기능 설명문은 문서가 아니라 검색 키다. 잘 쓰려 들지 말고 사용자가 실제로 칠 말이 들어 있는지로 고친다. 사람이 읽으라고 쓰는 것 같지만 실제로 하는 일은 사용자의 말과 맞춰져 그 기능이 뜰지를 정하는 것이다. 이 성질을 모르면 설명을 보태다가 길이만 늘린다.

웹소설 집필용 Claude Code 플러그인을 만들고 있다. 스킬 13종이고 어느 스킬이 뜰지는 SKILL.md 맨 위의 description 이 정한다. 그래서 이 저장소는 description 을 길게 쓴다. 작가가 실제로 하는 말을 인용문으로 잔뜩 넣고 기능을 더할 때마다 한 문장씩 붙인다. 검사도 그렇게 요구한다. 스킬 본문에 절을 늘렸는데 description 이 안 늘면 붉어진다.

Agent Plugins 1.0 표준을 다룬 글을 읽고 툴킷을 그 기준으로 점검했다. 그 표준은 스킬 형식을 직접 정하지 않는다. Agent Skills 스펙에 넘긴다. 그래서 그 스펙의 참조 검증기를 돌렸다.

$ npx --yes skills-ref validate webnovel-toolkit/skills/webnovel
Validation failed for webnovel-toolkit/skills/webnovel:
  - Description exceeds 1024 character limit (1197 chars)

셋이 걸렸다. webnovel 1197자, webnovel-bible 1241자, webnovel-reveal 1324자.

반전

규격을 따른다고 적었으면 그 규격을 정한 쪽의 검증기를 한 번 돌린다. 매일 쓰는 도구가 통과시킨 것은 규격 준수의 증거가 아니다. 같은 파일을 Claude Code 의 검증 명령에 넣으면 통과다.

$ claude plugin validate ./webnovel-toolkit/skills
Validating components in: C:\workspace\webnovel-toolkit\webnovel-toolkit\skills

✔ Validation passed

1100자짜리 스킬을 하나 만들어 넣어 봤다. 경고 없이 통과했다. --strict 를 붙여도 같았고 판 셋에서 같았다. 즉 이 플러그인은 첫 릴리스부터 한 달 가까이 규격 위반 상태였고 쓰는 도구가 그것을 한 번도 말해 주지 않았다.

한 줄에서 갈린다.

flowchart TD
  P["Agent Plugins 1.0 표준"] -->|스킬 형식은 여기를 따르라| S["Agent Skills 스펙"]
  S --> R["description 은 1024자 이내"]
  R --> A["skills-ref: 초과를 오류로 낸다"]
  R --> B["Codex: 로드를 거부한다"]
  R --> C["Copilot CLI: 목록에서 뺀다"]
  R -.안 본다.-> D["claude plugin validate"]

넘었는지를 알려 주는 것은 위에서 돌린 skills-ref 하나뿐이다. 실어 주는 쪽은 클라이언트마다 다르다.

클라이언트1024자 초과 스킬근거
Claude Code 2.1.273경고 없이 실어 준다실측
Codexinvalid description: exceeds maximum length of 1024 characters 로 로드 거부openai/codex 이슈 13941. 메인테이너가 "이건 의도된 동작이다" 로 닫았다
Copilot CLI경고도 오류도 없이 목록에서 빠진다github/copilot-cli 이슈 3494. 아직 열려 있다

여러 곳에 배포한다면 가장 조용히 실패하는 데를 기준으로 잡는다. 시끄러운 쪽은 개발 중에 잡히고 조용한 쪽은 배포된 뒤에야 드러난다. 여러 곳에 배포한다면 가장 관대한 데서 개발하고 가장 조용한 데서 터지는 배치가 기본값이다. 여기서는 Claude Code 가 관대한 쪽이고 Copilot CLI 가 조용한 쪽이었다.

그리고 관대해 보이는 쪽에도 상한은 있었다. Claude Code 는 스킬 이름과 description 목록이 컨텍스트 창의 1퍼센트를 넘으면 덜 쓰는 스킬의 description 부터 줄이거나 버린다. 신호는 디버그 로그뿐이다. 상한의 값도 다르고 넘겼을 때 하는 일도 다르다.

근거

한쪽으로 미는 검사를 넣을 때는 반대쪽을 막는 검사도 같은 판에 넣는다. 안 넣으면 반대쪽 한계를 언젠가 넘긴다. 이 저장소가 그랬다. 스킬에 기능을 넣고 description 을 안 고쳐서 작가가 그 기능에 도달 못 한 일이 두 판 연속 있었고, 그 뒤로 "절이 늘면 description 도 늘어라" 를 검사로 박았다. 늘리라는 검사는 있고 상한은 없었다.

길이 상한을 지킬 때는 그 상한의 단위부터 확인한다. 글자로 세는지 바이트로 세는지에 따라 비라틴 문자는 같은 문장이 세 배로 잡힌다. 스펙 원문은 description 이 "Must be 1-1024 characters" 다. 바이트가 아니라 문자 수다. 한글은 UTF-8 로 한 글자가 3바이트라 이 구분이 세 배를 가른다. Codex 구현도 value.chars().count() 로 센다. 바이트로 셌다면 한글 1017자는 여전히 못 실린다.

처리

세 스킬의 description 을 줄였다. 인용 발화는 그대로 두고 "같은 표현이 해당한다" 같은 되풀이와 굵은 표시만 걷었다. 1017자, 1015자, 1019자가 됐다.

낱말이 겹치는지 보는 검사로 모델이 무엇을 고르는지를 갈음하지 않는다. 둘을 따로 둔다. 이 저장소에는 발화 107개를 넣고 어느 스킬로 붙는지 세는 검사가 있다. 기준은 잘못 붙는 것 6개다. 처음 줄인 판에서 11개로 올랐다. 잃은 낱말을 대조해 보니 '할지', '있나', '방법', '회차', '봐줘', '뭐지' 였다. 조사와 어미에 가까운 것들이다. 그 검사가 낱말 겹침이라는 뜻이고 그 정도 어림으로는 "모델이 실제로 이 스킬을 고르는가" 를 못 본다.

지표가 안 변했다고 안심하기 전에, 그 지표가 이번에 바꾼 자리를 지나가는지 먼저 센다. 안 지나가면 그 숫자는 증거가 아니다. 6개라는 숫자가 그랬다. 발화 107개 중 처음 줄인 판에서 지운 인용 19개를 담은 것은 하나도 없었다. 기준선이 그대로였다는 말은 그 기준선이 바뀐 자리를 안 지나갔다는 말이다. 검토를 따로 두 번 띄웠는데 양쪽이 같은 자리를 짚어 왔고, 지운 발화를 바꿔 말한 발화 6개를 넣어 113개로 다시 쟀다.

그래서 셋을 넣었다.

  1. tools/check.py 에 description 1024자 상한 검사. YAML 의 > 나 | 로 줄을 나눠 쓴 것도 이어서 센다. 첫 줄만 세면 그 형태가 검사를 그냥 지난다
  2. 플러그인 루트에 표준 매니페스트 plugin.json 을 두고 $schema 를 적었다. Claude Code 는 .claude-plugin/plugin.json 만 읽어서 둘 다 둬야 했고, 두 파일이 갈리면 붉어지는 검사를 같이 넣었다
  3. claude plugin eval 로 도는 최소 점검 묶음. 스킬당 발화 하나를 실제 모델에 던지고 그 스킬이 호출됐는지를 tool_used: Skill 로 센다. 낱말 겹침이 못 보는 것을 보는 자리다

검증

새 검사는 통과를 보고 끝내지 말고, 일부러 어겨서 붉어지는 것까지 확인하고 넘어간다. 통과만 본 검사는 꽂혀 있다는 증거가 없다. 루트 매니페스트의 버전만 바꾸면 version 불일치, description 을 접힘 스칼라로 1100자 만들면 webnovel-world 1101자 로 붉어진다.

eval 은 케이스 하나만 따로 돌려 단가를 봤다.

fires-webnovel-concept run 1/1: score 1.00  $0.36
  ✓ skill-fired (weight 1): Skill called 1x (expected 1..∞)
1 case(s) · 49s · $0.36

한 케이스에 49초, 0.36달러다. 14케이스를 끝까지 돌리면 514초에 4.54달러였고 전부 통과였다. 채점식을 고친 뒤 다시 돌리니 하나가 안 떴다. 한 번 도는 값이라 그렇다. 한 판에 4.5달러라 릴리스마다 돌릴 것은 아니고 description 을 손댄 판에만 돌린다. 돌릴 때마다 값이 드는 검사는 늘 도는 자리에 걸지 않고, 그 검사가 보는 영역을 건드린 판에만 건다.

채점식은 두 번 틀렸다. 처음 적은 input_match: "webnovel" 은 호출 기록에 그 글자가 있으면 통과라는 뜻이라 webnovel-bible 이 떠도 통과한다. 닫는 따옴표를 붙이면서 webnovel-toolkit: 접두까지 값에 박았더니, 이번에는 접두 없이 webnovel 로 부르는 호출을 못 셌다. 같은 스킬이 두 이름으로 불린다. 공식 문서가 그래서 "skill"\s*:\s*"(?:[\w-]+:)?이름" 꼴을 예시로 든다.

개선

전후
상한 초과 스킬30
잘못 붙는 발화6/1076/113
매 세션 실리는 토큰11,34810,654
표준 매니페스트없음루트 plugin.json, 표준 JSON Schema 통과

설명문을 늘리기 전에 그것이 쓰는 예산이 누구 것인지 본다. 기능마다의 품질이 아니라 전부가 나눠 쓰는 공동 예산이다. 내 스킬의 설명문을 늘리면 그만큼 다른 스킬의 것이 밀린다. 예산을 넘기면 덜 쓰는 것부터 잘리므로, 새로 만든 기능이 먼저 사라진다. 스펙 권고는 스킬당 100토큰 안팎인데 이 플러그인은 아직 420에서 1,100 이고, 상시 10,654 토큰은 200K 컨텍스트 기준 예산의 다섯 배다. 길게 쓰는 것이 공짜로 보이는 동안 값은 남이 낸다.

남은 것

  • 다른 클라이언트에서 실제로 돌려 보지 않았다. Codex 와 Cursor 와 VS Code 가 루트 plugin.json 을 읽는다는 것은 각 문서 기준이고 한국어 스킬이 거기서 뜨는지는 안 재 봤다
  • 슬래시 커맨드 6개는 표준 밖이라 어느 클라이언트에도 안 실린다. 같은 기능이 스킬로 닿게만 해 뒀다
  • 목록 예산이 적용된 뒤 내 description 이 실제로 얼마나 남는지는 못 쟀다. 세션 안에서 /context 를 쳐야 나오는데 그 값을 아직 안 봤다

다음부터

에이전트에게 주는 설명문을 늘릴 때는 늘려도 되는 상한을 함께 확인하고 그 상한을 검사로 박는다. 한쪽으로만 미는 검사는 반대쪽 한계를 언젠가 넘기고, 그때 알려 주는 것은 내가 매일 쓰는 도구가 아니라 규격을 정한 쪽의 검증기다.

댓글

아직 보이는 댓글이 없습니다.

댓글 쓰기

댓글은 관리자가 승인한 뒤에 보입니다. 이메일은 공개하지 않습니다.

개인정보 수집·이용 동의 (필수)
  • 수집 항목: 이름, 이메일, 댓글 내용
  • 목적: 댓글 게시와 관리, 댓글에 관한 연락
  • 보유 기간: 이름과 내용은 댓글을 게시하는 동안, 이메일은 1년. 게시하지 않은 댓글은 90일 뒤에 지웁니다.
  • 스팸을 막으려고 접속 IP 주소를 90일 동안 저장합니다. 이 저장은 동의가 아니라 운영자의 정당한 이익에 근거합니다.
  • 동의를 거부할 권리가 있습니다. 거부하면 댓글을 쓸 수 없습니다. 자세한 내용은 개인정보처리방침에 있습니다.