Output Parser (出力パーサー)
LLM言語モデルはテキスト形式でコンテンツを出力しますが、AIアプリケーションを開発する際には、結果を対象オブジェクト、配列などに変換するなど、整形されたコンテンツを受け取りたいと考えています。これには、LangChainが提供する出力パーサーが必要です。
出力パーサーの機能は、言語モデルによって返される結果をフォーマットすることです。出力パーサーには、次の2つの必須メソッドを実装する必要があります。
- "get_format_instructions": 言語モデルがどのようなフォーマットで返すかについての命令を含む文字列を返します。
- "parse": モデルによって返されたコンテンツを対象の形式にパースします。
それではLangChainで組み込まれた出力パーサーを見てみましょう。
Pydantic Parser(Pydanticパーサー)
以下はLangChainによってカプセル化されたコアの出力パーサー「PydanticOutputParser」です。このパーサーはPythonのpydanticライブラリをベースにしており、モデルの出力結果をPythonオブジェクトに変換するために使用されます。
from langchain.output_parsers import PydanticOutputParser
from langchain.prompts import PromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field, validator
from langchain_openai import OpenAI
model = OpenAI(model_name="gpt-3.5-turbo-instruct", temperature=0.0)
class Joke(BaseModel):
setup: str = Field(description="ジョークの設定を表す質問")
punchline: str = Field(description="ジョークの答え")
@validator("setup")
def question_ends_with_question_mark(cls, field):
if field[-1] != "?":
raise ValueError("質問の形式が正しくありません!")
return field
parser = PydanticOutputParser(pydantic_object=Joke)
prompt = PromptTemplate(
template="ユーザーのクエリに回答してください。\n{format_instructions}\n{query}\n",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)
prompt_and_model = prompt | model
output = prompt_and_model.invoke({"query": "ジョークを教えて。"})
parser.invoke(output)
サンプルの返り値:
Joke(setup='Why did the chicken cross the road?', punchline='To get to the other side!')
LCEL インタフェース
Runnable インタフェース
出力パーサーはRunnableインタフェースを実装しており、これはLangChain Expression Language(LCEL)の基本的な構成要素の1つです。invoke
、ainvoke
、stream
、astream
、batch
、abatch
、astream_log
などの呼び出しメソッドをサポートしています。
LCELでの出力パーサーの適用
出力パーサーは文字列またはBaseMessage
を入力として受け取り、任意のタイプの構造化されたデータを返します。出力パーサーをRunnableシーケンスに追加してパーサーチェーンを構築し、呼び出すことができます。
chain = prompt | model | parser
chain.invoke({"query": "ジョークを教えて。"})
返り値
Joke(setup='Why did the chicken cross the road?', punchline='To get to the other side!')
一部のパーサーはSimpleJsonOutputParser
のように部分的なパースオブジェクトをストリーミングできますが、他のパーサーはストリーミングをサポートしていません。最終的な出力は、パーサーが部分的なパースオブジェクトを構築できるかどうかに依存します。
from langchain.output_parsers.json import SimpleJsonOutputParser
json_prompt = PromptTemplate.from_template(
"質問:{question}に答える`answer`キーを持つJSONオブジェクトを返す"
)
json_parser = SimpleJsonOutputParser()
json_chain = json_prompt | model | json_parser
list(json_chain.stream({"question": "顕微鏡は誰が発明しましたか?"}))
[{},
{'answer': ''},
{'answer': 'Ant'},
{'answer': 'Anton'},
{'answer': 'Antonie'},
{'answer': 'Antonie van'},
{'answer': 'Antonie van Lee'},
{'answer': 'Antonie van Leeu'},
{'answer': 'Antonie van Leeuwen'},
{'answer': 'Antonie van Leeuwenho'},
{'answer': 'Antonie van Leeuwenhoek'}]
LCELでは、さまざまな要件を満たすために異なるパーサーを組み合わせることで、複雑なデータ処理フローを構築することができます。