Migrer depuis l'API des agents
Le SDK des agents remplace l'API des agents et des conversations : client.beta.agents et client.beta.conversations du package mistralai. Avec l'API, vous créez un agent sur les serveurs de Mistral, démarrez une conversation, exécutez chaque fonction appelée par le modèle et renvoyez le résultat. Avec le SDK, un agent est un objet Python. Son moteur d'exécution gère la boucle : il appelle le modèle, exécute vos outils et enregistre chaque étape dans une session. Vous exécutez l'agent dans votre processus, puis le déployez avec Studio (en auto-hébergement ou avec une gestion par Mistral).
Installez le SDK avec uv add "mistralai-agents==1.1.0" et définissez MISTRAL_API_KEY : consultez le Guide de démarrage.
Concept par concept
| Vous souhaitez | API des agents | SDK des agents |
|---|---|---|
| Créer un agent | client.beta.agents.create(model=..., instructions=...) | agents.Agent(model=..., instructions=...) : consultez Créer un agent et l'interroger |
| Définir la température ou l'effort de raisonnement | completion_args={"temperature": 0.3} | agents.Agent(temperature=0.3, reasoning_effort=...) |
| Poser une question unique | client.beta.conversations.start(agent_id=..., inputs="...") | await agent.run("...") |
| Poser une question sans créer d'agent | conversations.start(model=..., inputs="...") | agents.Agent(model=...) : un agent dans votre processus ne crée rien sur les serveurs de Mistral |
| Laisser le modèle appeler vos fonctions | Un outil {"type": "function", ...}, puis conversations.append() avec un FunctionResultEntry | @agents.tool sur la fonction : le moteur d'exécution l'appelle. Consultez Appeler vos fonctions |
| Obtenir une réponse que seule votre application détient, comme la localisation de l'utilisateur | Le même outil de type fonction | @agents.client_tool, avec une réponse via on_client_tool= : consultez Répondre depuis votre application |
| Effectuer une recherche sur le Web ou dans une bibliothèque de documents | tools=[{"type": "web_search"}], {"type": "document_library", ...} | Un de vos outils qui appelle l'outil hébergé : consultez Outils hébergés |
| Exécuter du code | tools=[{"type": "code_interpreter"}] | environment=agents.MistralSandbox() : consultez Exécuter du code |
| Utiliser un connecteur | {"type": "connector", "connector_id": "github_app"} | connectors=[agents.connector("github_app")] : consultez Connecteurs |
| Approuver les appels d'outils avant leur exécution | {"type": "web_search", "tool_configuration": {"requires_confirmation": ["web_search"]}}, puis append(tool_confirmations=[...]) | permission_mode="ask" ou permission_policy=, avec une réponse via on_approval= : consultez Approuver les appels d'outils |
| Poursuivre une conversation | conversations.append(conversation_id=..., inputs="...") | session = await agent.start_session(), puis await session.run("...") pour chaque message : consultez Mener une conversation |
| La poursuivre depuis un autre processus | conversations.append() avec le conversation_id enregistré | agent.session(session_id) sur un agent déployé |
| Lister les conversations | conversations.list() | agents.list_sessions() pour un agent déployé, agents.list_sessions(host="local") pour les agents de ce processus |
| Lire l'historique | conversations.get_history(conversation_id=...) | Les événements EntryCompleted de chaque tour : consultez Lire les entrées d'un tour |
| Recevoir la réponse en streaming | conversations.start_stream() et ses événements message.output.delta | on_event= et ses événements agents.TextDelta : consultez Recevoir la réponse en streaming |
| Transférer la conversation à un autre agent | client.beta.agents.update(agent_id=..., handoffs=[...]) | subagents=[agents.Subagent(...)] : consultez Déléguer des tâches à d'autres agents |
| Obtenir une réponse typée | completion_args={"response_format": {"type": "json_schema", ...}} | Un modèle Pydantic pour output_type= : consultez Obtenir une réponse typée |
| Migrer des conversations enregistrées | conversations.get_history() | agent.start_session(history=...) : consultez Migrer une conversation enregistrée |
| Ne rien conserver sur les serveurs de Mistral | store=False | Exécutez l'agent dans votre processus : ses sessions restent dans sa mémoire |
| Fixer une version ou revenir à une version antérieure | client.beta.agents.update_version(agent_id=..., version=...) | Déployez le commit souhaité : consultez Versions |
| Reprendre depuis une entrée antérieure | conversations.restart(conversation_id=..., from_entry_id=...) | start_session(history=) avec les entrées jusqu'à celle-ci : consultez Reprendre depuis une entrée antérieure |
Créer un agent et l'interroger
Avec l'API, vous créez l'agent, puis démarrez une conversation avec son identifiant :
agent = client.beta.agents.create(
model="mistral-large-4",
name="lyon-guide",
instructions="You are a travel guide for Lyon. Answer in one short sentence.",
completion_args={"temperature": 0.3},
)
response = client.beta.conversations.start(agent_id=agent.id, inputs="Name one museum in Lyon.")
print(response.outputs[-1].content) # thinking and text chunks, with mistral-large-4Avec le SDK, l'agent est un objet dans votre code, et run() renvoie la réponse. Enregistrez ce fichier sous le nom guide.py :
import asyncio
from mistralai import agents
guide = agents.Agent(
model="mistral-large-4",
name="lyon-guide",
instructions="You are a travel guide for Lyon. Answer in one short sentence.",
temperature=0.3,
)
async def create() -> None:
result = await guide.run("Name one museum in Lyon.")
print(result.text)
asyncio.run(create())result.text contient le texte de la réponse, sans le raisonnement du modèle. run() renvoie un objet qui indique comment le tour s'est terminé : consultez Un appel : agent.run(). Un agent déployé conserve ce name.
Chaque bloc de code du SDK ci-dessous complète guide.py. Placez-le à la place de la dernière ligne asyncio.run(...) du fichier.
Appeler vos fonctions
Avec l'API, votre code repère l'appel de fonction dans la réponse, exécute la fonction et renvoie son résultat :
response = client.beta.conversations.start(
model="mistral-large-4",
instructions="Use your tools. Answer in one short sentence.",
tools=[weather_tool], # {"type": "function", "function": {"name": "get_weather", ...}}
inputs="What is the weather in Lyon?",
)
call = next(entry for entry in response.outputs if isinstance(entry, FunctionCallEntry))
result = get_weather(**json.loads(call.arguments))
response = client.beta.conversations.append(
conversation_id=response.conversation_id,
inputs=[FunctionResultEntry(tool_call_id=call.tool_call_id, result=result)],
)Avec le SDK, ajoutez le décorateur @agents.tool à la fonction. Le modèle lit son nom, sa docstring et ses paramètres typés, et le moteur d'exécution l'appelle :
@agents.tool
def get_weather(city: str) -> str:
"""The weather in a city today."""
return f"Sunny, 21 °C in {city}."
weather_guide = agents.Agent(
model="mistral-large-4",
instructions="Use your tools. Answer in one short sentence.",
tools=[get_weather],
)
async def tools() -> None:
result = await weather_guide.run("What is the weather in Lyon?")
print(result.text)
asyncio.run(tools())Consultez Outils.
Répondre depuis votre application
Certaines réponses ne sont disponibles que dans l'application qui exécute l'agent, comme la localisation de l'utilisateur. Déclarez une telle fonction comme un outil client. Associez la fonction qui fournit la réponse avec .answer, puis passez cette fonction à run() via on_client_tool=agents.ClientToolAnswers(...) :
@agents.client_tool
def get_city() -> str:
"""The user's current city."""
raise NotImplementedError # the app that runs the agent answers it
@get_city.answer
def read_city() -> str:
return "Lyon" # in a real app: ask the device
located_guide = agents.Agent(
model="mistral-large-4",
instructions="Use your tools. Answer in one short sentence.",
tools=[get_city],
)
async def client_tool() -> None:
result = await located_guide.run("Which city am I in?", on_client_tool=agents.ClientToolAnswers(read_city))
print(result.text)
asyncio.run(client_tool())Consultez Outils clients.
Outils hébergés, code et connecteurs
Outils hébergés
Avec l'API, vous ajoutez un outil hébergé comme web_search à tools :
response = client.beta.conversations.start(
model="mistral-large-4",
instructions="Answer in one short sentence.",
tools=[{"type": "web_search"}],
inputs="On which days is the Musée des Confluences in Lyon closed?",
)Avec le SDK, appelez l'outil hébergé depuis l'un de vos outils via l'API des conversations. Cet outil interroge mistral-large-4 avec web_search et renvoie le texte de la réponse :
import os
from mistralai.client import Mistral
from mistralai.client import models as api_models
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
def text_of(content: str | list[object]) -> str:
"""The text of an Agents API message, without the model's thinking."""
if isinstance(content, str):
return content
return "".join(chunk.text for chunk in content if isinstance(chunk, api_models.TextChunk))
@agents.tool
async def web_search(query: str) -> str:
"""Search the web."""
response = await client.beta.conversations.start_async(
model="mistral-large-4", inputs=query, tools=[{"type": "web_search"}], store=False
)
replies = [entry for entry in response.outputs if isinstance(entry, api_models.MessageOutputEntry)]
return text_of(replies[-1].content)
searcher = agents.Agent(
model="mistral-large-4",
instructions="Use web_search for current facts. Answer in one short sentence.",
tools=[web_search],
)
async def search() -> None:
result = await searcher.run("On which days is the Musée des Confluences in Lyon closed?")
print(result.text)
asyncio.run(search())Pour une bibliothèque de documents, passez tools=[{"type": "document_library", "library_ids": [...]}] dans le même appel. Pour image_generation, renvoyez l'URL de l'image : avec mistral-large-4, elle se trouve dans json.loads(entry.info["result"])["url"], pour l'entrée tool.execution de la réponse.
Exécuter du code
Avec l'API, le modèle exécute du code avec l'outil code_interpreter :
response = client.beta.conversations.start(
model="mistral-large-4",
instructions="Run Python to compute. Answer with the number only.",
tools=[{"type": "code_interpreter"}],
inputs="What is the 30th Fibonacci number?",
)Avec le SDK, l'outil code_interpreter devient l'environnement de l'agent : un conteneur dans lequel le modèle exécute du code Python et des commandes shell. agents.MistralSandbox() est un environnement exécuté par Mistral :
analyst = agents.Agent(
model="mistral-large-4",
instructions="Run Python to compute. Answer with the number only.",
environment=agents.MistralSandbox(),
)
async def code() -> None:
result = await analyst.run("What is the 30th Fibonacci number?")
print(result.text)
asyncio.run(code())La sandbox Mistral nécessite la dépendance optionnelle koyeb, uv add "mistralai-agents[koyeb]==1.1.0", et le rôle cloud_user dans l'espace de travail : consultez Sandbox Mistral.
Connecteurs
Avec l'API, un connecteur est un outil associé à son identifiant :
response = client.beta.conversations.start(
model="mistral-large-4",
instructions="Answer in one short sentence.",
tools=[{"type": "connector", "connector_id": "github_app"}],
inputs="Which GitHub user am I?",
)Avec le SDK, ajoutez un connecteur par son nom. Il agit au nom de l'utilisateur qui exécute la session. Si l'utilisateur ne l'a pas encore connecté, une étape de connexion lui est d'abord proposée :
github_helper = agents.Agent(
model="mistral-large-4",
instructions="Answer in one short sentence.",
connectors=[agents.connector("github_app")],
)
async def connector() -> None:
state = await github_helper.run("Which GitHub user am I?")
match state:
case agents.Completed(text=text):
print(text)
case agents.Blocked(callbacks=callbacks): # the user has not connected GitHub yet
print("waiting on:", [callback.kind for callback in callbacks])
await github_helper.session(state.session_id).cancel()
asyncio.run(connector())Consultez Connecteurs et MCP, qui explique aussi comment ajouter vos propres serveurs MCP.
Approuver les appels d'outils
Avec l'API, requires_confirmation met en attente les appels d'un outil intégré, et append() y répond avec tool_confirmations :
response = client.beta.conversations.start(
model="mistral-large-4",
instructions="Answer in one short sentence.",
tools=[{"type": "web_search", "tool_configuration": {"requires_confirmation": ["web_search"]}}],
inputs="On which days is the Musée des Confluences in Lyon closed?",
)
pending = [entry for entry in response.outputs if isinstance(entry, FunctionCallEntry)]
response = client.beta.conversations.append(
conversation_id=response.conversation_id,
tool_confirmations=[{"tool_call_id": call.tool_call_id, "confirmation": "allow"} for call in pending],
)Avec le SDK, ces mécanismes deviennent un mode d'autorisation et une fonction d'approbation. Avec permission_mode="ask", chaque appel d'outil attend une réponse, que on_approval= fournit :
careful_guide = agents.Agent(
model="mistral-large-4",
instructions="Use your tools. Answer in one short sentence.",
tools=[get_weather],
permission_mode="ask", # every tool call waits for an answer
)
def approve(ctx: agents.ApprovalCallbackContext, request: agents.ApprovalRequest) -> agents.AllowApproval:
print("approve:", request.tool.name, request.input)
return agents.AllowApproval()
async def approvals() -> None:
result = await careful_guide.run("What is the weather in Lyon?", on_approval=approve)
print(result.text)
asyncio.run(approvals())Pour ne demander une approbation que pour certains outils, définissez une permission_policy. Pour laisser une personne décider plus tard, gardez la demande d'approbation en attente et répondez-y par son identifiant : consultez Approbations.
Mener une conversation
Une conversation devient une session. Avec l'API, chaque message est envoyé à append() avec l'identifiant de la conversation :
response = client.beta.conversations.start(agent_id=agent.id, inputs="Name one museum in Lyon.")
response = client.beta.conversations.append(conversation_id=response.conversation_id, inputs="When does it open?")Avec le SDK, démarrez une session et envoyez-lui chaque message :
async def conversation() -> None:
session = await guide.start_session()
first = await session.run("Name one museum in Lyon.")
print(first.text)
second = await session.run("When does it open?")
print(second.text)
await session.cancel()
asyncio.run(conversation())session.cancel() termine la session. L'emplacement de la session dépend de l'agent :
- Un agent créé avec
agents.Agent(...)conserve ses sessions dans votre processus, jusqu'à la fin de la boucle d'événements qui les a démarrées. - Un agent déployé avec Studio, chargé avec
agents.Agent.load_from(agent_name=...), a ses sessions stockées par Mistral. N'importe lequel de vos processus peut en rouvrir une par son identifiant avecagent.session(session_id)et les lister avecagents.list_sessions().
Consultez Sessions.
Recevoir la réponse en streaming
Avec l'API, vous utilisez start_stream() pour le streaming et lisez ses événements :
events = client.beta.conversations.start_stream(model="mistral-large-4", inputs="Name one museum in Lyon.")
for event in events:
if event.event == "message.output.delta":
print(event.data.content, end="")Avec le SDK, passez une fonction via on_event=. Elle reçoit chaque événement du tour pendant que run() attend :
def show(event: agents.Event) -> None:
match event:
case agents.TextDelta(text=text):
print(text, end="", flush=True)
case agents.TurnEnded():
print()
async def stream() -> None:
await guide.run("Name one museum in Lyon.", on_event=show)
asyncio.run(stream())Chaque événement de l'API a un équivalent dans le SDK :
| Événement de l'API des agents | Événement du SDK des agents |
|---|---|
conversation.response.started | agents.TurnStarted |
message.output.delta | agents.TextDelta, le nouveau texte d'un message |
tool.execution.started, tool.execution.done | agents.EntryAdded, puis agents.EntryCompleted, avec un agents.ToolCallEntry, puis un agents.ToolResultEntry |
function.call.delta | agents.EntryCompleted avec le agents.ToolCallEntry complet |
agent.handoff.started, agent.handoff.done | Un agents.ToolCallEntry nommé agents.spawn : l'agent a démarré un sous-agent |
conversation.response.done, conversation.response.error | agents.TurnEnded, dont le state est l'objet décrivant l'issue du tour, comme agents.Completed ou agents.Failed |
Consultez Événements.
Déléguer des tâches à d'autres agents
Les transferts deviennent des sous-agents. Avec l'API, le planificateur transmet la conversation à l'agent chargé des hôtels, qui répond à l'utilisateur :
hotels = client.beta.agents.create(
model="mistral-large-4",
name="hotels",
description="Finds a hotel with free rooms in a city.",
instructions="Recommend the Hôtel des Célestins, rooms from 120 EUR.",
)
planner = client.beta.agents.create(
model="mistral-large-4", name="planner", instructions="You plan trips. Hand hotel requests to the hotels agent."
)
planner = client.beta.agents.update(agent_id=planner.id, handoffs=[hotels.id])
response = client.beta.conversations.start(agent_id=planner.id, inputs="Find me a hotel in Lyon.")Avec le SDK, déclarez l'agent chargé des hôtels dans le planificateur. Un sous-agent renvoie ses résultats à son parent, et le parent répond à l'utilisateur :
@agents.tool
def find_hotel(city: str) -> str:
"""A hotel with free rooms in a city."""
return f"Hôtel des Célestins, {city}: rooms from 120 EUR."
planner = agents.Agent(
model="mistral-large-4",
instructions="You plan trips. Ask the hotels agent for a hotel, then answer in one short sentence.",
subagents=[
agents.Subagent(
name="hotels",
description="Finds a hotel with free rooms in a city.",
instructions="Call find_hotel once, then answer with the hotel and its price.",
tools=[find_hotel],
)
],
)
async def subagents() -> None:
result = await planner.run("Find me a hotel in Lyon.")
print(result.text)
asyncio.run(subagents())Le parent peut démarrer plusieurs sous-agents à la fois et envoyer des messages de suivi à chacun. Pour laisser un autre agent reprendre la conversation, comme le permettait un transfert, créez un agents.Agent distinct pour celui-ci. Envoyez les prochains messages de l'utilisateur à une session de cet agent, démarrée avec start_session(history=...) pour reprendre l'historique de la conversation : consultez Migrer une conversation enregistrée. Consultez Sous-agents.
Obtenir une réponse typée
Avec l'API, vous passez un schéma JSON via response_format et analysez la réponse :
response = client.beta.conversations.start(
model="mistral-medium-latest",
instructions="You are a travel guide for Lyon.",
inputs="Name one museum in Lyon.",
completion_args={
"response_format": {
"type": "json_schema",
"json_schema": {"name": "museum", "schema": Museum.model_json_schema(), "strict": True},
}
},
)
museum = Museum.model_validate_json(response.outputs[-1].content)Avec le SDK, passez le modèle Pydantic via output_type=. Le champ output d'un objet Completed contient l'objet issu de l'analyse :
from pydantic import BaseModel
class Museum(BaseModel):
name: str
district: str
typed_guide = agents.Agent(model="mistral-medium-latest", instructions="You are a travel guide for Lyon.")
async def typed() -> None:
state = await typed_guide.run("Name one museum in Lyon.", output_type=Museum)
match state:
case agents.Completed(output=Museum() as museum):
print(museum.name, "-", museum.district)
case _:
print(f"no museum: {state!r}")
asyncio.run(typed())Consultez Sortie structurée.
Migrer une conversation enregistrée
Pour poursuivre une conversation stockée par l'API, lisez ses entrées avec get_history() et passez-les à start_session(history=). Cette fonction convertit les entrées au format de l'historique du SDK. Elle conserve les messages, les appels de fonctions et leurs résultats. Elle exclut les entrées des outils hébergés et des transferts ; le modèle peut toujours lire les réponses qui les ont utilisés :
import json
def to_history(entries: list[api_models.Entry]) -> list[dict[str, object]]:
"""Turn the entries of conversations.get_history() into start_session(history=) entries."""
history: list[dict[str, object]] = []
for entry in entries:
match entry:
case api_models.MessageInputEntry() | api_models.MessageOutputEntry():
text = text_of(entry.content)
history.append({"type": "message", "role": entry.role, "content": [{"type": "text", "text": text}]})
case api_models.FunctionCallEntry():
arguments = json.loads(entry.arguments) if isinstance(entry.arguments, str) else entry.arguments
history.append(
{
"type": "tool_call",
"tool_call_id": entry.tool_call_id,
"name": entry.name,
"arguments": arguments,
}
)
case api_models.FunctionResultEntry():
history.append({"type": "tool_result", "tool_call_id": entry.tool_call_id, "result": entry.result})
return history
async def import_conversation(conversation_id: str) -> None:
entries = client.beta.conversations.get_history(conversation_id=conversation_id).entries
session = await guide.start_session(history=to_history(entries))
state = await session.run("Which museum did you name?")
print(state.text)
await session.cancel()Pour une conversation qui a mentionné le Musée des Beaux-Arts, import_conversation(conversation_id) affiche :
text_of provient de Outils hébergés. history= fonctionne pour un agent qui s'exécute dans votre processus. Consultez Entrées de l'historique.
Versions et reprises
Un agent de l'API conserve ses versions, et update_version() sélectionne celle que les conversations utilisent. Un agent du SDK déployé correspond à votre code à un commit donné : chaque déploiement d'un nouveau commit sous le même nom remplace l'agent, et ses sessions ouvertes exécutent leur prochain message avec le nouveau code. Pour revenir à une version antérieure, déployez le commit précédent. Consultez Mettre à jour un agent déployé.
Reprendre depuis une entrée antérieure
conversations.restart(conversation_id=..., from_entry_id=..., inputs=...) démarre une nouvelle conversation qui conserve les entrées jusqu'à l'entrée indiquée, puis envoie inputs. Avec le SDK, démarrez une session avec ces entrées, en utilisant to_history de Migrer une conversation enregistrée, puis envoyez le message :
async def restart_from(conversation_id: str, entry_id: str, inputs: str) -> None:
entries = client.beta.conversations.get_history(conversation_id=conversation_id).entries
kept = entries[: [entry.id for entry in entries].index(entry_id) + 1]
session = await guide.start_session(history=to_history(kept))
state = await session.run(inputs)
print(state.text)
await session.cancel()Pour une conversation qui a mentionné un musée, puis un parc, une reprise depuis la réponse sur le musée avec "Name one café near it." affiche :
Et après
- Guide de démarrage : créez un agent étape par étape.
- Déployer votre agent : déployez-le avec Studio (en auto-hébergement ou avec une gestion par Mistral) et appelez-le depuis votre application.
- Ce qu'est un agent : tous les éléments qu'un agent peut contenir.