bots-garden/mini-mepublic Fork
⑂ feature/decider-no-tools
Commits
⬇ Clone ▾
git clone https://git.rickub.com/bots-garden/mini-me.git
git clone ssh://git@rickub.com/bots-garden/mini-me.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP
🛟 Updated. fd5d2a7 · on feature/decider-no-tools · k33g · 6d ago
write-a-wasm-tool.md · 89 lines · 4.4 KBmarkdown
Blame HistoryOpen raw

Écrire un outil personnalisé sous forme de module WebAssembly

Ce guide montre comment donner à l'agent un outil qui s'exécute comme module WebAssembly à l'intérieur de mm, dans un bac à sable : le module ne voit aucun fichier, aucune variable d'environnement et aucun réseau, sauf les dossiers du projet que vous lui ouvrez. Utilisez-le à la place d'un outil commande quand l'outil ne doit pas avoir vos droits — il lit un dossier, ou calcule quelque chose à partir de ses seuls arguments. Il suppose que TinyGo est installé et que vous avez un fichier d'agent YAML.

Étapes

  1. Écrivez l'outil comme un programme Go ordinaire. Il lit les arguments de l'appel en un seul objet JSON sur stdin, et ce qu'il écrit sur stdout ou stderr est ce que le modèle reçoit ; sortir avec un code non nul marque l'appel comme échoué :

    // tools-src/search_notes/main.go
    package main
    
    import (
    	"encoding/json"
    	"fmt"
    	"os"
    	"path"
    	"strings"
    )
    
    func main() {
    	var in struct {
    		Word string `json:"word"`
    	}
    	if err := json.NewDecoder(os.Stdin).Decode(&in); err != nil {
    		fmt.Fprintln(os.Stderr, "bad input:", err)
    		os.Exit(2)
    	}
    	entries, _ := os.ReadDir("notes")
    	for _, e := range entries {
    		data, _ := os.ReadFile(path.Join("notes", e.Name()))
    		for i, line := range strings.Split(string(data), "\n") {
    			if strings.Contains(strings.ToLower(line), strings.ToLower(in.Word)) {
    				fmt.Printf("%s:%d: %s\n", e.Name(), i+1, line)
    			}
    		}
    	}
    }
    
  2. Compilez-le en module WASI :

    tinygo build -o tools/search_notes.wasm -target=wasip1 -no-debug ./tools-src/search_notes
    
  3. Déclarez-le dans le fichier d'agent avec wasm: à la place de command:, et ouvrez les dossiers dont il a besoin — read: en lecture seule, write: en lecture-écriture :

    tools:
      - name: search_notes
        description: Find the lines of the notes that mention a word.
        parameters:
          word: { type: string, description: "One word, e.g. wazero" }
        wasm: ./tools/search_notes.wasm
        read: [notes]
    

    Le chemin du module est relatif au fichier d'agent ; les dossiers sont relatifs au répertoire de travail, et le module les ouvre sous le même nom (notes/…).

  4. Demandez-le avec vos propres mots, depuis le répertoire du projet :

    mm -p "Which notes mention TinyGo?"
    

    Le terminal affiche le module et le JSON qu'il a reçu, puis sa sortie :

    🧰 search_notes: search_notes.wasm {"word":"TinyGo"}
       │ tinygo.md:1: # TinyGo
       │ wazero.md:4: It runs WASI modules, such as the ones TinyGo builds with -target=wasip1.
    

Variantes

  • Un outil qui écrit. write: [out] lui permet de créer et de modifier des fichiers dans out/ seulement. Un dossier qu'il peut seulement lire va dans read: ; un dossier absent des deux listes n'existe pas pour lui.
  • Un calcul pur. Omettez read et write : le module ne voit alors aucun fichier.
  • Les chemins dans le module. Nettoyez-les avant d'ouvrir : la couche WASI de TinyGo refuse ./notes/a.md et .. — path.Join("notes", name) donne notes/a.md. Un nom comme ../agent.yaml devient alors agent.yaml, qui n'est pas monté : le bac à sable le tient à l'écart.
  • Paramètres typés, valeurs par défaut. Le JSON contient chaque valeur dans son type déclaré ("top": 5, "exact": true), le default d'un paramètre optionnel que l'appel a omis, et aucune clé pour un paramètre optionnel sans valeur par défaut.
  • Un calcul long. Ajoutez timeout: 2m ; au-delà, le module est arrêté. Sans lui, bashTimeout s'applique.
  • Demander avant chaque appel. yolo: false demande pour un outil wasm comme pour un outil commande.
  • Un autre langage. Tout module commande WASI preview 1 devrait s'exécuter de la même façon ; seuls des modules TinyGo ont été essayés.

Voir aussi

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# Écrire un outil personnalisé sous forme de module WebAssembly

Ce guide montre comment donner à l'agent un outil qui s'exécute comme module WebAssembly à l'intérieur de mm, dans un bac à sable : le module ne voit aucun fichier, aucune variable d'environnement et aucun réseau, sauf les dossiers du projet que vous lui ouvrez. Utilisez-le à la place d'un [outil commande](declare-a-custom-tool.md) quand l'outil ne doit pas avoir vos droits — il lit un dossier, ou calcule quelque chose à partir de ses seuls arguments. Il suppose que [TinyGo](https://tinygo.org) est installé et que vous avez un fichier d'agent YAML.

## Étapes

1. Écrivez l'outil comme un programme Go ordinaire. Il lit les arguments de l'appel en un seul objet JSON sur stdin, et ce qu'il écrit sur stdout ou stderr est ce que le modèle reçoit ; sortir avec un code non nul marque l'appel comme échoué :

   ```go
   // tools-src/search_notes/main.go
   package main

   import (
   	"encoding/json"
   	"fmt"
   	"os"
   	"path"
   	"strings"
   )

   func main() {
   	var in struct {
   		Word string `json:"word"`
   	}
   	if err := json.NewDecoder(os.Stdin).Decode(&in); err != nil {
   		fmt.Fprintln(os.Stderr, "bad input:", err)
   		os.Exit(2)
   	}
   	entries, _ := os.ReadDir("notes")
   	for _, e := range entries {
   		data, _ := os.ReadFile(path.Join("notes", e.Name()))
   		for i, line := range strings.Split(string(data), "\n") {
   			if strings.Contains(strings.ToLower(line), strings.ToLower(in.Word)) {
   				fmt.Printf("%s:%d: %s\n", e.Name(), i+1, line)
   			}
   		}
   	}
   }
   ```

2. Compilez-le en module WASI :

   ```bash
   tinygo build -o tools/search_notes.wasm -target=wasip1 -no-debug ./tools-src/search_notes
   ```

3. Déclarez-le dans le fichier d'agent avec `wasm:` à la place de `command:`, et ouvrez les dossiers dont il a besoin — `read:` en lecture seule, `write:` en lecture-écriture :

   ```yaml
   tools:
     - name: search_notes
       description: Find the lines of the notes that mention a word.
       parameters:
         word: { type: string, description: "One word, e.g. wazero" }
       wasm: ./tools/search_notes.wasm
       read: [notes]
   ```

   Le chemin du module est relatif au fichier d'agent ; les dossiers sont relatifs au répertoire de travail, et le module les ouvre sous le même nom (`notes/…`).

4. Demandez-le avec vos propres mots, depuis le répertoire du projet :

   ```bash
   mm -p "Which notes mention TinyGo?"
   ```

   Le terminal affiche le module et le JSON qu'il a reçu, puis sa sortie :

   ```
   🧰 search_notes: search_notes.wasm {"word":"TinyGo"}
      │ tinygo.md:1: # TinyGo
      │ wazero.md:4: It runs WASI modules, such as the ones TinyGo builds with -target=wasip1.
   ```

## Variantes

- **Un outil qui écrit.** `write: [out]` lui permet de créer et de modifier des fichiers dans `out/` seulement. Un dossier qu'il peut seulement lire va dans `read:` ; un dossier absent des deux listes n'existe pas pour lui.
- **Un calcul pur.** Omettez `read` et `write` : le module ne voit alors aucun fichier.
- **Les chemins dans le module.** Nettoyez-les avant d'ouvrir : la couche WASI de TinyGo refuse `./notes/a.md` et `..` — `path.Join("notes", name)` donne `notes/a.md`. Un nom comme `../agent.yaml` devient alors `agent.yaml`, qui n'est pas monté : le bac à sable le tient à l'écart.
- **Paramètres typés, valeurs par défaut.** Le JSON contient chaque valeur dans son type déclaré (`"top": 5`, `"exact": true`), le `default` d'un paramètre optionnel que l'appel a omis, et aucune clé pour un paramètre optionnel sans valeur par défaut.
- **Un calcul long.** Ajoutez `timeout: 2m` ; au-delà, le module est arrêté. Sans lui, `bashTimeout` s'applique.
- **Demander avant chaque appel.** `yolo: false` demande pour un outil wasm comme pour un outil commande.
- **Un autre langage.** Tout module commande WASI preview 1 devrait s'exécuter de la même façon ; seuls des modules TinyGo ont été essayés.

## Voir aussi

- Un exemple complet avec quatre modules, un dossier en lecture et un dossier en écriture : `demos/wasm-tools/`
- Chaque clé et chaque erreur : [référence de la configuration](../reference/configuration.md#clés-de-tools) et [outils déclarés](../reference/tools.md#outils-déclarés)
- Ce que le bac à sable permet et ce qu'il coûte : [Outils déclarés](../explanation/declared-tools.md#outils-wasm)