LangChainのQuickstartを読む (1) - LLM, プロンプトテンプレート, チェイン

本シリーズでは、LangChainの Quickstart の内容を元にLangChainの使い方について紹介します。
今回は、LLM、プロンプトテンプレート、チェインを扱います。

langchainとそのOpenAI拡張のlangchain-openaiをインストールします。

pip install langchain
pip install langchain-openai
  • 今回使用するバージョンは以下のとおりです。
    現状、LangChainは破壊的変更が多く、バージョンが異なると正常に動作しない可能性があるため注意してください。

    $ pip list|grep langchain
    langchain                0.1.17
    langchain-community      0.0.37
    langchain-core           0.1.52
    langchain-openai         0.1.6
    langchain-text-splitters 0.0.1
    

次に、OpenAIのAPIキーを環境変数OPENAI_API_KEYに設定します。

Info
Pythonには、環境変数を設定するための python-dotenv のようなライブラリも存在しますが、
セキュリティリスク等を考慮して、極力サードパーティ製ライブラリは使用しない方針で進めます。

作業用ディレクトリに.openaiファイルを作成して、その中にAPIキーを保存します。
APIキーはここから取得します。

先ほど作成した.openaiファイルの内容を読み込み、環境変数OPENAI_API_KEYに設定します。

import os

with open('.openai') as f:
    os.environ['OPENAI_API_KEY'] = f.read().strip()

初めに、OpenAIのチャットボット(LLM)を読み込みます。
何も引数を指定しない場合はモデル gpt-3.5-turbo がデフォルトで使用されます。

from langchain_openai import ChatOpenAI

llm = ChatOpenAI()

LLMと会話するにはinvokeメソッドを使用します。

llm.invoke("LangChainとは何でしょうか?")

実行すると以下のような出力が得られます。
(使用しているgpt-3.5-turboの訓練データは2021年9月が最新であるため、出力内容は間違っています。)

AIMessage(content='LangChainは、...', response_metadata={'token_usage': {'completion_tokens': 189, 'prompt_tokens': 19, 'total_tokens': 208}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-xxxxx')
エラーが発生する場合

直近でOpenAIでは、APIの課金方式が月末払いから事前払い(プリペイド方式)に変更されました。
そのため、以下のようなエラーが発生する場合はOpenAIのドキュメントを参照して、事前に課金しておきましょう。
(課金後、反映されるまでに少し時間がかかります。)

RateLimitError: Error code: 429 - {'error': {'message': 'You exceeded your current quota, please check your plan and billing details. For more information on this error, read the docs: https://platform.openai.com/docs/guides/error-codes/api-errors.', 'type': 'insufficient_quota', 'param': None, 'code': 'insufficient_quota'}}

同じ形式のプロンプトを何回も使用する場合、テンプレートを使用することで処理を簡略化できます。

以下はシステムメッセージとして「あなたは優秀なドキュメントライターです。」という文章を渡し、
ユーザの会話をinputという名前のキーで渡すためのテンプレートを作成しています。

from langchain_core.prompts import ChatPromptTemplate

template = ChatPromptTemplate.from_messages([
    ("system", "あなたは優秀なドキュメントライターです。"),
    ("user", "{input}")
])

作成されたテンプレートのinvokeメソッドを使用すると、プロンプトを生成できます。
入力として、inputキーを持つ辞書を渡します。

template.invoke({"input": "LangChainとは何でしょうか?"})

実行すると以下のプロンプトが出力されます。

ChatPromptValue(messages=[SystemMessage(content='あなたは優秀なドキュメントライターです。'), HumanMessage(content='LangChainとは何でしょうか?')])

このプロンプトをLLMに入力することで回答を生成することが出来ます。
(引き続き、回答内容は間違っています。)

prompt = template.invoke({"input": "LangChainとは何でしょうか?"})
llm.invoke(prompt)
  • 実行結果

    AIMessage(content='LangChainは、...', response_metadata={'token_usage': {'completion_tokens': 297, 'prompt_tokens': 43, 'total_tokens': 340}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-xxxxx')
    

先程のテンプレートを使用した例では、以下のような処理を行っていました。

ユーザの入力 => テンプレート => LLM => 回答

LangChainでは、このような複数の処理をチェインと呼ばれる1つの処理にまとめる仕組みが存在します。
チェインを作成するには、各処理をパイプ(|)でつなげます。

chain = template | llm

作成されたチェインのinvokeメソッドにユーザの入力を渡すと、一連の処理を実行した結果が得られます。

chain.invoke({"input": "LangChainとは何でしょうか?"})
  • 実行結果(回答内容は間違っています)

    AIMessage(content='LangChainは、...', response_metadata={'token_usage': {'completion_tokens': 315, 'prompt_tokens': 43, 'total_tokens': 358}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-xxxxx')
    

上記コードを実行すると AIMessageオブジェクトが出力されます。
このオブジェクトを文字列に変換するには、StrOutputParserを使用します。

from langchain_core.output_parsers import StrOutputParser

output_parser = StrOutputParser()

chain = template | llm | output_parser
chain.invoke({"input": "LangChainとは何でしょうか?"})
  • 実行結果(回答内容は間違っています)

    'LangChainは、...'
    

関連記事