2
1

Apexからのプロンプトテンプレート呼び出しで、JSONモードを使う

Last updated at Posted at 2024-07-18

Apexからプロンプトテンプレート呼び出し時、JSONモード を有効にできるか?試しました。

本記事ではOpenAIのJSONモードを使用します
https://platform.openai.com/docs/guides/json-mode

JSONモードとは

一般的にプロンプトにJSONを出力させようとすると、生成コンテンツ内にJSON以外の余計な文字列が含まれる可能性があります。

OpenAIの一部対応モデルでは、JSONモード を設定することで純粋にJSONのみ生成するよう制限することができ、後続パース処理でのエラー発生の軽減、パフォーマンス改善が期待できます。

対応モデル

現時点で、以下モデルが JSONモード に対応しているようです。(2024年7月18日時点)

  • gpt-4o
  • gpt-4-turbo
  • gpt-3.5-turbo

To prevent these errors and improve model performance, when using gpt-4o, gpt-4-turbo, or gpt-3.5-turbo, you can set...

https://platform.openai.com/docs/guides/json-mode

プロンプトテンプレートでJSONモードを使うには

プロンプトテンプレートはApexから呼び出し可能です。Apexから呼び出す際、LLM側の設定を細かく指定することができます。また、LLMプロバイダー固有のパラメータを渡すことも可能です。

  • ConnectApi
    • EinsteinLlm​Additional​ConfigInput (LLM共通の設定)
      • additionalParameters (LLMプロバイダー固有の設定)

ConnectApi.​EinsteinLlm​Additional​ConfigInput

Additional configuration information for the LLM provider.

Property Type Description Required or Optional
additionalParameters Map<String, ConnectApi.​WrappedValue> Map of parameters and values for the LLM provider. Optional

https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_connectapi_input_einstein_llm_additional_config.htm

このプロパティを使い、JSONモードが有効になるようなOpenAI固有のパラメータを定義してLLMを呼び出します。

検証

フリーテキストの入力文章を指定フォーマットのJSONに変換するプロンプトを想定し、1.プロンプトテンプレート とそれを呼び出す 2.Apex を作成します。

1. プロンプトテンプレートの作成

呼び出すプロンプトテンプレートを、プロンプトビルダーで作成します。

設定

  • 種別
    • Flex
  • プロンプトテンプレート名
    • フリーテキストをJSONに変換
  • API参照名
    • convertTextToJson
  • リソース定義
    • #1
      • 名前: フリーテキスト
      • パラメータ名: inputText
      • データ型: String
    • #2
      • 名前: JSONスキーマ
      • パラメータ名: jsonSchema
      • データ型: String

image.png

プロンプトテンプレート

テンプレート本体
テンプレート
以下JSON Schemaに厳密に従って
ユーザーの入力文章からデータを抽出し
JSONデータを返答します

JSON Schema:"""
{!$Input:jsonSchema}
"""

ユーザーの入力文章:"""
{!$Input:inputText}
"""

JSON:
モデル
  • OpenAI GPT 4 Omni

image.png

2. Apexコードの作成

JSONモードでプロンプトテンプレートを呼び出すためのコード例です。

Apexコード
// テンプレートパラメータの設定
String promptTemplateName = 'convertTextToJson';
String jsonSchema = '';
String inputText = '';

// プロンプト呼び出しの設定
ConnectApi.EinsteinPromptTemplateGenerationsInput promptGenerationsInput = new ConnectApi.EinsteinPromptTemplateGenerationsInput();
promptGenerationsInput.isPreview = false;

// 入力パラメータの定義
ConnectApi.WrappedValue jsonSchemaWrappedValue = new ConnectApi.WrappedValue();
jsonSchemaWrappedValue.value = jsonSchema;
ConnectApi.WrappedValue inputTextWrappedValue = new ConnectApi.WrappedValue();
inputTextWrappedValue.value = inputText;
promptGenerationsInput.inputParams = new Map<String, ConnectApi.WrappedValue>{
  'Input:jsonSchema' => jsonSchemaWrappedValue,
  'Input:inputText' => inputTextWrappedValue
};

// LLM共通の設定
promptGenerationsInput.additionalConfig = new ConnectApi.EinsteinLlmAdditionalConfigInput();
promptGenerationsInput.additionalConfig.applicationName = 'PromptTemplateGenerationsInvocable';
promptGenerationsInput.additionalConfig.temperature = 0.0;

// OpenAI固有の設定 - JSONモード有効化
Map<String, String> jsonType = new Map<String, String>{
    'type' => 'json_object'
};
ConnectApi.WrappedValue responseFormatProperty = new ConnectApi.WrappedValue();
responseFormatProperty.value = jsonType;
promptGenerationsInput.additionalConfig.additionalParameters = new Map<String, ConnectApi.WrappedValue>{
    'response_format' => responseFormatProperty
};

// プロンプトテンプレートの呼び出し実行
ConnectApi.EinsteinPromptTemplateGenerationsRepresentation generationsOutput = ConnectApi.EinsteinLLM.generateMessagesForPromptTemplate(promptTemplateName, promptGenerationsInput);
ConnectApi.EinsteinLLMGenerationItemOutput response = generationsOutput.generations[0];
System.debug(response.text);

〜Apexコードの解説〜

[解説] テンプレートパラメータの設定
String promptTemplateName = 'convertTextToJson';
String jsonSchema = '';
String inputText = '';
  • promptTemplateName
    • 作成したプロンプトテンプレートのAPI参照名 'convertTextToJson' を指定
  • jsonSchema
    • 変換したいJSONオブジェクト構造をJSON Schema形式で記載
  • inputText
    • JSONに変換したい自然言語文章をここに記載
ConnectApi.WrappedValue jsonSchemaWrappedValue = new ConnectApi.WrappedValue();
jsonSchemaWrappedValue.value = jsonSchema;
ConnectApi.WrappedValue inputTextWrappedValue = new ConnectApi.WrappedValue();
inputTextWrappedValue.value = inputText;
promptGenerationsInput.inputParams = new Map<String, ConnectApi.WrappedValue>{
  'Input:jsonSchema' => jsonSchemaWrappedValue,
  'Input:inputText' => inputTextWrappedValue
};
  • inputParams
    • Input:jsonSchema
      • 設定したテンプレートパラメータ jsonSchema
    • Input:inputText
      • 設定したテンプレートパラメータ inputText
[解説] LLM共通の設定

LLM呼び出し時の設定を定義します。

// applicationName
promptGenerationsInput.additionalConfig.applicationName = 'PromptTemplateGenerationsInvocable';

// temperature
promptGenerationsInput.additionalConfig.temperature = 0.0;

// additionalParameters
promptGenerationsInput.additionalConfig.additionalParameters = new Map<String, ConnectApi.WrappedValue>();
  • application​Name
    • 'PromptTemplateGenerationsInvocable' を指定
  • temperature
    • 今回は生成結果のランダム性を求めてないので 0.0 を指定
  • additional​Parameters
    • MaxToken、temperature、num​Generations等以外のLLMプロバイダー固有のパラメータはここで定義
[解説] OpenAI固有の設定

プロンプトテンプレート呼び出し時のLLMプロバイダー固有(今回はOpenAI)の設定を定義します。

Map<String, String> jsonType = new Map<String, String>{
  'type' => 'json_object'
};
ConnectApi.WrappedValue responseFormatProperty = new ConnectApi.WrappedValue();
responseFormatProperty.value = jsonType;
promptGenerationsInput.additionalConfig.additionalParameters = new Map<String, ConnectApi.WrappedValue>{
  'response_format' => responseFormatProperty
};

今回は、ここにOpenAIが定めるJSONモードを有効にするための設定として、response_format を指定しています。

  • additional​Parameters
    • response_format
      • {type: "json_object"}

you can set response_format to { "type": "json_object" } to enable JSON mode. When JSON mode is enabled, the model is...

https://platform.openai.com/docs/guides/json-mode

[解説] プロンプトテンプレートの呼び出し実行

promptTemplateNamepromptGenerationsInputを渡してメソッドを呼び出すことで、組み立てられたプロンプトがLLM側にリクエストされます。

ConnectApi.EinsteinPromptTemplateGenerationsRepresentation generationsOutput = ConnectApi.EinsteinLLM.generateMessagesForPromptTemplate(promptTemplateName, promptGenerationsInput);

最終的な promptTemplateName, promptGenerationsInput は以下の通りです。

  • promptTemplateName = 'convertTextToJson'
  • promptGenerationsInput
    • isPreview = false
    • inputParams
      • Input:jsonSchema = jsonSchema
      • Input:inputText = inputText
    • additionalConfig
      • applicationName = 'PromptTemplateGenerationsInvocable'
      • temperature = 0.0
      • additionalParameters
        • response_format
          • type = 'json_object'

3. プロンプト実行

以下の入力パラメータでプロンプトテンプレートを実行すると、

入力パラメータ
String jsonSchema = '{"$schema": "https://json-schema.org/draft/2020-12/schema","type": "object","properties": {"records": {"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": ["activityDate", "account", "subject", "detail"]}}},"required": ["records"]}';
String inputText = '2024年6月10日の活動です。午前、株式会社ABCに訪問。新商品の提案を実施し、新商品の特長や価格について説明。その後、株式会社XYZにも訪問。契約更新の再確認と更新に関して話し合いました。午後には株式会社123にメールで見積もりを送付しました。';

グラウンディング後のプロンプトは以下のようになります。

プロンプト
以下JSON Schemaに厳密に従って
ユーザーの入力文章からデータを抽出し
JSONデータを返答します

JSON Schema:"""
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "records": {
      "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": ["activityDate", "account", "subject", "detail"]
      }
    }
  },
  "required": ["records"]
}
"""

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

JSON:

生成結果 (通常モードの場合)

JSONモードを使わず通常モードで出力すると、

出力 (通常モード)
```json
{
  "records": [
    {
      "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モードの場合)

JSONモードをONにすると、

出力 (JSONモードON)
{
  "records": [
    {
      "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モードを使うと、より正確で構造化されたデータを取得できるので便利です。ただ、油断は禁物です。適切なプロンプト設計が重要なのは変わらないので、プロンプトビルダーを活用して継続的にプロンプトを改善しましょう。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1