
Un guide pratique sur les nouvelles capacités de sortie structurée d’Anthropic
a annoncé des sorties structurées pour ses meilleurs modèles dans son API, une nouvelle fonctionnalité conçue pour garantir que les sorties générées par le modèle correspondent exactement aux schémas JSON fournis par les développeurs.
Cela résout un problème auquel de nombreux développeurs sont confrontés lorsqu’un système ou un processus consomme la sortie d’un LLM pour un traitement ultérieur. Il est essentiel que ce système « sache » à quoi s’attendre en tant qu’entrée afin de pouvoir la traiter en conséquence.
De même, lorsque vous affichez la sortie du modèle à un utilisateur, vous souhaitez qu’elle soit dans le même format à chaque fois.
Jusqu’à présent, il a été difficile de garantir des formats de sortie cohérents à partir des modèles Anthropic. Cependant, il semble qu’Anthropic ait de toute façon résolu ce problème pour ses modèles haut de gamme. D’après leur annonce (liée à la fin de l’article), ils disent :
La plateforme de développement Claude prend désormais en charge les sorties structurées pour Claude Sonnet 4.5 et Opus 4.1. Disponible en version bêta publique, cette fonctionnalité garantit que les réponses de l’API correspondent toujours à vos schémas JSON ou définitions d’outils spécifiés.
Maintenant, une chose à retenir avant d’examiner un exemple de code, c’est qu’Anthropic garantit que la sortie du modèle respectera un format spécifié, pas que toute sortie sera précise à 100 %. Les modèles peuvent et peuvent encore halluciner de temps en temps.
Vous pourriez donc obtenir des réponses incorrectes parfaitement formatées !
Configuration de notre environnement de développement
Avant d’examiner un exemple de code Python, il est recommandé de créer un environnement de développement distinct dans lequel vous pouvez installer tous les logiciels nécessaires et expérimenter le codage. Désormais, tout ce que vous ferez dans cet environnement sera cloisonné et n’aura aucun impact sur vos autres projets.
J’utiliserai Miniconda pour cela, mais vous pouvez utiliser la méthode que vous connaissez le mieux.
Si vous souhaitez suivre la voie Miniconda et que vous ne l’avez pas déjà, vous devez d’abord l’installer. Obtenez-le en utilisant ce lien :
https://docs.anaconda.com/miniconda/
Pour suivre mes exemples, vous aurez également besoin d’une clé API Anthropic et d’un crédit sur votre compte. Pour référence, j’ai utilisé 12 cents pour exécuter le code de cet article. Si vous possédez déjà un compte Anthropic, vous pouvez obtenir une clé API en utilisant la console Anthropic à l’adresse https://console.anthropic.com/settings/keys.
1/ Créez notre nouvel environnement de développement et installez les bibliothèques requises
ceci sur WSL2 Ubuntu pour Windows.
(base) $ conda create -n anth_test python=3.13 -y
(base) $ conda activate anth_test
(anth_test) $ pip install anthropic beautifulsoup4 requests
(anth_test) $ pip install httpx jupyter
2/ Démarrez Jupyter
Tapez maintenant « jupyter notebook » dans votre invite de commande. Vous devriez voir un notebook Jupyter ouvert dans votre navigateur. Si cela ne se produit pas automatiquement, vous verrez probablement un écran d’informations après la commande. En bas, vous trouverez une URL à copier et coller dans votre navigateur. Cela ressemblera à ceci :
http://127.0.0.1:8888/tree?token=3b9f7bd07b6966b41b68e2350721b2d0b6f388d248cc69
Exemples de codes
Dans nos deux exemples de codage, nous utiliserons le nouveau paramètre output_format disponible dans l’API bêta. Lors de la spécification de la sortie structurée, nous pouvons utiliser deux styles différents.
1. Schéma JSON brut.
Comme son nom l’indique, la structure est définie par un bloc de schéma JSON passé directement à la définition du format de sortie.
2. Une classe modèle Pydantic.
Il s’agit d’une classe Python standard utilisant le BaseModel de Pydantic qui spécifie les données que nous souhaitons que le modèle génère. C’est une manière beaucoup plus compacte de définir une structure qu’un schéma JSON.
Exemple de code 1 — Résumé du texte
Ceci est utile si vous souhaitez résumer un certain nombre de textes différents, mais que vous souhaitez que les résumés aient la même structure. Dans cet exemple, nous traiterons les entrées Wikipédia de certains scientifiques célèbres et récupérerons des informations clés spécifiques à leur sujet de manière très organisée.
Dans notre résumé, nous souhaitons générer la structure suivante pour chaque scientifique,
- Le nom du scientifique
- Quand et où ils sont nés
- Leur principal titre de gloire
- L’année où ils ont remporté le prix Nobel
- Quand et où ils sont morts
Remarque : La plupart des textes de Wikipédia, à l’exclusion des citations, ont été publiés sous la licence internationale Creative Commons Attribution-Sharealike 4.0 (CC-BY-SA) et la licence de documentation libre GNU (GFDL). En bref, cela signifie que vous êtes libre :
à partager — copier et redistribuer le matériel sur n’importe quel support ou format
s’adapter — remixer, transformer et construire à partir du matériau
à quelque fin que ce soit, même commercialement.
Divisons le code en sections gérables, chacune avec une explication.
Tout d’abord, nous importons les bibliothèques tierces requises et configurons les connexions à Anthropic à l’aide de notre clé API.
import anthropic
import httpx
import requests
import json
import os
from bs4 import BeautifulSoup
http_client = httpx.Client()
api_key = 'YOUR_API_KEY'
client = anthropic.Anthropic(
api_key=api_key,
http_client=http_client
)
C’est la fonction qui va gratter Wikipédia pour nous.
def get_article_content(url):
try:
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'}
response = requests.get(url, headers=headers)
soup = BeautifulSoup(response.content, "html.parser")
article = soup.find("div", class_="mw-body-content")
if article:
content = "\n".join(p.text for p in article.find_all("p"))
return content[:15000]
else:
return ""
except Exception as e:
print(f"Error scraping {url}: {e}")
return ""
Ensuite, nous définissons notre schéma JSON, qui spécifie le format exact de la sortie du modèle.
summary_schema = {
"type": "object",
"properties": {
"name": {"type": "string", "description": "The name of the Scientist"},
"born": {"type": "string", "description": "When and where the scientist was born"},
"fame": {"type": "string", "description": "A summary of what their main claim to fame is"},
"prize": {"type": "integer", "description": "The year they won the Nobel Prize. 0 if none."},
"death": {"type": "string", "description": "When and where they died. 'Still alive' if living."}
},
"required": ["name", "born", "fame", "prize", "death"],
"additionalProperties": False
}
Cette fonction sert d’interface entre notre script Python et l’API Anthropic. Son objectif principal est de prendre un texte non structuré (un article) et de forcer l’IA à renvoyer un objet de données structurées (JSON) contenant des champs spécifiques, tels que le nom du scientifique, sa date de naissance et les détails du prix Nobel.
La fonction appelle client.messages.create pour envoyer une requête au modèle. Il fixe la température à 0,2, ce qui réduit la créativité du modèle pour garantir que les données extraites sont factuelles et précises. Le paramètre extra_headers active une fonctionnalité bêta spécifique qui n’est pas encore standard. En passant l’en-tête anthropic-beta avec la valeur structured-outputs-2025-11-13, le code indique à l’API d’activer la logique de sorties structurées pour cette requête spécifique, l’obligeant à produire un JSON valide qui correspond à votre structure définie.
Étant donné que le paramètre output_format est utilisé, le modèle renvoie une chaîne brute dont la validité JSON est garantie. La ligne json.loads(response.content[0].text) analyse cette chaîne dans un dictionnaire Python natif, rendant les données immédiatement prêtes pour une utilisation programmatique.
def get_article_summary(text: str):
if not text: return None
try:
response = client.messages.create(
model="claude-sonnet-4-5", # Use the latest available model
max_tokens=1024,
temperature=0.2,
messages=[
{"role": "user", "content": f"Summarize this article:\n\n{text}"}
],
# Enable the beta feature
extra_headers={
"anthropic-beta": "structured-outputs-2025-11-13"
},
# Pass the new parameter here
extra_body={
"output_format": {
"type": "json_schema",
"schema": summary_schema
}
}
)
# The API returns the JSON directly in the text content
return json.loads(response.content[0].text)
except anthropic.BadRequestError as e:
print(f"API Error: {e}")
return None
except Exception as e:
print(f"Error: {e}")
return None
C’est ici que nous rassemblons tout. Les différentes URL que nous souhaitons gratter sont définies. Leur contenu est transmis au modèle pour traitement, avant que les résultats finaux ne soient affichés.
urls = [
"https://en.wikipedia.org/wiki/Albert_Einstein",
"https://en.wikipedia.org/wiki/Richard_Feynman",
"https://en.wikipedia.org/wiki/James_Clerk_Maxwell",
"https://en.wikipedia.org/wiki/Alan_Guth"
]
print("Scraping and analyzing articles...")
for i, url in enumerate(urls):
print(f"\n--- Processing Article {i+1} ---")
content = get_article_content(url)
if content:
summary = get_article_summary(content)
if summary:
print(f"Scientist: {summary.get('name')}")
print(f"Born: {summary.get('born')}")
print(f"Fame: {summary.get('fame')}")
print(f"Nobel: {summary.get('prize')}")
print(f"Died: {summary.get('death')}")
else:
print("Failed to generate summary.")
else:
print("Skipping (No content)")
print("\nDone.")
Lorsque j’ai exécuté le code ci-dessus, j’ai obtenu ce résultat.
Scraping and analyzing articles...
--- Processing Article 1 ---
Scientist: Albert Einstein
Born: 14 March 1879 in Ulm, Kingdom of Württemberg, German Empire
Fame: Developing the theory of relativity and the mass-energy equivalence formula E = mc2, plus contributions to quantum theory including the photoelectric effect
Nobel: 1921
Died: 18 April 1955
--- Processing Article 2 ---
Scientist: Richard Phillips Feynman
Born: May 11, 1918, in New York City
Fame: Path integral formulation of quantum mechanics, quantum electrodynamics, Feynman diagrams, and contributions to particle physics including the parton model
Nobel: 1965
Died: February 15, 1988
--- Processing Article 3 ---
Scientist: James Clerk Maxwell
Born: 13 June 1831 in Edinburgh, Scotland
Fame: Developed the classical theory of electromagnetic radiation, unifying electricity, magnetism, and light through Maxwell's equations. Also key contributions to statistical mechanics, color theory, and numerous other fields of physics and mathematics.
Nobel: 0
Died: 5 November 1879
--- Processing Article 4 ---
Scientist: Alan Harvey Guth
Born: February 27, 1947 in New Brunswick, New Jersey
Fame: Pioneering the theory of cosmic inflation, which proposes that the early universe underwent a phase of exponential expansion driven by positive vacuum energy density
Nobel: 0
Died: Still alive
Done.
Pas trop mal ! Alan Guth sera ravi d’être encore en vie, mais hélas, il n’a pas encore remporté de prix Nobel. Notez également que James Clerk Maxwell était décédé avant que le prix Nobel ne soit décerné.
Exemple de code 2 — Agent automatisé de sécurité et de refactorisation du code.
Voici un cas d’utilisation complètement différent et un exemple très pratique pour le génie logiciel. Habituellement, lorsque vous demandez à un LLM de « corriger le code », il vous donne une réponse conversationnelle mélangée à des blocs de code. Cela rend difficile l’intégration dans un pipeline CI/CD ou un plugin IDE.
En utilisant les sorties structurées, nous pouvons forcer le modèle à renvoyer le code propreun liste des bugs spécifiques trouvéset un évaluation des risques de sécurité dans un seul objet JSON lisible par machine.
Le scénario
Nous alimenterons le modèle avec une fonction Python contenant une dangereuse vulnérabilité d’injection SQL et de mauvaises pratiques de codage. Le modèle doit identifier les failles spécifiques et réécrire le code de manière sécurisée.
import anthropic
import httpx
import os
import json
from pydantic import BaseModel, Field, ConfigDict
from typing import List, Literal
# --- SETUP ---
http_client = httpx.Client()
api_key = 'YOUR_API_KEY'
client = anthropic.Anthropic(api_key=api_key, http_client=http_client)
# Intentionally bad code
bad_code_snippet = """
import sqlite3
def get_user(u):
conn = sqlite3.connect('app.db')
c = conn.cursor()
# DANGER: Direct string concatenation
query = "SELECT * FROM users WHERE username = '" + u + "'"
c.execute(query)
return c.fetchall()
"""
# --- DEFINE SCHEMA WITH STRICT CONFIG ---
# We add model_config = ConfigDict(extra="forbid") to ensure
# "additionalProperties": false is generated in the schema.
class BugReport(BaseModel):
model_config = ConfigDict(extra="forbid")
severity: Literal["Low", "Medium", "High", "Critical"]
line_number_approx: int = Field(description="The approximate line number where the issue exists.")
issue_type: str = Field(description="e.g., 'Security', 'Performance', 'Style'")
description: str = Field(description="Short explanation of the bug.")
class CodeReviewResult(BaseModel):
model_config = ConfigDict(extra="forbid")
is_safe_to_run: bool = Field(description="True only if no Critical/High security risks exist.")
detected_bugs: List[BugReport]
refactored_code: str = Field(description="The complete, fixed Python code string.")
explanation: str = Field(description="A brief summary of changes made.")
# --- API CALL ---
try:
print("Analyzing code for security vulnerabilities...\n")
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=2048,
temperature=0.0,
messages=[
{
"role": "user",
"content": f"Review and refactor this Python code:\n\n{bad_code_snippet}"
}
],
extra_headers={
"anthropic-beta": "structured-outputs-2025-11-13"
},
extra_body={
"output_format": {
"type": "json_schema",
"schema": CodeReviewResult.model_json_schema()
}
}
)
# Parse Result
result = json.loads(response.content[0].text)
# --- DISPLAY OUTPUT ---
print(f"Safe to Run: {result['is_safe_to_run']}")
print("-" * 40)
print("BUGS DETECTED:")
for bug in result['detected_bugs']:
# Color code the severity (Red for Critical)
prefix = "🔴" if bug['severity'] in ["Critical", "High"] else "🟡"
print(f"{prefix} [{bug['severity']}] Line {bug['line_number_approx']}: {bug['description']}")
print("-" * 40)
print("REFACTORED CODE:")
print(result['refactored_code'])
except anthropic.BadRequestError as e:
print(f"API Schema Error: {e}")
except Exception as e:
print(f"Error: {e}")
Ce code agit comme un auditeur de sécurité automatisé. Au lieu de demander à l’IA de « discuter » du code, cela l’oblige à remplir un formulaire numérique strict contenant des détails spécifiques sur les bogues et les risques de sécurité.
Voici comment cela fonctionne en trois étapes simples.
- Premièrement, le code définit exactement à quoi doit ressembler la réponse en utilisant les classes Python en conjonction avec Pydantic. Il indique à l’IA : « Donnez-moi un objet JSON contenant une liste de bogues, un indice de gravité (comme « Critique » ou « Faible ») pour chacun et la chaîne de code corrigée. »
- Lors de l’envoi du code vulnérable à l’API, celui-ci transmet le plan Pydantic à l’aide du paramètre output_format. Cela contraint strictement le modèle, l’empêchant d’halluciner ou d’ajouter du remplissage conversationnel. Il doit renvoie des données valides correspondant à votre plan.
- Le script reçoit la réponse de l’IA, qui est garantie comme étant un JSON lisible par machine. Il analyse ensuite automatiquement ces données pour afficher un rapport propre, signalant par exemple l’injection SQL comme un problème « critique » et imprimant la version sécurisée et refactorisée du code.
Voici le résultat que j’ai reçu après avoir exécuté le code.
Analyzing code for security vulnerabilities...
Safe to Run: False
----------------------------------------
BUGS DETECTED:
🔴 [Critical] Line 7: SQL injection vulnerability due to direct string concatenation in query construction. Attacker can inject malicious SQL code through the username parameter.
🟡 [Medium] Line 4: Database connection and cursor are not properly closed, leading to potential resource leaks.
🟡 [Low] Line 1: Function parameter name 'u' is not descriptive. Should use meaningful variable names.
----------------------------------------
REFACTORED CODE:
import sqlite3
from contextlib import closing
def get_user(username):
"""
Retrieve user information from the database by username.
Args:
username (str): The username to search for
Returns:
list: List of tuples containing user data, or empty list if not found
"""
with sqlite3.connect('app.db') as conn:
with closing(conn.cursor()) as cursor:
# Use parameterized query to prevent SQL injection
query = "SELECT * FROM users WHERE username = ?"
cursor.execute(query, (username,))
return cursor.fetchall()
Pourquoi est-ce puissant ?
Prêt pour l’intégration. Vous pouvez exécuter ce script dans une action GitHub. Si is_safe_to_run est False, vous pouvez bloquer automatiquement une Pull Request.
Séparation des préoccupations. Vous obtenez les métadonnées (bogues, gravité) séparées du contenu (le code). Vous n’avez pas besoin d’utiliser Regex pour supprimer le texte « Voici votre code fixe » de la réponse.
Typage strict. Le champ de gravité est limité à des valeurs Enum spécifiques (Critique, Élevé, etc.), garantissant que votre logique en aval ne s’interrompt pas lorsque le modèle renvoie « Sévère » au lieu de « Critique » par exemple.
Résumé
La sortie d’Anthropic des sorties structurées natives change la donne pour les développeurs qui ont besoin de fiabilité, pas seulement de conversation. En appliquant des schémas JSON stricts, nous pouvons désormais traiter les grands modèles de langage moins comme des chatbots que comme des composants logiciels déterministes.
Dans cet article, j’ai montré comment utiliser cette nouvelle fonctionnalité bêta pour rationaliser l’extraction et la sortie de données, et créer des flux de travail automatisés qui s’intègrent parfaitement au code Python. Si vous êtes un utilisateur de l’API d’Anthropic, l’époque où il fallait écrire des Regex fragiles pour analyser les réponses de l’IA est enfin révolue.
Pour plus d’informations sur cette nouvelle fonctionnalité bêta, cliquez sur le lien ci-dessous pour visiter la page de documentation officielle d’Anthropics.
https://platform.claude.com/docs/en/build-with-claude/structured-outputs



