Семантический роутинг: Как выбрать нужную функцию без IF-ELSE

17.06.2026 13:00

Рано или поздно любой проект с текстовым интерфейсом упирается в предел жесткой логики. В начале архитектура выглядит опрятно. Текстовая команда парсится и уходит в нужный обработчик:

if "баланс" in text.lower:
 return get_balance(user_id)
elif "оплатить" in text.lower or "пополнить" in text.lower:
 return start_payment(user_id)
else:
 return fallback_answer

Схема дает сбой при столкновении с реальным трафиком. Пользователи формулируют запросы вне ваших шаблонов: «где мои деньги?», «хочу закинуть кеш», «почему минус на счету, я же закидывал?».

Разработчик отвечает добавлением новых условий. Подключаются регулярные выражения. Блок if-else мутирует в хрупкую конструкцию, где добавление нового правила ломает маршрутизацию старых команд. Срабатывают ложные триггеры: вопрос «как оплатить баланс» уходит не в тот обработчик из-за неверного порядка проверок.

Фундаментальный изъян здесь в том, что код жестко завязан на синтаксис (словоформы), тогда как пользователи оперируют семантикой (смыслами). Пытаться описать бесконечную вариативность человеческого языка конечным набором if-else — это гарантированный технический долг.

Механика семантического роутинга

Система обязана классифицировать намерение (интент) по смыслу текста. Инженеры решают эту задачу двумя способами: через векторные пространства и через генеративные классификаторы.

Векторный поиск (эмбеддинги) Разработчик формирует список интентов и пишет для каждого десятки эталонных фраз.

  • Интент billing: "пополнить счет", "куда кидать деньги".
  • Интент support: "не работает", "выдает ошибку".

Фразы пропускаются через модель эмбеддингов (например, text-embedding-3-small), конвертируются в векторы и оседают в базе данных. Входящий запрос проходит ту же трансформацию. Система ищет ближайшего соседа по косинусному расстоянию и вызывает соответствующий интент.

Подход дает минимальную задержку, но имеет архитектурные ограничения. Векторные модели агрегируют общую семантику, но теряют критические детали контекста — отрицания или сарказм. Фраза «я НЕ хочу пополнять счет, верните деньги» математически окажется близка к кластеру пополнения счета. Сопровождение такой системы требует постоянного ручного обогащения базы эталонных фраз.

LLM-роутинг через микро-модели Запрос пользователя передается языковой модели вместе с описанием доступных инструментов (Tool Calling). Модель сама определяет, какой инструмент нужно вызвать.

Долгое время этот паттерн игнорировали из-за стоимости. Использовать GPT-4 или Claude Opus исключительно для маршрутизации сообщений означает добавлять 1.5–2 секунды задержки на каждый чих пользователя. Роутинг становился дороже бизнес-логики.

Ситуация поменялась с релизом быстрых микро-моделей (8B–14B параметров). Llama 3 8B, Qwen 2.5, Claude 3.5 Haiku, Gemini 1.5 Flash способны принимать бинарные решения и извлекать структурированные аргументы за 200–400 миллисекунд.

Практическая интеграция через RouterAPI

Разворачивать кластер GPU с vLLM для хостинга роутера не требуется. Платформы вроде RouterAPI предоставляют единый интерфейс к десяткам оптимизированных моделей, забирая на себя балансировку и маршрутизацию самих запросов к провайдерам.

Архитектура роутинга сжимается до одного вызова API. Вы описываете функции приложения в стандарте OpenAI Tool Calling.

tools = [
 {
 "type": "function",
 "function": {
 "name": "get_billing_info",
 "description": "Получить баланс пользователя или статус пополнения."
 }
 },
 {
 "type": "function",
 "function": {
 "name": "escalate_to_human",
 "description": "Перевести диалог на оператора. Строго при жалобах или ошибках.",
 "parameters": {
 "type": "object",
 "properties": {
 "reason": {"type": "string", "description": "Суть проблемы"}
 },
 "required": ["reason"]
 }
 }
 }
]

user_message = "списали 500 рублей дважды, разберитесь немедленно!"

Текст летит в дешевую модель через RouterAPI:

import openai

client = openai.OpenAI(
 api_key="sk-router-..",
 base_url="https://api.routerapi.net/v1"
)

response = client.chat.completions.create(
 model="anthropic/claude-3-haiku", # Или qwen/qwen-2.5-72b-instruct
 messages=[{"role": "user", "content": user_message}],
 tools=tools,
 tool_choice="auto" 
)

tool_call = response.choices[0].message.tool_calls[0]
print(tool_call.function.name) # escalate_to_human
print(tool_call.function.arguments) # {"reason": "двойное списание 500 рублей"}

Вместо парсинга сырого текста разработчик оперирует готовым JSON-объектом. Модель берет на себя извлечение сущностей, а код динамически передает параметры в обработчик.

Разделение зон ответственности (Separation of Concerns)

Семантический роутинг трансформирует архитектуру AI-систем, жестко разделяя фазы понимания и выполнения.

  1. Фаза диспетчеризации (Роутер). Микро-модель читает запрос. Ее цель — классифицировать интент и собрать параметры. Текст ответа не генерируется. Процесс обрывается на выдаче JSON. Цена: доли цента за тысячу запросов.
  2. Фаза выполнения (Workers). Вызывается конкретная функция. Запрос баланса уходит в базу данных и отдает сырые числа. Запрос на анализ логов активирует тяжелую флагманскую LLM, которая получает очищенный и суженный контекст.
  3. Фаза форматирования. Результат от функции (числа из БД или статус операции) оборачивается в человекочитаемый текст через еще один вызов микро-LLM.

Тяжелые модели (GPT-4o, Claude 3.5 Sonnet) включаются только там, где требуется сложный анализ. Вы не сжигаете токены флагманов на классификацию спама или вопросов «как дела».

Изоляция краевых случаев

Внедрение микро-моделей требует жесткого контроля. Основная уязвимость LLM-роутера — попытка вызвать инструмент даже тогда, когда запрос не имеет к нему отношения. Модель не любит отдавать пустой результат.

Эта проблема решается внедрением функции-заглушки (fallback):

{
 "type": "function",
 "function": {
 "name": "general_conversation",
 "description": "ОБЯЗАТЕЛЬНО использовать для вопросов, которые не подходят под другие инструменты: флуд, вопросы не по теме."
 }
}

Вызов general_conversation сигнализирует системе об отсутствии нужного интента. Запрос перехватывается, и пользователь получает стандартный ответ об ограничениях системы.

Эффективность роутинга напрямую зависит от промпт-инжиниринга схем. Модель ориентируется на поле description. Формальное определение границ ("Использовать ТОЛЬКО если..") работает безотказно, в отличие от размытых описаний бизнес-логики.

Итог

Поддержание дерева if-else отвлекает инженера от развития продукта. Вместо оптимизации баз данных разработчик пишет костыли для обработки синонимов.

Делегирование роутинга LLM-моделям через единый API устраняет этот слой ручной работы. Архитектура сводится к плоскому реестру независимых обработчиков. Вы добавляете в критический путь сетевой запрос и задержку в сотни миллисекунд. Взамен вы получаете код, который больше не ломается от опечаток, молодежного сленга или сложного синтаксиса. Логика системы становится устойчивой к человеческому фактору.

Теги

Ещё по теме