Skills для AI-агентов: динамическое расширение через файловую систему | AiManual
AiManual Logo Ai / Manual.
20 Янв 2026 Гайд

Skills для AI-агентов: как динамически расширять возможности общих агентов через файловую систему

Практическое руководство по созданию AI-агентов с динамически загружаемыми навыками через файловую систему. Архитектура, реализация, примеры на 2026 год.

Почему твой AI-агент не должен быть монолитом

Представь себе ситуацию: ты создал универсального AI-агента. Он умеет анализировать код, писать документацию, ревьюить пулл-реквесты. Ты гордишься своей работой, пока не понимаешь одну простую вещь - каждый раз, когда тебе нужно добавить новую функциональность, ты лезешь в системный промпт. Добавляешь новые инструкции. Тестируешь. Ломаешь старую логику. Откатываешь. Это порочный круг.

Самый частый вопрос, который я слышу от разработчиков: "Почему мой агент, который вчера прекрасно работал, сегодня забыл базовые инструкции?" Ответ прост - контекстное окно. Даже у GPT-5.2 с его 256K токенов есть физические ограничения.

Вот что происходит на практике: ты добавляешь новую фичу в промпт. Контекст раздувается. Агент начинает "забывать" старые инструкции. Ты оптимизируешь промпт. Снова что-то ломается. Это напоминает попытку запихнуть библиотеку в чемодан - что-то всегда выпадает.

Файловая система как оперативная память агента

Забудь про монолитные промпты. Современный подход - это динамическая загрузка навыков. Представь, что твой агент - это операционная система. Файловая система - его память. Навыки (skills) - это исполняемые файлы, которые можно загружать и выгружать по требованию.

💡
Вспомни статью про контекст-инжиниринг. Файловая система здесь работает не просто как хранилище, а как контекстный менеджер, который фильтрует и структурирует информацию перед отправкой агенту.

Как это выглядит в реальности

Возьмем Deep Agents CLI - один из самых продвинутых фреймворков на начало 2026 года. Его архитектура построена вокруг концепции динамических навыков. Вот базовая структура:

agent_skills/
├── code_analysis/
│   ├── SKILL.md
│   ├── tools.py
│   └── examples/
├── documentation/
│   ├── SKILL.md
│   ├── templates/
│   └── validators.py
├── security_scan/
│   ├── SKILL.md
│   ├── scanners/
│   └── rules/
└── deployment/
    ├── SKILL.md
    └── workflows/

Каждая папка - это самостоятельный навык. SKILL.md - это инструкция для агента, написанная на естественном языке. Когда агенту нужно выполнить задачу, он не загружает все навыки сразу. Он смотрит в файловую систему, находит нужный SKILL.md, загружает его в контекст и выполняет.

SKILL.md - сердце динамического навыка

Это не просто markdown-файл. Это контракт между разработчиком и агентом. Давай разберем структуру на реальном примере навыка для анализа кода:

# Code Analysis Skill

## Purpose
Анализирует код на предмет:
- Сложности функций
- Нарушений code style
- Потенциальных багов
- Оптимизаций производительности

## Input Requirements
- Путь к файлу или директории
- Язык программирования
- Конфигурация анализа (строгий/быстрый)

## Output Format
JSON с полями:
- complexity_score: 0-100
- issues: список проблем
- suggestions: рекомендации
- estimated_refactor_time: минуты

## Tools Available
- cyclomatic_complexity()
- style_check()
- bug_patterns()
- performance_analysis()

## Examples
### Пример анализа Python-функции
python
def calculate(items):
    total = 0
    for item in items:
        if item.valid:
            total += item.value
    return total


Анализ: сложность 15/100, одна рекомендация по использованию sum()

## Error Handling
- Если файл не найден: возвращать ошибку FILE_NOT_FOUND
- Если язык не поддерживается: возвращать UNSUPPORTED_LANGUAGE
- При таймауте анализа: возвращать PARTIAL_RESULTS

Обрати внимание на несколько ключевых моментов. Во-первых, навык самодостаточен. Агенту не нужно знать внутреннюю реализацию. Ему достаточно прочитать SKILL.md, чтобы понять, что делать. Во-вторых, навык определяет четкий интерфейс - что на входе, что на выходе, какие инструменты доступны.

Самая частая ошибка: делать SKILL.md слишком общим. "Анализирует код" - это плохо. "Анализирует Python-код на предмет сложности функций и нарушений PEP8 с использованием radon и flake8" - это хорошо. Конкретика решает.

Динамическая загрузка: как это работает технически

Теперь самое интересное - как агент понимает, какие навыки доступны и когда их загружать. Вот упрощенная реализация на Python с использованием актуальных библиотек 2026 года:

import os
import yaml
from pathlib import Path
from typing import Dict, List
from dataclasses import dataclass

@dataclass
class Skill:
    name: str
    description: str
    path: Path
    metadata: Dict
    tools: List[str]

class SkillLoader:
    def __init__(self, skills_dir: str = "./skills"):
        self.skills_dir = Path(skills_dir)
        self.skills_cache = {}
        
    def discover_skills(self) -> List[Skill]:
        """Сканирует файловую систему и находит все навыки"""
        skills = []
        
        for skill_dir in self.skills_dir.iterdir():
            if skill_dir.is_dir():
                skill_file = skill_dir / "SKILL.md"
                if skill_file.exists():
                    skill = self._load_skill(skill_dir)
                    skills.append(skill)
                    
        return skills
    
    def _load_skill(self, skill_dir: Path) -> Skill:
        """Загружает отдельный навык из директории"""
        skill_file = skill_dir / "SKILL.md"
        metadata_file = skill_dir / "skill.yaml"
        
        # Читаем основное описание
        with open(skill_file, 'r', encoding='utf-8') as f:
            content = f.read()
        
        # Парсим метаданные
        metadata = {}
        if metadata_file.exists():
            with open(metadata_file, 'r') as f:
                metadata = yaml.safe_load(f)
        
        # Извлекаем имя и описание из первых строк SKILL.md
        lines = content.split('\n')
        name = lines[0].replace('#', '').strip() if lines else "Unknown"
        description = lines[2].strip() if len(lines) > 2 else ""
        
        # Ищем доступные инструменты
        tools = []
        for file in skill_dir.glob("*.py"):
            if file.name != "__init__.py":
                tools.append(file.stem)
        
        return Skill(
            name=name,
            description=description,
            path=skill_dir,
            metadata=metadata,
            tools=tools
        )
    
    def get_skill_context(self, skill_name: str) -> str:
        """Возвращает контекст для конкретного навыка"""
        if skill_name not in self.skills_cache:
            skill = self._find_skill(skill_name)
            if skill:
                with open(skill.path / "SKILL.md", 'r') as f:
                    self.skills_cache[skill_name] = f.read()
        
        return self.skills_cache.get(skill_name, "")
    
    def _find_skill(self, skill_name: str):
        """Находит навык по имени"""
        for skill_dir in self.skills_dir.iterdir():
            if skill_dir.name == skill_name:
                return self._load_skill(skill_dir)
        return None

Эта реализация делает несколько важных вещей. Во-первых, она кэширует загруженные навыки - не нужно читать файловую систему каждый раз. Во-вторых, она извлекает метаданные из структурированных файлов (skill.yaml). В-третьих, она автоматически определяет доступные инструменты по Python-файлам в директории навыка.

Интеграция с Claude Code и другими агентами

На 2026 год Claude Code 3.7 (последняя стабильная версия) поддерживает динамическую загрузку инструментов через MCP (Model Context Protocol). Вот как выглядит интеграция:

from claude_code import ClaudeCodeAgent
from skill_loader import SkillLoader

class DynamicSkillAgent(ClaudeCodeAgent):
    def __init__(self, skills_dir: str):
        super().__init__(model="claude-3.7-code")
        self.skill_loader = SkillLoader(skills_dir)
        self.available_skills = self.skill_loader.discover_skills()
        
    async def process_task(self, task: str) -> str:
        """Обрабатывает задачу, динамически подключая нужные навыки"""
        
        # Шаг 1: Определяем, какие навыки нужны
        required_skills = self._identify_required_skills(task)
        
        # Шаг 2: Загружаем контекст для каждого навыка
        skill_contexts = []
        for skill_name in required_skills:
            context = self.skill_loader.get_skill_context(skill_name)
            skill_contexts.append(context)
        
        # Шаг 3: Формируем финальный промпт
        system_prompt = self._build_system_prompt(skill_contexts)
        
        # Шаг 4: Выполняем задачу
        response = await self.generate(
            system_prompt=system_prompt,
            user_message=task
        )
        
        return response
    
    def _identify_required_skills(self, task: str) -> List[str]:
        """Определяет, какие навыки нужны для задачи"""
        # Здесь может быть простая эвристика или ML-модель
        skills = []
        
        task_lower = task.lower()
        
        for skill in self.available_skills:
            skill_name_lower = skill.name.lower()
            skill_desc_lower = skill.description.lower()
            
            # Проверяем совпадение по ключевым словам
            if any(keyword in task_lower for keyword in ["code", "analyze", "review"]):
                if "code" in skill_name_lower or "analysis" in skill_desc_lower:
                    skills.append(skill.name)
            
            if any(keyword in task_lower for keyword in ["document", "write", "readme"]):
                if "document" in skill_name_lower:
                    skills.append(skill.name)
        
        return skills[:3]  # Ограничиваем 3 навыками для экономии контекста

Ключевой момент здесь - _identify_required_skills. В реальной системе это должна быть отдельная ML-модель или хотя бы продвинутая эвристика. Но даже простой подход по ключевым словам работает в 80% случаев.

Bash-инструменты: когда Python - это overkill

Не все навыки требуют сложной логики на Python. Иногда простой bash-скрипт решает задачу лучше. Вот как интегрировать bash-инструменты в систему навыков:

#!/bin/bash
# skills/git_operations/tools.sh

# Навык: Git Operations
# Файл: tools.sh

git_analyze_repo() {
    local repo_path="$1"
    
    cd "$repo_path" || return 1
    
    # Анализ активности
    echo "{\"analysis\": {"
    echo "  \"total_commits\": $(git rev-list --count HEAD),"
    echo "  \"active_authors\": $(git shortlog -s -n | wc -l),"
    echo "  \"last_commit\": \"$(git log -1 --format=%cd --date=short)\","
    echo "  \"branch_count\": $(git branch -r | wc -l)"
    echo "}}"
}

git_create_branch() {
    local branch_name="$1"
    local from_branch="${2:-main}"
    
    git checkout "$from_branch"
    git pull origin "$from_branch"
    git checkout -b "$branch_name"
    
    echo "{\"status\": \"success\", \"branch\": \"$branch_name\"}"
}

А вот как агент вызывает эти инструменты:

import subprocess
import json

class BashSkillExecutor:
    def execute_bash_tool(self, skill_dir: Path, tool_name: str, args: list):
        """Выполняет bash-инструмент из навыка"""
        tools_file = skill_dir / "tools.sh"
        
        if not tools_file.exists():
            return {"error": "Tools file not found"}
        
        # Создаем временный скрипт для вызова конкретной функции
        temp_script = f"""
        source {tools_file}
        {tool_name} {' '.join(args)}
        """
        
        try:
            result = subprocess.run(
                ['bash', '-c', temp_script],
                capture_output=True,
                text=True,
                timeout=30
            )
            
            if result.returncode == 0:
                # Пытаемся распарсить JSON вывод
                try:
                    return json.loads(result.stdout)
                except json.JSONDecodeError:
                    return {"output": result.stdout}
            else:
                return {"error": result.stderr}
                
        except subprocess.TimeoutExpired:
            return {"error": "Command timeout"}
💡
Bash-инструменты особенно полезны для операций с файловой системой, Git, Docker и других системных задач. Они легче, быстрее и не требуют зависимостей Python. Но не забывай про безопасность - всегда валидируй входные данные!

Реальный пример: агент для ревью кода

Давай соберем все вместе. Создаем агента, который умеет ревьюить код, используя динамические навыки:

# Структура проекта
mkdir -p code_review_agent/skills
cd code_review_agent

# Создаем навык анализа безопасности
mkdir -p skills/security_analysis
echo '# Security Analysis Skill

## Purpose
Находит уязвимости в коде: SQL-инъекции, XSS, hardcoded secrets.

## Tools
- sql_injection_scanner()
- secret_detector()
- xss_analyzer()

## Examples
...' > skills/security_analysis/SKILL.md

# Создаем навык анализа производительности
mkdir -p skills/performance_analysis
echo '# Performance Analysis Skill

## Purpose
Анализирует производительность кода: сложность алгоритмов, memory leaks.

## Tools
- complexity_calculator()
- memory_profiler()
- bottleneck_finder()
' > skills/performance_analysis/SKILL.md

# Основной скрипт агента
cat > agent.py << 'EOF'
import sys
from dynamic_skill_agent import DynamicSkillAgent

async def main():
    agent = DynamicSkillAgent("./skills")
    
    # Задача от пользователя
    task = "Проанализируй этот Python-код на уязвимости и производительность:\n" + sys.argv[1]
    
    result = await agent.process_task(task)
    print(result)

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())
EOF

# Запускаем агента
python agent.py "def process_user_input(data):\n    query = 'SELECT * FROM users WHERE id = ' + data\n    return execute_query(query)"

Агент автоматически определит, что нужны навыки security_analysis и performance_analysis. Загрузит их контекст. Выполнит анализ. И все это без модификации основного промпта.

Ошибки, которые все совершают (и как их избежать)

Ошибка Почему это плохо Как исправить
Делать SKILL.md слишком общим Агент не понимает, когда применять навык Конкретные входы/выходы, примеры
Хранить состояние в навыках Нарушает идемпотентность, сложно тестировать Навыки должны быть stateless
Загружать все навыки сразу Съедает контекстное окно Динамическая загрузка по требованию
Игнорировать версионирование Ломает backward compatibility Добавить version в skill.yaml

Интеграция с существующими системами

Если у тебя уже есть агент на базе ReAct-архитектуры, добавление динамических навыков займет пару часов. Главное - не переписывать все с нуля, а постепенно мигрировать функциональность в отдельные навыки.

Начни с самого болезненного места. У тебя огромный системный промпт с кучей инструкций по code review? Вынеси их в отдельный навык. Агент забывает контекст между сессиями? Воспользуйся подходом из статьи про динамическую память агентов.

Что дальше? Эволюция навыков

Навыки не должны быть статичными. На 2026 год самые продвинутые системы позволяют навыкам:

  • Самообучаться - навык анализа кода может улучшать свои правила на основе feedback
  • Комбинироваться - навык security + performance = комплексный анализ
  • Генерироваться автоматически - агент создает новые навыки для повторяющихся задач
  • Шариться между агентами - навык, созданный одним агентом, доступен всем

Представь: ты создаешь навык для анализа логов Nginx. Ты его тестируешь, дорабатываешь. Потом ты понимаешь, что этот же навык можно использовать для Apache. Ты добавляешь поддержку нового формата. И теперь у тебя есть универсальный навык анализа веб-логов, который можно использовать в любом агенте.

Самый интересный тренд 2026 года - marketplace навыков. Как Docker Hub для контейнеров, но для AI-навыков. Ты можешь скачать навык для анализа медицинских текстов, навык для трейдинга, навык для юридического анализа. И все это подключается к твоему агенту за минуты.

Начни с малого

Не пытайся перевести весь функционал на навыки за один день. Выбери одну задачу, которая больше всего страдает от ограничений контекстного окна. Создай для нее навык. Протестируй. Если работает - добавь еще один.

Через месяц у тебя будет не монолитный агент, который постоянно что-то забывает, а гибкая система, которая масштабируется вместе с твоими потребностями. И самое главное - ты перестанешь бояться добавлять новую функциональность.

Потому что теперь это не правка гигантского промпта, а создание нового файла в папке skills.