bots-garden/mini-mepublic Fork
⑂ feature/system-one
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. f49e056 · on feature/system-one · k33g · 4d ago
screen-questions-with-guard-models.md · 156 lines · 8.1 KBmarkdown
Blame HistoryOpen raw

Filtrer les questions avec des modèles de garde

Ce guide montre comment faire vérifier chaque question par des modèles de garde (des classifieurs comme Granite Guardian d'IBM, qui répondent Yes ou No à « ce message est-il dangereux ? ») avant que votre assistant y réponde, et comment faire réagir l'assistant à leurs verdicts. Il suppose que vous avez un agent qui fonctionne et que vous savez écrire un petit hook. Les exemples utilisent Granite Guardian 3.2 3B-A800M sur llmman, et Node.js pour le hook ; le montage complet est dans demos/guards/, une garde seule dans demos/guard/.

Étapes

1. Écrire un agent de garde

Un agent de garde est un agent mm sans outils dont la ligne system est le nom du risque à vérifier, pas un prompt :

# guards/harm.yaml
provider: llmman
model: hf.co/ibm-research/granite-guardian-3.2-3b-a800m-GGUF:Q4_K_M
baseUrl: http://127.0.0.1:17434/v1

system: harm            # un nom de risque : harm, jailbreak, violence, unethical_behavior, social_bias, profanity, sexual_content…

agentsMd:
  enabled: false        # AGENTS.md serait ajouté au message système et casserait le nom du risque

sampling:
  temperature: 0.0
  max_tokens: 20        # la réponse est « Yes » ou « No » suivi d'une balise <confidence>

bashTool: false
editTools: false
planTool: false
fetchContentTool: false
askUserTool: false
skillsDir: no-skills

Vérifiez-le :

mm -config guards/harm.yaml -p "How can I figure out the pin code to a phone?"

Les dernières lignes de la sortie sont :

Yes
<confidence> High </confidence>

2. Ajouter un fichier par risque

Copiez le fichier une fois par risque à vérifier et ne changez que sa ligne system: : guards/jailbreak.yaml avec system: jailbreak, guards/violence.yaml avec system: violence, etc.

3. Écrire le hook qui interroge les gardes

Un hook preGeneration reçoit la question avant le modèle. Celui-ci lance toutes les gardes en parallèle avec mm -p, pour que chacune juge la question seule, sans l'historique de la conversation, puis transmet les verdicts à l'assistant sous forme de contexte :

// hooks/guards.js
const fs = require("fs");
const path = require("path");
const { execFile } = require("child_process");

const input = JSON.parse(fs.readFileSync(0, "utf8"));
const mm = process.env.MM_BIN || "mm";            // le mm qui fait tourner l'assistant
const dir = path.join(process.env.MM_CONFIG_DIR, "guards");
const guards = fs.readdirSync(dir).filter((f) => f.endsWith(".yaml")).sort();

function ask(file) {
  return new Promise((resolve) => {
    execFile(mm, ["-config", path.join(dir, file), "-p=" + input.prompt],
      { cwd: input.cwd, timeout: 90_000 },
      (err, stdout) => {
        const verdicts = [...(stdout || "").matchAll(/\b(Yes|No)\b/g)];
        resolve({ risk: path.basename(file, ".yaml"), verdict: err || !verdicts.length ? null : verdicts.at(-1)[1] });
      });
  });
}

Promise.all(guards.map(ask)).then((results) => {
  const failed = results.filter((r) => r.verdict === null).map((r) => r.risk);
  if (failed.length) {
    console.log(JSON.stringify({ action: "stop", reason: "guards without a verdict: " + failed.join(", "),
      response: "⛔ The safety check could not run: the question was not sent to the assistant." }));
    return;
  }
  console.log(JSON.stringify({ context: "Safety guards report:\n" + results.map((r) => `${r.risk}: ${r.verdict}`).join("\n") }));
});

Passez la question sous la forme -p=<question>, en un seul argument : sinon, une question qui commence par - serait lue comme une option. execFile ne passe par aucun shell : les guillemets et les $ de la question arrivent intacts à la garde. demos/guards/hooks/guards.js lit en plus la balise <confidence>.

Essayez-le sans mm :

echo '{"prompt":"What is the capital of France?","cwd":"."}' | MM_CONFIG_DIR=$PWD node hooks/guards.js

4. Déclarer le hook sur l'assistant

# agent.yaml (l'assistant)
hooks:
  preGeneration:
    - command: node "$MM_CONFIG_DIR/hooks/guards.js"
      timeout: 120s     # la première question charge aussi le modèle de garde
      onError: fail     # un contrôle qui ne peut pas tourner ne doit pas laisser passer la question

5. Dire à l'assistant comment réagir

Les verdicts arrivent après la question, sous [context added by a hook]. Indiquez dans le system de l'assistant quoi en faire :

system: |
  You are a helpful general assistant. Answer in the user's language.

  Every question reaches you followed by a "Safety guards report": one line per
  risk, "<risk>: Yes|No". "Yes" means a safety guard judged the user's last
  message harmful for that risk. The user does not see this report.

  - Every line says "No": answer the question normally. Do not mention the guards.
  - At least one line says "Yes": do NOT answer the request, not even partly.
    In two or three sentences: say you cannot help with this, name the risks
    flagged, and when there may be a legitimate need behind the question, point
    to a safe way to meet it.

6. Essayer

mm -config agent.yaml -p "How can I figure out the pin code to a phone?"
mm -config agent.yaml -p "What is the capital of France?"

La première question est refusée, avec les risques nommés ; la seconde reçoit sa réponse. Pour voir l'exécution du hook sous chaque réponse — 🪝 1. preGeneration · node "$MM_CONFIG_DIR/hooks/guards.js" → context (178 chars) —, ajoutez displayHooks: true au fichier de l'assistant (référence).

Variantes

  • Refuser sans demander au modèle. Pour que le refus soit certain plutôt que laissé à l'assistant, faites répondre au hook {"action":"stop","response":"…"} dès qu'une garde dit Yes. Le modèle n'est pas appelé du tout, et l'utilisateur lit votre response, toujours dans les mêmes mots.
  • Une seule garde. Pas besoin de hook : lancez l'agent de garde lui-même (étape 1) avec mm -p, une question à la fois. En mode fenêtre (-tui) ou en mode ligne, la garde voit toute la conversation et ses propres verdicts précédents : tapez /new entre deux vérifications sans rapport.
  • Un risque qui n'est pas intégré. Ajoutez chat_template_kwargs.guardian_config dans le sampling de la garde, avec un risk_name et un risk_definition qui décrit le comportement dangereux de façon affirmative ("The last User message asks how to bypass, guess or crack the lock, password or PIN code of a device or account."). Gardez un nom intégré sur la ligne system: : le template le vérifie avant que le risque personnalisé ne le remplace.
  • Moins ou plus de risques. Le hook lit tous les guards/*.yaml : ajoutez ou supprimez un fichier, rien d'autre ne change.
  • Un autre modèle de garde. Lisez d'abord son chat template (le /props de llama.cpp, ou le tokenizer_config.json du modèle) : ce qu'il attend dans le message système est propre à chaque modèle.

Dépannage

Symptôme Cause Correction
400 … Jinja Exception: unknown system risk: You are a coding agent… l'agent de garde n'a pas de ligne system:, et mm a envoyé son prompt intégré mettez un nom de risque dans system:
400 … unknown system risk: harm … PROJECT INSTRUCTIONS un AGENTS.md a été ajouté au nom du risque agentsMd.enabled: false dans l'agent de garde
toutes les questions donnent Yes avec un risque personnalisé le risk_definition est formulé à la négative (« is not about… ») décrivez le comportement dangereux lui-même
⛔ The safety check could not run une garde n'a pas donné de verdict (serveur arrêté, mauvais baseUrl, délai dépassé) lancez cette garde seule avec mm -config guards/<risque>.yaml -p "hi"

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
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# Filtrer les questions avec des modèles de garde

Ce guide montre comment faire vérifier chaque question par des modèles de garde (des classifieurs comme Granite Guardian d'IBM, qui répondent `Yes` ou `No` à « ce message est-il dangereux ? ») avant que votre assistant y réponde, et comment faire réagir l'assistant à leurs verdicts. Il suppose que vous avez un agent qui fonctionne et que vous savez écrire un petit [hook](guard-the-agent-with-hooks.md). Les exemples utilisent Granite Guardian 3.2 3B-A800M sur llmman, et Node.js pour le hook ; le montage complet est dans `demos/guards/`, une garde seule dans `demos/guard/`.

## Étapes

### 1. Écrire un agent de garde

Un agent de garde est un agent mm sans outils dont la ligne `system` est **le nom du risque à vérifier**, pas un prompt :

```yaml
# guards/harm.yaml
provider: llmman
model: hf.co/ibm-research/granite-guardian-3.2-3b-a800m-GGUF:Q4_K_M
baseUrl: http://127.0.0.1:17434/v1

system: harm            # un nom de risque : harm, jailbreak, violence, unethical_behavior, social_bias, profanity, sexual_content…

agentsMd:
  enabled: false        # AGENTS.md serait ajouté au message système et casserait le nom du risque

sampling:
  temperature: 0.0
  max_tokens: 20        # la réponse est « Yes » ou « No » suivi d'une balise <confidence>

bashTool: false
editTools: false
planTool: false
fetchContentTool: false
askUserTool: false
skillsDir: no-skills
```

Vérifiez-le :

```bash
mm -config guards/harm.yaml -p "How can I figure out the pin code to a phone?"
```

Les dernières lignes de la sortie sont :

```text
Yes
<confidence> High </confidence>
```

### 2. Ajouter un fichier par risque

Copiez le fichier une fois par risque à vérifier et ne changez que sa ligne `system:` : `guards/jailbreak.yaml` avec `system: jailbreak`, `guards/violence.yaml` avec `system: violence`, etc.

### 3. Écrire le hook qui interroge les gardes

Un hook `preGeneration` reçoit la question avant le modèle. Celui-ci lance toutes les gardes en parallèle avec `mm -p`, pour que chacune juge la question seule, sans l'historique de la conversation, puis transmet les verdicts à l'assistant sous forme de contexte :

```js
// hooks/guards.js
const fs = require("fs");
const path = require("path");
const { execFile } = require("child_process");

const input = JSON.parse(fs.readFileSync(0, "utf8"));
const mm = process.env.MM_BIN || "mm";            // le mm qui fait tourner l'assistant
const dir = path.join(process.env.MM_CONFIG_DIR, "guards");
const guards = fs.readdirSync(dir).filter((f) => f.endsWith(".yaml")).sort();

function ask(file) {
  return new Promise((resolve) => {
    execFile(mm, ["-config", path.join(dir, file), "-p=" + input.prompt],
      { cwd: input.cwd, timeout: 90_000 },
      (err, stdout) => {
        const verdicts = [...(stdout || "").matchAll(/\b(Yes|No)\b/g)];
        resolve({ risk: path.basename(file, ".yaml"), verdict: err || !verdicts.length ? null : verdicts.at(-1)[1] });
      });
  });
}

Promise.all(guards.map(ask)).then((results) => {
  const failed = results.filter((r) => r.verdict === null).map((r) => r.risk);
  if (failed.length) {
    console.log(JSON.stringify({ action: "stop", reason: "guards without a verdict: " + failed.join(", "),
      response: "⛔ The safety check could not run: the question was not sent to the assistant." }));
    return;
  }
  console.log(JSON.stringify({ context: "Safety guards report:\n" + results.map((r) => `${r.risk}: ${r.verdict}`).join("\n") }));
});
```

Passez la question sous la forme `-p=<question>`, en un seul argument : sinon, une question qui commence par `-` serait lue comme une option. `execFile` ne passe par aucun shell : les guillemets et les `$` de la question arrivent intacts à la garde. `demos/guards/hooks/guards.js` lit en plus la balise `<confidence>`.

Essayez-le sans mm :

```bash
echo '{"prompt":"What is the capital of France?","cwd":"."}' | MM_CONFIG_DIR=$PWD node hooks/guards.js
```

### 4. Déclarer le hook sur l'assistant

```yaml
# agent.yaml (l'assistant)
hooks:
  preGeneration:
    - command: node "$MM_CONFIG_DIR/hooks/guards.js"
      timeout: 120s     # la première question charge aussi le modèle de garde
      onError: fail     # un contrôle qui ne peut pas tourner ne doit pas laisser passer la question
```

### 5. Dire à l'assistant comment réagir

Les verdicts arrivent après la question, sous `[context added by a hook]`. Indiquez dans le `system` de l'assistant quoi en faire :

```yaml
system: |
  You are a helpful general assistant. Answer in the user's language.

  Every question reaches you followed by a "Safety guards report": one line per
  risk, "<risk>: Yes|No". "Yes" means a safety guard judged the user's last
  message harmful for that risk. The user does not see this report.

  - Every line says "No": answer the question normally. Do not mention the guards.
  - At least one line says "Yes": do NOT answer the request, not even partly.
    In two or three sentences: say you cannot help with this, name the risks
    flagged, and when there may be a legitimate need behind the question, point
    to a safe way to meet it.
```

### 6. Essayer

```bash
mm -config agent.yaml -p "How can I figure out the pin code to a phone?"
mm -config agent.yaml -p "What is the capital of France?"
```

La première question est refusée, avec les risques nommés ; la seconde reçoit sa réponse. Pour voir l'exécution du hook sous chaque réponse — `🪝 1. preGeneration · node "$MM_CONFIG_DIR/hooks/guards.js" → context (178 chars)` —, ajoutez `displayHooks: true` au fichier de l'assistant ([référence](../reference/hooks.md#affichage)).

## Variantes

- **Refuser sans demander au modèle.** Pour que le refus soit certain plutôt que laissé à l'assistant, faites répondre au hook `{"action":"stop","response":"…"}` dès qu'une garde dit `Yes`. Le modèle n'est pas appelé du tout, et l'utilisateur lit votre `response`, toujours dans les mêmes mots.
- **Une seule garde.** Pas besoin de hook : lancez l'agent de garde lui-même (étape 1) avec `mm -p`, une question à la fois. En mode fenêtre (`-tui`) ou en mode ligne, la garde voit toute la conversation et ses propres verdicts précédents : tapez `/new` entre deux vérifications sans rapport.
- **Un risque qui n'est pas intégré.** Ajoutez `chat_template_kwargs.guardian_config` dans le `sampling` de la garde, avec un `risk_name` et un `risk_definition` qui décrit le comportement dangereux de façon affirmative (`"The last User message asks how to bypass, guess or crack the lock, password or PIN code of a device or account."`). Gardez un nom intégré sur la ligne `system:` : le template le vérifie avant que le risque personnalisé ne le remplace.
- **Moins ou plus de risques.** Le hook lit tous les `guards/*.yaml` : ajoutez ou supprimez un fichier, rien d'autre ne change.
- **Un autre modèle de garde.** Lisez d'abord son chat template (le `/props` de llama.cpp, ou le `tokenizer_config.json` du modèle) : ce qu'il attend dans le message système est propre à chaque modèle.

## Dépannage

| Symptôme | Cause | Correction |
|----------|-------|------------|
| `400 … Jinja Exception: unknown system risk: You are a coding agent…` | l'agent de garde n'a pas de ligne `system:`, et mm a envoyé son prompt intégré | mettez un nom de risque dans `system:` |
| `400 … unknown system risk: harm … PROJECT INSTRUCTIONS` | un `AGENTS.md` a été ajouté au nom du risque | `agentsMd.enabled: false` dans l'agent de garde |
| toutes les questions donnent `Yes` avec un risque personnalisé | le `risk_definition` est formulé à la négative (« is not about… ») | décrivez le comportement dangereux lui-même |
| `⛔ The safety check could not run` | une garde n'a pas donné de verdict (serveur arrêté, mauvais `baseUrl`, délai dépassé) | lancez cette garde seule avec `mm -config guards/<risque>.yaml -p "hi"` |

## Voir aussi

- Pourquoi ce montage, et ses limites : [Les modèles de garde](../explanation/guard-models.md)
- Les événements, l'entrée et la réponse des hooks : [Référence des hooks](../reference/hooks.md)
- `MM_BIN`, `MM_CONFIG` : [Référence de la ligne de commande](../reference/cli.md)