Inicio / Artículos

Agentes profundos con SKILL.md: Un orquestador de LangChain que carga habilidades

Construir un agente profundo genérico en LangChain que escanee una carpeta de habilidades, dirija la acción según las descripciones de SKILL.md, comparta la memoria de AGENTS.md y adquiera nuevas capacidades sin cambios en el código.

Editor: REACTAPP.TOP8 min de lectura1811 palabras

Fuentes: Building Deep Agents + SKILL.md with Langchain

¿Qué pasaría si un agente no tuviera una lista fija de habilidades y solo pudiera descubrir qué es capaz de hacer observando su entorno? Ese es el enfoque aquí: un agente profundo funcional en LangChain que carga archivos SKILL.md según sea necesario para responder consultas. No hay cadenas de procesamiento predefinidas ni enrutadores personalizados. Solo un orquestador genérico, una carpeta con habilidades y la biblioteca deepagents que se encarga del trabajo pesado.

El problema con conjuntos de herramientas fijos

La mayoría de los tutoriales de LangChain muestran el mismo procedimiento: definir algunas herramientas, vincularlas a un modelo, crear un grafo y listo. El resultado es un agente superficial cuya lista de capacidades está fijada en su código.

La alternativa es un orquestador que no sabe qué puede hacer hasta que lo verifica. En tiempo de ejecución, escanea un directorio skills/, lee primero solo los metadatos y carga las instrucciones completas cuando una habilidad se ajusta a la solicitud. En lugar de codificar de forma fija “este agente investiga y escribe”, se colocan research/SKILL.md y writer/SKILL.md en una carpeta, y el agente se encarga del resto. Mañana se puede añadir una habilidad code-reviewer y será detectada sin tocar ni una línea de código. Ese es el patrón SKILL.md aplicado a LangChain.

Cómo encajan las piezas

Cuatro ideas guían el flujo de agent.py:

  1. Primero viene la planificación. El orquestador siempre elabora un plan, convirtiendo el objetivo general del usuario en pasos técnicos concretos.
  • La delegación se mantiene genérica. En lugar de especialistas según el rol, el trabajo recae en un subagente de uso general que cuenta con las mismas herramientas y el mismo acceso a la biblioteca de habilidades.
  • El conocimiento se proporciona bajo demanda. Ni el orquestador ni el subagente conocen los flujos de trabajo especializados al inicio. Los encuentran a través de SkillsMiddleware, que revela las habilidades de forma progresiva, y cargan toda la documentación solo cuando es necesario.
  • Se aplica un control estricto al espacio de trabajo. Las herramientas y las acciones basadas en habilidades se limitan al directorio ./workspace/ para su persistencia, mientras que el directorio raíz permanece visible para la configuración y las habilidades.
  • Todo el proyecto es compacto, y tres archivos se encargan del trabajo real.

    Paso 1: configuración del proyecto

    Las dependencias se encuentran en 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",
    ]
    

    El principal es deepagents, la biblioteca de código abierto de LangChain que formaliza el patrón de agente profundo. Ya listo para usar, ofrece funcionalidades de planificación a través de write_todos, herramientas para el sistema de archivos, creación de subagentes mediante task, compresión de contexto y, lo que es importante, carga de habilidades.

    Paso 2: definir las herramientas

    En agent.py, la única herramienta personalizada es un envoltorio estándar para búsquedas en Tavily:

    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)
    

    Todo lo demás, es decir, la planificación, las operaciones de entrada/salida de archivos, los subagentes y la carga de habilidades, proviene de deepagents.

    Paso 3: la fábrica de agentes

    Aquí es donde todo se combina:

    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",
        )
    

    Tres parámetros merecen una atención más detallada.

    skills=[“./skills”] indica a deepagents que escanee ese directorio, analice el frontmatter en formato YAML de cada SKILL.md que encuentre y coloque el catálogo de habilidades en la instrucción del sistema. El agente ve algo similar a esto:

    Available Skills:
    - research: "Conducts comprehensive web research on any topic..."
    - writer: "Specialized in writing high-quality content..."
    

    Por dos habilidades cuesta aproximadamente 200 tokens. El agente lee el catálogo y decide qué habilidad cargar para la solicitud. Se trata de un enrutamiento puro basado en LLM, sin clasificadores, embeddings ni expresiones regulares.

    memory=["./AGENTS.md"] carga las convenciones a nivel de proyecto en el prompt del sistema. Funciona como contexto compartido para el agente principal y cada subagente, una especie de página “así es como hacemos las cosas aquí” para el equipo.

    backend=FilesystemBackend(root_dir=".") proporciona al agente herramientas de archivo (read_file, write_file, edit_file) cuyo directorio raíz es la carpeta del proyecto, de modo que el sistema de archivos se convierte en el espacio de trabajo permanente del agente.

    Paso 4: escribir las habilidades

    Una habilidad de investigación deliberadamente 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.
    

    Y una habilidad de escritor:

    ---
    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/`.
    

    Cada archivo contiene frontmatter en formato YAML, que funciona como la tarjeta de identificación de la habilidad, seguido de un cuerpo en Markdown que sirve como la guía de operaciones. El agente ve el frontmatter en el nivel 1 y el cuerpo únicamente en el nivel 2. Esto sigue las especificaciones publicadas en agentskills.io.

    Paso 5: memoria compartida en AGENTS.md

    Este archivo se carga siempre. Indica a cada agente qué tarea debe realizar y cómo funciona el equipo:

    # 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.
    

    Cómo funciona realmente el enrutamiento

    No existe un router ni un clasificador, ya que el propio LLM se encarga del enrutamiento. Cuando la biblioteca crea el prompt del sistema, agrega texto más o menos de esta forma:

    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..."
    

    El modelo lee el mensaje del usuario y las descripciones de las habilidades, y luego decide si llamar a read_file("skills/research/SKILL.md") o no hacer nada. Toda la decisión se toma dentro del proceso de forward pass del transformador, sin ninguna lógica externa. En otras palabras, la descripción de la habilidad funciona como motor de enrutamiento; basta con una bien redactada que incluya palabras desencadenantes y límites negativos.

    La interfaz front-end de Streamlit

    app.py envuelve todo en una interfaz de Streamlit que muestra el razonamiento del agente a medida que ocurre. La función stream("updates") de LangGraph genera un evento para cada ejecución de nodo, y la aplicación los analiza para convertirlos en llamadas a herramientas, indicadores de carga de habilidades, actualizaciones del plan y la respuesta final.

    Agregar una habilidad sin cambios de código

    Supongamos que desea tener una habilidad para revisión de código. Cree una carpeta para ella junto a las demás:

    skills/
    ├── research/
    │   └── SKILL.md
    ├── writer/
    │   └── SKILL.md
    └── code-reviewer/
        └── SKILL.md
    

    Luego escriba su 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`.
    

    Reinicie la aplicación y el agente sabrá cómo revisar el código. agent.py permanece sin cambios, al igual que la instrucción del sistema. Solo agregó una carpeta, nada más. Este mismo enfoque conduce hacia agentes profundos más sofisticados.

    Lecciones aprendidas

    • Las descripciones de las habilidades son más importantes que sus instrucciones. Si la descripción es deficiente, la habilidad nunca se activa. Ajustar el campo description, con sus palabras desencadenantes y límites negativos como “No usar para...”, llevó más tiempo que escribir el cuerpo de la habilidad, ya que ese texto constituye la lógica de enrutamiento.
    • El sistema de archivos se subestima. Muchos tutoriales sobre agentes guardan todo en el historial de mensajes, lo cual funciona bien para ejercicios simples pero falla en tareas de múltiples pasos. Guardar los resultados intermedios en ./workspace/ y permitir que los subagentes los lean es lo que permite que las partes se combinen adecuadamente.
  • AGENTS.md sirve para unir todo. Sin él, el orquestador y sus subagentes trabajan por separado. Con él comparten convenciones, conocen la estructura del proyecto y evitan esfuerzos duplicados; se trata de un archivo pequeño con un impacto enorme.
  • Comience de forma genérica y especialícese a través de las habilidades. Es tentador incluir todo el conocimiento del dominio en la instrucción del sistema, pero mantener al orquestador genérico y colocar cada especialización en archivos SKILL.md hace que el sistema sea modular y susceptible de pruebas. Las habilidades pueden intercambiarse sin tocar al agente.
  • Qué añadiría una versión de producción

    Esta aplicación está diseñada de forma deliberadamente minimalista. Una implementación real probablemente incluiría memoria persistente a través de LangMem para el recuerdo episódico y semántico entre sesiones, integración con MCP para herramientas empresariales como Slack, Jira y bases de datos, puntos de control con intervención humana mediante el mecanismo de interrupción de LangGraph, compresión de contexto para tareas de investigación extensas y, por supuesto, más habilidades, cada una representada simplemente por una carpeta con un archivo SKILL.md.

    La dirección a seguir parece clara: desarrollar agentes no se trata tanto de escribir más código como de crear archivos SKILL.md de mejor calidad. El framework se encarga de los aspectos técnicos, las habilidades aportan la experiencia necesaria y el LLM decide cuándo utilizar cada una de ellas.

    Leer más

  • LangChain Deep Agents: Planificación, archivos y subagentes integrados — Lo que aprovechan los deepagents además de LangGraph: planificación write_todos, un sistema de archivos virtual, subagentes, resumen automático y memoria, además de indicaciones sobre cuándo omitirlo.