Перейти к содержанию

Агенты и промпты

AI — скальпель, не кувалда (VISION). Агент в Axon — это workflow-функция: workflow с config-формой agent_config (либо ссылка на reusable AgentDefinition через agent_ref). Запускается тем же WorkflowRunner, проходит через те же policy/budget/approval gates, что и любой другой step. Этот manual: как пишут / публикуют / версионируют prompts и AgentDefinitions, как работают AgentToolResolver exposure rule, adaptive model routing, validator retry-with-feedback, и где это в Console.


1. Что это и зачем

Зачем отдельные prompts и agent definitions

Промпт и agent — это переиспользуемые контракты. Если бы каждое workflow описывало свой prompt inline, было бы: - невозможно версионировать (один и тот же email-классификатор в десяти workflow → десять копий, ни одна не «канон»); - невозможно поменять text без публикации всех зависимых definitions; - невозможно гарантировать replay-safety (run, запущенный полгода назад, должен видеть тот же prompt, что был при start).

Поэтому Axon выделяет два immutable versioned DB-сущности:

  • PromptVersion (таблица app.prompt_versions, префикс id pver_<ulid>) — версия prompt template с placeholders. PROMPT-SCOPE.
  • AgentDefinition (таблица app.agent_definitions, префикс id agdef_<ulid>) — версия AgentSpec, в которой prompt pinned конкретным prompt_version_id (не key:latest). AGENTSPEC + AGENTS-FINISH.

И то и другое — append-only versioned: новая версия — новая строка, старая остаётся archived (но не удалена). Это даёт replay determinism: runtime/replay принимает только pver_* / agdef_* (никаких key:latest).

Транспорт LLM

Все LLM-вызовы идут через LiteLLM proxy (deterministic gateway). Бизнес-код использует Pydantic AI / Instructor → LiteLLM. Прямые SDK провайдеров (Anthropic, OpenAI) в бизнес-логике запрещены (CLAUDE.md, ARCHITECTURE-V6 §3 stack). Это нужно: для одного аудит-пути, для adaptive model routing (haiku→sonnet→opus), для observability через Langfuse.

Чем агент не является

  • Не самостоятельным сервисом. Agent — функция внутри workflow.
  • Не отдельным процессом исполнения. Step Runner владеет step semantics; agent — это execution strategy одного model_call / agent_config.
  • Не правом обходить policy/approval. Все side effects через connector tools проходят policy gate; internal/external_* actions — это tool_call/external_write workflow steps, не direct agent tools (см. §4 AgentSpec tool exposure rule).

2. Роли и доступ

Из core/models/auth.py. Полная матрица — Roles-And-Permissions.md.

Действие Permission owner admin manager operator reviewer read_only system
Опубликовать prompt version (publish_prompt_version) prompt:manage
Архивировать prompt version (archive_prompt_version) prompt:manage
Set active prompt version (set_prompt_version_active) prompt:manage
Опубликовать AgentDefinition (publish_agent_definition) agent_definition:manage
Архивировать AgentDefinition (archive_agent_definition) agent_definition:manage
Читать prompts / agents read

Manager НЕ имеет prompt:manage / agent_definition:manage. Это сознательное ограничение: prompt content — часть кодовой поверхности (engineers пишут, owner/admin publish'ат). Manager может смотреть список и историю, но не публиковать. system имеет оба — для seed-flow на старте проекта (см. §7 Lifecycle, project seed agents).

Engineer — не RBAC-роль. Engineer пишет AgentSpec/PromptVersionSpec в коде (modules/agents/ / modules/prompts/), а publish'ит — owner/admin через Console или CLI. Built-in (origin='system'/'code') agents/prompts публикуются через миграции 040/041 (system actor).

Scope discipline: - prompt:manage для instance scope требует instance-wide роли (owner/admin). prompt:manage для project scope — той же роли, но в project_scopes конкретного проекта. - Publish AgentDefinition в project scope, который пинит instance-scoped prompt — разрешено (project AgentDefinition может использовать platform-wide prompt). Обратное — нет: instance AgentDefinition не может пинить project-scoped prompt.

Все мутации идут через CommandEnvelope (sync_apply path) через handlers в core/api/commands/handlers/{prompt_versions,agent_definitions}.py. Backdoor SQL запрещён (VISION инвариант 5).


3. Где это в Console

Раздел Prompts (/{project_id}/prompts для project scope; /admin/prompts для instance scope)

Экран Что на нём
Prompts list таблица: prompt_key, scope (instance/project), latest active version, status (active/archived), origin (system/code/user), updated_at. Кнопка + New prompt (owner/admin).
Prompt detail full content latest active version, placeholders (имя + required + description), description, history (все versions с version, status, published_by, published_at), actions: Publish new version / Archive / Set active (всё через canonical CommandEnvelope; кнопки видны только при prompt:manage).
Publish new version форма: content (textarea, ≤ 32_000 chars), placeholders (parsed из {{ var }} syntax + ручная аннотация description / default), description (≤ 1_000 chars). После submit — POST publish_prompt_version → новая pver_<ulid> → старая active автоматически archive'ится.

Раздел Agents (/{project_id}/agents для project scope; /admin/agents для instance scope)

Экран Что на нём
Agents list таблица: agent_key, scope, latest active version, status, origin, model_profile, updated_at. Кнопка + New agent (owner/admin).
Agent detail агент definition: prompt_key (+ link на pinned pver_*), output schema (output_fields / output_schema_jsonschema / output_schema_python — XOR), tool_keys, validators, retries (0-5), model_profile, instructions/input builder keys, deps_overrides. History версий. Actions: Publish new version / Archive (owner/admin).
Publish new version форма AgentSpec → backend валидирует XOR output schema, scope contract (project ↔ project_id), input builder contract, deps_overrides safety, pin prompt_version_id (если задан) или резолвит latest active по prompt_key+scope.

Console читает только через query API поверх read.prompt_versions / read.agent_definitions projections, никогда напрямую app.*.


4. Концепции (mental model)

Scope и shadowing

Scope Где живёт Когда видится
instance app.prompt_versions / app.agent_definitions с project_id IS NULL во всех проектах инстанса (если не перекрыт project-scope сущностью с тем же ключом)
project то же, но project_id заполнен только в этом проекте; перекрывает instance-scope сущность с тем же prompt_key / agent_key

Shadowing rule: при резолве prompt_key="email_classify" в project prj_X: 1. Сначала ищется project-scoped active (scope='project', project_id='prj_X', prompt_key='email_classify'); 2. Если нет — instance-scoped active (scope='instance', project_id IS NULL, prompt_key='email_classify'); 3. Если нет — LookupError (никаких code-fallback'ов — см. инвариант I-DOMAIN-AGENTSPEC-NO-CODE-FALLBACK-SEEDS, AGENTS-FINISH Bundle A).

Те же правила для AgentDefinition. Для output_schema_ref в model_call — silent fallback на instance scope запрещён, если ref задан как agent_definition:key:<key> в project workflow и project-scope row отсутствует, валидатор отвергает publish с кодом output_schema_ref_instance_scope_not_allowed (явная защита от тихой утечки instance схемы).

Pinning и replay-safety

  • В коде workflow можно ссылаться на agent / prompt по key: agent_ref="key:email_classifier", output_schema_ref="agent_definition:key:email_classifier". Это удобно для authoring.
  • На materialization boundary (publish_definition / create_workflow / edit_workflow) walker резолвит key → конкретный agdef_<ulid> и подменяет ссылку pinned формой: agent_ref="agent_definition:id:agdef_01H...". Та же логика для output_schema_ref.
  • Runtime / replay принимает только pinned формы. Если в workflow_definitions.definition после publish лежит key:* — это баг materialization (workflow runner отвергает fail-closed).
  • Это значит: запущенный полгода назад workflow run при replay/reauthor видит тот же AgentDefinition и тот же PromptVersion, даже если с тех пор вышло 20 новых версий.

Lifecycle prompts

publish_prompt_version  →  новая pver_<ulid> со status='active'
                          (любая предыдущая active с тем же (scope, project, key) → автоматически archived)

set_prompt_version_active(pver_X)  →  X.status = 'active'
                                       (любая другая active с тем же ключом → archived)
                                       enforced: X не должна быть pinned ни одной active AgentDefinition... wait, нет: 
                                       set_active не имеет pin-protection (новый publish заменяет старый active без проблем,
                                       replay-pins на старую версию работают через id)

archive_prompt_version(pver_X)  →  X.status = 'archived'
                                    pin-protection: 
                                    rejected if X is pinned by any active AgentDefinition
                                    (нужно сначала опубликовать новую версию AgentDefinition без этого pin)

Lifecycle AgentDefinitions

publish_agent_definition  →  валидирует AgentSpec, резолвит prompt_version_id (key:latest → конкретный pver_*),
                              резолвит nested output_schema_refs (если AgentSpec их использует),
                              INSERT новой agdef_<ulid> со status='active',
                              старая active с тем же (scope, project, key) → archived,
                              outbox: agent_definition.published

archive_agent_definition  →  status='archived'
                              (вoркflow с pinned agdef_X продолжают работать;
                              новые publish_definition / create_workflow с ref на key:X → ошибка)

Output schema: три формы (XOR)

AgentSpec обязан иметь ровно одну из: - output_fields: list[AgentOutputField] — bounded декларативный список полей; runtime материализует bounded object JSON Schema (output_fields_json_schema()). User-origin AgentSpec обычно использует это. - output_schema_jsonschema: dict — произвольный JSON Schema (engineer-controlled); - output_schema_python: str — fully-qualified class name (например core.agents.demo1_intent.IntentClassification). Только для origin='system'/'code' — user-origin AgentSpec не может ссылаться на Python class (security boundary: code-owned schema).

AgentSpec tool exposure rule (ARCHITECTURE-V6 §11)

AgentToolResolver (core/agents/runtime_registries.py) — gatekeeper между AgentDefinition и tool registry. Правило: direct tools, доступные агенту через AgentSpec runtime, ограничены: 1. side_effect_class="none" — никаких internal/external_low/external_high actions напрямую агенту; 2. executor_profile.allowed_tools (если задан); 3. project.enabled_tools allowlist.

Все три пересечения должны выполниться. Поэтому: если агенту нужно «отправить email», это не direct tool, а отдельный workflow step tool_call (или external_write) с policy gate, approval path, audit trail, idempotency. Agent — это решатель, а не исполнитель сторонних эффектов.

Новая категория «audited read tools» (когда хочется дать агенту GET-like internal action) требует отдельного public contract и не вводится implicit-правилом.

Adaptive model routing (UPGRADE_CHAIN)

AdaptiveModelRouter (core/ai/model_router.py) выбирает LLM-модель по сложности шага: - complexity estimation (core/ai/complexity.py) — две оси: step_type (load_context простой, transform средний, model_call сложный...) + semantic_type (intent_classify простой, plan_generation сложный...). + signal adjustments (tokens, tool count, nested children, reasoning hints). - Адаптивный режим (routing_mode='adaptive') выбирает дешёвую модель по дефолту, эскалирует на validation failure: haiku → sonnet → opus (UPGRADE_CHAIN). Все вызовы (включая failed) записываются в budget_ledger с selected_model, complexity_score, routing_mode. - Фиксированный режим (routing_mode='fixed') использует WorkflowConfig.model_profile (наследует ProjectConfig.default_model_profile).

Инвариант I-DOMAIN-AGENTSPEC-ROUTING-UPGRADE-CHAIN (AGENTS-FINISH Bundle B) фиксирует chain в тестах; если кто-то поменяет порядок haiku→sonnet→opus, негативный тест упадёт.

Validator retry-with-feedback

Если LLM-ответ не прошёл validator (Pydantic, semantic), runtime делает retry с feedback (текст ошибки добавляется в conversation history → LLM получает шанс самоисправиться). Контролируется: - AxonAgentSpec.retries (0-5); - AxonAgentSpec.validator_refs — список валидаторов с фазой исполнения (execution_phase ∈ {'pydantic_output', 'post_acceptance'}). - Фаза pydantic_output — fail вызывает retry (если retries > 0). Фаза post_acceptance — только логирует, не ретраит (post-fact validation).

Инвариант I-DOMAIN-AGENTSPEC-VALIDATOR-RETRY-FEEDBACK (AGENTS-FINISH Bundle C) гарантирует поведение тестами с offline FunctionModel-mock'ом.

Реактивный runtime resolver: RuntimePromptService

core/agents/prompt_runtime.py:RuntimePromptService — DB-backed resolver, который читает prompts из app.prompt_versions (через read.prompt_versions для query path). Используется agent runtime и model_call (когда prompt задан через registry, не inline). До PROMPT-SCOPE был in-memory allowlist в коде; теперь источник — БД, allowlist убран (AGENTS-FINISH Bundle A invariant).


5. Флоу: пошаговые сценарии

5.1 Создать prompt в проекте (owner/admin)

  1. Prompts → + New prompt.
  2. Ввести prompt_key (snake_case, до 128 chars), scope = project (autopicked = текущий проект), content (текст с placeholders {{ var_name }}), description.
  3. Заполнить metadata placeholders (имя, required, description, default) — параметр placeholders модели. Default разрешён только если AgentSpec, который будет пинить этот prompt, не зависит от default (Stage 1 guard, см. §9).
  4. Submit → publish_prompt_version → pre-accept secret scan (reject_secret_like_string — токены/ключи/API URLs отвергаются на boundary) → INSERT pver_<ulid>, старая active с тем же ключом archive'ится. Outbox prompt_version.published. Console обновляет проекцию read.prompt_versions.

5.2 Опубликовать AgentDefinition (owner/admin)

  1. Engineer пишет AxonAgentSpec в коде (modules/agents/<key>.py) или admin заполняет форму в Console.
  2. Console / CLI отправляет publish_agent_definition (target_type='agent_definition', sync_apply, project_id или null для instance).
  3. Handler в transaction:
  4. валидирует AgentSpec (output schema XOR, input builder contract, scope contract, deps_overrides safety, tool_keys uniqueness);
  5. резолвит prompt: если prompt_version_id задан → использует pinned; иначе резолвит latest active по prompt_key + scope (shadowing project→instance);
  6. резолвит nested output_schema_ref если AgentSpec ссылается на другой AgentDefinition;
  7. INSERT agdef_<ulid>, старая active archive'ится. Outbox agent_definition.published.

5.3 Использовать AgentDefinition в workflow

  1. В коде workflow:
    Step(step_key="classify", step_type="model_call", config={
        "agent_ref": "key:email_classifier",      # authoring-form
        # ... input_data refs
    })
    
  2. axon pushpublish_definition → materialization boundary walker подменяет agent_ref="key:email_classifier"agent_ref="agent_definition:id:agdef_01H..." (concrete pin) внутри той же транзакции.
  3. Runtime / replay читает pinned agdef_*, резолвит prompt из RuntimePromptService по pinned pver_*, материализует output schema, выполняет.
  4. Никаких локальных overrides. Если в agent_config есть agent_ref AND свой system_prompt/prompt_template_key/output_schema/validation_rules — publish-time валидатор отвергает как ambiguous configuration. Локальный tools допустим только как narrowing cap (не расширяет toolset AgentDefinition).

5.4 Использовать output schema через output_schema_ref

Когда хочется reusable schema контракт без запуска полноценного agent (просто model_call со structured output): 1. В step config:

Step(step_type="model_call", config={
    "system_prompt": "You are an email classifier.",
    "output_schema_ref": "agent_definition:key:email_classifier",
})
2. Materialization резолвит ref на pinned agdef_* (project scope, shadowing); silent fallback на instance scope запрещён (output_schema_ref_instance_scope_not_allowed). 3. Runtime берёт output_fields_json_schema() из pinned agdef и применяет к LLM-ответу. Step Runner владеет step semantics; AgentDefinition — только schema contract.

См. WORKFLOW-ARCHITECTURE.md §4 для polный список валидируемых boundary cases.

5.5 Обновить prompt (новая версия → ротация)

  1. Open Prompt detail → Publish new version.
  2. Изменить content / placeholders → submit publish_prompt_version.
  3. Новая pver_* становится active, старая → archived автоматически.
  4. AgentDefinitions, которые пинят старую версию, продолжают работать. Чтобы перевести их на новую версию, нужен новый publish_agent_definition (либо явно с новым prompt_version_id, либо без prompt_version_id — handler резолвит latest active).
  5. Архивировать старую prompt version: только если она не pinned ни одной active AgentDefinition (см. §7 pin-protection).

5.6 Дать агенту доступ к новому direct tool

  1. Tool должен иметь side_effect_class="none" (если internal/external — agent через него не сможет, нужен workflow step).
  2. Добавить tool_key в enabled_tools проекта (если ещё нет).
  3. Опционально — добавить в executor_profile.allowed_tools (если профиль ограничивает).
  4. В новом publish_agent_definition указать tool_keys=[..., "new_tool_key"].
  5. После publish — agent runtime через AgentToolResolver отдаст агенту новый tool (если все три гейта пройдены).

6. Справочник опций

6.1 PromptVersionSpec (core/models/prompt_specs.py)

Поле Тип Ограничения
prompt_key str snake_case, ≤ MAX_PROMPT_KEY_LENGTH=128
scope_type 'project' \| 'instance' project_id обязателен для project, NULL для instance
content str MAX_PROMPT_CONTENT_LENGTH=32_000
placeholders list[PromptPlaceholderSpec] MAX_PLACEHOLDERS_PER_PROMPT=32, имена unique, имя ≤ 64 chars
description str | None MAX_DESCRIPTION_LENGTH=1_000
origin 'system' \| 'code' \| 'user' system/code только для seed/migration actor

PromptPlaceholderSpec: name, required: bool, description: str, default: str | None (≤ 1_000 chars). См. core/models/prompt_specs.py:63-156.

6.2 AxonAgentSpec (core/models/agent_specs.py)

Поле Тип Описание
agent_key str regex (см. AGENT_KEY_RE)
prompt_key str reference на prompt (резолвится в pinned prompt_version_id на publish)
prompt_version_id str | None explicit pin (если задан, скипает резолв latest active); префикс pver_
output_fields / output_schema_jsonschema / output_schema_python XOR — ровно одно output schema
prompt_template_vars dict[name → AgentPromptTemplateVar] bindings для placeholders prompt'а
input_template AgentInputTemplate | None required если input_builder_key='template' (default)
tool_keys list[str] unique; будут отфильтрованы AgentToolResolver по AgentSpec exposure rule
validator_refs list[AgentValidatorRef] каждый с execution_phase ∈ {'pydantic_output', 'post_acceptance'}
retries int 0-5 retry с feedback при validator fail в фазе pydantic_output
model_profile str | None LiteLLM alias или ProjectConfig default; вместе с routing_mode='fixed' определяет model
scope_type / project_id scope contract project ⇔ project_id required; instance ⇔ project_id=None
origin 'system' \| 'code' \| 'user' output_schema_python запрещён для user-origin
deps_overrides dict | None только из SAFE_DEPS_OVERRIDE_KEYS; runtime-owned keys reject'ятся

6.3 Команды (CommandEnvelope)

Команда path target_type scope Permission
publish_prompt_version sync_apply prompt_version project XOR instance prompt:manage
archive_prompt_version sync_apply prompt_version project XOR instance prompt:manage
set_prompt_version_active sync_apply prompt_version project XOR instance prompt:manage
publish_agent_definition sync_apply agent_definition project XOR instance agent_definition:manage
archive_agent_definition sync_apply agent_definition project XOR instance agent_definition:manage

target_type prompt_version / agent_definition — в CONDITIONAL_SCOPE_TARGET_TYPES (allow project_id NULL для instance, required для project).

6.4 Read projections

  • read.prompt_versions (alembic 042) — list/detail/history для Console, redacted (content виден; secrets отрезались уже на pre-accept).
  • read.agent_definitions (alembic 039+) — list/detail/version для Console.
  • Refreshers: PromptVersionRefresher, agent definition refresher; подписаны на prompt_version.* / agent_definition.* outbox events.

7. Жизненный цикл и обслуживание

Seed prompts + agents. Migrations 040 (prompt seeds) и 041 (agent seeds) инициализируют instance-scoped built-in: - prompts: канонические system prompts для triage / planner / reviewer / conversation / monitor + demo1.intent_classify + demo1.draft_response_v3; - AgentDefinitions: triage / planner / reviewer / conversation (project-seed pattern — каждый проект получает свои 4 копии через ensure_project_seed_agent_definitions() внутри create_project transaction); monitor — instance-scoped (scope='instance', project_id IS NULL, origin='system').

Per-project seed. core/storage/repositories/agent_definition_seeds.py:PROJECT_AGENT_SEEDS — список template AgentDefinitions, которые create_project handler разворачивает в новый проект (с pinned pver_* на seed prompt versions). Это гарантирует, что новый проект «работает из коробки» без ручного publish.

Версионирование. Append-only. version int monotonically increases per (scope, project_id, agent_key). Status: ровно одна active per ключ, остальные archived. Archived row не удаляется (compliance + replay безопасность для pinned references).

Pin-protection (на archive prompt): - archive_prompt_version(pver_X) → handler проверяет, что pver_X не pinned ни одной active AgentDefinition. Если pinned → reject с prompt_pinned_by_active_agents и списком agdef_*. - Решение: сначала опубликовать новую версию AgentDefinition (без pin'а на pver_X), затем архивировать prompt.

Reactivation. set_prompt_version_active(pver_X) — позволяет вернуть конкретную старую версию в active. Pin-protection здесь не действует (новый active заменяет старый без проблем; pinned references на старую версию продолжают работать через id).

Observability. - Langfuse traces: каждый LLM-вызов → trace со prompt_version_id, agent_definition_id, selected_model, complexity_score, routing_mode, validator outcomes; - budget_ledger rows для каждого LLM-вызова (включая failed validation retries); - Console / API возвращает degraded / stale маркер для read projections если refresher отстал.


8. Траблшутинг

Симптом Причина Что делать
publish_prompt_version → 422 «secret-like string in content» pre-accept secret scan нашёл токен / API key / credential URL в prompt content Убрать секреты из текста (а заодно из draft — secrets никогда не должны попадать в prompts)
archive_prompt_version → 409 prompt_pinned_by_active_agents Версия pinned одной или несколькими active AgentDefinition (replay-safety) Опубликовать новую AgentDefinition без pin'а на эту prompt version, затем повторить archive
publish_agent_definition → 422 «output_fields XOR output_schema_jsonschema XOR output_schema_python (exactly one required)» Указано 0 или 2+ output schema форм Указать ровно одну
publish_agent_definition → 422 «output_schema_python is code-owned and forbidden for user-origin specs» user-origin AgentSpec ссылается на Python class Использовать output_fields или output_schema_jsonschema
publish_agent_definition → 422 «Unsafe deps_overrides keys: [...]» Попытка переопределить runtime-owned dep или unknown key Использовать только ключи из SAFE_DEPS_OVERRIDE_KEYS
publish_agent_definition → 422 «user-origin AgentSpec must use input_builder_key='template'» user-origin ссылается на code-only input builder Использовать input_builder_key='template' + input_template (или сделать origin='code' если есть permission)
publish_definition (workflow) → output_schema_ref_not_resolvable / output_schema_ref_instance_scope_not_allowed Workflow в project_X ссылается на AgentDefinition по key:*, project-scope row отсутствует, silent fallback на instance запрещён Опубликовать AgentDefinition в project_X с тем же key, либо явно указать agent_definition:id:agdef_* если хочешь instance
Workflow runner → agent_definition_not_found После start workflow AgentDefinition был archived (run hold pinned ref на archived row — это норма, но в случае key:* — пытается резолвить latest active, которой нет) Run видит pinned id, не key — обновить ref в новой версии workflow definition
Agent ignores tool Tool либо не side_effect_class="none", либо не в enabled_tools проекта, либо не в executor_profile.allowed_tools См. §4 exposure rule, исправить уровень
Operator хочет audited read tool для агента Это требует отдельный public contract (не «давайте дадим internal action») Либо переделать в tool_call workflow step с policy gate, либо открыть отдельный backlog для «audited read tools» public contract
Adaptive routing постоянно эскалирует до opus Сложность шага оценена как очень высокая, либо validator постоянно fail'ит и заставляет escalate Проверить Langfuse: что валится валидатор? Уточнить prompt / schema / validator rules; либо force routing_mode='fixed' с конкретным model_profile
Run failed → UnexpectedModelBehavior после retries=N попыток LLM не смог удовлетворить validator после ретраев с feedback Увеличить retries (≤ 5), смягчить validator, или поменять model_profile / agent_ref

9. Ограничения и инварианты

  • No code-owned fallback seeds (I-DOMAIN-AGENTSPEC-NO-CODE-FALLBACK-SEEDS, AGENTS-FINISH Bundle A). Built-in agents / prompts seed'ятся только через migrations 040-041 (system actor). In-memory dict built_in_specs.py удалён; AgentSpecResolver поднимает LookupError если row нет; AgentPromptResolver не имеет fallback:* consumer.
  • Adaptive routing upgrade chain (I-DOMAIN-AGENTSPEC-ROUTING-UPGRADE-CHAIN, AGENTS-FINISH Bundle B): порядок haiku → sonnet → opus фиксирован; перестановка ломает тесты.
  • Validator retry-with-feedback (I-DOMAIN-AGENTSPEC-VALIDATOR-RETRY-FEEDBACK, AGENTS-FINISH Bundle C): фаза pydantic_output ретраит с feedback (≤ retries), фаза post_acceptance только логирует.
  • Pinning обязателен для runtime / replay. agent_ref="key:*" и output_schema_ref="agent_definition:key:*" допустимы только до materialization. После — pinned id (agdef_* / pver_*). Любой key:* в runtime payload → fail-closed.
  • AgentSpec tool exposure rule. Direct agent tools — только side_effect_class="none" ∩ project allowlist ∩ executor profile. internal/external_* actions — это workflow steps (tool_call/external_write), не direct tools.
  • Output schema python class — только для system/code origin. User-origin не может ссылаться на Python class (security boundary).
  • Secrets никогда в prompts. core/security/credential_scan.reject_secret_like_string отбивает на pre-accept.
  • Scope contract. project ⇔ project_id обязателен; instance ⇔ project_id=None. Cross-project leak запрещён на DB level (composite checks).
  • Shadowing project→instance — explicit-only для output_schema_ref. Silent fallback на instance запрещён; нужно либо опубликовать project-scope AgentDefinition, либо явно использовать agent_definition:id:* (что pinned, не нужно shadowing).
  • AI не обязателен ни для одного уровня. Workflow без агентов — first-class citizen (Степень 1: step-based, 55% реальных задач). AI добавляется по необходимости (VISION «лестница сложности»).
  • Объяснимость (VISION инвариант 9). Каждое автоматическое решение объяснимо: для AI — до уровня features/circuits/attribution graphs (interpretability — V6.1+ backlog).

10. Связанные мануалы и каноны

  • Workflows.mdmodel_call / agent_config / agent_ref / output_schema_ref в step config, Publish Flow.
  • Connectors-Credentials.md — connector tools и почему internal/external_* — это steps, не direct agent tools.
  • Approvals.md — approval path для side effects, которые агент может запросить (но не выполнить напрямую).
  • Budgets-And-Cost.mdbudget_ledger записи для LLM-вызовов, model routing cost impact.
  • Security-And-Audit.md — audit trail для prompt_version.* / agent_definition.* events, secrets scanning.
  • Model-Routing-And-Budgets.md (planned) — детально про adaptive routing, complexity estimation, fallback chain.
  • Каноны: ARCHITECTURE-V6.md §10 (AI-агенты), §11 (AgentSpec tool exposure rule), Appendix A (commands); WORKFLOW-ARCHITECTURE.md §4 (output_schema_ref pinning); CONCEPT-AGENTSPEC.md, CONCEPT-PROMPT-SCOPE.md; core/models/{agent_specs,prompt_specs}.py; core/agents/{spec_resolver,runtime,runtime_registries,prompt_runtime,model_router}.py; alembic 039-042; tests/invariant_registry.yaml (три инварианта AGENTS-FINISH).