실전 가이드 · 업로드 전 검증

Chub.ai 또는 RisuAI에 업로드하기 전 캐릭터 카드 검증 방법.

Tavern 카드를 올리기 전의 의식 —— Chub.ai, RisuAI, SillyTavern 업로드를 조용히 망치는 10가지 실수, 그것들을 잡아내는 수동 체크리스트, 그리고 카드 수가 늘었을 때 자동화해야 할 lint 워크플로.

fol. iv.r

출항 전 마지막 점검

주요 카드 호스트의 지원 게시판을 열어보면 같은 질문이 반복된다. 업로드는 됐는데 인사말이 비어 있다, 로어북이 안 따라온다, 이미지 미리보기가 깨졌다. 스레드의 결말도 비슷하다 —— 조용히 수정, 재 export, 재업로드. 카드 자체는 거의 다 맞았던 셈이다.

검증은 이 왕복을 막아주는 값싼 습관이다. Chub.ai 로 push 하거나 RisuAI 로 import 하기 전, 2분만 필드 목록을 훑어보자. 동일한 체크리스트가 확장된다 —— 10장은 눈으로, 100장은 스크립트로. 이 글은 그 양면을 다룬다: 사람이 봐야 할 것, linter가 대신해야 할 것.

fol. v.r

업로드를 망치는 10가지 실수

커뮤니티 bug 스레에서 등장하는 빈도순으로 대략:

  1. first_mes 누락 또는 빈 mes_example. 호스트는 카드를 받지만 채팅 창은 비어 있다. RisuAI는 특히 mes_example 로 톤을 잡으므로 빈 문자열은 합리적 기본값이 아니다.
  2. 너무 긴 description 이 토큰 예산을 터뜨림. 업로드는 깔끔하지만 첫 생성에서 prompt가 페르소나로 가득 차 응답 공간이 없다. 토큰 한도는 호스트마다 다르지만 원칙적으로 상주 블록(description + personality + scenario + system_prompt)은 대상 모델의 context window에 여유를 남겨야 한다.
  3. creator_notes system_prompt 혼동. 전자는 사람용 —— 사용 팁, 경고, 크레딧. 후자는 매 턴마다 모델에 전송된다. 바뀌면 메타 코멘트가 대화에 새거나, jailbreak이 모델에 닿지 않는다.
  4. PNG tEXt 청크 손상 또는 잘못된 인코딩. 관례는 keyword =chara 의 tEXt에 Base64로 인코딩된 UTF-8 JSON. 원시 UTF-8을 그대로 쓰거나 non-ASCII를 떼어내거나 잘못된 keyword를 쓰는 도구는 한 호스트에서는 열리지만 다음 호스트에서는 조용히 망가지는 PNG를 만든다.
  5. character_book 필드 타입 오류. entry는 특정 형태를 기대한다: keys 는 문자열 배열이지 쉼표로 구분된 문자열이 아니다. insertion_order 는 숫자, enabled 는 불리언. 손으로 편집한 로어북일수록 숫자를 문자열로, 배열을 단일 문자열로 적기 쉽고, importer는 경고 없이 entry를 버린다.
  6. extensions 아래 대상 플랫폼이 모르는 커스텀 키. SillyTavern이 적은 Regex pipeline 설정은 Chub 미리보기 렌더러에서는 무의미. 망가지지는 않지만 로컬에서 확인한 동작이 이동하는 순간 사라진다.
  7. V3 전용 필드가 V2 카드에 섞임. nickname, group_only_greetings, creator_notes_multilingual 은 V3 추가. 카드의 spec 가 여전히 chara_card_v2 이면 규격 준수 클라이언트는 이들을 무시할 권리가 있다.
  8. tags 형태 오류. 이는 「문자열 배열」. "tags": "fantasy, royalty" 는 문자열이며 거부되거나 「fantasy, royalty」라는 이상한 단일 태그로 색인된다.
  9. alternate_greetings 안의 빈 문자열. 보통 지우지 못한 초안 잔재. 빈 선택지로 렌더하는 클라이언트도 있고 예외를 던지는 것도 있다. 어느 쪽이든 노이즈 버그.
  10. 호스트 정책 —— 레이트 리밋, 이미지 크기 상한, NSFW 필터. 스키마 문제는 아니고 로컬 linter로 다 잡을 수는 없지만, 위 항목들 못지않게 업로드를 실패시킨다. 새벽에 일괄 업로드 돌리기 전에 각 호스트 규칙을 한 번씩 짚고 가자.
fol. vi.r

수동 체크리스트

카드가 적을 때는 직접 한 바퀴 도는 것이 어떤 도구보다 빠르다. 업로드 누르기 전에 다음을 체크:

  • [ ] spec spec_version 일치 (V2는 2.0, V3는 3.0).
  • [ ] name, description, first_mes 가 모두 존재하고 비어 있지 않음.
  • [ ] 상주 prompt가 대상 context window에 여유를 남김.
  • [ ] creator_notes 에 모델용 지시가 섞이지 않음.
  • [ ] PNG가 열리고 미리보기 정상, hex에서 keyword chara tEXt 청크 확인.
  • [ ] 모든 character_book.entries[*].keys 가 문자열 배열.
  • [ ] tags 가 배열; alternate_greetings 에 빈 문자열 없음.
  • [ ] V2 카드에 V3 전용 키 없음(반대도 마찬가지).
  • [ ] extensions 아래 각 키가 문서화돼 있고 어느 호스트용인지 파악함.
  • [ ] 대상 호스트의 업로드 규칙(이미지 크기, NSFW, 레이트 리밋) 확인 완료.
fol. vii.r

linter가 대신해야 할 일

수동 체크리스트는 30장쯤에서 재미가 사라진다. 그 다음은 자동화. 쓸 만한 카드 linter는 세 가지 일을 한다:

  • 스키마 검증. PNG를 파싱하고 tEXt 를 디코드한 뒤 선언된 spec 에 대해 JSON을 검증한다. 필수 필드, 타입, 열거, V2 카드에 섞인 V3 키 등.
  • 호스트 호환성 매트릭스. 카드의 각 키에 대해 어느 호스트가 실제로 소비하는지 보고. SillyTavern만 읽는 필드라고 「깨졌다」는 뜻은 아니다 —— 단지 이동 못 한다는 사실을 미리 알아야 가치가 있다.
  • 토큰 예산 추정. 자주 쓰는 tokenizer로 상주 prompt를 합산하고 대상 모델의 context를 위협하는 임계값을 넘으면 경고한다.

바로 tavernai.cards 가 채우려는 자리: 수동으로 할 검사를 실제 발행 대상 호스트들에 대해 돌려주는 업로드 전 워크벤치.

업로드 기록에서 깨진 카드를 발견하는 일은 그만하자. 발행 전에 V2 / V3 카드를 실제 호스트의 특이사항에 맞춰 lint 하고, 읽을 수 있는 diff로 스펙 간 마이그레이션을 수행한다.

다른 언어로 읽기