Перейти к основному содержимому

Model I/O

После главы вы сможете спроектировать provider-neutral границу model call и не смешивать output модели с выполненным действием или durable state.

Минимальный контракт​

На уровне HTTP model service принимает сериализуемый input и возвращает output. Для runtime полезнее более строгая граница:

interface ModelPort {
complete(request: ModelRequest): Promise<AgentDecision>;
}

type AgentDecision =
| {kind: 'final'; content: string}
| {kind: 'tool'; call: ToolCall};

Vendor adapter переводит этот контракт в Responses API, Messages API или локальный inference endpoint. Runtime не должен знать название SDK.

Что входит в request​

Полезно различать:

  • instructions: устойчивые правила приложения;
  • conversation: реплики пользователя и видимые ответы;
  • goal: актуальная задача текущего run;
  • task state: структурированная версия мира;
  • tool definitions: capability, schema и effect metadata;
  • observations: результаты уже выполненных действий;
  • retrieved context: выбранные данные с provenance.

Порядок и точная иерархия ролей зависят от API. Не кодируйте policy только порядком строк в одном prompt: authorization должна существовать в обычном коде.

Почему строка END — слабый протокол​

assistant: Готово. END

Проблемы:

  • пользовательский документ тоже может содержать END;
  • parser не знает, завершён ли ответ или только процитирован token;
  • невозможно выразить approval_required, budget_exhausted, cancelled;
  • tool arguments остаются невалидированным текстом.

Предпочтительнее discriminated union или native tool-call items. Structured output уменьшает двусмысленность, но всё равно требует runtime validation: output модели остаётся недоверенным вводом программы.

Messages прошлого turn​

На второй вопрос модели обычно нужен не «весь массив размышлений», а новая проекция рабочего состояния:

Если provider хранит conversation server-side, он может сам присоединить предыдущие items. Если приложение stateless, нужные items передаются снова. В обоих случаях приложение отвечает за то, чтобы актуальные instructions, policy и domain state не исчезли.

Практическое правило: пользовательский ввод, на который ещё отвечают, должен быть доступен; старые tool payloads можно сжимать; скрытый chain-of-thought прошлого turn хранить как application memory не следует.

Reasoning boundary​

Reasoning model может выполнять внутреннее вычисление до выдачи output. Это не тот же объект, что application events.

Храните:

  • final decision;
  • tool call и validated arguments;
  • policy result;
  • environment outcome;
  • краткое объяснение/summary, если оно нужно человеку;
  • token/latency/cost metadata.

Не делайте recovery зависимым от скрытого reasoning trace. Современные API могут переносить provider-specific reasoning items между вызовами, но это механизм inference continuity, не ваш audit log.

Streaming​

Streaming меняет доставку output, но не обязан менять семантику шага.

Наивный вариант выполняет tool, как только в потоке встретилось похожее имя. Надёжный вариант:

  1. принимает typed stream events;
  2. собирает полный tool-call item;
  3. проверяет завершение item;
  4. валидирует schema;
  5. выполняет policy/approval;
  6. только затем вызывает effect.

Частичный текст можно показывать пользователю, но он не является committed final answer. UI должен различать in_progress, completed, incomplete, cancelled и failed.

Cancellation и timeout​

Отмена model request не откатывает уже запущенный внешний tool. Нужны отдельные механизмы:

  • abort model/network call;
  • cooperative cancellation long-running tool;
  • deadline на каждый слой;
  • durable interrupt record;
  • compensation для уже завершённого effect.

Три стратегии stateful conversation​

СтратегияВыигрышЦенаПлохой выбор, когда…
Передавать весь transcriptПростотаРост tokens, stale factsДиалог длинный или sensitive
Provider conversation IDМеньше orchestrationVendor lifecycle/retentionНужен portable/ZDR runtime
Собственный state + projectionКонтроль, portabilityStorage и context policyОдноразовый прототип

Helios и реальный аналог​

Helios получает вопрос о доставке. Model adapter может быть любым, но AgentDecision остаётся одинаковым. Аналогично support-бот neobank должен отделять model output от ledger API: модель предлагает get_transaction, runtime выполняет чтение с tenant identity.

Лаборатория​

ScriptedModel записывает два запроса. Проверьте, что второй содержит исходную цель, пользовательский вопрос и tool observation:

npm run test:course:pattern -- "preserving the user goal"

Self-check​

  • Какие output variants поддерживает ваш adapter?
  • Где валидируется structured output?
  • Сохраняются ли instructions при переходе между turns?
  • Может ли partial stream запустить side effect?
  • Что именно отменяет cancellation?
  • Можно ли заменить provider, не переписав runtime?

Механика и источники​

Механика API, проверено 2026-08-30: OpenAI Responses API принимает instructions/input/tools, поддерживает conversation или previous response linkage, streaming, background status и tool calls. Это vendor mapping, не core contract курса: Responses API reference.