Nous avons amélioré l'interface compatible OpenAI avec des optimisations plus poussées, spécifiquement pour les modèles de la série Claude. Vous pouvez désormais contrôler la réflexion et la mise en cache de manière plus précise et plus pratique. La réflexion entrelacée dans les conversations multi-tours est maintenant plus conviviale : elle s'intègre de façon transparente, sans paramètres supplémentaires. Elle permet aussi d'activer les fonctionnalités bêta proposées par Anthropic.
1. Réflexion du modèle (Extended Thinking)
1.1 Avantages de la réflexion entrelacée
Lorsque la réflexion entrelacée n'est pas activée, le modèle ne réfléchit qu'une seule fois, au début d'un tour de l'assistant ; les réponses suivantes sont générées directement après la réception des résultats d'outils, sans produire de nouveaux blocs de réflexion :
User → [Thinking] → Tool Call → Tool Result → Response
Lorsque la réflexion entrelacée est activée, le modèle insère un nouveau bloc de réflexion à chaque réception d'un résultat d'outil, formant une chaîne de raisonnement :
User → [Thinking] → Tool Call → Tool Result → [Thinking] → Response
↑ Interleaved Thinking
Cela permet au modèle :
- d'effectuer un raisonnement secondaire à partir des résultats d'outils, au lieu de simplement concaténer les sorties.
- d'enchaîner le raisonnement entre plusieurs appels d'outils, chaque décision reposant sur l'analyse de l'étape précédente.
Référence : Anthropic Interleaved Thinking
1.2 Activer la réflexion
Vous pouvez activer la réflexion de quatre manières, en choisissant celle de votre choix :
| Méthode | Exemple | Description |
|---|---|---|
reasoning_effort |
"reasoning_effort": "low" |
Paramètre standard d'OpenAI, placé au niveau supérieur du corps de la requête |
reasoning.effort |
"reasoning": {"effort": "low"} |
Équivalent à la méthode précédente, placé dans l'objet reasoning |
reasoning.max_tokens |
"reasoning": {"max_tokens": 1024} |
Contrôle précisément le nombre maximal de tokens pour la réflexion |
Nom du modèle avec le suffixe -think |
"model": "claude-sonnet-4-5-think" |
La façon la plus simple, sans paramètres supplémentaires |
Priorité (lorsque plusieurs méthodes sont utilisées) :reasoning_effort>reasoning.max_tokens>reasoning.effort> suffixe-think
Valeurs possibles pour effort : minimal / low / medium / high / xhigh
1.3 Retour de la réflexion
Le message de réponse comprend deux nouveaux champs :
reasoning_content: contenu de la réflexion (chaîne de caractères), pour un affichage facile.reasoning_details: informations structurées complètes sur la réflexion, qui doivent être renvoyées telles quelles dans les conversations multi-tours ; la structure interne peut varier selon le fournisseur.
Exemple de réponse non diffusée en continu (champs non pertinents omis) :
{
"choices": [{
"message": {
"role": "assistant",
"content": "Hello! How can I help you today?",
"reasoning_content": "The user is just saying hello...",
"reasoning_details": {
"type": "thinking",
"thinking": "The user is just saying hello...",
"signature": "Er8CCkYI..."
}
}
}]
}
Dans les réponses en streaming, le contenu de la réflexion est envoyé par fragments via delta.reasoning_content et delta.reasoning_details. Pour la logique complète de concaténation en streaming, reportez-vous à l'exemple complet ci-dessous.
1.4 Conserver la réflexion dans les conversations multi-tours (la réflexion entrelacée est intégrée, aucun paramètre supplémentaire n'est nécessaire)
Pour permettre au modèle de poursuivre ses capacités de raisonnement dans les conversations multi-tours, placez simplement les reasoning_details renvoyés précédemment tels quels dans le message de l'assistant du tour suivant :
messages = [
{"role": "user", "content": "What's the weather like in 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 active automatiquement la réflexion entrelacée lorsqu'il détecte des informations de réflexion historiques dans la requête, ce qui permet au modèle de poursuivre un raisonnement approfondi après réception des résultats d'appels d'outils, sans paramètres supplémentaires.
1.5 Exemple complet
Les deux exemples suivants illustrent le processus complet d'appel d'outils multi-tours avec réflexion entrelacée : question de l'utilisateur → le modèle réfléchit et appelle un outil → injection des résultats de l'outil (en conservant reasoning_details) → le modèle réfléchit de manière entrelacée et donne la réponse finale.
Sans streaming · Réflexion entrelacée
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-***"),
)
# ── Tool definition ───────────────────────────────────────────
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a location",
"parameters": {
"type": "object",
"properties": {"location": {"type": "string", "description": "City name"}},
"required": ["location"]
}
}
}]
# ── Mock tool execution ───────────────────────────────────────
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 "{}"
# ── Multi-turn conversation loop ─────────────────────────────
messages = [
{"role": "user", "content": "What's the weather like in Boston? Then recommend what to wear."}
]
turn = 0
while True:
turn += 1
print(f"\n── Turn {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
# Print thinking process
if msg.reasoning_content:
label = "Interleaved Thinking" if turn > 1 else "Thinking"
print(f"[{label}] {msg.reasoning_content}")
# Print response content
if msg.content:
print(f"[Response] {msg.content}")
# Print tool calls
if msg.tool_calls:
for tc in msg.tool_calls:
print(f"[Tool Call: {tc.function.name}] {tc.function.arguments}")
# Build assistant message, preserve reasoning_details (critical!)
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 # pass back unmodified
messages.append(assistant_msg)
# No tool_calls means conversation is done
if not msg.tool_calls:
break
# Execute tools and append results to messages
for tc in msg.tool_calls:
args = json.loads(tc.function.arguments)
result = execute_tool(tc.function.name, args)
print(f"[Tool Result: {tc.function.name}] {result}")
messages.append({"role": "tool", "tool_call_id": tc.id, "content": result})
Streaming · Réflexion entrelacée
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-***"),
)
# ── Tool definition & mock execution ─────────────────────────
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a location",
"parameters": {
"type": "object",
"properties": {"location": {"type": "string", "description": "City name"}},
"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 "{}"
# ── Stream response collector ────────────────────────────────
def stream_and_collect(turn: int, **kwargs):
"""Stream response, print thinking/content in real-time, accumulate reasoning_details/tool_calls."""
rd = {} # accumulated reasoning_details
content = "" # accumulated response text
tc_map = {} # accumulated tool_calls (by index)
cur = "none" # current output section: 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
# ── Handle thinking ──
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
# Print thinking chunks in real-time
thinking_chunk = rd_delta.get("thinking", "")
if thinking_chunk:
if cur != "thinking":
cur = "thinking"
label = "Interleaved Thinking" if turn > 1 else "Thinking"
sys.stdout.write(f"\n[{label}] ")
sys.stdout.write(thinking_chunk)
sys.stdout.flush()
# ── Handle content ──
if delta.content:
if cur != "content":
if cur == "thinking":
sys.stdout.write("\n")
cur = "content"
sys.stdout.write("\n[Response] ")
sys.stdout.write(delta.content)
sys.stdout.flush()
content += delta.content
# ── Handle 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 ""
# End current output section
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,
}
# ── Multi-turn conversation loop ─────────────────────────────
messages = [
{"role": "user", "content": "What's the weather like in Boston? Then recommend what to wear."}
]
turn = 0
while True:
turn += 1
print(f"\n── Turn {turn} ──")
result = stream_and_collect(
turn,
model="claude-sonnet-4-5",
messages=messages,
tools=tools,
extra_body={"reasoning": {"max_tokens": 2000}},
)
# Print tool calls
if result["tool_calls"]:
for tc in result["tool_calls"]:
print(f"[Tool Call: {tc['function']['name']}] {tc['function']['arguments']}")
# Build assistant message, preserve reasoning_details (critical!)
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"] # pass back unmodified
messages.append(assistant_msg)
# No tool_calls means conversation is done
if not result["tool_calls"]:
break
# Execute tools and append results to messages
for tc in result["tool_calls"]:
args = json.loads(tc["function"]["arguments"])
tool_result = execute_tool(tc["function"]["name"], args)
print(f"[Tool Result: {tc['function']['name']}] {tool_result}")
messages.append({"role": "tool", "tool_call_id": tc["id"], "content": tool_result})
1.6 Règles de correspondance de l'intensité de réflexion
Mode effort :
- Opus 4.6 / Sonnet 4.6 et supérieurs : correspond au niveau d'effort natif Adaptive Thinking d'Anthropic.
- Autres modèles : calculé à l'aide de la formule de
budget_tokens:
budget_tokens = max(min(max_tokens × effort_ratio, 128000), 1024)
| effort | effort_ratio |
|---|---|
| xhigh | 0.95 |
| high | 0.80 |
| medium | 0.50 |
| low | 0.20 |
| minimal | 0.10 |
Correspondance de l'effort en Adaptive Thinking :
| Effort entrant | Opus 4.6 | Sonnet 4.6 |
|---|---|---|
| xhigh | max | high |
| high | high | high |
| medium | medium | medium |
| low | low | low |
| minimal | low | low |
Mode max_tokens : directement attribué comme budget_tokens d'Anthropic.
-think suffixe : Opus/Sonnet 4.6+ utilise la réflexion adaptative (effort=medium) ; pour les autres modèles, budget_tokens = min(10240, max_tokens - 1), avec une valeur par défaut de max_tokens de 4096.
2. Mise en cache des prompts (Prompt Caching)
Vous pouvez utiliser la mise en cache des prompts lorsque vous envoyez des requêtes au modèle Claude via l'interface Chat. En définissant des points d'arrêt cache_control dans les messages, de grands blocs de texte (comme des fiches de rôle, des données RAG, des chapitres de livres, etc.) peuvent être mis en cache pour être réutilisés. Les requêtes suivantes atteignent alors directement le cache, ce qui réduit considérablement les coûts.
Documentation officielle de Claude : Prompt Caching
2.1 Coûts de mise en cache
| Opération | Multiplicateur de prix (par rapport au prix d'entrée d'origine) |
|---|---|
| Écriture en cache (TTL de 5 minutes) | 1.25x |
| Écriture en cache (TTL d'1 heure) | 2x |
| Lecture du cache | 0.1x |
2.2 Modèles pris en charge et longueur minimale de cache
| Modèle | Nombre minimal de tokens à mettre en 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 (obsolète) | 1024 |
| Claude Haiku 4.5 | 4096 |
| Claude Haiku 3.5 (obsolète) / Haiku 3 | 2048 |
Limite du nombre de points d'arrêt : un maximum de 4 points d'arrêt cache_control par requête.2.3 TTL du cache
| TTL | Syntaxe | Scénarios d'utilisation |
|---|---|---|
| 5 minutes (par défaut) | "cache_control": {"type": "ephemeral"} |
Sessions courtes, requêtes courantes |
| 1 heure | "cache_control": {"type": "ephemeral", "ttl": "1h"} |
Sessions longues, pour éviter des écritures répétées dans le cache |
Les coûts d'écriture pour un TTL d'1 heure sont plus élevés, mais ils peuvent réduire la dépense totale en limitant les écritures répétées lors de longues sessions. Tous les modèles Claude à partir de la version 4.5, chez tous les fournisseurs (y compris Anthropic, Amazon Bedrock et Google Vertex AI), prennent en charge un TTL d'1 heure.
2.4 Utilisation
Vous pouvez définir des points d'arrêt de cache à l'aide du champ cache_control dans system, user (y compris les images) et tools. Les exemples suivants montrent uniquement la structure clé, sans les grands blocs de texte.
Mise en cache du message système (TTL de 5 minutes par défaut) :
{
"model": "claude-opus-4-5",
"messages": [
{
"role": "system",
"content": [
{"type": "text", "text": "You are an AI assistant"},
{
"type": "text",
"text": "(long context)",
"cache_control": {"type": "ephemeral"}
}
]
},
{
"role": "user",
"content": [{"type": "text", "text": "Hello"}]
}
]
}
Mise en cache du message utilisateur (TTL d'1 heure) :
{
"model": "claude-opus-4-5",
"messages": [
{
"role": "system",
"content": [{"type": "text", "text": "You are an AI assistant"}]
},
{
"role": "user",
"content": [
{
"type": "text",
"text": "(long context)",
"cache_control": {"type": "ephemeral", "ttl": "1h"}
},
{"type": "text", "text": "Hello"}
]
}
]
}
Mise en cache des messages d'image :
{
"role": "user",
"content": [
{
"type": "image_url",
"image_url": {"detail": "auto", "url": "data:image/jpeg;base64,/9j/4AAQ..."},
"cache_control": {"type": "ephemeral"}
},
{"type": "text", "text": "What's this?"}
]
}
Mise en cache des définitions d'outils :
cache_control est placé au niveau supérieur de l'objet outil (à côté de type et function) :
{
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a location",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"]
}
},
"cache_control": {"type": "ephemeral", "ttl": "1h"}
}]
}
2.5 Consulter l'état du cache
La valeur usage de la réponse renvoie claude_cache_tokens_details, qui enregistre les informations détaillées sur le cache :
Première requête (création du 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
}
}
}
Requêtes suivantes (succès du cache) :
{
"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
}
}
}
| Champ | Signification |
|---|---|
cache_creation_input_tokens |
Nombre de tokens écrits dans le cache lors de cette requête |
cache_read_input_tokens |
Nombre de tokens lus depuis le cache lors de cette requête |
cache_write_5_minutes_input_tokens |
Nombre de tokens écrits dans le cache avec un TTL de 5 minutes |
cache_write_1_hour_input_tokens |
Nombre de tokens écrits dans le cache avec un TTL d'1 heure |
prompt_tokens_details.cached_tokens |
Nombre de tokens mis en cache en cas de succès du cache, compatible avec le format OpenAI |
3. En-tête de requête pour anthropic-beta
Vous pouvez activer les fonctionnalités bêta du modèle Claude via l'en-tête HTTP anthropic-beta, qu'AIHubMix transmet à l'API d'Anthropic.
Utilisation
Ajoutez anthropic-beta à l'en-tête de la requête, avec pour valeur l'identifiant de la fonctionnalité bêta correspondante :
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": "You are an AI assistant"},
{
"type": "text",
"text": "(long context)",
"cache_control": {"type": "ephemeral"}
}
]
},
{"role": "user", "content": [{"type": "text", "text": "hello"}]}
]
}'
Pour connaître les identifiants bêta disponibles, reportez-vous à la documentation de l'API Anthropic.
Dernière mise à jour : 2026-06-01



