$ sudo teach IT

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

На эти вопросы отвечают два инструмента. Трейсинг показывает полный путь агента по шагам: какие запросы ушли модели, какие инструменты вызваны, сколько времени и токенов это заняло. Evals (оценки) — это автоматическая проверка агента на наборе тестовых вопросов с понятной метрикой. После урока вы сможете находить причину ошибки за минуты и измерять качество агента числом, а не впечатлением.

Что уже умеем

Вы собирали агентов на create_agent и графы на LangGraph, подключали инструменты, RAG и субагентов. В уроке 1.5 вы получали ответ модели строго по схеме Pydantic, а в уроке 2.5 писали middleware. Сегодня middleware пригодится для логирования, а структурированный ответ — для автоматической оценки ответов агента другой моделью.

Что вы узнаете в рамках статьи

  • как логировать вызовы инструментов через middleware;
  • как включить трейсинг LangSmith переменными окружения и что показывает трейс;
  • как составить датасет тест-кейсов для агента;
  • как написать eval-скрипт с проверкой exact match и LLM-as-judge;
  • как считать accuracy, precision и recall и сравнивать версии агента.

Ключевые термины

  • Трейс (trace) — запись одного запуска агента со всеми шагами: вызовами модели, инструментов, их входами, выходами, временем и токенами.
  • LangSmith — платформа LangChain для просмотра трейсов и проведения оценок.
  • Датасет — набор тест-кейсов: входной вопрос и эталонный ответ или ожидаемое поведение.
  • Eval (оценка) — прогон агента по датасету с автоматической проверкой каждого ответа.
  • Exact match — проверка, совпадает ли ответ с эталоном буквально.
  • LLM-as-judge — проверка, при которой ответ агента оценивает другая модель по заданному критерию.
  • Метрика — число, которое суммирует результаты оценки: доля правильных ответов, точность, полнота.

Логирование шагов агента

Самый простой способ увидеть, что делает агент, — записывать каждый вызов инструмента. Для этого подойдёт middleware с декоратором @wrap_tool_call: он оборачивает выполнение любого инструмента, и вы можете записать аргументы, результат и время.

import logging
import time
from langchain.agents.middleware import wrap_tool_call

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s")
log = logging.getLogger("agent")

@wrap_tool_call
def log_tools(request, handler):
    name, args = request.tool_call["name"], request.tool_call["args"]
    start = time.perf_counter()
    try:
        result = handler(request)
        log.info("tool=%s args=%s ok %.2fs", name, args, time.perf_counter() - start)
        return result
    except Exception as error:
        log.error("tool=%s args=%s error=%s", name, args, error)
        raise

Подключите его через create_agent(..., middleware=[log_tools]). Что увидим в консоли при каждом вызове: строку вроде 2026-10-02 12:00:01 tool=find_order args={'order_id': 'A-17'} ok 0.03s. Используйте модуль logging, а не print: у записей есть уровни и время, их легко отправить в файл или систему мониторинга. Не пишите в логи ключи API и персональные данные пользователей.

Трейсинг в LangSmith

Логи показывают инструменты, но не то, что именно ушло модели. Полную картину даёт трейсинг. Для агентов LangChain он включается без изменений в коде: достаточно задать переменные окружения и получить ключ в настройках LangSmith.

export LANGSMITH_TRACING=true
export LANGSMITH_API_KEY="ваш-ключ-langsmith"
export LANGSMITH_PROJECT="support-agent"

После этого каждый вызов invoke попадает в проект support-agent в веб-интерфейсе LangSmith. В трейсе видно дерево шагов: каждое обращение к модели с полным списком сообщений и ответом, каждый вызов инструмента с аргументами и результатом, время каждого шага, число токенов и стоимость. Если агент ответил неверно, откройте трейс и пройдите по шагам: обычно сразу видно, где всё пошло не так.

К запускам можно добавить теги и метаданные, чтобы потом фильтровать трейсы, например по версии промпта:

result = agent.invoke(
    {"messages": [{"role": "user", "content": "Где заказ A-17?"}]},
    config={"tags": ["prompt-v2"], "metadata": {"user_id": "u-123"}},
)

Датасет и простой eval-скрипт

Трейс помогает разобрать одну ошибку, а чтобы оценить агента в целом, нужен датасет. Начните с 20–50 реальных вопросов, для которых вы знаете правильный ответ. Включайте разные типы: простые, сложные, вопросы вне темы агента и случаи, на которых агент уже ошибался.

dataset = [
    {"question": "Сколько дней на возврат товара?", "expected": "14"},
    {"question": "Какой статус у заказа A-17?", "expected": "доставлен"},
    {"question": "Сколько стоит доставка в Казань?", "expected": "390"},
]

def run_agent(question: str) -> str:
    result = agent.invoke({"messages": [{"role": "user", "content": question}]})
    return result["messages"][-1].content

def contains_expected(answer: str, expected: str) -> bool:
    return expected.lower() in answer.lower()

passed = 0
for case in dataset:
    answer = run_agent(case["question"])
    ok = contains_expected(answer, case["expected"])
    passed += ok
    print("OK  " if ok else "FAIL", case["question"])

print(f"Accuracy: {passed / len(dataset):.0%}")

Что увидим в консоли: строки OK или FAIL для каждого вопроса и итог, например Accuracy: 67%. Строгое совпадение строк (exact match) для свободных ответов агента почти никогда не работает: «14 дней» и «Возврат возможен в течение 14 дней» — разные строки. Поэтому здесь используется мягкий вариант: ключевой факт должен содержаться в ответе. Exact match хорош там, где ответ короткий и однозначный: категория, число, да или нет.

LLM-as-judge и метрики

Когда правильный ответ можно сформулировать по-разному, оценку поручают модели-судье. Судья получает вопрос, эталон и ответ агента и возвращает вердикт по схеме Pydantic, как в уроке 1.5.

from openai import OpenAI
from pydantic import BaseModel

client = OpenAI()

class Verdict(BaseModel):
    correct: bool
    reason: str

def judge(question: str, expected: str, answer: str) -> Verdict:
    response = client.responses.parse(
        model="gpt-6-luna",
        instructions="Ты проверяешь ответы агента. Ответ верен, если по смыслу "
                     "совпадает с эталоном и не содержит выдуманных фактов.",
        input=f"Вопрос: {question}\nЭталон: {expected}\nОтвет агента: {answer}",
        text_format=Verdict,
    )
    return response.output_parsed

Судья тоже ошибается, поэтому вручную проверьте его вердикты на десятке примеров, прежде чем доверять. Для судьи иногда выбирают более сильную модель, например gpt-6.1-sol, чем для самого агента.

Главная метрика — accuracy, доля верных ответов. Для решений «да или нет», например «должен ли агент передать обращение оператору», полезны ещё две. Precision (точность) — какая доля передач действительно была нужна: TP / (TP + FP). Recall (полнота) — какую долю нужных передач агент сделал: TP / (TP + FN). Здесь TP — нужные передачи, которые агент сделал, FP — лишние передачи, FN — пропущенные.

Когда скрипт заработает, его можно перенести в LangSmith: датасет загружается через Client().create_dataset, а запуск client.evaluate(...) сохраняет результаты каждого эксперимента, чтобы сравнивать версии агента рядом. Главное правило не зависит от инструмента: после каждого изменения промпта, модели или инструментов прогоняйте один и тот же датасет и сравнивайте метрики.

Частые ошибки

  • Оценивать агента «на глаз» по трём вопросам. Несколько удачных ответов ничего не говорят о качестве. Нужен датасет и метрика.
  • Exact match для свободного текста. Верные ответы будут засчитаны как ошибки. Используйте проверку ключевых фактов или LLM-as-judge.
  • Слепо доверять судье. Проверяйте его вердикты вручную на выборке и уточняйте критерии в инструкции.
  • Писать в логи секреты. Ключи, пароли и персональные данные не должны попадать в логи и трейсы.

Наиболее частые вопросы

  • Сколько тест-кейсов нужно? — Для начала хватит 20–50. Важнее разнообразие и реальные случаи, чем количество. Добавляйте в датасет каждую найденную ошибку.
  • Почему результаты меняются от прогона к прогону? — Ответы модели не полностью детерминированы. Ставьте temperature=0 и сравнивайте версии на одном и том же датасете.
  • Трейсинг платный? — У LangSmith есть бесплатный уровень для небольших объёмов. Логирование через logging бесплатно всегда.
  • Сколько стоит eval? — Каждый тест-кейс — это полный запуск агента плюс вызов судьи. Прикиньте стоимость заранее по токенам.

Итоги

Поздравляем, теперь вы смотрите на агента глазами инженера! Вы умеете логировать вызовы инструментов, включать трейсинг LangSmith и разбирать запуск по шагам, собирать датасет тест-кейсов и писать eval-скрипт с мягкой проверкой и моделью-судьёй. Вы знаете, как считать accuracy, precision и recall и почему любые изменения агента нужно подтверждать прогоном по датасету.

Остался последний шаг на пути к продакшну. В заключительном уроке курса вы защитите агента от prompt injection, добавите guardrails и лимиты и научитесь держать расходы под контролем.

На следующий урок