Aggiornamento dell'interfaccia compatibile con OpenAI: supporto approfondito per Claude

31 lug 2026 · AIHubMix · 8 min read · Notizie

Aggiornamento dell'interfaccia compatibile con OpenAI: supporto approfondito per Claude

Abbiamo aggiornato l'interfaccia compatibile con OpenAI con ottimizzazioni più profonde specificamente per i modelli della serie Claude. Ora puoi controllare il pensiero e il caching in modo più preciso e conveniente. Il pensiero intercalato nelle conversazioni a più turni è ora più user-friendly, consentendo un'integrazione senza soluzione di continuità senza parametri aggiuntivi. Supporta anche l'attivazione delle funzionalità beta offerte da Anthropic.

1. Pensiero del Modello (Pensiero Esteso)

1.1 Vantaggi del Pensiero Intercalato

Quando il pensiero intercalato non è abilitato, il modello esegue il pensiero solo una volta all'inizio di un turno dell'assistente; le risposte successive vengono generate direttamente dopo aver ricevuto i risultati degli strumenti, senza produrre nuovi blocchi di pensiero:

User → [Pensiero] → Chiamata Strumento → Risultato Strumento → Risposta

Quando il pensiero intercalato è abilitato, il modello inserisce un nuovo blocco di pensiero ogni volta che riceve un risultato dello strumento, formando una catena di ragionamento:

User → [Pensiero] → Chiamata Strumento → Risultato Strumento → [Pensiero] → Risposta
                                                ↑ Pensiero Intercalato

Questo consente al modello di:

  • Eseguire un ragionamento secondario basato sui risultati degli strumenti, piuttosto che semplicemente concatenare le uscite.
  • Collegare il ragionamento tra più chiamate agli strumenti, dove ogni decisione si basa sull'analisi del passo precedente.
Riferimento: Pensiero Intercalato di Anthropic

1.2 Abilitare il Pensiero

Puoi abilitare il pensiero in quattro modi, scegliendo uno di essi:

Metodo Esempio Descrizione
reasoning_effort "reasoning_effort": "low" Parametro standard di OpenAI, posizionato a livello superiore del corpo della richiesta
reasoning.effort "reasoning": {"effort": "low"} Equivalente al metodo precedente, posizionato all'interno dell'oggetto di ragionamento
reasoning.max_tokens "reasoning": {"max_tokens": 1024} Controlla precisamente il numero massimo di token per il pensiero
Nome del modello con -think "model": "claude-sonnet-4-5-think" Il modo più semplice, non richiede parametri aggiuntivi
Priorità (quando vengono utilizzati più metodi): reasoning_effort > reasoning.max_tokens > reasoning.effort > -think suffisso

Valori possibili per l'impegno: minimal / low / medium / high / xhigh

1.3 Restituzione del Pensiero

Il messaggio di risposta includerà due nuovi campi:

  • reasoning_content: Contenuto del pensiero (stringa), per una facile visualizzazione.
  • reasoning_details: Informazioni strutturate complete sul pensiero, che devono essere restituite così come sono nelle conversazioni a più turni; la struttura interna può differire tra i fornitori.

Esempio non in streaming (omettendo campi non correlati):

{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": "Ciao! Come posso aiutarti oggi?",
      "reasoning_content": "L'utente sta solo dicendo ciao...",
      "reasoning_details": {
        "type": "thinking",
        "thinking": "L'utente sta solo dicendo ciao...",
        "signature": "Er8CCkYI..."
      }
    }
  }]
}

Nei risultati in streaming, il contenuto del pensiero sarà inviato in blocchi tramite delta.reasoning_content e delta.reasoning_details. Per la logica completa di concatenazione in streaming, fare riferimento all'esempio completo qui sotto.

1.4 Mantenere il Pensiero nelle Conversazioni a Più Turni (Il Pensiero Intercalato è integrato, non sono necessari parametri aggiuntivi)

Per consentire al modello di continuare le sue capacità di ragionamento nelle conversazioni a più turni, basta inserire il reasoning_details restituito in precedenza così com'è nel messaggio dell'assistente del turno successivo:

messages = [
    {"role": "user", "content": "Che tempo fa a 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 abiliterà automaticamente il pensiero intercalato quando rileva informazioni storiche sul pensiero nella richiesta, consentendo al modello di continuare un ragionamento profondo dopo aver ricevuto i risultati delle chiamate agli strumenti senza richiedere parametri aggiuntivi.

1.5 Esempio Completo

I seguenti due esempi dimostrano il processo completo di chiamata agli strumenti a più turni + pensiero intercalato: richiesta dell'utente → il modello pensa e chiama uno strumento → inietta i risultati dello strumento (preservando reasoning_details) → il modello pensiero intercalato fornisce la risposta finale.

Non in streaming · Pensiero Intercalato

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-***"),
)

# ── Definizione dello strumento ───────────────────────────────────────────
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Ottieni il meteo attuale per una località",
        "parameters": {
            "type": "object",
            "properties": {"location": {"type": "string", "description": "Nome della città"}},
            "required": ["location"]
        }
    }
}]

# ── Esecuzione simulata dello strumento ─────────────────────────────────────
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 "{}"

# ── Ciclo di conversazione a più turni ─────────────────────────────
messages = [
    {"role": "user", "content": "Che tempo fa a Boston? Poi consiglia cosa indossare."}
]

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

    # Stampa il processo di pensiero
    if msg.reasoning_content:
        label = "Pensiero Intercalato" if turn > 1 else "Pensiero"
        print(f"[{label}] {msg.reasoning_content}")

    # Stampa il contenuto della risposta
    if msg.content:
        print(f"[Risposta] {msg.content}")

    # Stampa le chiamate agli strumenti
    if msg.tool_calls:
        for tc in msg.tool_calls:
            print(f"[Chiamata Strumento: {tc.function.name}] {tc.function.arguments}")

    # Costruisci il messaggio dell'assistente, preserva reasoning_details (critico!)
    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  # restituisci non modificato
    messages.append(assistant_msg)

    # Nessuna tool_calls significa che la conversazione è finita
    if not msg.tool_calls:
        break

    # Esegui strumenti e aggiungi risultati ai messaggi
    for tc in msg.tool_calls:
        args = json.loads(tc.function.arguments)
        result = execute_tool(tc.function.name, args)
        print(f"[Risultato Strumento: {tc.function.name}] {result}")
        messages.append({"role": "tool", "tool_call_id": tc.id, "content": result})

Streaming · Pensiero Intercalato

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-***"),
)

# ── Definizione dello strumento & esecuzione simulata ─────────────────────────
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Ottieni il meteo attuale per una località",
        "parameters": {
            "type": "object",
            "properties": {"location": {"type": "string", "description": "Nome della città"}},
            "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 "{}"

# ── Collettore di risposta in streaming ────────────────────────────────
def stream_and_collect(turn: int, **kwargs):
    """Stream di risposta, stampa pensiero/contenuto in tempo reale, accumula reasoning_details/tool_calls."""
    rd = {}            # reasoning_details accumulati
    content = ""       # testo di risposta accumulato
    tc_map = {}        # tool_calls accumulati (per indice)
    cur = "none"       # sezione di output corrente: 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

        # ── Gestisci il pensiero ──
        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
            # Stampa i blocchi di pensiero in tempo reale
            thinking_chunk = rd_delta.get("thinking", "")
            if thinking_chunk:
                if cur != "thinking":
                    cur = "thinking"
                    label = "Pensiero Intercalato" if turn > 1 else "Pensiero"
                    sys.stdout.write(f"\n[{label}] ")
                sys.stdout.write(thinking_chunk)
                sys.stdout.flush()

        # ── Gestisci il contenuto ──
        if delta.content:
            if cur != "content":
                if cur == "thinking":
                    sys.stdout.write("\n")
                cur = "content"
                sys.stdout.write("\n[Risposta] ")
            sys.stdout.write(delta.content)
            sys.stdout.flush()
            content += delta.content

        # ── Gestisci 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 ""

    # Termina la sezione di output corrente
    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,
    }

# ── Ciclo di conversazione a più turni ─────────────────────────────
messages = [
    {"role": "user", "content": "Che tempo fa a Boston? Poi consiglia cosa indossare."}
]

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}},
    )

    # Stampa le chiamate agli strumenti
    if result["tool_calls"]:
        for tc in result["tool_calls"]:
            print(f"[Chiamata Strumento: {tc['function']['name']}] {tc['function']['arguments']}")

    # Costruisci il messaggio dell'assistente, preserva reasoning_details (critico!)
    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"]  # restituisci non modificato
    messages.append(assistant_msg)

    # Nessuna tool_calls significa che la conversazione è finita
    if not result["tool_calls"]:
        break

    # Esegui strumenti e aggiungi risultati ai messaggi
    for tc in result["tool_calls"]:
        args = json.loads(tc["function"]["arguments"])
        tool_result = execute_tool(tc["function"]["name"], args)
        print(f"[Risultato Strumento: {tc['function']['name']}] {tool_result}")
        messages.append({"role": "tool", "tool_call_id": tc["id"], "content": tool_result})

1.6 Regole di Mappatura dell'Intensità del Pensiero

Modalità di Impegno:

  • Opus 4.6 / Sonnet 4.6 e superiori: mappa al livello di impegno Pensiero Adattivo nativo di Anthropic.
  • Altri modelli: calcolato utilizzando la formula per budget_tokens:
budget_tokens = max(min(max_tokens × effort_ratio, 128000), 1024)
impegno effort_ratio
xhigh 0.95
high 0.80
medium 0.50
low 0.20
minimal 0.10

Mappatura dell'Impegno del Pensiero Adattivo:

Impegno in Entrata Opus 4.6 Sonnet 4.6
xhigh max high
high high high
medium medium medium
low low low
minimal low low

Modalità max_tokens: Assegnato direttamente come budget_tokens di Anthropic.

-think suffisso: Opus/Sonnet 4.6+ utilizza il pensiero adattivo (impegno=medio); altri modelli impostano budget_tokens = min(10240, max_tokens - 1), con un max_tokens predefinito di 4096.


2. Caching dei Prompt

Puoi utilizzare il Caching dei Prompt quando fai richieste al modello Claude tramite l'interfaccia Chat. Impostando i punti di interruzione cache_control nei messaggi, grandi blocchi di testo (come schede di ruolo, dati RAG, capitoli di libri, ecc.) possono essere memorizzati per riutilizzo, consentendo alle richieste successive di colpire direttamente la cache e ridurre significativamente i costi.

Documentazione ufficiale di Claude: Caching dei Prompt

2.1 Costi di Caching

Operazione Moltiplicatore di Prezzo (rispetto al prezzo di input originale)
Scrittura Cache (TTL di 5 minuti) 1.25x
Scrittura Cache (TTL di 1 ora) 2x
Lettura Cache 0.1x

2.2 Modelli Supportati e Lunghezza Minima della Cache

Modello Conteggio Minimo di Token nella Cache
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 (deprecato) 1024
Claude Haiku 4.5 4096
Claude Haiku 3.5 (deprecato) / Haiku 3 2048
Limite di Quantità dei Punti di Interruzione: Un massimo di 4 cache_control punti di interruzione per richiesta.

2.3 TTL della Cache

TTL Sintassi Scenari Applicabili
5 minuti (predefinito) "cache_control": {"type": "ephemeral"} Sessioni brevi, richieste di routine
1 ora "cache_control": {"type": "ephemeral", "ttl": "1h"} Sessioni lunghe, per evitare scritture ripetute nella cache

I costi di scrittura per TTL di 1 ora sono più elevati, ma possono risparmiare spese totali riducendo le scritture ripetute in sessioni lunghe. Tutti i modelli da Claude 4.5 in poi di tutti i fornitori (inclusi Anthropic, Amazon Bedrock, Google Vertex AI) supportano TTL di 1 ora.

2.4 Utilizzo

Puoi impostare i punti di interruzione della cache utilizzando il campo cache_control in system, user (inclusi immagini) e tools. I seguenti esempi mostrano solo la struttura chiave, omettendo grandi blocchi di testo.

Caching del Messaggio di Sistema (TTL predefinito di 5 minuti):

{
  "model": "claude-opus-4-5",
  "messages": [
    {
      "role": "system",
      "content": [
        {"type": "text", "text": "Sei un assistente AI"},
        {
          "type": "text",
          "text": "(lungo contesto)",
          "cache_control": {"type": "ephemeral"}
        }
      ]
    },
    {
      "role": "user",
      "content": [{"type": "text", "text": "Ciao"}]
    }
  ]
}

Caching del Messaggio dell'Utente (TTL di 1 ora):

{
  "model": "claude-opus-4-5",
  "messages": [
    {
      "role": "system",
      "content": [{"type": "text", "text": "Sei un assistente AI"}]
    },
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "(lungo contesto)",
          "cache_control": {"type": "ephemeral", "ttl": "1h"}
        },
        {"type": "text", "text": "Ciao"}
      ]
    }
  ]
}

Caching del Messaggio Immagine:

{
  "role": "user",
  "content": [
    {
      "type": "image_url",
      "image_url": {"detail": "auto", "url": "data:image/jpeg;base64,/9j/4AAQ..."},
      "cache_control": {"type": "ephemeral"}
    },
    {"type": "text", "text": "Cos'è questo?"}
  ]
}

Caching della Definizione dello Strumento:

cache_control è posizionato a livello superiore dell'oggetto strumento (insieme a type e function):

{
  "tools": [{
    "type": "function",
    "function": {
      "name": "get_weather",
      "description": "Ottieni il meteo attuale per una località",
      "parameters": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"]
      }
    },
    "cache_control": {"type": "ephemeral", "ttl": "1h"}
  }]
}

2.5 Visualizzazione dello Stato della Cache

Il campo usage della risposta restituirà claude_cache_tokens_details, registrando informazioni dettagliate sulla cache:

Prima Richiesta (Creazione della Cache):

{
  "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
    }
  }
}

Richieste Successive (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 Significato
cache_creation_input_tokens Numero di token scritti nella cache in questa richiesta
cache_read_input_tokens Numero di token letti dalla cache in questa richiesta
cache_write_5_minutes_input_tokens Numero di token scritti nella cache TTL di 5 minuti
cache_write_1_hour_input_tokens Numero di token scritti nella cache TTL di 1 ora
prompt_tokens_details.cached_tokens Numero di token memorizzati nella cache quando la cache viene colpita, compatibile con il formato OpenAI

3. Intestazione della Richiesta per anthropic-beta

Puoi abilitare le funzionalità beta del modello Claude tramite l'intestazione HTTP anthropic-beta, che AIHubMix passerà all'API di Anthropic.

Utilizzo

Aggiungi anthropic-beta all'intestazione della richiesta, con il valore che corrisponde all'identificatore della funzionalità beta:

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": "Sei un assistente AI"},
        {
          "type": "text",
          "text": "(lungo contesto)",
          "cache_control": {"type": "ephemeral"}
        }
      ]
    },
    {"role": "user", "content": [{"type": "text", "text": "ciao"}]}
  ]
}'
Per identificatori beta specifici disponibili, fare riferimento alla Documentazione API di Anthropic.

Ultimo aggiornamento: 2026-06-01

More from the blog