콘텐츠로 이동

vLLM Gemma 도구 인자 문자열 변환으로 422 실패

10/27(화) → 11/12(목)·2번 만남

한 줄 요약: Gemma로 앱·파이프라인을 만들면 같은 실패가 반복돼 422로 끝나고, 대시보드는 plan_hash 불일치로 글로만 답한다. 원인은 세 갈래다. (A) vLLM v0.24+ Gemma 파서가 숫자·참거짓을 문자열로 두는데 타입 없는 payload가 이를 그대로 노출, (B) Gemma가 코드 줄바꿈을 \n 두 글자로 씀, (C) 승인 단계에서 Gemma가 계획을 다시 써서 지문이 어긋남. 원인은 규명했고 수정은 아직이다.

🚨 증상

  • 어시스턴트에서 Gemma(gemma-4-31b-it)로 앱·파이프라인·온톨로지를 요청하면 실패한다. Claude는 같은 요청에 성공한다.
  • 대시보드는 계획 미리보기까지 나오지만 승인 카드 없이 글로만 답한다. 됐다 안 됐다 한다.
    schema.fields.N.nullable: Input should be a valid boolean      # 갈래 A
    layout.span must be an integer 1-12                            # 갈래 A
    code item '...' content is not valid Python at line 1, column 42:
      unexpected character after line continuation character      # 갈래 B
    plan does not match plan_hash                                  # 갈래 C
    422 "The assistant repeated a failing tool call."              # 공통 끝
    
  • 승인 카드가 뜬 대시보드도 위젯 위치가 {"x": "0", "w": "6"}처럼 전부 문자열이었다.

📍 상황

  • 2026-10-27 13:53 — 'gemma 어시스턴트 테스트' 결과를 보고 원인을 분석했다(→ 작업 4). 로컬 시험 후 이슈 #3561을 올렸다(→ 작업 6). → 그날 일지
  • 2026-11-12 14:57~17:47 — "앱·대시보드만이 아니라 모든 리소스가 되게" 하려고 다시 분석했다(→ 작업 4). 실패 13건 분류(14:57), 개발 클러스터 어시스턴트 로그 확인(16:54), 재난 컬렉션 재테스트(17:36~17:46). 지난번 원인(A)은 그대로이고, 다른 원인 B·C가 더 있었다. → 그날 일지

🔍 원인

겉으로는 "Gemma가 못한다" 하나로 보였다. 실제로는 갈래마다 책임이 달랐다.

갈래 무엇이 깨지나 책임
A 숫자·참거짓이 문자열 nullable, precision, span vLLM 파서 + 우리 스키마
B 코드 줄바꿈이 \n 두 글자 파이프라인 코드 문법 검사 Gemma 모델 (추정)
C 승인 때 계획 재작성 plan_hash 비교 Gemma 모델 + 승인 방식
  • A: Gemma는 형식대로 flag:true처럼 따옴표 없이 쓴다. vLLM v0.24부터 Gemma 파서가 바뀌며(#45588) 이 값을 전부 문자열로 둔다(vLLM 이슈 #47906, v0.30·main에서도 재현). 스키마에 타입이 적힌 경로만 되돌리는데, 우리 payload가 {"type":"object","additionalProperties":true}라 거기서 끊긴다. 수정 PR #47909는 리뷰 없이 11/6에 닫혔다.
  • B: Gemma 공식 chat_template.jinja는 글자 값을 <|"|>로 감싸기만 한다. \n 표기 규칙이 없다. Gemma가 JSON 버릇대로 \n을 썼고, 파서는 규칙대로 그대로 꺼냈다. 실패한 턴 원문은 남지 않아 추정이다.
  • C: plan_hash는 계획 전체로 계산한다. Gemma가 2,000자 넘는 계획을 승인 때 다시 쓰다 한 글자만 달라도 어긋난다. 확률 문제다(데모 환경에서 20번 중 5번).

🧭 해결 과정

  1. 10/27: 모델 능력 부족 의심 → 아니었다. 구조 오류는 고쳤고, 타입 오류에서만 같은 호출을 되풀이했다. 파서 소스와 보내는 스키마를 보고 갈래 A를 찾았다.
  2. 11/12: 실패 13건을 "마지막에 막힌 원인"으로 분류 → A를 고치면 앱·파이프라인·온톨로지 7건만 풀린다. 대시보드 5건은 타입 문제가 아니었다(C, <|"|> 표기 누수, 문자열 plan).
  3. 로그 확인 → 파이프라인 요청 2번이 B로 끝났다. 같은 오류를 직접 재현했다. 검사는 첫 오류에서 멈추므로 뒤에 A가 숨어 있었는지는 모른다.
  4. 책임 나누기 → 공식 템플릿 기준으로 A는 파서, B는 모델로 정리했다.
  5. "우리 잘못은 없나" 확인 → 실제 보내는 스키마에서 타입이 정확히 payload에서 끊겼다. vLLM 버그가 원인이고, 타입 없는 payload가 그 버그에 노출시킨 것이다. any로 둔 건 vLLM v0.23까지는 문제가 없었기 때문이다. 다만 anyOf로 적으면 vLLM이 따라가지 않아 그대로 실패한다.
  6. 재테스트(대시보드 2, 앱 2) → 대시보드 1번만 승인 카드. 계획을 똑같이 베껴 쓴 운이었다. 앱 2번은 A로 시작해 계획이 망가지며 422.
  7. 남은 할 일(미해결)
  8. 타입 되돌리기를 빠짐없이(#3561). 스키마로 하려면 anyOf 없이 적어야 한다.
  9. 승인은 계획 대신 제안 번호만 받기(C 제거).
  10. 코드 항목에서만, 문법 실패 + \n 치환 시 통과할 때만 줄바꿈 보정(B).
  11. 남은 <|"|> 표기와 문자열 plan 풀기.

✅ 다음에 같은 증상이 보이면

  • 오류 문장으로 갈래부터 나눈다: must be a number/boolean/integer → A, line continuation character → B, plan_hash → C, 값에 <|"|>가 남음 → 파서 표기 누수.
  • A면 같은 요청을 Claude로 보내 비교하고, 그 칸이 스키마에서 type까지 이어지는지 본다. 서빙 vLLM이 v0.24 이상인지도 확인한다.
  • 하나를 고친 뒤엔 꼭 다시 돌린다. 검사가 첫 오류에서 멈춰 다른 갈래가 숨어 있을 수 있다.
  • 재발 방지: 모델 출력을 그대로 믿는 계약(계획 재전송, 타입 없는 객체)을 줄인다. 외부 엔드포인트·고객 내부망 vLLM은 우리가 못 바꾸므로 받는 쪽이 견디게 만든다.

🔗 관련

🗓️ 이력

  • 2026-10-27: Gemma 계획의 숫자·참거짓이 문자열로 와서 422 반복. 원인 확인, 이슈 #3561 등록, 로컬 시험까지(미해결) → 일지
  • 2026-11-12: 같은 원인(A)에 더해 줄바꿈 \n(B)·승인 재작성(C) 갈래를 찾음. vLLM 버그 확인, 재테스트 4건 중 1건만 성공. 원인 규명만(미해결) → 일지