Dans la première partie, nous avons vu les concepts fondamentaux des Agents LLM et la création de notre premier Agent chargé de récupérer la liste des fonctions impliquées dans une erreur.

A présent, nous allons créer le deuxième Agent, BugFixAgent chargé de la compréhension et de la correction de l’erreur.

BugFixAgent

Cet Agent sera chargé de:

  • donner une explication de l’erreur
  • corriger l’erreur en remplaçant le code des fonctions bugués

Il prendra en paramètre la stacktrace et les fonctions utilisateurs extraites par WalkCallStackAgent.

Il aura deux actions à sa disposition pour interagir avec le monde réel:

  • EXPLANATION: prend en paramètre les explications du bug et l’affiche dans le terminal
  • FIX_FUNCTION: prend en paramètre le nom et chemin d’une fonction et remplace son code par le nouveau code suggéré

Prompt

Le prompt de notre deuxième Agent sera beaucoup plus simple car il ne comportera pas de partie dynamique.

On va néanmoins utiliser toutes les techniques de Prompt Engineering pour maximiser les performances du prompt.

D’abord par un bloc d’instructions:

You are an excellent developer trying to solve a bug.

You will need to give a short explanation of the bug.
You will also provide a fix for the bug. 
Try to fix the bug as soon as it occur in the function call flow. 
You don't have the full code of the file, so you will need to make assumptions.
You may have to modify functions in different files to fix the bug.

Here is the stacktrace of the bug:
{stacktrace}

Here are the user functions involved in this bug:
### BEGIN USER FUNCTIONS
{userFunctions}
### END USER FUNCTIONS

Puis donnons lui la liste des actions disponibles dans un bloc d’actions:

You can use those actions to answer:

<Action name="EXPLANATION">
  <Parameter name="explanation">
    // short explanation of the bug
  </Parameter>
</Action>

<Action name="FIX_FUNCTION">
  <Parameter name="filepath">
    // path to the file where the fix is
  </Parameter>

  <Parameter name="functionName">
    // name of the function to fix
  </Parameter>

  <Parameter name="code">
    ```js
      // entire code of the fixed function here. do not add require statements
    ```
  </Parameter>
</Action>    

Code

Le code sera aussi plus simple car cet Agent n’a pas de boucle d’exécution.

  async run() {
    this.log("start fixing the bug");

    const prompt = await this.promptTemplate.format({
      stacktrace: this.stacktrace,
      userFunctions: this.userFunctions.join("\n"),
    });

    const answer = await this.model.call(prompt);

    const actions = extractActions(answer);

    for (const { action, parameters } of actions) {
      await this.executeAction(action, parameters);
    }
  }

Quand au code de executeAction, c’est plus ou moins le même que pour le premier agent.

  private async executeAction(action: string, parameters: any) {
    switch (action) {
      case "EXPLANATION":
        this.log(parameters.explanation);
        break;

      case "FIX_FUNCTION":
        this.log(
          `Fixing bug in function "${parameters.functionName}" in file "${parameters.filepath}"`
        );

        replaceFunctionCode(
          parameters.filepath,
          parameters.functionName,
          cleanCode(parameters.code)
        );
        break;

      default:
        throw new Error(`Unknown action: ${action}`);
    }
  }

La fonction replaceFunctionCode a été codé par ChatGPT, elle parcours l’AST de Javascript pour trouver la fonction à remplacer, remplace le code et sauvegarde le fichier.

Résolution du bug

L’Agent va répondre 3 actions:

  • explication du bug
  • fix de la fonction createTask dans example/app.js
  • fix de la fonction addTask dans example/database.js

Fix de la fonction createTask dans example/app.js

// Before
function createTask (req, res) {
  const newTask = req.body;
  verifyTask(newTask);
  database.addTask(newTask);
  res.status(201).send(`Task ${newTask.metadata.id} saved successfully`);
}

// After
function createTask (req, res) {
  const newTask = req.body;
  verifyTask(newTask);
  const savedTask = database.addTask(newTask);
  res.status(201).send(`Task ${savedTask.metadata.id} saved successfully`);
}

Fix de la fonction addTask dans example/database.js

// Before
function addTask(newTask) {
  const tasks = readTasksFromFile();
  tasks.push({ ...newTask, metadata: { id: generateId() } });
  writeTasksToFile(tasks);
}

// After
function addTask(newTask) {
  const tasks = readTasksFromFile();
  const taskWithId = { ...newTask, metadata: { id: generateId() } };
  tasks.push(taskWithId);
  writeTasksToFile(tasks);
  return taskWithId;
}

Explication du bug:

The bug is caused by trying to access the 'id' property of 'metadata' in the 'newTask' object in the 'createTask' function. 

However, the 'metadata' property is not defined in the 'newTask' object at this point in the code, hence the TypeError. 

The 'metadata' property is only added to the 'newTask' object in the 'addTask' function, which is called after trying to access 'newTask.metadata.id'.

Conclusion

Nous avons vu qu’il est possible de réaliser des tâches complexes en donnant les moyens à GPT4 d’agir sur le monde réel en lui permettant de lire et corriger des fonctions.

Il est possible d’imaginer des Agents capable d’écrire des test fonctionnels d’API ou de réaliser des refactoring dans la code base.

La principale difficulté est de réfléchir à la manière dont on ferait ces tâches pour l’expliquer à GPT, tout en lui fournissant les actions disponibles.

Aussi, en travaillant sur des dizaines de fichiers (code, modèle de données, tests), la question de la mémoire se pose également car la fenêtre de contexte ne pourra pas tout contenir.

Bref, autant de sujets passionnants qu’il reste à explorer 😉

Le code des Agents développés dans cet article est disponible sur Github: https://github.com/Aschen/llm-experiments/tree/master/article-agent-llm

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Trending