Raisonnement

Le raisonnement constitue l’étape suivante du CoT (Chain of Thought), utilisée pour décrire les étapes logiques générées par le modèle avant d’aboutir à une conclusion. Le raisonnement renforce cette capacité en faisant intervenir des étapes d’apprentissage qui incitent le modèle à développer librement ses chaînes de pensée avant de produire la réponse finale. Cela permet aux modèles d’approfondir l’analyse du problème et d’atteindre une solution plus pertinente, en utilisant du temps de calcul supplémentaire pour générer plus de tokens et ainsi améliorer la réponse : on parle également de Test Time Computation.

Raisonnement LLM IA

Les modèles brillent sur des cas d’usage complexes comme les mathématiques ou le code, mais peuvent aborder des problématiques très variées. Le résultat fourni par un modèle de raisonnement inclut une séquence explicitant la démarche suivie, avant de livrer la réponse finale.

Mistral propose deux approches du raisonnement :

  • Ajustable — Disponible sur mistral-small-latest et mistral-medium-3-5 via le paramètre reasoning_effort. Permet au modèle de doser la profondeur de réflexion.

  • Natif — Disponible sur magistral-small-latest et magistral-medium-latest. Ces modèles génèrent toujours une trace de raisonnement et sont conçus pour traiter des situations nécessitant un travail d’analyse poussé.

Modèle

  • mistral-small-latest : Prend en charge le raisonnement ajustable via le paramètre reasoning_effort. Aucune configuration supplémentaire requise : il suffit d’ajouter le paramètre à n’importe quelle requête de Chat completion.
  • mistral-medium-3-5 : Prend en charge le raisonnement ajustable via le paramètre reasoning_effort. Pour les cas d’usage agentique et de code, il est recommandé d’utiliser reasoning_effort="high".

Le paramètre reasoning_effort contrôle la façon dont le raisonnement apparaît dans la réponse :

  • reasoning_effort = "high" : La réponse inclut un bloc de réflexion complet avant la réponse finale, au prix d’une consommation de tokens plus importante.
  • reasoning_effort = "none" : Le modèle raisonne au minimum et le bloc de réflexion est omis dans la réponse.
Note

reasoning_effort est également proposé sur les endpoints Agents et Conversations de l’API, à l’intérieur du champ completion_args.

Utilisation

Utilisation

Raisonnement avec Chat completion

Raisonnement avec Chat completion

Voici un exemple via notre endpoint chat completions :

import os
from mistralai.client import Mistral

api_key = os.environ["MISTRAL_API_KEY"]
model = "mistral-small-latest"

client = Mistral(api_key=api_key)

chat_response = client.chat.complete(
    model = model,
    messages = [
        {
            "role": "user",
            "content": "John is one of 4 children. The first sister is 4 years old. Next year, the second sister will be twice as old as the first sister. The third sister is two years older than the second sister. The third sister is half the age of her older brother. How old is John?",
        },
    ],
    reasoning_effort="high"
)

Gestion des blocs de réflexion

Gestion des blocs de réflexion

Lorsque reasoning_effort est réglé sur "high", la réponse message.content est une liste de blocs et non plus une chaîne simple. Deux types de blocs apparaissent :

  • ThinkChunk (type: "thinking") : contient la trace du raisonnement du modèle. Le champ thinking est lui-même une liste d’objets TextChunk.
  • TextChunk (type: "text") : contient la réponse finale.

Quand reasoning_effort est à "none", message.content est une simple chaîne de caractères, sans trace de réflexion.

Analyse de la réponse

Analyse de la réponse

from mistralai.client.models import TextChunk, ThinkChunk

# En supposant que chat_response provient de l’exemple ci-dessus
content = chat_response.choices[0].message.content

# reasoning_effort="none" retourne une chaîne de caractères simple
if isinstance(content, str):
    print(content)
else:
    for chunk in content:
        if isinstance(chunk, ThinkChunk):
            print("--- thinking ---")
            for inner in chunk.thinking:
                if isinstance(inner, TextChunk):
                    print(inner.text)
            print("--- /thinking ---")
        elif isinstance(chunk, TextChunk):
            print(chunk.text)

Flux de données (streaming)

Flux de données (streaming)

En streaming, la forme de delta.content évolue au fil de la réponse :

  1. Phase de réflexion : delta.content est une liste contenant un ThinkChunk
  2. Transition : une liste unique contenant à la fois un ThinkChunk de clôture et le premier TextChunk
  3. Phase de réponse : delta.content devient une chaîne de caractères simple
import os
from mistralai.client import Mistral
from mistralai.client.models import TextChunk, ThinkChunk

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

in_thinking = False

for event in client.chat.stream(
    model="mistral-medium-3-5",
    messages=[{"role": "user", "content": "What is 17 * 23?"}],
    reasoning_effort="high",
):
    delta = event.data.choices[0].delta.content
    if not delta:
        continue

    # Après réflexion, delta est une chaîne simple
    if isinstance(delta, str):
        if in_thinking:
            print("\n--- /thinking ---")
            in_thinking = False
        print(delta, end="", flush=True)
        continue

    # Pendant la réflexion, delta est une liste de blocs
    for chunk in delta:
        if isinstance(chunk, ThinkChunk):
            if not in_thinking:
                print("--- thinking ---")
                in_thinking = True
            for inner in chunk.thinking:
                if isinstance(inner, TextChunk):
                    print(inner.text, end="", flush=True)
        elif isinstance(chunk, TextChunk):
            if in_thinking:
                print("\n--- /thinking ---")
                in_thinking = False
            print(chunk.text, end="", flush=True)

Conversations multi-tours

Conversations multi-tours

Quand vous construisez des conversations multi-tours avec raisonnement, veillez à toujours réinjecter le message complet de l’assistant (y compris le ThinkChunk) dans l’historique des messages. Supprimer la trace du raisonnement entre les tours dégrade la performance du modèle.

from mistralai.client import Mistral
from mistralai.client.models import TextChunk, ThinkChunk, UserMessage

client = Mistral(api_key="your-api-key", timeout_ms=300_000)

messages = []

for user_text in ["What is 17 * 23?", "Now multiply that by 3."]:
    messages.append(UserMessage(content=user_text))

    response = client.chat.complete(
        model="mistral-medium-3-5",
        messages=messages,
        reasoning_effort="high",
    )

    assistant_message = response.choices[0].message

    # Affiche uniquement la réponse finale (pour affichage)
    content = assistant_message.content
    if isinstance(content, str):
        print(content)
    else:
        answer = "".join(
            c.text for c in (content or []) if isinstance(c, TextChunk)
        )
        print(answer)

    # IMPORTANT : ajouter le message complet de l’assistant à l’historique.
    # Cela préserve ThinkChunk pour que le modèle voie sa propre trace de raisonnement lors des tours suivants.
    # Ne reconstruisez PAS le message uniquement avec le texte de la réponse finale.
    messages.append(assistant_message)
Avertissement

Ne retirez pas le ThinkChunk des messages assistant avant de les réinjecter. Le modèle s’appuie sur la trace du raisonnement pour maintenir la cohérence entre les tours. Retirer ces blocs améliore l’efficacité en tokens mais dégrade fortement la qualité de la sortie.