Logfire上でオフライン実行可能なPythonまたはNode.js評価を実行・確認できます。既存のBraintrust評価スイートもLogfireにリダイレクト(転送)できます。 評価セットアップ、テストデータセット、AI採点、エージェント検証、LLM判定、Braintrust移行、またはLogfireのデータセット・実験機能に対応しています。 ライブトラフィック(本番環境の実際のデータ)や基盤システムの監視には使用できません。
Run offline Python (`pydantic_evals`) or Node.js (`logfire/evals`) evaluations and review them in Logfire. Also redirect existing Braintrust `Eval()` suites. Use for eval setup, test datasets, AI scoring, agent checks, LLM judges, Braintrust migration, or Logfire Datasets & Experiments. Not for live traffic or infrastructure monitoring.
pydantic_evals は、実際の関数やエージェント(AI が自動で判断・実行する仕組み)に対して、入力と期待される出力、そしてメタデータ(補足情報)を含むケース群をテストし、1つ以上の評価器で採点して結果レポートを出力します。logfire(datasets 拡張をインストールするとセットで入ります)に依存していますが、logfire.configure() を実行するかどうかで決まるのは「結果を Logfire の UI にもアップロードするか」だけです。省略すれば結果はすべてローカルのターミナルに出力されるだけで、エラーではありません。
一方、エージェント用の評価器(ツール呼び出しの正確さ、実行の流れの追跡など)は、それ以上の機能が必要です。タスク自体の実行経路データを読む必要があるため、logfire.configure() が正しく動いていないと、結果のアップロードに失敗するだけでなく、すべてのケースで「実行経路データがありません」と報告されて、そもそも評価が実行されません。
何より先に簡単な確認:このプロジェクトには既に Braintrust(別の評価ツール)を使った実績がありますか?つまり、単なる依存関係の記載ではなく、実際に Eval(...) の呼び出しや from braintrust import Eval というコードが存在していますか?この場合は CLI 認証が一切不要 です。ステップ 2 をスキップしてください。
既存の Eval() コードはそのまま保ちますが(Python braintrust>=0.30.1 以上、TypeScript braintrust>=3.24.0 以上であることを確認)、環境変数を変えるだけで Logfire に切り替えられます。pydantic_evals への変更は不要です:
export BRAINTRUST_APP_URL="https://logfire-us.pydantic.dev/v1/braintrust" # EU の場合は logfire-eu.pydantic.dev
export BRAINTRUST_API_KEY="<logfire-project-write-token>" # プロジェクト → 設定 → 書込トークン
unset BRAINTRUST_API_URL BRAINTRUST_PROXY_URL # 設定されていると上記エンドポイントが上書きされてしまう
これは 互換性プレビュー版で、完全ではありません:インラインデータ、ローカルタスク、複数スコア、スコアラーに対応していますが、Braintrust でホストされているデータセット・プロンプト・関数、BTQL、モデルプロキシ、サーバー側でのスコア計算には対応していません。また summarize_scores=False、手動の flush() 呼び出し、Rust SDK は概要データをリクエストしないため、実行が成功したように見えても結果が反映されません。詳細と用語の対応表(Braintrust の「プロジェクト」→ Logfire のデータセット名、「スコアラー」→「評価器」など)については、以下を参照してください:https://pydantic.dev/docs/logfire/get-started/comparisons/migrate-from-braintrust/
ステップ 5(検証)に直接進んでください。SDK が出力する結果 URL は Logfire で直接開きます。このパスでは、以下のステップの認証やデータセット定義は不要です。
既存の Braintrust スイートがない場合は、ステップ 2 に進んでください。 ステップ 3 の詳しい確認よりも先に進めば大丈夫です。この段階では、関数やエージェント、データセットの形状を知る必要はまだありません。
実行経路データを読まない評価器だけを使う、ローカルのみの pydantic_evals 実行を明確に希望する場合は、認証と同時に進めます。ステップ 4 で logfire.configure() を省略すれば、結果はターミナルに留まります。結果のアップロード、ホスト型のデータセット、または実行経路ベースの評価器(ToolCorrectness など)を使う場合は Logfire が必要なため、評価ファイルを開く・実行する前に認証を済ませて、正確なプロジェクトを指定してください。
Logfire でバックアップした実行の場合、まず確認を実行します。uvx logfire --non-interactive whoami(JavaScript の場合:npx logfire whoami)。正しいプロジェクトと地域が表示されていれば、ステップ 3 に進みます。そうでない場合は、フルコマンド、フラグ、注意点(--non-interactive の必須性、auth がブラウザを開かない理由、LOGFIRE_TOKEN と認証ファイルの競合、トークンファイルの安全性)を 認証と正確なプロジェクト選択 で確認してください。この CLI フローは logfire.configure() 向けです。ステップ 3 のホスト型データセット操作は、スコープが異なる別の API キーを使います。
テスト対象の関数またはエージェント(PydanticAI エージェント、LLM を呼ぶ関数、入力を受けて出力を返す何らかの処理)と、データセットが既に存在するかを確認します:
Case / Dataset を直接定義する。エージェント駆動のワークフローが標準です。LogfireAPIClient(from logfire.experimental.api_client import LogfireAPIClient)で取得・送信できます。client.get_dataset(name) を型引数なしで呼ぶと生の辞書が返ってきて、push_dataset や .evaluate_sync() に渡せません。入力・出力(使っている場合はメタデータ)の型を渡すと、pydantic_evals.Dataset が返されます:client.get_dataset(name, MyInputType, MyOutputType)。保存されたデータセットがカスタム評価器を含む場合、それらのクラスも custom_evaluator_types=[ExactMatch](カスタムレポート評価器の場合は custom_report_evaluator_types=[...])で渡して、逆シリアル化できるようにします。送信は client.push_dataset(dataset) で行います。このために別の API キーが 設定 → API キー から必要です(スコープ:project:read_datasets/project:write_datasets)。ステップ 2 の CLI 認証ではありません。ユーザーが特にコード外でケースを編集したい場合のみ関連があります。uv add 'logfire[datasets]'
from dataclasses import dataclass
import logfire
from pydantic_evals import Case, Dataset
from pydantic_evals.evaluators import Evaluator, EvaluatorContext, IsInstance
logfire.configure() # 省略すると結果はローカルのみ
@dataclass
class ExactMatch(Evaluator[str, str]):
def evaluate(self, ctx: EvaluatorContext[str, str]) -> bool:
return ctx.output == ctx.expected_output
def classify_sentiment(text: str) -> str:
... # テスト対象の関数
dataset = Dataset[str, str, None](
name='sentiment-eval',
cases=[
Case(name='positive', inputs='I love this', expected_output='positive'),
Case(name='negative', inputs='This is terrible', expected_output='negative'),
],
evaluators=[ExactMatch(), IsInstance(type_name='str')],
)
report = dataset.evaluate_sync(classify_sentiment) # または `await dataset.evaluate(...)`
report.print(include_input=True, include_output=True)
大規模なデータセットまたは LLMJudge(LLM を審査官として使う)などの実際に課金される呼び出しを含む場合は、フルセット前に 2~3 ケースでスモークテスト(事前確認)を実行してください。 バグを 3 ケースで発見すれば 3 回の呼び出しで済みますが、300 ケースで発見すれば 300 回かかります:
smoke = Dataset(
name=dataset.name,
cases=dataset.cases[:3],
evaluators=dataset.evaluators,
report_evaluators=dataset.report_evaluators,
)
smoke_report = smoke.evaluate_sync(classify_sentiment)
smoke_report.print(include_input=True, include_output=True)
スモークテストで予期しないエラーがなく、想定されるアサーションが成功することを確認します。その後、フルデータセットが大規模または課金ベースの呼び出しを使う場合は、ケース数とモデル呼び出しを行う評価器をユーザーに明示し、明確な承認を得てからフルセットを実行してください。スモークテストが成功したからといって、何も言わずにフルセットを実行してはいけません。
カスタム評価器は 必ず @dataclass デコレータ を付けたサブクラスである必要があります。通常のクラスは実行時にエラーになります。ケース名はデータセット内で一意である必要があります。よく使われる評価器は以下のとおりです:
| 評価器 | 確認内容 |
|---|---|
Equals(value) / EqualsExpected() |
指定値または expected_output への完全一致(expected_output が未設定の場合はスキップ。暗黙に対応しないため) |
IsInstance(type_name) |
出力の型が名前で一致 |
LLMJudge(rubric, model=None, score=False) |
LLM を審査官として使う採点。ケースごと・審査官ごとに実際のモデル呼び出しが発生。1~10 のスケールより、はい/いいえ、複数選択肢のようなカテゴリカルな基準が安定しています。審査官を信頼する前に 20~100 のケースで手作業検証をしてください |
ToolCorrectness(expected_tools, ...) |
エージェントが呼んだツール。実行経路データを読むため、ステップ 2 の logfire.configure() がないと、アップロード以前に動作しません |
その他:Contains、MaxDuration、TrajectoryMatch、ArgumentCorrectness、MaxToolCalls、MaxModelRequests も利用可能です。ツール・実行経路関連のものはすべて ToolCorrectness と同じく実行経路データが必要です。完全なリストは pydantic_evals.evaluators を参照してください。これら 5 つのエージェント用(実行経路ベース)評価器は pydantic-evals>=2.4.0 が必要です。古いバージョンを指定している場合は、pyproject.toml / uv.lock を確認してアップグレードしてください。インポート自体が失敗し、暗黙の無効化ではありません。
Python 評価器(任意コード実行)はセキュリティ上の理由で削除されました。古い例に記載されていても使わないでください。
ホスト型データセットを編集している場合:client.push_dataset(dataset) は 毎回サーバー側の評価器を上書き します。ローカルで削除した評価器も削除されます。UI で他のユーザーが編集したデータセット上に古いローカルコピーをプッシュしないでください。
ターミナルに表示されたレポートが Logfire に到達した証拠ではありません。実際に到達したことを確認してください。ケースが成功した、スコアが出た、実行が完了したと報告する前に、この セッション中に実際に確認してください。 実行が失敗した、キャンセルされた、スコアが出なかった場合は、その失敗を明確に報告してください。想像上のスコアや推測で結果を埋めないでください。
ステップ 1 の Braintrust パスから来た場合(ステップ 2 をスキップ)? ここで参照するプロジェクトがありません。SDK が出力した結果 URL を使ってください。正しい Logfire プロジェクトで直接開きます。以下の項目(完了、成功率、ケース詳細)をそのページから確認してください。プロジェクト名で検索する必要はありません。
evaluate {name} という名前で、gen_ai.operation.name = 'experiment'、dataset_name、task_name 属性を持っています。データセット名に合う最新のものを見つけて、logfire.experiment.metadata でケース数と成功率が期待値と合致することを確認してください。そうでない場合は、ステップ 2 で確認したプロジェクトのPython's pydantic_evals and Node.js's logfire/evals run a task against cases, apply evaluators, and return a report. An active Logfire or OpenTelemetry provider may export inputs and outputs even if this skill did not configure it. Intentional upload needs logfire.configure() in Python or a configured Node.js exporter.
Span-based evaluators inspect the task's OpenTelemetry span tree. Without working Logfire instrumentation, Python reports "No span tree available"; Node.js HasMatchingSpan can produce no evaluator result at all. Treat either signal as a setup failure, not evidence about the agent.
Cheap check, before anything else: does this repo already have an existing Braintrust suite — actual Eval(...) calls or from braintrust import Eval in source, not just a braintrust dependency listed without any real usage? This path needs no CLI auth at all — don't run Step 2 for it. The compatibility endpoint is documented only for Logfire Cloud US and EU; for any other supplied origin, use the native evaluation path instead.
Keep the existing Eval() code (Python braintrust>=0.30.1 / TypeScript braintrust>=3.24.0 — verified versions) and redirect its next run to Logfire by changing environment variables only, no pydantic_evals involved:
export BRAINTRUST_APP_URL="https://logfire-us.pydantic.dev/v1/braintrust" # EU: logfire-eu.pydantic.dev
export BRAINTRUST_API_KEY="<logfire-project-api-key>" # Settings -> API Keys
unset BRAINTRUST_API_URL BRAINTRUST_PROXY_URL # these override the endpoint above if set — the #1 "it still hit Braintrust" cause
The destination project's API key needs project:write_otlp and project:read_datasets: the SDK writes the run, then reads experiment metadata for its summary. An ingest-only write token fails with 403.
This compatibility preview covers inline/callable data, local tasks and scorers, multiple scores, one label per name, and normal summary finalization. It excludes Braintrust-hosted resources, BTQL, the model proxy, server-side scoring, and post-finalization feedback. Rust, summarize_scores=False, and manual flush() without comparison do not request the summary needed for projection. See the full coverage and concept mapping.
Skip straight to Step 5 (Verify) — the SDK's own printed result URL also opens directly in Logfire, and nothing else here (auth, dataset definition) applies to this path.
No existing Braintrust suite? Continue to Step 2 now, before the more detailed identification in Step 3 — nothing past this point requires knowing the function/agent or dataset shape yet.
For an explicitly local-only run without span evaluators, use a fresh process that neither preloads nor imports application telemetry; omit Python's logfire.configure() and any Node.js exporter bootstrap. If the task configures an exporter and has no documented disable switch, stop rather than claiming local-only. Uploading, hosted datasets, and span evaluators require Logfire authentication to the exact project.
For a Logfire-backed run, use Authenticate and Select the Exact Project to derive the CLI target from the supplied Logfire URL and run its target-aware whoami check with a verified CLI path — for JS/TS projects without uv, use the external-prefix npm fallback instead of plain npx, which can execute a repository-local binary. Skip to Step 3 if that already reports the right project and resolved --region or --base-url target; otherwise, continue through the full authentication and project-selection sequence there. This CLI flow is for logfire.configure(); Step 3's hosted-dataset operations use a separate API key with different scopes.
Identify the real task and any dataset. Follow repository package, test, and dependency conventions. For a first eval, prefer 3-5 cases from tests, schemas, examples, or synthetic fixtures, with deterministic checks for defined behavior. Do not copy the example unless it fits, replace an eval framework, or refactor unrelated code. Without a runnable task or safe expected behavior, ask one focused question instead of inventing either.
pydantic_evals, or a Node.js module using logfire/evals. This is the default for an agent-driven workflow.LogfireAPIClient (from logfire.experimental.api_client import LogfireAPIClient). Hosted inputs and outputs must be JSON objects; scalar roots accepted by code-defined datasets are rejected. client.get_dataset(name) with no type arguments returns a raw dict, not something push_dataset or .evaluate_sync() can take — pass the input/output (and metadata, if used) types to get back a real pydantic_evals.Dataset: client.get_dataset(name, MyInputType, MyOutputType). If the stored dataset contains custom evaluators, also pass their classes with custom_evaluator_types=[MyEvaluator] (and custom report evaluators with custom_report_evaluator_types=[...]) so they can be deserialized. Push with client.push_dataset(dataset). This needs its own API key from Settings → API Keys (scoped project:read_datasets/project:write_datasets), not Step 2's CLI auth flow. Only relevant if the user specifically wants case editing outside code.Use the repository's existing package manager and lockfile. Install only the missing integration for its language.
Add pydantic-evals[logfire] with the detected Python manager: uv add, poetry add, or pdm add. For a pip/requirements project, update its declared requirements and install from that file; do not introduce a second manager or lockfile.
import logfire
from pydantic_evals import Case, Dataset
from pydantic_evals.evaluators import EqualsExpected, IsInstance
logfire.configure() # omit only in the isolated local-only process described above
def classify_sentiment(text: str) -> str:
return 'positive' if 'love' in text else 'negative'
dataset = Dataset[str, str, None](
name='sentiment-eval',
cases=[
Case(name='positive', inputs='I love this', expected_output='positive'),
Case(name='negative', inputs='This is terrible', expected_output='negative'),
],
evaluators=[EqualsExpected(), IsInstance(type_name='str')],
)
report = dataset.evaluate_sync(classify_sentiment) # or `await dataset.evaluate(...)`
report.print(include_input=True, include_output=True)
Use logfire[datasets] instead only when the task specifically needs the hosted-dataset API from Step 3.
Do not apply this section to Deno, Bun, browsers, or workers; their exporter setup is not validated by this skill.
Add logfire and @pydantic/logfire-node with the manager selected by the existing lockfile: pnpm add, yarn add, bun add, or npm install. Do not introduce a second lockfile.
Configure Logfire before loading the task. Reuse an existing instrumentation entry point rather than configuring it twice.
import * as logfire from '@pydantic/logfire-node'
import { Case, Dataset, EqualsExpected, renderReport } from 'logfire/evals'
logfire.configure()
const classifySentiment = (text: string) => (text.includes('love') ? 'positive' : 'negative')
const dataset = new Dataset<string, string>({
name: 'sentiment-eval',
cases: [new Case({ name: 'positive', inputs: 'I love this', expectedOutput: 'positive' })],
evaluators: [new EqualsExpected()],
})
await dataset.evaluate(classifySentiment).then((report) => {
console.log(renderReport(report, { includeInput: true, includeOutput: true }))
}).finally(() => logfire.shutdown({ timeoutMillis: 5000 }))
Other built-ins include Equals, Contains, IsInstance, MaxDuration, HasMatchingSpan, and LLMJudge. Node.js custom evaluators extend Evaluator, and LLMJudge needs a judge callback. Use @pydantic/logfire-node/datasets only for hosted datasets.
Before running the full dataset, run a smoke test on 2-3 cases if the dataset is large or uses LLMJudge or any evaluator that makes billed model calls. This catches setup errors before they multiply cost across the dataset.
Python:
smoke = Dataset(
name=dataset.name,
cases=dataset.cases[:3],
evaluators=dataset.evaluators,
report_evaluators=dataset.report_evaluators,
)
smoke_report = smoke.evaluate_sync(classify_sentiment)
smoke_report.print(include_input=True, include_output=True)
Node.js:
const smoke = new Dataset({
name: dataset.name,
cases: dataset.cases.slice(0, 3),
evaluators: dataset.evaluators,
reportEvaluators: dataset.reportEvaluators,
})
await smoke.evaluate(classifySentiment).then((report) => {
console.log(renderReport(report, { includeInput: true, includeOutput: true }))
}).finally(() => logfire.shutdown({ timeoutMillis: 5000 }))
Confirm the smoke run has zero unexpected errors and the assertions that should pass do. Then, if the full dataset is large or uses paid model calls, tell the user the case count and which evaluators will make model calls, and get explicit confirmation before running the full dataset — don't run an expensive full pass on the strength of a clean smoke test alone without saying so.
The remaining details in this section are Python-specific. Custom evaluators inherit Evaluator and implement evaluate; use @dataclass for configurable fields and portable serialization. Case names must be unique within a dataset. The evaluators reached for most:
| Evaluator | Checks |
|---|---|
Equals(value) / EqualsExpected() |
Exact match against a literal / expected_output (no-op if expected_output is unset — don't rely on it silently catching that) |
IsInstance(type_name) |
Output's type matches by name |
LLMJudge(rubric, model=None, score=False) |
Subjective or rubric-based judgment; makes billed model requests, so validate the rubric against human-reviewed examples before treating it as a quality gate |
ToolCorrectness(expected_tools, ...) |
Which tools an agent called — reads the span tree, so needs Step 2's logfire.configure() to work at all, not just to upload |
Also available: Contains, MaxDuration, TrajectoryMatch, ArgumentCorrectness, MaxToolCalls, MaxModelRequests — same span-tree dependency as ToolCorrectness for the tool/trajectory ones; see pydantic_evals.evaluators for the full set. These five agentic (span-based) evaluators need pydantic-evals>=2.4.0 — on an older pin, check pyproject.toml/uv.lock and upgrade before reaching for them, since the import itself is what fails, not a silent no-op.
The Python evaluator (arbitrary code execution) was removed for security reasons — don't reach for it even if an older example references it.
If editing a hosted dataset: client.push_dataset(dataset) overwrites server-side evaluators on every push, including removing ones you deleted locally — don't push a stale local copy over a dataset others have edited in the UI.
A report printing to the terminal isn't proof it reached Logfire — confirm the run actually landed. Never report a case as passed, a score, or a run as complete without having actually checked it in this session — if a run fails, cancels, or produces no scores, report that failure plainly; never substitute an invented score or a manual guess at what the result "should" be.
Came from the Step 1 Braintrust path (Step 2 skipped)? There's no whoami-resolved project to look up here — use the SDK's own printed result URL instead, which already opens directly in the right Logfire project. Confirm the same things below (completion, pass mix, case detail) from that page rather than searching by name.
evaluate {name} and carries gen_ai.operation.name = 'experiment', dataset_name, and task_name attributes; find the most recent one matching your dataset's name and confirm logfire.experiment.metadata shows the case count and pass rate you expect. Otherwise, open AI Evaluations → Datasets & Experiments → Experiments in Logfire for the exact project from Step 2, and find the run by name/timestamp.Close with a final report built from what you just confirmed — the run name, exact case count and pass rate you queried, and which evaluators ran — not a template. Include the direct link to this experiment (the SDK's own printed result URL, or the Datasets & Experiments page you opened it from), so the user can see the run without having to ask where to look.
原文・著作権は Anthropic および各プラグイン作者に帰属します。日本語訳は Claude API による自動翻訳です。