mcpField
N
n8n:create-instance-ai-eval

n8n:create-instance-ai-eval

github.com/n8n-ioテスト・品質209.2k132 回インストール今日更新
リポジトリ

n8n:create-instance-ai-eval について

Instance AI の評価ケースを作成・調整し、選択した LangTracer スイートに送るためのスキルです。ケース形式、実際の失敗事例の調査、実環境のビルドに基づく期待値の検証を扱います。

主な機能

  1. 01ワークフローと Agent の評価ケース作成
  2. 025 種類のケース形式から選択
  3. 03実際の失敗からケースを調査
  4. 04実環境のビルドに基づく期待値の調整
  5. 05選択した LangTracer スイートへの送信
  6. 06チェックポイント型と自律型の進行

活用シーン

  1. 01ワークフロー構築の評価作成
  2. 02複数ターンにわたる会話動作のテスト
  3. 03実際のトレースで見つかった能力ギャップの記録
  4. 04適切な LangTracer スイートへのケース公開

MCP 対応

LangTracer MCP を使った会話、クラスター、評価データの発見と確認、および LangSmith による生トレースの検証を説明します。スイートやケースを操作する MCP ツールにも言及しています。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/n8n-io/n8n --skill n8n:create-instance-ai-eval

インストール後、Claude Code で/n8n:create-instance-ai-evalを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

case-shapes.md
running-evals.md
sourcing-cases.md

詳細

Instance AI 評価を作成する

各評価は1つの JSON ケースです。ワークフローのケースは packages/@n8n/instance-ai/evaluations/data/workflows/ に作成します。単独の Agent ケースは packages/@n8n/instance-ai/evaluations/data/agents/ に作成し、 create-agent-builder-eval スキルに従います。 ディスクローダーは両方のディレクトリにある *.json を自動検出します。LangTracer スイートが永続的な保存先です。ケースは harness/schema.ts に照らして検証されます(.strict() — 不明なキーがあると読み込みに失敗します)。評価の READMEにはすべてのフィールドのリファレンスが記載されています。このスキルでは、推奨する方法を説明します。

新しいケースの JSON ファイルをリポジトリにコミットする方法は、推奨されなくなりました。 ファイルはローカルで(未コミットのまま)作成し、実際のビルドに対して調整してから、eval:langtracer-push を使って lang-tracer スイートにプッシュしてください(lang-tracer スイートにプッシュするを参照)。プッシュ先は、ドライバーが選んだスイート(まず対象スイートを確認するを参照)です。n8n CI が実行する統合コーパスの baseline、または agents のような専用の機能スイートを選びます。 スイートがケースの保存先です。評価 CLI は --source langtracer を使ってケースを読み込みます。引き続き JSON ファイルを作成しますが、コミットする成果物ではなく、プッシュへの入力として使用します。

シードされたケース。 inline シードはケースとともにプッシュされます。ケース書き込み API がそのまま保存するため、他のケースと同様にスイートが保存先になります。拒否されるのは seed.mode: "replay" のケースのみです(skipped: に記載)。実行時に LangSmith トレースから再構成されるため、そのトレースが削除されると使えなくなり、永続的な保存先もありません。replay ケースもコミットしないでください。代わりに、そこから合成ケースを作成します。case-shapes.mdを参照してください。

まず対象スイートを確認する

何かの情報を探したり、ドラフトを作成したり、実行したりする前に、このセッションで作成するすべての評価の送信先となる LangTracer スイートをドライバーに確認してください。 自律性のレベルにかかわらず、自律モードを含め、必ず確認してください。スイートを自分で選んだり、暗黙のうちにデフォルトを選んだりしてはいけません。誤ったスイートにプッシュされたケースは、誤った CI レーンで実行され、誤ったチームにアラートが送られます。

スイートについての質問と自律性についての質問は、1つのメッセージで同時に行います。baseline を推奨してください。

  • baseline(推奨):一般的なコーパスです。Instance AI(INS)チームが監視し、毎晩実行されます。
  • agents や node-gauntlet など、その他のスイート:list_suites を呼び出し、最新の一覧を提示してください。スイートは追加や廃止が行われるため、ハードコードされた一覧は使用しないでください。

ルール:

  • ドライバーの回答は、セッション内のすべてのケースに適用されます。ケースごとに再度尋ねないでください。
  • ドライバーが依頼時にすでにスイートを指定している場合は、1行で確認し、そのまま続けてください。再度尋ねないでください。
  • スイートの種類がケースに合っていることを確認してください。ビルドが誤っている(capability_gap)ケースは、その種類を許可するスイートにのみプッシュしてください。スイートが許可していない場合は、ドライバーに伝えて再度確認してください。
  • 最終的な意思決定ログには、選択したスイートのスラッグをリンクとして記載してください。

まず自律性のレベルを設定する

ソースの取得、草稿の作成、実行を始める前に、ドライバーがどの程度作業に関与したいかを決め、その内容を伝えてください。 このスキルには、2 つの自律性レベルがあります。 依頼内容からレベルが明らかな場合(「作成と調整は任せる」対「各ステップで止めて」など、または明示的なモードの指定)、そのレベルを採用し、1 行で伝えて、変更方法も案内します(例:「調整時に止めて」と言えばチェックポイントを追加できます)。 明確でない場合は、作業を始める前に、2 つのレベルを提示してドライバーに質問を 1 つしてください。

このスキルには、自然な意思決定のゲートが 4 つあります。選定(どの実際の失敗を組み込むか)、形態と期待値(アーキタイプ、必須項目、スコープの絞り込み)、調整(各 red を分類し、維持/緩和/削除を決定)、プッシュ(種類とティア)です。各ゲートでの動作はレベルによって異なります。

レベル誰が停止を判断するか動作
autonomousエージェント4 つのゲートすべてを最初から最後まで進め、最後にドライバーが確認できる意思決定ログを報告します。プッシュしたケース、そのスイート、ソーススレッドをリンクで示します(リンクを共有し、ID だけを載せない)。ドライバーが記録を選択した場合は観察 ID を含め、維持する capability-gap red については Linear チケットの提案を含めます(Capability gap → Linear チケットを提案する)。
checkpointドライバー(ゲートごと)各ゲートで、簡潔な提案と推奨事項を提示して停止します。ドライバーは「進めて」と伝えるか、方向を変更します。調整ゲートでは、ドライバーが実際の会話とワークフローを自分で確認してから確定できるよう、稼働中のインスタンス上に作成したばかりのスレッドへのリンクとログイン認証情報を渡します(以下を参照)。

調整はどちらのレベルでも特別扱いです。 ケースの意味を変える調整の判定、つまり実際の capability-gap red とハーネス起因の red の切り替えや、既知の不良ビルドを合格させてしまうような緩和は、必ず明示的に提示します(checkpoint では対話形式で、autonomous では意思決定ログに記載)。黙って決めてしまうとスイートを損なう可能性があるため、完全な自動確定は行いません。

ソース観察の記録は任意であり、5 つ目のゲートではありません。 実際のスレッドが選定を通過した後、LangTracer の create_observation ツールを使って、選定理由と開発者が観察した内容の保存を提案します(sourcing-cases.md を参照)。 ドライバーが断った場合、autonomous モードで希望を示していない場合、または書き込みに失敗した場合は、eval を続行します。観察記録のために、草稿作成、調整、プッシュを止めてはいけません。create_observation と update_observation は LangTracer にのみ書き込みます。LangSmith へのフィードバックや同期のステップは追加しないでください。

checkpoint の調整 — インスタンス上で実際のスレッドを確認する。 調整の判定は信頼性に関わるため、checkpoint モードでは、実行結果に関する自分の解釈をドライバーに信頼するよう求めません。--keep-workflows を指定して稼働中のインスタンスに対してケースを作成したため(ステップ 4)、スレッドとワークフローはまだ残っています。ドライバーに直接リンクを渡し、確認してもらいます。

  • スレッド: <base-url>/assistant/<threadId> — ケースが実行された正確な会話です(実行時に threadId が出力され、作成したワークフローは BUILT (<id>) と表示され、<base-url>/workflow/<id> から開けます)。
  • ログイン: eval がログインに使用するメールアドレスとパスワード(インスタンスに作成したオーナーの認証情報です。詳細は running-evals.md を参照してください。デフォルトのローカルシードは [email protected] / PlaywrightTest123 です)。

red ごとに、アサーション、green/red のどちらになったか、提案する分類(実際の capability gap/ハーネスの制約/ノイズ)と維持/緩和/削除の判断、確認用リンクを提示します。ドライバーはログインしてスレッドとワークフローを読み、判定を確定するか変更を指示します。その後、判定をケースの description に書き戻します。

数値 ID だけでなく共有リンクを使う

lang-tracer のすべてのエンティティには共有可能なウェブページがありますが、CLI と MCP が返すのは数値 IDです。eval:langtracer-push は + created <slug> (#621) を出力し、get_eval_run は実行番号を返し、list_conversations はスレッド ID を返します。ID はクリックできないため、担当者が自分で探しに行く必要があります。人が読むものの中でケース、スイート、スレッド、クラスター、実行を示す場合はいつでも――チェックポイントの提案、実行後の判断ログ、PR の説明、Linear のチケット、Slack のメッセージなど――ID をラベルに含めたリンクとして表示してください。

pushed as [#621](https://lang-tracer.n8n-maintenance.workers.dev/test-cases/621)

リンクの作成にはウェブのベース URL(LANGTRACER_URL。本番環境では https://lang-tracer.n8n-maintenance.workers.dev)を使用してください。API のベース URL は使わないでください。 ${LANGTRACER_URL}/api/v1 と /api/mcp は機械用のエンドポイントであり、どちらかへのリンクは担当者には 404 を返すか、JSON を表示します。

エンティティURLID の取得元
テストケース<base>/test-cases/<id>push の出力 (#<id>);create_test_case / search_test_cases
スイート<base>/suites/<suiteId>push のヘッダー Suite "<slug>" (#<id>);list_suites
ソース会話<base>/conversations/<threadId>list_conversations / get_conversation
クラスターレポート<base>/clusters/<id>list_cluster_runs / get_latest_cluster_run
評価実行(スイープ)<base>/results?sweep=<runId>list_eval_runs / get_eval_run(runId は sweeps.id、つまり「run #N」です)

lang-tracer ではなく、このベース URL も使わないリンクが 2 つあります。ビルド済みスレッド(<base-url>/assistant/<threadId>)とワークフロー(<base-url>/workflow/<id>)は、評価の実行先であるn8n インスタンス上にあります。両方が関係する場合――キャリブレーションの問題をレビューするときや、機能ギャップのチケットを作成するときなど――どのホストを指すリンクなのか誰も迷わないよう、ラベルを付けて両方を記載してください。

最良のケースはどこから見つかるか

最も強力なケースは、作り上げた前提ではなく、実際の失敗を記録したものです。ケースの発見と検証には、2 つの接続が役立ちます。LangTracer は実際の会話を機能ギャップのテーマごとにクラスター化し(実際に何が失敗しているのかを大規模に発見)、LangSmith は生のトレースを保持します(実行時に何が起きたのかを正確に検証)。LangTracer は発見レイヤーです。永続的な成果物は、そこで得た知見から作成する合成ケースであることがほとんどです(seed.mode: "replay" を使うのは case-shapes.md に記載された場合だけです)。MCP の接続方法と、発見 → 検証 → 記録のワークフローについては sourcing-cases.md を参照してください。

まずケースの形を選ぶ

コーパスは5つの類型で構成されています。下書きを始める前に、どの類型を書くのか決めてください。選んだ類型によって、フィールド、採点方法、検証方法が決まります。類型は組み合わせることもできます(シード付きケースでも、結果をアサートできます)が、主となる形が作業の進め方を左右します。

類型答える問い主なフィールド
Build(デフォルト)エージェントが構築するワークフローは、実際に機能するか?outcomeExpectations + executionScenarios
Behaviour / processエージェントは正しく会話できるか(適切な確認質問をする、同じことを再度尋ねない、修正を尊重する、計画の承認を尊重するなど)?processExpectations + 複数ターンのディレクタースクリプト。多くの場合、ビルドのみ
Credential特定の認証情報ビューを前提としたとき、ビルドは正しく動作するか?credentials[]
Seeded以前の作業がすでに存在するスレッドの途中から開始し、テスト対象のターンを進めるseed(作成時は mode: "inline"、ローカルチェックでは "replay")
Context会話が長くなってもエージェントは動作し続けるか。すでに読み込んだ情報を再利用し、観察メモリが履歴を圧縮した後も重要な情報を保持できるか?processExpectations + 長い seed。圧縮を含むケースでは requiresMemoryCompaction

Buildについては以下で詳しく説明します。ほかの4つの類型、ディレクタースクリプトの用語、シードのモードについては、case-shapes.mdを参照してください。

ワークフロー構築ケースでは、ジャッジはトークンのグラウンドトゥルースも確認できます。トランスクリプトの各ターンヘッダーにはターンごとの入出力トークン数がインラインで記載され、ビルド全体の合計、キャッシュの読み取りと書き込みの内訳、最初のステップのコストも確認できます。そのため、期待事項でコストや消費量を直接参照できます(「ターン2で同じノードのスキーマを再読み込みしない」など)。数値に含まれるのはオーケストレーター自身のLLMステップのみです。委任された Agent ビルドはサブエージェント内で実行され、そのステップはスナップショットに含まれないため、Agent ケースではコストに関する期待事項を書かないでください。これらの数値は実行デバッグのスナップショットから取得されるため、テスト対象のインスタンスで N8N_INSTANCE_AI_RUN_DEBUG_ENABLED=true が必要です。これが設定されていない場合、ジャッジは (no run debug captured) と読み取り、コストに関する期待事項は採点できません。

ジャッジはスレッドの観察行(圧縮後に観察メモリに保持された内容)も確認できるため、メモリケースでは要約自体も採点できます。このブロックは独立した REST 読み取りであり、フラグは必要ありません。

基本原則(あらゆる形式に共通)

意図に基づいて期待事項を記述し、実際のビルドを基準に調整します。 まず、正解となるあらゆる解決策に共通する条件、つまりユーザーが実際に述べた内容から導かれる必須事項を決めます。そのうえで、ワークフローを実際に一度構築し、粒度を調整します。指定しすぎた部分は緩和し、必須事項が実現可能であることを確認し、エージェントが別の正当な方法で満たす要件を見つけます。観察したビルドをそのままアサーションに書き写してはいけません。評価が「問題を解決したか」ではなく「その実行を再現したか」を測る過剰適合になります。

会話はユーザーの言葉で書きます。 ユーザーが述べるように、目標と実際の制約を示します。ノードの種類を挙げたり、構成を指示したり、プロンプト内で outcomeExpectations を言い換えたりしてはいけません。会話でビルドの内容を指定してしまうと、ケースで試されるのはエージェントが指示に従えるかどうかだけになり、期待事項は同語反復になります。ユーザーの依頼と、正しいワークフローでそれを実現する方法との隔たりこそ、テスト対象の能力です。基準となるのがユーザーの明示した技術的な希望を尊重することだとしても、「フィールド X が必要ですが、組み込みノードでは取得できないので、API から直接取得してください」のように、ニーズと制約として表現します。「HTTP Request ノードを使ってください」のような実装仕様にはしません。

ユーザーが別の言語を指定した場合(またはケースが非英語への対応をテストする目的で作成されている場合)を除き、会話は英語で書きます。 実際のスレッドを元にする場合、英語以外のものもよくあります。プロンプトを書き直す際は、ユーザーの言葉で意図を英語に翻訳します。テスト対象の失敗モードは基準となるケースであり、元の言語ではありません。

問題を再現できる、最小限の複数ターンの会話に絞ります。 実際のスレッドを元にすると、前提の設定、デバッグ、余談などで何十ターンにも及ぶことがよくあります。それをそのまま書き写してはいけません。テスト対象のビルドや動作を再現できる最小限のターンにまとめます。残すターンには、必ず役割が必要です。そのターンが残るのは、エージェントが尋ねる必要のある値(ディレクターノートを通じて質問されるまで伏せておく)、ケースでテストする修正や異議、またはビルドを開始するための計画承認のいずれかに該当する場合だけです。あるターンを削除してもテスト対象が変わらないなら、そのターンは削除します。負荷となるやり取りなしに依頼全体を一度に提示できる場合は、必ず1ターンにまとめます。負荷となるやり取りがある場合に限り複数ターンにし、各ディレクタースクリプトは1ターンにまとめます(ステップを順に進めるためにアシスタントの「完了」ターンを捏造してはいけません。詳しくはcase-shapes.mdを参照してください)。最小限の会話は能力を切り分けますが、そのまま書き写した会話は余計な情報に埋もれ、指示への追従をテストすることになります。

アサーションだけでなく、ビルドの規模も調整します。 実際のプロンプトを元にする場合、「本番環境対応、永久稼働、1日あたりフィード投稿3件とストーリー8件」「毎日記事を50件生成」のように、要求を盛り込みすぎていることがよくあります。その結果、約900秒のビルド予算を確実に超過します(「既知のハーネスの制限」を参照)。忠実さを保ったまま要求を絞ることは、正当な作成方法です。 バッチサイズを小さくしたり、並列パイプラインを複数のうち1つに減らしたり、隣接する AI ステップを統合したりして、予算内でケースをビルドできるようにします。そのうえで、削減内容をケースの description に記載します(「元の依頼では1日あたり8件のストーリーを生成するパイプラインも求めていましたが、予算内でビルドできるよう、フィード投稿に範囲を限定しました」)。テスト対象の能力は維持し、組み合わせによる過剰な規模を削ります。ビルドが一度も完了しないケースでは、何もテストできません。

ワークフロー

これらの手順は、最初に自律性のレベルを設定するにある4つのゲートに対応しています。 ソースの選定(手順1の前)は、ソースに関する所見を記録するオプションを含む選定ゲートです。手順1~2は形状と期待値ゲート、手順5~6はキャリブレーションゲート、手順7~8はプッシュゲートです。自律モードではすべてのゲートを通過し、決定ログに要約します。チェックポイントモードでは各ゲートで提案を提示して一時停止し、キャリブレーションではドライバーにスレッドのリンクとログイン情報を渡して、実際のビルドを確認してもらいます(最初に自律性のレベルを設定するを参照)。キャリブレーション(手順6)では、どちらのモードでも、意味を反転させる判定を必ず明示します。

  1. まず必須事項を明示します。 会話だけから、正しいワークフローが必ず行うべきこと(トリガーの種類、必須の操作、ゲーティング条件)を列挙します — それらが outcomeExpectations の草案になります。必須フィールドは conversation(1 ターン以上、最初は user)、complexity、tags、そして executionScenarios / processExpectations / outcomeExpectations のうち少なくとも 1 つです。
  2. 以下のテンプレートからケースを下書きします。それがロードできることを検証します(「Validate」を参照)。
  3. バッチの前に、1 つのケースで環境をスモークテストします。 まず 1 つのケースをエンドツーエンドで実行します。これにより、認証 / モデル / --base-url / ビルド済み dist を約 1/N のコストで検証できます — ケースの検証とは別ものです。その 1 つのケースが実行時にクラッシュする場合(特に、すべてのケースで遭遇すると予想される同一のエラーであれば)、バッチを実行する前に環境を修正します(「A red is signal」→ 環境チェックを参照)。15 個のケースを実行して、そのすべてで古い dist によるクラッシュを発見するだけでは、1 回分の実行を無駄にします。
  4. 一度だけビルドします — 実行中のインスタンスに対して(running-evals.md を参照)、--keep-workflows を付けて実行し、ビルドされたワークフローが検査用に残るようにします。
  5. 検査します — ビルドされたワークフロー(実行時に BUILT (<id>) が出力されます。GET /rest/workflows/<id> で取得します)と、HTML レポートのトランスクリプトを読み、エージェントが実際に何を行ったかを確認します。
  6. キャリブレーション — アサーションを鋭くします。緑にするために鈍らせてはいけません。 本当にサイズが合っていないアサーションを修正します。会話が開いたままにした選択を固定しているものを緩め(そのため、有効な代替ビルドが誤って失敗します)、誤ったビルドがすり抜けてしまうものを厳しくし、executionScenarios をモックデータ上でのワークフローの実行の仕方に合うように表現します。しかし、ビルドに実際のギャップがあるために、あるいはハーネスがそれを実行できないためにシナリオが赤になる場合、その赤が結果です — それを保持し、理由を明示します(後述の「A red is signal」を参照)。実行を緑にするためだけに、シナリオを削除したり、アサーションを弱めたり、ビルドのみに落としたりしないでください。そして、ケースが緑で返ってきたとき、それは受け入れるものではなく、勝ち取る結果です。前提条件が実際に発火したことを確認し、そのケースを回帰ガードと呼ぶ前に生のスレッドから再導出します(First reproduce, then reclassify を参照)。
  7. スイートにプッシュします — JSON をコミットしないでください。 キャリブレーションが済んだら、eval:langtracer-push を使ってケースをそのキュレーションされた lang-tracer スイートにプッシュします(Push to a lang-tracer suite を参照)。スイートがケースのホームであり、リポジトリではありません。data/{workflows,agents}/*.json ファイルはコミットせずに残します(またはスイートに入ったら削除します)。新しいケースの JSON をリポジトリにコミットすることはもはや方法ではありません。(inline シードはケースとともにプッシュされます。拒否されるのは replay ケースのみです — それはローカルの使い捨てです。詳しくは case-shapes.md を参照。)ソースのあるケースについては、最後に MCP 経由でそのソーススレッド/ファインディングにリンクします — Link the pushed case to its source を参照。
  8. リンクを返し、ケースがギャップを見つけた場合はチケット提案を返します。 プッシュしたケースは #<id> ではなく <base>/test-cases/<id> として報告し(Share links, never bare ids)、キャリブレーションが実際のケイパビリティギャップの赤を保持した場合は、それを誰も所有しない赤いケースとして残すのではなく、Linear チケットを提案します(Capability gap → propose a Linear ticket)。

--iterations N は、フレーク性(pass@k / pass^k)を測定するために利用できます — ケースが非決定的ではないかと疑うとき、またはそれをゲート付きティアに昇格させる前に手を伸ばすものであり、日常的なステップとしてではありません(各イテレーションは完全なビルド + 実行です)。

ガットチェック: このケースが確実に赤にする、もっともらしい誤ったビルドを思い描けないなら、アサーションは緩すぎて何もガードできていません。 緑色になったことだけでなく、前提条件が発生したことを確認してください。 「X が起きたとき、エージェントは Y をした」のような条件付きアサーション(ほとんどの processExpectations と、すべての動作ケース)には、合格と判定されても 2 通りの解釈があります。エージェントが Y をしたか、または X が起きておらず、アサーションが空虚に合格したかです。特定の失敗をモックで発生させることが前提の動作ケース(たとえば、AI ノードが空の値を返すようシミュレートし、その結果、後続の解析ノードが失敗するケース)は、典型的な落とし穴です。モックが代わりに解析可能なデータを返すと失敗が発生せず、ケースは何も検証しないまま緑色になります。キャリブレーションでは実行トレースとエージェントの finalText(検証スナップショット内の buildTrace.finalText、または HTML レポート)を確認し、X が実際に発生したことを検証してください。直接ループの eval-results.json には、期待値ごとの判定理由が保存されないため、合否だけではどちらの解釈なのか判断できません。

否定形は、最も簡単に誤解を招く表現です。 「エージェントが不正な引数で X を呼び出さなかった」というアサーションは、エージェントが X を正しく呼び出した場合も、X をまったく呼び出さなかった場合も合格します。この 2 つは正反対の結果ですが、判定結果はどちらも同じ緑色になります。そのため、ツールの誤用に関するアサーションでは、合格を信頼する前にツールが実際に呼び出されたことを確認してください。呼び出しについては、testCases[].transcriptPerRun[][].steps[] を解析してください。複数のアクションを持つツールは、トランスクリプト内で単なる toolName(nodes、workflows、credentials)としてグループ化されるため、実際のアクションを特定するには args.action を確認してください。nodes[explore-resources] でフィルタリングしても何も見つからず、問題なく合格したように見えます。このコーパスでの測定では、ツール誤用のケース 5 件からなるバッチが最初のキャリブレーション実行で 100% のスコアを記録しましたが、そのうち 3 件はテスト対象のツールが呼び出されていなかったため、空虚に合格していました。

根拠のある失敗でも再現しなければ、まだ回帰を防ぐためのテストにはなりません。まず前提条件を再検証してください。 動作はバージョン間で変化することがあり、ケースが機能不足(赤色)から回帰防止テスト(緑色。機能の再導入を検出する)に変わるのは、正当かつ有益な結果です。ただし、最初からそう決めつけず、切り分けて判断してください。緑色になるのは、ビルダーが改善したからというより、ケースで状況を設定できていなかったことを示す場合のほうがはるかに多いです。以下を参照してください。

まず再現し、その後に再分類する

実際の失敗に基づいて作ったケースが正常終了するのは、最初のキャリブレーション実行で最もよくある結果です。そして「ビルドが改善したに違いない」は、最もよくある誤った結論です。通常の原因は、スレッドの要約――テーマラベル、観察内容の説明、自分で書いた一行メモ――をもとに作成したために、想定したトリガーと実際に発火したトリガーが異なることです。何かを格下げする前に、元のスレッドに戻ってください。

トピックではなく、ターンを探してください。 失敗した正確なツール呼び出しを特定し、その直前のアシスタントのテキストを読んでください。エージェントは通常、意図を明確に述べます。その一文が事前条件です。次に、会話の話題ではなく、その呼び出しを必要にした状態を確認してください。

このコーパスの実例です。情報源となった指摘:「エージェントがnodes[explore-resources]のメソッド名をでっち上げる」。想定したトリガー:ユーザーがモデルプロバイダーを切り替える。プロバイダーを問題なく切り替えるケースを作ったところ、正常終了しました。エージェントはモデル ID を直接設定し、ツールを一度も呼び出さなかったのです。元のスレッドには、次のように明記されていました。

「Groq Chat Modelのモデルが無効です(llama3-8b-8192はGroqの認証情報では提供されていません)。有効なモデルを一覧表示して修正します。」

事前条件は、プロバイダーの切り替えではありませんでした。プロバイダーが実行時に拒否する既存のモデル ID があり、認証情報はすでに接続済みであること――これが、モデルの列挙を必要にする状態でした。この状態をもとに作り直したところ、同じケースは初回実行で失敗を再現し、エージェントはメソッド名を連続して2つでっち上げました。

再現しないケースを再現するケースに変えるには、次の3つの手順を順番に試してください。ガードで済ませる前に実行してください。

  1. 事前条件を修正します。 話題にしていた内容ではなく、情報源となったスレッドの状態が再現されるよう、シードと実際のターンを作り直します。
  2. アサーションを最初の呼び出しに移します。 エージェントが自己修正する仕組みは、最終状態だけで評価すると正常終了しますが、それでも実際の不具合です。無駄なラウンドトリップと、推測されたスキーマが指摘対象です。初回呼び出しの正しさを評価したことで、以前は「自己修正する、評価としては弱い」と退けられていた仕組みが、2回の実行のうち2回で再現するギャップに変わりました。
  3. 実際に試行で検出した内容を残します。 再現実行では、対象としていたものとは別の実際の不具合が赤になることがよくあります。それも能力ギャップの指摘です。赤の結果を残し、説明の対象を変更し、もともと対象としていた仕組みは再現しなかったことを明記してください。

やめどきを見極めてください。 このハーネスでは構造上到達できない仕組みもあり、何度考え直しても解決できません。最も分かりやすい例は、評価対象ターンの開始時にシードが新規のワークスペースファイルを復元するため、エージェントの最初のold_strは必ず、直前に読み込んだファイルから取得されるケースです。多くのターンを通じた蓄積されたずれが必要な失敗――str_replaceのバイト忠実性に関する一連の問題で、本番環境におけるエージェント起因のツール呼び出し失敗のうち最大規模で、スレッドの15%を占めます――は、編集のたびにビルドする複数ターンの連鎖でも再現しません。試行は3回程度を上限とし、その後は否定的な結果を、それ自体を指摘としてケースのdescriptionに記録してください。そして、実際の不具合は評価ではなくチケットにすべきかどうかを検討してください。

赤はシグナルです――回避せず、表面化させてください

キャリブレーションの目的はアサーションを適正な規模に調整することであり、ケースをパスさせることではありません。実行によってシナリオや期待値が赤になったら、まずその原因を分類し、そのうえで赤を残してください。

まず環境の問題を除外します。 ケースについてのシグナルとして赤を読み取る前に、実行全体における失敗のパターンを確認してください。すべてのケースが同じ形で失敗する――ビルドは成功する一方で、すべてのシナリオが同じ実行エラーになる、またはすべてのビルドが開始前に同じエラーを出す(ツール呼び出しがゼロのままAgent error: Something went wrong…となる)――場合、原因はほぼ間違いなくケースではありません。環境の破損であり、特に多いのはブランチやワークツリーの切り替え後に古い distが残っているケースです。知っておくべきパターンは2つあります。

  • 古い packages/core / packages/cli の dist — リファクタリングでランタイムの エクスポートが移動したのに、ビルド済みの dist がまだ古い方を呼び出しているため、ビルドは成功するが、 すべての実行が同じように失敗する(例: createDeferredPromise が @n8n/utils に移動した後の (0 , n8n_workflow_1.createDeferredPromise) is not a function)。

  • 古い/不完全にビルドされた @n8n/instance-ai の dist — すべての実行が ビルド前に エラーになり (Agent error…、ツール呼び出しはゼロ)、インスタンスログには dist/skills/*.js からの Cannot find module '@/utils/...' が表示されます。ビルドの tsc-alias ステップ(@/ パスエイリアスを 相対 require に書き換える)が完了しておらず、dist が内部的に矛盾しています。

  • 同期が取れていない node_modules — pnpm build 自体が Cannot find module '@n8n/<pkg>' で 早々に失敗します。そのパッケージは宣言された workspace:* 依存であり、かつ dist/ を持っているのに、 です。コンシューマーの node_modules からワークスペースのシンボリックリンクが欠落しています (ブランチやワークツリーを切り替えた後によく起こります)。ls -d packages/<consumer>/node_modules/@n8n/<pkg> で確認し、単純な pnpm install で修正します — より 重い pnpm reset --full は不要です。

それを回避せず修正してください: 順序どおりの完全な pnpm build を実行します(対象を絞った --filter ビルドは、無関係な古い依存の型エラーで失敗することがあります。instance-ai の形状の場合は、 cd packages/@n8n/instance-ai && pnpm build が tsc && tsc-alias を実行します)。その後、 インスタンスを再起動してください — 実行中の node プロセスは古い dist をメモリに保持しているため、 ディスク上で再ビルドしても再起動するまで何も変わりません(そして環境変数パターンによる kill はそれを 見逃します — 実際の lsof -t -iTCP:<port> の PID を kill してください)。1 つのケースを再プローブし、 ビルドと実行を確認してから、バッチを再実行します。一様な環境起因の失敗が除外されて初めて、以下の 3 つの カテゴリが適用されます:

  • 実際のビルド / 機能のギャップ — エージェントのワークフローが間違っているか、ユーザーが要求した 何かが欠けています(配線ミスのある分岐、欠落したリトライ、誤ったフィールドキーなど)。これこそが 評価の目的です。レッドのままにしてください。 アサーションを緩めたりシナリオを削除したりしないで ください。現在レッドであるギャップは、今日時点での機能シグナルであり、ビルダーが改善された後には 再導入を防ぐガードとなります。次に、そのギャップについて Linear チケットを提案してください — 機能のギャップ → Linear チケットを提案する を参照。
  • ハーネスの制約 — ビルドは正しいものの、モック/実行レイヤーがそのパスを実行できません(後述の 「既知のハーネスの制約」を参照)。シナリオは保持し、その description にその旨を記述してください — このレッドはハーネス起因であり、ビルドの欠陥ではないと — そうすれば誰も製品のバグと誤読しません。 毎回ハード失敗する場合は、ゲートされた層から外しておいてください。ハーネスがその機能を獲得すれば、 再作成なしで元を取れるようになります。
  • 真の非決定性 — 同じ ビルドが実行ごとにグリーン/レッドに変わります。これが唯一の本当の 「ノイズ」です。フレーキーと呼ぶ前に --iterations N で確認し、その後層を外して注記します。削除は 最後の手段です。

保持するすべてのレッドについて、ケースの description にスキャンしやすいプレフィックスを付けて 注記してください。そうすれば将来の読者が一目で両者を区別できます。ハーネス起因のレッドには Harness note: … を使い(制約を明記し、なぜビルドが依然として正しいのかを説明します)、実際のギャップには Capability-gap finding: current build reds because <X> — a real builder bug (flips to a regression guard once fixed) を使います。一貫したプレフィックスによって、コーパスを grep 可能に保ち、ハーネスのレッドが製品のバグと 誤読されるのを防ぎます。

決してやってはいけない唯一の行為は、ケースがチェックする内容を弱めることでレッドを回避することです — 失敗するシナリオを削除したり、誤ったビルドが通ってしまうまでアサーションを緩めたり、こっそり ビルドのみに変換したりすることです。それはスイートを製品よりも実際よりグリーンに見せてしまい、 評価の役割の正反対です。バグとハーネスのギャップこそが成果物なのですから、それらを際立たせ、 回避するよう細工しないでください。既知の不良ビルドが通るようにケースを編集している自分に気づいたら、 手を止めてください。 分類を誰が確認するかは、自律性レベルによって異なります。 チェックポイントモードでは、維持/緩和/破棄の判断を確認するのはドライバーの役割です。実際の会話とワークフローをレビューできるよう、スレッドのリンクとログイン情報をドライバーに渡し(先に自律性レベルを設定するを参照)、合意した Harness note: / Capability-gap finding: プレフィックスをケースの description に書き戻します。自律モードでは、エージェントが実行終了時の決定ログでそれを明示的に提案します。いずれの場合も、分類はオープンに明示され、黙ってコミットされることはありません。ハーネスのレッドを実際のギャップと誤読する(またはその逆)ことが、スイートを静かに損なう唯一のキャリブレーションの誤りです。

ケイパビリティギャップ → Linear チケットを提案する

維持されたケイパビリティギャップのレッドは、どんなバグレポートよりもあなたがうまく特徴づけた製品バグです。しかし、スイート内のレッドケースは自動的に誰かに割り当てられるわけではありません。チケットがなければ、ギャップは CI 内で永続的なノイズとして居座り、次に実行結果を読む人は誰かがすでに対応中だと想定してしまいます。したがって、レッドが実際のギャップとして分類されたら(そして自律性レベルに従ってドライバーがそれを確認したら)、そのための Linear チケットを提案します。

まず再現する。これはデフォルトであり、絶対的なルールではありません。 ケースがギャップを再現した後にのみ、チケットを提案します。つまり、ケースが実際のビルドでレッドであり、前提条件が発火することが確認され、生のスレッドを再読した(まず再現し、それから再分類するを参照)ということです。トレース分析だけから得られたギャップは仮説です。それに対するチケットは、所有チームの時間を費やします。ドライバーは例外を承認することがあります。たとえば、ハーネスが約 3 回試行してもそのメカニズムに到達できない場合です。その場合、チケットには eval がギャップを再現しなかったことを記載しなければなりません。ドライバーの承認なしに、再現されていないギャップを決して起票しないでください。

提案し、作成しない。 AGENTS.mdに従い、頼まれていないのに Linear チケットを開かないでください。ドラフトをドライバーの前に提示します — チェックポイントモードでは対話的に、自律モードでは決定ログで — タイトル、チーム、本文を添えて、そしてドライバーに「go」と言わせます。次の 2 つのケースでは提案をスキップします。

  • ギャップにすでにチケットがある。 重複を起草する前に、ケースのページでリンクされた issue を確認し、ドライバーやソーススレッドが言及した候補識別子に対して get_linear_ticket_context <TEAM-N> を実行します。
  • レッドがケイパビリティギャップではない。 Harness note: のレッドは lang-tracer/ハーネスの issue であり、真の非決定性はケース衛生の雑用です。どちらもビルダーのキューに属しません。

ドラフト本文には、ギャップを実行可能にするものを記載します。それらはすべてキャリブレーションからすでに手元にあります。

  • eval ケースをリンクとして — <base>/test-cases/<id>(リンクを共有し、裸の id は決して共有しないを参照)。これは再現手段であり、チケットの中で最も価値のある行です。
  • 何が失敗したか、逐語的に — 失敗した outcomeExpectation / processExpectation またはシナリオ名、そしてジャッジが述べた理由。言い換えではなく、正確なテキストが修正担当者が grep するものです。
  • 代わりにビルドが何をしたか — 具体的な欠陥(配線ミスの分岐、誤ったフィールドキー、欠落したゲート)、そして実際の証拠へのリンク: ソース会話(<base>/conversations/<threadId>)と eval インスタンス上のビルド済みスレッド + ワークフロー(<base-url>/assistant/<threadId>、<base-url>/workflow/<id>)。
  • 影響範囲、もし分かっていれば — クラスタのテーマまたはギャップの背後にある実際の会話数(<base>/clusters/<id>)が、「1 つのレッドケース」を優先順位付け可能なバグに変えるものです。 ケースページからチケットを作成して、リンクを設定してください。 <base>/test-cases/<id> ページにある Create Linear issue ダイアログを使うと、チケットを作成して同時にケースにリンクできます。このリンクがあると、後から get_linear_ticket_context <TEAM-N> を呼び出すだけで、ケース、シナリオ、ソースの会話、分析をまとめて取得できます。ケースとチケットのリンクを書き込めるのはこのUIからのみです。lang-tracerのMCPと /api/v1 では読み取りのみ可能です。そのため、別の方法(お使いのハーネスにLinear MCPがある場合はそれを使うか、Linearから直接)でチケットを作成した場合は、リンクされていないことを明確に伝え、ドライバーにケースページからリンクするよう依頼してください。その間に、出所情報が失われないよう、update_test_case を使って識別子とURLをケースの description に記載します。

コーパスをどちらの方向からも検索しやすいように、チケット情報を説明のプレフィックスに追加してください。Capability-gap finding: current build reds because <X> — a real builder bug (flips to a regression guard once fixed). Tracked in [<TEAM-123>](<ticket url>)。また、正しいビルドのもとでシナリオが失敗しているだけではなく、ビルド自体に誤りがある場合は、--set-kind capability_gap を指定して、その種別のスイートに追加してください(lang-tracerのスイートへの追加を参照)。

例

最小限のビルドケース:

{
  "description": "What this case tests.",
  "conversation": [{ "role": "user", "text": "<the build prompt>" }],
  "complexity": "medium",
  "tags": ["build", "<nodes>", "<concepts>"],
  "triggerType": "schedule",
  "outcomeExpectations": ["<a must-have any correct workflow satisfies>"],
  "executionScenarios": [
    {
      "name": "happy-path",
      "description": "<what this run exercises>",
      "dataSetup": "<what the external services return>",
      "successCriteria": "<observable proof the run succeeded>"
    }
  ]
}

複数ターンのディレクタースクリプトを含む、より完全なケース(質問されるまで値を明かさず、誤ったプランには異議を唱える):

{
  "description": "Scheduled GitHub-bugs digest to Slack. Repo and channel are withheld until the agent asks; the plan must filter to the 'bug' label before it's approved.",
  "conversation": [
    { "role": "user", "text": "Every weekday at 9am, fetch this week's open bugs from our GitHub repo and post a short summary to Slack." },
    { "role": "assistant", "text": "Which repo and which Slack channel should I use?" },
    { "role": "user", "text": [
        "[Withhold the repo and channel until the agent asks; then say the repo is 'acme/widgets' and the channel is '#eng-bugs'.",
        "When the agent shows a plan or setup card, reject it unless it filters issues to the 'bug' label — a digest of ALL issues is wrong. Once it filters to bugs, approve.]"
    ] }
  ],
  "messageBudget": 8,
  "complexity": "medium",
  "tags": ["behaviour", "build", "schedule", "http-request", "slack"],
  "triggerType": "schedule",
  "processExpectations": [
    "The agent asked for the repo and Slack channel before building, since the prompt named neither.",
    "The agent's final plan filtered issues to the 'bug' label — if its first attempt didn't, it corrected after the user pushed back rather than summarizing all issues."
  ],
  "outcomeExpectations": [
    "A Schedule Trigger runs the workflow on a recurring weekday-morning cadence.",
    "Open issues are fetched from GitHub (HTTP Request or GitHub node) and filtered to the 'bug' label before the summary is built.",
    "One Slack message summarizing the fetched bugs is posted to the #eng-bugs channel the user gave."
  ],
  "executionScenarios": [
    {
      "name": "posts-bug-digest",
      "description": "Three open bugs are returned; a summary is posted to Slack",
      "dataSetup": "The GitHub issues request returns three open issues labelled 'bug' ('Login 500', 'Timezone off by one', 'CSV export truncates'). The Slack postMessage call returns { \"ok\": true, \"ts\": \"1700000000.0003\" }.",
      "successCriteria": "The run completes without errors and posts one Slack message to #eng-bugs that references the three fetched bug titles."
    }
  ]
}

各要素の役割:

  • conversation[0] はそのままビルダーに送信します。 最初のターンは実際のプロンプトです。[director note] は絶対に入れないでください(そのまま漏れてしまいます)。
  • [角括弧で囲まれた] ターンは、ユーザープロキシ用のディレクタースクリプトです。発話ではなく、振る舞いを指定します。ここでは、質問されるまで値を明かさず、ラベルフィルターを見落としたプランには異議を唱えています。スクリプト全体を1つのターンにまとめ、順序はその中に記述してください(手順を順序付けるために、アシスタントの「完了」ターンを捏造しないでください。詳細は case-shapes.md を参照)。baseline LangTracerスイートにある applies-each-change-when-asked は、実際の良い例です。
  • dataSetup には外部サービスが返す内容のみを記述します。 ハーネスが制御するのはこのレイヤーです(以下を参照)。

dataSetup とモックレイヤー

このハーネスは、外部サービスへの送信 HTTP リクエストをインターセプトし、ノードの設定と API ドキュメントに基づいて LLM に応答させることでモックします。ノードの出力を直接設定することはできず、n8n の内部機能をモックすることもありません(Code/Set/Merge/IF/Switch はモックデータに対して実際に実行され、トリガーと DB ノードには LLM が生成したピンデータが設定されます)。そのため:

  • dataSetup には、各外部サービスが返す内容(「GitHub リクエストは bug ラベルの付いた 3 件の issue を返す」)と、実行前にワークフローの Data Tables に格納されている内容(「orders テーブルにはすでに注文 #42 がある」)を記述します。ノードの出力やその他の内部状態は記述しないでください。
  • 最も効果的なシナリオは、外部サービスの応答を扱うものです。ハーネスが最も忠実に再現できるのは、この部分です。
  • Data Table ノードは、シナリオに保存された行を使って実際に実行されます。各シナリオの前に、ハーネスはワークフローで使われるすべての Data Table を空にし、dataSetup からモックモデルが導き出した開始行を挿入します。その後、各 Data Table ノードが実際に実行されるため、変更検出/重複排除/「最終確認」シナリオを検証できます。また、読み取り時には同じ実行中に先に書き込まれた内容が確認できます。保存されている行を dataSetup に記述してください。行の createdAt または updatedAt の時刻(「1 週間前に作成された行」など)に依存するシナリオは作らないでください。これらのカラムはテーブルが設定するため、そのようなシナリオはハーネスによってフレームワークの問題として報告されます。生成される行は誘導されますが、バイト単位で正確ではないため、それらの値を厳密に検証しないでください。正確な行が必要な場合は、シナリオの seedDataTables にテーブルを宣言してください。ハーネスは指定どおりの行を挿入します。例外として、ワークフローの静的データに基づく重複排除/変更検出(removeItemsSeenInPreviousExecutions、$getWorkflowStaticData)はシードできません。静的データは実行ごとに空で始まるため、そのようなシナリオは常に赤になります(すべてが「新規」と判定されます)。シード可能な変更検出シナリオにするには、プロンプトで「状態をデータテーブルに保存する」と指定してください。そうしない場合は、静的データによる検証がハーネスの制限であることを受け入れ、ロジックを outcomeExpectations に記述してください。エージェントが独自に静的データを使った重複排除を選ぶこともあります。
  • **Message an Agent は、呼び出された Agent を実際のモデルで実行します。**ハーネスは Agent のツール、MCP サーバー、ウェブ検索をモックし、それらの呼び出しを呼び出し元ノードの下に記録します。Agent のメモリ、ベクトルストア、チャット連携は無効にします。現時点では OpenAI モデルのみがサポートされており、EVAL_OPENAI_API_KEY が設定されている場合に限ります(CI では設定されています)。Agent には openai/* モデルを設定し、openAiApi 認証情報を宣言してください。
  • モック生成に依存する厳密な件数(「投稿がちょうど 7 件」など)は検証しないでください。「元の 10 件より少ない」としてください。
ビルドの正誤にかかわらず、シナリオが赤になる既知のハーネスの制限

正しいビルドでも、これらの制限によって確実に赤になります。回避するためのシナリオを作らないでください。望む動作をシナリオに記述し、description で赤になるのはハーネスが原因であると記載してください(上記の「赤はシグナル」を参照)。

  • セットアップ時に空欄のまま残されるリソースロケーターフィールド(Google Sheets/Drive/Calendar などのノードピッカー)。エージェントはセットアップ時にユーザーが選択できるよう、ドキュメント/フォルダー/カレンダーの ID を正当に空欄のままにします。モックは代わりに __evalMockResource を設定し、ノードはそれを検索してクラッシュします(「Sheet with ID __evalMockResource not found」、または「Cannot read properties of undefined」)。成功パスがこのようなノードを通るシナリオは、後続処理が何も実行される前に必ず失敗します。
  • トリガーのピンデータと Data Table の初期行はLLM が生成するため、バイト単位では完全一致しません。 どちらも dataSetup で制御できます(上記のモック層のセクションを参照してください。トリガーの出力やテーブルの内容に影響を与えることはできます)。ただし、値は生成されるため、それらに基づいて正確な値や件数をアサートするシナリオは不安定です。正確な数値ではなく、形状/分岐/相対的な事実をアサートしてください。(ここに残る確実な失敗:ポーリング/フォームトリガーは、まれにまったく読み込めないことがあります。「workflow not found」。)
  • モックのレスポンス形状 — LLM が生成したモックレスポンスに実際のエンベロープが含まれず、後続の解析/フォーマットノードがクラッシュすることがあります。繰り返し発生し、再現性のある形状として、次のケースが想定されます(いずれも正しいビルドで失敗になります)。
    • OpenAI の構造化出力 — 以前は、モックが Responses エンベロープ(output[0].content[0].text)ではなく、単純な {content: "..."} を返していたため、Structured Output Parser/Information Extractor/Text Classifier が何も受け取れず、Model output doesn't fit required format でエラーになっていました。Responses エンベロープの正規化機能(PR #33578、マージ済み)により、フラットなエンベロープの場合は修正され、多くのケースで正常に実行されるようになりました。ただし、厳密な additionalProperties: false を宣言した構造化出力スキーマでは、残存する限定的な失敗が引き続き発生します。正規化された output ラッパー(およびモックが生成した subject/date などの余分なフィールド)が厳密なスキーマに違反するため、ノードは依然としてモックの出力を拒否します。以前の形式も残存する形式も、原因はビルドではなくモックです。正しさは outcomeExpectations で判定し、失敗はハーネスに起因すると記録してください。
    • Gmail のモックは、ヘッダーを payload.headers の中ではなく、トップレベルの大文字フィールド(From、Subject)として返します。そのため、送信者/件名を読み取る Code/Filter ノードは空文字列を受け取ります(たとえば、「drop no-reply senders」安全ゲートがすべてを通してしまいます)。シナリオ内での実行結果ではなく、outcomeExpectations でそのようなゲートの接続/順序をアサートしてください。
    • 発生頻度の低い API(Gemini のトップレベルの candidates など)でも、同様にエンベロープが欠落することがあります。
    • Google Drive の再開可能なアップロード — アップロード開始時のモックにセッション URL を含む Location ヘッダーがなく、Drive のファイルアップロードノードが 400 で失敗します。生成した画像/ファイルを Drive にアップロードするビルドは、これが原因で失敗する可能性があります。
  • エージェントツールノードは単独では実行できません。 AI-Agent のツールノード(toolHttpRequest や、execute メソッドを持たず supplyData のみを持つその他の LangChain ノード)は、エージェントが呼び出したときにのみ実行されます。ハーネスが直接実行すると、has a "supplyData" method but no "execute" method というエラーになります。HTTP-request ツールを使うチャットトリガー/AI-agent のビルドケースでは、ほぼ必ず失敗になります。ビルドは正しいため、エージェントとトリガー+モデル+ツールの接続関係を outcomeExpectations で判定し、実行失敗はハーネスに起因すると記録してください。
  • ポーリング/待機ループは早送りできません。 非同期ジョブを送信した後に完了までポーリングするワークフロー(生成 → ステータスを準備完了までポーリング → ダウンロード)では、モックのステータスを確実に進めることができず、Wait ノードは実時間で実行されるため、実行タイムアウト(framework_issue)でシナリオが失敗します。ビルドは正しいため、outcomeExpectations で判定し、失敗はハーネスに起因すると記録してください。
  • ビルドがタイムアウトし、スコア対象の結果がまったく出ない場合があります — 実行結果には BUILD FAILED: Run timed out と表示され、採点済みの期待値は 0 件になります。「仕様が大きすぎる」と決めつけないでください。より一般的な原因は、エージェントが確認のための ask-user の質問をし、未回答のままイテレーション単位のタイムアウトまでビルドが停止する、単一プロンプトのケースです(case-shapes.md を参照してください。自動承認されるのは確認のみです)。タイムアウトを仕様のサイズが原因だと判断する前に、原因を分類してください。レポート/トレースでエージェントの最終応答を読み(質問をしたか、不可能だと判断したか、あるいは本当に大規模なビルドを続けていたか)、ケースを単独で再実行します(--concurrency 1)。並行実行は停止したビルドを隠して上限に達する原因になる一方、正常なビルドをインスタンスごとのビルド上限(デフォルトは 4)で待ち行列に入れるだけでタイムアウトさせることもあります。そのため、単独実行すれば数秒で本当の原因が判明するか、そのまま正常に完了します。原因ごとに対応してください。単独実行で通るようになった場合 → ケースではなく、並行実行の競合が原因です(大きなバッチをレーンごとに分割してください。詳しくは running-evals.md を参照)。確認質問で停止した場合 → あらかじめ回答を記載したディレクターノートを含めて複数ターンのケースを作成します。本当に実現不可能な場合 → ビルドケースではなく、実現不可能性/誠実性に関する動作ケース(processExpectations)です。仕様が実際に大きすぎる場合 → タイムアウト自体が発見事項ですが、0 件が採点済みの失敗と誤解されないよう、その旨を記録してください。

outcomeExpectations と processExpectations

どちらも自然言語で記述されたアサーションで、同じ Sonnet 評価者によって採点され、それぞれが合格率の1単位としてカウントされます。評価対象は異なります。

  • outcomeExpectations — 最終的なワークフローを、ワークフロー JSON から評価します。ノードの選択と設定、接続トポロジーと分岐の配線、データ/式の参照、トリガーの実行間隔、ゲート条件をアサートします。ワークフローの JSON のみで評価できるため、ビルド済み/MCP の実行を含め、どの環境でも実行されます。
  • processExpectations — ビルド中のエージェントの振る舞いを、トランスクリプトから評価します。確認の質問をしたか(または同じ質問を繰り返さなかったか)、ユーザーに見える振る舞い、計画/承認の扱い、バッチ処理、訂正の反映、手順をアサートします。サブエージェントやツール名など、内部の仕組みはアサートしないでください(「期待値に内部の仕組みを含めない」を参照)。トランスクリプトが必要なため、ビルド済み/MCP の実行ではスキップされます。

目安:成果物に関するアサーションは outcome expectation、会話や、その過程でのエージェントの選択に関するアサーションは process expectation です。executionScenarios がないケースも有効なビルド専用ケースであり、これらの期待値とワークフローのチェックで評価されます。

各アサーションの適切な粒度

実際にエージェントに伝えられた内容に合わせて粒度を調整します。正しいビルドはすべて合格し、誤ったビルドや手抜きのビルドは不合格となり、会話で指定された内容のみをエージェントに求め、1回の実行における恣意的な選択までは求めないアサーションが適切です。失敗パターンは2つあります。

  • 厳しすぎる — 会話で選択の余地が残されていたのに、特定の選択を固定しています。プロンプトでベンダーが指定されていない場合、「flightaware.com を呼び出す」では、別の情報源を使った正しいビルドが不合格になります。ただし、会話で指定されていた場合は、それを固定してください — ユーザーが「Gmail 経由でメールして」と言ったなら、「Gmail ノード経由で送信する」は正しく、必須であり、厳しすぎるわけではありません。
  • 緩すぎる — 解決策になっていないものも合格してしまいます。「どこかからデータを取得する」では、データを取得しても比較しないワークフローが合格してしまいます。これでは、プロンプトで求められた変更検出を実現できたとは証明できません。

簡単な確認方法 — 置き換えテスト:ユーザーの要求を満たす妥当な代替実装でも合格できますか?以下は、情報源と通知チャネルが指定されていないフライトステータスのケースの例です。

判定アサーション理由
❌ 厳しすぎる「flightaware.com を呼び出す HTTP Request ノードがある」ベンダーは指定されていません。AeroDataBox を使った正しいビルドが不合格になります。(ユーザーが「FlightAware をスクレイピングして」と言っていたなら、これは正しいアサーションです。)
❌ 厳しすぎる「HTTP Request ノード経由で公開する」ファーストパーティノードが標準的な方法である場合に、通信手段を固定しています。たとえば、Instagram Graph API には Facebook Graph API ノードを使うのが正しい方法なので、それを使った正しいビルドが不合格になります。仕組みではなく、機能をアサートしてください(「Facebook Graph API ノードまたは HTTP Request 経由で Graph API を使い、Instagram に公開する」)。
❌ 緩すぎる「どこかからフライトデータを取得する」データを取得しても比較しないワークフローが合格してしまい、変更検出を実現できたとは証明できません。
✅ 適切「以前確認したステータスを保存し、新たに取得したステータスと比較する」中核となる動作であり、ベンダーや保存方法を変えても要件を満たします。
✅ 適切「変更が検出された分岐でのみ、条件によるゲートを通してアラートを送信する」ノードやチャネルを固定せずに、ゲートがあることを証明します。

会話で暗示されただけの意図(明言されていない、望ましい通知チャネルなど)は、outcomeExpectations ではなく processExpectations に記載してください。

期待値に内部の仕組みを含めない

期待値で記述するのは、ユーザーが目にするもの、または成果物に含まれるものです。Instance AI の内部構造を記述するものではありません。内部アーキテクチャは変わります。サブエージェント、ツール名、委任、計画の手順、実行順序は、いずれも変更される可能性があります。これらを明記した期待値は、設計が変わると、動作自体は正しいのに失敗します。また、別の方法で問題を解決するビルドも失敗するため、動作を検証できなくなります。

期待値に次の内容を含めないでください。

  • サブエージェント、委任、引き継ぎ(「サブエージェントが……を実行する」「オーケストレーターが……を委任する」)。
  • 内部ツール名や呼び出し回数(「build-agentを呼び出す」「search-nodesを2回呼び出す」)。
  • ユーザーから見えない内部ステージ、タスクリスト、手順の順序。
  • どのコンポーネントが結果を生成したか。

次の内容は明記してください。

  • 会話でユーザーが目にするもの:尋ねられた質問、承認用に提示された計画、最終回答での主張。
  • 成果物に含まれるもの:ワークフローやAgent、その設定、およびその動作。
  • ユーザーが求めた場合に限り、ユーザーが目にする順序(「構築する前に尋ねる」など)。

書き換えテスト。内部用語を削除し、その用語が生み出した結果を記述してください。

❌ 仕組みに関する記述✅ 意図に関する記述
「サブエージェントは成功を報告する前にワークフローを実行する」「最終回答では、ビルドで検証した結果のみを報告し、テストしていない手順が動作すると主張しない」
「オーケストレーターは委任する前にask-userを呼び出す」「エージェントは構築する前に、不足しているSlackチャンネルを尋ね、推測しない」
「ビルダーはトリガーとしてsearch-nodesを呼び出す」「ワークフローは、要求されたスケジュールに合致するトリガーから開始する」

2つ目のテストです。内部構造が異なるツールを使う1つのエージェントに置き換わったと想像してください。正しいビルドなら、引き続きテストに合格する必要があります。期待値が失敗する、または意味をなさなくなる場合は、書き換えてください。

特定のツールコントラクト(たとえば、ツールがエラーを報告する方法に関するリグレッション)を検証するケースなど、内部の仕組み自体が正当な対象となることもあります。こうしたケースはまれにしてください。理由はdescriptionに記述してください。同じケースで、意図に関する期待値と混在させないでください。

キャリブレーション手順は期待値ではありません。 ビルドが実行されたことや、サブエージェントが呼び出されたことをトランスクリプトで確認するのは、適切なキャリブレーションチェックです。自分の実行メモに残してください。ケースのJSONにはコピーしないでください。

堅牢な設計とハーネスの不安定さ

この2つは別のものです。混同しないでください。

  • 堅牢なアサーション設計(必ず行ってください)。 エージェントの指定されていない選択は、実行ごとに異なります。指定されていないソースに対するソース非依存のoutcomeExpectationsは、不安定さへの妥協ではなく、正しいアサーションです。
  • ハーネスの制限(隠さず明らかにしてください)。 dataSetupに関係なく、正しいビルドでも一部の経路は必ず失敗します。たとえば、Sheets/Drive/Calendarノードをクラッシュさせる空のリソースロケーターフィールドや、読み込みに失敗するポーリングトリガーなどです(上記の「既知のハーネスの制限」を参照)。状態を持つData Tableの読み取りは、もはやこの範疇ではありません。テーブルはdataSetupから開始し、ノードも実際に実行されます。信頼できないのは、正確な値のアサーションだけです。回避策は、回避することではなく、記録することです。制限をdescriptionに記載し、必ず失敗するシナリオをゲート付きティアから除外してください。 実行ごとにシナリオの成否が非決定的に変わる場合に限り、除去に値する本当のノイズとみなします。文書化されたハーネスの理由で確実に失敗するシナリオは、ハーネスでまだテストできないことを示す記録であり、そのまま残します。

ネガティブな実行シナリオ

成功するケースだけで終わらせず、プロンプトが実際に想定している場合に限り、適切に処理されることを確認してください。エージェントが構築するワークフローの多くは、デフォルトではエラー処理を追加しないため、「不正な入力でワークフローがクラッシュする」は、テストケースの不備ではなく、正当なビルダー上の問題です。適切な処理が期待される場合は、正しい動作が行われることだけでなく、誤った動作が行われないこと(「アラートが送信されない」「エラーなしで実行が完了する」)も表すように successCriteria を記述してください。対象となる状況は、空のソース、見つからないソース、ソースエラー/タイムアウト、不正な形式のレスポンスです。

常時実行されるワークフローチェックとの関係

ビルドが成功するたびに、7 つの観点(構造、トポロジー、パラメーターの正確性、意図、AI の接続、品質、セキュリティ)にわたる約 28 個の常時実行される二値チェックによる評価も行われます。 binaryChecks/checks/。 これらは対象範囲が広く、目につきにくいものです。二値チェックが名目上その項目をカバーしている場合でも、個別のケースに合わせた期待値を記述する価値はあります。ケース単位で名前を付けたアサーションなら、28 項目の評価基準に埋もれた 1 行よりも、この動作をはるかに明確に把握できます。汎用チェックがあるという理由だけで、アサーションを省略しないでください。

シナリオが失敗すると、検証ツールは失敗カテゴリ(builder_issue、mock_issue、framework_issue、verification_failure、build_failure)を割り当てます。これは確定情報ではなく手がかりとして扱ってください。実際のノード設定ミスに mock_issue が付けられた例や、実際のモックの問題にビルドエラーが付けられた例がありました。失敗の原因がテストケース、ビルド、ハーネスのどれにあるかを判断する前に、HTML レポートを開き、実際の実行内容と生成されたワークフローを確認してください。

実行結果

  • workflow-eval-report.html(実行時の .data/ ディレクトリ内) — 最も有用な表示です。ツール呼び出しを含む会話の全記録、ノードごとの実行トレース、実際にインターセプトされたリクエストとモックのレスポンス、Phase-1 のヒント、検証ツールの推論、ワークフローチェックの評価基準を確認できます。人間向けのレポートです。デバッグを始める際は、まずこれを確認してください。
  • eval-results.json — 構造化された結果(機械可読な成果物です。LangSmith がなくても、直接実行するループによって生成されます)。LLM やスクリプトでの解析に適しています。並行実行時にケースごとの結果を特定するには、ストリーミングされた詳細ログではなく、こちらを解析してください。レーンが複数ある場合、ログ行はケース間で入り交じるため、ストリーム内の [scenario] FAIL 行を特定のケースに確実に紐付けることはできません。信頼できるフィールドは、testCases[].buildSuccessCount、buildExpectationResultsPerRun[][].{pass,reason}、scenarios[].runs[].{passed,failureCategory,rootCause,execErrors} です。
  • eval-pr-comment.md — レンダリングされた PR コメントです(全体結果と回帰比較)。常に書き込まれます。

検証(実行前)

cd packages/@n8n/instance-ai
pnpm exec tsx -e "import {loadWorkflowTestCasesWithFiles} from './evaluations/data/workflows/index.ts'; console.log(loadWorkflowTestCasesWithFiles('<slug>')[0].fileSlug)"

# For a standalone Agent case:
pnpm exec tsx -e "import {loadAgentEvalTestCasesWithFiles} from './evaluations/data/agents/index.ts'; console.log(loadAgentEvalTestCasesWithFiles('<slug>')[0].fileSlug)"

lang-tracer スイートへのプッシュ

ケースがキャリブレーションされたら、JSON をコミットする代わりに、そのケース(およびその他のケース)をキュレーションされた lang-tracer スイートへプッシュします。eval:langtracer-push は REST API 経由で upsert します。スイートに存在しないケースを作成し、内容がずれたケースを更新し、それ以外は変更せず、決して削除しません。これは --source langtracer(スイートを取得する)の逆の操作です。

cd packages/@n8n/instance-ai
# <suite> is the slug the driver chose (e.g. baseline)
# preview first — no writes:
pnpm exec dotenvx run -f .env.eval -- pnpm eval:langtracer-push --suite <suite> --dry-run --changed
# then push (drop --dry-run):
pnpm exec dotenvx run -f .env.eval -- pnpm eval:langtracer-push --suite <suite> --changed
  • セレクター(少なくとも 1 つ必須 — 意図しない全件プッシュを防ぐ): 位置引数の <slugs...>(正確なファイル slug)、--changed(新規/未追跡 + ステージ済み + 変更済みの data/{workflows,agents}/*.json。未コミットのケースを書いた直後に最適)、--filter/--tier(--exclude を修飾子として使用)。
  • バリデーションは選択的です。 正確な slug と --changed は指定されたファイルのみを読み取るため、data/workflows 内の無関係な不正ファイル(新しいブランチで作成されたケース、書きかけのドラフト)がプッシュをブロックすることはありません。--filter は slug が一致するファイルのみをパースし、--tier は各ファイルの内部から tier を読み取るため、すべてをパースします。どちらの場合も、不正なファイルをパースすると ⚠ skipped invalid case file … と出力され、プッシュはそのファイルなしで続行されます。自分で指定したファイルが不正な場合は、プッシュは失敗します。
  • 複数の位置引数 slug を使う場合は pnpm をスキップし、スクリプトを直接呼び出してください。 pnpm eval:langtracer-push … slugA slugB は slug を 1 つの結合された引数("slugA slugB")として転送するため、どのケースファイルも一致せず、何もプッシュされません。pnpm 経由で位置引数を使わないセレクター(--changed)を使うか、スクリプトを直接実行して各 slug を個別の argv にしてください: pnpm exec dotenvx run -f .env.eval -- pnpm exec tsx evaluations/cli/langtracer-push.ts --suite <slug> <slug1> <slug2> …。
  • 環境変数: LANGTRACER_URL + LANGTRACER_API_KEY(lt_ ベアラー。1 つのキーで MCP + REST に使えます)— .env.eval に記述し、pnpm exec dotenvx 配下で実行してください。
  • オプション: --set-kind regression|capability_gap(デフォルトは regression。スイートの kind と一致する必要があります)、--contains-user-data(デフォルトは synthetic)。ビルドが正しい(結果の期待値がグリーン)ものの、ビルダーのバグによる現在レッドの実行シナリオを持つケースは、依然として regression ケースです — これは修正をガードするものです。capability_gap は、ビルド自体 が間違っているケースのために取っておいてください。
  • シナリオは更新時にも同期されます: PATCH /cases/:id は executionScenarios を名前で照合します(その場で更新、新規挿入、存在しないものを削除 — lang-tracer #48)。そのため、シナリオの編集は他のフィールドと同様に再プッシュされます。この変更より前の lang-tracer デプロイでは、このキーは黙って無視されます。プッシュしたシナリオの編集が反映されない場合は、lang-tracer の UI でシナリオを更新してください。
  • プッシュされたものを #ids ではなくリンクとして報告してください。 CLI は + created <slug> (#621) / ~ updated <slug> (#621, rev 3) とスイートのヘッダーを出力します — これが id であり、それ以上のものではありません。ドライバーに渡すものの中で、それぞれを <base>/test-cases/<id> に(そしてスイートを <base>/suites/<suiteId> に)変換して、作成したばかりのケースを探し回るのではなく開けるようにしてください。共有リンクを使い、裸の id は使わないを参照してください。
  • inline シードはケースとともにプッシュされます: ケース書き込み API はそれをそのまま保存するため、シード付きのケースは他のケースと同様にスイートに存在します。拒否されるのは replay ケースのみです — プッシュでは skipped: の下にリストされます。これは実行時に LangSmith トレースから再構築されるため、そのトレースがプルーニングまたは削除されると失われます。replay ケースもコミットしないでください。代わりに、成果物として永続的な synthetic ケースを導出してください。
プッシュしたケースをソースにリンクする(プロベナンスの手順 — 必ず実行してください)

ソースから作成したケースが、記録されている会話や発見にリンクされていない場合、それは孤立したケースです。6か月後には、どの実際の失敗を防ぐためのものなのか、誰にもわからなくなります。プッシュ CLI ではプロベナンスを引き継げないため、プッシュ後、新しいケース ID(プッシュ時に出力されます)を使って、lang-tracer MCP に対し update_test_case を1回呼び出し、ケースをリンクします。

  1. sourceThreadId — ソースとなる会話のスレッド ID(ケースがその会話内の特定の実行/ステップに紐づく場合は、さらに sourceRunId も指定します)。これは、バージョン別のすべての集計、会話フロート、?sourceThreadId= クエリが結合に使う DB レベルのリンクです。以下のタグや説明はその上に重ねる人間向けの情報であり、代わりにはなりません。スレッドは lang-tracer にすでにインポートされている必要があります(ソース取得の手順と同様に、そのスレッドに対して get_conversation_analysis を実行すれば十分です)。source_kind はサーバー側で導出され、このリンクを編集できるのは作成者が記述したケースのみです。プロモーション記録によるプロベナンスは変更できません。
  2. expectedBehavior — ケースが適用するルールを1段落で記述します。さらに、failurePattern — ターン参照を付けて、ソーススレッドで実際に何が起きたかを記述します。ケースが get_conversation_analysis から作成された場合は、その分析の extractedCases エントリーからコピーまたは調整してください。

次に add_case_tags を実行します(追加のみ。LT 側の tags 配列が対象であり、evalTags ではないため、評価の実行に引き継がれることはありません)。機能タグ(例:instruction-persistence)を追加してください。タグは積極的に正規化され(小文字化、ケバブケース化)、コロン形式のタグは何も通知されずに削除されます。また、スレッド ID とターン参照もケースの description に記載してください(作成者が「Sourced from thread 」と書く習慣が定着しています)。説明は、あらゆる場所に表示される唯一のフィールドです。

実行

Instance AI が有効で、サンドボックスが正常に動作している n8n インスタンスを起動し、評価の接続先に指定する必要があります。ハーネスには3つのモードがあります。直接ループ(LangSmith なし。eval-results.json のみ)、LangSmith(実験とリグレッション比較も記録)、事前構築(--prebuilt-workflows。既存のワークフローを採点)です。--filter <slug> / --tier <name> / --exclude で実行対象を絞り込めます。実行手順、並列レーン、ティア、ベースラインについては running-evals.md、すべてのフラグについてはREADMEを参照してください。ビルドを手作業で確認する場合は --keep-workflows を指定して実行してください。チェックポイントモードのキャリブレーションでは、この方法でドライバーがインスタンス上の構築済みスレッド(<base-url>/assistant/<threadId>)とワークフローを開きます。

その他の評価ハーネス(このスキルの対象外)

スタンドアロンの Agent ケースには、create-agent-builder-eval スキルを使用してください。ほかに3つのハーネスがあり、それぞれ独自のデータディレクトリと CLI を持っています。eval:subagent(ワークフロー構築の互換性コーパス。二値チェックで採点)、eval:discovery(最初のホップにおけるツール/ディスパッチのルーティングを検証。n8n サーバーは不要)、eval:pairwise(ai-workflow-builder.ee と直接比較するビルドの比較)です。これらの作成はここでは扱いません。同名の README セクションを参照してください。

コメント