Cuando realizas una solicitud a la API de Messages, la respuesta de Claude incluye un campo stop_reason que indica por qué el modelo dejó de generar su respuesta. Entender estos valores es crucial para construir aplicaciones robustas que manejen diferentes tipos de respuesta de manera apropiada.

Para detalles sobre stop_reason en la respuesta de la API, consulta la referencia de la API de Messages.

¿Qué es stop_reason?

El campo stop_reason es parte de cada respuesta exitosa de la API de Messages. A diferencia de los errores, que indican fallas en el procesamiento de tu solicitud, stop_reason te dice por qué Claude completó exitosamente la generación de su respuesta.

Example response
{
  "id": "msg_01234",
  "type": "message",
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "Here's the answer to your question..."
    }
  ],
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "usage": {
    "input_tokens": 100,
    "output_tokens": 50
  }
}

Valores de razón de parada

end_turn

La razón de parada más común. Indica que Claude terminó su respuesta de manera natural.

if response.stop_reason == "end_turn":
    # Procesar la respuesta completa
    print(response.content[0].text)

max_tokens

Claude se detuvo porque alcanzó el límite de max_tokens especificado en tu solicitud.

# Solicitud con tokens limitados
response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=10,
    messages=[{"role": "user", "content": "Explain quantum physics"}]
)

if response.stop_reason == "max_tokens":
    # La respuesta fue truncada
    print("Response was cut off at token limit")
    # Considera hacer otra solicitud para continuar

stop_sequence

Claude encontró una de tus secuencias de parada personalizadas.

response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    stop_sequences=["END", "STOP"],
    messages=[{"role": "user", "content": "Generate text until you say END"}]
)

if response.stop_reason == "stop_sequence":
    print(f"Stopped at sequence: {response.stop_sequence}")

tool_use

Claude está llamando a una herramienta y espera que la ejecutes.

response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    tools=[weather_tool],
    messages=[{"role": "user", "content": "What's the weather?"}]
)

if response.stop_reason == "tool_use":
    # Extraer y ejecutar la herramienta
    for content in response.content:
        if content.type == "tool_use":
            result = execute_tool(content.name, content.input)
            # Devolver resultado a Claude para respuesta final

pause_turn

Usado con herramientas de servidor como búsqueda web cuando Claude necesita pausar una operación de larga duración.

response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    tools=[{"type": "web_search_20250305", "name": "web_search"}],
    messages=[{"role": "user", "content": "Search for latest AI news"}]
)

if response.stop_reason == "pause_turn":
    # Continuar la conversación
    messages = [
        {"role": "user", "content": original_query},
        {"role": "assistant", "content": response.content}
    ]
    continuation = client.messages.create(
        model="claude-sonnet-4-20250514",
        messages=messages,
        tools=[{"type": "web_search_20250305", "name": "web_search"}]
    )

refusal

Claude se negó a generar una respuesta debido a preocupaciones de seguridad.

response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    messages=[{"role": "user", "content": "[Unsafe request]"}]
)

if response.stop_reason == "refusal":
    # Claude se negó a responder
    print("Claude was unable to process this request")
    # Considera reformular o modificar la solicitud

Mejores prácticas para manejar razones de parada

1. Siempre verifica stop_reason

Haz un hábito de verificar el stop_reason en tu lógica de manejo de respuestas:

def handle_response(response):
    if response.stop_reason == "tool_use":
        return handle_tool_use(response)
    elif response.stop_reason == "max_tokens":
        return handle_truncation(response)
    elif response.stop_reason == "pause_turn":
        return handle_pause(response)
    elif response.stop_reason == "refusal":
        return handle_refusal(response)
    else:
        # Manejar end_turn y otros casos
        return response.content[0].text

2. Maneja max_tokens con gracia

Cuando una respuesta es truncada debido a límites de tokens:

def handle_truncated_response(response):
    if response.stop_reason == "max_tokens":
        # Opción 1: Advertir al usuario
        return f"{response.content[0].text}\n\n[Response truncated due to length]"
        
        # Opción 2: Continuar generación
        messages = [
            {"role": "user", "content": original_prompt},
            {"role": "assistant", "content": response.content[0].text}
        ]
        continuation = client.messages.create(
            model="claude-sonnet-4-20250514",
            max_tokens=1024,
            messages=messages + [{"role": "user", "content": "Please continue"}]
        )
        return response.content[0].text + continuation.content[0].text

3. Implementa lógica de reintento para pause_turn

Para herramientas de servidor que pueden pausar:

def handle_paused_conversation(initial_response, max_retries=3):
    response = initial_response
    messages = [{"role": "user", "content": original_query}]
    
    for attempt in range(max_retries):
        if response.stop_reason != "pause_turn":
            break
            
        messages.append({"role": "assistant", "content": response.content})
        response = client.messages.create(
            model="claude-sonnet-4-20250514",
            messages=messages,
            tools=original_tools
        )
    
    return response

Razones de parada vs. errores

Es importante distinguir entre valores de stop_reason y errores reales:

Razones de parada (respuestas exitosas)

  • Parte del cuerpo de respuesta
  • Indican por qué la generación se detuvo normalmente
  • La respuesta contiene contenido válido

Errores (solicitudes fallidas)

  • Códigos de estado HTTP 4xx o 5xx
  • Indican fallas en el procesamiento de solicitudes
  • La respuesta contiene detalles del error
try:
    response = client.messages.create(...)
    
    # Manejar respuesta exitosa con stop_reason
    if response.stop_reason == "max_tokens":
        print("Response was truncated")
    
except anthropic.APIError as e:
    # Manejar errores reales
    if e.status_code == 429:
        print("Rate limit exceeded")
    elif e.status_code == 500:
        print("Server error")

Consideraciones de streaming

Cuando usas streaming, stop_reason es:

  • null en el evento inicial message_start
  • Proporcionado en el evento message_delta
  • No proporcionado en ningún otro evento
with client.messages.stream(...) as stream:
    for event in stream:
        if event.type == "message_delta":
            stop_reason = event.delta.stop_reason
            if stop_reason:
                print(f"Stream ended with: {stop_reason}")

Patrones comunes

Manejo de flujos de trabajo de uso de herramientas

def complete_tool_workflow(client, user_query, tools):
    messages = [{"role": "user", "content": user_query}]
    
    while True:
        response = client.messages.create(
            model="claude-sonnet-4-20250514",
            messages=messages,
            tools=tools
        )
        
        if response.stop_reason == "tool_use":
            # Ejecutar herramientas y continuar
            tool_results = execute_tools(response.content)
            messages.append({"role": "assistant", "content": response.content})
            messages.append({"role": "user", "content": tool_results})
        else:
            # Respuesta final
            return response

Asegurar respuestas completas

def get_complete_response(client, prompt, max_attempts=3):
    messages = [{"role": "user", "content": prompt}]
    full_response = ""
    
    for _ in range(max_attempts):
        response = client.messages.create(
            model="claude-sonnet-4-20250514",
            messages=messages,
            max_tokens=4096
        )
        
        full_response += response.content[0].text
        
        if response.stop_reason != "max_tokens":
            break
            
        # Continuar desde donde se quedó
        messages = [
            {"role": "user", "content": prompt},
            {"role": "assistant", "content": full_response},
            {"role": "user", "content": "Please continue from where you left off."}
        ]
    
    return full_response

Al manejar apropiadamente los valores de stop_reason, puedes construir aplicaciones más robustas que manejen con gracia diferentes escenarios de respuesta y proporcionen mejores experiencias de usuario.