Strona główna / Artykuły

Głębokie agenci z plikami SKILL.md: Orchestrator LangChain, który ładowa umiejętności

Stwórz uniwersalnego głębokiego agenta w LangChain, który przeszukuje folder z umiejętnościami, kieruje działanie na podstawie opisów w plikach SKILL.md, dzieli się pamięcią z plikiem AGENTS.md oraz nabywa nowe możliwości bez zmian w kodzie.

Wydawca: REACTAPP.TOP8 min czytania1811 słów

Źródła: Building Deep Agents + SKILL.md with Langchain

A co, jeśli agent nie miałby ustalonej listy umiejętności i odkrywałby, co może robić, tylko obserwując otoczenie? To właśnie jest podejście w tym przypadku: działający agent typu deep agent w LangChain, który na żądanie ładuje pliki SKILL.md, aby odpowiadać na zapytania. Nie ma tu żadnych sztywno zdefiniowanych łańcuchów ani specjalnego routera. Tylko ogólny orkiestrator, folder z umiejętnościami oraz biblioteka deepagents, która zajmuje się trudniejszymi zadaniami.

Problem ze sztywnymi zestawami narzędzi

Większość tutoriali dotyczących LangChain pokazuje tę samą metodologię. Definiuje się kilka narzędzi, łączy je z modelem, buduje graf i to już koniec. Rezultatem jest agent o ograniczonych możliwościach, którego lista umiejętności jest zamrożona w kodzie.

Opcją alternatywną jest orkiestrator, który nie wie, co może zrobić, dopóki tego nie sprawdzi. W czasie wykonywania skanuje katalog skills/, najpierw czyta tylko metadane, a pełne instrukcje ładuje wtedy, gdy dana umiejętność pasuje do żądania. Zamiast kodować ściśle „ten agent badający i piszący”, umieszcza się pliki research/SKILL.md oraz writer/SKILL.md w folderze, a agent sam ustala resztę. Jutro można dodać umiejętność code-reviewer, a system ją wykorzysta bez konieczności edytowania ani jednej linijki kodu. To właśnie wzorzec SKILL.md zastosowany w LangChain.

Jak wszystko się łączy

Cztery koncepcje napędzają działanie pliku agent.py:

  1. Najpierw przychodzi planowanie. Orkiestrator zawsze tworzy wstępny plan, przekształcając ogólne cele użytkownika w konkretne kroki techniczne.
  • Delegacja pozostaje ogólna. Zamiast specjalistów od konkretnych ról, praca trafia do podagenta o zastosowaniu uniwersalnym, który dysponuje tymi samymi narzędziami oraz takim samym dostępem do biblioteki umiejętności.
  • Wiedza jest dostarczana na żądanie. Ani orkiestrator, ani podagent nie znają specjalistycznych procesów pracy przy uruchomieniu. Odnajdują je za pomocą SkillsMiddleware, który stopniowo ujawnia umiejętności, a pełną dokumentację ładują tylko wtedy, gdy jest to konieczne.
  • Przestrzeń robocza jest kontrolowana. Narzędzia oraz działania oparte na umiejętnościach są przechowywane w katalogu ./workspace/ dla zachowania ciągłości, natomiast katalog główny pozostaje widoczny do celów konfiguracji i przechowywania umiejętności.
  • Cały projekt jest zwięzły – rzeczywistą pracę wykonują trzy pliki.

    Krok 1: konfiguracja projektu

    Zależności znajdują się w pliku 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",
    ]
    

    Główną biblioteką jest deepagents – otwarte oprogramowanie od LangChain, które formalizuje wzorzec głębokiego agenta. Od razu umożliwia planowanie za pomocą write_todos, narzędzia do zarządzania systemem plików, tworzenie podagentów poprzez task, kompresję kontekstu oraz, co ważne, ładowanie umiejętności.

    Krok 2: zdefiniuj narzędzia

    W pliku agent.py jedynym dostosowanym narzędziem jest standardowy wrapper do wyszukiwania w 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)
    

    Wszystko inne, czyli planowanie, operacje wejścia/wyjścia z plikami, podagenty oraz ładowanie umiejętności, pochodzi z deepagents.

    Krok 3: fabryka agentów

    Tutaj wszystko się łączy:

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

    Trzy parametry wymagają bliższego przyjrzenia się.

    skills=[„./skills“] poleca deepagents, aby przeszukał ten katalog, przeanalizował pliki YAML typu frontmatter w każdym znalezionym pliku SKILL.md i umieścił katalog umiejętności w promptzie systemowym. Agent widzi coś w tym rodzaju:

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

    Dwie takie umiejętności kosztują około 200 tokenów. Agent czyta katalog i decyduje, którą umiejętność załadować w odpowiedzi na żądanie. Jest to czyste routowanie LLM, bez klasyfikatorów, embeddingów ani wyrażeń regularnych.

    memory=[„./AGENTS.md“] załadowuje konwencje na poziomie projektu do promptu systemowego. Służy jako wspólny kontekst dla głównego agenta oraz każdego podagenta, rodzaj strony „jak u nas robimy” dla zespołu.

    backend=FilesystemBackend(root_dir=„.“) dostarcza agentowi narzędzia do pracy z plikami (read_file, write_file, edit_file), przy czym katalog projektu stanowi ich korzeń, dzięki czemu system plików staje się trwałą przestrzenią roboczą agenta.

    Krok 4: napisanie umiejętności

    Celowo prosta umiejętność badawcza:

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

    I umiejętność pisania:

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

    Każdy plik zawiera YAML frontmatter, który pełni rolę karty identyfikacyjnej umiejętności, a następnie treść w formacie Markdown, która służy jako instrukcja postępowania. Agent widzi frontmatter na poziomie 1, a treść tylko na poziomie 2. Jest to zgodne ze specyfikacją opublikowaną na agentskills.io.

    Krok 5: wspólna pamięć w AGENTS.md

    Plik ten jest zawsze ładowany. Określa on, kto robi co oraz jak działa zespół:

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

    Jak naprawdę działa routowanie

    Nie ma routera ani klasyfikatora, ponieważ sam LLM zajmuje się routowaniem. Gdy biblioteka tworzy prompt systemowy, dodaje tekst w następującym stylu:

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

    Model czyta wiadomość użytkownika oraz opisy umiejętności, a następnie decyduje, czy wywołać read_file("skills/research/SKILL.md"), czy nic nie robić. Cała decyzja zapada w trakcie procesu przetwarzania danych przez model typu transformer, bez żadnej zewnętrznej logiki. Innymi słowy, opis umiejętności pełni rolę mechanizmu kierowania działaniami, a wystarczy dobrze napisany opis zawierający słowa wyzwalające oraz określenia granic negatywnych.

    Interfejs Streamlit

    app.py otacza wszystko interfejsem Streamlit, który pokazuje proces myślenia agenta w czasie rzeczywistym. Funkcja stream("updates") z LangGraph generuje wydarzenie dla każdej eksploatacji węzła, a aplikacja przetwarza je na wywołania narzędzi, informacje o ładowaniu umiejętności, aktualizacje planu oraz ostateczną odpowiedź.

    Dodawanie umiejętności bez żadnych zmian w kodzie

    Załóżmy, że chcemy umiejętność do przeglądania kodu. Stwórzmy dla niej folder obok pozostałych:

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

    Następnie napisz plik SKILL.md dla niego:

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

    Zrestartuj aplikację – wtedy agent będzie wiedział, jak przeglądać kod. agent.py pozostał nietknięty, podobnie jak komenda systemowa. Dodałeś tylko folder, nic więcej. Ten sam podejście pozwala tworzyć bardziej zaawansowanych agentów głębokich.

    Lekcje wyciągnięte z tego doświadczenia

    • Opisy umiejętności są ważniejsze niż instrukcje dotyczące ich używania. Jeśli opis jest słaby, umiejętność nigdy się nie aktywuje. Dostosowywanie pola description, w tym słów wyzwalających oraz ograniczeń negatywnych typu „Nie używać do...”, zajęło więcej czasu niż napisanie samego treści umiejętności, ponieważ to właśnie ten tekst stanowi logikę kierowania działaniami.
    • System plików jest niedoceniany. Wiele tutoriali dotyczących agentów przechowuje wszystko w historii wiadomości, co nadaje się do prostych zadań, ale zawodzi przy pracach wieloetapowych. Zapisywanie wyników pośrednich do ./workspace/ i umożliwianie podagentom ich odczytu sprawia, że wszystkie elementy mogą się prawidłowo połączyć.
  • AGENTS.md łączy wszystko w całość. Bez niego orkiestrator oraz jego podagenty działają osobno. Dzięki niemu dzielą się konwencjami, znają strukturę projektu i unikają dublowania pracy – to niewielki plik o ogromnym wpływie.
  • Zacznij od rozwiązań uniwersalnych, a następnie specjalizuj się poprzez umiejętności. Kuszące jest wypełnianie instrukcji systemu wiedzą specjalistyczną, ale utrzymanie orkiestratora w formie uniwersalnej oraz umieszczenie każdej specjalizacji w plikach SKILL.md sprawia, że system jest modułowy i poddawalny testom. Umiejętności można wymieniać bez ingerencji w sam agenta.
  • Czego dodałaby wersja produkcyjna

    To aplikacja celowo minimalistyczna. Rzeczywiste wdrożenie prawdopodobnie obejmowałoby trwałą pamięć za pomocą LangMem do przywoływania informacji epizodycznych i semantycznych pomiędzy sesjami, integrację MCP z narzędziami korporacyjnymi takimi jak Slack, Jira i bazy danych, punkty kontrolne z udziałem człowieka wykorzystujące mechanizm przerwania LangGraph, kompresję kontekstu przy długich zadaniach badawczych oraz oczywiście więcej umiejętności, z których każda to po prostu folder zawierający plik SKILL.md.

    Kierunek rozwoju wydaje się jasny. Tworzenie agentów polega mniej na pisaniu większej ilości kodu, a bardziej na tworzeniu lepszych plików SKILL.md. Framework zajmuje się infrastrukturą, umiejętności przenoszą wiedzę specjalistyczną, a LLM decyduje, kiedy użyć której z nich.

    Literatura pokrewna

  • LangChain Deep Agents: Planowanie, pliki i podagenty wbudowane — Co dają deep agents dodatkowo w porównaniu z LangGraph: planowanie z funkcją write_todos, wirtualny system plików, podagenty, automatyczne streszczanie i pamięć, a także informacje o tym, kiedy można ich pominąć.