はじめに
ChatGPTのようなLLMにユーザが質問すると、理路整然と自然な日本語で答えてくれる。このような出力を自作アプリ内で利用するとき、特定の情報だけを出力文字列の中から抽出したいことが良くある。JSON形式での出力を要求したり正規表現を使用することもできるが、もっと便利な手法が存在するので紹介する。
Structured Outputs
任意の文字列から必要な情報だけを抽出し構造化データに変換する方法を、LLMの業界では「Structured Outputs」と呼ぶ。以下、具体的なソースコードを見ながら説明する。最初に、全ソースを以下に示す。ここではAzure OpenAIのPython SDKを利用した。
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70  | 
						from openai import AzureOpenAI from pydantic import BaseModel import src.utils as utils class Output(BaseModel):     """     出力形式の定義     Attributes:         persons_name (str): 抽出された人物名         datetime (str): 抽出された日時         organization_names (list[str]): 抽出された組織名のリスト     """     persons_name: str     datetime: str     organization_names: list[str] REVIEW_TEXT = """ 小泉氏は2015年10月から自民党の農林部会長を務め、JAグループの改革に取り組んだ。 政府の規制改革会議は、競争力を強化するためにJA全農の株式会社化などの組織改革案を打ち出し、 小泉氏も支持していた。 """ def print_output(output: Output) -> None:     """     出力結果を整形して表示する関数     Args:         output (Output): 抽出結果のデータ     """     print(f"人物名: {output.persons_name}")     print(f"日時: {output.datetime}")     print(f"組織名: {', '.join(output.organization_names)}") if __name__ == "__main__":     # Azure OpenAIクライアントの初期化     client = AzureOpenAI(         azure_endpoint=utils.azure_openai_endpoint,         api_key=utils.azure_openai_api_key,         api_version=utils.azure_openai_api_version,     )     # レビューのテキストから情報を抽出     response = client.beta.chat.completions.parse(         model=utils.azure_openai_model_name,         messages=[             {                 "role": "system",                 "content": "あなたは情報抽出の専門家です。",             },             {                 "role": "user",                 "content": "以下のテキストから、人物名、日時、組織名を抽出してください。\n" f"{REVIEW_TEXT}",             },         ],         response_format=Output,     )     # レスポンスから抽出結果を取得     output = response.choices[0].message.parsed     # 出力結果を表示     if output:         print_output(output)  | 
					
今回のサンプルでは以下の文章(上記コードの23行目から25行目)から人名、日時、組織名を抽出することが目的である(引用元)。
| 
					 1 2 3 4 5  | 
						REVIEW_TEXT = """ 小泉氏は2015年10月から自民党の農林部会長を務め、JAグループの改革に取り組んだ。 政府の規制改革会議は、競争力を強化するためにJA全農の株式会社化などの組織改革案を打ち出し、 小泉氏も支持していた。 """  | 
					
以下、必要な個所を切り出しながら順に説明する。
出力用クラスの作成
 まず最初に出力用のクラスOutputを以下のように定義する。
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13  | 
						class Output(BaseModel):     """     出力形式の定義     Attributes:         persons_name (str): 抽出された人物名         datetime (str): 抽出された日時         organization_names (list[str]): 抽出された組織名のリスト     """     persons_name: str     datetime: str     organization_names: list[str]  | 
					
メンバ変数の内訳は以下の通りである。
 ● persons_name:人名
 ● datetime:日時
 ● organization_names:組織名
これらは今回切り出したい対象物である。
Azure OpenAIクライアントの初期化
Azure OpenAIクライアントを初期化する。
| 
					 1 2 3 4 5 6  | 
						   # Azure OpenAIクライアントの初期化     client = AzureOpenAI(         azure_endpoint=utils.azure_openai_endpoint,         api_key=utils.azure_openai_api_key,         api_version=utils.azure_openai_api_version,     )  | 
					
引数には以下3つの情報が必要である。
 ● Azure OpenAIのエンドポイント
 ● Azure OpenAIのAPIキー
 ● Azure OpenAIのAPIバージョン
これら3つの情報はあらかじめ用意しておくものである。
Azure OpenAIの実行
 関数parseを呼び出し
 ● 最初の引数modelにAzure OpenAIのデプロイ名
 ● 2番目の引数messagesに入力プロンプト
 ● 最後の引数response_formatに上で定義した出力用クラスOutput
を渡して実行する。
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15  | 
						    # レビューのテキストから情報を抽出     response = client.beta.chat.completions.parse(         model=utils.azure_openai_model_name,         messages=[             {                 "role": "system",                 "content": "あなたは情報抽出の専門家です。",             },             {                 "role": "user",                 "content": "以下のテキストから、人物名、日時、組織名を抽出してください。\n" f"{REVIEW_TEXT}",             },         ],         response_format=Output,     )  | 
					
結果の出力
最後に結果を出力する。以下の関数を定義し
| 
					 1 2 3 4 5 6 7 8 9 10  | 
						def print_output(output: Output) -> None:     """     出力結果を整形して表示する関数     Args:         output (Output): 抽出結果のデータ     """     print(f"人物名: {output.persons_name}")     print(f"日時: {output.datetime}")     print(f"組織名: {', '.join(output.organization_names)}")  | 
					
以下のように呼び出す。
| 
					 1 2 3 4 5 6  | 
						    # レスポンスから抽出結果を取得     output = response.choices[0].message.parsed     # 出力結果を表示     if output:         print_output(output)  | 
					
出力は以下の通り。
| 
					 1 2 3  | 
						人物名: 小泉 日時: 2015年10月 組織名: 自民党, JAグループ, 政府の規制改革会議, JA全農  | 
					
クラスOutputのメンバ変数に目的の文字列が格納されていることが分かる。
まとめ
今回は、LLMの出力から特定の情報だけを抽出する便利な方法を紹介した。自作アプリ内でLLMの出力を利用する際に大いに役立つ手法である。