Actualización de la Interfaz Compatible con OpenAI: Soporte Profundo para Claude

31 jul 2026 · AIHubMix · 8 min read · Noticias

Actualización de la Interfaz Compatible con OpenAI: Soporte Profundo para Claude

Hemos actualizado la interfaz compatible con OpenAI con optimizaciones más profundas específicamente para los modelos de la serie Claude. Ahora puedes controlar el pensamiento y el almacenamiento en caché de manera más precisa y conveniente. El pensamiento entrelazado en conversaciones de múltiples turnos es ahora más fácil de usar, permitiendo una integración fluida sin parámetros adicionales. También admite la habilitación de las funciones beta ofrecidas por Anthropic.

1. Pensamiento del Modelo (Pensamiento Ampliado)

1.1 Ventajas del Pensamiento Entrelazado

Cuando el pensamiento entrelazado no está habilitado, el modelo realiza el pensamiento solo una vez al comienzo de un turno del asistente; las respuestas subsiguientes se generan directamente después de recibir los resultados de la herramienta, sin producir nuevos bloques de pensamiento:

Usuario → [Pensando] → Llamada a la Herramienta → Resultado de la Herramienta → Respuesta

Cuando el pensamiento entrelazado está habilitado, el modelo inserta un nuevo bloque de pensamiento cada vez que recibe un resultado de la herramienta, formando una cadena de razonamiento:

Usuario → [Pensando] → Llamada a la Herramienta → Resultado de la Herramienta → [Pensando] → Respuesta
                                                ↑ Pensamiento Entrelazado

Esto permite que el modelo:

  • Realice razonamientos secundarios basados en los resultados de la herramienta, en lugar de simplemente concatenar salidas.
  • Encadene razonamientos entre múltiples llamadas a herramientas, donde cada decisión se basa en el análisis del paso anterior.
Referencia: Pensamiento Entrelazado de Anthropic

1.2 Habilitando el Pensamiento

Puedes habilitar el pensamiento de cuatro maneras, eligiendo cualquiera de ellas:

Método Ejemplo Descripción
reasoning_effort "reasoning_effort": "low" Parámetro estándar de OpenAI, colocado en el nivel superior del cuerpo de la solicitud
reasoning.effort "reasoning": {"effort": "low"} Equivalente al método anterior, colocado dentro del objeto de razonamiento
reasoning.max_tokens "reasoning": {"max_tokens": 1024} Controla de manera precisa el número máximo de tokens para el pensamiento
Nombre del modelo con -think "model": "claude-sonnet-4-5-think" La forma más sencilla, no requiere parámetros adicionales
Prioridad (cuando se utilizan múltiples métodos): reasoning_effort > reasoning.max_tokens > reasoning.effort > -think sufijo

Valores posibles para el esfuerzo: minimal / low / medium / high / xhigh

1.3 Retorno del Pensamiento

El mensaje de respuesta incluirá dos nuevos campos:

  • reasoning_content: Contenido del pensamiento (cadena), para una fácil visualización.
  • reasoning_details: Información estructurada completa sobre el pensamiento, que debe ser devuelta tal cual en conversaciones de múltiples turnos; la estructura interna puede diferir entre proveedores.

Ejemplo no en streaming (omitindo campos no relacionados):

{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": "¡Hola! ¿Cómo puedo ayudarte hoy?",
      "reasoning_content": "El usuario solo está diciendo hola...",
      "reasoning_details": {
        "type": "thinking",
        "thinking": "El usuario solo está diciendo hola...",
        "signature": "Er8CCkYI..."
      }
    }
  }]
}

En respuestas en streaming, el contenido del pensamiento se enviará en fragmentos a través de delta.reasoning_content y delta.reasoning_details. Para la lógica completa de concatenación en streaming, consulta el ejemplo completo a continuación.

1.4 Retener el Pensamiento en Conversaciones de Múltiples Turnos (El Pensamiento Entrelazado está integrado, no se necesitan parámetros adicionales)

Para permitir que el modelo continúe con sus capacidades de razonamiento en conversaciones de múltiples turnos, simplemente coloca el reasoning_details devuelto anteriormente tal cual en el mensaje del asistente de la siguiente ronda:

messages = [
    {"role": "user", "content": "¿Cómo está el clima en Boston?"},
    {
        "role": "assistant",
        "content": response.choices[0].message.content,
        "tool_calls": response.choices[0].message.tool_calls,
        "reasoning_details": response.choices[0].message.reasoning_details,
    },
    {
        "role": "tool",
        "tool_call_id": "toolu_xxx",
        "content": '{"temperature": 45, "condition": "rainy"}',
    }
]

AIHubMix habilitará automáticamente el pensamiento entrelazado cuando detecte información histórica de pensamiento en la solicitud, permitiendo que el modelo continúe razonando profundamente después de recibir los resultados de la llamada a la herramienta sin requerir parámetros adicionales.

1.5 Ejemplo Completo

Los siguientes dos ejemplos demuestran el proceso completo de llamada a la herramienta de múltiples turnos + pensamiento entrelazado: consulta del usuario → el modelo piensa y llama a una herramienta → inyectar resultados de la herramienta (preservando reasoning_details) → el modelo pensamiento entrelazado da la respuesta final.

No en streaming · Pensamiento Entrelazado

import os
import json
from openai import OpenAI

client = OpenAI(
    base_url="https://aihubmix.com/v1",
    api_key=os.environ.get("AIHUBMIX_API_KEY", "sk-***"),
)

# ── Definición de la herramienta ───────────────────────────────────────────
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Obtener el clima actual para una ubicación",
        "parameters": {
            "type": "object",
            "properties": {"location": {"type": "string", "description": "Nombre de la ciudad"}},
            "required": ["location"]
        }
    }
}]

# ── Ejecución simulada de la herramienta ─────────────────────────────────────
WEATHER_DB = {
    "boston": {"temperature": "45°F (7°C)", "condition": "rainy", "humidity": "85%", "wind": "15 mph NE"},
    "tokyo":  {"temperature": "72°F (22°C)", "condition": "sunny", "humidity": "45%", "wind": "5 mph S"},
}

def execute_tool(name: str, args: dict) -> str:
    if name == "get_weather":
        key = next((k for k in WEATHER_DB if k in args.get("location", "").lower()), None)
        return json.dumps(WEATHER_DB.get(key, {"temperature": "65°F", "condition": "clear"}))
    return "{}"

# ── Bucle de conversación de múltiples turnos ─────────────────────────────
messages = [
    {"role": "user", "content": "¿Cómo está el clima en Boston? Luego recomienda qué usar."}
]

turn = 0
while True:
    turn += 1
    print(f"\n── Turno {turn} ──")

    response = client.chat.completions.create(
        model="claude-sonnet-4-5",
        messages=messages,
        tools=tools,
        extra_body={"reasoning": {"max_tokens": 2000}},
    )
    msg = response.choices[0].message

    # Imprimir proceso de pensamiento
    if msg.reasoning_content:
        label = "Pensamiento Entrelazado" if turn > 1 else "Pensando"
        print(f"[{label}] {msg.reasoning_content}")

    # Imprimir contenido de respuesta
    if msg.content:
        print(f"[Respuesta] {msg.content}")

    # Imprimir llamadas a herramientas
    if msg.tool_calls:
        for tc in msg.tool_calls:
            print(f"[Llamada a la Herramienta: {tc.function.name}] {tc.function.arguments}")

    # Construir mensaje del asistente, preservar reasoning_details (¡crítico!)
    assistant_msg = {"role": "assistant", "content": msg.content}
    if msg.tool_calls:
        assistant_msg["tool_calls"] = msg.tool_calls
    if msg.reasoning_details:
        assistant_msg["reasoning_details"] = msg.reasoning_details  # pasar de vuelta sin modificar
    messages.append(assistant_msg)

    # Sin tool_calls significa que la conversación ha terminado
    if not msg.tool_calls:
        break

    # Ejecutar herramientas y agregar resultados a los mensajes
    for tc in msg.tool_calls:
        args = json.loads(tc.function.arguments)
        result = execute_tool(tc.function.name, args)
        print(f"[Resultado de la Herramienta: {tc.function.name}] {result}")
        messages.append({"role": "tool", "tool_call_id": tc.id, "content": result})

Streaming · Pensamiento Entrelazado

import os
import sys
import json
from openai import OpenAI

client = OpenAI(
    base_url="https://aihubmix.com/v1",
    api_key=os.environ.get("AIHUBMIX_API_KEY", "sk-***"),
)

# ── Definición de la herramienta y ejecución simulada ─────────────────────────
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Obtener el clima actual para una ubicación",
        "parameters": {
            "type": "object",
            "properties": {"location": {"type": "string", "description": "Nombre de la ciudad"}},
            "required": ["location"]
        }
    }
}]

WEATHER_DB = {
    "boston": {"temperature": "45°F (7°C)", "condition": "rainy", "humidity": "85%", "wind": "15 mph NE"},
    "tokyo":  {"temperature": "72°F (22°C)", "condition": "sunny", "humidity": "45%", "wind": "5 mph S"},
}

def execute_tool(name: str, args: dict) -> str:
    if name == "get_weather":
        key = next((k for k in WEATHER_DB if k in args.get("location", "").lower()), None)
        return json.dumps(WEATHER_DB.get(key, {"temperature": "65°F", "condition": "clear"}))
    return "{}"

# ── Recolector de respuestas en streaming ────────────────────────────────
def stream_and_collect(turn: int, **kwargs):
    """Transmitir respuesta, imprimir pensamiento/contenido en tiempo real, acumular reasoning_details/tool_calls."""
    rd = {}            # reasoning_details acumulados
    content = ""       # texto de respuesta acumulado
    tc_map = {}        # llamadas a herramientas acumuladas (por índice)
    cur = "none"       # sección de salida actual: none / thinking / content

    stream = client.chat.completions.create(stream=True, **kwargs)
    for chunk in stream:
        if not chunk.choices:
            continue
        delta = chunk.choices[0].delta

        # ── Manejar pensamiento ──
        rd_delta = getattr(delta, "reasoning_details", None)
        if rd_delta and isinstance(rd_delta, dict):
            for k, v in rd_delta.items():
                if k == "type":
                    rd[k] = v
                elif isinstance(v, str):
                    rd[k] = rd.get(k, "") + v
                elif v is not None:
                    rd[k] = v
            # Imprimir fragmentos de pensamiento en tiempo real
            thinking_chunk = rd_delta.get("thinking", "")
            if thinking_chunk:
                if cur != "thinking":
                    cur = "thinking"
                    label = "Pensamiento Entrelazado" if turn > 1 else "Pensando"
                    sys.stdout.write(f"\n[{label}] ")
                sys.stdout.write(thinking_chunk)
                sys.stdout.flush()

        # ── Manejar contenido ──
        if delta.content:
            if cur != "content":
                if cur == "thinking":
                    sys.stdout.write("\n")
                cur = "content"
                sys.stdout.write("\n[Respuesta] ")
            sys.stdout.write(delta.content)
            sys.stdout.flush()
            content += delta.content

        # ── Manejar tool_calls ──
        for tc in delta.tool_calls or []:
            i = tc.index
            if i not in tc_map:
                tc_map[i] = {"id": "", "type": "function",
                             "function": {"name": "", "arguments": ""}}
            if tc.id:
                tc_map[i]["id"] = tc.id
            if tc.function:
                tc_map[i]["function"]["name"] += tc.function.name or ""
                tc_map[i]["function"]["arguments"] += tc.function.arguments or ""

    # Finalizar la sección de salida actual
    if cur in ("thinking", "content"):
        sys.stdout.write("\n")

    tool_calls = [tc_map[i] for i in sorted(tc_map)] if tc_map else None
    return {
        "content": content or None,
        "reasoning_details": rd or None,
        "tool_calls": tool_calls,
    }

# ── Bucle de conversación de múltiples turnos ─────────────────────────────
messages = [
    {"role": "user", "content": "¿Cómo está el clima en Boston? Luego recomienda qué usar."}
]

turn = 0
while True:
    turn += 1
    print(f"\n── Turno {turn} ──")

    result = stream_and_collect(
        turn,
        model="claude-sonnet-4-5",
        messages=messages,
        tools=tools,
        extra_body={"reasoning": {"max_tokens": 2000}},
    )

    # Imprimir llamadas a herramientas
    if result["tool_calls"]:
        for tc in result["tool_calls"]:
            print(f"[Llamada a la Herramienta: {tc['function']['name']}] {tc['function']['arguments']}")

    # Construir mensaje del asistente, preservar reasoning_details (¡crítico!)
    assistant_msg = {"role": "assistant", "content": result["content"]}
    if result["tool_calls"]:
        assistant_msg["tool_calls"] = result["tool_calls"]
    if result["reasoning_details"]:
        assistant_msg["reasoning_details"] = result["reasoning_details"]  # pasar de vuelta sin modificar
    messages.append(assistant_msg)

    # Sin tool_calls significa que la conversación ha terminado
    if not result["tool_calls"]:
        break

    # Ejecutar herramientas y agregar resultados a los mensajes
    for tc in result["tool_calls"]:
        args = json.loads(tc["function"]["arguments"])
        tool_result = execute_tool(tc["function"]["name"], args)
        print(f"[Resultado de la Herramienta: {tc['function']['name']}] {tool_result}")
        messages.append({"role": "tool", "tool_call_id": tc["id"], "content": tool_result})

1.6 Reglas de Mapeo de Intensidad de Pensamiento

Modo de Esfuerzo:

  • Opus 4.6 / Sonnet 4.6 y superiores: se mapea al nivel de esfuerzo nativo de Pensamiento Adaptativo de Anthropic.
  • Otros modelos: calculado utilizando la fórmula para budget_tokens:
budget_tokens = max(min(max_tokens × effort_ratio, 128000), 1024)
esfuerzo esfuerzo_ratio
xhigh 0.95
high 0.80
medium 0.50
low 0.20
minimal 0.10

Mapeo de Esfuerzo de Pensamiento Adaptativo:

Esfuerzo Entrante Opus 4.6 Sonnet 4.6
xhigh máx alto
high alto alto
medium medio medio
low bajo bajo
minimal bajo bajo

Modo max_tokens: Asignado directamente como budget_tokens de Anthropic.

-think sufijo: Opus/Sonnet 4.6+ utiliza pensamiento adaptativo (esfuerzo=medio); otros modelos establecen budget_tokens = min(10240, max_tokens - 1), con un max_tokens predeterminado de 4096.


2. Almacenamiento en Caché de Prompts

Puedes utilizar el Almacenamiento en Caché de Prompts al realizar solicitudes al modelo Claude a través de la interfaz de Chat. Al establecer puntos de control cache_control en los mensajes, se pueden almacenar en caché grandes bloques de texto (como tarjetas de rol, datos RAG, capítulos de libros, etc.) para su reutilización, permitiendo que solicitudes posteriores accedan directamente a la caché y reduzcan significativamente los costos.

Documentación Oficial de Claude: Almacenamiento en Caché de Prompts

2.1 Costos de Almacenamiento en Caché

Operación Multiplicador de Precio (relativo al precio de entrada original)
Escritura en Caché (TTL de 5 minutos) 1.25x
Escritura en Caché (TTL de 1 hora) 2x
Lectura en Caché 0.1x

2.2 Modelos Soportados y Longitud Mínima de Caché

Modelo Conteo Mínimo de Tokens en Caché
Claude Opus 4.8 1024
Claude Opus 4.7 2048
Claude Opus 4.6 / Opus 4.5 4096
Claude Sonnet 4.6 / Sonnet 4.5 / Opus 4.1 / Opus 4 / Sonnet 4 / Sonnet 3.7 (obsoleto) 1024
Claude Haiku 4.5 4096
Claude Haiku 3.5 (obsoleto) / Haiku 3 2048
Límite de Cantidad de Puntos de Control: Un máximo de 4 puntos de control cache_control por solicitud.

2.3 TTL de Caché

TTL Sintaxis Escenarios Aplicables
5 minutos (predeterminado) "cache_control": {"type": "ephemeral"} Sesiones cortas, solicitudes rutinarias
1 hora "cache_control": {"type": "ephemeral", "ttl": "1h"} Sesiones largas, para evitar escrituras repetidas en caché

Los costos de escritura para TTL de 1 hora son más altos, pero pueden ahorrar gastos totales al reducir escrituras repetidas en sesiones largas. Todos los modelos de Claude 4.5 y posteriores de todos los proveedores (incluyendo Anthropic, Amazon Bedrock, Google Vertex AI) admiten TTL de 1 hora.

2.4 Uso

Puedes establecer puntos de control de caché utilizando el campo cache_control en system, user (incluyendo imágenes), y tools. Los siguientes ejemplos solo muestran la estructura clave, omitiendo grandes bloques de texto.

Almacenamiento en Caché del Mensaje del Sistema (TTL predeterminado de 5 minutos):

{
  "model": "claude-opus-4-5",
  "messages": [
    {
      "role": "system",
      "content": [
        {"type": "text", "text": "Eres un asistente de IA"},
        {
          "type": "text",
          "text": "(contexto largo)",
          "cache_control": {"type": "ephemeral"}
        }
      ]
    },
    {
      "role": "user",
      "content": [{"type": "text", "text": "Hola"}]
    }
  ]
}

Almacenamiento en Caché del Mensaje del Usuario (TTL de 1 hora):

{
  "model": "claude-opus-4-5",
  "messages": [
    {
      "role": "system",
      "content": [{"type": "text", "text": "Eres un asistente de IA"}]
    },
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "(contexto largo)",
          "cache_control": {"type": "ephemeral", "ttl": "1h"}
        },
        {"type": "text", "text": "Hola"}
      ]
    }
  ]
}

Almacenamiento en Caché del Mensaje de Imagen:

{
  "role": "user",
  "content": [
    {
      "type": "image_url",
      "image_url": {"detail": "auto", "url": "data:image/jpeg;base64,/9j/4AAQ..."},
      "cache_control": {"type": "ephemeral"}
    },
    {"type": "text", "text": "¿Qué es esto?"}
  ]
}

Almacenamiento en Caché de la Definición de la Herramienta:

cache_control se coloca en el nivel superior del objeto de la herramienta (junto a type y function):

{
  "tools": [{
    "type": "function",
    "function": {
      "name": "get_weather",
      "description": "Obtener el clima actual para una ubicación",
      "parameters": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"]
      }
    },
    "cache_control": {"type": "ephemeral", "ttl": "1h"}
  }]
}

2.5 Visualización del Estado de la Caché

El usage de la respuesta devolverá claude_cache_tokens_details, registrando información detallada de la caché:

Primera Solicitud (Creando Caché):

{
  "usage": {
    "prompt_tokens": 22,
    "completion_tokens": 890,
    "total_tokens": 912,
    "claude_cache_tokens_details": {
      "cache_creation_input_tokens": 6266,
      "cache_read_input_tokens": 0,
      "cache_write_5_minutes_input_tokens": 6266,
      "cache_write_1_hour_input_tokens": 0
    }
  }
}

Solicitudes Subsiguientes (Cache Hit):

{
  "usage": {
    "prompt_tokens": 22,
    "completion_tokens": 810,
    "total_tokens": 832,
    "prompt_tokens_details": {
      "cached_tokens": 6266
    },
    "claude_cache_tokens_details": {
      "cache_creation_input_tokens": 0,
      "cache_read_input_tokens": 6266,
      "cache_write_5_minutes_input_tokens": 0,
      "cache_write_1_hour_input_tokens": 0
    }
  }
}
Campo Significado
cache_creation_input_tokens Número de tokens escritos en caché en esta solicitud
cache_read_input_tokens Número de tokens leídos de la caché en esta solicitud
cache_write_5_minutes_input_tokens Número de tokens escritos en la caché de TTL de 5 minutos
cache_write_1_hour_input_tokens Número de tokens escritos en la caché de TTL de 1 hora
prompt_tokens_details.cached_tokens Número de tokens en caché cuando se accede a la caché, compatible con el formato de OpenAI

3. Encabezado de Solicitud para anthropic-beta

Puedes habilitar funciones beta del modelo Claude a través del Encabezado HTTP anthropic-beta, que AIHubMix pasará a la API de Anthropic.

Uso

Agrega anthropic-beta al encabezado de la solicitud, con el valor siendo el identificador de la función beta correspondiente:

curl "https://aihubmix.com/v1/chat/completions" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY" \
  -H "anthropic-beta: context-1m-2025-08-07" \
  -d '{
  "model": "claude-opus-4-5",
  "messages": [
    {
      "role": "system",
      "content": [
        {"type": "text", "text": "Eres un asistente de IA"},
        {
          "type": "text",
          "text": "(contexto largo)",
          "cache_control": {"type": "ephemeral"}
        }
      ]
    },
    {"role": "user", "content": [{"type": "text", "text": "hola"}]}
  ]
}'
Para identificadores beta específicos disponibles, consulta la Documentación de la API de Anthropic.

Última actualización: 2026-06-01

More from the blog