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 terminalFIX_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
dansexample/app.js
- fix de la fonction
addTask
dansexample/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