Проверка трасс Langfuse в Potato
Загружайте трассы Langfuse в Potato опросом проекта или преобразованием экспорта, назначайте на каждую трассу нескольких рецензентов и отправляйте ответы обратно в виде оценок.
Potato читает трассы Langfuse двумя способами, опрашивая проект Langfuse на предмет завершённых трасс или преобразуя файл трасс командой python -m potato.trace_converter --input-format langfuse. Объединённые ответы рецензентов по опрошенной трассе возвращаются в Langfuse как оценки (scores) на самой трассе и на каждом оценённом наблюдении, а результаты по преобразованной трассе остаются в экспортах Potato.
Langfuse — платформа ИИ-инженерии с открытым исходным кодом, которую можно разместить у себя, для отладки и оценки приложений на основе LLM и агентов, а репозиторий Langfuse распространяется под лицензией MIT за пределами папок ee. В модели данных Langfuse трасса (trace) представляет один запрос или одну операцию. Наблюдение (observation) — это один шаг внутри трассы, например вызов LLM, вызов инструмента или шаг извлечения данных, а вложенные наблюдения повторяют структуру приложения. Сессия (session) объединяет трассы одного взаимодействия пользователя, а оценка (score) — это значение, прикреплённое к трассе, наблюдению или сессии.
В собственных очередях разметки Langfuse рецензенты оценивают трассы, наблюдения и сессии по конфигурации оценки (score config). Potato добавляет несколько слепых рецензентов на трассу, измерение согласованности между ними и арбитраж расхождений, а затем возвращает результат в ту же трассу в Langfuse. Очереди ручной проверки трасс LLM и агентов сравнивают функции проверки в Langfuse, LangSmith, Braintrust, Arize и Potato.
Опрос и файл экспорта
В таблице сравниваются два пути.
Опрос (trace_ingestion) | Файл экспорта (trace_converter) | |
|---|---|---|
| Трассы | Все трассы проекта, начавшиеся в пределах окна просмотра назад, а затем каждая новая трасса после её завершения | Трассы из файла |
| API Langfuse | Observations API v2, с прежним API трасс как запасным вариантом | Не используется, поскольку файл готовите вы |
| Поле элемента, которое читает отображение | steps, по одной записи на наблюдение | conversation, по одной реплике на мысль, действие или результат |
| Сохраняемые типы наблюдений | Все типы | GENERATION, SPAN и EVENT |
| Идентификатор сессии | Сохраняется в metadata.session_id | Не сохраняется |
| Оценки, отправляемые в Langfuse | Да | Нет |
Опрос подходит команде, которой нужны проверенные оценки рядом с каждой трассой в Langfuse. Файл экспорта подходит для фиксированной выборки, результаты по которой могут остаться в Potato. Типичные фиксированные выборки — трассы, отобранные для статьи или для отложенного набора для оценки.
Опрос проекта Langfuse
Конфигурация ниже опрашивает Langfuse Cloud раз в минуту и отдаёт каждую трассу двум рецензентам. Первый опрос забирает все трассы, начавшиеся за последние семь дней. Когда оба рецензента ответили, Potato отправляет каждый объединённый ответ обратно в трассу как оценку с именем human.resolved, human.helpfulness или human.step_check. Конфигурация полная и проходит python -m potato.validate_cli --strict.
annotation_task_name: Langfuse trace review
task_dir: .
output_annotation_dir: annotation_output/
data_files:
- data/traces.jsonl
item_properties:
id_key: id
text_key: task_description
user_config:
allow_all_users: true
users: []
# Pull every trace that started in the last 7 days, then poll once a minute.
# Keys for write-back come from LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY.
trace_ingestion:
enabled: true
api_key: change-me
sources:
- type: langfuse
api_url: https://cloud.langfuse.com
public_key: pk-lf-...
secret_key: sk-lf-...
poll_interval: 60
lookback_hours: 168
writeback:
trigger: fully_annotated
min_annotators: 2
score_prefix: human
langfuse:
score_configs: true
num_annotators_per_item: 2
instance_display:
fields:
- key: steps
type: agent_trace
label: Langfuse trace
display_options:
collapse_observations: true
annotation_schemes:
- annotation_type: radio
name: resolved
description: Did the agent answer the user's question correctly?
labels: [correct, partly_correct, wrong]
- annotation_type: likert
name: helpfulness
description: How helpful was the final answer?
size: 5
min_label: Not helpful
max_label: Very helpful
- annotation_type: process_reward
name: step_check
description: Mark each step correct or incorrect.
steps_key: steps
mode: per_stepЗапустите сервер командой potato start config.yaml -p 8000. Для запуска Potato нужен файл данных, и трассы, уже лежащие в data/traces.jsonl, попадают в очередь вместе с опрошенными. Трасса из файла появляется в показанном выше отображении, только если у неё есть список steps.
Каждая запись в trace_ingestion.sources с type: langfuse запускает один опросчик. Опросчик читает такие ключи:
| Ключ | Значение по умолчанию | Смысл |
|---|---|---|
api_url | https://cloud.langfuse.com | Хост Langfuse. Сервер, размещённый у вас, использует свой URL |
public_key, secret_key | нет | API-ключи проекта |
poll_interval | 30 | Секунды между опросами |
lookback_hours | 24 | Как далеко назад заходит первый опрос |
settle_seconds | 30 | Сколько секунд ждать после завершения трассы, прежде чем её забрать |
max_trace_seconds | 3600 | Через сколько секунд незавершённая трасса всё равно забирается |
Какие трассы забирает опросчик
Опросчик читает GET /api/public/v2/observations за временное окно и группирует строки по идентификатору трассы. Сервер без этой конечной точки получает вместо неё прежний API GET /api/public/traces. В руководстве по миграции Langfuse сказано, что Langfuse Cloud обслуживает прежние конечные точки чтения до 16 ноября 2026 года, и при переключении опросчик менять не нужно. Трасса считается завершённой, когда у корневого наблюдения есть время окончания и прошло settle_seconds. Трасса, у которой корневое наблюдение так и не завершается, забирается через max_trace_seconds, а metadata.complete получает значение false. Potato пропускает любую трассу, уже лежащую в хранилище элементов, поэтому после перезапуска ни одна трасса не добавляется дважды.
Каждая трасса становится одним элементом с идентификатором langfuse_<trace id>. Список steps элемента содержит дочерние наблюдения в порядке начала, и каждый шаг хранит вход, выход, тип, имя, модель и идентификатор наблюдения. Имя трассы становится task_description, а у трассы без имени берётся вход трассы. Идентификатор сессии, идентификатор пользователя, теги, вход и выход попадают в metadata. Опросчик забирает все трассы за окно и не фильтрует их по имени, тегу или окружению. Правила автоматизации и очередь триажа решают, какие из опрошенных трасс рецензенты увидят первыми.
Преобразование экспорта Langfuse
Конвертер langfuse читает один объект трассы или список объектов трасс в JSON или JSONL. Каждой трассе нужен список observations с полными объектами наблюдений. Прежняя конечная точка GET /api/public/traces/{traceId} возвращает трассу в такой форме, а конечная точка списка GET /api/public/traces возвращает идентификаторы наблюдений, а не объекты. Строки из Observations API v2 нужно перед преобразованием сгруппировать по traceId в один объект на трассу, положив наблюдения в observations. Преобразуйте файл так:
python -m potato.trace_converter \
--input langfuse_traces.json \
--input-format langfuse \
--output data/traces.jsonlКоманда записывает одну строку JSON на трассу, а --auto-detect распознаёт формат по списку observations, у первой записи которого есть type. Конвертер сопоставляет поля так:
| Поле Langfuse | Поле Potato |
|---|---|
id трассы | id |
name трассы | agent_name |
input.query, input.input, input.question трассы или строковый input | task_description |
Выход наблюдения GENERATION (content, text или completion) | реплика Agent (Thought) в conversation |
Имя и вход наблюдения SPAN | реплика Agent (Action), записанная как name(key=value, ...) |
Выход наблюдения SPAN (result, output или весь объект) | реплика Environment |
Выход наблюдения EVENT или вход, если выхода нет | реплика System (<name>) |
| число наблюдений, имя трассы и модели | строки в metadata_table |
Конвертер сохраняет наблюдения в порядке файла и отбрасывает все остальные типы наблюдений. Типы наблюдений Langfuse включают также agent, tool, chain, retriever, evaluator, embedding и guardrail, и трасса, оснащённая этими типами, теряет эти шаги при преобразовании. Выход трассы, метаданные, идентификатор сессии и уровень наблюдения тоже не переносятся. Если трасса опирается на такие типы или поля, опрашивайте проект.
Для преобразованного файла направьте отображение и любую пошаговую схему на conversation. В конфигурации опроса замените блок instance_display и запись step_check в annotation_schemes вот этим:
instance_display:
fields:
- key: task_description
type: text
label: User request
- key: conversation
type: agent_trace
label: Langfuse trace
annotation_schemes:
- annotation_type: process_reward
name: step_check
description: Click the first step that went wrong.
steps_key: conversation
mode: first_errorОтображения и схемы для трасс Langfuse
Отображение agent_trace показывает каждый шаг карточкой со значком типа и читает и список steps от опросчика, и список conversation от конвертера. Трёхпанельное отображение eval_trace делит те же шаги на рассуждения, вызовы функций и итоговый ответ. Задайте key отображения равным steps или conversation в зависимости от пути, которым пришли трассы.
Обратная запись отправляет ответы на уровне трассы из схем radio, likert, slider, number, select и multiselect. Вопросы на уровне шага используют process_reward, trajectory_eval, tool_call_review, failure_attribution или gui_trajectory. В опрошенной трассе каждая метка шага, которую поставили min_annotators рецензентов, становится оценкой на собственном наблюдении шага. Текстовое поле записывает обоснование для экспорта Potato и никогда не отправляется. Для вопросов о целой сессии Langfuse блок sessions с enabled: true группирует опрошенные трассы по metadata.session_id, а схемы с session_level: true получают один ответ на сессию на странице /sessions (оценка на уровне сессии).
Дизайны таксономия ошибок MAST и оценка агентов τ-bench дают готовые схемы уровня трассы для ошибок агентов.
Оценки в Langfuse
Обратная запись отправляет одну оценку на схему, как только ответили min_annotators рецензентов. При trigger: adjudicated Potato вместо этого ждёт решения арбитра, а арбитражное значение заменяет голосование при любом из триггеров. Каждая оценка называется <score_prefix>.<scheme name> и несёт значение из таблицы ниже:
| Схема | Значение оценки | Тип данных Langfuse |
|---|---|---|
radio, select, likert с labels | Метка, которую выбрало большинство рецензентов | Categorical |
multiselect | Метки, которые выбрали больше половины рецензентов, через запятую | Categorical |
likert без labels | Медианный балл | Numeric |
slider, number | Среднее | Numeric |
| Схемы уровня шага | Метка, которую большинство рецензентов дало шагу, отправляется на наблюдение шага | Categorical |
При ничьей ничего не отправляется, пока арбитр не решит или другой рецензент не разрешит ничью. Комментарий сообщает, как получено значение, например Potato: 2 of 2 annotator(s) chose correct. Метаданные содержат число рецензентов, число ответов по каждому варианту и долю пар рецензентов, которые согласились. Имена пользователей не отправляются. При langfuse.score_configs: true Potato создаёт в Langfuse конфигурацию оценки с именем каждой оценки при первой её отправке. После этого Langfuse показывает в фильтрах категории resolved и диапазон полезности от 1 до 5.
Обратная запись читает ключи Langfuse из LANGFUSE_PUBLIC_KEY и LANGFUSE_SECRET_KEY, когда заданы обе переменные, а иначе из источника langfuse. Potato присваивает каждой оценке фиксированный идентификатор для пары трасса и схема. Langfuse заменяет оценку, только если совпадают идентификатор, имя и дата (предотвращение дубликатов оценок). Поэтому ответ, изменившийся в более поздний день, добавляет вторую оценку с датой этого дня. Проверьте доставку с помощью административного ключа API:
curl -H "X-API-Key: $KEY" http://localhost:8000/admin/api/trace_writebackОтвет считает ожидающие, отправленные, неудавшиеся и удержанные оценки и указывает причину, по которой ждёт каждая удержанная трасса.
Результаты в экспортах Potato
Каждая трасса, опрошенная или преобразованная, хранит свои аннотации в annotation_output/<user>/user_state.json. Экспорт agent_eval агрегирует ответы по трассам, давая распределение меток и большинство для каждой схемы с одним выбором и среднее для каждой числовой:
python -m potato.export --config config.yaml --format agent_eval --output results/Форматы экспорта перечисляют jsonl, parquet, huggingface и другие форматы. Ответы уровня сессии попадают в annotation_output/session_annotations.jsonl. Панель /admin/iaa показывает согласованность рецензентов по каждой схеме, а Согласованность разметчиков объясняет выбор коэффициента. Страница наборов данных строит версионируемые наборы для оценки из опрошенных трасс кнопкой Import ingested traces. Исправленные траектории становятся записями SFT и DPO через редактирование траекторий.
Ограничения
- Обратная запись охватывает только опрошенные трассы. Трасса из файла данных, преобразованная или нет, хранит результаты в Potato.
- Текстовые ответы остаются в Potato. Обратная запись не отправляет свободный текст, и ответы уровня сессии тоже не отправляются.
- Опросчик берёт весь проект. Выборка и приоритет определяются правилами автоматизации и очередью триажа после поступления трассы.
Часто задаваемые вопросы
Что такое Langfuse?
Langfuse — платформа ИИ-инженерии с открытым исходным кодом, которая трассирует приложения на основе LLM и агентов, оценивает трассы и управляет промптами. Репозиторий распространяется под лицензией MIT за пределами папок ee, а Langfuse работает как Langfuse Cloud или на вашем собственном сервере. Potato подключается к Langfuse, чтобы назначить на каждую трассу нескольких рецензентов-людей и отправить их объединённые ответы обратно в Langfuse в виде оценок.
Что Langfuse записывает для наблюдаемости агентов?
Langfuse записывает каждый запуск агента как трассу, а каждый шаг внутри запуска как наблюдение, вложенное в трассу. Шаг — это вызов LLM, вызов инструмента, шаг извлечения данных или другая операция внутри запуска. Наблюдения бывают десяти типов, среди которых generation, agent, tool, retriever и guardrail, а сессии объединяют трассы одного взаимодействия пользователя. Когда трасса содержит наблюдение типа, отличного от span, event и generation, Langfuse строит граф агента по времени и вложенности наблюдений. Опросчик Potato читает эти наблюдения и превращает каждое в шаг, который рецензенты могут разметить.
Что почитать дальше
- Очереди ручной проверки трасс LLM и агентов и Сравнение инструментов оценки ИИ-агентов
- Агентная разметка о других 14 конвертерах трасс
- Как измерить согласованность между LLM-судьёй и разметчиками-людьми для сравнения оценок LLM-as-a-judge в Langfuse с метками рецензентов
- Potato, LangSmith и Langfuse для оценки агентов
- Обратная запись оценок трасс в исходной документации Potato