langsmith-evaluator について
langsmith-evaluatorは、データセット上のエージェントの性能を測る評価器を作成・管理するスキルです。LLMによる判定と決定的なコード評価に対応し、LangSmithへの評価器アップロードも行えます。
主な機能
- 01LLMによる判定
- 02決定的な評価器の作成
- 03回答と実行軌跡の検査
- 04LangSmithへの評価器登録
活用シーン
- 01データセットで回答品質を評価
- 02出力と正解例を照合
- 03エージェントのツール呼び出し順を検証
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ jackjin1997/ clawforge --skill langsmith-evaluatorインストール後、Claude Code で/langsmith-evaluatorを入力して読み込まれたか確認します。
同梱ファイルscripts/upload_evaluators.py
詳細
LangSmith 評価者
データセットでエージェントのパフォーマンスを測定する評価者を作成します。LangSmith では、LLM による判定(LLM を使用して出力を評価)とカスタムコード(決定論的なロジック)の 2 種類がサポートされています。
セットアップ
環境変数
LANGSMITH_API_KEY=lsv2_pt_your_api_key_here # Required
LANGSMITH_WORKSPACE_ID=your-workspace-id # Optional: for org-scoped keys
OPENAI_API_KEY=your_openai_key # For LLM as Judge
依存関係
pip install langsmith langchain-openai python-dotenv
評価者の形式
評価者では、次の 2 種類の関数シグネチャがサポートされています。
方法 1:Dict パラメーター(ローカルで評価を実行する場合):
def evaluator_name(inputs: dict, outputs: dict, reference_outputs: dict = None) -> dict:
"""Evaluate a single prediction."""
user_query = inputs.get("query", "")
agent_response = outputs.get("expected_response", "")
expected = reference_outputs.get("expected_response", "") if reference_outputs else None
return {
"key": "metric_name", # Metric identifier
"score": 0.85, # Number or boolean
"comment": "Reason..." # Optional explanation
}
方法 2:Run/Example パラメーター(LangSmith にアップロードする場合):
def evaluator_name(run, example):
"""Evaluate using run/example dicts.
Args:
run: Dict with run["outputs"] containing agent outputs
example: Dict with example["outputs"] containing expected outputs
"""
agent_response = run["outputs"].get("expected_response", "")
expected = example["outputs"].get("expected_response", "")
return {
"metric_name": 0.85, # Metric name as key directly
"comment": "Reason..." # Optional explanation
}
LLM による判定の評価者
確実に評価するには、構造化出力を使用します。
from typing import TypedDict, Annotated
from langchain_openai import ChatOpenAI
class AccuracyGrade(TypedDict):
"""Structured evaluation output."""
reasoning: Annotated[str, ..., "Explain your reasoning"]
is_accurate: Annotated[bool, ..., "True if response is accurate"]
confidence: Annotated[float, ..., "Confidence 0.0-1.0"]
# Configure model with structured output
judge = ChatOpenAI(model="gpt-4o-mini", temperature=0).with_structured_output(
AccuracyGrade, method="json_schema", strict=True
)
async def accuracy_evaluator(run, example):
"""Evaluate factual accuracy for LangSmith upload."""
expected = example["outputs"].get('expected_response', '')
agent_output = run["outputs"].get('expected_response', '')
prompt = f"""Expected: {expected}
Agent Output: {agent_output}
Evaluate accuracy:"""
grade = await judge.ainvoke([{"role": "user", "content": prompt}])
return {
"accuracy": 1 if grade["is_accurate"] else 0,
"comment": f"{grade['reasoning']} (confidence: {grade['confidence']})"
}
一般的な指標: 完全性、正確性、有用性、プロフェッショナリズム
カスタムコード評価者
完全一致
def exact_match_evaluator(run, example):
"""Check if output exactly matches expected."""
output = run["outputs"].get("expected_response", "").strip().lower()
expected = example["outputs"].get("expected_response", "").strip().lower()
match = output == expected
return {
"exact_match": 1 if match else 0,
"comment": f"Match: {match}"
}
軌跡の検証
def trajectory_evaluator(run, example):
"""Evaluate tool call sequence."""
trajectory = run["outputs"].get("expected_trajectory", [])
expected = example["outputs"].get("expected_trajectory", [])
# Exact sequence match
exact = trajectory == expected
# All required tools used (order-agnostic)
all_tools = set(expected).issubset(set(trajectory))
# Efficiency: count extra steps
extra_steps = len(trajectory) - len(expected)
return {
"trajectory_match": 1 if exact else 0,
"comment": f"Exact: {exact}, All tools: {all_tools}, Extra: {extra_steps}"
}
単一ステップの検証
def single_step_evaluator(run, example):
"""Evaluate single node output."""
output = run["outputs"].get("output", {})
expected = example["outputs"].get("expected_output", {})
node_name = run["outputs"].get("node_name", "")
# For classification nodes
if "classification" in node_name:
classification = output.get("classification", "")
expected_class = expected.get("classification", "")
match = classification.lower() == expected_class.lower()
return {
"classification_correct": 1 if match else 0,
"comment": f"Output: {classification}, Expected: {expected_class}"
}
# For other nodes
match = output == expected
return {
"output_match": 1 if match else 0,
"comment": f"Match: {match}"
}
評価の実行
from langsmith import Client
client = Client()
# Define your agent function
def run_agent(inputs: dict) -> dict:
"""Your agent invocation logic."""
result = your_agent.invoke(inputs)
return {"expected_response": result}
# Run evaluation
results = await client.aevaluate(
run_agent,
data="Skills: Final Response", # Dataset name
evaluators=[
exact_match_evaluator,
accuracy_evaluator,
trajectory_evaluator
],
experiment_prefix="skills-eval-v1",
max_concurrency=4
)
評価者を LangSmith にアップロードする
アップロードスクリプトは、カスタム評価者を LangSmith にデプロイするためのユーティリティツールです。ユースケースに特化した評価者を作成してから、アップロードします。
評価者をアップロードするには、skills/langsmith-evaluator/scripts/ に移動します。
重要: LangSmith API では、評価者に (run, example) シグネチャを使用する必要があります。
run:エージェントの出力を含むrun["outputs"]を持つ dictexample:期待される出力を含むexample["outputs"]を持つ dict
評価者ファイルの作成
# my_project/evaluators/custom_evals.py
def my_custom_evaluator(run, example):
"""Your custom evaluation logic.
Args:
run: Dict with run["outputs"] - agent outputs
example: Dict with example["outputs"] - expected outputs
Returns:
Dict with metric_name as key, score as value, optional comment
"""
# Extract relevant data
agent_output = run["outputs"].get("expected_trajectory", [])
expected = example["outputs"].get("expected_trajectory", [])
# Your custom logic here
match = agent_output == expected
return {
"my_metric": 1 if match else 0,
"comment": "Custom reasoning here"
}
アップロード
# List existing evaluators
python upload_evaluators.py list
# Upload evaluator
python upload_evaluators.py upload my_evaluators.py \
--name "Trajectory Match" \
--function trajectory_match \
--dataset "Skills: Trajectory" \
--replace
# Delete evaluator (will prompt for confirmation)
python upload_evaluators.py delete "Trajectory Match"
# Skip confirmation prompts (use with caution)
python upload_evaluators.py delete "Trajectory Match" --yes
python upload_evaluators.py upload my_evaluators.py \
--name "Trajectory Match" \
--function trajectory_match \
--replace --yes
オプション:
--name- LangSmith での表示名--function- 抽出する関数名--dataset- 対象のデータセット名--project- 対象のプロジェクト名--sample-rate- サンプリング率(0.0~1.0)--replace- 存在する場合は置き換えます(確認を求めます)--yes- 置き換え/削除の確認プロンプトをスキップします
重要 — 安全確認プロンプト:
- 削除や置き換えなどの破壊的な操作の前に、スクリプトは確認を求めます
- 必ずこれらのプロンプトに従ってください — 続行する前にユーザーの入力を待ちます
- ユーザーが明示的に要求しない限り、
--yesフラグは使用しないでください --yesフラグはすべての安全確認プロンプトをスキップします。ユーザーが明示的に許可した自動化ワークフローでのみ使用してください
ベストプラクティス
- LLM による判定には構造化出力を使用する - 自由形式のテキストを解析するよりも信頼性が高くなります
- データセットの種類に合わせて評価者を選ぶ
- 最終応答 → 品質の評価には LLM による判定、形式の評価にはカスタムコード
- 単一ステップ → 完全一致にはカスタムコード
- 軌跡 → 順序/効率の評価にはカスタムコード
- 複数の評価者を組み合わせる - 主観的な評価(LLM)と客観的な評価(コード)の両方を実行します
- LLM による判定には非同期処理を使用する - 並列評価が可能になり、大幅に高速化します
- 評価者を個別にテストする - まず、正解と不正解が分かっている例で検証します
- LangSmith にアップロードする - 新しい実行に対する評価を自動化します
ワークフローの例
# 1. Create evaluators file
cat > evaluators.py <<'EOF'
def exact_match(run, example):
"""Check if output exactly matches expected."""
output = run["outputs"].get("expected_response", "").strip().lower()
expected = example["outputs"].get("expected_response", "").strip().lower()
match = output == expected
return {
"exact_match": 1 if match else 0,
"comment": f"Match: {match}"
}
EOF
# 2. Upload to LangSmith
python upload_evaluators.py upload evaluators.py \
--name "Exact Match" \
--function exact_match \
--dataset "Skills: Final Response" \
--replace
# 3. Evaluator runs automatically on new dataset runs
リソース
関連スキル
- トレースのクエリとエクスポートには langsmith-trace スキルを使用します
- トレースから評価データセットを生成するには langsmith-dataset スキルを使用します
コメント
Markdown 対応