С того момента, когда вы начинаете использовать LLM в production, вы осознаёте необходимость дисциплины классической инженерии ПО — test suite. Что происходит с консистентностью выхода при изменении промпта? Как меняется соотношение стоимость-качество при обновлении версии модели? Как конвертировать ощущение "Claude ответил лучше" в численную метрику? В 2026 году, когда LLM-операции достигли зрелости, побеждают те, кто систематически, а не вручную отвечает на эти вопросы. Инструменты как Promptfoo и LangSmith, а также evaluation pipeline'ы — это страховка для поддержания LLM в production.
Изменение промпта = Изменение кода
Представьте workflow генерации маркетингового контента. Вы отправляете промпт Claude API, получаете черновик блога. В первой версии вы пишете "напиши", во второй добавляете в system prompt "Пиши для Roibase, инженерный тон", в третьей добавляете список "ЗАПРЕЩЁННЫЕ СЛОВА". Каждое изменение влияет на выход, но как вы измеряете это влияние?
В классическом ПО есть unit-тесты — вход фиксирован, выход детерминирован. В LLM вход фиксирован, но выход стохастичен. Одного запуска недостаточно. Необходимо запустить один и тот же промпт 10 раз с разными seed'ами, посмотреть на среднее количество токенов, latency, оценку coherence. Поэтому версионирование промпта критично как версионирование кода. Вы отслеживаете изменения промпта в Git commit'ах, но можете не отслеживать изменения выхода. Здесь вступает в дело evaluation suite: при каждом commit'е автоматически запускаются тесты, вы видите регрессию метрик.
Конкретный сценарий: в n8n workflow вы генерируете контент через Claude. Изменив в промпте "1500 слов" на "1400-1600 слов", средняя длина падает с 1520 до 1480 слов, стоимость токенов снижается на 3%, но оценка читаемости теряет 0.2 пункта. Чтобы увидеть этот трейдофф без ручных экспериментов, необходим автоматический evaluation pipeline.
Promptfoo: Regression Test Suite для промптов
Promptfoo — это open-source CLI-инструмент, где вы определяете промпты в YAML-конфиге, задаёте test case'ы в CSV или JSON, пишете assertion'ы. Команда promptfoo eval запускает все варианты, выдаёт таблицу успех/неудач.
Типичный promptfoo.yaml выглядит так:
prompts:
- id: baseline
text: "Write a blog post about {{topic}}"
- id: roibase-tone
text: "Write a blog post about {{topic}}. Use engineering discipline tone. No hype words."
providers:
- anthropic:messages:claude-3-5-sonnet-20241022
tests:
- vars:
topic: "server-side GTM setup"
assert:
- type: contains
value: "first-party"
- type: javascript
value: output.length > 1400 && output.length < 1600
- type: cost
threshold: 0.05
Запустив этот конфиг, Promptfoo отправляет оба промпта в Claude, проверяет assertion'ы: появляется ли слово "first-party", находится ли объём в пределах 1400-1600 слов, стоит ли стоимость API ниже $0.05? При ошибке показывает, в каком промпте она произошла. Интегрировав в CI/CD, вы получаете автоматическое тестирование изменений промпта в pull request'е — как классический unit-тест.
Почему автоматизация, а не ручное тестирование?
Ручное тестирование: вы отправляете 5 разных тем в Claude, просматриваете выходы, даёте оценку "хорошо". На следующий день меняете промпт и снова тестируете вручную. К 10-й итерации вы забыли, какое изменение как повлияло на какую метрику.
Автоматизация: у вас есть 50 test case'ов (реальные ключевые слова из GSC), при каждом изменении промпта они запускаются автоматически. Таблица регрессии: "baseline prompt — в среднем 1520 слов, новый промпт — 1480 слов, снижение 2.6%". Решение основано на метрике, не на чувстве.
LangSmith: Production Observability
Promptfoo — это инструмент тестирования на этапе разработки. LangSmith (от команды LangChain) позволяет отслеживать, что происходит в production. Каждый вызов LLM логируется в LangSmith: вход, выход, latency, количество токенов, метаданные. В dashboard'е вы видите trace — retrieval, construction промпта, вызов LLM, post-processing цепь шаг за шагом.
Пример: в работах Roibase по оптимизации генеративных движков мы строим LLM pipeline для отслеживания citation'ов ChatGPT. Pipeline: вопрос пользователя → embedding → Pinecone retrieval → injection контекста → Claude → extraction citation'ов. LangSmith записывает каждый шаг. Если rate citation'ов падает ниже 15%, приходит alert — сразу видна проблема с drift промпта или качеством retrieval.
Trace vs Logging
Классический logging: "отправил этот промпт в Claude API, получил этот ответ". Trace: "Retrieval занял 120мс, вернул 5 документов, construction промпта — 15мс, Claude — 2.3 сек, total latency — 2.45 сек, SLA не нарушен". Trace показывает весь end-to-end pipeline. В LLM цепях критично найти узкое место: если retrieval медленный — оптимизируйте индекс БД, если LLM медленный — изменяйте версию модели или уменьшайте токены промпта.
При A/B-тестировании в production LangSmith используется так: 50% трафика идёт на baseline промпт, 50% на новый — каждый вариант записывается отдельной trace-группе, метрики сравниваются в реальном времени. Baseline: средняя latency 2.1 сек, новый промпт: 1.9 сек, но оценка качества выхода падает с 0.85 до 0.80 — таблица трейдоффа видна live.
Evaluation Pipeline: Автоматическая оценка качества
Выход LLM субъективен — как автоматизировать вопрос "хорошо ли это или плохо"? Два подхода: rule-based assertion'ы и LLM-as-a-judge.
Rule-based: assertion'ы как в Promptfoo — contains, length, regex-match. Правила типа "1400-1600 слов", "ни одного восклицательного знака", "как минимум 1 внутренняя ссылка". Быстро, детерминировано, но не захватывает семантическое качество.
LLM-as-a-judge: вы отправляете выход другой LLM (обычно GPT-4 или Claude) на оценку. Пример: "Соответствует ли этот пост инженерному тону? Оцени от 1 до 10". Если judge выдаёт 7.5 — пройдено, если 6 — не пройдено. Этот метод захватывает семантическое качество, но недетерминирован — сама judge-модель стохастична. Решение: каждую eval запускать 3 раза и брать среднее.
В content production workflow Roibase evaluation pipeline выглядит так:
- Claude генерирует черновик блога
- Отправляем черновик в Promptfoo
- Rule-based: проверяем количество слов, количество внутренних ссылок, запрещённые слова
- LLM-as-a-judge: GPT-4 оценивает "соответствие тону" от 1 до 10
- Все метрики записываются в Notion
- Если средняя оценка ниже 8 — alert в Slack
Благодаря этому pipeline'у при генерации 1000 статей стандарты качества сохраняются. Вместо того чтобы manual QA-команда читала каждую статью, она смотрит только на ошибки eval — экономия 90% времени.
A/B-тест: Два промпта, два трейдоффа стоимость-качество
A/B-тест промптов в production работает как классический feature flag. Используете LaunchDarkly или custom flag service: 50% пользователей видят prompt_v1, 50% — prompt_v2. Для каждого варианта собираете метрики: среднее количество токенов, latency, downstream conversion (например, редактор утверждает ли черновик?).
Конкретный пример: в Roibase тестируем новую версию промпта с category-specific guidance. Baseline — общий промпт, новый — содержит дополнительные инструкции по категориям. A/B-тест идёт 2 недели:
| Метрика | Baseline | Новый промпт | Дельта |
|---|---|---|---|
| Средний токен (input+output) | 3200 | 3450 | +7.8% |
| Средняя latency (сек) | 2.1 | 2.3 | +9.5% |
| Стоимость/статья ($) | 0.042 | 0.046 | +9.5% |
| Процент одобрения редактором | 72% | 81% | +12.5% |
| Точность внутренних ссылок | 65% | 89% | +36.9% |
Новый промпт на 10% дороже, но процент одобрения редактором вырастает на 12.5% — стоимость revisions редактора ниже. Точность внутренних ссылок возрастает на 36.9% — SEO выигрыш покрывает стоимость. Решение: новый промпт побеждает, идёт в production.
На время A/B-теста в LangSmith создаются отдельные trace-группы для каждого варианта. Если заметите anomaly (например, у нового промпта 5% HTTP 429 rate limit ошибок), сразу это увидите.
Версионирование: Git + Метаданные
Версию промпта храните в Git как код, но с отдельными метаданными. Папка prompts/:
prompts/
roibase-blog-v1.md
roibase-blog-v2.md
roibase-blog-v3.md
Каждый файл содержит frontmatter метаданные:
---
version: 3
model: claude-3-5-sonnet-20241022
temperature: 0.7
max_tokens: 8000
created: 2026-07-15
deprecated: false
test_suite: promptfoo-blog-eval.yaml
---
# РОЛЬ
Вы пишете для Roibase.
...
Git commit message: "prompt v3: добавлена category-specific guidance, расширен список запрещённых слов". Когда CI/CD видит commit, автоматически запускает Promptfoo test suite. Если тесты прошли — deploy на staging, 24 часа A/B-теста, если успешно — идёт в production.
Благодаря версионированию rollback быстрый: если в production проблема, git revert, за 5 минут старый промпт активен.
Оптимизация стоимости: Token Audit
В LLM приложениях стоимость определяется формулой: input token + output token. Цена Claude Sonnet 3.5 API: $3 за 1M input token'ов, $15 за 1M output token'ов (цена 2026 года). Черновик блога из 1500 слов — ~2000 output token'ов, system prompt + user prompt — ~1200 input token'ов, итого ~$0.042 за статью.
Если вы генерируете 1000 статей в месяц, это $42. Если оптимизировать промпт на 10% снижение output token'ов, сэкономите $6.3/месяц — $75.6/год. Кажется малым, но масштабируется. При 10,000 статей/месяц это $756/год.
В Promptfoo eval suite добавляете cost assertion:
assert:
- type: cost
threshold: 0.045
Если после изменения промпта стоимость превышает $0.045, тест не пройдёт. Вы устанавливаете этот threshold'ы, связывая его с business метриками (процент одобрения редактором, conversion).
Для token audit смотрите на LangSmith trace'ы: какой компонент промпта потребляет больше всего токенов? Например, раздел "ЗАПРЕТЫ" в system prompt занимает 300 токенов — действительно ли он нужен при каждом выз