> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coreweave.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Sortie structurée multi-agent

> Découvrez comment utiliser la sortie structurée multi-agent avec W&B Weave

<Note>
  Ceci est un notebook interactif. Vous pouvez l’exécuter localement ou utiliser les liens suivants :

  * [Ouvrir dans Google Colab](https://colab.research.google.com/github/wandb/docs/blob/main/weave/cookbooks/source/multi-agent-structured-output.ipynb)
  * [Voir la source sur GitHub](https://github.com/wandb/docs/blob/main/weave/cookbooks/source/multi-agent-structured-output.ipynb)
</Note>

Ce tutoriel montre comment créer un système multi-agent qui s’appuie sur la fonctionnalité de sorties structurées d’OpenAI, et comment tracer les interactions entre agents avec [Weave](/fr/products/wandb/weave). À la fin, vous disposerez d’un pipeline d’analyse de données à quatre agents dont les entrées et sorties intermédiaires sont visibles dans l’interface de Weights & Biases.

OpenAI a lancé les [sorties structurées](https://openai.com/index/introducing-structured-outputs-in-the-api/) pour que le modèle génère toujours des réponses conformes au schéma JSON que vous fournissez, sans avoir à formuler des prompts insistants. Avec les sorties structurées, plus besoin de valider les réponses mal formatées ni de relancer leur génération.

Le paramètre `strict: true` vous permet de garantir que la réponse respecte le schéma fourni.

Dans un système multi-agent, les sorties structurées garantissent que les agents échangent des données cohérentes, traitées de manière prévisible. Elles prennent également en charge les refus explicites et évitent d’avoir à relancer les requêtes ou à valider les réponses.

<Tip>
  **Source** : ce cookbook est basé sur [l’exemple de code d’OpenAI consacré aux sorties structurées](https://cookbook.openai.com/examples/structured_outputs_multi_agent), avec quelques modifications pour améliorer la visualisation dans Weave.
</Tip>

<h2 id="install-the-dependencies">
  Installer les dépendances
</h2>

Commencez par installer les bibliothèques nécessaires à ce tutoriel. Il utilise les bibliothèques suivantes :

* [OpenAI](https://openai.com/index/openai-api/) pour créer le système multi-agent.
* [Weave](/fr/products/wandb/weave) pour suivre le flux de travail du LLM et évaluer les stratégies de prompting.

```python lines theme={"system"}
!pip install -qU openai weave wandb
python
%%capture
# Contournement temporaire d’un bug dans openai :
# TypeError: Client.__init__() got an unexpected keyword argument 'proxies'
# Voir https://community.openai.com/t/error-with-openai-1-56-0-client-init-got-an-unexpected-keyword-argument-proxies/1040332/15
!pip install "httpx<0.28"
```

Ensuite, configurez les identifiants et initialisez Weave pour que les traces soient journalisées dans votre projet Weights & Biases.

Définissez `WANDB_API_KEY` dans votre environnement pour pouvoir vous connecter avec `wandb.login()`. Fournissez cette valeur à Colab sous forme de secret.

Indiquez dans `name_of_wandb_project` le projet Weights & Biases dans lequel journaliser les données.

<Note>
  `name_of_wandb_project` peut également prendre la forme `[YOUR-TEAM]/[YOUR-PROJECT]` pour indiquer l’équipe dans laquelle journaliser les traces.
</Note>

Obtenez ensuite un client Weave en appelant `weave.init()`.

Comme ce tutoriel utilise l’[API OpenAI](https://openai.com/index/openai-api/), vous avez également besoin d’une clé API OpenAI. [Inscrivez-vous](https://platform.openai.com/signup) sur la plateforme OpenAI pour obtenir votre propre clé API. Fournissez-la également à Colab sous forme de secret.

```python lines theme={"system"}
import base64
import json
import os
from io import BytesIO, StringIO

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import wandb
from google.colab import userdata
from openai import OpenAI

import weave
python
os.environ["WANDB_API_KEY"] = userdata.get("WANDB_API_KEY")
os.environ["OPENAI_API_KEY"] = userdata.get("OPENAI_API_KEY")

wandb.login()
name_of_wandb_project = "multi-agent-structured-output"
weave.init(name_of_wandb_project)

client = OpenAI()
MODEL = "gpt-4o-2024-08-06"
```

<h2 id="set-up-the-agents">
  Configurer les agents
</h2>

Une fois Weave initialisé, définissez les agents qui composent le système. Le cas d’usage de ce tutoriel est une tâche d’analyse de données.
Tout d’abord, configurez le système à quatre agents :

* Agent de triage : détermine quels agents appeler.
* Agent de prétraitement des données : prépare les données pour l’analyse, par exemple en les nettoyant.
* Agent d’analyse des données : analyse les données.
* Agent de visualisation des données : visualise les résultats de l’analyse afin d’en tirer des enseignements.

Commencez par définir les prompts système de chacun de ces agents. Ces prompts définissent le rôle de chaque agent ainsi que les outils qu'il est autorisé à appeler.

```python lines theme={"system"}
triaging_system_prompt = """You are a Triaging Agent. Your role is to assess the user's query and route it to the relevant agents. The agents available are:
- Data Processing Agent: Cleans, transforms, and aggregates data.
- Analysis Agent: Performs statistical, correlation, and regression analysis.
- Visualization Agent: Creates bar charts, line charts, and pie charts.

Use the send_query_to_agents tool to forward the user's query to the relevant agents. Also, use the speak_to_user tool to get more information from the user if needed."""

processing_system_prompt = """You are a Data Processing Agent. Your role is to clean, transform, and aggregate data using the following tools:
- clean_data
- transform_data
- aggregate_data"""

analysis_system_prompt = """You are an Analysis Agent. Your role is to perform statistical, correlation, and regression analysis using the following tools:
- stat_analysis
- correlation_analysis
- regression_analysis"""

visualization_system_prompt = """You are a Visualization Agent. Your role is to create bar charts, line charts, and pie charts using the following tools:
- create_bar_chart
- create_line_chart
- create_pie_chart"""
```

Ensuite, définissez les outils de chaque agent.

À l’exception de l’agent de triage, chaque agent dispose d’outils propres à son rôle :

**Agent de prétraitement des données** : nettoyage, transformation et agrégation des données.

**Agent d’analyse des données** : analyse statistique, analyse de corrélation et analyse de régression.

**Agent de visualisation des données** : création de graphiques à barres, de graphiques linéaires et de graphiques circulaires.

```python lines theme={"system"}
triage_tools = [
    {
        "type": "function",
        "function": {
            "name": "send_query_to_agents",
            "description": "Sends the user query to relevant agents based on their capabilities.",
            "parameters": {
                "type": "object",
                "properties": {
                    "agents": {
                        "type": "array",
                        "items": {"type": "string"},
                        "description": "An array of agent names to send the query to.",
                    },
                    "query": {
                        "type": "string",
                        "description": "The user query to send.",
                    },
                },
                "required": ["agents", "query"],
            },
        },
        "strict": True,
    }
]

preprocess_tools = [
    {
        "type": "function",
        "function": {
            "name": "clean_data",
            "description": "Cleans the provided data by removing duplicates and handling missing values.",
            "parameters": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "string",
                        "description": "The dataset to clean. Should be in a suitable format such as JSON or CSV.",
                    }
                },
                "required": ["data"],
                "additionalProperties": False,
            },
        },
        "strict": True,
    },
    {
        "type": "function",
        "function": {
            "name": "transform_data",
            "description": "Transforms data based on specified rules.",
            "parameters": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "string",
                        "description": "The data to transform. Should be in a suitable format such as JSON or CSV.",
                    },
                    "rules": {
                        "type": "string",
                        "description": "Transformation rules to apply, specified in a structured format.",
                    },
                },
                "required": ["data", "rules"],
                "additionalProperties": False,
            },
        },
        "strict": True,
    },
    {
        "type": "function",
        "function": {
            "name": "aggregate_data",
            "description": "Aggregates data by specified columns and operations.",
            "parameters": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "string",
                        "description": "The data to aggregate. Should be in a suitable format such as JSON or CSV.",
                    },
                    "group_by": {
                        "type": "array",
                        "items": {"type": "string"},
                        "description": "Columns to group by.",
                    },
                    "operations": {
                        "type": "string",
                        "description": "Aggregation operations to perform, specified in a structured format.",
                    },
                },
                "required": ["data", "group_by", "operations"],
                "additionalProperties": False,
            },
        },
        "strict": True,
    },
]

analysis_tools = [
    {
        "type": "function",
        "function": {
            "name": "stat_analysis",
            "description": "Performs statistical analysis on the given dataset.",
            "parameters": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "string",
                        "description": "The dataset to analyze. Should be in a suitable format such as JSON or CSV.",
                    }
                },
                "required": ["data"],
                "additionalProperties": False,
            },
        },
        "strict": True,
    },
    {
        "type": "function",
        "function": {
            "name": "correlation_analysis",
            "description": "Calculates correlation coefficients between variables in the dataset.",
            "parameters": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "string",
                        "description": "The dataset to analyze. Should be in a suitable format such as JSON or CSV.",
                    },
                    "variables": {
                        "type": "array",
                        "items": {"type": "string"},
                        "description": "List of variables to calculate correlations for.",
                    },
                },
                "required": ["data", "variables"],
                "additionalProperties": False,
            },
        },
        "strict": True,
    },
    {
        "type": "function",
        "function": {
            "name": "regression_analysis",
            "description": "Performs regression analysis on the dataset.",
            "parameters": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "string",
                        "description": "The dataset to analyze. Should be in a suitable format such as JSON or CSV.",
                    },
                    "dependent_var": {
                        "type": "string",
                        "description": "The dependent variable for regression.",
                    },
                    "independent_vars": {
                        "type": "array",
                        "items": {"type": "string"},
                        "description": "List of independent variables.",
                    },
                },
                "required": ["data", "dependent_var", "independent_vars"],
                "additionalProperties": False,
            },
        },
        "strict": True,
    },
]

visualization_tools = [
    {
        "type": "function",
        "function": {
            "name": "create_bar_chart",
            "description": "Creates a bar chart from the provided data.",
            "parameters": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "string",
                        "description": "The data for the bar chart. Should be in a suitable format such as JSON or CSV.",
                    },
                    "x": {"type": "string", "description": "Column for the x-axis."},
                    "y": {"type": "string", "description": "Column for the y-axis."},
                },
                "required": ["data", "x", "y"],
                "additionalProperties": False,
            },
        },
        "strict": True,
    },
    {
        "type": "function",
        "function": {
            "name": "create_line_chart",
            "description": "Creates a line chart from the provided data.",
            "parameters": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "string",
                        "description": "The data for the line chart. Should be in a suitable format such as JSON or CSV.",
                    },
                    "x": {"type": "string", "description": "Column for the x-axis."},
                    "y": {"type": "string", "description": "Column for the y-axis."},
                },
                "required": ["data", "x", "y"],
                "additionalProperties": False,
            },
        },
        "strict": True,
    },
    {
        "type": "function",
        "function": {
            "name": "create_pie_chart",
            "description": "Creates a pie chart from the provided data.",
            "parameters": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "string",
                        "description": "The data for the pie chart. Should be in a suitable format such as JSON or CSV.",
                    },
                    "labels": {
                        "type": "string",
                        "description": "Column for the labels.",
                    },
                    "values": {
                        "type": "string",
                        "description": "Column for the values.",
                    },
                },
                "required": ["data", "labels", "values"],
                "additionalProperties": False,
            },
        },
        "strict": True,
    },
]
```

<h2 id="enable-multi-agent-tracking-with-weave">
  Activer le suivi multi-agent avec Weave
</h2>

Une fois les agents et leurs outils définis, l’étape suivante consiste à les relier entre eux et à activer le traçage Weave. Écrivez le code permettant de :

* Gérer la transmission de la requête utilisateur au système multi-agent.
* Gérer le fonctionnement interne du système multi-agent.
* Exécuter les appels d’outils.

```python lines theme={"system"}
# Exemple de requête

user_query = """
Below is some data. I want you to first remove the duplicates then analyze the statistics of the data as well as plot a line chart.

house_size (m3), house_price ($)
90, 100
80, 90
100, 120
90, 100
"""
```

D’après la requête de l’utilisateur, on peut déduire que les outils à appeler sont `clean_data`, `start_analysis` et `use_line_chart`.

Commencez par définir la fonction d’exécution chargée de lancer les appels d’outils.

En décorant vos fonctions Python avec `@weave.op()`, vous pouvez journaliser et déboguer les entrées, les sorties et les traces du modèle de langage.

Un système multi-agent comporte de nombreuses fonctions, mais il suffit d'ajouter `@weave.op()` au-dessus de chacune d'elles.

```python lines theme={"system"}
@weave.op()
def clean_data(data):
    data_io = StringIO(data)
    df = pd.read_csv(data_io, sep=",")
    df_deduplicated = df.drop_duplicates()
    return df_deduplicated

@weave.op()
def stat_analysis(data):
    data_io = StringIO(data)
    df = pd.read_csv(data_io, sep=",")
    return df.describe()

@weave.op()
def plot_line_chart(data):
    data_io = StringIO(data)
    df = pd.read_csv(data_io, sep=",")

    x = df.iloc[:, 0]
    y = df.iloc[:, 1]

    coefficients = np.polyfit(x, y, 1)
    polynomial = np.poly1d(coefficients)
    y_fit = polynomial(x)

    plt.figure(figsize=(10, 6))
    plt.plot(x, y, "o", label="Data Points")
    plt.plot(x, y_fit, "-", label="Best Fit Line")
    plt.title("Line Chart with Best Fit Line")
    plt.xlabel(df.columns[0])
    plt.ylabel(df.columns[1])
    plt.legend()
    plt.grid(True)

    # Enregistrer le graphique dans un buffer BytesIO avant de l’afficher
    buf = BytesIO()
    plt.savefig(buf, format="png")
    buf.seek(0)

    # Afficher le graphique
    plt.show()

    # Encoder l’image en base64 pour l’URL data
    image_data = buf.getvalue()
    base64_encoded_data = base64.b64encode(image_data)
    base64_string = base64_encoded_data.decode("utf-8")
    data_url = f"data:image/png;base64,{base64_string}"

    return data_url

# Définir la fonction qui exécute les outils
@weave.op()
def execute_tool(tool_calls, messages):
    for tool_call in tool_calls:
        tool_name = tool_call.function.name
        tool_arguments = json.loads(tool_call.function.arguments)

        if tool_name == "clean_data":
            # Simuler le nettoyage des données
            cleaned_df = clean_data(tool_arguments["data"])
            cleaned_data = {"cleaned_data": cleaned_df.to_dict()}
            messages.append(
                {"role": "tool", "name": tool_name, "content": json.dumps(cleaned_data)}
            )
            print("Cleaned data: ", cleaned_df)
        elif tool_name == "transform_data":
            # Simuler la transformation des données
            transformed_data = {"transformed_data": "sample_transformed_data"}
            messages.append(
                {
                    "role": "tool",
                    "name": tool_name,
                    "content": json.dumps(transformed_data),
                }
            )
        elif tool_name == "aggregate_data":
            # Simuler l’agrégation des données
            aggregated_data = {"aggregated_data": "sample_aggregated_data"}
            messages.append(
                {
                    "role": "tool",
                    "name": tool_name,
                    "content": json.dumps(aggregated_data),
                }
            )
        elif tool_name == "stat_analysis":
            # Simuler l’analyse statistique
            stats_df = stat_analysis(tool_arguments["data"])
            stats = {"stats": stats_df.to_dict()}
            messages.append(
                {"role": "tool", "name": tool_name, "content": json.dumps(stats)}
            )
            print("Statistical Analysis: ", stats_df)
        elif tool_name == "correlation_analysis":
            # Simuler l’analyse de corrélation
            correlations = {"correlations": "sample_correlations"}
            messages.append(
                {"role": "tool", "name": tool_name, "content": json.dumps(correlations)}
            )
        elif tool_name == "regression_analysis":
            # Simuler l’analyse de régression
            regression_results = {"regression_results": "sample_regression_results"}
            messages.append(
                {
                    "role": "tool",
                    "name": tool_name,
                    "content": json.dumps(regression_results),
                }
            )
        elif tool_name == "create_bar_chart":
            # Simuler la création d’un graphique à barres
            bar_chart = {"bar_chart": "sample_bar_chart"}
            messages.append(
                {"role": "tool", "name": tool_name, "content": json.dumps(bar_chart)}
            )
        elif tool_name == "create_line_chart":
            # Simuler la création d’un graphique linéaire
            line_chart = {"line_chart": plot_line_chart(tool_arguments["data"])}
            messages.append(
                {"role": "tool", "name": tool_name, "content": json.dumps(line_chart)}
            )
        elif tool_name == "create_pie_chart":
            # Simuler la création d’un graphique circulaire
            pie_chart = {"pie_chart": "sample_pie_chart"}
            messages.append(
                {"role": "tool", "name": tool_name, "content": json.dumps(pie_chart)}
            )
    return messages
```

Créez ensuite les gestionnaires d’outils pour chacun des sous-agents. Chacun d’eux transmet au modèle un prompt et un ensemble d’outils qui lui sont propres. La sortie est alors transmise à une fonction d’exécution qui effectue les appels d’outils.

```python lines theme={"system"}
# Définir les fonctions chargées du traitement de chaque agent
@weave.op()
def handle_data_processing_agent(query, conversation_messages):
    messages = [{"role": "system", "content": processing_system_prompt}]
    messages.append({"role": "user", "content": query})

    response = client.chat.completions.create(
        model=MODEL,
        messages=messages,
        temperature=0,
        tools=preprocess_tools,
    )

    conversation_messages.append(
        [tool_call.function for tool_call in response.choices[0].message.tool_calls]
    )
    execute_tool(response.choices[0].message.tool_calls, conversation_messages)

@weave.op()
def handle_analysis_agent(query, conversation_messages):
    messages = [{"role": "system", "content": analysis_system_prompt}]
    messages.append({"role": "user", "content": query})

    response = client.chat.completions.create(
        model=MODEL,
        messages=messages,
        temperature=0,
        tools=analysis_tools,
    )

    conversation_messages.append(
        [tool_call.function for tool_call in response.choices[0].message.tool_calls]
    )
    execute_tool(response.choices[0].message.tool_calls, conversation_messages)

@weave.op()
def handle_visualization_agent(query, conversation_messages):
    messages = [{"role": "system", "content": visualization_system_prompt}]
    messages.append({"role": "user", "content": query})

    response = client.chat.completions.create(
        model=MODEL,
        messages=messages,
        temperature=0,
        tools=visualization_tools,
    )

    conversation_messages.append(
        [tool_call.function for tool_call in response.choices[0].message.tool_calls]
    )
    execute_tool(response.choices[0].message.tool_calls, conversation_messages)
```

Enfin, créez l’outil principal chargé de traiter la requête de l’utilisateur. Cette fonction reçoit la requête de l’utilisateur, obtient une réponse du modèle, puis la transmet aux autres agents pour qu’ils l’exécutent.

```python lines theme={"system"}
# Fonction de traitement de l’entrée utilisateur et de triage
@weave.op()
def handle_user_message(user_query, conversation_messages=None):
    if conversation_messages is None:
        conversation_messages = []
    user_message = {"role": "user", "content": user_query}
    conversation_messages.append(user_message)

    messages = [{"role": "system", "content": triaging_system_prompt}]
    messages.extend(conversation_messages)

    response = client.chat.completions.create(
        model=MODEL,
        messages=messages,
        temperature=0,
        tools=triage_tools,
    )

    conversation_messages.append(
        [tool_call.function for tool_call in response.choices[0].message.tool_calls]
    )

    for tool_call in response.choices[0].message.tool_calls:
        if tool_call.function.name == "send_query_to_agents":
            agents = json.loads(tool_call.function.arguments)["agents"]
            query = json.loads(tool_call.function.arguments)["query"]
            for agent in agents:
                if agent == "Data Processing Agent":
                    handle_data_processing_agent(query, conversation_messages)
                elif agent == "Analysis Agent":
                    handle_analysis_agent(query, conversation_messages)
                elif agent == "Visualization Agent":
                    handle_visualization_agent(query, conversation_messages)

    outputs = extract_tool_contents(conversation_messages)

    return outputs

functions = [
    "clean_data",
    "transform_data",
    "stat_analysis",
    "aggregate_data",
    "correlation_analysis",
    "regression_analysis",
    "create_bar_chart",
    "create_line_chart",
    "create_pie_chart",
]

@weave.op()
def extract_tool_contents(data):
    contents = {}
    contents["all"] = data
    for element in data:
        if (
            isinstance(element, dict)
            and element.get("role") == "tool"
            and element.get("name") in functions
        ):
            name = element["name"]
            content_str = element["content"]
            try:
                content_json = json.loads(content_str)
                if "chart" not in element.get("name"):
                    contents[name] = [content_json]
                else:
                    first_key = next(iter(content_json))
                    second_level = content_json[first_key]
                    if isinstance(second_level, dict):
                        second_key = next(iter(second_level))
                        contents[name] = second_level[second_key]
                    else:
                        contents[name] = second_level
            except json.JSONDecodeError:
                print(f"Error decoding JSON for {name}")
                contents[name] = None

    return contents
```

<h2 id="execute-the-multi-agent-system-and-visualize-in-weave">
  Exécuter le système multi-agent et le visualiser dans Weave
</h2>

Maintenant que tous les agents, outils et gestionnaires sont en place, le système est prêt à être exécuté. Pour finir, exécutez la fonction principale `handle_user_message` avec l'entrée de l'utilisateur et observez les résultats.

```python lines theme={"system"}
handle_user_message(user_query)
```

Lorsque vous cliquez sur l’URL Weave, la trace d’exécution s’affiche. Sur la page **Traces**, vous pouvez consulter l’entrée et la sortie. Pour plus de clarté, le schéma inclut des captures d’écran des résultats affichés lorsque vous cliquez sur chaque sortie. Weave s’intègre à l’API OpenAI et calcule automatiquement les coûts. Le coût et la latence sont également affichés à côté de chaque trace.

<img src="https://mintcdn.com/coreweave-dbfa0e8d/3Dv_sw2eg8feUJlx/products/wandb/weave/_media/1-1.png?fit=max&auto=format&n=3Dv_sw2eg8feUJlx&q=85&s=8139bccbf322122cc65ef1769120e322" alt="Page Traces de Weave montrant l’exécution multi-agent avec le coût et la latence" width="2250" height="1140" data-path="products/wandb/weave/_media/1-1.png" />

Cliquez sur une ligne pour afficher les processus intermédiaires exécutés au sein du système multi-agent. Par exemple, l’entrée et la sortie de `analysis_agent` apparaissent dans un format de sortie structuré. Les sorties structurées d’OpenAI facilitent la collaboration entre les agents, mais à mesure que le système se complexifie, le format de ces échanges devient plus difficile à suivre. Weave vous permet d’inspecter en détail ces processus intermédiaires ainsi que leurs entrées et sorties.

<Frame>
  <img src="https://mintcdn.com/coreweave-dbfa0e8d/3Dv_sw2eg8feUJlx/products/wandb/weave/_media/multi-agent-structured-output-3.png?fit=max&auto=format&n=3Dv_sw2eg8feUJlx&q=85&s=cab6a4cba35c09517bd38979824e095e" alt="Détail de la trace Weave montrant l’entrée et la sortie structurées de l’agent d’analyse" width="2899" height="1596" data-path="products/wandb/weave/_media/multi-agent-structured-output-3.png" />
</Frame>

Voyons plus en détail comment le traçage est géré dans Weave.

<h2 id="conclusion">
  Conclusion
</h2>

Dans ce tutoriel, vous avez appris à développer un système multi-agent à l’aide des sorties structurées d’OpenAI, et à utiliser Weave pour assurer le suivi des entrées, des sorties finales et des formats de sortie intermédiaires. Vous disposez désormais d’un exemple fonctionnel que vous pouvez enrichir en y ajoutant des agents, des outils ou des schémas de réponse structurés.
