Суть урока
MLOps звучит как что-то из корпоративного Netflix с сотней инженеров. На деле — это ответ на один вопрос: "мой AI работает так же хорошо, как неделю назад?" Для indie-разработчика MLOps — это три вещи: следить за стоимостью, следить за качеством, и не сломать то что работает.
Ключевые концепции
- Prompt drift — промпт деградирует со временем без изменений в коде
- LangSmith — трейсинг и мониторинг вызовов Claude
- Promptfoo — тестирование промптов на регрессии
- Cost monitoring — бюджетные алерты и аномалии расходов
- Версионирование промптов — Git для промптов, не только для кода
- Baseline metrics — метрики качества для сравнения со временем
Теория
Что такое prompt drift и почему он происходит
Ты написал промпт три месяца назад. Код не менялся. Но вдруг начинаешь замечать: ответы стали длиннее, или менее конкретными, или немного другого тона. Ты ничего не трогал — что случилось?
Причины prompt drift:
Модель сменилась — в коде стоит алиас вместо конкретной версии, или старую модель убрали из API и ты перешёл на новую. Поведение меняется незначительно или заметно. Закрепляй версию модели в коде и следи за списком моделей и выводов из API: Актуальное сейчас.
Изменился контекст — твои пользователи стали писать иначе, вопросы изменились, появились новые паттерны которые промпт не учитывал.
Цепочка агентов сдвинулась — один агент стал выдавать немного другой формат → следующий агент перестал его правильно парсить.
Системный промпт устарел — ты описал контекст который уже не актуален.
LangSmith: трейсинг вызовов Claude
LangSmith (от LangChain) — это observability платформа для AI. Записывает каждый вызов: входной промпт, ответ, latency, стоимость, метки.
Зачем нужен:
- Видеть что реально происходит внутри цепочки агентов
- Сравнивать ответы "сейчас vs неделю назад"
- Находить дорогие вызовы которые можно оптимизировать
- Дебажить когда что-то сломалось в production
Интеграция с Anthropic SDK:
import anthropic
from langsmith import traceable
from langsmith.wrappers import wrap_anthropic
# wrap_anthropic записывает токены и стоимость каждого вызова
client = wrap_anthropic(anthropic.Anthropic())
@traceable(name="content-generator")
def generate_content(topic: str, style: str) -> str:
response = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=1000,
messages=[{
"role": "user",
"content": f"Напиши контент на тему '{topic}' в стиле {style}"
}]
)
return "".join(b.text for b in response.content if b.type == "text")
# Все вызовы автоматически попадают в LangSmith dashboard
result = generate_content("email marketing", "дружелюбный")Что видишь в дашборде:
- Каждый вызов с полным промптом и ответом
- Latency percentiles (p50, p90, p99)
- Стоимость по каждому эндпоинту
- Аномалии (вызов занял в 10 раз дольше обычного)
Promptfoo: тестирование промптов
Promptfoo — это CLI-инструмент для запуска regression-тестов на промптах. Работает как unit-tests, только для AI. В марте 2026 Promptfoo купила OpenAI; по заявлению компании, проект остаётся с открытым исходным кодом, но следи за его развитием.
npm install -g promptfooКонфигурация теста (promptfooconfig.yaml):
prompts:
- "Проанализируй этот отзыв и определи настроение: {{review}}"
providers:
- anthropic:messages:claude-haiku-4-5
tests:
- vars:
review: "Отличный продукт, очень доволен!"
assert:
- type: contains
value: "позитив"
- type: not-contains
value: "негатив"
- vars:
review: "Ужасное качество, больше не куплю"
assert:
- type: contains
value: "негатив"
- type: llm-rubric
value: "Ответ должен идентифицировать негативное настроение"
- vars:
review: "Нормально, ничего особенного"
assert:
- type: contains-any
value: ["нейтрал", "смешанный", "неопределённый"]# Запустить тесты
promptfoo eval
# Сравнить две версии промпта
promptfoo eval --prompts v1-prompt.txt v2-prompt.txtCI/CD интеграция:
# .github/workflows/prompt-tests.yml
name: Prompt Regression Tests
on: [push, pull_request]
jobs:
test-prompts:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- run: npm install -g promptfoo
- run: promptfoo eval --no-progress-bar
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}Теперь при каждом пуше прогоняются тесты. Если промпт регрессировал, promptfoo завершается с ошибкой и CI падает.
Cost monitoring: деньги под контролем
Для indie деньги важнее всего. Один баг в промпте может многократно увеличить дневные расходы.
Встроенный мониторинг стоимости:
import anthropic
from datetime import datetime, timezone
import json
import os
class CostTracker:
def __init__(self, daily_budget_usd: float = 20.0):
self.client = anthropic.Anthropic()
self.daily_budget = daily_budget_usd
self.today_cost = 0.0
self.log_file = "cost_log.jsonl"
# Цены за 1 млн токенов на октябрь 2026. Они меняются: актуальные на странице
# «Актуальное сейчас» (../actual.html). Другие модели добавь по той же схеме.
COSTS = {
"claude-sonnet-5-5": {"input": 2.0, "output": 10.0},
"claude-haiku-4-5": {"input": 1.0, "output": 5.0},
}
def track_call(self, model: str, input_tokens: int, output_tokens: int, endpoint: str):
rates = self.COSTS.get(model, self.COSTS["claude-sonnet-5-5"])
cost = (input_tokens / 1_000_000 * rates["input"] +
output_tokens / 1_000_000 * rates["output"])
self.today_cost += cost
entry = {
"ts": datetime.now(timezone.utc).isoformat(),
"model": model,
"endpoint": endpoint,
"input_tokens": input_tokens,
"output_tokens": output_tokens,
"cost_usd": round(cost, 6)
}
with open(self.log_file, "a") as f:
f.write(json.dumps(entry) + "\n")
# Алерт при превышении 80% бюджета
if self.today_cost > self.daily_budget * 0.8:
self.send_alert(f"⚠️ Потрачено ${self.today_cost:.2f} из ${self.daily_budget} бюджета")
return cost
def send_alert(self, message: str):
# Telegram уведомление
import requests
requests.post(
f"https://api.telegram.org/bot{os.environ['TELEGRAM_BOT_TOKEN']}/sendMessage",
json={"chat_id": os.environ['TELEGRAM_CHAT_ID'], "text": message}
)
tracker = CostTracker(daily_budget_usd=20.0)Аномалии расходов:
def detect_cost_anomaly(log_file: str, window_days: int = 7):
"""Сравнивает сегодняшние расходы со средним за неделю."""
import statistics
with open(log_file) as f:
entries = [json.loads(line) for line in f]
today = datetime.now(timezone.utc).date()
daily_costs = {}
for entry in entries:
date = datetime.fromisoformat(entry['ts']).date()
daily_costs[date] = daily_costs.get(date, 0) + entry['cost_usd']
recent = [cost for date, cost in daily_costs.items()
if (today - date).days <= window_days and date != today]
if len(recent) < 3:
return False
avg = statistics.mean(recent)
std = statistics.stdev(recent)
today_cost = daily_costs.get(today, 0)
# Аномалия: сегодня дороже среднего больше чем на 2 стандартных отклонения
if today_cost > avg + 2 * std:
return f"🚨 Аномалия! Сегодня: ${today_cost:.2f}, среднее: ${avg:.2f}"
return FalseВерсионирование промптов как кода
Промпты — это код. Он должен быть в Git с историей изменений.
Структура репозитория:
prompts/
v1/
system-prompt.md # Версия 1.0
user-template.md
v2/
system-prompt.md # Версия 2.0 — изменили тон
user-template.md
current -> v2/ # Симлинк на текущую версию
CHANGELOG.md # Что изменилось и почемуCHANGELOG.md для промптов:
## v2.0 — 2026-04-15
### Изменения
- Убрал инструкцию "будь кратким" — ответы были слишком короткими для деловых писем
- Добавил примеры форматирования
- Изменил тон с формального на "дружелюбно-профессиональный"
### Метрики до/после (promptfoo)
- Средняя длина ответа: 150 → 280 слов ✅
- Тест "содержит приветствие": 60% → 95% ✅
- Тест "нет слова 'Здравствуйте'": 100% (всё равно) ✅
### Откат
git checkout v1/ если v2 окажется хужеПрактика
Шаг 1: Установить инструменты
# Promptfoo для тестирования промптов
npm install -g promptfoo
# LangSmith (для трейсинга)
pip install langsmith
# Переменные окружения
export LANGSMITH_TRACING="true" # без этого трейсинг не включается
export LANGSMITH_API_KEY="ls__xxx"
export LANGSMITH_PROJECT="my-ai-app"Шаг 2: Написать первые тесты промпта
Создай файл promptfooconfig.yaml для своего основного промпта. Придумай 5-10 тестовых случаев — включая граничные: пустой ввод, очень длинный текст, текст на разных языках, потенциально вредоносный запрос.
promptfoo eval
# Смотри что прошло, что упалоШаг 3: Настроить cost tracking
# cost_tracker.py
# Скопируй код из урока, замени TELEGRAM_BOT_TOKEN и TELEGRAM_CHAT_ID
# Добавь вызов tracker.track_call() после каждого messages.create()Шаг 4: Базовый мониторинг
Запускай раз в день:
# Итог за день
python -c "
import json
from datetime import datetime, timezone
today = datetime.now(timezone.utc).date().isoformat()
total = 0
calls = 0
with open('cost_log.jsonl') as f:
for line in f:
e = json.loads(line)
if e['ts'].startswith(today):
total += e['cost_usd']
calls += 1
print(f'Сегодня: {calls} вызовов, \${total:.4f}')
"Шаг 5: Задание
- Возьми любой промпт из своего проекта
- Напиши 10 тестов в promptfoo
- Запусти тесты — зафиксируй baseline (сколько прошло)
- Внеси небольшое изменение в промпт
- Запусти снова — улучшилось или ухудшилось?
- Закоммить обе версии в Git с описанием изменений
Инструменты и ресурсы
- LangSmith: smith.langchain.com (есть бесплатный тариф, лимиты смотри на сайте)
- Promptfoo: promptfoo.dev —
npm install -g promptfoo - Helicone — альтернатива LangSmith: helicone.ai (в марте 2026 компанию купила Mintlify, сервис работает в режиме поддержки без новых функций; для нового проекта лучше выбрать другой инструмент)
- Braintrust — ещё одна платформа оценки и мониторинга: braintrust.dev
- Weights & Biases — enterprise MLOps: wandb.ai
Ключевые выводы
MLOps для indie — это не DevOps, это дисциплина. Три правила: тестируй промпты перед деплоем (Promptfoo), следи за расходами с алертами (cost tracker), логируй всё в LangSmith чтобы было куда смотреть когда что-то пойдёт не так. Prompt drift — тихий убийца качества. Без тестов ты его не заметишь, пока клиенты не начнут уходить.
Следующий урок
→ Production Observability — что мониторить когда агент в проде
Отметка хранится только в этом браузере и никуда не отправляется. Мой прогресс