EvaluationLogger を使うと、Python または TypeScript のコードから評価データを直接、柔軟かつ段階的にログできます。Weave の内部データ型に関する深い知識は必要ありません。ロガーをインスタンス化し、そのメソッド (log_prediction、log_score、log_summary) を使用して評価の各 step を記録するだけです。
この方法は、データセット全体やすべての scorer を最初に定義できない複雑な workflow で特に役立ちます。
あらかじめ定義した Dataset と Scorer オブジェクトの list が必要な標準の Evaluation オブジェクトとは異なり、EvaluationLogger では個々の予測とそれに関連するスコアを、利用可能になった時点で段階的にログできます。
より構造化された評価をご希望ですか?事前定義されたデータセットと scorer を備えた、より定型的な評価フレームワークを使用したい場合は、Weave の標準 Evaluation フレームワークを参照してください。EvaluationLogger は柔軟性を重視している一方、標準フレームワークは構造とガイダンスを提供します。
- ロガーを初期化する:
EvaluationLogger のインスタンスを作成し、必要に応じて model と dataset に関するメタデータを指定します。省略した場合はデフォルトが使用されます。
LLM Call (例: OpenAI) のトークン使用量とコストを記録するには、LLMを呼び出す前に EvaluationLogger を初期化してください。
先にLLMを呼び出してから予測をログしても、トークンとコストのデータは記録されません。
- 予測をログする: システムの各入力/出力ペアに対して
log_prediction() を呼び出します。
- スコアをログする: 返された
ScoreLogger を使用して、その予測に対する log_score() を呼び出します。1つの予測に対して複数のスコアをログできます。
- 予測を完了する: 予測を確定するため、スコアをログしたら必ず
finish() を呼び出します。
- サマリーをログする: すべての予測の処理が完了したら、
log_summary() を呼び出してスコアを集計し、必要に応じてカスタムメトリクスを追加します。
予測に対して finish() を呼び出した後は、その予測にそれ以上スコアをログできません。
説明したワークフローを示す Python コードについては、基本例を参照してください。出力とすべてのスコアが一度に利用可能な場合、Python ユーザーは log_example() を使用して手順 2~4 を 1 回の呼び出しにまとめることができます。
次の例は、既存のコードにインラインで EvaluationLogger を使用して予測とスコアをログする方法を示しています。
user_model モデル関数を定義し、入力のリストに適用します。各例について:
- 入力と出力は
log_prediction を使用してログします。
- 単純な正確性スコア (
correctness_score) は log_score を使ってログします。
finish() はその予測のログ記録を完了します。
最後に、log_summary は集計メトリクスを記録し、Weave での自動スコア要約をトリガーします。
import weave
from openai import OpenAI
from weave import EvaluationLogger
weave.init('your-team/your-project')
# トークントラッキングを確実にするため、モデルを呼び出す前に EvaluationLogger を初期化する
eval_logger = EvaluationLogger(
model="my_model",
dataset="my_dataset"
)
# 入力データの例(任意のデータ構造を使用できます)
eval_samples = [
{'inputs': {'a': 1, 'b': 2}, 'expected': 3},
{'inputs': {'a': 2, 'b': 3}, 'expected': 5},
{'inputs': {'a': 3, 'b': 4}, 'expected': 7},
]
# OpenAI を使用したモデルロジックの例
@weave.op
def user_model(a: int, b: int) -> int:
oai = OpenAI()
response = oai.chat.completions.create(
messages=[{"role": "user", "content": f"What is {a}+{b}?"}],
model="gpt-4o-mini"
)
# レスポンスを何らかの方法で使用する(ここでは簡略化のため a + b を返すだけ)
return a + b
# 例を反復処理し、予測してログする
for sample in eval_samples:
inputs = sample["inputs"]
model_output = user_model(**inputs) # 入力を kwargs として渡す
# 予測の入力と出力をログする
pred_logger = eval_logger.log_prediction(
inputs=inputs,
output=model_output
)
# この予測のスコアを計算してログする
expected = sample["expected"]
correctness_score = model_output == expected
pred_logger.log_score(
scorer="correctness", # スコアラーのシンプルな文字列名
score=correctness_score
)
# この特定の予測のログ記録を完了する
pred_logger.finish()
# 評価全体の最終サマリーをログする。
# Weave は上記でログした 'correctness' スコアを自動集計する。
summary_stats = {"subjective_overall_score": 0.8}
eval_logger.log_summary(summary_stats)
print("評価のログ記録が完了しました。Weave UI で結果を確認してください。")
TypeScript SDK には、2 つの API パターンがあります。
- Fire-and-forget API (ほとんどのケースで推奨) : 同期的かつノンブロッキングにログするには、
await を付けずに logPrediction() を使用します
- Awaitable API: 続行する前に処理の完了を確実にしたい場合は、
await とともに logPredictionAsync() を使用します
以下のような場合は fire-and-forget を推奨します。
- 高スループット: 各ログ処理の完了を待たずに、複数の予測を並列で処理できます
- コード変更を最小限に抑えられる: 既存の async/await フローを組み替えることなく、評価ログを追加できます
- シンプル: ほとんどの評価シナリオで、定型コードが少なく、構文もすっきりします
fire-and-forget パターンが安全なのは、logSummary() が結果を集計する前に、保留中のすべての処理が完了するまで自動的に待機するためです。次の例では、fire-and-forget パターンを使ってモデルの予測を評価します。評価ロガーをセットアップし、3 つのテストサンプルでシンプルなモデルを実行した後、await を使わずに予測をログします。import weave, { EvaluationLogger } from "weave"
import OpenAI from "openai"
await weave.init("your-team/your-project")
// モデルを呼び出す前に EvaluationLogger を初期化してトークントラッキングを確実にする
const evalLogger = new EvaluationLogger({
name: "my-eval",
model: "my_model",
dataset: "my_dataset"
})
// 入力データの例
const evalSamples = [
{ inputs: { a: 1, b: 2 }, expected: 3 },
{ inputs: { a: 2, b: 3 }, expected: 5 },
{ inputs: { a: 3, b: 4 }, expected: 7 }
]
// OpenAI を使用したモデルロジックの例
const userModel = weave.op(async function userModel(a: number, b: number): Promise<number> {
const oai = new OpenAI()
const response = await oai.chat.completions.create({
messages: [{ role: "user", content: `What is ${a}+${b}?` }],
model: "gpt-4o-mini"
})
return a + b
})
// 例を反復処理し、fire-and-forget パターンを使って予測とログを行う
for (const sample of evalSamples) {
const { inputs } = sample
const modelOutput = await userModel(inputs.a, inputs.b)
// Fire-and-forget: logPrediction に await は不要
const scoreLogger = evalLogger.logPrediction(inputs, modelOutput)
// この予測のスコアを計算してログする
const correctnessScore = modelOutput === sample.expected
// Fire-and-forget: logScore に await は不要
scoreLogger.logScore("correctness", correctnessScore)
// Fire-and-forget: finish に await は不要
scoreLogger.finish()
}
// logSummary は内部で保留中のすべての処理が完了するまで待機する
const summaryStats = { subjective_overall_score: 0.8 }
await evalLogger.logSummary(summaryStats)
console.log("Evaluation logging complete. View results in the Weave UI.")
各操作が完了してから次に進む必要がある場合、たとえばエラー処理や順次依存する処理を扱うときは、await 可能な API を使用します。次の例では、logPrediction() を await なしで呼び出す代わりに、await を付けて logPredictionAsync() を使用し、各操作が完了してから次の操作に進むようにしています。// logPrediction の代わりに logPredictionAsync を使用する
const scoreLogger = await evalLogger.logPredictionAsync(inputs, modelOutput)
// 各操作を await する
await scoreLogger.logScore("correctness", correctnessScore)
await scoreLogger.finish()
log_example() を使用した簡易ログ記録
log_example() を使用すると、入力、1 つの出力、スコアを 1 回の呼び出しでログできます。この便利なメソッドは、log_prediction()、log_score()、finish() を 1 つのステップにまとめたものです。バッチ評価やオフライン評価のように、ログする入力、モデル出力、スコアがすでにそろっている場合に便利です。
import weave
from weave import EvaluationLogger
weave.init('your-team-name/your-project-name')
eval_logger = EvaluationLogger(
model="my_model",
dataset="my_dataset"
)
eval_samples = [
{'inputs': {'a': 1, 'b': 2}, 'expected': 3},
{'inputs': {'a': 2, 'b': 3}, 'expected': 5},
{'inputs': {'a': 3, 'b': 4}, 'expected': 7},
]
for sample in eval_samples:
inputs = sample['inputs']
output = inputs['a'] + inputs['b']
eval_logger.log_example(
inputs=inputs,
output=output,
scores={"correctness": output == sample['expected']}
)
eval_logger.log_summary({"avg_score": 1.0})
上記の log_example() 呼び出しは、次と同等です:
pred = eval_logger.log_prediction(inputs=inputs, output=output)
pred.log_score(scorer="correctness", score=output == sample['expected'])
pred.finish()
Weave TypeScript SDK では log_example() は使用できません。TypeScript ユーザーは、基本例 に示されている logPrediction() と logScore() のパターンを使用してください。
EvaluationLogger は、基本的なワークフローを超えて、より複雑な評価シナリオに対応できる柔軟なパターンを提供します。このセクションでは、コンテキストマネージャーを使った自動的なリソース管理、モデル実行とログすることの分離、リッチメディアデータの活用、複数のモデル評価の比較表示などの高度な手法を紹介します。
EvaluationLogger は、予測とスコアの両方でコンテキストマネージャー (with 文) をサポートしています。これにより、コードをより簡潔に保ち、リソースを自動的にクリーンアップし、LLM judge call のようなネストされた操作をより適切にトラッキングできます。
このコンテキストで with 文を使用する主な利点は次のとおりです。
- コンテキストを抜ける際に
finish() が自動的に呼び出される
- ネストされた LLM call の token/cost tracking が向上する
- prediction コンテキスト内で、モデル実行後に output を設定できる
import openai
import weave
weave.init("nested-evaluation-example")
oai = openai.OpenAI()
# logger を初期化します
ev = weave.EvaluationLogger(
model="gpt-4o-mini",
dataset="joke_dataset"
)
user_prompt = "Tell me a joke"
# prediction ではコンテキストマネージャーを使用するため、finish() を呼び出す必要はありません
with ev.log_prediction(inputs={"user_prompt": user_prompt}) as pred: # コンテキスト内でモデル呼び出しを実行します
result = oai.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": user_prompt}],
)
# モデル呼び出し後に output を設定します
pred.output = result.choices[0].message.content
# 単純なスコアをログします
pred.log_score("correctness", 1.0)
pred.log_score("ambiguity", 0.3)
# LLM call が必要なスコアでは、ネストされたコンテキストマネージャーを使用します
with pred.log_score("llm_judge") as score:
judge_result = oai.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "Rate how funny the joke is from 1-5"},
{"role": "user", "content": pred.output},
],
)
# 計算後にスコア値を設定します
score.value = judge_result.choices[0].message.content
# 'with' ブロックを抜けると finish() が自動的に呼び出されます
ev.log_summary({"avg_score": 1.0})
このパターンにより、ネストされたすべての操作が親 prediction にひも付けられてトラッキングされるため、Weave UI で正確な token usage と cost data を確認できます。TypeScript には、コンテキストマネージャー向けの Python の with 文のようなパターンはありません。代わりに、finish() を明示的に呼び出す fire-and-forget パターンを使用します。次の例では、prediction をログし、単純なスコアと LLM judge score を追加した後、finish() で prediction を完了します。import weave from 'weave';
import OpenAI from 'openai';
import {EvaluationLogger} from 'weave/evaluationLogger';
await weave.init('your-team/your-project');
const oai = new OpenAI();
// logger を初期化します
const ev = new EvaluationLogger({
name: 'joke-eval',
model: 'gpt-4o-mini',
dataset: 'joke_dataset',
});
const userPrompt = 'Tell me a joke';
// モデル出力を取得します
const result = await oai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [{role: 'user', content: userPrompt}],
});
const modelOutput = result.choices[0].message.content;
// output 付きで prediction をログします
const pred = ev.logPrediction({user_prompt: userPrompt}, modelOutput);
// 単純なスコアをログします
pred.logScore('correctness', 1.0);
pred.logScore('ambiguity', 0.3);
// LLM judge score については、呼び出しを行って結果をログします
const judgeResult = await oai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [
{role: 'system', content: 'Rate how funny the joke is from 1-5'},
{role: 'user', content: modelOutput || ''},
],
});
pred.logScore('llm_judge', judgeResult.choices[0].message.content);
// スコアの記録が完了したら、finish を明示的に呼び出します
pred.finish();
await ev.logSummary({avg_score: 1.0});
TypeScript にはコンテキストマネージャーによる自動クリーンアップはありませんが、logSummary() は結果を集計する前に、未完了の prediction を自動的に finish します。finish() を明示的に呼び出したくない場合は、この動作に任せることもできます。
生のデータセットを log_prediction に inputs として渡すと、Weave は評価の run ごとにデータを再取り込みします。そのため重複データが保存され、データセットが大きい場合や、多数の評価で再利用する場合には容量の無駄になる可能性があります。
この重複を避けるには、評価を実行する前にデータセットを Weave に公開し、その公開済みデータセットの行を inputs として渡してください。Weave はデータを再取り込みする代わりに、公開済みの行への参照を内部参照として解決します。これにより、標準の Evaluation フレームワーク と同様に、各予測が Weave UI 内の特定のデータセット行にリンクされるようになります。
次の例では、データセットを公開して EvaluationLogger からそれにリンクし、その後は他のデータセットと同様に取得して反復処理します。
import weave
from weave import EvaluationLogger
weave.init("your-team-name/your-project-name")
# データセットを公開します(必要なのは一度だけです)
dataset = weave.Dataset(
name="my_eval_dataset",
rows=[
{"question": "What is the capitol of France?", "expected": "Paris"},
{"question": "What U.S. state is Seattle in?", "expected": "Washington"},
{"question": "In what country is Mount Fuji located in?", "expected": "Japan"},
],
)
weave.publish(dataset)
# 公開済みデータセットを取得します
dataset = weave.ref("my_eval_dataset").get()
import weave, {EvaluationLogger, Dataset} from 'weave';
await weave.init('your-team-name/your-project-name');
// データセットを公開します(必要なのは一度だけです)
const dataset = new Dataset({
name: 'my_eval_dataset',
rows: [
{"question": "What is the capitol of France?", "expected": "Paris"},
{"question": "What U.S. state is Seattle in?", "expected": "Washington"},
{"question": "In what country is Mount Fuji located in?", "expected": "Japan"},
],
});
const datasetRef = await dataset.save();
// 公開済みデータセットを取得します
const published = await datasetRef.get();
まずモデルの出力を計算し、その後で予測とスコアを個別にログできます。これにより、評価ロジックとロギングロジックをより明確に分離できます。
# トークン トラッキング を確実に行うため、モデルを呼び出す前に EvaluationLogger を初期化します
ev = EvaluationLogger(
model="example_model",
dataset="example_dataset"
)
# トークン トラッキング のため、モデル出力(例: OpenAI への Call)はロガーの初期化後に実行する必要があります
outputs = [your_output_generator(**inputs) for inputs in your_dataset]
preds = [ev.log_prediction(inputs, output) for inputs, output in zip(your_dataset, outputs)]
for pred, output in zip(preds, outputs):
pred.log_score(scorer="greater_than_5_scorer", score=output > 5)
pred.log_score(scorer="greater_than_7_scorer", score=output > 7)
pred.finish()
ev.log_summary()
複数の予測を並列で処理する場合、fire-and-forget パターンは特に効果的です。次の例では、EvaluationLogger の複数の同時インスタンスを作成して、評価を並列にバッチ処理します。// トークン トラッキング を確実に行うため、モデルを呼び出す前に EvaluationLogger を初期化します
const ev = new EvaluationLogger({
name: 'parallel-eval',
model: 'example_model',
dataset: 'example_dataset'
});
// トークン トラッキング のため、OpenAI への Call などのモデル出力はロガーの初期化後に実行する必要があります
const outputs = await Promise.all(
yourDataset.map(inputs => yourOutputGenerator(inputs))
);
// fire-and-forget: await せずにすべての予測を処理します
const preds = yourDataset.map((inputs, i) =>
ev.logPrediction(inputs, outputs[i])
);
preds.forEach((pred, i) => {
const output = outputs[i];
// fire-and-forget: await は不要です
pred.logScore('greater_than_5_scorer', output > 5);
pred.logScore('greater_than_7_scorer', output > 7);
pred.finish();
});
// logSummary は保留中のすべての操作が完了するまで待機します
await ev.logSummary();
fire-and-forget パターンを使用すると、計算リソースが許す限り多くの評価を並列に処理できます。
入力、出力、スコアには、画像、動画、オーディオ、構造化された表データなどのリッチメディアを含めることができます。log_prediction または log_score メソッドに dict またはメディアオブジェクトを渡すだけです。
import io
import wave
import struct
from PIL import Image
import random
from typing import Any
import weave
def generate_random_audio_wave_read(duration=2, sample_rate=44100):
n_samples = duration \* sample_rate
amplitude = 32767 # 16 ビットの最大振幅
buffer = io.BytesIO()
# バッファに wave データを書き込む
with wave.open(buffer, 'wb') as wf:
wf.setnchannels(1)
wf.setsampwidth(2) # 16 ビット
wf.setframerate(sample_rate)
for _ in range(n_samples):
sample = random.randint(-amplitude, amplitude)
wf.writeframes(struct.pack('<h', sample))
# 先頭から読み取れるように、バッファを先頭に巻き戻す
buffer.seek(0)
# Wave_read オブジェクトを返す
return wave.open(buffer, 'rb')
rich*media_dataset = [
{
'image': Image.new(
"RGB",
(100, 100),
color=(
random.randint(0, 255),
random.randint(0, 255),
random.randint(0, 255),
),
),
"audio": generate_random_audio_wave_read(),
}
for * in range(5)
]
@weave.op
def your_output_generator(image: Image.Image, audio) -> dict[str, Any]:
return {
"result": random.randint(0, 10),
"image": image,
"audio": audio,
}
ev = EvaluationLogger(model="example_model", dataset="example_dataset")
for inputs in rich_media_dataset:
output = your_output_generator(\*\*inputs)
pred = ev.log_prediction(inputs, output)
pred.log_score(scorer="greater_than_5_scorer", score=output["result"] > 5)
pred.log_score(scorer="greater_than_7_scorer", score=output["result"] > 7)
ev.log_summary()
TypeScript SDK では、weaveImage 関数と weaveAudio 関数を使用して画像とオーディオをログできます。次の例では、画像ファイルとオーディオファイルを読み込み、モデルで処理し、スコア付きで結果をログします。import weave, {EvaluationLogger} from 'weave';
import * as fs from 'fs';
await weave.init('your-team/your-project');
// ファイルから画像とオーディオを読み込む
const richMediaDataset = [
{
image: weave.weaveImage({data: fs.readFileSync('sample1.png')}),
audio: weave.weaveAudio({data: fs.readFileSync('sample1.wav')}),
},
{
image: weave.weaveImage({data: fs.readFileSync('sample2.png')}),
audio: weave.weaveAudio({data: fs.readFileSync('sample2.wav')}),
},
];
// メディアを処理して結果を返すモデル
const yourOutputGenerator = weave.op(
async (inputs: {image: any; audio: any}) => {
const result = Math.floor(Math.random() * 10);
return {
result,
image: inputs.image,
audio: inputs.audio,
};
},
{name: 'yourOutputGenerator'}
);
const ev = new EvaluationLogger({
name: 'rich-media-eval',
model: 'example_model',
dataset: 'example_dataset',
});
for (const inputs of richMediaDataset) {
const output = await yourOutputGenerator(inputs);
// 入力と出力の両方にリッチメディアを含めて予測をログする
const pred = ev.logPrediction(inputs, output);
pred.logScore('greater_than_5_scorer', output.result > 5);
pred.logScore('greater_than_7_scorer', output.result > 7);
pred.finish();
}
await ev.logSummary();
EvaluationLogger を使用すると、複数の評価をログして比較できます。
-
以下のコードサンプルを実行します。
-
Weave UI で
Evals タブにアクセスします。
-
比較したい eval を選択します。
-
Compare ボタンをクリックします。Compare ビューでは、次のことができます。
- 追加または削除する Evals を選択する
- 表示または非表示にするメトリクスを選択する
- 特定の例をページで切り替えながら、同じデータセット内の同じ入力に対して各モデルがどのような結果を返したかを確認する
比較の詳細については、Comparisons を参照してください
import weave
models = [
"model1",
"model2",
{"name": "model3", "metadata": {"coolness": 9001}}
]
for model in models: # トークンを取得するには、モデル呼び出しの前に EvaluationLogger を初期化する必要があります
ev = EvaluationLogger(
name="comparison-eval",
model=model,
dataset="example_dataset",
scorers=["greater_than_3_scorer", "greater_than_5_scorer", "greater_than_7_scorer"],
eval_attributes={"experiment_id": "exp_123"}
)
for inputs in your_dataset:
output = your_output_generator(\*\*inputs)
pred = ev.log_prediction(inputs=inputs, output=output)
pred.log_score(scorer="greater_than_3_scorer", score=output > 3)
pred.log_score(scorer="greater_than_5_scorer", score=output > 5)
pred.log_score(scorer="greater_than_7_scorer", score=output > 7)
pred.finish()
ev.log_summary()
import weave from 'weave';
import {EvaluationLogger} from 'weave/evaluationLogger';
import {WeaveObject} from 'weave/weaveObject';
await weave.init('your-team/your-project');
const models = [
'model1',
'model2',
new WeaveObject({name: 'model3', metadata: {coolness: 9001}})
];
for (const model of models) {
// トークンを取得するには、モデル呼び出しの前に EvaluationLogger を初期化する必要があります
const ev = new EvaluationLogger({
name: 'comparison-eval',
model: model,
dataset: 'example_dataset',
description: 'Model comparison evaluation',
scorers: ['greater_than_3_scorer', 'greater_than_5_scorer', 'greater_than_7_scorer'],
attributes: {experiment_id: 'exp_123'}
});
for (const inputs of yourDataset) {
const output = await yourOutputGenerator(inputs);
// クリーンで効率的なロギングのための Fire-and-forget パターン
const pred = ev.logPrediction(inputs, output);
pred.logScore('greater_than_3_scorer', output > 3);
pred.logScore('greater_than_5_scorer', output > 5);
pred.logScore('greater_than_7_scorer', output > 7);
pred.finish();
}
await ev.logSummary();
}
- 各予測の後は、すぐに
finish() を呼び出してください。
log_summary を使用して、個々の予測に紐づかないメトリクス (例: 全体のレイテンシ) を記録します。
- リッチメディアのログ記録は、定性的な分析に最適です。
- 自動終了の動作: わかりやすさのため、各予測で
finish() を明示的に呼び出すことを推奨しますが、logSummary() は未終了の予測を自動的に終了します。ただし、スクリプトで finish() を呼び出した後は、それ以上スコアをログできません。
- 設定オプション:
name、description、dataset、model、scorers、attributes などの設定オプションを使用すると、Weave UI で評価を整理してフィルターできます。