Volver al blog

Solución de problemasGuía

¿Respuesta de IA cortada? Corrige finish_reason "length" y max_tokens

¿Tu respuesta de API se detiene a mitad de frase o devuelve JSON roto? Comprueba finish_reason, sube max_tokens dentro del límite y añade un paso de continuación.

Por CheapTokens TeamPublicado el 6 min de lectura

Portada estilo terminal que muestra finish_reason length en una respuesta de API
En esta página
  1. Cómo se ve una respuesta cortada en pantalla
  2. Paso 1: lee el campo que te lo indica
  3. Paso 2: sube max_tokens, pero sin pasar el techo
  4. Paso 3: JSON que se detiene a mitad
  5. Paso 4: deja que el modelo continúe
  6. Cuánto cuesta una respuesta larga
  7. Antes de publicar: una lista de comprobación breve

Tu script se ejecutó, el modelo respondió y la función termina a mitad de una línea. O el parser de JSON falla ante una respuesta que parece perfectamente normal. Lo más habitual es que el modelo no haya fallado: alcanzó el límite max_tokens que estableciste (o un valor por defecto que nunca viste) y la API lo detuvo a mitad de palabra.

La solución rápida: lee finish_reason en la respuesta. Si es "length" (API compatible con OpenAI) o stop_reason es "max_tokens" (Anthropic Messages), la respuesta se cortó. Sube max_tokens hasta el límite de salida del modelo, o pide al modelo que continúe. El resto de esta página muestra ambas cosas en código que puedes pegar.

Si quieres la versión en lenguaje llano de por qué ocurre esto, empieza por por qué una respuesta de IA se detiene a mitad de frase. Esta página es la versión de depuración.

Cómo se ve una respuesta cortada en pantalla#

Los síntomas varían según lo que hayas pedido, pero la causa es la misma. Intenta relacionar estos casos con lo que has visto:

Emparejar los pares

Relaciona el síntoma con su causa probable

  • Una función termina con return resuLa respuesta de código alcanzó max_tokens a mitad de línea
  • json.loads falla con "Unterminated string" o "Expecting ',' delimiter"El JSON se cortó a mitad
  • Una traducción se detiene tras la primera mitad del textoEl límite de salida es menor que la traducción
  • La respuesta está completa y el campo indica stopEl modelo terminó por sí solo; busca en otro sitio

Esa última fila importa. Si finish_reason es stop, el límite no es tu problema, y subir max_tokens no cambiará nada.

Paso 1: lee el campo que te lo indica#

Cada respuesta indica por qué terminó la generación. En una respuesta compatible con OpenAI es choices[0].finish_reason: "stop" cuando el modelo terminó, "length" cuando se quedó sin tokens. En la API Anthropic Messages el campo es stop_reason, y el valor de corte es "max_tokens". La guía de stop-reason de Anthropic (consultada el 28 de octubre de 2026) recomienda exactamente las dos soluciones siguientes: subir max_tokens o continuar la respuesta.

Aquí tienes la misma comprobación en tres formas. La URL base es https://api.cheaptokens.dev/v1 para el SDK de OpenAI y https://api.cheaptokens.dev (sin /v1) para el SDK de Anthropic. El valor de max_tokens=200 es deliberadamente pequeño, para que puedas ver el corte tú mismo.

Una comprobación, tres formas

Python (SDK de OpenAI)

python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.cheaptokens.dev/v1",
    api_key=os.environ["CHEAPTOKENS_API_KEY"],
)

resp = client.chat.completions.create(
    model="deepseek-ai/DeepSeek-V4-Flash-0731",
    max_tokens=200,
    messages=[{"role": "user", "content": "Write a Python class for a todo list with tests."}],
)

choice = resp.choices[0]
if choice.finish_reason == "length":
    print("Cut off at the token limit")
print(choice.message.content)
print(resp.usage.completion_tokens)

Python (SDK de Anthropic)

python
import os
import anthropic

client = anthropic.Anthropic(
    base_url="https://api.cheaptokens.dev",
    api_key=os.environ["CHEAPTOKENS_API_KEY"],
)

msg = client.messages.create(
    model="deepseek-ai/DeepSeek-V4-Flash-0731",
    max_tokens=200,
    messages=[{"role": "user", "content": "Write a Python class for a todo list with tests."}],
)

if msg.stop_reason == "max_tokens":
    print("Cut off at the token limit")
print(msg.content[0].text)

curl

bash
curl https://api.cheaptokens.dev/v1/chat/completions \
  -H "Authorization: Bearer $CHEAPTOKENS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-ai/DeepSeek-V4-Flash-0731",
    "max_tokens": 200,
    "messages": [{"role": "user", "content": "Write a Python class for a todo list with tests."}]
  }'

Busca "finish_reason": "length" en el JSON que devuelve.

Cuando funciona como diagnóstico, verás este par: un límite bajo da length, un límite holgado da stop.

La misma petición con un max_tokens bajo y otro alto: finish_reason cambia de length a stop
Los dos valores de finish_reason que encontrarás con más frecuencia

Paso 2: sube max_tokens, pero sin pasar el techo#

La solución obvia es un número mayor. La trampa: cada modelo tiene su propio tope para la respuesta. No establezcas un valor por encima: el límite es por modelo, así que consulta primero la tabla siguiente. Estos son los límites por respuesta en CheapTokens (datos consultados el 28 de octubre de 2026):

Modelo Salida máxima por respuesta Contexto
DeepSeek-V4-Flash 32.768 tokens 380K
GLM-5.3-Flash 8.192 tokens 390K
MiniMax-M2.7 8.192 tokens 180K

Una traducción de 9.000 tokens necesita un modelo con un límite por respuesta mayor: DeepSeek-V4-Flash admite hasta 32.768 tokens por respuesta. Para código largo, JSON largo o un documento entero, deepseek-ai/DeepSeek-V4-Flash-0731 tiene más margen: hasta 32.768 tokens por respuesta, un único precio a cualquier hora del día. Toma los ids exactos de los modelos de la documentación de CheapTokens y de la página de modelos, ya que los proveedores los nombran de forma distinta.

Una regla práctica: establece max_tokens en la respuesta más larga que aceptarías, no en el máximo. Pagas por los tokens que el modelo escribe realmente, así que un techo alto no cuesta nada cuando las respuestas son cortas.

Paso 3: JSON que se detiene a mitad#

Una respuesta JSON cortada es el caso más desagradable, porque el texto parece correcto hasta que el parser llega al final. Supón que extraes campos de 40 facturas en una sola llamada y pides un array. El modelo escribe 31 objetos, alcanza el límite y el array nunca se cierra.

Un error de json.loads en un array cortado por el límite de tokens
Cómo se ve un array JSON truncado en un parser

No intentes reparar la cadena añadiendo ]}. Obtendrás JSON válido con registros faltantes, lo que es peor que un error. Comprueba primero finish_reason; si es length, sube el límite y vuelve a ejecutar la llamada, o divide la entrada en lotes más pequeños. Para forzar el formato en sí, consulta JSON válido desde una API de LLM.

Paso 4: deja que el modelo continúe#

A veces la respuesta es genuinamente más larga que cualquier límite: un capítulo de un libro, una refactorización grande. Entonces devuelve lo que tienes y pide al modelo que siga. El bucle siguiente conserva el texto que ya tiene, pide el resto y se detiene tras unas cuantas rondas para que un bug no pueda girar indefinidamente.

python
def complete(base_messages, model, max_tokens=4096, rounds=4):
    text = ""
    for _ in range(rounds):
        messages = list(base_messages)
        if text:
            messages += [
                {"role": "assistant", "content": text},
                {"role": "user", "content": "Continue exactly where you stopped. Do not repeat anything."},
            ]
        r = client.chat.completions.create(model=model, max_tokens=max_tokens, messages=messages)
        choice = r.choices[0]
        text += choice.message.content or ""
        if choice.finish_reason != "length":
            return text, True   # finished on its own
    return text, False          # still cut after all rounds

Dos hábitos mantienen esto honesto. Devuelve el flag True/False y muestra al usuario una advertencia en False, en lugar de presentar medio documento como terminado. Y recuerda que cada ronda reenvía el texto anterior como entrada, así que una respuesta continuada cuesta más que una sola respuesta grande. Para datos estructurados, prefiere un límite mayor o lotes más pequeños antes que continuar.

Cuánto cuesta una respuesta larga#

Supón que una respuesta larga son 2.000 tokens de entrada y 8.000 tokens de salida. En DeepSeek-V4-Flash esa petición cuesta $0.00083 al precio de hoy, y en total, contando entrada y salida juntas, es más barato que OpenRouter. Cambia los números por tu propia carga:

Calculadora de costos

Un día de 50 respuestas largas

Tokens de entrada por solicitud: 2000 · Tokens de salida por solicitud: 8000 · Solicitudes por día: 50

ModeloPor díaPor mes
GLM-5.3-Flash$0.018$0.54
DeepSeek-V4-Flash$0.0415$1.25
MiniMax-M2.7$0.065$1.95

La conclusión: el lado de la salida domina en este escenario, así que el margen en max_tokens es un seguro barato contra una reejecución. Una respuesta cortada que tienes que regenerar se paga dos veces. Tienes contexto sobre los dos lados de la factura en tokens de entrada vs salida, y la lista completa de modelos con precios está en la página de precios.

Antes de publicar: una lista de comprobación breve#

Lista de verificación

Lista de comprobación de truncamiento para tu código