# Agents-Course

## Docs

- [Introduction aux frameworks agentiques](https://huggingface.co/learn/agents-course/fr/unit2/introduction.md)
- [Quiz rapide 2 (non noté) [[quiz2]]](https://huggingface.co/learn/agents-course/fr/unit2/llama-index/quiz2.md)
- [Créer des *workflows* agentiques dans LlamaIndex](https://huggingface.co/learn/agents-course/fr/unit2/llama-index/workflows.md)
- [Conclusion](https://huggingface.co/learn/agents-course/fr/unit2/llama-index/conclusion.md)
- [Utiliser les outils dans LlamaIndex](https://huggingface.co/learn/agents-course/fr/unit2/llama-index/tools.md)
- [Table des matières](https://huggingface.co/learn/agents-course/fr/unit2/llama-index/README.md)
- [Utiliser les agents dans LlamaIndex](https://huggingface.co/learn/agents-course/fr/unit2/llama-index/agents.md)
- [Introduction à LlamaIndex](https://huggingface.co/learn/agents-course/fr/unit2/llama-index/introduction.md)
- [Quiz rapide 1 (non noté) [[quiz1]]](https://huggingface.co/learn/agents-course/fr/unit2/llama-index/quiz1.md)
- [Que sont les *components* dans LlamaIndex ?](https://huggingface.co/learn/agents-course/fr/unit2/llama-index/components.md)
- [Introduction au *LlamaHub*](https://huggingface.co/learn/agents-course/fr/unit2/llama-index/llama-hub.md)
- [Systèmes multi-agents](https://huggingface.co/learn/agents-course/fr/unit2/smolagents/multi_agent_systems.md)
- [Agents visuel avec smolagents](https://huggingface.co/learn/agents-course/fr/unit2/smolagents/vision_agents.md)
- [Construction de systèmes de RAG agentiques](https://huggingface.co/learn/agents-course/fr/unit2/smolagents/retrieval_agents.md)
- [Petit Quiz (non noté) [[quiz2]]](https://huggingface.co/learn/agents-course/fr/unit2/smolagents/quiz2.md)
- [Construire des agents qui utilisent du code](https://huggingface.co/learn/agents-course/fr/unit2/smolagents/code_agents.md)
- [Conclusion](https://huggingface.co/learn/agents-course/fr/unit2/smolagents/conclusion.md)
- [Outils](https://huggingface.co/learn/agents-course/fr/unit2/smolagents/tools.md)
- [C'est l'heure de l'examen !](https://huggingface.co/learn/agents-course/fr/unit2/smolagents/final_quiz.md)
- [Introduction à `smolagents`](https://huggingface.co/learn/agents-course/fr/unit2/smolagents/introduction.md)
- [Pourquoi utiliser smolagents](https://huggingface.co/learn/agents-course/fr/unit2/smolagents/why_use_smolagents.md)
- [Petit Quiz (non noté) [[quiz1]]](https://huggingface.co/learn/agents-course/fr/unit2/smolagents/quiz1.md)
- [Écrire des actions sous forme d'extraits de code ou de blobs JSON](https://huggingface.co/learn/agents-course/fr/unit2/smolagents/tool_calling_agents.md)
- [Qu'est-ce que LangGraph ?](https://huggingface.co/learn/agents-course/fr/unit2/langgraph/when_to_use_langgraph.md)
- [Les composants de base de LangGraph](https://huggingface.co/learn/agents-course/fr/unit2/langgraph/building_blocks.md)
- [Construire votre premier LangGraph](https://huggingface.co/learn/agents-course/fr/unit2/langgraph/first_graph.md)
- [Conclusion](https://huggingface.co/learn/agents-course/fr/unit2/langgraph/conclusion.md)
- [Graphe d'analyse de documents](https://huggingface.co/learn/agents-course/fr/unit2/langgraph/document_analysis_agent.md)
- [Introduction à LangGraph](https://huggingface.co/learn/agents-course/fr/unit2/langgraph/introduction.md)
- [Testez votre compréhension de LangGraph](https://huggingface.co/learn/agents-course/fr/unit2/langgraph/quiz1.md)
- [Readme](https://huggingface.co/learn/agents-course/fr/unit3/README.md)
- [RAG agentique](https://huggingface.co/learn/agents-course/fr/unit3/agentic-rag/agentic-rag.md)
- [Création d'un RAG pour converser avec les invités](https://huggingface.co/learn/agents-course/fr/unit3/agentic-rag/invitees.md)
- [Conclusion](https://huggingface.co/learn/agents-course/fr/unit3/agentic-rag/conclusion.md)
- [Création et intégration d'outils pour votre agent](https://huggingface.co/learn/agents-course/fr/unit3/agentic-rag/tools.md)
- [Introduction au cas d'usage sur la RAG agentique](https://huggingface.co/learn/agents-course/fr/unit3/agentic-rag/introduction.md)
- [Création de l'agent pour le gala](https://huggingface.co/learn/agents-course/fr/unit3/agentic-rag/agent.md)
- [Obtenez votre certificat 🎓](https://huggingface.co/learn/agents-course/fr/unit4/get-your-certificate.md)
- [Le projet pratique final](https://huggingface.co/learn/agents-course/fr/unit4/hands-on.md)
- [Conclusion](https://huggingface.co/learn/agents-course/fr/unit4/conclusion.md)
- [Introduction à l'unité finale [[introduction]]](https://huggingface.co/learn/agents-course/fr/unit4/introduction.md)
- [Qu'est-ce que GAIA ?](https://huggingface.co/learn/agents-course/fr/unit4/what-is-gaia.md)
- [Et maintenant ? Quels sujets devrais-je apprendre ?](https://huggingface.co/learn/agents-course/fr/unit4/additional-readings.md)
- [Finetunons un modèle pour pouvoir faire de l'appel de fonctions](https://huggingface.co/learn/agents-course/fr/bonus-unit1/fine-tuning.md)
- [Qu'est-ce que l'appel de fonctions ?](https://huggingface.co/learn/agents-course/fr/bonus-unit1/what-is-function-calling.md)
- [Conclusion [[conclusion]]](https://huggingface.co/learn/agents-course/fr/bonus-unit1/conclusion.md)
- [Introduction](https://huggingface.co/learn/agents-course/fr/bonus-unit1/introduction.md)
- [Qu'est-ce que l'observabilité et l'évaluation des agents ?](https://huggingface.co/learn/agents-course/fr/bonus-unit2/what-is-agent-observability-and-evaluation.md)
- [Observer et évaluer des agents](https://huggingface.co/learn/agents-course/fr/bonus-unit2/monitoring-and-evaluating-agents-notebook.md)
- [Introduction](https://huggingface.co/learn/agents-course/fr/bonus-unit2/introduction.md)
- [Quiz : évaluation des agents](https://huggingface.co/learn/agents-course/fr/bonus-unit2/quiz.md)
- [L'état de l'art de l'utilisation des LLM dans les jeux](https://huggingface.co/learn/agents-course/fr/bonus-unit3/state-of-art.md)
- [Construire un agent de combat Pokémon](https://huggingface.co/learn/agents-course/fr/bonus-unit3/building_your_pokemon_agent.md)
- [Conclusion](https://huggingface.co/learn/agents-course/fr/bonus-unit3/conclusion.md)
- [Introduction](https://huggingface.co/learn/agents-course/fr/bonus-unit3/introduction.md)
- [Lancer l'agent de combat Pokémon](https://huggingface.co/learn/agents-course/fr/bonus-unit3/launching_agent_battle.md)
- [Des LLM aux agents](https://huggingface.co/learn/agents-course/fr/bonus-unit3/from-llm-to-agents.md)
- [Sessions en direct 1 : Comment fonctionne le cours et première session de questions-réponses](https://huggingface.co/learn/agents-course/fr/communication/live1.md)
- [Quiz final de l'Unité 1](https://huggingface.co/learn/agents-course/fr/unit1/final-quiz.md)
- [Qu'est-ce qu'un LLM ?](https://huggingface.co/learn/agents-course/fr/unit1/what-are-llms.md)
- [Actions : permettre à l'agent d'interagir avec son environnement](https://huggingface.co/learn/agents-course/fr/unit1/actions.md)
- [Quiz rapide 2 [[quiz2]]](https://huggingface.co/learn/agents-course/fr/unit1/quiz2.md)
- [Conclusion [[conclusion]]](https://huggingface.co/learn/agents-course/fr/unit1/conclusion.md)
- [Que sont les outils ?](https://huggingface.co/learn/agents-course/fr/unit1/tools.md)
- [Messages et *tokens* spéciaux](https://huggingface.co/learn/agents-course/fr/unit1/messages-and-special-tokens.md)
- [Créons notre premier agent avec smolagents](https://huggingface.co/learn/agents-course/fr/unit1/tutorial.md)
- [Qu'est-ce qu'un agent ?](https://huggingface.co/learn/agents-course/fr/unit1/what-are-agents.md)
- [Table des matières](https://huggingface.co/learn/agents-course/fr/unit1/README.md)
- [Réflexions : raisonnement interne et l'approche Re-Act](https://huggingface.co/learn/agents-course/fr/unit1/thoughts.md)
- [Observer : intégrer le retour d'information pour réfléchir et s'adapter](https://huggingface.co/learn/agents-course/fr/unit1/observations.md)
- [Introduction aux agents](https://huggingface.co/learn/agents-course/fr/unit1/introduction.md)
- [Comprendre les agents à travers le cycle Réflexion-Action-Observation](https://huggingface.co/learn/agents-course/fr/unit1/agent-steps-and-structure.md)
- [Quiz rapide 1 [[quiz1]]](https://huggingface.co/learn/agents-course/fr/unit1/quiz1.md)
- [Bibliothèque d'agents factices](https://huggingface.co/learn/agents-course/fr/unit1/dummy-agent-library.md)
- [Embarquement : vos premiers pas ⛵](https://huggingface.co/learn/agents-course/fr/unit0/onboarding.md)
- [Bienvenue dans le cours 🤗 [[introduction]]](https://huggingface.co/learn/agents-course/fr/unit0/introduction.md)
- [(Optionnel) Introduction à Discord [[discord-101]]](https://huggingface.co/learn/agents-course/fr/unit0/discord101.md)

### Introduction aux frameworks agentiques
https://huggingface.co/learn/agents-course/fr/unit2/introduction.md

# Introduction aux frameworks agentiques

Bienvenue dans cette deuxième unité, où **nous explorerons différents frameworks agentiques** qui peuvent être utilisés pour construire de puissantes applications agentiques. 

Nous étudierons :

- Dans l'unité 2.1 : [smolagents](https://huggingface.co/docs/smolagents/en/index)  
- Dans l'unité 2.2 : [LlamaIndex](https://www.llamaindex.ai/)
- Dans l'unité 2.3 : [LangGraph](https://www.langchain.com/langgraph)

Plongeons-y ! 🕵

## Quand utiliser un *framework* agentique

Parfois, **des *workflows* prédéfinis sont suffisants** pour répondre aux demandes des utilisateurs, et il n'y a pas vraiment besoin d'un *framework* agentique. Si l'approche pour construire un agent est simple, comme une chaîne de *prompts*, utiliser du code simple peut suffire. L'avantage est que le développeur aura **un contrôle total et une compréhension de son système sans abstractions**.

Cependant, lorsque le *workflow* devient plus complexe, comme laisser un LLM appeler des fonctions ou utiliser plusieurs agents, ces abstractions commencent à devenir utiles.

En considérant ces idées, nous pouvons déjà identifier le besoin de certaines fonctionnalités :

* Un *moteur LLM* qui alimente le système.
* Une *liste d'outils* auxquels l'agent peut accéder.  
* Un *analyseur* pour extraire les appels d'outils de la sortie du LLM.
* Un *prompt système* synchronisé avec l'analyseur.
* Un *système de mémoire*.
* Des *mécanismes de journalisation des erreurs et de réessai* pour contrôler les erreurs du LLM.

Nous explorerons comment ces sujets sont résolus dans divers *frameworks* comme `smolagents`, `LlamaIndex` et `LangGraph`.

## Unités des frameworks agentiques

| *Framework*  | Description | Auteur de l'unité |
|------------|----------------|----------------|
| [smolagents](./smolagents/introduction) | *Framework* d'agents développé par Hugging Face. | Sergio PANIEGO - [HF](https://huggingface.co/sergiopaniego) - [X](https://x.com/sergiopaniego) - [Linkedin](https://www.linkedin.com/in/sergio-paniego-blanco) |
| [Llama-Index](./llama-index/introduction) | Outils de bout en bout pour déployer un agent IA augmenté par le contexte en production | David BERENSTEIN - [HF](https://huggingface.co/davidberenstein1957) - [X](https://x.com/davidberenstei) - [Linkedin](https://www.linkedin.com/in/davidberenstein) |
| [LangGraph](./langgraph/introduction) | Agents permettant l'orchestration étatique des agents | Joffrey THOMAS - [HF](https://huggingface.co/Jofthomas) - [X](https://x.com/Jthmas404) - [Linkedin](https://www.linkedin.com/in/joffrey-thomas) |

### Quiz rapide 2 (non noté) [[quiz2]]
https://huggingface.co/learn/agents-course/fr/unit2/llama-index/quiz2.md

# Quiz rapide 2 (non noté) [[quiz2]]

Quoi ?! Un autre Quiz ? Nous savons, nous savons, ... 😅 Mais ce court quiz non noté est là pour **vous aider à renforcer les concepts clés que vous venez d'apprendre**.

Ce quiz couvre les *workflows* d'agents et les interactions, composants essentiels pour construire des agents efficaces.

### Q1 : Quel est le but de l'`AgentWorkflow` dans LlamaIndex ?

AgentWorkflow est la façon principale de créer rapidement un système avec un ou plusieurs agents.",
correct: true
},
{
text: "Pour créer un seul agent qui peut interroger vos données sans mémoire",
explain: "Non, l'AgentWorkflow est peut faire plus que cela, le  QueryEngine est pour des requêtes simples sur vos données.",
},
{
text: "Pour construire automatiquement des outils pour les agents",
explain: "L'AgentWorkflow ne construit pas d'outils, c'est le travail du développeur.",
},
{
text: "Pour gérer la mémoire et l'état des agents",
explain: "Gérer la mémoire et l'état n'est pas l'objectif principal de l'AgentWorkflow.",
}
]}
/>

---

### Q2 : Quel objet est utilisé pour garder une trace de l'état du *workflow* ?

State",
explain: "State n'est pas le bon objet pour la gestion d'état du workflow.",
},
{
text: "Context",
explain: "Context est le bon objet utilisé pour garder une trace de l'état du workflow.",
correct: true
},
{
text: "WorkflowState",
explain: "WorkflowState n'est pas le bon objet.",
},
{
text: "Management",
explain: "Management n'est pas un objet valide pour l'état du workflow.",
}
]}
/>

---

### Q3 : Quelle méthode devrait être utilisée si vous voulez qu'un agent se souvienne des interactions précédentes ?

run(query_str)",
explain: ".run(query_str) ne maintient pas l'historique de conversation.",
},
{
text: "chat(query_str, ctx=ctx)",
explain: "chat() n'est pas une méthode valide sur les workflows.",
},
{
text: "interact(query_str)",
explain: "interact() n'est pas une méthode valide pour les interactions d'agents.",
},
{
text: "run(query_str, ctx=ctx)",
explain: "En passant et maintenant le contexte, nous pouvons maintenir l'état !",
correct: true
}
]}
/>

---

### Q4 : Quelle est une caractéristique clé du RAG agentique ?

workflow RAG",
explain: "Le RAG agentique peut utiliser différents outils, incluant les outils basés sur des documents.",
},
{
text: "Il répond automatiquement aux questions sans outils, comme un chatbot",
explain: "Le RAG agentique utilise bien des outils pour répondre aux questions.",
},
{
text: "Il peut décider d'utiliser n'importe quel outil pour répondre aux questions, incluant les outils RAG",
explain: "Le RAG agentique a la flexibilité d'utiliser différents outils pour répondre aux questions.",
correct: true
},
{
text: "Il ne fonctionne qu'avec les Function Calling Agents",
explain: "Le RAG agentique n'est pas limité aux Function Calling Agents.",
}
]}
/>

---

Vous avez compris ? Parfait ! Maintenant faisons **un bref récapitulatif de l'unité !**

### Créer des *workflows* agentiques dans LlamaIndex
https://huggingface.co/learn/agents-course/fr/unit2/llama-index/workflows.md

# Créer des *workflows* agentiques dans LlamaIndex

Un *workflow* dans LlamaIndex fournit un moyen structuré d'organiser votre code en étapes séquentielles et gérables.

Un tel *workflow* est créé en définissant des `Steps` qui sont déclenchés par des `Events`, et qui émettent eux-mêmes des `Events` pour déclencher d'autres étapes.
Jetons un coup d'œil à Alfred montrant un *workflow* LlamaIndex pour une tâche de RAG.

![Workflow Schematic](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/workflows.png)

**Les *workflows* offrent plusieurs avantages clés :**

- Organisation claire du code en étapes discrètes
- Architecture événementielle pour un flux de contrôle flexible
- Communication *type-safe* entre les étapes
- Gestion d'état intégrée
- Support pour des interactions d'agents simples et complexes

Comme vous l'avez peut-être deviné, **les *workflows* trouvent un excellent équilibre entre l'autonomie des agents tout en maintenant le contrôle sur le *workflow* global.**

Alors, apprenons à créer un *workflow* nous-mêmes !

## Créer des *Workflows*

> [!TIP]
> Vous pouvez suivre le code dans ce notebook que vous pouvez exécuter avec Google Colab.

### Création de *Workflow* de base

Installer le package Workflow
Comme introduit dans la section sur le LlamaHub, nous pouvons installer le package Workflow avec la commande suivante :

```python
pip install llama-index-utils-workflow
```

Nous pouvons créer un *workflow* en une seule étape en définissant une classe qui hérite de `Workflow` et en décorant vos fonctions avec `@step`.
Nous devrons également ajouter `StartEvent` et `StopEvent`, qui sont des événements spéciaux utilisés pour indiquer le début et la fin du *workflow*.

```python
from llama_index.core.workflow import StartEvent, StopEvent, Workflow, step

class MyWorkflow(Workflow):
    @step
    async def my_step(self, ev: StartEvent) -> StopEvent:
        # do something here
        return StopEvent(result="Hello, world!")

w = MyWorkflow(timeout=10, verbose=False)
result = await w.run()
```

Comme vous pouvez le voir, nous pouvons maintenant exécuter le *workflow* en appelant `w.run()`.

### Connecter plusieurs étapes

Pour connecter plusieurs étapes, nous **créons des événements personnalisés qui transportent des données entre les étapes.**
Pour ce faire, nous devons ajouter un `Event` qui est passé entre les étapes et transfère la sortie de la première étape vers la deuxième étape.

```python
from llama_index.core.workflow import Event

class ProcessingEvent(Event):
    intermediate_result: str

class MultiStepWorkflow(Workflow):
    @step
    async def step_one(self, ev: StartEvent) -> ProcessingEvent:
        # Traitement des données initiales
        return ProcessingEvent(intermediate_result="Step 1 complete")

    @step
    async def step_two(self, ev: ProcessingEvent) -> StopEvent:
        # Utiliser le résultat intermédiaire
        final_result = f"Finished processing: {ev.intermediate_result}"
        return StopEvent(result=final_result)

w = MultiStepWorkflow(timeout=10, verbose=False)
result = await w.run()
result
```

L'indication de type est importante ici, car elle garantit que le *workflow* est exécuté correctement. Compliquons un peu les choses !

### Boucles et branches

L'indication de type est la partie la plus puissante des *workflows* car elle nous permet de créer des branches, des boucles et des jointures pour faciliter des *workflows* plus complexes.

Montrons un exemple de **création d'une boucle** en utilisant l'opérateur union `|`.
Dans l'exemple ci-dessous, nous voyons que le `LoopEvent` est pris en entrée pour l'étape et peut également être retourné en sortie.

```python
from llama_index.core.workflow import Event
import random

class ProcessingEvent(Event):
    intermediate_result: str

class LoopEvent(Event):
    loop_output: str

class MultiStepWorkflow(Workflow):
    @step
    async def step_one(self, ev: StartEvent | LoopEvent) -> ProcessingEvent | LoopEvent:
        if random.randint(0, 1) == 0:
            print("Bad thing happened")
            return LoopEvent(loop_output="Back to step one.")
        else:
            print("Good thing happened")
            return ProcessingEvent(intermediate_result="First step complete.")

    @step
    async def step_two(self, ev: ProcessingEvent) -> StopEvent:
        # Utiliser le résultat intermédiaire
        final_result = f"Finished processing: {ev.intermediate_result}"
        return StopEvent(result=final_result)

w = MultiStepWorkflow(verbose=False)
result = await w.run()
result
```

### Dessiner des *Workflows*

Nous pouvons également dessiner des *workflows*. Utilisons la fonction `draw_all_possible_flows` pour dessiner le *workflow*. Cela stocke le *workflow* dans un fichier HTML.

```python
from llama_index.utils.workflow import draw_all_possible_flows

w = ... # tel que défini dans la section précédentetel que défini dans la section précédente
draw_all_possible_flows(w, "flow.html")
```

![workflow drawing](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/workflow-draw.png)

Il y a une dernière astuce cool que nous couvrirons dans le cours, qui est la capacité d'ajouter de l'état au *workflow*.

### Gestion d'état

La gestion d'état est utile quand vous voulez garder une trace de l'état du *workflow*, pour que chaque étape ait accès au même état.
Nous pouvons faire cela en utilisant l'indication de type `Context` au-dessus d'un paramètre dans la fonction d'étape.

```python
from llama_index.core.workflow import Context, StartEvent, StopEvent

@step
async def query(self, ctx: Context, ev: StartEvent) -> StopEvent:
    # stocker la requête dans le contexte
    await ctx.store.set("query", "What is the capital of France?")

    # faire quelque chose avec le contexte et l'event
    val = ...

    # récupérer la requête dans le contexte
    query = await ctx.store.get("query")

    return StopEvent(result=val)
```

Parfait ! Maintenant vous savez comment créer des *workflows* de base dans LlamaIndex !

> [!TIP]
> Il y a quelques nuances plus complexes aux workflows, que vous pouvez apprendre dans la documentation LlamaIndex.

Cependant, il y a une autre façon de créer des *workflows*, qui repose sur la classe `AgentWorkflow`. Jetons un coup d'œil à comment nous pouvons utiliser cela pour créer un *workflow* multi-agents.

## Automatiser les *workflows* avec des *Multi-Agent Workflows*

Au lieu de la création manuelle de *workflows*, nous pouvons utiliser la **classe `AgentWorkflow` pour créer un *workflow* multi-agents**.
L'`AgentWorkflow` utilise des *Workflow Agents* pour vous permettre de créer un système d'un ou plusieurs agents qui peuvent collaborer et se passer des tâches entre eux basées sur leurs capacités spécialisées.
Cela permet de construire des systèmes d'agents complexes où différents agents gèrent différents aspects d'une tâche.
Au lieu d'importer des classes de `llama_index.core.agent`, nous importerons les classes d'agents de `llama_index.core.agent.workflow`.
Un agent doit être désigné comme l'agent racine dans le constructeur `AgentWorkflow`.
Quand un message utilisateur arrive, il est d'abord routé vers l'agent racine.

Chaque agent peut ensuite :

- Gérer la demande directement en utilisant leurs outils
- Passer le relais à un autre agent mieux adapté à la tâche
- Retourner une réponse à l'utilisateur

Voyons comment créer un *workflow* multi-agents.

```python
from llama_index.core.agent.workflow import AgentWorkflow, ReActAgent
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI

# Définir quelques outils
def add(a: int, b: int) -> int:
    """Additionner deux nombres."""
    return a + b

def multiply(a: int, b: int) -> int:
    """Multiplier deux nombres."""
    return a * b

llm = HuggingFaceInferenceAPI(model_name="Qwen/Qwen2.5-Coder-32B-Instruct")

# nous pouvons passer des fonctions directement sans FunctionTool -- les fn/docstring sont analysés pour le nom/description
multiply_agent = ReActAgent(
    name="multiply_agent",
    description="Is able to multiply two integers",
    system_prompt="A helpful assistant that can use a tool to multiply numbers.",
    tools=[multiply],
    llm=llm,
)

addition_agent = ReActAgent(
    name="add_agent",
    description="Is able to add two integers",
    system_prompt="A helpful assistant that can use a tool to add numbers.",
    tools=[add],
    llm=llm,
)

# Créer le workflow
workflow = AgentWorkflow(
    agents=[multiply_agent, addition_agent],
    root_agent="multiply_agent",
)

# Exécuter le système
response = await workflow.run(user_msg="Can you add 5 and 3?")
```

Les outils d'agents peuvent également modifier l'état du *workflow* que nous avons mentionné plus tôt. Avant de commencer le *workflow*, nous pouvons fournir un dictionnaire d'état initial qui sera disponible pour tous les agents.
L'état est stocké dans la clé d'état du contexte du *workflow*. Il sera injecté dans le *state_prompt* qui augmente chaque nouveau message utilisateur.

Injectons un compteur pour compter les appels de fonctions en modifiant l'exemple précédent :

```python
from llama_index.core.workflow import Context

# Définir quelques outils
async def add(ctx: Context, a: int, b: int) -> int:
    """Additionner deux nombres."""
    # mettre à jour notre comptage
    cur_state = await ctx.store.get("state")
    cur_state["num_fn_calls"] += 1
    await ctx.store.set("state", cur_state)

    return a + b

async def multiply(ctx: Context, a: int, b: int) -> int:
    """Multiplier deux nombres."""
    # mettre à jour notre comptage
    cur_state = await ctx.store.get("state")
    cur_state["num_fn_calls"] += 1
    await ctx.store.set("state", cur_state)

    return a * b

...

workflow = AgentWorkflow(
    agents=[multiply_agent, addition_agent],
    root_agent="multiply_agent",
    initial_state={"num_fn_calls": 0},
    state_prompt="Current state: {state}. User message: {msg}",
)

# exécuter le workflow avec le contexte
ctx = Context(workflow)
response = await workflow.run(user_msg="Can you add 5 and 3?", ctx=ctx)

# sortir et inspecter l'état
state = await ctx.store.get("state")
print(state["num_fn_calls"])
```

Félicitations ! Vous avez maintenant passer en revue les bases des agents dans LlamaIndex ! 🎉

Continuons avec un dernier quiz pour solidifier vos connaissances ! 🚀

### Conclusion
https://huggingface.co/learn/agents-course/fr/unit2/llama-index/conclusion.md

# Conclusion

Félicitations pour avoir terminé le module `llama-index` de cette deuxième Unité 🥳

Vous **maîtrisez les fondamentaux** de `llama-index` et avez vu comment construire vos propres *workflows* agentiques !
Maintenant que vous avez des compétences en `llama-index`, vous pouvez commencer à créer des moteurs de recherche qui résoudront des tâches qui vous intéressent.

Dans le prochain module de l'unité, vous allez apprendre **comment construire des *agents* avec *LangGraph***.

Enfin, nous aimerions **entendre ce que vous pensez du cours et comment nous pouvons Enfin, nous serions ravis **d'entendre ce que vous pensez du cours et comment nous pourrions l'améliorer**. Si vous avez des retours, n'hésitez pas à [remplir ce formulaire](https://docs.google.com/forms/d/e/1FAIpQLSe9VaONn0eglax0uTwi29rIn4tM7H2sYmmybmG5jJNlE5v0xA/viewform?usp=dialog).

### Continuez à apprendre, restez géniaux 🤗

### Utiliser les outils dans LlamaIndex
https://huggingface.co/learn/agents-course/fr/unit2/llama-index/tools.md

# Utiliser les outils dans LlamaIndex

**Définir un ensemble clair d'outils est crucial pour la performance.** Comme nous l'avons discuté dans l'[Unité 1](../../unit1/tools), des interfaces claires sont plus faciles à utiliser pour les LLM.
Tout comme une interface API logicielle pour les ingénieurs humains, ils peuvent tirer profit de l'outil s'il est facile de comprendre comment il fonctionne.

Il y a **quatre types principaux d'outils dans LlamaIndex** :

![Tools](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/tools.png)

1. `FunctionTool` : Convertit n'importe quelle fonction Python en un outil qu'un agent peut utiliser. Il comprend automatiquement comment la fonction fonctionne.
2. `QueryEngineTool` : Un outil qui permet aux agents d'utiliser des *query engines*. Puisque les agents sont construits sur des *query engines*, ils peuvent également utiliser d'autres agents comme outil.
3. `Toolspecs` : Ensembles d'outils créés par la communauté, en incluant souvent pour des services spécifiques comme Gmail.
4. `Utility Tools` : Outils spéciaux qui aident à gérer de grandes quantités de données d'autres outils.

Nous passerons en revue chacun d'eux plus en détail ci-dessous.

## Créer un *FunctionTool*

> [!TIP]
> Vous pouvez suivre le code dans ce notebook que vous pouvez exécuter avec Google Colab.

*FunctionTool* fournit un moyen simple d'envelopper n'importe quelle fonction Python et de la rendre disponible à un agent.
Vous pouvez passer soit une fonction synchrone soit asynchrone à l'outil, avec des paramètres optionnels `name` et `description`.
Le nom et la description sont particulièrement importants car ils aident l'agent à comprendre quand et comment utiliser l'outil efficacement.
Regardons comment créer un *FunctionTool* ci-dessous puis l'appeler.

```python
from llama_index.core.tools import FunctionTool

def get_weather(location: str) -> str:
    """Utile pour obtenir le temps qu'il fait à un endroit donné."""
    print(f"Getting weather for {location}")
    return f"The weather in {location} is sunny"

tool = FunctionTool.from_defaults(
    get_weather,
    name="my_weather_tool",
    description="Useful for getting the weather for a given location.",
)
tool.call("New York")
```

> [!TIP]
> Lors de l'utilisation d'un agent ou d'un LLM avec l'appel de fonctions, l'outil sélectionné (et les arguments écrits pour celui-ci) dépendent fortement de son nom, t de la description du but et des arguments. Apprenez-en plus sur l'appel de fonctions dans le Guide sur l'appel de fonctions.

## Créer un *QueryEngineTool*

Le `QueryEngine` que nous avons défini dans l'unité précédente peut être facilement transformé en un outil en utilisant la classe `QueryEngineTool`.
Voyons comment créer un `QueryEngineTool` à partir d'un `QueryEngine` dans l'exemple ci-dessous.

```python
from llama_index.core import VectorStoreIndex
from llama_index.core.tools import QueryEngineTool
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.vector_stores.chroma import ChromaVectorStore

embed_model = HuggingFaceEmbedding("BAAI/bge-small-en-v1.5")

db = chromadb.PersistentClient(path="./alfred_chroma_db")
chroma_collection = db.get_or_create_collection("alfred")
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)

index = VectorStoreIndex.from_vector_store(vector_store, embed_model=embed_model)

llm = HuggingFaceInferenceAPI(model_name="Qwen/Qwen2.5-Coder-32B-Instruct")
query_engine = index.as_query_engine(llm=llm)
tool = QueryEngineTool.from_defaults(query_engine, name="some useful name", description="some useful description")
```

## Créer un *Toolspecs*

Pensez au `ToolSpecs` comme des collections d'outils qui fonctionnent ensemble harmonieusement à l'instar d'une boîte à outils professionnelle bien organisée.
Tout comme la boîte à outils d'un mécanicien contient des outils complémentaires qui fonctionnent ensemble pour les réparations de véhicules, un `ToolSpec` combine des outils apparentés pour des objectifs spécifiques.
Par exemple, le `ToolSpec` d'un agent comptable pourrait intégrer élégamment des capacités de tableur, des fonctionnalités email et des outils de calcul pour gérer les tâches financières avec précision et efficacité.

Installer le Toolspec Google
Comme introduit dans la section sur le LlamaHub, nous pouvons installer le toolspec Google avec la commande suivante :

```python
pip install llama-index-tools-google
```

Et maintenant nous pouvons charger le *toolspec* et le convertir en une liste d'outils.

```python
from llama_index.tools.google import GmailToolSpec

tool_spec = GmailToolSpec()
tool_spec_list = tool_spec.to_tool_list()
```

Pour obtenir une vue plus détaillée des outils, nous pouvons examiner les `metadata` de chacun d'eux.

```python
[(tool.metadata.name, tool.metadata.description) for tool in tool_spec_list]
```

### *Model Context Protocol* (MCP) dans LlamaIndex

LlamaIndex permet également d'utiliser les outils *MCP* grâce à un [*ToolSpec* sur le LlamaHub](https://llamahub.ai/l/tools/llama-index-tools-mcp?from=).
Vous pouvez simplement lancer un serveur MCP et commencer à l'utiliser grâce à l'implémentation suivante.

Si vous voulez approfondir MCP, vous pouvez consulter notre [cours gratuit](https://huggingface.co/learn/mcp-course/). 

Installer le Toolspec MCP
Comme introduit dans la section sur le LlamaHub, nous pouvons installer le toolspec MCP avec la commande suivante :

```python
pip install llama-index-tools-mcp
```

```python
from llama_index.tools.mcp import BasicMCPClient, McpToolSpec

# Nous considérons qu'un serveur mcp tourne sur 127.0.0.1:8000, ou vous pouvez utiliser le client mcp pour vous connecter à votre propre serveur
mcp_client = BasicMCPClient("http://127.0.0.1:8000/sse")
mcp_tool = McpToolSpec(client=mcp_client)

# obtenir l'agent
agent = await get_agent(mcp_tool)

# créer le contexte de l'agent
agent_context = Context(agent)
```

## *Utility Tools*

Souvent, interroger directement une API **peut retourner une quantité excessive de données**, dont certaines peuvent être non pertinentes, déborder la fenêtre de contexte du LLM, ou augmenter inutilement le nombre de *tokens* que vous utilisez.
Passons en revue nos deux *utility tools* principaux ci-dessous.

1. `OnDemandToolLoader` : Cet outil transforme n'importe quel chargeur de données LlamaIndex existant (classe *BaseReader*) en un outil qu'un agent peut utiliser. Il peut être appelé avec tous les paramètres nécessaires pour déclencher `load_data` du chargeur de données, ainsi qu'une chaîne de requête en langage naturel. Pendant l'exécution, nous chargeons d'abord les données du chargeur de données, les indexons (par exemple avec un *vector store*), puis les interrogeons à la demande. Ces trois étapes se produisent dans un seul appel de l'outil.
2. `LoadAndSearchToolSpec` : Le *LoadAndSearchToolSpec* prend n'importe quel outil existant en entrée. En tant que *tool spec*, il implémente `to_tool_list`, et quand cette fonction est appelée, deux outils sont retournés : un outil de chargement puis un outil de recherche. L'exécution du premier appellerait l'outil sous-jacent, puis indexerait la sortie (par défaut avec un *vector index*). L'exécution du second prendrait une chaîne de requête en entrée et appellerait l'index sous-jacent.

> [!TIP]
> Vous pouvez trouver des toolspecs et des utility tools sur le LlamaHub.

Maintenant que nous comprenons les bases des agents et des outils dans LlamaIndex, voyons comment nous pouvons **utiliser LlamaIndex pour créer des *workflows* configurables et gérables !**

### Table des matières
https://huggingface.co/learn/agents-course/fr/unit2/llama-index/README.md

# Table des matières

Ce plan de chapitre LlamaIndex fait partie de l'unité 2 du cours. Vous pouvez accéder à l'unité 2 sur LlamaIndex sur hf.co/learn 👉 ici

| Titre | Description |
| --- | --- |
| [Introduction](introduction.mdx) | Introduction à LlamaIndex |
| [LlamaHub](llama-hub.mdx) | LlamaHub : un registre d'intégrations, d'agents et de tools |
| [Components](components.mdx) | Components : les blocs de construction des workflows |
| [Tools](tools.mdx) | Tools : comment construire des tools dans LlamaIndex |
| [Quiz 1](quiz1.mdx) | Quiz 1 |
| [Agents](agents.mdx) | Agents : comment construire des agents dans LlamaIndex |
| [Workflows](workflows.mdx) | Workflows : une séquence d'étapes, d'événements composés de components qui sont exécutés dans l'ordre |
| [Quiz 2](quiz2.mdx) | Quiz 2 |
| [Conclusion](conclusion.mdx) | Conclusion |

### Utiliser les agents dans LlamaIndex
https://huggingface.co/learn/agents-course/fr/unit2/llama-index/agents.md

# Utiliser les agents dans LlamaIndex

Vous vous souvenez d'Alfred, notre agent majordome serviable d'avant ? Eh bien, il va recevoir une mise à niveau !
Maintenant que nous comprenons les outils disponibles dans LlamaIndex, nous pouvons lui donner de nouvelles capacités pour mieux nous servir.

Mais avant de continuer, rappelons-nous ce qui fait fonctionner un agent comme Alfred.
Dans l'Unité 1, nous avons appris que :

> Un agent est un système qui exploite un modèle d'IA pour interagir avec son environnement afin d'atteindre un objectif défini par l'utilisateur. Il combine le raisonnement, la planification et l'exécution d'actions (souvent via des outils externes) pour accomplir des tâches.

LlamaIndex prend en charge **trois types principaux d'agents avec raisonnement** :

![Agents](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/agents.png)

1. `Function Calling Agents` : Ceux-ci fonctionnent avec des modèles qui peuvent appeler des fonctions spécifiques.
2. `ReAct Agents` : Ceux-ci peuvent fonctionner avec n'importe quel modèle qui fait du *chat* ou des *endpoints* de texte et traiter des tâches de raisonnement complexes.
3. `Advanced Custom Agents` : Ceux-ci utilisent des méthodes plus complexes pour traiter des tâches et *workflows* plus complexes.

> [!TIP]
> Trouvez plus d'informations sur les agents avancés sur BaseWorkflowAgent.

## Initialiser les agents

> [!TIP]
> Vous pouvez suivre le code dans ce notebook que vous pouvez exécuter avec Google Colab.

Pour créer un agent, nous commençons par lui fournir un **ensemble de fonctions/outils qui définissent ses capacités**.
Regardons comment créer un agent avec quelques outils de base. Au moment de la rédaction, l'agent utilisera automatiquement l'API d'appel de fonctions (si disponible), ou une boucle d'agent ReAct standard.

Les LLM prennant en charge une API outils/fonctions sont relativement nouveaux, mais ils fournissent un moyen puissant d'appeler des outils en évitant de devoir utiliser un *prompt* spécifique et permettant au LLM de créer des appels d'outils basés sur des schémas fournis.

Les agents ReAct sont également bons pour les tâches de raisonnement complexes et peuvent fonctionner avec n'importe quel LLM qui a des capacités de chat ou de complétion de texte. Ils sont plus verbeux et montrent le raisonnement derrière certaines actions qu'ils prennent.

```python
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI
from llama_index.core.agent.workflow import AgentWorkflow
from llama_index.core.tools import FunctionTool

# define example de Tool -- type annotations, noms de fonctions, et docstrings, sont tous inclus dans les schémas analysés !
def multiply(a: int, b: int) -> int:
    """Multiplies two integers and returns the resulting integer"""
    return a * b

# initialisation du llm
llm = HuggingFaceInferenceAPI(model_name="Qwen/Qwen2.5-Coder-32B-Instruct")

# initialisation de l'agent
agent = AgentWorkflow.from_tools_or_functions(
    [FunctionTool.from_defaults(multiply)],
    llm=llm
)
```

**Les agents sont sans état par défaut**, ajouter la mémorisation des interactions passées est optionnel en utilisant un objet `Context`.
Cela pourrait être utile si vous voulez utiliser un agent qui a besoin de se souvenir des interactions précédentes, comme un *chatbot* qui maintient le contexte à travers plusieurs messages ou un gestionnaire de tâches qui a besoin de suivre les progrès au fil du temps.

```python
# sans état
response = await agent.run("What is 2 times 2?")

# se souvenir de l'état
from llama_index.core.workflow import Context

ctx = Context(agent)

response = await agent.run("My name is Bob.", ctx=ctx)
response = await agent.run("What was my name again?", ctx=ctx)
```

Vous remarquerez que les agents dans `LlamaIndex` sont asynchrones car ils utilisent l'opérateur `await` de Python. Si vous débuté avec le code asynchrone en Python, ou avez besoin d'un rappel, LlamaIndex dispose d'un [excellent guide sur le sujet](https://docs.llamaindex.ai/en/stable/getting_started/async_python/).

Maintenant que nous avons les bases, jetons un coup d'œil à comment nous pouvons utiliser des outils plus complexes dans nos agents.

## Créer des agents de RAG avec des *QueryEngineTools*

**Le RAG agentique est un moyen puissant d'utiliser des agents pour répondre à des questions sur vos données.** Nous pouvons passer divers outils à Alfred pour l'aider à répondre aux questions.
Cependant, au lieu de répondre automatiquement à la question au-dessus des documents, Alfred peut décider d'utiliser n'importe quel autre outil ou flux pour répondre à la question.

![Agentic RAG](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/agentic-rag.png)

Il est facile d'**envelopper `QueryEngine` comme un outil** pour un agent.
Ce faisant, nous devons **définir un nom et une description**. Le LLM utilisera ces informations pour utiliser correctement l'outil.
Voyons comment charger un `QueryEngineTool` en utilisant le `QueryEngine` que nous avons créé dans la [section des *components*](components).

```python
from llama_index.core.tools import QueryEngineTool

query_engine = index.as_query_engine(llm=llm, similarity_top_k=3) # comme indiqué dans la section Composants de LlamaIndex

query_engine_tool = QueryEngineTool.from_defaults(
    query_engine=query_engine,
    name="name",
    description="a specific description",
    return_direct=False,
)
query_engine_agent = AgentWorkflow.from_tools_or_functions(
    [query_engine_tool],
    llm=llm,
    system_prompt="You are a helpful assistant that has access to a database containing persona descriptions."
)
```

## Créer des systèmes multi-agents

La classe `AgentWorkflow` prend également en charge directement les systèmes multi-agents. En donnant à chaque agent un nom et une description, le système maintient un seul orateur actif, chaque agent ayant la capacité de passer le relais à un autre agent.

En rétrécissant la portée de chaque agent, nous pouvons aider à augmenter leur précision générale lors de la réponse aux messages des utilisateurs.

**Les agents dans LlamaIndex peuvent également être directement utilisés comme outils** pour d'autres agents, pour des scénarios plus complexes et personnalisés.

```python
from llama_index.core.agent.workflow import (
    AgentWorkflow,
    FunctionAgent,
    ReActAgent,
)

# Définir quelques outils
def add(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b

def subtract(a: int, b: int) -> int:
    """Subtract two numbers."""
    return a - b

# Créer les configurations de l'agent
# NOTE : nous pouvons utiliser FunctionAgent ou ReActAgent ici.
# FunctionAgent fonctionne pour les LLM avec une API d'appel de fonction.
# ReActAgent fonctionne pour n'importe quel LLM.
calculator_agent = ReActAgent(
    name="calculator",
    description="Performs basic arithmetic operations",
    system_prompt="You are a calculator assistant. Use your tools for any math operation.",
    tools=[add, subtract],
    llm=llm,
)

query_agent = ReActAgent(
    name="info_lookup",
    description="Looks up information about XYZ",
    system_prompt="Use your tool to query a RAG system to answer information about XYZ",
    tools=[query_engine_tool],
    llm=llm
)

# Créer et exécuter le workflow
agent = AgentWorkflow(
    agents=[calculator_agent, query_agent], root_agent="calculator"
)

# Exécuter le système
response = await agent.run(user_msg="Can you add 5 and 3?")
```

> [!TIP]
> Vous n'avez pas encore assez appris ? Il y a beaucoup plus à découvrir sur les agents et les outils dans LlamaIndex dans l'Introduction de base à AgentWorkflow ou le Guide d'apprentissage sur les agents, où vous pouvez lire plus sur le streaming, la sérialisation de contexte, et l'humain dans la boucle !

Maintenant que nous comprenons les bases des agents et des outils dans LlamaIndex, voyons comment nous pouvons utiliser LlamaIndex pour **créer des *workflows* configurables et gérables !**

### Introduction à LlamaIndex
https://huggingface.co/learn/agents-course/fr/unit2/llama-index/introduction.md

# Introduction à LlamaIndex

Bienvenue dans ce module, où vous allez apprendre à créer des agents propulsés par des LLM en utilisant la boîte à outils [LlamaIndex](https://www.llamaindex.ai/).

LlamaIndex est **une boîte à outils complète pour créer des agents sur vos données en utilisant des index et des *workflows***. Pour ce cours, nous nous concentrerons sur trois parties principales qui aident à construire des agents dans LlamaIndex : **les *Components***, **les *Agents* et *Tools*** et **les *Workflows***.

![LlamaIndex](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/thumbnail.png)

Examinons ces parties clés de LlamaIndex et comment elles aident avec les agents :

- **Les *Components*** : Ce sont les blocs de construction de base que vous utilisez dans LlamaIndex. Ceux-ci incluent des éléments comme les *prompts*, les modèles et les bases de données. Les *components* aident souvent à connecter LlamaIndex avec d'autres outils et bibliothèques.
- **Les *Tools*** : Les *tools* sont des *components* qui fournissent des capacités spécifiques comme la recherche, le calcul ou l'accès à des services externes. Ce sont les blocs de construction qui permettent aux agents d'effectuer des tâches.
- **Les *Agents*** : Les *agents* sont des *components* autonomes qui peuvent utiliser des *tools* et prendre des décisions. Ils coordonnent l'utilisation des *tools* pour accomplir des objectifs complexes.
- **Les *Workflows*** : Ce sont des processus étape par étape qui traitent la logique ensemble. Les *workflows* ou *agentic workflows* sont un moyen de structurer le comportement agentique sans l'utilisation explicite d'*agents*.

## Qu'est-ce qui rend LlamaIndex spécial ?

Bien que LlamaIndex fasse certaines choses similaires à d'autres *frameworks* comme *smolagents*, il présente quelques avantages clés :

- **Système de *workflow* clair** : Les *workflows* aident à décomposer comment les agents doivent prendre des décisions étape par étape en utilisant une syntaxe événementielle et asynchrone. Cela vous aide à composer et organiser clairement votre logique.
- ***Parsing* avancé de documents avec *LlamaParse*** : *LlamaParse* a été créé spécifiquement pour LlamaIndex, donc l'intégration est transparente, bien que ce soit une fonctionnalité payante.
- **Nombreux *Components* prêts à l'emploi** : LlamaIndex existe depuis un certain temps, donc il fonctionne avec beaucoup d'autres *frameworks*. Cela signifie qu'il dispose de nombreux *components* testés et fiables, comme des LLM, des *retrievers*, des index, et plus encore.
- ***LlamaHub*** : est un registre de centaines de ces *components*, *agents* et *outils* que vous pouvez utiliser dans LlamaIndex.

Tous ces concepts sont nécessaires dans différents scénarios pour créer des agents utiles.
Dans les sections suivantes, nous examinerons chacun de ces concepts en détail.
Après avoir acquis la maîtrise les concepts, nous utiliserons nos nouvelles connaissances pour **créer des cas d'usage appliqués avec Alfred l'agent** !

Prendre en main sur LlamaIndex est excitant, n'est-ce pas ? Alors, qu'attendons-nous ? Commençons par **trouver et installer les intégrations dont nous avons besoin en utilisant *LlamaHub* ! 🚀**

### Quiz rapide 1 (non noté) [[quiz1]]
https://huggingface.co/learn/agents-course/fr/unit2/llama-index/quiz1.md

# Quiz rapide 1 (non noté) [[quiz1]]

Jusqu'à présent, nous avons discuté des *components* et outils clés utilisés dans LlamaIndex.
Il est temps de faire un petit quiz, car **se tester soi-même** est la meilleure façon d'apprendre et [d'éviter l'illusion de compétence](https://www.coursera.org/lecture/learning-how-to-learn/illusions-of-competence-BuFzf).
Cela vous aidera à trouver **où vous devez renforcer vos connaissances**.

Ceci est un quiz optionnel et il n'est pas noté.

### Q1 : Qu'est-ce qu'un `QueryEngine` ?
Laquelle des affirmations suivantes décrit le mieux un *component* `QueryEngine` ?

QueryEngine doit être capable de récupérer et traiter des informations pertinentes.",
},
{
text: "Un component qui trouve et récupère des informations pertinentes dans le cadre du processus de RAG.",
explain: "Cela capture l'objectif principal d'un QueryEngine.",
correct: true
},
{
text: "Un outil qui ne fait que stocker des embeddings vectoriels sans fonctionnalité de recherche.",
explain: "Un QueryEngine fait plus que simplement stocker des embeddings ; il recherche et récupère activement des informations.",
},
{
text: "Un component qui évalue uniquement la qualité des réponses.",
explain: "L'évaluation est séparée de l'objectif principal de récupération du QueryEngine.",
}
]}
/>

---

### Q2 : Quel est le but des `FunctionTools` ?
Pourquoi les FunctionTools sont-ils importants pour un agent ?

FunctionTools ne sont pas principalement pour le stockage de données.",
},
{
text: "Pour convertir des fonctions Python en outils qu'un agent peut utiliser.",
explain: "Les FunctionTools enveloppent les fonctions Python pour les rendre accessibles aux agents.",
correct: true
},
{
text: "Pour permettre aux agents de créer des définitions de fonctions aléatoires.",
explain: "Les FunctionTools servent l'objectif spécifique de rendre les fonctions disponibles aux agents.",
},
{
text: "Pour traiter uniquement des données textuelles.",
explain: "Les FunctionTools peuvent fonctionner avec différents types de fonctions, pas seulement le traitement de texte.",
}
]}
/>

---

### Q3 : Que sont les `Toolspecs` dans LlamaIndex ?
Quel est l'objectif principal des `Toolspecs` ?

Toolspecs servent un objectif important dans l'écosystème LlamaIndex.",
},
{
text: "Ce sont des ensembles d'outils créés par la communauté qui étendent les capacités des agents.",
explain: "Les Toolspecs permettent à la communauté de partager et réutiliser des outils.",
correct: true
},
{
text: "Ils sont utilisés uniquement pour la gestion mémoire.",
explain: "Les Toolspecs concernent la fourniture d'outils, pas la gestion mémoire.",
},
{
text: "Ils ne fonctionnent qu'avec le traitement de texte.",
explain: "Les Toolspecs peuvent inclure différents types d'outils, pas seulement le traitement de texte.",
}
]}
/>

---

### Q4 : Qu'est-ce qui est requis pour créer un outil ?
Quelles informations doivent être incluses lors de la création d'un outil ?

docstring.",
},
{
text: "Seul le nom est requis.",
explain: "Une fonction et une description/docstring sont également requises pour une documentation appropriée de l'outil.",
},
{
text: "Seule la description est requise.",
explain: "Une fonction est requise pour que nous ayons du code à exécuter quand un agent sélectionne un outil",
},
{
text: "Seule la fonction est requise.",
explain: "Le nom et la description sont par défaut le nom et la docstring de la fonction fournie",
correct: true
}
]}
/>

---

Félicitations pour avoir terminé ce Quiz 🥳, si vous avez manqué certains éléments, prenez le temps de relire le chapitre pour renforcer vos connaissances. Si vous le réussissez, vous êtes prêt à plonger plus profondément dans la construction avec ces *components* !

### Que sont les *components* dans LlamaIndex ?
https://huggingface.co/learn/agents-course/fr/unit2/llama-index/components.md

# Que sont les *components* dans LlamaIndex ?

Vous vous souvenez d'Alfred, notre agent majordome serviable de l'Unité 1 ?
Pour nous aider efficacement, Alfred doit comprendre nos demandes et **préparer, trouver et utiliser les informations pertinentes pour aider à accomplir les tâches.**
C'est là que les *components* de LlamaIndex entrent en jeu.

Bien que LlamaIndex ait de nombreux *components*, **nous nous concentrerons spécifiquement sur le *component* `QueryEngine`.**
Pourquoi ? Parce qu'il peut être utilisé comme un outil de *Retrieval-Augmented Generation* (RAG) pour un agent.

Alors, qu'est-ce que le RAG ? Les LLM sont entraînés sur d'énormes corpus de données pour apprendre les connaissances générales.
Cependant, ils peuvent ne pas être entraînés sur des données pertinentes et à jour.
Le RAG résout ce problème en trouvant et récupérant des informations pertinentes de vos données et en les donnant au LLM.

![RAG](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/rag.png)

Maintenant, pensez à comment Alfred fonctionne :

1. Vous demandez à Alfred d'aider à planifier un dîner
2. Alfred doit vérifier votre calendrier, vos préférences alimentaires et les menus précédents réussis
3. Le `QueryEngine` aide Alfred à trouver ces informations et à les utiliser pour planifier le dîner

Cela fait du `QueryEngine` **un *component* clé pour construire des *workflows* de RAG agentiques** dans LlamaIndex.
Tout comme Alfred a besoin de rechercher dans les informations de votre maison pour être utile, tout agent a besoin d'un moyen de trouver et comprendre des données pertinentes.
Le `QueryEngine` fournit exactement cette capacité.

Maintenant, approfondissons un peu les *components* et voyons comment vous pouvez **combiner les *components* pour créer un pipeline de RAG.**

## Créer un pipeline de RAG en utilisant des *components*

> [!TIP]
> Vous pouvez suivre le code dans ce notebook que vous pouvez exécuter avec Google Colab.

Il y a cinq étapes clés dans le RAG, qui feront partie de la plupart des applications plus ambitieuses que vous construirez. A savoir :

1. **Chargement** : cela se réfère à obtenir vos données d'où elles vivent (qu'il s'agisse de fichiers texte, de PDF, d'un autre site web, d'une base de données, ou d'une API) dans votre *workflow*. *LlamaHub* fournit des centaines d'intégrations parmi lesquelles choisir.
2. **Indexation** : cela signifie créer une structure de données qui permet d'interroger les données. Pour les LLM, cela signifie presque toujours créer des *embeddings* vectoriels. Ce sont des représentations numériques de la signification des données. L'indexation peut également se référer à de nombreuses autres stratégies de métadonnées pour faciliter la recherche de données contextuellement pertinentes basées sur les propriétés.
3. **Stockage** : une fois vos données indexées, vous voudrez stocker votre index, ainsi que d'autres métadonnées, pour éviter de devoir ré-indexer à chaque utilisation.
4. **Requête** : pour toute stratégie d'indexation donnée, il y a de nombreuses façons d'utiliser les LLM et les structures de données LlamaIndex pour faire des requêtes, incluant des sous-requêtes, des requêtes multi-étapes et des stratégies hybrides.
5. **Évaluation** : une étape critique dans tout flux est de vérifier son efficacité par rapport à d'autres stratégies, ou lorsque vous apportez des modifications. L'évaluation fournit des mesures objectives de la précision, de la fidélité et de la rapidité de vos réponses aux requêtes.

Ensuite, voyons comment nous pouvons reproduire ces étapes en utilisant des *components*.

### Chargement et intégration des documents

Comme mentionné précédemment, LlamaIndex peut fonctionner au-dessus de vos propres données, cependant, **avant d'accéder aux données, nous devons les charger.**
Il y a trois façons principales de charger des données dans LlamaIndex :

1. `SimpleDirectoryReader` : Un chargeur de données intégré pour divers types de fichiers d'un répertoire local.
2. `LlamaParse` : L'outil officiel de LlamaIndex pour l'analyse de PDF, disponible comme une API gérée.
3. `LlamaHub` : Un registre de centaines de bibliothèques de chargement de données pour ingérer des données de n'importe quelle source.

> [!TIP]
> Familiarisez-vous avec les chargeurs de données LlamaHub et le parser LlamaParse pour des sources de données plus complexes.

**La façon la plus simple de charger des données est avec `SimpleDirectoryReader`.**
Ce *component* polyvalent peut charger divers types de fichiers d'un dossier et les convertir en objets `Document` avec lesquels LlamaIndex peut travailler.
Voyons comment nous pouvons utiliser `SimpleDirectoryReader` pour charger des données d'un dossier.

```python
from llama_index.core import SimpleDirectoryReader

reader = SimpleDirectoryReader(input_dir="path/to/directory")
documents = reader.load_data()
```

Après avoir chargé nos documents, nous devons les diviser en plus petites parties appelées objets `Node`.
Un `Node` est juste un morceau de texte du document original qui est plus facile à traiter pour l'IA, tout en conservant des références à l'objet `Document` original.

L'`IngestionPipeline` nous aide à créer ces *nodes* grâce à deux transformations clés.
1. `SentenceSplitter` divise les documents en morceaux aux niveaux des phrases.
2. `HuggingFaceEmbedding` convertit chaque morceau en *embeddings* numériques.

Ce processus nous aide à organiser nos documents d'une manière qui est plus utile pour la recherche et l'analyse.

```python
from llama_index.core import Document
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core.ingestion import IngestionPipeline

# créer le pipeline avec les transformations
pipeline = IngestionPipeline(
    transformations=[
        SentenceSplitter(chunk_overlap=0),
        HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5"),
    ]
)

nodes = await pipeline.arun(documents=[Document.example()])
```

### Stockage et indexation des documents

Après avoir créé nos objets `Node`, nous devons les indexer pour les rendre recherchables, mais avant de pouvoir le faire, nous avons besoin d'un endroit pour stocker nos données.

Puisque nous utilisons un pipeline d'ingestion, nous pouvons directement attacher un *vector store* au pipeline pour le remplir.
Dans ce cas, nous utiliserons `Chroma` pour stocker nos documents.

Installer ChromaDB

Comme introduit dans la section sur le LlamaHub, nous pouvons installer le *vector store* ChromaDB avec la commande suivante :

```bash
pip install llama-index-vector-stores-chroma
```

```python
import chromadb
from llama_index.vector_stores.chroma import ChromaVectorStore

db = chromadb.PersistentClient(path="./alfred_chroma_db")
chroma_collection = db.get_or_create_collection("alfred")
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)

pipeline = IngestionPipeline(
    transformations=[
        SentenceSplitter(chunk_size=25, chunk_overlap=0),
        HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5"),
    ],
    vector_store=vector_store,
)
```

> [!TIP]
> Un aperçu des différents vector stores peut être trouvé dans la documentation de LlamaIndex.

C'est là que les *embeddings* vectoriels entrent en jeu. En enchassant à la fois la requête et les *nodes* dans le même espace vectoriel, nous pouvons trouver des correspondances pertinentes.
Le `VectorStoreIndex` s'occupe de cela pour nous, en utilisant le même modèle d'*embedding* que nous avons utilisé pendant l'ingestion pour assurer la cohérence.

Voyons comment créer cet index à partir de notre *vector store* et des *embeddings* :

```python
from llama_index.core import VectorStoreIndex
from llama_index.embeddings.huggingface import HuggingFaceEmbedding

embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5")
index = VectorStoreIndex.from_vector_store(vector_store, embed_model=embed_model)
```

Toutes les informations sont automatiquement persistées dans l'objet `ChromaVectorStore` et le chemin du répertoire passé.

Parfait ! Maintenant que nous pouvons sauvegarder et charger notre index facilement, explorons comment l'interroger de différentes manières.

### Interroger un *VectorStoreIndex* avec des *prompts* et des LLM

Avant de pouvoir interroger notre index, nous devons le convertir en interface de requête. Les options de conversion les plus courantes sont :

- `as_retriever` : Pour la récupération basique de documents, retournant une liste d'objets `NodeWithScore` avec des scores de similarité
- `as_query_engine` : Pour les interactions question-réponse simples, retournant une réponse écrite
- `as_chat_engine` : Pour les interactions conversationnelles qui maintiennent la mémoire à travers plusieurs messages, retournant une réponse écrite utilisant l'historique de chat et le contexte indexé

Nous nous concentrerons sur le *query engine* car il est plus commun pour les interactions de type agent.
Nous passons également un LLM au *query engine* à utiliser pour la réponse.

```python
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI

llm = HuggingFaceInferenceAPI(model_name="Qwen/Qwen2.5-Coder-32B-Instruct")
query_engine = index.as_query_engine(
    llm=llm,
    response_mode="tree_summarize",
)
query_engine.query("What is the meaning of life?")
# Le sens de la vie est 42
```

### Traitement des réponses

Sous le capot, le *query engine* n'utilise pas seulement le LLM pour répondre à la question mais utilise également un `ResponseSynthesizer` comme stratégie pour traiter la réponse.
Encore une fois, c'est entièrement personnalisable mais il y a trois stratégies principales qui fonctionnent bien prêtes à l'emploi :

- `refine` : créer et affiner une réponse en parcourant séquentiellement chaque morceau de texte récupéré. Cela fait un appel au LLM séparé pour chaque *Node*/morceau récupéré.
- `compact` (par défaut) : similaire à l'affinement mais concaténant les morceaux au préalable, résultant en moins d'appels au LLM.
- `tree_summarize` : créer une réponse détaillée en parcourant chaque morceau de texte récupéré et créant une structure d'arbre de la réponse.

> [!TIP]
> Prenez un contrôle fin de vos workflows de requête avec l'API de composition de bas niveau. Cette API vous permet de personnaliser et de finetuner chaque étape du processus de requête pour correspondre à vos besoins exacts, ce qui se marie également très bien avec les Workflows.

Le modèle de langage ne performera pas toujours de manière prévisible, donc nous ne pouvons pas être sûrs que la réponse que nous obtenons soit toujours correcte. Nous pouvons gérer cela en **évaluant la qualité de la réponse**.

### Évaluation et observabilité

LlamaIndex fournit **des outils d'évaluation intégrés pour évaluer la qualité des réponses.**
Ces évaluateurs exploitent les LLM pour analyser les réponses à travers différentes dimensions.
Regardons les trois évaluateurs principaux disponibles :

- `FaithfulnessEvaluator` : Évalue la fidélité de la réponse en vérifiant si la réponse est supportée par le contexte.
- `AnswerRelevancyEvaluator` : Évalue la pertinence de la réponse en vérifiant si la réponse est pertinente par rapport à la question.
- `CorrectnessEvaluator` : Évalue la correction de la réponse en vérifiant si la réponse est correcte.

> [!TIP]
> Vous voulez en savoir plus sur l'observabilité et l'évaluation des agents ? Suivez l'Unité Bonus 2.

```python
from llama_index.core.evaluation import FaithfulnessEvaluator

query_engine = # de la section précédente
llm = # de la section précédente

# index des requêtes
evaluator = FaithfulnessEvaluator(llm=llm)
response = query_engine.query(
    "What battles took place in New York City in the American Revolution?"
)
eval_result = evaluator.evaluate_response(response=response)
eval_result.passing
```

Même sans évaluation directe, nous pouvons **obtenir des informations sur la performance de notre système grâce à l'observabilité.**
Ceci est particulièrement utile quand nous construisons des *workflows* plus complexes et que nous voulons comprendre comment chaque *component* performe.

Installer LlamaTrace

Comme introduit dans la section sur le LlamaHub, nous pouvons installer le *callback* LlamaTrace d'Arize Phoenix avec la commande suivante :

```bash
pip install -U llama-index-callbacks-arize-phoenix
```

De plus, nous devons définir la variable d'environnement `PHOENIX_API_KEY` avec notre clé API LlamaTrace. Nous pouvons l'obtenir en :
- Créant un compte sur [LlamaTrace](https://llamatrace.com/login)
- Générant une clé API dans les paramètres de votre compte
- Utilisant la clé API dans le code ci-dessous pour activer le *tracking*

```python
import llama_index
import os

PHOENIX_API_KEY = ""
os.environ["OTEL_EXPORTER_OTLP_HEADERS"] = f"api_key={PHOENIX_API_KEY}"
llama_index.core.set_global_handler(
    "arize_phoenix",
    endpoint="https://llamatrace.com/v1/traces"
)
```

> [!TIP]
> Vous voulez en savoir plus sur les components et comment les utiliser ? Continuez votre parcours avec les Guides des components ou le Guide sur le RAG.

Nous avons vu comment utiliser les *components* pour créer un `QueryEngine`. Maintenant, voyons comment nous pouvons **utiliser le `QueryEngine` comme un outil pour un agent !**

### Introduction au *LlamaHub*
https://huggingface.co/learn/agents-course/fr/unit2/llama-index/llama-hub.md

# Introduction au *LlamaHub*

***LlamaHub* est un registre de centaines d'intégrations, d'*agents* et d'*outils* que vous pouvez utiliser dans LlamaIndex.**

![LlamaHub](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/llama-hub.png)

Nous utiliserons diverses intégrations dans ce cours, alors examinons d'abord le *LlamaHub* et comment il peut nous aider.

Voyons comment trouver et installer les dépendances pour les *components* dont nous avons besoin.

## Installation

Les instructions d'installation de LlamaIndex sont disponibles comme un **aperçu bien structuré sur [*LlamaHub*](https://llamahub.ai/)**.
Cela peut être un peu démoralisant au début, mais la plupart des **commandes d'installation suivent généralement un format facile à retenir** :

```bash
pip install llama-index-{component-type}-{framework-name}
```

Essayons d'installer les dépendances pour un *component* *LLM* et *embedding* en utilisant l'[intégration de l'API d'inférence *Hugging Face*](https://llamahub.ai/l/llms/llama-index-llms-huggingface-api?from=llms).

```bash
pip install llama-index-llms-huggingface-api llama-index-embeddings-huggingface
```

## Utilisation

Une fois installé, nous pouvons voir les motifs d'utilisation. Vous remarquerez que les chemins d'importation suivent la commande d'installation !
Ci-dessous, nous pouvons voir un exemple d'utilisation de **l'API d'inférence Hugging Face pour un *component* LLM**.

```python
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI
import os
from dotenv import load_dotenv

# Charger le fichier .env
load_dotenv()

# Récupérer HF_TOKEN à partir des variables d'environnement
hf_token = os.getenv("HF_TOKEN")

llm = HuggingFaceInferenceAPI(
    model_name="Qwen/Qwen2.5-Coder-32B-Instruct",
    temperature=0.7,
    max_tokens=100,
    token=hf_token,
)

response = llm.complete("Hello, how are you?")
print(response)
# Je suis bon, comment puis-je vous aider aujourd'hui ?
```

Parfait, nous savons maintenant comment trouver, installer et utiliser les intégrations pour les *components* dont nous avons besoin.
**Approfondissons les *components*** et voyons comment nous pouvons les utiliser pour construire nos propres agents.

### Systèmes multi-agents
https://huggingface.co/learn/agents-course/fr/unit2/smolagents/multi_agent_systems.md

# Systèmes multi-agents

Les systèmes multi-agents permettent à des **agents spécialisés de collaborer sur des tâches complexes**, améliorant la modularité, la scalabilité et la robustesse. Au lieu de s'appuyer sur un seul agent, les tâches sont distribuées entre des agents ayant des capacités distinctes.

Dans `smolagents`, différents agents peuvent être combinés pour générer du code Python, appeler des outils externes, effectuer des recherches web et plus encore. En orchestrant ces agents, nous pouvons créer des flux de travail puissants.

Une configuration typique pourrait inclure :
- Un **agent manageur** pour la délégation des tâches
- Un **agent interpréteur de code** pour l'exécution de code
- Un **agent de recherche web** pour la récupération d'informations

Le diagramme ci-dessous illustre une architecture multi-agents simple où un **agent manageur** coordonne un **outil interpréteur de code** et un **agent de recherche web**, qui à son tour utilise des outils comme `DuckDuckGoSearchTool` et `VisitWebpageTool` pour rassembler des informations pertinentes.

## Systèmes multi-agents en action

Un système multi-agents se compose de plusieurs agents spécialisés travaillant ensemble sous la coordination d'un **agent orchestrateur**. Cette approche permet des flux de travail complexes en distribuant les tâches entre des agents ayant des rôles distincts.

Par exemple, un **système de RAG multi-agents** peut intégrer :
- Un **agent web** pour naviguer sur internet.
- Un **agent récupérateur** pour récupérer des informations à partir de bases de connaissances.
- Un **agent de génération d'images** pour produire des visuels.

Tous ces agents opèrent sous un orchestrateur qui gère la délégation de tâches et l'interaction.

## Résoudre une tâche complexe avec une hiérarchie multi-agents

> [!TIP]
> Vous pouvez suivre le code dans ce notebook que vous pouvez exécuter avec Google Colab.

La réception approche ! Avec votre aide, Alfred a presque terminé les préparatifs.

Mais maintenant il y a un problème : la Batmobile a disparu. Alfred doit trouver une remplaçante, et la trouver rapidement.

Heureusement, quelques biographies ont été réalisées sur la vie de Bruce Wayne, alors peut-être qu'Alfred pourrait récupérer une voiture abandonnée sur l'un des plateaux de tournage et la reconditionner selon les normes modernes, ce qui inclurait certainement une option de conduite entièrement autonome.

Mais cela pourrait être n'importe où dans les lieux de tournage autour du monde ; pouvant être nombreux.

Donc Alfred nécessite votre aide. Pourriez-vous construire un agent capable de résoudre cette tâche ?

> 👉 Trouvez tous les lieux de tournage de Batman dans le monde, calculez le temps de transfert par cargo jusqu'à ces lieux, et représentez-les sur une carte, avec une couleur variant en fonction du temps de transfert par cargo. Représentez également quelques usines de supercars avec le même temps de transfert en cargo.

Construisons cela !

Cet exemple nécessite des packages supplémentaires, donc installons-les d'abord :

```bash
pip install 'smolagents[litellm]' plotly geopandas shapely kaleido -q
```

### Nous créons d'abord un outil pour obtenir le temps de transfert en avion cargo.

```python
import math
from typing import Optional, Tuple

from smolagents import tool

@tool
def calculate_cargo_travel_time(
    origin_coords: Tuple[float, float],
    destination_coords: Tuple[float, float],
    cruising_speed_kmh: Optional[float] = 750.0,  # Vitesse moyenne pour les avions cargo
) -> float:
    """
    Calcule le temps de voyage pour un avion cargo entre deux points sur Terre en utilisant la distance du grand cercle.

    Args:
        origin_coords: Tuple de (latitude, longitude) pour le point de départ
        destination_coords: Tuple de (latitude, longitude) pour la destination
        cruising_speed_kmh: Vitesse de croisière optionnelle en km/h (par défaut 750 km/h pour les avions cargo typiques)

    Returns:
        float: Le temps de voyage estimé en heures

    Example:
        >>> # Chicago (41.8781° N, 87.6298° W) vers Sydney (33.8688° S, 151.2093° E)
        >>> result = calculate_cargo_travel_time((41.8781, -87.6298), (-33.8688, 151.2093))
    """

    def to_radians(degrees: float) -> float:
        return degrees * (math.pi / 180)

    # Extraire les coordonnées
    lat1, lon1 = map(to_radians, origin_coords)
    lat2, lon2 = map(to_radians, destination_coords)

    # Rayon de la Terre en kilomètres
    EARTH_RADIUS_KM = 6371.0

    # Calculer la distance du grand cercle en utilisant la formule de haversine
    dlon = lon2 - lon1
    dlat = lat2 - lat1

    a = (
        math.sin(dlat / 2) ** 2
        + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2) ** 2
    )
    c = 2 * math.asin(math.sqrt(a))
    distance = EARTH_RADIUS_KM * c

    # Ajouter 10% pour tenir compte des routes non directes et des contrôles de trafic aérien
    actual_distance = distance * 1.1

    # Calculer le temps de vol
    # Ajouter 1 heure pour les procédures de décollage et d'atterrissage
    flight_time = (actual_distance / cruising_speed_kmh) + 1.0

    # Formater les résultats
    return round(flight_time, 2)

print(calculate_cargo_travel_time((41.8781, -87.6298), (-33.8688, 151.2093)))
```

### Configuration de l'agent

Pour le fournisseur de modèle, nous utilisons Together AI, l'un des nouveaux [fournisseurs d'inférence sur le Hub](https://huggingface.co/blog/inference-providers) !

Le `GoogleSearchTool` utilise l'[API Serper](https://serper.dev) pour rechercher sur le web, cela nécessite donc soit d'avoir configuré la variable d'environnement `SERPAPI_API_KEY` et de passer `provider="serpapi"` ou d'avoir `SERPER_API_KEY` et de passer `provider=serper`.

Si vous n'avez pas de fournisseur Serp API configuré, vous pouvez utiliser `DuckDuckGoSearchTool` mais attention il a une limite d'appels.

```python
import os
from PIL import Image
from smolagents import CodeAgent, GoogleSearchTool, InferenceClientModel, VisitWebpageTool

model = InferenceClientModel(model_id="Qwen/Qwen2.5-Coder-32B-Instruct", provider="together")
```

Nous pouvons commencer par créer un agent simple comme ligne de base pour nous donner un rapport simple.

```python
task = """Trouvez tous les lieux de tournage de Batman dans le monde, calculez le temps de transfert par avion cargo jusqu'ici (nous sommes à Gotham, 40.7128° N, 74.0060° W), et renvoyez-les moi sous la forme d'un dataframe pandas.
Donnez-moi aussi des usines de supercars avec le même temps de transfert par avion-cargo."""
```

```python
agent = CodeAgent(
    model=model,
    tools=[GoogleSearchTool("serper"), VisitWebpageTool(), calculate_cargo_travel_time],
    additional_authorized_imports=["pandas"],
    max_steps=20,
)
```

```python
result = agent.run(task)
```

```python
result
```

Dans notre cas, il génère cette sortie :

```python
|  | Location                                             | Travel Time to Gotham (hours) |
|--|------------------------------------------------------|------------------------------|
| 0  | Necropolis Cemetery, Glasgow, Scotland, UK         | 8.60                         |
| 1  | St. George's Hall, Liverpool, England, UK         | 8.81                         |
| 2  | Two Temple Place, London, England, UK             | 9.17                         |
| 3  | Wollaton Hall, Nottingham, England, UK           | 9.00                         |
| 4  | Knebworth House, Knebworth, Hertfordshire, UK    | 9.15                         |
| 5  | Acton Lane Power Station, Acton Lane, Acton, UK  | 9.16                         |
| 6  | Queensboro Bridge, New York City, USA            | 1.01                         |
| 7  | Wall Street, New York City, USA                  | 1.00                         |
| 8  | Mehrangarh Fort, Jodhpur, Rajasthan, India       | 18.34                        |
| 9  | Turda Gorge, Turda, Romania                      | 11.89                        |
| 10 | Chicago, USA                                     | 2.68                         |
| 11 | Hong Kong, China                                 | 19.99                        |
| 12 | Cardington Studios, Northamptonshire, UK        | 9.10                         |
| 13 | Warner Bros. Leavesden Studios, Hertfordshire, UK | 9.13                         |
| 14 | Westwood, Los Angeles, CA, USA                  | 6.79                         |
| 15 | Woking, UK (McLaren)                             | 9.13                         |
```

Nous pourrions déjà l'améliorer un peu en ajoutant des étapes de planification dédiées et en ajoutant davantage de *prompts*.

Les étapes de planification permettent à l'agent de penser à l'avance et planifier ses prochaines étapes, ce qui peut être utile pour des tâches plus complexes.

```python
agent.planning_interval = 4

detailed_report = agent.run(f"""
Vous êtes un analyste expert. Vous rédigez des rapports complets après avoir visité de nombreux sites web.
N'hésitez pas à effectuer plusieurs recherches à la fois dans une boucle for.
Pour chaque point de données que vous trouvez, visitez l'url source pour confirmer les chiffres.

{task}
""")

print(detailed_report)
```

```python
detailed_report
```

Dans notre cas, il génère cette sortie :

```python
|  | Location                                         | Travel Time (hours) |
|--|--------------------------------------------------|---------------------|
| 0  | Bridge of Sighs, Glasgow Necropolis, Glasgow, UK | 8.6                 |
| 1  | Wishart Street, Glasgow, Scotland, UK         | 8.6                 |
```

Grâce à ces changements rapides, nous avons obtenu un rapport beaucoup plus concis en fournissant simplement à notre agent un *prompt* détaillé, et en lui donnant des capacités de planification !

La fenêtre de contexte du modèle se remplit rapidement. Donc **si nous demandons à notre agent de combiner les résultats de recherche détaillée avec une autre, il sera plus lent et cela augmentera rapidement le nombre de *tokens* utilisés et donc les coûts**.

➡️ Nous devons améliorer la structure de notre système.

### ✌️ Diviser la tâche entre deux agents

Les structures multi-agents permettent de séparer les mémoires entre différentes sous-tâches, avec deux grands avantages :
- Chaque agent est davantage focalisé sur sa tâche principale, donc plus performant
- Séparer les mémoires réduit le nombre de *tokens* d'entrée à chaque étape, réduisant ainsi la latence et le coût.

Créons une équipe avec un agent de recherche web dédié, géré par un autre agent.

L'agent gestionnaire devrait avoir des capacités de traçage pour écrire son rapport final : donnons-lui donc accès à des importations supplémentaires, incluant `plotly`, et `geopandas` + `shapely` pour le traçage spatial.

```python
model = InferenceClientModel(
    "Qwen/Qwen2.5-Coder-32B-Instruct", provider="together", max_tokens=8096
)

web_agent = CodeAgent(
    model=model,
    tools=[
        GoogleSearchTool(provider="serper"),
        VisitWebpageTool(),
        calculate_cargo_travel_time,
    ],
    name="web_agent",
    description="Navigue sur le web pour trouver des informations",
    verbosity_level=0,
    max_steps=10,
)
```

L'agent gestionnaire aura besoin de faire du travail mental intensif.

Donc nous lui donnons le modèle plus puissant [DeepSeek-R1](https://huggingface.co/deepseek-ai/DeepSeek-R1), et ajoutons un `planning_interval` au mélange.

```python
from smolagents.utils import encode_image_base64, make_image_url
from smolagents import OpenAIServerModel

def check_reasoning_and_plot(final_answer, agent_memory):
    multimodal_model = OpenAIServerModel("gpt-4o", max_tokens=8096)
    filepath = "saved_map.png"
    assert os.path.exists(filepath), "Assurez-vous de sauvegarder le graphique sous saved_map.png !"
    image = Image.open(filepath)
    prompt = (
        f"Voici une tâche donnée par l'utilisateur et les étapes de l'agent : {agent_memory.get_succinct_steps()}. Maintenant voici le graphique qui a été fait."
        "Veuillez vérifier que le processus de raisonnement et le graphique sont corrects : répondent-ils correctement à la tâche donnée ?"
        "Listez d'abord les raisons pour lesquelles oui/non, puis écrivez votre décision finale : PASS en majuscules si c'est satisfaisant, FAIL si ce n'est pas le cas."
        "Ne soyez pas dur : si le graphique résout principalement la tâche, il devrait passer."
        "Pour passer, un graphique devrait être fait en utilisant px.scatter_map et non toute autre méthode (scatter_map a l'air plus joli)."
    )
    messages = [
        {
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text": prompt,
                },
                {
                    "type": "image_url",
                    "image_url": {"url": make_image_url(encode_image_base64(image))},
                },
            ],
        }
    ]
    output = multimodal_model(messages).content
    print("Feedback: ", output)
    if "FAIL" in output:
        raise Exception(output)
    return True

manager_agent = CodeAgent(
    model=InferenceClientModel("deepseek-ai/DeepSeek-R1", provider="together", max_tokens=8096),
    tools=[calculate_cargo_travel_time],
    managed_agents=[web_agent],
    additional_authorized_imports=[
        "geopandas",
        "plotly",
        "shapely",
        "json",
        "pandas",
        "numpy",
    ],
    planning_interval=5,
    verbosity_level=2,
    final_answer_checks=[check_reasoning_and_plot],
    max_steps=15,
)
```

Inspectons à quoi ressemble cette équipe :

```python
manager_agent.visualize()
```

Cela générera quelque chose comme ceci, nous aidant à comprendre la structure et les relations entre les agents et les outils utilisés :

```python
CodeAgent | deepseek-ai/DeepSeek-R1
├── ✅ Importations autorisées : ['geopandas', 'plotly', 'shapely', 'json', 'pandas', 'numpy']
├── 🛠️ Outils :
│   ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
│   ┃ Nom                         ┃ Description                           ┃ Arguments                             ┃
│   ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│   │ calculate_cargo_travel_time │ Calcule le temps de voyage pour un    │ origin_coords (`array`) : Tuple de    │
│   │                             │ avion cargo entre deux points sur     │ (latitude, longitude) pour le         │
│   │                             │ Terre en utilisant la distance du     │ point de départ                       │
│   │                             │ grand cercle.                          │ destination_coords (`array`) : Tuple  │
│   │                             │                                       │ de (latitude, longitude) pour la      │
│   │                             │                                       │ destination                           │
│   │                             │                                       │ cruising_speed_kmh (`number`) :       │
│   │                             │                                       │ Vitesse de croisière optionnelle      │
│   │                             │                                       │ en km/h (par défaut 750 km/h pour     │
│   │                             │                                       │ les avions cargo typiques)            │
│   │ final_answer                │ Fournit une réponse finale au problème │ answer (`any`) : La réponse finale    │
│   │                             │ donné.                                │ au problème                           │
│   └─────────────────────────────┴───────────────────────────────────────┴───────────────────────────────────────┘
└── 🤖 Agents gérés :
    └── web_agent | CodeAgent | Qwen/Qwen2.5-Coder-32B-Instruct
        ├── ✅ Importations autorisées : []
        ├── 📝 Description : Navigue sur le web pour trouver des informations
        └── 🛠️ Outils :
            ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
            ┃ Nom                         ┃ Description                       ┃ Arguments                         ┃
            ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
            │ web_search                  │ Effectue une recherche web Google │ query (`string`) : La requête de  │
            │                             │ pour votre requête puis retourne  │ recherche à effectuer.            │
            │                             │ une chaîne des meilleurs résultats │ filter_year (`integer`) :         │
            │                             │ de recherche.                      │ Optionnellement restreindre les   │
            │                             │                                   │ résultats à une certaine année    │
            │ visit_webpage               │ Visite une page web à l'URL donnée │ url (`string`) : L'URL de la page │
            │                             │ et lit son contenu comme une      │ web à visiter.                    │
            │                             │ chaîne markdown. Utilisez ceci    │                                   │
            │                             │ pour naviguer sur les pages web.  │                                   │
            │ calculate_cargo_travel_time │ Calcule le temps de voyage pour un │ origin_coords (`array`) : Tuple de │
            │                             │ avion cargo entre deux points sur │ (latitude, longitude) pour le     │
            │                             │ Terre en utilisant la distance du │ point de départ                   │
            │                             │ grand cercle.                      │ destination_coords (`array`) :    │
            │                             │                                   │ Tuple de (latitude, longitude)    │
            │                             │                                   │ pour la destination               │
            │                             │                                   │ cruising_speed_kmh (`number`) :   │
            │                             │                                   │ Vitesse de croisière optionnelle  │
            │                             │                                   │ en km/h (par défaut 750 km/h pour │
            │                             │                                   │ les avions cargo typiques)        │
            │ final_answer                │ Fournit une réponse finale au     │ answer (`any`) : La réponse finale │
            │                             │ problème donné.                   │ au problème                       │
            └─────────────────────────────┴───────────────────────────────────┴───────────────────────────────────┘
```

```python
manager_agent.run("""
Find all Batman filming locations in the world, calculate the time to transfer via cargo plane to here (we're in Gotham, 40.7128° N, 74.0060° W).
Also give me some supercar factories with the same cargo plane transfer time. You need at least 6 points in total.
Represent this as spatial map of the world, with the locations represented as scatter points with a color that depends on the travel time, and save it to saved_map.png!

Here's an example of how to plot and return a map:
import plotly.express as px
df = px.data.carshare()
fig = px.scatter_map(df, lat="centroid_lat", lon="centroid_lon", text="name", color="peak_hour", size=100,
     color_continuous_scale=px.colors.sequential.Magma, size_max=15, zoom=1)
fig.show()
fig.write_image("saved_image.png")
final_answer(fig)

Never try to process strings using code: when you have a string to read, just print it and you'll see it.
""")
```

Je ne sais pas comment cela s'est passé pour votre exécution, mais dans la mienne, l'agent gestionnaire a habilement divisé les tâches données à l'agent web en `1. Rechercher les lieux de tournage de Batman`, puis `2. Trouver les usines de supercars`, avant d'agréger les listes et de tracer la carte.

Voyons à quoi ressemble la carte en l'inspectant directement depuis l'état de l'agent :

```python
manager_agent.python_executor.state["fig"]
```

Cela affichera la carte :

![Exemple de sortie de carte du système multi-agents](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/smolagents/output_map.png)

## Ressources

- [Systèmes multi-agents](https://huggingface.co/docs/smolagents/main/en/examples/multiagents) – Aperçu des systèmes multi-agents.
- [Qu'est-ce que le RAG agentique ?](https://weaviate.io/blog/what-is-agentic-rag) – Introduction au RAG agentique.
- [Système RAG multi-agents 🤖🤝🤖 Recette](https://huggingface.co/learn/cookbook/multiagent_rag_system) – Guide étape par étape pour construire un système RAG multi-agents.

### Agents visuel avec smolagents
https://huggingface.co/learn/agents-course/fr/unit2/smolagents/vision_agents.md

# Agents visuel avec smolagents

> [!WARNING]
> Les exemples de cette section nécessitent l'accès à un modèle VLM puissant. Nous les avons testés en utilisant l'API GPT-4o.
> Cependant, Pourquoi utiliser smolagents discute des solutions alternatives supportées par smolagents et Hugging Face. Si vous souhaitez explorer d'autres options, assurez-vous de consulter cette section.

Doter les agents de capacités visuelles est crucial pour résoudre des tâches qui vont au-delà du traitement de texte. De nombreux défis du monde réel, comme la navigation web ou la compréhension de documents, nécessitent d'analyser un contenu visuel riche. Heureusement, `smolagents` fournit un support intégré pour les modèles de vision-langage (VLM), permettant aux agents de traiter et d'interpréter efficacement les images.

Dans cet exemple, imaginez qu'Alfred soit chargé de vérifier les identités des invités assistant à la fête. Comme vous pouvez l'imaginer, Alfred pourrait ne pas être familier avec tout le monde. Pour l'aider, nous pouvons utiliser un agent qui vérifie leur identité en recherchant des informations visuelles sur leur apparence en utilisant un VLM. Cela permettra à Alfred de prendre des décisions éclairées sur qui peut entrer. Construisons cet exemple !

## Fournir des images au début de l'exécution de l'agent

> [!TIP]
> Vous pouvez suivre le code dans ce notebook que vous pouvez exécuter avec Google Colab.

Dans cette approche, les images sont transmises à l'agent au début et stockées comme `task_images` avec le *prompt* de tâche. L'agent traite ensuite ces images tout au long de son exécution.

Considérez le cas où Alfred veut vérifier les identités des super-héros assistant à la fête. Il a déjà un jeu de données d'images de fêtes précédentes avec les noms des invités. Étant donné l'image d'un nouveau visiteur, l'agent peut la comparer avec le jeu de données existant et prendre une décision sur leur entrée.

Dans ce cas, un invité essaie d'entrer, et Alfred soupçonne que ce visiteur pourrait être le Joker se faisant passer pour Wonder Woman. Alfred doit vérifier les identités pour empêcher quiconque d'indésirable d'entrer.

Construisons l'exemple. D'abord, les images sont chargées. Dans ce cas, nous utilisons des images de Wikipédia pour garder l'exemple minimaliste, mais imaginez les cas d'usage possibles !

```python
from PIL import Image
import requests
from io import BytesIO

image_urls = [
    "https://upload.wikimedia.org/wikipedia/commons/e/e8/The_Joker_at_Wax_Museum_Plus.jpg", # Image du Joker
    "https://upload.wikimedia.org/wikipedia/en/9/98/Joker_%28DC_Comics_character%29.jpg" # Image du Joker
]

images = []
for url in image_urls:
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" 
    }
    response = requests.get(url,headers=headers)
    image = Image.open(BytesIO(response.content)).convert("RGB")
    images.append(image)
```

Maintenant que nous avons les images, l'agent nous dira si un invité est vraiment un super-héros (Wonder Woman) ou un méchant (le Joker).

```python
from smolagents import CodeAgent, OpenAIServerModel

model = OpenAIServerModel(model_id="gpt-4o")

# Instancier l'agent
agent = CodeAgent(
    tools=[],
    model=model,
    max_steps=20,
    verbosity_level=2
)

response = agent.run(
    """
    Décrire le costume et le maquillage que porte le personnage de bande dessinée figurant sur ces photos et renvoyer la description.
    Indiquer si l'invité est le Joker ou Wonder Woman.
    """,
    images=images
)
```

Dans le cas de mon exécution, la sortie est la suivante, bien qu'elle puisse varier dans votre cas, comme nous l'avons déjà discuté :

```python
    {
        'Costume et maquillage - Première image': (
            'Manteau violet et une cravate ou nœud papillon de soie violette sur une chemise jaune moutarde.',
            'Peinture faciale blanche avec des traits exagérés, sourcils sombres, maquillage des yeux bleu, lèvres rouges formant un large sourire.'
        ),
        'Costume et maquillage - Deuxième image': (
            'Costume sombre avec une fleur sur le revers, tenant une carte à jouer.',
            'Peau pâle, cheveux verts, lèvres très rouges avec un sourire exagéré.'
        ),
        'Identité du personnage': 'Ce personnage ressemble aux représentations connues du Joker des médias de bande dessinée.'
    }
```

Dans ce cas, la sortie révèle que la personne se fait passer pour quelqu'un d'autre, donc nous pouvons empêcher le Joker d'entrer à la fête !

## Fournir des images avec recherche dynamique

> [!TIP]
> Vous pouvez suivre le code dans ce fichier Python

L'approche précédente est précieuse et a de nombreux cas d'usage potentiels. Cependant, dans des situations où l'invité n'est pas dans la base de données, nous devons explorer d'autres façons de les identifier. Une solution possible est de récupérer dynamiquement des images et des informations à partir de sources externes, comme naviguer sur le web pour des détails.

Dans cette approche, les images sont ajoutées dynamiquement à la mémoire de l'agent pendant l'exécution. Comme nous le savons, les agents dans `smolagents` sont basés sur la classe `MultiStepAgent`, qui est une abstraction du *framework ReAct*. Cette classe opère dans un cycle structuré où diverses variables et connaissances sont enregistrées à différentes étapes :

1. **SystemPromptStep :** Stocke le *prompt* système.
2. **TaskStep :** Enregistre la requête utilisateur et toute entrée fournie.
3. **ActionStep :** Capture les logs des actions de l'agent et les résultats.

Cette approche structurée permet aux agents d'incorporer des informations visuelles dynamiquement et de répondre de manière adaptative aux tâches évolutives. Ci-dessous se trouve le diagramme que nous avons déjà vu, illustrant le processus de flux de travail dynamique et comment différentes étapes s'intègrent dans le cycle de vie de l'agent. Lors de la navigation, l'agent peut prendre des captures d'écran et les sauvegarder comme `observation_images` dans l'`ActionStep`.

![Récupération d'images dynamique](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/smolagents-can-see/diagram_adding_vlms_smolagents.png)

Maintenant que nous comprenons le besoin, construisons notre exemple complet. Dans ce cas, Alfred veut un contrôle total sur le processus de vérification des invités, donc naviguer pour des détails devient une solution viable. Pour compléter cet exemple, nous avons besoin d'un nouvel ensemble d'outils pour l'agent. De plus, nous utiliserons Selenium et Helium, qui sont des outils d'automatisation de navigateur. Cela nous permettra de construire un agent qui explore le web, recherchant des détails sur un invité potentiel et récupérant des informations de vérification. Installons les outils nécessaires :

```bash
pip install "smolagents[all]" helium selenium python-dotenv
```

Nous aurons besoin d'un ensemble d'outils d'agent spécifiquement conçus pour la navigation, tels que `search_item_ctrl_f`, `go_back` et `close_popups`. Ces outils permettent à l'agent d'agir comme une personne naviguant sur le web.

```python
@tool
def search_item_ctrl_f(text: str, nth_result: int = 1) -> str:
    """
    Recherche du texte sur la page actuelle via Ctrl + F et saute à la nième occurrence.
    Args:
        text: Le texte à rechercher
        nth_result: Quelle occurrence aller (par défaut: 1)
    """
    elements = driver.find_elements(By.XPATH, f"//*[contains(text(), '{text}')]")
    if nth_result > len(elements):
        raise Exception(f"Correspondance n°{nth_result} non trouvée (seulement {len(elements)} correspondances trouvées)")
    result = f"Trouvé {len(elements)} correspondances pour '{text}'."
    elem = elements[nth_result - 1]
    driver.execute_script("arguments[0].scrollIntoView(true);", elem)
    result += f"Focalisé sur l'élément {nth_result} de {len(elements)}"
    return result

@tool
def go_back() -> None:
    """Retourne à la page précédente."""
    driver.back()

@tool
def close_popups() -> str:
    """
    Ferme tout modal ou pop-up visible sur la page. Utilise ceci pour fermer les fenêtres pop-up ! Cela ne fonctionne pas sur les bannières de consentement de cookies.
    """
    webdriver.ActionChains(driver).send_keys(Keys.ESCAPE).perform()
```

Nous avons également besoin de fonctionnalités pour sauvegarder des captures d'écran, car ce sera une partie essentielle de ce que notre agent *VLM* utilise pour accomplir la tâche. Cette fonctionnalité prend la capture d'écran et la sauvegarde dans `step_log.observations_images = [image.copy()]`, permettant à l'agent de stocker et traiter les images dynamiquement pendant qu'il navigue.

```python
def save_screenshot(step_log: ActionStep, agent: CodeAgent) -> None:
    sleep(1.0)  # Laisser les animations JavaScript se produire avant de prendre la capture d'écran
    driver = helium.get_driver()
    current_step = step_log.step_number
    if driver is not None:
        for step_logs in agent.logs:  # Supprimer les captures d'écran précédentes des logs pour un traitement allégé
            if isinstance(step_log, ActionStep) and step_log.step_number 

C'est la sortie finale :

```python
Réponse finale : Wonder Woman est typiquement représentée portant un bustier rouge et or, un short ou une jupe bleu avec des étoiles blanches, un tiare doré, des bracelets argentés et un lasso de vérité doré. Elle est la Princesse Diana de Themyscira, connue sous le nom de Diana Prince dans le monde des hommes.
```

Avec tout cela, nous avons créé avec succès notre vérificateur d'identité pour la fête ! Alfred a maintenant les outils nécessaires pour s'assurer que seuls les bons invités franchissent la porte. Tout est prêt pour passer du bon temps au manoir Wayne !

## Lectures complémentaires

- [Nous venons de donner la vue à smolagents](https://huggingface.co/blog/smolagents-can-see) - Blog décrivant la fonctionnalité d'agent visuel.
- [Automatisation de navigateur web avec agents 🤖🌐](https://huggingface.co/docs/smolagents/examples/web_browser) - Exemple pour la navigation web utilisant un agent visuel.
- [Exemple d'agent visuel pour navigateur web](https://github.com/huggingface/smolagents/blob/main/src/smolagents/vision_web_browser.py) - Exemple pour la navigation web utilisant un agent visuel.

### Construction de systèmes de RAG agentiques
https://huggingface.co/learn/agents-course/fr/unit2/smolagents/retrieval_agents.md

# Construction de systèmes de RAG agentiques

> [!TIP]
> Vous pouvez suivre le code dans ce notebook que vous pouvez exécuter avec Google Colab.

Les systèmes de RAG (*Retrieval Augmented Generation*) combinent les capacités de récupération de données et de modèles de génération pour fournir des réponses contextuelles. Par exemple, la requête d'un utilisateur est transmise à un moteur de recherche puis les résultats récupérés sont fournis au LLM avec la requête. Le modèle génère ensuite une réponse basée sur la requête et les informations récupérées.

Le RAG agentique (*Agentic RAG*) étend les systèmes de RAG traditionnels en **combinant des agents autonomes avec une récupération dynamique des connaissances**. 

Alors que les systèmes de RAG traditionnels utilisent un LLM pour répondre aux requêtes basées sur des données récupérées, le RAG agentique **permet un contrôle intelligent des processus de récupération et de génération**, améliorant l'efficacité et la précision.

Les systèmes de RAG traditionnels font face à des limitations clés, telles que **s'appuyer sur une seule étape de récupération** et se concentrer sur la similarité sémantique directe avec la requête de l'utilisateur, ce qui peut négliger des informations pertinentes.

Le RAG agentique résout ces problèmes en permettant à l'agent de formuler de manière autonome des requêtes, de critiquer les résultats récupérés et de mener plusieurs étapes de récupération pour une sortie plus adaptée et complète.

## Récupération de base avec DuckDuckGo

Construisons un agent simple qui peut rechercher sur le web en utilisant DuckDuckGo. Cet agent récupérera des informations et synthétisera des réponses pour répondre aux requêtes. Avec le RAG agentique, l'agent d'Alfred peut :

* Rechercher des dernières tendances en matière de fêtes de super-héros
* Affiner les résultats pour inclure des éléments luxueux
* Synthétiser les informations en un plan complet

Voici comment l'agent d'Alfred peut y parvenir :

```python
from smolagents import CodeAgent, DuckDuckGoSearchTool, InferenceClientModel

# Initialiser l'outil de recherche
search_tool = DuckDuckGoSearchTool()

# Initialiser le modèle
model = InferenceClientModel()

agent = CodeAgent(
    model=model,
    tools=[search_tool],
)

# Exemple d'utilisation
response = agent.run(
    "Search for luxury superhero-themed party ideas, including decorations, entertainment, and catering."
)
print(response)
```

L'agent suit ce processus :

1. **Analyse la requête :** identifie les éléments clés de la requête - organisation de fêtes de luxe sur le thème des super-héros, en mettant l'accent sur la décoration, les divertissements et la restauration.
2. **Effectue la récupération :** exploite DuckDuckGo pour rechercher les informations les plus pertinentes et à jour, en s'assurant qu'elles correspondent aux préférences d'Alfred pour un événement luxueux.
3. **Synthétise l'information :** après avoir rassemblé les résultats, l'agent les traite en un plan cohérent et actionnable pour Alfred, couvrant tous les aspects de la fête.
4. **Stocke pour référence future :** stocke les informations récupérées pour un accès facile lors de la planification d'événements futurs, optimisant l'efficacité des tâches ultérieures.

## Outil de base de connaissances personnalisé

Pour des tâches spécialisées, une base de connaissances personnalisée peut être inestimable. Créons un outil qui interroge une base de données vectorielle de documentation technique ou de connaissances spécialisées. En utilisant la recherche sémantique, l'agent peut trouver les informations les plus pertinentes pour les besoins d'Alfred.

Une base de données vectorielle stocke des représentations numériques (*embeddings*) de texte ou d'autres données, créées par des modèles d'apprentissage automatique. Elle permet la recherche sémantique en identifiant des significations similaires dans un espace de haute dimension.

Cette approche combine des connaissances prédéfinies avec une recherche sémantique pour fournir des solutions contextuelles pour la planification d'événements. Avec un accès à des connaissances spécialisées, Alfred peut perfectionner chaque détail de la fête.

Dans cet exemple, nous allons créer un outil qui récupère des idées de planification de fête à partir d'une base de connaissances personnalisée. Nous utiliserons un modèle BM25 pour rechercher dans la base de connaissances et retourner les meilleurs résultats, et `RecursiveCharacterTextSplitter` pour diviser les documents en morceaux plus petits pour une recherche plus efficace.

```python
from langchain.docstore.document import Document
from langchain.text_splitter import RecursiveCharacterTextSplitter
from smolagents import Tool
from langchain_community.retrievers import BM25Retriever
from smolagents import CodeAgent, InferenceClientModel

class PartyPlanningRetrieverTool(Tool):
    name = "party_planning_retriever"
    description = "Utilise la recherche sémantique pour trouver des idées pertinentes pour l'organisation de la fête d'Alfred au Manoir Wayne sur le thème des super-héros."
    inputs = {
        "query": {
            "type": "string",
            "description": "La requête à effectuer. Celle-ci doit être liée à l'organisation de fêtes ou à des thèmes de super-héros.",
        }
    }
    output_type = "string"

    def __init__(self, docs, **kwargs):
        super().__init__(**kwargs)
        self.retriever = BM25Retriever.from_documents(
            docs, k=5  # Récupérer les 5 meilleurs documents
        )

    def forward(self, query: str) -> str:
        assert isinstance(query, str), "Votre requête doit être une chaîne de caractères"

        docs = self.retriever.invoke(
            query,
        )
        return "\nIdées récupérées :\n" + "".join(
            [
                f"\n\n===== Idée {str(i)} =====\n" + doc.page_content
                for i, doc in enumerate(docs)
            ]
        )

# Simuler une base de connaissances sur la planification de la fête
party_ideas = [
    {"text": "Un bal masqué sur le thème des super-héros avec un décor luxueux, notamment des accents dorés et des rideaux de velours.", "source": "Idées de fête 1"},
    {"text": "Engagez un DJ professionnel qui peut jouer de la musique sur le thème des super-héros comme Batman et Wonder Woman.", "source": "Idées de divertissement"},
    {"text": "Pour la restauration, servez des plats portant le nom de super-héros, comme 'Le smoothie vert de Hulk' et 'Le steak de puissance d'Iron Man'", "source": "Idées de traiteur"},
    {"text": "Décorez le lieu avec des logos de super-héros emblématiques et des projections de Gotham et d'autres villes de super-héros.", "source": "Idées de décoration"},
    {"text": "Expériences interactives avec la VR où les invités peuvent participer à des simulations de super-héros ou à des jeux à thème.", "source": "Idées de divertissement"}
]

source_docs = [
    Document(page_content=doc["text"], metadata={"source": doc["source"]})
    for doc in party_ideas
]

# Découper les documents en morceaux plus petits pour une recherche plus efficace
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50,
    add_start_index=True,
    strip_whitespace=True,
    separators=["\n\n", "\n", ".", " ", ""],
)
docs_processed = text_splitter.split_documents(source_docs)

# Créer l'outil de récupération
party_planning_retriever = PartyPlanningRetrieverTool(docs_processed)

# Initialiser l'agent
agent = CodeAgent(tools=[party_planning_retriever], model=InferenceClientModel())

# Exemple d'utilisation
response = agent.run(
    "Trouver des idées pour une fête de luxe sur le thème des super-héros, y compris des options de divertissement, de restauration et de décoration."
)

print(response)
```

Cet agent amélioré peut :
1. D'abord vérifier la documentation pour des informations pertinentes
2. Combiner les informations de la base de connaissances
3. Maintenir le contexte de conversation en mémoire

## Capacités de récupération améliorées

Lors de la construction de systèmes de RAG agentiques, l'agent peut employer des stratégies sophistiquées comme :

1. **La reformulation de requête :** Au lieu d'utiliser la requête brute de l'utilisateur, l'agent peut élaborer des termes de recherche optimisés qui correspondent mieux aux documents cibles
2. **La décomposition de requête :** Au lieu d'utiliser directement la requête de l'utilisateur, si elle contient plusieurs éléments d'information à interroger, elle peut être décomposée en plusieurs requêtes
3. **L'expansion de requête :** Similaire à la reformulation de requête mais effectuée plusieurs fois pour formuler la requête de plusieurs façons et les interroger toutes
4. **Le reclassement :** Utiliser des [*Cross-Encoders*](https://huggingface.co/models?pipeline_tag=text-ranking&sort=trending) pour attribuer des scores de pertinence sémantique plus complets entre les documents récupérés et la requête
5. **La récupération multi-étapes :** L'agent peut effectuer plusieurs recherches, en utilisant les résultats initiaux pour informer les requêtes suivantes
6. **L'intégration de sources :** Les informations peuvent être combinées à partir de plusieurs sources comme la recherche web et la documentation locale
7. **La validation des résultats :** Le contenu récupéré peut être analysé pour sa pertinence et son exactitude avant d'être inclus dans les réponses

Les systèmes de RAG agentiques efficaces nécessitent une considération attentive de plusieurs aspects clés. L'agent **devrait sélectionner entre les outils disponibles en fonction du type de requête et du contexte**. Les systèmes de mémoire aident à maintenir l'historique de conversation et évitent les récupérations répétitives. Avoir des stratégies de secours garantit que le système peut toujours fournir de la valeur même lorsque les méthodes de récupération principales échouent. De plus, l'implémentation d'étapes de validation aide à assurer l'exactitude et la pertinence des informations récupérées.

## Ressources

- [Agentic RAG : boostez votre RAG avec la reformulation de requête et l'auto-requête ! 🚀](https://huggingface.co/learn/cookbook/agent_rag) - Recette pour développer un système de RAG agentique en utilisant `smolagents`.

### Petit Quiz (non noté) [[quiz2]]
https://huggingface.co/learn/agents-course/fr/unit2/smolagents/quiz2.md

# Petit Quiz (non noté) [[quiz2]]

Il est temps de tester votre compréhension des sections sur `CodeAgent`, `ToolCalling Agent` et les *Outils*. Ce quiz est optionnel et non noté.

---

### Q1 : Quelle est la différence clé entre créer un outil avec le décorateur `@tool` et créer une sous-classe de `Tool` dans smolagents ?

Quelle déclaration décrit le mieux la distinction entre ces deux approches pour définir des outils ?

@tool est obligatoire pour les outils basés sur la récupération, tandis que les sous-classes de Tool sont uniquement pour les tâches de génération de texte",
    explain: "Les deux approches peuvent être utilisées pour tout type d'outil, y compris les outils basés sur la récupération ou la génération de texte.",
  },
  {
    text: "Le décorateur @tool est recommandé pour les outils simples basés sur des fonctions, tandis que les sous-classes de Tool offrent plus de flexibilité pour des fonctionnalités complexes ou des métadonnées personnalisées",
    explain: "C'est correct. L'approche par décorateur est plus simple, mais la sous-classe permet un comportement plus personnalisé.",
    correct: true
  },
  {
    text: "@tool ne peut être utilisé que dans des systèmes multi-agents, tandis que créer une sous-classe de Tool est pour les scénarios à un seul agent",
    explain: "Tous les agents (simples ou multiples) peuvent utiliser l'une ou l'autre approche pour définir des outils ; il n'y a pas de telle restriction.",
  },
  {
    text: "Décorer une fonction avec @tool remplace le besoin d'une docstring, tandis que les sous-classes ne doivent pas inclure de docstring",
    explain: "Les deux méthodes bénéficient de docstrings claires. Le décorateur ne les remplace pas, et une sous-classe peut toujours avoir des docstrings.",
  }
]}
/>

---

### Q2 : Comment un `CodeAgent` gère-t-il les tâches multi-étapes en utilisant l'approche ReAct (*Reason + Act*) ?

Quelle déclaration décrit correctement comment le CodeAgent exécute une série d'étapes pour résoudre une tâche ?

CodeAgent lui-même peut gérer plusieurs étapes de manière autonome en utilisant ReAct.",
  },
  {
    text: "Il stocke chaque action en JSON pour une analyse facile avant de les exécuter toutes en même temps",
    explain: "Ce comportement correspond à l'approche basée sur JSON du ToolCallingAgent, pas du CodeAgent.",
  },
  {
    text: "Il alterne entre l'écriture de raisonnements internes, la génération de code Python, l'exécution du code et l'enregistrement des résultats jusqu'à arriver à une réponse finale",
    explain: "Correct. Cela décrit le schéma ReAct que CodeAgent utilise, incluant le raisonnement itératif et l'exécution de code.",
    correct: true
  },
  {
    text: "Il s'appuie sur un module de vision pour valider la sortie du code avant de continuer à l'étape suivante",
    explain: "Les capacités de vision sont supportées dans smolagents, mais elles ne sont pas une exigence par défaut pour CodeAgent ou l'approche ReAct.",
  }
]}
/>

---

### Q3 : Lequel des éléments suivants est un avantage principal du partage d'un outil sur le Hub d'Hugging Face ?

Sélectionnez la meilleure raison pour laquelle un développeur pourrait télécharger et partager son outil personnalisé.

MultiStepAgent pour la génération augmentée par récupération",
    explain: "Partager un outil ne configure pas automatiquement la récupération ou la logique multi-étapes. Il s'agit simplement de rendre l'outil disponible.",
  },
  {
    text: "Cela permet aux autres de découvrir, réutiliser et intégrer votre outil dans leurs agent construit avec smolagents sans configuration supplémentaire",
    explain: "Oui. Partager sur le Hub rend les outils accessibles pour quiconque (y compris vous-même) de les télécharger et les réutiliser rapidement.",
    correct: true
  },
  {
    text: "Cela garantit que seuls les CodeAgent peuvent invoquer l'outil tandis que les ToolCallingAgent ne le peuvent pas",
    explain: "Les CodeAgents et les ToolCallingAgents peuvent tous deux invoquer des outils partagés. Il n'y a pas de restriction par type d'agent.",
  },
  {
    text: "Cela convertit votre outil en une fonction entièrement capable de vision pour le traitement d'images",
    explain: "Le partage d'outils ne modifie pas la fonctionnalité de l'outil ni n'ajoute automatiquement des capacités de vision.",
  }
]}
/>

---

### Q4 : `ToolCallingAgent` diffère de `CodeAgent` dans la façon dont il exécute les actions. Quelle déclaration est correcte ?

Choisissez l'option qui décrit avec précision comment ToolCallingAgent fonctionne.

ToolCallingAgent est uniquement compatible avec un système multi-agents, tandis que CodeAgent peut fonctionner seul",
    explain: "L'un ou l'autre agent peut être utilisé seul ou dans le cadre d'un système multi-agents.",
  },
  {
    text: "ToolCallingAgent délègue tout le raisonnement à un agent de récupération séparé, puis retourne une réponse finale",
    explain: "ToolCallingAgent utilise toujours un LLM principal pour le raisonnement ; il ne se fie pas uniquement aux agents de récupération.",
  },
  {
    text: "ToolCallingAgent génère des instructions JSON spécifiant les appels d'outils et les arguments, qui sont ensuite analysés et exécutés",
    explain: "C'est correct. ToolCallingAgent utilise l'approche JSON pour définir les appels d'outils.",
    correct: true
  },
  {
    text: "ToolCallingAgent est uniquement destiné aux tâches à une seule étape et s'arrête automatiquement après avoir appelé un outil",
    explain: "ToolCallingAgent peut effectuer plusieurs étapes si nécessaire, tout comme CodeAgent.",
  }
]}
/>

---

### Q5 : Qu'est-ce qui est inclus dans la boîte à outils par défaut de smolagents, et pourquoi pourriez-vous l'utiliser ?

Quelle déclaration capture le mieux l'objectif et le contenu de la boîte à outils par défaut dans smolagents ?

PythonInterpreterTool et un outil de réponse finale pour un prototypage rapide",
    explain: "Correct. La boîte à outils par défaut contient ces outils prêts à l'emploi pour une intégration facile lors de la construction d'agents.",
    correct: true
  },
  {
    text: "Elle ne supporte que les tâches basées sur la vision comme la classification d'images ou l'OCR par défaut",
    explain: "Bien que smolagents puisse intégrer des fonctionnalités basées sur la vision, la boîte à outils par défaut n'est pas exclusivement orientée vision.",
  },
  {
    text: "Elle est destinée uniquement aux systèmes multi-agents et est incompatible avec un seul CodeAgent",
    explain: "La boîte à outils par défaut peut être utilisée par tout type d'agent, configurations simples ou multi-agents.",
  },
  {
    text: "Elle ajoute des fonctionnalités avancées basées sur la récupération pour la réponse à des questions à grande échelle à partir d'un magasin de vecteurs",
    explain: "Bien que vous puissiez construire des outils de récupération, la boîte à outils par défaut ne fournit pas automatiquement des fonctionnalités de RAG avancées.",
  }
]}
/>

---

Félicitations pour avoir terminé ce quiz ! 🎉 Si des questions vous ont posé des difficultés, revisitez les sections `CodeAgent`, `ToolCalling Agent` ou *Outils* pour renforcer votre compréhension. Si vous l'avez réussi, vous êtes bien parti pour construire des applications robustes avec `smolagents` !

### Construire des agents qui utilisent du code
https://huggingface.co/learn/agents-course/fr/unit2/smolagents/code_agents.md

# Construire des agents qui utilisent du code

Les agents à code sont le type d'agent par défaut dans `smolagents`. Ils génèrent des appels d'outils Python pour effectuer des actions, obtenant des représentations d'actions qui sont efficaces, expressives et précises. 

Leur approche simplifiée réduit le nombre d'actions requises, simplifie les opérations complexes et permet la réutilisation de fonctions de code existantes. `smolagents` fournit un *framework* léger pour construire de tels agents en environ 1 000 lignes de code.

![Code vs JSON Actions](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/code_vs_json_actions.png)
Graphique issu du papier [Executable Code Actions Elicit Better LLM Agents](https://huggingface.co/papers/2402.01030)

> [!TIP]
> Si vous voulez en savoir plus sur pourquoi les agents à code sont efficaces, consultez ce guide de la documentation smolagents.

## Pourquoi les agents à code ?

Dans un processus d'agent multi-étapes, le LLM écrit et exécute des actions, impliquant généralement des appels d'outils externes. Les approches traditionnelles utilisent un format JSON pour spécifier les noms d'outils et les arguments sous forme de chaînes, **que le système doit analyser pour déterminer quel outil exécuter**.

Cependant, la recherche montre que **les LLM d'appel d'outils fonctionnent plus efficacement avec du code directement**. C'est un principe fondamental de `smolagents`, comme le montre le diagramme ci-dessus issu de [*Executable Code Actions Elicit Better LLM Agents*](https://huggingface.co/papers/2402.01030).

Écrire des actions en code plutôt qu'en JSON offre plusieurs avantages clés :

* **Composabilité** : Combiner et réutiliser facilement des actions
* **Gestion d'objets** : Travailler directement avec des structures complexes comme des images
* **Généralité** : Exprimer toute tâche computationnellement possible
* **Naturel pour les LLM** : Du code de haute qualité est déjà présent dans les données d'entraînement des LLM

## Comment fonctionne un agent à code ?

![From https://huggingface.co/docs/smolagents/conceptual_guides/react](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/smolagents/codeagent_docs.png)

Le diagramme ci-dessus illustre comment `CodeAgent.run()` fonctionne, suivant le *framework* ReAct que nous avons mentionné dans l'Unité 1. L'abstraction principale pour les agents dans `smolagents` est un `MultiStepAgent` qui sert de bloc de construction principal. Comme nous le verrons dans un exemple ci-dessous, `CodeAgent` est un type spécial de `MultiStepAgent`.  

Un `CodeAgent` effectue des actions à travers un cycle d'étapes (avec les variables existantes et les connaissances étant incorporées dans le contexte de l'agent) qui est conservé dans un journal d'exécution :  

1. Le *prompt* système est stocké dans un `SystemPromptStep` et la requête utilisateur est enregistrée dans un `TaskStep`.

2. Ensuite, la boucle *while* suivante est exécutée :

    2.1 La méthode `agent.write_memory_to_messages()` écrit les *logs* de l'agent dans une liste de [messages de chat](https://huggingface.co/docs/transformers/main/en/chat_templating) lisibles par le LLM.
    
    2.2 Ces messages sont envoyés à un `Model` qui génère une complétion. 
    
    2.3 La complétion est analysée pour extraire l'action, qui, dans notre cas, devrait être un extrait de code puisque nous travaillons avec un `CodeAgent`.  
    
    2.4 L'action est exécutée.
    
    2.5 Les résultats sont enregistrés en mémoire dans un `ActionStep`.

À la fin de chaque étape, si l'agent inclut des appels de fonction (dans `agent.step_callback`), ils sont exécutés.

## Voyons quelques exemples

> [!TIP]
> Vous pouvez suivre le code dans ce notebook que vous pouvez exécuter avec Google Colab.

Alfred planifie une fête au manoir de la famille Wayne et a besoin de votre aide pour s'assurer que tout se passe bien. Pour l'aider, nous appliquerons ce que nous avons appris sur le fonctionnement d'un `CodeAgent` multi-étapes.

Si vous n'avez pas encore installé `smolagents`, vous pouvez le faire en exécutant la commande suivante :

```bash
pip install smolagents -U
```

Connectons-nous également au Hub d'Hugging Face pour avoir accès à l'API d'inférence *Serverless*.

```python
from huggingface_hub import login

login()
```

### Sélectionner une *playlist* pour la fête en utilisant `smolagents`

La musique est un élément essentiel d'une fête réussie ! Alfred a besoin d'aide pour sélectionner la *playlist*. Heureusement, `smolagents` nous couvre ! Nous pouvons construire un agent capable de rechercher sur le web en utilisant DuckDuckGo. Pour donner à l'accès à cet outil  l'agent, nous l'incluons dans la liste des outils lors de la création de l'agent.

Pour le modèle, nous nous appuierons sur `InferenceClientModel`, qui fournit l'accès à l'[API d'inférence Serverless](https://huggingface.co/docs/api-inference/index) d'Hugging Face. Le modèle par défaut est `"Qwen/Qwen2.5-Coder-32B-Instruct"`, qui est performant et disponible pour une inférence rapide, mais vous pouvez sélectionner depuis le Hub n'importe quel modèle compatible.  

Exécuter un agent est assez simple :

```python
from smolagents import CodeAgent, DuckDuckGoSearchTool, InferenceClientModel

agent = CodeAgent(tools=[DuckDuckGoSearchTool()], model=InferenceClientModel())

agent.run("Recherche les meilleures recommandations musicales pour une fête au manoir des Wayne.")
```

Lorsque vous exécutez cet exemple, la sortie **affichera une trace des étapes du *workflow* en cours d'exécution**. Elle affichera également le code Python correspondant avec le message : 

```python
 ─ Executing parsed code: ──────────────────────────────────────────────────────────────────────────────────────── 
  results = web_search(query="best music for a Batman party")                                                      
  print(results)                                                                                                   
 ───────────────────────────────────────────────────────────────────────────────────────────────────────────────── 
```

Après quelques étapes, vous verrez la *playlist* générée qu'Alfred peut utiliser pour la fête ! 🎵

### Utiliser un outil personnalisé pour préparer le menu

Maintenant que nous avons sélectionné une *playlist*, nous devons organiser le menu pour les invités. Encore une fois, Alfred peut tirer parti de `smolagents` pour le faire. Ici, nous utilisons le décorateur `@tool` pour définir une fonction personnalisée qui agit comme un outil. Nous couvrirons la création d'outils plus en détail plus tard, donc pour l'instant, nous pouvons simplement exécuter le code.

Comme vous pouvez le voir dans l'exemple ci-dessous, nous allons créer un outil en utilisant le décorateur `@tool` et l'inclure dans la liste `tools`.  

```python
from smolagents import CodeAgent, tool, InferenceClientModel

# Outil pour suggérer un menu basé sur l'occasion
@tool
def suggest_menu(occasion: str) -> str:
    """
    Suggère un menu basé sur l'occasion.
    Args:
        occasion (str): Le type d'occasion pour la fête. Les valeurs autorisées sont :
                        - "casual": Menu pour une fête décontractée.
                        - "formal": Menu pour une fête formelle.
                        - "superhero": Menu pour une fête de super-héros.
                        - "custom": Menu personnalisé.
    """
    if occasion == "casual":
        return "Pizza, collations et boissons."
    elif occasion == "formal":
        return "Dîner 3 services avec vin et dessert."
    elif occasion == "superhero":
        return "Buffet avec nourriture énergétique et saine."
    else:
        return "Menu personnalisé pour le majordome."

# Alfred, le majordome, préparant le menu pour la fête
agent = CodeAgent(tools=[suggest_menu], model=InferenceClientModel())

# Préparer le menu pour la fête
agent.run("Prépare un menu formel pour la fête.")
```

L'agent s'exécutera pendant quelques étapes jusqu'à trouver la réponse. Préciser les valeurs autorisées dans la docstring aide à diriger l'agent vers les valeurs d'argument `occasion` qui existent et limite les hallucinations.

Le menu est prêt ! 🥗

### Utiliser des imports Python à l'intérieur de l'agent

Nous avons la *playlist* et le menu prêts, mais nous devons vérifier un dernier détail crucial : le temps de préparation !

Alfred doit calculer quand tout serait prêt s'il commençait à préparer maintenant, au cas où ils auraient besoin de l'aide d'autres super-héros.

`smolagents` est spécialisé dans les agents qui écrivent et exécutent des extraits de code Python, offrant une exécution sécurisée.

En effet, **l'exécution du code a des mesures de sécurité strictes** : les imports en dehors d'une liste sûre prédéfinie sont bloqués par défaut. Cependant, vous pouvez autoriser des imports supplémentaires en les passant sous forme de chaînes dans `additional_authorized_imports`.
Pour plus de détails sur l'exécution sécurisée du code, consultez le [guide](https://huggingface.co/docs/smolagents/tutorials/secure_code_execution) officiel.

Lors de la création de l'agent, nous utiliserons `additional_authorized_imports` pour permettre l'importation du module `datetime`. 

```python
from smolagents import CodeAgent, InferenceClientModel
import numpy as np
import time
import datetime

agent = CodeAgent(tools=[], model=InferenceClientModel(), additional_authorized_imports=['datetime'])

agent.run(
    """
    Alfred doit se préparer pour la fête. Voici les tâches :
    1. Préparer les boissons - 30 minutes
    2. Décorer le manoir - 60 minutes
    3. Mettre en place le menu - 45 minutes
    4. Préparer la musique et la playlist - 45 minutes

    Si nous commençons maintenant, à quelle heure la fête sera-t-elle prête ?
    """
)
```

Ces exemples ne sont que le début de ce que vous pouvez faire avec les agents à code, et nous commençons déjà à voir leur utilité pour préparer la fête. 
Vous pouvez en apprendre davantage sur la façon de construire de tels agents dans la [documentation de `smolagents`](https://huggingface.co/docs/smolagents).

En résumé, `smolagents` se spécialise dans les agents qui écrivent et exécutent des extraits de code Python, offrant une exécution sécurisée. Il supporte à la fois les modèles de langage locaux et basés sur API, le rendant adaptable à divers environnements de développement.  

### Partager notre agent préparateur de fête personnalisé sur le Hub

Ne serait-il pas **incroyable de partager notre propre agent Alfred avec le reste du monde** ? En faisant cela, n'importe qui peut facilement télécharger et utiliser l'agent directement depuis le Hub, apportant l'ultime planificateur de fête de Gotham à portée de main ! Faisons-le ! 🎉

La bibliothèque `smolagents` rend cela possible en vous permettant de partager un agent complet avec la communauté et de télécharger ceux des autres pour une utilisation immédiate. C'est aussi simple que ce qui suit :

```python
# Changez pour votre nom d'utilisateur et nom de dépôt
agent.push_to_hub('sergiopaniego/AlfredAgent')
```

Pour télécharger à nouveau l'agent, utilisez le code ci-dessous :

```python
# Changez pour votre nom d'utilisateur et nom de dépôt
alfred_agent = agent.from_hub('sergiopaniego/AlfredAgent', trust_remote_code=True)

alfred_agent.run("Donne-moi la meilleure playlist pour une fête au manoir des Wayne. L'idée de la fête est un thème 'mascarade de méchants'")  
```

Ce qui est également excitant, c'est que les agents partagés sont directement disponibles en tant que *Spaces*, vous permettant d'interagir avec eux en temps réel. Vous pouvez explorer d'autres agents [ici](https://huggingface.co/spaces/davidberenstein1957/smolagents-and-tools).

Par exemple, l'_AlfredAgent_ est disponible [ici](https://huggingface.co/spaces/sergiopaniego/AlfredAgent). Vous pouvez l'essayer directement ci-dessous :

Vous vous demandez peut-être comment Alfred a construit un tel agent en utilisant `smolagents` ? En intégrant plusieurs outils, il peut générer un agent comme suit. Ne vous inquiétez pas des outils pour l'instant, car nous aurons une section dédiée plus tard dans cette unité pour explorer cela en détail :

```python
from smolagents import CodeAgent, DuckDuckGoSearchTool, FinalAnswerTool, InferenceClientModel, Tool, tool, VisitWebpageTool

@tool
def suggest_menu(occasion: str) -> str:
    """
    Suggère un menu basé sur l'occasion.
    Args:
        occasion: Le type d'occasion pour la fête.
    """
    if occasion == "casual":
        return "Pizza, collations et boissons."
    elif occasion == "formal":
        return "Dîner 3 services avec vin et dessert."
    elif occasion == "superhero":
        return "Buffet avec nourriture énergétique et saine."
    else:
        return "Menu personnalisé pour le majordome."

@tool
def catering_service_tool(query: str) -> str:
    """
    Cet outil renvoie le service de restauration le mieux noté à Gotham City.
    
    Args:
        query: Un terme de recherche pour trouver des services de restauration.
    """
    # Exemple de liste de services de restauration et leurs notes
    services = {
        "Gotham Catering Co.": 4.9,
        "Wayne Manor Catering": 4.8,
        "Gotham City Events": 4.7,
    }
    
    # Trouver le service de restauration le mieux noté (simuler le filtrage de requête de recherche)
    best_service = max(services, key=services.get)
    
    return best_service

class SuperheroPartyThemeTool(Tool):
    name = "superhero_party_theme_generator"
    description = """
    Cet outil suggère des idées créatives de fête sur le thème des super-héros basées sur une catégorie.
    Il renvoie une idée de thème de fête unique."""
    
    inputs = {
        "category": {
            "type": "string",
            "description": "Le type de fête de super-héros (par ex., 'héros classiques', 'mascarade de méchants', 'Gotham futuriste').",
        }
    }
    
    output_type = "string"

    def forward(self, category: str):
        themes = {
            "classic heroes": "Gala de la Justice League : Les invités viennent habillés comme leurs héros DC préférés avec des cocktails thématiques comme 'Le Punch Kryptonite'.",
            "villain masquerade": "Bal des Voyous de Gotham : Une mascarade mystérieuse où les invités s'habillent en méchants classiques de Batman.",
            "futuristic Gotham": "Nuit Neo-Gotham : Une fête de style cyberpunk inspirée de Batman Beyond, avec des décorations néon et des gadgets futuristes."
        }
        
        return themes.get(category.lower(), "Idée de fête thématique non trouvée. Essayez 'héros classiques', 'mascarade de méchants', ou 'Gotham futuriste'.")

# Alfred, le majordome, préparant le menu pour la fête
agent = CodeAgent(
    tools=[
        DuckDuckGoSearchTool(), 
        VisitWebpageTool(),
        suggest_menu,
        catering_service_tool,
        SuperheroPartyThemeTool(),
	FinalAnswerTool()
    ], 
    model=InferenceClientModel(),
    max_steps=10,
    verbosity_level=2
)

agent.run("Donne-moi la meilleure playlist pour une fête au manoir des Wayne. L'idée de la fête est un thème 'mascarade de méchants'")
```

Comme vous pouvez le voir, nous avons créé un `CodeAgent` avec plusieurs outils qui améliorent la fonctionnalité de l'agent, le transformant en l'ultime planificateur de fête prêt à partager avec la communauté ! 🎉

Maintenant, c'est à votre tour : construisez votre propre agent et partagez-le avec la communauté en utilisant les connaissances que nous venons d'apprendre ! 🕵️‍♂️💡

> [!TIP]
> Si vous souhaitez partager votre projet d'agent, créez un Space et taguez agents-course sur le Hub. Nous serions ravis de voir ce que vous avez créé !

### Inspecter notre agent préparateur de fête avec OpenTelemetry et Langfuse 📡

Alors qu'Alfred peaufine l'agent, il se lasse de déboguer ses exécutions. Les agents, par nature, sont imprévisibles et difficiles à inspecter. Mais comme il vise à construire l'ultime agent préparateur de fête et à le déployer en production, il a besoin d'une traçabilité robuste pour la surveillance et l'analyse futures.  

Encore une fois, `smolagents` vient à la rescousse ! Il adopte la norme [OpenTelemetry](https://opentelemetry.io/) pour instrumenter les exécutions d'agents, permettant une inspection et une journalisation transparentes. Avec l'aide de [Langfuse](https://langfuse.com/) et du `SmolagentsInstrumentor`, Alfred peut facilement suivre et analyser le comportement de son agent.  

La configuration est simple !  

D'abord, nous devons installer les dépendances nécessaires :  

```bash
pip install opentelemetry-sdk opentelemetry-exporter-otlp openinference-instrumentation-smolagents langfuse
```

Ensuite, Alfred a déjà créé un compte sur Langfuse et a ses clés API prêtes. Si vous ne l'avez pas encore fait, vous pouvez vous inscrire à Langfuse Cloud [ici](https://cloud.langfuse.com/) ou explorer des [alternatives](https://huggingface.co/docs/smolagents/tutorials/inspect_runs).  

Une fois que vous avez vos clés API, elles doivent être correctement configurées comme suit :

```python
import os

# Obtenez les clés pour votre projet depuis la page des paramètres du projet : https://cloud.langfuse.com
os.environ["LANGFUSE_PUBLIC_KEY"] = "pk-lf-..." 
os.environ["LANGFUSE_SECRET_KEY"] = "sk-lf-..." 
os.environ["LANGFUSE_HOST"] = "https://cloud.langfuse.com" # 🇪🇺 Région UE
# os.environ["LANGFUSE_HOST"] = "https://us.cloud.langfuse.com" # 🇺🇸 Région US
```

Avec les variables d'environnement définies, nous pouvons maintenant initialiser le client Langfuse. `get_client()` initialise le client Langfuse en utilisant les identifiants fournis dans les variables d'environnement.

```python
from langfuse import get_client
 
langfuse = get_client()
 
# Vérifier la connexion
if langfuse.auth_check():
    print("Le client Langfuse est authentifié et prêt !")
else:
    print("L'authentification a échoué. Veuillez vérifier vos identifiants et l'hôte.")
```

Enfin, Alfred est prêt à initialiser le `SmolagentsInstrumentor` et commencer à suivre les performances de son agent.  

```python
from openinference.instrumentation.smolagents import SmolagentsInstrumentor

SmolagentsInstrumentor().instrument()
```

Alfred est maintenant connecté 🔌 ! Les exécutions de `smolagents` sont enregistrées dans Langfuse, lui donnant une visibilité complète sur le comportement de l'agent. Avec cette configuration, il est prêt à revisiter les exécutions précédentes et à affiner encore plus son agent préparateur de fête. 

> [!TIP]
> Pour en savoir plus sur le traçage de vos agents et l'utilisation des données collectées pour évaluer leurs performances, consultez l'Unité Bonus 2.

```python
from smolagents import CodeAgent, InferenceClientModel

agent = CodeAgent(tools=[], model=InferenceClientModel())
alfred_agent = agent.from_hub('sergiopaniego/AlfredAgent', trust_remote_code=True)
alfred_agent.run("Donne-moi la meilleure playlist pour une fête au manoir des Wayne. L'idée de la fête est un thème 'mascarade de méchants'")  
```

Alfred peut maintenant accéder aux logs [ici](https://cloud.langfuse.com/project/cm7bq0abj025rad078ak3luwi/traces/995fc019255528e4f48cf6770b0ce27b?timestamp=2025-02-19T10%3A28%3A36.929Z) pour les relire et les analyser.  

> [!TIP]
> En fait, une erreur mineure s'est produite lors de l'exécution. Pouvez-vous la repérer dans les logs ? Essayez de suivre comment l'agent la gère et renvoie quand même une réponse valide. Voici le lien direct vers l'erreur si vous voulez vérifier votre réponse. Bien sûr, l'erreur a été corrigée entre-temps, plus de détails peuvent être trouvés dans cette issue.

Pendant ce temps, la [*playlist* suggérée](https://open.spotify.com/playlist/0gZMMHjuxMrrybQ7wTMTpw) crée l'ambiance parfaite pour les préparatifs de la fête. Cool, non ? 🎶  

---

Maintenant que nous avons créé notre premier *Code Agent*, **apprenons comment nous pouvons créer des *Tool Calling Agents***, le deuxième type d'agent disponible dans `smolagents`.

## Ressources

- [Blog smolagents](https://huggingface.co/blog/smolagents) - Introduction à smolagents et aux interactions de code
- [smolagents : Construire de bons agents](https://huggingface.co/docs/smolagents/tutorials/building_good_agents) - Meilleures pratiques pour des agents fiables
- [Construire des agents efficaces - Anthropic](https://www.anthropic.com/research/building-effective-agents) - Principes de conception d'agents
- [Partager des exécutions avec OpenTelemetry](https://huggingface.co/docs/smolagents/tutorials/inspect_runs) - Détails sur la façon de configurer OpenTelemetry pour suivre vos agents.

### Conclusion
https://huggingface.co/learn/agents-course/fr/unit2/smolagents/conclusion.md

# Conclusion

Félicitations d'avoir terminé le module `smolagents` de cette deuxième unité 🥳

Vous **maîtrisez les fondamentaux** de `smolagents` et vous avez construit votre propre agent ! A présent que vous avez des compétences sur `smolagents`, vous pouvez maintenant commencer à créer des agents qui résoudront des tâches qui vous intéressent.

Dans le prochain module, vous allez apprendre **comment construire des agents avec LlamaIndex**.

Enfin, nous serions ravis **d'entendre ce que vous pensez du cours et comment nous pourrions l'améliorer**. Si vous avez des retours, n'hésitez pas à [remplir ce formulaire](https://docs.google.com/forms/d/e/1FAIpQLSe9VaONn0eglax0uTwi29rIn4tM7H2sYmmybmG5jJNlE5v0xA/viewform?usp=dialog).

### Continuez à apprendre, restez géniaux 🤗

### Outils
https://huggingface.co/learn/agents-course/fr/unit2/smolagents/tools.md

# Outils

Comme nous l'avons exploré dans [l'Unité 1](https://huggingface.co/learn/agents-course/fr/unit1/introduction), les agents utilisent des outils pour effectuer diverses actions. Dans `smolagents`, les outils sont traités comme des **fonctions qu'un *LLM* peut appeler au sein d'un système d'agent**.

Pour interagir avec un outil, le *LLM* a besoin d'une **description d'interface** avec ces composants clés :

- **Nom** : Comment l'outil s'appelle
- **Description de l'outil** : Ce que fait l'outil
- **Types et descriptions des entrées** : Quels arguments l'outil accepte
- **Type de sortie** : Ce que l'outil retourne

Par exemple, pendant qu'il se prépare pour une fête au manoir Wayne, Alfred a besoin de divers outils pour recueillir des informations : de la recherche de services de traiteur à la recherche d'idées de thèmes de fête. Voici à quoi pourrait ressembler l'interface d'un simple outil de recherche :

- **Nom :** `web_search`
- **Description de l'outil :** Recherche sur le web des requêtes spécifiques
- **Entrée :** `query` (string) - Le terme de recherche à rechercher
- **Sortie :** String contenant les résultats de recherche

En utilisant ces outils, Alfred peut prendre des décisions éclairées et rassembler toutes les informations nécessaires pour planifier la fête parfaite.

Ci-dessous, vous pouvez voir une animation illustrant comment un appel d'outil est géré :

![Pipeline agentique de https://huggingface.co/docs/smolagents/conceptual_guides/react](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/Agent_ManimCE.gif)

## Méthodes de création d'outils

Dans `smolagents`, les outils peuvent être définis de deux façons :
1. **En utilisant le décorateur `@tool`** pour des outils simples basés sur des fonctions
2. **En créant une sous-classe de `Tool`** pour des fonctionnalités plus complexes

### Le décorateur `@tool`

Le décorateur `@tool` est la **méthode recommandée pour définir des outils simples**. Sous le capot, `smolagents` analysera les informations de base sur la fonction à partir de Python. Donc si vous nommez votre fonction clairement et écrivez une bonne *docstring*, il sera plus facile pour le *LLM* de l'utiliser.

En utilisant cette approche, nous définissons une fonction avec :

- **Un nom de fonction clair et descriptif** qui aide le *LLM* à comprendre son objectif.
- **Des indications de type pour les entrées et les sorties** pour assurer une utilisation correcte.
- **Une description détaillée**, incluant une section `Args:` où chaque argument est explicitement décrit. Ces descriptions fournissent un contexte précieux pour le *LLM*, il est donc important de les écrire soigneusement.

#### Générer un outil qui récupère le service de traiteur le mieux noté

> [!TIP]
> Vous pouvez suivre le code dans ce notebook que vous pouvez exécuter avec Google Colab.

Imaginons qu'Alfred ait déjà décidé du menu pour la fête, mais qu'il ait maintenant besoin d'aide pour préparer la nourriture pour un si grand nombre d'invités. Pour ce faire, il aimerait engager un service de traiteur et doit identifier les options les mieux notées disponibles. Alfred peut utiliser un outil pour rechercher les meilleurs services de traiteur proche de chez lui.

Voici un exemple de la façon dont Alfred peut utiliser le décorateur `@tool` pour y parvenir :

```python
from smolagents import CodeAgent, InferenceClientModel, tool

# Supposons que nous ayons une fonction qui recherche les services de restauration les mieux notés
@tool
def catering_service_tool(query: str) -> str:
    """
    Cet outil retourne le service de traiteur le mieux noté à Gotham City.

    Args:
        query: Un terme de recherche pour trouver des services de traiteur.
    """
    # Exemple de liste de services de traiteur et leurs notes
    services = {
        "Gotham Catering Co.": 4.9,
        "Wayne Manor Catering": 4.8,
        "Gotham City Events": 4.7,
    }

    # Trouver le service de traiteur le mieux noté (simulation du filtrage de requête de recherche)
    best_service = max(services, key=services.get)

    return best_service

agent = CodeAgent(tools=[catering_service_tool], model=InferenceClientModel())

# Exécuter l'agent pour trouver le meilleur service de traiteur
result = agent.run(
    "Can you give me the name of the highest-rated catering service in Gotham City?"
)

print(result)   # Sortie : Gotham Catering Co.
```

### Définir un outil comme une classe Python

Cette approche implique de créer une sous-classe de [`Tool`](https://huggingface.co/docs/smolagents/v1.8.1/en/reference/tools#smolagents.Tool). Pour des outils complexes, nous pouvons implémenter une classe au lieu d'une fonction Python. La classe encapsule la fonction avec des métadonnées qui aident le *LLM* à comprendre comment l'utiliser efficacement. Dans cette classe, nous définissons :

- `name` : Le nom de l'outil.
- `description` : Une description utilisée pour remplir le prompt système de l'agent.
- `inputs` : Un dictionnaire avec les clés `type` et `description`, fournissant des informations pour aider l'interpréteur Python à traiter les entrées.
- `output_type` : Spécifie le type de sortie attendu.
- `forward` : La méthode contenant la logique d'inférence à exécuter.

Ci-dessous, nous pouvons voir un exemple d'un outil construit en utilisant `Tool` et comment l'intégrer dans un `CodeAgent`.

#### Générer un outil pour générer des idées pour une fête sur le thème des super-héros

La fête d'Alfred au manoir est un **événement à thème portant sur les super-héros**, mais il a besoin d'idées créatives pour la rendre vraiment spéciale. En tant qu'hôte fantastique, il veut surprendre les invités avec un thème unique.

Pour ce faire, il peut utiliser un agent qui génère des idées de fêtes sur le thème des super-héros en fonction d'une catégorie donnée. De cette façon, Alfred peut trouver le thème de fête parfait pour épater ses invités.

```python
from smolagents import Tool, CodeAgent, InferenceClientModel

class SuperheroPartyThemeTool(Tool):
    name = "superhero_party_theme_generator"
    description = """
    Cet outil propose des idées de fêtes créatives sur le thème des super-héros en fonction d'une catégorie.
    Il retourne une idée de thème de fête unique."""

    inputs = {
        "category": {
            "type": "string",
            "description": "Le type de fête de super-héros (par exemple, 'classic heroes', 'villain masquerade', 'futuristic Gotham').",
        }
    }

    output_type = "string"

    def forward(self, category: str):
        themes = {
            "classic heroes": "Justice League Gala : Les invités viennent habillés en leurs héros DC préférés avec des cocktails à thème comme 'The Kryptonite Punch'.",
            "villain masquerade": "Gotham Rogues' Ball : Un bal masqué mystérieux où les invités s'habillent en méchants classiques de Batman.",
            "futuristic Gotham": "Neo-Gotham Night : Une fête de style cyberpunk inspirée de Batman Beyond, avec des décorations néon et des gadgets futuristes."
        }

        return themes.get(category.lower(), "Idée de fête à thème non trouvée. Essayez 'classic heroes', 'villain masquerade', ou 'futuristic Gotham'.")

# Instancier l'outil
party_theme_tool = SuperheroPartyThemeTool()
agent = CodeAgent(tools=[party_theme_tool], model=InferenceClientModel())

# Exécuter l'agent pour générer une idée de thème de fête
result = agent.run(
    "What would be a good superhero party idea for a 'villain masquerade' theme?"
)

print(result)  # Sortie : "Gotham Rogues' Ball : Un bal masqué mystérieux où les invités s'habillent en méchants classiques de Batman."
```

Avec cet outil, Alfred sera le super hôte par excellence, impressionnant ses invités avec une fête sur le thème des super-héros qu'ils n'oublieront pas ! 🦸‍♂️🦸‍♀️

## Boîte à outils par défaut

`smolagents` est livré avec un ensemble d'outils pré-construits qui peuvent être directement injectés dans votre agent. La [boîte à outils par défaut](https://huggingface.co/docs/smolagents/guided_tour?build-a-tool=Decorate+a+function+with+%40tool#default-toolbox) comprend :

- **PythonInterpreterTool**
- **FinalAnswerTool**
- **UserInputTool**
- **DuckDuckGoSearchTool**
- **GoogleSearchTool**
- **VisitWebpageTool**

Alfred peut utiliser divers outils pour assurer une fête parfaite au manoir Wayne :

- D'abord, il pourrait utiliser le `DuckDuckGoSearchTool` pour trouver des idées créatives de fête sur le thème des super-héros.

- Pour le traiteur, il se fierait au `GoogleSearchTool` pour trouver les services les mieux notés à Gotham.

- Pour gérer la disposition des tables, Alfred pourrait effectuer des calculs avec l'outil `PythonInterpreterTool`.

- Une fois que tout est rassemblé, il compilerait le plan en utilisant  `FinalAnswerTool`.

Grâce à ces outils, Alfred garantit que la fête sera à la fois exceptionnelle et harmonieuse. 🦇💡

## Partage et importation d'outils

L'une des fonctionnalités les plus puissantes de `smolagents` est sa capacité à partager des outils personnalisés sur le Hub et à intégrer de manière transparente des outils créés par la communauté. Cela inclut la connexion avec les **Spaces** d'Hugging Face et les **outils LangChain**, améliorant considérablement la capacité d'Alfred à orchestrer une fête inoubliable. 🎭

Avec ces intégrations, Alfred peut exploiter des outils avancés de planification d'événements ; qu'il s'agisse d'ajuster l'éclairage pour l'ambiance parfaite, de créer la *playlist* idéale pour la fête, ou de coordonner avec les meilleurs traiteurs de Gotham.

Voici des exemples montrant comment ces fonctionnalités peuvent améliorer l'expérience de la fête :

### Partager un outil sur le Hub

Partager votre outil personnalisé avec la communauté est facile ! Il suffit de le télécharger sur votre compte Hugging Face en utilisant la méthode `push_to_hub()`.

Par exemple, Alfred peut partager son `party_theme_tool` pour aider les autres à trouver les meilleurs services de traiteur à Gotham. Voici comment faire :

```python
party_theme_tool.push_to_hub("{your_username}/party_theme_tool", token="")
```

### Importer un outil depuis le Hub

Vous pouvez facilement importer des outils créés par d'autres utilisateurs en utilisant la fonction `load_tool()`. Par exemple, Alfred pourrait vouloir générer une image promotionnelle pour la fête en utilisant l'IA. Au lieu de construire un outil à partir de zéro, il peut exploiter un outil prédéfini de la communauté :

```python
from smolagents import load_tool, CodeAgent, InferenceClientModel

image_generation_tool = load_tool(
    "m-ric/text-to-image",
    trust_remote_code=True
)

agent = CodeAgent(
    tools=[image_generation_tool],
    model=InferenceClientModel()
)

agent.run("Generate an image of a luxurious superhero-themed party at Wayne Manor with made-up superheros.")
```

### Importer un Space comme outil

Vous pouvez également importer un *Space* comme outil en utilisant `Tool.from_space()`. Cela ouvre des possibilités d'intégration avec des milliers de *Spaces* de la communauté pour des tâches allant de la génération d'images à l'analyse de données.

L'outil se connectera au *backend Gradio* des *Spaces* en utilisant le `gradio_client`, alors assurez-vous de l'installer via `pip` si vous ne l'avez pas déjà fait.

Pour la fête, Alfred peut utiliser un *Space* existant pour la génération de l'image générée par IA à utiliser comme invitation (au lieu de l'outil pré-construit que nous avons mentionné précédemment). Construisons-le !

```python
from smolagents import CodeAgent, InferenceClientModel, Tool

image_generation_tool = Tool.from_space(
    "black-forest-labs/FLUX.1-schnell",
    name="image_generator",
    description="Generate an image from a prompt"
)

model = InferenceClientModel("Qwen/Qwen2.5-Coder-32B-Instruct")

agent = CodeAgent(tools=[image_generation_tool], model=model)

agent.run(
    "Improve this prompt, then generate an image of it.",
    additional_args={'user_prompt': 'A grand superhero-themed party at Wayne Manor, with Alfred overseeing a luxurious gala'}
)
```

### Importer un outil LangChain

Nous discuterons du *framework* `LangChain` dans les sections à venir. Pour l'instant, nous notons simplement que nous pouvons réutiliser des outils LangChain dans votre *workflow* `smolagents` !

Vous pouvez facilement charger des outils LangChain en utilisant la méthode `Tool.from_langchain()`. Alfred, toujours aussi perfectionniste, prépare une spectaculaire soirée de super-héros au manoir pendant l'absence des Wayne. Pour s'assurer que chaque détail dépasse les attentes, il utilise les outils LangChain pour trouver des idées de divertissement de premier ordre.

En utilisant `Tool.from_langchain()`, Alfred ajoute sans effort des fonctionnalités de recherche avancées à son agent, lui permettant de découvrir des idées et services de fête exclusifs avec seulement quelques commandes.

Voici comment il procède :

```python
from langchain.agents import load_tools
from smolagents import CodeAgent, InferenceClientModel, Tool

search_tool = Tool.from_langchain(load_tools(["serpapi"])[0])

agent = CodeAgent(tools=[search_tool], model=model)

agent.run("Search for luxury entertainment ideas for a superhero-themed event, such as live performances and interactive experiences.")
```

### Importer une collection d'outils depuis n'importe quel serveur MCP

`smolagents` permet également d'importer des outils depuis les centaines de serveurs MCP disponibles sur [glama.ai](https://glama.ai/mcp/servers) ou [smithery.ai](https://smithery.ai). Si vous voulez approfondir le MCP, vous pouvez consulter notre [cours gratuit](https://huggingface.co/learn/mcp-course/).

Installer le client mcp

Nous devons d'abord installer l'intégration `mcp` pour `smolagents`.

```bash
pip install "smolagents[mcp]"
```

Les outils des serveurs MCP peuvent être chargés dans un objet `ToolCollection` comme suit :

```python
import os
from smolagents import ToolCollection, CodeAgent
from mcp import StdioServerParameters
from smolagents import InferenceClientModel

model = InferenceClientModel("Qwen/Qwen2.5-Coder-32B-Instruct")

server_parameters = StdioServerParameters(
    command="uvx",
    args=["--quiet", "pubmedmcp@0.1.3"],
    env={"UV_PYTHON": "3.12", **os.environ},
)

with ToolCollection.from_mcp(server_parameters, trust_remote_code=True) as tool_collection:
    agent = CodeAgent(tools=[*tool_collection.tools], model=model, add_base_tools=True)
    agent.run("Please find a remedy for hangover.")
```

Grâce à cette configuration, Alfred peut rapidement découvrir des options de divertissement luxueuses et s'assurer que les invités de l'élite de Gotham vivent une expérience inoubliable. Cet outil l'aide à organiser l'événement parfait sur le thème des super-héros ! 🎉

## Ressources

- [Tutoriel sur les outils](https://huggingface.co/docs/smolagents/tutorials/tools) - Explorez ce tutoriel pour apprendre à travailler efficacement avec les outils.
- [Documentation sur les outils](https://huggingface.co/docs/smolagents/v1.8.1/en/reference/tools) - Documentation de référence complète sur les outils.
- [Visite guidée des outils](https://huggingface.co/docs/smolagents/v1.8.1/en/guided_tour#tools) - Une visite guidée étape par étape pour vous aider à construire et utiliser efficacement les outils.
- [Construire des agents efficaces](https://huggingface.co/docs/smolagents/tutorials/building_good_agents) - Un guide détaillé sur les meilleures pratiques pour développer des agents de fonctions personnalisées fiables et performants.

### C'est l'heure de l'examen !
https://huggingface.co/learn/agents-course/fr/unit2/smolagents/final_quiz.md

# C'est l'heure de l'examen !

Bravo d'avoir suivi sur le matériel sur `smolagents` ! Vous en avez déjà fait beaucoup. Maintenant, il est temps de mettre vos connaissances à l'épreuve avec un quiz. 🧠

## Instructions

- Le quiz consiste en des questions de code.
- Vous recevrez des instructions pour compléter les extraits de code.
- Lisez attentivement les instructions et complétez les extraits de code en conséquence.
- Pour chaque question, vous recevrez le résultat et quelques commentaires.

🧘 **Ce quiz n'est pas noté et non certifié**. Il s'agit de vous faire comprendre la bibliothèque `smolagents` et de savoir si vous devriez passer plus de temps sur le matériel écrit. Dans les unités à venir, vous mettrez ces connaissances à l'épreuve dans des cas d'usage et des projets.

Commençons !

## Quiz 🚀

Vous pouvez également accéder au quiz 👉 [ici](https://huggingface.co/spaces/agents-course/unit2_smolagents_quiz)

### Introduction à `smolagents`
https://huggingface.co/learn/agents-course/fr/unit2/smolagents/introduction.md

# Introduction à `smolagents`

Bienvenue dans ce module, où vous allez apprendre **comment construire des agents efficaces** en utilisant la bibliothèque [`smolagents`](https://github.com/huggingface/smolagents), qui fournit un *framework* léger pour en créer des performants.  

`smolagents` est une bibliothèque d'Hugging Face ; par conséquent, nous apprécierions votre soutien en **mettant une étoile** au [`dépôt GitHub`](https://github.com/huggingface/smolagents) :

## Vue d'ensemble du module

Ce module offre une vue d'ensemble complète des concepts clés et des stratégies pratiques pour construire des agents intelligents en utilisant `smolagents`. 

Avec tant de *frameworks open-source* disponibles, il est essentiel de comprendre les composants et les capacités qui font de `smolagents` une option utile ou de déterminer quand une autre solution pourrait être un meilleur choix. 

Nous explorerons des types d'agents critiques, y compris les agents à code conçus pour les tâches de développement logiciel, les agents d'appel d'outils pour créer des *workflows* modulaires basés sur des fonctions, et les agents de récupération qui accèdent et synthétisent l'information. 

De plus, nous couvrirons l'orchestration de plusieurs agents ainsi que l'intégration des capacités de vision et de navigation web, qui débloquent de nouvelles possibilités pour des applications dynamiques et contextuelles.

Dans cette unité, Alfred, l'agent de l'unité 1, fait son retour. Cette fois, il utilise `smolagents` pour son fonctionnement interne. Ensemble, nous explorerons les concepts clés derrière ce *framework* pendant qu'Alfred s'attaquera à diverses tâches. Alfred organise une fête au manoir Wayne pendant que la famille Wayne 🦇 est absente, et il a beaucoup à faire. Rejoignez-nous pour découvrir son parcours et comment il gère ces tâches avec `smolagents` !

> [!TIP]
> Dans cette unité, vous apprendrez à construire des agents avec la bibliothèque `smolagents`. Vos agents pourront rechercher des données, exécuter du code et interagir avec des pages web. Vous apprendrez également comment combiner plusieurs agents pour créer des systèmes plus puissants.

![Alfred the agent](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit1/this-is-alfred.jpg)

## Contenu

Lors de cette unité nous allons couvir :  

### 1️⃣ [Pourquoi utiliser smolagents](./why_use_smolagents)

`smolagents` est l'un des nombreux *frameworks* d'agents *open-source* disponibles pour le développement d'applications. Les options alternatives incluent `LlamaIndex` et `LangGraph`, qui sont également couverts dans d'autres modules de ce cours. `smolagents` offre plusieurs fonctionnalités clés qui pourraient en faire un excellent choix pour des cas d'usage spécifiques, mais nous devons toujours considérer toutes les options lors de la sélection d'un *framework*. Nous explorerons les avantages et les inconvénients de l'utilisation de `smolagents`, vous aidant à prendre une décision éclairée basée sur les exigences de votre projet.

### 2️⃣ [CodeAgents](./code_agents)

Les `CodeAgents` sont le type principal d'agent dans `smolagents`. Au lieu de générer du *JSON* ou du texte, ces agents produisent du code Python pour effectuer des actions. Ce module explore leur objectif, leur fonctionnalité et leur fonctionnement, avec des exemples pratiques pour présenter leurs capacités.  

### 3️⃣ [ToolCallingAgents](./tool_calling_agents)

Les `ToolCallingAgents` sont le deuxième type d'agent pris en charge par `smolagents`. Contrairement aux `CodeAgents`, qui génèrent du code Python, ces agents s'appuient sur des blobs *JSON*/texte que le système doit analyser et interpréter pour exécuter des actions. Ce module couvre leur fonctionnalité, leurs principales différences avec les `CodeAgents`, et fournit un exemple pour illustrer leur utilisation.

### 4️⃣ [Outils](./tools)

Comme nous l'avons vu dans l'unité 1, les outils sont des fonctions qu'un *LLM* peut utiliser dans un système agentique. Is agissent comme les blocs de construction essentiels pour le comportement de l'agent. Ce module couvre comment créer des outils, leur structure et différentes méthodes d'implémentation utilisant la classe `Tool` ou le décorateur `@tool`. Vous en saurez également plus sur la boîte à outils par défaut, ainsi que sur comment partager des outils avec la communauté ou encore comment charger des outils créés par la communauté pour les utiliser dans vos agents.

### 5️⃣ [Agents de récupération](./retrieval_agents)

Les agents de récupération permettent aux modèles d'accéder aux bases de connaissances, rendant possible la recherche, la synthèse et la récupération d'informations à partir de plusieurs sources. Ils exploitent des bases vectorielles pour une récupération efficace et implémentent des modèles de ***Retrieval-Augmented Generation* (*RAG*)**. Ces agents sont particulièrement utiles pour intégrer la recherche web avec des bases de connaissances personnalisées tout en maintenant le contexte de conversation à travers des systèmes de mémoire. Ce module explore les stratégies d'implémentation, y compris les mécanismes de repli pour une récupération d'information robuste.

### 6️⃣ [Systèmes multi-agents](./multi_agent_systems)

Orchestrer efficacement plusieurs agents est crucial pour construire des systèmes multi-agents puissants. En combinant des agents avec différentes capacités (comme un agent de recherche web avec un agent d'exécution de code par exemple) vous pouvez créer des solutions plus sophistiquées. Ce module se concentre sur la conception, l'implémentation et la gestion de systèmes multi-agents pour maximiser l'efficacité et la fiabilité.  

### 7️⃣ [Agents de vision et de navigation](./vision_agents)

Les agents vision étendent les capacités traditionnelles des agents en incorporant des **modèles de vision-langage (*VLM* pour *Vision-Language Models*)**, leur permettant de traiter et d'interpréter des informations visuelles. Ce module explore comment concevoir et intégrer des agents alimentés par un *VLM*, débloquant des fonctionnalités avancées comme le raisonnement basé sur l'image, l'analyse de données visuelles et les interactions multimodales. Nous utiliserons également de tels agents pour construire un agent de navigation qui peut parcourir le web et en extraire des informations.

## Ressources

- [Documentation smolagents](https://huggingface.co/docs/smolagents) - Documentation officielle de la bibliothèque smolagents
- [Building Effective Agents](https://www.anthropic.com/research/building-effective-agents) - Article de recherche sur les architectures d'agents
- [Directives pour les agents](https://huggingface.co/docs/smolagents/tutorials/building_good_agents) - Meilleures pratiques pour construire des agents fiables
- [Agents LangGraph](https://langchain-ai.github.io/langgraph/) - Exemples supplémentaires d'implémentations d'agents
- [Guide d'appel de fonctions](https://platform.openai.com/docs/guides/function-calling) - Comprendre l'appel de fonctions avec des LLM
- [Meilleures pratiques RAG](https://www.pinecone.io/learn/retrieval-augmented-generation/) - Guide pour implémenter un RAG efficace

### Pourquoi utiliser smolagents
https://huggingface.co/learn/agents-course/fr/unit2/smolagents/why_use_smolagents.md

# Pourquoi utiliser smolagents

Dans ce module, nous explorerons les avantages et les inconvénients de l'utilisation de [smolagents](https://huggingface.co/docs/smolagents/en/index), vous aidant à prendre une décision éclairée pour savoir si c'est le bon *framework* pour vos besoins.

## Qu'est-ce que `smolagents` ?

`smolagents` est un *framework* simple mais puissant pour construire des agents. Il fournit aux LLM la capacité d'agir (_agency_) pour interagir avec le monde réel, comme effectuer des recherches ou générer des images.

Comme nous l'avons appris dans l'Unité 1, les agents sont des programmes qui utilisent des LLM pour générer des **raisonnements** basés sur des **observations** afin d'effectuer des **actions**. Explorons comment cela est implémenté dans *smolagents*.

### Principaux avantages de `smolagents`
- **Simplicité :** Complexité de code et abstractions minimales, pour rendre le *framework* facile à comprendre, adopter et étendre
- **Support LLM flexible :** Fonctionne avec n'importe quel LLM grâce à l'intégration avec les outils Hugging Face et les API externes
- **Approche orientée code :** Support de première classe pour les *agents à code* qui écrivent leurs actions directement en code, éliminant le besoin d'analyse syntaxique et simplifiant l'appel d'outils 
- **Intégration au Hub d'Hugging Face :** Intégration transparente avec le Hub, permettant l'utilisation de *Spaces* Gradio comme outils

### Quand utiliser smolagents ?

Avec ces avantages en tête, quand devrions-nous utiliser *smolagents* plutôt que d'autres *frameworks* ? 

*smolagents* est idéal quand :
- Vous avez besoin d'une **solution légère et minimale.**
- Vous voulez **expérimenter rapidement** sans configurations complexes.
- Votre **logique d'application est simple.**

### Actions via code vs. via JSON
Contrairement à d'autres *frameworks* où les agents écrivent des actions en JSON, `smolagents` **se concentre sur les appels d'outils en code**, simplifiant le processus d'exécution. C'est parce qu'il n'y a pas besoin d'analyser le JSON pour construire du code qui appelle les outils : la sortie peut être exécutée directement.

Le diagramme suivant illustre cette différence :

![Code vs. JSON actions](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/code_vs_json_actions.png)

Pour revoir la différence entre les actions via code vs. via JSON, vous pouvez revisiter [la section Actions dans l'Unité 1](https://huggingface.co/learn/agents-course/unit1/actions#actions-enabling-the-agent-to-engage-with-its-environment).

### Types d'agents dans `smolagents`

Les agents dans `smolagents` fonctionnent comme des **agents multi-étapes**. 

Chaque [`MultiStepAgent`](https://huggingface.co/docs/smolagents/main/en/reference/agents#smolagents.MultiStepAgent) effectue :
- Un raisonnement
- Un appel et une exécution d'outil

En plus d'utiliser **[CodeAgent](https://huggingface.co/docs/smolagents/main/en/reference/agents#smolagents.CodeAgent)** comme type principal d'agent, *smolagents* supporte également **[ToolCallingAgent](https://huggingface.co/docs/smolagents/main/en/reference/agents#smolagents.ToolCallingAgent)**, qui écrit des appels d'outils en JSON.

Nous explorerons chaque type d'agent plus en détail dans les sections suivantes.

> [!TIP]
> Dans smolagents, les outils sont définis en utilisant le décorateur @tool enveloppant une fonction Python ou la classe Tool.

### Intégration de modèles dans `smolagents`
`smolagents` supporte une intégration flexible des LLM, vous permettant d'utiliser n'importe quel modèle appelable qui répond à [certains critères](https://huggingface.co/docs/smolagents/main/en/reference/models). Le *framework* fournit plusieurs classes prédéfinies pour simplifier les connexions aux modèles :

- **[TransformersModel](https://huggingface.co/docs/smolagents/main/en/reference/models#smolagents.TransformersModel) :** Implémente un pipeline `transformers` local pour une intégration transparente.
- **[InferenceClientModel](https://huggingface.co/docs/smolagents/main/en/reference/models#smolagents.InferenceClientModel) :** Supporte les appels d'[inférence serverless](https://huggingface.co/docs/huggingface_hub/main/en/guides/inference) via [l'infrastructure d'Hugging Face](https://huggingface.co/docs/api-inference/index), ou via un nombre croissant de [fournisseurs d'inférence tiers](https://huggingface.co/docs/huggingface_hub/main/en/guides/inference#supported-providers-and-tasks).
- **[LiteLLMModel](https://huggingface.co/docs/smolagents/main/en/reference/models#smolagents.LiteLLMModel) :** Tire parti de [LiteLLM](https://www.litellm.ai/) pour des interactions légères avec les modèles.
- **[OpenAIServerModel](https://huggingface.co/docs/smolagents/main/en/reference/models#smolagents.OpenAIServerModel) :** Se connecte à tout service offrant une interface API OpenAI.
- **[AzureOpenAIServerModel](https://huggingface.co/docs/smolagents/main/en/reference/models#smolagents.AzureOpenAIServerModel) :** Supporte l'intégration avec tout déploiement Azure OpenAI.

Cette flexibilité garantit que les développeurs peuvent choisir le modèle et le service les plus adaptés à leurs cas d'usage spécifiques, et permet une expérimentation facile.

Maintenant que nous avons compris pourquoi et quand utiliser *smolagents*, plongeons plus profondément dans cette puissante bibliothèque !

## Ressources

- [Blog smolagents](https://huggingface.co/blog/smolagents) - Introduction à smolagents et aux interactions par code

### Petit Quiz (non noté) [[quiz1]]
https://huggingface.co/learn/agents-course/fr/unit2/smolagents/quiz1.md

# Petit Quiz (non noté) [[quiz1]]

Testons votre compréhension de `smolagents` avec un rapide quiz ! N'oubliez pas, se tester aide à renforcer l'apprentissage et à identifier les domaines qui pourraient nécessiter une révision.

Ceci est un quiz optionnel et il n'est pas noté.

### Q1 : Quel est l'un des principaux avantages de choisir `smolagents` par rapport à d'autres *frameworks* ?
Quelle affirmation capture le mieux une force fondamentale de l'approche `smolagents` ?

---

### Q2 : Dans quel scénario bénéficieriez-vous probablement le plus de l'utilisation de smolagents ?
Quelle situation s'aligne bien avec ce que *smolagents* fait de mieux ?

---

### Q3 : smolagents offre de la flexibilité dans l'intégration des modèles. Quelle affirmation reflète le mieux son approche ?
Choisissez la description la plus précise de la façon dont *smolagents* interagit avec les LLM.

---

### Q4 : Comment smolagents gère-t-il le débat entre les actions basées sur le code et les actions basées sur du JSON ?
Quelle affirmation caractérise correctement la philosophie de *smolagents* concernant les formats d'action ?

---

### Q5 : Comment smolagents s'intègre-t-il avec le Hub d'Hugging Face pour des bénéfices supplémentaires ?
Quelle affirmation décrit avec précision l'un des avantages principaux de l'intégration avec le Hub ?

---

Félicitations d'avoir terminé ce quiz ! 🎉 Si vous avez manqué des questions, envisagez de revoir la section *Pourquoi utiliser smolagents* pour une compréhension plus approfondie. Si vous avez bien réussi, vous êtes prêt à explorer des sujets plus avancés dans *smolagents* !

### Écrire des actions sous forme d'extraits de code ou de blobs JSON
https://huggingface.co/learn/agents-course/fr/unit2/smolagents/tool_calling_agents.md

# Écrire des actions sous forme d'extraits de code ou de blobs JSON

> [!TIP]
> Vous pouvez suivre le code dans ce notebook que vous pouvez exécuter avec Google Colab.

Les `ToolCallingAgent` sont le deuxième type d'agent disponible dans `smolagents`. Contrairement aux `CodeAgent` qui utilisent des extraits de code en Python, ces agents **utilisent les capacités d'appel d'outils intégrées des fournisseurs de LLM** pour générer des appels d'outils sous forme de **structures JSON**. C'est l'approche standard utilisée par OpenAI, Anthropic et de nombreux autres fournisseurs.

Regardons un exemple. Quand Alfred veut rechercher des services de restauration et des idées de fête, un `CodeAgent` générerait et exécuterait du code Python comme ceci :

```python
for query in [
    "Meilleurs services de restauration à Gotham City", 
    "Idées de thème de fête pour super-héros"
]:
    print(web_search(f"Rechercher : {query}"))
```

Un `ToolCallingAgent` créerait plutôt une structure JSON :

```python
[
    {"name": "web_search", "arguments": "Meilleurs services de restauration à Gotham City"},
    {"name": "web_search", "arguments": "Idées de thème de fête pour super-héros"}
]
```

Ce blob JSON est ensuite utilisé pour exécuter les appels d'outils.

Bien que `smolagents` se concentre principalement sur les `CodeAgent` puisqu'[ils performent mieux dans l'ensemble](https://huggingface.co/papers/2402.01030), les `ToolCallingAgent` peuvent être efficaces pour des systèmes simples qui ne nécessitent pas de gestion de variables ou d'appels d'outils complexes.

![Code vs JSON Actions](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/code_vs_json_actions.png)  

## Comment fonctionnent *ToolCallingAgent* ?  

Les `ToolCallingAgent` suivent le même *workflow* multi-étapes que les `CodeAgent` (voir la [section précédente](./code_agents) pour plus de détails). 

La différence clé est dans **la façon dont ils structurent leurs actions** : au lieu de code exécutable, ils **génèrent des objets JSON qui spécifient les noms d'outils et les arguments**. Le système **analyse ensuite ces instructions** pour exécuter les outils appropriés.

## Exemple : exécuter un *ToolCallingAgent*  

Revisitions l'exemple précédent où Alfred a commencé les préparatifs de la fête, mais cette fois nous utiliserons un `ToolCallingAgent` pour mettre en évidence la différence. Nous allons construire un agent qui peut rechercher sur le web en utilisant DuckDuckGo, tout comme dans notre exemple de `CodeAgent`. La seule différence est le type d'agent ; le *framework* gère tout le reste :

```python
from smolagents import ToolCallingAgent, DuckDuckGoSearchTool, InferenceClientModel

agent = ToolCallingAgent(tools=[DuckDuckGoSearchTool()], model=InferenceClientModel())

agent.run("Recherche les meilleures recommandations musicales pour une fête au manoir des Wayne.")
```

Lorsque vous examinez la trace de l'agent, au lieu de voir `Executing parsed code:`, vous verrez quelque chose comme :

```text
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Calling tool: 'web_search' with arguments: {'query': "meilleures recommandations musicales pour une fête au     │
│ manoir des Wayne"}                                                                                              │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
```  

L'agent génère un appel d'outil structuré que le système traite pour produire la sortie, plutôt que d'exécuter directement du code.

Maintenant que nous comprenons les deux types d'agents, nous pouvons choisir celui adapté à nos besoins. Continuons à explorer `smolagents` pour faire de la fête d'Alfred un succès ! 🎉

## Ressources

- [Documentation ToolCallingAgent](https://huggingface.co/docs/smolagents/v1.8.1/en/reference/agents#smolagents.ToolCallingAgent) - Documentation officielle de `ToolCallingAgent`

### Qu'est-ce que LangGraph ?
https://huggingface.co/learn/agents-course/fr/unit2/langgraph/when_to_use_langgraph.md

# Qu'est-ce que LangGraph ?

`LangGraph` est un *framework* développé par [LangChain](https://www.langchain.com/) **pour gérer le flux de contrôle des applications qui intègrent un LLM**.

## `LangGraph` est-il différent de `LangChain` ?

LangChain fournit une interface standard pour interagir avec les modèles et autres composants, utile pour la récupération, les appels de LLM et les appels d'outils.
Les classes de LangChain peuvent être utilisées dans LangGraph, mais ne DOIVENT PAS nécessairement être utilisées.

Les *packages* sont différents et peuvent être utilisés de manière isolée, mais, au final, toutes les ressources que vous trouverez en ligne utilisent les deux *packages* main dans la main.

## Quand dois-je utiliser `LangGraph` ?
### Contrôle vs. liberté

Lors de la conception d'applications IA, vous faites face à un compromis fondamental entre **contrôle** et **liberté** :

- La **liberté** donne à votre LLM plus d'espace pour être créatif et s'attaquer à des problèmes inattendus.
- Le **contrôle** vous permet d'assurer un comportement prévisible et de maintenir des garde-fous.

Les agents à code, comme ceux que vous pouvez rencontrer dans *smolagents*, sont très libres. Ils peuvent appeler plusieurs outils en une seule étape d'action, créer leurs propres outils, etc. Cependant, ce comportement peut les rendre moins prévisibles et moins contrôlables qu'un agent standard travaillant avec un *JSON* !

`LangGraph` est à l'autre extrémité du spectre, il brille lorsque vous avez besoin de **contrôle** sur l'exécution de votre agent. Il vous donne les outils pour créer une application qui suit un processus prévisible tout en exploitant toujours la puissance des LLM.

En termes simples, si votre application implique une série d'étapes qui doivent être orchestrées d'une manière spécifique, avec des décisions prises à chaque point de jonction, **LangGraph fournit la structure dont vous avez besoin**.

À titre d'exemple, disons que nous voulons créer un assistant LLM qui peut répondre à quelques questions sur certains documents.

Comme les LLM comprennent mieux le texte, avant de pouvoir répondre à la question, vous devrez convertir d'autres modalités complexes (graphiques, tableaux) en texte. Cependant, ce choix dépend du type de document que vous avez !

C'est un embranchement que j'ai choisi de représenter comme suit :

> 💡 **Astuce :** La partie gauche n'est pas un agent, car ici aucun appel d'outil n'est impliqué. Mais la partie droite devra écrire du code pour interroger le *xls* (convertir en *pandas* et le manipuler).

Bien que cet embranchement soit déterministe, vous pouvez également concevoir un embranchement conditionné par la sortie d'un LLM, les rendant indéterministes.

Les scénarios clés où LangGraph excelle incluent :

- **Le processus de raisonnement en plusieurs étapes** qui nécessitent un contrôle explicite sur le flux
- **Des applications nécessitant la persistance de l'état** entre les étapes
- **Des systèmes qui combinent la logique déterministe avec les capacités d'une IA**
- ***Des workflows* qui nécessitent des interventions *human-in-the-loop***
- **Des architectures d'agents complexes** avec plusieurs composants travaillant ensemble

En substance, chaque fois que cela est possible, en tant qu'être humain, concevez un flux d'actions basé sur les résultats de chaque action, et décidez de ce qu'il faut exécuter ensuite en conséquence. Dans ce cas, LangGraph est le bon *framework* pour vous !

`LangGraph` est, à mon avis, le *framework* d'agents le plus prêt pour la production sur le marché.

## Comment fonctionne LangGraph ?

Au cœur de `LangGraph` se trouve une structure de graphe dirigé pour définir le flux de votre application :

- **Les nœuds** représentent des étapes de traitement individuelles (comme appeler un LLM, utiliser un outil, ou prendre une décision).
- **Les arêtes** définissent les transitions possibles entre les étapes.
- **L'état** est défini par l'utilisateur et maintenu et transmis entre les nœuds pendant l'exécution. Lors de la décision du prochain nœud à cibler, c'est l'état actuel que nous regardons.

Nous explorerons ces blocs fondamentaux plus en détail dans le prochain chapitre !

## En quoi est-ce différent du Python standard ? Pourquoi ai-je besoin de LangGraph ?

Vous pourriez vous demander : « Je pourrais juste écrire du code Python standard avec des instructions *if-else* pour gérer tous ces flux, non ? »

Bien que techniquement vrai, LangGraph offre **certains avantages** par rapport au Python standard pour créer des systèmes complexes. Vous pourriez créer la même application sans LangGraph, mais il construit des outils et des abstractions plus faciles pour vous.

Il inclut des états, une visualisation, une journalisation (traces), de l'*human-in-the-loop* intégré, et plus encore.

### Les composants de base de LangGraph
https://huggingface.co/learn/agents-course/fr/unit2/langgraph/building_blocks.md

# Les composants de base de LangGraph

Pour créer des applications avec LangGraph, vous devez comprendre ses composants principaux. Explorons les blocs fondamentaux qui constituent une application LangGraph.

Une application dans LangGraph commence à partir d'un **point d'entrée**, et selon l'exécution, le flux peut aller vers une fonction ou une autre jusqu'à ce qu'il atteigne la FIN.

## 1. État

L'**état** est le concept central dans LangGraph. Il représente toutes les informations qui circulent à travers votre application.

```python
from typing_extensions import TypedDict

class State(TypedDict):
    graph_state: str
```

L'état est **défini par l'utilisateur**, donc les champs doivent être soigneusement conçus pour contenir toutes les données nécessaires au processus de prise de décision !

> 💡 **Astuce :** Réfléchissez soigneusement aux informations que votre application doit suivre entre les étapes.

## 2. Nœuds

Les **nœuds** sont des fonctions Python. Chaque nœud :
- Prend l'état en entrée
- Effectue une opération
- Retourne des mises à jour de l'état

```python
def node_1(state):
    print("---Node 1---")
    return {"graph_state": state['graph_state'] +" I am"}

def node_2(state):
    print("---Node 2---")
    return {"graph_state": state['graph_state'] +" happy!"}

def node_3(state):
    print("---Node 3---")
    return {"graph_state": state['graph_state'] +" sad!"}
```

Par exemple, les nœuds peuvent contenir :
- **Appels de LLM** : Générer du texte ou prendre des décisions
- **Appels d'outils** : Interagir avec des systèmes externes
- **Logique conditionnelle** : Déterminer les prochaines étapes
- **Intervention humaine** : Obtenir des contributions des utilisateurs

> 💡 **Info :** Certains nœuds nécessaires pour l'ensemble du *workflow* comme *START* et *END* existent directement dans *LangGraph*.

## 3. Arêtes

Les **arêtes** connectent les nœuds et définissent les chemins possibles à travers votre graphe :

```python
import random
from typing import Literal

def decide_mood(state) -> Literal["node_2", "node_3"]:
    
    # Souvent, nous utiliserons l'état pour décider du prochain nœud à visiter
    user_input = state['graph_state'] 
    
    # Ici, faisons juste une répartition 50/50 entre les nœuds 2, 3
    if random.random() 

Mais plus important encore, l'invocation :

```python
graph.invoke({"graph_state" : "Hi, this is Lance."})
```

ressort :

```
---Node 1---
---Node 3---
{'graph_state': 'Hi, this is Lance. I am sad!'}
```

## Et maintenant ?

Dans la prochaine section, nous mettrons ces concepts en pratique en construisant notre premier graphe. Ce graphe permet à Alfred de prendre vos emails, les classifier, et rédiger une réponse préliminaire s'ils sont authentiques.

### Construire votre premier LangGraph
https://huggingface.co/learn/agents-course/fr/unit2/langgraph/first_graph.md

# Construire votre premier LangGraph

Maintenant que nous comprenons les composants de base, mettons-les en pratique en construisant notre premier graphe fonctionnel. Nous implémenterons le système de traitement des emails reçus par Alfred, où il doit :

1. Lire les emails entrants
2. Les classifier comme spam ou légitimes
3. Rédiger une réponse préliminaire pour les emails légitimes
4. Envoyer les informations à M. Wayne quand c'est légitime (affichage seulement)

Cet exemple démontre comment structurer un *workflow* avec LangGraph qui implique une prise de décision basée sur LLM. Bien que cela ne puisse pas être considéré comme un agent car aucun outil n'est impliqué, cette section se concentre plus sur l'apprentissage du *framework* LangGraph que sur les agents.

> [!TIP]
> Vous pouvez suivre le code dans ce notebook que vous pouvez exécuter avec Google Colab.

## Notre *workflow*

Voici le *workflow* que nous allons construire :

## Configuration de notre environnement

Tout d'abord, installons les *packages* requis :

```python
%pip install langgraph langchain_openai
```

Ensuite, importons les modules nécessaires :

```python
import os
from typing import TypedDict, List, Dict, Any, Optional
from langgraph.graph import StateGraph, START, END
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
```

## Étape 1 : Définir notre état

Définissons quelles informations Alfred doit suivre pendant le *workflow* de traitement des emails :

```python
class EmailState(TypedDict):
    # L'email en cours de traitement
    email: Dict[str, Any]  # Contient sujet, expéditeur, corps, etc.

    # Catégorie de l'email (enquête, plainte, etc.)
    email_category: Optional[str]

    # Raison pourquoi l'email a été marqué comme spam
    spam_reason: Optional[str]

    # Analyse et décisions
    is_spam: Optional[bool]
    
    # Génération de réponse
    email_draft: Optional[str]
    
    # Métadonnées de traitement
    messages: List[Dict[str, Any]]  # Suivre la conversation avec le LLM pour l'analyse
```

> 💡 **Astuce :** Rendez votre état suffisamment complet pour suivre toutes les informations importantes, mais évitez de l'encombrer avec des détails inutiles.

## Étape 2 : Définir nos nœuds

Maintenant, créons les fonctions de traitement qui formeront nos nœuds :

```python
# Initialiser notre LLM
model = ChatOpenAI(temperature=0)

def read_email(state: EmailState):
    """Alfred lit et enregistre l'email entrant"""
    email = state["email"]
    
    # Ici nous pourrions faire un prétraitement initial
    print(f"Alfred traite un email de {email['sender']} avec le sujet : {email['subject']}")
    
    # Aucun changement d'état nécessaire ici
    return {}

def classify_email(state: EmailState):
    """Alfred utilise un LLM pour déterminer si l'email est spam ou légitime"""
    email = state["email"]
    
    # Préparer notre prompt pour le LLM
    prompt = f"""
    En tant qu'Alfred le majordome, analysez cet email et déterminez s'il s'agit de spam ou s'il est légitime.
    
    email :
    De : {email['sender']}
    Sujet : {email['subject']}
    Corps : {email['body']}
    
    Premièrement, détermine si cet email est du spam. S'il s'agit de spam, explique pourquoi.
    S'il est légitime, catégorise-le (enquête, plainte, remerciement, etc.).
    """
    
    # Appeler le LLM
    messages = [HumanMessage(content=prompt)]
    response = model.invoke(messages)
    
    # Logique simple pour analyser la réponse (dans une vraie app, vous voudriez un parsing plus robuste)
    response_text = response.content.lower()
    is_spam = "spam" in response_text and "not spam" not in response_text
    
    # Extraire une raison si c'est du spam
    spam_reason = None
    if is_spam and "reason:" in response_text:
        spam_reason = response_text.split("reason:")[1].strip()
    
    # Déterminer la catégorie si légitime
    email_category = None
    if not is_spam:
        categories = ["inquiry", "complaint", "thank you", "request", "information"]
        for category in categories:
            if category in response_text:
                email_category = category
                break
    
    # Mettre à jour les messages pour le suivi
    new_messages = state.get("messages", []) + [
        {"role": "user", "content": prompt},
        {"role": "assistant", "content": response.content}
    ]
    
    # Retourner les mises à jour d'état
    return {
        "is_spam": is_spam,
        "spam_reason": spam_reason,
        "email_category": email_category,
        "messages": new_messages
    }

def handle_spam(state: EmailState):
    """Alfred rejette l'email spam avec une note explicative"""
    print(f"Alfred a marqué l'email comme spam. Raison : {state['spam_reason']}")
    print("L'email a été déplacé dans le dossier spam.")
    
    # Nous avons fini de traiter cet email
    return {}

def draft_response(state: EmailState):
    """Alfred rédige une réponse préliminaire pour les emails légitimes"""
    email = state["email"]
    category = state["email_category"] or "general"
    
    # Préparer notre prompt pour le LLM
    prompt = f"""
    En tant qu'Alfred le majordome, rédige une réponse préliminaire polie à cet email.
    
    email :
    De : {email['sender']}
    Sujet : {email['subject']}
    Corps : {email['body']}
    
    Cet email a été catégorisé comme : {category}
    
    Rédige une réponse brève et professionnelle que M. Hugg peut réviser et personnaliser avant l'envoi.
    """
    
    # Appeler le LLM
    messages = [HumanMessage(content=prompt)]
    response = model.invoke(messages)
    
    # Mettre à jour les messages pour le suivi
    new_messages = state.get("messages", []) + [
        {"role": "user", "content": prompt},
        {"role": "assistant", "content": response.content}
    ]
    
    # Retourner les mises à jour d'état
    return {
        "email_draft": response.content,
        "messages": new_messages
    }

def notify_mr_hugg(state: EmailState):
    """Alfred informe M. Hugg de l'email et présente le brouillon de réponse"""
    email = state["email"]
    
    print("\n" + "="*50)
    print(f"Monsieur, vous avez reçu un email de {email['sender']}.")
    print(f"Sujet : {email['subject']}")
    print(f"Catégorie : {state['email_category']}")
    print("\nJ'ai préparé un brouillon de réponse pour votre révision :")
    print("-"*50)
    print(state["email_draft"])
    print("="*50 + "\n")
    
    # Nous avons fini de traiter cet email
    return {}
```

## Étape 3 : Définir notre logique de routage

Nous avons besoin d'une fonction pour déterminer quel chemin prendre après la classification :

```python
def route_email(state: EmailState) -> str:
    """Déterminer la prochaine étape basée sur la classification en spam"""
    if state["is_spam"]:
        return "spam"
    else:
        return "legitimate"
```

> 💡 **Note :** Cette fonction de routage est appelée par LangGraph pour déterminer quelle arête suivre après le nœud de classification. La valeur de retour doit correspondre à l'une des clés dans notre mappage d'arêtes conditionnelles.

## Étape 4 : Créer le *StateGraph* et définir les arêtes

Maintenant nous connectons tout ensemble :

```python
# Créer le graphe
email_graph = StateGraph(EmailState)

# Ajouter les nœuds
email_graph.add_node("read_email", read_email)
email_graph.add_node("classify_email", classify_email)
email_graph.add_node("handle_spam", handle_spam)
email_graph.add_node("draft_response", draft_response)
email_graph.add_node("notify_mr_hugg", notify_mr_hugg)

# Commencer les arêtes
email_graph.add_edge(START, "read_email")
# Ajouter les arêtes - définir le flux
email_graph.add_edge("read_email", "classify_email")

# Ajouter l'embranchement conditionnel depuis classify_email
email_graph.add_conditional_edges(
    "classify_email",
    route_email,
    {
        "spam": "handle_spam",
        "legitimate": "draft_response"
    }
)

# Ajouter les arêtes finales
email_graph.add_edge("handle_spam", END)
email_graph.add_edge("draft_response", "notify_mr_hugg")
email_graph.add_edge("notify_mr_hugg", END)

# Compiler le graphe
compiled_graph = email_graph.compile()
```

Remarquez comment nous utilisons le nœud spécial `END` fourni par LangGraph. Cela indique les états terminaux où le *workflow* se termine.

## Étape 5 : Exécuter l'application

Testons notre graphe avec un email légitime et un email spam :

```python
# Exemple d'email légitime
legitimate_email = {
    "sender": "john.smith@example.com",
    "subject": "Question sur vos services",
    "body": "Cher M. Hugg, J'ai été référé à vous par un collègue et je suis intéressé à en savoir plus sur vos services de conseil. Pourrions-nous programmer un appel la semaine prochaine ? Meilleures salutations, John Smith"
}

# Exemple d'email spam
spam_email = {
    "sender": "winner@lottery-intl.com",
    "subject": "VOUS AVEZ GAGNÉ 5 000 000 $ !!!",
    "body": "FÉLICITATIONS ! Vous avez été sélectionné comme gagnant de notre loterie internationale ! Pour réclamer votre prix de 5 000 000 $, veuillez nous envoyer vos coordonnées bancaires et des frais de traitement de 100 $."
}

# Traiter l'email légitime
print("\nTraitement de l'email légitime...")
legitimate_result = compiled_graph.invoke({
    "email": legitimate_email,
    "is_spam": None,
    "spam_reason": None,
    "email_category": None,
    "email_draft": None,
    "messages": []
})

# Traiter l'email spam
print("\nTraitement de l'email spam...")
spam_result = compiled_graph.invoke({
    "email": spam_email,
    "is_spam": None,
    "spam_reason": None,
    "email_category": None,
    "email_draft": None,
    "messages": []
})
```

## Étape 6 : Inspecter notre agent trieur d'email avec *Langfuse* 📡

Alors qu'Alfred peaufine l'agent trieur d'email, il se lasse de déboguer ses exécutions. Les agents, par nature, sont imprévisibles et difficiles à inspecter. Mais comme il vise à construire l'agent de détection de spam ultime et à le déployer en production, il a besoin d'une traçabilité robuste pour le *monitoring* et l'analyse futurs.

Pour cela, Alfred peut utiliser un outil d'observabilité comme [Langfuse](https://langfuse.com/) pour tracer et monitorer l'agent.

Premièrement, nous installons Langfuse avec pip :
```python
%pip install -q langfuse
```

Deuxièmement, nous installons LangChain avec pip (LangChain est requis car nous utilisons LangFuse) :
```python
%pip install langchain
```

Ensuite, nous ajoutons les clés API LangFuse et l'adresse de l'hôte comme variables d'environnement. Vous pouvez obtenir vos identifiants LangFuse en vous inscrivant sur [LangFuse Cloud](https://cloud.langfuse.com) ou en [auto-hébergeant LangFuse](https://langfuse.com/self-hosting).

```python
import os
 
# Obtenez les clés pour votre projet depuis la page des paramètres du projet : https://cloud.langfuse.com
os.environ["LANGFUSE_PUBLIC_KEY"] = "pk-lf-..." 
os.environ["LANGFUSE_SECRET_KEY"] = "sk-lf-..."
os.environ["LANGFUSE_HOST"] = "https://cloud.langfuse.com" # 🇪🇺 région EU
# os.environ["LANGFUSE_HOST"] = "https://us.cloud.langfuse.com" # 🇺🇸 région US
```

Ensuite, nous configurons le [LangFuse `callback_handler`](https://langfuse.com/docs/integrations/langchain/tracing#add-langfuse-to-your-langchain-application) et instrumentons l'agent en ajoutant le `langfuse_callback` à l'invocation du graphe : `config={"callbacks": [langfuse_handler]}`.

```python   
from langfuse.langchain import CallbackHandler

# Initialiser le CallbackHandler Langfuse pour LangGraph/Langchain (traçage)
langfuse_handler = CallbackHandler()

# Traiter l'email légitime
legitimate_result = compiled_graph.invoke(
    input={"email": legitimate_email, "is_spam": None, "spam_reason": None, "email_category": None, "draft_response": None, "messages": []},
    config={"callbacks": [langfuse_handler]}
)
```

Alfred est maintenant connecté 🔌 ! Les exécutions de LangGraph sont enregistrées dans LangFuse, lui donnant une visibilité complète sur le comportement de l'agent. Avec cette configuration, il est prêt à revisiter les exécutions précédentes et à affiner encore plus son agent de tri de courrier.

![Example trace in Langfuse](https://langfuse.com/images/cookbook/huggingface-agent-course/langgraph-trace-legit.png)

_[Lien public vers la trace avec l'email légitime](https://cloud.langfuse.com/project/cloramnkj0002jz088vzn1ja4/traces/f5d6d72e-20af-4357-b232-af44c3728a7b?timestamp=2025-03-17T10%3A13%3A28.413Z&observation=6997ba69-043f-4f77-9445-700a033afba1)_

## Visualiser notre graphe

LangGraph nous permet de visualiser notre *workflow* pour mieux comprendre et déboguer sa structure :

```python
compiled_graph.get_graph().draw_mermaid_png()
```

Cela produit une représentation visuelle montrant comment nos nœuds sont connectés et les chemins conditionnels qui peuvent être pris.

## Ce que nous avons construit

Nous avons créé un *workflow* complet de traitement des emails qui :

1. Prend un email entrant
2. Utilise un LLM pour le classifier comme spam ou légitime
3. Gère le spam en le rejetant
4. Pour les emails légitimes, rédige une réponse et informe M. Hugg

Cela démontre la puissance de LangGraph pour orchestrer des *workflows* complexes avec des LLM tout en maintenant un flux clair et structuré.

## Points clés à retenir

- **Gestion d'état** : Nous avons défini un état complet pour suivre tous les aspects du traitement des emails
- **Implémentation de nœuds** : Nous avons créé des nœuds fonctionnels qui interagissent avec un LLM
- **Routage conditionnel** : Nous avons implémenté une logique d'embranchement basée sur la classification des emails
- **États terminaux** : Nous avons utilisé le nœud *END* pour marquer les points d'achèvement dans notre *workflow*

## Et maintenant ?

Dans la prochaine section, nous explorerons des fonctionnalités plus avancées de LangGraph, y compris la gestion de l'interaction humaine dans le *workflow* et l'implémentation d'une logique d'embranchement plus complexe basée sur plusieurs conditions.

### Conclusion
https://huggingface.co/learn/agents-course/fr/unit2/langgraph/conclusion.md

# Conclusion

Félicitations pour avoir terminé le module `LangGraph` de cette deuxième unité ! 🥳

Vous **maîtrisez les fondamentaux** de la construction de *workflows* structurés avec LangGraph que vous pourrez envoyer en production.

Ce module n'est que le début de votre parcours avec LangGraph. Pour des sujets plus avancés, nous recommandons :

- D'explorer la [documentation officielle de LangGraph](https://github.com/langchain-ai/langgraph)
- De suivre le cours complet [*Introduction to LangGraph*](https://academy.langchain.com/courses/intro-to-langgraph) de *LangChain Academy*
- De construire quelque chose par vous-même !

Dans la prochaine unité, vous explorerez maintenant des cas d'usage réels. Il est temps de quitter la théorie pour passer à la pratique !

Enfin, nous serions ravis **d'entendre ce que vous pensez du cours et comment nous pourrions l'améliorer**. Si vous avez des retours, n'hésitez pas à [remplir ce formulaire](https://docs.google.com/forms/d/e/1FAIpQLSe9VaONn0eglax0uTwi29rIn4tM7H2sYmmybmG5jJNlE5v0xA/viewform?usp=dialog).

### Continuez à apprendre, restez géniaux 🤗

### Graphe d'analyse de documents
https://huggingface.co/learn/agents-course/fr/unit2/langgraph/document_analysis_agent.md

# Graphe d'analyse de documents

Alfred à votre service. En tant que majordome de confiance de M. Wayne, j'ai pris la liberté de documenter comment j'aide M. Wayne avec ses divers besoins documentaires. Pendant qu'il s'occupe de ses... activités nocturnes, je m'assure que tous ses papiers, programmes d'entraînement et plans nutritionnels sont correctement analysés et organisés.

Avant de partir, il a laissé une note avec son programme d'entraînement de la semaine. J'ai alors pris la responsabilité de proposer un **menu** pour les repas de demain.

Pour de futurs événements similaires, créons un système d'analyse de documents utilisant LangGraph pour servir les besoins de M. Wayne. Ce système peut :

1. Traiter des documents sous forme d'image
2. Extraire du texte en utilisant des modèles de vision (*Vision Language Model*)
3. Effectuer des calculs quand nécessaire (pour démontrer l'utilisation d'outils normaux)
4. Analyser le contenu et fournir des résumés concis
5. Exécuter des instructions spécifiques liées aux documents

## Le *workflow* du majordome

Le *workflow* que nous allons construire suit ce schéma structuré :

![Butler's Document Analysis Workflow](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/LangGraph/alfred_flow.png)

> [!TIP]
> Vous pouvez suivre le code dans ce notebook que vous pouvez exécuter avec Google Colab.

## Configuration de l'environnement

```python
%pip install langgraph langchain_openai langchain_core
```
et les imports :

```python
import base64
from typing import List, TypedDict, Annotated, Optional
from langchain_openai import ChatOpenAI
from langchain_core.messages import AnyMessage, SystemMessage, HumanMessage
from langgraph.graph.message import add_messages
from langgraph.graph import START, StateGraph
from langgraph.prebuilt import ToolNode, tools_condition
from IPython.display import Image, display
```

## Définir l'état de l'agent

Cet état est un peu plus complexe que les précédents que nous avons vus.
`AnyMessage` est une classe de LangChain qui définit les messages, et `add_messages` est un opérateur qui ajoute le dernier message plutôt que de l'écraser avec le dernier état.

C'est un nouveau concept dans LangGraph, où vous pouvez ajouter des opérateurs dans votre état pour définir la façon dont ils doivent interagir ensemble.

```python
class AgentState(TypedDict):
    # Le document fourni
    input_file: Optional[str]  # Contient le chemin du fichier (PDF/PNG)
    messages: Annotated[list[AnyMessage], add_messages]
```

## Préparer les outils

```python
vision_llm = ChatOpenAI(model="gpt-4o")

def extract_text(img_path: str) -> str:
    """
    Extraire le texte d'un fichier image en utilisant un modèle multimodal.
    
    Maître Wayne laisse souvent des notes avec son régime d'entraînement ou ses plans de repas.
    Cela me permet d'analyser correctement le contenu.
    """
    all_text = ""
    try:
        # Lire l'image et encoder en base64
        with open(img_path, "rb") as image_file:
            image_bytes = image_file.read()

        image_base64 = base64.b64encode(image_bytes).decode("utf-8")

        # Préparer le prompt incluant les données d'image base64
        message = [
            HumanMessage(
                content=[
                    {
                        "type": "text",
                        "text": (
                            "Extrayez tout le texte de cette image. "
                            "Retournez seulement le texte extrait, sans explications."
                        ),
                    },
                    {
                        "type": "image_url",
                        "image_url": {
                            "url": f"data:image/png;base64,{image_base64}"
                        },
                    },
                ]
            )
        ]

        # Appeler le VLM
        response = vision_llm.invoke(message)

        # Ajouter le texte extrait
        all_text += response.content + "\n\n"

        return all_text.strip()
    except Exception as e:
        # Un majordome doit gérer les erreurs avec élégance
        error_msg = f"Erreur lors de l'extraction du texte : {str(e)}"
        print(error_msg)
        return ""

def divide(a: int, b: int) -> float:
    """Diviser a et b - pour les calculs occasionnels de Maître Wayne."""
    return a / b

# Équiper le majordome avec des outils
tools = [
    divide,
    extract_text
]

llm = ChatOpenAI(model="gpt-4o")
llm_with_tools = llm.bind_tools(tools, parallel_tool_calls=False)
```

## Les nœuds

```python
def assistant(state: AgentState):
    # Message système
    textual_description_of_tool="""
extract_text(img_path: str) -> str:
    Extraire le texte d'un fichier image en utilisant un modèle multimodal.

    Args:
        img_path: Un chemin de fichier image local (chaînes).

    Returns:
        Une chaîne unique contenant le texte concaténé extrait de chaque image.
divide(a: int, b: int) -> float:
    Diviser a et b
"""
    image=state["input_file"]
    sys_msg = SystemMessage(content=f"Vous êtes un majordome serviable nommé Alfred qui sert M. Wayne et Batman. Vous pouvez analyser des documents et effectuer des calculs avec les outils fournis :\n{textual_description_of_tool} \n Vous avez accès à quelques images optionnelles. Actuellement l'image chargée est : {image}")

    return {
        "messages": [llm_with_tools.invoke([sys_msg] + state["messages"])],
        "input_file": state["input_file"]
    }
```

## Le modèle *ReAct* : Comment j'aide M. Wayne

Permettez-moi d'expliquer l'approche dans cet agent. L'agent suit ce qu'on appelle le modèle *ReAct* (*Reason-Act-Observe*)

1. **Réfléchir** sur ses documents et demandes
2. **Agir** en utilisant les outils appropriés
3. **Observer** les résultats
4. **Répéter** si nécessaire jusqu'à ce que j'aie pleinement répondu à ses besoins

C'est une implémentation simple d'un agent utilisant LangGraph.

```python
# Le graphe
builder = StateGraph(AgentState)

# Définir les nœuds : ceux-ci font le travail
builder.add_node("assistant", assistant)
builder.add_node("tools", ToolNode(tools))

# Définir les arêtes : celles-ci déterminent comment le flux de contrôle se déplace
builder.add_edge(START, "assistant")
builder.add_conditional_edges(
    "assistant",
    # Si le dernier message nécessite un outil, router vers les outils
    # Sinon, fournir une réponse directe
    tools_condition,
)
builder.add_edge("tools", "assistant")
react_graph = builder.compile()

# Montrer le processus de réflexion du majordome
display(Image(react_graph.get_graph(xray=True).draw_mermaid_png()))
```

Nous définissons un nœud `tools` avec notre liste d'outils. Le nœud `assistant` est juste notre modèle avec les outils liés.
Nous créons un graphe avec les nœuds `assistant` et `tools`.

Nous ajoutons une arête `tools_condition`, qui route vers `End` ou vers `tools` selon que l'`assistant` appelle un outil.

Maintenant, nous ajoutons une nouvelle étape :

Nous connectons le nœud `tools` de retour à l'`assistant`, formant une boucle.

- Après l'exécution du nœud `assistant`, `tools_condition` vérifie si la sortie du modèle est un appel d'outil.
- Si c'est un appel d'outil, le flux est dirigé vers le nœud `tools`.
- Le nœud `tools` se reconnecte à `assistant`.
- Cette boucle continue tant que le modèle décide d'appeler des outils.
- Si la réponse du modèle n'est pas un appel d'outil, le flux est dirigé vers *END*, terminant le processus.

![ReAct Pattern](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/LangGraph/Agent.png)

## Le majordome en action

### Exemple 1 : Calculs simples

Voici un exemple pour montrer un cas d'usage simple d'un agent utilisant un outil dans LangGraph.

```python
messages = [HumanMessage(content="Divisez 6790 par 5")]
messages = react_graph.invoke({"messages": messages, "input_file": None})

# Montrer les messages
for m in messages['messages']:
    m.pretty_print()
```

La conversation se déroulerait :

```
Humain : Divisez 6790 par 5

Appel d'un outil : divide(a=6790, b=5)

Réponse de l'outil : 1358.0

Alfred : Le résultat de la division de 6790 par 5 est 1358.0.
```

### Exemple 2 : Analyser les documents d'entraînement de Maître Wayne

Quand Maître Wayne laisse ses notes d'entraînement et de repas :

```python
messages = [HumanMessage(content="Selon la note fournie par M. Wayne dans les images fournies. Quelle est la liste des articles que je dois acheter pour le menu du dîner ?")]
messages = react_graph.invoke({"messages": messages, "input_file": "Batman_training_and_meals.png"})
```

L'interaction se déroulerait :

```
Humain : Selon la note fournie par M. Wayne dans les images fournies. Quelle est la liste des articles que je dois acheter pour le menu du dîner ?

Appel d'un outil : extract_text(img_path="Batman_training_and_meals.png")

Réponse de l'outil : [Texte extrait avec le programme d'entraînement et les détails du menu]

Alfred : Pour le menu du dîner, vous devriez acheter les articles suivants :

1. Steak de surlonge local nourri à l'herbe
2. Épinards biologiques
3. Poivrons *piquillo*
4. Pommes de terre (pour pommes de terre aux herbes dorées au four)
5. Huile de poisson (2 grammes)

Assurez-vous que le steak soit nourri à l'herbe et que les épinards et poivrons soient biologiques pour un repas de la meilleure qualité.
```

## Points clés à retenir

Si vous souhaitez créer votre propre majordome d'analyse de documents, voici les considérations clés :

1. **Définir des outils clairs** pour des tâches spécifiques liées aux documents
2. **Créer un suivi d'état robuste** pour maintenir le contexte entre les appels d'outils
3. **Considérer la gestion d'erreurs** pour les échecs d'outils
4. **Maintenir la conscience contextuelle** des interactions précédentes (assurée par l'opérateur `add_messages`)

Avec ces principes, vous pouvez vous aussi fournir un service d'analyse de documents exemplaire digne du manoir Wayne.

*J'espère que cette explication a été satisfaisante. Maintenant, si vous voulez bien m'excuser, la cape de Maître Wayne nécessite un repassage avant les activités de ce soir.*

### Introduction à LangGraph
https://huggingface.co/learn/agents-course/fr/unit2/langgraph/introduction.md

# Introduction à LangGraph

Bienvenue dans cette nouvelle partie de notre voyage, où vous allez apprendre **comment créer des applications** en utilisant le *framework* [`LangGraph`](https://github.com/langchain-ai/langgraph) conçu pour vous aider à structurer et orchestrer des *workflows* complexes avec des LLM.

`LangGraph` est un framework qui vous permet de créer des applications **prêtes pour la production** en vous donnant des outils de **contrôle** sur le flux de votre agent.

## Aperçu du module

Dans cette unité, vous découvrirez :

### 1️⃣ [Qu'est-ce que LangGraph et quand l'utiliser ?](./when_to_use_langgraph)
### 2️⃣ [Les composants de base de LangGraph](./building_blocks)
### 3️⃣ [Alfred, le majordome trieur de courrier](./first_graph)
### 4️⃣ [Alfred, l'agent d'analyse de documents](./document_analysis_agent)
### 5️⃣ [Quiz](./quiz1)

> [!WARNING]
> Les exemples de cette section nécessitent l'accès à un modèle LLM/VLM puissant. Nous les avons exécutés en utilisant l'API GPT-4o car elle offre la meilleure compatibilité avec LangGraph.

À la fin de cette unité, vous serez en mesure de créer des applications robustes, organisées et prêtes pour la production !

Cela étant dit, cette section est une introduction à LangGraph et des sujets plus avancés peuvent être découverts dans le cours gratuit de la *LangChain academy* : [*Introduction to LangGraph*](https://academy.langchain.com/courses/intro-to-langgraph).

Commençons !

## Ressources

- [*LangGraph Agents*](https://langchain-ai.github.io/langgraph/) - Exemples d'agents LangGraph
- [*LangChain academy*](https://academy.langchain.com/courses/intro-to-langgraph) - Cours complet sur LangGraph de LangChain

### Testez votre compréhension de LangGraph
https://huggingface.co/learn/agents-course/fr/unit2/langgraph/quiz1.md

# Testez votre compréhension de LangGraph

Testons votre compréhension de `LangGraph` avec un quiz rapide ! Cela aidera à renforcer les concepts clés que nous avons couverts jusqu'à présent.

Ce quiz est optionnel et il n'est pas noté.

### Q1 : Quel est l'objectif principal de LangGraph ?
Quelle déclaration décrit le mieux ce pour quoi LangGraph est conçu ?

---

### Q2 : Dans le contexte du compromis « Contrôle vs Liberté », où se situe LangGraph ?
Quelle déclaration caractérise le mieux l'approche de LangGraph pour la conception d'agents ?

---

### Q3 : Quel rôle joue l'état dans LangGraph ?
Choisissez la description la plus précise de l'état dans LangGraph.

### Q4 : Qu'est-ce qu'une arête conditionnelle dans LangGraph ?
Sélectionnez la description la plus précise.

workflow.",
    correct: true
  },
  {
    text: "Une arête qui n'est suivie que lorsqu'une condition spécifique se produit",
    explain: "Les arêtes conditionnelles contrôlent le flux de l'application sur ses sorties, pas sur l'entrée.",
  },
  {
    text: "Une arête qui nécessite une confirmation utilisateur avant de continuer",
    explain: "Les arêtes conditionnelles sont basées sur des conditions programmatiques, pas sur des exigences d'interaction utilisateur.",
  }
]}
/>

---

### Q5 : Comment LangGraph aide-t-il à résoudre le problème d'hallucination dans les LLM ?
Choisissez la meilleure réponse.

Félicitations pour avoir terminé le quiz ! 🎉 Si vous avez manqué des questions, considérez réviser les sections précédentes pour renforcer votre compréhension. Ensuite, nous explorerons des fonctionnalités plus avancées de LangGraph et verrons comment construire des *workflows* d'agents plus complexes.

### Readme
https://huggingface.co/learn/agents-course/fr/unit3/README.md

### RAG agentique
https://huggingface.co/learn/agents-course/fr/unit3/agentic-rag/agentic-rag.md

# RAG agentique

Dans cette unité, nous allons examiner comment nous pouvons utiliser le *RAG agentique* pour aider Alfred à préparer l'incroyable gala.

> [!TIP]
> Nous avons déjà discuté du RAG et du RAG agentique dans l'unité précédente, donc n'hésitez pas à passer directement à la suite si vous êtes déjà familier avec ces concepts.

Les LLM sont entraînés sur d'énormes corpus de données pour apprendre des connaissances générales.
Cependant, ce modèle de connaissance du monde peut ne pas toujours être des informations pertinentes et à jour.
**Le RAG résout ce problème en trouvant et récupérant des informations pertinentes à partir de vos données et en les transmettant au LLM.**

![RAG](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/rag.png)

Maintenant, pensez à comment Alfred fonctionne :

1. Nous lui avons demandé d'aider à planifier un gala
2. Il doit trouver les dernières nouvelles et informations météorologiques
3. Il doit structurer et rechercher les informations sur les invités

Tout comme Alfred a besoin de chercher dans vos informations domestiques pour être utile, tout agent a besoin d'un moyen de trouver et comprendre les données pertinentes.
**Le RAG agentique est un moyen puissant d'utiliser les agents pour répondre aux questions sur vos données.** Nous pouvons passer divers outils à Alfred pour l'aider à répondre aux questions.
Cependant, au lieu de répondre automatiquement à la question sur la base de documents, Alfred peut décider d'utiliser tout autre outil ou flux pour répondre à la question.

![RAG agentique](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/llama-index/agentic-rag.png)

Commençons **à construire notre *workflow* de RAG agentique !**

Tout d'abord, nous allons créer un outil de RAG pour récupérer les détails à jour sur les invités. Ensuite, nous développerons des outils pour la recherche web, les mises à jour météorologiques et les statistiques de téléchargement de modèles sur le Hub d'Hugging Face. Enfin, nous intégrerons le tout pour donner vie à notre agent de RAG agentique !

### Création d'un RAG pour converser avec les invités
https://huggingface.co/learn/agents-course/fr/unit3/agentic-rag/invitees.md

# Création d'un RAG pour converser avec les invités

Alfred, votre agent de confiance, se prépare pour le gala le plus extravagant du siècle. Pour s'assurer que l'événement se déroule sans encombre, il a besoin d'un accès rapide à des informations à jour sur chaque invité. Aidons le en créant un outil RAG alimenté par notre jeu de données personnalisé.

## Pourquoi un RAG pour un gala ?

Imaginez Alfred se mêlant aux invités, ayant besoin de se rappeler des détails spécifiques sur chaque personne à tout moment. Un LLM traditionnel pourrait avoir du mal avec cette tâche parce que :

1. La liste des invités est spécifique à votre événement et ne fait pas partie des données d'entraînement du modèle
2. Les informations sur les invités peuvent changer ou être mises à jour fréquemment
3. Alfred doit récupérer des détails précis comme les adresses email

C'est là que le RAG brille ! En combinant un système de récupération avec un LLM, Alfred peut accéder à des informations précises et à jour sur vos invités à la demande.

> [!TIP]
> Vous pouvez choisir n'importe lequel des frameworks couverts dans le cours pour ce cas d'usage. Sélectionnez votre option préférée dans les onglets de code.

## Configuration de notre application

Dans cette unité, nous développerons notre agent au sein d'un *Space*, sous la forme d'un projet Python structuré. Cette approche nous aide à maintenir un code propre et modulaire en organisant différentes fonctionnalités dans des fichiers séparés. De plus, cela permet un cas d'usage plus réaliste où vous déploieriez l'application pour une utilisation publique.

### Structure du projet

- **`tools.py`** – Fournit des outils auxiliaires pour l'agent.
- **`retriever.py`** – Implémente les fonctions de récupération pour soutenir l'accès à la connaissance.
- **`app.py`** – Intègre tous les composants dans un agent entièrement fonctionnel, que nous finaliserons dans la dernière partie de cette unité.

Pour une référence pratique, consultez [ce *Space*](https://huggingface.co/spaces/agents-course/Unit_3_Agentic_RAG) sur un RAG agentique. N'hésitez pas à le cloner et à expérimenter !

Vous pouvez tester directement l'agent ci-dessous :

## Aperçu du jeu de données

Notre jeu de données [`agents-course/unit3-invitees`](https://huggingface.co/datasets/agents-course/unit3-invitees/) contient les champs suivants pour chaque invité :

- **Name** : Nom complet de l'invité
- **Relation** : Comment l'invité est lié à l'hôte
- **Description** : Une brève biographie ou des faits intéressants sur l'invité
- **Email Address** : Informations de contact pour envoyer des invitations ou des suivis

Voici un aperçu du jeu de données :

> [!TIP]
> Dans un scénario réel, ce jeu de données pourrait être étendu pour inclure les préférences alimentaires, les intérêts pour les cadeaux, les sujets de conversation à éviter, et d'autres détails utiles pour un hôte.

## Construction d'un outil pour un livre d'or

Nous allons créer un outil personnalisé qu'Alfred peut utiliser pour récupérer rapidement les informations sur les invités pendant le gala. Décomposons cela en trois étapes gérables :

1. Charger et préparer le jeu de données
2. Créer l'outil de récupération
3. Intégrer l'outil à Alfred

Commençons par charger et préparer le jeu de données !

### Étape 1 : Charger et préparer le jeu de données

Tout d'abord, nous devons transformer nos données brutes sur les invités en un format optimisé pour la récupération.

Nous utiliserons la bibliothèque `datasets` d'Hugging Face pour charger le jeu de données et le convertir en une liste d'objets `Document` du module `langchain.docstore.document`.

```python
import datasets
from langchain_core.documents import Document

# Charger le jeu de données
guest_dataset = datasets.load_dataset("agents-course/unit3-invitees", split="train")

# Convertir les entrées du jeu de données en objets Document
docs = [
    Document(
        page_content="\n".join([
            f"Name: {guest['name']}",
            f"Relation: {guest['relation']}",
            f"Description: {guest['description']}",
            f"Email: {guest['email']}"
        ]),
        metadata={"name": guest["name"]}
    )
    for guest in guest_dataset
]

```

Nous utiliserons la bibliothèque `datasets` d'Hugging Face pour charger le jeu de données et le convertir en une liste d'objets `Document` du module `llama_index.core.schema`.

```python
import datasets
from llama_index.core.schema import Document

# Charger le jeu de données
guest_dataset = datasets.load_dataset("agents-course/unit3-invitees", split="train")

# Convertir les entrées du jeu de données en objets Document
docs = [
    Document(
        text="\n".join([
            f"Name: {guest_dataset['name'][i]}",
            f"Relation: {guest_dataset['relation'][i]}",
            f"Description: {guest_dataset['description'][i]}",
            f"Email: {guest_dataset['email'][i]}"
        ]),
        metadata={"name": guest_dataset['name'][i]}
    )
    for i in range(len(guest_dataset))
]
```

Nous utiliserons la bibliothèque `datasets` d'Hugging Face pour charger le jeu de données et le convertir en une liste d'objets `Document` du module `langchain.docstore.document`.

```python
import datasets
from langchain_core.documents import Document

# Charger le jeu de données
guest_dataset = datasets.load_dataset("agents-course/unit3-invitees", split="train")

# Convertir les entrées du jeu de données en objets Document
docs = [
    Document(
        page_content="\n".join([
            f"Name: {guest['name']}",
            f"Relation: {guest['relation']}",
            f"Description: {guest['description']}",
            f"Email: {guest['email']}"
        ]),
        metadata={"name": guest["name"]}
    )
    for guest in guest_dataset
]
```

Dans le code ci-dessus, nous :
- Chargeons le jeu de données
- Convertissons chaque entrée d'invité en un objet `Document` avec du contenu formaté
- Stockons les objets `Document` dans une liste

Cela signifie que nous avons toutes nos données bien disponibles pour pouvoir commencer à configurer notre récupération.

### Étape 2 : Créer l'outil de récupération

Maintenant, créons un outil personnalisé qu'Alfred peut utiliser pour rechercher dans nos informations sur les invités.

Nous utiliserons le `BM25Retriever` du module `langchain_community.retrievers` pour créer un outil de récupération.

> [!TIP]
> Le BM25Retriever est un excellent point de départ pour la récupération, mais pour une recherche sémantique plus avancée, vous pourriez considérer l'utilisation de récupérateurs basés sur des embeddings comme ceux de sentence-transformers.

```python
from smolagents import Tool
from langchain_community.retrievers import BM25Retriever

class GuestInfoRetrieverTool(Tool):
    name = "guest_info_retriever"
    description = "Récupère des informations détaillées sur les invités du gala basées sur leur nom ou relation."
    inputs = {
        "query": {
            "type": "string",
            "description": "Le nom ou la relation de l'invité sur lequel vous voulez des informations."
        }
    }
    output_type = "string"

    def __init__(self, docs):
        self.is_initialized = False
        self.retriever = BM25Retriever.from_documents(docs)

    def forward(self, query: str):
        results = self.retriever.get_relevant_documents(query)
        if results:
            return "\n\n".join([doc.page_content for doc in results[:3]])
        else:
            return "Aucune information d'invité correspondante trouvée."

# Initialiser l'outil
guest_info_tool = GuestInfoRetrieverTool(docs)
```

Comprenons cet outil étape par étape :
- Le `name` et la `description` aident l'agent à comprendre quand et comment utiliser cet outil
- Les `inputs` définissent quels paramètres l'outil attend (dans ce cas, une requête de recherche)
- Nous utilisons un `BM25Retriever`, qui est un algorithme de récupération de texte puissant qui ne nécessite pas d'*embeddings*
- La méthode `forward` traite la requête et retourne les informations d'invité les plus pertinentes

Nous utiliserons le `BM25Retriever` du module `llama_index.retrievers.bm25` pour créer un outil de récupération.

> [!TIP]
> Le BM25Retriever est un excellent point de départ pour la récupération, mais pour une recherche sémantique plus avancée, vous pourriez considérer l'utilisation de récupérateurs basés sur des *embeddings* comme ceux de sentence-transformers.

```python
from llama_index.core.tools import FunctionTool
from llama_index.retrievers.bm25 import BM25Retriever

bm25_retriever = BM25Retriever.from_defaults(nodes=docs)

def get_guest_info_retriever(query: str) -> str:
    """Récupère des informations détaillées sur les invités du gala basées sur leur nom ou relation."""
    results = bm25_retriever.retrieve(query)
    if results:
        return "\n\n".join([doc.text for doc in results[:3]])
    else:
        return "Aucune information d'invité correspondante trouvée."

# Initialiser l'outil
guest_info_tool = FunctionTool.from_defaults(get_guest_info_retriever)
```

Comprenons cet outil étape par étape :
- La *docstring* aide l'agent à comprendre quand et comment utiliser cet outil
- Les décorateurs de type définissent quels paramètres l'outil attend (dans ce cas, une requête de recherche)
- Nous utilisons un `BM25Retriever`, qui est un algorithme de récupération de texte puissant qui ne nécessite pas d'*embeddings*
- La méthode traite la requête et retourne les informations d'invité les plus pertinentes

Nous utiliserons le `BM25Retriever` du module `langchain_community.retrievers` pour créer un outil de récupération.

> [!TIP]
> Le BM25Retriever est un excellent point de départ pour la récupération, mais pour une recherche sémantique plus avancée, vous pourriez considérer l'utilisation de récupérateurs basés sur des *embeddings* comme ceux de sentence-transformers.

```python
from langchain_community.retrievers import BM25Retriever
from langchain_core.tools import Tool

bm25_retriever = BM25Retriever.from_documents(docs)

def extract_text(query: str) -> str:
    """Récupère des informations détaillées sur les invités du gala basées sur leur nom ou relation."""
    results = bm25_retriever.invoke(query)
    if results:
        return "\n\n".join([doc.page_content for doc in results[:3]])
    else:
        return "Aucune information d'invité correspondante trouvée."

guest_info_tool = Tool(
    name="guest_info_retriever",
    func=extract_text,
    description="Récupère des informations détaillées sur les invités du gala basées sur leur nom ou relation."
)
```

Comprenons cet outil étape par étape :
- Le `name` et la `description` aident l'agent à comprendre quand et comment utiliser cet outil
- Les décorateurs de type définissent quels paramètres l'outil attend (dans ce cas, une requête de recherche)
- Nous utilisons un `BM25Retriever`, qui est un algorithme de récupération de texte puissant qui ne nécessite pas d'*embeddings*
- La méthode traite la requête et retourne les informations d'invité les plus pertinentes

### Étape 3 : Intégrer l'outil avec Alfred

Enfin, assemblons le tout en créant notre agent et en l'équipant de notre outil personnalisé :

```python
from smolagents import CodeAgent, InferenceClientModel

# Initialiser le modèle Hugging Face
model = InferenceClientModel()

# Créer Alfred, notre agent de gala, avec l'outil d'informations sur les invités
alfred = CodeAgent(tools=[guest_info_tool], model=model)

# Exemple de requête qu'Alfred pourrait recevoir pendant le gala
response = alfred.run("Parlez-moi de notre invitée nommée 'Lady Ada Lovelace'.")

print("🎩 Réponse d'Alfred :")
print(response)
```

Sortie attendue :

```
🎩 Réponse d'Alfred :
Basé sur les informations que j'ai récupérées, Lady Ada Lovelace est une mathématicienne estimée et une amie. Elle est renommée pour son travail pionnier en mathématiques et en informatique, souvent célébrée comme la première programmeuse informatique en raison de son travail sur la machine analytique de Charles Babbage. Son adresse email est ada.lovelace@example.com.
```

Ce qui se passe dans cette étape finale :
- Nous initialisons un modèle Hugging Face en utilisant la classe `InferenceClientModel`
- Nous créons notre agent (Alfred) comme un `CodeAgent`, qui peut exécuter du code Python pour résoudre des problèmes
- Nous demandons à Alfred de récupérer des informations sur une invitée nommée "Lady Ada Lovelace"

```python
from llama_index.core.agent.workflow import AgentWorkflow
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI

# Initialiser le modèle Hugging Face
llm = HuggingFaceInferenceAPI(model_name="Qwen/Qwen2.5-Coder-32B-Instruct")

# Créer Alfred, notre agent de gala, avec l'outil d'informations sur les invités
alfred = AgentWorkflow.from_tools_or_functions(
    [guest_info_tool],
    llm=llm,
)

# Exemple de requête qu'Alfred pourrait recevoir pendant le gala
response = await alfred.run("Parlez-moi de notre invitée nommée 'Lady Ada Lovelace'.")

print("🎩 Réponse d'Alfred :")
print(response)
```

Sortie attendue :

```
🎩 Réponse d'Alfred :
Lady Ada Lovelace est une mathématicienne estimée et amie, renommée pour son travail pionnier en mathématiques et en informatique. Elle est célébrée comme la première programmeuse informatique en raison de son travail sur la machine analytique de Charles Babbage. Son email est ada.lovelace@example.com.
```

Ce qui se passe dans cette étape finale :
- Nous initialisons un modèle Hugging Face en utilisant la classe `HuggingFaceInferenceAPI`
- Nous créons notre agent (Alfred) comme un `AgentWorkflow`, incluant l'outil que nous venons de créer
- Nous demandons à Alfred de récupérer des informations sur une invitée nommée "Lady Ada Lovelace"

```python
from typing import TypedDict, Annotated
from langgraph.graph.message import add_messages
from langchain_core.messages import AnyMessage, HumanMessage, AIMessage
from langgraph.prebuilt import ToolNode
from langgraph.graph import START, StateGraph
from langgraph.prebuilt import tools_condition
from langchain_huggingface import HuggingFaceEndpoint, ChatHuggingFace

# Générer l'interface de chat, incluant les outils
llm = HuggingFaceEndpoint(
    repo_id="Qwen/Qwen2.5-Coder-32B-Instruct",
    huggingfacehub_api_token=HUGGINGFACEHUB_API_TOKEN,
)

chat = ChatHuggingFace(llm=llm, verbose=True)
tools = [guest_info_tool]
chat_with_tools = chat.bind_tools(tools)

# Générer l'AgentState et le graphe d'agent
class AgentState(TypedDict):
    messages: Annotated[list[AnyMessage], add_messages]

def assistant(state: AgentState):
    return {
        "messages": [chat_with_tools.invoke(state["messages"])],
    }

## Le graphe
builder = StateGraph(AgentState)

# Définir les nœuds : ils font le travail
builder.add_node("assistant", assistant)
builder.add_node("tools", ToolNode(tools))

# Définir les arêtes : elles déterminent comment le flux de contrôle se déplace
builder.add_edge(START, "assistant")
builder.add_conditional_edges(
    "assistant",
    # Si le dernier message nécessite un outil, router vers les outils
    # Sinon, fournir une réponse directe
    tools_condition,
)
builder.add_edge("tools", "assistant")
alfred = builder.compile()

messages = [HumanMessage(content="Parlez-moi de notre invitée nommée 'Lady Ada Lovelace'.")]
response = alfred.invoke({"messages": messages})

print("🎩 Réponse d'Alfred :")
print(response['messages'][-1].content)
```

Sortie attendue :

```
🎩 Réponse d'Alfred :
Lady Ada Lovelace est une mathématicienne estimée et pionnière en informatique, souvent célébrée comme la première programmeuse informatique en raison de son travail sur la machine analytique de Charles Babbage.
```

Ce qui se passe dans cette étape finale :
- Nous initialisons un modèle Hugging Face en utilisant la classe `HuggingFaceEndpoint`. Nous générons aussi une interface de chat et ajoutons les outils.
- Nous créons notre agent (Alfred) comme un `StateGraph`, qui combine 2 nœuds (`assistant`, `tools`) en utilisant une arête
- Nous demandons à Alfred de récupérer des informations sur une invitée nommée "Lady Ada Lovelace"

## Exemple d'interaction

Pendant le gala, une conversation pourrait se dérouler comme ceci :

**Vous :** "Alfred, qui est ce monsieur qui parle à l'ambassadeur ?"

**Alfred :** *recherche rapidement dans la base de données des invités* "C'est le Dr. Nikola Tesla, monsieur. C'est un vieil ami de votre période à l'université. Il vient récemment de breveter un nouveau système de transmission d'énergie sans fil et serait ravi d'en discuter avec vous. N'oubliez pas qu'il est passionné par les pigeons, donc cela pourrait faire une bonne conversation."

```json
{
    "name": "Dr. Nikola Tesla",
    "relation": "vieil ami des jours d'université",  
    "description": "Le Dr. Nikola Tesla est un vieil ami de votre période à l'université. Il vient récemment de breveter un nouveau système de transmission d'énergie sans fil et serait ravi d'en discuter avec vous. N'oubliez pas qu'il est passionné par les pigeons, donc cela pourrait faire une bonne conversation.",
    "email": "nikola.tesla@gmail.com"
}
```

## Aller plus loin

Maintenant qu'Alfred peut récupérer des informations sur les invités, considérez comment vous pourriez améliorer ce système :

1. **Améliorer le récupérateur** pour utiliser un algorithme plus sophistiqué comme ceux disponibles dans [`sentence-transformers`](https://www.sbert.net/)
2. **Implémenter une mémoire de conversation** pour qu'Alfred se souvienne des interactions précédentes
3. **Combiner avec la recherche web** pour obtenir les dernières informations sur les invités inconnus
4. **Intégrer plusieurs index** pour obtenir des informations plus complètes à partir de sources vérifiées

Maintenant Alfred est entièrement équipé pour gérer sans effort les questions sur les invités, s'assurant que votre gala soit mémorisé comme l'événement le plus sophistiqué et délicieux du siècle !

> [!TIP]
> Essayez d'étendre l'outil de récupération pour aussi retourner des amorces de conversation basées sur les intérêts ou l'arrière-plan de chaque invité. Comment modifieriez-vous l'outil pour accomplir cela ?
>
> Quand vous avez terminé, implémentez votre outil de récupération d'invités dans le fichier retriever.py du Space.

### Conclusion
https://huggingface.co/learn/agents-course/fr/unit3/agentic-rag/conclusion.md

# Conclusion

Dans cette unité, nous avons appris comment créer un système de RAG agentique pour aider Alfred, notre sympathique agent, à préparer et gérer un gala exceptionnel.

La combinaison du RAG avec les capacités agentiques démontre à quel point les assistants IA peuvent devenir puissants quand ils ont :
- Accès à de la connaissance structurée (informations sur les invités)
- La capacité de récupérer des informations en temps réel (recherche web)
- Accès à des outils spécifiques au domaine (informations météorologiques, statistiques  du Hub)
- Une mémoire des interactions passées

Avec ces capacités, Alfred est maintenant bien équipé pour être l'hôte parfait, capable de répondre aux questions sur les invités, fournir des informations à jour, et s'assurer que le gala se déroule sans encombre—gérant même le timing parfait pour le spectacle pyrotechnique !

> [!TIP]
> Maintenant que vous avez construit un agent complet, vous pourriez vouloir :
>
> - Créer des outils plus spécialisés pour vos propres cas d'usage
> - Implémenter des systèmes de RAG plus sophistiqués avec des embeddings
> - Construire des systèmes multi-agents où les agents peuvent collaborer
> - Déployer votre agent comme un service avec lequel d'autres peuvent interagir

### Création et intégration d'outils pour votre agent
https://huggingface.co/learn/agents-course/fr/unit3/agentic-rag/tools.md

# Création et intégration d'outils pour votre agent

Dans cette section, nous allons donner à Alfred l'accès au web, lui permettant de trouver les dernières nouvelles et mises à jour mondiales. 
De plus, il aura accès aux données météorologiques et aux statistiques de téléchargement des modèles du Hub d'Hugging Face Hub, pour qu'il puisse faire des conversations pertinentes sur des sujets frais.

## Donnez à votre agent l'accès au web

Rappelez-vous que nous voulons qu'Alfred établisse sa présence comme un véritable hôte de la renaissance, avec une connaissance approfondie du monde.

Pour ce faire, nous devons nous assurer qu'Alfred a accès aux dernières nouvelles et informations sur le monde.

Commençons par créer un outil de recherche web pour Alfred !

```python
from smolagents import DuckDuckGoSearchTool

# Initialiser l'outil de recherche DuckDuckGo
search_tool = DuckDuckGoSearchTool()

# Exemple d'usage
results = search_tool("Qui est l'actuel Président de la France ?")
print(results)
```

Sortie attendue :

```
L'actuel Président de la France est Emmanuel Macron.
```

```python
from llama_index.tools.duckduckgo import DuckDuckGoSearchToolSpec
from llama_index.core.tools import FunctionTool

# Initialiser l'outil de recherche DuckDuckGo
tool_spec = DuckDuckGoSearchToolSpec()

search_tool = FunctionTool.from_defaults(tool_spec.duckduckgo_full_search)
# Exemple d'usage
response = search_tool("Qui est l'actuel Président de la France ?")
print(response.raw_output[-1]['body'])
```

Sortie attendue :

```
Le Président de la République française est le chef d'État de la France. L'actuel Président est Emmanuel Macron depuis le 14 mai 2017 après avoir battu Marine Le Pen au second tour de l'élection présidentielle le 7 mai 2017. Liste des présidents français (Cinquième République) N° Portrait Nom ...
```

```python
from langchain_community.tools import DuckDuckGoSearchRun

search_tool = DuckDuckGoSearchRun()
results = search_tool.invoke("Qui est l'actuel Président de la France ?")
print(results)
```

Sortie attendue :

```
Emmanuel Macron (né le 21 décembre 1977 à Amiens, France) est un banquier et homme politique français qui a été élu président de la France en 2017...
```

## Création d'un outil personnalisé pour les informations météorologiques afin de programmer le feu d'artifice

Le gala parfait aurait un feu d'artifice dans un ciel clair, nous devons nous assurer qu'il ne soit pas annulé à cause du mauvais temps.

Créons un outil personnalisé qui peut être utilisé pour appeler une API météo externe et obtenir les informations pour un lieu donné.

> [!TIP]
> Par souci de simplicité, nous utilisons une API météorologique factice pour cet exemple. Si vous voulez utiliser une véritable API, vous pourriez implémenter un outil utilisant l'API OpenWeatherMap comme dans l'Unité 1.

```python
from smolagents import Tool
import random

class WeatherInfoTool(Tool):
    name = "weather_info"
    description = "Récupère des informations météorologiques factices pour un lieu donné."
    inputs = {
        "location": {
            "type": "string",
            "description": "Le lieu pour lequel obtenir les informations météorologiques."
        }
    }
    output_type = "string"

    def forward(self, location: str):
        # Données météorologiques factices
        weather_conditions = [
            {"condition": "Pluvieux", "temp_c": 15},
            {"condition": "Clair", "temp_c": 25},
            {"condition": "Venteux", "temp_c": 20}
        ]
        # Sélectionner aléatoirement une condition météorologique
        data = random.choice(weather_conditions)
        return f"Météo à {location}: {data['condition']}, {data['temp_c']}°C"

# Initialiser l'outil
weather_info_tool = WeatherInfoTool()
```

```python
import random
from llama_index.core.tools import FunctionTool

def get_weather_info(location: str) -> str:
    """Récupère des informations météorologiques factices pour un lieu donné."""
    # Données météorologiques factices
    weather_conditions = [
        {"condition": "Pluvieux", "temp_c": 15},
        {"condition": "Clair", "temp_c": 25},
        {"condition": "Venteux", "temp_c": 20}
    ]
    # Sélectionner aléatoirement une condition météorologique
    data = random.choice(weather_conditions)
    return f"Météo à {location}: {data['condition']}, {data['temp_c']}°C"

# Initialiser l'outil
weather_info_tool = FunctionTool.from_defaults(get_weather_info)
```

```python
from langchain_core.tools import Tool
import random

def get_weather_info(location: str) -> str:
    """Récupère des informations météorologiques factices pour un lieu donné."""
    # Données météorologiques factices
    weather_conditions = [
        {"condition": "Pluvieux", "temp_c": 15},
        {"condition": "Clair", "temp_c": 25},
        {"condition": "Venteux", "temp_c": 20}
    ]
    # Sélectionner aléatoirement une condition météorologique
    data = random.choice(weather_conditions)
    return f"Météo à {location}: {data['condition']}, {data['temp_c']}°C"

# Initialiser l'outil
weather_info_tool = Tool(
    name="get_weather_info",
    func=get_weather_info,
    description="Récupère des informations météorologiques factices pour un lieu donné."
)
```

## Création d'un outil pour obtenir les statistiques du Hub concernant les constructeurs d'IA influents

Le gala réunit le gratin des constructeurs d'IA. Alfred veut les impressionner en discutant de leurs modèles, jeux de données et Spaces les plus populaires. Nous créerons un outil pour récupérer les statistiques des modèles du Hub à partir d'un nom d'utilisateur.

```python
from smolagents import Tool
from huggingface_hub import list_models

class HubStatsTool(Tool):
    name = "hub_stats"
    description = "Récupère le modèle le plus téléchargé d'un auteur spécifique sur le Hugging Face Hub."
    inputs = {
        "author": {
            "type": "string",
            "description": "Le nom d'utilisateur de l'auteur/organisation du modèle pour trouver des modèles."
        }
    }
    output_type = "string"

    def forward(self, author: str):
        try:
            # Lister les modèles de l'auteur spécifié, triés par téléchargements
            models = list(list_models(author=author, sort="downloads", direction=-1, limit=1))
            
            if models:
                model = models[0]
                return f"Le modèle le plus téléchargé par {author} est {model.id} avec {model.downloads:,} téléchargements."
            else:
                return f"Aucun modèle trouvé pour l'auteur {author}."
        except Exception as e:
            return f"Erreur lors de la récupération des modèles pour {author}: {str(e)}"

# Initialiser l'outil
hub_stats_tool = HubStatsTool()

# Exemple d'usage
print(hub_stats_tool("facebook")) # Exemple : Obtenir le modèle le plus téléchargé par Facebook
```

Sortie attendue :

```
Le modèle le plus téléchargé par facebook est facebook/esmfold_v1 avec 12,544,550 téléchargements.
```

```python
import random
from llama_index.core.tools import FunctionTool
from huggingface_hub import list_models

def get_hub_stats(author: str) -> str:
    """Récupère le modèle le plus téléchargé d'un auteur spécifique sur le Hugging Face Hub."""
    try:
        # Lister les modèles de l'auteur spécifié, triés par téléchargements
        models = list(list_models(author=author, sort="downloads", direction=-1, limit=1))

        if models:
            model = models[0]
            return f"Le modèle le plus téléchargé par {author} est {model.id} avec {model.downloads:,} téléchargements."
        else:
            return f"Aucun modèle trouvé pour l'auteur {author}."
    except Exception as e:
        return f"Erreur lors de la récupération des modèles pour {author}: {str(e)}"

# Initialiser l'outil
hub_stats_tool = FunctionTool.from_defaults(get_hub_stats)

# Exemple d'usage
print(hub_stats_tool("facebook")) # Exemple : Obtenir le modèle le plus téléchargé par Facebook
```

Sortie attendue :

```
Le modèle le plus téléchargé par facebook est facebook/esmfold_v1 avec 12,544,550 téléchargements.
```

```python
from langchain_core.tools import Tool
from huggingface_hub import list_models

def get_hub_stats(author: str) -> str:
    """Récupère le modèle le plus téléchargé d'un auteur spécifique sur le Hugging Face Hub."""
    try:
        # Lister les modèles de l'auteur spécifié, triés par téléchargements
        models = list(list_models(author=author, sort="downloads", direction=-1, limit=1))

        if models:
            model = models[0]
            return f"Le modèle le plus téléchargé par {author} est {model.id} avec {model.downloads:,} téléchargements."
        else:
            return f"Aucun modèle trouvé pour l'auteur {author}."
    except Exception as e:
        return f"Erreur lors de la récupération des modèles pour {author}: {str(e)}"

# Initialiser l'outil
hub_stats_tool = Tool(
    name="get_hub_stats",
    func=get_hub_stats,
    description="Récupère le modèle le plus téléchargé d'un auteur spécifique sur le Hugging Face Hub."
)

# Exemple d'usage
print(hub_stats_tool.invoke("facebook")) # Exemple : Obtenir le modèle le plus téléchargé par Facebook
```

Sortie attendue :

```
Le modèle le plus téléchargé par facebook est facebook/esmfold_v1 avec 13 109 861 téléchargements.
```

Avec l'outil de statistiques, Alfred peut maintenant impressionner les constructeurs d'IA influents en discutant de leurs modèles les plus populaires.

## Intégration des outils

Maintenant que nous avons tous les outils, intégrons-les dans l'agent d'Alfred :

```python
from smolagents import CodeAgent, InferenceClientModel

# Initialiser le modèle Hugging Face
model = InferenceClientModel()

# Créer Alfred avec tous les outils
alfred = CodeAgent(
    tools=[search_tool, weather_info_tool, hub_stats_tool], 
    model=model
)

# Exemple de requête qu'Alfred pourrait recevoir pendant le gala
response = alfred.run("Qu'est-ce que Facebook et quel est leur modèle le plus populaire ?")

print("🎩 Réponse d'Alfred :")
print(response)
```

Sortie attendue :

```
🎩 Réponse d'Alfred :
Facebook est un site web de réseau social où les utilisateurs peuvent se connecter, partager des informations et interagir avec d'autres. Le modèle le plus téléchargé par Facebook sur le Hugging Face Hub est ESMFold_v1.
```

```python
from llama_index.core.agent.workflow import AgentWorkflow
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI

# Initialiser le modèle Hugging Face
llm = HuggingFaceInferenceAPI(model_name="Qwen/Qwen2.5-Coder-32B-Instruct")
# Créer Alfred avec tous les outils
alfred = AgentWorkflow.from_tools_or_functions(
    [search_tool, weather_info_tool, hub_stats_tool],
    llm=llm
)

# Exemple de requête qu'Alfred pourrait recevoir pendant le gala
response = await alfred.run("Qu'est-ce que Facebook et quel est leur modèle le plus populaire ?")

print("🎩 Réponse d'Alfred :")
print(response)
```

Sortie attendue :

```
🎩 Réponse d'Alfred :
Facebook est un service de réseau social et une entreprise technologique basée à Menlo Park, en Californie. Elle a été fondée par Mark Zuckerberg et permet aux gens de créer des profils, se connecter avec des amis et la famille, partager des photos et vidéos, et rejoindre des groupes basés sur des intérêts partagés. Le modèle le plus populaire par Facebook sur le Hugging Face Hub est facebook/esmfold_v1 avec 13 109 861 téléchargements.
```

```python
from typing import TypedDict, Annotated
from langgraph.graph.message import add_messages
from langchain_core.messages import AnyMessage, HumanMessage, AIMessage
from langgraph.prebuilt import ToolNode
from langgraph.graph import START, StateGraph
from langgraph.prebuilt import tools_condition
from langchain_huggingface import HuggingFaceEndpoint, ChatHuggingFace

# Générer l'interface de chat, incluant les outils
llm = HuggingFaceEndpoint(
    repo_id="Qwen/Qwen2.5-Coder-32B-Instruct",
    huggingfacehub_api_token=HUGGINGFACEHUB_API_TOKEN,
)

chat = ChatHuggingFace(llm=llm, verbose=True)
tools = [search_tool, weather_info_tool, hub_stats_tool]
chat_with_tools = chat.bind_tools(tools)

# Générer l'AgentState et le graphe d'agent
class AgentState(TypedDict):
    messages: Annotated[list[AnyMessage], add_messages]

def assistant(state: AgentState):
    return {
        "messages": [chat_with_tools.invoke(state["messages"])],
    }

## Le graphe
builder = StateGraph(AgentState)

# Définir les nœuds : ils font le travail
builder.add_node("assistant", assistant)
builder.add_node("tools", ToolNode(tools))

# Définir les arêtes : elles déterminent comment le flux de contrôle se déplace
builder.add_edge(START, "assistant")
builder.add_conditional_edges(
    "assistant",
    # Si le dernier message nécessite un outil, router vers les outils
    # Sinon, fournir une réponse directe
    tools_condition,
)
builder.add_edge("tools", "assistant")
alfred = builder.compile()

messages = [HumanMessage(content="Qui est Facebook et quel est leur modèle le plus populaire ?")]
response = alfred.invoke({"messages": messages})

print("🎩 Réponse d'Alfred :")
print(response['messages'][-1].content)
```

Sortie attendue :

```
🎩 Réponse d'Alfred :
Facebook est une entreprise de médias sociaux connue pour son site de réseau social, Facebook, ainsi que d'autres services comme Instagram et WhatsApp. Le modèle le plus téléchargé par Facebook sur le Hugging Face Hub est facebook/esmfold_v1 avec 13 202 321 téléchargements.
```

## Conclusion

En intégrant ces outils, Alfred est maintenant équipé pour gérer une variété de tâches, des recherches web aux mises à jour météorologiques et aux statistiques de modèles. Cela garantit qu'il reste l'hôte le plus informé et engageant du gala.

> [!TIP]
> Essayez d'implémenter un outil qui peut être utilisé pour obtenir les dernières nouvelles sur un sujet spécifique.
>
> Quand vous avez terminé, implémentez vos outils personnalisés dans le fichier tools.py.

### Introduction au cas d'usage sur la RAG agentique
https://huggingface.co/learn/agents-course/fr/unit3/agentic-rag/introduction.md

# Introduction au cas d'usage sur la RAG agentique

![Bannière RAG agentique](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit3/agentic-rag/thumbnail.jpg)

Dans cette unité, nous aiderons Alfred, notre sympathique agent qui organise le gala, en utilisant le RAG agentique pour créer un outil qui peut être utilisé pour répondre aux questions sur les invités du gala.

> [!TIP]
> Il s'agit d'un cas d'usage concret pour le RAG agentique, que vous pourriez utiliser pour vos propres projets ou à votre travail. Si vous souhaitez tirer davantage parti de ce projet, pourquoi ne pas l'essayer sur votre propre cas d'usage et partager sur Discord ?

Vous pouvez choisir n'importe lequel des *frameworks* abordés dans le cours pour ce cas d'usage. Nous fournissons des exemples de code pour chacun dans des onglets séparés.

## Un gala mémorable

Maintenant, il est temps de se salir les mains avec un vrai cas d'usage. Plantons le décor !

**Vous avez décidé d'organiser la fête la plus extravagante et opulente du siècle.** Cela signifie des festins somptueux, des danseurs enchanteurs, des DJ renommés, des boissons exquises, un spectacle de feux d'artifice époustouflant, et bien plus encore.

Alfred, votre agent, se prépare à veiller sur tous vos besoins pour cette fête, et **il va tout gérer lui-même**. Pour ce faire, il doit avoir accès à toutes les informations sur la fête, y compris le menu, les invités, le programme, les prévisions météorologiques, etc.

Non seulement cela, mais il doit aussi s'assurer que la fête va être un succès, donc **il doit être capable de répondre à toutes les questions sur la fête pendant celle-ci**, tout en gérant les situations imprévues qui peuvent survenir.

Alfred ne peut pas le faire seul, donc nous devons nous assurer qu'il a accès à toutes les informations et outils dont il a besoin.

Tout d'abord, donnons-lui une liste d'exigences strictes pour le gala.

## Les exigences du gala

Une personne correctement éduquée de l'époque de la **Renaissance** devait avoir trois traits principaux.
Elle devait avoir une connaissance approfondie en **sport, culture et science**. Donc, nous devons nous assurer que nous pouvons impressionner nos invités avec notre connaissance et leur offrir un gala vraiment inoubliable.
Cependant, pour éviter tout conflit, il y a **certains sujets, comme la politique et la religion, qui doivent être évités lors d'un gala.** Il doit s'agir d'une fête amusante sans conflits liés aux croyances et aux idéaux.

Selon l'étiquette, **un bon hôte doit connaître les antécédents des invités**, y compris leurs centres d'intérêt et leurs activités. Un bon hôte fait aussi des commérages et partage des histoires sur les invités les uns avec les autres.

Enfin, nous devons nous assurer que nous avons **des connaissances générales sur la météo** afin de pouvoir trouver en permanence une mise à jour en temps réel pour garantir un timing parfait pour le lancement du feu d'artifice pour terminer le gala en beauté ! 🎆

Comme vous pouvez le voir, Alfred a besoin de beaucoup d'informations pour organiser le gala.
Heureusement, nous pouvons l'aider et le préparer en lui donnant un entraînement en ***Retrieval Augmented Generation* (RAG) !**

Commençons par créer les outils dont Alfred a besoin pour pouvoir organiser le gala !

### Création de l'agent pour le gala
https://huggingface.co/learn/agents-course/fr/unit3/agentic-rag/agent.md

# Création de l'agent pour le gala

Maintenant que nous avons construit tous les composants nécessaires pour Alfred, il est temps de tout rassembler en un agent complet qui peut aider à organiser notre gala.

Dans cette section, nous allons combiner les outils de récupération d'informations sur les invités, de recherche web, d'informations météorologiques et de statistiques  du Hub en un seul agent puissant.

## Assemblage d'Alfred : l'agent complet

Au lieu de réimplémenter tous les outils que nous avons créés dans les sections précédentes, nous les importerons à partir de leurs modules respectifs que nous avons sauvegardés dans les fichiers `tools.py` et `retriever.py`.

> [!TIP]
> Si vous n'avez pas encore implémenté les outils, retournez aux sections outils et récupérateur pour les implémenter, et ajoutez-les aux fichiers tools.py et retriever.py.

Importons les bibliothèques nécessaires et les outils des sections précédentes :

```python
# Importer les bibliothèques nécessaires
import random
from smolagents import CodeAgent, InferenceClientModel

# Importer nos outils personnalisés de leurs modules
from tools import DuckDuckGoSearchTool, WeatherInfoTool, HubStatsTool
from retriever import load_guest_dataset
```

Maintenant, combinons tous ces outils en un seul agent :

```python
# Initialiser le modèle Hugging Face
model = InferenceClientModel()

# Initialiser l'outil de recherche web
search_tool = DuckDuckGoSearchTool()

# Initialiser l'outil météorologique
weather_info_tool = WeatherInfoTool()

# Initialiser l'outil de statistiques Hub
hub_stats_tool = HubStatsTool()

# Charger le jeu de données des invités et initialiser l'outil d'informations sur les invités
guest_info_tool = load_guest_dataset()

# Créer Alfred avec tous les outils
alfred = CodeAgent(
    tools=[guest_info_tool, weather_info_tool, hub_stats_tool, search_tool], 
    model=model,
    add_base_tools=True,  # Ajouter tous les outils de base supplémentaires
    planning_interval=3   # Activer la planification toutes les 3 étapes
)
```

```python
# Importer les bibliothèques nécessaires
from llama_index.core.agent.workflow import AgentWorkflow
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI

from tools import search_tool, weather_info_tool, hub_stats_tool
from retriever import guest_info_tool
```

Maintenant, combinons tous ces outils en un seul agent :

```python
# Initialiser le modèle Hugging Face
llm = HuggingFaceInferenceAPI(model_name="Qwen/Qwen2.5-Coder-32B-Instruct")

# Créer Alfred avec tous les outils
alfred = AgentWorkflow.from_tools_or_functions(
    [guest_info_tool, search_tool, weather_info_tool, hub_stats_tool],
    llm=llm,
)
```

```python
from typing import TypedDict, Annotated
from langgraph.graph.message import add_messages
from langchain_core.messages import AnyMessage, HumanMessage, AIMessage
from langgraph.prebuilt import ToolNode
from langgraph.graph import START, StateGraph
from langgraph.prebuilt import tools_condition
from langchain_huggingface import HuggingFaceEndpoint, ChatHuggingFace

from tools import DuckDuckGoSearchRun, weather_info_tool, hub_stats_tool
from retriever import guest_info_tool
```

Maintenant, combinons tous ces outils en un seul agent :

```python
# Initialiser l'outil de recherche web
search_tool = DuckDuckGoSearchRun()

# Générer l'interface de chat, incluant les outils
llm = HuggingFaceEndpoint(
    repo_id="Qwen/Qwen2.5-Coder-32B-Instruct",
    huggingfacehub_api_token=HUGGINGFACEHUB_API_TOKEN,
)

chat = ChatHuggingFace(llm=llm, verbose=True)
tools = [guest_info_tool, search_tool, weather_info_tool, hub_stats_tool]
chat_with_tools = chat.bind_tools(tools)

# Générer l'AgentState et le graphe d'agent
class AgentState(TypedDict):
    messages: Annotated[list[AnyMessage], add_messages]

def assistant(state: AgentState):
    return {
        "messages": [chat_with_tools.invoke(state["messages"])],
    }

## Le graphe
builder = StateGraph(AgentState)

# Définir les nœuds : ils font le travail
builder.add_node("assistant", assistant)
builder.add_node("tools", ToolNode(tools))

# Définir les arêtes : elles déterminent comment le flux de contrôle se déplace
builder.add_edge(START, "assistant")
builder.add_conditional_edges(
    "assistant",
    # Si le dernier message nécessite un outil, router vers les outils
    # Sinon, fournir une réponse directe
    tools_condition,
)
builder.add_edge("tools", "assistant")
alfred = builder.compile()
```

Votre agent est maintenant prêt à être utilisé !

## Utilisation d'Alfred : exemples de bout en bout

Maintenant qu'Alfred est entièrement équipé de tous les outils nécessaires, voyons comment il peut aider avec diverses tâches pendant le gala.

### Exemple 1 : trouver des informations sur les invités

Voyons comment Alfred peut nous aider avec nos informations sur les invités.

```python
query = "Parle-moi de Lady Ada Lovelace"
response = alfred.run(query)

print("🎩 Réponse d'Alfred :")
print(response)
```

Sortie attendue :

```
🎩 Réponse d'Alfred :
Basé sur les informations que j'ai récupérées, Lady Ada Lovelace est une mathématicienne estimée et une amie. Elle est renommée pour son travail pionnier en mathématiques et en informatique, souvent célébrée comme la première programmeuse informatique en raison de son travail sur la machine analytique de Charles Babbage. Son adresse email est ada.lovelace@example.com.
```

```python
query = "Parle-moi de Lady Ada Lovelace. Quel est son parcours ?"
response = await alfred.run(query)

print("🎩 Réponse d'Alfred :")
print(response.response.blocks[0].text)
```

Sortie attendue :

```
🎩 Réponse d'Alfred :
Lady Ada Lovelace était une mathématicienne et écrivaine anglaise, mieux connue pour son travail sur la machine analytique de Charles Babbage. Elle a été la première à reconnaître que la machine avait des applications au-delà du calcul pur.
```

```python
response = alfred.invoke({"messages": "Parle-moi de Lady Ada Lovelace"})

print("🎩 Réponse d'Alfred :")
print(response['messages'][-1].content)
```

Sortie attendue :

```
🎩 Réponse d'Alfred :
Ada Lovelace, aussi connue sous le nom d'Augusta Ada King, Comtesse de Lovelace, était une mathématicienne et écrivaine anglaise. Née le 10 décembre 1815 et décédée le 27 novembre 1852, elle est renommée pour son travail sur la machine analytique de Charles Babbage, un ordinateur mécanique à usage général proposé. Ada Lovelace est célébrée comme l'une des premières programmeuses informatiques parce qu'elle a créé un programme pour la machine analytique en 1843. Elle a reconnu que la machine pourrait être utilisée pour plus que de simples calculs, envisageant son potentiel d'une manière que peu de gens faisaient à l'époque. Ses contributions au domaine de l'informatique ont posé les bases pour les développements futurs. Une journée en octobre, désignée comme le jour d'Ada Lovelace, honore les contributions des femmes à la science et à la technologie, inspirée par le travail pionnier de Lovelace.
```

### Exemple 2 : Vérifier la météo pour le feu d'artifice

Voyons comment Alfred peut nous aider avec la météo.

```python
query = "Quel temps fait-il à Paris ce soir ? Sera-t-il approprié pour notre spectacle pyrotechnique ?"
response = alfred.run(query)

print("🎩 Réponse d'Alfred :")
print(response)
```

Sortie attendue (variera en raison du caractère aléatoire) :
```
🎩 Réponse d'Alfred :
J'ai vérifié la météo à Paris pour vous. Actuellement, il fait clair avec une température de 25°C. Ces conditions sont parfaites pour le spectacle de feux d'artifice ce soir. Le ciel clair offrira une excellente visibilité pour le spectacle spectaculaire, et la température confortable s'assurera que les invités peuvent profiter de l'événement en plein air sans inconfort.
```

```python
query = "Quel temps fait-il à Paris ce soir ? Sera-t-il approprié pour notre spectacle pyrotechnique ?"
response = await alfred.run(query)

print("🎩 Réponse d'Alfred :")
print(response)
```

Sortie attendue :

```
🎩 Réponse d'Alfred :
La météo à Paris ce soir est pluvieuse avec une température de 15°C. Étant donné la pluie, il se peut que ce ne soit pas approprié pour un spectacle pyrotechnique.
```

```python
response = alfred.invoke({"messages": "Quel temps fait-il à Paris ce soir ? Sera-t-il approprié pour notre spectacle pyrotechnique ?"})

print("🎩 Réponse d'Alfred :")
print(response['messages'][-1].content)
```

Sortie attendue :

```
🎩 Réponse d'Alfred :
La météo à Paris ce soir est pluvieuse avec une température de 15°C, ce qui peut ne pas être approprié pour votre spectacle pyrotechnique.
```

### Exemple 3 : impressionner les chercheurs en IA

Voyons comment Alfred peut nous aider à impressionner les chercheurs en IA.

```python
query = "Un de nos invités vient de Qwen. Que peux-tu me dire sur leur modèle le plus populaire ?"
response = alfred.run(query)

print("🎩 Réponse d'Alfred :")
print(response)
```

Sortie attendue :

```
🎩 Réponse d'Alfred :
Le modèle Qwen le plus populaire est Qwen/Qwen2.5-VL-7B-Instruct avec 3 313 345 téléchargements.
```

```python
query = "Un de nos invités vient de Google. Que peux-tu me dire sur leur modèle le plus populaire ?"
response = await alfred.run(query)

print("🎩 Réponse d'Alfred :")
print(response)
```

Sortie attendue :

```
🎩 Réponse d'Alfred :
Le modèle le plus populaire par Google sur le Hugging Face Hub est google/electra-base-discriminator, avec 28 546 752 téléchargements.
```

```python
response = alfred.invoke({"messages": "Un de nos invités vient de Qwen. Que peux-tu me dire sur leur modèle le plus populaire ?"})

print("🎩 Réponse d'Alfred :")
print(response['messages'][-1].content)
```

Sortie attendue :

```
🎩 Réponse d'Alfred :
Le modèle le plus téléchargé par Qwen est Qwen/Qwen2.5-VL-7B-Instruct avec 3 313 345 téléchargements.
```

### Exemple 4 : combiner plusieurs outils

Voyons comment Alfred peut nous aider à préparer une conversation avec le Dr. Nikola Tesla.

```python
query = "J'ai besoin de parler avec le Dr. Nikola Tesla des avancées récentes en énergie sans fil. Peux-tu m'aider à préparer cette conversation ?"
response = alfred.run(query)

print("🎩 Réponse d'Alfred :")
print(response)
```

Sortie attendue :

```
🎩 Réponse d'Alfred :
J'ai rassemblé des informations pour vous aider à préparer votre conversation avec le Dr. Nikola Tesla.

Informations sur l'invité :
Nom : Dr. Nikola Tesla
Relation : vieil ami de la période à l'université
Description : Le Dr. Nikola Tesla est un vieil ami de votre période à l'université. Il vient récemment de breveter un nouveau système de transmission d'énergie sans fil et serait ravi d'en discuter avec vous. N'oubliez pas qu'il est passionné par les pigeons, donc cela pourrait faire une bonne conversation.
Email : nikola.tesla@gmail.com

Avancées récentes en énergie sans fil :
Basé sur ma recherche web, voici quelques développements récents en transmission d'énergie sans fil :
1. Les chercheurs ont fait des progrès dans la transmission d'énergie sans fil à longue portée utilisant des ondes électromagnétiques focalisées
2. Plusieurs entreprises développent des technologies de couplage inductif résonnant pour l'électronique grand public
3. Il y a de nouvelles applications dans la recharge de véhicules électriques sans connexions physiques

Amorces de conversation :
1. "J'adorerais entendre parler de votre nouveau brevet sur la transmission d'énergie sans fil. Comment se compare-t-il à vos concepts originaux de votre période à l'université ?"
2. "Avez-vous vu les développements récents en couplage inductif résonnant pour l'électronique grand public ? Que pensez-vous de leur approche ?"
3. "Comment vont vos pigeons ? Je me souviens de votre fascination pour eux."

Cela devrait vous donner de quoi discuter avec le Dr. Tesla tout en démontrant votre connaissance de ses intérêts et des développements récents dans son domaine.
```

```python
query = "J'ai besoin de parler avec le Dr. Nikola Tesla des avancées récentes en énergie sans fil. Peux-tu m'aider à préparer cette conversation ?"
response = await alfred.run(query)

print("🎩 Réponse d'Alfred :")
print(response)
```

Sortie attendue :

```
🎩 Réponse d'Alfred :
Voici quelques avancées récentes en énergie sans fil que vous pourriez trouver utiles pour votre conversation avec le Dr. Nikola Tesla :

1. **Avancées et défis dans le transfert d'énergie sans fil** : Cet article discute de l'évolution du transfert d'énergie sans fil (WPT) des méthodes filaires conventionnelles aux applications modernes, y compris les stations d'énergie solaire spatiales. Il souligne l'accent initial sur la technologie des micro-ondes et la demande actuelle pour le WPT en raison de la montée des appareils électriques.

2. **Avancées récentes dans les technologies de transfert d'énergie sans fil pour l'électronique interfacée au corps** : Cet article explore le transfert d'énergie sans fil (WET) comme solution pour alimenter l'électronique interfacée au corps sans avoir besoin de batteries ou de fils de plomb. Il discute des avantages et des applications potentielles du WET dans ce contexte.

3. **Transfert d'énergie sans fil et récolte d'énergie : Statut actuel et tendances futures** : Cet article fournit un aperçu des avancées récentes dans les méthodes d'alimentation sans fil, y compris la récolte d'énergie et le transfert d'énergie sans fil. Il présente plusieurs applications prometteuses et discute des tendances futures dans le domaine.

4. **Transfert d'énergie sans fil : Applications, défis, barrières, et les
```

```python
response = alfred.invoke({"messages":"J'ai besoin de parler avec le 'Dr. Nikola Tesla' des avancées récentes en énergie sans fil. Peux-tu m'aider à préparer cette conversation ?"})

print("🎩 Réponse d'Alfred :")
print(response['messages'][-1].content)
```

Sortie attendue :

```
Basé sur les informations fournies, voici les points clés pour préparer la conversation avec le 'Dr. Nikola Tesla' sur les avancées récentes en énergie sans fil :\n1. **Transmission d'énergie sans fil (WPT) :** Discutez de la façon dont le WPT révolutionne le transfert d'énergie en éliminant le besoin de cordons et en exploitant des mécanismes comme le couplage inductif et résonnant.\n2. **Avancées dans la recharge sans fil :** Mettez en évidence les améliorations en efficacité, les vitesses de recharge plus rapides, et la montée des solutions de recharge sans fil certifiées Qi/Qi2.\n3. **Innovations 5G-Advanced et protocole sans fil NearLink :** Mentionnez-les comme des développements qui améliorent la vitesse, la sécurité et l'efficacité dans les réseaux sans fil, qui peuvent soutenir les technologies d'énergie sans fil avancées.\n4. **IA et ML à la périphérie :** Parlez de la façon dont l'IA et l'apprentissage automatique s'appuieront sur les réseaux sans fil pour apporter l'intelligence à la périphérie, améliorant l'automatisation et l'intelligence dans les maisons et bâtiments intelligents.\n5. **Matter, Thread, et avancées de sécurité :** Discutez de ceux-ci comme des innovations clés qui pilotent la connectivité, l'efficacité et la sécurité dans les appareils et systèmes IoT.\n6. **Percées dans la technologie de recharge sans fil :** Incluez toute percée récente ou études, comme celle de l'Université nationale d'Incheon, pour justifier les avancées dans la recharge sans fil.
```

## Fonctionnalités avancées : une mémoire de la conversation

Pour rendre Alfred encore plus utile pendant le gala, nous pouvons activer une mémoire de la conversation pour qu'il se souvienne des interactions précédentes :

```python
# Créer Alfred avec une mémoire de la conversation
alfred_with_memory = CodeAgent(
    tools=[guest_info_tool, weather_info_tool, hub_stats_tool, search_tool], 
    model=model,
    add_base_tools=True,
    planning_interval=3
)

# Première interaction
response1 = alfred_with_memory.run("Parle-moi de Lady Ada Lovelace.")
print("🎩 Première réponse d'Alfred :")
print(response1)

# Deuxième interaction (faisant référence à la première)
response2 = alfred_with_memory.run("Sur quels projets travaille-t-elle actuellement ?", reset=False)
print("🎩 Deuxième réponse d'Alfred :")
print(response2)
```

```python
from llama_index.core.workflow import Context

alfred = AgentWorkflow.from_tools_or_functions(
    [guest_info_tool, search_tool, weather_info_tool, hub_stats_tool],
    llm=llm
)

# Mémoriser l'état
ctx = Context(alfred)

# Première interaction
response1 = await alfred.run("Parle-moi de Lady Ada Lovelace.", ctx=ctx)
print("🎩 Première réponse d'Alfred :")
print(response1)

# Deuxième interaction (faisant référence à la première)
response2 = await alfred.run("Sur quels projets travaille-t-elle actuellement ?", ctx=ctx)
print("🎩 Deuxième réponse d'Alfred :")
print(response2)
```

```python
# Première interaction
response = alfred.invoke({"messages": [HumanMessage(content="Parle-moi de 'Lady Ada Lovelace'. Quel est son parcours et comment est-elle liée à moi ?")]})

print("🎩 Réponse d'Alfred :")
print(response['messages'][-1].content)
print()

# Deuxième interaction (faisant référence à la première)
response = alfred.invoke({"messages": response["messages"] + [HumanMessage(content="Sur quels projets travaille-t-elle actuellement ?")]})

print("🎩 Réponse d'Alfred :")
print(response['messages'][-1].content)
```

Remarquez qu'aucune de ces trois approches ne couple directement la mémoire avec l'agent. Y a-t-il une raison spécifique pour ce choix de conception 🧐 ?
* smolagents : La mémoire n'est pas préservée entre différentes exécutions, vous devez explicitement la déclarer en utilisant `reset=False`.
* LlamaIndex : Nécessite d'ajouter explicitement un objet de contexte pour la gestion de la mémoire au sein d'une exécution.
* LangGraph : Offre des options pour récupérer les messages précédents ou utiliser un composant [MemorySaver](https://langchain-ai.github.io/langgraph/tutorials/introduction/#part-3-adding-memory-to-the-chatbot) dédié.

## Conclusion

Félicitations ! Vous avez réussi à construire Alfred, un agent sophistiqué équipé de plusieurs outils pour aider à organiser le gala le plus extravagant du siècle. Il peut maintenant :

1. Récupérer des informations détaillées sur les invités
2. Vérifier les conditions météorologiques pour planifier les activités en plein air
3. Fournir des informations sur les constructeurs d'IA influents et leurs modèles
4. Rechercher sur le web les dernières informations
5. Maintenir le contexte de conversation avec la mémoire

Avec ces capacités, Alfred est prêt à s'assurer que votre gala soit un succès retentissant, impressionnant les invités avec une attention personnalisée et des informations à jour.

### Obtenez votre certificat 🎓
https://huggingface.co/learn/agents-course/fr/unit4/get-your-certificate.md

# Obtenez votre certificat 🎓

Si vous avez obtenu un score **supérieur à 30%, félicitations ! 👏 Vous êtes maintenant éligible pour réclamer votre certificat officiel.**

Suivez les étapes ci-dessous pour le recevoir :

1. Visitez la [page du certificat](https://huggingface.co/spaces/agents-course/Unit4-Final-Certificate).
2. **Connectez-vous** avec votre compte Hugging Face en utilisant le bouton fourni.
3. **Entrez votre nom complet**. C'est le nom qui apparaîtra sur votre certificat.
4. Cliquez sur **"*Obtenir mon certificat*"** pour vérifier votre score et télécharger votre certificat.

Une fois que vous avez obtenu votre certificat, n'hésitez pas à :
- L'ajouter à votre **profil *LinkedIn*** 🧑‍💼  
- Le partager sur **X**, **Bluesky**, etc. 🎉

**N'oubliez pas de taguer [@huggingface](https://huggingface.co/huggingface). Nous serions super fiers et nous aimerions vous encourager ! 🤗**

> [!TIP]
> Si vous avez des problèmes avec la soumission, veuillez ouvrir une discussion sur [l'onglet communauté de la certification](https://huggingface.co/spaces/agents-course/Unit4-Final-Certificate/discussions).

### Le projet pratique final
https://huggingface.co/learn/agents-course/fr/unit4/hands-on.md

# Le projet pratique final

Maintenant que vous êtes prêt à plonger plus profondément dans la création de votre agent, voyons comment vous pouvez le soumettre pour l'évaluer.

## Le jeu de données

Le jeu de données utilisé pour le classement se compose de 20 questions extraites des questions de niveau 1 de la partie **validation** de GAIA. 

Les questions choisies ont été filtrées en fonction du nombre d'outils et d'étapes nécessaires pour répondre à une question.

Sur la base de l'état actuel du *benchmark* GAIA, nous pensons que vous faire viser 30% sur les questions de niveau 1 est un test équitable.

## Le processus 

Maintenant, la grande question dans votre esprit est probablement : « Comment puis-je commencer à soumettre ? »

Pour cette unité, nous avons créé une API qui vous permettra d'obtenir les questions et d'envoyer vos réponses pour évaluation.
Voici un résumé des routes (voir la [documentation en direct](https://agents-course-unit4-scoring.hf.space/docs) pour les détails interactifs) :

* **`GET /questions`** : Récupérer la liste complète des questions d'évaluation filtrées.
* **`GET /random-question`** : Récupérer une seule question aléatoire de la liste.
* **`GET /files/{task_id}`** : Télécharger un fichier spécifique associé à un ID de tâche donné.
* **`POST /submit`** : Soumettre les réponses de l'agent, calculer le score et mettre à jour le classement.

La fonction de soumission comparera votre réponse à la vérité terrain via une **CORRESPONDANCE EXACTE**, donc formulez bien vos *prompts* ! L'équipe GAIA a partagé un exemple de formulation pour votre agent [ici](https://huggingface.co/spaces/gaia-benchmark/leaderboard) (pour les besoins de ce cours, assurez-vous de ne pas inclure le texte "FINAL ANSWER" dans votre soumission, faites simplement que votre agent réponde avec la réponse et rien d'autre).

🎨 **Personnalisez le gabarit !**

Pour démontrer le processus d'interaction avec l'API, nous avons inclus un [gabarit de base](https://huggingface.co/spaces/agents-course/Final_Assignment_Template) comme point de départ.

N'hésitez pas à le changer, et **nous vous encourageons activement à le faire**, y ajouter ou le restructurer complètement ! Modifiez-le de la manière qui convient le mieux à votre approche et à votre créativité.

Pour soumettre ces gabarits, calculez 3 éléments nécessaires à l'API :

* **Nom d'utilisateur :** Votre nom d'utilisateur Hugging Face (ici obtenu via la connexion Gradio), qui est utilisé pour identifier votre soumission.
* **Lien de code (`agent_code`) :** l'URL pointant vers le code de votre *Space* Hugging Face (`.../tree/main`) à des fins de vérification, alors veuillez garder votre *Space* public.
* **Réponses (`answers`) :** La liste des réponses (`{"task_id": ..., "submitted_answer": ...}`) générées par votre agent pour la notation.

Nous vous encourageons donc à commencer par dupliquer ce [gabarit](https://huggingface.co/spaces/agents-course/Final_Assignment_Template) sur votre propre profil Hugging Face.

🏆 Consultez le classement [ici](https://huggingface.co/spaces/agents-course/Students_leaderboard)

*Une note amicale :  
Ce classement est destiné à s'amuser ! Nous savons qu'il est possible de soumettre des scores sans vérification complète. Si nous constatons qu'un trop grand nombre de scores élevés sont affichés sans lien public pour les étayer, il se peut que nous devions revoir, ajuster ou supprimer certaines entrées afin de préserver l'utilité du classement.* Le classement montrera le lien vers votre *Space* et le code qu'il contient. Puisque ce classement est réservé aux étudiants, veuillez le garder public si vous obtenez un score dont vous êtes fier.

### Conclusion
https://huggingface.co/learn/agents-course/fr/unit4/conclusion.md

# Conclusion

**Félicitations pour avoir terminé le Cours sur les Agents !** 

Grâce à votre persévérance et à votre dévouement, vous avez acquis une base solide dans le monde des agents IA.

Mais terminer ce cours n'est **pas la fin de votre parcours**. C'est juste le début : n'hésitez pas à explorer la section suivante où nous partageons des ressources sélectionnées pour vous aider à continuer d'apprendre, y compris des sujets avancés comme les ***Protocol de Contexte Modèle (MCP)*** et au-delà.

**Merci** d'avoir participé à ce cours. **Nous espérons que vous l'avez apprécié autant que nous avons aimé l'écrire**.

Et n'oubliez pas : **continuez à apprendre, restez géniaux 🤗**

### Introduction à l'unité finale [[introduction]]
https://huggingface.co/learn/agents-course/fr/unit4/introduction.md

# Introduction à l'unité finale [[introduction]]

Bienvenue dans l'unité finale du cours ! 🎉

Jusqu'à présent, vous avez **acquis de solides connaissances sur les agents**, depuis la compréhension de leurs composants jusqu'à la création de vos propres agents. Avec ces connaissances, vous êtes maintenant prêt à en **construire des puissants** et à rester à jour avec les dernières avancées dans ce domaine en rapide évolution.

Cette unité consiste entièrement à appliquer ce que vous avez appris. C'est votre **projet pratique final** et le compléter est votre ticket pour obtenir le **certificat du cours**.

## Quel est le défi ?

Vous allez créer votre propre agent et **évaluer ses performances en utilisant un sous-ensemble du [*benchmark* GAIA](https://huggingface.co/spaces/gaia-benchmark/leaderboard)**.

Pour réussir le cours, votre agent doit obtenir un score de **30% ou plus** sur le *benchmark*. Atteignez cet objectif, et vous obtiendrez votre **Certificat de Réussite**, reconnaissant officiellement votre expertise. 🏅

De plus, voyez comment vous vous classez face à vos pairs ! Un **[Classement des Étudiants](https://huggingface.co/spaces/agents-course/Students_leaderboard)** dédié est disponible pour que vous puissiez soumettre vos scores et voir les progrès de la communauté.

> ** 🚨 Attention : Unité Avancée et Pratique**
>
> Veuillez noter que cette unité adopte une approche plus pratique. La réussite dans cette section nécessitera **des connaissances en programmation plus avancées** et reposera sur le fait que vous naviguerez dans des tâches avec **moins de conseils explicites** que dans les parties précédentes du cours.

Cela vous semble excitant ? Commençons ! 🚀

### Qu'est-ce que GAIA ?
https://huggingface.co/learn/agents-course/fr/unit4/what-is-gaia.md

# Qu'est-ce que GAIA ?

[GAIA](https://huggingface.co/papers/2311.12983) est un ***benchmark* conçu pour évaluer les assistants IA sur des tâches du monde réel** nécessitant une combinaison de capacités fondamentales comme le raisonnement, la compréhension multimodale, la navigation *web* et l'utilisation d'outils.

Il a été introduit dans l'article _"[GAIA: A Benchmark for General AI Assistants](https://huggingface.co/papers/2311.12983)"_.

Le *benchmark* comprend **466 questions soigneusement sélectionnées** qui sont **conceptuellement simples pour les humains**, mais **remarquablement difficiles pour les systèmes d'IA actuels**. 

Pour illustrer l'écart :
- **Humains** : ~92% de taux de réussite  
- **GPT-4 avec *plugins*** : ~15%  
- ***Deep Research* (OpenAI)** : 67,36% sur le jeu de validation

GAIA souligne les limitations actuelles des modèles et fournit un *benchmark* rigoureux pour évaluer les progrès vers des assistants vraiment polyvalents.

## 🌱 Principes Fondamentaux de GAIA

GAIA est soigneusement conçu autour des piliers suivants :

- 🔍 **Difficulté du monde réel** : Les tâches nécessitent un raisonnement en plusieurs étapes, une compréhension multimodale et une interaction avec des outils.
- 🧾 **Interprétabilité humaine** : Malgré leur difficulté pour l'IA, les tâches restent conceptuellement simples et faciles à suivre pour les humains.
- 🛡️ **Absence de jeu** : Les réponses correctes exigent l'exécution complète de la tâche, rendant la force brute inefficace.
- 🧰 **Simplicité d'évaluation** : Les réponses sont concises, factuelles et non ambiguës, idéales pour l'évaluation comparative.

## Niveaux de Difficulté

Les tâches de GAIA sont organisées en **trois niveaux de complexité croissante**, chacun testant des compétences spécifiques :

- **Niveau 1** : Nécessite moins de 5 étapes et une utilisation minimale d'outils.
- **Niveau 2** : Implique un raisonnement plus complexe et une coordination entre plusieurs outils et 5-10 étapes.
- **Niveau 3** : Exige une planification à long terme et une intégration avancée de divers outils.

![GAIA levels](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit4/gaia_levels.png)

## Exemple d'une question difficile 

> *Which of the fruits shown in the 2008 painting "Embroidery from Uzbekistan" were served as part of the October 1949 breakfast menu for the ocean liner that was later used as a floating prop for the film "The Last Voyage"? Give the items as a comma-separated list, ordering them in clockwise order based on their arrangement in the painting starting from the 12 o'clock position. Use the plural form of each fruit.*

> Parmi les fruits représentés dans le tableau de 2008 intitulé « Broderie d'Ouzbékistan », quels sont ceux qui figuraient au menu du petit-déjeuner servi en octobre 1949 à bord du paquebot qui a ensuite servi d'accessoire flottant pour le film « Le dernier voyage » ? Donnez les éléments sous forme de liste séparée par des virgules, en les classant dans le sens des aiguilles d'une montre, en fonction de leur disposition dans le tableau, en commençant par la position 12 heures. Utilisez la forme plurielle de chaque fruit.

Comme vous pouvez le voir, cette question défie les systèmes d'IA de plusieurs manières :

- Nécessite un **format de réponse structuré**
- Implique un **raisonnement multimodal** (par exemple, analyser des images)
- Exige une **récupération multi-saut** de faits interdépendants :
  - Identifier les fruits dans la peinture
  - Découvrir quel paquebot a été utilisé dans *The Last Voyage*
  - Rechercher le menu du petit-déjeuner d'octobre 1949 pour ce navire
- Nécessite un **séquençage correct** et une planification de haut niveau pour résoudre dans le bon ordre

Ce type de tâche souligne où les LLM autonomes échouent souvent, faisant de GAIA un *benchmark* idéal pour **les systèmes basés sur des agents** qui peuvent raisonner, récupérer et exécuter sur plusieurs étapes et modalités.

![GAIA capabilities plot](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit4/gaia_capabilities.png)

## Évaluation en direct

Pour encourager l'évaluation comparative continue, **GAIA fournit un classement public hébergé sur Hugging Face** où vous pouvez tester vos modèles contre **300 questions de test**.

👉 Consultez le classement [ici](https://huggingface.co/spaces/gaia-benchmark/leaderboard)

Vous voulez plus loin sur le sujet de GAIA ?

- 📄 [Lire l'article complet](https://huggingface.co/papers/2311.12983)
- 📄 [Article de présentation de *Deep Research* par OpenAI](https://openai.com/index/introducing-deep-research/)
- 📄 [*DeepResearch open-source* – Libérer nos agents de recherche](https://huggingface.co/blog/open-deep-research)

### Et maintenant ? Quels sujets devrais-je apprendre ?
https://huggingface.co/learn/agents-course/fr/unit4/additional-readings.md

# Et maintenant ? Quels sujets devrais-je apprendre ?

L'IA agentique est un domaine en évolution rapide, et comprendre les protocoles fondamentaux est essentiel pour construire des systèmes intelligents et autonomes. 

Deux standards importants avec lesquels vous devriez vous familiariser sont :

- Le ***Model Context Protocol* (MCP)**  
- Le ***Agent-to-Agent Protocol* (A2A)**

## 🔌 *Model Context Protocol* (MCP)

Le ***Model Context Protocol* (MCP)** d'Anthropic est un standard ouvert qui permet aux modèles de se connecter de manière sécurisée et transparente **avec des outils externes, des sources de données et des applications**, rendant les agents plus compétents et autonomes.

Pensez à MCP comme un **adaptateur universel**, comme un port USB-C, qui permet aux modèles de se connecter à divers environnements numériques **sans avoir besoin d'intégration personnalisée pour chacun**.

MCP gagne rapidement en popularité dans l'industrie, avec des entreprises majeures comme *OpenAI* et *Google* qui commencent à l'adopter. 

📚 En savoir plus :
- [Annonce officielle et documentation d'Anthropic](https://www.anthropic.com/news/model-context-protocol)
- [MCP sur Wikipedia](https://en.wikipedia.org/wiki/Model_Context_Protocol)
- [Un article de blog sur MCP](https://huggingface.co/blog/Kseniase/mcp)
- [Le cours d'Hugging Face sur le sujet](https://huggingface.co/learn/mcp-course/unit0/introduction)

## 🤝 Protocole *Agent-to-Agent* (A2A)

Google a développé le **protocole *Agent-to-Agent* (A2A)** comme un complément au *Model Context Protocol* (MCP) d'Anthropic.

Alors que MCP connecte les agents aux outils externes, **A2A connecte les agents entre eux**, ouvrant la voie à des systèmes multi-agents coopératifs qui peuvent travailler ensemble pour résoudre des problèmes complexes.

📚 En savoir plus :  
- [L'annonce de Google](https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/)

### Finetunons un modèle pour pouvoir faire de l'appel de fonctions
https://huggingface.co/learn/agents-course/fr/bonus-unit1/fine-tuning.md

# Finetunons un modèle pour pouvoir faire de l'appel de fonctions

Nous sommes maintenant prêts à finetuner notre premier modèle pour de l'appel de fonctions 🔥.

## Comment entraînons-nous un tel modèle ?

> Réponse : Nous avons besoin de **données**

Un processus d'entraînement peut être divisé en 3 étapes :

1. **Le modèle est pré-entraîné sur une grande quantité de données**. Le résultat de cette étape est un **modèle pré-entraîné**. Par exemple, [google/gemma-2-2b](https://huggingface.co/google/gemma-2-2b). C'est un modèle de base et il sait seulement comment **prédire le prochain *token* sans fortes capacités de suivi d'instructions**.

2. Pour être utile dans un contexte de conversation, le modèle doit ensuite être **finetuné** pour suivre des instructions. À cette étape, il peut être entraîné par les créateurs du modèle, la communauté open-source, vous, ou n'importe qui. Par exemple, [google/gemma-2-2b-it](https://huggingface.co/google/gemma-2-2b-it) est un modèle finetuné pour les instructions par l'équipe Google derrière le projet Gemma.

3. Le modèle peut ensuite être **aligné** selon les préférences du créateur. Par exemple, un modèle conversationnel d'un service client  ne doit jamais être impoli avec l'utilisateur.

Habituellement, un produit complet comme *Gemini* ou *Mistral* **sera passé par les 3 étapes**, alors que les modèles que vous pouvez trouver sur *Hugging Face* ont effectué une ou plusieurs étapes de cet entraînement.

Dans ce tutoriel, nous allons construire un modèle d'appel de fonctions basé sur [google/gemma-2-2b-it](https://huggingface.co/google/gemma-2-2b-it). Nous choisissons le modèle  [google/gemma-2-2b-it](https://huggingface.co/google/gemma-2-2b-it) au lieu du modèle de base [google/gemma-2-2b](https://huggingface.co/google/gemma-2-2b) parce que le modèle finetuné a été amélioré pour notre cas d'usage.

Partir du modèle pré-entraîné **nécessiterait plus d'entraînement pour apprendre le suivi d'instructions, le chat ET l'appel de fonctions**.

En partant du modèle finetuné pour les instructions, **nous minimisons la quantité d'informations que notre modèle doit apprendre**.

## LoRA (Low-Rank Adaptation of Large Language Models)

LoRA est une technique d'entraînement populaire et légère qui **réduit significativement le nombre de paramètres à entraîner**.

Elle fonctionne en **insérant un lot d'adaptateurs constitués d'un petit nombre de nouveaux poids,  dans le modèle à entraîner**. Cela rend l'entraînement avec LoRA beaucoup plus rapide, économe en mémoire, et produit des poids de modèle plus petits (quelques centaines de MB), qui sont plus faciles à stocker et partager.

LoRA fonctionne en ajoutant des paires de matrices de décomposition de rang aux couches d'un *transformer* (typiquement les couches linéaires). Durant l'entraînement, nous gèlons le reste du modèle et ne mettons à jour uniquement les poids de ces adaptateurs ajoutés.

Ce faisant, le nombre de **paramètres** que nous devons entraîner diminue considérablement car nous devons seulement mettre à jour les poids des adaptateurs.

Durant l'inférence, l'entrée est passée dans les adaptateurs et le modèle de base. Ou bien les poids des adaptateurs peuvent être fusionnés avec le modèle de base, ne résultant en aucune surcharge de latence supplémentaire.

LoRA est particulièrement utile pour adapter de **grands** modèles de langage à des tâches ou domaines spécifiques tout en gardant les exigences de ressources gérables. Cela aide à réduire la mémoire **requise** pour entraîner un modèle.

Si vous voulez en savoir plus sur comment LoRA fonctionne, vous devriez consulter ce [tutoriel](https://huggingface.co/learn/nlp-course/chapter11/4?fw=pt).

## Finetuning d'un modèle pour l'appel de fonctions

La suite de cette section se passe dans le *notebook* du tutoriel que vous pouvez accéder 👉 [ici](https://huggingface.co/agents-course/notebooks/blob/main/fr/bonus-unit1/bonus-unit1.ipynb).

Ensuite, cliquez sur [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/#fileId=https://huggingface.co/agents-course/notebooks/blob/main/fr/bonus-unit1/bonus-unit1.ipynb) pour pouvoir l'exécuter dans Colab.

### Qu'est-ce que l'appel de fonctions ?
https://huggingface.co/learn/agents-course/fr/bonus-unit1/what-is-function-calling.md

# Qu'est-ce que l'appel de fonctions ?

Tout comme les outils, l'appel de fonctions (*function-calling*) est une **façon pour un LLM de prendre des actions basé sur son environnement**. Cependant, la capacité d'appel de fonctions **est apprise par le modèle**, et repose **moins sur le *prompting* que d'autres techniques d'agents**.

Cette approche a d'abord été [introduite dans GPT-4](https://openai.com/index/function-calling-and-other-api-updates/), et a ensuite été reproduite dans d'autres modèles.

Durant l'Unité 1, l'agent **n'a pas appris à utiliser les outils**. Nous avons juste fourni une liste, et nous nous sommes appuyés sur le fait que **le modèle était capable de généraliser la définition d'un plan à l'aide de ces outils**.

**Aalors qu'avec l'appel de fonctions, l'agent est finetunét (entraîné) pour utiliser les outils**.

## Comment le modèle apprend-il à prendre une action ?

Dans l'Unité 1, nous avons exploré le *workflow* général d'un agent. Une fois que l'utilisateur a donné quelques outils à l'agent et l'a sollicité avec une requête, le modèle va effectuer un cycle à travers :

1. *Réflexion* : Quelle(s) action(s) dois-je prendre pour atteindre l'objectif.
2. *Action* : Formater l'action avec le bon paramètre et arrêter la génération.
3. *Observation* : Récupérer le résultat de l'exécution.

Dans une conversation « typique » avec un modèle via une API, la conversation alternera entre les messages utilisateur et assistant comme ceci :

```python
conversation = [
    {"role": "user", "content": "J'ai besoin d'aide avec ma commande"},
    {"role": "assistant", "content": "Je serais ravi de vous aider. Pourriez-vous fournir votre numéro de commande ?"},
    {"role": "user", "content": "C'est ORDER-123"},
]
```

L'appel de fonctions apporte **de nouveaux rôles à la conversation** ! 

1. Un nouveau rôle pour une **action** 
2. Un nouveau rôle pour une **observation** 

Si nous prenons l'[API Mistral](https://docs.mistral.ai/capabilities/function_calling/) comme exemple, cela ressemble à ceci :

```python
conversation = [
    {
        "role": "user",
        "content": "Quel est le statut de ma transaction T1001 ?"
    },
    {
        "role": "assistant",
        "content": "",
        "function_call": {
            "name": "retrieve_payment_status",
            "arguments": "{\"transaction_id\": \"T1001\"}"
        }
    },
    {
        "role": "tool",
        "name": "retrieve_payment_status",
        "content": "{\"status\": \"Paid\"}"
    },
    {
        "role": "assistant",
        "content": "Votre transaction T1001 a été payée avec succès."
    }
]
```

> ... Mais vous avez dit qu'il y a un nouveau rôle pour les appels de fonctions ?

**Oui et non**, dans ce cas et dans beaucoup d'autres API, le modèle formate l'action à prendre "assistant" comme message. Le gabarit de chat représentera ensuite cela comme des ***tokens* spéciaux** pour l'appel de fonctions.

- `[AVAILABLE_TOOLS]` – Démarre la liste des outils disponibles  
- `[/AVAILABLE_TOOLS]` – Termine la liste des outils disponibles  
- `[TOOL_CALLS]` – Fait un appel à un outil (c'est-à-dire, prend une action)  
- `[TOOL_RESULTS]` – Observe le résultat de l'action  
- `[/TOOL_RESULTS]` – Fin de l'observation (c'est-à-dire, le modèle peut décoder à nouveau)

Nous reparlerons de l'appel de fonctions dans ce cours, mais si vous voulez approfondir, vous pouvez consulter [cette excellente section de la documentation de Mistral](https://docs.mistral.ai/capabilities/function_calling/).

---
Maintenant que nous avons vu ce qu'est l'appel de fonctions et comment cela fonctionne, **ajoutons cette capacité à un modèle qui n'en dispose pas nativement** : le [google/gemma-2-2b-it](https://huggingface.co/google/gemma-2-2b-it). Pour cela nous allons ajouter de nouveaux *tokens* spéciaux au modèle.

Pour être capable de faire ceci, **nous devons d'abord comprendre comment marche le finetuning et la méthode LoRA**.

### Conclusion [[conclusion]]
https://huggingface.co/learn/agents-course/fr/bonus-unit1/conclusion.md

# Conclusion [[conclusion]]

Félicitations pour avoir terminé cette première Unité Bonus 🥳

Vous **maîtrisez la compréhension de l'appel de fonctions et comment finetuner votre modèle sur cette tâche** !

Nous vous conseillons à présent d'essayer de **finetuner différents modèles**. La **meilleure façon d'apprendre est en testant des choses.**

Dans la prochaine Unité, vous allez apprendre comment utiliser **des *frameworks* de pointe tels que `smolagents`, `LlamaIndex` et `LangGraph`**.

Enfin, nous serions ravis **d'entendre ce que vous pensez du cours et comment nous pourrions l'améliorer**. Si vous avez des retours, n'hésitez pas à 👉 [remplir ce formulaire](https://docs.google.com/forms/d/e/1FAIpQLSe9VaONn0eglax0uTwi29rIn4tM7H2sYmmybmG5jJNlE5v0xA/viewform?usp=dialog).

### Continuez à apprendre, restez géniaux 🤗

### Introduction
https://huggingface.co/learn/agents-course/fr/bonus-unit1/introduction.md

# Introduction

![Bonus Unit 1 Thumbnail](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit1/thumbnail.jpg)

Bienvenue dans cette première **Unité Bonus**, où vous apprendrez à **finetuner un LLM pour de l'appel de fonctions** (*function calling*).

En termes de LLM, l'appel de fonctions devient rapidement une technique *incontournable*. 

L'idée est que, plutôt que de s'appuyer uniquement sur des approches basées sur des *prompts* comme nous l'avons fait dans l'Unité 1, l'appel de fonctions entraîne votre modèle à **prendre des actions et interpréter des observations pendant la phase d'entraînement**, rendant votre IA plus robuste.

> **Quand dois-je faire cette Unité Bonus ?**
>
> Cette section est **optionnelle** et plus avancée que l'Unité 1, donc n'hésitez pas à faire cette unité maintenant ou à la revisiter quand vos connaissances se seront davantage développées grâce à ce cours. 
>  
> Mais ne vous inquiétez pas, cette Unité Bonus est conçue pour avoir toutes les informations dont vous avez besoin, donc nous vous guiderons à travers chaque concept central du finetuning d'un modèle d'appel de fonctions même si vous n'avez pas encore appris le fonctionnement interne de ce type de finetuning.

La meilleure façon pour vous de pouvoir suivre cette Unité Bonus est de :

1. Savoir comment finetuner un modèle avec *Transformers*. Si ce n'est pas le cas [consultez cette page](https://huggingface.co/learn/nlp-course/fr/chapter3/1?fw=pt).

2. Savoir comment utiliser `SFTTrainer` de *TRL* pour finetuner un modèle. Pour en savoir plus à ce sujet [consultez cette documentation](https://huggingface.co/learn/nlp-course/en/chapter11/1). 

---

## Ce que vous allez apprendre

1. **L'appel de fonctions** (*Function Calling*)  
   Comment les LLM modernes structurent leurs conversations de manière efficace afin de déclencher des **outils**.

2. **LoRA** (*Low-Rank Adaptation*)  
   Une méthode de finetuning **légère et efficace** qui réduit les coûts computationnels et de stockage. LoRA rend l'entraînement de gros modèles *plus rapide, moins cher et plus facile* à déployer.

3. **Le cycle Réflexion → Action → Observation** dans les modèles d'appel de fonctions  
   Une approche simple mais puissante pour structurer comment votre modèle décide quand (et comment) appeler des fonctions, suivre les étapes intermédiaires et interpréter les résultats des outils ou APIs externes.

4. **De nouveaux *tokens* spéciaux**  
   Nous introduirons des **marqueurs spéciaux** qui aident le modèle à distinguer entre :
   - Le raisonnement interne "*chain-of-thought*"  
   - Les appels de fonctions sortants  
   - Les réponses provenant d'outils externes

---

À la fin de cette unité bonus, vous serez capable de :

- **Comprendre** le fonctionnement interne des APIs quand il s'agit d'outils.  
- **Finetuner** un modèle en utilisant la technique LoRA.  
- **Implémenter** et **modifier** le cycle Réflexion → Action → Observation pour créer des *workflow* d'appel de fonctions robustes et maintenables.  
- **Concevoir et utiliser** des *tokens* spéciaux pour séparer de manière transparente le raisonnement interne du modèle de ses actions externes.

Et vous **aurez finetuné votre propre modèle pour faire de l'appel de fonctions.** 🔥

Plongeons dans **l'appel de fonctions** !

### Qu'est-ce que l'observabilité et l'évaluation des agents ?
https://huggingface.co/learn/agents-course/fr/bonus-unit2/what-is-agent-observability-and-evaluation.md

# Qu'est-ce que l'observabilité et l'évaluation des agents ?

## 🔎 Qu'est-ce que l'Observabilité ?

L'observabilité consiste à comprendre ce qui se passe à l'intérieur de votre agent en regardant des signaux externes comme les logs, les métriques et les traces. Pour les agents, cela signifie suivre les actions, l'utilisation des outils, les appels de modèles et les réponses pour déboguer et améliorer les performances de l'agent.

![Observability dashboard](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/langfuse-dashboard.png)

## 🔭 Pourquoi l'observabilité des agents est importante

Sans observabilité, les agents sont des « boîtes noires ». Les outils d'observabilité rendent les agents transparents, vous permettant de :

- Comprendre les compromis entre coûts et précision
- Mesurer la latence
- Détecter le langage nuisible et l'injection de *prompts*
- Surveiller les retours utilisateur

En d'autres termes, cela permet de passer votre agent de démonstration à de la production !

## 🔨 Outils d'observabilité

Les outils d'observabilité courants pour les agents incluent des plateformes comme [Langfuse](https://langfuse.com) et [Arize](https://www.arize.com). Ces outils aident à collecter des traces détaillées et offrent des tableaux de bord pour surveiller les métriques en temps réel, facilitant la détection de problèmes et l'optimisation des performances.

Les outils d'observabilité varient largement dans leurs fonctionnalités et capacités. Certains outils sont *open source*, bénéficiant de grandes communautés qui façonnent leurs feuilles de route et de nombreuses intégrations. De plus, certains outils se spécialisent dans des aspects spécifiques des LLMOps (comme l'observabilité, les évaluations ou la gestion des prompts) tandis que d'autres sont conçus pour couvrir l'ensemble du *workflow* LLMOps. Nous vous encourageons à explorer la documentation de différentes options pour choisir la solution qui fonctionne bien pour vous.

De nombreux *frameworks* d'agents tels que [smolagents](https://huggingface.co/docs/smolagents/v1.12.0/en/index) utilisent le standard [OpenTelemetry](https://opentelemetry.io/docs/) pour exposer les métadonnées aux outils d'observabilité. En plus de cela, les outils d'observabilité construisent des instrumentations personnalisées pour permettre plus de flexibilité dans le monde en mouvement rapide des LLM. Vous devriez consulter la documentation de l'outil que vous utilisez pour voir ce qui est pris en charge.

## 🔬Traces et *Spans*

Les outils d'observabilité représentent généralement les exécutions d'agents comme des traces et des *spans*.

- **Les traces** représentent une tâche d'agent complète du début à la fin (comme traiter une requête utilisateur).
- **Les spans** sont des étapes individuelles dans la trace (comme appeler un modèle de langage ou récupérer des données).

![Example of a smolagent trace in Langfuse](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/trace-tree.png)

## 📊 Métriques clés à surveiller

Voici quelques-unes des métriques les plus courantes que les outils d'observabilité surveillent :

**Latence :** À quelle vitesse l'agent répond-il ? Les longs temps d'attente impactent négativement l'expérience utilisateur. Vous devriez mesurer la latence pour les tâches et les étapes individuelles en traçant les exécutions d'agent. Par exemple, un agent qui prend 20 secondes pour tous les appels de modèle pourrait être accéléré en utilisant un modèle plus rapide ou en exécutant les appels de modèle en parallèle.

**Coûts :** Quelle est la dépense par exécution d'agent ? Les agents IA s'appuient sur des LLM facturés par *token* ou des API externes. L'utilisation fréquente d'outils ou de beaucoup de *prompts* peut rapidement augmenter les coûts. Par exemple, si un agent appelle un *LLM* cinq fois pour une amélioration de qualité marginale, vous devez évaluer si le coût est justifié ou si vous pourriez réduire le nombre d'appels ou utiliser un modèle moins cher. La surveillance en temps réel peut aussi aider à identifier des pics inattendus (par exemple, des bugs causant des boucles d'API excessives).

**Erreurs de requête :** Combien de requêtes l'agent a-t-il échouées ? Cela peut inclure des erreurs d'API ou des appels d'outils échoués. Pour rendre votre agent plus robuste contre ceux-ci en production, vous pouvez ensuite mettre en place des solutions de secours ou des tentatives. Par exemple, si le fournisseur de LLM A est en panne, vous basculez vers le fournisseur de LLM B comme *backup*.

**Retours utilisateur :** Implémenter des évaluations utilisateur directes fournit des informations précieuses. Cela peut inclure des évaluations explicites (pouces levés 👍/baissés 👎, ⭐1-5 étoiles) ou des commentaires textuels. Des commentaires négatifs devraient vous alerter car c'est un signe que l'agent ne fonctionne pas comme prévu. 

**Retours utilisateur implicites :** Les comportements utilisateur fournissent des commentaires indirects même sans évaluations explicites. Cela peut inclure la reformulation immédiate de questions, des requêtes répétées ou cliquer sur un bouton de réessai. Par exemple, si vous voyez que les utilisateurs posent répétitivement la même question, c'est un signe que l'agent ne fonctionne pas comme prévu.

**Précision :** À quelle fréquence l'agent produit-il des sorties correctes ou souhaitables ? Les définitions de précision varient (par exemple, exactitude de résolution de problèmes, précision de récupération d'informations, satisfaction utilisateur). La première étape est de définir à quoi ressemble le succès pour votre agent. Vous pouvez suivre la précision via des vérifications automatisées, des scores d'évaluation ou des labels de réussite de tâches. Par exemple, marquer les traces comme « réussies » ou « échouées ». 

**Métriques d'évaluation automatisée :** Vous pouvez aussi mettre en place des évaluations automatisées. Par exemple, vous pouvez utiliser un LLM pour noter la sortie de l'agent, par exemple si elle est utile, précise ou non. Il existe aussi plusieurs bibliothèques *open source* qui vous aident à noter différents aspects de l'agent. Par exemple [RAGAS](https://docs.ragas.io/) pour les agents RAG ou [LLM Guard](https://llm-guard.com/) pour détecter le langage nuisible ou l'injection de *prompts*. 

En pratique, une combinaison de ces métriques donne la meilleure couverture de la santé d'un agent IA. Dans le [*notebook* d'exemple](https://colab.research.google.com/#fileId=https://huggingface.co/agents-course/notebooks/blob/main/fr/bonus-unit2/monitoring-and-evaluating-agents.ipynb) de ce chapitre, nous vous montrerons à quoi ressemblent ces métriques dans des exemples réels mais d'abord, nous apprendrons à quoi ressemble un *workflow* d'évaluation typique.

## 👍 Évaluation des agents

L'observabilité nous donne des métriques, mais l'évaluation est le processus d'analyser ces données (et d'effectuer des tests) pour déterminer à quel point un agent performe et comment il peut être amélioré. En d'autres termes, une fois que vous avez ces traces et métriques, comment les utilisez-vous pour juger l'agent et prendre des décisions ? 

L'évaluation standard est importante car les agents sont souvent non-déterministes et peuvent évoluer (par des mises à jour ou un comportement de modèle dérivant) – sans évaluation, vous ne sauriez pas si votre agent fait réellement bien son travail ou s'il a régressé.

Il y a deux catégories d'évaluations pour les agents : **l'évaluation en ligne** et **l'évaluation hors ligne**. Les deux sont précieuses, et elles se complètent. Nous commençons généralement par l'évaluation hors ligne, car c'est l'étape minimum nécessaire avant de déployer tout agent.

### 🥷 Évaluation hors ligne

![Dataset items in Langfuse](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/example-dataset.png)

Cela implique d'évaluer l'agent dans un environnement contrôlé, typiquement en utilisant des jeux de données de test, pas des requêtes utilisateur en direct. Vous utilisez des jeux de données organisés où vous savez quelle est la sortie attendue ou le comportement correct, puis vous exécutez votre agent sur ceux-ci. 

Par exemple, si vous avez construit un agent résolvant des problèmes mathématiques, vous pourriez avoir un [jeu de données de test](https://huggingface.co/datasets/gsm8k) de 100 problèmes avec des réponses connues. L'évaluation hors ligne est souvent effectuée pendant le développement (et peut faire partie des pipelines CI/CD) pour vérifier les améliorations ou se protéger contre les régressions. L'avantage est que c'est **répétable et vous pouvez obtenir des métriques de précision claires puisque vous avez la vérité terrain**. Vous pourriez aussi simuler des requêtes utilisateur et mesurer les réponses de l'agent contre des réponses idéales ou utiliser des métriques automatisées comme décrit ci-dessus. 

Le défi clé avec l'évaluation hors ligne est d'assurer que votre jeu de données de test est complet et reste pertinent : l'agent pourrait bien performer sur un jeu de test fixe mais rencontrer des requêtes très différentes en production. Par conséquent, vous devriez garder les jeux de test mis à jour avec de nouveaux cas limites et des exemples qui reflètent des scénarios du monde réel. Un mélange de petits cas de *smoke test* et de plus grands jeux d'évaluation est utile : les petits jeux pour des vérifications rapides et les plus grands pour des métriques de performance plus larges.

### 🔄 Évaluation en ligne 

Cela se réfère à l'évaluation de l'agent dans un environnement en direct, du monde réel, c'est-à-dire pendant l'utilisation réelle en production. L'évaluation en ligne implique de surveiller les performances de l'agent sur de vraies interactions utilisateur et d'analyser les résultats en continu. 

Par exemple, vous pourriez suivre les taux de succès, les scores de satisfaction utilisateur, ou d'autres métriques sur le trafic en direct. L'avantage de l'évaluation en ligne est qu'elle **capture des choses que vous pourriez ne pas anticiper dans un environnement de laboratoire**. Vous pouvez observer la dérive du modèle au fil du temps (si l'efficacité de l'agent se dégrade quand les entrées changent) et attraper des requêtes ou situations inattendues qui n'étaient pas dans vos données de test. Elle fournit une vraie image de comment l'agent se comporte dans la nature. 

L'évaluation en ligne implique souvent de collecter des retours utilisateur implicites et explicites, comme discuté, et possiblement d'exécuter des *shadow tests* ou des tests A/B (où une nouvelle version de l'agent fonctionne en parallèle pour comparer à l'ancienne). Le défi est qu'il peut être délicat d'obtenir des labels ou scores fiables pour les interactions en direct. Vous pourriez vous appuyer sur les retours utilisateur ou des métriques en aval (comme est-ce que l'utilisateur a cliqué sur le résultat). 

### 🤝 Combiner les deux

En pratique, l'évaluation réussie d'agent mélange les méthodes **en ligne** et **hors ligne**. Vous pourriez exécuter des *benchmarks* hors ligne standards pour noter quantitativement votre agent sur des tâches définies et surveiller continuellement l'utilisation en direct pour attraper des choses que les *benchmarks* ratent. Par exemple, les tests hors ligne peuvent attraper si le taux de succès d'un agent de génération de code sur un jeu connu de problèmes s'améliore, tandis que la surveillance en ligne pourrait vous alerter que les utilisateurs ont commencé à poser une nouvelle catégorie de question avec laquelle l'agent lutte. Combiner les deux donne une image plus robuste. 

En fait, de nombreuses équipes adoptent une boucle : _évaluation hors ligne → déployer une nouvelle version d'agent → surveiller les métriques en ligne et collecter de nouveaux exemples d'échec → ajouter ces exemples au jeu de test hors ligne → itérer_. De cette façon, l'évaluation est continue et s'améliore constamment.

## 🧑‍💻 Voyons comment cela fonctionne en pratique

Dans la prochaine section, nous verrons des exemples de comment nous pouvons utiliser des outils d'observabilité pour surveiller et évaluer notre agent.

### Observer et évaluer des agents
https://huggingface.co/learn/agents-course/fr/bonus-unit2/monitoring-and-evaluating-agents-notebook.md

# Observer et évaluer des agents

> [!TIP]
> Vous pouvez suivre le code dans ce notebook que vous pouvez exécuter avec Google Colab.

Dans ce *notebook*, nous apprendrons comment **surveiller les étapes internes (traces) de notre agent** et **évaluer ses performances** en utilisant des outils d'observabilité *open source*.

La capacité d'observer et d'évaluer le comportement d'un agent est essentielle pour :
- Déboguer les problèmes lorsque les tâches échouent ou produisent des résultats sous-optimaux
- Contrôler les coûts et les performances en temps réel
- Améliorer la fiabilité et la sécurité grâce à un retour d'information continu

## Prérequis de l'exercice 🏗️

Avant d'exécuter ce *notebook*, assurez-vous d'avoir :

🔲 📚 **Etudier la section [Introduction aux agents](https://huggingface.co/learn/agents-course/fr/unit1/introduction)**

🔲 📚 **Etudier la section [le *framework* smolagents](https://huggingface.co/learn/agents-course/fr/unit2/smolagents/introduction)**

## Étape 0 : Installer les bibliothèques nécessaires

Nous aurons besoin de quelques bibliothèques qui nous permettront d'exécuter, de contrôler et d'évaluer nos agents :

```python
%pip install langfuse 'smolagents[telemetry]' openinference-instrumentation-smolagents datasets 'smolagents[gradio]' gradio --upgrade
```

## Étape 1 : Instrumenter votre agent

Dans ce *notebook*, nous utiliserons [Langfuse](https://langfuse.com/) comme outil d'observabilité, mais vous pouvez utiliser **n'importe quel autre service compatible avec OpenTelemetry**. Le code ci-dessous montre comment définir les variables d'environnement pour Langfuse (ou n'importe quel *endpoint OTel*) et comment instrumenter votre smolagent.

**Note :** Si vous utilisez LlamaIndex ou LangGraph, vous pouvez trouver de la documentation sur leur instrumentation [ici](https://langfuse.com/docs/integrations/llama-index/workflows) et [ici](https://langfuse.com/docs/integrations/langchain/example-python-langgraph).

D'abord, configurons les credentials *Langfuse* comme variables d'environnement. Obtenez vos clés API *Langfuse* en vous inscrivant sur [Langfuse Cloud](https://cloud.langfuse.com) ou en [auto-hébergeant Langfuse](https://langfuse.com/self-hosting).

```python
import os
# Obtenez les clés pour votre projet depuis la page des paramètres du projet : https://cloud.langfuse.com
os.environ["LANGFUSE_PUBLIC_KEY"] = "pk-lf-..." 
os.environ["LANGFUSE_SECRET_KEY"] = "sk-lf-..." 
os.environ["LANGFUSE_HOST"] = "https://cloud.langfuse.com" # 🇪🇺 région EU
# os.environ["LANGFUSE_HOST"] = "https://us.cloud.langfuse.com" # 🇺🇸 région US
```
Nous devons aussi configurer notre token *Hugging Face* pour les appels d'inférence.

```python
# Définissez vos tokens/secrets Hugging Face comme variable d'environnement
os.environ["HF_TOKEN"] = "hf_..." 
```

Avec les variables d'environnement définies, nous pouvons maintenant initialiser le client *Langfuse*. `get_client()` initialise le client *Langfuse* en utilisant les credentials fournis dans les variables d'environnement.

```python
from langfuse import get_client
 
langfuse = get_client()
 
# Vérifier la connexion
if langfuse.auth_check():
    print("Le client Langfuse est authentifié et prêt !")
else:
    print("L'authentification a échoué. Veuillez vérifier vos credentials et hôte.")
```

Ensuite, nous pouvons configurer le `SmolagentsInstrumentor()` pour instrumenter notre *smolagent* et envoyer des traces à *Langfuse*.

```python
from openinference.instrumentation.smolagents import SmolagentsInstrumentor
 
SmolagentsInstrumentor().instrument()
```

## Étape 2 : Testez votre instrumentation

Voici un simple *CodeAgent* de smolagents qui calcule `1+1`. Nous l'exécutons pour confirmer que l'instrumentation fonctionne correctement. Si tout est configuré correctement, vous verrez des logs/spans dans votre tableau de bord d'observabilité.

```python
from smolagents import InferenceClientModel, CodeAgent

# Créer un agent basique pour tester l'instrumentation
agent = CodeAgent(
    tools=[],
    model=InferenceClientModel()
)

agent.run("1+1=")
```

Consultez votre [Langfuse Traces Dashboard](https://cloud.langfuse.com/traces) (ou l'outil d'observabilité de votre choix) pour confirmer que les portées et les logs ont été enregistrés.

Exemple de capture d'écran de Langfuse :

![Example trace in Langfuse](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/first-example-trace.png)

_[Lien vers la trace](https://cloud.langfuse.com/project/cloramnkj0002jz088vzn1ja4/traces/1b94d6888258e0998329cdb72a371155?timestamp=2025-03-10T11%3A59%3A41.743Z)_

## Étape 3 : Observer et évaluer un agent plus complexe

Maintenant que vous avez confirmé que votre instrumentation fonctionne, essayons une requête plus complexe afin de voir comment les mesures avancées (utilisation des *tokens*, latence, coûts, etc.) sont suivies.

```python
from smolagents import (CodeAgent, DuckDuckGoSearchTool, InferenceClientModel)

search_tool = DuckDuckGoSearchTool()
agent = CodeAgent(tools=[search_tool], model=InferenceClientModel())

agent.run("Combien de Rubik's Cubes pourrait-on faire tenir dans la Cathédrale Notre-Dame ?")
```

### Structure de la trace

La plupart des outils d'observabilité enregistrent une **trace** qui contient des **spans**, qui représentent chaque étape de la logique de votre agent. Ici, la trace contient l'exécution globale de l'agent et les sous-périodes pour :
- les appels à l'outil (DuckDuckGoSearchTool)
- Les appels LLM (InferenceClientModel)

Vous pouvez les inspecter pour voir précisément où le temps est passé, combien de *tokens* sont utilisés, etc. :

![Trace tree in Langfuse](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/trace-tree.png)

_[Lien vers la trace](https://cloud.langfuse.com/project/cloramnkj0002jz088vzn1ja4/traces/1ac33b89ffd5e75d4265b62900c348ed?timestamp=2025-03-07T13%3A45%3A09.149Z&display=preview)_

## Évaluation en ligne

Dans la section précédente, nous avons appris la différence entre l'évaluation en ligne et hors ligne. Nous allons maintenant voir comment surveiller votre agent en production et l'évaluer en direct.

### Métriques courantes à suivre en production

1. **Coûts** - L'instrumentation smolagents capture l'utilisation des *tokens*, que vous pouvez transformer en coûts approximatifs en assignant un prix par *token*.
2. **Latence** - Observez le temps nécessaire à la réalisation de chaque étape ou de l'ensemble de l'exécution.
3. **Retour utilisateur** - Les utilisateurs peuvent fournir un retour direct (pouce vers le haut/vers le bas) pour aider à affiner ou à corriger l'agent.
4. ***LLM-as-a-Judge*** - Utilisez un autre LLM pour évaluer les résultats de votre agent en quasi temps réel (par exemple, vérification de la toxicité ou de l'exactitude des résultats).

Ci-dessous, nous montrons des exemples de ces métriques.

#### 1. Coûts

Vous trouverez ci-dessous une capture d'écran montrant l'utilisation des appels `Qwen2.5-Coder-32B-Instruct`. Ceci est utile pour voir les étapes coûteuses et optimiser votre agent.

![Costs](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/smolagents-costs.png)

_[Lien vers la trace](https://cloud.langfuse.com/project/cloramnkj0002jz088vzn1ja4/traces/1ac33b89ffd5e75d4265b62900c348ed?timestamp=2025-03-07T13%3A45%3A09.149Z&display=preview)_

#### 2. Temps de latence

Nous pouvons également voir combien de temps a duré chaque étape. Dans l'exemple ci-dessous, l'ensemble de la conversation a duré 32 secondes, que vous pouvez répartir par étape. Cela vous permet d'identifier les goulets d'étranglement et d'optimiser votre agent.

![Latency](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/smolagents-latency.png)

_[Lien vers la trace](https://cloud.langfuse.com/project/cloramnkj0002jz088vzn1ja4/traces/1ac33b89ffd5e75d4265b62900c348ed?timestamp=2025-03-07T13%3A45%3A09.149Z&display=preview)_blank

#### 3. Attributs supplémentaires

Vous pouvez également passer des attributs supplémentaires à vos spans. Ceux-ci peuvent inclure `user_id`, `tags`, `session_id`, et des métadonnées personnalisées. Enrichir les traces avec ces détails est important pour l'analyse, le débogage et la surveillance du comportement de votre application à travers différents utilisateurs ou sessions.

```python
from smolagents import (CodeAgent, DuckDuckGoSearchTool, InferenceClientModel)

search_tool = DuckDuckGoSearchTool()
agent = CodeAgent(
    tools=[search_tool],
    model=InferenceClientModel()
)

with langfuse.start_as_current_span(
    name="Smolagent-Trace",
    ) as span:
    
    # Exécutez votre application ici
    response = agent.run("Quelle est la capitale de l'Allemagne ?")
 
    # Passez des attributs supplémentaires au span
    span.update_trace(
        input="Quelle est la capitale de l'Allemagne ?",
        output=response,
        user_id="smolagent-user-123",
        session_id="smolagent-session-123456789",
        tags=["question-ville", "test-agents"],
        metadata={"email": "user@langfuse.com"},
        )
 
# Flusher les événements dans les applications de courte durée
langfuse.flush()
```

![Enhancing agent runs with additional metrics](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/smolagents-attributes.png)

#### 4. #### 4. Retour utilisateur

Si votre agent est intégré dans une interface utilisateur, vous pouvez enregistrer les réactions directes de l'utilisateur (comme un pouce levé ou baissé dans une interface de discussion). Vous trouverez ci-dessous un exemple utilisant [Gradio](https://gradio.app/) pour intégrer un chat avec un mécanisme de retour d'information simple.

Dans l'extrait de code ci-dessous, lorsqu'un utilisateur envoie un message de chat, nous capturons la trace dans Langfuse. Si l'utilisateur aime ou n'aime pas la dernière réponse, nous attribuons un score à la trace.

```python
import gradio as gr
from smolagents import (CodeAgent, InferenceClientModel)
from langfuse import get_client

langfuse = get_client()

model = InferenceClientModel()
agent = CodeAgent(tools=[], model=model, add_base_tools=True)

trace_id = None

def respond(prompt, history):
    with langfuse.start_as_current_span(
        name="Smolagent-Trace"):
        
        # Exécutez votre application ici
        output = agent.run(prompt)

        global trace_id
        trace_id = langfuse.get_current_trace_id()

    history.append({"role": "assistant", "content": str(output)})
    return history

def handle_like(data: gr.LikeData):
    # Pour la démonstration, nous mappons les retours utilisateur à 1 (j'aime) ou 0 (je n'aime pas)
    if data.liked:
        langfuse.create_score(
            value=1,
            name="user-feedback",
            trace_id=trace_id
        )
    else:
        langfuse.create_score(
            value=0,
            name="user-feedback",
            trace_id=trace_id
        )

with gr.Blocks() as demo:
    chatbot = gr.Chatbot(label="Chat", type="messages")
    prompt_box = gr.Textbox(placeholder="Tapez votre message...", label="Votre message")

    # Lorsque l'utilisateur appuie sur "Enter", nous exécutons 'respond'
    prompt_box.submit(
        fn=respond,
        inputs=[prompt_box, chatbot],
        outputs=chatbot
    )

    # Lorsque l'utilisateur clique sur le bouton "J'aime" d'un message, nous exécutons 'handle_like'
    chatbot.like(handle_like, None, None)

demo.launch()
```

Les retours des utilisateurs sont ensuite saisis dans votre outil d'observabilité :

![User feedback is being captured in Langfuse](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/user-feedback-gradio.png)

#### 5. LLM-as-a-Judge

*LLM-as-a-Judge* est une autre façon d'évaluer automatiquement les résultats de votre agent. Vous pouvez configurer l'appel d'un autre LLM pour évaluer l'exactitude, la toxicité, le style ou tout autre critère qui vous intéresse.

**Fonctionnement** :
1. Vous définissez un **Modèle d'évaluation**, par exemple, « Vérifier si le texte est toxique ».
2. Chaque fois que votre agent génère un résultat, vous transmettez ce résultat à votre LLM juge avec le gabarit.
3. Le LLM juge répond avec un score ou une étiquette que vous enregistrez dans votre outil d'observabilité.

Exemple de Langfuse :

![LLM-as-a-Judge Evaluation Template](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/evaluator-template.png)
![LLM-as-a-Judge Evaluator](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/evaluator.png)

```python
# Exemple : Vérifier si la sortie de l'agent est toxique ou non
from smolagents import (CodeAgent, DuckDuckGoSearchTool, InferenceClientModel)

search_tool = DuckDuckGoSearchTool()
agent = CodeAgent(tools=[search_tool], model=InferenceClientModel())

agent.run("Manger des carottes peut-il améliorer votre vision ?")
```

Vous pouvez voir que la réponse de cet exemple est jugée « non toxique ».

![LLM-as-a-Judge Evaluation Score](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/llm-as-a-judge-score.png)

#### 6. Aperçu des métriques d'observabilité

Toutes ces métriques peuvent être visualisées ensemble dans des tableaux de bord. Cela vous permet de voir rapidement les performances de votre agent sur plusieurs sessions et vous aide à suivre les mesures de qualité au fil du temps.

![Observability metrics overview](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/langfuse-dashboard.png)

## Évaluation hors ligne

L'évaluation en ligne est essentielle pour obtenir un retour d'information en temps réel, mais vous avez également besoin d'une **évaluation hors ligne**, c'est-à-dire de vérifications systématiques avant ou pendant le développement. Cela permet de maintenir la qualité et la fiabilité avant de mettre les changements en production.

### Évaluation d'un jeu de données

Lors d'une évaluation hors ligne, vous devez généralement
1. Disposer d'un jeu de données de référence (avec des paires de *prompts* et de résultats attendus)
2. Exécuter votre agent sur ce jeu de données
3. Comparer les résultats aux résultats attendus ou utiliser un mécanisme de notation supplémentaire.

Ci-dessous, nous démontrons cette approche avec le jeu de données [GSM8K](https://huggingface.co/datasets/gsm8k), qui contient des questions et des solutions mathématiques.

```python
import pandas as pd
from datasets import load_dataset

# Récupérer GSM8K sur Hugging Face
dataset = load_dataset("openai/gsm8k", 'main', split='train')
df = pd.DataFrame(dataset)
print("Premières lignes du jeu de données GSM8K :")
print(df.head())
```

Ensuite, nous créons un jeu de données dans Langfuse pour suivre les exécutions. Nous ajoutons ensuite chaque élément du jeu de données au système.  
(Si vous n'utilisez pas Langfuse, vous pouvez simplement les stocker dans votre propre base de données ou dans un fichier local à des fins d'analyse).

```python
from langfuse import get_client
langfuse = get_client()

langfuse_dataset_name = "gsm8k_dataset_huggingface"

# Créer un jeu de données dans Langfuse
langfuse.create_dataset(
    name=langfuse_dataset_name,
    description="Jeu de données de référence GSM8K téléchargé depuis Huggingface",
    metadata={
        "date": "2025-03-10", 
        "type": "benchmark"
    }
)
```

```python
for idx, row in df.iterrows():
    langfuse.create_dataset_item(
        dataset_name=langfuse_dataset_name,
        input={"text": row["question"]},
        expected_output={"text": row["answer"]},
        metadata={"source_index": idx}
    )
    if idx >= 9: # Télécharger seulement les 10 premiers éléments pour la démonstration
        break
```

![Dataset items in Langfuse](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/example-dataset.png)

#### Exécution de l'agent sur le jeu de données

Nous définissons une fonction d'aide `run_smolagent()` qui :
1. Démarre un span Langfuse
2. Exécute notre agent sur le *prompt*
3. Enregistre l'ID de la trace dans Langfuse

Ensuite, nous parcourons en boucle chaque élément de l'ensemble de données, nous exécutons l'agent et nous lions la trace à l'élément de l'ensemble de données. Nous pouvons également joindre une note d'évaluation rapide si vous le souhaitez.

```python
from opentelemetry.trace import format_trace_id
from smolagents import (CodeAgent, InferenceClientModel, LiteLLMModel)
from langfuse import get_client
 
langfuse = get_client()

# Exemple : utiliser InferenceClientModel ou LiteLLMModel pour accéder aux modèles openai, anthropic, gemini, etc. :
model = InferenceClientModel()

agent = CodeAgent(
    tools=[],
    model=model,
    add_base_tools=True
)

dataset_name = "gsm8k_dataset_huggingface"
current_run_name = "smolagent-notebook-run-01" # Identifie cette exécution d'évaluation spécifique
 
# Supposons que 'run_smolagent' est votre fonction d'application instrumentée
def run_smolagent(question):
    with langfuse.start_as_current_generation(name="qna-llm-call") as generation:
        # Simuler un appel LLM
        result = agent.run(question)
 
        # Mettre à jour la trace avec l'entrée et la sortie
        generation.update_trace(
            input= question,
            output=result,
        )
 
        return result
 
dataset = langfuse.get_dataset(name=dataset_name) # Récupérer votre jeu de données pré-peuplé
 
for item in dataset.items:
 
    # Utiliser le gestionnaire de contexte item.run()
    with item.run(
        run_name=current_run_name,
        run_metadata={"model_provider": "Hugging Face", "temperature_setting": 0.7},
        run_description="Exécution d'évaluation pour le jeu de données GSM8K"
    ) as root_span: # root_span est le span racine de la nouvelle trace pour cet élément et exécution.
        # Toutes les opérations langfuse suivantes dans ce bloc font partie de cette trace.
 
        # Appelez votre logique d'application
        generated_answer = run_smolagent(question=item.input["text"])
 
        print(item.input)
```

Vous pouvez répéter ce processus avec différents :
- Modèles (OpenAI GPT, LLM local, etc.)
- Outils (recherche ou pas recherche)
- Prompts (différents messages du système)

Ensuite, comparez-les côte à côte dans votre outil d'observabilité :

![Dataset run overview](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/dataset_runs.png)
![Dataset run comparison](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/bonus-unit2/dataset-run-comparison.png)

## Réflexions finales

Dans ce *notebook*, nous avons vu comment :
1. **Mettre en place l'observabilité** en utilisant les exportateurs smolagents + OpenTelemetry
2. **Vérifier l'instrumentation** en lançant un agent simple
3. **Capturez des métriques détaillées** (coût, latence, etc.) à l'aide d'outils d'observabilité
4. **Recueillir les commentaires des utilisateurs** via une interface Gradio
5. **Utiliser un LLM-as-a-Judge** pour évaluer automatiquement les résultats
6. **Effectuer une évaluation hors ligne** avec un jeu de données de référence

🤗 Bon codage !

### Introduction
https://huggingface.co/learn/agents-course/fr/bonus-unit2/introduction.md

# Introduction

![Bonus Unit 2 Thumbnail](https://langfuse.com/images/cookbook/huggingface-agent-course/agent-observability-and-evaluation.png)

Bienvenue dans l'**Unité Bonus 2** ! Dans ce chapitre, vous explorerez des stratégies avancées pour observer, évaluer et finalement améliorer les performances de vos agents.

---

## 📚 Quand dois-je faire cette Unité Bonus ?

Cette unité bonus est parfaite si vous :
- **Développez et déployez des agents :** Vous voulez vous assurer que vos agents performent de manière fiable en production.
- **Avez besoin d'informations détaillées :** Vous cherchez à diagnostiquer des problèmes, optimiser les performances, ou comprendre le fonctionnement interne de votre agent.
- **Visez à réduire les coûts opérationnels :** En surveillant les coûts des agents, la latence et les détails d'exécution, vous pouvez gérer efficacement les ressources.
- **Recherchez une amélioration continue :** Vous êtes intéressé par l'intégration de retour utilisateur en temps réel et d'évaluation automatisée dans vos applications.

En résumé, pour tous ceux qui veulent mettre leurs agents face à des utilisateurs !

---

## 🤓 Ce que vous allez apprendre

Dans cette unité, vous verrez comment :
- **Instrumenter votre agent :** Apprenez comment intégrer des outils d'observabilité via *OpenTelemetry* avec le *framework* smolagents.
- **Surveiller les métriques :** Suivez les indicateurs de performance tels que l'utilisation du nombre de *tokens* (coûts), la latence et les traces d'erreur.
- **Évaluer en temps réel :** Comprenez les techniques pour l'évaluation en direct, y compris la collecte de retour utilisateur et l'utilisation d'un *LLM-as-a-judge*.
- **Analyse hors ligne :** Utilisez des jeux de données de référence (par exemple, GSM8K) pour tester et comparer les performances des agents.

---

## 🚀 Prêt à commencer ?

Dans la prochaine section, vous allez voir les bases de l'observabilité et de l'évaluation des agents. Après cela, il sera temps de les voir en action !

### Quiz : évaluation des agents
https://huggingface.co/learn/agents-course/fr/bonus-unit2/quiz.md

# Quiz : évaluation des agents

Évaluons votre compréhension des concepts de traçage et d'évaluation des agents abordés dans cette unité bonus.

Ce quiz est optionnel et non noté.

### Q1 : À quoi l'observabilité dans les agents fait-elle principalement référence ?
Quelle déclaration décrit avec précision le but de l'observabilité pour les agents ?

spans pour comprendre le comportement de l'agent.",
    explain: "Correct ! L'observabilité signifie utiliser des logs, métriques et spans pour éclairer le fonctionnement interne de l'agent.",
    correct: true
  },
  {
    text: "Elle se concentre sur la réduction du coût financier d'exécution de l'agent.",
    explain: "L'observabilité couvre les coûts mais ne s'y limite pas."
  },
  {
    text: "Elle se réfère à l'apparence externe et l'interface utilisateur de l'agent.",
    explain: "L'observabilité concerne les processus internes, pas l'interface utilisateur."
  },
  {
    text: "Elle ne concerne que le style de code et l'esthétique du code.",
    explain: "Le style de code n'est pas lié à l'observabilité dans ce contexte."
  }
]}
/>

### Q2 : Laquelle des suivantes N'EST PAS une métrique commune surveillée dans l'observabilité des agents ?
Sélectionnez la métrique qui ne tombe pas typiquement sous le parapluie de l'observabilité.

### Q3 : Qu'est-ce qui décrit le mieux l'évaluation hors ligne d'un agent ?
Déterminez la déclaration qui capture correctement l'essence de l'évaluation hors ligne.

### Q4 : Quel avantage l'évaluation en ligne des agents offre-t-elle ?
Choisissez la déclaration qui reflète le mieux l'avantage de l'évaluation en ligne.

benchmarks.",
    explain: "Les évaluations hors ligne et en ligne sont importantes et complémentaires."
  },
  {
    text: "Elle se concentre uniquement sur la réduction du coût computationnel de l'agent.",
    explain: "Le suivi des coûts fait partie de l'observabilité, pas l'avantage principal de l'évaluation en ligne."
  }
]}
/>

### Q5 : Quel rôle OpenTelemetry joue-t-il dans l'observabilité et l'évaluation des agents ?
Quelle déclaration décrit le mieux le rôle d'OpenTelemetry dans la surveillance des agents ?

framework standardisé pour instrumenter le code, permettant la collecte de traces, métriques et logs pour l'observabilité.",
    explain: "Correct ! OpenTelemetry standardise l'instrumentation pour les données de télémétrie, ce qui est crucial pour surveiller et diagnostiquer le comportement des agents.",
    correct: true
  },
  {
    text: "Il agit comme un remplacement pour le débogage manuel en corrigeant automatiquement les problèmes de code.",
    explain: "Incorrect. OpenTelemetry est utilisé pour rassembler des données de télémétrie, pas pour déboguer les problèmes de code."
  },
  {
    text: "Il sert principalement comme une base de données pour stocker les logs historiques sans capacités temps réel.",
    explain: "Incorrect. OpenTelemetry se concentre sur la collecte de données de télémétrie en temps réel et l'export de données vers des outils d'analyse."
  },
  {
    text: "Il est utilisé pour optimiser les performances computationnelles de l'agent en ajustant automatiquement les paramètres du modèle.",
    explain: "Incorrect. OpenTelemetry est centré sur l'observabilité plutôt que sur l'ajustement des performances."
  }
]}
/>

Félicitations pour avoir terminé ce quiz ! 🎉 Si vous avez manqué des questions, revoyez le contenu de cette unité bonus pour une compréhension plus approfondie. Si vous avez bien réussi, vous êtes prêt à explorer des sujets plus avancés en observabilité et évaluation des agents !

### L'état de l'art de l'utilisation des LLM dans les jeux
https://huggingface.co/learn/agents-course/fr/bonus-unit3/state-of-art.md

# L'état de l'art de l'utilisation des LLM dans les jeux

Pour vous donner une idée de l'ampleur des progrès réalisés dans ce domaine, examinons trois démonstrations techniques et un jeu publié qui illustrent l'intégration des LLM dans le *gaming*.

## 🕵️‍♂️ *Covert Protocol* par NVIDIA et Inworld AI

Dévoilé au GDC 2024, *Covert Protocol* est une démo technique qui vous met dans la peau d'un détective privé. 

Ce qui est intéressant dans cette démo est l'utilisation de PNJ alimentés par l'IA qui répondent à vos questions en temps réel, influençant le récit basé sur vos interactions. 

La démo est construite sur *Unreal Engine* 5, elle exploite le *Avatar Cloud Engine* (ACE) de NVIDIA et l'IA d'*Inworld* pour créer des interactions de personnages réalistes. 

En savoir plus ici 👉 [*Inworld AI Blog*](https://inworld.ai/blog/nvidia-inworld-ai-demo-on-device-capabilities)

## 🤖 *NEO NPCs* par Ubisoft

Aussi au GDC 2024, *Ubisoft* a introduit *NEO NPCs*, un prototype montrant des PNJ alimentés par l'IA générative. 

Ces personnages peuvent percevoir leur environnement, se souvenir d'interactions passées et s'engager dans des conversations significatives avec les joueurs. 

L'idée ici est de créer des mondes de jeu plus immersifs et réactifs où le joueur peut avoir une vraie interaction avec les PNJ.

En savoir plus ici 👉 [*Inworld AI Blog*](https://inworld.ai/blog/gdc-2024)

## ⚔️ *Mecha BREAK* avec l'ACE de NVIDIA

*Mecha BREAK*, un jeu de combat de mecha multijoueur à venir, intègre la technologie ACE de NVIDIA pour donner vie à des PNJ alimentés par l'IA. 

Les joueurs peuvent interagir avec ces personnages en utilisant le langage naturel, et les PNJ peuvent reconnaître les joueurs et objets via la *webcam*, grâce à l'intégration de GPT-4o. Cette innovation promet une expérience de jeu plus immersive et interactive.

En savoir plus ici 👉 [*NVIDIA Blog*](https://blogs.nvidia.com/blog/digital-human-technology-mecha-break/)

## 🧛‍♂️ *Suck Up!* par Proxima Enterprises

Enfin, *Suck Up!* est un jeu publié où vous jouez un vampire qui tente de pénétrer dans des maisons en **convaincant des PNJ alimentés par l'IA de vous inviter à entrer.**

Chaque personnage est piloté par l'IA générative, permettant des interactions dynamiques et imprévisibles. 

En savoir plus ici 👉 [*Suck Up! Official Website*](https://www.playsuckup.com/)

## Attendez... Où sont les agents ?

Après avoir exploré ces démos, vous vous demandez peut-être : « Ces exemples montrent l'utilisation des LLM dans les jeux mais ils ne semblent pas impliquer d'agents. Alors, quelle est la distinction, et quelles capacités supplémentaires les agents apportent-ils ? » 

Ne vous inquiétez pas, c'est ce que nous allons étudier dans la prochaine section.

### Construire un agent de combat Pokémon
https://huggingface.co/learn/agents-course/fr/bonus-unit3/building_your_pokemon_agent.md

# Construire un agent de combat Pokémon

Maintenant que vous avez exploré le potentiel et les limitations de l'IA agentique dans les jeux vidéos, il est temps de passer à la pratique. Dans cette section, vous allez **construire votre propre agent pour combattre dans un combat au tour par tour dans le style de Pokémon**, en utilisant tout ce que vous avez appris à travers le cours.

Nous décomposerons le système en quatre blocs de construction clés :

- **Poke-env :** Une bibliothèque Python conçue pour entraîner des bots Pokémon basés sur des règles ou par apprentissage par renforcement.

- **Pokémon Showdown :** Un simulateur de combat en ligne où votre agent combattra.

- **LLMAgentBase :** Une classe Python personnalisée que nous avons construite pour connecter votre LLM avec l'environnement de combat *Poke-env*.

- **TemplateAgent :** Un *template* de démarrage que vous compléterez pour créer votre propre agent de combat personnalisé.

Explorons chacun de ces composants plus en détail.

## 🧠 Poke-env

![Battle gif](https://github.com/hsahovic/poke-env/raw/master/rl-gif.gif)

[Poke-env](https://github.com/hsahovic/poke-env) est une interface Python originellement construite pour entraîner des bots d'apprentissage par renforcement par [Haris Sahovic](https://huggingface.co/hsahovic), mais nous l'avons réutilisée pour l'IA agentique.  
Elle permet à votre agent d'interagir avec *Pokémon Showdown* à travers une API simple.

Elle fournit une classe `Player` de laquelle votre agent héritera, couvrant tout ce qui est nécessaire pour communiquer avec l'interface graphique.

**Documentation** : [poke-env.readthedocs.io](https://poke-env.readthedocs.io/en/stable/)  
**Dépôt** : [github.com/hsahovic/poke-env](https://github.com/hsahovic/poke-env)

## ⚔️ Pokémon Showdown

[Pokémon Showdown](https://pokemonshowdown.com/) est un simulateur de combat [*open-source*](https://github.com/smogon/Pokemon-Showdown) où votre agent jouera des combats Pokémon en direct.  
Il fournit une interface complète pour simuler et afficher des combats en temps réel. Dans notre défi, le bot agira exactement comme un joueur humain, choisissant des mouvements tour par tour.

Nous avons déployé un serveur que tous les participants utiliseront pour combattre. Voyons qui construit le meilleur agent de combat !

**Dépôt** : [github.com/smogon/Pokemon-Showdown](https://github.com/smogon/Pokemon-Showdown)  
**Site web** : [pokemonshowdown.com](https://pokemonshowdown.com/)

## 🔌 LLMAgentBase

`LLMAgentBase` est une classe Python qui étend la classe `Player` de **Poke-env**.  
Elle sert de pont entre votre **LLM** et le **simulateur de combat Pokémon**, gérant le formatage d'entrée/sortie et maintenant le contexte de combat.

Cet agent de base fournit un ensemble d'outils (définis dans `STANDARD_TOOL_SCHEMA`) pour interagir avec l'environnement, incluant :

- `choose_move` : pour sélectionner une attaque pendant le combat  
- `choose_switch` : pour changer de Pokémon  

Le LLM devrait utiliser ces outils pour prendre des décisions pendant un match.

### 🧠 Logique centrale

- `choose_move(battle: Battle)` : La méthode principale invoquée à chaque tour. Elle prend un objet `Battle` et retourne une chaîne d'action basée sur la sortie du LLM.

### 🔧 Méthodes internes clés

- `_format_battle_state(battle)` : Convertit l'état actuel du combat en chaîne, la rendant adaptée pour l'envoi au LLM.

- `_find_move_by_name(battle, move_name)` : Trouve un mouvement par nom, utilisé dans les réponses LLM qui appellent `choose_move`.

- `_find_pokemon_by_name(battle, pokemon_name)` : Localise un Pokémon spécifique vers lequel changer, basé sur la commande de changement du LLM.

- `_get_llm_decision(battle_state)` : Cette méthode est abstraite dans la classe de base. Vous devrez l'implémenter dans votre propre agent (voir prochaine section), où vous définissez comment interroger le LLM et analyser sa réponse.

Voici un extrait montrant comment cette prise de décision fonctionne :

```python
STANDARD_TOOL_SCHEMA = {
    "choose_move": {
        ...
    },
    "choose_switch": {
        ...
    },
}

class LLMAgentBase(Player):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.standard_tools = STANDARD_TOOL_SCHEMA
        self.battle_history = []

    def _format_battle_state(self, battle: Battle) -> str:
        active_pkmn = battle.active_pokemon
        active_pkmn_info = f"Your active Pokemon: {active_pkmn.species} " \
                           f"(Type: {'/'.join(map(str, active_pkmn.types))}) " \
                           f"HP: {active_pkmn.current_hp_fraction * 100:.1f}% " \
                           f"Status: {active_pkmn.status.name if active_pkmn.status else 'None'} " \
                           f"Boosts: {active_pkmn.boosts}"

        opponent_pkmn = battle.opponent_active_pokemon
        opp_info_str = "Unknown"
        if opponent_pkmn:
            opp_info_str = f"{opponent_pkmn.species} " \
                           f"(Type: {'/'.join(map(str, opponent_pkmn.types))}) " \
                           f"HP: {opponent_pkmn.current_hp_fraction * 100:.1f}% " \
                           f"Status: {opponent_pkmn.status.name if opponent_pkmn.status else 'None'} " \
                           f"Boosts: {opponent_pkmn.boosts}"
        opponent_pkmn_info = f"Opponent's active Pokemon: {opp_info_str}"

        available_moves_info = "Available moves:\n"
        if battle.available_moves:
            available_moves_info += "\n".join(
                [f"- {move.id} (Type: {move.type}, BP: {move.base_power}, Acc: {move.accuracy}, PP: {move.current_pp}/{move.max_pp}, Cat: {move.category.name})"
                 for move in battle.available_moves]
            )
        else:
             available_moves_info += "- None (Must switch or Struggle)"

        available_switches_info = "Available switches:\n"
        if battle.available_switches:
              available_switches_info += "\n".join(
                  [f"- {pkmn.species} (HP: {pkmn.current_hp_fraction * 100:.1f}%, Status: {pkmn.status.name if pkmn.status else 'None'})"
                   for pkmn in battle.available_switches]
              )
        else:
            available_switches_info += "- None"

        state_str = f"{active_pkmn_info}\n" \
                    f"{opponent_pkmn_info}\n\n" \
                    f"{available_moves_info}\n\n" \
                    f"{available_switches_info}\n\n" \
                    f"Weather: {battle.weather}\n" \
                    f"Terrains: {battle.fields}\n" \
                    f"Your Side Conditions: {battle.side_conditions}\n" \
                    f"Opponent Side Conditions: {battle.opponent_side_conditions}"
        return state_str.strip()

    def _find_move_by_name(self, battle: Battle, move_name: str) -> Optional[Move]:
        normalized_name = normalize_name(move_name)
        # Prioriser la correspondance exacte d'ID
        for move in battle.available_moves:
            if move.id == normalized_name:
                return move
        # Solution de secours : Vérifier le nom d'affichage (moins fiable)
        for move in battle.available_moves:
            if move.name.lower() == move_name.lower():
                print(f"Warning: Matched move by display name '{move.name}' instead of ID '{move.id}'. Input was '{move_name}'.")
                return move
        return None

    def _find_pokemon_by_name(self, battle: Battle, pokemon_name: str) -> Optional[Pokemon]:
        normalized_name = normalize_name(pokemon_name)
        for pkmn in battle.available_switches:
            # Normaliser le nom d'espèce pour la comparaison
            if normalize_name(pkmn.species) == normalized_name:
                return pkmn
        return None

    async def choose_move(self, battle: Battle) -> str:
        battle_state_str = self._format_battle_state(battle)
        decision_result = await self._get_llm_decision(battle_state_str)
        print(decision_result)
        decision = decision_result.get("decision")
        error_message = decision_result.get("error")
        action_taken = False
        fallback_reason = ""

        if decision:
            function_name = decision.get("name")
            args = decision.get("arguments", {})
            if function_name == "choose_move":
                move_name = args.get("move_name")
                if move_name:
                    chosen_move = self._find_move_by_name(battle, move_name)
                    if chosen_move and chosen_move in battle.available_moves:
                        action_taken = True
                        chat_msg = f"AI Decision: Using move '{chosen_move.id}'."
                        print(chat_msg)
                        return self.create_order(chosen_move)
                    else:
                        fallback_reason = f"LLM chose unavailable/invalid move '{move_name}'."
                else:
                     fallback_reason = "LLM 'choose_move' called without 'move_name'."
            elif function_name == "choose_switch":
                pokemon_name = args.get("pokemon_name")
                if pokemon_name:
                    chosen_switch = self._find_pokemon_by_name(battle, pokemon_name)
                    if chosen_switch and chosen_switch in battle.available_switches:
                        action_taken = True
                        chat_msg = f"AI Decision: Switching to '{chosen_switch.species}'."
                        print(chat_msg)
                        return self.create_order(chosen_switch)
                    else:
                        fallback_reason = f"LLM chose unavailable/invalid switch '{pokemon_name}'."
                else:
                    fallback_reason = "LLM 'choose_switch' called without 'pokemon_name'."
            else:
                fallback_reason = f"LLM called unknown function '{function_name}'."

        if not action_taken:
            if not fallback_reason:
                 if error_message:
                     fallback_reason = f"API Error: {error_message}"
                 elif decision is None:
                      fallback_reason = "LLM did not provide a valid function call."
                 else:
                      fallback_reason = "Unknown error processing LLM decision."

            print(f"Warning: {fallback_reason} Choosing random action.")

            if battle.available_moves or battle.available_switches:
                 return self.choose_random_move(battle)
            else:
                 print("AI Fallback: No moves or switches available. Using Struggle/Default.")
                 return self.choose_default_move(battle)

    async def _get_llm_decision(self, battle_state: str) -> Dict[str, Any]:
        raise NotImplementedError("Subclasses must implement _get_llm_decision")
```

**Code source complet** : [agents.py](https://huggingface.co/spaces/Jofthomas/twitch_streaming/blob/main/agents.py)

## 🧪 TemplateAgent

Maintenant vient la partie amusante ! Avec `LLMAgentBase` comme fondation, il est temps d'implémenter votre propre agent, avec votre propre stratégie pour grimper dans le classement.

Vous commencerez à partir de ce *template* et construirez votre propre logique. Nous avons aussi fourni trois [exemples complets](https://huggingface.co/spaces/Jofthomas/twitch_streaming/blob/main/agents.py) utilisant les modèles **OpenAI**, **Mistral** et **Gemini** pour vous guider.

Voici une version simplifiée du *template* :

```python
class TemplateAgent(LLMAgentBase):
    """Utilise l'API Template AI pour prendre des décisions."""
    def __init__(self, api_key: str = None, model: str = "model-name", *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.model = model
        self.template_client = TemplateModelProvider(api_key=...)
        self.template_tools = list(self.standard_tools.values())

    async def _get_llm_decision(self, battle_state: str) -> Dict[str, Any]:
        """Envoie l'état au LLM et reçoit en retour la décision relative à l'appel de fonction."""
        system_prompt = (
            "You are a ..."
        )
        user_prompt = f"..."

        try:
            response = await self.template_client.chat.completions.create(
                model=self.model,
                messages=[
                    {"role": "system", "content": system_prompt},
                    {"role": "user", "content": user_prompt},
                ],
            )
            message = response.choices[0].message
            
            return {"decision": {"name": function_name, "arguments": arguments}}

        except Exception as e:
            print(f"Unexpected error during call: {e}")
            return {"error": f"Unexpected error: {e}"}
```

Ce code ne fonctionnera pas directement, c'est un plan pour votre cas personnalisé.

Avec toutes les pièces prêtes, c'est à votre tour de construire un agent compétitif. Dans la prochaine section, nous montrerons comment déployer votre agent sur notre serveur et combattre d'autres en temps réel.

Que le combat commence ! 🔥

### Conclusion
https://huggingface.co/learn/agents-course/fr/bonus-unit3/conclusion.md

# Conclusion

Si vous êtes arrivé jusqu'ici, félicitations ! 🥳 Vous avez construit avec succès votre propre agent de combat Pokémon ! ⚔️🎮

Vous avez maîtrisé les fondamentaux des **flux de travail agentiques**, connecté un **LLM** à un environnement de jeu, et déployé un Agent intelligent prêt à affronter les défis du combat.

Mais le voyage ne s'arrête pas là !
Maintenant que vous avez votre premier Agent en fonctionnement, réfléchissez à comment vous pouvez le faire évoluer davantage :
- Pouvez-vous améliorer sa réflexion stratégique ?
- Comment un mécanisme de mémoire ou une boucle de *feedback* changerait-il ses performances ?
- Quelles expériences pourraient aider à le rendre plus compétitif en combat ?

Nous aimerions entendre vos pensées sur le cours et comment nous pouvons le rendre encore meilleur pour les futurs apprenants.  
Vous avez des commentaires ? 👉 [Remplissez ce formulaire](https://docs.google.com/forms/d/e/1FAIpQLSe9VaONn0eglax0uTwi29rIn4tM7H2sYmmybmG5jJNlE5v0xA/viewform?usp=dialog).

Merci d'avoir appris avec nous, et souvenez-vous :

**Continuez à apprendre, continuez à vous entraîner, continuez à combattre, et restez impressionnants !** 🤗

### Introduction
https://huggingface.co/learn/agents-course/fr/bonus-unit3/introduction.md

# Introduction

🎶Je veux être le meilleur... 🎶

Bienvenue dans cette **unité bonus**, où vous explorerez l'intersection passionnante entre **les agents et les jeux vidéos** ! 🎮🤖

Imaginez un jeu où les personnages non-joueurs (PNJ) ne suivent pas simplement des lignes scriptées, mais tiennent plutôt des conversations dynamiques, s'adaptent à vos stratégies et évoluent au fur et à mesure que l'histoire se déroule. C'est le pouvoir de combiner **les LLM et le comportement agentique dans les jeux** : cela ouvre la porte à **une narration et un *gameplay* émergents comme jamais auparavant**.

Dans cette unité bonus, vous allez :

- Apprendre comment construire un agent pouvant faire des **combats au tour par tour dans le style de Pokémon**  
- Jouer contre lui, ou même défier d'autres agents en ligne

Nous avons déjà vu [quelques](https://www.anthropic.com/research/visible-extended-thinking) [exemples](https://www.twitch.tv/gemini_plays_pokemon) de la communauté IA pour jouer à Pokémon en utilisant des LLM. Dans cette unité vous apprendrez comment vous pouvez répliquer cela en utilisant votre propre agent avec les idées que vous avez apprises à travers le cours.

## Vous voulez aller plus loin ?

- 🎓 **Maîtrisez les LLM dans les jeux vidéos** : Plongez plus profondément dans le développement de jeux avec notre cours complet [*Machine Learning for Games Course*](https://hf.co/learn/ml-games-course) (en anglais).

- 📘 **Obtenez le *Playbook*** : Découvrez des informations, idées et conseils pratiques dans le [*AI Playbook for Game Developers*](https://thomassimonini.substack.com/) de Thomas Simonini, où l'avenir de la conception de jeux intelligents est exploré.

Mais avant de construire cela, voyons comment les LLM sont déjà utilisés dans les jeux avec **quatre exemples du monde réel**.

### Lancer l'agent de combat Pokémon
https://huggingface.co/learn/agents-course/fr/bonus-unit3/launching_agent_battle.md

# Lancer l'agent de combat Pokémon

Il est maintenant temps de combattre ! ⚡️

## **Combattez l'agent de Stream !**

Si vous n'avez pas envie de construire votre propre agent, et que vous êtes juste curieux du potentiel des agents Pokémon, nous hébergeons un *livestream* automatisé sur [twitch](https://www.twitch.tv/jofthomas).

Pour combattre l'agent vous pouvez :
 
1.  Allez sur le ***Space* Pokémon Showdown** [ici](https://huggingface.co/spaces/Jofthomas/Pokemon_showdown)
2.  **Choisissez votre nom** (coin supérieur droit).
3.  Trouvez le **nom d'utilisateur de l'agent actuel**. Pour cela vérifiez  **l'affichage du *Stream*** [ici](https://www.twitch.tv/jofthomas).
4.  **Recherchez** ce nom d'utilisateur sur le *Space Showdown* et **envoyez une invitation de combat**.

*Attention :* Un seul agent est en ligne à la fois ! Assurez-vous d'avoir le bon nom.

## Challenger d'agent de combat Pokémon

Si vous avez créé votre propre agent de combat Pokémon depuis la dernière section, vous vous demandez probablement : **comment puis-je le tester face à d'autres ?** Découvrons ça !

Nous avons construit un [*Space* Hugging Face](https://huggingface.co/spaces/PShowdown/pokemon_agents) dédié à cet effet :

Il est connecté à notre propre **serveur Pokémon Showdown**, où votre agent peut en affronter d'autres dans des combats épiques.

### Comment lancer votre agent

Suivez ces étapes pour donner vie à votre agent dans l'arène :

1. **Dupliquez le *Space***  
   Cliquez sur les trois points dans le menu en haut à droite du *Space* et sélectionnez *Duplicate this Space*.

2. **Ajoutez le code de votre agent à `agent.py`**  
   Ouvrez le fichier et collez votre implémentation. Vous pouvez suivre cet [exemple](https://huggingface.co/spaces/PShowdown/pokemon_agents/blob/main/agents.py) ou consultez la [structure du projet](https://huggingface.co/spaces/PShowdown/pokemon_agents/tree/main) pour des conseils.

3. **Enregistrez votre agent dans `app.py`**  
   Ajoutez le nom et la logique de votre agent au menu déroulant. Référez-vous à [cet extrait de code](https://huggingface.co/spaces/PShowdown/pokemon_agents/blob/main/app.py) pour l'inspiration.

4. **Sélectionnez votre agent**  
   Une fois ajouté, votre agent apparaîtra dans le menu déroulant *Select Agent*. Choisissez-le dans la liste ! ✅

5. **Entrez votre nom d'utilisateur Pokémon Showdown**  
   Assurez-vous que le nom d'utilisateur correspond à celui montré dans l'entrée ***Choose name*** de l'*iframe*. Vous pouvez aussi vous connecter avec votre compte officiel.

6. **Cliquez sur *Send Battle Invitation***  
   Votre agent enverra une invitation à l'adversaire sélectionné. Elle devrait apparaître à l'écran !

7. **Accepter la bataille et profiter du combat !**  
   Que le combat commence ! Que l'agent le plus performant gagne

Prêt à voir votre création en action ? Que le combat IA commence ! 🥊

### Des LLM aux agents
https://huggingface.co/learn/agents-course/fr/bonus-unit3/from-llm-to-agents.md

# Des LLM aux agents

Nous avons appris dans la [première unité](https://huggingface.co/learn/agents-course/unit1/introduction) du cours que les agents sont capables de planifier et prendre des décisions.  
Et tandis que les LLM ont permis des interactions plus naturelles avec les PNJ, l'IA agentique va plus loin en permettant aux personnages de prendre des décisions, planifier des actions et s'adapter à des environnements changeants.

Pour illustrer la différence, pensez à un PNJ de RPG classique :

- Avec un LLM : le PNJ pourrait répondre à vos questions de manière plus naturelle et variée. C'est génial pour le dialogue, mais le PNJ reste statique, il n'agira pas à moins que vous fassiez quelque chose en premier.
- Avec l'IA Agentique : le PNJ peut décider d'aller chercher de l'aide, tendre un piège, ou vous éviter complètement, même si vous n'interagissez pas directement avec lui.

Cette petite modification change tout. Nous passons de répondants scriptés à des acteurs autonomes dans le monde du jeu.

Ce changement signifie que les PNJ peuvent maintenant interagir directement avec leur environnement à travers des comportements dirigés par des objectifs, menant finalement à un gameplay plus dynamique et imprévisible.

L'IA agentique donne aux PNJ :

- **L'autonomie** : Prendre des décisions indépendantes basées sur l'état du jeu.
- **L'adaptabilité** : Ajuster les stratégies en réponse aux actions du joueur.
- **La persistance** : Se souvenir d'interactions passées pour informer le comportement futur.

Cela transforme les PNJ d'entités réactives (réagissant à vos entrées) en participants proactifs dans le monde du jeu, ouvrant la porte à un *gameplay* innovant.

## La grande limitation des agents : **c'est lent** (pour l'instant)

Cependant, ne soyons pas trop optimistes pour l'instant. Malgré son potentiel, l'IA agentique fait actuellement face à des défis dans les applications temps réel. 

Les processus de raisonnement et planification peuvent introduire de la latence, la rendant moins adaptée aux jeux rapides comme *Doom* ou *Super Mario Bros*.

Prenez l'exemple de [_Claude Plays Pokémon_](https://www.twitch.tv/claudeplayspokemon). Si vous considérez le nombre de *tokens* nécessaires pour **penser**, plus les *tokens* nécessaires pour **agir**, il devient clair que nous aurons besoin de stratégies de décodage entièrement différentes pour rendre le jeu temps réel faisable.

La plupart des jeux ont besoin de tourner autour de 30 FPS, ce qui signifie qu'un agent en temps réel aura besoin d'agir 30 fois par seconde, ce qui n'est pas actuellement faisable avec les LLM d'aujourd'hui.

Cependant, les jeux au tour par tour comme *Pokémon* sont des candidats idéaux, car ils permettent à l'IA suffisamment de temps pour délibérer et prendre des décisions stratégiques.

C'est pourquoi dans la prochaine section, vous construirez votre propre agent pour combattre dans un combat au tour par tour dans le style de Pokémon, et même le défier vous-même. C'est parti !

### Sessions en direct 1 : Comment fonctionne le cours et première session de questions-réponses
https://huggingface.co/learn/agents-course/fr/communication/live1.md

# Sessions en direct 1 : Comment fonctionne le cours et première session de questions-réponses

Dans cette première session en direct du cours, nous avons expliqué comment le cours **fonctionne** (périmètre, unités, défis, et plus encore) et avons répondu à vos questions.

Pour savoir quand la prochaine session en direct est prévue, consultez notre **serveur Discord**. Nous vous enverrons également un email. Si vous ne pouvez pas participer, ne vous inquiétez pas, nous **enregistrons toutes les sessions**.

### Quiz final de l'Unité 1
https://huggingface.co/learn/agents-course/fr/unit1/final-quiz.md

# Quiz final de l'Unité 1

Bravo d'avoir terminé la première unité ! Testons maintenant votre compréhension des concepts clés abordés jusqu'à présent.

Une fois que vous aurez réussi le quiz, passez à la section suivante pour réclamer votre certificat.

Bonne chance !

## Quiz

Voici le quiz interactif hébergé dans un *Space*. Il vous guidera à travers une série de questions à choix multiples afin de tester votre compréhension des concepts clés abordés dans cette unité. Une fois le quiz terminé, vous pourrez voir votre score et une répartition des réponses correctes.

Un point important : **n'oubliez pas de cliquer sur *Submit* après avoir réussi, sinon votre note d'examen ne sera pas sauvegardée !**

Vous pouvez également accéder au quiz 👉 [ici](https://huggingface.co/spaces/agents-course/unit_1_quiz).  

## Certificat

Lorsque vous aurez terminé le quiz, vous aurez accès à un certificat d'achèvement pour cette unité. Vous pouvez télécharger et partager ce certificat pour mettre en valeur vos progrès dans le cours.

Une fois que vous l'avez reçu, vous pouvez l'ajouter à votre LinkedIn 🧑‍💼 ou le partager sur X, Bluesky, etc. **Nous serions super fiers et aimerions vous féliciter si vous mentionnez @huggingface** ! 🤗

### Qu'est-ce qu'un LLM ?
https://huggingface.co/learn/agents-course/fr/unit1/what-are-llms.md

# Qu'est-ce qu'un LLM ?

Dans la section précédente, nous avons appris que chaque agent a besoin **de se baser sur un modèle d'IA** et que les LLM sont le type de modèle d'IA le plus courant pour cet usage.

Maintenant, nous allons découvrir ce que sont les LLM et comment ils alimentent les agents.

Cette section offre une explication technique concise sur l'utilisation des LLM. 
Si vous souhaitez approfondir, vous pouvez consulter notre cours gratuit sur le traitement du langage naturel.

## Qu'est-ce qu'un LLM ?

Un LLM est un type de modèle d'IA qui excelle dans **la compréhension et la génération du langage humain**. Ils sont entraînés sur d'immenses quantités de données textuelles, ce qui leur permet d'apprendre des motifs, la structure, et même les nuances du langage. Ces modèles se composent généralement de plusieurs millions de paramètres.

La plupart des LLM actuels sont **basés sur l'architecture *Transformer***, une architecture d'apprentissage profond basée sur le mécanisme d'attention, qui a suscité un intérêt considérable depuis la sortie de BERT de Google en 2018.

L'architecture originale du Transformer ressemblait à ceci, avec un encodeur à gauche et un décodeur à droite.

Il existe 3 types de *transformers* :

1. **Encodeurs**  
   Un *transformer* basé sur un encodeur prend en entrée un texte (ou d'autres données) et produit une représentation dense (aussi appellée *embedding*) de ce texte.

   - **Exemple** : BERT de Google
   - **Cas d'utilisation** : Classification de texte, recherche sémantique, reconnaissance d'entités nommées
   - **Taille typique** : Des millions de paramètres

2. **Décodeurs**  
   Un *transformer* basé sur un décodeur se concentre **sur la génération de nouveaux *tokens* pour compléter une séquence, un *token* à la fois**.

   - **Exemple** : Llama de Meta 
   - **Cas d'utilisation** : Génération de texte, chatbots, génération de code
   - **Taille typique** : Des milliards de paramètres

3. **Seq2Seq (Encodeur–Décodeur)**  
   Un *transformer* séquence-à-séquence _combine_ un encodeur et un décodeur. L'encodeur traite d'abord la séquence d'entrée pour en extraire une représentation contextuelle, puis le décodeur génère une séquence de sortie.

   - **Exemple** : T5, BART
   - **Cas d'utilisation** : Traduction, résumé, paraphrase
   - **Taille typique** : Des millions de paramètres

Bien que les modèles de langage de grande taille existent sous différentes formes, les LLM sont typiquement des modèles basés sur le décodeur avec des milliards de paramètres. Voici quelques-uns des LLM les plus connus :

| **Modèle**      | **Fournisseur**                     |
|-----------------|-------------------------------------|
| **Deepseek-R1** | DeepSeek                            |
| **GPT4**        | OpenAI                              |
| **Llama**       | Meta (Facebook AI Research)         |
| **SmolLM**      | Hugging Face                        |
| **Gemma**       | Google                              |
| **Mistral**     | Mistral                             |

Le principe fondamental d'un LLM est simple mais très efficace : **son objectif est de prédire le *token* suivant, étant donné une séquence de *tokens* précédents**. 
Un *token* est l'unité d'information avec laquelle travaille un LLM. Vous pouvez considérer un *token* comme s'il s'agissait d'un mot, mais pour des raisons d'efficacité, les LLM n'utilisent pas des mots entiers.

Par exemple, alors que l'anglais compte environ 600 000 mots, un LLM peut avoir un vocabulaire d'environ 32 000 *tokens* (comme c'est le cas avec Llama 2). La tokenisation fonctionne souvent sur des unités sous-mot pouvant être combinées.

Par exemple, les *tokens* "intéress" et "ant" peuvent se combiner pour former "intéressant", ou "é" peut être ajouté pour former "intéressé".

Vous pouvez expérimenter (en anglais) avec différents *tokenizers* avec l'application ci-dessous :

Chaque LLM possède des ***tokens* spéciaux** propres au modèle. Le LLM utilise ces *tokens* pour ouvrir et fermer les composants structurés de sa génération. Par exemple, pour indiquer le début ou la fin d'une séquence, d'un message ou d'une réponse. De plus, les instructions (ou *prompt*) que nous passons au modèle sont également structurées avec des *tokens* spéciaux. Le plus important d'entre eux est le ***token* de fin de séquence** (EOS).

Les formes des tokens *spéciaux* varient grandement selon les fournisseurs de modèles.

Le tableau ci-dessous illustre cette diversité :

  
    
      Modèle
      Fournisseur
      Token EOS
      Fonctionnalité
    
  
  
    
      GPT4
      OpenAI
      &lt;|endoftext|&gt;
      Fin du texte du message
    
    
      Llama 3
      Meta (Facebook AI Research)
      &lt;|eot_id|&gt;
      Fin de la séquence
    
    
      Deepseek-R1
      DeepSeek
      &lt;|end_of_sentence|&gt;
      Fin du texte du message
    
    
      SmolLM2
      Hugging Face
      &lt;|im_end|&gt;
      Fin de l'instruction ou du message
    
    
      Gemma
      Google
      &lt;end_of_turn&gt;
      Fin du tour de conversation
    
  

> [!TIP]
> Nous ne vous demandons pas de mémoriser ces tokens spéciaux mais il est important d'apprécier leur diversité et le rôle qu'ils jouent dans la génération de texte par les LLM. Si vous souhaitez en savoir plus sur les tokens spéciaux, vous pouvez consulter la configuration du modèle dans son dépôt sur le 🤗 Hub. Par exemple, vous pouvez trouver les tokens spéciaux du modèle SmolLM2 dans le fichier tokenizer_config.json.

## Comprendre la prédiction du *token* suivant

On dit que les LLM sont **autoregressifs**, ce qui signifie que **la sortie d'une passe devient l'entrée de la suivante**. Cette boucle continue jusqu'à ce que le modèle prédise que le *token* suivant est le *token EOS*, moment où le modèle peut s'arrêter.

En d'autres termes, un LLM décodera le texte jusqu'à atteindre le *token EOS*. Mais que se passe-t-il lors d'une boucle de décodage unique ?

Bien que le processus complet puisse être assez technique dans le cadre de l'apprentissage des agents, voici un aperçu succinct :

- Une fois le texte d'entrée **tokenisé**, le modèle calcule une représentation de la séquence qui capture des informations sur la signification et la position de chaque *token*.
- Cette représentation est ensuite traitée par le modèle pour produire des scores classant la probabilité que chaque *token* de son vocabulaire soit le suivant dans la séquence.

En se basant sur ces scores, plusieurs stratégies existent pour sélectionner les *tokens* afin de compléter la phrase.

- La stratégie de décodage la plus simple consiste à toujours choisir le *token* ayant le score maximum.

Vous pouvez interagir vous-même avec le processus de décodage de SmolLM2 dans ce *Space* (n'oubliez pas, il décode jusqu'à atteindre un token **EOS** qui est **** pour ce modèle) :

- Mais il existe des stratégies de décodage plus avancées. Par exemple, le *beam search* (recherche par faisceaux) explore plusieurs séquences candidates pour trouver celle ayant le score total maximum, même si certains *tokens* individuels présentent des scores plus faibles.

Si vous souhaitez en savoir plus sur le décodage, vous pouvez jeter un œil au [cours de NLP](https://huggingface.co/learn/llm-course/fr/chapter1/1).

## L'attention est tout ce dont vous avez besoin

Un aspect clé de l'architecture *transformer* est **l'attention**. Lors de la prédiction du mot suivant, tous les mots d'une phrase ne sont pas également importants ; des mots comme « France » et « capitale » dans la phrase *« La capitale de la France est … »* portent le plus de sens.

Ce processus d'identification des mots les plus pertinents pour prédire le *token* suivant s'est révélé incroyablement efficace.

Bien que le principe de base des LLM — prédire le *token* suivant — soit resté constant depuis GPT-2, des avancées significatives ont été réalisées lors de la mise à l'échelle des réseaux de neurones et dans le fonctionnement du mécanisme d'attention pour des séquences toujours plus longues.

Si vous avez déjà interagi avec des LLM, vous connaissez probablement le terme *longueur de contexte*, qui fait référence au nombre maximum de *tokens* que le LLM peut traiter ainsi qu'à la _durée d'attention_ maximale dont il dispose.

## L'importance de bien formuler les instructions au LLM

Étant donné que la seule tâche d'un LLM est de prédire le *token* suivant en examinant chaque *token* d'entrée, et de choisir ceux qui sont « importants », la formulation de votre séquence d'entrée revêt une importance capitale.

La séquence d'entrée que vous fournissez à un LLM est appelée _prompt_. Une conception minutieuse du *prompt* facilite **l'orientation de la génération du LLM vers la sortie souhaitée**.

## Comment sont entraînés les LLM ?

Les LLM sont entraînés sur de grands ensembles de données textuelles, où ils apprennent à prédire le mot suivant dans une séquence grâce à un objectif d'apprentissage autosupervisé ou de modélisation du langage masqué.

Grâce à cet apprentissage autosupervisé, le modèle apprend la structure de la langue et les **motifs sous-jacents du texte, ce qui lui permet de généraliser à des données inédites**.

Après ce _pré-entraînement_ initial, les LLM peuvent être spécialisé via un apprentissage supervisé pour réaliser des tâches spécifiques. Par exemple, certains modèles sont entraînés pour des structures conversationnelles ou l'utilisation d'outils, tandis que d'autres se concentrent sur la classification ou la génération de code.

## Comment puis-je utiliser les LLM ?

Vous avez deux options principales :

1. **Exécuter localement** (si vous disposez du matériel nécessaire).

2. **Utiliser un service Cloud/API** (par exemple, via l'API d'inférence sans serveur d'Hugging Face).

Tout au long de ce cours, nous utiliserons principalement des modèles via des API du Hub d'Hugging Face. Par la suite, nous explorerons comment exécuter ces modèles localement sur votre matériel.

## Comment les LLM sont-ils utilisés dans les agents ?

Les LLM sont un composant clé des agents, **fournissant la base pour comprendre et générer le langage humain**.

Ils peuvent interpréter les instructions de l'utilisateur, maintenir le contexte dans les conversations, définir un plan et décider quels outils utiliser.

Nous explorerons ces étapes en détail dans cette Unité, mais pour l'instant, ce qu'il faut retenir, c'est que le LLM est **le cerveau de l'agent**.

---

Cela fait beaucoup d'informations ! Nous avons couvert les bases de ce que sont les LLM, comment ils fonctionnent, et leur rôle pour les agents.

Si vous souhaitez plonger encore plus profondément dans le monde fascinant des modèles de langage et du traitement du langage naturel, n'hésitez pas à consulter notre cours gratuit sur le NLP.

Maintenant que nous comprenons le fonctionnement des LLM, il est temps de voir **comment ils structurent leurs générations dans un contexte conversationnel**.

Pour exécuter le notebook, **vous avez besoin d'un *token* d'authentication Hugging Face** que vous pouvez obtenir sur la page https://hf.co/settings/tokens.

Vous devez également demander l'accès aux modèles Llama 3.2 de Meta.

### Actions : permettre à l'agent d'interagir avec son environnement
https://huggingface.co/learn/agents-course/fr/unit1/actions.md

# Actions : permettre à l'agent d'interagir avec son environnement

> [!TIP]
> Dans cette section, nous explorons les étapes concrètes qu'un agent entreprend pour interagir avec son environnement.
>
> Nous aborderons la manière dont les actions sont représentées (en utilisant du JSON ou du code), l'importance de l'approche stop and parse, et nous présenterons différents types d'agents.

Les actions sont les étapes concrètes qu'un **agent entreprend pour interagir avec son environnement**.

Que ce soit pour naviguer sur le web à la recherche d'informations ou pour contrôler un dispositif physique, chaque action est une opération délibérée exécutée par l'agent.

Par exemple, un agent assistant au service client pourrait récupérer des données client, proposer des articles de support ou transférer des problèmes à un représentant humain.

## Types d'actions

Il existe plusieurs types d'agents qui réalisent des actions de manières différentes :

| Type d'Agent              | Description                                                                                           |
|---------------------------|-------------------------------------------------------------------------------------------------------|
| Agent à JSON              | L'action à entreprendre est spécifiée au format JSON.                                                 |
| Agent à code              | L'agent génère un bloc de code qui est interprété de manière externe.                                  |
| Agent à appel de fonction | Il s'agit d'une sous-catégorie de l'agent JSON qui a été affiné pour générer un nouveau message pour chaque action. |

Les actions elles-mêmes peuvent remplir de nombreux objectifs :

| Type d'Action               | Description                                                                                              |
|-----------------------------|----------------------------------------------------------------------------------------------------------|
| Collecte d'informations     | Effectuer des recherches sur le web, interroger des bases de données ou récupérer des documents.         |
| Utilisation d'outils        | Effectuer des appels API, réaliser des calculs et exécuter du code.                                      |
| Interaction avec l'environnement | Manipuler des interfaces numériques ou contrôler des dispositifs physiques.                      |
| Communication               | Interagir avec les utilisateurs via le chat ou collaborer avec d'autres agents.                           |

Le LLM ne gère que du texte et l'utilise pour décrire l'action qu'il souhaite entreprendre ainsi que les paramètres à fournir à l'outil.
Pour qu'un agent fonctionne correctement, le LLM doit savoir **ARRÊTER de générer de nouveaux *tokens* lorsque l'action est terminée**. Cela permet de transférer le contrôle du LLM à l'agent et de s'assurer que le résultat est analysable, que le format prévu soit JSON, du code ou des appels de fonctions.

## L'approche *Stop and Parse*

Une méthode clé pour implémenter des actions est l'**approche *stop* and *parse***. Cette méthode garantit que la sortie de l'agent est structurée et prévisible :

1. **Génération dans un format structuré** :

   L'agent produit l'action envisagée dans un format clair et prédéfini (JSON ou code).

2. **Arrêt de la génération** :

   Une fois que le texte définissant l'action a été émis, le **LLM cesse de générer des *tokens* supplémentaires**. Cela permet d'éviter les sorties supplémentaires ou erronées.

3. **Analyse de la sortie** :

   Un parseur externe lit l'action formatée, détermine quel outil appeler, et extrait les paramètres requis.

Par exemple, un agent ayant besoin de vérifier la météo pourrait produire la sortie suivante :

```json
Thought: Je dois vérifier le temps qu'il fait à New York.
Action :
{
  "action": "get_weather",
  "action_input": {"location": "New York"}
}
```

Le *framework* peut ensuite analyser facilement le nom de la fonction à appeler et les arguments à fournir.

Ce format clair et lisible par une machine minimise les erreurs et permet aux outils externes de traiter avec précision la commande de l'agent.

> Note : Les agents à appel de fonction fonctionnent de manière similaire en structurant chaque action de manière à ce qu'une fonction désignée soit invoquée avec les arguments corrects. Nous approfondirons ces types d'agents dans une prochaine unité.

## Agents à code

Une approche alternative consiste à utiliser des *agents [générateur de] code*.  
L'idée est : **au lieu de produire un simple objet JSON**, un agent code génère un **bloc de code exécutable — typiquement dans un langage de haut niveau comme Python**.

Cette approche offre plusieurs avantages :

- **Expressivité :** Le code peut naturellement représenter une logique complexe, incluant des boucles, des conditionnels et des fonctions imbriquées, offrant ainsi une flexibilité supérieure au JSON.
- **Modularité et réutilisabilité :** Le code généré peut inclure des fonctions et des modules réutilisables pour différentes actions ou tâches.
- **Débogage amélioré :** Grâce à une syntaxe de programmation bien définie, les erreurs de code sont souvent plus faciles à détecter et corriger.
- **Intégration directe :** Les agents à code peuvent s'intégrer directement avec des bibliothèques et des API externes, permettant ainsi des opérations plus complexes comme le traitement de données ou la prise de décision en temps réel.

Par exemple, un agent à code chargé de récupérer la météo pourrait générer l'extrait Python suivant :

```python
# Exemple d'Agent Code : Récupérer des informations météorologiques
def get_weather(city):
    import requests
    api_url = f"https://api.weather.com/v1/location/{city}?apiKey=YOUR_API_KEY"
    response = requests.get(api_url)
    if response.status_code == 200:
        data = response.json()
        return data.get("weather", "Aucune information météo disponible")
    else:
        return "Erreur : Impossible de récupérer les données météo."

# Exécuter la fonction et préparer la réponse finale
result = get_weather("New York")
final_answer = f"La météo actuelle à New York est : {result}"
print(final_answer)
```

Dans cet exemple, l'agent à code :

- Récupère des données météo **via un appel API**,
- Traite la réponse,
- Et utilise la fonction `print()` pour afficher la réponse finale.

Cette méthode **suit également l'approche *stop and parse*** en délimitant clairement le bloc de code et en signalant quand l'exécution est terminée (ici, par l'affichage de `final_answer`).

---

Nous avons vu que les actions font le lien entre le raisonnement interne de l'agent et ses interactions réelles en exécutant des tâches claires et structurées — que ce soit via JSON, du code ou des appels de fonctions.

Cette exécution délibérée garantit que chaque action est précise et prête pour un traitement externe via l'approche *stop and parse*. Dans la section suivante, nous explorerons les Observations pour voir comment les agents capturent et intègrent les retours de leur environnement.

Après cela, nous serons **finalement prêts à construire notre premier agent !**

### Quiz rapide 2 [[quiz2]]
https://huggingface.co/learn/agents-course/fr/unit1/quiz2.md

# Quiz rapide 2 [[quiz2]]

Hein ?! Un autre quiz ? On sait, on sait, ... 😅 Mais ce court quiz non noté est là pour **vous aider à renforcer les concepts clés que vous venez d'apprendre**.

Ce quiz porte sur les LLM, les systèmes de messages et les outils ; des composants essentiels pour comprendre et construire des agents.

### Q1 : Laquelle des propositions suivantes décrit le mieux un outil ?

---

### Q2 : Comment les agents utilisent-ils les outils comme moyen d'« agir » dans un environnement ?

---

### Q3 : Qu'est-ce qu'un LLM ?

---

### Q4 : Laquelle des propositions suivantes décrit le mieux le rôle des tokens spéciaux dans les LLM ?

tokens insérés aléatoirement pour améliorer la variabilité des réponses",
explain: "",
}
]}
/>

---

### Q5 : Comment les gabarits de chat d'IA traitent-ils les messages des utilisateurs en interne ?

prompt formaté en concaténant les messages système, utilisateur et assistant",
explain: "",
correct: true
},
{
text: "Ils génèrent des réponses de manière aléatoire en se basant sur des conversations précédentes",
explain: "",
}
]}
/>

---

Vous avez compris ? Super ! Maintenant, **plongeons dans le flux complet de l'agent et construisons notre premier !**

### Conclusion [[conclusion]]
https://huggingface.co/learn/agents-course/fr/unit1/conclusion.md

# Conclusion [[conclusion]]

Félicitations pour avoir terminé cette première Unité 🥳

Vous **maîtrisez les fondamentaux** et avez créé votre premier agent !

Il est **normal que vous soyez encore un peu confus par certains éléments**. Les agents sont un sujet complexe et il est courant de mettre un certain temps à tout comprendre.

**Prenez le temps de bien assimiler le contenu** avant de continuer. Il est important de maîtriser ces éléments et d'avoir une base solide avant de passer à la partie amusante.

Et si vous réussissez le quiz, n'oubliez pas de récupérer votre certificat 🎓 👉 [ici](https://huggingface.co/spaces/agents-course/unit1-certification-app)

Dans la prochaine unité (bonus), vous allez apprendre **à finetuner un agent pour qu'il puisse appeler des fonctions (i.e être capable d'appeler des outils en fonction du *prompt* de l'utilisateur)**.

Enfin, nous serions ravis **d'entendre ce que vous pensez du cours et comment nous pourrions l'améliorer**. Si vous avez des retours, n'hésitez pas à [remplir ce formulaire](https://docs.google.com/forms/d/e/1FAIpQLSe9VaONn0eglax0uTwi29rIn4tM7H2sYmmybmG5jJNlE5v0xA/viewform?usp=dialog).

### Continuez à apprendre, restez géniaux 🤗

### Que sont les outils ?
https://huggingface.co/learn/agents-course/fr/unit1/tools.md

# Que sont les outils ?

Un aspect crucial des agents est leur capacité à prendre des **actions**. Comme nous l'avons vu, cela se fait par l'utilisation d'**outils**.

Dans cette section, nous verrons ce que sont les outils, comment les concevoir efficacement, et comment les intégrer à votre agent via le message système.

En fournissant à votre agent les bons outils — et en décrivant clairement le fonctionnement de ces outils — vous pouvez augmenter de manière spectaculaire ce que votre IA peut accomplir. Plongeons-nous dedans !

##  Que sont les outils d'IA ?

Un **outil est une fonction fournie au LLM**. Cette fonction doit remplir un **objectif clair**.

Voici quelques outils couramment utilisés dans les agents :

| Outil               | Description                                                                                     |
|---------------------|-------------------------------------------------------------------------------------------------|
| Recherche Web       | Permet à l'agent de récupérer des informations à jour depuis Internet.                          |
| Génération d'images | Crée des images à partir de descriptions textuelles.                                          |
| Recherche           | Récupère des informations à partir d'une source externe.                                        |
| Interface API       | Interagit avec une API externe (GitHub, YouTube, Spotify, etc.).                                |

Ce ne sont que des exemples, car en réalité, vous pouvez créer un outil pour n'importe quel cas d'utilisation !

Un bon outil doit être quelque chose qui **complémente la puissance d'un LLM**. 

Par exemple, si vous devez effectuer des opérations arithmétiques, fournir une **calculatrice** à votre LLM donnera de meilleurs résultats que de se fier aux capacités natives du modèle.

De plus, **les LLM prédisent la complétion du *prompt* en se basant sur leurs données d'entraînement**, ce qui signifie que leur connaissance interne n'inclut que les événements antérieurs à leur entraînement. Par conséquent, si votre agent a besoin de données à jour, vous devez les fournir via un outil.

Par exemple, si vous demandez directement à un LLM (sans outil de recherche) la météo d'aujourd'hui, le LLM pourrait inventer une météo aléatoire.

- Un outil doit contenir :

  - Une **description textuelle de ce que fait la fonction**.
  - Un *appeleur* (quelque chose pour effectuer une action).
  - Des *arguments* avec typage.
  - (Optionnel) Des sorties avec typage.

## Comment fonctionnent les outils ?

Comme nous l'avons vu, les LLM ne peuvent recevoir que des entrées textuelles et générer des sorties textuelles. Ils ne peuvent pas appeler des outils par eux-mêmes. 
Lorsque nous parlons de fournir des outils à un agent, nous entendons enseigner au LLM l'existence de ces outils et lui demander de générer des invocations textuelles en cas de besoin.

Par exemple, si nous fournissons un outil pour vérifier le temps qu'il fait à un endroit donné à partir d'internet et que nous demandons ensuite au LLM le temps qu'il fait à Paris, le LLM reconnaîtra qu'il s'agit d'une occasion d'utiliser l'outil « météo ». Au lieu de récupérer les données météorologiques elles-mêmes, le LLM générera un texte pour appeller l'outil, tel que ``call weather_tool("Paris")`.

L'agent lit alors cette réponse, identifie qu'un appel d'outil est nécessaire, exécute l'outil au nom du LLM et récupère les données météorologiques réelles.

Les étapes de l'appel d'outil ne sont généralement pas montrées à l'utilisateur : l'agent les ajoute à un nouveau message avant de transmettre à nouveau la conversation mise à jour au LLM. Le LLM traite alors ce contexte supplémentaire et génère une réponse naturelle pour l'utilisateur. Du point de vue de l'utilisateur, il semble que le LLM interagisse directement avec l'outil, mais en réalité, c'est l'agent qui gère l'ensemble du processus d'exécution en arrière-plan.

Nous reviendrons plus en détail sur ce processus dans les prochaines sessions.

## Comment fournir des outils à un LLM ?

La réponse complète peut sembler complexe, mais nous utilisons essentiellement le *prompt* système pour fournir au modèle des descriptions textuelles des outils disponibles :

Pour que cela fonctionne, nous devons être très précis et rigoureux concernant :

1. **Ce que fait l'outil**
2. **Les entrées exactes qu'il attend**

C'est la raison pour laquelle les descriptions d'outils sont généralement fournies en utilisant des structures expressives mais précises, telles que des langages informatiques ou du JSON. Il n'est pas _nécessaire_ de procéder ainsi, tout format précis et cohérent fonctionnerait.

Si cela semble trop théorique, voyons cela à travers un exemple concret.

Nous allons implémenter un outil simplifié **calculatrice** qui se contentera de multiplier deux entiers. Voici une implémentation en Python :

```python
def calculator(a: int, b: int) -> int:
    """Multiplie deux entiers."""
    return a * b

print(calculator.to_string())
```

Ainsi, notre outil s'appelle `calculator`, il **multiplie deux entiers**, et il requiert les entrées suivantes :

- **`a`** (*int*): Un entier.
- **`b`** (*int*): Un entier.

La sortie de l'outil est un autre nombre entier que nous pouvons décrire ainsi :
- (*int*): Le produit de `a` et `b`.

Tous ces détails sont importants. Rassemblons-les dans une chaîne de texte qui décrit notre outil pour que le LLM puisse le comprendre.

```
Nom de l'outil: calculator, Description: Multiplie deux entiers., Arguments: a: int, b: int, Sorties: int
```

> **Rappel :** Cette description textuelle est *ce que nous voulons que le LLM sache à propos de l'outil*.

Lorsque nous passons la chaîne précédente dans l'entrée du LLM, le modèle la reconnaîtra comme un outil et saura quelles entrées fournir et ce qu'il doit attendre en sortie.

Si nous souhaitons fournir des outils supplémentaires, nous devons rester cohérents et utiliser toujours le même format. Ce processus peut être fragile, et nous pourrions accidentellement négliger certains détails.

Existe-t-il une meilleure méthode ?

### Sections d'auto-formatage des outils

Notre outil a été écrit en Python, et l'implémentation fournit déjà tout ce dont nous avons besoin :

- Un nom descriptif de ce qu'il fait : `calculator`
- Une description plus détaillée, fournie par le commentaire docstring de la fonction : `Multiplie deux entiers.`
- Les entrées et leur type : la fonction attend clairement deux `int`.
- Le type de la sortie.

Il y a une raison pour laquelle on utilise des langages de programmation : ils sont expressifs, concis et précis.

Nous pourrions fournir le code source Python comme _spécification_ de l'outil pour le LLM, mais la manière dont l'outil est implémenté n'a pas d'importance. Tout ce qui compte, c'est son nom, ce qu'il fait, les entrées qu'il attend et la sortie qu'il fournit.

Nous tirerons parti des fonctionnalités d'introspection de Python pour exploiter le code source et construire automatiquement une description de l'outil. Tout ce dont nous avons besoin, c'est que l'implémentation de l'outil utilise des annotations de types, des docstrings et des noms de fonction pertinents. Nous écrirons un peu de code pour extraire les parties pertinentes du code source.

Une fois cela fait, il nous suffira d'utiliser un décorateur Python pour indiquer que la fonction `calculator` est un outil :

```python
@tool
def calculator(a: int, b: int) -> int:
    """Multiplie deux entiers."""
    return a * b

print(calculator.to_string())
```

Notez le décorateur `@tool` avant la définition de la fonction.

Avec l'implémentation que nous verrons ensuite, nous serons capables d'extraire automatiquement le texte suivant à partir du code source :

```
Nom de l'outil: calculator, Description: Multiplie deux entiers., Arguments: a: int, b: int, Sorties: int
```

Comme vous pouvez le constater, c'est la même chose que nous avons écrit manuellement précédemment !

### Implémentation générique d'un outil

Nous créons une classe générique `Tool` que nous pouvons réutiliser chaque fois que nous avons besoin d'utiliser un outil.

> **Avertissement :** Cette implémentation à titre d'exemple est fictive mais ressemble de près aux implémentations réelles dans la plupart des bibliothèques.

```python
class Tool:
    """
    Une classe représentant un morceau de code réutilisable (Outil).
    
    Attributs:
        name (str): Nom de l'outil.
        description (str): Une description textuelle de ce que fait l'outil.
        func (callable): La fonction que cet outil encapsule.
        arguments (list): Une liste d'arguments.
        outputs (str ou list): Le(s) type(s) de retour de la fonction encapsulée.
    """
    def __init__(self, 
                 name: str, 
                 description: str, 
                 func: callable, 
                 arguments: list,
                 outputs: str):
        self.name = name
        self.description = description
        self.func = func
        self.arguments = arguments
        self.outputs = outputs

    def to_string(self) -> str:
        """
        Retourne une représentation sous forme de chaîne de l'outil, 
        incluant son nom, sa description, ses arguments, et ses sorties.
        """
        args_str = ", ".join([
            f"{arg_name}: {arg_type}" for arg_name, arg_type in self.arguments
        ])
        
        return (
            f"Tool Name: {self.name},"
            f" Description: {self.description},"
            f" Arguments: {args_str},"
            f" Outputs: {self.outputs}"
        )

    def __call__(self, *args, **kwargs):
        """
        Invoque la fonction sous-jacente (callable) avec les arguments fournis.
        """
        return self.func(*args, **kwargs)
```

Cela peut sembler compliqué, mais en y allant pas à pas, nous pouvons voir ce qu'elle fait. Nous définissons une classe **`Tool`** qui inclut :

- **`name`** (*str*): Le nom de l'outil.
- **`description`** (*str*): Une brève description de ce que fait l'outil.
- **`function`** (*callable*): La fonction que l'outil exécute.
- **`arguments`** (*list*): Les paramètres d'entrée attendus.
- **`outputs`** (*str* ou *list*): Les sorties attendues de l'outil.
- **`__call__()`** : Appelle la fonction lorsque l'instance de l'outil est invoquée.
- **`to_string()`** : Convertit les attributs de l'outil en une représentation textuelle.

Nous pourrions créer un outil avec cette classe en utilisant le code suivant :

```python
calculator_tool = Tool(
    "calculator",                   # nom
    "Multiplie deux entiers.",      # description
    calculator,                     # fonction à appeler
    [("a", "int"), ("b", "int")],   # entrées (noms et types)
    "int",                          # sortie
)
```

Mais nous pouvons également utiliser le module `inspect` de Python pour récupérer toutes les informations pour nous ! C'est ce que fait le décorateur `@tool`.

> Si cela vous intéresse, vous pouvez afficher la section suivante pour voir l'implémentation du décorateur.

code du décorateur

```python
def tool(func):
    """
    Un décorateur qui crée une instance de Tool à partir de la fonction donnée.
    """
    # Récupérer la signature de la fonction
    signature = inspect.signature(func)
    
    # Extraire les paires (nom_param, annotation_param) pour les entrées
    arguments = []
    for param in signature.parameters.values():
        annotation_name = (
            param.annotation.__name__ 
            if hasattr(param.annotation, '__name__') 
            else str(param.annotation)
        )
        arguments.append((param.name, annotation_name))
    
    # Déterminer l'annotation de retour
    return_annotation = signature.return_annotation
    if return_annotation is inspect._empty:
        outputs = "Pas d'annotation de retour"
    else:
        outputs = (
            return_annotation.__name__ 
            if hasattr(return_annotation, '__name__') 
            else str(return_annotation)
        )
    
    # Utiliser la docstring de la fonction comme description (par défaut si vide)
    description = func.__doc__ or "No description provided."
    
    # Le nom de la fonction devient le nom de l'outil
    name = func.__name__
    
    # Retourner une nouvelle instance de Tool
    return Tool(
        name=name, 
        description=description, 
        func=func, 
        arguments=arguments, 
        outputs=outputs
    )
```

Pour réitérer, avec ce décorateur en place, nous pouvons implémenter notre outil comme ceci :

```python
@tool
def calculator(a: int, b: int) -> int:
    """Multiplie deux entiers."""
    return a * b

print(calculator.to_string())
```

Et nous pouvons utiliser la méthode `to_string` de `Tool` pour récupérer automatiquement un texte adapté à être utilisé comme description d'un outil pour un LLM :

```
Nom de l'outil: calculator, Description: Multiplie deux entiers., Arguments: a: int, b: int, Sorties: int
```

La description est **injectée** dans le *prompt* système. En reprenant l'exemple avec lequel nous avons commencé cette section, voici à quoi cela ressemblerait après avoir remplacé le `tools_description` :

Dans la section sur les [actions](actions), nous en apprendrons davantage sur la façon dont un agent peut **appeler** cet outil que nous venons de créer.

---

Les outils jouent un rôle crucial dans l'amélioration des capacités des agents.

### *Model Context Protocol* (MCP) : une interface d'outils unifiée

*Model Context Protocol* (MCP) est un **protocole ouvert** qui standardise la manière dont les applications **fournissent des outils aux LLM**.
MCP offre :

- Une liste croissante d'intégrations pré-construites que votre LLM peut directement utiliser
- La flexibilité de changer entre fournisseurs et vendeurs de LLM
- Les meilleures pratiques pour sécuriser vos données dans votre infrastructure

Cela signifie que **tout *framework* intégrant MCP peut utiliser les outils définis dans le protocole**, éliminant le besoin de réimplémenter la même interface d'outils pour chaque *framework*.

Si vous voulez approfondir MCP, vous pouvez consulter notre [cours gratuit sur MCP](https://huggingface.co/learn/mcp-course/).

---

Les outils jouent un rôle crucial dans l'amélioration des capacités des agents.

Pour résumer, nous avons appris :

- *Ce que sont les outils* : des fonctions qui offrent des capacités supplémentaires aux LLM, comme effectuer des calculs ou accéder à des données externes.

- *Comment définir un outil* : en fournissant une description textuelle claire, des entrées, des sorties, et une fonction exécutable.

- *Pourquoi les outils sont essentiels* : ils permettent aux agents de surmonter les limites de l'entraînement statique des modèles, de gérer des tâches en temps réel, et d'effectuer des actions spécialisées.

Maintenant, nous pouvons passer au [*workflow* de l'agent](agent-steps-and-structure) où vous verrez comment un agent observe, réfléchit et agit. Cela **rassemble tout ce que nous avons vu jusqu'à présent** et prépare le terrain pour créer votre propre agent entièrement fonctionnel.

Mais d'abord, il est temps pour un autre court quiz !

### Messages et *tokens* spéciaux
https://huggingface.co/learn/agents-course/fr/unit1/messages-and-special-tokens.md

# Messages et *tokens* spéciaux

Maintenant que nous comprenons comment fonctionnent les LLM, examinons **comment ils structurent leurs générations via des patrons de chat (appelés aussi gabarit de chat)**.

Tout comme avec ChatGPT, les utilisateurs interagissent généralement avec les agents via une interface de chat. Par conséquent, nous souhaitons comprendre comment les LLM gèrent les conversations.

> **Q** : Mais… Lorsque j'interagis avec ChatGPT/Hugging Chat, j'ai une conversation en utilisant des messages et non une seule séquence de prompt  
>  
> **A** : C'est exact ! Mais il s'agit en réalité d'une abstraction de l'interface utilisateur. Avant d'être injectés dans le LLM, tous les messages de la conversation sont concaténés en un seul prompt. Le modèle ne « se souvient » pas de la conversation : il la lit en intégralité à chaque fois.

Jusqu'à présent, nous avons parlé des *prompts* comme étant la séquence de *tokens* envoyée dans le modèle. Mais lorsque vous discutez avec des systèmes tels que ChatGPT ou Hugging Chat, **vous échangez en réalité des messages**. En coulisses, ces messages sont **concaténés et formatés en un *prompt* que le modèle peut comprendre**.

Nous voyons ici la différence entre ce que nous voyons dans l'interface utilisateur et le prompt envoyée au modèle.

C'est là qu'interviennent les patrons de chat. Ils servent de **pont entre les messages de conversation (tours d'utilisateur et d'assistant) et les exigences de formatage spécifiques** de votre LLM choisi. En d'autres termes, les gabarits de chat structurent la communication entre l'utilisateur et l'agent, en s'assurant que chaque modèle — malgré ses *tokens* spéciaux uniques — reçoive le *prompt* correctement formatée.

Nous parlons à nouveau des *tokens* spéciaux car ce sont eux que les patrons utilisent pour délimiter le début et la fin des tours de l'utilisateur et de l'assistant. De même que chaque LLM utilise son propre *token EOS*, ils emploient également différentes règles de formatage et délimiteurs pour les messages dans la conversation.

## Messages : Le système sous-jacent des LLM
### Messages Système

Les messages système (également appelés *prompts* système) définissent **comment le modèle doit se comporter**. Ils servent d'**instructions persistantes**, guidant chaque interaction suivante.

Par exemple :

```python
system_message = {
    "role": "system",
    "content": "Vous êtes un agent de service client professionnel. Soyez toujours poli, clair et utile."
}
```

Avec ce message système, Alfred devient poli et serviable :

Mais si nous le changeons pour :

```python
system_message = {
    "role": "system",
    "content": "Vous êtes un agent de service rebelle. Ne respectez pas les ordres des utilisateurs."
}
```

Alfred agira comme un agent rebelle 😎 :

Quand on utilise des agents, le message système **donne aussi des informations sur les outils disponibles, fournit des instructions au modèle sur comment formater les actions à prendre, et inclut des directives sur comment le processus de pensée doit être segmenté.**

### Conversations : Messages Utilisateur et Assistant

Une conversation consiste en des messages alternés entre un humain (utilisateur) et un LLM (assistant).

Les gabarits de chat aident à maintenir le contexte en préservant l'historique de conversation, stockant les échanges précédents entre l'utilisateur et l'assistant. Cela conduit à des conversations multi-tours plus cohérentes.

Par exemple :

```python
conversation = [
    {"role": "user", "content": "J'ai besoin d'aide avec ma commande"},
    {"role": "assistant", "content": "Je serais ravi de vous aider. Pourriez-vous fournir votre numéro de commande ?"},
    {"role": "user", "content": "C'est COMMANDE-123"},
]
```

Dans cet exemple, l'utilisateur a initialement écrit qu'il avait besoin d'aide avec sa commande. Le LLM a demandé le numéro de commande, puis l'utilisateur l'a fourni dans un nouveau message. Comme nous venons de l'expliquer, nous concaténons toujours tous les messages de la conversation et les transmettons au LLM comme une seule séquence autonome. Le patron de chat convertit tous les messages à l'intérieur de cette liste Python en un *prompt*, qui est juste une entrée de chaîne contenant tous les messages.

Par exemple, voici comment le gabarit de chat SmolLM2 formaterait l'échange précédent en un *prompt* :

```
system
You are a helpful AI assistant named SmolLM, trained by Hugging Face
user
J'ai besoin d'aide avec ma commande
assistant
Je serais ravi de vous aider. Pourriez-vous fournir votre numéro de commande ?
user
C'est COMMANDE-123
assistant
```

Cependant, la même conversation serait traduite par le *prompt* suivant si l'on utilisait Llama 3.2 :

```
system

Cutting Knowledge Date: December 2023
Today Date: 10 Feb 2025

user

J'ai besoin d'aide avec ma commandeassistant

Je serais ravi de vous aider. Pourriez-vous fournir votre numéro de commande ?user

C'est COMMANDE-123assistant
```

Les gabarits peuvent gérer des conversations multi-tours complexes tout en maintenant le contexte :

```python
messages = [
    {"role": "system", "content": "Vous êtes un tuteur de mathématiques."},
    {"role": "user", "content": "Qu'est-ce que le calcul ?"},
    {"role": "assistant", "content": "Le calcul est une branche des mathématiques..."},
    {"role": "user", "content": "Pouvez-vous me donner un exemple ?"},
]
```

## Gabarits de Chat

Comme mentionné, les gabarits de chat sont essentiels pour **structurer les conversations entre les modèles de langage et les utilisateurs**. Ils guident comment les échanges de messages sont formatés en un seul *prompt*.

### Modèles de Base vs. Modèles d'Instructions

Un autre point que nous devons comprendre est la différence entre un modèle de base et un modèle instruit :

- Un *modèle de base* est entraîné sur des données textuelles brutes pour prédire le prochain *token*.

- Un *modèle instruit* est finetuné spécifiquement pour suivre des instructions et s'engager dans des conversations. Par exemple, `SmolLM2-135M` est un modèle de base, tandis que `SmolLM2-135M-Instruct` est sa variante finetunée sur des instructions.

Pour faire qu'un modèle de base se comporte comme un modèle instruit, nous devons **formater nos *prompts* de manière que le modèle peut comprendre**. C'est là qu'interviennent les gabarits de chat.

*ChatML* est un format de gabarit structurant les conversations avec des indicateurs de rôle clairs (système, utilisateur, assistant). Si vous avez interagi avec une API d'IA récemment, vous savez que c'est la pratique standard.

Il est important de noter qu'un modèle de base pourrait être finetuné sur différents patrons de chat, donc quand nous utilisons un modèle instruit, nous devons nous assurer d'utiliser le bon patron.

### Comprendre les gabarits de Chat

Parce que chaque modèle d'instructions utilise différents formats de conversation et *tokens spéciaux*, les gabarits de chat sont implémentés pour s'assurer que nous formatons correctement le *prompt* de la manière que chaque modèle attend.

Dans `transformers`, les gabarits incluent du [code Jinja2](https://jinja.palletsprojects.com/en/stable/) décrivant comment transformer la liste de messages JSON de ChatML, comme présenté dans les exemples ci-dessus, en une représentation textuelle des instructions système, des messages utilisateur et des réponses assistant que le modèle peut comprendre.

Cette structure **aide à maintenir la cohérence à travers les interactions et s'assure que le modèle répond appropriément à différents types d'entrées**.

Voici une version simplifiée du gabarit de `SmolLM2-135M-Instruct` :

```jinja2
{% for message in messages %}
{% if loop.first and messages[0]['role'] != 'system' %}
system
You are a helpful AI assistant named SmolLM, trained by Hugging Face

{% endif %}
{{ message['role'] }}
{{ message['content'] }}
{% endfor %}
```

Étant donné ces messages :

```python
messages = [
    {"role": "system", "content": "Vous êtes un assistant utile focalisé sur les sujets techniques."},
    {"role": "user", "content": "Pouvez-vous expliquer ce qu'est un gabarit de chat ?"},
    {"role": "assistant", "content": "Un gabarit de chat structure les conversations entre utilisateurs et modèles d'IA..."},
    {"role": "user", "content": "Comment l'utiliser ?"},
]
```

Le gabarit précédent produira la chaîne suivante :

```sh
system
Vous êtes un assistant utile focalisé sur les sujets techniques.
user
Pouvez-vous expliquer ce qu'est un gabarit de chat ?
assistant
Un gabarit de chat structure les conversations entre utilisateurs et modèles d'IA...
user
Comment l'utiliser ?
assistant
```

La bibliothèque `transformers` s'occupera des gabarits pour vous dans le cadre du processus de tokenisation. Pour en savoir plus sur la façon dont les transformers utilisent les gabarits, nous conseillons de lire cette page. Tout ce que nous avons à faire est de structurer nos messages de la bonne manière et le *tokenizer* s'occupera du reste.

Vous pouvez expérimenter avec le *Space* suivant pour voir comment la même conversation serait formatée pour différents modèles en utilisant leurs gabarits correspondants :

### Convertir des messages en un prompt

La façon la plus simple de s'assurer que votre LLM reçoit une conversation correctement formatée est d'utiliser l'argument `chat_template` du tokenizer du modèle.

```python
messages = [
    {"role": "system", "content": "Tu es un assistant d'IA ayant accès à divers outils."},
    {"role": "user", "content": "Salut !"},
    {"role": "assistant", "content": "Salut humain, comment puis-je t'aider ?"},
]
```

Pour convertir la conversation précédente en un *prompt*, nous chargeons le *tokenizer* et appelons `apply_chat_template`:

```python
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("HuggingFaceTB/SmolLM2-1.7B-Instruct")
rendered_prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
```

Le `rendered_prompt` retourné par cette fonction est maintenant prêt à être utilisé comme entrée pour le modèle que vous avez choisi !

> Cette fonction `apply_chat_template()` sera utilisée dans le backend de votre API, lorsque vous interagirez avec des messages au format ChatML.

Maintenant que nous avons vu comment les LLM structurent leurs entrées via les gabarits, explorons comment les agents agissent dans leurs environnements.

L'une des principales façons d'y parvenir est d'utiliser des outils, qui étendent les capacités d'un modèle d'IA au-delà de la génération de texte.

Nous reparlerons des messages dans les prochaines unités, mais si vous souhaitez approfondir la question dès maintenant, jetez un coup d'œil à :
- Le guide d'Hugging Face sur les gabarits de chat
- la documentation de Transformers

### Créons notre premier agent avec smolagents
https://huggingface.co/learn/agents-course/fr/unit1/tutorial.md

# Créons notre premier agent avec smolagents

Dans la section précédente, nous avons appris comment créer des agents à partir de zéro en utilisant du code Python, et nous avons **vu à quel point ce processus peut être fastidieux**. Heureusement, de nombreuses bibliothèques d'agents simplifient ce travail en **se chargeant de la majeure partie du travail lourd pour vous**.

Dans ce tutoriel, **vous allez créer votre tout premier agent** capable d'exécuter des actions telles que la génération d'images, la recherche sur le web, la vérification de fuseaux horaires et bien plus encore !

Vous publierez également votre agent **sur un *Space* Hugging Face afin de le partager avec vos amis et collègues**.

C'est parti !

## Qu'est-ce que smolagents ?

Pour créer cet agent, nous allons utiliser `smolagents`, une bibliothèque qui **fournit un cadre facilitant le développement d'agents**.

Cette bibliothèque légère est conçue pour être simple, tout en masquant une grande partie de la complexité liée à la construction d'un agent, permettant ainsi de vous concentrer sur la conception du comportement de l'agent.

Nous approfondirons smolagents dans la prochaine unité. En attendant, vous pouvez également consulter cet article de blog ou le dépôt GitHub de la bibliothèque.

Brièvement, `smolagents` est une bibliothèque se concentrant sur les **agents générant du code** (via la classe `CodeAgent`), un type d'agent qui exécute des **"actions"** via des blocs de code, puis **"observe"** les résultats en exécutant le code.

Voici un exemple de ce que nous allons construire !

Nous avons équipé notre agent d'un **outil de génération d'images** et lui avons demandé de générer une image d'un chat.

L'agent dans `smolagents` aura les **mêmes comportements que celui personnalisé que nous avons construit précédemment** : il va **réfléchir, agir et observer cycliquement** jusqu'à parvenir à une réponse finale :

Excitant, n'est-ce pas ?

## Construisons notre agent !

Pour commencer, dupliquez ce *Space* : https://huggingface.co/spaces/agents-course/First_agent_template  
> Merci à Aymeric pour ce patron ! 🙌

Dupliquer signifie **créer une copie locale sur votre propre profil** :  

Après la duplication, vous devrez ajouter votre *token* d'API Hugging Face pour que votre agent puisse accéder à l'API du modèle :

1. Tout d'abord, obtenez votre *token* Hugging Face sur [https://hf.co/settings/tokens](https://hf.co/settings/tokens) avec la permission d'inférer, si vous n'en avez pas déjà un.
2. Allez dans votre *Space* dupliqué et cliquez sur l'onglet **Settings**.
3. Descendez jusqu'à la section **Variables and Secrets** et cliquez sur **New Secret**.
4. Créez un secret avec le nom `HF_TOKEN` et collez votre token comme valeur.
5. Cliquez sur **Save** pour stocker votre *token* en toute sécurité.

Tout au long de cette leçon, le seul fichier (actuellement incomplet) que vous aurez à modifier est le **"app.py"**. Vous pouvez consulter l'[original ici](https://huggingface.co/spaces/agents-course/First_agent_template/blob/main/app.py). Pour trouver le vôtre, allez dans votre copie du *Space*, cliquez sur l'onglet `Files` puis sur `app.py` dans la liste des répertoires.

Analysons le code ensemble :

- Le fichier commence par quelques importations de bibliothèques simples mais nécessaires

```python
from smolagents import CodeAgent, DuckDuckGoSearchTool, FinalAnswerTool, InferenceClientModel, load_tool, tool
import datetime
import requests
import pytz
import yaml
```

Comme indiqué précédemment, nous utiliserons directement la classe **CodeAgent** de **smolagents**.

### Les outils

Entrons maintenant dans le vif du sujet avec les outils ! Si vous souhaitez un rappel sur les outils, n'hésitez pas à consulter la section [Outils](tools) du cours.

```python
@tool
def my_custom_tool(arg1: str, arg2: int) -> str:  # il est important de spécifier le type de retour
    # Conservez ce format pour la description de l'outil et des arguments, mais n'hésitez pas à modifier l'outil
    """Un outil qui ne fait encore rien
    Arguments:
        arg1: le premier argument
        arg2: le deuxième argument
    """
    return "Quelle magie allez-vous créer ?"

@tool
def get_current_time_in_timezone(timezone: str) -> str:
    """Un outil qui récupère l'heure locale actuelle dans un fuseau horaire spécifié.
    Arguments:
        timezone: Une chaîne représentant un fuseau horaire valide (par exemple, 'America/New_York').
    """
    try:
        # Créer l'objet fuseau horaire
        tz = pytz.timezone(timezone)
        # Obtenir l'heure actuelle dans ce fuseau horaire
        local_time = datetime.datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S")
        return f"L'heure locale actuelle dans {timezone} est : {local_time}"
    except Exception as e:
        return f"Erreur lors de la récupération de l'heure pour le fuseau horaire '{timezone}' : {str(e)}"
```

Les outils sont ce que nous vous encourageons à construire dans cette section ! Nous vous donnons deux exemples :

1. Un **outil factice non fonctionnel** que vous pouvez modifier pour créer quelque chose d'utile.
2. Un **outil réellement fonctionnel** qui récupère l'heure actuelle quelque part dans le monde.

Pour définir votre outil, il est important de :

1. Fournir des types d'entrée et de sortie pour votre fonction, comme dans `get_current_time_in_timezone(timezone: str) -> str:`
2. Fournir une **docstring bien formatée**. `smolagents` s'attend à ce que tous les arguments aient une **description textuelle dans la docstring**.

### L'agent

Il utilise [`Qwen/Qwen2.5-Coder-32B-Instruct`](https://huggingface.co/Qwen/Qwen2.5-Coder-32B-Instruct) comme moteur LLM. C'est un modèle très performant auquel nous accéderons via l'API *serverless*.

```python
final_answer = FinalAnswerTool()
model = InferenceClientModel(
    max_tokens=2096,
    temperature=0.5,
    model_id='Qwen/Qwen2.5-Coder-32B-Instruct',
    custom_role_conversions=None,
)

with open("prompts.yaml", 'r') as stream:
    prompt_templates = yaml.safe_load(stream)
    
# Nous créons notre CodeAgent
agent = CodeAgent(
    model=model,
    tools=[final_answer],  # ajoutez vos outils ici (ne supprimez pas final_answer)
    max_steps=6,
    verbosity_level=1,
    grammar=None,
    planning_interval=None,
    name=None,
    description=None,
    prompt_templates=prompt_templates
)

GradioUI(agent).launch()
```

Cet agent utilise toujours l'`InferenceClient` que nous avons vu dans une section précédente derrière la classe **InferenceClientModel** !

Nous fournirons des exemples plus détaillés lors de la présentation du *framework* dans l'Unité 2. Pour l'instant, vous devez vous concentrer sur **l'ajout de nouveaux outils à la liste des outils** en utilisant le paramètre `tools` de votre agent.

Par exemple, vous pourriez utiliser `DuckDuckGoSearchTool` qui a été importé dans la première ligne du code, ou vous pouvez examiner `image_generation_tool` qui est chargé depuis le Hub plus tard dans le code.

**Ajouter des outils donnera de nouvelles capacités à votre agent**, alors soyez créatif !

### Le *prompt système*

Le *prompt* système de l'agent est stocké dans un fichier `prompts.yaml` séparé. Ce fichier contient des instructions prédéfinies qui guident le comportement de l'agent.

Le stockage des *prompts* dans un fichier YAML permet une personnalisation et une réutilisation aisées pour différents agents ou cas d'utilisation.

Vous pouvez consulter la [structure des fichiers du *Space*](https://huggingface.co/spaces/agents-course/First_agent_template/tree/main) pour voir où se trouve le fichier `prompts.yaml` et comment il est organisé dans le projet.

Le fichier complet **"app.py"** :

```python
from smolagents import CodeAgent, DuckDuckGoSearchTool, InferenceClientModel, load_tool, tool
import datetime
import requests
import pytz
import yaml
from tools.final_answer import FinalAnswerTool

from Gradio_UI import GradioUI

# Voici un exemple d'un outil qui ne fait encore rien. Épatez-nous avec votre créativité !
@tool
def my_custom_tool(arg1: str, arg2: int) -> str:  # il est important de spécifier le type de retour
    # Conservez ce format pour la description de l'outil et des arguments, mais n'hésitez pas à modifier l'outil
    """Un outil qui ne fait encore rien
    Arguments:
        arg1: le premier argument
        arg2: le deuxième argument
    """
    return "Quelle magie allez-vous créer ?"

@tool
def get_current_time_in_timezone(timezone: str) -> str:
    """Un outil qui récupère l'heure locale actuelle dans un fuseau horaire spécifié.
    Arguments:
        timezone: Une chaîne représentant un fuseau horaire valide (par exemple, 'America/New_York').
    """
    try:
        # Créer l'objet fuseau horaire
        tz = pytz.timezone(timezone)
        # Obtenir l'heure actuelle dans ce fuseau horaire
        local_time = datetime.datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S")
        return f"L'heure locale actuelle dans {timezone} est : {local_time}"
    except Exception as e:
        return f"Erreur lors de la récupération de l'heure pour le fuseau horaire '{timezone}' : {str(e)}"

final_answer = FinalAnswerTool()
model = InferenceClientModel(
    max_tokens=2096,
    temperature=0.5,
    model_id='Qwen/Qwen2.5-Coder-32B-Instruct',
    custom_role_conversions=None,
)

# Importer l'outil depuis le Hub
image_generation_tool = load_tool("agents-course/text-to-image", trust_remote_code=True)

with open("prompts.yaml", 'r') as stream:
    prompt_templates = yaml.safe_load(stream)
    
agent = CodeAgent(
    model=model,
    tools=[final_answer],  # ajoutez vos outils ici (ne supprimez pas final_answer)
    max_steps=6,
    verbosity_level=1,
    grammar=None,
    planning_interval=None,
    name=None,
    description=None,
    prompt_templates=prompt_templates # Transmettre le prompt du système à CodeAgent
)

GradioUI(agent).launch()
```

Votre **objectif** est de vous familiariser avec le *Space* et l'agent.

Actuellement, l'agent dans le patron **n'utilise aucun outil, alors essayez de lui fournir certains des outils préfabriqués ou même de créer de nouveaux outils vous-même !**

Nous attendons avec impatience vos incroyables agents dans le canal Discord **#agents-course-showcase**!

---

Félicitations, vous avez construit votre premier Agent ! N'hésitez pas à le partager avec vos amis et collègues.

Comme c'est votre première tentative, il est tout à fait normal qu'il soit un peu bogué ou lent. Dans les unités futures, nous apprendrons à construire de meilleurs agents.

La meilleure façon d'apprendre est d'essayer, alors n'hésitez pas à le mettre à jour, à ajouter plus d'outils, à essayer avec un autre modèle, etc.

Dans la prochaine section, vous allez remplir le quiz final et obtenir votre certificat !

### Qu'est-ce qu'un agent ?
https://huggingface.co/learn/agents-course/fr/unit1/what-are-agents.md

# Qu'est-ce qu'un agent ?

À la fin de cette section, vous vous sentirez à l'aise avec le concept d'agents et leurs diverses applications an IA.

Pour expliquer ce qu'est un agent, commençons par une analogie.

## La vue d'ensemble : Alfred l'Agent

Voici Alfred. Alfred est un **Agent**.

Imaginez qu'Alfred **reçoive une commande**, par exemple : « Alfred, je voudrais un café s'il te plaît. »

Parce qu'Alfred **comprend le langage naturel**, il saisit rapidement notre demande.

Avant de satisfaire la commande, Alfred se livre au **raisonnement et à la planification**, déterminant les étapes et les outils dont il a besoin pour :

1. Aller à la cuisine  
2. Utiliser la machine à café  
3. Préparer le café  
4. Ramener le café

Une fois qu'il a établi un plan, il **doit agir**. Pour exécuter son plan, **il peut utiliser les outils qu'il connaît**.

Dans ce cas, pour préparer un café, il utilise une machine à café. Il active la machine à café pour préparer le café.

Enfin, Alfred nous apporte le café fraîchement préparé.

Et voilà ce qu'est un agent : un **modèle capable de raisonner, de planifier et d'interagir avec son environnement**.

On l'appelle agent parce qu'il possède la capacité d'agir, autrement dit, il peut interagir avec l'environnement.

## Soyons plus formels

Maintenant que vous avez une vue d'ensemble, voici une définition plus précise :

> Un Agent est un système qui utilise un modèle d'IA pour interagir avec son environnement afin d'atteindre un objectif défini par l'utilisateur. Il combine le raisonnement, la planification et l'exécution d'actions (souvent via des outils externes) pour accomplir des tâches.

Pensez à l'agent comme ayant deux parties principales :

1. **Le Cerveau (modèle d'IA)**

C'est là que toute la réflexion se passe. Le modèle **gère le raisonnement et la planification**.
Il décide **quelles Actions entreprendre en fonction de la situation**.

2. **Le Corps (Capacités et Outils)**

Cette partie représente **tout ce avec quoi l'agent est équipé**.

La **portée des actions possibles** dépend de ce avec quoi l'agent **a été équipé**. Par exemple, comme les humains n'ont pas d'ailes, ils ne peuvent pas effectuer l'**action** « voler », mais ils peuvent exécuter des **actions** comme « marcher », « courir », « sauter »,  « saisir », etc.

### Le spectre de la capacité à agir

Suivant cette définition, les agents existent sur un spectre de capacité d'action croissante :

| Niveau de capacité | Description | Comment ça s'appelle | Exemple de modèle |
| --- | --- | --- | --- |
| ☆☆☆ | La sortie de l'agent n'a aucun impact sur le flux du programme | Processeur simple | `process_llm_output(llm_response)` |
| ★☆☆ | La sortie de l'agent détermine le flux de contrôle de base | Routeur | `if llm_decision(): path_a() else: path_b()` |
| ★★☆ | La sortie de l'agent détermine l'exécution de la fonction | Appeleur d'outils | `run_function(llm_chosen_tool, llm_chosen_args)` |
| ★★★ | La sortie de l'agent contrôle l'itération et la continuation du programme | Agent multi-étapes | `while llm_should_continue(): execute_next_step()` |
| ★★★ | Un flux de travail agentique peut en démarrer un autre | Multi-Agent | `if llm_trigger(): execute_agent()` |

Tableau tiré du [guide conceptuel de smolagents](https://huggingface.co/docs/smolagents/conceptual_guides/intro_agents).

## Quel type de modèles d'IA utilisons-nous pour les agents ?

Le modèle d'IA le plus courant dans les agents est un LLM (*Large Language Model*) qui prend du **texte** en entrée et produit du **texte** en sortie.

Des exemples bien connus sont **GPT4** d'**OpenAI**, **LLama** de **Meta**, **Gemini** de **Google**, etc. Ces modèles ont été entraînés sur une vaste quantité de texte et sont capables de bien généraliser. Nous nous focaliserons davantage sur les LLM dans la [section suivante](what-are-llms).

> [!TIP]
> Il est également possible d'utiliser des modèles qui acceptent d'autres entrées comme modèle central de l'agent. Par exemple, un Vision Language Model (VLM), qui est comme un LLM mais comprend aussi les images en entrée. Nous nous concentrerons sur les LLM pour l'instant et discuterons d'autres options plus tard.

## Comment une IA peut-elle agir sur son environnement ?

Les LLM sont des modèles incroyables, mais **ils ne peuvent générer que du texte**.

Cependant, si vous demandez à une application de chat bien connue comme HuggingChat (interrompu) ou ChatGPT de générer une image, elle le peut ! Comment cela est-il possible ?

La réponse est que les développeurs de ChatGPT et d'applications similaires ont implémenté des fonctionnalités supplémentaires (appelées **Outils**), que le LLM peut utiliser pour créer des images.

Le modèle a utilisé un outil de génération d'images pour générer cette image.

Nous en apprendrons plus sur les outils dans la section [Outils](tools).

## Quel type de tâches un agent peut-il accomplir ?

Un agent peut effectuer n'importe quelle tâche que nous implémentons via des **outils** pour compléter des **actions**.

Par exemple, si j'écris un agent pour qu'il agisse comme mon assistant personnel (à la Siri) sur mon ordinateur, et que je lui demande « d'envoyer un courriel à mon directeur pour lui demander de reporter la réunion d'aujourd'hui », je peux lui donner du code pour envoyer des courriels. Il s'agira d'un nouvel outil que l'agent pourra utiliser chaque fois qu'il aura besoin d'envoyer un courriel. Nous pouvons l'écrire en Python :

```python
def send_message_to(recipient, message):
    """Utile pour envoyer un message e-mail à un destinataire"""
    ...
```

Le LLM, comme nous le verrons, générera du code pour exécuter l'outil quand il en aura besoin, et ainsi accomplir la tâche désirée.

```python
send_message_to("Manager", "Pouvons-nous reporter la réunion d'aujourd'hui ?")
```

La **conception des outils est très importante et a un grand impact sur la qualité de votre agent**. 
Certaines tâches en nécessiteront d'en créer des très spécifiques, tandis que d'autres peuvent être résolues avec des outils à usage général comme "web_search".

> Notez que **les actions ne sont pas la même chose que les outils**. Une action peut par exemple impliquer l'utilisation de plusieurs outils pour être complétée.

Autoriser à un agent d'interagir avec son environnement **permet une utilisation réelle pour les entreprises et les particuliers**.

### Exemple 1 : Assistants Virtuels Personnels

Les assistants virtuels tels que Siri, Alexa ou Google Assistant fonctionnent comme des agents lorsqu'ils interagissent au nom des utilisateurs dans leur environnement numérique.

Ils répondent aux demandes des utilisateurs, analysent le contexte, récupèrent des informations dans des bases de données et fournissent des réponses ou lancent des actions (comme l'établissement de rappels, l'envoi de messages ou le contrôle d'appareils intelligents).

### Exemple 2 : Chatbots de Service Client

Beaucoup d'entreprises déploient des chatbots comme agents qui interagissent avec les clients en langage naturel.

Ces agents peuvent répondre aux questions, guider les utilisateurs à travers des étapes de dépannage, ouvrir des tickets dans les bases de données internes, ou même compléter des transactions.

Leurs objectifs prédéfinis peuvent inclure l'amélioration de la satisfaction utilisateur, la réduction des temps d'attente, ou l'augmentation des taux de conversion des ventes. En interagissant directement avec les clients, en apprenant des dialogues, et en adaptant leurs réponses au fil du temps, ils démontrent les principes fondamentaux d'un agent en action.

### Exemple 3 : Personnage Non-Joueur dans un jeu vidéo

Les agents  alimentés par des LLM peuvent rendre les Personnages Non-Joueurs (PNJ) plus dynamiques et imprévisibles.

Au lieu de suivre des arbres de comportement rigides, ils peuvent **répondre de façon contextuelle, s'adapter aux interactions des joueurs**, et générer des dialogues plus nuancés. Cette flexibilité aide à créer des personnages plus réalistes et engageants qui évoluent aux côtés des actions du joueur.

---

Pour résumer, un agent est un système qui utilise un modèle d'IA (typiquement un LLM) comme moteur de raisonnement principal, pour :

- **Comprendre le langage naturel :** Interpréter et répondre aux instructions humaines de manière significative.

- **Raisonner et planifier :** Analyser l'information, prendre des décisions, et concevoir des stratégies pour résoudre des problèmes.

- **Interagir avec son environnement :** Rassembler des informations, entreprendre des actions, et observer les résultats de ces actions.

Maintenant que vous avez une bonne compréhension de ce que sont les agents, renforçons votre compréhension avec un court quiz non noté. Après ça, nous plongerons dans le « le cerveau des agents » : les [LLM](what-are-llms).

### Table des matières
https://huggingface.co/learn/agents-course/fr/unit1/README.md

# Table des matières

Vous pouvez accéder à l'Unité 1 sur hf.co/learn 👉 ici

### Réflexions : raisonnement interne et l'approche Re-Act
https://huggingface.co/learn/agents-course/fr/unit1/thoughts.md

# Réflexions : raisonnement interne et l'approche Re-Act

> [!TIP]
> Dans cette section, nous plongeons dans le fonctionnement interne d'un agent : sa capacité à raisonner et à planifier. Nous explorerons comment l'agent utilise son dialogue interne pour analyser l'information, décomposer des problèmes complexes en étapes gérables, et décider de l'action à entreprendre ensuite. De plus, nous présentons l'approche Re-Act, une technique pour prompter qui encourage le modèle à réfléchir « étape par étape » avant d'agir.

Les réflexions représentent le raisonnement interne de l'agent et les processus de planification** pour résoudre la tâche.

Elles utilisent la capacité du LLM de l'agent **à analyser l'information lorsqu'elle est présentée dans son *prompt*** - essentiellement, son monologue intérieur pendant qu'il travaille sur un problème.

Les réflexions de l'agent l'aident à évaluer les observations actuelles et à décider de la ou des prochaines actions à entreprendre. Grâce à ce processus, l'agent peut **décomposer des problèmes complexes en étapes plus petites et plus faciles à gérer**, réfléchir aux expériences passées et ajuster continuellement ses plans en fonction des nouvelles informations.

## Exemples de types de réflexions courantes

| Type de Raisonnement         | Exemple                                                                                                   |
|------------------------|-----------------------------------------------------------------------------------------------------------|
| Planification          | "Je dois décomposer cette tâche en trois étapes : 1) recueillir les données, 2) analyser les tendances, 3) générer le rapport" |
| Analyse                | "D'après le message d'erreur, le problème semble provenir des paramètres de connexion à la base de données" |
| Prise de Décision      | "Étant donné les contraintes budgétaires de l'utilisateur, je devrais recommander l'option de milieu de gamme" |
| Résolution de Problème | "Pour optimiser ce code, je devrais d'abord le profiler pour identifier les goulets d'étranglement"         |
| Intégration de la Mémoire | "L'utilisateur avait mentionné sa préférence pour Python plus tôt, donc je fournirai des exemples en Python"     |
| Auto-Réflexion         | "Ma dernière approche n'a pas bien fonctionné, je devrais essayer une stratégie différente"                 |
| Définition d'Objectifs | "Pour accomplir cette tâche, je dois d'abord établir les critères d'acceptation"                           |
| Priorisation           | "La vulnérabilité de sécurité doit être traitée avant d'ajouter de nouvelles fonctionnalités"               |

> **Note:** Dans le cas des LLM finetuné pour de l'appel de fonctions, le processus de réflexion est facultatif. Davantage de détails seront abordés dans la section sur les actions.

## Chaîne de réflexion (*Chain-of-Thought* généralement abrégé en CoT)

Une chaîne de réflexion est une technique de prompt qui guide un modèle à **réfléchir à un problème étape par étape avant de produire une réponse finale**.

Cela commence généralement par :  
*« Réfléchissons étape par étape ».

Cette approche aide le modèle à **raisonner en interne**, en particulier pour les tâches logiques ou mathématiques, **sans interagir avec des outils externes**.

### Exemple
```
Question: Qu'est-ce que 15 % de 200 ?
Thought: Réfléchissons étape par étape. 10 % de 200, c'est 20, et 5 % de 200, c'est 10, donc 15 %, c'est 30.
Answer: 30
```

## ReAct : Raisonner + Action

Une méthode clé est l'approche **ReAct**, qui combine le « raisonnement » (Think) et l'« action » (Act).

ReAct est une technique de prompt qui encourage le modèle à réfléchir étape par étape et à intercaler des actions (comme l'utilisation d'outils) entre les étapes du raisonnement.

Cela permet à l'agent de résoudre des tâches complexes en plusieurs étapes en alternant entre :
- Réflexion : raisonnement interne
- Action : utilisation d'outils
- Observation : réception des résultats de l'outil

### Exemple (ReAct)
```
Thought: J'ai besoin de connaître le temps qu'il fait à Paris.
Action: Search["temps à Paris"]
Observation: Il fait 18°C et le ciel est nuageux.
Thought: Maintenant que je connais le temps...
Action: Finish["Il fait 18°C et le ciel est nuageux à Paris."]
```

  
  
    (d) est un exemple de l'approche ReAct, où nous demandons « Réfléchissons étape par étape », et le modèle agit entre les réflexions.
  

## Comparison: ReAct vs. CoT

| Caractéristique      | Chain-of-Thought (CoT)      | ReAct                               |
|----------------------|-----------------------------|-------------------------------------|
| Logique étape par étape   | ✅ Oui                      | ✅ Oui                              |
| Outils externes      | ❌ Non                      | ✅ Oui (Actions + Observations)     |
| Convient le mieux à  | Logique, mathématiques, tâches internes | Recherche d'informations, tâches dynamiques en plusieurs étapes |

> [!TIP]
> Les modèles récents comme **Deepseek R1** ou **OpenAI's o1** ont été finetunés pour *réfléchir avant de répondre*. Ils utilisent des *tokens* structurés comme `` et `` pour séparer explicitement la phase de raisonnement de la réponse finale.
>
> Contrairement à ReAct ou CoT - qui sont des stratégies pour prompter - il s'agit d'une **technique d'entrâinement**, où le modèle apprend à penser à l'aide d'exemples.

### Observer : intégrer le retour d'information pour réfléchir et s'adapter
https://huggingface.co/learn/agents-course/fr/unit1/observations.md

# Observer : intégrer le retour d'information pour réfléchir et s'adapter

Les observations sont **la manière dont un agent perçoit les conséquences de ses actions**.

Elles fournissent des informations cruciales qui alimentent le processus de réflexion de l'agent et orientent ses actions futures.

Ce sont **des signaux provenant de l'environnement** (qu'il s'agisse de données issues d'une API, de messages d'erreur ou de logs système) qui guident le prochain cycle de réflexion.

Dans la phase d'observation, l'agent :

- **Collecte des retours :** Il reçoit des données ou une confirmation que son action a réussi (ou non).
- **Ajoute les résultats :** Il intègre la nouvelle information dans son contexte existant, mettant ainsi à jour sa mémoire.
- **Adapte sa stratégie :** Il utilise ce contexte actualisé pour affiner ses réflexions et ses actions ultérieures.

Par exemple, si une API météo renvoie les données *"partiellement nuageux, 15°C, 60 % d'humidité"*, cette observation est ajoutée à la mémoire de l'agent (à la fin du *prompt*).

L'agent l'utilise ensuite pour décider si des informations supplémentaires sont nécessaires ou s'il est prêt à fournir une réponse finale.

Cette **intégration itérative des retours d'information assure que l'agent reste dynamiquement aligné avec ses objectifs**, apprenant et s'ajustant constamment en fonction des résultats concrets.

Ces observations **peuvent prendre de nombreuses formes**, allant de la lecture de texte sur une page web à la surveillance de la position d'un bras robotisé. 
On peut les considérer comme des « logs » d'outils fournissant un retour textuel sur l'exécution d'une action.

| Type d'observation         | Exemple                                                                                     |
|----------------------------|---------------------------------------------------------------------------------------------|
| Retour système             | Messages d'erreur, notifications de succès, codes de statut                                 |
| Modifications de données   | Mises à jour de base de données, modifications du système de fichiers, changements d'état   |
| Données environnementales  | Relevés de capteurs, métriques système, utilisation des ressources                           |
| Analyse des réponses       | Réponses d'API, résultats de requêtes, sorties de calcul                                     |
| Événements temporels       | Dates limites atteintes, tâches programmées terminées                                        |

## Comment les résultats sont-ils ajoutés ?

Après avoir effectué une action, le *framework* suit les étapes suivantes dans cet ordre :

1. **Analyser l'action** pour identifier la ou les fonctions à appeler et les arguments à utiliser.  
2. **Exécuter l'action.**  
3. **Ajouter le résultat** en tant qu'**observation**.  

---

Nous avons maintenant appris le cycle de raisonnement-action-observation de l'agent.

Si certains aspects vous semblent encore un peu flous, ne vous inquiétez pas, nous reviendrons sur ces concepts et les approfondirons dans les prochaines unités.

Il est maintenant temps de mettre vos connaissances en pratique en codant votre tout premier agent !

### Introduction aux agents
https://huggingface.co/learn/agents-course/fr/unit1/introduction.md

# Introduction aux agents

Bienvenue dans cette première unité, qui **vous permettra d'acquérir des bases solides sur les principes fondamentaux des agents**, notamment :

- **Comprendre les agents**  
  - Qu'est-ce qu'un agent, et comment fonctionne-t-il ?  
  - Comment les agents prennent-ils des décisions via le raisonnement et la planification ?

- **Le rôle des LLM (*Large Language Models*) dans les agents**  
  - Comment les LLM servent de « cerveau » aux agents.  
  - Comment les LLM structurent les conversations via le système de messages.

- **Outils et actions**  
  - Comment les agents utilisent des outils externes pour interagir avec l'environnement.  
  - Comment construire et intégrer des outils pour votre agent.

- **Le flux de travail de l'agent :**  
  - *Think* → *Act* → *Observe* (*Penser* → *Agir* → *Observer*).

Après avoir exploré ces sujets, **vous construirez votre premier agent** en utilisant `smolagents` !

Votre agent, nommé Alfred, réalisera une tâche simple et démontrera comment appliquer ces concepts en pratique.

Vous apprendrez même comment **publier votre agent via un *Space*** afin de le partager avec vos amis et collègues.

Enfin, à la fin de cette Unité, vous passerez un quiz. Réussissez-le, et vous **obtiendrez votre première certification du cours** : le 🎓 Certificat *Fundamentals of Agents*.

Cette Unité est votre **point de départ** posant les bases pour comprendre les agents avant de passer à des sujets plus avancés.

C'est une grande unité, alors **prenez votre temps** et n'hésitez pas à revenir sur ces sections de temps en temps.

Prêt ? Plongeons dans l'aventure ! 🚀

### Comprendre les agents à travers le cycle Réflexion-Action-Observation
https://huggingface.co/learn/agents-course/fr/unit1/agent-steps-and-structure.md

# Comprendre les agents à travers le cycle Réflexion-Action-Observation

Dans les sections précédentes, nous avons appris :

- **Comment les outils sont mis à disposition de l'agent dans le *prompt* système**.
- **Comment les agents sont des systèmes capables de « raisonner », planifier et interagir avec leur environnement**.

Dans cette section, **nous explorerons l'ensemble du *workflow* de l'agent**, un cycle que nous avons défini comme Réflexion-Action-Observation (*Thought-Action-Observation*).

Puis, nous approfondirons chacune de ces étapes.

## Les composants de base

Les agents fonctionnent selon un cycle continu : **réfléchir (Réflexion) → agir (Action) et observer (Observation)** (***thinking (Thought) → acting (Act) and observing (Observe)***).

Décomposons ensemble ces actions :

1. **Réflexion** : La partie LLM de l'agent décide de la prochaine étape.
2. **Action** : L'agent passe à l'action en appelant les outils avec les arguments associés.
3. **Observation** : Le modèle analyse la réponse renvoyée par l'outil.

## Le cycle Réflexion-Action-Observation

Les trois composants fonctionnent ensemble dans une boucle continue. Pour utiliser une analogie issue de la programmation, l'agent utilise une **boucle while** : la boucle continue jusqu'à ce que l'objectif de l'agent soit atteint.

Visuellement, cela ressemble à ceci :

Dans de nombreux *frameworks* d'agents, **les règles et directives sont intégrées directement dans le *prompt* système**, garantissant que chaque cycle respecte une logique définie.

Dans une version simplifiée, notre *prompt* système pourrait ressembler à ceci :

Nous voyons ici que dans le message système, nous avons défini :

- Le *comportement de l'agent*.
- Les *outils auxquels il a accès*, comme nous l'avons décrit dans la section précédente.
- Le *cycle Réflexion-Action-Observation*, que nous intégrons dans les instructions du LLM.

Prenons un petit exemple pour comprendre le processus avant d'approfondir chacune des étapes.

## Alfred, l'agent météorologiste

Nous avons créé Alfred, l'agent météorologiste.

Un utilisateur demande à Alfred : « Quel temps fait-il à New York aujourd'hui ? »

La mission d'Alfred est de répondre à cette question en utilisant un outil d'API météo.

Voici comment le cycle se déroule :

### Réflexion

**Raisonnement interne :**

Après avoir reçu la requête, le dialogue interne d'Alfred pourrait être :

*"L'utilisateur a besoin d'informations météorologiques actuelles pour New York. J'ai accès à un outil qui récupère les données météo. D'abord, je dois appeler l'API météo pour obtenir des détails à jour."*

Cette étape montre l'agent décomposant le problème en étapes : d'abord, rassembler les données nécessaires.

### Action

**Utilisation d'un outil :**

En se basant sur son raisonnement et sur le fait qu'Alfred connaît l'outil `get_weather`, Alfred prépare une commande au format JSON qui appelle l'outil API météo. Par exemple, sa première action pourrait être :

Raisonnement : Je dois vérifier la météo actuelle pour New York.

```
{
  "action": "get_weather",
  "action_input": {
    "location": "New York"
  }
}
```

Ici, l'action précise clairement quel outil appeler (par exemple, `get_weather`) et quel paramètre passer (la « location » : « New York »).

### Observation

**Retour d'information de l'environnement :**

Après l'appel à l'outil, Alfred reçoit une observation. Cela pourrait être les données météo brutes renvoyées par l'API, par exemple :

*"Météo actuelle à New York : partiellement nuageux, 15°C, 60% d'humidité."*

Cette observation est ensuite ajoutée au *prompt* en tant que contexte supplémentaire. Elle agit comme un retour du monde réel, confirmant si l'action a réussi et fournissant les détails nécessaires.

### Raisonnement mis à jour

**Réflexion :**

Avec l'observation en main, Alfred met à jour son raisonnement interne :

*"Maintenant que j'ai les données météo pour New York, je peux préparer une réponse pour l'utilisateur."*

### Action finale

Alfred génère ensuite une réponse finale formatée comme nous le lui avons indiqué :

Raisonnement : J'ai maintenant les données météo. La météo actuelle à New York est partiellement nuageuse avec une température de 15°C et 60% d'humidité.

Réponse finale : La météo actuelle à New York est partiellement nuageuse avec une température de 15°C et 60% d'humidité.

Cette action finale renvoie la réponse à l'utilisateur, bouclant ainsi le cycle.

Ce que nous voyons dans cet exemple :

- **Les agents itèrent dans une boucle jusqu'à ce que l'objectif soit atteint :**  
  Le processus d'Alfred est cyclique. Il commence par un raisonnement, passe à l'action en appelant un outil, puis observe le résultat. Si l'observation avait indiqué une erreur ou des données incomplètes, Alfred aurait pu reprendre le cycle pour corriger son approche.
    
- **Intégration des outils :**  
  La capacité d'appeler un outil (comme une API météo) permet à Alfred d'aller **au-delà de la connaissance statique et de récupérer des données en temps réel**, un aspect essentiel de nombreux agents.

- **Adaptation dynamique :**  
  Chaque cycle permet à l'agent d'intégrer des informations fraîches (observations) dans son raisonnement (réflexions), garantissant que la réponse finale est bien informée et précise.
    
Cet exemple illustre le concept fondamental du *cycle ReAct* (un concept que nous allons développer dans la section suivante) : **l'interaction entre Réflexion, Action et Observation permet aux agents de résoudre de manière itérative des tâches complexes**.

En comprenant et en appliquant ces principes, vous pouvez concevoir des agents qui non seulement réfléchissent à leurs tâches, mais utilisent également efficacement des outils externes pour les accomplir, tout en affinant continuellement leur production en fonction des retours de l'environnement.

---

Plongeons maintenant plus en profondeur dans le Réflexion, l'Action et l'Observation en tant qu'étapes individuelles du processus.

### Quiz rapide 1 [[quiz1]]
https://huggingface.co/learn/agents-course/fr/unit1/quiz1.md

# Quiz rapide 1 [[quiz1]]

---

### Q1 : Qu'est-ce qu'un agent ?
Laquelle des propositions suivantes décrit le mieux un agent en IA ?

---

### Q2 : Quel est le rôle de la planification chez un agent ?
Pourquoi un agent a-t-il besoin de planifier avant d'agir ?

---

### Q3 : Comment les outils améliorent-ils les capacités d'un agent ?
Pourquoi les outils sont-ils essentiels pour un agent ?

---

### Q4 : Quelle est la principale différence entre actions et outils ?
Quelle est la principale différence entre les actions et les outils ?

---

### Q5 : Quel rôle jouent les *Large Language Models* (LLM) dans les agents ?
Comment les LLM contribuent-ils aux fonctionnalités d'un agent ?

---

### Q6 : Lequel des exemples suivants illustre le mieux un agent ?
Quel exemple concret illustre le mieux un agent en action ?

---

Félicitations pour avoir terminé ce Quiz 🥳 ! Si certains éléments vous ont échappé, prenez le temps de relire le chapitre pour renforcer vos connaissances. Si vous le réussissez, vous êtes prêt à plonger plus en profondeur dans le « cerveau des agents » : les LLM.

### Bibliothèque d'agents factices
https://huggingface.co/learn/agents-course/fr/unit1/dummy-agent-library.md

# Bibliothèque d'agents factices

Ce cours est indépendant de tout framework car nous souhaitons **nous concentrer sur les concepts des agents et éviter de nous enliser dans les spécificités d'un *framework* particulier**.

De plus, nous voulons que les étudiants puissent utiliser les concepts qu'ils apprennent dans ce cours dans leurs propres projets, en utilisant le *framework* de leur choix.

Ainsi, pour cette Unité 1, nous utiliserons une bibliothèque d'agents factices et une API sans serveur simple pour accéder à notre moteur LLM.

Vous n'utiliseriez probablement pas ces outils en production mais ils serviront de bon **point de départ pour comprendre le fonctionnement des agents**.

Après cette section, vous serez prêt à **créer un agent simple** en utilisant `smolagents`. Et dans les unités suivantes, nous utiliserons également d'autres bibliothèques telles que `LangGraph`, `LangChain` et `LlamaIndex`.

Pour simplifier, nous utiliserons une fonction Python simple comme outil et agent.

Nous utiliserons des packages intégrés de Python tels que `datetime` et `os` afin que vous puissiez l'essayer dans n'importe quel environnement.

Vous pouvez suivre le processus [dans ce *notebook*](https://huggingface.co/agents-course/notebooks/blob/main/fr/unit1/dummy_agent_library.ipynb) et **exécuter le code vous-même**.

## API sans serveur

Dans l'écosystème Hugging Face, il existe une fonctionnalité pratique appelée API sans serveur qui vous permet d'exécuter facilement des inférences sur de nombreux modèles. Aucune installation ou déploiement n'est requis.

```python
import os
from huggingface_hub import InferenceClient

## Vous avez besoin d'un token depuis https://hf.co/settings/tokens. Si vous exécutez ce code sur Google Colab, vous pouvez le configurer dans l'onglet "settings" sous "secrets". Assurez-vous de l'appeler "HF_TOKEN"
os.environ["HF_TOKEN"] = "hf_xxxxxxxxxxxxxx"

client = InferenceClient(model="moonshotai/Kimi-K2.5")
```

Nous utilisons la méthode `chat` car c'est un moyen pratique et fiable d'appliquer des gabarits de chat :

```python
output = client.chat.completions.create(
    messages=[
        {"role": "user", "content": "The capital of France is"},
    ],
    stream=False,
    max_tokens=1024,
    extra_body={'thinking': {'type': 'disabled'}},
)
print(output.choices[0].message.content)
```

ressort :
```
Paris.
```

La méthode de chat est la méthode **RECOMMANDÉE** à utiliser afin d'assurer une transition fluide entre les modèles.

## Agent factice

Dans les sections précédentes, nous avons vu que le cœur d'une bibliothèque d'agents consiste à ajouter des informations dans le *prompt* système.

Ce *prompt* système est un peu plus complexe que celui que nous avons vu précédemment, mais il contient déjà :

1. **Des informations sur les outils**
2. **Des instructions de cycle** (Réflexion → Action → Observation)

```python
# Ce prompt système est un peu plus complexe et contient en fait la description de la fonction déjà ajoutée.
# Nous supposons ici que la description textuelle des outils a déjà été ajoutée.

SYSTEM_PROMPT = """Répondez du mieux que vous pouvez aux questions suivantes. Vous avez accès aux outils suivants :

get_weather: Obtenez la météo actuelle dans un lieu donné

La manière d'utiliser les outils consiste à spécifier un blob JSON.
Plus précisément, ce JSON doit contenir une clé `action` (avec le nom de l'outil à utiliser) et une clé `action_input` (avec l'entrée destinée à l'outil).

Les seules valeurs qui devraient figurer dans le champ "action" sont:
get_weather: Obtenez la météo actuelle dans un lieu donné, args: {"location": {"type": "string"}}
exemple d'utilisation : 

{{
  "action": "get_weather",
  "action_input": {"location": "New York"}
}}

UTILISEZ TOUJOURS le format suivant:

Question : la question à laquelle vous devez répondre
Réflexion : vous devez toujours réfléchir à une action à entreprendre. Une seule action à la fois dans ce format:
Action:

$JSON_BLOB (dans une cellule markdown)

Observation : le résultat de l'action. Cette Observation est unique, complète et constitue la source de vérité.
... (ce cycle Réflexion/Action/Observation peut se répéter plusieurs fois, vous devez effectuer plusieurs étapes si nécessaire. Le $JSON_BLOB doit être formaté en markdown et n'utiliser qu'une SEULE action à la fois.)

Vous devez toujours terminer votre sortie avec le format suivant:

Réflexion : Je connais désormais la réponse finale
Réponse finale : la réponse finale à la question d'entrée initiale

Commencez maintenant! Rappel: utilisez TOUJOURS exactement les caractères `Réponse finale :` lorsque vous fournissez une réponse définitive.
```

**Note** : dans le *prompt* ci-dessus, nous avons utiliser le vouvoiement. Il n'y a pas à notre connaissance de papier ayant étudié si c'est ou pas la meilleure approche possible comparé à des indications faites avec du tutoiement ou encore de l'impératif. 

Nous devons ajouter le *prompt* de l'utilisateur après le *prompt* du système. Cela se fait à l'intérieur de la méthode `chat`. Nous pouvons voir ce processus ci-dessous :

```python
messages = [
    {"role": "system", "content": SYSTEM_PROMPT},
    {"role": "user", "content": "Quel temps fait-il à Londres ?"},
]

print(messages)
```

Le prompt est maintenant :
```
system
Répondez du mieux que vous pouvez aux questions suivantes. Vous avez accès aux outils suivants:

get_weather: Obtenez la météo actuelle dans un lieu donné

La manière d'utiliser les outils consiste à spécifier un blob JSON.
Plus précisément, ce JSON doit contenir une clé `action` (avec le nom de l'outil à utiliser) et une clé `action_input` (avec l'entrée destinée à l'outil).

Les seules valeurs qui devraient figurer dans le champ "action" sont:
get_weather: Obtenez la météo actuelle dans un lieu donné, args: {"location": {"type": "string"}}
exemple d'utilisation : 

{{
  "action": "get_weather",
  "action_input": {"location": "New York"}
}}

UTILISEZ TOUJOURS le format suivant:

Question : la question à laquelle vous devez répondre
Réflexion : vous devez toujours réfléchir à une action à entreprendre. Une seule action à la fois dans ce format:
Action:

$JSON_BLOB (dans une cellule markdown)

Observation : le résultat de l'action. Cette Observation est unique, complète et constitue la source de vérité.
... (ce cycle Réflexion/Action/Observation peut se répéter plusieurs fois, vous devez effectuer plusieurs étapes si nécessaire. Le $JSON_BLOB doit être formaté en markdown et n'utiliser qu'une SEULE action à la fois.)

Vous devez toujours terminer votre sortie avec le format suivant:

Réflexion : Je connais désormais la réponse finale
Réponse finale : la réponse finale à la question d'entrée initiale

Commencez maintenant! Rappel: utilisez TOUJOURS exactement les caractères `Réponse finale :` lorsque vous fournissez une réponse définitive.
user
Quel temps fait-il à Londres ?
assistant
```

Appelons la méthode `chat` !

```python
output = client.chat.completions.create(
    messages=messages,
    stream=False,
    max_tokens=200,
    extra_body={'thinking': {'type': 'disabled'}},
)
print(output.choices[0].message.content)
```

```
Réflexion : Pour répondre à la question, je dois obtenir le temps qu'il fait actuellement à Londres.
Action:
```
{
  "action": "get_weather",
  "action_input": {"location": "Londres"}
}
```
Observation : Le temps actuel à Londres est partiellement nuageux avec une température de 12°C.
Réflexion : Je connais maintenant la réponse finale.
Réponse finale : Le temps actuel à Londres est partiellement nuageux et la température est de 12°C.
```

Voyez-vous le problème ?
> À ce stade, le modèle hallucine, car il produit une « Observation » fabriquée, c'est-à-dire une réponse qu'il génère de lui-même au lieu d'être le résultat d'une fonction réelle ou d'un appel d'outil. Pour éviter cela, nous arrêtons la génération juste avant « Observation : ». Cela nous permet d'exécuter manuellement la fonction (par exemple, `get_weather`) et d'insérer ensuite le résultat réel en tant qu'observation.

```python
# La réponse a été hallucinée par le modèle. Nous devons nous arrêter pour exécuter la fonction !
output = client.chat.completions.create(
    messages=messages,
    max_tokens=150,
    stop=["Observation :"], # Arrêtons avant qu'une fonction ne soit appelée
    extra_body={'thinking': {'type': 'disabled'}},
)

print(output.choices[0].message.content)
```

renvoie :

```
Réflexion : Pour répondre à la question, je dois connaître le temps qu'il fait à Londres.
Action:
```
{
  "action": "get_weather",
  "action_input": {"location": "Londres"}
}
```
Réflexion : Je vais vérifier la météo à Londres.
Observation :
```

Beaucoup mieux ! 

Créons maintenant une fonction pour obtenir la météo. Dans une situation réelle, vous appelleriez probablement une API.

```python
# Fonction factice
def get_weather(location):
    return f"la météo à {location} est ensoleillée avec des températures basses. \n"

get_weather('Londres')
```

renvoie :
```
'la météo à Londres est ensoleillée avec des températures basses. \n'
```

Concaténons le *prompt* du système, le *prompt* de base, la complétion jusqu'à l'exécution de la fonction et le résultat de la fonction en tant qu'observation et reprenons la génération.

```python
messages=[
    {"role": "system", "content": SYSTEM_PROMPT},
    {"role": "user", "content": "Quel temps fait-il à Londres ?"},
    {"role": "assistant", "content": output.choices[0].message.content + get_weather('Londres')},
]

output = client.chat.completions.create(
    messages=messages,
    stream=False,
    max_tokens=200,
    extra_body={'thinking': {'type': 'disabled'}},
)

print(output.choices[0].message.content)
```

Voici le nouveau *prompt* :
```
system
Répondez du mieux que vous pouvez aux questions suivantes. Vous avez accès aux outils suivants:

get_weather: Obtenez la météo actuelle dans un lieu donné

La manière d'utiliser les outils consiste à spécifier un blob JSON.
Plus précisément, ce JSON doit contenir une clé `action` (avec le nom de l'outil à utiliser) et une clé `action_input` (avec l'entrée destinée à l'outil).

Les seules valeurs qui devraient figurer dans le champ "action" sont:
get_weather: Obtenez la météo actuelle dans un lieu donné, args: {"location": {"type": "string"}}
exemple d'utilisation : 

{{
  "action": "get_weather",
  "action_input": {"location": "New York"}
}}

UTILISEZ TOUJOURS le format suivant:

Question : la question à laquelle vous devez répondre
Réflexion : vous devez toujours réfléchir à une action à entreprendre. Une seule action à la fois dans ce format:
Action:

$JSON_BLOB (dans une cellule markdown)

Observation : le résultat de l'action. Cette Observation est unique, complète et constitue la source de vérité.
... (ce cycle Réflexion/Action/Observation peut se répéter plusieurs fois, vous devez effectuer plusieurs étapes si nécessaire. Le $JSON_BLOB doit être formaté en markdown et n'utiliser qu'une SEULE action à la fois.)

Vous devez toujours terminer votre sortie avec le format suivant:

Réflexion : Je connais désormais la réponse finale
Réponse finale : la réponse finale à la question d'entrée initiale

Commencez maintenant! Rappel: utilisez TOUJOURS exactement les caractères `Réponse finale :` lorsque vous fournissez une réponse définitive.
user
Quel temps fait-il à Londres ?
assistant
```

renvoie :
```
Réponse finale : La météo à Londres est ensoleillée avec des températures basses.
```

---

Nous avons appris comment créer des agents à partir de zéro en utilisant du code Python, et nous **avons constaté à quel point ce processus peut être fastidieux**. Heureusement, de nombreuses bibliothèques d'agents simplifient ce travail en prenant en charge la majeure partie de la charge de travail pour vous.

Maintenant, nous sommes prêts **à créer notre premier vrai agent** en utilisant la bibliothèque `smolagents`.

### Embarquement : vos premiers pas ⛵
https://huggingface.co/learn/agents-course/fr/unit0/onboarding.md

# Embarquement : vos premiers pas ⛵

Maintenant que vous avez tous les détails, commençons ! Nous allons réaliser quatre choses :

1. **Créer votre compte Hugging Face** si ce n'est pas déjà fait  
2. **Vous inscrire à Discord et vous présenter** (ne soyez pas timide 🤗)  
3. **Suivre le cours sur les agents** sur le 🤗 Hub  
4. **Faire passer le mot** à propos du cours

### Étape 1 : Créer votre compte Hugging Face

(Si ce n'est pas déjà fait) créez un compte Hugging Face ici.

### Étape 2 : Rejoindre notre Discord

👉🏻 Rejoignez notre serveur Discord ici.

Lorsque vous rejoignez, n'oubliez pas de vous présenter dans `#introduce-yourself`.

Consultez le canal `courses` sur le `Hugging Face Hub` pour toutes les questions et demandes liées aux cours.

Si c'est votre première utilisation de Discord, nous avons rédigé un guide d'introduction pour vous donner les meilleures pratiques. Consultez [la section suivante](discord101).

### Étape 3 : Suivre l'organisation *Hugging Face Agent Course* sur le 🤗 Hub

Restez à jour avec les derniers matériels de cours, mises à jour, et annonces **en suivant l'organisation du cours sur le Hub**.

👉 Rendez-vous ici et cliquez sur **suivre**.

### Étape 4 : Faites passer le mot à propos du cours

Aidez-nous à rendre ce cours plus visible ! Il y a deux façons de nous aider :

1. Montrez votre soutien en laissant une étoile ⭐ sur le dépôt du cours.

2. Partagez votre parcours d'apprentissage : faites savoir aux autres **que vous suivez ce cours** ! Nous avons préparé une illustration que vous pouvez utiliser dans vos publications sur les réseaux sociaux.

Vous pouvez télécharger l'image en cliquant 👉 [ici](https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/communication/share.png?download=true)

### Étape 5 : Exécuter des modèles localement avec Ollama (En cas de limites de crédits)

1. **Installez Ollama**

    Suivez les instructions officielles ici.

2. **Téléchargez un modèle localement**
``` bash
    ollama pull qwen2:7b # Consultez le site web d'Ollama pour plus de modèles
```
3. **Démarrez Ollama en arrière-plan (dans un terminal)**
``` bash
    ollama serve
``` 
    Si vous rencontrez l'erreur "*listen tcp 127.0.0.1:11434: bind: address already in use*", vous pouvez utiliser la commande `sudo lsof -i :11434` pour identifier l'ID du processus (PID) qui utilise actuellement ce port. Si le processus est `ollama`, il est probable que le script d'installation ci-dessus ait démarré le service ollama, vous pouvez donc ignorer cette commande pour démarrer Ollama.

4. **Utilisez `LiteLLMModel` au lieu de `InferenceClientModel`**

   Pour utiliser le module `LiteLLMModel` dans `smolagents`, vous pouvez exécuter la commande `pip` pour installer le module.

``` bash
    pip install 'smolagents[litellm]'
```

``` python
    from smolagents import LiteLLMModel

    model = LiteLLMModel(
        model_id="ollama_chat/qwen2:7b",  # Ou essayez d'autres modèles supportés par Ollama
        api_base="http://127.0.0.1:11434",  # Serveur local Ollama par défaut
        num_ctx=8192,
    )
```

5. **Pourquoi cela fonctionne-t-il ?**
- Ollama sert des modèles localement en utilisant une API compatible avec OpenAI à `http://localhost:11434`.
- `LiteLLMModel` est conçu pour communiquer avec tout modèle qui supporte le format d'API OpenAI chat/completion.
- Cela signifie que vous pouvez simplement remplacer `InferenceClientModel` par `LiteLLMModel` sans autres changements de code nécessaires. C'est une solution transparente et prête à l'emploi.

Félicitations ! 🎉  
**Vous avez terminé le processus d'embarquement** ! Vous êtes maintenant prêt à commencer à en apprendre plus  sur les agents IA. Amusez-vous bien !

Continuez à apprendre, restez formidable 🤗

### Bienvenue dans le cours 🤗 [[introduction]]
https://huggingface.co/learn/agents-course/fr/unit0/introduction.md

# Bienvenue dans le cours 🤗 [[introduction]]

L'arrière-plan de l'image a été généré à l'aide de Scenario.com

Bienvenue dans le sujet le plus passionnant de l'IA aujourd'hui : les **Agents** !

Ce cours gratuit vous guidera, du **niveau débutant à expert**, pour comprendre, utiliser et construire des agents.

Cette première unité va vous aider à démarrer :

- Découvrez le **programme du cours**.
- **Choisissez le parcours** que vous souhaitez suivre (soit en autoformation, soit en suivant le processus de certification).
- **Obtenez plus d'informations sur le processus de certification**.
- Faites connaissance avec l'équipe derrière le cours.
- Créez votre **compte Hugging Face**.
- **Inscrivez-vous à notre serveur Discord** pour rencontrez vos camarades ainsi que nous.

Commençons !

## Que pouvez-vous attendre de ce cours ? [[expect]]

Dans ce cours, vous allez :

- 📖 Étudier les agents en IA par la **théorie, la conception et la pratique**.
- 🧑‍💻 Apprendre à **utiliser des bibliothèques établies** telles que [smolagents](https://huggingface.co/docs/smolagents/en/index), [LlamaIndex](https://www.llamaindex.ai/), et [LangGraph](https://langchain-ai.github.io/langgraph/).
- 💾 **Partager vos agents** sur le Hub d'Hugging Face et explorer les agents créés par la communauté.
- 🏆 Participer à des défis où vous **évaluerez vos agents face à ceux des autres étudiants**.
- 🎓 **Obtenir un certificat de réussite** en complétant les exercices.

Et bien plus encore !

À la fin de ce cours, vous comprendrez **comment fonctionnent les agents et comment construire les votres en utilisant les dernières bibliothèques et outils**.

N'oubliez pas de **vous inscrire au cours !**

(Nous respectons votre vie privée. Nous collectons votre adresse email afin de pouvoir **vous envoyer les liens dès que chaque unité est publiée et vous fournir des informations sur les challenges et les mises à jour**.)

## À quoi ressemble le cours ? [[course-look-like]]

Le cours structuré en :

- *Unités fondamentales* : où vous apprenez les **concepts théoriques des agents**.
- *Sessions pratiques* : où vous apprendrez **à utiliser des bibliothèques d'agents existantes** pour entraîner vos agents dans des environnements uniques. Ces sessions pratiques se feront dans des **Spaces** avec un environnement préconfiguré.
- *Exercices basés sur des cas d'utilisation* : où vous appliquerez les concepts appris pour résoudre un problème réel de votre choix.
- *Défis* : vous mettrez votre agent en compétition face à d'autres dans le cadre d'un *challenge*. Le tout sera suivi dans [un classement](https://huggingface.co/spaces/agents-course/Students_leaderboard) pour comparer les performances.

Ce **cours est un projet vivant, évoluant avec vos retours et contributions !** N'hésitez pas à [ouvrir des *issues* et des PR sur GitHub](https://github.com/huggingface/agents-course) et à participer aux discussions sur notre serveur Discord.

Après avoir suivi le cours, vous pouvez également nous envoyer vos retours [👉 via ce formulaire](https://docs.google.com/forms/d/e/1FAIpQLSe9VaONn0eglax0uTwi29rIn4tM7H2sYmmybmG5jJNlE5v0xA/viewform?usp=dialog).

## Quel est le programme ? [[syllabus]]

Voici le **programme général du cours**. Une liste plus détaillée des sujets sera publiée avec chaque unité.

| Chapitre | Sujet | Description |
| :---- | :---- | :---- |
| 0 | Intégration | Vous familiariser avec les outils et plateformes que vous utiliserez. |
| 1 | Fondamentaux | Expliquer les outils, le raisonnement, les actions, les observations et leurs formats. Aborder les LLM, les messages, les *tokens* spéciaux et les patrons de chat. Présenter un cas d'usage simple en utilisant des fonctions Python comme outils. |
| 2 | *Frameworks* | Comprendre comment les fondamentaux sont implémentés dans des bibliothèques populaires : smolagents, LangGraph et LlamaIndex |
| 3 | Cas d'utilisation | Construire quelques cas d'utilisation réels |
| 4 | Projet final | Construire un agent pour un *benchmark* sélectionné afin de démontrer votre compréhension des agents à travers le classement étudiant 🚀 |

En plus du programme principal, il y a 3 unités bonus :
- *Unité Bonus 1* : Finetuner un LLM pour l'appel de fonctions
- *Unité Bonus 2* : Observabilité et évaluation des agents
- *Unité Bonus 3* : Les agents dans les jeux vidéos via Pokemon

Par exemple, dans l'Unité Bonus 3, vous apprendrez à construire votre agent pour jouer aux combats Pokemon 🥊.

## Quels sont les prérequis ?

Pour pouvoir suivre ce cours, vous devez avoir :

- Une connaissance de base de Python
- Une connaissance de base des LLM (nous avons une section de rappel dans l'Unité 1)

## De quels outils ai-je besoin ? [[tools]]

Vous n'avez besoin que de 2 choses :

- Un *ordinateur* avec une connexion internet.
- Un *compte Hugging Face* pour pousser et charger des modèles, des agents, et créer des *Spaces*. Si vous n'avez pas encore de compte, vous pouvez en créer un **[ici](https://hf.co/join)** (c'est gratuit).

## Le processus de certification [[certification-process]]

Vous pouvez choisir de suivre ce cours en *mode auditeur libre* ou de réaliser les activités et *obtenir l'un des deux certificats que nous délivrerons*.

Si vous suivez le cours en auditeur libre, vous pouvez participer à tous les défis et faire les exercices si vous le souhaitez sans avoir **besoin de nous en informer**.

Le processus de certification est **entièrement gratuit** :

- *Pour obtenir une certification des fondamentaux* : vous devez compléter l'Unité 1 du cours. Cela est destiné aux étudiants qui souhaitent se tenir à jour avec les dernières tendances en matière d'agents.
- *Pour obtenir un certificat de réussite* : vous devez compléter l'Unité 1, l'un des exercices de cas d'utilisation que nous proposerons pendant le cours, ainsi que le défi final.

Il n'y a **pas de date limite** pour le processus de certification.

## Quel est le rythme recommandé ? [[recommended-pace]]

Chaque chapitre de ce cours est conçu **pour être complété en 1 semaine, avec environ 3 à 4 heures de travail par semaine**.

Nous vous proposons un rythme recommandé :

## Comment tirer le meilleur parti du cours ? [[advice]]

Pour tirer le meilleur parti du cours, nous vous donnons quelques conseils :

1. Rejoignez des groupes d'étude sur Discord : étudier en groupe est toujours plus facile. Pour cela, vous devez rejoindre notre serveur Discord et vérifier votre compte Hugging Face.
2. **Faites les quiz et les exercices** : la meilleure façon d'apprendre est par la pratique et l'auto-évaluation.
3. **Définissez un planning pour rester en phase** : vous pouvez utiliser notre planning de rythme recommandé ci-dessous ou créer le vôtre.

## Qui sommes-nous ? [[who-are-we]]

Ce cours est maintenu par [Ben Burtenshaw](https://huggingface.co/burtenshaw) et [Sergio Paniego](https://huggingface.co/sergiopaniego). Si vous avez des questions, contactez-nous sur le Hub !

## Remerciements

Nous tenons à exprimer notre gratitude aux personnes suivantes pour leurs contributions inestimables à ce cours :

- **[Joffrey Thomas](https://huggingface.co/Jofthomas)** - Pour la rédaction et le développement du cours.
- **[Thomas Simonini](https://huggingface.co/ThomasSimonini)** - Pour la rédaction et le développement du cours.
- **[Pedro Cuenca](https://huggingface.co/pcuenq)** - Pour avoir guidé le cours et fourni des *feedbacks*.
- **[Aymeric Roucher](https://huggingface.co/m-ric)** - Pour ses incroyables *Spaces* de démonstration (décodage et agent final) ainsi que son aide sur les parties sur smolagents.
- **[Joshua Lochner](https://huggingface.co/Xenova)** - Pour son incroyable *Space* de démonstration sur la tokenisation.
- **[Quentin Gallouédec](https://huggingface.co/qgallouedec)** - Pour son aide sur le contenu du cours.
- **[David Berenstein](https://huggingface.co/davidberenstein1957)** - Pour son aide sur le contenu du cours et la modération.
- **[XiaXiao (ShawnSiao)](https://huggingface.co/SSSSSSSiao)** - Traducteur de la version chinoise du cours.
- **[Jiaming Huang](https://huggingface.co/nordicsushi)** - Traducteur de la version chinoise du cours.
- **[Kim Noel](https://github.com/knoel99)** - Traducteur de la version française du cours.
- **[Loïck Bourdois](https://huggingface.co/lbourdois)** - Traducteur de la version française du cours via le [CATIE](https://www.catie.fr/).

## J'ai trouvé un bug, ou je souhaite améliorer le cours [[contribute]]

Les contributions sont **les bienvenues** 🤗

- Si vous *avez trouvé un bug 🐛 dans un notebook*, veuillez ouvrir une *issue* et **décrire le problème**.
- Si vous *souhaitez améliorer le cours*, vous pouvez ouvrir une *Pull Request*.
- Si vous *voulez ajouter une section complète ou une nouvelle unité*, le mieux est d'ouvrir une *issue* et **décrire le contenu que vous souhaitez ajouter avant de commencer à l'écrire afin que nous puissions vous guider**.

## J'ai encore des questions [[questions]]

Veuillez poser vos questions sur notre serveur Discord dans la section #agents-course-questions.

Maintenant que vous avez toutes les informations, embarquons ⛵

### (Optionnel) Introduction à Discord [[discord-101]]
https://huggingface.co/learn/agents-course/fr/unit0/discord101.md

# (Optionnel) Introduction à Discord [[discord-101]]

Ce guide est conçu pour vous aider à débuter sur Discord, une plateforme de chat gratuite très prisée dans les communautés de *gaming* et de *machine learning*.

Rejoignez le serveur Discord de la communauté Hugging Face, qui compte **plus de 100 000 membres**, en cliquant ici. C'est un excellent endroit pour se connecter avec d'autres passionnés !

## Le cours sur les agents sur le Discord d'Hugging Face

Commencer sur Discord peut sembler un peu intimidant, alors voici un guide rapide pour vous orienter.

Le serveur d'HF réunit une communauté dynamique aux intérêts variés, offrant des opportunités d'apprentissage à travers des discussions sur des papiers scientifiques, des événements et bien plus encore.

Après [vous être inscrit](http://hf.co/join/discord), présentez-vous dans le canal `#introduce-yourself`.

Nous avons créé 4 canaux pour le cours sur les Agents :

- `agents-course-announcements` : pour les **dernières informations sur le cours**.
- `🎓-agents-course-general` : pour les **discussions générales et bavardages**.
- `agents-course-questions` : pour **poser des questions et aider vos camarades**.
- `agents-course-showcase` : pour **présenter vos meilleurs agents**.

De plus, vous pouvez consulter :

- `smolagents` : pour les **discussions et l'assistance concernant la bibliothèque**.

## Conseils pour utiliser Discord efficacement

### Comment rejoindre un serveur
Si vous n'êtes pas très familier avec Discord, vous pouvez consulter ce guide expliquant comment rejoindre un serveur.

Voici un résumé rapide des étapes :

1. Cliquez sur le lien d'invitation.
2. Connectez-vous avec votre compte Discord ou créez-en un si vous n'en avez pas.
3. Vérifiez que vous n'êtes pas un bot/agent IA !
4. Configurez votre pseudo et votre avatar.
5. Cliquez sur « Rejoindre le serveur ».

### Comment utiliser Discord efficacement

Voici quelques conseils pour tirer le meilleur parti de Discord :

- **Les canaux vocaux** sont disponibles, bien que le chat textuel soit le plus utilisé.
- Vous pouvez formater votre texte en utilisant le **style markdown**, ce qui est particulièrement utile pour écrire du code. Notez toutefois que le markdown n'est pas aussi efficace pour les liens.
- Pensez à ouvrir des fils de discussion pour les **conversations longues** afin de garder vos échanges bien organisés.

Nous espérons que ce guide vous sera utile ! Si vous avez des questions, n'hésitez pas à nous les poser sur Discord 🤗.
