Torna al blog

Risoluzione dei problemiGuida

Risposta AI troncata? Correggi finish_reason "length" e max_tokens

La tua risposta API si interrompe a metà frase o restituisce JSON rotto? Controlla finish_reason, aumenta max_tokens entro il limite e aggiungi un passo di continuazione.

Di CheapTokens TeamPubblicato il 6 min di lettura

Copertina in stile terminale che mostra finish_reason length in una risposta API
In questa pagina
  1. Come appare a schermo una risposta troncata
  2. Passo 1: leggi il campo che te lo dice
  3. Passo 2: aumenta max_tokens, ma non oltre il tetto
  4. Passo 3: JSON che si interrompe a metà
  5. Passo 4: lascia che il modello continui
  6. Quanto costa una risposta lunga
  7. Prima di rilasciare: una breve checklist

Il tuo script è stato eseguito, il modello ha risposto e la funzione termina a metà riga. Oppure il parser JSON va in errore su una risposta dall'aspetto del tutto normale. Il più delle volte il modello non ha fallito: ha raggiunto il limite max_tokens che hai impostato (o un default che non hai mai visto) e l'API lo ha interrotto a metà parola.

La soluzione rapida: leggi finish_reason nella risposta. Se è "length" (API compatibile OpenAI) o stop_reason è "max_tokens" (Anthropic Messages), la risposta è stata troncata. Aumenta max_tokens fino al limite di output del modello, oppure chiedi al modello di continuare. Il resto di questa pagina mostra entrambe le cose in codice che puoi incollare.

Se vuoi la versione in linguaggio semplice del perché accade, inizia da perché una risposta AI si interrompe a metà frase. Questa pagina è la versione per il debug.

Come appare a schermo una risposta troncata#

I sintomi variano in base a cosa hai chiesto, ma la causa è la stessa. Prova ad associare questi casi a ciò che hai visto:

Abbina le coppie

Associa il sintomo alla sua probabile causa

  • Una funzione termina con return resuLa risposta di codice ha raggiunto max_tokens a metà riga
  • json.loads fallisce con "Unterminated string" o "Expecting ',' delimiter"Il JSON è stato troncato a metà
  • Una traduzione si ferma dopo la prima metà del testoIl limite di output è inferiore alla traduzione
  • La risposta è completa e il campo indica stopIl modello ha terminato da solo; cerca altrove

L'ultima riga è importante. Se finish_reason è stop, il limite non è il tuo problema, e aumentare max_tokens non cambierà nulla.

Passo 1: leggi il campo che te lo dice#

Ogni risposta indica perché la generazione è terminata. In una risposta compatibile OpenAI è choices[0].finish_reason: "stop" quando il modello ha finito, "length" quando ha esaurito i token. Nell'API Anthropic Messages il campo è stop_reason, e il valore di troncamento è "max_tokens". La guida agli stop reason di Anthropic (verificata il 28 ottobre 2026) consiglia esattamente le due correzioni seguenti: aumentare max_tokens o continuare la risposta.

Ecco lo stesso controllo in tre forme. La base URL è https://api.cheaptokens.dev/v1 per l'SDK OpenAI e https://api.cheaptokens.dev (senza /v1) per l'SDK Anthropic. Il valore max_tokens=200 è volutamente piccolo, così puoi vedere il troncamento con i tuoi occhi.

Un controllo, tre modi

Python (SDK 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 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."}]
  }'

Cerca "finish_reason": "length" nel JSON che ritorna.

Quando funziona come diagnosi, vedrai questa coppia: un limite basso dà length, un limite ampio dà stop.

Stessa richiesta con un max_tokens basso e uno alto: finish_reason passa da length a stop
I due valori di finish_reason che incontrerai più spesso

Passo 2: aumenta max_tokens, ma non oltre il tetto#

La correzione ovvia è un numero più grande. Il problema: ogni modello ha il proprio limite sulla risposta. Non impostare un valore superiore: il limite è per modello, quindi controlla prima la tabella qui sotto. Questi sono i limiti per risposta su CheapTokens (dati verificati il 28 ottobre 2026):

Modello Output massimo per risposta Contesto
DeepSeek-V4-Flash 32.768 token 380K
GLM-5.3-Flash 8.192 token 390K
MiniMax-M2.7 8.192 token 180K

Una traduzione da 9.000 token richiede un modello con un limite per risposta più ampio — DeepSeek-V4-Flash arriva fino a 32.768 token per risposta. Per codice lungo, JSON lungo o un intero documento, deepseek-ai/DeepSeek-V4-Flash-0731 ha più spazio: fino a 32.768 token per risposta, un unico prezzo a qualsiasi ora del giorno. Prendi gli id esatti dei modelli dalla documentazione CheapTokens e dalla pagina dei modelli, dato che i provider li denominano in modo diverso.

Una regola pratica: imposta max_tokens alla risposta più lunga che accetteresti, non al massimo. Paghi i token che il modello scrive davvero, quindi un tetto alto non costa nulla quando le risposte sono brevi.

Passo 3: JSON che si interrompe a metà#

Una risposta JSON troncata è il caso peggiore, perché il testo sembra corretto finché il parser non arriva alla fine. Supponi di estrarre campi da 40 fatture in un'unica chiamata e di chiedere un array. Il modello scrive 31 oggetti, raggiunge il limite e l'array non si chiude mai.

Un errore di json.loads su un array troncato al limite di token
Come appare un array JSON troncato in un parser

Non provare a riparare la stringa aggiungendo ]}. Otterrai JSON valido con record mancanti, che è peggio di un errore. Controlla prima finish_reason; se è length, aumenta il limite e riesegui la chiamata, oppure suddividi l'input in batch più piccoli. Per forzare il formato stesso, vedi JSON valido da un'API LLM.

Passo 4: lascia che il modello continui#

A volte la risposta è davvero più lunga di qualsiasi limite: un capitolo di un libro, un grande refactoring. Allora rimanda al modello ciò che hai e chiedigli di proseguire. Il ciclo qui sotto conserva il testo ottenuto finora, chiede il resto e si ferma dopo qualche giro, così un bug non può girare all'infinito.

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

Due abitudini mantengono tutto onesto. Restituisci il flag True/False e mostra all'utente un avviso su False, invece di presentare mezzo documento come finito. E ricorda che ogni giro rimanda il testo precedente come input, quindi una risposta continuata costa più di una risposta unica e grande. Per i dati strutturati, preferisci un limite più alto o batch più piccoli rispetto alla continuazione.

Quanto costa una risposta lunga#

Supponi che una risposta lunga sia 2.000 token in input e 8.000 token in output. Su DeepSeek-V4-Flash quella richiesta costa $0.00083 al prezzo odierno, e in totale, contando insieme input e output, è più economica di OpenRouter. Cambia i numeri con il tuo carico:

Calcolatore di costi

Una giornata di 50 risposte lunghe

Token di input per richiesta: 2000 · Token di output per richiesta: 8000 · Richieste al giorno: 50

ModelloAl giornoAl mese
GLM-5.3-Flash$0.018$0.54
DeepSeek-V4-Flash$0.0415$1.25
MiniMax-M2.7$0.065$1.95

La conclusione: in questo scenario domina il lato output, quindi un margine in max_tokens è un'assicurazione economica contro una nuova esecuzione. Una risposta troncata che devi rigenerare la paghi due volte. Il contesto sui due lati del conto è in token di input vs output, e l'elenco completo dei modelli con i prezzi è nella pagina dei prezzi.

Prima di rilasciare: una breve checklist#

Lista di controllo

Checklist sul troncamento per il tuo codice