Как вы логируете и дебажите многошаговые агенты?
Краткий тезис
Многошаговые агенты — это системы, которые выполняют последовательность вызовов LLM, инструментов и логических решений, часто с циклами и ветвлениями. Без структурированного логирования и трейсинга (tracing) отладка таких агентов превращается в «чёрный ящик»: невозможно понять, почему агент принял неверное решение или зациклился. Ключевой подход — записывать каждый шаг в формате «мысль → действие → наблюдение» (ReAct-паттерн) с помощью специализированных инструментов (LangSmith, LangFuse), которые визуализируют граф вызовов, замеряют latency и позволяют воспроизводить сессии.
1. Термины и контекст
Многошаговый агент (multi-step agent) — программа, которая в цикле вызывает LLM, интерпретирует ответ, выбирает следующее действие (вызов инструмента, запрос к БД, переход к другому агенту) и повторяет до достижения цели или лимита шагов. Примеры: ReAct-агенты, Plan-and-Execute, Agentic RAG.
Логирование (logging) — запись событий (входные/выходные данные, ошибки, метаданные) в структурированном виде (JSON, таблицы) для последующего анализа.
Дебаг (debugging) — процесс поиска и исправления ошибок в поведении агента. Включает воспроизведение сценария, анализ логов, сравнение ожидаемого и фактического поведения.
Трейсинг (tracing) — форма логирования, которая фиксирует полную цепочку вызовов (trace) — от пользовательского запроса до финального ответа, включая все вложенные вызовы LLM, инструментов и под-агентов. Позволяет видеть «граф» выполнения.
ReAct-паттерн — архитектура агента, где на каждом шаге LLM генерирует мысль (рассуждение), действие (вызов инструмента) и наблюдение (результат действия). Этот паттерн естественно ложится в структуру лога.
2. Почему отладка многошаговых агентов сложнее обычных RAG-систем?
| Аспект | Обычная RAG | Многошаговый агент |
|---|---|---|
| Количество вызовов LLM | 1 (генерация ответа) | от 2 до десятков (планирование, выполнение, рефлексия) |
| Ветвления | нет | есть (if-else, циклы, параллельные вызовы) |
| Зависимость от состояния | нет (каждый запрос независим) | есть (агент накапливает контекст, память) |
| Недетерминизм | умеренный (разные ответы на один запрос) | высокий (разные пути выполнения из-за LLM) |
| Источники ошибок | retrieval + генерация | планирование, выбор инструмента, выполнение, синтаксис вызова, таймауты |
Без логирования каждого шага невозможно ответить на вопросы:
- Почему агент выбрал именно этот инструмент?
- Где произошла ошибка — в рассуждении LLM или в работе инструмента?
- Не зациклился ли агент?
- Сколько шагов потребовалось и сколько токенов потрачено?
3. Инструменты для трейсинга и логирования
3.1 LangSmith (от LangChain)
- Трейсинг каждого вызова LLM, инструмента, цепочки.
- Визуализация графа выполнения (DAG).
- Метрики: latency, количество шагов, стоимость токенов.
- Воспроизведение (replay) сессии с теми же параметрами.
- Аннотации и ручная оценка.
3.2 LangFuse (open-source)
- Аналогичный функционал: трейсинг, метрики, дашборды.
- Поддержка не только LangChain, но и LlamaIndex, OpenAI SDK, custom агентов.
- Сессии — группировка шагов одного диалога.
- Prompt management — версионирование промптов.
3.3 Weights & Biases (WandB)
- Tracing через
wandb.logиwandb.trace. - Хорош для экспериментов и сравнения конфигураций.
- Меньше специализирован для агентов, но гибок.
3.4 MLflow
- MLflow Tracing (новый модуль) — захват вызовов LLM и инструментов.
- Интеграция с OpenTelemetry.
3.5 Самописное логирование
- JSON-логи в файл/базу данных.
- Плюс: полный контроль.
- Минус: нет визуализации, сложно анализировать.
4. Структура лога для многошагового агента
Рекомендуемый формат — запись каждого шага как отдельного события с полями:
{
"session_id": "abc123",
"step": 1,
"timestamp": "2025-03-20T10:00:00Z",
"type": "thought",
"content": "Пользователь спрашивает про погоду. Нужно вызвать инструмент get_weather.",
"llm_call_id": "call_001"
}
Далее:
{
"session_id": "abc123",
"step": 2,
"timestamp": "2025-03-20T10:00:01Z",
"type": "action",
"tool": "get_weather",
"arguments": {"city": "Москва"},
"llm_call_id": "call_001"
}
Затем:
{
"session_id": "abc123",
"step": 3,
"timestamp": "2025-03-20T10:00:02Z",
"type": "observation",
"result": {"temperature": 5, "condition": "облачно"},
"tool_call_id": "tool_001"
}
И так далее. В конце — финальный ответ агента.
Важные поля
session_id— идентификатор диалога.parent_id— для вложенных вызовов (если агент вызывает под-агента).duration_ms— время выполнения шага.error— если шаг завершился ошибкой.token_usage— количество токенов (prompt + completion).
5. Пример интеграции трейсинга (LangChain + LangSmith)
import os
from langchain.agents import create_react_agent, AgentExecutor
from langchain.tools import tool
from langchain_openai import ChatOpenAI
from langchain.callbacks.tracers import LangChainTracer
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "your_key"
os.environ["LANGCHAIN_PROJECT"] = "agent_debug"
tracer = LangChainTracer()
@tool
def get_weather(city: str) -> str:
"""Get current weather for a city."""
# имитация вызова API
return f"Погода в {city}: 5°C, облачно"
llm = ChatOpenAI(model="gpt-4", temperature=0)
tools = [get_weather]
agent = create_react_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, callbacks=[tracer])
response = agent_executor.invoke({"input": "Какая погода в Москве?"})
В LangSmith появится trace с шагами: вызов LLM → мысль → вызов инструмента → наблюдение → финальный ответ. Можно кликнуть на каждый шаг и увидеть входные/выходные данные, длительность, стоимость.
6. Метрики для оценки работы агента
| Метрика | Описание | Как считать |
|---|---|---|
| Success rate | Доля сессий, где агент достиг цели | (успешные сессии) / (все сессии) |
| Average steps | Среднее количество шагов до завершения | сумма шагов / количество сессий |
| Max steps | Максимальное количество шагов (показатель зацикливания) | максимум по сессиям |
| Average latency | Среднее время выполнения сессии | сумма duration / количество сессий |
| Token cost | Стоимость токенов на сессию | сумма prompt_tokens + completion_tokens по всем вызовам LLM |
| Tool error rate | Доля вызовов инструментов, завершившихся ошибкой | (ошибки инструментов) / (все вызовы инструментов) |
| Hallucination rate | Доля ответов, содержащих фактические ошибки (требует ручной проверки) | (галлюцинации) / (все ответы) |
Эти метрики можно собирать автоматически из трейсов и выводить в дашборд.
7. Стратегии дебага
7.1 Воспроизведение сессии
- Используйте
session_idдля повторного запуска агента с теми же входными данными. - В LangSmith есть кнопка «Replay» — он запускает агента с тем же seed (если задан) и показывает отличия.
7.2 Логирование промежуточных состояний
- Сохраняйте состояние памяти агента (историю сообщений) на каждом шаге.
- При ошибке можно восстановить контекст и понять, на каком шаге произошёл сбой.
7.3 A/B тестирование
- Сравнивайте две версии агента (разные промпты, модели, наборы инструментов) на одном наборе тестовых запросов.
- Используйте метрики из раздела 6 для объективного сравнения.
7.4 Визуализация графа
- Инструменты (LangSmith, LangFuse) показывают дерево вызовов. Ищите узлы с большим временем выполнения или ошибками.
- Если агент зациклился, на графе будет видна повторяющаяся последовательность шагов.
7.5 Логирование не только успехов, но и ошибок
- Фиксируйте все исключения (timeout, rate limit, некорректный ответ инструмента).
- Добавляйте в лог stack trace и входные данные, которые привели к ошибке.
8. Продвинутые техники
8.1 Семантическое логирование
- Вместо сырых JSON-логов используйте векторные эмбеддинги для поиска похожих сессий.
- Например, если агент часто ошибается на запросах про «погоду», можно найти все такие сессии по эмбеддингу запроса.
8.2 Автоматическая детекция аномалий
- Настройте алерты на метрики: если success rate упал ниже 80% за последний час, или среднее количество шагов превысило 10.
- Используйте ML для классификации «хороших» и «плохих» трейсов.
8.3 Интеграция с OpenTelemetry
- Для production-систем можно экспортировать трейсы в Jaeger или Grafana Tempo.
- Это позволит объединить логи агента с логированием инфраструктуры (базы данных, API).
9. Сравнение инструментов трейсинга
| Инструмент | Бесплатный tier | Визуализация графа | Replay сессий | Интеграция с LangChain | Интеграция с LlamaIndex | Open-source |
|---|---|---|---|---|---|---|
| LangSmith | 5000 шагов/мес | Да | Да | Нативная | Через callback | Нет |
| LangFuse | 50 000 шагов/мес | Да | Да | Нативная | Нативная | Да |
| WandB | Ограниченный | Частично (таблицы) | Нет | Через callback | Через callback | Нет |
| MLflow | Неограниченно | Да (экспериментально) | Нет | Через callback | Через callback | Да |
| Самописный | Неограниченно | Нет | Нет | Любая | Любая | Да |
Рекомендация для разработки и отладки — LangSmith или LangFuse (выбор зависит от бюджета и требований к open-source). Для production с большим объёмом — LangFuse self-hosted или комбинация самописного логирования + OpenTelemetry.
10. Best practices
- Логируйте всё, что может пригодиться — входные данные, выходные, метаданные, время, ошибки. Лучше потом отфильтровать лишнее, чем не иметь нужного.
- Используйте структурированные форматы (JSON, Protobuf) — это упрощает парсинг и анализ.
- Добавляйте идентификаторы сессии и родительского вызова — для построения дерева.
- Не логируйте чувствительные данные (пароли, персональные данные) — маскируйте или исключайте.
- Настройте алерты на критические метрики (success rate, количество шагов).
- Регулярно просматривайте случайные трейсы — это помогает заметить паттерны ошибок.
- Версионируйте промпты и конфигурации агента — чтобы можно было сопоставить поведение с версией.
- Используйте seed для LLM (если модель поддерживает) — для воспроизводимости.
Пет-проект для закрепления
Задача Создать простого ReAct-агента, который отвечает на вопросы о погоде и времени, и подключить к нему трейсинг через LangFuse (self-hosted или cloud). Затем намеренно внести ошибку (например, неправильное имя инструмента) и отладить её с помощью визуализации.
Инструменты
- Python, LangChain, LangFuse SDK.
- OpenAI API (или любая другая LLM).
- LangFuse (регистрация на cloud.langfuse.com или локальный запуск через Docker).
Шаги:
- Установить
langchain,langchain-openai,langfuse. - Создать два инструмента:
get_weather(city)иget_time(timezone). - Создать ReAct-агента с промптом, который заставляет LLM сначала думать, потом действовать.
- Подключить
LangFuseCallbackHandlerкAgentExecutor. - Запустить несколько запросов (например, «Какая погода в Лондоне?», «Сколько времени в Нью-Йорке?»).
- Зайти в дашборд LangFuse, найти трейсы, изучить каждый шаг.
- Намеренно изменить имя инструмента в коде (например,
get_weathrвместоget_weather) и запустить снова. Посмотреть, как ошибка отобразится в трейсе (вызов инструмента с ошибкой, LLM попытается исправить или зациклится). - Добавить метрику
success_rateчерез API LangFuse и вывести в дашборд.
Ожидаемый результат Вы сможете визуально отследить каждый шаг агента, увидеть, где произошла ошибка, и понять, как её исправить. Пет-проект демонстрирует, что без трейсинга отладка такой ошибки заняла бы гораздо больше времени.
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 146 | Как проектировать многошаговых агентов? |
| 148 | Как обеспечить воспроизводимость агентов? |
| 149 | Какие метрики использовать для оценки агентов? |
| 150 | Как тестировать агентов в production? |
| 145 | Что такое Agentic RAG и чем отличается от обычного RAG? |
| 144 | Как управлять памятью агента? |
Навигация
- Предыдущий: 146
- Следующий: 148
- Индекс: 00. Индекс разборов