AI

LangChainのOutput Parser入門|CommaSeparatedListOutputParserなど主要パーサーの使い方と使い分け

Output Parserは、LLMが返すただの文字列を、Pythonのリスト・辞書・型付きオブジェクトへ変換するLangChainの部品です。この記事では、CommaSeparatedListOutputParserでリスト型応答を得る方法を軸に、主要パーサーの使い方とimport元、書式指示(format_instructions)の役割、そして新規実装で先に検討すべきwith_structured_outputとの使い分け、パース失敗時のリカバリまでを、実行できるコードで整理します。

まとめ:Output Parserの要点

  • Output Parserは、LLMの文字列出力を構造化データ(リスト・辞書・オブジェクト)に変換する。get_format_instructions()でプロンプトに書式指示を注入し、parse()で変換する二段構えが基本。
  • LCEL(LangChain Expression Language)なら prompt | model | parser と繋ぐだけでチェーン化でき、parse()の明示呼び出しは不要になる。
  • リスト型はCommaSeparatedListOutputParser、型検証つきの構造化はPydanticOutputParserが第一候補。StructuredOutputParserは旧来型で、いまはPydanticに寄せるのが定石。
  • 新規実装ではまずchatモデルのwith_structured_output()を検討する。ツール/関数呼び出しベースで、文字列を解析するパーサーより崩れにくい。Output Parserはそれが使えないモデルや追加検証が要る場面で使う。
  • パース失敗はOutputFixingParserRetryOutputParserでラップして回復させる。

Output Parserの役割と基本の仕組み

LLMの出力は自然言語の文字列で、そのままではプログラムから扱いづらい。Output Parserは「LLMに期待する書式を指示する」「返ってきた文字列を目的の型に変換する」の2役を担う。多くのパーサーはget_format_instructions()(プロンプトへ埋める書式指示文の生成)とparse()(文字列→型変換)を備え、この2つがセットで機能する。

LangChain 0.1以降、基本的なパーサーはlangchain_core.output_parsersに集約された。呼び出しはLCELで繋ぐのが現在の書き方で、prompt | model | parserとパイプすると、モデルの応答が自動でパーサーに渡る。

CommaSeparatedListOutputParserによるリスト型応答の取得

カンマ区切りの単純なリストが欲しいときはCommaSeparatedListOutputParserを使う。get_format_instructions()は「Your response should be a list of comma separated values, eg: foo, bar, baz…」という指示文を返し、これをプロンプトに埋めることでモデルにカンマ区切り出力を促す。返り値はPythonのlist[str]になる。

from langchain_core.output_parsers import CommaSeparatedListOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI

parser = CommaSeparatedListOutputParser()
prompt = PromptTemplate(
    template="{subject}を5つ挙げてください。\n{format_instructions}",
    input_variables=["subject"],
    partial_variables={"format_instructions": parser.get_format_instructions()},
)

chain = prompt | ChatOpenAI(model="gpt-4o-mini") | parser
result = chain.invoke({"subject": "日本の代表的な果物"})
print(result)
# ['りんご', 'みかん', 'ぶどう', 'いちご', 'もも']

各要素をさらに型変換したい(数値のリストにしたい等)場合は、パース後にPython側でint()などにマッピングする。要素そのものに構造がある場合は、次のPydanticやJSONのパーサーへ切り替える。

主要なOutput Parserの種類と使い分け

用途ごとに使うパーサーが変わる。import元がlangchain_corelangchainかで分かれる点に注意する(基礎的なものはcore、修正系や一部特殊なものはlangchain側)。

パーサー 出力の型 import元 主な用途
StrOutputParser str langchain_core 応答文字列をそのまま取り出す
CommaSeparatedListOutputParser list[str] langchain_core カンマ区切りの単純なリスト
JsonOutputParser dict langchain_core JSON・辞書型の応答
PydanticOutputParser Pydanticモデル langchain_core 型検証つきの構造化データ
StructuredOutputParser dict langchain 簡易構造化(旧来型)
DatetimeOutputParser datetime langchain 日時の抽出
OutputFixingParser / RetryOutputParser ラップ対象に準拠 langchain パース失敗のリカバリ

辞書がほしいだけならJsonOutputParserで足りるが、キーの欠落や型のブレを事前に弾きたいなら次のPydanticOutputParserを選ぶ。

PydanticOutputParserによる型安全な構造化

フィールドの型・必須性・説明までモデルとして定義し、応答をそのオブジェクトに束ねたいときはPydanticOutputParserを使う。Field(description=...)の記述がそのまま書式指示に反映されるため、モデルが各項目の意味を取り違えにくい。パース時に型検証が走り、条件を満たさない応答はエラーになる。

from pydantic import BaseModel, Field
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI

class Book(BaseModel):
    title: str = Field(description="書名")
    author: str = Field(description="著者名")
    year: int = Field(description="出版年")

parser = PydanticOutputParser(pydantic_object=Book)
prompt = PromptTemplate(
    template="次の書籍情報を抽出してください。\n{format_instructions}\n\n{text}",
    input_variables=["text"],
    partial_variables={"format_instructions": parser.get_format_instructions()},
)

chain = prompt | ChatOpenAI(model="gpt-4o-mini") | parser
book = chain.invoke({"text": "1999年刊行、村上春樹の『スプートニクの恋人』"})
print(book.title, book.year)
# スプートニクの恋人 1999

同じ発想でより手軽に書けるのが、先の表に挙げたStructuredOutputParserResponseSchemaだが、型の細かな検証はできない。厳密さが要るならPydanticに寄せるのが無難だ。

with_structured_outputとの使い分け(先にこちらを検討する)

ここが実装判断で一番差が出るところだ。LangChain公式は「多くのLLMは構造化出力にネイティブ対応しており、可能ならモデル側の機能を使うべき」という立場を取っている。chatモデルのwith_structured_output()は、ツール(関数)呼び出しの仕組みで構造化を担うため、プロンプトに書式指示を書いて文字列を後から解析するOutput Parserより崩れにくい。

from pydantic import BaseModel, Field
from langchain_openai import ChatOpenAI

class Book(BaseModel):
    title: str = Field(description="書名")
    year: int = Field(description="出版年")

model = ChatOpenAI(model="gpt-4o-mini").with_structured_output(Book)
book = model.invoke("『スプートニクの恋人』の書名と出版年を答えてください。")
print(book)
# title='スプートニクの恋人' year=1999

判断の目安はこうだ。OpenAIやAnthropicなどツール呼び出し対応モデルで新規に構造化するならwith_structured_output()を第一選択にする。一方、(1)ツール呼び出しに非対応のモデルを使う、(2)単純なカンマ区切りリストで十分、(3)パース後にさらに独自の後処理・再試行ロジックを挟みたい——といった場合はOutput Parserが向く。「とりあえずOutput Parser」で組む必要はなく、モデルの対応状況で選ぶ。

OutputFixingParser・RetryOutputParserによるパースエラーの回復

LLMが書式指示を守らず、括弧の欠落や余計な前置きを付けて返すとパースは失敗する。これを握り潰さず自動回復させる仕組みが2つある。OutputFixingParserは、壊れた出力をLLMにもう一度渡して整形し直させる。RetryOutputParserは、元のプロンプトごと再送して応答自体を取り直す(情報が欠落しているケースに強い)。ただし後者はparse()だけでは足りず、元のプロンプト値を渡すparse_with_prompt()で呼ぶ点に注意する。

from langchain.output_parsers import OutputFixingParser
from langchain_openai import ChatOpenAI

fixing = OutputFixingParser.from_llm(
    parser=parser,
    llm=ChatOpenAI(model="gpt-4o-mini"),
)
book = fixing.parse(broken_llm_text)  # 壊れた出力を整形して再パース

いずれもLLMを追加で1回呼ぶためコストとレイテンシは増える。まずはプロンプトに具体例を1つ足して失敗自体を減らし、それでも残る崩れをこれらで拾う、という順序が現実的だ。

よくある質問

Output Parserでパースエラーが頻発します。どう直せばよいですか?

第一に、プロンプトへget_format_instructions()を必ず埋め、期待する出力の実例を1つ添える。それでも崩れるならOutputFixingParserRetryOutputParserでラップして回復させる。恒常的に崩れるモデルなら、そもそもwith_structured_output()への切り替えを検討する。

JsonOutputParserとPydanticOutputParserはどちらを使うべきですか?

辞書がほしいだけならJsonOutputParserで十分。キーの欠落・型のブレを事前に弾きたい、あるいは各フィールドの意味を説明として書式指示に載せたいならPydanticOutputParserを使う。堅牢性を求めるほどPydantic側が有利だ。

StrOutputParserは何のために使うのですか?

chatモデルの応答はAIMessageオブジェクトで返るため、本文の文字列だけを取り出したいときにStrOutputParserを挟む。prompt | model | StrOutputParser()とすると、後続処理へ素の文字列を渡せる。

独自のOutput Parserは自作できますか?

できる。BaseOutputParserを継承し、parse()(文字列→目的の型)とget_format_instructions()(書式指示文)を実装すればよい。独自フォーマットや社内ルールに沿った変換が必要な場合に用いる。

StructuredOutputParserはもう使わない方がよいですか?

ResponseSchemaで手軽に辞書を得られるため今も動くが、型検証がなく、実務ではPydanticOutputParserwith_structured_output()に置き換えるのが定石だ。既存コードの保守以外で新規採用する理由は薄い。

関連記事

資料請求

RELATED POSTS 関連記事