Tips & Tricks

Le guide complet du harness engineering : construire des agents IA à la manière de Claude Code

Le prompt seul ne suffit pas à maîtriser un LLM. Apprenez à tisser outils, contexte et boucle de contrôle dans un harness, avec du code exécutable et l'architecture de Claude Code comme boussole.

L’ère du « j’envoie un prompt à ChatGPT et basta » est révolue. Depuis 2025, le centre de gravité de l’ingénierie IA s’est déplacé à toute vitesse vers le harness engineering. C’est l’un des mots-clés les plus récurrents dans les billets internes d’Anthropic et dans les travaux d’OpenAI sur les agents.

Pourtant, demandez « qu’est-ce qu’un harness ? » et rares sont ceux qui répondent clairement. Dans cet article, on décortique le harness engineering à l’aide de code qui tourne et de l’architecture même de Claude Code. À la fin, vous aurez tout pour bâtir votre propre agent à partir de zéro.

Un harness, c’est l’« échafaudage » qui entoure l’IA

À l’origine, « harness » désigne un harnais de cheval ou un baudrier d’escalade. En informatique, on pense plutôt au « test harness » : la structure externe qui permet à quelque chose de fonctionner réellement.

Dans le monde de l’IA, un harness est la couche enveloppante autour du LLM. Concrètement, il rassemble tout ce dont le modèle a besoin pour s’attaquer à des tâches réelles :

  • Outils : lire des fichiers, exécuter des commandes, appeler des API…
  • Gestion du contexte : que retenir, qu’oublier, que compresser ?
  • Boucle de contrôle : quand appeler, quand arrêter, quand réessayer ?
  • Permissions et garde-fous : empêcher les opérations destructrices de s’exécuter sans contrôle
  • Mémoire : savoir qui persiste entre les sessions

Le prompt n’est qu’une des entrées de ce harness. Avec un harness faible, même le prompt le plus habile plafonne. C’est pourquoi on entend de plus en plus : « le prompt engineering seul ne suffit plus ».

Pourquoi le harness est décisif : raisonnons en boucle OODA

Un LLM isolé sait seulement « générer le token suivant ». Pour résoudre des tâches réelles, il faut faire tourner une boucle OODA (Observe → Orient → Decide → Act) empruntée à la stratégie militaire.

PhaseContenuResponsable
Observe (observer)Lire l’environnement (fichiers, requêtes DB)Harness
Orient (situer)Structurer l’information et la donner au LLMHarness
Decide (décider)Choisir l’action suivanteLLM
Act (agir)Exécuter (commandes, appels d’API)Harness

Vous le voyez, trois phases sur quatre appartiennent au harness. Le LLM n’excelle qu’au moment Decide. La qualité de l’échafaudage autour détermine la qualité de tout l’agent.

Trois niveaux de harness, par l’exemple

Résolvons la même tâche « générer un billet de blog » à trois niveaux de harness croissants.

Niveau 1 : appel brut de l’API (quasi aucun harness)

import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();

const res = await client.messages.create({
  model: "claude-opus-4-6",
  max_tokens: 4096,
  messages: [{ role: "user", content: "Écris un article de blog" }],
});
console.log(res.content[0].text);

Résultat : du texte générique et creux. Chaque exécution change de sujet et de structure.

Niveau 2 : donner des outils (harness intermédiaire)

const tools = [
  {
    name: "read_existing_posts",
    description: "Retourne la liste des articles existants avec leur titre",
    input_schema: { type: "object", properties: {} },
  },
  {
    name: "write_post",
    description: "Écrit un fichier MDX",
    input_schema: {
      type: "object",
      properties: {
        slug: { type: "string" },
        frontmatter: { type: "object" },
        body: { type: "string" },
      },
      required: ["slug", "frontmatter", "body"],
    },
  },
];

async function runAgent(userGoal: string) {
  let messages = [{ role: "user", content: userGoal }];
  while (true) {
    const res = await client.messages.create({
      model: "claude-opus-4-6",
      max_tokens: 4096,
      tools,
      messages,
    });
    if (res.stop_reason === "end_turn") break;

    // Le harness exécute l'appel de l'outil
    const toolUse = res.content.find((c) => c.type === "tool_use");
    const result = await executeTool(toolUse.name, toolUse.input);
    messages.push({ role: "assistant", content: res.content });
    messages.push({
      role: "user",
      content: [{ type: "tool_result", tool_use_id: toolUse.id, content: result }],
    });
  }
}

Résultat : un sujet non redondant est choisi et un MDX au format correct est produit. Ajouter des outils transforme déjà radicalement la qualité.

Niveau 3 : un harness complet façon Claude Code

  • Boucle autonome (validation utilisateur, retries en cas d’erreur)
  • Compression du contexte (résumer les longues conversations pour économiser les tokens)
  • Délégation à des sous-agents (traduire dans un contexte isolé)
  • Prompt caching (ne pas renvoyer le préfixe statique)
  • Hooks (lint automatique avant commit)

Tout câbler à la main est un vrai projet. D’où l’intérêt d’étudier Claude Code comme implémentation de référence.

Autopsie du harness de Claude Code

Claude Code est le harness d’agent le plus abouti chez Anthropic. Il se décompose en cinq couches.

Couche 1 : conception des outils

Read, Edit, Write, Bash, Glob, Grep, Agent… sont fournis d’emblée. Ce qui est frappant, c’est la granularité :

  • Grep n’est pas un simple grep mais un wrapper autour de ripgrep : précis et rapide
  • Edit ne réécrit pas le fichier entier mais remplace une chaîne précise : diff minimal
  • Agent lance un sous-agent avec contexte isolé

La qualité des outils se répercute directement sur celle de l’agent. « Pourvu que ça marche » ne suffit pas. Concevez vos outils en visant idempotence, messages d’erreur explicites et responsabilité unique.

Couche 2 : contexte hiérarchisé

~/.claude/CLAUDE.md         ← règles globales
./CLAUDE.md                 ← règles projet (chargement automatique)
~/.claude/memory/           ← mémoire longue durée (entre sessions)
  ├── user_profile.md
  ├── feedback_xxx.md
  └── project_xxx.md
historique de conversation   ← échanges récents
tâches/plan                  ← avancement de la session courante

Chaque niveau a sa durée de vie et son rôle. Écrire au mauvais endroit, c’est perdre l’information très vite ou, pire, laisser survivre des données périmées. « Juste pour cette session » → tâches ; « réutilisable » → mémoire.

Couche 3 : délégation à des sous-agents

Avec l’outil Agent, on lance un agent dans son propre contexte.

# L'agent principal donne les consignes, le sous-agent fait le gros du travail
Agent(
  subagent_type: "general-purpose",
  prompt: "Traduis blog/harness.mdx en anglais et 8 autres langues,
           enregistre chaque version dans blog-{lang}/ puis fais le rapport"
)

Résultat : le contexte principal n’est pas pollué par des logs bruyants. Longs logs de build, traductions intermédiaires, vidages de recherche – tout ce dont vous ne voulez que le livrable se délègue entièrement.

Couche 4 : hooks (traitements déterministes)

.claude/settings.json permet d’insérer des commandes shell avant et après chaque appel d’outil.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "npx tsc --noEmit" }
        ]
      }
    ]
  }
}

Le contrôle de types s’exécute automatiquement après chaque édition. Tout ce qui « devrait être traité de façon déterministe au lieu d’être demandé au LLM à chaque fois » a sa place dans un hook.

Couche 5 : modes de permissions

{
  "permissions": {
    "allow": ["Read", "Grep", "Glob"],
    "deny": ["Bash(rm -rf*)", "Bash(git push --force*)"],
    "ask": ["Write", "Edit", "Bash"]
  }
}

Interdisez explicitement les commandes destructrices et demandez confirmation pour les écritures. Les accidents surviennent quand quelque chose s’exécute sans supervision, donc cette couche conditionne la sécurité opérationnelle.

Cinq pièges classiques

1. Trop d’outils Donnez 30 outils au modèle et il hésite, la précision chute. Règle empirique : 5 à 15. Les capacités en trop, mettez-les côté sous-agent.

2. Prompt caching ignoré Sans cache_control de l’API Claude, vous renvoyez l’intégralité du long system prompt à chaque tour et la facture explose. Gardez en tête le TTL de 5 minutes et cachez la partie statique.

messages: [{
  role: "system",
  content: [
    { type: "text", text: longStaticInstructions,
      cache_control: { type: "ephemeral" } },   // ← ceci
    { type: "text", text: dynamicContext },
  ],
}]

3. Messages d’erreur illisibles pour le LLM Si un outil renvoie uniquement Error: undefined, le modèle ne peut pas se corriger seul. Indiquez ce qui cloche et comment le réparer.

throw new Error(
  `Le fichier '${path}' n'existe pas. ` +
  `Fichiers actuellement dans scripts/ : ${list.join(", ")}`
);

4. Sauter la validation humaine Auto-approuver les actions destructrices (suppression, force push, mises à jour de BDD) garantit un accident tôt ou tard. Par défaut : demander pour les écritures, refuser pour les suppressions.

5. Mémoire jamais nettoyée Des informations obsolètes tirent l’agent vers de fausses hypothèses. La mémoire aussi exige un élagage régulier (dans Claude Code : /compact ou édition manuelle).

Faites tourner votre mini-harness

Pour finir, voici un harness minimal en Node.js + TypeScript, exécutable localement.

// mini-harness.ts
import Anthropic from "@anthropic-ai/sdk";
import { readFileSync, writeFileSync } from "fs";

const client = new Anthropic();

const tools = [
  { name: "read_file",
    description: "Lit un fichier texte",
    input_schema: { type: "object", properties: { path: { type: "string" } }, required: ["path"] } },
  { name: "write_file",
    description: "Écrit un fichier texte",
    input_schema: { type: "object", properties: { path: { type: "string" }, content: { type: "string" } }, required: ["path", "content"] } },
];

const executors = {
  read_file: ({ path }) => readFileSync(path, "utf-8"),
  write_file: ({ path, content }) => { writeFileSync(path, content); return `written ${path}`; },
};

async function loop(goal: string, maxSteps = 10) {
  const messages: any[] = [{ role: "user", content: goal }];
  for (let i = 0; i < maxSteps; i++) {
    const res = await client.messages.create({
      model: "claude-opus-4-6", max_tokens: 4096, tools, messages,
    });
    messages.push({ role: "assistant", content: res.content });
    if (res.stop_reason === "end_turn") return res.content;

    const toolUse = res.content.find((c: any) => c.type === "tool_use") as any;
    if (!toolUse) return res.content;
    const result = executors[toolUse.name](toolUse.input);
    messages.push({
      role: "user",
      content: [{ type: "tool_result", tool_use_id: toolUse.id, content: String(result) }],
    });
  }
}

await loop("Lis README.md et enregistre un résumé de 3 lignes dans TL;DR.md");

Avec ça, vous avez déjà un mini-agent capable de lire un fichier existant et d’en écrire un nouveau. Ajoutez Grep, Bash, Agent, et vous obtenez un Claude Code miniature.

Conclusion : du rédacteur de prompts à l’architecte de harness

Ancien point de vueNouveau point de vue
Un bon prompt donne un bon résultatUn bon harness donne un bon résultat
Choisir un modèleConcevoir modèle + outils + contexte + droits
Questions isoléesExploitation continue en boucle

Claude Code est le meilleur manuel pour intégrer ce changement de perspective. Ne vous contentez pas de l’utiliser : démontez-le et injectez ses idées dans votre propre agent. Voilà l’attitude attendue des ingénieurs IA à partir de 2026.

Commencez par copier-coller le mini-harness ci-dessus et lancez-le. Dix minutes plus tard, vous aurez fait le premier pas vers votre agent personnel.

Articles liés

Références

#claude-code #agent-sdk #harness #prompt-engineering #llm #anthropic

Passez votre flux Claude Code au niveau supérieur

50 modèles de prompts éprouvés, prêts à être copiés-collés dans Claude Code.

Gratuit

PDF gratuit : aide-mémoire Claude Code en 5 minutes

Laissez simplement votre e-mail et nous vous enverrons immédiatement l'aide-mémoire A4 en PDF.

Nous traitons vos données avec soin et n'envoyons jamais de spam.

Masa

À propos de l'auteur

Masa

Ingénieur passionné par Claude Code. Il gère claudecode-lab.com, un média tech en 10 langues avec plus de 2 000 pages.