Инструменты — состояние, базы и визуализация
🔵 Хранение состояния (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}}:
- строка → SQL-литерал в одинарных кавычках,
'удваивается; - число /
true/false→ как есть; - отсутствующий или пустой параметр →
NULL(удобно для фильтров вида({{status}} IS NULL OR status = {{status}})).
Если 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 в платформе (вкладка «Документация»).