Structured Outputs & Agent Contracts — способ превратить обмен между AI-компонентами из «надеемся, что текст поймут» в формальный протокол: какие данные принимаются, какие возвращаются, какие ошибки возможны, какие поля обязательны и как меняется контракт со временем.
№12 Tools & Function Calling описывает механизм вызова конкретной операции; №50 описывает формальный input/output/error contract на границе. №43 A2A определяет lifecycle делегирования между агентами; №50 задаёт payload schema сообщений. №45 Verification проверяет correctness результата; schema validation проверяет только форму/constraints. №48 Guardrails решает, допустимо ли действие; contract задаёт, как действие представлено. №49 HITL использует decision/review contracts. №68 Durable Workflow позже использует versioned state/activity contracts.
Prerequisites: №10 State Management, №12 Tools & Function Calling, №41 Architecture, №43 A2A, №45 Verification. Forward references: №51 Permissions, №54 Connectors, №57 Queues, №59 Broker, №68 Durable Execution, №69 Distributed Reliability.
REQUEST-TIME: YES — model/tool/A2A boundaries. CONTROL PLANE: YES — schema registry/version/deprecation policy. DATA PLANE: YES — serialized payloads/events/results. OFFLINE: YES — contract tests, migration tests, compatibility analysis.
Success: payload validates against expected version and semantic preconditions are separately checked. Retryable: generation produces invalid structure and bounded targeted repair is allowed. Permanent: unsupported schema version, missing mandatory data, incompatible contract, forbidden enum. Idempotency: contract carries stable operation/message IDs where needed. Persist: schema version + payload + validation result + adapter version. Trace: validation/repair/compatibility events.
№50 не владеет business correctness, policy decisions, transport protocol, tool execution, workflow lifecycle, state database или schema generation vendor APIs. Она владеет THE SHAPE, MEANING, VERSION AND VALIDATION RULES OF MACHINE-CONSUMED EXCHANGES.
«Отправь письмо Ивану завтра. Всё ок.»
Неясны recipient ID, timestamp, timezone, status, confidence, side-effect intent.
recipient_id, send_at, timezone, body_ref, action_intent, policy_context.
Каждое поле имеет type и constraints.
Code принимает только корректный payload и отдельно проверяет permissions/policy.
JSON Schema отлично описывает форму. Но production contract также фиксирует смысл статусов, допустимые переходы, error taxonomy, compatibility и ownership.
Контракт должен быть понятен как producer, так и consumer. Если consumer вынужден угадывать, что означает поле status: "ok", контракт неполный.
{
"contract": "task.result",
"version": "1.2",
"task_id": "TASK-123",
"status": "SUCCESS",
"result": {
"artifact_ref": "artifact://...",
"summary": "...",
"claims": [
{
"claim_id": "c1",
"text": "...",
"evidence_refs": ["src://..."]
}
]
},
"quality": {
"verification": "PASS",
"unresolved_items": []
},
"usage": {
"model_calls": 2,
"tool_calls": 1
}
}JSON/MessagePack/typed encoding корректно декодируется.
Types, required fields, enum, ranges, pattern, additionalProperties.
start_at < end_at, resource belongs to tenant, mutually exclusive fields.
Object exists, version current, transition allowed, no stale approval.
Permissions, guardrails, data egress, approval requirement.
Execution/readback/result verification after side effect.
schema_pass → execute для чувствительных операций.{
"contract": "operation.error",
"version": "1.0",
"error_id": "ERR-...",
"code": "RESOURCE_NOT_FOUND",
"class": "PERMANENT",
"retryable": false,
"message_safe": "Target resource does not exist.",
"details": {
"resource_type": "document"
},
"source": "tool.files",
"trace_id": "TRACE-..."
}Consumer не должен парсить строку «Something went wrong: 404 maybe...», чтобы понять retry.
Typed error contract позволяет router/orchestrator deterministically решить:
| Error class | Пример | Обычное действие |
|---|---|---|
| TRANSIENT | Provider timeout, temporary network issue. | Bounded retry / fallback. |
| RATE_LIMITED | Quota/rate constraint. | Backoff / queue / alternate provider if policy allows. |
| INVALID_INPUT | Schema/semantic validation failed. | Targeted repair or caller fix. |
| NOT_FOUND | Resource absent. | Retrieve alternate / clarify / terminal depending task. |
| CONFLICT | Optimistic version mismatch. | Reload state / re-evaluate / avoid blind retry. |
| PERMISSION_DENIED | Scope missing. | Do not retry unchanged; human/admin path if appropriate. |
| POLICY_DENIED | Action prohibited. | Terminate/narrow; never retry to bypass. |
| UNSUPPORTED_VERSION | Consumer cannot read contract v3. | Adapter/upgrade/compatible producer. |
{
"contract": "agent.task",
"version": "2.1",
"task_id": "SUB-42",
"parent_task_id": "ROOT-1",
"goal": "...",
"inputs": {
"artifact_refs": ["..."],
"evidence_refs": ["..."]
},
"constraints": [
"read_only",
"no_external_publish"
],
"budget": {
"deadline_ms": 30000,
"max_model_calls": 4
},
"output_contract": "research.answer.v2",
"callback": {
"mode": "ASYNC"
}
}Subagent получает task contract, а не весь parent conversation.
Контракт фиксирует goal, permitted inputs, constraints, budget, output schema и completion semantics.
A2A lifecycle остаётся темой №43; здесь важна shape/compatibility сообщения.
Каждый machine boundary carries contract name/version или знает его из endpoint/topic.
v1 → canonical internal model → v2. Не распространять compatibility hacks по business logic.
Сначала telemetry consumers/producers, затем warning, migration, removal.
| Pattern | Проблема | Лучше |
|---|---|---|
| Everything optional | Consumer вынужден угадывать состояния. | Required core + explicit nullable/optional semantics. |
| null means many things | Unknown? not applicable? failed? redacted? | Status/reason enum + field. |
| Magic default | Producer omitted field, consumer silently assumed dangerous value. | Host-controlled default with documented semantics. |
| Open string enum | Typos become states. | Closed enum for control fields; extensible metadata separately. |
Agent message содержит полный PDF, trace, 20 images и весь conversation state. Это усложняет retries, logging, privacy и versioning.
Передавать artifact_ref, evidence_ref, state_ref + hashes/versions/metadata. Сам blob хранится в соответствующем store.
№60 позже детализирует Artifact Store.
{
"contract": "tool.publish_post.command",
"version": "1.0",
"operation_id": "OP-...",
"resource": {
"channel_id": "vk:brand_A"
},
"payload_ref": "artifact://post/17",
"mode": "PUBLISH",
"expected_state": {
"draft_version": 12
}
}Contract делает command однозначным; он не заменяет runtime validation.
Representative correct messages pass.
Missing required, wrong enum, wrong type, extra prohibited field.
Can vN consumer read vN-1? Do adapters preserve meaning?
Canonical model survives serialization without semantic drift.
| Role | Responsibility |
|---|---|
| Contract owner | Defines semantics, versioning and deprecation policy. |
| Producer | Must emit valid supported version. |
| Consumer | Must reject unsupported/invalid input predictably. |
| Adapter owner | Maintains compatibility translations. |
| Quality/CI | Runs validation, compatibility and regression tests. |
Для чувствительных commands использовать строгие schemas и запрещать unexpected fields.
Помечать secret/PII/internal refs, чтобы logger/redactor/connector knew handling policy.
Контракт не должен включать «весь user object», если operation нужен только user_id.
contract_id, version, validator, failure path.
repair count, success rate, error classes.
producer_version / consumer_version / adapter path.
Validation/serialization overhead where material.
% machine outputs failing schema/semantic validation.
Сколько invalid outputs требуют repair; сколько repair успешны.
Contract regressions caught before production.
Runtime failures caused by version mismatch.
Overhead critical boundaries.
Traffic still using old contract versions.
Schema-valid but semantically invalid payloads.
Invalid/unsupported payloads reaching downstream side effect.
contracts/
├── task/
│ ├── task_request_v1.json
│ └── task_result_v1.json
├── tools/
│ ├── publish_command_v1.json
│ └── publish_result_v1.json
├── agents/
│ ├── delegate_v1.json
│ └── response_v1.json
├── errors/
│ └── operation_error_v1.json
├── adapters/
├── generated/
└── tests/
├── valid/
├── invalid/
├── compatibility/
└── regression/Отдельный schema registry/IDL platform появляется позже, когда many independently deployed producers/consumers justify it.
| Вопрос | Ответ |
|---|---|
| Стоит ли реализовывать? | Да, с самого начала для machine-consumed boundaries. |
| Separate Component? | NO. Contracts — cross-cutting discipline внутри Tool/Action + Quality и других consumers. |
| Минимум 80% ценности? | Versioned schemas, strict validator, typed errors, control enums, compatibility tests. |
| Когда overkill? | Если строить enterprise schema registry, codegen platform и distributed IDL governance для одного monolith prototype. |
| Trigger? | Всякий раз, когда output потребляет код/другой agent/tool/workflow, а не только человек. |
| Как измерить uplift? | Parsing/validation failures, downstream contract escapes, regression rate, repair rate, integration defects. |
| Можно ли rule/tool/code вместо LLM-agent? | Полностью. Это прежде всего schema/types/validators/adapters/tests. |
Модель не выбирает schema/version на critical path.
Producer correctness не заменяет consumer validation.
Schema pass не заменяет semantic/verification/policy.
Retry/fallback decisions не должны зависеть от parsing exception text.
Не менять semantics существующего поля silently.
Compatibility conversion централизована, а не размазана по business code.
Большие artifacts/state передавать refs + versions/hashes.
State/action/error class — finite known vocabulary.
Breaking interface changes должны ловиться до deployment.
LLM / TOOL / AGENT / WORKFLOW
↓
HOST SELECTS CONTRACT + VERSION
↓
PRODUCER EMITS STRUCTURED VALUE
↓
PARSE
↓
SCHEMA VALIDATION
↓
SEMANTIC / STATE VALIDATION
↓
POLICY / PERMISSION CHECK
↓
CANONICAL INTERNAL MODEL
↓
TOOL / STATE / EVENT / AGENT / ARTIFACT
↓
TYPED RESULT OR TYPED ERROR
↓
TRACE:
contract_id + version + validation + adapter + outcome
CONTRACT EVOLUTION:
v1 → compatible add → v1.x
breaking semantics → v2
legacy producer → adapter → canonical model
CORE PRINCIPLE:
PROMPTS EXPRESS INTENT.
CONTRACTS DEFINE ACCEPTABLE MACHINE INTERFACES.
VALID JSON IS NOT ENOUGH.
VALID SCHEMA IS NOT ENOUGH.
A PRODUCTION CONTRACT ALSO NEEDS
SEMANTICS, VERSIONING, ERRORS AND OWNERSHIP.
A. Related ECC ideas. Context-as-cache, scoped memory, lifecycle hooks, selective capabilities, feature flags, deterministic enforcement, provider-neutral adapters and eval-gated learning are applied only where relevant to №50 Structured Outputs & Agent Contracts.
B–E. Existing boundary and placement. The existing conceptual boundary, class CORE, default ON and owner Tool / Action Engine + Quality Engine remain authoritative. Runtime/control/data/offline placement is unchanged; durable state stays outside model context.
F–H. Hooks and contracts. Use bounded PRE_MODEL/POST_MODEL, PRE_TOOL/POST_TOOL, CHECKPOINT and TASK_COMPLETED events as applicable. Illustrative fields and canonical contracts are defined in NEW_CONTRACTS_SPEC.md; no universal schema is implied.
I–J. Security and evaluation. Host-side schema, permission, secret, budget, idempotency and audit checks take precedence over LLM output. Optional mechanisms require a feature flag and WITH/WITHOUT ablation; measure quality, acceptance, correction, latency, cost, escalations and severe errors.
K–L. Task profiles and cross-references. A TaskProfile selects the relevant skill, tool/context slice, memory scope and enforcement profile independently from FAST/STANDARD/DEEP. See cross-reference map, hook spec and ablation plan. Provider adapters remain outside the core.