Инструменты — состояние, базы и визуализация

🔵 Хранение состояния (scoped store, SQL-база DuckDB) и генерация визуальных блоков (графики/таблицы). Сводную таблицу всех движков см. в «Справочник инструментов».

5.11 state_tool

Хранилище состояния со скоупом. kind: state_tool открывает агенту scoped state store ядра. Каждый YAML привязывает одну операцию (op) к одному скоупу + ключу — агенту даётся узкая, целенаправленная способность (например «только читать user/name»).

Конкретный id скоупа (session/user/template) резолвится в рантайме из контекста вызывающего инструмента — LLM никогда не видит чужой id.

Поле Обязат. Описание
kind да state_tool
name да имя для LLM
description да описание для LLM
op да одна операция (см. таблицу ниже)
scope да user | session | agent | team:<id>
key да ключ хранения
key_param нет входной аргумент, перекрывающий key
default нет значение get при промахе
ttl_secs нет TTL для записей
limit / offset нет пагинация чтения коллекций
filter нет JSON-фильтр по коллекциям
parameters нет переопределить авто-схему

Скоупы

Скоуп Хранится под Резолвится из
user user:<user_id> X-User-Id сессии
session session:<session_id> текущая сессия
agent agent:<template> имя шаблона агента
team:<id> team:<id> явно заданный id; вызывающий обязан быть участником команды (см. §45)

Виды значений

Вид Запись Чтение
скаляр set get
список push list, range
множество sadd, srem smembers, scard

delete удаляет любой ключ; keys перечисляет ключи скоупа.

Чтение состояния в шаблонах

Тот же scoped store доступен в шаблонах rest_tool/site_tool через функцию state('scope', 'key'). Запиши настройки в agent-скоуп — и они подставятся в запрос без прокидки их аргументами:

# tools/my_api.yaml
fetch:
  url: "https://api.example.com/repos/{{ state('agent', 'repo') }}/issues"

state(...) резолвит скоуп из вызывающей сессии (как и state_tool); так «настройка агента» применяется к его же инструментам.

op — справочник

op Описание Авто-параметры Возвращает
get прочитать скаляр (или default/null) — значение
set upsert скаляра value {"ok":true}
delete удалить ключ — {"deleted":true|false}
keys список ключей скоупа limit, offset [{key,kind,size}]
push добавить в список value {"seq":N}
list прочитать список limit, offset ["a", …]
range срез списка [from..=to] from, to ["a", …]
sadd добавить в множество value {"added":true|false}
smembers члены множества limit, offset ["x", …]
srem удалить из множества value {"removed":true|false}
scard размер множества — {"count":N}

Чтение коллекций ограничено (по умолчанию 1000 элементов).

filter

filter:
  path: meta.priority
  op: gte               # eq | ne | contains | prefix | gt | gte | lt | lte | exists
  value: 3

Строковые операции регистронезависимы; gt/gte/lt/lte сравнивают численно.


5.17 duckdb_tool

Своя DuckDB-база для агента. Универсальный SQL-доступ к DuckDB: агент получает собственную базу (или доступ к общей), а забота о соединениях и схеме остаётся в движке. Подходит, когда key/value state_tool мало — нужны таблицы, JOIN, агрегации, индексы.

kind: duckdb_tool
name: memory
description: SQL-база фактов пользователя.
path: data/dbs/{user}.db     # файл, ":memory:" или шаблон пути
operation: query             # необязательно: query | execute | schema
init:                        # необязательно: выполняются ОДИН раз (идемпотентно)
  - |
    CREATE TABLE IF NOT EXISTS facts (
      id BIGINT PRIMARY KEY,
      topic TEXT NOT NULL,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    );
readonly: false              # запретить execute
limit: 100                   # страница по умолчанию
max_rows: 1000               # жёсткий кап строк
emit_visual: false           # не рисовать авто-таблицу в чате (по умолч. true)
sandbox: true                # запретить доступ к ФС/ATTACH + lock_configuration
Поле Обязат. Описание
kind да duckdb_tool
name да имя для LLM
description нет описание для LLM
path да файл базы или :memory:. Поддерживает {user}, {session}, {template}/{agent}
operation нет привязать к одной операции; без него операция выбирается аргументом operation (по умолч. query)
init нет строка или список SQL; выполняется один раз при первом открытии базы
readonly нет true запрещает execute
limit / max_rows нет страница по умолчанию / кап строк
emit_visual нет false — не отдавать авто-таблицу query в визуал чата (по умолч. true)
sandbox нет true — выключить доступ к файлам/ATTACH/расширениям и залочить конфигурацию DuckDB
operations нет декларативные операции (см. ниже); при наличии отключает свободный sql
parameters нет переопределить авто-схему

Декларативные операции (operations:)

Если агенту не нужен произвольный SQL, опишите фиксированный набор операций — у каждой свой SQL-шаблон. Наружу торчит только operation + параметры; sql в схеме не появляется и в запрос не попадает.

kind: duckdb_tool
name: content
path: data/dbs/{template}.db
emit_visual: false
sandbox: true
operations:
  plan_list:
    sql: "SELECT * FROM content_plan WHERE ({{status}} IS NULL OR status = {{status}}) ORDER BY updated_at DESC"
  plan_upsert:
    mode: execute          # query (по умолчанию) | execute
    sql: |
      INSERT INTO content_plan (id, title, status) VALUES ({{id}}, {{title}}, COALESCE({{status}}, 'idea'))
      ON CONFLICT (id) DO UPDATE SET title = EXCLUDED.title, status = EXCLUDED.status

Правила подстановки {{param}}:

Если parameters не задан, схема выводится автоматически: operation (enum имён) + строковый параметр на каждый найденный плейсхолдер. mode по умолчанию query; execute — для DDL/DML (уважает readonly).

Когда operations задан, аргумент sql игнорируется — движок исполняет только SQL выбранной операции.

Операции

Без operations: (raw-режим) доступны три операции со свободным sql:

operation Назначение Параметры
query выполнить SQL и вернуть columns + rows sql, limit
execute DDL/DML (CREATE, INSERT, UPDATE, DELETE, …) sql
schema таблицы/вью и их колонки table (необязательно)

query материализует до limit строк и выставляет has_more, когда их больше — агент повторяет запрос со своим LIMIT/OFFSET. Результат query дополнительно кладётся в data (для композиции через visualize_tool source) и в visual (таблица для UI); content — JSON для LLM. Временны́е типы (TIMESTAMP, DATE, TIME) возвращаются строками ISO 8601.

Свои базы через user / session / agent

Плейсхолдеры в path резолвятся из контекста вызова:

path: data/dbs/{user}.db       # одна база на пользователя
path: data/dbs/{template}.db   # одна база на шаблон агента
path: data/dbs/{session}.db    # одна база на сессию

Нерезолвимый плейсхолдер (например {user} без user id) — явная ошибка, а не молчаливое падение в общий файл. Подстановка санитизируется (нельзя выйти из каталога).

Общая база для нескольких инструментов и инстансов

DuckDB допускает одного писателя на файл, поэтому движок держит процесс-глобальный пул соединений по каноническому пути: несколько определений duckdb_tool (и параллельные сессии агентов), указывающих на один файл, делят одно мьютекс-защищённое соединение. init при этом выполняется ровно один раз. Пример — memory + memory_schema на общей {user}.db в config/agents/db-demo/.


5.22 sql_tool

SQL-доступ к внешней PostgreSQL или MySQL — сетевой собрат duckdb_tool с тем же интерфейсом query/execute/schema, но для подключения к существующей учётной / CRM / ERP базе.

kind: sql_tool
name: crm_db
description: Чтение из базы CRM.
dialect: postgres            # postgres | mysql
url_env: CRM_DATABASE_URL    # URL подключения из env (postgres://… / mysql://…)
operation: query             # необязательно: query | execute | schema
readonly: false              # запретить execute
limit: 100                   # страница по умолчанию
max_rows: 1000               # жёсткий кап строк
init:                        # необязательно: выполняется один раз при создании пула
  - SET NAMES utf8mb4;
Поле Обязат. Описание
kind да sql_tool
name да имя для LLM
description нет описание для LLM
dialect да postgres | mysql
url_env да¹ имя env с URL подключения (секреты — не в YAML)
url нет буквальный URL (только для dev; предпочти url_env)
operation нет привязать к одной операции; без него операция выбирается аргументом operation (по умолч. query)
readonly нет true запрещает execute
limit / max_rows нет страница по умолчанию / кап строк
parameters нет переопределить авто-схему

¹ либо url_env, либо url — одно из двух обязательно.

Операции

operation Назначение Параметры
query выполнить SQL и вернуть columns + rows sql, limit
execute DDL/DML (CREATE, INSERT, UPDATE, DELETE, …) sql
schema таблицы/вью и их колонки table (необязательно)

query материализует до limit строк и выставляет has_more; результат кладётся в data (композиция через visualize_tool source) и visual (таблица). Соединения делятся через процесс-глобальный пул по URL: несколько определений sql_tool на одну базу используют один пул, init выполняется один раз. Временны́е типы, uuid и пр. возвращаются строками.


5.12 chart_tool и table_tool

Статичные визуальные данные. Движки для предвычисленных/статичных данных: компактный content (сводка для LLM) + visual (полный блок для UI).

kind: table_tool
name: price_list
description: Показать прайс.
parameters: { type: object, properties: {} }
data_file: data/prices.json
title: "Цены"
summary: "{{ count }} позиций"
columns:
  - { key: model, title: "Модель" }
  - { key: price, title: "Цена", align: right }
options: { sortable: true, searchable: true }
kind: chart_tool
name: sales_chart
description: Продажи по кварталам.
parameters: { type: object, properties: {} }
data_file: data/sales.json       # { type, title, labels, series }
summary: "{{ count }} точек"

Общие поля: kind, name, description, parameters, data_file (путь относительно YAML) или inline data, summary (MiniJinja, контекст count), ui. table_tool дополнительно: title, columns, options (sortable / searchable / paged). chart_tool берёт data как spec (type = bar | line | area | pie | scatter).

Оба движка принимают динамический аргумент data в вызове — он перекрывает статичный data/data_file.


5.13 visualize_tool

Графики и таблицы из произвольных данных. Универсальный собрат chart_tool/table_tool: превращает данные, переданные аргументами, в график или таблицу, с пайплайном трансформаций. LLM отдаёт строки (или spec), движок детерминированно агрегирует.

kind: visualize_tool
name: make_chart
description: Построить график/таблицу из переданных строк.
Аргумент Описание
data массив объектов-строк, или полный spec таблицы/графика
source сослаться на результат прошлого вызова инструмента: { tool, index, field, path, pages }
group_by поле группировки
aggregate { count, sum, avg, min, max } — результаты с префиксом (sum_cost, …)
filter { field: { op: value } }
sort { by, dir: asc|desc }
limit максимум строк
as table | bar | line | area | pie | scatter
title, x, series, columns, options оформление

Композиция инструментов (source)

Вместо копирования данных можно сослаться на результат прошлого вызова в той же сессии:

{ "source": { "tool": "trace_sessions" }, "group_by": "agent",
  "aggregate": { "sum": ["errors"] }, "as": "bar" }

trace_tool's sessions возвращает структурированные строки в data — готовый источник. Явный data выигрывает у source.


5.14 map_tool

Показать карту с одним или несколькими маркерами. Движок резолвит место (геокодинг через Photon — быстрый OSM-геокодер) или принимает явные координаты и отдаёт visual-блок типа map. В веб-виджете рендерится интерактивная карта (Leaflet), Telegram / VK получают статичную картинку + ссылку, Android — кнопки «Открыть в картах» / «в браузере».

kind: map_tool
name: show_map
description: Показать карту с маркером.
provider: osm        # osm (по умолчанию) | google | yandex | 2gis | custom
Поле Описание
provider предустановка тайлов: osm (без ключей), google, yandex, 2gis, custom
tile_url свой URL тайлов ({z}/{x}/{y}/{s}) — перекрывает provider
attribution подпись источника (перекрывает предустановку)
subdomains массив поддоменов для {s}
geocode_url эндпоинт геокодинга (по умолчанию Photon photon.komoot.io/api/)
zoom масштаб по умолчанию (1–20, по умолчанию 14)

Аргументы вызова (любой один источник):

{ "place": "Академгородок, Новосибирск" }                 // геокодинг
{ "lat": 54.85, "lon": 83.10, "label": "Академ" }         // координаты
{ "markers": [ {"lat": 54.85, "lon": 83.10, "label": "A"}, {"lat": 55.0, "lon": 83.5} ] }

label / title / zoom опциональны. Тайлы Google/Yandex/2ГИС — неофициальные предустановки; при необходимости укажите свои tile_url и attribution (соблюдая условия использования провайдера).

Блок map на проводе: { type, title?, center{lat,lon}, zoom?, markers[], tile_url?, attribution?, subdomains?, link } — см. формат блоков.

5.14 scratch_tool

Файлы в черновике сессии (ToolContext.temp_dir) — временном каталоге, который ядро создаёт на каждую сессию. Агент может раскладывать там промежуточные результаты, заметки и патчи, читать их по частям и править, не имея доступа к файловой системе хоста. Движок даёт модуль scratchpad; один YAML-файл описывает одну операцию (op).

kind: scratch_tool
name: scratch_read
description: Прочитать файл из черновика сессии (с постраничным чтением)
op: read                    # list | read | write | edit | delete | mkdir | move
op Аргументы Что делает
list path?, recursive? список записей {path, dir, size} (до 1000)
read path, start_char?, max_chars?, start_line?, line_count?, line_numbers? чтение окном (по символам или строкам)
write path, content, append? создать/перезаписать/дозаписать (каталоги создаются)
edit path, old_string, new_string?, replace_all? или path, start_char, end_char, content замена фрагмента (уникального) или диапазона символов
delete path удалить файл или каталог (рекурсивно)
mkdir path создать каталог с родителями
move path, to переименовать/переместить внутри черновика

Все пути — относительные корня черновика; абсолютные пути и .. отклоняются. Большие файлы читаются окнами: ответ содержит подсказку со смещением (start_char=), чтобы продолжить. Агент-инженер подключает scratch_list/read/write/edit/delete/mkdir/move из config/agents/admin/tools/.

Готовый пример — в config/agents/admin/tools/scratch_*.yaml; справочник типа — tools/scratch_tool в платформе (вкладка «Документация»).