Перейти к содержимому

Проверка трасс 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 LangfuseObservations 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.

yaml
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_urlhttps://cloud.langfuse.comХост Langfuse. Сервер, размещённый у вас, использует свой URL
public_key, secret_keyнетAPI-ключи проекта
poll_interval30Секунды между опросами
lookback_hours24Как далеко назад заходит первый опрос
settle_seconds30Сколько секунд ждать после завершения трассы, прежде чем её забрать
max_trace_seconds3600Через сколько секунд незавершённая трасса всё равно забирается

Какие трассы забирает опросчик

Опросчик читает 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. Преобразуйте файл так:

bash
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 трассы или строковый inputtask_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 вот этим:

yaml
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:

bash
curl -H "X-API-Key: $KEY" http://localhost:8000/admin/api/trace_writeback

Ответ считает ожидающие, отправленные, неудавшиеся и удержанные оценки и указывает причину, по которой ждёт каждая удержанная трасса.

Результаты в экспортах Potato

Каждая трасса, опрошенная или преобразованная, хранит свои аннотации в annotation_output/<user>/user_state.json. Экспорт agent_eval агрегирует ответы по трассам, давая распределение меток и большинство для каждой схемы с одним выбором и среднее для каждой числовой:

bash
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 читает эти наблюдения и превращает каждое в шаг, который рецензенты могут разметить.

Что почитать дальше