工具 — 数据采集与搜索
🔵 用于从外部获取数据的引擎:HTTP 抓取、本地归档、JSON、RSS、 PDF、地理编码。所有引擎的汇总表见 《工具参考》。
5.4 site_tool
通用声明式 HTTP/API 客户端:请求 → 字段提取 → 输出
格式。尽管名称如此,它不仅仅是抓取器——还能处理任意 REST:
method(GET/POST/PUT/PATCH/DELETE)、请求体 json/form/multipart/body、
自定义请求头、OAuth2、HMAC 签名、Cookie、分页。对于 REST 集成,
可以编写 kind: rest_tool——这是同一引擎的别名(语义更清晰)。
kind: site_tool # или rest_tool — алиас для REST/API
name: my_scraper
description: Парсит страницу товара.
parameters:
type: object
properties:
url: { type: string }
required: [url]
fetch:
url: "{{ input.url }}" # шаблон MiniJinja
method: POST # GET (по умолч.) | POST | PUT | PATCH | DELETE
json: { query: "{{ input.q }}" } # JSON-тело (листья — шаблоны)
# body: "q={{ input.q | urlencode }}" # сырое тело (шаблон)
# form: { search: "{{ input.q }}" } # x-www-form-urlencoded
# multipart: { fields: {...}, files: {...} }
timeout_secs: 15
cache_ttl_secs: 300
follow_redirects: true
cookies: true
headers:
User-Agent: "MyBot/1.0"
Authorization: "Bearer {{ env_var('API_KEY') }}"
# oauth2: # OAuth2 (refresh_token / client_credentials)
# token_url: "https://oauth.yandex.ru/token"
# grant: refresh_token
# client_id_env: X_CLIENT_ID
# client_secret_env: X_CLIENT_SECRET
# refresh_token_env: X_REFRESH_TOKEN
# prefix: "OAuth " # префикс заголовка (Bearer по умолч.; Yandex — OAuth)
source:
type: script_json # JSON внутри <script>
contains: productData
# type: json # или тело само — JSON
extract: { ... }
output: { ... }
fetch — URL
两种风格:
- 直接 URL —
url: "{{ input.url }}"。 - 路径构建器 —
base+path_opt+path+query(示例 —drom_search.yaml):
fetch:
base: "https://auto.drom.ru/"
path_opt:
- when: { has: region }
value: "region{{ region | region_code }}"
- when: { equals: [condition, "new"] }
value: "new"
path:
- "{{ brand }}"
- "{{ model | default('') | slugify }}"
- "{% if page is defined and page > 1 %}page{{ page }}{% endif %}"
query:
unsold: "1"
minprice: "{{ price_from }}"
filters:
slugify: _builtin_
region_code:
moscow: "77"
nsk: "54"
region_code_ci:
case_insensitive: true
default: "77"
"Москва": "77"
filters 将过滤器名称与 _builtin_(例如 slugify)或
键→值表进行匹配。Lookup 表是普通映射,或者是带有
case_insensitive: true 和 default 的对象(值缺失时的回退)。
env_var('NAME') 读取环境变量——用于请求头中的密钥。
query 在直接 url 时也会应用(以 URL 编码方式添加)。
模板:命名空间与函数
在所有配置字段中(url、base/path/path_opt、query、headers、
body、json、form、multipart、output)都可访问同一个上下文:
- 输入参数的扁平名称 —
{{ brand }}、{{ title }}; - 嵌套对象 —
{{ input.brand }}、{{ input.title }}。
也就是说,{{ org }} 和 {{ input.org }} 在任何字段中都可互换(此前
扁平名称仅在 path 中可见)。
可用的 MiniJinja 函数:
| 函数 | 作用 |
|---|---|
env_var('NAME') |
环境变量的值(未设置时为 "") |
state('scope', 'key') |
来自调用方状态的值;scope = agent | user | session |
template_var('name') |
会话模板变量(template_vars),例如 admin_templates |
state(...) 从调用方会话解析 scope,而不是从参数:这样
代理的设置(例如 state('agent', 'repo'),由 state_tool 写入)
无需作为参数传递即可代入请求。缺失的键渲染为空
(undefined);在工具调用之外或未启用
state-store 时调用 state(...) 是渲染错误,而不是静默的空值。
fetch — 通用配置档案(extends)
为了避免重复 base_url 和 Authorization,将它们提取到配置档案中并指定
extends(相对于工具 YAML 的路径):
# tools/_shared.yaml
base_url: "https://api.example.com"
headers:
Authorization: "Bearer {{ env_var('API_KEY') }}"
# tools/list.yaml
fetch:
extends: _shared.yaml
path: ["issues"]
headers:
Accept: "text/plain" # перекрывает Accept из профиля
工具自身的 url/base/headers 优先于配置档案。
配置档案不是工具(它没有 kind):不要将其列入
tool_files,否则加载器无法识别该文件,工具也不会注册。
可以在 templates_list 中检查所有 tool_files 是否真正加载
(字段 tools_declared/tools_loaded/tools_failed)。
fetch — 请求体
body / json / form / multipart 中恰好一个(歧义配置
在加载时会被拒绝)。json: — JSON 请求体,每个字符串叶子都是模板。
form: — urlencoded。multipart: { fields, files } — files 是本地路径。
MiniJinja 中的布尔值/null 以 Python 风格渲染(True/False/None),
这不是有效的 JSON——在原始 body 中请将值通过 tojson 处理:
body: '{"completed": {{ input.completed | tojson }}}'
对于 json 请求体,omit_empty: true 会递归丢弃值为空的
键(""、null、[]、{})——便于 PATCH。
fetch — 重试 / backoff
临时故障会自动重试:HTTP 429、5xx、网络/超时错误。
retries — 尝试次数(默认 3,0 禁用);backoff_ms — 基础
延迟并加倍(默认 3000 → 3 秒、6 秒、12 秒)。
fetch — 分页(token 风格)
对于带有下一页 token 的列表,添加 pagination:
pagination:
token_field: /next_page_token
token_param: page_token
items_field: /issues
max_items: 100
max_pages: 10
工具会自动翻页(聚合元素)并告知在哪里
继续。结果:{ items, page_info: { returned, total, has_more, next_token } }。
LLM 通过 page_token: <next_token> 继续调用。
source — 结构化数据所在位置
| 字段 | 取值 |
|---|---|
type |
script_json(<script> 中的 JSON 数据块)或 json(请求体即 JSON) |
contains |
脚本必须包含的子串(用于 script_json) |
min_length |
脚本最小长度(默认 5000) |
其他 source 类型:
json_ld— schema.org JSON-LD;通过@type选择数据块:source: type: json_ld type_selector: Product # совпадает с "Product" или "https://schema.org/Product" graph: true # искать и во вложенных @graph (по умолчанию true)file_json— 来自本地文件的 JSON(无 HTTP):source: { type: file_json, file: data/input.json }devalue— Nuxt 3 payload(__NUXT_DATA__):解析整数引用 和标记。source: { type: devalue, contains: __NUXT_DATA__, min_length: 1000 }html— 通过 CSS 选择器提取(无需 JSON):
返回source: type: html selector: ".product-card" # повторяющийся элемент-запись fields: title: { selector: ".product-card__name", attr: text } price: { selector: ".product-card__price-current", attr: text } url: { selector: "a.product-card__name", attr: href }{ "items": [ {field: value, …}, …] }。
fetch — 两步 token(auth)
当需要先获取 token(匿名授权)时,添加 auth:
fetch:
auth:
token_url: "https://…/api/v1/auth/anonymous"
method: POST
json: { app: "mobile" } # или body / form / headers
field: token.accessToken # JSON-указатель /a/b или точка a.b
header: Authorization
prefix: "Bearer "
fetch — OAuth2(oauth2)
对于 OAuth2 服务(Bitrix24、amoCRM、Google)—— oauth2。与
auth 不同,该引擎会缓存 access token 直到过期,轮换
refresh token,并在 HTTP 401 时自动刷新 token 并重试请求。密钥
从 env 获取(绝不写入 YAML):
fetch:
oauth2:
token_url: "https://…/oauth2/token"
grant: refresh_token # refresh_token (по умолч.) | client_credentials
client_id_env: OAUTH_CLIENT_ID
client_secret_env: OAUTH_CLIENT_SECRET
refresh_token_env: OAUTH_REFRESH_TOKEN # для grant: refresh_token
# scope: "..." # для grant: client_credentials
header: Authorization # по умолчанию
prefix: "Bearer " # по умолчанию
响应按标准 OAuth2 字段解析:access_token、expires_in
(秒)以及可选的 refresh_token。返回的 refresh_token
会替换缓存中的值以供后续刷新。oauth2 和 auth
互斥。
fetch — 请求签名(sign)
用于需要签名的 API(ЮKassa、Тинькофф、Сбер)的 HMAC-SHA256。规范
字符串是 MiniJinja 模板,带有变量 method、url、body(规范
请求体)和 input:
fetch:
sign:
secret_env: SIGN_SECRET # ключ подписи из env
template: "{{ method }}{{ url }}{{ body }}" # по умолчанию
header: X-Signature # либо header, либо query
# query: sign
encoding: hex # hex (по умолч.) | base64
header 和 query 互斥:签名要么放入请求头,要么
放入 query 参数。
extract — 字段类型
三种类型,按关键字段区分。
简单字段 — 按 JSON 指针导航:
extract:
location:
pointer: /geoInfo/0/text
value: /sub/path # доп. навигация вглубь
fallback: "unknown"
transform: int
指针可以是基于 input 的 MiniJinja 模板;段 *(或 ~first)
可跨越动态键。
数组字段 — 按判别器过滤数组:
year:
pointer: /fields
array_match: { type: year } # первый элемент с .type == "year"
value: /payload
transform: int
photos:
pointer: /gallery/images
collect_all: true # все элементы → массив
value: /image/src
transform: [ { skip: 5 }, { take: 20 } ]
计算字段 — 条件逻辑:
status:
compute:
- when: { op: not_null, pointer: /soldNotification } then: sold
- when: { op: equals, pointer: /shouldShowDeleted, value: true } then: deleted
default: active
op 在发行版中:not_null、equals。
转换
按顺序应用(也可以列表链式):
| 转换 | YAML | 描述 |
|---|---|---|
| Lookup | { lookup: { "1": foo, "2": bar } } |
按表取值(未知 → "?") |
| Truncate | { truncate: 2000 } |
截断到 N 个字符 |
| Take | { take: 20 } |
取前 N 个 |
| Skip | { skip: 5 } |
丢弃前 N 个 |
| Len | len |
数组长度 |
| NotNull | not_null |
若非 null 则为 true |
| Bool | bool |
转为布尔值 |
| Int | int |
转为整数 |
| Filter | { filter: "not {{ item._sold }}" } |
丢弃元素(数组) |
| Transliterate | transliterate |
转写为 slug |
| StripHtml | strip_html |
移除 HTML/XML 标签(<noindex>…</noindex> → 文本) |
output — 格式
可选的 MiniJinja 模板。叶子节点基于 input 和 extracted 渲染。
当字段未能解析时,{{ extracted._warnings }} 可用。没有 output 时,
返回提取出的映射原样。
错误处理
引擎不会 panic:错误会返回带消息的 success: false(HTTP
403/404/429/5xx、网络错误、“未找到结构化数据”)。
5.5 archive_tool
在本地归档中搜索。
kind: archive_tool
name: ngu_search
description: Поиск по архиву НГУ.
parameters:
type: object
properties:
query: { type: string }
required: [query]
mode: search_grouped
archive:
path: data/ngu
default_limit: 10
filters:
- { field: players, op: players_min, param: players_min }
- { field: price, op: gte, param: price_min }
sort:
field: price
order: asc
mode
| 模式 | 用途 |
|---|---|
search |
按关键词搜索 |
search_grouped |
按页面分组的结果(matched_chunks) |
lookup |
按 URL 获取完整文档 |
read_chunks |
分块范围(start_chunk、end_chunk) |
outline |
页面结构:标题、字符/分块数量、分块预览 |
归档是一个目录,包含 meta.json、docs.bin(按分块的文档)和 terms.bin
(词项索引);通过命令 agent-os scrape 构建。
5.6 json_tool
在预加载的 JSON 中搜索。
kind: json_tool
name: find_brands
description: Поиск брендов по имени.
parameters:
type: object
properties:
query: { type: string }
section: { type: string, enum: [auto, moto, truck] }
required: [query]
data_file: data/drom_brands.json
search:
paths: ["{{ input.section | default('*') }}.top", "*.all"]
field: name
query_param: query
mode: contains
limit: 20
dedupe_by: id
source_field: section
computed:
slug: "{{ item.url | path_segment(0) }}"
| 字段 | 描述 |
|---|---|
data_file |
要加载的 JSON 文件 |
search.paths |
搜索的 JSON 路径;* = 通配键 |
search.field |
用于匹配的字段 |
search.query_param |
携带查询的输入参数 |
search.mode |
contains(部分匹配) |
search.limit |
最大结果数 |
search.dedupe_by |
按字段去重 |
computed |
针对每个结果的 MiniJinja 模板(作用域中有 item) |
5.7 rss_tool
在 RSS 订阅源中搜索。
kind: rss_tool
name: rss_search
description: Поиск новостей по ключевому слову.
parameters:
type: object
properties:
site: { type: string }
query: { type: string }
required: [site, query]
feeds:
lenta: "https://lenta.ru/rss"
ria: "https://ria.ru/export/rss2/archive/index.xml"
rbc: "https://rssexport.rbc.ru/rbcnews/news/30/full.rss"
site — feeds 中的键。结果包含 count、标题、摘要、URL。
5.8 pdf_tool
按页读取 PDF。
kind: pdf_tool
name: pdf_read
description: Скачать и прочитать PDF.
parameters:
type: object
properties:
url: { type: string }
page: { type: integer, default: 1 }
required: [url]
fetch:
url: "{{ input.url }}"
timeout_secs: 30
cache_ttl_secs: 3600
pdf:
chars_per_page: 3000
output:
page: "{{ extracted.page }}"
total_pages: "{{ extracted.total_pages }}"
text: "{{ extracted.text }}"
next_page: "{{ extracted.next_page }}"
5.15 geo_tool
地理编码和地点(OpenStreetMap)。免费、无需密钥,地理编码和 POI 搜索 (Nominatim + Overpass API)——智能体不会编造地址。
kind: geo_tool
name: geo_places
description: Найти места категории рядом.
operation: places
| operation | 用途 | 参数 |
|---|---|---|
locate |
地点地理编码 → 坐标 + bbox | query |
places |
附近 / bbox 内的 POI 类别 | category、place/bbox、limit |
distance |
按距离对地址排序 | place、addresses |
类别(俄语/英语同义词):сто/автосервис/car_repair、шиномонтаж、
аптека/pharmacy、банк/атм、кафе/ресторан、школа、магазин/
продукты/супермаркет、заправка/азс、парковка。未知类别 →
Overpass 搜索 shop=<value>。
可选:nominatim_url / overpass_url、user_agent、ui。
5.15.1 rasp_tool
交通时刻表(火车、通勤列车、飞机、巴士、轮船、
渡轮),通过免费的 Yandex.Raspisaniya API。授权使用免费密钥
(apikey query 参数),在开发者后台
developer.tech.yandex.ru 发放(API“Yandex.Raspisaniya”)。
kind: rasp_tool
name: rasp
description: Расписания транспорта Яндекса.
api_key_env: YANDEX_RASP_API_KEY # или api_key: "..." (не рекомендуется)
lang: ru_RU # опц., язык ответов
| 字段 | 描述 |
|---|---|
api_key / api_key_env |
免费密钥(内联或环境变量) |
lang |
响应语言(默认 ru_RU) |
操作(参数 action):
| 操作 | API 方法 | 作用 |
|---|---|---|
station_list |
stations_list |
按名称搜索车站(query)→ 名称 + 代码 |
search |
search |
A→B 路线(from、to、date、transport_types) |
schedule |
schedule |
车站时刻板(station、date、direction) |
thread |
thread |
班次详情(uid) |
nearest_stations |
nearest_stations |
距离某点最近的车站(lat、lng) |
nearest_settlement |
nearest_settlement |
最近的居民点(lat、lng) |
车站既可以用名称指定(会自动解析为代码),也可以用现成的
代码 yandex_code(例如 s2000006)。对于 search,城市会解析为
settlement 代码(城市→城市),对于 schedule —— 解析为具体车站(优先
真正的火车站)。
{ "action": "station_list", "query": "Москва" }
{ "action": "search", "from": "Москва", "to": "Санкт-Петербург", "date": "2026-09-15", "transport_types": ["train"] }
{ "action": "schedule", "station": "Москва", "date": "2026-09-15", "direction": "departure" }
{ "action": "thread", "uid": "122S_15_2" }
{ "action": "nearest_stations", "lat": 55.75, "lng": 37.61 }
transport_types:plane、train、suburban、bus、water(默认——全部)。
API 的强制要求——注明 Yandex 为数据来源。
5.23 browser_tool
无头浏览器。对于通过 JavaScript 渲染的网站(SPA、
无限滚动、登录)——普通的 HTTP 抓取 site_tool 不够。browser_tool
在 Chromium 中加载页面,执行交互并返回结果——
要么通过在页面内部执行的 JS 脚本(“内部脚本编写”),
要么通过渲染后的文本。
浏览器会在系统中查找,如果没有——则自动下载
(chrome-headless-shell 来自 Chrome-for-Testing;与 OfficeCLI 的 PDF 导出
逻辑相同)。
浏览器进程会被复用(池):每次调用都会在存活的 Chromium 中打开一个全新的
隔离标签页(CDP browser context),并在完成后将浏览器归还到池中——重复调用无需支付
约 1–2 秒的启动开销。数量
进程数量受变量 AGENT_OS_MAX_BROWSERS 限制(默认 4);
多余的调用会等待空闲槽位(最多 60 秒,之后报错)。
kind: browser_tool
name: scrape_spa
description: Извлечь товары из JS-листинга.
parameters:
type: object
properties:
url: { type: string }
required: [url]
steps: # необязательные действия, по порядку
- { type: goto, url: "{{ input.url }}" }
- { type: wait, selector: ".product-card" }
- { type: click, selector: "button.load-more" }
- { type: type, selector: "#q", text: "{{ input.q }}" }
- { type: check, selector: "input[name=agree]" }
- { type: eval, script: "return document.title" }
- { type: screenshot, name: "page.png" }
- { type: pdf, name: "page.pdf" }
script: | # JS внутри страницы → итоговый результат (JSON)
[...document.querySelectorAll('.product-card')].map(c => ({
title: c.querySelector('h2')?.innerText,
price: c.querySelector('.price')?.innerText,
}))
timeout_secs: 30
步骤
type |
字段 | 操作 |
|---|---|---|
goto |
url |
跳转并等待 readyState |
wait |
ms 或 selector |
暂停或等待选择器 |
click |
selector |
点击第一个匹配的元素 |
type |
selector, text |
填充 <input>/<textarea>/<select> 并触发事件 |
check |
selector, checked? |
设置复选框/单选框的 checked(默认 true) |
eval |
script |
在页面中执行 JS(结果会被保存) |
screenshot |
name |
PNG 截图 → 文件 |
pdf |
name |
打印为 PDF → 文件 |
脚本与结果
步骤中的字符串字段(url、selector、text)是基于
input 的 MiniJinja 模板。script / eval.script 中的 JS 在页面上下文中执行,调用参数
可作为全局变量 input 访问(例如 input.url)。
结果:如果 script(或最后一个 eval)返回了值——它会进入
data 和 content(字符串或 pretty-JSON);否则返回页面文本。
screenshot/pdf 会生成可下载的块 file 到 visual 中;对象数组
还会额外渲染为表格。
5.24 web_search
🔵 通过提供商进行互联网搜索(默认 FireCrawl)。引擎
提供模块 searchers(linked-crate 或 DLL);内核注册 kind: web_search автоматически, когда модуль включён. Демо-агент searchers-demo
随附提供。
支持的提供商:firecrawl(默认)、tavily、brave、
serper、serpapi、exa、google(CSE)、searxng(自托管,无需密钥)。
每个都有自己的密钥/环境变量——完整表格见页面
模块 searchers。
调用参数(由 LLM 指定):
| 参数 | 必填 | 描述 |
|---|---|---|
query |
是 | 搜索查询 |
limit |
否 | 结果数量,1–20(默认 5) |
provider |
否 | 提供商(默认 firecrawl) |
配置:
kind: web_search
name: web_search
description: Поиск в интернете (title, url, snippet).
# provider: firecrawl # firecrawl|tavily|brave|serper|serpapi|exa|google|searxng
# api_key: fc-... # явный ключ (не хранить в git)
# api_key_env: FIRECRAWL_API_KEY # по умолчанию — своя для провайдера
# base_url: https://api.firecrawl.dev
# cx: <google-cse-id> # только для provider: google
# limit: 5
密钥按以下顺序解析:配置中的 api_key → 变量 api_key_env
(默认每个提供商各有自己的:FIRECRAWL_API_KEY、TAVILY_API_KEY、
BRAVE_API_KEY、SERPER_API_KEY、SERPAPI_API_KEY、EXA_API_KEY、
GOOGLE_API_KEY)。searxng 不需要密钥。密钥也可以通过模块
设置 指定——值会投射到相应的变量中,
无需重启。
结果——JSON:{ provider, query, count, results: [ { title, url, description } ] }。
模块、演示代理和详细信息——见页面 模块 searchers。