JSONが欲しいのに!AIの回答を確実に構造化する「OutputParser」の奥義
はじめに
APIとしてLLMを利用する際、最大の悩みの一つが「出力形式の不安定さ」です。「JSONで返して」と頼んでも、余計な前置き("Sure! Here is the
JSON...")が入ったり、プロパティ名が勝手に変わったりして、json.loads() でパースエラーになった経験はありませんか?
LangChainのOutputParserを使えば、スキーマ定義から自動的に指示プロンプトを生成し、返答をPythonオブジェクト(辞書やデータクラス)に確実に変換することができます。
LLMの出力はただの文字列
単なる文字列が欲しい場合は `StrOutputParser` を使いますが、アプリ開発では `PydanticOutputParser` が主役になります。
プログラムとAIを連携させる上で、「非構造化データ(テキスト)」を「構造化データ(JSONなど)」に変換することは必須のプロセスです。これを安定させるために、LangChainは以下の戦略をとります。
- Pythonの型定義(Pydantic)からスキーマを作る。
- スキーマに基づいた「JSONフォーマット指示」をプロンプトに注入する。
- AIの返答からJSON部分だけを切り出してパースする。
PydanticOutputParserの基本
まずはPydanticを使って、欲しいデータの形を定義します。
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_core.output_parsers import PydanticOutputParser
# 1. データ構造の定義
class Recipe(BaseModel):
name: str = Field(description="料理の名前")
ingredients: list[str] = Field(description="材料のリスト")
steps: list[str] = Field(description="調理手順")
# 2. パーサーの作成
parser = PydanticOutputParser(pydantic_object=Recipe)
# 3. フォーマット指示の取得
format_instructions = parser.get_format_instructions()
print(format_instructions)
# 出力: The output should be formatted as a JSON instance that conforms to the JSON schema...
実践:レシピ生成AI
プロンプトテンプレートにフォーマット指示を組み込みます。
template = """
あなたはプロの料理研究家です。
以下の食材を使った料理を1つ考案してください。
食材: {ingredients}
{format_instructions}
"""
prompt = PromptTemplate(
template=template,
input_variables=["ingredients"],
partial_variables={"format_instructions": format_instructions},
)
chain = prompt | llm | parser
result = chain.invoke({"ingredients": "卵, 玉ねぎ, 鶏肉"})
# 結果はPydanticモデルとして返ってくる
print(f"料理名: {result.name}")
print(f"材料数: {len(result.ingredients)}")
これにより、result.name のようにドットアクセスでデータを扱えるようになり、アプリへの組み込みが圧倒的に楽になります。
エラーハンドリング(RetryParser)
稀に、AIが壊れたJSONを返すことがあります。そんな時のために、LangChainには「自動修復」の仕組みがあります。
OutputFixingParser や RetryOutputParser
を使うと、パースエラーが発生した際に、AIに対して「このJSONは壊れています。修正してください」と再送(リトライ)要求を自動で行い、正しい形式を取得しようと試みます。
まとめ
OutputParserを制する者はLangChainを制します。AIを「ただのチャットボット」から「信頼できるAPIバックエンド」に昇格させるためには、この構造化出力の技術が不可欠です。
次回は、チャットボットに必須の機能である「Memory(会話履歴の保持)」の実装方法について解説します。
コメント
コメントを投稿