До и после
Было — 3 стадии
Сообщение пользователя
↓
Стадия 1
переписать запрос (LLM)
3× поиск в базе знаний
счётчик баллов URL
LLM-вызов "определи URL"
ответ (Prompt1)
↓
Стадия 2
получить документы услуги
извлечь цену (LLM)
3× параллельные извлечения
ответ (Prompt2)
↓
Стадия 3
сгенерировать резюме (LLM)
закрывающее сообщение (Prompt3)
отправить в CRM
4 промпта на проект, переходы стадий захардкожены, до 7 LLM-вызовов на ход.
Стало — один цикл с инструментами
Сообщение пользователя
↓
Один цикл LLM (макс 8 итераций)
→ может искать в БЗ (hybrid_search)
→ может сохранять найденное (set_state)
→ может передавать заявку (send_lead)
→ завершается ответом
Инструменты:
hybrid_search, set_state,
get_state, send_lead
Модель сама решает,
какой инструмент когда вызвать.
1 промпт на проект, модель владеет каждым решением, обычно 2–4 LLM-вызова на ход.
Что меняется для клиента
Регресса быть не должно. Заметные плюсы:
- Диалоги стали более естественными. Нет жёсткой стены «Стадия 1 задаёт только уточняющие вопросы» — бот может сразу ответить на вопрос о цене, если он чёткий.
- Быстрее на простых ходах. Загрузки файлов, «спасибо» и т.п. теперь пропускают полный пайплайн поиска и ответа.
- Более конкретные ответы. Модель может сделать дополнительный поиск в базе знаний, если вопрос того требует, вместо обязательных трёх поисков.
- Формат заявок не изменился. Лид-группа получает тот же структурированный пакет (контакт, город, резюме, файлы, история).
send_lead. Таймер фолоу-апов незаметно подбирает такие случаи и передаёт заявку позже. На GLM-5.1 (текущая прод-модель) в продакшене такого поведения не наблюдалось.
Операторы: как писать Prompt1
Рекомендуемая структура (~3,500–4,500 символов)
Идентичность и тон (1–2 абзаца)
ЧТО СОБРАТЬ (нумерованный список полей)
1) тип услуги
2) объект
3) город
4) контакт (телефон или email) — обязателен перед передачей
5) документы — обязательны / желательны
6) другие специфичные для бизнеса поля
ЛОГИКА ДИАЛОГА (нумерованные шаги)
1. Сначала пойми тип услуги.
2. Когда тип ясен, кратко объясни ценность, дай ориентир по цене из базы.
3. Запроси документы.
4. Попроси контакт.
5. Передавай заявку когда [критерии].
6. После передачи — короткое прощание.
КОГДА ПЕРЕДАВАТЬ ЗАЯВКУ
- Минимум: контакт + 3 ключевых параметра.
- Или: клиент явно просит передачу человеку.
ЧЕГО НЕ ДЕЛАТЬ
- Не называй цены, которых нет в базе знаний.
- Не назначай конкретные даты, не занимайся оплатой.
- Не выдумывай имена менеджеров.
Как движок переводит естественный язык в вызовы инструментов
Вы пишете бизнес-инструкции на простом языке. Движок автоматически переводит фразы в правильные вызовы инструментов:
| Фраза в Prompt1 | Движок переводит как |
|---|---|
| «передавай специалисту», «отправляй заявку», «передавай в CRM» | send_lead |
| «сверяйся с базой знаний», «опирайся на факты», «проверь на сайте» | hybrid_search |
| «запомни», «сохрани», «отметь», «зафиксируй» | set_state |
| «город», «бюджет», «дата», «тип услуги» (всё из ЧТО СОБРАТЬ) | set_state(extras=…) со стабильными английскими ключами |
Вам не нужно писать технические инструкции вида «вызови set_state с {contact: {method: 'phone', value: ...}}» в промпте. Движок справится. Упоминание имён инструментов или их аргументов в Prompt1 наоборот вредит — модель путается, когда ваше формулирование конкурирует с встроенными инструкциями.
Типичные ошибки в Prompt1
| Упоминание имён инструментов или схем аргументов | Движок справляется. Используйте простой бизнес-язык: «передай заявку», а не «вызови send_lead(summary)». |
| Расплывчатые критерии передачи | Будьте конкретны: «Минимум: контакт + тип услуги + объект + город». Не пишите «когда у тебя достаточно данных». |
| Прайс-листы прямо в промпте | Цены живут в базе знаний. Используйте price_url, чтобы указать на прайс-страницу; агент сам найдёт конкретные цены через hybrid_search. |
| Длина в ущерб смыслу | Промпты > 5,000 символов начинают терять внимание модели к деталям. Цельтесь в 3,500–4,500. |
| Забыть про start-reply для новых клиентов | Первое сообщение по команде /start приходит из поля start-reply проекта, а не из Prompt1. |
Тестирование изменений
Промпт обновляется из лид-группы Telegram — командой /prompt1 <текст>. Перезапуск бота не нужен. Полный список команд — в руководстве оператора. После обновления просто прогоните типовой сценарий с ботом в чате и проверьте: правильно ли он собирает поля и в нужный ли момент передаёт заявку.
Что изменилось в коде
Добавлено
internal/llm/toolcaller.go— абстракция tool-calling с двумя бэкендами (Anthropic native, OpenAI tools spec).internal/agent/toolagent.go— цикл выполнения: построение системного промпта, диспетчер инструментов, сохранение блоков.migrations/012_chat_history_blocks.sql— добавляет JSONB-колонку для полной последовательности assistant + tool-call на ход.migrations/013_drop_3stage_remnants.sql— убирает мёртвые колонки и JSONB-ключи.
Удалено
internal/agent/stages.go(~970 строк 3-стадийного флоу).- Отдельные LLM-вызовы таймера
classifyStatusиgenerateFollowUp— пинги таймера теперь идут через тот же цикл агента с синтетическим ходом[TIMER PING]. - Поля
Prompt2,Prompt3,PricePromptи соответствующие Telegram-команды. - Per-project пороги URL-определения.
- ~46 мёртвых i18n-ключей.
Переработано
- Системный промпт теперь делится на стабильный кэшируемый префикс (преамбула движка + Prompt1 проекта + схема инструментов) и изменчивый суффикс (per-turn состояние). Кэширование Anthropic запускается автоматически; кэш OpenAI / DeepSeek / GLM тоже срабатывает.
ChatStateпотерялURLsCounter,PriceSummary,ContactRequested. ПолучилExtras,ClientStatus(hot/cold),LeadSummary.- Поведение таймера на холодном клиенте на последнем интервале: теперь передаёт заявку вместо тихого завершения (потом сделаем настраиваемым).
Влияние на стоимость
На новой архитектуре с tool-calling GLM-5.1 (продакшен, ~$0.059 за диалог) стоит примерно вдвое дешевле cached Claude Sonnet (~$0.118 за диалог) при том же или лучшем качестве. Уточнение: это сравнение моделей внутри новой архитектуры, а не сравнение «новый бот против старой 3-стадийной версии» — в старой системе была другая модель и другая структура промптов, поэтому прямое сравнение $/диалог было бы некорректным. См. отчёт об исследовании стоимости для полного сравнения.