Agents profonds avec SKILL.md : un orchestrateur LangChain qui charge les compétences
Créer un agent profond générique avec LangChain qui parcourt un dossier de compétences, dirige le traitement selon les descriptions SKILL.md, partage la mémoire AGENTS.md, et acquiert de nouvelles capacités sans modification de code.
Que se passerait-il si un agent ne disposait pas d’une liste fixe de capacités et devait découvrir ce qu’il pouvait faire uniquement en explorant son environnement ? C’est l’approche adoptée ici : un agent profond fonctionnel dans LangChain qui charge les fichiers SKILL.md sur demande pour répondre aux questions. Il n’y a ni chaînes de traitement codées à l’avance ni routeur personnalisé. Seul un orchestrateur générique, un dossier contenant les compétences et la bibliothèque deepagents chargée du travail complexe.
Le problème des ensembles d’outils fixes
La plupart des tutoriels LangChain suivent la même méthode : définir des outils, les lier à un modèle, construire un graphe et c’est terminé. Le résultat est un agent superficiel dont la liste de capacités est figée dans son code.
L’alternative est un orchestrateur qui ne sait pas ce qu’il peut faire avant de vérifier. Lors de l’exécution, il parcourt un répertoire skills/, ne lit d’abord que les métadonnées et charge ensuite les instructions complètes lorsque une compétence correspond à la demande. Au lieu d’encoder de manière fixe « cet agent recherche et écrit », on place research/SKILL.md et writer/SKILL.md dans un dossier, et l’agent s’occupe du reste. Demain, vous pouvez ajouter une compétence code-reviewer, et elle sera reconnue sans avoir à modifier une seule ligne de code. C’est le modèle SKILL.md appliqué à LangChain.
Comment les éléments s’assemblent
Quatre idées guident le fonctionnement de agent.py :
- La planification vient en premier. L’orchestrateur élabore toujours un plan, transformant l’objectif général de l’utilisateur en étapes techniques concrètes.
SkillsMiddleware, qui révèle progressivement les compétences, et ne chargent la documentation complète qu’en cas de besoin../workspace/ pour assurer leur persistance, tandis que le répertoire racine reste accessible pour la configuration et les compétences.Tout le projet est compact, et trois fichiers suffisent pour effectuer le travail réel.
Étape 1 : configuration du projet
Les dépendances se trouvent dans pyproject.toml:
# pyproject.toml
[project]
name = "deep-agents-langchain"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"deepagents>=0.4.12",
"langchain-anthropic>=1.4.0",
"langchain-openai>=1.1.12",
"python-dotenv>=1.2.2",
"streamlit>=1.55.0",
"tavily-python>=0.7.23",
]
Le principal est deepagents, la bibliothèque open source de LangChain qui formalise le modèle d’agent profond. Dès sa mise en place, elle vous offre des fonctionnalités de planification via write_todos, des outils pour le système de fichiers, la création de sous-agents grâce à task, une compression du contexte, ainsi que, ce qui est important, le chargement de compétences.
Étape 2 : définir les outils
Dans agent.py, le seul outil personnalisé est un enveloppe de recherche Tavily standard :
import os
from typing import Literal
from dotenv import load_dotenv
from langchain_anthropic import ChatAnthropic
from langchain_openai import ChatOpenAI
from tavily import TavilyClient
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend
load_dotenv()
# --- Generic Tools ---
tavily = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))
def internet_search(
query: str,
max_results: int = 5,
topic: Literal["general", "news", "finance"] = "general",
):
"""Search the web for current information."""
return tavily.search(query, max_results=max_results, topic=topic)
Tout le reste, c’est-à-dire la planification, les opérations d’entrée/sortie de fichiers, les sous-agents et le chargement des compétences, provient de deepagents.
Étape 3 : l’usine d’agents
C’est ici que tout se met en place :
def get_deep_agent():
model = ChatAnthropic(
model="claude-3-5-sonnet-20240620",
temperature=0,
)
return create_deep_agent(
model=model,
system_prompt="""You are a generic Deep Agent orchestrator.
1. Use `write_todos` to plan complex requests.
2. You have access to skills in the `skills/` directory.
- You ONLY see names and descriptions initially.
- For specialized tasks, load the matching SKILL.md first.
3. Use the `task` tool to delegate to subagents.
4. ALL outputs go to `./workspace/` exclusively.""",
tools=[internet_search],
skills=["./skills"], # <-- This is the magic line
memory=["./AGENTS.md"],
backend=FilesystemBackend(root_dir="."),
name="generic_deep_agent",
)
Trois paramètres méritent une attention plus approfondie.
skills=[“./skills”] indique à deepagents de parcourir ce répertoire, d’analyser le frontmatter YAML de chaque fichier SKILL.md qu’il trouve et d’insérer le catalogue des compétences dans l’instruction système. L’agent voit alors quelque chose comme ceci :
Available Skills:
- research: "Conducts comprehensive web research on any topic..."
- writer: "Specialized in writing high-quality content..."
Pour deux compétences, le coût est d’environ 200 tokens. L’agent lit le catalogue et décide quelle compétence charger pour la demande. Il s’agit d’un routage pur basé sur un LLM, sans classificateurs, embeddings ou expressions régulières.
memory=["./AGENTS.md"] charge les conventions au niveau du projet dans le prompt système. Il sert de contexte partagé pour l’agent principal et chaque sous-agent, une sorte de page « c’est ainsi que nous travaillons ici » pour l’équipe.
backend=FilesystemBackend(root_dir=".") fournit à l’agent des outils de fichiers (read_file, write_file, edit_file) dont le répertoire racine est le dossier du projet, de sorte que le système de fichiers devient l’espace de travail permanent de l’agent.
Étape 4 : écrire les compétences
Une compétence de recherche délibérément simple :
---
name: research
description: >
Conducts comprehensive web research on any topic using search APIs
and web scraping. Use when the user asks for research, information
gathering, competitive analysis, or needs current data from the web.
license: MIT
compatibility: Requires internet access and Tavily API key
allowed-tools: internet_search Read Write
---
# Web Research Skill
## Instructions
1. Parse the research query to identify key topics.
2. Use the `internet_search` tool to gather initial results.
3. If results are insufficient, try alternative search queries.
4. Synthesize findings into a structured summary.
5. Save detailed findings to `./workspace/` as a markdown file.
Et une compétence d’écriture :
---
name: writer
description: >
Specialized in writing high-quality, creative, and technical content.
Use when the user needs a blog post, report, technical guide,
or any form of structured writing.
license: MIT
---
# Creative Writer Skill
## Instructions
1. Review the input notes or research data provided.
2. Define an outline (Introduction, Body, Conclusion).
3. Use a tone appropriate for the target audience.
4. Ensure all claims are backed by provided data.
5. Save the final draft to `./workspace/`.
Chaque fichier contient du frontmatter en YAML, qui fonctionne comme la carte d’identité de la compétence, suivi d’un corps en Markdown qui sert de manuel opérationnel. L’agent consulte le frontmatter au niveau 1 et le corps uniquement au niveau 2. Cela suit les spécifications publiées sur agentskills.io.
Étape 5 : mémoire partagée dans AGENTS.md
Ce fichier est toujours chargé. Il indique à chaque agent ce qu’il doit faire et comment l’équipe fonctionne :
# Project Context: Deep Agents Demo
## Conventions
- Skills are stored as directories under `skills/`.
- Every skill must have a `SKILL.md` file.
- Subagents should be used for complex, multi-step tasks.
- All intermediate work goes to `./workspace/`.
## Known Entities
- **Main Agent**: The orchestrator who plans and delegates.
- **Researcher**: Specialized in web search and data aggregation.
- **Writer**: Specialized in formatting and synthesizing information.
Comment fonctionne réellement le routage
Il n’y a ni routeur ni classificateur, car c’est l’LLM lui-même qui s’occupe du routage. Lorsque la bibliothèque crée le prompt système, elle ajoute du texte dans ce style :
You have access to a library of skills in the `skills/` directory.
You ONLY see names and descriptions initially.
For any specialized task, look for matching skills and use
`read_file` to load the SKILL.md before executing.
Available skills:
- research: "Conducts comprehensive web research..."
- writer: "Specialized in writing high-quality content..."
Le modèle lit le message de l’utilisateur ainsi que les descriptions des compétences, puis choisit entre appeler read_file("skills/research/SKILL.md") ou ne rien faire. Toute cette décision s’effectue au sein du processus de traitement par le transformateur, sans aucune logique externe. En d’autres termes, la description de la compétence agit comme moteur de routage ; il suffit d’une description bien rédigée, contenant des mots déclencheurs et des limites négatives.
L’interface Streamlit
app.py encapsule tout cela dans une interface Streamlit qui affiche en temps réel les réflexions de l’agent. La fonction stream("updates") de LangGraph génère un événement pour chaque exécution de nœud, et l’application analyse ces événements pour obtenir des appels d’outils, des indicateurs de chargement des compétences, des mises à jour du plan ainsi que la réponse finale.
Ajouter une compétence sans aucun changement de code
Supposons que vous souhaitiez une compétence pour l’évaluation de code. Créez un dossier à côté des autres pour la stocker :
skills/
├── research/
│ └── SKILL.md
├── writer/
│ └── SKILL.md
└── code-reviewer/
└── SKILL.md
Ensuite, écrivez son SKILL.md :
---
name: code-reviewer
description: >
Reviews code for bugs, security issues, and best practices.
Use when the user asks for a code review, security audit,
or wants feedback on code quality. Do NOT use for writing
new code or debugging runtime errors.
license: MIT
allowed-tools: Read Write
---
# Code Review Skill
## Instructions
1. Read the target file using `read_file`.
2. Analyze for: bugs, security issues, performance, readability.
3. Write a structured review to `./workspace/review.md`.
Redémarrez l’application et l’agent saura comment examiner le code. agent.py reste inchangé, tout comme l’invite système. Vous avez simplement ajouté un dossier, rien de plus. La même approche permet d’aboutir à des agents profonds plus sophistiqués.
Leçons tirées de cette expérience
- Les descriptions des compétences sont plus importantes que leurs instructions. Si la description est faible, la compétence ne s’active jamais. Ajuster le champ
description, avec ses mots déclencheurs et des limites négatives comme "Ne pas utiliser pour...", a pris plus de temps que d’écrire le corps de la compétence, car ce texte constitue en fait la logique de routage. - Le système de fichiers est sous-estimé. De nombreux tutoriels sur les agents conservent tout dans l’historique des messages, ce qui convient aux applications simples mais échoue pour les tâches à plusieurs étapes. En enregistrant les résultats intermédiaires dans
./workspace/et en permettant aux sous-agents de les lire, on assure que toutes les parties fonctionnent correctement ensemble.
AGENTS.md permet de coordonner tous les éléments. Sans ce fichier, l’orchestrateur et ses sous-agents fonctionnent chacun de manière indépendante. Avec lui, ils partagent des conventions communes, connaissent la structure du projet et évitent les doubles efforts ; il s’agit d’un petit fichier ayant un impact considérable.Ce que la version de production ajouterait
Cette application est délibérément minimaliste. Une mise en production réelle inclurait probablement une mémoire persistante via LangMem pour le rappel épisodique et sémantique entre sessions, une intégration MCP pour des outils professionnels tels que Slack, Jira et les bases de données, des points de contrôle avec intervention humaine grâce au mécanisme d’interruption de LangGraph, une compression du contexte pour les tâches de recherche longues, ainsi que, bien sûr, davantage de compétences, chacune représentée simplement par un dossier contenant un SKILL.md.
La direction à suivre semble claire. Concevoir des agents ne consiste pas tant à écrire plus de code qu’à rédiger de meilleurs fichiers SKILL.md. Le framework s’occupe des aspects techniques, les compétences transmettent l’expertise, et le LLM détermine quand utiliser telle ou telle compétence.