Урок 02 · Основы

Цикл не изменился — просто стало больше инструментов

«Цикл не тронул ни строчки — я просто добавил кое-что в массив TOOLS.» — оригинальный комментарий в s02_tool_use.py.

⏱ ~10 мин · 📝 3 интерактивных компонента · 🧑‍💻 На основе shareAI-lab · s02_tool_use.py

Что нужно изменить, чтобы добавить инструмент?

Агент из S01 умеет только bash. Как добавить read_file / write_file / edit_file?

Первый инстинкт — изменить loop. Неверно. Loop не нужно трогать вообще. Нужны три вещи:

  1. Написать Python-функцию-обработчик (run_read(path, limit)).
  2. Зарегистрировать её в TOOL_HANDLERS ("read_file": lambda **kw: run_read(...)).
  3. Добавить JSON schema в массив TOOLS (сообщить модели, как называется инструмент и какие параметры принимает).

Когда цикл видит блок tool_use, он ищет функцию по block.name в dispatch map, вызывает её и помещает вывод в tool_result. Ровно тот же путь, что и для bash.

# Dispatch map: name → handler lambda
TOOL_HANDLERS = {
    "bash":       lambda **kw: run_bash(kw["command"]),
    "read_file":  lambda **kw: run_read(kw["path"], kw.get("limit")),
    "write_file": lambda **kw: run_write(kw["path"], kw["content"]),
    "edit_file":  lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
}

Как работает маршрутизация в dispatch

Виджет ниже позволяет выбрать запрос tool_use, который мог бы прислать агент, и проследить, как он маршрутизируется к конкретной Python-функции. Обратите внимание: путь определяется полем block.name.

safe_path: защитный рубеж, который нельзя опустить

Предоставляя агенту доступ к файлам, самая частая проблема безопасности — path escape (выход за пределы директории): модель должна работать в /home/user/project/, но присылает read_file("../../etc/passwd").

В s02 есть небольшая функция именно для этого:

def safe_path(p: str) -> Path:
    path = (WORKDIR / p).resolve()  # нормализация, разрешение .. и симлинков
    if not path.is_relative_to(WORKDIR):
        raise ValueError(f"Path escapes workspace: {p}")
    return path

Ключ — два шага: .resolve() + .is_relative_to(): сначала разворачиваем до абсолютного пути, потом проверяем, что он внутри песочницы. Без первого foo/../../etc проскочит; без второго проверки вообще нет.

Определите безопасность пути

Ниже 5 путей, которые могла бы прислать модель в параметре read_file. Какие пропустит safe_path, а какие отклонит? Предположим, WORKDIR = /home/user/project.

Не добавляйте опасные инструменты в агент

Добавить инструмент просто, но помните: каждый новый инструмент расширяет возможности модели. В s02 для bash уже есть чёрный список (rm -rf /, sudo, shutdown), а write_file ограничен через safe_path. Перед добавлением инструмента в продакшн задайте себе три вопроса:

  • Может ли инструмент совершить необратимое действие? (rm, отправка email, git push)
  • Может ли он утечь что-нибудь? (env vars, .ssh, cookies)
  • Может ли вывод ошибки быть воспринят как команда? (prompt injection через вывод инструмента)

В уроке s07 мы добавим «слой разрешений», который выносит эти решения из кода в декларативный формат.

Интерактив

Виджет 1 · Tool Dispatch · как tool_use маршрутизируется к обработчику

Кликните на запрос tool_use и наблюдайте, как block.name находит нужную функцию в цикле, вызывает её и помещает результат в блок tool_result.

Запрос tool_use от модели
Таблица TOOL_HANDLERS

        
Результат выполнения · блок tool_result
(нажмите на любой tool_use выше, чтобы запустить маршрутизацию)
Интерактив

Виджет 2 · Safe Path · проверка 5 путей на escape

Каждый путь проходит через safe_path(). Предположим WORKDIR = /home/user/project. Нажмите «Допустить» или «Отклонить» — проверьте свою интуицию насчёт path escape.

Правильно: 0 / 5
Интерактив

Виджет 3 · Add a Tool · что нужно изменить, чтобы добавить glob

Предположим, нужно добавить инструмент glob(pattern), чтобы агент мог находить файлы по шаблону. Заполните пропуски: нужно изменить три места, без каждого не обойтись.

Место 1 · написать функцию-обработчик
def run_glob(pattern: str) -> str:
    import glob
    return "\n".join(glob.glob(pattern))
Место 2 · зарегистрировать в dispatch map (выберите правильную строку)
Место 3 · добавить schema в массив TOOLS (выберите правильный вариант)