Инструкции и автоматизация
Инструкции — основа поведения вашего торгового ассистента. Они выходят далеко за рамки простых текстовых запросов: у каждой инструкции есть категория, которая определяет как и когда она выполняется. Одни инструкции добавляют контекст в «мозг» ИИ, другие регистрируют инструменты, которые ИИ может использовать, а третьи выполняются как детерминированный код, запускающий действия до или после каждого сообщения клиента.
Проблема простых инструкций
В большинстве чат-платформ можно просто написать список инструкций. ИИ каждый раз читает их все и пытается выполнить. Это работает в простых случаях, но быстро перестаёт быть эффективным:
- «Отправь меню, когда кто-то спросит о ценах» — ИИ может отправить, может забыть, а может отправить не то. Это ненадёжно.
- «Пометь клиента как «оплатил» после получения счёта» — ИИ должен помнить об этом каждый раз. Легко забыть.
- «Показывай правила ценообразования на выходные только по пятницам» — ИИ путается, когда инструкции противоречат друг другу.
QuotyAI решает эту проблему, присваивая каждой инструкции категорию, которая точно определяет её поведение — как запрос (prompt), как инструмент, как предварительное условие или как автоматизированный код. Вы просто пишете, что нужно, на обычном языке — система определяет категорию, генерирует детерминированный код и позволяет просмотреть его до публикации.
Шесть категорий
У каждой инструкции есть категория. Категория определяет, что происходит с инструкцией во время работы:
| Категория | Где выполняется | Что делает |
|---|---|---|
| Consistent Prompt (Постоянный запрос) | В системном запросе ИИ | Всегда включён — даёт ИИ постоянный контекст |
| Conditional Prompt (Условный запрос) | В системном запросе ИИ | Включается только при выполнении условия |
| Consistent Prompt + Tool (Постоянный запрос + инструмент) | Запрос + зарегистрированный LangChain-инструмент | Постоянный контекст + вызываемая функция, которую ИИ может использовать |
| Conditional Prompt + Tool (Условный запрос + инструмент) | Запрос + зарегистрированный LangChain-инструмент | Контекст + инструмент, но только когда условие истинно |
| Deterministic Router (Детерминированный маршрутизатор) | Перед обработкой сообщения ИИ | Выполняет код, который может перехватить сообщение, отправить ответ или полностью обойти ИИ |
| Deterministic Callback (Детерминированный обратный вызов) | После ответа ИИ | Выполняет код, который обновляет состояние, помечает клиентов или уведомляет команду |
Реальная история из бизнеса
Небольшой ресторан в Хошимине использует QuotyAI для обработки заказов из Facebook и Telegram. Вот как они применяют каждую категорию инструкций:
Consistent Prompt:
«Наш фо стоит 65 000 VND за обычную порцию, 85 000 VND за большую. Бань ми — 35 000 VND.»
Эта информация всегда в «мозгу» ИИ — клиент может спросить в любое время и получить правильный ответ.
Conditional Prompt:
«После 22:00 доступен только самовывоз. Посадка в зале закрывается в 21:30.»
Функция условия проверяет текущее время. До 21:30 эта инструкция невидима для ИИ. После 21:30 она появляется автоматически, и ИИ начинает сообщать клиентам только о самовывозе.
Deterministic Router:
«Если клиент говорит «меню» или «что у вас есть», отправьте ему PDF-меню и не заставляйте его ждать ответа ИИ.»
Никаких форм, конструкторов действий или выпадающих списков — просто напишите, что нужно. ИИ-кодировщик читает вашу инструкцию, определяет подходящие действия (отправить сообщение, отправить вложение, прервать обработку и т.д.) и генерирует TypeScript-функцию автоматически. Функция маршрутизатора проверяет каждое входящее сообщение. Когда она обнаруживает запрос меню, она немедленно отправляет вложение и прерывает работу ИИ — клиент получает меню менее чем за 100 мс вместо ожидания генерации ответа ИИ.
Deterministic Callback:
«Когда клиент подтверждает платный заказ, отметь его как «has_paid_order» в состоянии разговора.»
После того как ИИ успешно обработал заказ и отправил подтверждение, срабатывает обратный вызов. Он обновляет состояние разговора, чтобы в следующий раз, когда этот клиент напишет, ИИ знал, что он платящий клиент.
Consistent Prompt + Tool:
«Вы можете узнать сегодняшние специальные предложения с помощью инструмента getDailySpecial. Спецпредложения меняются ежедневно в зависимости от свежих ингредиентов.»
ИИ знает об этой возможности из запроса, и у него есть зарегистрированная функция, которую он может вызвать, чтобы узнать сегодняшние спецпредложения.
Как это работает
Конвейер автоматизации
Каждое сообщение клиента проходит через детерминированный конвейер до и после обработки ИИ:
Клиент отправляет сообщение
↓
┌───────────────────────────────────────┐
│ 1. Извлечение деталей заказа │
│ Ассистент извлекает │
│ структурированные данные заказа │
│ из сообщения │
└───────────────────────────────────────┘
↓
┌───────────────────────────────────────┐
│ 2. Детерминированный маршрутизатор │ ← Автоматизация до ИИ
│ Запускается ДО того, как ИИ │
│ увидит сообщение. Может │
│ перехватывать, отправлять │
│ вложения или прерывать обработку. │
└───────────────────────────────────────┘
↓ (если не прервано)
┌───────────────────────────────────────┐
│ 3. Обработка ИИ-агентом │
│ • Consistent prompts │ ← Всегда в мозгу
│ • Conditional prompts │ ← Только если условие выполнено
│ • Зарегистрированные инструменты │ ← ИИ может вызывать функции
└───────────────────────────────────────┘
↓
┌───────────────────────────────────────┐
│ 4. Детерминированный обратный вызов │ ← Автоматизация после ИИ
│ Запускается ПОСЛЕ ответа ИИ. │
│ Обновляет состояние, помечает │
│ клиентов, уведомляет команду. │
└───────────────────────────────────────┘
Детерминированность — это гарантия
В отличие от ИИ, который может забыть или неверно истолковать инструкцию, Deterministic Router и Deterministic Callback выполняются как настоящий TypeScript-код, сгенерированный нашими ИИ-кодировщиками. Вам не нужно настраивать действия вручную — просто напишите, что должно произойти. Когда вы пишете:
«Если сумма заказа превышает 1 000 000 VND, предложите бесплатную доставку»
ИИ-кодировщик анализирует вашу инструкцию, определяет подходящие действия маршрутизации и условия, и генерирует отдельную TypeScript-функцию во время сборки ассистента. Вы можете просмотреть сгенерированный код прямо в редакторе — разверните любую инструкцию маршрутизатора, чтобы увидеть TypeScript с подсветкой синтаксиса, скопировать его или перегенерировать. Каждая функция работает независимо — если одна даст сбой, остальные продолжат работу без прерывания.
function route(ctx: RouterContext): RouterAction[] {
const actions: RouterAction[] = [];
if (ctx.order.totalAmount && ctx.order.totalAmount > 1000000) {
actions.push({
type: 'send_message',
message: 'Ваш заказ подходит для бесплатной доставки! 🚚',
});
}
actions.push({ type: 'noop' });
return actions;
}
RouterContext предоставляет каждой функции маршрутизатора всё необходимое для принятия решений:
| Поле | Тип | Описание |
|---|---|---|
ctx.order |
Record<string, unknown> |
Структурированные параметры заказа, извлечённые из разговора |
ctx.message |
string |
Исходный текст сообщения клиента — используйте для поиска ключевых слов |
ctx.state |
Record<string, unknown> |
Полное состояние разговора (флаги клиента, прошлые данные) |
ctx.business |
object |
Информация о бизнесе: timezone, name, industries, customAttributes — общедоступные атрибуты, определённые пользователем, которые применяются ко всем контактам/разговорам |
ctx.contact |
object (опционально) |
Контактные данные клиента: name, email, phone, customAttributes, channelIdentifiers — создавайте условия на основе информации о клиенте |
ctx.conversation |
object (опционально) |
Метаданные разговора: channel, externalId, lastInboundMessage, lastOutboundMessage — создавайте логику для конкретных каналов |
ctx.now |
Date |
Текущее время (детерминированное — одинаковое для всех функций в одном запуске) |
Каждая сгенерированная функция выполняется в изолированной среде — она никогда не забывает, не предполагает и не галлюцинирует. Поскольку контекст включает контактную информацию, метаданные разговора и бизнес-атрибуты, функции могут строить богатые условия, такие как «если это VIP-клиент из Telegram», «если домен email клиента указывает на корпоративный аккаунт» или «если общедоступный флаг allow_discounts установлен в true».
Действия маршрутизатора
Функция маршрутизатора возвращает действия. Платформа интерпретирует их:
| Действие | Эффект |
|---|---|
send_message |
Немедленно отправить статическое сообщение клиенту |
send_linked_attachments |
Отправить все привязанные вложения для этой инструкции |
update_state |
Изменить значение в состоянии разговора |
handover |
Перевести на оператора с указанием причины |
short_circuit |
Полностью пропустить ИИ — ответ ИИ не генерируется |
noop |
Ничего не делать, продолжить обычную обработку |
Действия обратного вызова
Функция обратного вызова выполняется после ответа ИИ:
| Действие | Эффект |
|---|---|
update_state |
Изменить значение в состоянии разговора (например, отметить как оплачено) |
notify_agent |
Отправить приватное уведомление вашей команде |
noop |
Ничего не делать |
Справочник категорий
Consistent Prompt (Постоянный запрос)
Назначение: Инструкции, которые всегда должны быть частью системного запроса ИИ. Используйте для постоянной бизнес-информации, политик и стандартных ответов.
Когда использовать:
- Ваше меню, прайс-лист или каталог услуг
- Стандартные часы работы
- Часто задаваемые вопросы и ответы на них
- Бизнес-политики (отмена, возврат и т.д.)
Пример:
«Наш спа-салон предлагает: 60-мин шведский массаж — 500 000 VND, 90-мин глубокотканевой — 750 000 VND, добавить горячие камни — 150 000 VND.»
Conditional Prompt (Условный запрос)
Назначение: Инструкции, которые должны появляться в контексте ИИ только при выполнении условия. Условие — это сгенерированная TypeScript-функция, которая вычисляется во время выполнения.
Когда использовать:
- Правила на основе времени (после работы, в выходные, праздники)
- Правила на основе статуса клиента (постоянный клиент, VIP, отмечен флагом)
- Правила на основе состояния заказа (только после выбора товаров)
- Сезонные акции и ограниченные по времени предложения
Под капотом генерируется функция shouldInclude с сигнатурой function shouldInclude(ctx: ConditionContext): boolean. ConditionContext предоставляет:
| Поле | Тип | Описание |
|---|---|---|
ctx.order |
Record<string, unknown> |
Структурированные входные параметры заказа из разговора |
ctx.contact |
object (опционально) |
Контактные данные клиента: name, email, phone, customAttributes |
ctx.conversation |
object (опционально) |
Метаданные разговора: channel, externalId, lastInboundMessage |
ctx.state |
Record<string, unknown> (опционально) |
Снимок состояния разговора (флаги клиента, прошлые данные) |
ctx.business |
object (опционально) |
Информация о бизнесе: timezone, name, industries, customAttributes |
ctx.now |
Date |
Текущее время (детерминированное) |
Если функция возвращает false, содержимое инструкции невидимо для ИИ.
Consistent Prompt + Tool (Постоянный запрос + инструмент)
Назначение: Даёт ИИ постоянный контекст И регистрирует детерминированную функцию-инструмент, которую он может вызывать. Используйте, когда ИИ нужно как знание о чём-то, так и возможность действовать на его основе.
Когда использовать:
- Поиск динамических данных (складские остатки, ежедневные спецпредложения, наличие)
- Выполнение проверок (проверить адрес, подтвердить код купона)
- Расчёт производных значений (налоговые ставки, стоимость доставки)
Инструмент регистрируется как функция-инструмент LangChain со сгенерированной JSON-схемой. ИИ может вызвать его во время разговора.
Conditional Prompt + Tool (Условный запрос + инструмент)
Назначение: Аналогично предыдущему, но и содержимое запроса, и инструмент доступны только при выполнении условия.
Когда использовать:
- Инструменты только для выходных (например, поиск меню бранча)
- Возможности только для VIP-клиентов (например, применить скидку лояльности)
- Инструменты после заказа (например, отслеживание статуса доставки — доступно только после оформления заказа)
Условие вычисляется один раз — если оно истинно, и содержимое запроса, И инструмент становятся доступны ИИ.
Deterministic Router (Детерминированный маршрутизатор)
Назначение: Выполняется как настоящий TypeScript-код до того, как ИИ-агент увидит сообщение. Может перехватывать сообщения, отправлять вложения, обновлять состояние, инициировать перевод на оператора или полностью обходить ИИ.
Когда использовать:
- Мгновенные ответы: «Если клиент спрашивает часы работы, немедленно отправить вложение с часами» — без задержки ИИ
- Ограничения: «Если клиент упоминает названия конкурентов, заблокировать и перевести на оператора»
- Предобработка: «Если клиент говорит «меню» на любом языке, отправить PDF»
- Комплаенс: «Если клиент из заблокированного региона, отправить сообщение об ошибке и прервать обработку»
- По времени: «После 22:00 отправить сообщение «закрыто» и прервать обработку»
- По статусу клиента: «Если у клиента уже есть активный заказ, заблокировать повторный заказ»
Все инструкции маршрутизатора генерируют отдельные TypeScript-функции во время сборки — по одной на инструкцию. Каждая функция получает полный объект RouterContext с исходным сообщением клиента, структурированными данными заказа, состоянием разговора, информацией о бизнесе, контактными данными клиента, метаданными разговора и текущим временем. Это означает, что каждое правило может независимо оценивать все доступные контекстные данные.
Изоляция инструкций: Если одна функция маршрутизатора выдаёт ошибку, остальные продолжают работу. Ни одна ошибочная инструкция не может сломать все ваши правила маршрутизации.
Как это работает в конвейере:
- Извлечение структурированных данных заказа из сообщения клиента
- Построение
RouterContextс исходным сообщением, данными заказа, состоянием разговора, информацией о бизнесе, контактными данными клиента, метаданными разговора и текущим временем - Последовательный запуск каждой функции
route(ctx)— если возвращаетсяshort_circuit, остальные функции всё равно выполняются, но ИИ пропускается - Обработка накопленных действий: отправка сообщений, отправка вложений, обновление состояния, перевод на оператора или прерывание
Deterministic Callback (Детерминированный обратный вызов)
Назначение: Выполняется как настоящий TypeScript-код после того, как ИИ ответил. Для побочных эффектов, которые не должны прерывать взаимодействие с клиентом.
Когда использовать:
- Отслеживание состояния: «После подтверждения заказа установить флаг
hasActiveOrder» - Уведомления: «После неудачной попытки оплаты уведомить команду»
- Аудит: «После предоставления коммерческого предложения записать его в состояние разговора»
- Сегментация клиентов: «После 5-го заказа клиента пометить его как VIP»
Каждая инструкция обратного вызова генерирует собственную функцию с сигнатурой function callback(ctx: CallbackContext): CallbackAction[]. CallbackContext предоставляет:
| Поле | Тип | Описание |
|---|---|---|
ctx.order |
Record<string, unknown> |
Структурированные входные параметры заказа из разговора |
ctx.aiResponse |
string |
Ответ, сгенерированный ИИ и только что отправленный клиенту |
ctx.contact |
object (опционально) |
Контактные данные клиента: name, email, phone, customAttributes — условия на основе личности клиента |
ctx.conversation |
object (опционально) |
Метаданные разговора: channel, externalId — условия на основе канала, из которого пришёл разговор |
ctx.state |
Record<string, unknown> (опционально) |
Снимок состояния разговора для чтения или обновления флагов клиента |
ctx.business |
object (опционально) |
Информация о бизнесе: timezone, name, industries, customAttributes |
В дополнение к стандартным действиям, обратные вызовы могут записывать данные в контакты и разговоры:
| Действие | Эффект |
|---|---|
set_contact_attribute |
Установить пользовательский атрибут в документе контакта (сохраняется между разговорами) |
set_conversation_attribute |
Установить пользовательский атрибут в документе разговора |
Это позволяет сгенерированному ИИ обратному вызову сохранять такие данные, как «отметить этого клиента как VIP» или «пометить этот разговор как обработанный», записывая в ctx.contact.customAttributes или ctx.conversation.customAttributes.
Обратные вызовы неблокирующие — клиент получает ответ, пока обратные вызовы обрабатываются в фоновом режиме.
Детали технической реализации
Интеграция с конвейером сборки
Во время сборки ассистента инструкции проходят через генерацию кода:
Инструкции из БД
↓
Сборщик получает активные инструкции
↓
┌────────────────────────────────────────────┐
│ Проверка категории │
│ │
│ CONSISTENT_* → Генерация кода не требуется│
│ (только запрос) │
│ │
│ CONDITIONAL_* → Генерация │
│ shouldInclude() для каждой инструкции │
│ │
│ DETERMINISTIC_ROUTER → Генерация │
│ route() для КАЖДОЙ инструкции — │
│ каждая функция работает независимо │
│ с полным RouterContext │
│ │
│ DETERMINISTIC_CALLBACK → Генерация │
│ callback() для каждой инструкции │
│ │
│ *_PROMPT_AND_TOOL → Генерация │
│ функции инструмента + JSON-схемы │
│ для каждой инструкции │
└────────────────────────────────────────────┘
↓
Код записывается обратно в документ инструкции (для предпросмотра)
↓
Код сохраняется в aiExecutableSource (источник истины)
↓
Снимок фиксируется в неизменяемом ассистенте
Хранение сгенерированного кода
Сгенерированный код записывается в два места для разных целей:
- Документ инструкции (поле
generatedCode) — для мгновенного предпросмотра в редакторе с подсветкой синтаксиса aiExecutableSourceассистента — источник истины, который фиксируется в неизменяемых снимках и используется во время выполнения
Структура хранения в aiExecutableSource с ключом по ID инструкции:
| Категория | Хранение | Пример ключа |
|---|---|---|
| Маршрутизатор (на инструкцию) | deterministicRouterInstructions[instructionId].code |
Одна запись на инструкцию маршрутизатора — изолирована и независима |
| Обратный вызов (на инструкцию) | deterministicCallbacks[instructionId].code |
Одна запись на инструкцию обратного вызова |
| Условие (на инструкцию) | conditionFunctions[instructionId].code |
Одна запись на условную инструкцию |
| Инструмент (на инструкцию) | instructionTools[instructionId] |
Одна запись на инструкцию-инструмент |
Изолированное выполнение
Все сгенерированные TypeScript-функции выполняются в той же изолированной среде, что и формулы ценообразования:
- Встроенный конструктор
FunctionBun - Нет доступа к файловой системе, сети или переменным окружения
- Переопределённые методы
consoleдля аудита - Возвращают типизированные объекты действий, которые интерпретирует конвейер
Пользовательские атрибуты на трёх уровнях
Все три типа контекста (RouterContext, CallbackContext, ConditionContext) предоставляют доступ к пользовательским атрибутам на трёх уровнях, позволяя создавать условия на основе общедоступных настроек бизнеса, данных клиента или контекста разговора:
| Уровень | Путь доступа | Область | Пример |
|---|---|---|---|
| Бизнес | ctx.business.customAttributes |
Наследуется всеми контактами и разговорами | allow_discounts: true, max_order_limit: 5000000 |
| Контакт | ctx.contact.customAttributes |
Конкретный клиент во всех его разговорах | vip_tier: 'gold', preferred_language: 'vi' |
| Разговор | ctx.conversation.customAttributes |
Только конкретный разговор | no_ai: true, priority: 'urgent' |
Атрибуты полностью определяются пользователем — вы сами задаёте ключи и значения. Сгенерированный ИИ код читает их напрямую:
// Маршрутизатор — предложить скидку только если и бизнес, и контакт разрешают
if (ctx.business.customAttributes?.allow_discounts && ctx.contact.customAttributes?.vip_tier === 'gold') {
actions.push({ type: 'send_message', message: 'Вы получаете скидку 10%!' });
}
// Обратный вызов — сохранить флаг после успешного заказа
if (ctx.order.totalAmount > 1000000) {
actions.push({ type: 'set_contact_attribute', key: 'high_value', value: true });
}
// Условие — скрыть инструкции для отмеченных разговоров
if (ctx.conversation.customAttributes?.no_ai) {
return false;
}
Лучшие практики
Для Consistent Prompts
- Делайте их точными и фактическими: точные цены, точные часы, точные политики
- Одна концепция на инструкцию (не смешивайте несвязанные правила)
- Используйте тот же язык, что и ваши клиенты
Для Conditional Prompts
- Условие должно быть очевидным из содержания инструкции: «После 22:00…» работает лучше, чем «Ночные правила»
- Не злоупотребляйте условиями — ИИ работает лучше со стабильным контекстом
- Тестируйте условия, моделируя разные сценарии
Для Deterministic Routers
- Используйте для всего чувствительного ко времени — маршрутизатор выполняется за миллисекунды, ИИ — за секунды
- Просматривайте сгенерированный код в редакторе, чтобы убедиться, что ИИ правильно определил действия
- Если сгенерированный код не соответствует вашему замыслу, перепишите инструкцию более понятным языком
short_circuit— мощный инструмент: используйте его, когда ИИ не может добавить ничего полезного- Маршрутизаторы не имеют доступа к знаниям ИИ — они работают только со структурированными данными заказа
Для Deterministic Callbacks
- Используйте для изменений состояния, которые должны всегда происходить после определённых событий
- Не используйте обратные вызовы для действий, обращённых к клиенту — клиент уже получил ответ
- Обратные вызовы идеально подходят для комплаенса и аудита
Общие рекомендации
- Порядок инструкций не имеет значения для маршрутизаторов (они объединяются в одну функцию)
- Для запросов порядок важен — более ранние инструкции располагаются выше в контексте ИИ
- Если инструкция не подходит ни под одну категорию, она пропускается — каждой инструкции нужна действительная категория