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 respuestas 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 fallos en el procesamiento de tu solicitud, stop_reason te dice por qué Claude completó exitosamente la generación de su respuesta.

Ejemplo de respuesta
{
  "id": "msg_01234",
  "type": "message",
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "Aquí está la respuesta a tu pregunta..."
    }
  ],
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "usage": {
    "input_tokens": 100,
    "output_tokens": 50
  }
}

Valores de stop reason

end_turn

La razón de detención más común. Indica que Claude terminó su respuesta de forma 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-3-7-sonnet-20250219",
    max_tokens=10,
    messages=[{"role": "user", "content": "Explica la física cuántica"}]
)

if response.stop_reason == "max_tokens":
    # La respuesta fue truncada
    print("La respuesta fue cortada en el límite de tokens")
    # Considera hacer otra solicitud para continuar

stop_sequence

Claude encontró una de tus secuencias de detención personalizadas.

response = client.messages.create(
    model="claude-3-7-sonnet-20250219",
    max_tokens=1024,
    stop_sequences=["FIN", "PARAR"],
    messages=[{"role": "user", "content": "Genera texto hasta que digas FIN"}]
)

if response.stop_reason == "stop_sequence":
    print(f"Se detuvo en la secuencia: {response.stop_sequence}")

tool_use

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

response = client.messages.create(
    model="claude-3-7-sonnet-20250219",
    max_tokens=1024,
    tools=[weather_tool],
    messages=[{"role": "user", "content": "¿Cómo está el clima?"}]
)

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 el resultado a Claude para la respuesta final

pause_turn

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

response = client.messages.create(
    model="claude-3-7-sonnet-20250219",
    max_tokens=1024,
    tools=[{"type": "web_search_20250305", "name": "web_search"}],
    messages=[{"role": "user", "content": "Busca las últimas noticias sobre IA"}]
)

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-3-7-sonnet-20250219",
        messages=messages,
        tools=[{"type": "web_search_20250305", "name": "web_search"}]
    )

Mejores prácticas para manejar razones de detención

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)
    else:
        # Manejar end_turn y otros casos
        return response.content[0].text

2. Maneja max_tokens con elegancia

Cuando una respuesta se trunca 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[Respuesta truncada debido a la longitud]"
        
        # Opción 2: Continuar la generación
        messages = [
            {"role": "user", "content": original_prompt},
            {"role": "assistant", "content": response.content[0].text}
        ]
        continuation = client.messages.create(
            model="claude-3-7-sonnet-20250219",
            max_tokens=1024,
            messages=messages + [{"role": "user", "content": "Por favor continúa"}]
        )
        return response.content[0].text + continuation.content[0].text

3. Implementa lógica de reintento para pause_turn

Para herramientas de servidor que pueden pausarse:

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-3-7-sonnet-20250219",
            messages=messages,
            tools=original_tools
        )
    
    return response

Razones de detención vs. errores

Es importante distinguir entre los valores de stop_reason y los errores reales:

Razones de detención (respuestas exitosas)

  • Parte del cuerpo de la 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 fallos en el procesamiento de la solicitud
  • La respuesta contiene detalles del error
try:
    response = client.messages.create(...)
    
    # Manejar respuesta exitosa con stop_reason
    if response.stop_reason == "max_tokens":
        print("La respuesta fue truncada")
    
except anthropic.APIError as e:
    # Manejar errores reales
    if e.status_code == 429:
        print("Límite de tasa excedido")
    elif e.status_code == 500:
        print("Error del servidor")

Consideraciones de streaming

Cuando se usa streaming, stop_reason es:

  • null en el evento inicial message_start
  • Proporcionado en el evento message_delta
  • No nulo en todos los demás eventos
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"El stream terminó con: {stop_reason}")

Patrones comunes

Manejo de flujos de trabajo de herramientas

def complete_tool_workflow(client, user_query, tools):
    messages = [{"role": "user", "content": user_query}]
    
    while True:
        response = client.messages.create(
            model="claude-3-7-sonnet-20250219",
            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-3-7-sonnet-20250219",
            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": "Por favor continúa desde donde lo dejaste."}
        ]
    
    return full_response

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