工具 — 状态、数据库与可视化
🔵 状态存储(scoped store、DuckDB SQL 数据库)与可视化块(图表/表格)的生成。所有引擎的汇总表见 《工具参考》。
5.11 state_tool
带作用域的状态存储。kind: state_tool 为 agent 打开内核的 scoped state
store。每个 YAML 将一个操作(op)绑定到一个作用域 +
键——从而赋予 agent 一个狭窄、有针对性的能力(例如「只
读取 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 |
否 | 覆盖自动 schema |
作用域
| 作用域 | 存储于 | 解析自 |
|---|---|---|
user |
user:<user_id> |
会话的 X-User-Id |
session |
session:<session_id> |
当前会话 |
agent |
agent:<template> |
agent 模板名称 |
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 相同);这样
「agent 的设置」就会应用到它自己的工具上。
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
为 agent 提供专属的 DuckDB 数据库。对 DuckDB 的通用 SQL 访问:agent 获得
自己的数据库(或对共享数据库的访问),而连接与 schema 的管理则留给
引擎。适用于 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 |
否 | 覆盖自动 schema |
声明式操作(operations:)
如果 agent 不需要任意 SQL,就描述一组固定的操作——
每个操作有自己的 SQL 模板。对外只暴露 operation + 参数;sql 不会
出现在 schema 中,也不会进入查询。
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 未指定,schema 会自动推导: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 — 供 LLM 使用的 JSON。时间类型(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 id 时的 {user})是显式错误,而不是
静默回退到公共文件。替换会经过清理(不能逃逸出
目录)。
多个工具和实例共享的公共数据库
DuckDB 允许每个文件只有一个写入者,因此引擎按规范路径维护
进程级连接池:多个指向同一
文件的 duckdb_tool 定义(以及并行的代理会话)共享同一个受互斥锁保护的连接。此时 init 只会执行
一次。示例 — memory + memory_schema 在 config/agents/db-demo/ 中共享 {user}.db。
5.22 sql_tool
对外部 PostgreSQL 或 MySQL 的 SQL 访问——是
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 |
是¹ | 连接 URL 的环境变量名(密钥不放在 YAML 中) |
url |
否 | 字面 URL(仅用于开发;优先使用 url_env) |
operation |
否 | 绑定到单个操作;没有它时,操作由参数 operation 选择(默认 query) |
readonly |
否 | true 禁止 execute |
limit / max_rows |
否 | 默认页 / 行数上限 |
parameters |
否 | 覆盖自动 schema |
¹ 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 的路径)
或内联 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 的 sessions 会将
结构化行返回到 data 中——这是现成的数据源。显式的 data 优先于
source。
5.14 map_tool
显示带一个或多个标记的地图。引擎会解析地点
(通过 Photon 进行地理编码——快速的 OSM 地理编码器),或接受显式
坐标,并返回类型为 map 的 visual 块。在 Web 组件中会渲染
交互式地图(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/2GIS 瓦片是
非官方预设;如有需要,请指定自己的 tile_url 和 attribution
(遵守提供商的使用条款)。
传输中的 map 块:{ type, title?, center{lat,lon}, zoom?, markers[], tile_url?, attribution?, subdomains?, link } — 参见块格式。
5.14 scratch_tool
会话草稿区(ToolContext.temp_dir)中的文件——这是内核为每个会话创建的临时目录。Agent 可以在那里存放中间结果、笔记和补丁,分块读取并修改它们,而无需访问宿主文件系统。引擎提供模块 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=),以便继续。工程师 Agent 从 config/agents/admin/tools/ 接入 scratch_list/read/write/edit/delete/mkdir/move。
现成示例见 config/agents/admin/tools/scratch_*.yaml;类型参考——平台中的 tools/scratch_tool(“文档”选项卡)。