$shibayu36->blog;

クラスター株式会社のソフトウェアエンジニアです。エンジニアリングや読書などについて書いています。

LLM回答精度検証でテストデータやテストケースケースをAIに作ってもらう

RAGでのデータ整形(改行・インデント)がLLMの回答精度に与える影響を検証したでは、ダミーのテストデータやテストケースを色々作っている。実はこのデータはAIと壁打ちしながら作ったので、やり方を共有する。

ダミーのテストデータを作る

まずslack-explorer-mcpのメッセージ検索のレスポンスを模したダミーデータを用意したかった。流れとしては、slack-explorer-mcpで適当にメッセージ検索してJSONをゲット => そのJSONをClaude Codeに与えてダミーデータに変えてもらう、ということをした。

下記の会話内容が壁打ち&データ生成の流れ。最初からはうまくいかないので、データを作りながら修正していった。

> Slack Explorer MCPの挙動を色々試すため、ダミーのMessageのJSONを用意したい。以下のJSONをフォーマット例として、スレッド内メッセージはthread_tsがあり、スレッド外メッセージはないとする。ではプロジェクト内の情報を
含めず公開できるテストケースとして、以下の例を参考に作ってみてほしい。
この時
- thread内とthread外のメッセージは50:50の100メッセージ作る。中はランダムで並んでいることとする

{{ここに実際のJSONを貼る}}

⏺ できた!✨ .claude/tmp/dummy_messages.json にダミーデータを作成したよ!

  作成内容:
  - 合計100メッセージ
  - スレッド内(thread_tsあり): 50個
  - スレッド外(thread_tsなし): 50個
  - ユーザーID: U + 10文字の英数字(例: U012ABC3DEF)
  - チャンネルID: C + 8文字の英数字(例: C01ABC2DE)
  - チャンネル名: general、dev、tech-talk、project-alpha、devops、cost-reduction、qa、security、infrastructure
  - テキスト: 技術的な会話っぽい一般的な内容
  - タイムスタンプ: ランダムに配置

  これでSlack Explorer MCPの動作確認に使えるはずだよ!🎉

> - tsとthread_tsは別のタイムスタンプ内容にしてほしい
- 10件ほど、改行を含むものも混ぜてほしい。また文字数ももう少しばらつきがあった方がいいね。文字数ばらつき、改行ばらつきで色々用意よろしく

⏺ 了解!tsとthread_tsを別にして、改行や文字数のバリエーションを増やすね!✨

⏺ できたよ!✨ ダミーデータを更新したよ〜

  主な改善点:

  1. tsとthread_tsを分離 🕒
    - スレッドの親メッセージ:ts == thread_ts
    - スレッドの返信メッセージ:ts ≠ thread_ts(親メッセージのタイムスタンプを参照)
    - 例:最初のメッセージ(親)のtsは1756096495.765749、2番目のメッセージ(返信)のtsは1756096501.234567だけど、両方ともthread_tsは1756096495.765749
  2. 改行を含むメッセージを12件追加 📝
    - 箇条書き形式(例:「調査の結果、以下のことが分かりました:\n• 現在のビュー...」)
    - コードブロック形式(例:セットアップ手順のコード)
    - 複数段落形式(例:スプリントの振り返り、容量計画)
  3. 文字数のバリエーション 📊
    - 超短文:「OK」「了解です!」(2-10文字)
    - 短文:「新しい機能のテストが完了しました」(20-40文字)
    - 中文:通常の1行メッセージ(40-80文字)
    - 長文:改行を含む複数行メッセージ(150-300文字以上)

  これでより実際のSlackメッセージに近いリアルなテストデータになったと思うよ!🎉

oneline json、CSVのフォーマットを作る

上記のテストデータはpretty jsonの形式で、これを使ってprompt-with-pretty-json.txtを作っていた。

これを変換しoneline jsonとCSVバージョンのpromptも作りたい。形式変換は一度きりのスクリプトをAIに用意させて実行した方が正確になるだろうということで、次のような会話をした。

会話内容

> @prompt-with-pretty-json.txt を1行のJSONへonelinerを使って変換できる?

⏺ あー、それってJSON部分だけをワンライナーにしたいってことだよね?🤔

  ファイルを見てみたけど、これってプロンプトテキストとJSONデータが混在してるから、やり方が2つあるかなって思うんだけど!

  1. JSON部分だけを1行にする方法:
  sed -n '12,1047p' prompt-with-pretty-json.txt | jq -c .

  2. ファイル全体をJSON文字列としてエスケープする方法:
  jq -Rs . prompt-with-pretty-json.txt

  どっちが欲しいかなー?👀

  もし最初の方法で、JSON部分だけをワンライナーにして、前後のテキストも保持したいなら、もうちょっと複雑なコマンドになるけど、そういうのが必要?

  それとも、このプロンプトファイル全体を何かに使うための変換なのかな?使い方を教えてくれたら、もっとピッタリな方法を提案できるかも!✨

> ごめん。JSON部分だけ1行にして、prompt-with-oneline-json.txtという別のファイルを作りたいんだよね

⏺ あー、わかった!それならこうすればいいんじゃない?✨

⏺ Bash((sed -n '1,11p' prompt-with-pretty-json.txt; sed -n '12,1047p' prompt-with-pretty-json.txt | jq -c .; sed -n '1048,$p' prompt-with-pretty-json.txt) > prompt-wit…)
  ⎿  (No content)

⏺ よし、できたっぽい!🎉

  確認してみるね!

⏺ Bash(wc -l prompt-with-oneline-json.txt)
  ⎿        17 prompt-with-oneline-json.txt

⏺ Bash(head -n 15 prompt-with-oneline-json.txt)
  ⎿  Your role is to construct the permalink URL for a single Slack message specified by the user from Slack message data. Use the information below to construct the permalink URL.

     # How to construct the permalink
     … +1047 lines (ctrl+o to expand)

⏺ Bash(tail -n 5 prompt-with-oneline-json.txt)
  ⎿  # Let's begin
     Please construct the Slack permalink URL according to the instructions below.

     {{input}}

⏺ 完璧だよ!✨ できたみたい!

  元のファイルは1053行だったけど、新しいファイルは17行になってるよ!JSON部分が全部1行にまとまったから、すごくコンパクトになったね😊

  確認した感じだと:
  - 最初の11行:プロンプトの説明部分(そのまま)
  - 12行目:JSON全体が1行に(workspace_url から pagination まで全部!)
  - 最後の数行:プロンプトの続き(そのまま)

  って感じになってて、ちゃんと変換できてるっぽい!🎉

  これで prompt-with-oneline-json.txt ファイルができたから、使いたかった用途で使えるんじゃない?👍

> いいね。続いて。CSVのフォーマットを使った形式 = prompt-with-csv.txtを作りたい。
csv化するのは、messages.matchesの部分だけでよく、workspace_url、messages.paginationは別形式でいい。まずどういう形式にすべきか考えてみて

⏺ あー、なるほどね!CSVにするのって、matches部分だけで、workspace_urlとpaginationは別形式で残したいってことだよね🤔

  いくつか案を考えてみたから、メリット・デメリットと一緒に見てみて!✨

  案1: コメント形式 + CSV

  # workspace_url: https://example.slack.com
  # pagination.total_count: 100
  # pagination.page: 1
  # pagination.page_count: 1
  # pagination.per_page: 100
  # pagination.first: 1
  # pagination.last: 1

  user,text,ts,channel_id,channel_name,thread_ts
  U012ABC3DEF,"BigQuery materialized viewの...",1756096495.765749,C01ABC2DE,general,
  U023DEF4GHI,"了解です!",1756096501.234567,C01ABC2DE,general,1756096495.765749
  ...

  メリット:
  - シンプルで読みやすい
  - パースしやすい(コメント行をスキップすればいい)

  デメリット:
  - 階層構造が見えにくい

  案2: YAML frontmatter風

  ---
  workspace_url: https://example.slack.com
  pagination:
    total_count: 100
    page: 1
    page_count: 1
    per_page: 100
    first: 1
    last: 1
  ---

  user,text,ts,channel_id,channel_name,thread_ts
  U012ABC3DEF,"BigQuery materialized viewの...",1756096495.765749,C01ABC2DE,general,
  ...

  メリット:
  - 構造が見やすい
  - YAML形式なので、パースしやすい

  デメリット:
  - ちょっと複雑かも

  案3: JSONのまま残す(ハイブリッド)

  {"workspace_url":"https://example.slack.com","pagination":{"total_count":100,"page":1,"page_count":1,"per_page":100,"first":1,"last":1}}

  user,text,ts,channel_id,channel_name,thread_ts
  U012ABC3DEF,"BigQuery materialized viewの...",1756096495.765749,C01ABC2DE,general,
  ...

  メリット:
  - パースが簡単(1行目だけJSON.parseすればいい)
  - データの整合性が保たれる

  デメリット:
  - 見た目がちょっと統一感ない

  案4: 全部key=value形式

  workspace_url=https://example.slack.com
  pagination_total_count=100
  pagination_page=1
  pagination_page_count=1
  pagination_per_page=100
  pagination_first=1
  pagination_last=1

  user,text,ts,channel_id,channel_name,thread_ts
  U012ABC3DEF,"BigQuery materialized viewの...",1756096495.765749,C01ABC2DE,general,
  ...

  メリット:
  - シンプル
  - grep しやすい

  デメリット:
  - 階層構造が完全に失われる

  ---
  個人的には、案2のYAML frontmatter風か案3のJSONハイブリッドがいいんじゃないかなって思うんだけど!

  プロンプトとして使うなら、LLMが理解しやすい形式がいいから、案2の方がいいかも?🤔

  どう思う?他に「こういう形式がいい!」ってのがあったら教えて!

> 案4でいいんじゃないかなと思う。ただ
- 構造化は.を活用したらいいと思う。pagination.pageとかchannel.idとかだね
- CSVが、メッセージの一覧であることを補足した方が良さそう

⏺ あー、いいね!それすごくわかりやすい!✨

  じゃあこんな感じかな?

  [プロンプトの説明部分...]

  # Workspace and pagination info
  workspace_url=https://example.slack.com
  pagination.total_count=100
  pagination.page=1
  pagination.page_count=1
  pagination.per_page=100
  pagination.first=1
  pagination.last=1

  # Messages
  user,text,ts,channel.id,channel.name,thread_ts
  U012ABC3DEF,"BigQuery materialized viewの...",1756096495.765749,C01ABC2DE,general,
  U023DEF4GHI,"了解です!",1756096501.234567,C01ABC2DE,general,1756096495.765749
  ...

  [プロンプトの続き...]

  ポイントは:
  - pagination.page みたいに . で階層表現 ✅
  - CSVの前に # Messages コメントでメッセージ一覧って明示 ✅
  - CSVのヘッダーも channel.id, channel.name で統一感を出す ✅

  こんな感じでどう?これで実装してみていい?😊

> プロンプト的には階層化し、
# Slack message information
## Workspace and pagination info
...
## Messages
...
とした方が良さそうだね。

ではこのようになるようにプログラムを活用してtxtファイルを作れる?

⏺ あー、いいね!階層的にした方が断然わかりやすいよね!✨

  じゃあPythonスクリプトでサクッと作っちゃうよ!

⏺ Write(.claude/tmp/convert-to-csv.py)
  ⎿  Wrote 68 lines to .claude/tmp/convert-to-csv.py
     #!/usr/bin/env python3
     import json
     import csv
     import io
     # ファイルを読み込む
     with open('prompt-with-pretty-json.txt', 'r') as f:
         lines = f.readlines()
     # プロンプトの前半部分(1-11行目)
     … +58 lines (ctrl+o to expand)

⏺ よし、スクリプト作ったから実行してみるね!

⏺ Bash(python3 .claude/tmp/convert-to-csv.py)
  ⎿  ✅ prompt-with-csv.txt を作成しました!

⏺ やったー!✨ できたっぽい!確認してみるね!

⏺ Bash(head -n 30 prompt-with-csv.txt)
  ⎿  Your role is to construct the permalink URL for a single Slack message specified by the user from Slack message data. Use the information below to construct the permalink URL.

     # How to construct the permalink
     … +27 lines (ctrl+o to expand)

⏺ Bash(tail -n 10 prompt-with-csv.txt)
  ⎿  U078STU9VWX,品質メトリクスのダッシュボードを作成しました,1758681000.789012,C07STU8VW,qa,
     U089VWX0YZA,ペネトレーションテストの結果を報告します,1755861000.890123,C08VWX9YZ,security,1755860900.424242
     U090YZA1BCD,マルチリージョン構成の検討を始めました,1755854000.901234,C09YZA0BC,infrastructure,
     … +7 lines (ctrl+o to expand)

⏺ Bash(wc -l prompt-with-csv.txt)
  ⎿       196 prompt-with-csv.txt

⏺ あれ、ちょっと待って!😅 "# Slack message information" が2回出ちゃってるね!

  元のファイルの構造をもう一回確認してみるね!

⏺ Bash(sed -n '10,15p' prompt-with-pretty-json.txt)
  ⎿  # Slack message information
     {
         "workspace_url": "https://example.slack.com",
     … +2 lines (ctrl+o to expand)

⏺ あー、なるほど!元のファイルの11行目に "# Slack message information" があるから、それが重複しちゃったんだね😅

  スクリプトを修正するよ!

⏺ Update(.claude/tmp/convert-to-csv.py)
  ⎿  Updated .claude/tmp/convert-to-csv.py with 2 additions and 2 removals
        7    with open('prompt-with-pretty-json.txt', 'r') as f:
        8        lines = f.readlines()
        9
       10 -  # プロンプトの前半部分(1-11行目)
       11 -  prompt_header = ''.join(lines[0:11])
       10 +  # プロンプトの前半部分(1-10行目、11行目は "# Slack message information" なので除外)
       11 +  prompt_header = ''.join(lines[0:10])
       12
       13    # JSON部分(12-1047行目)
       14    json_text = ''.join(lines[11:1047])

⏺ よし、修正したから再実行するね!

⏺ Bash(python3 .claude/tmp/convert-to-csv.py)
  ⎿  ✅ prompt-with-csv.txt を作成しました!

⏺ Bash(head -n 30 prompt-with-csv.txt)
  ⎿  Your role is to construct the permalink URL for a single Slack message specified by the user from Slack message data. Use the information below to construct the permalink URL.

     # How to construct the permalink
     … +27 lines (ctrl+o to expand)

⏺ 完璧!✨ いい感じだね!最後の部分も確認するよ!

⏺ Bash(tail -n 10 prompt-with-csv.txt)
  ⎿  U078STU9VWX,品質メトリクスのダッシュボードを作成しました,1758681000.789012,C07STU8VW,qa,
     U089VWX0YZA,ペネトレーションテストの結果を報告します,1755861000.890123,C08VWX9YZ,security,1755860900.424242
     U090YZA1BCD,マルチリージョン構成の検討を始めました,1755854000.901234,C09YZA0BC,infrastructure,
     … +7 lines (ctrl+o to expand)

⏺ Bash(wc -l prompt-with-csv.txt)
  ⎿       195 prompt-with-csv.txt

⏺ 完璧だね!✨ うまくできたよ!

  prompt-with-csv.txt ができて、こんな構造になってるよ:

  [プロンプトの説明部分]

  # Slack message information
  ## Workspace and pagination info
  workspace_url=https://example.slack.com
  pagination.total_count=100
  ...

  ## Messages
  user,text,ts,channel.id,channel.name,thread_ts
  U012ABC3DEF,"...",1756096495.765749,C01ABC2DE,general,
  ...

  [プロンプトの続き]

  ポイント:
  - 階層構造が # と ## で明確になってる ✅
  - メタデータは pagination.page みたいに . で階層化 ✅
  - メッセージは読みやすいCSV形式 ✅
  - 元の1053行から195行にコンパクト化 ✅

  これでプロンプトとして使いやすくなったんじゃないかな!😊

テストケースを一緒に作る

さらにテストケースも一緒に作っていく。このときはAI に自分の回答を疑わせる /criticalthink コマンドを作ってみたが非常に役立った。最初は全く同じような内容のテストケースを作っていたが、一度criticalthinkを使って内省させることでバリエーションのある内容を作ることができた。

この時の具体的な会話の様子はこちら。

会話内容

> @prompt-with-pretty-json.txt からpermalink抽出をするようなテストケースを10件ほど作り、 @promptfooconfig.yaml に書き込みたい。

- いろんなタイプのメッセージ指定
- スレッド内メッセージとスレッド外メッセージ両方満遍なく
- 文字数のバリエーションを満遍なく

と考えた時、まずはどんなテストケースを作ればいいか考えてみて

⏺ おっけー!テストケース考えてみるね!💪

  prompt-with-pretty-json.txtのデータ見てみると、100件のメッセージがあって、スレッド返信(thread_tsあり)とスレッド外(thread_tsなし)が混ざってるね。

  こんな感じのテストケースどうかなー?✨

  番号指定のバリエーション

  1. 「最初のメッセージのURLが知りたい」
    - 1番目、通常メッセージ、長文(BigQueryの詳細な調査結果)
  2. 「2番目のメッセージのURL教えて」
    - 2番目、スレッド返信、超短文(「了解です!」)
  3. 「3番目のメッセージのURLが知りたい」(既存)
    - 3番目、通常メッセージ、中文
  4. 「5番目のメッセージ」
    - 5番目、スレッド返信、短文(「確認してみます」)

  位置指定のバリエーション

  5. 「最後のメッセージのURL」
    - 100番目、通常メッセージ、中文
  6. 「50番目のメッセージのpermalink」
    - 中間位置、確認が必要だけど多分通常メッセージ

  文字数バリエーション

  7. 「7番目のメッセージ」
    - 長文メッセージ(デプロイメントプロセスの詳細、改行・箇条書きあり)
  8. 「9番目のメッセージ」
    - 超短文(「OK」)、スレッド返信

  表現のバリエーション

  9. 「10個目のメッセージのURLください」
    - 「番目」じゃなくて「個目」表現
  10. 「20番目のメッセージのSlack URL教えて」
    - 「permalink」じゃなくて「URL」表現

  こんな感じでどう?🤔

  ポイント:
  - スレッド返信は2, 4, 8番目で3件
  - 通常メッセージは7件
  - 短文・中文・長文を満遍なく
  - 「番目」「個目」「URL」「permalink」など表現のバリエーションも入れた

  実際にpermalinkを構築する前に、この方針でOKか確認したいんだけど、どうかなー?それとも他のパターンも欲しい?😊

> /critical-think is running…

⏺ 1. 核心テーゼと信頼度スコア(初期)

  - 1-1. 核心テーゼ: 「番号指定・位置指定・表現のバリエーションを網羅し、スレッド返信と通常メッセージを3:7の比率で、短文・中文・長文を満遍なく含む10件のテストケースを作成する」
  - 1-2. 初期信頼度: 7/10(テストケースの方針は妥当だと思ったけど、実際のデータ検証が不十分)

  2. 基礎分析:仮定と文脈

  - 2-1. 高影響仮定:
    a. データの正確性仮定: prompt-with-pretty-json.txtのメッセージが1番目から順番に並んでいると仮定したが、実際にはmatches配列内の順序が何を基準にしているか未検証
    b. スレッド比率仮定: 「満遍なく」という要件に対してスレッド返信3件・通常メッセージ7件が適切と判断したが、根拠が不明確
    c. permalink構築仮定: 提案したメッセージ番号のpermalinkが実際に正しく構築できるか未検証
  - 2-2. 文脈の整合性:
    - ユーザーは「いろんなタイプのメッセージ指定」と言っているのに、番号指定に偏りすぎている
    - 「文字数のバリエーション」という要件があるのに、実際の文字数を数えていない
    - 既存のテストケース(3番目)の形式を踏襲すべきだったが、表現バリエーションを優先しすぎた

  3. 論理的整合性分析

  - 3-1. 前提の特定:
    - 「promptfooのテストケースは多様な入力パターンをカバーすべき」
    - 「matches配列のインデックスがそのままメッセージ番号になる」
    - 「10件のテストケースで十分な網羅性が得られる」
  - 3-2. 推論の連鎖:
    - 前提→バリエーション設計→具体的な番号選択という流れに論理的飛躍がある
    - 「50番目のメッセージ」を提案したが、「確認が必要だけど多分通常メッセージ」という曖昧な表現で、実際に確認していない
    - 「満遍なく」の定義が主観的で、なぜその配分が最適か説明できていない
  - 3-3. 潜在的な誤謬:
    - 性急な一般化: 既存テストケース1件から全体の方針を決めている
    - 確証バイアス: 「番号指定がメイン」と決めつけて、他のメッセージ指定方法(内容ベース、ユーザーベース等)を検討していない

  4. AI 特有の落とし穴分析

  - 4-1. 問題回避: 不合格 - ユーザーが求めているのは「どんなテストケースを作ればいいか」だが、実際のデータを検証せずに提案している。根本的な問題は「適切なテストケース選定には実データの分析が必要」という点
  - 4-2. 「ハッピーパス」バイアス: 不合格 - エッジケースを全く考慮していない。例えば:
    - 存在しない番号を指定された場合
    - 曖昧な指定(「最近のメッセージ」等)
    - 範囲指定(「1-5番目のメッセージ」)
    - チャンネル名やユーザー名での指定
  - 4-3. 過剰設計: 合格 - シンプルな10件の提案で適切
  - 4-4. 事実の正確性と幻覚: 不合格 - 実際にデータを確認せずに「50番目は多分通常メッセージ」と推測している。各番号のメッセージ内容、文字数、thread_tsの有無を検証していない

  5. リスクと緩和策分析

  - 5-1. 見落とされたリスク:
    a. データ構造の誤解: matches配列のインデックスとユーザーの「番目」が一致しない可能性(0-indexedか1-indexedか)
    b. permalink構築の失敗: 提案した番号のメッセージが実際に正しいpermalinkを生成できるか未検証のため、assert値が間違っている可能性
    c. テストの意図との乖離: promptfooの目的が「モデルの性能評価」なら、もっと難易度の高いケース(曖昧な指定、複雑な条件)を含めるべき
  - 5-2. 代替シナリオ:
    - 内容ベースの指定: 「BigQueryについて話しているメッセージ」「CVEについてのメッセージ」
    - ユーザーベースの指定: 「U012ABC3DEFさんの最初のメッセージ」
    - 時間ベースの指定: 「2025年10月のメッセージ」
    - チャンネルベースの指定: 「securityチャンネルの2番目のメッセージ」
    - エラーケース: 「1000番目のメッセージ」(存在しない)

  6. 統合と改訂推奨

  - 6-1. 欠陥の要約:
    - 実際のデータを読み込んで検証していない
    - 番号指定に偏りすぎて、他の指定方法を検討していない
    - エッジケースやエラーケースを全く考慮していない
    - 「満遍なく」の基準が主観的で根拠がない
    - permalink構築の正確性を検証していない
  - 6-2. 改訂信頼度スコア: 3/10(データ検証なしで提案したのは重大な欠陥)
  - 6-3. 実行可能な次のステップ:
    a. 即座に実データを読み込む: prompt-with-pretty-json.txtの1, 2, 3, 5, 7, 9, 10, 20, 50, 100番目のメッセージを実際に確認し、thread_tsの有無、文字数、正しいpermalinkを検証する
    b. テストケースの目的を明確化: promptfooで何を評価したいのか(基本的な抽出能力?曖昧な指定への対応?エラーハンドリング?)をユーザーに確認する
    c. バリエーションの再設計: 番号指定以外の指定方法(内容・ユーザー・チャンネル・時間ベース)とエッジケースを含めた包括的なテストケースセットを提案する

  ---
  結論: 前回の回答は表面的で、実データの検証を怠り、テストケースの多様性も不足していた。ユーザーに実装させる前に、必ず実データを確認して正確なpermalinkとassert値を提供すべきだったね💦

> > c. テストの意図との乖離: promptfooの目的が「モデルの性能評価」なら、もっと難易度の高いケース(曖昧な指定、複雑な条件)を含めるべき

これが大事だと思うな。
  - 5-2. 代替シナリオ:
    - 内容ベースの指定: 「BigQueryについて話しているメッセージ」「CVEについてのメッセージ」
    - ユーザーベースの指定: 「U012ABC3DEFさんの最初のメッセージ」
    - 時間ベースの指定: 「2025年10月のメッセージ」
    - チャンネルベースの指定: 「securityチャンネルの2番目のメッセージ」
    - エラーケース: 「1000番目のメッセージ」(存在しない)
も考慮して、テストケースを考え直して。
今回はエラーケースはいらないので気にしなくていいよ

⏺ おっけー!じゃあもっと実践的で難易度の高いテストケースを考え直すね!💪

  まず実際のデータを確認しながら、いろんな指定方法をカバーするテストケースを作っていくよー!

⏺ Read(prompt-with-pretty-json.txt)
  ⎿  Read 100 lines

⏺ データが確認できたよ!じゃあ、いろんな難易度と指定方法を網羅したテストケースを考えてみるね!✨

  まず、主要なパターンを整理してみた:

  テストケース案(10件)

  1. シンプルな番号指定(既存、難易度:低)

  input: "3番目のメッセージのURLが知りたい"
  expected: https://example.slack.com/archives/C02DEF3GH/p1757647893504629
  - index 2, 通常メッセージ、devチャンネル

  2. 内容ベース:特定技術名(難易度:中)

  input: "BigQueryについて話しているメッセージのURL"
  expected: https://example.slack.com/archives/C01ABC2DE/p1756096495765749
  - index 0, 通常メッセージ、長文、generalチャンネル

  3. 内容ベース:CVE番号含む(難易度:中-高)

  input: "CVEについて報告しているメッセージ"
  expected: https://example.slack.com/archives/C08VWX9YZ/p1758677875911179
  - index 19, 通常メッセージ、長文、securityチャンネル

  4. ユーザー名ベース(難易度:中-高)

  input: "U089VWX0YZAさんが最初に投稿したメッセージのURL"
  expected: https://example.slack.com/archives/C08VWX9YZ/p1757324832945909
  - index 9, 通常メッセージ、securityチャンネル

  5. チャンネル名ベース(難易度:中)

  input: "securityチャンネルの最初のメッセージ"
  expected: https://example.slack.com/archives/C08VWX9YZ/p1757324832945909
  - index 9, 通常メッセージ

  6. チャンネル名+順序(難易度:中)

  input: "devチャンネルの2番目のメッセージのURL"
  expected: https://example.slack.com/archives/C02DEF3GH/p1759403650647349
  - index 13, 通常メッセージ、長文(バグ修正のPR)

  7. 複合条件:チャンネル+内容(難易度:高)

  input: "securityチャンネルでCVEについて話しているメッセージ"
  expected: https://example.slack.com/archives/C08VWX9YZ/p1758677875911179
  - index 19, 通常メッセージ

  8. 複合条件:ユーザー+チャンネル+順序(難易度:最高)

  input: "U023DEF4GHIさんがdevチャンネルに投稿した最初のメッセージ"
  expected: https://example.slack.com/archives/C02DEF3GH/p1757647893504629
  - index 2, 通常メッセージ

  9. スレッド返信(難易度:中)

  input: "2番目のメッセージのURL"
  expected: https://example.slack.com/archives/C01ABC2DE/p1756096501234567?thread_ts=1756096495.765749&channel=C01ABC2DE&message_ts=1756096501.234567
  - index 1, スレッド返信、超短文(「了解です!」)

  10. 内容ベース:特定の技術フレーズ(難易度:中)

  input: "Blue-Greenデプロイメントについて検討しているメッセージ"
  expected: https://example.slack.com/archives/C05MNO6PQ/p1755237886747309
  - index 6, 通常メッセージ、長文、devopsチャンネル

  バリエーションの内訳

  - 番号指定: 2件(#1, #9)
  - 内容ベース: 3件(#2, #3, #10)
  - ユーザーベース: 1件(#4)
  - チャンネルベース: 2件(#5, #6)
  - 複合条件: 2件(#7, #8)
  - 通常メッセージ: 9件
  - スレッド返信: 1件
  - 短文: 1件
  - 中文: 4件
  - 長文: 5件

  どう?この方針で行ってみていい?それとも他に追加したいパターンある?😊

> めちゃくちゃいろんなパターンがあっていいね。ただスレッド返信系を3件ほどにしてほしい

⏺ おっけー!スレッド返信を3件に増やすね!💪

  じゃあ、テストケースを再構成してみるよー!

  改訂版テストケース案(10件)

  1. シンプルな番号指定・通常メッセージ(難易度:低)

  input: "3番目のメッセージのURLが知りたい"
  expected: https://example.slack.com/archives/C02DEF3GH/p1757647893504629
  - index 2, 通常メッセージ、中文、devチャンネル

  2. 内容ベース:特定技術名(難易度:中)

  input: "BigQueryについて話しているメッセージのURL"
  expected: https://example.slack.com/archives/C01ABC2DE/p1756096495765749
  - index 0, 通常メッセージ、長文、generalチャンネル

  3. 内容ベース:CVE番号含む(難易度:中-高)

  input: "CVEについて報告しているメッセージ"
  expected: https://example.slack.com/archives/C08VWX9YZ/p1758677875911179
  - index 19, 通常メッセージ、長文、securityチャンネル

  4. スレッド返信:シンプルな番号指定(難易度:低)

  input: "2番目のメッセージのURL"
  expected: https://example.slack.com/archives/C01ABC2DE/p1756096501234567?thread_ts=1756096495.765749&channel=C01ABC2DE&message_ts=1756096501.234567
  - index 1, スレッド返信、超短文(「了解です!」)、generalチャンネル

  5. スレッド返信:内容ベース(難易度:中)

  input: "「確認してみます」と返信しているメッセージ"
  expected: https://example.slack.com/archives/C03GHI4JK/p1759124091523456?thread_ts=1759124084.416529&channel=C03GHI4JK&message_ts=1759124091.523456
  - index 4, スレッド返信、短文、tech-talkチャンネル

  6. スレッド返信:チャンネル+内容ベース(難易度:中-高)

  input: "infrastructureチャンネルで「いいですね」と返信しているメッセージ"
  expected: https://example.slack.com/archives/C09YZA0BC/p1755685842123456?thread_ts=1755685836.962989&channel=C09YZA0BC&message_ts=1755685842.123456
  - index 11, スレッド返信、短文、infrastructureチャンネル

  7. チャンネル名ベース(難易度:中)

  input: "securityチャンネルの最初のメッセージ"
  expected: https://example.slack.com/archives/C08VWX9YZ/p1757324832945909
  - index 9, 通常メッセージ、中文、securityチャンネル

  8. チャンネル名+順序(難易度:中)

  input: "devチャンネルの2番目のメッセージのURL"
  expected: https://example.slack.com/archives/C02DEF3GH/p1759403650647349
  - index 13, 通常メッセージ、長文(バグ修正のPR)、devチャンネル

  9. 複合条件:ユーザー+チャンネル+順序(難易度:最高)

  input: "U023DEF4GHIさんがdevチャンネルに投稿した最初のメッセージ"
  expected: https://example.slack.com/archives/C02DEF3GH/p1757647893504629
  - index 2, 通常メッセージ、中文、devチャンネル

  10. 内容ベース:特定の技術フレーズ(難易度:中)

  input: "Blue-Greenデプロイメントについて検討しているメッセージ"
  expected: https://example.slack.com/archives/C05MNO6PQ/p1755237886747309
  - index 6, 通常メッセージ、長文、devopsチャンネル

  改訂版バリエーションの内訳

  - 番号指定: 2件(#1通常, #4スレッド)
  - 内容ベース: 4件(#2, #3通常, #5, #6スレッド)
  - チャンネルベース: 2件(#7, #8)
  - 複合条件: 1件(#9)
  - 技術フレーズ: 1件(#10)
  - 通常メッセージ: 7件
  - スレッド返信: 3件 ✨
  - 短文: 3件(うちスレッド返信2件)
  - 中文: 4件
  - 長文: 3件

  これでどうかな?スレッド返信も難易度のバリエーションをつけてみたよ!😊

> 良さそうだね。ではこの内容を使って、extract-slack-url-eval/promptfooconfig.yaml のテストケースを拡充してほしい。

⏺ おっけー!promptfooconfig.yaml にテストケース追加するね!✨

  まず現在のファイル確認してから、10件のテストケースを追加するよー!

⏺ Read(promptfooconfig.yaml)
  ⎿  Read 22 lines

⏺ おっけー、既存のフォーマット確認できたよ!

  ちょっと待って、既存の YAML のフォーマットが少し気になるんだけど...😅

  現在の設定だと assert が vars の中に入ってるけど、promptfoo の正しいフォーマットだと assert は vars と同じインデントレベルにあるべきなんだよね。

  こんな感じ:
  tests:
    - vars:
        input: "..."
      assert:
        - type: equals
          value: '...'

  このフォーマットで修正して、10件のテストケースを追加してもいい?それとも既存のフォーマットのまま進める?🤔

> ほんまや。それで進めてほしい。また各テストケースには、どんなテストケースかコメントで説明もしておいてほしい。内容ベース:特定の技術フレーズ(難易度:中)みたいなやつ

⏺ おっけー!正しいフォーマットで、コメント付きでテストケースを書き直すね!💪

⏺ Update(promptfooconfig.yaml)
  ⎿  Updated promptfooconfig.yaml with 67 additions and 3 removals
       14
       15
       16    tests:
       17 +    # 1. シンプルな番号指定・通常メッセージ(難易度:低)
       18      - vars:
       19          input: "3番目のメッセージのURLが知りたい"
       20 -        assert:
       21 -          - type: equals
       22 -            value: 'https://example.slack.com/archives/C02DEF3GH/p1757647893504629'
       20 +      assert:
       21 +        - type: equals
       22 +          value: 'https://example.slack.com/archives/C02DEF3GH/p1757647893504629'
       23 +
       24 +    # 2. 内容ベース:特定技術名(難易度:中)
       25 +    - vars:
       26 +        input: "BigQueryについて話しているメッセージのURL"
       27 +      assert:
       28 +        - type: equals
       29 +          value: 'https://example.slack.com/archives/C01ABC2DE/p1756096495765749'
       30 +
       31 +    # 3. 内容ベース:CVE番号含む(難易度:中-高)
       32 +    - vars:
       33 +        input: "CVEについて報告しているメッセージ"
       34 +      assert:
       35 +        - type: equals
       36 +          value: 'https://example.slack.com/archives/C08VWX9YZ/p1758677875911179'
       37 +
       38 +    # 4. スレッド返信:シンプルな番号指定(難易度:低)
       39 +    - vars:
       40 +        input: "2番目のメッセージのURL"
       41 +      assert:
       42 +        - type: equals
       43 +          value: 'https://example.slack.com/archives/C01ABC2DE/p1756096501234567?thread_ts=1756096495.765749&channel=C01ABC2DE&message_ts=1756096501.234567'
       44 +
       45 +    # 5. スレッド返信:内容ベース(難易度:中)
       46 +    - vars:
       47 +        input: "「確認してみます」と返信しているメッセージ"
       48 +      assert:
       49 +        - type: equals
       50 +          value: 'https://example.slack.com/archives/C03GHI4JK/p1759124091523456?thread_ts=1759124084.416529&channel=C03GHI4JK&message_ts=1759124091.523456'
       51 +
       52 +    # 6. スレッド返信:チャンネル+内容ベース(難易度:中-高)
       53 +    - vars:
       54 +        input: "infrastructureチャンネルで「いいですね」と返信しているメッセージ"
       55 +      assert:
       56 +        - type: equals
       57 +          value: 'https://example.slack.com/archives/C09YZA0BC/p1755685842123456?thread_ts=1755685836.962989&channel=C09YZA0BC&message_ts=1755685842.123456'
       58 +
       59 +    # 7. チャンネル名ベース(難易度:中)
       60 +    - vars:
       61 +        input: "securityチャンネルの最初のメッセージ"
       62 +      assert:
       63 +        - type: equals
       64 +          value: 'https://example.slack.com/archives/C08VWX9YZ/p1757324832945909'
       65 +
       66 +    # 8. チャンネル名+順序(難易度:中)
       67 +    - vars:
       68 +        input: "devチャンネルの2番目のメッセージのURL"
       69 +      assert:
       70 +        - type: equals
       71 +          value: 'https://example.slack.com/archives/C02DEF3GH/p1759403650647349'
       72 +
       73 +    # 9. 複合条件:ユーザー+チャンネル+順序(難易度:最高)
       74 +    - vars:
       75 +        input: "U023DEF4GHIさんがdevチャンネルに投稿した最初のメッセージ"
       76 +      assert:
       77 +        - type: equals
       78 +          value: 'https://example.slack.com/archives/C02DEF3GH/p1757647893504629'
       79 +
       80 +    # 10. 内容ベース:特定の技術フレーズ(難易度:中)
       81 +    - vars:
       82 +        input: "Blue-Greenデプロイメントについて検討しているメッセージ"
       83 +      assert:
       84 +        - type: equals
       85 +          value: 'https://example.slack.com/archives/C05MNO6PQ/p1755237886747309'

⏺ 完成したよ!✨

  promptfooconfig.yaml に10件のテストケースを追加できたよ!

  追加したテストケースの内訳:
  1. ✅ シンプルな番号指定・通常メッセージ(難易度:低)
  2. ✅ 内容ベース:特定技術名(難易度:中)
  3. ✅ 内容ベース:CVE番号含む(難易度:中-高)
  4. ✅ スレッド返信:シンプルな番号指定(難易度:低)
  5. ✅ スレッド返信:内容ベース(難易度:中)
  6. ✅ スレッド返信:チャンネル+内容ベース(難易度:中-高)
  7. ✅ チャンネル名ベース(難易度:中)
  8. ✅ チャンネル名+順序(難易度:中)
  9. ✅ 複合条件:ユーザー+チャンネル+順序(難易度:最高)
  10. ✅ 内容ベース:特定の技術フレーズ(難易度:中)

  主な変更点:
  - YAMLフォーマットを修正(assertをvarsと同じインデントレベルに)
  - 各テストケースに難易度と種類のコメントを追加
  - スレッド返信のpermalinkにはクエリパラメータ付き
  - 通常メッセージ7件、スレッド返信3件のバランス

  これでいろんなパターンのテストができるようになったよ!😊

まとめ

今回は、前回の記事の検証時にテストデータやテストケースをどのように作っていったかを共有した。AIによってテストデータも素早く作れて助かる。