Проблема: агент — чёрный ящик
В обычном бэкенде упавший запрос оставляет стектрейс, и по логам видно, что произошло. С LLM-агентом всё иначе: он не «падает», он тихо ошибается — уверенно выдаёт неправильный ответ. И причина спрятана в цепочке невидимых шагов: какой промпт реально ушёл в модель после подстановки, что вернул retriever, как агент решил вызвать инструмент, что тот ответил, как модель это проинтерпретировала.
Три вопроса, на которые обычные логи не отвечают, а observability для LLM — да:
- Что пошло не так? На каком шаге reasoning свернул не туда — плохой retrieval, кривой tool-ответ, галлюцинация?
- Сколько это стоит? Токены и деньги на каждый вызов, латентность каждого шага — где узкое место.
- Стало ли лучше? Поменял промпт — качество выросло или деградировало? Без измерения это гадание.
Термин из SRE: способность понять внутреннее состояние системы, наблюдая её внешние сигналы (трейсы, метрики, логи). Для LLM-агентов ключевой сигнал — трейс: полное дерево вложенных вызовов одного запроса. LangSmith специализируется именно на LLM-трейсах: он понимает промпты, токены, tool-вызовы, а не просто строки.
Что такое LangSmith
LangSmith — платформа для наблюдаемости и оценки LLM-приложений от команды LangChain. Три роли, вокруг которых построен этот урок:
- Трейсинг (observe). Записывает каждый запуск агента как дерево runs — с промптами, ответами, токенами, латентностью, ошибками.
- Дебаг (debug). Даёт разобрать любой трейс по шагам, найти сбойный узел, переиграть промпт в Playground.
- Оценка (evaluate). Прогоняет систему на датасете и измеряет качество автоматическими и человеческими оценщиками — до и после изменений.
Частое заблуждение: «LangSmith работает лишь с LangChain/LangGraph». Нет — трейсить можно любой Python/JS-код: голые вызовы OpenAI/Anthropic SDK, кастомные функции, что угодно. С LangChain/LangGraph трейсинг включается автоматически, а для остального есть декоратор @traceable. Это отдельный SaaS-продукт (есть облако и self-hosted), а не часть фреймворка.
В модуле 03 ты уже подключал LangSmith к LangGraph-агенту ради отладки графа. Здесь смотрим шире — как на production-инструмент: не только «посмотреть трейс», но и построить измеримый процесс контроля качества.
Модель данных: runs, trace, spans
Чтобы читать трейсы, надо понять их структуру. Всё строится на одной сущности — run (запуск). Run — это одна единица работы: вызов LLM, вызов инструмента, шаг цепочки. Runs вкладываются друг в друга, образуя дерево. Всё дерево одного запроса — это trace.
Такое дерево сразу отвечает на «где сломалось»: раскрываешь трейс, идёшь по узлам и видишь, что, например, web_search вернул пустоту → следующий LLM-call галлюцинировал на пустом контексте. В плоских логах это заняло бы час; в трейсе — 20 секунд.
Роль 1: трейсинг — как подключить
Хорошая новость: включение трейсинга почти не требует кода. LangSmith читает переменные окружения и начинает записывать автоматически.
export LANGSMITH_TRACING=true
export LANGSMITH_API_KEY="ls-..." # ключ из настроек LangSmith
export LANGSMITH_PROJECT="my-agent-prod" # проект = группа трейсов
# (в старых версиях переменные назывались LANGCHAIN_TRACING_V2 и т.д.)
Дальше — два сценария. С LangChain/LangGraph ничего писать не надо: трейсы полетят сами. Для любого другого кода — оборачиваешь функции декоратором @traceable, и они появляются в дереве как runs.
from langsmith import traceable
from openai import OpenAI
client = OpenAI() # вызовы OpenAI SDK LangSmith подхватит автоматически
@traceable # эта функция станет run в трейсе
def research(topic: str) -> str:
docs = search_web(topic) # если тоже @traceable — вложится как дочерний run
resp = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": f"Суммаризируй: {docs}"}],
)
return resp.choices[0].message.content
@traceable
def search_web(topic: str) -> list[str]:
...
# просто вызываешь — трейс с деревом runs появится в LangSmith
research("рынок EV 2025")
В проде трейсов тысячи. Навешивай tags (например, версия промпта, тип запроса) и metadata (user_id, session_id) — по ним потом фильтруешь: «покажи все медленные трейсы версии prompt-v3 у пользователей из группы B». Без этого observability в проде превращается в стог сена. Это ровно то, что ты делал в LangGraph-уроке модуля 03, — здесь тот же приём для любых систем.
Роль 2: дебаг по трейсу
Трейс записан — теперь по нему дебажим. Типичный цикл разбора сбойного запроса:
1. Фильтр → находим трейс: по ошибке / высокой латентности / фидбеку юзера 2. Дерево → раскрываем runs, идём по шагам сверху вниз 3. Узел → находим сбойный: пустой retrieval? кривой tool-ответ? плохой промпт? 4. Inspect → смотрим ТОЧНЫЙ вход/выход этого run (промпт после подстановки!) 5. Playground → правим промпт прямо в UI, прогоняем, сравниваем результат 6. Fix → переносим исправление в код, проверяем на новом трейсе
Что именно смотреть в узлах — по типам:
| Тип run | На что смотреть при дебаге |
|---|---|
| llm | Финальный промпт после подстановки переменных, ответ, токены. Часто баг — в самом промпте, а не в модели |
| tool | Что реально передали инструменту и что он вернул. Мусор на входе LLM часто отсюда |
| retriever | Какие документы вернулись и релевантны ли. Пустой/нерелевантный retrieval → галлюцинация |
| chain | Латентность и стоимость поддерева — где узкое место по времени и деньгам |
Самый частый инсайт дебага: баг не в модели, а в промпте, который до неё дошёл. Переменная не подставилась, контекст обрезался, в шаблон попал мусор из retrieval. Трейс показывает реальный текст, ушедший в LLM, — а не то, что ты думаешь, что отправил. Playground позволяет тут же поправить его и переиграть, не трогая код.
Роль 3: оценка качества
Трейсинг и дебаг отвечают на «что случилось сейчас». Но production-агента надо улучшать, не ломая. Здесь ключевой вопрос: «я поменял промпт — стало лучше или хуже?» Глазами на трёх примерах это не проверить. Нужна систематическая оценка на датасете.
Три сущности:
- Dataset — набор примеров
{вход → ожидаемый выход}(golden examples). Можно собрать вручную, синтетически или из реальных трейсов прода — прямо в LangSmith добавить интересный трейс в датасет. - Evaluator — функция, ставящая оценку выходу: точное совпадение, кастомная логика или LLM-as-judge (модель оценивает по критериям).
- Experiment — прогон твоей системы на датасете со всеми оценщиками. Результаты разных версий сравниваются бок о бок.
from langsmith import Client, evaluate
client = Client()
# 1. Датасет golden-примеров (создаётся раз; можно пополнять из прод-трейсов)
dataset = client.create_dataset("qa-golden")
client.create_examples(
dataset_id=dataset.id,
inputs=[{"question": "Столица Франции?"}],
outputs=[{"answer": "Париж"}],
)
# 2. Оценщик: LLM-as-judge — правильный ли ответ по смыслу
def correctness(run, example) -> dict:
predicted = run.outputs["answer"]
expected = example.outputs["answer"]
score = llm_judge(predicted, expected) # модель судит: 0..1
return {"key": "correctness", "score": score}
# 3. Эксперимент: прогоняем систему на всём датасете
evaluate(
lambda inputs: my_agent(inputs["question"]), # тестируемая система
data="qa-golden",
evaluators=[correctness],
experiment_prefix="prompt-v3", # версия для сравнения
)
# → в UI: таблица оценок, сравнение prompt-v3 vs prompt-v2 по метрикам
Круг замыкается: прод-трейсы → находишь плохие кейсы → добавляешь их в датасет → меняешь промпт → эксперимент показывает, что метрика выросла и старые кейсы не сломались (это и есть regression testing) → катишь в прод → снова трейсишь. Без оценки ты улучшаешь вслепую; с ней — по числам. Детали оценщиков (LLM-as-judge, RAGAS, A/B) — в разделе «Оценка качества».
Типичные ошибки
Подключать observability, когда прод уже горит, — поздно: сбойные запросы уже не воспроизвести. Включай трейсинг с первого дня, это две переменные окружения.
Тысячи безымянных трейсов невозможно фильтровать. Без user_id, версии промпта, типа запроса нужный кейс не найти. Размечай сразу.
Правка промпта без прогона на датасете — рулетка: чинишь один кейс, ломаешь пять. Любое изменение → эксперимент с оценщиками до/после.
Трейсы содержат реальные вход/выход — там могут оказаться PII, токены, секреты. Настраивай маскирование/фильтрацию и учитывай приватность (для строгих требований — self-hosted или Langfuse).
Шпаргалка
ЗАЧЕМ: агент — чёрный ящик, тихо ошибается. Нужна observability под LLM.
3 вопроса: что сломалось? · сколько стоит? · стало ли лучше?
LANGSMITH = трейсинг + дебаг + оценка (от LangChain, но НЕ только для него)
работает с любым кодом: LangChain/LangGraph — авто, остальное — @traceable
МОДЕЛЬ ДАННЫХ:
run = одна единица работы (llm / tool / retriever / chain)
trace = дерево runs одного запроса
каждый run: type · inputs · outputs · latency · tokens+cost · error · tags/meta
① ТРЕЙСИНГ (подключение):
env: LANGSMITH_TRACING=true · LANGSMITH_API_KEY · LANGSMITH_PROJECT
@traceable на функции · теги+метаданные (user_id, версия промпта) — ОБЯЗАТЕЛЬНО
② ДЕБАГ: фильтр → дерево → сбойный узел → inspect (промпт ПОСЛЕ подстановки!) → Playground → fix
llm: финальный промпт+ответ · tool: вход/выход · retriever: релевантность · chain: latency/cost
③ ОЦЕНКА: dataset (golden, из прод-трейсов) + evaluator (LLM-as-judge/custom) + experiment
меняешь промпт → эксперимент до/после → метрика выросла, регрессий нет → в прод
ПЕТЛЯ: прод-трейс → плохой кейс → в датасет → правка → эксперимент → прод → трейс
ГРАБЛИ: трейсинг «на потом» · без тегов · правка промпта на глаз · PII в трейсах
Практическое задание
Задание: подключи observability к агенту
- Заведи бесплатный проект в LangSmith, включи трейсинг тремя переменными окружения и прогони любого агента из модулей 03–05. Открой трейс, разверни дерево runs.
- Найди в трейсе самый долгий и самый «дорогой» (по токенам) шаг. Где узкое место — LLM-вызов, инструмент, retrieval?
- Оберни в
@traceableкусок кода без LangChain (голый вызов OpenAI/Anthropic SDK) и убедись, что он появился в дереве. - Навесь на трейсы теги (версия промпта) и метаданные (user_id). Отфильтруй трейсы по тегу в UI.
- Сломай что-нибудь специально (испорти промпт-шаблон, чтобы переменная не подставлялась) и найди это по трейсу. Что показывает узел llm — промпт до или после подстановки?
- Собери мини-датасет из 5 примеров, напиши простой evaluator (точное совпадение или LLM-as-judge) и запусти
evaluateдля двух версий промпта. Какая победила по метрике? - Со звёздочкой: добавь интересный трейс из «прода» (своего теста) в датасет прямо из UI и включи его в regression-набор.
Что дальше
LangSmith — облачный SaaS. Если нужны self-hosted, полный контроль над данными и open-source — есть Langfuse с той же идеологией трейсов и оценки. Следующий урок — про него и про то, когда какой инструмент выбрать.