Забыли как писать обычный код?
Помните тот момент, когда впервые запустили GPT-4 и подумали: "Боже, это решает всё"? А потом потратили три недели на отладку RAG-пайплайна, который выдавал случайные факты о динозаврах вместо финансовых отчетов? (Да, это реальная история. Клиент не оценил.)
Мы все прошли через это. LLM-лихорадка. Кажется, что большие языковые модели могут заменить половину кодовой базы. Но на практике получается дорого, медленно и ненадежно.
Вот вам мой личный баг, который стоил компании $15,000 в месяц на лишних API-вызовах:
# Как НЕ надо делать
async def process_document(text: str) -> dict:
"""Извлекаем структурированные данные из документа"""
prompt = f"""Извлеки из текста:
- Имя клиента
- Сумму договора
- Дату подписания
Текст: {text}
"""
response = await openai.ChatCompletion.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}]
)
# Парсим JSON из ответа
return json.loads(response.choices[0].message.content)
Кажется логичным? А теперь представьте, что этот пайплайн обрабатывает 10,000 документов в день. Каждый вызов к GPT-4o-mini стоит денег. Каждый раз мы парсим JSON, который может сломаться. Каждый раз мы ждем сетевого ответа.
Проблема не в том, что LLM плохи. Проблема в том, что мы используем их там, где нужен обычный код. Мы забыли про детерминированные алгоритмы.
Что такое Delegation Filter?
Delegation Filter - это не инструмент и не библиотека. Это ментальная модель. Простой вопрос, который нужно задать перед каждым архитектурным решением:
"Можно ли решить эту задачу детерминированным кодом?"
Если ответ "да" - не используйте LLM. Если ответ "нет" или "можно, но будет сложно" - тогда уже думайте про нейросети.
Звучит очевидно? Посмотрите на свой текущий проект. Сколько там LLM-вызовов, которые можно заменить регулярными выражениями?
Чек-лист: 7 ситуаций, когда НЕ использовать LLM
1 Извлечение структурированных данных из шаблонов
Документы с фиксированной структурой. Договоры, счета, формы. У них есть поля, которые всегда в одном месте.
# Вместо LLM - используйте это
import re
from datetime import datetime
def extract_contract_data(text: str) -> dict:
"""Извлекаем данные из стандартного договора"""
# Ищем паттерны
client_match = re.search(r"Клиент:\s*(.+)\n", text)
amount_match = re.search(r"Сумма:\s*(\d+)\s*руб", text)
date_match = re.search(r"Дата:\s*(\d{2}\.\d{2}\.\d{4})", text)
return {
"client": client_match.group(1) if client_match else None,
"amount": int(amount_match.group(1)) if amount_match else None,
"date": datetime.strptime(date_match.group(1), "%d.%m.%Y").date()
if date_match else None
}
В 100 раз быстрее. В 1000 раз дешевле. На 100% надежнее.
2 Простые классификации
"Это положительный или отрицательный отзыв?" Если у вас есть 1000 размеченных примеров - обучите классификатор. Random Forest, XGBoost, даже логистическая регрессия.
3 Валидация форматов
Проверка email, телефонов, ИНН. Это регулярные выражения. Точка.
Недавно видел в продакшене:
# Реальный код из продакшена (имена изменены)
prompt = "Проверь, что это валидный email: {email}"
response = llm.generate(prompt)
if "да" in response.lower():
return True
Серьезно? Запускать LLM для проверки email? Это стоит денег. Это создает задержку. И самое главное - это ненадежно.
4 Поиск точных совпадений
Если вам нужно найти в тексте конкретные термины, названия компаний, коды - используйте поиск по подстроке или триграммы. Не семантический поиск.
Семантический поиск отлично работает для похожих смыслов. Но если ищете "ООО 'Рога и копыта'", а LLM находит "компания по производству рогов" - это баг, а не фича.
5 Агрегация числовых данных
Суммирование, средние значения, подсчет. Это математика. Не нужно спрашивать у GPT, сколько будет 2+2.
Хотя... однажды видел, как LLM использовали для подсчета количества строк в CSV. Ответ был: "Примерно 1000 строк, но точное число может отличаться".
6 Роутинг запросов
"Этот запрос про техническую поддержку или про продажи?" Если у вас 5 категорий - сделайте классификатор. Если правила простые - используйте ключевые слова.
LLM для роутинга - это гарантия случайных ошибок. И каждая ошибка означает, что запрос попадет не в тот отдел.
7 Генерация кода по шаблону
CRUD-операции, стандартные API-эндпоинты, миграции баз данных. Используйте шаблонизаторы. Jinja2, Mako, даже f-строки.
LLM будут генерировать разный код каждый раз. Потом этот код нужно тестировать. Потом находить в нем уязвимости.
Когда ИСПОЛЬЗОВАТЬ LLM? Всего 3 случая
| Ситуация | Почему LLM | Пример |
|---|---|---|
| Понимание естественного языка | Человек пишет свободным текстом | "Мне нужна помощь с ошибкой в консоли" |
| Творческие задачи | Нет единственного правильного ответа | Генерация маркетингового текста |
| Работа с неструктурированными данными | Документы без четкого формата | Анализ переписки в email |
Практический пример: фикс RAG-пайплайна
Вернемся к моей истории с динозаврами. Вот как выглядел пайплайн:
# Старый пайплайн (упрощенно)
async def rag_pipeline(query: str, documents: list) -> str:
# 1. Все документы отправляем в эмбеддинг-модель
embeddings = await get_embeddings(documents)
# 2. Семантический поиск
relevant_docs = semantic_search(query, embeddings)
# 3. LLM генерирует ответ
prompt = f"На основе документов ответь: {query}"
return await llm.generate(prompt, relevant_docs)
Проблема: 80% запросов были типа "Сколько страниц в документе?" или "Есть ли в документе слово 'договор'?".
Решение - добавить Delegation Filter:
# Новый пайплайн с Delegation Filter
async def smart_rag_pipeline(query: str, documents: list) -> str:
# Delegation Filter: проверяем, можно ли ответить без LLM
if is_simple_fact_query(query):
return answer_without_llm(query, documents)
if is_format_validation(query):
return validate_with_rules(query, documents)
# Только сложные запросы идут в LLM
embeddings = await get_embeddings(documents)
relevant_docs = semantic_search(query, embeddings)
prompt = f"На основе документов ответь: {query}"
return await llm.generate(prompt, relevant_docs)
def is_simple_fact_query(query: str) -> bool:
"""Определяем, можно ли ответить простыми правилами"""
simple_patterns = [
r"сколько.*страниц",
r"есть ли.*слово",
r"дата.*документа",
r"автор.*кто",
]
for pattern in simple_patterns:
if re.search(pattern, query.lower()):
return True
return False
Результат: стоимость API-вызовов упала на 65%. Задержка уменьшилась в 3 раза. Качество ответов на простые вопросы стало 100%.
Архитектурный паттерн: двухслойная валидация
Лучший способ внедрить Delegation Filter - использовать архитектуру двухслойной валидации. Я подробно писал об этом в статье "Архитектура двухслойной валидации для анализа документов".
Суть проста:
- Первый слой: детерминированные правила (регулярки, шаблоны, алгоритмы)
- Второй слой: LLM для всего, что не попало в первый слой
Это работает лучше, чем чистая LLM. И дешевле. И надежнее.
Ошибки, которые все совершают
Ошибка 1: Использовать LLM для всего "потому что так проще". Да, написать промпт быстрее, чем алгоритм. Но поддерживать это в продакшене - ад.
Ошибка 2: Не измерять стоимость. Каждый LLM-вызов имеет цену. Умножьте на 1000 запросов в день. Умножьте на 30 дней. Ужаснетесь.
Ошибка 3: Доверять LLM критическую логику. Валидация платежей, медицинские диагнозы, юридические решения. Если ошибка стоит дорого - нужен детерминированный код.
Инструменты для внедрения Delegation Filter
Не нужно писать все с нуля. Вот что использую я:
- Marvin - для построения пайплайнов с условиями "если можно без LLM - не используем LLM"
- Guardrails - для валидации выходов LLM (да, иногда LLM нужны, но их ответы нужно проверять)
- Semantic Kernel - для планирования: какие задачи делегировать LLM, какие - обычному коду
Но самый главный инструмент - ваш мозг. И вопрос: "А точно ли здесь нужна нейросеть?"
Что будет дальше?
К 2027 году (да, я заглядываю вперед) мы увидим обратную тенденцию. После хайпа вокруг LLM начнется "возвращение к алгоритмам". Компании поймут, что 80% задач решаются без нейросетей.
Уже сейчас появляются инструменты, которые автоматически определяют, можно ли решить задачу без LLM. Что-то вроде статического анализатора, но для архитектурных решений.
Мой прогноз: через год у каждого уважающего себя инженера будет чек-лист Delegation Filter. Как сейчас у всех есть чек-лист безопасности или code review.
А пока - начните с простого. Возьмите ваш текущий проект. Найдите один LLM-вызов, который можно заменить регулярным выражением. Замените. Посчитайте, сколько сэкономили.
Потом найдите второй. И третий.
Скоро вы заметите, что система стала быстрее, дешевле и надежнее. А главное - вы снова почувствуете себя инженером, а не оператором нейросети.
P.S. Если хотите глубже разобраться в построении пайплайнов, посмотрите мою статью "Как построить семантический пайплайн для LLM". Там есть и про Delegation Filter, и про многое другое.