🤖 AI-агенты для бизнеса

5. Инструменты — основы

🔵 Общая механика всех инструментов: поля YAML, шаблоны MiniJinja, визуальные блоки, встроенные движки. Сводную таблицу всех движков см. в «Справочник инструментов».

5.1 Общие поля

Каждый инструмент-YAML разделяет общие верхнеуровневые поля:

Поле Описание
kind дискриминатор движка (обязательно)
name имя инструмента, которое вызывает LLM (по умолчанию — имя файла)
description текст, показываемый LLM для function-calling
parameters JSON Schema аргументов инструмента
ui рендер в виджете: icon, call_template, result_template, failure_template, плюс result_blocks / chart / table
visual UI-only payload (упорядоченный список блоков) поверх сырого extracted — показывается пользователю, никогда не попадает в контекст LLM
warnings декларативные правила предупреждений, всплывающие в трейсах

ui

ui:
  icon: 🚗
  call_template: "Ищу {{ input.query }}..."      # до выполнения
  result_template: "{{ extracted.total }} найдено"  # после успеха
  failure_template: "⚠️ {{ error }}"                # после ошибки

Шаблоны — MiniJinja. call_template видит input; result_template видит extracted. При неудаче (success: false) result_template пропускается, а failure_template (если задан) рендерится с error (сообщение), status (HTTP-статус, если есть), extracted (распарсенное тело ошибки, если JSON), content (сырьё) и success: false. Без failure_template показывается сырой текст ошибки.

Визуальные блоки (графики и таблицы)

Инструмент может вернуть визуальный слой — упорядоченный список типизированных блоков (text, chart, table, file). Блоки рендерятся в веб-виджете (Chart.js + таблицы + ссылка на скачивание файла), Android (Vico + Compose) и Telegram (PNG + таблица + документ).

Два слоя: данные для LLM и презентация для UI

Результат инструмента служит двум потребителям, и их нельзя смешивать:

Слой Кому Что несёт
content (строка) LLM компактная сводка результата
visual (опционально) UI полные данные / готовые блоки, минуя LLM
result_blocks клиенту упорядоченные блоки, отправляемые по проводу

content по-прежнему уходит в контекст модели; visual — только в UI и никогда в LLM. Клиент вычисляет result_blocks либо из visual, либо из content + ui-шаблонов.

Способы задать блоки

  1. ui.result_blocks / ui.chart / ui.table — декларативные шаблоны поверх extracted:

    ui:
      result_blocks:
        - type: text
          markdown: "Найдено **{{ extracted.count }}**"
        - type: table
          columns: [{ key: model, title: "Модель" }]
          rows: "{{ extracted.items | tojson }}"
    

    ui.chart: и ui.table: — сокращения для одного блока; result_template: — один text-блок.

  2. visual — верхнеуровневое поле, рендерится поверх сырого extracted (не content), уходит напрямую в UI, а content остаётся компактной сводкой:

    output:
      count: "{{ extracted.items | length }}"   # компактная сводка → LLM
    visual:
      - type: table
        rows: "{{ extracted.items | tojson }}"  # полные данные → только UI
    
  3. Специализированные движкиchart_tool / table_tool / visualize_tool сами заполняют content (сводка) и visual (полный блок). Скриптовые инструменты задают visual ключом в run(args) (см. 07-scripts).

  4. Fenced-блоки в ответе модели```chart {…} и ```table в markdown-ответе парсятся клиентом.

Формат блоков

Блок chart несёт spec:

{
  "type": "bar",            // bar | line | area | pie | scatter
  "title": "Цены",
  "labels": ["Q1", "Q2", "Q3", "Q4"],
  "series": [ { "name": "Цена", "values": [10, 20, 15, 30] } ],
  "options": { "stacked": false }
}

Блок table несёт columns + rows (массив объектов), опционально title, page_info и options (sortable/searchable/paged):

{
  "type": "table",
  "columns": [
    { "key": "model", "title": "Модель", "align": "left" },
    { "key": "price", "title": "Цена",  "align": "right" }
  ],
  "rows": [ { "model": "A", "price": 100 } ],
  "page_info": { "returned": 50, "total": 500, "has_more": true },
  "options": { "sortable": true, "searchable": true }
}

Блок file несёт файл для скачивания — name (имя файла), mime и data (base64-содержимое). Создаётся движком file_tool:

{
  "type": "file",
  "name": "report.html",
  "mime": "text/html",
  "data": "PGgxPkhpPC9oMT4="
}

Ленивая пагинация (continuation)

Блок table может нести page_info и continuation — клиент запрашивает следующую страницу через POST /v1/continuations с id, сервер повторно вызывает инструмент с сохранёнными аргументами (без LLM-раунда). Web — infinite scroll, Telegram — кнопка «Load more». Первая страница рендерится всегда; мёртвый id просто отключает подгрузку.

warnings

warnings:
  - contains: '"total_server": 0'   # подстрока в сериализованном выводе
    message: "Нет результатов"
  - failed: true                    # срабатывает при success=false
    message: "Инструмент упал"

Сработавшие предупреждения видны в трейсах (trace_sessions, trace_search).


5.2 Шаблоны и переменные

Инструменты используют MiniJinja (подмножество Jinja2) в полях-шаблонах: url, заголовки, тело запроса, call_template / result_template / failure_template, output, указатели в extract, summary, computed.

Контекстные переменные

Переменная Где доступна Значение
input fetch (url/headers/body), extract (указатели), call_template, output аргументы вызова инструмента
extracted result_template, output, visual извлечённые поля
item json_tool.computed текущий элемент результата
count summary (chart/table/visualize) число элементов
error / status / content / extracted failure_template детали ошибки (сообщение, HTTP-статус, сырое тело, распарсенное тело)
env_var('NAME') везде (функция) значение переменной окружения процесса

Встроенные фильтры MiniJinja

Фильтр Описание
slugify в нижний регистр, неалфавитно-цифровые → _
transliterate кириллица → латиница (для slug)
urlencode percent-encoding для URL
tojson сериализация значения в корректный JSON (true/false/null, кавычки)
strip_html убрать HTML/XML-теги
fromunixtime unix-время → строка даты
кастомные из filters: таблица ключ→значение (lookup) с case_insensitive и default

Пост-рендер: MiniJinja выводит null/булевы как None/True/False — движок преобразует их обратно в корректный JSON null/true/false.

Переменные в инструкциях агента

В instructions: агента доступны подстановки, заполняемые ядром при создании сессии (пишутся заглавными в двойных подчёркиваниях):

Плейсхолдер Значение
__DATE__ / __WEEKDAY__ / __TIME__ текущая дата / день недели / время (локальное сервера)
__GEOIP__ город пользователя (резолв по IP; unknown, если не определён)
__COUNTRY__ / __COUNTRY_CODE__ страна / код страны
__USER_ID__ id пользователя
__ADMIN_TEMPLATES__ скоуп видимости админа (* или список шаблонов)

Пример из drom-agent: User's city: __GEOIP__. If user doesn't specify region, use this city.


5.3 Встроенные инструменты

Реализация уже в ядре, YAML не нужен — перечисли имя в tools: в agent.yaml.

Имя Назначение Ключевые параметры
http_fetch скачать URL url, max_chars (по умолчанию 8000, 0=всё), start_char
fs_read прочитать файл path, max_chars, start_char
job_done сигнал завершения reason (или схема output_schema)