Transférer une boucle de chat d’OpenAI à Claude en Python : une page de modifications
Déplacer un cycle de messages sans état vers l’API Messages d’Anthropic : le paramètre système de niveau supérieur, le nombre maximal de tokens requis, les blocs de contenu et l’alternance stricte des rôles.
Sources: Part 3 — Call the Claude API in Python (same messages, system on the side)
Tout ceux qui ont écrit un cycle de chat pour OpenAI peuvent le transférer vers Claude d’Anthropic en environ une page de modifications. Les deux fournisseurs partagent la même structure de base : une liste messages étiquetée par rôle, envoyée dans son intégralité à chaque appel, à laquelle est ajoutée la réponse par la suite. Un seul élément change, et il est utile de savoir exactement lequel.
Même liste, système en dehors
L’API Messages de Claude accepte le format de conversation que vous connaissez déjà : une liste de dictionnaires contenant un role et un content, sans état sur le serveur, réenvoyée dans son intégralité à chaque fois. Les noms et les significations des rôles sont identiques, user et assistant.
La seule différence structurelle réside dans le fait que system n’est pas un message. OpenAI place les instructions dans la première ligne de la liste. Anthropic les place plutôt dans un paramètre system= de niveau supérieur qui accompagne la liste. La liste ne contient que la véritable conversation, c’est-à-dire les tours alternés du utilisateur et de l’assistant.
import anthropic
client = anthropic.Anthropic() # key from env
resp = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=300,
system="You are a concise Python assistant.",
messages=[
{
"role": "user",
"content": "Why is system a parameter "
"here, not a message?",
},
],
)
print(resp.content[0].text)
Deux petites différences apparaissent avant même que tout cela ne fonctionne :
max_tokensdoit être défini. OpenAI permet d’envoyer une demande sans plafond de sortie. Claude exige cette valeur à chaque appel, ce qui est un peu agaçant mais permet de garder un œil sur les coûts, car une demande ne peut être envoyée qu’après avoir déterminé la durée maximale de la réponse.- Les réponses arrivent sous forme de blocs de contenu. Au lieu d’une seule chaîne de caractères, on obtient une liste, et le texte brut se trouve à
resp.content[0].text. Cela semble superflu aujourd’hui, mais les appels aux outils arriveront plus tard dans cette même structure, donc cela s’avère utile.
Configuration et un seul tour
Installez le SDK avec pip install anthropic, puis exportez la clé depuis la console d’Anthropic (elle commence par sk-ant-) sous le nom ANTHROPIC_API_KEY. Le client la récupérera automatiquement.
En le plaçant à côté de l’appel OpenAI, la différence totale se limite à quatre modifications. La méthode client.chat.completions.create devient client.messages.create, la ligne système passe dans system=, une limite de sortie devient obligatoire, et le texte est lu depuis content[0].text au lieu de choices[0].message.content. Une remarque concernant le SDK : l’package anthropic actuel ne prend pas en charge temperature= avec messages.create, donc les exemples plus anciens qui l’incluent généreront une erreur. La méthode de sélection par échantillonnage par défaut est utilisée, ce qui convient bien pour ces exemples.
Le comptage des tokens se trouve dans resp.usage.input_tokens et resp.usage.output_tokens. Il s’agit du même compteur avec des noms de champs légèrement différents, et il est utile de les enregistrer à chaque fois.
La boucle d’historique sur Claude
Ci est la fonction ask() de la version OpenAI, adaptée. La logique d’ajout puis de réenvoi n’a pas changé. Seul l’appel au milieu a été modifié.
msgs = []
def ask(text: str) -> str:
msgs.append(
{"role": "user", "content": text}
)
resp = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=300,
system="You are a concise assistant.",
messages=msgs, # full history
)
reply = resp.content[0].text
msgs.append(
{"role": "assistant", "content": reply}
)
return reply
print(ask("Define a context window."))
print(ask("Now for a five-year-old."))
print(ask("Which answer was shorter?"))
La troisième question dans une conversation prouve toujours ce mécanisme circulaire, car elle ne fonctionne que si les deux réponses précédentes se trouvent dans msgs et sont renvoyées. Les mêmes principes économiques s’appliquent également : le nombre de tokens d’entrée augmente à chaque tour, ce qui fait que les conversations longues coûtent plus par appel.
Claude est également plus exigeant en ce qui concerne l’ordre des messages : ceux-ci doivent alterner entre l’utilisateur et l’assistant. Deux messages de l’utilisateur consécutifs, ce qui arrive facilement lorsqu’une demande échouée est réessayée, font que Claude rejette l’appel, tandis qu’OpenAI le tolère. Considérez cela comme une règle de vérification gratuite pour tout système gérant l’historique des conversations.
Lancement
Le script exécute un seul tour, puis une conversation de trois tours, en enregistrant les tokens d’entrée et de sortie après chaque appel. Il s’arrête prématurément avec un message explicite si ANTHROPIC_API_KEY est manquant. Il effectue également des requêtes payantes en temps réel, il convient donc de l’éviter dans tout exécution CI.
pip install -r requirements.txt
export ANTHROPIC_API_KEY="sk-ant..."
python examples/part03_claude.py
Pourquoi l’intégration était simple, et quels avantages en retirez-vous
C’était un changement mineur, puisque les deux fournisseurs exposent le même contrat : une extrémité sans état qui prend l’intégralité de la conversation à chaque appel et répond une seule fois. Ce qui diffère, c’est le dialecte, comme l’emplacement du prompt système, l’apparence de l’objet de réponse et les paramètres requis. LangChain abstracte cette couche complexe, et ses classes ChatOpenAI ainsi que ChatAnthropic existent pour gérer les différences de quatre lignes que vous auriez dû saisir manuellement. Comprendre ce que cache cette abstraction fait la différence entre l’utiliser efficacement et en être limité.
Il existe également un avantage pratique. Deux fournisseurs derrière une même structure ask() vous permettent de tester des modèles en mode A/B, de basculer en cas de problème avec une API, ou d’orienter les questions simples vers l’un et les questions complexes vers l’autre, tout cela sans modifier le code de l’application. L’API Gemini de Google suit la même structure de conversation avec un seul rôle renommé : l’assistant est désormais appelé model.