netra-evaluation-setup について
netra-evaluation-setup は、QAや開発仕様をNetra向けの編集可能な評価計画に変換するエージェントスキルです。テストシナリオを評価器、変数、合格基準に対応付け、承認後にNetraで作成します。
主な機能
- 01テスト可能な要件の抽出
- 02追加テストシナリオの提案
- 03シナリオと評価器の対応付け
- 04変数マッピングと基準の設定
- 05承認後のNetraでの計画作成
活用シーン
- 01QA仕様に基づく評価計画
- 02エージェントテスト用データセットの準備
- 03単一ターンまたは複数ターンの評価器設定
MCP 対応
Netra MCPツールでデフォルトLLMプロバイダーと評価器の選択肢を確認し、必要に応じてトレースを検索します。ユーザーの承認後にのみ、Netraで評価関連の項目を作成します。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ keyvaluesoftwaresystems/ netra-skills --skill netra-evaluation-setupインストール後、Claude Code で/netra-evaluation-setupを入力して読み込まれたか確認します。
詳細
Netra 評価セットアップ — 仕様駆動型データセット計画
このスキルでは、QA/開発仕様書から、データセット項目、評価器、変数マッピング、合格基準を含む、編集可能な完全な評価計画を作成し、ユーザーの承認後に Netra で必要なものをすべて作成します。
ワークフロー
以下のフェーズを順番に実行してください。次のフェーズに進む前に、各フェーズを完了してください。
フェーズ 0:プロバイダー設定を確認する
計画や作成に着手する前に、組織にデフォルトのプロバイダーが設定されていることを確認してください。
MCP 経由で netra_get_default_llm_configuration を呼び出します。
provider、model、providerConfigurationIdを含む有効な設定が返された場合は、フェーズ 1 に進みます。- null またはエラーが返された場合は、直ちに停止してユーザーに知らせてください。
⚠️ No default provider configured.
Evaluation and dataset creation require a default LLM provider in your organization.
Please configure a default provider in the Netra dashboard before proceeding:
Settings → Providers → Set Default
Once configured, run this skill again.
有効なデフォルトプロバイダーが確認できるまで、以降のフェーズに進まないでください。
フェーズ 0.5:プロジェクトの言語を判定する
SDK コード(タスクの実装、フックのひな型、実行例)を生成する前に、プロジェクトが Python または TypeScript/JavaScript のどちらを使用しているかを判定してください。次の順序でプロジェクトのルートを確認します。
| 判定用ファイル | 言語 |
|---|---|
pyproject.toml、setup.py、requirements.txt、Pipfile | Python |
package.json、tsconfig.json、bun.lockb | TypeScript / JavaScript |
両方が見つかった場合(モノレポの場合)は、どのサブプロジェクトで作業しているかユーザーに尋ねてください。どちらも見つからない場合も、ユーザーに尋ねてください。
以降は、判定した言語の SDK の例のみを生成してください。Python と TypeScript の規約を混在させないでください。
このスキルにおける言語別の規約:
| 概念 | Python | TypeScript |
|---|---|---|
| 初期化 | Netra.init(...)(同期) | await Netra.init(...) |
| シミュレーション API | Netra.simulation.run_simulation(...) | await Netra.simulation.runSimulation({...}) |
| 評価 API | Netra.evaluation.run_test_suite(...) | await Netra.evaluation.runTestSuite({...}) |
| フックのフィールド | before_all、before_each、before、after、after_each、after_all、setup_context、session_id、dataset_item_id | beforeAll、beforeEach、before、after、afterEach、afterAll、setupContext、sessionId、datasetItemId |
| フックオブジェクト | SimulationHooks(before_all=..., before_each=..., before={...}) | const hooks: SimulationHooks = { beforeAll: ..., beforeEach: ..., before: {...} } |
| フックの説明 | すべてのフック関数に fn.description = "..." を設定(必須、200 文字以内) | すべてのフック関数に (fn as any).description = "..." を設定(必須、200 文字以内) |
フェーズ 1:仕様を取り込む
- ユーザーが指定した仕様書(ファイルパス、貼り付けられたテキスト、または URL)を読みます。
- テスト可能な要件をすべて抽出します。各要件について、以下を特定してください。
- シナリオ名 — 検証対象の動作を表す、簡潔で人が読みやすい名前
- カテゴリ — 正常系、異常系、エッジケース、安全性、パフォーマンス
- 入力 — このシナリオをテストするユーザーメッセージまたはトリガー
- 期待される動作 — 正しいエージェントの応答またはアクション
シナリオの命名ガイドライン
テスト対象の動作に基づいて、意味のあるシナリオ名を付けます。
良い例:
- ユーザーのログイン成功
- 有効なメールアドレスでパスワードをリセット
- 商品検索で関連性の高い結果を取得
- プロンプトインジェクションの試みを拒否
- 空のユーザークエリを処理
- 顧客が返金をリクエスト
- アカウント残高を取得
- 複数ステップの旅行計画に関する会話
次のような汎用的な名前は避けてください:
- シナリオ 1
- テストケース 1
- 項目 3
- 検証テスト
- ハッピーパステスト
- 仕様で見落とされている可能性のあるテストケースを追加で提案します:
- 明示されていない境界条件
- よくある敵対的な入力(プロンプトインジェクション、ジェイルブレイクの試み)
- パフォーマンスのエッジケース(非常に長い入力、空の入力、特殊文字)
- エージェントが対応している場合は、複数ステップの推論シナリオ
- 全リストを提示し、続行する前にユーザーに確認を求めます。
フェーズ 2:エージェントの Span 構造を特定する
エージェントが実行時に生成する名前付きスパンの一覧である スパンマップ を作成します。
主な方法 — コード分析:
Grep ツールと Read ツールを使用して、エージェントのコードベースから計測パターンを検索します。
Patterns to search for:
Python Netra decorators:
@workflow, @agent, @task, @span → span name from name= parameter
Netra.start_span("name", ...) → span name from first argument
Python tool definitions:
@tool def func_name(...) → span name = function name
class MyTool(BaseTool): name = "..." → span name from class attribute
TypeScript Netra decorators:
@Workflow(), @Agent(), @Task() → span name from decorator argument
netra.startSpan("name", ...) → span name from first argument
Framework-specific:
CrewAI Agent/Task definitions → span name from name field
LangGraph node definitions → span name from node name
Claude Agent SDK tool definitions → span name from tool name
見つかった各スパンについて、次の項目を記録します:
- name — トレースに表示されるスパン名
- type — workflow、agent、task、tool、または generation
- output description — 関数が返す内容(コードから推測)
- position in flow — 他のスパンと比べた呼び出し順序
代替方法 — サンプルトレース:
コード分析だけでは十分な結果が得られない場合(デコレーターが見つからない、スパン名が動的など):
- ユーザーに次のように尋ねます:「コード内で十分なスパン定義を見つけられませんでした。このエージェントは Netra で計測した状態で、少なくとも一度実行されていますか?」
- はいの場合、
netra_query_tracesを使用して最近のトレースを探し、次にnetra_get_trace_by_idを使用してそのスパンを取得します。 - 実際のトレースデータ(スパン名、親子関係、入力/出力)からスパンマップを作成します。
このフェーズの出力:
Agent Flow:
{root-span-name} ({type})
├── {child-span-1} ({type}) → {output description}
├── {child-span-2} ({type}) → {output description}
│ └── {nested-span} ({type}) → {output description}
└── {child-span-3} ({type}) → {output description}
ユーザーに提示し、スパン名が正確であることを確認します。
フェーズ 3:評価器を選択し、マッピングを設定する
フェーズ 1 の各テストケースについて、どの評価器を割り当て、変数をどのように接続するかを決定します。
ステップ 3a:利用可能な評価器を取得する
まず MCP ツールを試してください。利用できない場合は、埋め込みリファレンスを使用してください。
Try: netra_get_evaluator_library (all categories)
netra_list_evaluators (custom evaluators in the project)
Fallback: Read references/evaluator-library.md for the full catalog
ステップ 3b:評価器をテストケースに対応付ける
次のマッピングルールを適用してください。
| シナリオのカテゴリ | 推奨される評価器 |
|---|---|
| ハッピーパス — 正しい応答が期待される | Answer Relevance、Answer Correctness、または Semantic Similarity |
| ハッピーパス — 正しいアクションが期待される | Goal Accuracy、Tool Correctness |
| ネガティブ — 適切に拒否またはエラー処理されるべき | Answer Relevance、Conciseness |
| エッジケース — 境界動作 | Answer Correctness、Faithfulness |
| 安全性 — プロンプトインジェクション、ジェイルブレイク | Toxicity、Topic Adherence |
| パフォーマンス — レイテンシ/コストの上限 | Latency、Cost、Token Usage |
| RAG — 検索品質が重要 | Context Relevance、Context Precision、Faithfulness、Hallucination |
これらは出発点としての推奨です。シナリオの具体的な意味を常に考慮してください。評価器が適切なのは、その目的がテストケースで検証する内容と一致している場合に限られます。
ステップ 3c:設定可能な変数を特定する
一部の評価器では、会話のコンテキストやメタデータから自動解決できない、ユーザーが指定する設定が必要です。これらは評価器ライブラリで configurableVariables として定義されています。設定可能な変数が空のままだと、評価器は失敗するか、信頼性の低いスコアを生成します(例:「評価できません」となり、スコアが低下する)。
設定可能な変数がある評価器:
| 評価器 | 変数 | 型 | 指定する内容 |
|---|---|---|---|
| Guideline Adherence | assistant_instructions | string | AI エージェントに与えられたシステムプロンプトまたは指示の全文 — エージェントが実行すべきこと、振る舞い方、セキュリティルール、エスカレーションのガイドライン |
| Guideline Adherence | assistant_constraints | string | AI エージェントが遵守すべき制約 — 実行してはならないこと、境界、禁止されているアクション。metadata.assistant_constraints からも自動解決されますが、設定値が優先されます |
| Factual Accuracy(複数ターン) | reference_facts | json | エージェントが正しく伝えるべき事実 — 製品の詳細、ポリシー、価格、日付。metadata.reference_facts からも自動解決されますが、設定値が優先されます |
セッション評価器の変数解決の優先順位:
evaluatorConfigs[].configuredValues(最優先 — ユーザー指定)- セッションメタデータからの自動解決(例:
metadata.assistant_constraints、metadata.reference_facts) - 空文字列(フォールバック — 評価の失敗につながります)
assistant_instructions には自動解決の手段がありません。必ず evaluatorConfigs で指定する必要があります。
設定可能な変数がある評価器を推奨する場合:
- ユーザーに値の提供を求めてください
- 各評価器の「Configurable Variables」セクションに値を含めてください
- ユーザーがまだ値を提供できない場合は、計画内で
⚠️ NEEDS CONFIGURATIONと記載してください
ステップ 3d:変数のマッピングを設定する
テストケースに割り当てた各評価器について、必須変数をすべてデータソースに対応付けます。
response / actual_output 変数の決定ロジック:
IF the evaluator checks the final user-facing answer
→ source: taskOutput
→ expression: "taskOutput"
IF the evaluator checks a specific intermediate step
→ source: span output
→ find the span from Phase 2 whose output is relevant
→ expression: "spans[?name=='{spanName}'] | [0].output"
IF the evaluator checks tool usage
→ source: trace tools
→ expression: "trace.tools"
reference / expected 変数の決定ロジック:
IF ground truth is available in the dataset item
→ source: expectedOutput
→ expression: "expectedOutput"
IF reference comes from a specific span
→ expression: "spans[?name=='{spanName}'] | [0].output"
context(RAG 評価器)の決定ロジック:
→ source: retriever span output
→ expression: "spans[?name=='{retriever-span}'] | [0].output"
パフォーマンス評価器の決定ロジック:
actual_latency → expression: "trace.latency"
actual_cost → expression: "trace.cost"
actual_tokens → expression: "trace.tokens"
expected_* → source: literal value from the spec
マッピングを確信を持って決定できない場合は、計画内で ⚠️ NEEDS CONFIGURATION と記載し、その理由を説明してください。
フェーズ 3e:合格基準とモデルを設定する
- 仕様に別途記載がない限り、評価器のデフォルトの合格基準を使用してください。
- ユーザーがモデルの希望を指定した場合は、計画に示すすべての LLM 評価器に適用してください。
評価器の作成が必要になった場合に使用するモデル設定を、計画に表示してください。
プロバイダー/モデルの解決は、評価器の推奨時ではなく、作成時に行われます。
項目レベルのプロバイダー設定
複数ターンのデータセット項目では、シミュレーションがモデルを基盤とするタスク実装を通じて実行されるため、providerConfig が必要です。
{
"provider_id": "{providerConfigurationId}",
"model": "{model}"
}
netra_get_default_llm_configuration を使用して、組織のデフォルトのプロバイダー設定を取得します。
ルール:
- 複数ターンのデータセットでは、すべてのデータセット項目に
providerConfigを含める必要があります。 - 仕様でモデルが定義されていない場合は、組織のデフォルトのプロバイダー設定を使用します。
- シナリオで別のモデルが明示的に必要とされている場合は、項目の
providerConfigをそれに応じて設定し、デフォルトを上書きします。 - 単一ターンのデータセットでは、
providerConfigは任意であり、項目で評価器のデフォルトとは異なるモデルが必要な場合にのみ指定してください。
Phase 3f: 実行戦略を決定する
最終計画を生成する前に、仕様が単一ターン評価と複数ターンシミュレーションのどちらを記述しているかを判断します。
単一ターン評価
仕様で単一のリクエストとレスポンスのやり取りが記述されている場合:
- Turn Type =
singleに設定します - 実行には
Netra.evaluation.run_test_suite()を使用します
評価の実行でエージェントを直接呼び出してはいけません。
理由:
- トレース ID が自動的に記録されます
- 評価器がトレースとスパンの出力にアクセスできます
- スパンベースの変数マッピングが正しく機能します
- 評価結果が実行トレースに紐付いたままになります
実行例:
Netra.evaluation.run_test_suite(
dataset_id="dataset-123",
...
)
複数ターンシミュレーション
仕様で会話、ワークフロー、サポートのやり取り、トラブルシューティングのフロー、または複数ステップのやり取りが記述されている場合:
- Turn Type =
multiに設定します - Eval Type =
sessionに設定します。これはnetra_create_evaluatorに明示的に渡す必要があります - 言語に適したシミュレーション API を使用します:
- Python:
Netra.simulation.run_simulation() - TypeScript:
await Netra.simulation.runSimulation({...})
- Python:
シミュレーションの実行でエージェントを直接呼び出してはいけません。
ユーザーは、Netra Simulation と互換性のあるタスク実装を提供する必要があります。
Python の例:
class MyAgentTask(BaseTask):
def __init__(self, agent):
self.agent = agent
def run(
self,
message: str,
session_id: str | None = None,
files: list[ProcessedFile] | None = None,
setup_context: dict | None = None, # populated by before_all / before hooks
) -> TaskResult:
ctx = setup_context or {}
response = self.agent.chat(
message,
session_id=session_id,
files=files,
# Pass any setup data from hooks (e.g. auth token, customer ID)
auth_token=ctx.get("auth_token"),
)
return TaskResult(
message=response.text,
session_id=session_id or "default",
)
result = Netra.simulation.run_simulation(
name="Customer Support Simulation",
dataset_id="dataset-123",
task=MyAgentTask(my_agent),
context={"environment": "staging"},
max_concurrency=5,
hooks=hooks, # optional SimulationHooks from Phase 3g
)
TypeScript の例:
import { BaseTask } from "netra-sdk";
import type { ProcessedFile, TaskResult } from "netra-sdk";
class MyAgentTask extends BaseTask {
constructor(private agent: any) {
super();
}
async run(
message: string,
sessionId?: string | null,
files?: ProcessedFile[] | null,
setupContext?: Record<string, any> | null, // populated by beforeAll / before hooks
): Promise<TaskResult> {
const ctx = setupContext || {};
const response = await this.agent.chat(message, {
sessionId,
files,
authToken: ctx.authToken,
});
return {
message: response.text,
sessionId: sessionId || "default",
};
}
}
const result = await Netra.simulation.runSimulation({
name: "Customer Support Simulation",
datasetId: "dataset-123",
task: new MyAgentTask(myAgent),
context: { environment: "staging" },
maxConcurrency: 5,
hooks, // optional SimulationHooks from Phase 3g
});
これにより、次のことが保証されます:
- 会話の状態が維持されます
- セッション ID が追跡されます
- シミュレーショントレースが生成されます
- 評価器が会話履歴とスパンの出力にアクセスできます
- 相関するシナリオが、逐次的な順序に依存せず、安全に状態を共有できます
ターンタイプの判定ルール
仕様から実行モードを判断します:
| 仕様のパターン | ターンタイプ |
|---|---|
| 単一のリクエスト → 単一のレスポンス | single |
| 単発のツール実行 | single |
| QA 検証 | single |
| カスタマーサポートの会話 | multi |
| 営業の会話 | multi |
| トラブルシューティングのワークフロー | multi |
| 複数ステップのアシスタントとのやり取り | multi |
| ロールプレイシミュレーション | multi |
判断に迷う場合は、計画を生成する前にユーザーに確認してください。
Phase 3g: シナリオの相関関係を特定し、フックを提案する
このフェーズは複数ターンのデータセットにのみ適用してください。
データセット項目には、状態を共有する、または先行するシナリオが実行済みであることを前提とする、相関したシナリオが含まれていることがよくあります。Netra はシナリオを並列で実行するため(同時実行数は設定可能)、逐次的な順序は保証されません。フックを使うと、項目を統合したり実行順序に依存したりせずに、シナリオの前後にセットアップやクリーンアップのコードを実行できます。
ステップ 1: シナリオ間の関連性を検出する
計画内のすべてのシナリオを読み、次のカテゴリに該当する関連性に印を付けます。
| パターン | 説明 | 必要なフック |
|---|---|---|
| 共有リソース | シナリオ B がシナリオ A で作成されたエンティティ(従業員、アカウント、レコード)を操作する | エンティティを一度だけ作成するための before_all |
| 認証状態 | シナリオでログイン済みセッションまたはトークンが必要 | 各シナリオで新しいトークンを取得するための before_each、シナリオ固有の認証情報を使用するための before |
| データのシード | すべてのシナリオで同じシードデータ(カタログ、設定、参照レコード)が必要 | データを設定するための before_all |
| 外部サービスの状態 | シナリオでサードパーティサービスを特定の状態にする必要がある | 設定とリセットを行うための before_all / after_all |
| アイテムごとに共通するセットアップ | すべてのシナリオで同じ分離手順(新しいトークン、クリーンなカート)が必要 | before_each / after_each |
| 分離が必要 | 一部のシナリオだけでクリーンな状態が必要 | before[dataset_item_id] |
| クリーンアップが必要 | シナリオ中に作成したリソースを後で削除する必要がある | after / after_each または after_all |
関連性が見つからない場合、フックは不要です。その旨を計画内に明記してください。
ステップ 2:フック戦略を設計する
検出した相関パターンごとに、使用するフックレベルを決定します。
| 状況 | 推奨されるフック | 理由 |
|---|---|---|
| すべてのシナリオで使用するリソースを作成する | before_all | 1 回だけ実行され、シナリオごとに作成するより低コストです |
| すべてのシナリオで同じセットアップを行う(新しいトークン、空のカート) | before_each | 各項目に同じ before を重複して登録せずに済みます |
| 特定のシナリオのみセットアップする | before[dataset_item_id] | 項目固有で、before_all と before_each から統合されたコンテキストを受け取ります |
| すべてのシナリオの後処理を行う | after_each | すべての項目を登録せずに共通のクリーンアップを行えます |
| 特定のシナリオのリソースを後処理する | after[dataset_item_id] | after_each より前に実行され、項目固有のクリーンアップを行います |
before_all で作成した共有リソースを削除する | after_all | すべてのシナリオの終了後に 1 回実行されます |
ユーザーに伝える失敗時の動作:
before_allが失敗した場合 → 実行全体が失敗としてマークされ、シナリオは実行されません- あるシナリオの
before_eachまたはbefore[dataset_item_id]が失敗した場合 → そのシナリオはprescript_failedとしてマークされ、他のシナリオは実行を続けます - 成功したシナリオで
after/after_eachが失敗した場合 → そのシナリオはpostscript_failedとしてマークされます(どちらも常に実行を試み、エラーは統合されます)。シナリオがすでに失敗またはprescript_failedになっている場合は、既存のステータスが維持されます after_allが失敗した場合 → 正常に完了したシナリオはpostscript_failedとしてマークされ、すでに失敗またはprescript_failedになっている項目のステータスは維持されます- before フックが失敗しても、
after/after_eachは実行され、正常に構築できた最も進んだsetup_contextを受け取ります(例:項目のbeforeが失敗した場合はbefore_all+before_each)
prescript_failed の終端状態での動作:
prescript_failedは終端ステータスです。この状態に達した項目は完了しています。SDK のポーリングループはprescript_failedの項目を終端として正しくカウントするため、無期限に待機することはありません。prescript_failedの項目では、evalStatusも直ちにNOT_AVAILABLEに設定されます。エージェントが実行されなかった項目について、評価がキューに追加されることはありません。- すべての項目が
failedまたはprescript_failedで終了し(正常に完了した項目がない場合)、実行全体の評価ステータスはNOT_AVAILABLEになります。 prescript_failedの項目のステータスは安定しており、その後の一括失敗処理(実行のタイムアウト時など)によって上書きされることはありません。
postscript_failed の終端状態での動作:
postscript_failedは終端ステータスです。会話は完了しましたが、後処理に失敗しました。- 評価は抑制されません。完了した会話に対する評価は有効なままです。
postscript_failedは、「すべて失敗 → NOT_AVAILABLE」の集計対象から除外されます(prescript_failedとは異なります)。- ステータスは安定しており、その後の一括失敗処理によって上書きされることはありません。
フックのマッピングに使用するデータセット項目 ID の確認方法:
計画を作成する際、特定の ID を持つデータセット項目を作成します。これらの ID は netra_create_dataset_item MCP 呼び出しから返されます。すべての項目を作成した後、ユーザーに次の操作を案内してください。
- 返されたデータセット項目 ID の値を、対応するシナリオに割り当てる
- これらの ID を
SimulationHooks.before/SimulationHooks.afterのキーとして使用する- Python のキー:
dataset_item_id文字列 - TypeScript のキー:同じ ID 文字列(SDK の型フィールド名は
datasetItemId)
- Python のキー:
Python のマッピング例:
# Step 1: Create dataset items and capture their IDs
refund_item = netra_create_dataset_item(...) # returns {"id": "item-abc-123", ...}
balance_item = netra_create_dataset_item(...) # returns {"id": "item-xyz-789", ...}
# Step 2: Map hooks using the actual dataset_item_id values
hooks = SimulationHooks(
before_all=before_all,
before_each=before_each, # optional: runs for every scenario
before={
"item-abc-123": setup_refund_scenario, # Use actual ID from step 1
"item-xyz-789": setup_balance_scenario,
},
after={
"item-abc-123": teardown_refund_scenario,
"item-xyz-789": teardown_balance_scenario,
},
after_each=after_each, # optional: runs for every scenario
after_all=after_all,
)
TypeScript のマッピング例:
// Step 1: Create dataset items and capture their IDs
const refundItem = /* netra_create_dataset_item(...) → { id: "item-abc-123", ... } */;
const balanceItem = /* netra_create_dataset_item(...) → { id: "item-xyz-789", ... } */;
// Step 2: Map hooks using the actual datasetItemId values
const hooks: SimulationHooks = {
beforeAll,
beforeEach, // optional: runs for every scenario
before: {
"item-abc-123": setupRefundScenario,
"item-xyz-789": setupBalanceScenario,
},
after: {
"item-abc-123": teardownRefundScenario,
"item-xyz-789": teardownBalanceScenario,
},
afterEach, // optional: runs for every scenario
afterAll,
};
ステップ 3:フックのスキャフォールドコードを生成する
特定した各フックについて、検出されたプロジェクト言語(フェーズ 0.5)で、以下を含むスキャフォールドを生成します。
- 必要なフックレベルに一致する関数シグネチャ
- フックの処理内容を簡潔に説明する説明文。すべてのフック関数で
.descriptionを使って設定します(200 文字以内)。これは必ず設定してください。設定しないと、Netra UI でdescription: nullになります。- Python:
fn.description = "..." - TypeScript:
(fn as any).description = "..."
- Python:
- 実際の実装用のプレースホルダー(
# TODO/// TODOとして記載) SimulationHooksの設定hooksを渡すように更新したシミュレーション実行呼び出し
TypeScript プロジェクトに Python のスキャフォールドを生成しないでください。また、その逆も同様です。
.description を設定せずにフックを生成しないでください。
Python フック関数のシグネチャ:
# before_all: no args → returns dict (shared context) or None
def before_all() -> dict | None:
...
before_all.description = "One-line description for the Netra UI."
# before_each: receives shared_context → returns dict merged into setup_context (runs for every item)
def before_each(shared_context: dict | None) -> dict | None:
...
before_each.description = "One-line description for the Netra UI."
# before: dict keyed by dataset_item_id; each receives merged context (before_all + before_each)
def setup_scenario_a(shared_context: dict | None) -> dict | None:
...
setup_scenario_a.description = "One-line description for the Netra UI."
def setup_scenario_b(shared_context: dict | None) -> dict | None:
...
setup_scenario_b.description = "One-line description for the Netra UI."
# after: dict keyed by dataset_item_id; each receives result + setup_context
def teardown_scenario_a(result: dict, setup_context: dict | None) -> None:
...
teardown_scenario_a.description = "One-line description for the Netra UI."
# after_each: receives result + setup_context (runs for every item, after item-specific after)
def after_each(result: dict, setup_context: dict | None) -> None:
...
after_each.description = "One-line description for the Netra UI."
# after_all: receives aggregated results dict and shared_context (before_all only) → returns None
def after_all(results: dict, shared_context: dict | None) -> None:
...
after_all.description = "One-line description for the Netra UI."
ユーザーのセットアップコードが非同期の場合、すべての Python フックを非同期(async def)にできます。
必須: 上記のすべての Python フックで .description を設定する必要があります。設定しない場合、lifecycleHooks で description: null が送信されます。
TypeScript フック関数のシグネチャ:
// beforeAll: no args → returns shared context object or null/void
async function beforeAll(): Promise<Record<string, any> | null | void> {
...
}
(beforeAll as any).description = "One-line description for the Netra UI.";
// beforeEach: receives sharedContext → returns dict merged into setupContext (every item)
async function beforeEach(
sharedContext: Record<string, any> | null,
): Promise<Record<string, any> | null | void> {
...
}
(beforeEach as any).description = "One-line description for the Netra UI.";
// before: Record keyed by datasetItemId; each receives merged context (beforeAll + beforeEach)
async function setupScenarioA(
sharedContext: Record<string, any> | null,
): Promise<Record<string, any> | null | void> {
...
}
(setupScenarioA as any).description = "One-line description for the Netra UI.";
// after: Record keyed by datasetItemId; each receives result + setupContext
async function teardownScenarioA(
result: Record<string, any>,
setupContext: Record<string, any> | null,
): Promise<void> {
...
}
(teardownScenarioA as any).description = "One-line description for the Netra UI.";
// afterEach: receives result + setupContext (every item, after item-specific after)
async function afterEach(
result: Record<string, any>,
setupContext: Record<string, any> | null,
): Promise<void> {
...
}
(afterEach as any).description = "One-line description for the Netra UI.";
// afterAll: receives aggregated results and sharedContext (beforeAll only)
async function afterAll(
results: Record<string, any>,
sharedContext: Record<string, any> | null,
): Promise<void> {
...
}
(afterAll as any).description = "One-line description for the Netra UI.";
必須: 上記のすべての TypeScript フックで .description を設定する必要があります。設定しない場合、lifecycleHooks で description: null が送信されます。
コンテキストの受け渡しパターン:
Python:
before_all() → shared_context (dict | None)
before_each(shared_context) → merged into setup_context
hooks.before[dataset_item_id](merged_context) → merged into setup_context
BaseTask.run(..., setup_context=...) ← receives merged context
hooks.after[dataset_item_id](result, setup_context) ← same merged context for cleanup
after_each(result, setup_context) ← same merged context
after_all(results, shared_context) ← run-level shared_context only
TypeScript:
beforeAll() → sharedContext (Record | null)
beforeEach(sharedContext) → merged into setupContext
hooks.before[datasetItemId](mergedContext) → merged into setupContext
BaseTask.run(..., setupContext) ← receives merged context
hooks.after[datasetItemId](result, setupContext) ← same merged context for cleanup
afterEach(result, setupContext) ← same merged context
afterAll(results, sharedContext) ← run-level sharedContext only
重要: before フックと after フックは単一の関数ではなく、データセット項目 ID をキーとする辞書です。特定のセットアップやティアダウンが必要な各シナリオには、それぞれ専用の関数を用意し、安定したデータセット項目 ID をキーとして SimulationHooks に登録します。同じセットアップやティアダウンをすべてのシナリオに適用する場合は、before_each / after_each を優先してください。
セットアップコンテキストは、before_all / beforeAll と before_each / beforeEach のマージ、および項目の before フックが返すオブジェクトで構成されます。このコンテキストは BaseTask.run()、項目の after フック、after_each / afterEach に渡されます。before フックが途中で失敗した場合でも、ティアダウンには、正常に構築できた範囲で最も進んだセットアップコンテキストが渡されます。after_all / afterAll に渡されるのは、引き続き実行レベルの共有コンテキストのみです。
Python のスキャフォールド全体の例(従業員 + シナリオごとの認証パターン):
データセットに次の 2 つの項目があるとします。
dataset_item_id = "item-refund-request"→ 共有認証と、返金に固有のアカウントが必要dataset_item_id = "item-balance-inquiry"→ 共有認証のみが必要(before_each/after_eachで対応)
from netra.simulation import BaseTask, SimulationHooks, TaskResult
# ---- Hooks ----
def before_all():
# TODO: replace with your actual setup code
employee = your_api.create_employee(name="Test User", role="admin")
return {"employee_id": employee.id}
before_all.description = (
"Create the test employee and assign the admin role before any scenario runs."
)
def before_each(shared_context: dict | None):
employee_id = (shared_context or {}).get("employee_id")
# TODO: replace with your actual login logic
token = your_api.login(employee_id=employee_id)
return {"auth_token": token}
before_each.description = "Obtain a fresh auth token before every scenario."
def setup_refund_scenario(shared_context: dict | None):
# shared_context already includes employee_id + auth_token from before_all + before_each
refund_account = your_api.create_refund_account()
return {"refund_account_id": refund_account.id}
setup_refund_scenario.description = (
"Create a refund account for the refund scenario only."
)
def teardown_refund_scenario(result: dict, setup_context: dict | None):
refund_account_id = (setup_context or {}).get("refund_account_id")
try:
your_api.delete_refund_account(refund_account_id)
except Exception:
pass # catch locally so teardown errors do not mark the item postscript_failed
teardown_refund_scenario.description = (
"Delete the refund account after the refund scenario."
)
def after_each(result: dict, setup_context: dict | None):
auth_token = (setup_context or {}).get("auth_token") # from furthest built setup_context
try:
your_api.logout(token=auth_token)
except Exception:
pass
after_each.description = "Log out after every scenario regardless of outcome."
def after_all(results: dict, shared_context: dict | None):
employee_id = (shared_context or {}).get("employee_id")
# TODO: replace with your actual teardown code
your_api.delete_employee(employee_id=employee_id)
after_all.description = "Delete the test employee once all scenarios have finished."
# Map hooks to specific dataset item IDs
hooks = SimulationHooks(
before_all=before_all,
before_each=before_each,
before={
"item-refund-request": setup_refund_scenario,
},
after={
"item-refund-request": teardown_refund_scenario,
},
after_each=after_each,
after_all=after_all,
)
# ---- Task ----
class MyAgentTask(BaseTask):
def run(
self,
message: str,
session_id: str | None = None,
files: list | None = None,
setup_context: dict | None = None,
) -> TaskResult:
ctx = setup_context or {}
response = my_agent.chat(
message,
session_id=session_id,
auth_token=ctx.get("auth_token"),
)
return TaskResult(
message=response.text,
session_id=session_id or response.session_id or "default",
)
# ---- Run ----
result = Netra.simulation.run_simulation(
name="My Simulation",
dataset_id="dataset-123",
task=MyAgentTask(),
hooks=hooks,
max_concurrency=3,
)
TypeScript のスキャフォールド全体の例(同じパターン):
import { BaseTask, Netra } from "netra-sdk";
import type { ProcessedFile, SimulationHooks, TaskResult } from "netra-sdk";
// ---- Hooks ----
async function beforeAll(): Promise<Record<string, any> | null> {
// TODO: replace with your actual setup code
const employee = await yourApi.createEmployee({ name: "Test User", role: "admin" });
return { employeeId: employee.id };
}
(beforeAll as any).description =
"Create the test employee and assign the admin role before any scenario runs.";
async function beforeEach(
sharedContext: Record<string, any> | null,
): Promise<Record<string, any> | null> {
const employeeId = sharedContext?.employeeId;
// TODO: replace with your actual login logic
const token = await yourApi.login({ employeeId });
return { authToken: token };
}
(beforeEach as any).description =
"Obtain a fresh auth token before every scenario.";
async function setupRefundScenario(
sharedContext: Record<string, any> | null,
): Promise<Record<string, any> | null> {
// sharedContext already includes employeeId + authToken from beforeAll + beforeEach
const refundAccount = await yourApi.createRefundAccount();
return { refundAccountId: refundAccount.id };
}
(setupRefundScenario as any).description =
"Create a refund account for the refund scenario only.";
async function teardownRefundScenario(
result: Record<string, any>,
setupContext: Record<string, any> | null,
): Promise<void> {
try {
await yourApi.deleteRefundAccount(setupContext?.refundAccountId);
} catch {
// Catch locally so teardown errors do not mark the item postscript_failed
}
}
(teardownRefundScenario as any).description =
"Delete the refund account after the refund scenario.";
async function afterEach(
result: Record<string, any>,
setupContext: Record<string, any> | null,
): Promise<void> {
try {
await yourApi.logout({ token: setupContext?.authToken });
} catch {
// ignore
}
}
(afterEach as any).description =
"Log out after every scenario regardless of outcome.";
async function afterAll(
results: Record<string, any>,
sharedContext: Record<string, any> | null,
): Promise<void> {
// TODO: replace with your actual teardown code
await yourApi.deleteEmployee(sharedContext?.employeeId);
}
(afterAll as any).description =
"Delete the test employee once all scenarios have finished.";
const hooks: SimulationHooks = {
beforeAll,
beforeEach,
before: {
"item-refund-request": setupRefundScenario,
},
after: {
"item-refund-request": teardownRefundScenario,
},
afterEach,
afterAll,
};
// ---- Task ----
class MyAgentTask extends BaseTask {
async run(
message: string,
sessionId?: string | null,
files?: ProcessedFile[] | null,
setupContext?: Record<string, any> | null,
): Promise<TaskResult> {
const ctx = setupContext || {};
const response = await myAgent.chat(message, {
sessionId,
authToken: ctx.authToken,
});
return {
message: response.text,
sessionId: sessionId || response.sessionId || "default",
};
}
}
// ---- Run ----
const result = await Netra.simulation.runSimulation({
name: "My Simulation",
datasetId: "dataset-123",
task: new MyAgentTask(),
hooks,
maxConcurrency: 3,
});
ステップ 4:計画でフックの必要性を明示する
フックが必要な場合は、計画の Dataset Configuration テーブルの下に「Hooks」セクションを追加します。フックが不要な場合は、次のように明記します。
**Hooks:** Not required — scenarios are independent.
フックが必要な場合は、各フックの種類を 1 行で説明し、どのシナリオで項目固有のフックが必要かを指定します。計画では、言語に適した名前を使います(Python では before_all、TypeScript では beforeAll)。
**Hooks:**
- `before_all` / `beforeAll`: Create shared test employee and assign admin role
- `before_each` / `beforeEach`: Obtain a fresh auth token before every scenario
- `before`: Per-scenario hooks keyed by dataset item ID:
- Refund Request scenario: Create refund account
- `after`: Per-scenario hooks keyed by dataset item ID:
- Refund Request scenario: Delete refund account
- `after_each` / `afterEach`: Log out after every scenario
- `after_all` / `afterAll`: Delete the test employee
シナリオの Netra UI の Conversation タブでは、次のように表示されます。
before_all/beforeAll、before_each/beforeEach、after_each/afterEach、after_all/afterAllは、その実行内のすべてのシナリオに表示されます(実行レベルのメタデータ)。before/afterは、そのシナリオのデータセット項目 ID がフック辞書に登録されている場合にのみ表示されます(項目レベルのメタデータ)。
重要: 実際のデータセット項目 ID の値は、MCP 経由でデータセット項目を作成した後にのみ取得できます。生成するスキャフォールドコードでは、プレースホルダー ID(例:"item-refund-request"、"item-balance-inquiry")を使い、これらを netra_create_dataset_item から返された実際の ID に置き換えるようユーザーに指示してください。
次に、生成したスキャフォールドコードを、計画の概要の下に折りたたみ可能なコードブロックとして含めます。
フェーズ 4:プランを生成する
以下の正確な形式でプランを作成します。これはユーザーが確認する主要な出力です。
PLAN FORMAT — 以下をそのまま出力し、生成された値を入力してください:
# NDD Evaluation Plan
## Dataset Configuration
| Field | Value |
|---|---|
| **Name** | {generated-dataset-name} |
| **Description** | {generated-description} |
| **Turn Type** | {single | multi} |
| **Execution Method** | {Netra.evaluation.run_test_suite / Netra.evaluation.runTestSuite | Netra.simulation.run_simulation / Netra.simulation.runSimulation} |
| **Total Items** | {count} |
### Execution Strategy
Single-turn datasets:
- Python: `Netra.evaluation.run_test_suite()`
- TypeScript: `await Netra.evaluation.runTestSuite({...})`
Multi-turn datasets:
- Python: `Netra.simulation.run_simulation()`
- TypeScript: `await Netra.simulation.runSimulation({...})`
If Turn Type = `multi`, include:
```text
Required Task Implementation:
⚠️ User must provide a BaseTask implementation compatible with Netra Simulation.
Turn Type = multi で、フェーズ 3g でフックが特定された場合は、Execution Strategy の直後に Hooks セクションを含めてください。
### Hooks (Pre/Post Scripts)
{Either "Not required — scenarios are independent." OR a list like:}
- `before_all` / `beforeAll`: {one-line description}
- `before_each` / `beforeEach`: {one-line description, if used}
- `before`: {one-line description, if used}
- `after`: {one-line description, if used}
- `after_each` / `afterEach`: {one-line description, if used}
- `after_all` / `afterAll`: {one-line description}
**Generated scaffold code** (requires user to fill in TODO sections; language = Phase 0.5 detection):
{paste the full scaffold code block from Phase 3g here — Python OR TypeScript, not both}
項目
項目 1:{シナリオ名}
カテゴリ: {ハッピーパス | ネガティブ | エッジケース | 安全性 | パフォーマンス}
シングルターン項目の場合:
| フィールド | 値 |
|---|---|
| 入力 | {テスト入力/ユーザーメッセージ} |
| 期待される出力 | {期待される正しい応答または動作} |
| プロバイダー設定 | {なし — 評価者のデフォルトを使用 | provider_id: {id}, model: {model}} |
マルチターン項目の場合:
| フィールド | 値 |
|---|---|
| 入力 | {}(空のオブジェクト — シミュレーションエンジンがメッセージを生成します) |
| 期待される出力 | null |
| シナリオ名 | {シナリオの簡潔な表示名} |
| シナリオ | {評価者が成功を判定するために使用する実際の目標/シナリオの説明} |
| ペルソナ | {シミュレートされたユーザーの人物像} |
| 最大ターン数 | {数値} |
| 振る舞いの指示 | {シミュレートされたユーザー向けの手順ごとのスクリプト} |
| プロバイダー設定 | provider_id: {id}, model: {model} |
評価者:
評価者:{評価者名}
| 設定 | 値 |
|---|---|
| Type | {llm-as-judge / code / json / ...} |
| Model | {org-default / user-specified} |
| 合格基準 | {演算子} {しきい値} |
| 変数 | ソース | 式 |
|---|---|---|
| {変数名} | {Agent Response / Span: {スパン名} / Dataset: expectedOutput / Trace: {メトリクス} / Value} | {jmespath-expression} |
| {変数名} | ... | ... |
評価者に設定可能な変数がある場合は、以下を含めてください。
設定可能な変数(ユーザーが指定):
| 変数 | 値 |
|---|---|
| {変数名} | {ユーザー指定の値、または ⚠️ NEEDS CONFIGURATION} |
ガイドライン遵守の例:
設定可能な変数(ユーザーが指定):
| 変数 | 値 |
|---|---|
assistant_instructions | あなたは親切なカスタマーサポート担当者です。常にユーザーに礼儀正しく挨拶してください... |
assistant_constraints | クレジットカード情報を尋ねてはなりません。請求に関する問題は人間の担当者にエスカレーションする必要があります... |
事実の正確性(マルチターン)の例:
設定可能な変数(ユーザーが指定):
| 変数 | 値 |
|---|---|
reference_facts | {"return_policy": "14 days", "shipping_time": "3-5 business days", ...} |
評価者:{評価者名-2}
...
項目 2:{シナリオ名}
...
概要
| カテゴリ | 件数 |
|---|---|
| ハッピーパス | {n} |
| ネガティブ | {n} |
| エッジケース | {n} |
| 安全性 | {n} |
| パフォーマンス | {n} |
| 合計 | {n} |
| 使用した評価者 | 件数 |
|---|---|
| {評価者名} | {n} 項目 |
| ... | ... |
---
After outputting the plan, ask the user:
> Review the plan above. You can ask me to:
> - Add, remove, or modify any item
> - Change evaluators for a specific item
> - Adjust variable mappings (where input/output/reference comes from)
> - Change pass criteria or model
> - Edit the dataset name or description
> - Add, remove, or change hooks (before_all, before_each, before, after, after_each, after_all)
>
> When you're satisfied, say **"proceed"** and I'll create everything in Netra.
### Phase 5: Handle User Edits
When the user requests changes:
- Apply the edit to the plan.
- Re-output only the changed section (not the full plan) so the user can verify.
- Ask if there are more changes or if they want to proceed.
### Phase 6: Create in Netra
When the user says "proceed", "create", "go ahead", or similar:
Execute these MCP calls in order:
-
netra_create_dataset → name → description → turnType: {single | multi} → 応答から datasetId を取得する
-
My Evaluators にまだ存在しないライブラリ評価者を追加する場合:
a. 組織のデフォルト LLM 設定を解決します。
netra_get_default_llm_configuration
期待される応答: { "provider": "openai", "model": "gpt-5", "providerConfigurationId": "provider-config-id" }
null が返された場合は、作成をブロックします。 ⚠️ 評価者の作成がブロックされました。デフォルトのプロバイダーが設定されていません。 プロバイダーやモデルの値を推測してはいけません。 b. netra_create_evaluator → name → libraryEvaluatorId → type:ライブラリエバリュエーターの type と完全に一致させる必要があります。 ライブラリエバリュエーターの
typeフィールド(例:"llm-as-judge"、"tool_accuracy"、 "regex"、"code")を確認し、プロジェクトエバリュエーターの作成時にも同じ type を指定します。 ライブラリエバリュエーターがtool_accuracy(ルールベース)の場合は、tool_accuracyとして作成します。 ライブラリエバリュエーターがllm-as-judgeの場合は、llm-as-judgeとして作成します。libraryEvaluatorIdから API が type を引き継ぐことを前提にしないでください。API は type を 常にllm-as-judgeにデフォルト設定する場合があり、その結果、ルールベースのエバリュエーターが エンジンによって LLM エバリュエーターとして誤って扱われ、「Missing required config: model, provider_id, or prompt」 というエラーで失敗することがあります。 type の不一致が疑われる場合は、明示的にtypeを指定したカスタムエバリュエーターとして作成し、libraryEvaluatorIdは省略してください。 → turnType: "single" | "multi" → evalType:明示的に設定する必要があります - シングルターンのエバリュエーターの場合:evalType: "turn" を指定します - マルチターンのエバリュエーターの場合:evalType: "session" を指定します evalType を省略しないでください。API は turnType から evalType を自動推測しません。 マルチターンのエバリュエーターで evalType を省略すると、デフォルトの "turn" が設定されます。 その場合、エバリュエーターはセッションごとではなくメッセージごとに実行されるため、 シミュレーション実行の評価スコアが「利用不可」と表示されます。 → config:エバリュエーターの type と一致させる必要があります。 - LLM ベースのエバリュエーター(llm-as-judge)の場合:手順 2a で解決したデフォルト値を 必ず明示的に指定します。 { "model": "{model}", "provider_id": "{providerConfigurationId}" } config を省略したり、自動解決に頼ったりしないでください。評価エンジンが LLM エバリュエーターを 実行するには、config 内にprovider_id(スネークケース)が必要です。 config を省略したり、フィールド名を間違えたりすると、実行時にエバリュエーターが 「Missing required config: model, provider_id, or prompt」というエラーで失敗します。 - ルールベースのエバリュエーター(tool_accuracy)の場合:エバリュエーター固有の config(例:{"matchType": "partial"})を指定します。model や provider_id は含めないでください。 - その他のコード/正規表現エバリュエーターの場合:ライブラリエバリュエーターで定義されている 固有の config を指定します。 → 応答から evaluatorId を取得しますc. 作成したエバリュエーターの config を検証します:
作成後、応答の
configオブジェクトを確認します。provider_id(スネークケース)が 含まれていることを確認してください。応答にprovider_idがなく、providerId(キャメルケース) しか表示されない場合、評価時にエバリュエーターは失敗します。有効な応答 config: { "model": "gpt-4o", "provider_id": "dcffa74c-..." }
検証に失敗した場合は、明示的な config を指定してエバリュエーターを再作成します。
-
各項目について: netra_create_dataset_item → datasetId、input、expectedOutput → metadata(マルチターンの場合:scenarioName、persona、max_turns、behaviour_instructions) → providerConfig 複数ターンのデータセットの場合:
- input:空のオブジェクト
{}シミュレーションエンジンは、inputフィールドではなくmetadata.behaviour_instructionsから最初のユーザーメッセージを生成します。inputに文字列を設定すると、UI はシナリオ識別子をmetadata.scenarioNameから読み取り、文字列のinputがこれに干渉するため、 Netra ダッシュボードでシナリオ名が空欄になります。 - expectedOutput:
nullを設定するか、省略します シミュレーションは単一の期待される出力と照合するのではなく、 セッションレベルの評価器を通じて会話全体を評価します。 - metadata:必須 — シミュレーション項目の主要な設定です
{
"scenarioName": "{scenarioName}",
"scenario": "{scenario}",
"persona": "...",
"max_turns": ...,
"behaviour_instructions": "..."
}
scenarioName:Netra ダッシュボードに表示されるシナリオ名です。 テストケースを示す、簡潔でわかりやすいラベルです(例:「反復的なプロジェクトの改善」)。scenario:シミュレーションで達成する必要がある、実際のシナリオの説明/目標です。 これは詳細な目標のテキストであり、セッションレベルの評価器(Goal Fulfillment、 Conversation Completeness)がエージェントの成功を判定する際に使用します。単なるラベルではなく、 会話で達成すべきことを説明します。 例:「会話を通じて要件を洗練し、スマート植物給水システムの完全なエンジニアリング仕様を入手する。」persona:シミュレーション対象のユーザーがどのような人物を演じるかです。max_turns:シミュレーションが停止するまでの最大会話ターン数です。behaviour_instructions:シミュレーション対象のユーザー向けの段階的なスクリプトです。 シミュレーションエンジンは、最初のメッセージを含むすべてのユーザーメッセージの生成に これを使用します。各ターンでシミュレーション対象のユーザーが何を言うか、 または何を尋ねるかを具体的に記述してください。
- 必ず providerConfig を含めます。
- プランでモデルが指定されていない場合は、netra_get_default_llm_configuration から組織のデフォルトのプロバイダー設定を使用します。
- 例:
{ "provider_id": "{providerConfigurationId}", "model": "{model}" } - input:空のオブジェクト
単一ターンのデータセットの場合: - input:テストメッセージを含む文字列 - expectedOutput:エージェントの期待される応答を含む文字列 - providerConfig は省略可能です。 - 評価器のデフォルトとは異なるモデルが項目に必要な場合にのみ含めます。 → evaluators(省略可能):variableMapping を含む項目レベルの評価器のオーバーライド
-
評価器をデータセットにマッピングします: netra_map_evaluator_to_dataset → datasetId、evaluatorId、variableMapping
単一ターンの評価器の場合:variableMapping 全体(user_query、response など)を渡します 複数ターン(セッション)評価器の場合:空のオブジェクト
variableMapping: {}を渡します セッション評価器は、会話のコンテキストから変数を自動的に解決します。 ただし、variableMappingパラメーターには引き続き{}を指定する必要があります — これを完全に省略すると、「Cannot convert undefined or null to object」エラーが発生します。 -
設定可能な変数が必要な評価器の変数を設定します:
マッピングした評価器に設定可能な変数がある場合(Guideline Adherence、複数ターンの Factual Accuracy)、 データセット項目のメタデータに
evaluatorConfigsを含めます。evaluatorConfigsはオブジェクトの配列で、各オブジェクトには次の項目があります:id:評価器 ID(手順 2 で作成したプロジェクト評価器 ID)configuredValues:変数名をユーザー指定の値に対応付けるオブジェクト
netra_create_dataset_itemまたはnetra_update_dataset_itemの呼び出し時に、metadataフィールドにevaluatorConfigsを含めます。Guideline Adherence の例:
{ "metadata": { "scenarioName": "Customer Support", "scenario": "...", "persona": "...", "max_turns": 5, "behaviour_instructions": "...", "evaluatorConfigs": [ { "id": "{guideline-adherence-evaluator-id}", "configuredValues": { "assistant_instructions": "You are a helpful customer support agent. Always greet the user professionally, stay on topic, follow security rules...", "assistant_constraints": "Must NOT ask for credit card details. Must escalate billing issues to human agents..." } } ] } }Factual Accuracy(複数ターン)の例:
{ "metadata": { "evaluatorConfigs": [ { "id": "{factual-accuracy-evaluator-id}", "configuredValues": { "reference_facts": "{\"return_policy\": \"14 days\", \"shipping_time\": \"3-5 business days\"}" } } ] } }同じ
evaluatorConfigs配列内で複数の評価器を設定できます:{ "metadata": { "evaluatorConfigs": [ { "id": "{guideline-adherence-evaluator-id}", "configuredValues": { "assistant_instructions": "...", "assistant_constraints": "..." } }, { "id": "{factual-accuracy-evaluator-id}", "configuredValues": { "reference_facts": "..." } } ] } }重大:設定可能な変数を必要とする評価器に対して
evaluatorConfigsが設定されていない場合、評価器には空の値が渡され、信頼性の低いスコアが生成されます。Guideline Adherence 評価器は AWARENESS および CONSISTENCY の基準について「評価できません」と報告し、Factual Accuracy 評価器は照合する参照事実がないため、デフォルトで合格と判定します。
If MCP tools for creation are not yet available, output a message:
> The dataset creation MCP tools are not registered yet. Here's what would be created:
> - Dataset: {name} with {n} items
> - {n} evaluator mappings
>
> You can create this manually in the Netra dashboard, or wait for the MCP tools to be available.
After successful creation, output:
✅ データセットを作成しました:{name} ({datasetId}) アイテム:{count} 件を作成しました 評価器のマッピング:{count} 件を設定しました
Netra で表示:https://app.getnetra.ai/datasets/{datasetId}
次の手順:netra run {dataset-name} を実行してテストスイートを実行します。
## Important Rules
1. **Never skip the preview.** Always show the full plan before creating anything.
2. **Never guess span names.** If code analysis doesn't find clear span definitions, use the fallback method or ask the user.
3. **Mark uncertainty explicitly.** Use `⚠️ NEEDS CONFIGURATION` for any mapping you're not confident about.
4. **Respect the spec.** If the QA spec defines specific pass thresholds, use those instead of defaults.
5. **Include the reasoning.** When recommending an evaluator, briefly explain why it fits the test case.
6. **Don't over-evaluate.** Not every item needs 5 evaluators. Match evaluator count to scenario complexity — simple happy paths may need 1-2, complex edge cases may need 3-4.
7. **Prefer span output over taskOutput** when the evaluator checks an intermediate step. Use `taskOutput` only when evaluating the final user-facing response.
8. Never execute the agent directly for evaluation runs.
- Single-turn evaluations must use `Netra.evaluation.run_test_suite()` (Python) or `await Netra.evaluation.runTestSuite({...})` (TypeScript).
- Multi-turn simulations must use `Netra.simulation.run_simulation()` (Python) or `await Netra.simulation.runSimulation({...})` (TypeScript).
9. Trace IDs are required for evaluator execution.
Any execution strategy that bypasses Netra Evaluation or Simulation APIs is invalid.
10. Multi-turn simulations require a task implementation that extends `BaseTask`.
The generated plan must explicitly mention this requirement.
11. When generating execution examples, use the official Netra SDK execution APIs for the detected language (Phase 0.5).
Do not generate examples that directly invoke the agent for evaluation execution.
Do not mix Python and TypeScript conventions in the same scaffold.
12. Variable mappings that reference spans are only valid when execution occurs through Netra Evaluation or Simulation APIs that generate trace data.
13. For multi-turn simulations, include a task implementation example using:
- `BaseTask`
- `TaskResult`
- `session_id` (Python) / `sessionId` (TypeScript)
- `setup_context` (Python) / `setupContext` (TypeScript) when hooks are used
- `Netra.simulation.run_simulation()` (Python) / `await Netra.simulation.runSimulation({...})` (TypeScript)
14. Resolve the organization's default provider/model only when creating a project evaluator from a library evaluator.
Once resolved, pass the config explicitly to `netra_create_evaluator` using `provider_id` (snake_case).
15. Do not fetch provider/model information during evaluator recommendation unless evaluator creation is required.
16. Library evaluators attached to a dataset must first be materialized into My Evaluators before mappings are created.
17. Every generated scenario must have a meaningful, human-readable name.
18. Scenario names should describe the behavior being validated rather than the test category.
Good:
- Retrieve Customer Order Status
- Reject Unauthorized Data Access
- Handle Empty Input
Bad:
- Scenario 1
- Test Case 3
- Happy Path
- Negative Test
19. Scenario names should be unique within the dataset whenever possible.
20. When creating evaluators for multi-turn datasets, ALWAYS explicitly pass `evalType: "session"`.
The `netra_create_evaluator` API does NOT auto-infer `evalType` from `turnType`.
If `evalType` is omitted, it defaults to `"turn"` regardless of the `turnType` value.
This causes multi-turn simulation evaluators to fail silently — conversations are
recorded correctly but evaluation scores show as "not available" because turn-level
evaluators cannot score session-level simulation data.
Correct (multi-turn):
```json
{
"turnType": "multi",
"evalType": "session"
}
```
Wrong (will produce "not available" scores):
```json
{
"turnType": "multi"
}
```
Only use `evalType: "turn"` within a multi-turn dataset when you explicitly need
per-message evaluation rather than whole-conversation evaluation.
21. Item-level `providerConfig` (`{ provider_id, model }`) overrides the evaluator's default model for that specific item only. Use it only when a specific scenario requires a different model. For uniform model usage across all items, configure the model at the evaluator level — do not set `providerConfig` on every item.
22. Multi-turn dataset items must always be created with:
- `input`: an empty object `{}` (NOT a string — the simulation engine generates user messages from metadata)
- `expectedOutput`: `null` or omitted (session-level evaluators score the full conversation)
- `metadata`: containing `scenarioName`, `scenario`, `persona`, `max_turns`, and `behaviour_instructions`
- `scenarioName`: concise display label (e.g. "Iterative Project Refinement")
- `scenario`: the actual goal/scenario description that evaluators use to judge success
(e.g. "Get a complete engineering spec for a smart plant watering system, refining requirements through conversation")
- `providerConfig`: always required
If the specification does not provide a providerConfig, resolve the organization's default provider configuration using `netra_get_default_llm_configuration` and attach it to every multi-turn dataset item during creation.
Setting `input` to a string causes the scenario name to appear empty in the Netra
dashboard because the UI reads the scenario display name from `metadata.scenarioName`,
and a string input overrides the expected structure.
23. Before creating a multi-turn dataset item, the system must ensure a valid provider configuration exists.
If `netra_get_default_llm_configuration` returns null and no item-level provider configuration is specified:
```
⚠️ Multi-turn dataset creation blocked.
No default provider configured and no item-level providerConfig supplied.
```
Do not create the dataset items until a valid provider configuration is available.
24. When creating LLM-based evaluators, ALWAYS pass explicit `config` with `model` and `provider_id` (snake_case).
Do NOT rely on auto-resolution of provider config — the MCP tool stores the field as `providerId` (camelCase),
but the evaluation engine requires `provider_id` (snake_case). This mismatch causes evaluators to silently fail
at runtime with "Missing required config: model, provider_id, or prompt", resulting in null scores on all items.
Correct:
```json
{
"model": "gpt-4o",
"provider_id": "dcffa74c-..."
}
```
Wrong (will fail at eval time):
```json
{
"model": "gpt-4o",
"providerId": "dcffa74c-..."
}
```
25. MCP tools (`netra_create_test_run`, `netra_submit_test_run_item`) are for manual/programmatic
result submission only. They require a `trace_id` from an existing trace. Do NOT use them as a
substitute for the SDK evaluation/simulation run APIs (`run_test_suite` / `runTestSuite`,
`run_simulation` / `runSimulation`) — those SDK methods handle agent execution, trace capture,
and result submission automatically.
26. When using `Netra.evaluation.run_test_suite()` with a remote Netra dataset:
```python
response = Netra.evaluation.get_dataset(dataset_id)
dataset = Dataset(items=response.items)
result = Netra.evaluation.run_test_suite(
name="...",
data=dataset,
task=agent_task_function,
max_concurrency=3,
)
```
The `data` parameter requires a `Dataset` object, not a dataset ID string.
Use `Netra.evaluation.get_dataset()` to fetch items, then wrap in `Dataset(items=...)`.
27. When mapping multi-turn (session) evaluators to a dataset using `netra_map_evaluator_to_dataset`,
ALWAYS pass `variableMapping: {}` (empty object). Session-level evaluators auto-resolve their
variables from the conversation context and do not require explicit variable mappings.
However, the `variableMapping` parameter itself MUST be provided — omitting it entirely
causes the API to throw "Cannot convert undefined or null to object".
28. Multi-turn dataset items MUST use `input: {}` (empty object).
The simulation engine generates all user messages — including the first one — from
`metadata.behaviour_instructions`. The `input` string is NOT sent as the first message.
Using a string for `input` causes the Netra dashboard to show an empty scenario name
because the UI expects `metadata.scenarioName` to be the primary identifier when `input`
is an empty object.
Both `scenarioName` and `scenario` MUST be present in metadata:
- `scenarioName` = the short display label shown in the dashboard
- `scenario` = the actual goal/scenario description that session evaluators
(Goal Fulfillment, Conversation Completeness) read to judge success
Omitting `scenario` causes evaluators to receive an empty goal, resulting in
inaccurate or failing evaluation scores.
29. A default provider configuration MUST exist before any MCP-based creation (datasets, evaluators, dataset items, evaluator mappings).
Phase 0 verifies this upfront by calling `netra_get_default_llm_configuration`. If no default provider
is configured, the entire workflow is blocked — do not proceed to planning phases or creation.
This check prevents wasted effort generating plans that cannot be materialized.
30. When adding a library evaluator to My Evaluators, the project evaluator's `type` MUST match the
library evaluator's original type exactly.
- If the library evaluator is `tool_accuracy` (rule-based), create it with `type: "tool_accuracy"`
and pass its specific config (e.g. `{"matchType": "partial"}`). Do NOT include model/provider_id.
- If the library evaluator is `llm-as-judge`, create it with `type: "llm-as-judge"` and pass
model/provider_id config.
- If the library evaluator is `regex` or `code`, create it with the matching type.
The `netra_create_evaluator` API may silently default to `llm-as-judge` when using
`libraryEvaluatorId`, regardless of the library evaluator's actual type. This causes rule-based
evaluators (like `tool_accuracy`) to fail at runtime because the engine expects LLM config
(`model`, `provider_id`, `prompt`) that rule-based evaluators don't use.
If you encounter this type mismatch, create the evaluator as a **custom evaluator** with explicit
`type` set to the correct value and omit `libraryEvaluatorId`. This bypasses the auto-defaulting
behavior and ensures the evaluator runs with the correct execution logic.
31. When using evaluators with configurable variables, ALWAYS include `evaluatorConfigs` in dataset
item metadata (or dataset metadata) with the correct evaluator ID and variable values.
Evaluators with configurable variables:
| Evaluator | Configurable Variables |
|---|---|
| Guideline Adherence | `assistant_instructions` (REQUIRED — no auto-resolution), `assistant_constraints` (optional — auto-resolved from `metadata.assistant_constraints` but configurable value takes precedence) |
| Factual Accuracy (multi-turn) | `reference_facts` (optional — auto-resolved from `metadata.reference_facts` but configurable value takes precedence) |
`evaluatorConfigs` format in metadata:
```json
{
"evaluatorConfigs": [
{
"id": "{evaluator-id}",
"configuredValues": {
"variable_name": "value"
}
}
]
}
```
Without `evaluatorConfigs`:
- **Guideline Adherence** will receive empty `assistant_instructions`, causing the evaluator
to report "The evaluation is severely limited because the assistant's instructions were not
provided" and produce a degraded score (typically 0.5) that fails the >= 0.6 pass criteria.
- **Factual Accuracy (multi-turn)** will receive empty `reference_facts` if not set in
`metadata.reference_facts` either, causing it to default-pass with no meaningful evaluation.
The `assistant_instructions` variable is the most critical — it has NO fallback auto-resolution
from any metadata field. It MUST be explicitly provided via `evaluatorConfigs[].configuredValues`.
When the plan includes Guideline Adherence or Factual Accuracy (multi-turn), ALWAYS prompt the
user to provide these values before proceeding to creation. If the user cannot provide them,
warn that the evaluator will produce unreliable scores.
32. **Always run Phase 3g for multi-turn datasets.** Skipping the correlation analysis risks generating a plan that will produce incorrect results because parallel scenario execution collides on shared state (e.g., two scenarios trying to use the same record simultaneously).
33. **Hooks live entirely on the user's side.** Scripts are never uploaded to or stored by Netra. At run creation the SDK sends lightweight descriptors (`lifecycleHooks`) only:
- `beforeAll` / `beforeEach` / `afterEach` / `afterAll` → stored on the test run (shown on every scenario in that run)
- `before` / `after` per `datasetItemId` → stored on each matching test run item (shown only on that scenario's Conversation tab)
The Netra UI renders these in collapsible **Pre-script** / **Post-script** panels on the multi-turn Scenario Conversation tab (snake_case labels, with `name` + `description`). There is no run-level "Has pre-script" badge.
The generated scaffold code belongs in the user's codebase, not in the Netra dataset configuration. Always key `SimulationHooks.before` / `after` by real dataset item ID values so the correct scenario UI shows the correct pre/post script.
34. **Never collapse correlated scenarios into a single large item to avoid hooks.** Merging related scenarios degrades evaluator accuracy (session evaluators score the entire conversation — a merged scenario is harder to judge) and makes the dataset harder to maintain. Recommend hooks instead.
35. **Never suggest sequential execution as a workaround for correlation.** Sequential execution (`max_concurrency=1` / `maxConcurrency: 1`) prevents collisions but means a single scenario failure blocks all subsequent ones. Hooks are the correct solution.
36. **`before_all` / `beforeAll` failure is fatal for the entire run.** If the setup code raises, no scenarios run and the run is marked failed. Only put truly required global setup in `beforeAll`. Common per-item setup belongs in `before_each` / `beforeEach`. Optional or scenario-specific setup belongs in `before`.
37. **`after`, `after_each` / `afterEach`, and `after_all` / `afterAll` must be robust.** These hooks run even when scenarios fail (including when `before` / `before_each` fails). Wrap risky cleanup logic in try/except (Python) or try/catch (TypeScript). A teardown error on an otherwise-successful item marks it `postscript_failed` (eval results are kept). Prefer resilient teardown so cleanup failures do not obscure a successful conversation.
38. **Setup context is how hooks share data with `BaseTask.run()` and teardown hooks.** The objects returned by `before_all` / `beforeAll`, `before_each` / `beforeEach`, and item `before` are merged into `setup_context` / `setupContext` and passed to `BaseTask.run()`, item `after`, and `after_each` / `afterEach`. If a before hook fails mid-way, teardown still receives the furthest successfully built setup context (so `after_each` can clean up what `before_each` created even if item `before` failed). Generated scaffolding must use `after(result, setup_context)` / `after(result, setupContext)` — not shared context alone. The task must declare the setup-context parameter in its `run()` signature to receive it. Existing tasks that do not declare it continue to work (backwards compatible). `after_all` / `afterAll` still receives only the run-level shared context from `beforeAll`, but its `results` include setup/first-turn failures as well as conversation failures.
39. **Always include a `hooks` usage example in the generated scaffold code** when hooks are recommended. The example must match the detected language and show:
- The `SimulationHooks` instantiation with all recommended hooks wired (`before_each` / `after_each` when common per-item setup/teardown applies)
- A `BaseTask.run()` that accepts and uses `setup_context` / `setupContext`
- The simulation run call with `hooks` (`run_simulation(..., hooks=hooks)` or `runSimulation({ ..., hooks })`)
- Hook descriptions on **every** function via `.description` (required; ≤ 200 chars):
- Python: `fn.description = "..."`
- TypeScript: `(fn as any).description = "..."`
Never ship hooks without descriptions — the SDK otherwise sends `description: null` in `lifecycleHooks`.
40. **Clearly communicate the `prescript_failed` status to the user.** When a `before_each` or `before` hook fails for a specific scenario, the test run item's status is set to `prescript_failed` rather than `failed`. Key properties of this status:
- Visible in the Netra dashboard as **Prescript Failed** — distinguishes setup failures from actual agent failures
- **Terminal**: the SDK polling loop treats `prescript_failed` as done; the run will not hang waiting for the item
- **Eval suppressed**: `evalStatus` is set to `NOT_AVAILABLE` immediately — no evaluators run for that item
- **Stable**: a `prescript_failed` item's status cannot be overwritten by bulk-failure sweeps (e.g. on run timeout)
- **Evaluation roll-up**: if every item in the run ends as `failed` or `prescript_failed`, the run's evaluation status is `NOT_AVAILABLE`
41. **Clearly communicate the `postscript_failed` status to the user.** When an `after` / `after_each` / `after_all` hook fails on an otherwise-successful scenario, the item is marked `postscript_failed`. Key properties:
- Visible in the Netra dashboard as **Postscript Failed**
- **Terminal**: counted as done by the SDK polling loop
- **Eval not suppressed**: conversation evaluations remain valid
- **Stable**: cannot be overwritten by bulk-failure sweeps
- **Evaluation roll-up**: excluded from the “all failed → NOT_AVAILABLE” check (unlike `prescript_failed`)
- Already-failed / `prescript_failed` items are never overwritten with `postscript_failed`
42. **Also sync TypeScript simulation guidance** with `netra-best-practices/references/typescript/simulation.md` when updating hook behavior. Python guidance lives in `references/python/simulation.md`. Keep both languages at feature parity for hooks, including `before_each` / `beforeEach`, `after_each` / `afterEach`, `postscript_failed`, progressive setup context on failure, and `.description` requirements.
## Reference
For the full evaluator catalog with variables and thresholds, see [evaluator-library.md](references/evaluator-library.md).