tutorial

Agents IA autonomes : de la boucle agentique au pattern ReAct

Construire un agent IA en TypeScript avec Mistral : boucle agentique, pattern ReAct, planification et gestion de la mémoire.

15 minutes de lecture

Dans l'article précédent, on a vu comment le function calling permet au LLM d'interagir avec l'extérieur. Mais un LLM qui appelle une fonction, c'est encore toi qui gères le flux. Un agent, c'est un LLM qui décide lui-même quoi faire, dans quel ordre, et quand s'arrêter.

Chatbot vs Agent : quelle différence ?

Un chatbot répond à tes questions. Tu poses une question, il répond. Tu en poses une autre, il répond. C'est du ping-pong.

Un agent accomplit des tâches. Tu lui donnes un objectif ("Trouve-moi les 3 meilleurs articles sur Rust parus cette semaine"), et il se débrouille : cherche sur le web, filtre les résultats, lit les articles, et te fait un résumé.

1Chatbot : Question → Réponse → Question → Réponse
2
3Agent : Objectif → [Réflexion → Action → Observation]* → Résultat

L'astérisque est important : la boucle se répète autant de fois que nécessaire. L'agent décide seul quand il a terminé.

Ce qui change concrètement

Avec le function calling de l'article précédent, ton code ressemblait à ça :

1// Tu contrôles le flux
2const response = await chat("Lance un d20");
3// Le LLM appelle roll_dice, tu exécutes, il répond

Avec un agent, tu donnes juste l'objectif :

1// L'agent contrôle le flux
2const result = await agent.run("Analyse ce repo GitHub et liste les vulnérabilités de sécurité");
3// L'agent décide : clone le repo, lit les fichiers, cherche des patterns, génère un rapport

La différence ? Tu ne sais pas à l'avance combien d'actions seront nécessaires, ni lesquelles.

Le pattern ReAct : raisonner avant d'agir

La plupart des agents modernes utilisent le pattern ReAct (Reasoning and Acting), introduit par Google en 2022. L'idée : forcer le LLM à expliciter son raisonnement avant chaque action.

Pourquoi c'est important ?

Sans ReAct, le LLM a tendance à foncer tête baissée. Demande-lui de "trouver le bug dans ce code", et il va immédiatement proposer une correction — souvent à côté de la plaque.

Avec ReAct, il doit d'abord réfléchir :

1THOUGHT: Le code plante à la ligne 42. L'erreur mentionne "undefined".
2         Je dois d'abord comprendre d'où vient cette variable.
3ACTION:  read_file({ path: "src/utils.ts", lines: "35-50" })
4OBSERVATION: [contenu du fichier]
5THOUGHT: La variable `user` vient de la fonction `getUser()` qui peut retourner null.
6         Le bug est un accès sans vérification. Je vais proposer un fix.
7ACTION:  [aucune - réponse finale]
8ANSWER:  Le bug est à la ligne 42 : `user.name` sans vérifier que `user` existe...

Le raisonnement explicite améliore la qualité des décisions et permet de debugger l'agent.

Le prompt ReAct

Voici un prompt système typique pour un agent ReAct :

1const AGENT_SYSTEM_PROMPT = `Tu es un agent autonome capable d'accomplir des tâches complexes.
2
3Pour chaque étape, tu dois :
41. RÉFLÉCHIR : analyse la situation et décide de la prochaine action
52. AGIR : utilise un outil si nécessaire
63. OBSERVER : analyse le résultat
7
8Continue jusqu'à avoir accompli l'objectif ou déterminé que c'est impossible.
9
10Règles :
11- Réfléchis toujours avant d'agir
12- Une seule action à la fois
13- Si tu es bloqué, essaie une approche différente
14- Quand l'objectif est atteint, réponds directement sans utiliser d'outil`;

La boucle agentique

Le cœur d'un agent, c'est sa boucle. On l'a vue brièvement dans l'article sur le function calling, mais ici elle prend tout son sens.

1import { Mistral } from "@mistralai/mistralai";
2
3interface Tool {
4  type: "function";
5  function: {
6    name: string;
7    description: string;
8    parameters: object;
9  };
10}
11
12interface AgentConfig {
13  model: string;
14  tools: Tool[];
15  systemPrompt: string;
16  maxIterations?: number;
17}
18
19async function runAgent(
20  objective: string,
21  config: AgentConfig
22): Promise<string> {
23  const client = new Mistral({ apiKey: process.env.MISTRAL_API_KEY });
24  const maxIterations = config.maxIterations ?? 10;
25
26  const messages: any[] = [
27    { role: "system", content: config.systemPrompt },
28    { role: "user", content: objective },
29  ];
30
31  let iterations = 0;
32
33  while (iterations < maxIterations) {
34    iterations++;
35    console.log(`\n--- Itération ${iterations} ---`);
36
37    const response = await client.chat.complete({
38      model: config.model,
39      messages,
40      tools: config.tools,
41    });
42
43    const choice = response.choices?.[0];
44    if (!choice) throw new Error("Pas de réponse du modèle");
45
46    const assistantMessage = choice.message;
47    messages.push(assistantMessage);
48
49    // Cas 1 : le LLM veut utiliser des outils
50    if (choice.finishReason === "tool_calls" && assistantMessage.toolCalls) {
51      for (const toolCall of assistantMessage.toolCalls) {
52        const name = toolCall.function.name;
53        const args = JSON.parse(toolCall.function.arguments);
54
55        console.log(`🔧 Action: ${name}`, args);
56
57        // Exécute l'outil (on verra l'implémentation après)
58        const result = await executeToolCall(name, args);
59
60        console.log(`📋 Résultat:`, result);
61
62        messages.push({
63          role: "tool",
64          toolCallId: toolCall.id,
65          name: name,
66          content: JSON.stringify(result),
67        });
68      }
69      continue;
70    }
71
72    // Cas 2 : le LLM a terminé
73    console.log(`✅ Agent terminé après ${iterations} itération(s)`);
74    return assistantMessage.content as string;
75  }
76
77  throw new Error(`Agent bloqué après ${maxIterations} itérations`);
78}

Exécution des outils

L'exécution des outils est découplée de la boucle. Tu peux l'implémenter comme tu veux :

1const toolRegistry: Record<string, (args: any) => Promise<any>> = {
2  web_search: async ({ query }) => {
3    // Appel à une API de recherche (SerpAPI, Tavily, etc.)
4    const response = await fetch(`https://api.tavily.com/search`, {
5      method: "POST",
6      headers: { "Content-Type": "application/json" },
7      body: JSON.stringify({ query, api_key: process.env.TAVILY_API_KEY }),
8    });
9    return response.json();
10  },
11
12  read_url: async ({ url }) => {
13    const response = await fetch(url);
14    const html = await response.text();
15    // Extraire le texte (simplifié)
16    return html.replace(/<[^>]*>/g, "").slice(0, 5000);
17  },
18
19  calculate: async ({ expression }) => {
20    // Évaluation sécurisée (ne jamais utiliser eval en prod !)
21    return Function(`"use strict"; return (${expression})`)();
22  },
23};
24
25async function executeToolCall(name: string, args: any): Promise<any> {
26  const tool = toolRegistry[name];
27  if (!tool) {
28    return { error: `Outil inconnu: ${name}` };
29  }
30
31  try {
32    return await tool(args);
33  } catch (error) {
34    return { error: String(error) };
35  }
36}

Planification : diviser pour mieux régner

Pour les objectifs complexes, un agent naïf risque de s'éparpiller. La solution : ajouter une phase de planification.

L'approche "plan-then-execute"

Avant d'agir, l'agent crée un plan :

1import { Mistral } from "@mistralai/mistralai";
2
3interface PlanStep {
4  step: number;
5  description: string;
6  tools_needed: string[];
7}
8
9interface ToolDefinition {
10  name: string;
11  description: string;
12}
13
14function buildPlanningPrompt(availableTools: ToolDefinition[]): string {
15  const toolsList = availableTools
16    .map((t) => `- ${t.name}: ${t.description}`)
17    .join("\n");
18
19  return `Tu es un agent de planification.
20
21Outils disponibles :
22${toolsList}
23
24Étant donné un objectif, crée un plan d'action détaillé.
25Chaque étape doit être :
26- Spécifique et actionnable
27- Indépendante autant que possible
28- Vérifiable (on peut savoir si c'est fait)
29- Utilisant uniquement les outils listés ci-dessus
30
31Réponds en JSON :
32{
33  "plan": [
34    { "step": 1, "description": "...", "tools_needed": ["nom_outil"] },
35    { "step": 2, "description": "...", "tools_needed": ["nom_outil"] }
36  ]
37}`;
38}
39
40async function planTask(
41  client: Mistral,
42  objective: string,
43  availableTools: ToolDefinition[]
44): Promise<PlanStep[]> {
45  const response = await client.chat.complete({
46    model: "mistral-small-latest",
47    messages: [
48      { role: "system", content: buildPlanningPrompt(availableTools) },
49      { role: "user", content: objective },
50    ],
51    responseFormat: { type: "json_object" },
52  });
53
54  const content = response.choices?.[0]?.message?.content;
55  return JSON.parse(content as string).plan;
56}

Exécution du plan

Une fois le plan créé, l'agent exécute chaque étape :

1async function runPlanningAgent(
2  objective: string,
3  config: AgentConfig
4): Promise<string> {
5  const client = new Mistral({ apiKey: process.env.MISTRAL_API_KEY });
6
7  // Extraire les définitions d'outils pour le planificateur
8  const toolDefinitions: ToolDefinition[] = config.tools.map((t) => ({
9    name: t.function.name,
10    description: t.function.description,
11  }));
12
13  console.log("📝 Phase de planification...");
14  const plan = await planTask(client, objective, toolDefinitions);
15
16  console.log("Plan créé :");
17  plan.forEach((step) => console.log(`  ${step.step}. ${step.description}`));
18
19  const results: string[] = [];
20
21  for (const step of plan) {
22    console.log(`\n🎯 Étape ${step.step}: ${step.description}`);
23
24    const stepResult = await runAgent(
25      `Contexte: Tu exécutes l'étape ${step.step} d'un plan plus large.
26       Objectif global: ${objective}
27       Ta tâche: ${step.description}
28       Résultats précédents: ${results.join("\n")}`,
29      config
30    );
31
32    results.push(`Étape ${step.step}: ${stepResult}`);
33  }
34
35  // Synthèse finale
36  return runAgent(
37    `Synthétise ces résultats pour répondre à l'objectif "${objective}":\n${results.join("\n")}`,
38    { ...config, tools: [] } // Pas d'outils pour la synthèse
39  );
40}

Quand planifier ?

La planification ajoute de la latence et des coûts (appels API supplémentaires). Utilise-la pour :

  • Les tâches multi-étapes (recherche + analyse + rédaction)
  • Les objectifs ambigus qui nécessitent une décomposition
  • Les cas où l'ordre des actions compte

Pour les tâches simples ("Quelle heure est-il à Tokyo ?"), un agent sans planification suffit.

Gestion de la mémoire

Un agent doit gérer deux types de mémoire.

Mémoire court terme : le contexte

C'est l'historique des messages de la conversation en cours. Chaque action et observation s'ajoute au contexte.

Le problème : la context window est limitée. Un agent qui fait 50 actions va dépasser la limite.

Solutions :

1import { Mistral } from "@mistralai/mistralai";
2
3interface Message {
4  role: "system" | "user" | "assistant" | "tool";
5  content: string;
6}
7
8// 1. Résumé périodique
9async function summarizeContext(
10  client: Mistral,
11  messages: Message[]
12): Promise<string> {
13  const response = await client.chat.complete({
14    model: "mistral-small-latest",
15    messages: [
16      {
17        role: "system",
18        content: "Résume cette conversation en gardant les informations clés.",
19      },
20      ...messages,
21    ],
22  });
23  return response.choices?.[0]?.message?.content as string;
24}
25
26// 2. Sliding window : garder les N derniers messages
27function trimMessages(messages: Message[], maxMessages: number): Message[] {
28  // Garder le system prompt + les derniers messages
29  const systemPrompt = messages[0];
30  const recent = messages.slice(1).slice(-maxMessages);
31
32  return [systemPrompt, ...recent];
33}

Mémoire long terme : au-delà de la session

Pour qu'un agent se souvienne entre les sessions, tu as besoin d'un stockage persistant :

1import { Mistral } from "@mistralai/mistralai";
2
3interface Memory {
4  id: string;
5  content: string;
6  embedding: number[];
7  timestamp: Date;
8  metadata: Record<string, any>;
9}
10
11class LongTermMemory {
12  private memories: Memory[] = [];
13  private client: Mistral;
14
15  constructor(client: Mistral) {
16    this.client = client;
17  }
18
19  async store(content: string, metadata: Record<string, any>): Promise<void> {
20    const embedding = await this.embed(content);
21    this.memories.push({
22      id: crypto.randomUUID(),
23      content,
24      embedding,
25      timestamp: new Date(),
26      metadata,
27    });
28  }
29
30  async recall(query: string, limit: number = 5): Promise<Memory[]> {
31    const queryEmbedding = await this.embed(query);
32
33    // Recherche par similarité cosinus
34    return this.memories
35      .map((m) => ({
36        memory: m,
37        score: this.cosineSimilarity(queryEmbedding, m.embedding),
38      }))
39      .sort((a, b) => b.score - a.score)
40      .slice(0, limit)
41      .map((r) => r.memory);
42  }
43
44  private async embed(text: string): Promise<number[]> {
45    const response = await this.client.embeddings.create({
46      model: "mistral-embed",
47      inputs: [text],
48    });
49    return response.data[0].embedding;
50  }
51
52  private cosineSimilarity(a: number[], b: number[]): number {
53    const dot = a.reduce((sum, val, i) => sum + val * b[i], 0);
54    const normA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0));
55    const normB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0));
56    return dot / (normA * normB);
57  }
58}

On utilise ici des embeddings (vecteurs numériques représentant le sens du texte) pour retrouver les souvenirs pertinents. C'est la base du RAG (Retrieval-Augmented Generation), qu'on explorera dans un prochain article.

Limites et garde-fous

Un agent autonome, c'est puissant mais risqué. Voici les pièges courants et comment les éviter.

Boucles infinies

L'agent peut tourner en rond, répétant les mêmes actions sans progresser.

1// Protection basique : limite d'itérations
2const MAX_ITERATIONS = 15;
3
4interface AssistantMessage {
5  role: "assistant";
6  content: string;
7  toolCalls?: Array<{ function: { name: string; arguments: string } }>;
8}
9
10// Protection avancée : détection de répétition
11function detectLoop(messages: AssistantMessage[]): boolean {
12  const recentActions = messages
13    .filter((m) => m.toolCalls)
14    .slice(-5)
15    .map((m) => JSON.stringify(m.toolCalls));
16
17  // Si les 3 dernières actions sont identiques → boucle
18  if (recentActions.length >= 3) {
19    const last3 = recentActions.slice(-3);
20    if (last3.every((a) => a === last3[0])) {
21      return true;
22    }
23  }
24  return false;
25}

Actions dangereuses

Un agent avec accès au filesystem ou à des APIs peut faire des dégâts.

1import * as readline from "readline";
2
3// Classification des outils
4const DANGEROUS_TOOLS = ["write_file", "execute_command", "send_email"];
5
6// Demande de confirmation utilisateur
7async function askUserConfirmation(prompt: string): Promise<boolean> {
8  const rl = readline.createInterface({
9    input: process.stdin,
10    output: process.stdout,
11  });
12
13  return new Promise((resolve) => {
14    rl.question(`${prompt} (y/n): `, (answer) => {
15      rl.close();
16      resolve(answer.toLowerCase() === "y");
17    });
18  });
19}
20
21// Exécution avec garde-fou
22async function executeToolCallSafe(
23  name: string,
24  args: any,
25  toolRegistry: Record<string, (args: any) => Promise<any>>
26): Promise<any> {
27  if (DANGEROUS_TOOLS.includes(name)) {
28    console.log(`⚠️  Action dangereuse détectée: ${name}`);
29    console.log(`   Arguments: ${JSON.stringify(args)}`);
30
31    const confirmed = await askUserConfirmation("Autoriser cette action ?");
32    if (!confirmed) {
33      return { error: "Action refusée par l'utilisateur" };
34    }
35  }
36
37  return toolRegistry[name](args);
38}

Explosion des coûts

Chaque itération = un appel API. Un agent bavard peut coûter cher.

1class CostTracker {
2  private inputTokens = 0;
3  private outputTokens = 0;
4  private maxBudgetUSD: number;
5
6  // Prix Mistral Small 3 (janvier 2025)
7  // Input: $0.03 / 1M tokens, Output: $0.11 / 1M tokens
8  private readonly INPUT_COST_PER_TOKEN = 0.03 / 1_000_000;
9  private readonly OUTPUT_COST_PER_TOKEN = 0.11 / 1_000_000;
10
11  constructor(maxBudgetUSD: number) {
12    this.maxBudgetUSD = maxBudgetUSD;
13  }
14
15  track(inputTokens: number, outputTokens: number): void {
16    this.inputTokens += inputTokens;
17    this.outputTokens += outputTokens;
18  }
19
20  getCost(): number {
21    return (
22      this.inputTokens * this.INPUT_COST_PER_TOKEN +
23      this.outputTokens * this.OUTPUT_COST_PER_TOKEN
24    );
25  }
26
27  isOverBudget(): boolean {
28    return this.getCost() > this.maxBudgetUSD;
29  }
30
31  report(): string {
32    const cost = this.getCost();
33    return `Tokens: ${this.inputTokens} in / ${this.outputTokens} out (~$${cost.toFixed(6)})`;
34  }
35}

Hallucinations amplifiées

Un agent qui hallucine peut propager ses erreurs sur plusieurs étapes. Le résultat final sera complètement faux, mais présenté avec assurance.

Mitigations :

  • Vérification croisée (plusieurs sources)
  • Retour aux sources primaires quand possible
  • Humilité dans le prompt ("Si tu n'es pas sûr, dis-le")

Exemple complet : agent de recherche

Mettons tout ensemble avec un agent capable de rechercher des informations sur le web.

1import { Mistral } from "@mistralai/mistralai";
2import { z } from "zod";
3import { zodToJsonSchema } from "zod-to-json-schema";
4
5// Schémas des outils
6const webSearchSchema = z.object({
7  query: z.string().describe("La requête de recherche"),
8});
9
10const readUrlSchema = z.object({
11  url: z.string().url().describe("L'URL à lire"),
12});
13
14// Configuration des outils
15const tools = [
16  {
17    type: "function" as const,
18    function: {
19      name: "web_search",
20      description:
21        "Recherche sur le web. Retourne une liste de résultats avec titre, URL et extrait.",
22      parameters: zodToJsonSchema(webSearchSchema),
23    },
24  },
25  {
26    type: "function" as const,
27    function: {
28      name: "read_url",
29      description: "Lit le contenu d'une page web et retourne le texte.",
30      parameters: zodToJsonSchema(readUrlSchema),
31    },
32  },
33];
34
35// Prompt système
36const SYSTEM_PROMPT = `Tu es un agent de recherche. Tu aides l'utilisateur à trouver des informations fiables.
37
38Stratégie :
391. Commence par une recherche web pour trouver des sources pertinentes
402. Lis les pages les plus prometteuses (2-3 max)
413. Synthétise les informations en citant tes sources
42
43Règles :
44- Privilégie les sources officielles et récentes
45- Croise les informations quand possible
46- Indique clairement ce qui est factuel vs ton analyse
47- Si tu ne trouves pas l'info, dis-le honnêtement`;
48
49// Implémentation des outils (simplifiée)
50async function webSearch(query: string): Promise<any> {
51  // En prod, utilise une vraie API (Tavily, SerpAPI, Brave Search)
52  const response = await fetch("https://api.tavily.com/search", {
53    method: "POST",
54    headers: { "Content-Type": "application/json" },
55    body: JSON.stringify({
56      query,
57      api_key: process.env.TAVILY_API_KEY,
58      max_results: 5,
59    }),
60  });
61  return response.json();
62}
63
64async function readUrl(url: string): Promise<string> {
65  const response = await fetch(url);
66  const html = await response.text();
67  // Extraction basique (en prod, utilise mozilla/readability)
68  return html
69    .replace(/<script[^>]*>[\s\S]*?<\/script>/gi, "")
70    .replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "")
71    .replace(/<[^>]+>/g, " ")
72    .replace(/\s+/g, " ")
73    .trim()
74    .slice(0, 8000);
75}
76
77// Exécution
78async function executeToolCall(
79  name: string,
80  args: any
81): Promise<any> {
82  switch (name) {
83    case "web_search":
84      return webSearch(args.query);
85    case "read_url":
86      return readUrl(args.url);
87    default:
88      return { error: `Outil inconnu: ${name}` };
89  }
90}
91
92// Agent principal
93async function researchAgent(question: string): Promise<string> {
94  const client = new Mistral({ apiKey: process.env.MISTRAL_API_KEY });
95
96  const messages: any[] = [
97    { role: "system", content: SYSTEM_PROMPT },
98    { role: "user", content: question },
99  ];
100
101  const MAX_ITERATIONS = 10;
102
103  for (let i = 0; i < MAX_ITERATIONS; i++) {
104    console.log(`\n--- Itération ${i + 1} ---`);
105
106    const response = await client.chat.complete({
107      model: "mistral-small-latest",
108      messages,
109      tools,
110    });
111
112    const choice = response.choices?.[0];
113    if (!choice) throw new Error("Pas de réponse");
114
115    const assistantMessage = choice.message;
116    messages.push(assistantMessage);
117
118    if (choice.finishReason === "tool_calls" && assistantMessage.toolCalls) {
119      for (const toolCall of assistantMessage.toolCalls) {
120        const name = toolCall.function.name;
121        const args = JSON.parse(toolCall.function.arguments);
122
123        console.log(`🔧 ${name}:`, args);
124
125        const result = await executeToolCall(name, args);
126
127        // Tronquer les résultats trop longs
128        const content =
129          typeof result === "string"
130            ? result.slice(0, 4000)
131            : JSON.stringify(result).slice(0, 4000);
132
133        console.log(`📄 Résultat: ${content.slice(0, 200)}...`);
134
135        messages.push({
136          role: "tool",
137          toolCallId: toolCall.id,
138          name,
139          content,
140        });
141      }
142      continue;
143    }
144
145    console.log(`\n✅ Recherche terminée`);
146    return assistantMessage.content as string;
147  }
148
149  throw new Error("Limite d'itérations atteinte");
150}
151
152// Utilisation
153async function main() {
154  const answer = await researchAgent(
155    "Quelles sont les nouveautés de TypeScript 5.4 ?"
156  );
157  console.log("\n📝 Réponse finale :\n", answer);
158}
159
160main();

Exemple d'exécution

1--- Itération 1 ---
2🔧 web_search: { query: "TypeScript 5.4 new features 2024" }
3📄 Résultat: { results: [{ title: "Announcing TypeScript 5.4", url: "https://devblogs.microsoft.com/typescript/announcing-typescript-5-4/", snippet: "..." }...
4
5--- Itération 2 ---
6🔧 read_url: { url: "https://devblogs.microsoft.com/typescript/announcing-typescript-5-4/" }
7📄 Résultat: TypeScript 5.4 brings several improvements including...
8
9--- Itération 3 ---
10✅ Recherche terminée
11
12📝 Réponse finale :
13TypeScript 5.4 (mars 2024) apporte plusieurs nouveautés :
14
151. **Preserved Narrowing in Closures** : Le type narrowing est maintenant préservé dans les closures après la dernière assignation.
16
172. **NoInfer Utility Type** : Nouveau type utilitaire pour empêcher l'inférence sur certains paramètres génériques.
18
193. **Object.groupBy et Map.groupBy** : Support natif de ces nouvelles méthodes ES2024.
20
21Source : [Blog officiel TypeScript](https://devblogs.microsoft.com/typescript/announcing-typescript-5-4/)

L'agent a fait 3 itérations : une recherche, une lecture de page, puis la synthèse. Simple et efficace.

Aller plus loin

Les agents qu'on a construits jusqu'ici sont des "single-agent" : un seul LLM qui fait tout. Des architectures plus avancées existent :

Multi-agents : plusieurs agents spécialisés qui collaborent. Un "manager" distribue les tâches, des "workers" les exécutent.

Agents hiérarchiques : un agent de haut niveau planifie, des sous-agents exécutent chaque étape.

Agents avec réflexion : l'agent évalue sa propre performance et s'améliore.

On explorera ces patterns dans de futurs articles.

Conclusion

Un agent, c'est un LLM + une boucle + des outils. Le LLM raisonne et décide, la boucle orchestre, les outils agissent.

Ce qu'on a couvert :

  • La différence fondamentale entre chatbot et agent
  • Le pattern ReAct pour structurer le raisonnement
  • L'implémentation d'une boucle agentique
  • La planification pour les tâches complexes
  • La gestion de la mémoire court et long terme
  • Les garde-fous indispensables

Le function calling de l'article précédent était la brique de base. Les agents sont l'étage au-dessus : ils utilisent le function calling en autonomie pour accomplir des objectifs.

Dans le prochain article, on verra MCP (Model Context Protocol), un standard pour créer des outils réutilisables entre différents agents et applications.