T-CREATOR

Dify の評価基盤を俯瞰:観測・指標・人手評価の設計ポイント

Dify の評価基盤を俯瞰:観測・指標・人手評価の設計ポイント

生成 AI アプリケーションの開発では、モデルの性能を正確に評価し、継続的に改善することが成功の鍵となります。Dify は LLM アプリケーション開発プラットフォームとして、豊富な評価基盤を提供していますが、その全体像を把握し、適切に設計することは容易ではありません。

本記事では、Dify における評価基盤の全体像を俯瞰し、観測(Observability)、指標(Metrics)、人手評価(Human Evaluation)という 3 つの柱について、設計ポイントと実装方法を詳しく解説します。これにより、AI アプリケーションの品質向上と運用改善を実現できるでしょう。

背景

Dify における評価の重要性

Dify は、LLM を活用したアプリケーションを迅速に構築できるプラットフォームです。チャットボット、エージェント、ワークフローなど、さまざまな AI アプリケーションを GUI 上で設計できる点が特徴ですね。

しかし、AI アプリケーションは従来のソフトウェアと異なり、確定的な動作をしません。同じ入力でも異なる出力が生成される可能性があり、品質の定義や測定が難しいという特性を持ちます。

このため、継続的な評価と改善のサイクルを回すことが、AI アプリケーションの成功に不可欠なのです。

評価基盤の 3 つの柱

Dify の評価基盤は、以下の 3 つの要素で構成されています。

#要素目的特徴
1観測(Observability)システム動作の可視化ログ、トレース、メトリクスの収集
2指標(Metrics)定量的な品質評価自動計算される客観的指標
3人手評価(Human Evaluation)主観的な品質評価人間による判断とフィードバック

これら 3 つの要素を組み合わせることで、多角的な評価が可能になります。

以下の図は、評価基盤の全体像を示しています。

mermaidflowchart TB
    user["ユーザー"] -->|リクエスト| app["Dify アプリケーション"]
    app -->|実行| llm["LLM モデル"]
    llm -->|レスポンス| app
    app -->|結果| user

    app -->|ログ出力| obs["観測基盤<br/>Logs/Traces"]
    app -->|メトリクス計算| metrics["指標基盤<br/>自動評価"]
    user -->|評価入力| human["人手評価<br/>フィードバック"]

    obs -->|分析| dashboard["ダッシュボード"]
    metrics -->|集計| dashboard
    human -->|集約| dashboard

    dashboard -->|改善施策| app

図で理解できる要点:

  • ユーザーからのリクエストが Dify アプリケーションを経由して LLM に渡される流れ
  • 観測、指標、人手評価の 3 つが並行して動作し、ダッシュボードで統合される仕組み
  • 評価結果がアプリケーション改善にフィードバックされる循環構造

課題

AI アプリケーション評価の難しさ

生成 AI アプリケーションの評価には、従来のソフトウェアにはない特有の課題があります。

非決定的な動作

LLM は同じ入力に対して毎回異なる出力を生成する可能性があります。温度パラメータや Top-p などの設定により、出力の多様性が変化するためです。

このため、単純なユニットテストのようなアプローチでは品質を保証できません。

品質基準の曖昧さ

「良い回答」の定義は、ユースケースによって大きく異なります。正確性、有用性、安全性、トーン、長さなど、複数の観点が存在し、それらの優先順位も状況により変わるでしょう。

コストと時間の制約

すべての出力を人間が評価するには、膨大な時間とコストがかかります。一方で、完全に自動化された評価だけでは、微妙なニュアンスや文脈を捉えきれない可能性があります。

以下の図は、評価における課題の関係性を示しています。

mermaidflowchart LR
    challenge1["非決定的な<br/>出力"] -->|結果| difficulty["評価の<br/>困難さ"]
    challenge2["曖昧な<br/>品質基準"] -->|影響| difficulty
    challenge3["コストと<br/>時間の制約"] -->|制限| difficulty

    difficulty -->|要求| solution1["自動評価<br/>(指標)"]
    difficulty -->|要求| solution2["人手評価<br/>(サンプリング)"]
    difficulty -->|要求| solution3["観測基盤<br/>(モニタリング)"]

図で理解できる要点:

  • 3 つの主要課題が評価の困難さを生み出している構造
  • それぞれの課題に対応する解決策の関係性

運用上の課題

評価基盤を運用する上でも、いくつかの課題があります。

#課題詳細影響
1データ量の増加ログやトレースが膨大になるストレージコストと分析の困難さ
2評価者の負担人手評価に時間がかかるフィードバックサイクルの遅延
3指標の選定適切な評価指標が不明確誤った改善方向への誘導
4リアルタイム性問題の早期発見が難しいユーザー体験の低下

これらの課題に対処するためには、体系的な評価基盤の設計が必要です。

解決策

観測(Observability)の設計

観測基盤は、アプリケーションの動作を可視化し、問題の早期発見と原因究明を支援します。

ログの構造化

Dify では、すべての会話とワークフロー実行がログとして記録されます。これらのログを効果的に活用するには、構造化が重要です。

以下は、ログデータの構造例です。

typescript// ログエントリの型定義
interface DifyLogEntry {
  // 基本情報
  conversationId: string; // 会話ID
  messageId: string; // メッセージID
  timestamp: Date; // タイムスタンプ

  // リクエスト情報
  input: {
    query: string; // ユーザー入力
    variables?: Record<string, any>; // 変数
  };

  // レスポンス情報
  output: {
    answer: string; // AI回答
    tokens: number; // トークン数
    latency: number; // レイテンシ(ms)
  };

  // メタデータ
  metadata: {
    modelName: string; // 使用モデル
    appId: string; // アプリケーションID
    userId?: string; // ユーザーID
  };
}

このように型定義を行うことで、ログデータの一貫性が保たれます。

構造化されたログは、後の分析やメトリクス計算に活用できるでしょう。

トレースの活用

複雑なワークフローでは、どのノードでどれだけ時間がかかったかを追跡することが重要です。

typescript// トレース情報の型定義
interface WorkflowTrace {
  workflowId: string; // ワークフローID
  executionId: string; // 実行ID
  startTime: Date; // 開始時刻
  endTime: Date; // 終了時刻
  totalDuration: number; // 総実行時間(ms)

  // ノード単位のトレース
  nodeTraces: NodeTrace[];
}

interface NodeTrace {
  nodeId: string; // ノードID
  nodeType: string; // ノードタイプ(LLM/Tool/Condition等)
  startTime: Date; // 開始時刻
  duration: number; // 実行時間(ms)
  status: 'success' | 'error'; // 実行ステータス
  error?: string; // エラーメッセージ
}

トレース情報により、ボトルネックの特定やエラーの原因究明が容易になります。

メトリクスの収集

観測基盤では、以下のようなメトリクスをリアルタイムで収集します。

typescript// メトリクスコレクターの実装例
class DifyMetricsCollector {
  // レイテンシの記録
  recordLatency(latency: number, appId: string): void {
    // レイテンシをヒストグラムとして記録
    this.histogram.observe(
      { app_id: appId, metric: 'response_latency' },
      latency
    );
  }

  // トークン使用量の記録
  recordTokenUsage(
    tokens: number,
    modelName: string
  ): void {
    // トークン数をカウンターとして記録
    this.counter.inc(
      { model: modelName, metric: 'token_usage' },
      tokens
    );
  }

  // エラー率の記録
  recordError(appId: string, errorType: string): void {
    // エラーをカウンター として記録
    this.counter.inc(
      { app_id: appId, error_type: errorType },
      1
    );
  }
}

これらのメトリクスは、ダッシュボードでリアルタイムに監視できます。

以下の図は、観測基盤のデータフローを示しています。

mermaidflowchart LR
    request["リクエスト"] -->|入力| app["Dify App"]
    app -->|実行| workflow["ワークフロー<br/>実行"]

    workflow -->|ログ出力| logger["ログ<br/>コレクター"]
    workflow -->|トレース出力| tracer["トレース<br/>コレクター"]
    workflow -->|メトリクス出力| metrics["メトリクス<br/>コレクター"]

    logger -->|保存| storage[("ログ<br/>ストレージ")]
    tracer -->|保存| storage
    metrics -->|集計| timeseries[("時系列<br/>データベース")]

    storage -->|クエリ| analytics["分析<br/>ツール"]
    timeseries -->|可視化| analytics

図で理解できる要点:

  • ワークフロー実行から 3 種類のデータ(ログ、トレース、メトリクス)が並行して収集される
  • それぞれ適切なストレージに保存され、分析ツールで統合的に活用される

指標(Metrics)の設計

指標は、AI アプリケーションの品質を定量的に評価するための重要な要素です。

自動評価指標の種類

Dify では、複数の自動評価指標を活用できます。

#指標名評価対象計算方法
1BLEU スコアテキスト類似度n-gram の一致率
2ROUGE スコア要約品質単語の重複率
3Perplexity言語モデル性能次トークン予測の確信度
4Semantic Similarity意味的類似度埋め込みベクトルの類似度
5Answer Relevance回答の適切性LLM による評価

これらの指標を組み合わせることで、多面的な評価が可能になります。

LLM-as-a-Judge パターン

より高度な評価として、LLM 自身を評価者として使用する「LLM-as-a-Judge」パターンがあります。

typescript// LLM-as-a-Judge による評価の実装例
interface EvaluationCriteria {
  name: string; // 評価基準名
  description: string; // 基準の説明
  scale: number; // スケール(1-5など)
}

class LLMJudge {
  // 回答を評価する
  async evaluateAnswer(
    query: string,
    answer: string,
    criteria: EvaluationCriteria[]
  ): Promise<EvaluationResult> {
    // 評価プロンプトの構築
    const prompt = this.buildEvaluationPrompt(
      query,
      answer,
      criteria
    );

    // LLMに評価を依頼
    const evaluation = await this.llm.complete(prompt);

    // 評価結果をパース
    return this.parseEvaluation(evaluation);
  }
}

評価プロンプトには、明確な基準とスケールを含めることが重要です。

typescript// 評価プロンプトの構築
private buildEvaluationPrompt(
  query: string,
  answer: string,
  criteria: EvaluationCriteria[]
): string {
  // 評価基準をフォーマット
  const criteriaText = criteria.map(c =>
    `- ${c.name}: ${c.description} (1-${c.scale})`
  ).join('\n');

  return `
以下の回答を評価してください。

【質問】
${query}

【回答】
${answer}

【評価基準】
${criteriaText}

各基準について、スコアと理由を JSON 形式で出力してください。
  `.trim();
}

このアプローチにより、柔軟で文脈に応じた評価が可能になるでしょう。

カスタム指標の実装

ユースケースに応じて、独自の評価指標を実装することも重要です。

typescript// カスタム指標の実装例:回答の安全性チェック
class SafetyScoreCalculator {
  private forbiddenPatterns: RegExp[];

  constructor() {
    // 禁止パターンの定義
    this.forbiddenPatterns = [
      /個人情報.*漏洩/,
      /不適切.*内容/,
      // その他のパターン
    ];
  }

  // 安全性スコアを計算(0-1の範囲)
  calculateSafetyScore(answer: string): number {
    let violations = 0;

    // 各パターンをチェック
    for (const pattern of this.forbiddenPatterns) {
      if (pattern.test(answer)) {
        violations++;
      }
    }

    // スコアを計算(違反が多いほど低い)
    const score = Math.max(0, 1 - violations * 0.2);

    return score;
  }
}

ドメイン固有の要件に合わせた指標を設計することで、より実用的な評価が実現します。

以下の図は、指標計算のフローを示しています。

mermaidflowchart TB
    output["LLM 出力"] -->|入力| eval["評価エンジン"]

    eval -->|計算| auto["自動指標<br/>BLEU/ROUGE等"]
    eval -->|計算| llm_judge["LLM-as-a-Judge<br/>評価"]
    eval -->|計算| custom["カスタム指標<br/>安全性等"]

    auto -->|集約| aggregator["スコア<br/>集約"]
    llm_judge -->|集約| aggregator
    custom -->|集約| aggregator

    aggregator -->|保存| db[("評価<br/>データベース")]
    db -->|分析| report["評価<br/>レポート"]

図で理解できる要点:

  • LLM 出力が評価エンジンに渡され、3 種類の指標で並行評価される
  • すべての指標がスコア集約で統合され、データベースに保存される

人手評価(Human Evaluation)の設計

自動評価だけでは捉えきれない品質を、人間の判断で補完します。

評価タスクの設計

効率的な人手評価のためには、明確なタスク設計が必要です。

typescript// 評価タスクの型定義
interface HumanEvaluationTask {
  taskId: string; // タスクID
  conversationId: string; // 会話ID

  // 評価対象
  context: {
    query: string; // ユーザー質問
    answer: string; // AI回答
    metadata?: Record<string, any>; // メタデータ
  };

  // 評価項目
  evaluationItems: EvaluationItem[];

  // ステータス
  status: 'pending' | 'completed' | 'skipped';
  assignedTo?: string; // 担当者
  completedAt?: Date; // 完了日時
}

評価項目は、明確な質問と選択肢で構成します。

typescript// 評価項目の型定義
interface EvaluationItem {
  itemId: string; // 項目ID
  question: string; // 評価質問
  type: 'scale' | 'choice' | 'text'; // 回答タイプ

  // スケール評価の場合
  scale?: {
    min: number; // 最小値
    max: number; // 最大値
    labels: string[]; // ラベル
  };

  // 選択肢評価の場合
  choices?: string[];

  // 回答
  answer?: any;
  comment?: string; // コメント
}

具体的な評価項目の例を示します。

typescript// 評価項目の設定例
const evaluationItems: EvaluationItem[] = [
  {
    itemId: 'relevance',
    question: '回答は質問に適切に答えていますか?',
    type: 'scale',
    scale: {
      min: 1,
      max: 5,
      labels: [
        '全く適切でない',
        'やや不適切',
        'どちらとも言えない',
        'やや適切',
        '非常に適切',
      ],
    },
  },
  {
    itemId: 'accuracy',
    question: '回答の内容は正確ですか?',
    type: 'choice',
    choices: [
      '正確',
      '一部不正確',
      '不正確',
      '判断できない',
    ],
  },
  {
    itemId: 'tone',
    question: '回答のトーンは適切ですか?',
    type: 'scale',
    scale: { min: 1, max: 5, labels: [] },
  },
];

このように、具体的で回答しやすい質問を用意することが重要です。

サンプリング戦略

すべての会話を評価するのは現実的ではないため、適切なサンプリング戦略が必要です。

typescript// サンプリング戦略の実装例
class EvaluationSampler {
  // ランダムサンプリング
  randomSample(
    conversations: Conversation[],
    sampleSize: number
  ): Conversation[] {
    // シャッフルして指定数を取得
    return this.shuffle(conversations).slice(0, sampleSize);
  }

  // 層化サンプリング
  stratifiedSample(
    conversations: Conversation[],
    sampleSize: number,
    stratifyBy: keyof Conversation
  ): Conversation[] {
    // 層ごとにグループ化
    const groups = this.groupBy(conversations, stratifyBy);

    // 各層から均等にサンプリング
    const samplesPerGroup = Math.floor(
      sampleSize / Object.keys(groups).length
    );

    return Object.values(groups).flatMap((group) =>
      this.randomSample(group, samplesPerGroup)
    );
  }
}

層化サンプリングを使用することで、偏りの少ない評価が可能になります。

typescript// 優先度ベースのサンプリング
class PrioritySampler {
  // 優先度を計算
  calculatePriority(conversation: Conversation): number {
    let priority = 0;

    // エラーがあれば優先度を上げる
    if (conversation.hasError) {
      priority += 10;
    }

    // レイテンシが高ければ優先度を上げる
    if (conversation.latency > 5000) {
      priority += 5;
    }

    // ユーザーフィードバックが否定的なら優先度を上げる
    if (conversation.userFeedback === 'negative') {
      priority += 8;
    }

    return priority;
  }

  // 優先度順にサンプリング
  prioritySample(
    conversations: Conversation[],
    sampleSize: number
  ): Conversation[] {
    // 優先度でソート
    return conversations
      .sort(
        (a, b) =>
          this.calculatePriority(b) -
          this.calculatePriority(a)
      )
      .slice(0, sampleSize);
  }
}

問題のある会話を優先的に評価することで、効率的な品質改善が可能です。

フィードバックループの構築

評価結果を改善に活かすためのフィードバックループを構築します。

typescript// フィードバック集約の実装
class FeedbackAggregator {
  // 評価結果を集約
  aggregateEvaluations(
    evaluations: HumanEvaluationTask[]
  ): AggregatedFeedback {
    // 項目ごとの平均スコアを計算
    const scoresByItem =
      this.calculateAverageScores(evaluations);

    // 問題のあるパターンを特定
    const issues = this.identifyIssues(evaluations);

    // 改善提案を生成
    const suggestions = this.generateSuggestions(
      scoresByItem,
      issues
    );

    return {
      scoresByItem,
      issues,
      suggestions,
      totalEvaluations: evaluations.length,
    };
  }
}

集約されたフィードバックは、プロンプト改善やモデル選択に活用できます。

以下の図は、人手評価のフローを示しています。

mermaidflowchart TB
    conv["会話ログ"] -->|サンプリング| sampler["サンプリング<br/>エンジン"]

    sampler -->|ランダム| random["ランダム<br/>サンプル"]
    sampler -->|層化| stratified["層化<br/>サンプル"]
    sampler -->|優先度| priority["優先度<br/>サンプル"]

    random -->|タスク作成| task["評価<br/>タスク"]
    stratified -->|タスク作成| task
    priority -->|タスク作成| task

    task -->|割り当て| evaluator["評価者"]
    evaluator -->|回答| result["評価結果"]

    result -->|集約| aggregator["フィードバック<br/>集約"]
    aggregator -->|改善| improve["プロンプト/<br/>モデル改善"]

図で理解できる要点:

  • 3 種類のサンプリング手法から評価タスクが生成される
  • 評価者による回答が集約され、改善アクションにつながる循環

具体例

評価基盤の実装例

実際の Dify アプリケーションに評価基盤を実装する例を示します。

プロジェクト構成

以下のようなディレクトリ構成で評価基盤を実装します。

typescript// プロジェクト構成
/*
evaluation-platform/
├── src/
│   ├── observability/      # 観測関連
│   │   ├── logger.ts       # ログ収集
│   │   ├── tracer.ts       # トレース収集
│   │   └── metrics.ts      # メトリクス収集
│   ├── metrics/            # 指標関連
│   │   ├── auto-eval.ts    # 自動評価
│   │   ├── llm-judge.ts    # LLM評価
│   │   └── custom.ts       # カスタム指標
│   ├── human-eval/         # 人手評価関連
│   │   ├── sampler.ts      # サンプリング
│   │   ├── task.ts         # タスク管理
│   │   └── aggregator.ts   # 集約
│   └── dashboard/          # ダッシュボード
│       └── api.ts          # API
└── config/
    └── evaluation.yaml     # 設定ファイル
*/

統合クラスの実装

各コンポーネントを統合する評価基盤クラスを実装します。

typescript// 評価基盤の統合クラス
import { Logger } from './observability/logger';
import { MetricsCollector } from './observability/metrics';
import { AutoEvaluator } from './metrics/auto-eval';
import { LLMJudge } from './metrics/llm-judge';
import { EvaluationSampler } from './human-eval/sampler';

class DifyEvaluationPlatform {
  private logger: Logger;
  private metrics: MetricsCollector;
  private autoEval: AutoEvaluator;
  private llmJudge: LLMJudge;
  private sampler: EvaluationSampler;

  constructor(config: EvaluationConfig) {
    // 各コンポーネントを初期化
    this.logger = new Logger(config.logging);
    this.metrics = new MetricsCollector(config.metrics);
    this.autoEval = new AutoEvaluator(config.autoEval);
    this.llmJudge = new LLMJudge(config.llmJudge);
    this.sampler = new EvaluationSampler(config.sampling);
  }
}

会話処理時に評価を実行するメソッドを追加します。

typescript// 会話の評価処理
async evaluateConversation(
  conversation: Conversation
): Promise<EvaluationReport> {
  // 1. ログの記録
  await this.logger.log({
    conversationId: conversation.id,
    query: conversation.query,
    answer: conversation.answer,
    timestamp: new Date()
  });

  // 2. メトリクスの収集
  this.metrics.recordLatency(
    conversation.latency,
    conversation.appId
  );
  this.metrics.recordTokenUsage(
    conversation.tokens,
    conversation.modelName
  );

  // 3. 自動評価の実行
  const autoScores = await this.autoEval.evaluate(
    conversation.query,
    conversation.answer
  );

  return {
    conversationId: conversation.id,
    scores: autoScores,
    timestamp: new Date()
  };
}

バッチ評価の実装

定期的にバッチで評価を実行する処理を実装します。

typescript// バッチ評価の実装
async runBatchEvaluation(
  startDate: Date,
  endDate: Date
): Promise<BatchEvaluationReport> {
  // 1. 期間内の会話を取得
  const conversations = await this.fetchConversations(
    startDate,
    endDate
  );

  // 2. 自動評価を実行
  const autoEvalResults = await Promise.all(
    conversations.map(conv =>
      this.autoEval.evaluate(conv.query, conv.answer)
    )
  );

  // 3. サンプリングして人手評価タスクを作成
  const samples = this.sampler.prioritySample(
    conversations,
    100  // 100件をサンプリング
  );

  const humanEvalTasks = samples.map(conv =>
    this.createHumanEvalTask(conv)
  );

  return {
    totalConversations: conversations.length,
    autoEvalResults,
    humanEvalTasks,
    period: { startDate, endDate }
  };
}

ダッシュボード API の実装

評価結果を可視化するための API を実装します。

typescript// ダッシュボード API の実装
import express from 'express';

class EvaluationDashboardAPI {
  private platform: DifyEvaluationPlatform;
  private app: express.Application;

  constructor(platform: DifyEvaluationPlatform) {
    this.platform = platform;
    this.app = express();
    this.setupRoutes();
  }

  private setupRoutes(): void {
    // メトリクスの取得
    this.app.get('/api/metrics', async (req, res) => {
      const metrics = await this.getMetrics(
        req.query.startDate as string,
        req.query.endDate as string
      );
      res.json(metrics);
    });

    // 評価結果の取得
    this.app.get('/api/evaluations', async (req, res) => {
      const evaluations = await this.getEvaluations(
        req.query.appId as string
      );
      res.json(evaluations);
    });
  }
}

メトリクスデータの集約処理を追加します。

typescript// メトリクスデータの集約
private async getMetrics(
  startDate: string,
  endDate: string
): Promise<MetricsSummary> {
  // データベースからメトリクスを取得
  const rawMetrics = await this.fetchMetricsFromDB(
    new Date(startDate),
    new Date(endDate)
  );

  // 集計処理
  return {
    averageLatency: this.calculateAverage(
      rawMetrics.map(m => m.latency)
    ),
    totalTokens: this.calculateSum(
      rawMetrics.map(m => m.tokens)
    ),
    errorRate: this.calculateErrorRate(rawMetrics),
    conversationCount: rawMetrics.length
  };
}

以下の図は、実装全体のアーキテクチャを示しています。

mermaidflowchart TB
    subgraph frontend["フロントエンド"]
        dashboard["ダッシュボード<br/>UI"]
    end

    subgraph backend["バックエンド"]
        api["ダッシュボード<br/>API"]
        platform["評価基盤<br/>コア"]

        subgraph components["コンポーネント"]
            obs["観測"]
            met["指標"]
            human["人手評価"]
        end
    end

    subgraph storage["ストレージ"]
        logs[("ログ<br/>DB")]
        metrics_db[("メトリクス<br/>DB")]
        eval_db[("評価<br/>DB")]
    end

    dashboard -->|HTTP| api
    api -->|クエリ| platform
    platform -->|利用| components

    obs -->|保存| logs
    met -->|保存| metrics_db
    human -->|保存| eval_db

図で理解できる要点:

  • フロントエンドからバックエンド API を経由して評価基盤コアにアクセスする構造
  • 3 つのコンポーネントがそれぞれ専用のストレージに データを保存する

設定ファイルの例

評価基盤の動作を制御する設定ファイルの例です。

yaml# evaluation.yaml - 評価基盤の設定

# ログ設定
logging:
  level: info
  format: json
  output:
    - type: file
      path: ./logs/evaluation.log
    - type: elasticsearch
      host: localhost:9200
      index: dify-evaluations

# メトリクス設定
metrics:
  collection_interval: 60 # 秒
  retention_days: 90
  exporters:
    - type: prometheus
      port: 9090
    - type: cloudwatch
      region: ap-northeast-1

# 自動評価設定
auto_evaluation:
  enabled: true
  metrics:
    - name: semantic_similarity
      threshold: 0.7
    - name: answer_relevance
      model: gpt-4
  batch_size: 100

人手評価の設定を追加します。

yaml# 人手評価設定
human_evaluation:
  enabled: true

  # サンプリング設定
  sampling:
    strategy: priority # random, stratified, priority
    sample_size: 100
    frequency: daily

    # 優先度設定
    priority_factors:
      - name: has_error
        weight: 10
      - name: high_latency
        weight: 5
      - name: negative_feedback
        weight: 8

  # 評価項目設定
  evaluation_items:
    - id: relevance
      question: '回答は質問に適切に答えていますか?'
      type: scale
      scale: 1-5
      required: true

    - id: accuracy
      question: '回答の内容は正確ですか?'
      type: choice
      choices:
        - '正確'
        - '一部不正確'
        - '不正確'
        - '判断できない'
      required: true

このように、YAML ファイルで柔軟に設定を管理できます。

運用フローの例

実際の運用における評価フローを示します。

typescript// 日次評価バッチの実装例
import cron from 'node-cron';

class DailyEvaluationJob {
  private platform: DifyEvaluationPlatform;

  constructor(platform: DifyEvaluationPlatform) {
    this.platform = platform;
  }

  // 日次バッチをスケジュール
  schedule(): void {
    // 毎日午前2時に実行
    cron.schedule('0 2 * * *', async () => {
      await this.runDailyEvaluation();
    });
  }

  // 日次評価の実行
  private async runDailyEvaluation(): Promise<void> {
    const yesterday = new Date();
    yesterday.setDate(yesterday.getDate() - 1);
    yesterday.setHours(0, 0, 0, 0);

    const today = new Date();
    today.setHours(0, 0, 0, 0);

    // バッチ評価を実行
    const report = await this.platform.runBatchEvaluation(
      yesterday,
      today
    );

    // レポートを保存
    await this.saveReport(report);

    // アラートをチェック
    await this.checkAlerts(report);
  }
}

アラート機能の実装例です。

typescript// アラート機能の実装
private async checkAlerts(
  report: BatchEvaluationReport
): Promise<void> {
  const alerts: Alert[] = [];

  // エラー率が閾値を超えていないかチェック
  const errorRate = this.calculateErrorRate(
    report.autoEvalResults
  );
  if (errorRate > 0.05) {  // 5%を超える
    alerts.push({
      level: 'warning',
      message: `エラー率が ${errorRate * 100}% に上昇しています`,
      metric: 'error_rate',
      value: errorRate
    });
  }

  // レイテンシが閾値を超えていないかチェック
  const avgLatency = this.calculateAverageLatency(
    report.autoEvalResults
  );
  if (avgLatency > 3000) {  // 3秒を超える
    alerts.push({
      level: 'critical',
      message: `平均レイテンシが ${avgLatency}ms に上昇しています`,
      metric: 'latency',
      value: avgLatency
    });
  }

  // アラートを通知
  if (alerts.length > 0) {
    await this.sendAlerts(alerts);
  }
}

この実装により、問題を早期に発見し、迅速に対応できます。

まとめ

本記事では、Dify における評価基盤の全体像と設計ポイントについて解説しました。

観測(Observability)、指標(Metrics)、人手評価(Human Evaluation)という 3 つの柱を組み合わせることで、AI アプリケーションの品質を多角的に評価できます。これらの要素は互いに補完し合い、より正確で実用的な評価を実現するでしょう。

評価基盤を構築する際の重要なポイントをまとめます。

#ポイント詳細
1構造化されたログ分析しやすい形式でデータを記録
2適切な指標の選定ユースケースに合わせた評価軸の設定
3効率的なサンプリング優先度を考慮した人手評価の実施
4フィードバックループ評価結果を改善に活かす仕組み
5自動化と定期実行継続的な評価の実現

AI アプリケーション開発において、評価は一度きりの作業ではありません。継続的に評価し、改善を重ねることで、ユーザーに価値を提供できるアプリケーションへと成長させることができます。

本記事で紹介した設計パターンと実装例を参考に、皆様のプロジェクトに適した評価基盤を構築していただければ幸いです。評価データは、プロダクトの成長を導く貴重な資産となるでしょう。

関連リンク

以下のリンクでは、Dify や AI アプリケーション評価に関する詳細な情報を確認できます。