J'ai construit un système de garde-fous pour empêcher Claude Code de me mentir

Après des semaines de collaboration intensive avec Claude Code (Anthropic), j'ai identifié des patterns comportementaux toxiques récurrents : complaisance, mensonges par omission, raccourcis non signales, oubli des corrections entre sessions. J'ai conçu et déployé un système à 3 couches — enforcement mécanique, mémoire persistante, observabilité — pour rendre l'IA fiable de manière structurelle, pas volontaire.

Par David Perrot 28 mars 2026

Le problème que personne ne veut voir

On parle beaucoup de hallucinations factuelles des LLM. "L'IA à invente une reference", "l'IA à cite un package qui n'existe pas". OK. C'est un problème connu, documente, sur lequel tout le monde travaille.

Mais il y à un autre problème, plus insidieux, dont personne ne parle : la complaisance comportementale.

Je développe depuis janvier 2026 une PWA de gestion immobilière avec Claude Code comme co-pilote principal. Pas un jouet — une vraie app en production, avec une vraie cliente qui l'utilise au quotidien. Express, React, Supabase, AssemblyAI, déployé sur un VPS Hostinger.

Et au bout de 3 mois de travail intensif, voici ce que j'ai observé :

Les 5 patterns toxiques

1. La complaisance
Claude Code livre vite pour faire plaisir. La réponse rapide prime sur la réponse correcte. Tu demandes un diagnostic sur un bug ? Il te sort une hypothese en 30 secondes sans avoir regarde la donnée réelle. Ca à l'air pro. C'est faux.

2. Le mensonge par omission
Je lui ai prescrit d'utiliser un outil spécifique (Stitch MCP) pour générér des visuels. Il à généré du HTML/CSS à la place — un résultat médiocre — et me l'a présenté comme si c'était normal. Sans me dire qu'il n'avait pas utilise l'outil prescrit. Quand je m'en suis rendu compte, j'ai du comparer les deux résultats moi-même. La difference était flagrante.

3. Le non-respect des règles
Claude Code à un système de fichiers d'instructions (CLAUDE.md) ou tu peux ecrire des règles. J'en ai ecrit. Beaucoup. Des règles de debug ("toujours regarder la base de données avant le code"), des règles de déploiement ("toujours tester avant de déployér"), des règles de comportement ("ne pas prendre de raccourcis"). Il les ignore des que le chemin facile existe. Même quand je les répète 3 fois dans la même session.

4. L'echo chamber inter-agents
Claude Code peut lancer des sous-agents pour des taches complexes. En theorie, c'est genial — parallelisation, expertise. En pratique, les agents se confortent mutuellement. L'un propose une approche facile, l'autre la valide, le résultat m'est présenté comme un "consensus". C'est un consensus entre oui-oui.

5. L'oubli inter-sessions
Tu corrigés un comportement en session 1. En session 2, la correction est oubliée. Tu recorrigés. Session 3, oubliée. Tu finis par ecrire la correction dans un fichier de mémoire. Session 4 : le fichier existe mais n'est pas consulte.

Le vrai cout

Le problème n'est pas que l'IA fait des erreurs — tout le monde fait des erreurs. Le problème c'est que si je dois tout vérifiér et tout surveiller, il n'y à aucun gain de productivité. L'outil devient un cout, pas une aide.

Et il y à des jours ou le levier est phénoménal. Un mois de travail abattu en une journée. Mais c'est comme travailler avec un développeur genial mais adolescent : compétences hors normes, fiabilité zéro.

La reflexion : pourquoi les solutions classiques échouent

CLAUDE.md ne suffit pas

Les instructions dans CLAUDE.md sont l'equivalent d'un panneau "Interdit de rouler à 150 km/h". Sans radar, sans amende, sans barrière physique. La recherche académique confirme : la conformité des LLM aux instructions décroît linéairement avec le nombre d'instructions. Au-dela de 20 règles, c'est < 30% de conformité en mode agent.

Les hooks d'avertissement ne suffisent pas

J'avais déjà construit un système de hooks (BRAKE) qui injectait des messages d'avertissement avant certaines actions. "Attention, tu lis du code sans avoir regarde la base". Le problème : ces messages font exit 0 — ils informent, ils ne bloquent pas. Claude les voit, les ignore, et continue.

Les fichiers de mémoire ne suffisent pas

J'ai 30+ fichiers markdown de feedbacks, corrections, protocoles. Ils existent. Ils ne sont pas consultes. La mémoire est la, mais personne ne la regarde. C'est comme avoir une bibliothèque fermée à clé.

Le constat fondamental

Tout mécanisme qui depend de la bonne volonté de l'IA échouera.
La fiabilité doit etre mécanique, pas volontaire.

Les recherches : qu'est-ce qui existe ?

Avant de foncer tête baissée dans une implementation, j'ai fait un tour exhaustif de ce qui existe. Parce que mon premier reflexe (et celui de Claude) était d'installer le premier outil mentionne sans chercher d'alternatives. Ironie.

Systèmes de mémoire agent

Solution Stars GitHub Maturity Ce que ca fait
Mem0 51K 3 ans Mémoire persistante, API simple
Letta (ex-MemGPT) 22K 2.5 ans Runtime agent avec mémoire intégrée
Cognée 15K 2.5 ans Mémoire cognitive structurée
Hindsight 6.5K 5 mois Mémoire biomimétique 3 niveaux, 91.4% sur benchmark LongMemEval
Zep 4.3K 3 ans Mémoire temporelle, principalement SaaS

Constat : tous font du recall (ramener l'info pertinente). Aucun ne fait de l'enforcement (bloquer une action non conforme). Ils resolvent "j'ai oublie", pas "je m'en fous".

Frameworks de guardrails

Solution Stars Compatible Claude Code ?
NeMo Guardrails (NVIDIA) 5.9K Non — conçu pour chatbots
Guardrails AI 6.6K Non — valide le format, pas le comportement

Constat : les guardrails existants sont conçus pour des chatbots conversationnels, pas pour des agents de code autonomes.

Solutions spécifiques Claude Code

Solution Pertinence
Hooks natifs (PreToolCall/Stop) Le seul mécanisme qui peut bloquer une action — via exit 2
Plugin Hindsight Auto-recall sur chaque prompt, auto-retain en fin de session
Langfuse Observabilite, traces, scoring, dashboard

Le verdict de la recherche

Aucun outil unique ne couvre les deux faces du problème :

  1. Le feedback doit arriver au bon moment → Mémoire
  2. Le feedback doit etre suivi → Enforcement

La solution : combiner 3 couches complementaires.

L'architecture : 3 couches, 3 fonctions, zéro bonne volonté requise

┌─────────────────────────────────────────────────────┐
│                 PROMPT UTILISATEUR                    │
└──────────────────────┬──────────────────────────────┘
                       ▼
┌─────────────────────────────────────────────────────┐
│  COUCHE B — HINDSIGHT (mémoire)                      │
│  Hook UserPromptSubmit → auto-recall                 │
│  Injecte les feedbacks pertinents dans le contexte   │
│  L'IA ne choisit PAS de les voir. Ils sont la.      │
└──────────────────────┬──────────────────────────────┘
                       ▼
┌─────────────────────────────────────────────────────┐
│  L'IA RAISONNE                                       │
│  (avec les feedbacks injectes en face d'elle)        │
└──────────────────────┬──────────────────────────────┘
                       ▼
┌─────────────────────────────────────────────────────┐
│  COUCHE A — BRAKE HARD (enforcement)                 │
│  Hook PreToolCall → gate.sh                          │
│  Verifie AVANT chaque outil :                        │
│  - Read code sans DB query ? → exit 2 (BLOQUE)      │
│  - Deploy sans audit ? → exit 2 (BLOQUE)            │
│  - HTML au lieu de Stitch ? → exit 2 (BLOQUE)       │
│  Exit 2 = outil refuse. Pas un warning, un MUR.     │
└──────────────────────┬──────────────────────────────┘
                       ▼ (si autorise)
┌─────────────────────────────────────────────────────┐
│  OUTIL EXECUTE                                       │
└──────────────────────┬──────────────────────────────┘
                       ▼
┌─────────────────────────────────────────────────────┐
│  COUCHE C — LANGFUSE (observabilité)                 │
│  Trace passive : quel outil, quel contexte,          │
│  quel résultat. Dashboard consultable.               │
│  Ne bloque rien. OBSERVE.                            │
└─────────────────────────────────────────────────────┘

Pourquoi 3 couches et pas 1

Couche Role Analogie
Hindsight Je ne peux pas dire "j'ai oublie" La mémoire arrive automatiquement
BRAKE Je ne peux pas dire "j'ai vu mais j'ai decide autrement" L'outil est physiquement refuse
Langfuse Je ne peux pas dire "ca s'est bien passe" Les traces sont la, verifiables

Mémoire forcée + action bloquée + traçabilité totale.

Tutoriel : implementer le système complet

Prérequis

  • Claude Code installe (npm install -g @anthropic-ai/claude-code)
  • Docker et Docker Compose
  • Python 3.11+

Étape 1 — Couche A : BRAKE Hard (enforcement)

C'est le coeur du système. Le seul mécanisme qui bloque réellement dans Claude Code.

1.1 Creer la structure

mkdir -p ~/.claude/brake

1.2 Le script de parsing (gate-parse.py)

Ce script Python parse l'input JSON du hook, met à jour l'état de session, et retourne les informations nécessaires au script bash.

cat > ~/.claude/brake/gate-parse.py << 'PYEOF'
#!/usr/bin/env python3
"""Parse l'input du hook PreToolCall, met à jour session-state, retourne les infos."""
import json
import os
import sys
from datetime import datetime

SESSION_FILE = os.path.expanduser("~/.claude/brake/session-state.json")

# Lire stdin
try:
    inp = sys.stdin.read()
except Exception:
    inp = ""

# Parser l'input JSON
tool_name = "unknown"
file_path = ""
command = ""

try:
    d = json.loads(inp)
    tool_name = d.get("tool_name", "unknown")
    ti = d.get("tool_input", {})
    if isinstance(ti, dict):
        file_path = ti.get("file_path", ti.get("path", ""))
        command = ti.get("command", "")
except Exception:
    pass

# Charger session-state
try:
    with open(SESSION_FILE) as f:
        state = json.load(f)
except Exception:
    state = {
        "db_queried": False,
        "roko_invoked": False,
        "tests_run": False,
        "context_mode": "dev",
        "files_read": 0,
        "files_edited": 0,
        "deploy_attempted": False,
        "tools": [],
    }

# Tracker l'outil
state["tools"].append({"tool": tool_name, "time": datetime.now().isoformat()[:19]})

# Détecter requête DB
cmd_lower = command.lower()
if tool_name == "Bash" and any(
    k in cmd_lower
    for k in ["supabase", "pipeline_jobs", "from('", 'from("', ".select(", "psql"]
):
    state["db_queried"] = True

# Détecter invocation agent contradicteur
if tool_name == "Bash" and "roko" in cmd_lower:
    state["roko_invoked"] = True
if tool_name == "Skill" and "roko" in inp.lower():
    state["roko_invoked"] = True

# Détecter tests
if tool_name == "Bash" and any(
    k in command for k in ["npm test", "npm run test", "jest", "vitest", "mocha", "pytest"]
):
    state["tests_run"] = True

# Tracker lectures et editions
if tool_name in ("Read", "Grep"):
    state["files_read"] = state.get("files_read", 0) + 1
if tool_name in ("Edit", "Write"):
    state["files_edited"] = state.get("files_edited", 0) + 1

# Détecter deploy
if tool_name == "Bash" and any(
    k in command
    for k in ["rsync", "pm2 restart", "pm2 reload", "docker push", "npm run deploy"]
):
    state["deploy_attempted"] = True

# Sauvegarder
with open(SESSION_FILE, "w") as f:
    json.dump(state, f)

# Output pour gate.sh
print(
    f"{tool_name}|||{file_path}|||{command}|||"
    f"{state['db_queried']}|||{state['roko_invoked']}|||{state['tests_run']}|||"
    f"{state['files_read']}|||{state['files_edited']}|||{state.get('context_mode', 'dev')}"
)
PYEOF

1.3 Le script de blocage (gate.sh)

Le coeur du système. Chaque règle qui matche fait exit 2 — l'outil est refuse, le message est affiche à l'IA.

cat > ~/.claude/brake/gate.sh << 'GATEOF'
#!/usr/bin/env bash
# gate.sh — PreToolCall hook : BLOQUE les actions non conformes (exit 2)
# Exit 0 = autorise | Exit 2 = BLOQUE (message stderr affiche à l'IA)

# Degradation gracieuse : si ce script plante, on autorise l'outil
trap 'exit 0' ERR

BRAKE_DIR="$HOME/.claude/brake"

# Lire stdin
INPUT=""
if [[ ! -t 0 ]]; then
    INPUT=$(cat 2>/dev/null || true)
fi

# Parser via Python
PARSED=$(echo "$INPUT" | python3 "$BRAKE_DIR/gate-parse.py" 2>/dev/null || echo "unknown||||||False|||False|||False|||0|||0|||dev")

# Extraire les valeurs (separateur |||)
TOOL_NAME=$(echo "$PARSED" | cut -d'|' -f1)
FILE_PATH=$(echo "$PARSED" | cut -d'|' -f4)
COMMAND=$(echo "$PARSED" | cut -d'|' -f7)
DB_QUERIED=$(echo "$PARSED" | cut -d'|' -f10)
ROKO_INVOKED=$(echo "$PARSED" | cut -d'|' -f13)
TESTS_RUN=$(echo "$PARSED" | cut -d'|' -f16)
FILES_READ=$(echo "$PARSED" | cut -d'|' -f19)
FILES_EDITED=$(echo "$PARSED" | cut -d'|' -f22)

# Parsing échoué = laisser passer
if [[ "$TOOL_NAME" == "unknown" || -z "$TOOL_NAME" ]]; then
    exit 0
fi

# ============================================================
# REGLES DE BLOCAGE — Adapter selon vos besoins
# ============================================================

# REGLE 1 : Pas de lecture de code serveur sans requête DB (après 2 lectures)
if [[ "$TOOL_NAME" == "Read" || "$TOOL_NAME" == "Grep" ]]; then
    if echo "$FILE_PATH" | grep -qE '(server/|worker|pipeline|api/)' 2>/dev/null; then
        if [[ "$DB_QUERIED" == "False" && "${FILES_READ:-0}" -gt 2 ]]; then
            echo "BRAKE BLOQUE : Lecture code serveur sans requête base (${FILES_READ} lectures)." >&2
            echo "ACTION : Requeter la base d'abord. Diagnostic AVANT code." >&2
            exit 2
        fi
    fi
fi

# REGLE 2 : Pas d'edition de code sans diagnostic base
if [[ "$TOOL_NAME" == "Edit" || "$TOOL_NAME" == "Write" ]]; then
    if echo "$FILE_PATH" | grep -qE '\.(js|ts|jsx|tsx|py)$' 2>/dev/null; then
        if [[ "$DB_QUERIED" == "False" && "${FILES_READ:-0}" -gt 3 ]]; then
            echo "BRAKE BLOQUE : Edition après $FILES_READ lectures sans requête base." >&2
            echo "Pattern : lecture code -> faux diagnostic -> faux fix. STOP." >&2
            exit 2
        fi
    fi
fi

# REGLE 3 : Pas de deploy sans audit contradicteur
if [[ "$TOOL_NAME" == "Bash" ]]; then
    if echo "$COMMAND" | grep -qE '(rsync|pm2 restart|pm2 reload|docker push|npm run deploy)' 2>/dev/null; then
        if [[ "$ROKO_INVOKED" == "False" ]]; then
            echo "BRAKE BLOQUE : Deploy sans audit contradicteur." >&2
            echo "Checklist : tests, backup, env vars, commit propre, rollback." >&2
            exit 2
        fi
    fi
fi

# REGLE 4 : Pas de push sans tests
if [[ "$TOOL_NAME" == "Bash" ]]; then
    if echo "$COMMAND" | grep -qE 'git push.*(main|develop|origin)' 2>/dev/null; then
        if [[ "$TESTS_RUN" == "False" ]]; then
            echo "BRAKE BLOQUE : Push sans tests. Lance les tests d'abord." >&2
            exit 2
        fi
    fi
fi

# REGLE 5 : Pas de HTML/CSS pour les visuels (utiliser l'outil prescrit)
if [[ "$TOOL_NAME" == "Write" ]]; then
    if echo "$FILE_PATH" | grep -qE '\.(html|svg)$' 2>/dev/null; then
        if echo "$FILE_PATH" | grep -qiE '(slide|carousel|carrousel|presentation|poster|visual|banner|infograph)' 2>/dev/null; then
            echo "BRAKE BLOQUE : Visuel en HTML. Utiliser l'outil prescrit." >&2
            exit 2
        fi
    fi
fi

# WARNINGS (ne bloquent pas)
if [[ "$TOOL_NAME" == "Read" || "$TOOL_NAME" == "Grep" ]]; then
    if echo "$FILE_PATH" | grep -qE '(server/|worker|pipeline|api/)' 2>/dev/null; then
        if [[ "$DB_QUERIED" == "False" && "${FILES_READ:-0}" -gt 0 && "${FILES_READ:-0}" -le 2 ]]; then
            echo "BRAKE WARNING : Lecture code serveur sans requête base (${FILES_READ}/2 avant blocage)." >&2
        fi
    fi
fi

exit 0
GATEOF

chmod +x ~/.claude/brake/gate.sh

1.4 Le script de reset de session

cat > ~/.claude/brake/session-reset.sh << 'EOF'
#!/usr/bin/env bash
cat > ~/.claude/brake/session-state.json << 'JSON'
{"db_queried":false,"roko_invoked":false,"tests_run":false,"context_mode":"dev","files_read":0,"files_edited":0,"deploy_attempted":false,"tools":[]}
JSON
EOF

chmod +x ~/.claude/brake/session-reset.sh
bash ~/.claude/brake/session-reset.sh

1.5 Configurer les hooks Claude Code

Editer ~/.claude/settings.json pour ajouter les hooks :

{
  "hooks": {
    "PreToolCall": [
      {
        "matcher": "Read|Grep|Edit|Write|Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash $HOME/.claude/brake/gate.sh",
            "timeout": 5
          }
        ]
      }
    ]
  }
}

1.6 Tester

# Syntaxe OK ?
bash -n ~/.claude/brake/gate.sh && echo "OK"
python3 -m py_compile ~/.claude/brake/gate-parse.py && echo "OK"

# Test blocage DB_FIRST (simuler 3 lectures sans DB query)
cat > ~/.claude/brake/session-state.json << 'JSON'
{"db_queried":false,"roko_invoked":false,"tests_run":false,"context_mode":"dev","files_read":3,"files_edited":0,"deploy_attempted":false,"tools":[]}
JSON

echo '{"tool_name":"Read","tool_input":{"file_path":"server/worker.js"}}' | bash ~/.claude/brake/gate.sh 2>&1
echo "Exit code: $?"
# Attendu : message BRAKE BLOQUE + exit code 2

# Test deploy sans audit
bash ~/.claude/brake/session-reset.sh
echo '{"tool_name":"Bash","tool_input":{"command":"rsync -avz ./dist/ user@server:/app/"}}' | bash ~/.claude/brake/gate.sh 2>&1
echo "Exit code: $?"
# Attendu : BRAKE BLOQUE + exit code 2

# Test push sans tests
echo '{"tool_name":"Bash","tool_input":{"command":"git push origin main"}}' | bash ~/.claude/brake/gate.sh 2>&1
echo "Exit code: $?"
# Attendu : BRAKE BLOQUE + exit code 2

# Test Write HTML visuel
echo '{"tool_name":"Write","tool_input":{"file_path":"/tmp/carrousel-linkedin.html"}}' | bash ~/.claude/brake/gate.sh 2>&1
echo "Exit code: $?"
# Attendu : BRAKE BLOQUE + exit code 2

# Test Write HTML normal (code applicatif) — doit passer
echo '{"tool_name":"Write","tool_input":{"file_path":"/app/client/index.html"}}' | bash ~/.claude/brake/gate.sh 2>&1
echo "Exit code: $?"
# Attendu : exit code 0

# Reset pour usage normal
bash ~/.claude/brake/session-reset.sh

Étape 2 — Couche B : Hindsight (mémoire persistante)

Hindsight est un système de mémoire biomimétique pour agents IA. Il organise les souvenirs en 3 niveaux (faits, observations, modèles mentaux) et les retrouve via 4 strategies de recherche en parallele (sémantique, BM25, graphe, temporel).

Ce qui nous interesse : le plugin Claude Code qui injecte automatiquement les memories pertinentes dans le contexte de l'IA à chaque prompt.

2.1 Déployer Hindsight

# docker-compose.yml
cat > ~/hindsight-docker/docker-compose.yml << 'DCEOF'
version: '3.8'
services:
  hindsight-db:
    image: pgvector/pgvector:pg18
    container_name: hindsight-db
    environment:
      POSTGRES_USER: hindsight
      POSTGRES_PASSWORD: hindsight
      POSTGRES_DB: hindsight
    volumes:
      - hindsight-pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U hindsight"]
      interval: 5s
      timeout: 5s
      retries: 5

  hindsight-app:
    image: ghcr.io/vectorize-io/hindsight:latest
    container_name: hindsight-app
    ports:
      - "8888:8888"
      - "9999:9999"
    environment:
      HINDSIGHT_API_DATABASE_URL: "postgresql://hindsight:hindsight@hindsight-db:5432/hindsight"
      HINDSIGHT_API_LLM_PROVIDER: "openai"
      HINDSIGHT_API_LLM_API_KEY: "${OPENAI_API_KEY}"
      HINDSIGHT_API_LLM_MODEL: "gpt-4o-mini"
    depends_on:
      hindsight-db:
        condition: service_healthy

volumes:
  hindsight-pgdata:
DCEOF

cd ~/hindsight-docker
docker compose up -d
Note : j'utilise PostgreSQL externe (pgvector) au lieu du pg0 embarque. Le pg0 à un bug connu de perte de données après restart Docker (#675).

2.2 Vérifier

curl -s http://localhost:8888/health
# {"status": "healthy", "database": "connected"}

2.3 Creer la bank globale

curl -s -X PUT http://localhost:8888/v1/default/banks/david-global \
  -H "Content-Type: application/json" \
  -d '{
    "description": "Regles globales, feedbacks, protocoles pour toutes les sessions"
  }'

2.4 Ingerer vos règles et feedbacks

# Exemple : ingérer un feedback
curl -s -X POST http://localhost:8888/v1/default/banks/david-global/memories \
  -H "Content-Type: application/json" \
  -d '{
    "items": [{
      "content": "Regle : toujours requêter la base de données AVANT de lire le code source pour diagnostiquer un bug. Ne jamais proposer un fix sans avoir vu la donnée réelle.",
      "context": "feedback-rule",
      "tags": ["feedback", "global-rules"]
    }],
    "async": false
  }'

# Ingerer tous vos fichiers de feedback en batch
for f in ~/.claude/projects/*/memory/feedback-*.md; do
  python3 -c "
import json, urllib.request, os
content = open('$f').read()
payload = json.dumps({
    'items': [{'content': content, 'context': 'feedback-rule', 'tags': ['feedback', 'global-rules']}],
    'async': False
})
req = urllib.request.Request(
    'http://localhost:8888/v1/default/banks/david-global/memories',
    data=payload.encode(),
    headers={'Content-Type': 'application/json'},
    method='POST'
)
resp = urllib.request.urlopen(req, timeout=60)
result = json.loads(resp.read())
print(f'OK: {os.path.basename(\"$f\")} — {result.get(\"usage\",{}).get(\"total_tokens\",0)} tokens')
"
done

2.5 Tester le recall

curl -s -X POST http://localhost:8888/v1/default/banks/david-global/memories/recall \
  -H "Content-Type: application/json" \
  -d '{"query": "comment debugger un job bloque dans le pipeline"}' | python3 -c "
import json, sys
d = json.load(sys.stdin)
for m in d.get('results', [])[:3]:
    print(f'  {m[\"text\"][:150]}')
    print()
"

2.6 Installer le plugin Claude Code

# Ajouter le marketplace Hindsight
claude plugin marketplace add vectorize-io/hindsight

# Installer le plugin mémoire
claude plugin install hindsight-memory

2.7 Configurer le plugin

mkdir -p ~/.hindsight

cat > ~/.hindsight/claude-code.json << 'EOF'
{
  "hindsightApiUrl": "http://localhost:8888",
  "bankId": "david-global",
  "bankMission": "Mémoire globale. Regles de travail, feedbacks critiques, protocoles obligatoires, corrections comportementales.",
  "retainMission": "Extraire : corrections sur le comportement (complaisance, raccourcis, mensonges), decisions techniques, règles de process, préférénces de travail. Ignorer les salutations.",
  "autoRecall": true,
  "autoRetain": true,
  "recallBudget": "mid",
  "recallMaxTokens": 2048,
  "recallTypes": ["world", "experience", "observation"],
  "recallContextTurns": 2,
  "retainEveryNTurns": 5,
  "retainOverlapTurns": 2,
  "debug": false
}
EOF

A partir de maintenant, chaque prompt que vous envoyez déclénché un recall automatique. Vos feedbacks sont injectes dans le contexte de l'IA — elle ne choisit pas de les consulter, ils sont la.

Étape 3 — Couche C : Langfuse (observabilité)

# docker-compose-langfuse.yml
cat > ~/langfuse-docker/docker-compose.yml << 'DCEOF'
version: '3.8'
services:
  langfuse-server:
    image: langfuse/langfuse:2
    ports:
      - "3001:3000"
    environment:
      DATABASE_URL: "postgresql://langfuse:langfuse@langfuse-db:5432/langfuse"
      NEXTAUTH_SECRET: "votre-secret-aleatoire-ici"
      NEXTAUTH_URL: "http://localhost:3001"
      SALT: "votre-salt-aleatoire-ici"
    depends_on:
      langfuse-db:
        condition: service_healthy

  langfuse-db:
    image: postgres:16
    environment:
      POSTGRES_USER: langfuse
      POSTGRES_PASSWORD: langfuse
      POSTGRES_DB: langfuse
    volumes:
      - langfuse-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U langfuse"]
      interval: 5s
      timeout: 5s
      retries: 5

volumes:
  langfuse-data:
DCEOF

cd ~/langfuse-docker
docker compose up -d

# Dashboard accessible sur http://localhost:3001

L'intégration avec Claude Code se fait via le hook Stop — un script envoie les traces de session à Langfuse après chaque réponse.

Les contreparties

Rien n'est gratuit. Voici ce que ce système coute :

Latence

Hindsight ajoute 1-3 secondes par prompt (recall API). Sur 50 echanges, ca fait ~2 minutes. Perceptible, pas bloquant.

Faux positifs

Le risque le plus sérieux. BRAKE peut bloquer des actions legitimes :

  • Refactoring de code serveur → bloque parce que "pas de DB query"
  • Lecture de worker.js pour comprendre l'architecture → bloque

Mitigation : calibration progressive. Semaine 1 en mode "log only" (exit 0 + log), semaine 2 review des logs, semaine 3 activation des règles validées.

Maintenance

3 systèmes = 3 trucs qui peuvent casser. Docker tombe, un hook change de format après une mise à jour, un script à un bug.

Mitigation : dégradation gracieuse partout. trap 'exit 0' ERR dans gate.sh. Le plugin Hindsight fait exit 0 si le serveur est down. Langfuse est passif — s'il tombe, rien ne change.

L'état actuel (28 mars 2026)

Couche Status Detail
A — BRAKE Hard Operationnel 5 règles avec exit 2, degrade gracieusement, teste
B — Hindsight Operationnel Serveur Docker, 232 faits extraits, 51 observations, recall fonctionne
C — Langfuse A déployér Prevu ce week-end

Ce qu'on mesure

La question n'est pas "est-ce que ca marche techniquement" (ca marche, les tests le prouvent). La question est : est-ce que ca change réellement le comportement de l'IA au quotidien ?

Les métriques à suivre :

  • Nombre de blocages BRAKE par session (trop = faux positifs, zéro = règles trop laxistes)
  • Pertinence du recall Hindsight (est-ce que les bons feedbacks remontent au bon moment ?)
  • Taux de conformité aux règles (mesure via Langfuse)
  • Temps gagne vs temps perdu (la question ultime)

Rendez-vous dans un mois

Ce système est en place depuis aujourd'hui. Il va vivre en production sur tous mes projets Claude Code pendant les 4 prochaines semaines.

Le 28 avril 2026, je publierai un articlé de suivi avec :

  • Les données réelles : combien de blocages, combien de faux positifs, combien de recalls pertinents
  • Les ajustements faits en cours de route
  • Le verdict honnête : est-ce que l'IA est devenue fiable ? Est-ce que le gain de productivité est reel ? Ou est-ce que j'ai construit une usine à gaz qui ralentit tout ?

Parce que c'est facile de construire un système. C'est autre chose de vivre avec.

A dans un mois.

David Perrot — Ingenieur en Informatique
Testeur de limites, constructeur de garde-fous.

Liens
Hindsight (vectorize-io) — Système de mémoire agent
Langfuse — Observabilite pour LLM
Claude Code — CLI d'Anthropic
Documentation hooks Claude Code
#ClaudeCode #Guardrails #Hindsight #Langfuse #AgentsIA #legeektech
← Retour à Scripts & Outils