
Comment créer des agents IA en 2026 (Cours complet)
AI features
- Views
- 696K
- Likes
- 368
- Reposts
- 68
- Comments
- 22
- Bookmarks
- 1.2K
TL;DR
Une plongée technique approfondie dans le runtime agentic-harness, expliquant comment utiliser une architecture à trois couches, des sandboxes distantes et la compaction de contexte pour concevoir des agents IA résilients et prêts pour la production.
Reading the FRANÇAIS translation
voici la vérité que personne ne dit aux développeurs d'IA.
la plupart d'entre eux construisent des démos
tout ce dont vous avez besoin pour construire
un agent IA de niveau production
TLDR; si vous ne voulez pas le lire, donnez ce lien à votre agent et posez-lui des questions : ➡️https://github.com/codejunkie99/agentic-harness
voici le tweet qui a tout déclenché
le problème est que la plupart des ingénieurs IA n'ont aucune idée claire de ce qu'ils doivent réellement construire lorsqu'ils décident de prendre les agents au sérieux.
certains se tournent vers LangChain parce que les démos multi-agents ont l'air propres sur YouTube, et passent les deux semaines suivantes à se battre avec l'interopérabilité Python et les incompatibilités de runtime asynchrone avant de tout jeter.
certains essaient de construire une couche d'orchestration personnalisée à partir de zéro : une boucle, un magasin de sessions, un assembleur de contexte, et ne terminent jamais l'agent réel parce que l'infrastructure a dévoré le calendrier.
d'autres copient l'exemple de webhook hello-world, reçoivent une réponse JSON, supposent qu'ils comprennent le système, et livrent quelque chose qui casse la première fois qu'une session dure plus de dix minutes, qu'un sandbox distant tombe en panne en cours de tâche, ou que la fenêtre de contexte se remplit sans compaction configurée.
le résultat est généralement le même : beaucoup de tuyauterie, pas d'agents de production, et aucun modèle mental de ce à quoi ressemble réellement un runtime d'agent de production.
si votre objectif est de construire et de livrer de vrais agents en 2026, vous n'avez pas besoin d'apprendre six frameworks.
vous devez comprendre un runtime suffisamment en profondeur pour posséder un agent de production du handler au déploiement.
cela signifie apprendre à :
- câbler l'architecture à trois couches pour que votre logique de handler survive aux changements de fournisseur et de cible sans toucher au code de l'agent
- utiliser les sessions et les tâches correctement pour que les longs travaux ne contaminent pas leur propre contexte
- écrire des rôles et des compétences qui façonnent le comportement du modèle sans rien recompiler
- configurer la compaction pour que les sessions qui durent deux heures ne commencent pas à halluciner à la première heure
- pointer HttpSessionEnv vers des sandbox distants pour que le binaire s'exécute localement pendant que l'exécution se fait sur Linux
- choisir la bonne cible de build : native, node, ou Cloudflare, sans réécrire aucune logique d'agent entre elles
- générer des connecteurs au lieu d'écrire des adaptateurs à la main, et comprendre pourquoi cette distinction compte sous charge réelle
ce guide est une procédure technique complète construite à partir du codebase agentic-harness réel, six semaines à construire et casser de vrais agents avec, et les modes de défaillance qui ont coûté le plus de temps à déboguer.
cet article fait plus de 4 000 MOTS et s'inspire directement du dépôt et de la documentation, pas de résumés de seconde main ou d'exemples de niveau démo.
mais sa vraie valeur est que chaque section contient un extrait de code fonctionnel, une explication claire de la raison de la décision, et le mode de défaillance exact que vous rencontrerez si vous l'ignorez.
ainsi, à la fin de votre lecture, vous pouvez posséder un agent de production de bout en bout, du premier handler au sandbox en passant par le job CI qui l'exécute sans surveillance.
construire cette compréhension a pris plus de 6 SEMAINES de travail quotidien avec le codebase, la plupart du temps à déboguer des choses qui semblaient correctes avant de casser dans des conditions réelles.
maintenant, entrons dans le vif du sujet. ⬇️
La Forme du Projet
deux crates. un binaire. chaque cible d'exécution est un choix de configuration, pas une réécriture.
- le SDK est une bibliothèque que vous intégrez dans n'importe quel projet Rust. le CLI l'encapsule. votre agent est un binaire Rust qui commence par
use agentic_harness::prelude::*;. cargo buildest l'ensemble du pipeline. pas de bundler. pas d'étape de transpilation. pas de runtime de langage sur la machine cible. un exécutable autonome plus un manifest.json.
la contrainte de conception qui a tout motivé : le même binaire d'agent doit pouvoir s'exécuter sur votre ordinateur portable en mode interactif, dans un job GitHub Actions clonant un nouveau dépôt, contre un sandbox E2B distant via HTTP, et sur une limite Cloudflare Worker sans changer une seule ligne de logique d'agent entre eux.
chaque décision dans ce codebase existe pour respecter cette contrainte.
Les 3 couches et pourquoi chacune existe
le modèle mental est trois anneaux concentriques. savoir où se trouve chaque limite vous fera gagner plus de temps de débogage que tout le reste dans ce guide.
votre code Rust est l'anneau extérieur.
- vous écrivez des handlers. les handlers reçoivent un AgentContext. ils appellent des sessions. les sessions appellent le modèle, lisent des fichiers, écrivent des fichiers, exécutent des commandes shell, lancent des tâches, se connectent à des serveurs MCP.
- vous ne touchez jamais un client HTTP directement. vous ne parsez jamais une réponse de modèle directement. le SDK gère les deux.
le harnais est l'anneau du milieu.
- il gère le registre des agents, route l'identité par chemin d'URL, gère la persistance des sessions entre les appels, la compaction du contexte lorsque les sessions grandissent, la découverte des rôles et compétences, la précédence de sélection du modèle, et le trait ModelClient neutre vis-à-vis du fournisseur.
- cela vous permet de remplacer Anthropic par OpenAI ou une instance Ollama locale sans toucher au code du handler.
- le harnais est ce qui rend votre logique d'agent réutilisable entre fournisseurs et cibles.
- c'est aussi là où toutes les choses qui cassent en production sont gérées : état de session, débordement de contexte, échecs de fournisseur, ordonnancement des requêtes concurrentes.
les cibles d'exécution sont l'anneau intérieur.
- système de fichiers local. checkout CI. HttpSessionEnv pointé vers Daytona ou E2B. limite Cloudflare Worker.
- le harnais ne se soucie pas de celui que vous utilisez. vos handlers non plus. ils appellent session.shell() et session.write() et le harnais les traduit en ce dont la cible sous-jacente a besoin.
- cette séparation est tout l'intérêt. quand E2B publie une nouvelle version d'API, vous mettez à jour le connecteur, pas votre logique d'agent.
- quand Anthropic publie claude-opus-4-7, vous mettez à jour runtime.json, pas vos handlers. l'anneau extérieur reste propre parce que l'anneau du milieu absorbe tout le changement.
configuration runtime : le fichier qui contrôle la couche modèle
avant d'écrire un seul handler, vous avez besoin de runtime.json dans votre espace de travail.
placez-le dans .agentic-harness/config.json ou à la racine de l'espace de travail sous le nom agentic-harness.json. load_workspace_context() le récupère automatiquement.
la sélection du modèle au runtime suit cette précédence :
- PromptOptions::model(...) : remplacement par appel
- les métadonnées du modèle du rôle sélectionné : valeur par défaut par rôle
- defaultModel de la configuration runtime : valeur par défaut de l'espace de travail
ce qu'il faut comprendre : l'ID du modèle doit être enregistré avant de pouvoir l'utiliser. openaiCompatibleModels est la liste que le harnais utilise pour câbler le client chat-completions intégré. si votre modèle n'est pas dans cette liste, vous obtenez une erreur claire au démarrage au lieu d'un échec déroutant en cours de session.
pour les passerelles compatibles OpenAI, la configuration est la même. pointez baseUrl vers votre passerelle :
- ne jamais écrire une clé API littérale dans runtime.json. utilisez apiKeyEnv et gardez la clé réelle dans votre environnement.
- le harnais lit la variable d'environnement au moment de la requête, pas au démarrage, ce qui signifie que vous pouvez faire tourner les clés sans redémarrer le serveur.
l'identité de l'agent est un chemin d'URL, jamais une recherche dans un registre
c'était la première décision de conception qui m'a surpris. maintenant je pense que c'est la bonne.
il n'y a pas de système d'ID d'agent. pas de clé de registre. pas d'UUID que vous générez vous-même. l'identité de votre agent est POST /agents/<name>/<id>.
- le harnais gère toute la comptabilité d'état de session derrière cette URL.
- la raison pour laquelle cela fonctionne : chaque appelant dans chaque système sait déjà comment construire un ID significatif à partir du contexte. un numéro de PR. un ID d'exécution. un horodatage combiné à un nom de tâche. un identifiant d'utilisateur.
- vous n'avez pas besoin d'un point de terminaison de création de session. vous n'avez pas besoin de stocker les ID de session séparément. l'URL est la session.
le handler d'agent côté Rust appelle ctx.id() pour obtenir l'ID fourni par l'appelant :
1let session = ctx.session_with_id(ctx.id()).await?;
sessions : le contexte d'exécution avec état
une session est plus qu'un fil de conversation. c'est le contexte d'exécution complet pour une invocation d'agent.
elle contient :
- l'historique des messages avec le modèle
- l'accès aux fichiers de l'espace de travail (lecture, écriture, édition, grep, glob, stat, readdir)
- l'exécution shell avec contrôle du cwd et de l'environnement
- les enregistrements d'outils (serveurs MCP, outils personnalisés)
- le rôle assigné et sa superposition de prompt système
- le budget de compaction et le watermark de l'historique
vous obtenez une session en appelant ctx.session_with_id() avec l'ID qui a du sens :
1let session = ctx.session_with_id("pr-1423").await?;
- les sessions persistent à travers les appels HTTP lorsque vous utilisez le même ID. appelez le même point de terminaison d'agent trois fois avec le même ID de session, le modèle voit les trois échanges comme une conversation continue.
- l'historique s'accumule automatiquement. vous ne le gérez pas.
- c'est ce qui rend les workflows multi-étapes possibles sans gérer l'état vous-même. vous continuez à appeler session.prompt() et le harnais gère tout le reste.
lorsque vous devez passer de grandes quantités de contexte avec un prompt, lisez le fichier et formatez-le en ligne :
1let content = session.read("src/main.rs").await?;2let response = session.prompt(format!("Review this file:\n\n{content}")).await?;
la session gère le comptage de tokens pour que vous ne débordiez pas accidentellement la fenêtre de contexte en cours de conversation. lorsque vous êtes proche du budget, la compaction se déclenche. plus de détails dans une section ultérieure.
tâches : des sessions enfants ciblées qui gardent le parent propre
- c'est la primitive que j'aurais aimé comprendre dès le premier jour. c'est la différence entre les agents qui restent cohérents sur de longs travaux et ceux qui commencent à halluciner à mi-parcours.
- une tâche est une session enfant unique. historique frais. espace de travail partagé. renvoie un résultat au parent. l'historique du parent ne voit jamais aucun des raisonnements intermédiaires de la tâche.
- la tâche de recherche s'exécute en isolation. toute sa chaîne de raisonnement.
- chaque observation intermédiaire que le modèle a faite sur le code, chaque "attendez, laissez-moi vérifier ce fichier aussi", reste à l'intérieur de la tâche.
- la session parent reçoit un résumé propre. c'est tout ce qu'elle voit. pourquoi cela compte en pratique : lorsque vous exécutez une analyse exploratoire directement dans une session de longue durée, l'historique se remplit d'appels d'outils intermédiaires, de réponses partielles et de raisonnements du modèle sur des choses qui ne sont plus pertinentes.
- le modèle s'ancre sur ce bruit alors qu'il ne le devrait pas. la compaction finit par se déclencher et perd le contexte dont vous aviez réellement besoin. les tâches sont la solution chirurgicale.
la règle : si le sous-problème a un livrable clair et n'a pas besoin de l'historique de conversation du parent pour être complété, faites-en une tâche. le seuil pour "en faire une tâche" est plus bas que vous ne le pensez.
pour une analyse parallèle sur un codebase : le modèle cartographe, déployez des tâches et collectez les résultats :
1let tasks = vec!["src/auth", "src/api", "src/db"];2let results = join_all(tasks.map(|dir| {3 let session = ctx.session_with_id(format!("cartographer-{dir}"));4 async move { session.prompt(format!("Analyze {dir}")).await }5})).await;
chaque tâche est propre. chaque tâche est concentrée sur exactement un répertoire. la session parent collecte les résultats et écrit le document final.
si vous avez 12 modules, vous exécutez 12 tâches ciblées, chacune commençant avec zéro bagage des autres.
rôles et compétences : façonner le comportement sans recompiler
- les rôles se trouvent dans .agentic-harness/roles/. les compétences se trouvent dans .agents/skills/. les deux sont découverts automatiquement au démarrage du harnais.
- les rôles sont des superpositions de prompt système limitées à un appel. appliqués au moment de l'appel et jetés après. ils ne persistent pas dans l'historique des messages. ils ne s'accumulent pas entre les appels.
la chaîne de précédence : rôle d'appel > rôle de session > rôle d'agent > aucun rôle.
- le frontmatter du modèle est optionnel mais utile. il vous permet de router des rôles spécifiques vers des modèles spécifiques.
- votre rôle d'explicateur s'exécute sur claude-sonnet-4-6 pour la vitesse et le coût. votre auditeur de sécurité s'exécute sur claude-opus-4-7 pour la profondeur. vous configurez cela une fois dans le fichier de rôle et n'y pensez plus jamais.
- les compétences sont des fichiers descripteurs de comportement que le modèle lit au début d'une session.
- ce sont des fichiers markdown dans .agents/skills/. le harnais les trouve automatiquement. vous ne les enregistrez nulle part.
l'utilisation pratique : une bibliothèque de compétences aux côtés de votre codebase décrit comment vous travaillez. format des messages de commit, bibliothèques préférées, conventions de nommage des migrations, modèles de conception d'API, exigences de test.
le modèle lit cela avant chaque session. vous éditez le markdown. le comportement se met à jour à la prochaine exécution. pas de recompilation.
le modèle lit cela. il écrit des commits qui correspondent à votre convention. vous ne lui rappelez pas à chaque session. vous maintenez un fichier.
la boucle de l'agent de codage en détail
la boucle de l'agent de codage est le cas d'utilisation principal autour duquel le CLI a été construit. c'est aussi là où le plus de choses peuvent mal tourner si vous le configurez mal.
la commande complète avec toutes les options qui comptent :
1agentic-harness run \2 --workspace . \3 --llm auto \4 --deny-path .env \5 --deny-path config/ \6 --approve-dependencies \7 --commit "feat: add user authentication" \8 --pr
ce que fait chaque drapeau et pourquoi il compte :
- --workspace . définit la racine. toutes les opérations sur les fichiers sont sandboxées ici. l'agent ne peut pas lire ou écrire en dehors de ce chemin, appliqué au niveau du harnais — pas en faisant confiance au modèle pour s'auto-restreindre.
- --llm auto sélectionne le modèle à partir de defaultModel dans votre configuration runtime. utilisez --llm anthropic/claude-opus-4-7 pour les tâches complexes nécessitant un raisonnement profond, ou --llm anthropic/claude-sonnet-4-6 pour une itération plus rapide.
- --deny-path est un blocage dur. il correspond au style de préfixe, donc --deny-path config/ couvre tout ce qui se trouve sous config/. auditez votre espace de travail avant la première exécution et énumérez chaque chemin qui contient des secrets ou une configuration de production — pas seulement .env.
- --approve-dependencies permet les modifications de Cargo.toml sans étape d'approbation humaine. omettez-le si vous voulez examiner chaque nouvelle crate avant qu'elle ne soit ajoutée.
- --commit met automatiquement en scène toutes les modifications et les commit à la fin d'une exécution réussie avec le message que vous fournissez. sans ce drapeau, les modifications atterrissent comme des modifications non préparées pour que vous les examiniez.
- --pr ouvre une pull request à partir du commit. nécessite un état git propre avant l'exécution et une vraie branche, pas un HEAD détaché.
la boucle elle-même : Inspecter → Résumer → LLM + Outils → Éditer + Tester → Commit · PR.
- inspecter : lit la structure de l'espace de travail, charge les compétences et les rôles, identifie les fichiers les plus susceptibles d'être pertinents pour le prompt.
- écrit sa compréhension dans coding-brief.md avant de toucher au code.
- résumer : le modèle s'engage sur un plan. vous pouvez lire .agentic-harness/runs/<id>/coding-brief.md en cours d'exécution pour voir ce qu'il a décidé.
- si le résumé semble erroné, tuez l'exécution. il est moins coûteux de redémarrer avec un prompt plus clair que de laisser l'agent exécuter un mauvais plan.
- LLM + outils : la boucle édition-test. le modèle apporte des modifications, exécute la suite de tests, lit la sortie, apporte d'autres modifications. itère jusqu'à ce que les tests passent, que la limite d'itération soit atteinte, ou qu'il décide que la tâche est terminée.
commit · PR : met en scène, commit, pousse, ouvre la PR avec le diff attaché.
chaque exécution écrit six artefacts dans .agentic-harness/runs/<id>/ :
- coding-brief.md : le plan auquel l'agent s'est engagé avant d'écrire du code
- summary.md : compte rendu lisible par l'homme de ce qui a été fait, ce qui a été essayé, et pourquoi
- run.json : métadonnées structurées : modèle utilisé, durée totale, comptes de tokens d'entrée/sortie, nombre d'itérations, statut de sortie final
- events.jsonl : chaque appel d'outil dans l'ordre avec les entrées et sorties complètes, pour déboguer ce qui a mal tourné
- diff.patch : le diff complet de toutes les modifications de fichiers
- checks.json : résultats finaux des tests et du lint qui ont déterminé le succès ou l'échec
Conseils à retenir
- traitez-les comme des logs structurés, pas une sortie éphémère. je commit les artefacts d'exécution dans le dépôt pour toute tâche que je dois pouvoir reproduire.
- le run.json seul (2 Ko) vous indique le modèle, les coûts de tokens, et s'il a réussi. le events.jsonl vous dit exactement ce que l'agent a fait et dans quel ordre lorsque vous devez déboguer une mauvaise exécution.
pour CI, le modèle est :
1agentic-harness run \2 --workspace . \3 --llm auto \4 --deny-path .env \5 --deny-path config/ \6 --approve-dependencies \7 --commit "ci: automated fix" \8 --pr
HttpSessionEnv : exécuter le binaire localement, exécuter à distance
- c'est la capacité que j'ai mis le plus de temps à comprendre pleinement. je l'utilise maintenant sur presque toutes les tâches qui touchent à l'infrastructure.
- le binaire de l'agent s'exécute sur votre machine ou dans CI. les opérations sur le système de fichiers et le shell s'exécutent dans un sandbox distant.
- l'agent ne sait pas et ne se soucie pas de l'environnement dans lequel il se trouve.
1use agentic_harness::HttpSessionEnv;
le protocole filaire est JSON sur HTTP. chaque opération :
- exec
- read
- write
- edit
- grep
- glob
- stat
- readdir
- mkdir
- rm a une forme requête/réponse définie.
tout sandbox qui implémente ce protocole fonctionne comme une cible HttpSessionEnv.
pour câbler un sandbox nommé :
1let env = HttpSessionEnv::new("my-sandbox", "https://sandbox.example.com");
les connecteurs intégrés gèrent le boilerplate d'authentification et de cycle de vie pour Vercel Sandbox, Daytona, et E2B :
1let env = E2B::new("e2b-sandbox-id").await?;
- le cas d'utilisation concret pour lequel je l'utilise le plus : reproduire les échecs CI dans un environnement Linux propre.
- l'agent clone le dépôt au hash de commit exact qui échoue, exécute la commande de test exacte qui échoue, lit la sortie complète, diagnostique l'échec, et écrit un rapport.
- je lis le rapport. je n'ai jamais touché à ma machine locale. le sandbox est jeté lorsque la session se termine.
la chose de performance dont personne ne vous avertit : chaque appel shell via HttpSessionEnv est un aller-retour réseau. les boucles serrées : éditer, tester, vérifier la sortie, éditer : accumulent la latence rapidement.
une boucle de 40 itérations qui prend 5 secondes localement prend plusieurs minutes contre un sandbox distant si chaque itération fait trois appels shell séparés.
la solution : regrouper le travail shell dans des scripts.
un appel par itération au lieu de trois. écrivez le script une fois, exécutez-le plusieurs fois. la différence de latence sur une boucle de 40 itérations est réelle.
cibles de build : même codebase, trois formes de déploiement
native est la valeur par défaut. un binaire. un manifest. rien d'autre sur la machine cible. s'exécute partout où un binaire Linux natif peut être exécuté.
node est pour les plateformes d'hébergement qui nécessitent un point d'entrée Node. le build génère server.mjs qui démarre le binaire Rust natif comme un processus enfant et proxy HTTP vers lui. la logique de l'agent s'exécute toujours en Rust. la couche Node est un shim HTTP de 30 lignes.
Cloudflare est pour le déploiement en périphérie.
- le build génère un fichier de limite Worker et lie un adaptateur d'application compatible Worker.
- les handlers compilent en WASM via l'ABI JSON WASM.
- les liaisons Durable Object gèrent la persistance des sessions via Cloudflare KV.
la contrainte importante à propos de Cloudflare : les Workers ne supportent pas les commandes shell de longue durée. ils n'ont pas de vrai système de fichiers.
ils ne supportent pas cargo ni aucun outil de build. --target cloudflare est pour la gestion des webhooks, les métadonnées de route, les petits points de terminaison de contrôle, et le routage Durable Object, pas pour le travail de codage.
pour tout ce qui nécessite d'exécuter cargo test, déléguez à un processus natif ou un sandbox distant.
la matrice de décision pratique :
- livrer un agent comme une API que d'autres services appellent → native derrière nginx ou une plateforme gérée
- héberger sur Railway, Render, ou une plateforme qui attend Node → node
- ingestion de webhooks, routage léger, gestion d'état Durable Object → cloudflare
- tout le reste → native
sortie guidée par schéma : structures Rust typées à partir des réponses du modèle
demander au modèle de retourner du JSON et espérer qu'il le fasse est la moitié de la solution.
avoir le harnais qui extrait, valide et désérialise en votre structure Rust est la solution complète.
le modèle peut retourner de la prose de raisonnement à côté de la charge utile typée dans la même réponse. le harnais extrait le bloc de résultat entre les marqueurs --RESULT_START--- et ---RESULT_END---.
vous obtenez une structure Rust. sécurité de type au moment de la compilation de la sortie du modèle à votre logique de handler.
- le schéma fait deux choses : il dit au modèle quelle forme produire, et il donne au harnais quelque chose à valider avant la désérialisation.
- si le modèle retourne quelque chose qui ne correspond pas au schéma, vous obtenez PromptError::SchemaValidationFailed au lieu d'une panique trois sites d'appel plus tard lorsque vous accédez à un champ manquant.
outils MCP : atteindre l'extérieur du sandbox
lorsque l'agent a besoin de capacités au-delà du fichier et du shell, connect_mcp est la trappe de secours.
l'agent obtient l'ensemble complet des outils du serveur MCP. pas de définitions d'outils à écrire. les descriptions viennent du serveur. le modèle décide quand appeler quel outil en fonction de ces descriptions.
vous pouvez câbler plusieurs serveurs MCP à une session :
1session.connect_mcp("sentry", "https://mcp.sentry.io").await?;2session.connect_mcp("github", "https://mcp.github.com").await?;
- le modèle appelle les outils en fonction de leurs descriptions. une description vague comme "rechercher sentry" est appelée de manière incohérente.
- une description qui dit "appelez ceci avant de répondre à toute question sur les erreurs, incidents ou problèmes de production" est appelée de manière fiable.
- si vous contrôlez le serveur MCP, écrivez des descriptions prescriptives : dites au modèle quand appeler, pas seulement ce qu'il retourne.
connecteurs : générer des adaptateurs au lieu de les écrire
au lieu d'écrire du code d'adaptateur à la main contre une API inconnue, passez une recette de connecteur à votre agent de codage :
1agentic-harness generate connector daytona --output src/connectors/daytona.rs
- la recette de connecteur est une description structurée de l'API du sandbox et du contrat SessionEnv qu'elle doit satisfaire.
- l'agent de codage la lit, écrit le module d'adaptateur Rust, gère l'authentification, encapsule le cycle de vie du fournisseur, et l'expose comme un HttpSessionEnv.
- vous examinez le diff. vous le fusionnez. l'adaptateur vit dans votre projet. c'est votre code maintenant.
j'ai câblé Daytona en utilisant cela en environ 20 minutes, y compris le cycle de révision complet. l'agent a obtenu le format d'en-tête d'authentification correct du premier coup.
écrire l'adaptateur à partir de zéro contre la documentation de Daytona aurait pris la majeure partie d'un après-midi et au moins deux mauvaises hypothèses sur le flux de rafraîchissement du token.
une fois le connecteur généré :
1let env = daytona::SessionEnv::new("daytona-sandbox-id").await?;
compaction automatique : gérer les longues sessions sans perdre le contexte
les sessions de longue durée accumulent de l'historique.
finalement, elles débordent de la fenêtre de contexte du modèle.
le harnais gère cela automatiquement, mais vous devez le configurer correctement ou vous perdrez le contexte exactement au mauvais moment.
context_window_tokens est le budget total pour la session.
- reserve_tokens est ce que vous réservez pour la réponse du modèle. la limite effective pour l'historique est context_window_tokens - reserve_tokens.
- keep_recent_messages est le nombre de messages à la fin qui sont toujours préservés textuellement, indépendamment de la compaction.
lorsque l'historique dépasse le budget, le harnais demande au modèle de résumer tout ce qui se trouve entre le prompt système et la fin conservée.
ce résumé remplace la section du milieu. les messages de fin restent intacts. la session compactée est plus petite et le prochain appel tient dans le budget.
le compromis est réel : les résumés perdent en précision. une décision spécifique prise il y a 50 messages : "nous avons choisi authlib parce que c'est la seule bibliothèque avec le support PKCE qui fonctionne avec le modèle middleware d'axum" : pourrait survivre comme "nous avons choisi authlib pour l'authentification" dans le résumé.
si cette précision est cruciale pour les décisions ultérieures dans la session, stockez-la explicitement :
- écrivez les décisions dans des fichiers. les fichiers survivent à la compaction. le modèle peut les relire à la demande. l'historique n'a pas besoin de tout porter si l'espace de travail le fait.
- exécutez agentic-harness doctor pour voir la fenêtre de contexte réelle rapportée de votre modèle. définissez context_window_tokens à 80-90% de cette valeur.
- le compteur de tokens n'est pas parfaitement précis côté modèle, et une seule lecture de fichier volumineux peut vous faire dépasser si vous êtes à 99%.
À quoi faire attention
- contamination de l'historique de session
- problème : l'analyse exploratoire à l'intérieur d'une longue session empoisonne les prompts ultérieurs avec le bruit de la phase d'exploration
- solution : utilisez des tâches. l'historique des tâches ne touche jamais le parent. le seuil pour "en faire une tâche" est plus bas que vous ne le pensez
- surprises de précédence des rôles
- problème : un rôle au niveau de l'appel masque le rôle de session. le modèle se comporte différemment que prévu et vous ne savez pas pourquoi
- solution : le rôle de session définit l'identité. le rôle d'appel réduit la focalisation. ils se superposent — le rôle d'appel ajoute, il ne devrait pas annuler
- lacunes de --deny-path
- problème : vous refusez .env. vos secrets vivent aussi dans .env.local et config/staging.yaml. l'agent lit l'un d'eux
- solution : refusez les préfixes, pas les noms de fichiers. --deny-path config/ couvre tout ce qui se trouve en dessous
- HEAD détaché dans CI
- problème : l'agent édite, les tests passent, le commit échoue — parce qu'il n'y a pas de branche sur laquelle commiter
- solution : git checkout -b agent-run-$RUN_ID avant d'invoquer le harnais
- latence HttpSessionEnv dans les boucles serrées
- problème : 40 itérations à trois appels shell chacune, c'est des minutes de latence réseau pure
- solution : écrivez agent-check.sh qui fait tout en une seule invocation. un appel par itération
- sous-estimation du budget de contexte
- problème : la compaction se déclenche en cours de tâche. le modèle perd son plan et commence à improviser à partir du résumé
- solution : exécutez agentic-harness doctor pour obtenir la fenêtre réelle. définissez le budget à 80-90% de celle-ci
- configuration runtime chargée après l'enregistrement du handler
- problème : un handler s'exécute avant
load_workspace_context(). aucun modèle enregistré. l'erreur ne ressemble en rien à un problème de configuration - correctif : toujours appeler
load_workspace_context()dansapp()avant de connecter des agents
--llmchange automatiquement entre les exécutions
- problème :
defaultModelest mis à jour. deux exécutions à six mois d'intervalle ne sont pas comparables. vous ne pouvez pas reproduire la première - correctif : épingler le modèle dans
runtime.jsonpour tout ce qui nécessite de la reproductibilité
- suppression des artefacts d'exécution
- problème : vous nettoyez
runs/dans une règlegitignore. trois semaines plus tard, vous devez reproduire une régression et tout a disparu - correctif : commiter les artefacts d'exécution pour toute tâche que vous devez reproduire.
run.jsonfait 2 Ko. gardez-le
ce que je ferais différemment
- lire le guide
agentic-harnessavant de toucher à quoi que ce soit. - écrire des tests au niveau session avant d'écrire la logique des handlers.
- utiliser des tâches pour tout ce qui a un sous-livrable.
- épingler le modèle dès la première exécution sérieuse.
- stocker les décisions dans des fichiers, pas dans l'historique de session.
- regrouper les opérations shell dès le début quand on utilise des sandbox distants.
l'essentiel
la plupart des frameworks d'agents sont des wrappers autour d'un appel API. ceci est un runtime.
un wrapper résout « faire répondre le modèle ». un runtime résout « déployer un agent en production et le maintenir opérationnel après que le modèle change, après que le sandbox change, après que le codebase change, après que la session tourne pendant deux heures et dépasse la fenêtre de contexte ».
l'architecture en 3 couches
- votre code
- le harness
- la cible d'exécution
est ce qui rend cela possible. vous écrivez des handlers. le harness absorbe toute la complexité opérationnelle. la cible d'exécution est un choix de configuration.
les choses qui ne changent pas : la logique des handlers, la structure de session, les patterns de tâches, les définitions de rôles, les fichiers de compétences. les choses qui changent : les modèles, les fournisseurs, les fournisseurs de sandbox, les cibles de déploiement.
l'architecture est conçue pour que les choses qui changent ne touchent jamais celles qui ne changent pas.
c'est le pari. c'est le bon pari.
j'espère que vous avez apprécié la lecture et l'exploration de ma façon de construire pour les agents et en général ❣️
Avertissements
Cet article a été recherché et écrit par l'auteur, édité par Minimax-M2.7. La vignette a été prise sur Pinterest.
Harrison Chase « la mémoire devrait être ouverte ! » —
[https://x.com/hwchase17/status/2046308913939919232Harrison](https://x.com/hwchase17/status/2046308913939919232Harrison)
Chase : « Votre Harness, votre mémoire » —
[https://www.langchain.com/blog/your-harness-your-memory](https://www.langchain.com/blog/your-harness-your-memory)
Vivek Trivedi : « L'anatomie d'un Harness d'agent » —
[https://www.langchain.com/blog/the-anatomy-of-an-agent-harness](https://www.langchain.com/blog/the-anatomy-of-an-agent-harness)


