🤖 AI-агенты для бизнеса

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 — лимиты полномочий, каналы — линии связи.

Принципы хорошего агента

Шаблон → процесс → сессия

Ты описываешь шаблон (рецепт). Ядро создаёт процесс по первому запросу (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 Практики инструкций


4.6 Live reload

При live_reload: true (по умолчанию) можно править agent.yaml, инструменты и хуки на лету: новые сессии берут новые инструкции, перерегистрированные инструменты/хуки действуют со следующего вызова. Уже запущенные процессы сохраняют исходный системный промпт.