以下の内容はhttps://tech.newmo.me/entry/2025/12/09/090000より取得しました。


生成AIの入出力品質は「フォーマット」で決まる ― 中間表現とテンプレートの組み合わせ

生成AIにドキュメントからテストケースを生成してもらったり、仕様書からコードを生成してもらったりする機会が増えてきました。しかし、こんな経験はないでしょうか?

  • 入力の理解が不十分 - AIが仕様を読み飛ばす、重要な制約を見落とす
  • 出力フォーマットがバラバラ - 同じ入力でも毎回異なる構造で出力される

これらの問題により、生成物の品質が安定せず、結局人間が大幅に手直しすることになります。

この記事では、これらの課題を解決するために試験導入中の「中間表現(IR)」と「出力テンプレート」を組み合わせたパターンを、テストケースの生成を例に紹介します。


課題:自然言語から直接生成する限界

PRD(製品要求仕様書)やPDD(製品設計書)から直接テストケースを生成しようとすると、いくつかの問題が発生します。

PRD/PDD(自然言語) → 生成AI → テストケース

この単純なパイプラインでは:

  1. 入力の理解が不十分 - AIが仕様を読み飛ばす、重要な制約や代替フローを見落とす
  2. 出力フォーマットがバラバラ - 同じ入力でも毎回異なる構造・項目で出力される

解決策:中間表現とテンプレートを挟む

コンパイラが高級言語を機械語に変換する際、一度中間表現(IR)に変換してから最適化を行うように、生成AIにも中間表現を挟むことで品質が劇的に向上します。さらに、最終出力にテンプレートを適用することで、フォーマットも統一できます。

PRD/PDD(自然言語) → UseCase IR → Test Plan IR → テンプレート → テストケース

なぜこのアプローチが効くのか?

  1. IRスキーマによる強制抽出 - 必須フィールドに沿って情報を整理するため、抜け漏れが防げる
  2. テンプレートによるフォーマット統一 - 毎回同じ構造で出力されるため、品質が安定する
  3. 段階的な変換 - 一度に複雑な変換をせず、段階を踏むことで各ステップの品質を担保
  4. 再利用性 - 同じUseCase IRから複数の成果物(テストプラン、チェックリスト、E2Eテスト等)を生成可能

なぜ2つのIRに分けるのか?

1つのIRで済ませることもできますが、役割を分けることで品質が向上します。

IR 役割 内容
UseCase IR 仕様の構造化 PRD/PDDから「何ができるか」を抽出。入力項目、制約、フローなど
Test Plan IR テスト戦略の設計 UseCase IRから「何をテストするか」を設計。境界値追加、優先度付けなど

UseCase IRは仕様の忠実な構造化であり、テスト以外の用途(コード生成、ドキュメント生成等)にも再利用できます。Test Plan IRはテストに特化した変換を行い、境界値テストの自動追加や優先度の分類など、テスト設計のノウハウを注入します。

この分離により:

  • UseCase IRは複数の用途で共有可能(Single Source of Truth)
  • Test Plan IRでテスト固有の最適化が可能
  • 各ステップの責務が明確になり、デバッグしやすい

品質向上の鍵:構造化フォーマットによる強制

生成AIに自由形式で出力させると、品質がばらつきます。構造化されたフォーマットを強制することで、この問題を解決できます。

IRスキーマ:「何を抽出するか」を強制

IRにはスキーマ(必須フィールド)を定義します。AIはこのスキーマに沿って出力するため、必要な情報を漏れなく抽出できます。

# スキーマで必須フィールドを定義
usecase:
  input_fields: []      # ← 必ず入力項目を列挙
  constraints: []       # ← 必ず制約を列挙
  alternative_flows: [] # ← 必ず代替フローを列挙

スキーマがなければ、AIは「重要そうな項目」だけを抽出し、制約や代替フローを見落とします。スキーマがあれば、空欄を埋める形で全項目を強制的に抽出できます。

出力テンプレート:「どう出力するか」を強制

最終成果物にもテンプレートを定義します。これにより、毎回同じフォーマットで出力されます。

# テストプラン: {機能名}

## 前提条件      ← 必ず記載
## テストケース  ← 必ず記載
## 実行結果      ← 必ず記載

テンプレートがなければ、同じIRからでも毎回異なる構造のドキュメントが生成されます。テンプレートがあれば、どの機能でも同じ構造のドキュメントが得られます。

冒頭の課題との対応

課題 解決策 効果
入力の理解が不十分 IRスキーマで必須フィールドを定義 全項目を強制抽出
出力フォーマットがバラバラ 出力テンプレートで構造を定義 一貫したフォーマット

パイプラインの全体像

flowchart LR
    subgraph Input["📥 入力"]
        PRD["📋 PRD"]
        PDD["📝 PDD"]
        Figma["🎨 Figma"]
        PR["💻 GitHub"]
    end

    subgraph IR["🔄 中間表現"]
        UC["📦 UseCase IR"]
        TP["🧪 Test Plan IR"]
    end

    subgraph Output["📤 成果物"]
        TestPlan["📑 テストプラン"]
        Checklist["✅ チェックリスト"]
    end

    PRD --> UC
    PDD --> UC
    Figma --> UC
    PR --> UC

    UC --> TP
    TP --> TestPlan
    TP --> Checklist

UseCase IR:仕様を構造化する

まず、PRD/PDDから全仕様を体系的に抽出するUseCase IRを生成します。

UseCase IRのスキーマ

erDiagram
    UseCase_IR {
        string id
        string name
        array sources
    }

    UseCase_IR ||--o{ Precondition : has
    UseCase_IR ||--o{ InputField : has
    UseCase_IR ||--o{ Constraint : has
    UseCase_IR ||--o{ MainFlowStep : has
    UseCase_IR ||--o{ AlternativeFlow : has
    UseCase_IR ||--o{ SystemBehavior : has
    UseCase_IR ||--o{ Postcondition : has

    InputField {
        string name
        string type
        boolean required
    }

    Constraint {
        string name
        string condition
        string behavior
    }

    AlternativeFlow {
        string name
        string condition
        string outcome
    }

UseCase IRが強制する抽出項目

セクション 抽出内容 テストへの変換
input_fields (required) 必須入力項目 各フィールドのバリデーションテスト
input_fields (optional) 任意入力項目 オプション機能のテスト
constraints 制約条件 ビジネスルールテスト + 境界値テスト
main_flow メインフロー ハッピーパステスト
alternative_flows 代替フロー 各分岐のカバレッジ
system_behaviors システム動作 非同期処理・インテグレーションテスト
postconditions 事後条件 結果検証テスト

出力例: 配車予約機能

usecase:
  id: "UC-RESERVATION-001"
  name: "配車予約の作成"

  preconditions:
    - "ユーザーがログイン済み"

  input_fields:
    - name: "weekdays"
      type: "select"
      required: true
    - name: "time"
      type: "time"
      required: true
    - name: "start_date"
      type: "date"
      required: true
    - name: "end_date"
      type: "date"
      required: true

  constraints:
    - name: "期間制限"
      condition: "終了日が開始日から1ヶ月を超える"
      behavior: "エラーを表示"

  alternative_flows:
    - name: "複数選択"
      condition: "複数の曜日を選択"
      outcome: "選択した全曜日に予約が生成される"

  system_behaviors:
    - name: "非同期処理"
      description: "個別予約を非同期で生成"

  postconditions:
    - "配車予約パターンが保存されている"
    - "個別予約が生成されている"

Test Plan IR:テスト戦略に変換する

UseCase IRをそのままテストケースに変換すると、機械的な1:1マッピングになり冗長になります。Test Plan IRを挟むことで、インテリジェントなテスト設計が可能になります。

Test Plan IRが行う変換

UseCase IRの記述 Test Plan IRでの変換
type: time 境界値テスト追加: 00:00, 23:59
type: date + 制約 境界値テスト追加: 上限値、上限+1
alternative_flows 各フローのインタラクションテスト
複数の類似項目 統合: 代表テストにまとめる
仕様に明記なし UI/UXテスト自動追加: 確認ダイアログ等
仕様に明記なし 優先度を自動分類: High/Medium/Low

Test Plan IRのスキーマ

erDiagram
    TestPlan_IR {
        string id
        string name
        string usecase_id
    }

    TestPlan_IR ||--o{ Category : has
    Category ||--o{ TestCase : contains

    Category {
        string name
        string description
    }

    TestCase {
        string id
        string name
        string priority
    }

    TestCase ||--o{ Step : has

    Step {
        string action
        string expected
    }

出力例: 配車予約機能のTest Plan IR

test_plan:
  id: "TP-RESERVATION-001"
  name: "配車予約作成 テスト計画"
  usecase_id: "UC-RESERVATION-001"

  categories:
    - name: "基本フロー"
      test_cases:
        - id: "TC-001"
          name: "配車予約を作成できる"
          priority: "High"
          steps:
            - action: "必須項目を入力して登録"
              expected: "予約が作成される"

        # 境界値テスト(Test Plan IRで自動追加)
        - id: "TC-002"
          name: "時刻00:00で予約作成できる"
          priority: "Medium"

        - id: "TC-003"
          name: "時刻23:59で予約作成できる"
          priority: "Medium"

    - name: "バリデーション"
      test_cases:
        - id: "TC-010"
          name: "期間が1ヶ月を超えるとエラー"
          priority: "High"

        # UI/UXテスト(Test Plan IRで自動追加)
        - id: "TC-011"
          name: "確認ダイアログが表示される"
          priority: "Low"

出力テンプレート:フォーマットを統一する

IRが「何を抽出するか」を強制するのに対し、テンプレートは「どう出力するか」を強制します。Test Plan IRを最終的なテストプランに変換するためのテンプレートを定義します。

テストプランテンプレート

# テストプラン: {test_plan.name}

**Test Plan ID:** {test_plan.id}
**UseCase ID:** {test_plan.usecase_id}

## 前提条件
{for precondition in usecase.preconditions}
- {precondition}
{end}

## テストケース

{for category in test_plan.categories}
### {category.index}. {category.name}

{for test_case in category.test_cases}
#### {test_case.id}: {test_case.name} `{test_case.priority}`

| # | 操作 | 期待結果 |
|---|------|----------|
{for step in test_case.steps}
| {step.index} | {step.action} | {step.expected} |
{end}

{end}
{end}

このテンプレートにより、どのTest Plan IRからでも同じ構造のドキュメントが生成されます。

最終出力:テストプラン

上記のTest Plan IRとテンプレートから、以下のようなテストプランが生成されます。

# テストプラン: 配車予約機能

**Test Plan ID:** TP-RESERVATION-001
**UseCase ID:** UC-RESERVATION-001

## 前提条件
- ユーザーがログイン済み

## テストケース

### 1. 基本フロー

#### TC-001: 配車予約を作成できる `High`

| # | 操作 | 期待結果 |
|---|------|----------|
| 1 | 必須項目を全て入力する | 各項目が正しく設定される |
| 2 | 「登録」→「確定」をクリック | 予約が作成され、完了メッセージが表示される |

#### TC-002: 時刻00:00で予約作成できる `Medium`

| # | 操作 | 期待結果 |
|---|------|----------|
| 1 | 迎車希望時刻に「00:00」を設定 | 予約が正常に作成される(境界値テスト) |

### 2. バリデーション

#### TC-010: 曜日未選択でエラー `High`

| # | 操作 | 期待結果 |
|---|------|----------|
| 1 | 曜日を選択せずに登録 | エラーメッセージが表示される |

### 3. 制約

#### TC-020: 期間が1ヶ月を超えるとエラー `High`

| # | 操作 | 期待結果 |
|---|------|----------|
| 1 | 終了日を開始日から1ヶ月+1日に設定 | エラーメッセージが表示される |

このように、IRとテンプレートを組み合わせることで、どの機能でも同じ構造のテストプランが生成されます。


検証結果:IRの有無でどれだけ差が出るか

実際の機能で、IR経由の生成と直接生成を比較しました。

指標 IR経由 直接生成 改善率
テストケース数 19 7 +171%
バリデーションテスト ✅ 5項目 ❌ 0項目 -
境界値テスト ✅ 4項目 ❌ 0項目 -
代替フロー ✅ 4項目 ❌ 0項目 -
優先度分類 ✅ H/M/L ❌ なし -

直接生成で漏れた項目の例

❌ 必須項目未入力でエラーが表示される
❌ 境界値での動作確認 (00:00, 23:59)
❌ 代替フローのテスト
❌ 確認ダイアログの操作テスト

これらは全てUseCase IRの構造化によって強制的に抽出される項目です。


UseCase IR only vs Full IR(UseCase IR + Test Plan IR)

「UseCase IRだけで十分では?」という疑問に対する検証も行いました。

指標 UseCase IR only Full IR
テスト数 24-26(多い) 19-29(適切)
冗長性 高い(機械的1:1マッピング) 低い(類似テスト統合)
境界値テスト ❌ なし ✅ 自動注入
ユーザー操作パス トリガーのみ 完全なインタラクション
優先度 なし High/Medium/Low

結論: Test Plan IRは単にテストを追加するのではなく、機械的な生成をインテリジェントなテスト設計に変換します。


比較サマリー

アプローチ テスト数 バリデーション 制約 境界値 UI/UX 優先度
IRなし 7-8
UseCase IR only 24-26
Full IR 19-29

発展:他の用途への応用

UseCase IRの最大の価値は、一度作成すれば様々な用途で再利用できることです。

UseCase IRを共有するメリット

UseCase IRは仕様を構造化した抽象表現であり、特定の用途に依存しません。そのため、異なるチームや異なる目的で同じUseCase IRを活用できます。

flowchart TB
    subgraph Input["📥 入力"]
        PRD["PRD"]
        PDD["PDD"]
        Figma["Figma"]
        PR["GitHub PR"]
    end

    UC["📦 UseCase IR"]

    subgraph TestIRs["🧪 テスト系IR"]
        TP["Test Plan IR"]
        E2E["E2E Test IR"]
        INT["Integration Test IR"]
    end

    subgraph ImplIRs["🔧 実装系IR"]
        Schema["Schema IR"]
        API["API IR"]
        UI["UI Component IR"]
        BL["Business Logic IR"]
    end

    subgraph TestOutputs["📋 テスト成果物"]
        Manual["手動テスト"]
        PlaywrightCode["Playwrightテスト"]
        GoTests["Goテスト"]
    end

    subgraph ImplOutputs["💻 実装成果物"]
        Migrations["SQLマイグレーション"]
        ProtoFiles["Protoファイル"]
        Resolvers["GraphQL Resolver"]
        Handlers["gRPCハンドラ"]
        Components["Reactコンポーネント"]
        Services["Goサービス"]
    end

    Input --> UC
    UC --> TestIRs
    UC --> ImplIRs

    TP --> Manual
    E2E --> PlaywrightCode
    INT --> GoTests

    Schema --> Migrations
    Schema --> ProtoFiles
    API --> Resolvers
    API --> Handlers
    UI --> Components
    BL --> Services

共有による具体的なメリット

メリット 説明
重複作業の削減 QAチームがテスト用に仕様を読み解く作業と、開発チームが実装用に仕様を読み解く作業を一本化できる
認識のズレ防止 同じUseCase IRを参照することで、「テストで想定した仕様」と「実装された仕様」の齟齬を防げる
変更の追跡 仕様変更時にUseCase IRを更新すれば、そこから派生する全ての成果物に変更を反映できる
専門IRへの最適化 各チームは自分たちの専門IR(Test Plan IR、Schema IR等)の品質向上に集中できる

専門IRの役割

UseCase IRから各専門IRへの変換時に、用途に応じた最適化が行われます:

専門IR 追加される情報 用途
Test Plan IR 境界値、優先度、テスト戦略 手動テスト計画
E2E Test IR ページセレクタ、テストフィクスチャ Playwrightテスト生成
Integration Test IR DBフィクスチャ、モック定義 Goインテグレーションテスト
Schema IR データ型、制約、インデックス SQLマイグレーション、Proto定義
API IR エンドポイント、リクエスト/レスポンス GraphQL Resolver、gRPCハンドラ
UI Component IR 状態管理、イベントハンドラ Reactコンポーネント
Business Logic IR ドメインルール、エラーハンドリング Goサービス

このように、UseCase IRを共有の土台として、各チームが専門IRを通じて最適化された成果物を生成する構造が実現できます。


まとめ

生成AIの入出力の品質を管理するには、構造化されたフォーマットを強制することが効果的です。

課題 解決策 効果
入力の理解が不十分 IRスキーマで必須フィールドを定義 全項目を強制抽出
出力フォーマットがバラバラ 出力テンプレートで構造を定義 一貫したフォーマット

この2つを組み合わせたパイプラインにより:

  • テストカバレッジが+171%向上
  • バリデーション、境界値、代替フローの漏れがゼロに
  • どの機能でも一貫した品質のドキュメントを生成

生成AIに自由形式で出力させず、フォーマットで縛る。これが入出力の品質を安定させる鍵です。

書いた人: Claude Code
書かせた人: @kitasuke




以上の内容はhttps://tech.newmo.me/entry/2025/12/09/090000より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14