Je n'ai pas su expliquer ce qu'était un harness en entretien. Alors j'en ai construit un.

Vendredi, j’étais en entretien avec la compagnie X.
Ça se passait bien. On parle agents, spec-first, vélocité. Mon terrain.
Et puis la question tombe :
« C’est quoi, exactement, un harness ? »
Blanc.
J’ai bafouillé un truc sur « l’environnement autour du modèle ». Vague. Mou. Pas moi.
Le pire ? J’utilise des harness tous les jours. Mes agents tournent la nuit, gèrent ma newsletter, surveillent mes commits, mergent mes PR après review. Je vis dedans.
Mais utiliser un truc et savoir l’expliquer, ce sont deux compétences différentes. Et ce jour-là, la deuxième m’a lâché.
Alors l’après-midi même, j’ai fait la seule chose qui me remet les idées en place : j’ai ouvert un fichier vide et j’en ai construit un. 200 lignes de TypeScript. Le soir, je pouvais expliquer un harness à n’importe qui.
Cet article, c’est ce que j’aurais dû répondre. Avec le code.
C’est quoi un harness, vraiment
Un LLM seul ne fait rien. Il reçoit du texte, il rend du texte. Point.
Il ne lit pas tes fichiers. Il ne lance pas tes tests. Il ne se souvient de rien entre deux appels.
Le harness, c’est tout ce qu’on met autour du modèle pour transformer ce générateur de texte en agent qui agit.
Quatre composants. Pas plus.
1. La boucle
Le cœur. Un while qui appelle le modèle, exécute ce qu’il demande, lui renvoie le résultat, et recommence.
Tant que le modèle veut agir, la boucle tourne.
Quand il a fini, elle s’arrête.
2. Les tools
Les mains de l’agent. Des fonctions que toi tu écris — lire un fichier, lancer une commande, chercher sur le web — et que tu déclares au modèle. Le modèle ne les exécute jamais lui-même. Il demande. Le harness exécute.
3. La mémoire
La liste des messages. Chaque tour ajoute ce que le modèle a dit et ce que les tools ont répondu. C’est ça, la « mémoire » d’un agent : un tableau qui grossit. Rien de magique.
4. Les garde-fous
Ce qui empêche la boucle de partir en vrille. Nombre max de tours. Budget. Tools interdits. Timeout. Sans ça, tu n’as pas un agent, tu as une facture API qui tourne en boucle.
Voilà. Boucle, tools, mémoire, garde-fous. Tout le reste — sous-agents, skills, sandboxes — c’est du raffinement au-dessus de ces quatre briques.
Les 200 lignes qui comptent
Voici le harness que j’ai codé vendredi après-midi. Simplifié pour la lecture, mais c’est la vraie structure.
On commence par la mémoire. Un simple tableau de messages :
type Message =
| { role: "user"; content: string }
| { role: "assistant"; content: string; toolCalls?: ToolCall[] }
| { role: "tool"; toolCallId: string; content: string };
interface ToolCall {
id: string;
name: string;
input: Record<string, unknown>;
}
Ensuite, les tools. Un registre de fonctions, avec leur schéma pour que le modèle sache quoi appeler :
const tools = {
read_file: {
description: "Lit un fichier et retourne son contenu",
run: async (input: { path: string }) =>
fs.readFile(input.path, "utf8"),
},
list_dir: {
description: "Liste les fichiers d'un dossier",
run: async (input: { path: string }) =>
(await fs.readdir(input.path)).join("\n"),
},
run_command: {
description: "Exécute une commande shell et retourne stdout",
run: async (input: { cmd: string }) =>
execSync(input.cmd, { timeout: 30_000 }).toString(),
},
};
Trois tools suffisent pour un agent qui explore un repo et lance des tests. Tu en ajoutes selon le besoin. C’est tout l’intérêt : les tools définissent ce que ton agent peut faire, donc ce qu’il ne peut pas faire.
Le dispatch, maintenant. Le modèle demande un tool, le harness l’exécute — et surtout, il renvoie les erreurs au modèle au lieu de crasher :
async function dispatch(call: ToolCall): Promise<string> {
const tool = tools[call.name as keyof typeof tools];
if (!tool) return `Erreur : tool inconnu "${call.name}"`;
try {
return await tool.run(call.input as never);
} catch (err) {
// L'erreur repart dans le contexte : le modèle
// la lit et corrige son prochain essai lui-même.
return `Erreur : ${String(err)}`;
}
}
Ce catch est le détail qui m’a le plus appris.
Un agent robuste, ce n’est pas un agent qui ne se trompe jamais.
C’est un agent qui voit ses erreurs et réessaie autrement.
Et enfin, la boucle. Le vrai cœur du harness :
const MAX_TURNS = 20; // garde-fou : jamais de boucle infinie
async function runAgent(task: string): Promise<string> {
const messages: Message[] = [{ role: "user", content: task }];
for (let turn = 0; turn < MAX_TURNS; turn++) {
// 1. On donne TOUT l'historique au modèle
const response = await llm.chat({
system: SYSTEM_PROMPT,
messages,
tools: toolSchemas(tools),
});
// 2. La mémoire s'accumule
messages.push({
role: "assistant",
content: response.text,
toolCalls: response.toolCalls,
});
// 3. Condition d'arrêt : plus de tool call = travail fini
if (response.toolCalls.length === 0) {
return response.text;
}
// 4. Sinon : on exécute, on renvoie, on reboucle
for (const call of response.toolCalls) {
const result = await dispatch(call);
messages.push({
role: "tool",
toolCallId: call.id,
content: result,
});
}
}
return "Arrêt : limite de tours atteinte.";
}
Relis cette boucle. Elle tient en une phrase :
appelle le modèle → s’il demande des tools, exécute-les et renvoie les résultats → sinon, c’est fini.
Quand j’ai vu ce for tourner pour la première fois — l’agent qui liste mon repo, lit trois fichiers, lance les tests, corrige, relance — j’ai compris pourquoi j’avais bafouillé en entretien.
Je pensais que « harness » cachait un concept compliqué. En vrai, tout ce que j’utilise au quotidien — Claude Code, mes crons, mon agent Hermes — c’est cette boucle-là, avec de meilleurs tools, une meilleure gestion du contexte et plus de garde-fous.
Le modèle fait l’intelligence. Le harness fait la structure. Et c’est toi qui écris la structure.
C’est exactement ma vision de l’IA : un accélérateur, pas un remplaçant. Le modèle ne décide ni des tools disponibles, ni des limites, ni de ce qu’est un travail « fini ». Ça, c’est ton job. Et c’est là que tes années d’expérience comptent.
Ce que je répondrais aujourd’hui
Si l’entretien de vendredi se rejouait, voici ma réponse en deux phrases :
« Un harness, c’est la boucle qui entoure le LLM : elle lui présente des tools, exécute ses tool calls, réinjecte les résultats dans le contexte, et recommence jusqu’à ce que le modèle rende un travail fini. Le modèle fournit le raisonnement ; le harness fournit les mains, la mémoire et les limites. »
Deux phrases. Quatre composants. Une après-midi pour les mériter.
La vraie leçon n’est pas technique. Rater une question, ça arrive. Ce qui compte, c’est le délai entre « je ne sais pas » et « maintenant je sais » — et le fait de le prouver avec du code plutôt qu’avec une excuse.
Vendredi matin, je ne savais pas expliquer un harness. Vendredi soir, j’en avais un qui tournait.
Ce genre de sujet — agents, harness, IA branchée sur du vrai code — c’est littéralement ce que je fais en mission freelance. Si ton équipe est en train de monter ça, on peut en parler 30 minutes : calendly.com/yelkamel/30min.