4. Сборка агента
🔵 Полный путь создания агента — только YAML и, при желании, скрипты. Сначала — философия, потом — механика.
4.1 Философия
Прежде чем писать agent.yaml, стоит понять, что ты вообще создаёшь.
Агент — это описание, а не код
В Agent OS агента собирают из YAML: личность, инструменты, хуки, бюджет. Код (Rhai/JS) нужен только когда понадобился кастомный инструмент или хук. Это декларативная сборка: ты описываешь сотрудника в «должностной инструкции», а не пишешь его «мозг». Всю тяжёлую часть — цикл хода, обработку tool-call'ов, учёт токенов, изоляцию, логирование, планирование — делает ядро.
Пять вопросов, определяющих агента
Любой агент — это ответ на пять вопросов. Каждый — отдельная грань, отдельное место в конфиге:
| Вопрос | Поле | Что это |
|---|---|---|
| Кто он? | instructions |
роль, личность, правила поведения и формат ответа |
| Что он умеет? | tools / tool_files |
встроенные и YAML/скриптовые инструменты |
| Как ведёт себя в цикле жизни? | hooks / hook_files |
память, кэш, эскалация, инъекции контекста |
| Сколько ему можно? | budget / budget_group |
лимиты токенов (это деньги) |
| Откуда приходят? | telegram, email, виджет, API |
каналы входа |
Разделение ответственности
Каждая грань живёт в своём файле, поэтому их можно менять по отдельности:
подменить инструмент, не трогая промпт; переписать промпт, не трогая хуки;
перенести лимиты в budgets.yaml, не трогая агента вовсе. Это и есть главный
смысл структуры:
config/agents/<name>/
├── agent.yaml # личность + привязки
├── tools/*.yaml # возможности
├── hooks/*.yaml # жизненный цикл
├── bench/scenarios.yaml # контроль качества
└── tests/*.yaml # тесты инструментов
Агент как сотрудник
Полезная метафора: instructions — должностная инструкция (что делать и как
отвечать), tools — навыки и инструменты на рабочем месте, hooks — рефлексы
и правила (память, кэш частого, передача человеку), budget — лимиты
полномочий, каналы — линии связи.
Принципы хорошего агента
- Приземлённость. Требуй источники и ссылки, запрещай выдумывать, проверяй
инструментом. Пример
ngu-agentзаставляет приводить ссылки на источник;trace-agent— «никогда не угадывай, всегда проверяй вызовом». - Ясная роль и формат. Фиксируй язык и структуру ответа прямо в инструкциях.
- Экономность. Бюджеты — это деньги: кэш (
faq_cache) и ограничения контекста срезают расход. - Наблюдаемость. Покрой агента бенчмарками (
bench/) — иначе регрессии незаметны.
Шаблон → процесс → сессия
Ты описываешь шаблон (рецепт). Ядро создаёт процесс по первому запросу (lazy spawn), а разговор пользователя — это сессия. Меняешь шаблон — новые сессии получают новое поведение; уже запущенные процессы сохраняют старый системный промпт.
4.2 Структура каталога
Сервер сам находит агентов: любая подпапка config/agents/, содержащая
agent.yaml (или agent.yml), становится шаблоном. tool_files и
hook_files разрешаются относительно папки агента.
4.3 agent.yaml — справочник
name: my-agent
welcome: |
👋 Привет! Чем помочь?
instructions: |
You are a helpful assistant.
Use my_tool to answer questions about X.
Answer in Russian.
tools: [http_fetch]
tool_files:
- tools/my_tool.yaml
hooks: [] # имена хуков, зарегистрированных при старте
hook_files:
- hooks/faq_cache.yaml
route_files: # HTTP-маршруты, которые добавляет агент (http_route / static_route)
- routes/api.yaml
max_context_tokens: 32000
model: "" # именованная модель из server.yaml → models:
output_schema: null # JSON Schema структурированного завершения
budget: # опциональное перекрытие бюджета
refill_per_sec: 30
max_bucket: 10000
max_total: 200000
budget_group: my-shared
budget_messages:
session_exhausted: "Лимит диалога исчерпан. Начните новый."
user_exhausted: "Лимит на сегодня исчерпан."
group_exhausted: "Общий лимит исчерпан. Попробуйте позже."
rate_limited: "Слишком много запросов. Подождите."
inherit_budget: false
parent: null
cron: [] # задания по расписанию этого агента
telegram:
token_env: MY_TG_BOT_TOKEN
allowed_user_ids: []
show_typing: true
show_tool_calls: true
email:
imap_host: imap.example.com
imap_port: 993
imap_username: support@example.com
imap_password_env: EMAIL_PASSWORD
allowed_senders: []
smtp:
host: smtp.example.com
username: support@example.com
password_env: SMTP_PASSWORD
from: support@example.com
notify: []
poll_secs: 60
vk:
token_env: VK_GROUP_TOKEN
group_id: 123456789
allowed_user_ids: []
show_typing: true
show_tool_calls: true
poll_wait: 25
Справочник полей
| Поле | Обязат. | По умолч. | Описание |
|---|---|---|---|
name |
да | — | уникальное имя шаблона |
instructions |
да | — | системный промпт |
welcome |
нет | — | приветствие виджета |
tools |
нет | [] |
встроенные инструменты (http_fetch, fs_read, job_done) |
tool_files |
нет | [] |
пути к YAML-инструментам (относительно папки агента) |
hooks |
нет | [] |
имена хуков (зарегистрированы при старте) |
hook_files |
нет | [] |
пути к YAML-хукам (относительно папки агента) |
route_files |
нет | [] |
пути к YAML HTTP-маршрутам http_route/static_route (относительно папки агента; см. §21) |
max_context_tokens |
нет | 32000 |
лимит контекстного окна |
model |
нет | пусто | именованная модель из server.yaml → models: (пусто = дефолтный провайдер) |
output_schema |
нет | — | JSON Schema завершения: заменяет параметры job_done |
budget |
нет | — | {refill_per_sec, max_bucket, max_total} |
budget_group |
нет | — | имя общей бюджетной группы |
budget_messages |
нет | — | сообщения исчерпания (4 уровня) |
inherit_budget |
нет | false |
дети наследуют группу родителя |
parent |
нет | — | имя родителя (дерево процессов) |
cron |
нет | [] |
задания по расписанию, запускающие этого агента |
telegram |
нет | — | {token_env, allowed_user_ids, show_typing, show_tool_calls} |
email |
нет | — | входящий email-канал (см. ниже) |
vk |
нет | — | входящий VK-бот сообщества (см. ниже) |
Каналы ввода/вывода
Шаблон может привязать каналы — входящие источники, маршрутизирующие
сообщения в этого агента: виджет, Telegram-бот, email, HTTP API, webhook.
В agent.yaml это поля telegram и email. Полный разбор всех каналов
(встраивание виджета, конфигурация Telegram/email, sink'и) — в §19.
Структурированный вывод (output_schema)
Задай output_schema, чтобы агент возвращал фиксированную форму вместо
свободного текста. Ядро заменяет параметры job_done этой схемой (добавляя
job_done, если его нет в tools:), и модель обязана завершиться вызовом
job_done(...) с подходящим значением:
output_schema:
type: object
properties:
needs_action: { type: boolean }
summary: { type: string }
required: [needs_action, summary]
Аргументы job_done валидируются до завершения; некорректный вызов
отклоняется (модель видит ошибку и может исправиться в том же цикле).
Проверенное значение доступно вызывающему (например, subagent_tool кладёт
его в data результата).
4.4 Пошагово
Шаг 1 — скелет
mkdir -p config/agents/mybot/tools config/agents/mybot/hooks config/agents/mybot/bench config/agents/mybot/tests
Напиши config/agents/mybot/agent.yaml (см. §4.3).
Шаг 2 — инструмент
Создай config/agents/mybot/tools/my_tool.yaml (см.
справочник инструментов) и перечисли его в tool_files:.
Проверь без сервера:
site_tool config/agents/mybot/tools/my_tool.yaml '{"query":"..."}'
Шаг 3 — хук (опционально)
Создай config/agents/mybot/hooks/<hook>.yaml (см. 06-hooks-reference) и
перечисли его в hook_files:. Проверь без сервера:
hook_tool config/agents/mybot/hooks/<hook>.yaml user "Привет"
Шаг 4 — бюджетная группа
Добавь группу в config/budgets.yaml и привяжи шаблон:
budget_groups:
- name: mybot-shared
refill_per_sec: 30
max_bucket: 30000
max_total: 3000000
agents:
- template: my-agent
group: mybot-shared
Шаг 5 — сценарии бенчмарка
Создай config/agents/mybot/bench/scenarios.yaml:
name: "MyBot"
model: "my-agent"
scenarios:
- name: "Простой вопрос"
turns:
- user: "Как мне ...?"
assert:
contains: [ожидаемое, слово]
min_links: 1
valid_urls: true
Типы проверок: contains (подстроки, без учёта регистра), min_links /
min_urls (число markdown-ссылок), valid_urls (все ссылки http/https).
Запуск (см. 09-operations):
agent_bench --port=3001 --agent=my-agent --max=3
Шаг 6 — тесты инструментов (опционально)
Fixture-тесты (без сети) в config/agents/mybot/tests/my_tool_tests.yaml:
kind: site_tool_tests
tool: tools/my_tool.yaml
tests:
- name: "извлекает заголовок"
input: { url: "https://example.com/p/1" }
fixture: { productData: { title: "Widget", price: 99 } }
assert:
- { path: /title, equals: "Widget" }
- { path: /price, equals: 99 }
Live-тесты (реальный HTTP: kind: site_tool_live_tests,
archive_tool_live_tests, rss_tool_live_tests) вместо fixture: задают
fetch: true. Как их запускать — в 09-operations.
4.5 Практики инструкций
- Называй какой инструмент когда вызывать, включая цепочки из нескольких
шагов и поиск по slug/id (живой пример —
config/agents/drom/agent.yaml). - Требуй копировать
links[]из вывода инструмента в ответ; запрещай голые URL. - Фиксируй язык и формат ответа.
- Используй
__GEOIP__для подстановки города пользователя (см.drom). - Держи агента «приземлённым»: «никогда не угадывай, всегда проверяй вызовом»
(см.
trace-agent).
4.6 Live reload
При live_reload: true (по умолчанию) можно править agent.yaml, инструменты
и хуки на лету: новые сессии берут новые инструкции, перерегистрированные
инструменты/хуки действуют со следующего вызова. Уже запущенные процессы
сохраняют исходный системный промпт.