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.
| Phase | Contenu | Responsable |
|---|---|---|
| Observe (observer) | Lire l’environnement (fichiers, requêtes DB) | Harness |
| Orient (situer) | Structurer l’information et la donner au LLM | Harness |
| Decide (décider) | Choisir l’action suivante | LLM |
| 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é :
Grepn’est pas un simplegrepmais un wrapper autour de ripgrep : précis et rapideEditne réécrit pas le fichier entier mais remplace une chaîne précise : diff minimalAgentlance 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 vue | Nouveau point de vue |
|---|---|
| Un bon prompt donne un bon résultat | Un bon harness donne un bon résultat |
| Choisir un modèle | Concevoir modèle + outils + contexte + droits |
| Questions isolées | Exploitation 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
- 10 patrons de sous-agents dans Claude Code
- Bonnes pratiques CLAUDE.md
- Techniques d’optimisation des tokens dans Claude Code
- Guide des permissions Claude Code
Références
PDF gratuit: cheatsheet Claude Code
Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.
Nous protégeons vos données et n'envoyons pas de spam.
À propos de l'auteur
Masa
Ingénieur spécialisé dans les workflows pratiques avec Claude Code.
Articles liés
Reçu de vérification Claude Code : build, URL publique, CTA et captures
Workflow Claude Code pour prouver un changement avec diff, build, URL publique, CTA, captures et chemin de revenus.
Permission budget Claude Code: avancer sans approuver chaque commande
Concevoir un budget de permissions Claude Code pour protéger secrets, déploiements, billing et données.
Entretenir une bibliothèque de prompts Claude Code réutilisable
Nommez, testez et réutilisez les prompts Claude Code pour relier apprentissage gratuit et pack payant.