Состояние нельзя прятать в разговоре
Инструменты дают действия, но действия бессмысленны без состояния. История сообщений удобна для языка, но плохо годится как единственная база состояния: заказ, actor, выбранный язык, approval и текущий этап должны жить в типизированном state, а не растворяться в тексте диалога.
type ConversationState = {
sessionId: string;
actorId: string;
tenantId: string;
locale: string;
verifiedEntities: {
orderId?: string;
callbackPhone?: string;
};
workflow: {
intent?: string;
stage: "discover" | "explain" | "preview" | "awaiting_approval" | "done";
pendingActionId?: string;
};
memoryRefs: string[];
};Разные данные живут разное время, и хранить их нужно по-разному.
- Текущая реплика и промежуточный tool result - на один turn, в invocation context.
- Выбранный заказ и pending action - на сессию, в session state.
- Предпочтительный язык - между сессиями и с согласием, в user profile или memory.
- Политика возврата - это версионируемое знание, ему место в knowledge base, а не в памяти пользователя.
- Approval - с коротким TTL и одним применением, в server-side token store.
Модели тоже нужно явно сказать, как обращаться со state.
- Не считай entity подтверждённым, пока оно не появилось в verifiedEntities.
- Не извлекай permissions из текста диалога.
- При смене намерения сбрось только workflow-specific поля, сохрани actor и locale.
- Перед завершением перечисли state changes как proposed delta.
- Backend применяет delta после schema и policy validation.И осторожно со сводками.
Summary может потерять запрет. Compaction и handoff обязаны сохранять policy, approvals и неразрешённые конфликты в структурированном виде. Короткая литературная сводка - ненадёжный continuation state.