確実にJSONだけを出力させたい！プロンプトTips

生成AIアプリケーションを作る中で、大規模言語モデル(LLM)にJSONを生成してもらいたい場面があるかと思います。

一般的にはLangChainのJSON parserや、LLMのFunction callingでJSON部分を取得する方法がありますが、本記事は、純粋にプロンプトエンジニアリングのみで期待通りのJSONを生成する方法について、試行錯誤のまとめです。

## よく陥る失敗パターン

例えば、以下のようなダミーデータをJSONで生成させたい場合、

```json:期待するJSON
{
    "activityDate": "2024-07-02",
    "account": "株式会社ダミー企業",
    "subject": "初回訪問",
    "detail": "株式会社ダミー企業との初回お打ち合わせを実施。製品Xを紹介し、・・・"
}
```

プロンプトの書き方によっては以下のようなレスポンスになり、後続のparse処理でエラーになることが考えられます。


### 失敗パターン#1 余計な文章が付加される
JSON部分のみを生成してほしいのに、前後に余計な説明文章が入ってくるパターン。
```json:NG
JSON形式を作成しました。
{
    "activityDate": "2024-07-02",
    "account": "株式会社ダミー企業",
    "subject": "初回訪問",
    "detail": "株式会社ダミー企業との初回お打ち合わせを実施。製品Xを紹介し、・・・"
}
このJSONデータには、以下の情報が含まれております。activityDa・・・
```

### 失敗パターン#2 JSON形式がおかしい
一部または全体的に、JSONが期待する形式と異なってしまうケース。
```json:NG
{
    "アクティビティ日": "2024-07-02",
    "企業": "株式会社ダミー企業",
    "タイトル": "初回訪問",
    "詳細": "株式会社ダミー企業との初回お打ち合わせを実施。製品Xを紹介し、・・・"
}
```

### 失敗パターン#3 そもそもJSONが作成されない
プロンプトの指示とフォーマットがあまりにもずれていると、そもそもJSONが全く作成されないケースも考えられます。
```text:NG
よくわかりませんでした。もう一度試してください。
```


## 解決策

上記パターンを回避し、期待するJSONを得るためのTipsです。

### :o: #1 Temperatureの設定
一般的にこの値が小さいと回答のバラツキが抑えられ、大きいとランダム度が増します。JSON生成では`失敗パターン#1 余計な文章が付加される`の発生を抑えるために、極力`temperature=0.0`に設定します。

### :o: #2 明確な指示

すごくシンプルなことですが、JSONのみを出力して欲しい旨をきちんと記述します。また、最終行を`JSON:`で終わらせることでも、`失敗パターン#1 余計な文章が付加される`の抑制効果が期待できます。


##### プロンプト
`以下フォーマットのJSONを生成してください。`

`JSONフォーマット:`
`（フォーマットについて記述）`

`最終応答は、"{"で始まり"}"で終わる。または"["で始まり"]"で終わるJSONのみを出力し、JSON以外の文字は一切応答に含めないでください。`

`JSON:`



### :o: #3 JSONフォーマットの指定

#### #3.1 JSON 形式

期待するJSONフォーマットを元に、項目値の部分に指示内容を自然言語で記述します。

```json:JSON
{
    "activityDate": "活動日（yyyy-MM-dd形式）",
    "account": "顧客企業名",
    "subject": "営業活動のタイトル",
    "detail": "営業活動の詳細"
}
```

シンプルな構造だとこれで大丈夫なことが多いです。ただ、構造が複雑になればなるほど、期待と異なる形式で出力される可能性も高まります。
  
また、目視では問題なさそうでも実は特殊な文字コードのスペースが入っていて、後続のJSON parse処理でエラー発生。という事象に遭遇したことがありました。  
 => *なるべくJSONフォーマット部分には、余計なスペース・改行を含めない方が良さそうです。*

```text:プロンプト例
以下フォーマットのJSONで返答してください。

JSONフォーマット:
{"activityDate":"活動日（yyyy-MM-dd形式）","account":"顧客企業名","subject":"営業活動のタイトル","detail":"営業活動の詳細"}

最終応答は、"{"で始まり"}"で終わる、または"["で始まり"]"で終わるJSONのみを出力し、JSON以外の文字は一切応答に含めないでください。

JSON:
```
 
#### #3.2 JSON Schema 形式
少し複雑な構造を扱う場合は、JSON Schema形式で記述するのがおすすめです。

https://json-schema.org/

```json:JSON Schema
{
  "type": "object",
  "description": "営業活動データ",
  "properties": {
    "activityDate": {"type": "string", "format": "date", "description": "営業活動の実施日"},
    "account": {"type": "string", "description": "営業活動先企業"},
    "subject": {"type": "string", "description": "営業活動の件名"},
    "detail": {"type": "string", "description": "営業活動の詳細説明"}
  }
}
```




JSON Schemaを使うとデータ型、LLMへの指示等を指定フォーマットで定義できるので、人間の可読性も確保しつつ、`失敗パターン#2 JSON形式がおかしい`の発生をだいぶ抑えられます。

```text:プロンプト例
以下のJSON Schemaに厳密に従ってJSONで返答してください。

JSON Schema:
{
    "type": "object", 
    "properties": {
        "activityDate": {"type": "string", "format": "date", "description": "営業活動の実施日"},
        "account": {"type": "string", "description": "営業活動先企業"},
        "subject": {"type": "string", "description": "営業活動の件名"},
        "detail": {"type": "string", "description": "営業活動の詳細説明"}
    }
}

最終応答は、"{"で始まり"}"で終わる、または"["で始まり"]"で終わるJSONのみを出力し、JSON以外の文字は一切応答に含めないでください。

JSON:
```

## ユースケース別のプロンプト例

これらTipsを実践した、いくつかのユースケースを想定したプロンプト例です。

### :heavy_check_mark: ダミーデータ生成

```text:プロンプト
以下のJSON Schemaに厳密に従って、ダミーの活動データ3件をJSONで生成してください。

JSON Schema:
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "array",
  "items": {
    "type": "object",
    "properties": {
      "activityDate": {"type": "string", "format": "date", "description": "営業活動の実施日"},
      "account": {"type": "string", "description": "営業活動先企業"},
      "subject": {"type": "string", "description": "営業活動の件名"},
      "detail": {"type": "string", "description": "営業活動の詳細説明"}
    }
  }
}

最終応答は、"["で始まり"]"で終わるJSONのみを出力し、JSON以外の文字は一切応答に含めないでください。

JSON:
```

```json:レスポンス
[
  {
    "activityDate": "2022-04-01",
    "account": "株式会社A",
    "subject": "新製品プレゼンテーション",
    "detail": "新製品のプレゼンテーションを行いました。詳細については、資料をご参照ください。"
  },
  {
    "activityDate": "2022-04-02",
    "account": "株式会社B",
    "subject": "契約更新の打ち合わせ",
    "detail": "契約更新についての打ち合わせを行いました。次回は具体的な条件について話し合う予定です。"
  },
  {
    "activityDate": "2022-04-03",
    "account": "株式会社C",
    "subject": "新規ビジネス提案",
    "detail": "新規ビジネスの提案を行いました。彼らは興味を持っているようで、引き続き話を進めていきます。"
  }
]
```

### :heavy_check_mark: 非構造データ ⇒ 構造データ変換
自然言語文章をJSON形式の構造データに変換
```text:プロンプト
ユーザインプットから活動データを抽出し、以下のJSON Schemaに厳密に従ってJSONを生成してください。

JSON Schema:
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "array",
  "items": {
    "type": "object",
    "properties": {
      "activityDate": {"type": "string", "format": "date", "description": "営業活動の実施日"},
      "account": {"type": "string", "description": "営業活動先企業"},
      "subject": {"type": "string", "description": "営業活動の件名"},
      "detail": {"type": "string", "description": "営業活動の詳細説明"}
    }
  }
}

ユーザインプット:
2024年6月10日の活動です。午前、株式会社ABCに訪問。新商品の提案を実施し、新商品の特長や価格について説明。その後、株式会社XYZにも訪問。契約更新の再確認と更新に関して話し合いました。午後には株式会社123にメールで見積もりを送付しました。

最終応答は、"["で始まり"]"で終わるJSONのみを出力し、JSON以外の文字は一切応答に含めないでください。

JSON:
```

```json:レスポンス
[
  {
    "activityDate": "2024-06-10",
    "account": "株式会社ABC",
    "subject": "新商品の提案",
    "detail": "新商品の特長や価格について説明"
  },
  {
    "activityDate": "2024-06-10",
    "account": "株式会社XYZ",
    "subject": "契約更新の再確認と更新",
    "detail": "契約更新に関して話し合い"
  },
  {
    "activityDate": "2024-06-10",
    "account": "株式会社123",
    "subject": "見積もりの送付",
    "detail": "メールで見積もりを送付"
  }
]
```


### :heavy_check_mark: 非構造データ ⇒ 構造データ変換（エラーハンドリング対応）

前ユースケース`非構造データ ⇒ 構造データ変換`を応用し、`失敗パターン#3 そもそもJSONが作成されない`対策tとして、例外エラーも考慮したJSON構造を定義します。
 - success - 正常時はtrue、エラー時はfalse
 - payload - 正常時、データはpayloadに格納
 - errorMessage - エラー発生時のみ、エラー内容をここに書き出す

```text:プロンプト
ユーザーインプットから活動データを抽出し、以下のJSON Schemaに厳密に従ってJSONを生成してください。（もし抽出が上手くできない場合もJSON Schemaに厳密に従ってエラー内容をJSONで生成してください）

JSON Schema:
{
  "type": "object",
  "description": "営業活動データを抽出できた場合もエラーで抽出できなかった場合も対応したJSONです",
  "properties": {"success": {"type": "boolean", "description":"営業活動データ抽出が成功したかどうか"}},
  "required": ["success"],
  "if": {"properties": { "success": { "const": true } }},
  "then": {
    "properties": {
      "payload": {
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "activityDate": {"type": "string", "format": "date", "description": "営業活動の実施日"},
            "account": {"type": "string", "description": "営業活動先企業"},
            "subject": {"type": "string", "description": "営業活動の件名"},
            "detail": {"type": "string", "description": "営業活動の詳細説明"}
          }
        }
      }
    },
    "required": ["payload"]
  },
  "else": {
    "properties": {
      "errorMessage": {"type": "string", "description": "エラー内容の詳細を記述します"}
    },
    "required": ["errorMessage"]
  }
}

ユーザーインプット:
（ユーザーインプット）

最終応答は、"{"で始まり"}"で終わる、または"["で始まり"]"で終わるJSONのみを出力し、JSON以外の文字は一切応答に含めないでください。`

JSON:
```


#### 成功時
##### ユーザーインプット
`2024年6月10日の活動です。午前、株式会社ABCに訪問。新商品の提案を実施し、新商品の特長や価格について説明。その後、株式会社XYZにも訪問。契約更新の再確認と更新に関して話し合いました。午後には株式会社123にメールで見積もりを送付しました。`

```json:レスポンス
{
  "success": true,
  "payload": [
    {
      "activityDate": "2024-06-10",
      "account": "株式会社ABC",
      "subject": "新商品の提案",
      "detail": "新商品の特長や価格について説明"
    },
    {
      "activityDate": "2024-06-10",
      "account": "株式会社XYZ",
      "subject": "契約更新の再確認",
      "detail": "契約更新に関して話し合い"
    },
    {
      "activityDate": "2024-06-10",
      "account": "株式会社123",
      "subject": "見積もりの送付",
      "detail": "メールで見積もりを送付"
    }
  ]
}
```

#### エラー発生時

##### ユーザーインプット
`おはようございます。今日はいい天気ですね。`

##### レスポンス
```json:エラー発生時のJSON
{
  "success": false,
  "errorMessage": "営業活動データが含まれていないため、抽出できませんでした。"
}
```




## 【付録】 JSON -> JSON Schema 変換

JSON Schemaを作成するのが面倒！という場合は、ChatGPTやClaudeあたりに以下プロンプトを投げてJSON Schemaのベースを作成してもらい、それを元に適宜修正していくのがおすすめです。

##### プロンプト

`以下JSONをJSON Schema 形式（スキーマ:2020-12）に変換してください。`

`JSON:`
`（期待するJSONをここに記述）`

##### Claude 3.5 Sonnet Artifactsでの実行イメージ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3112543/65f549e5-c05a-e4c2-908b-d1fd345a71f9.png)



## まとめ

JSONだけを出力させるためのプロンプトTipsをまとめます。

 - temperature=0.0に設定する
 - JSONだけ生成して欲しい旨を明確に指示する
 - JSONフォーマットはJSON Schemaで記述する
 - 最終行を`JSON:`で終わらせる
 - (JSONモード対応モデルを使う場合はONにする)