mcpField
I
inngest-agent-evals

inngest-agent-evals

github.com/inngestテスト・品質29573 回インストール4 週間前に更新
リポジトリ

inngest-agent-evals について

inngest-agent-evalsは、Inngestの永続的なスコアリングとトレース機能を使い、本番環境のAIエージェントやワークフローを評価するスキルです。スコアリング、遅延評価、セッションのグループ化、実験を通じて品質分析や比較を案内します。

主な機能

  1. 01直接スコアリングと遅延スコアリング
  2. 02セッション単位の実行グループ化
  3. 03プロンプト、モデル、ツールの実験
  4. 04トレースと連携した評価ワークフロー
  5. 05成果と品質シグナルの設計

活用シーン

  1. 01実行中のエージェント出力の採点
  2. 02実行後のユーザーフィードバック記録
  3. 03プロンプトやモデルの比較
  4. 04会話やチケット別の関連実行の調査

MCP 対応

Inngestでの本番評価ワークフローを説明しますが、MCP機能については記載されていません。

インストール

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

$npx skills add https://github.com/inngest/inngest-skills --skill inngest-agent-evals

インストール後、Claude Code で/inngest-agent-evalsを入力して読み込まれたか確認します。

詳細

Inngest Agent Evals

このスキルは、ユーザーが本番環境で AI エージェントや AI ワークフローを評価したい場合、スコアリングを追加したい場合、プロンプト・モデル・ツールを比較したい場合、関連する実行をグループ化したい場合、またはエージェントの結果が良かった理由や悪かった理由をデバッグしたい場合に使用します。

Agent Evals は独立したパッケージではありません。Inngest の関数、耐久性のあるステップ、スコア、遅延スコアラー、セッション、トレース、実験、Insights から構築された本番評価ワークフローです。

正規のリファレンス

正確な API の詳細が重要な場合は、まず公開ドキュメントを確認してください。

判断フロー

メカニズムではなく、結果から始めてください。

  1. プロダクトまたは品質のシグナルを特定します。役立ったクリック、チケットの解決、コンバージョン、ガードレールの通過、検索品質、モデルの信頼度、レイテンシ、コスト、人間によるレビュー、または LLM-as-judge の結果などです。
  2. シグナルがいつ現れるかを判断します。
    • 実行中: step.score() または inngest.score() を使った直接スコアリングを使用します。
    • 実行後: createScorer() と defer() を使った遅延スコアリング、または明示的な inngest.score({ runId }) を使用します。
  3. 人間が関連する作業をどのように確認するかを判断します。
    • conversation_id、ticket_id、agent_run_id、import_id など、繰り返し出現する高カーディナリティの識別子には meta.sessions を使用します。
    • アドホックな履歴分析、低カーディナリティのフィルター、SQL 集計、広範な調査には Insights を使用します。
  4. バリアントを比較するかどうかを判断します。
    • プロンプト、モデル、プロバイダー、ツール、ワークフロー、ロールアウトの比較には group.experiment() を使用します。
    • スコアを選択されたバリアントに帰属させる必要がある場合は、遅延スコアまたは後続のスコアに experimentRef を渡します。
  5. トレーサビリティを維持します。
    • モデル呼び出し、ツール呼び出し、待機、データベース書き込み、スコアリング作業を耐久性のあるステップに保ち、トレースがスコアの理由を説明できるようにします。

セットアップ要件

  • TypeScript SDK v4 を使用し、最新の SDK をインストールします: npm install inngest@latest。
  • スコアリングと遅延スコアリングはベータ版 API です。ユーザー向けの例を公開する前に、現在のインポートをドキュメントと照合して確認してください。
  • step.score() は Inngest クライアントに inngest/experimental の scoreMiddleware() を必要とします。
  • createScorer() は inngest/experimental からインポートし、スコアラーは他の関数とともに serve ハンドラーに登録する必要があります。
  • TypeScript SDK を通じて送信されるセッションには v4.7.0 以降が必要です。
  • ステップ実験では、group.experiment() と inngest の experiment ヘルパーに v4.8.0 以降が必要です。

クライアントの形:

import { Inngest } from "inngest";
import { scoreMiddleware } from "inngest/experimental";

export const inngest = new Inngest({
  id: "support-agent",
  middleware: [scoreMiddleware()],
});

セッション

イベントがユーザーフロー、会話、チケット、インポート、または誰かが繰り返し確認するエージェントタスクに属する場合は、セッションを追加します。

await inngest.send({
  name: "support/ticket.created",
  data: {
    ticketId: "tk_123",
    message: "I can't sign in.",
  },
  meta: {
    sessions: {
      ticket_id: "tk_123",
    },
  },
});

ルール:

  • 安定した、シークレットではない、高カーディナリティの ID を使用します。
  • キーは conversation_id のような汎用的なものにし、実際の ID は値に入れます。
  • 下流の実行が同じセッションに参加する必要がある場合は、step.invoke() と step.sendEvent() を通じてセッションを明示的に渡します。
  • environment: prod のようなラベルにはセッションを使用しないでください。そのスタイルのフィルタリングには Insights を使用します。

直接スコアリング

関数の実行中にスコアが判明する場合は、直接スコアリングを使用します。

直接スコアリングに適した例:

  • ガードレールの合否
  • JSON の妥当性
  • 検索の信頼度
  • ツールの成功
  • モデルの信頼度
  • インラインの LLM-as-judge の結果
export const answerTicket = inngest.createFunction(
  {
    id: "answer-ticket",
    triggers: [{ event: "support/ticket.created" }],
  },
  async ({ event, step }) => {
    const answer = await step.run("generate-answer", () =>
      generateAnswer(event.data.ticketId)
    );

    const passed = await step.run("check-answer", () => validateAnswer(answer));

    await step.score("score-answer-quality", {
      name: "answer-quality",
      value: passed,
    });

    return { answer, passed };
  }
);

安定したスコア名を使用してください。スコア名を変更すると、別のメトリクスが作成されます。 スコア値は有限の数値またはブール値である必要があります。

遅延スコアリング

有用なシグナルが親ワークフローの終了後に到着する場合は、遅延スコアリングを使用します。たとえば、ユーザーフィードバック、コンバージョン、チケットの再オープン、リテンション、または時間のかかる LLM-as-judge の実行などです。

import { createScorer } from "inngest/experimental";
import { z } from "zod";

export const feedbackScorer = createScorer(
  inngest,
  {
    id: "support-feedback-scorer",
    schema: z.object({ ticketId: z.string() }),
  },
  async ({ event, step }) => {
    const feedback = await step.waitForEvent("wait-for-feedback", {
      event: "support/feedback.received",
      timeout: "7d",
      if: `async.data.ticketId == '${event.data.ticketId}'`,
    });

    return {
      name: "user-feedback",
      value: feedback?.data.helpful ? 1 : 0,
    };
  }
);

生成側の関数からトリガーします:

async ({ event, step, defer }) => {
  const answer = await step.run("generate-answer", () =>
    generateAnswer(event.data.ticketId)
  );

  defer("score-feedback", {
    function: feedbackScorer,
    data: { ticketId: event.data.ticketId },
  });

  return { answer };
}

ガードレール:

  • スコアラーは serve({ functions: [...] }) で登録します。
  • defer() は fire-and-forget です。await しないでください。
  • 遅延スコアラーの場合、親の実行は自動的に紐付けられます。
  • シグナルがタイムアウトした場合は、プロダクトのセマンティクスに基づいて、デフォルトのスコアまたは null を返します。

実験

プロンプト、モデル、プロバイダー、ツール、ワークフローの書き換え、または運用設定を実際のトラフィックと比較する場合は、group.experiment() を使用します。

import { experiment } from "inngest";

const { result, variant, experimentRef } = await group.experiment(
  "answer-style",
  {
    variants: {
      concise: () => step.run("answer-concise", () => answerConcise(event.data)),
      detailed: () =>
        step.run("answer-detailed", () => answerDetailed(event.data)),
    },
    select: experiment.bucket(event.data.accountId, {
      weights: { concise: 50, detailed: 50 },
    }),
  }
);

選択戦略:

  • 実行レベルのトラフィック分割には experiment.weighted()。
  • ユーザー、アカウント、またはテナントが通常同じ体験を維持すべき場合は experiment.bucket(stableId, { weights })。
  • 割り当てがデータベース、フラグサービス、またはロールアウトテーブルから来る場合は experiment.custom()。
  • テスト中または勝者を選択した後に1つのバリアントを強制するには experiment.fixed()。

ルール:

  • 各バリアントのコールバックは、少なくとも1つの step.* ツールを呼び出す必要があります。
  • 実験 ID とバリアント名はトレースに表示されるため、安定させてください。
  • 選択されたバリアントは、リトライとリプレイのためにメモ化されます。
  • 後で別のプロセスが選択されたバリアントをスコアリングする場合は、experimentRef と親の実行 ID を永続化してください。

バリアントを遅延スコアリングする場合は、ref を渡します:

defer("score-answer-feedback", {
  function: feedbackScorer,
  data: { ticketId: event.data.ticketId },
  experiment: experimentRef,
});

後続の実行から明示的にスコアリングする場合:

await inngest.score.experiment({
  name: "clickthrough",
  value: 1,
  experiment: experimentRef,
  runId: originalRunId,
});

ブラウンフィールド移行

既存のアプリに Agent Evals を追加する場合:

  1. 既存のエージェント/ワークフローの出力と、ユーザーがそれらに対して行動する場所を探します。
  2. ドメインにすでに存在する永続的な識別子を見つけます: 会話 ID、チケット ID、アカウント ID、インポート ID、実行 ID、またはレコメンド ID。
  3. 相関を中心にダッシュボードを構築する前に、イベントプロデューサーに meta.sessions を追加します。
  4. 結果がすでに判明している場所に、1つの直接スコアを追加します。
  5. シグナルイベントが存在するか、クリーンに発行できるようになってから、遅延スコアラーを追加します。
  6. group.experiment() は、一度に1つの分離された意思決定の周囲にのみ追加します。
  7. スコアリングが新しいパスを証明するまで、古いプロンプト/モデル/ツールの動作を安定させておきます。

リポジトリに多数の候補ワークフローがある場合は、まず inngest-brownfield-audit を使用してください。作業が AgentKit または耐久性のあるエージェントワークフローである場合は、このスキルとともに inngest-agents を使用してください。

アンチパターン

  • 結果メトリクスに名前を付ける前にプロンプト実験を開始すること。
  • 不安定な名前、文字列、オブジェクト、NaN、または Infinity でスコアリングすること。
  • scoreMiddleware() を忘れて、step.score() が存在しないと仮定すること。
  • スコアラーが耐久性のある待機を必要とする場合に、外部キューでスコアラーを実行すること。
  • 実験の割り当てをメモリ内にのみ保持すること。
  • 元の runId なしで、後続の実行から実験バリアントをスコアリングすること。
  • カーディナリティの低いラベルや機密性の高い個人データにセッションを使用すること。
  • モデル/ツールの作業をステップの外に残し、トレースがスコアを説明できなくすること。

検証

  • Inngest クライアント、関数、スコアラースキーマ、および serve ハンドラーを型チェックします。
  • スコアラーが serve エンドポイントに登録されていることを確認します。
  • プロデューサーが期待される meta.sessions を含むイベントを発行することをテストします。
  • 直接スコアが安定した名前と有限の数値/ブール値を使用することをテストします。
  • 遅延スコアラーのタイムアウト動作をテストします。
  • 実験については、各バリアントのコールバックにステップ処理が含まれていること、および必要に応じて後続のスコアリングが experimentRef と元の実行 ID を受け取ることをテストします。
  • 可能であれば、Inngest 開発サーバーを実行し、ダッシュボードまたは利用可能な CLI/API ツールを通じて、トレース、セッション、スコア、および実験のバリアント選択を検査します。

コメント