LLM 답변이 중간에 잘릴 때 확인할 max_tokens
목차
모델에게 “300자 이내로” 쓰라고 해도 길이가 줄지 않거나 문장이 중간에서 뚝 끊기는 문제를 겪었습니다. 모델은 글자가 아니라 토큰 단위로 생성하는데, max_tokens를 글자 수로 어림해서 잡았고 그 어림이 틀려 한도에 먼저 걸린 것이 원인이었습니다. 토큰과 글자의 비율을 실측(제 한글 콘텐츠 기준 1토큰 ≈ 1.7자)해서 max_tokens에 문장을 끝낼 여유를 주고, 원하는 길이는 프롬프트와 길이 검사로 따로 맞추자 문장이 잘리는 문제가 사라졌습니다. 이 비율은 언어와 내용마다 다르니 추정하지 말고 직접 재야 합니다.
환경
이 글은 제가 혼자 만들어 쓰는 AI 집사 Jarvis 기준입니다.
- 모델: OpenAI GPT-5.1 (Discord 봇의 면접 연습 기능에서 API 직접 호출)
- 문제 났던 기능 A:
max_tokens≈ 1300 (주석에 ”≈ 400~900자”라고 추정값을 박아둠) - 문제 났던 기능 B:
max_tokens= 340 (주석에 ”≈ 한글 330~400자”라고 추정값을 박아둠) - 콘텐츠: 한글 위주 (코드·숫자 일부 섞임)
“300자 이내로 써”가 안 먹혔습니다
어느 순간 Jarvis 답변이 너무 길어졌습니다. 디스코드에서 보기 답답할 정도였습니다. 그래서 시스템 프롬프트에 적었습니다. “답변은 300자 이내로.”
안 줄었습니다. 톤만 살짝 바뀌고 길이는 그대로였습니다. 더 강하게 적었습니다. “반드시 300자 이내. 초과 금지.” 이번엔 가끔 줄긴 했는데, 이상하게 문장이 중간에서 뚝 끊겼습니다. “…그래서 팀장” 하고 끝나버리는 식입니다.
처음엔 모델이 지시를 무시한다고 생각했습니다. “얘가 글자 수를 못 세나?” 싶어서 retry 로직을 붙였습니다. 1차 답변이 길면 “더 줄여” 하고 다시 부르고, 그래도 길면 또 부르고. 비용은 늘고, 응답은 느려지고, 그런데도 길이는 들쭉날쭉했습니다.
프롬프트로 안 되는 걸 프롬프트로 더 세게 미는 일을 한참 했습니다.
모델은 글자를 안 봅니다, 토큰을 봅니다
막혀서 한 발 물러나 모델이 텍스트를 어떻게 만드는지부터 다시 봤습니다. 여기에 답이 있었습니다.
모델은 글자(character)를 하나씩 쓰는 게 아니라 토큰(token) 단위로 생성합니다. 토큰은 단어보다 작고 글자보다 큰, 모델이 텍스트를 쪼개는 조각입니다. 영어는 보통 한 단어가 1~2토큰입니다. 한글이 한 토큰에 몇 글자씩 담기는지는 모델의 토크나이저(글을 토큰으로 자르는 규칙)마다 다르고, 같은 모델이라도 내용에 따라 달라집니다. 그래서 글자 수로 미리 짐작하기가 어렵습니다.
그리고 결정적으로, API에는 max_tokens라는 파라미터가 있습니다. “이번 응답은 최대 몇 토큰까지 생성해라”라는 하드 리밋입니다. 이 한도에 도달하면 모델은 문장이 끝났든 말든 그 자리에서 생성을 멈춥니다. “…그래서 팀장”에서 끊긴 게 바로 이거였습니다. 글자 수 지시를 무시한 게 아니라, 토큰 한도에 먼저 부딪힌 것이었습니다.
정리하면 프롬프트의 “300자 이내”는 소프트한 권유입니다. 모델이 참고는 하지만 보장은 하지 않습니다. 반면 max_tokens는 물리적 천장이라 여기 걸리면 무조건 잘립니다. 저는 소프트한 권유로 물리적 천장을 통제하려 했으니 만지는 곳이 처음부터 틀렸습니다.
종이에 계산해보니 답이 나왔습니다
그래서 계산을 해봤습니다. 제 설정은 max_tokens가 약 1300이었습니다. 그때 저는 한글이 대략 1글자당 1.45토큰쯤 될 거라고 어림했습니다.
최대 글자 수 = max_tokens ÷ (글자당 토큰)
= 1300 ÷ 1.45
≈ 896자이 어림대로라면 모델이 만들 수 있는 한글은 최대 900자 가까이입니다. 그 안에서는 프롬프트로 길이를 조절할 수 있지만, 천장 자체는 프롬프트로 못 넘습니다. 답변이 천장에 닿으면 문장 완결과 무관하게 잘립니다. (이 어림이 틀렸다는 건 아래에서 드러납니다.)
여기서 둘을 나눠야 했습니다. max_tokens는 길이를 맞추는 손잡이가 아니라 안전 천장입니다. 이걸 낮춰서 길이를 줄이면 답이 짧아지는 게 아니라 문장 중간에서 잘립니다. 원하는 길이는 프롬프트로 요청하고, 천장은 그보다 넉넉하게 둬야 합니다. 프롬프트 문구만 며칠을 만지작거리는 동안 함께 봐야 할 숫자는 코드 한 줄 위에 있었습니다.
추정한 환산 비율이 틀렸습니다
여기서 한 번 더 데였습니다. 다른 기능에서는 max_tokens를 340으로 잡아두고 주석에 “340토큰 ≈ 한글 330~400자”라고 적어놨었습니다. 그 비율을 믿고 설계했는데, 실제로 돌려보니 모델이 574자를 만들고 나서 8번째 문장 중간에 잘렸습니다.
원인은 제가 토큰↔글자 비율을 실측하지 않고 추정했다는 겁니다. 주석엔 1토큰당 1.18자로 적었는데, 실제 제 한글 콘텐츠는 1토큰당 1.7자 가까이 나왔습니다. 추정과 실측이 40% 넘게 차이 났고, 그 오차가 그대로 문장 중간 절단으로 나타났습니다.
그래서 비율을 실측해서 박아뒀습니다. 제 한글 콘텐츠, 이 모델 기준으로 1토큰 ≈ 1.7자입니다. 이 숫자를 고정하니 “이 기능은 600자까지 받고 싶다 → 600 ÷ 1.7 ≈ 353토큰”처럼 역산이 됐습니다. 실제로 는 문장을 끝낼 여유까지 두고 340에서 520으로 올렸습니다. 길이는 프롬프트로 요청하고, 그래도 넘치면 길이 검사가 걸러내고, 응답이 한도에 걸려 끊겼는지(finish_reason)를 확인해서 처리하게 했습니다.
토큰 비율은 언어마다, 심지어 같은 언어라도 내용에 따라 달라집니다. 코드가 많은 답변이냐 숫자가 많은 답변이냐에 따라 달랐습니다. 그래서 “대충 이 정도겠지”로 환산하면 거의 항상 어긋났습니다. 제 경우엔 그 어긋남이 8번째 문장 한복판에서 답변이 잘리는 걸로 돌아왔습니다.
마무리
새 기능을 만들 때는 토큰 비율을 한 번 돌려 보고 숫자를 확정한 뒤 max_tokens를 잡으려 합니다. 이번에는 OpenAI GPT-5.1 기준으로만 쟀기 때문에 다른 모델을 쓰게 되면 그 모델로 다시 재야 합니다.
참고 :
OpenAI Tokenizer
OpenAI API Reference - Chat Completions (max_completion_tokens, finish_reason)
