今回実施したのは、プロンプトテンプレートとチャットモデルを使って、シンプルなLLMアプリケーションを作るチュートリアル。
LangSmith
これはチュートリアルページに書かれている通り。
言語モデルの使用
まず、言語モデルを単体で使う方法を学ぶ。LangChainはさまざまな言語モデルをサポートしており、相互に活用できる。
LMStudioでは、OpenAIの使用のエンドポイントを公開できるため、langchain[openai]を使う。
チュートリアル内で使っている、init_chat_modelはLMStudioを使う場合には使えない(使えるかもしれないが、ChatOpenAIを使う方が簡単)ため、下記のように書き換える。
from langchain_openai import ChatOpenAI
model = ChatOpenAI(
api_key="dummy-key", # ダミーでOK
base_url="http://localhost:1234/v1", # LM Studio のエンドポイント
model="your-model-name" # LM Studio でロードしたモデル
)
呼び出しはチュートリアルと同様に行える。
from langchain_core.messages import HumanMessage, SystemMessage
messages = [
SystemMessage(content="Translate the following from English into Japanese."),
HumanMessage(content="Hello, how are you?")
]
model.invoke(messages)
ChatModelsは入力としてメッセージオブジェクトを受け取り、出力としてメッセージオブジェクトを生成する。メッセージオブジェクトはテキストコンテンツに加えて、会話の役割を伝え、ツールの呼び出しやトークンの使用回数などの重要なデータを保持する。
LangChainは、文字列またはOpenAI形式によるチャットモデルの入力もサポートしている。
model.invoke('Hello')
model.invoke([{'role': 'user', 'content': 'Hello'}])
model.invoke([HumanMessage('Hello')])
チャットモデルはRunnableであるため、非同期およびストリーミング呼び出しモードを含む標準インターフェースを公開している。これにより、チャットモデルから個々のトークンをストリーミングできる。
for token in model.stream(messages):
print(token.content, end="|")
このようにすると、
|||||||||||||||||||||こんにちは|、お|元|気|です|か|?|||
このように、トークン単位でイテレートすることができる。
Runnable Interface
「共通実行インターフェース」で、すべてのコンポーネント(モデル・チェーン・ツール・変換処理など)を一貫した方法で扱えるようにする抽象化。
LangChainにはさまざまな要素(コンポーネント)があるが、呼び出し方がそれぞれ異なるという複雑さを持っていたが、Runnable Interfaceの登場により一貫した呼び出し方ができるようになった(.invoke())
ストリーミング呼び出し
トークンが生成されるごとに逐次イベントとして受け取れる仕組み。
プロンプト
現状では、メッセージのリストを言語モデルに直接渡している。通常、メッセージはユーザー入力とアプリケーションロジックの組み合わせから構築される。アプリケーションロジックは、生のユーザー入力を受けとり、言語モデルに渡す準備が整ったメッセージのリストに変換することが一般的。一般的な変換には、システムメッセージの追加やユーザー入力に基づいたテンプレートのフォーマットなどがある。
プロンプトテンプレートは、この変換を支援するためにLangChainに設計された概念。生のユーザー入力を受けとり、言語モデルに渡す準備が整ったデータを返す。
簡単なプロンプトテンプレートを作成してみる。2つのユーザー変数を受け取れるようにする。
-
language: テキストを翻訳する言語 -
text: 翻訳するテキスト
from langchain_core.prompts import ChatPromptTemplate
system_template = "Translate the following from English into {language}"
prompt_template = ChatPromptTemplate.from_messages(
[("system", system_template), ("user", "{text}")]
)
一つのChatPromptTemplateで、複数のメッセージロールをサポートしていることに注意。languageパラメータはシステムメッセージに、textパラメータはユーザーメッセージにフォーマットする。
SystemMessageとHumanMessage
チャットモデルに渡すときの役割(role)を区別するためのラッパークラス。
LLMに文字列を入力する際に、誰がそのメッセージを発しているのかを与えることが一般的。例えば以下のようなメッセージがLLMには入力されている。
<|system|>
You are a helpful assistant.
<|user|>
Hello, how are you?
<|assistant|>
I'm fine, thank you!
このように、誰がそのメッセージを発しているのかを区別するためにこれらのクラスが存在する。
このプロンプトテンプレートへの入力は辞書。このプロンプトテンプレートを単体で操作して、どのような動作をするかを確認してみる。
prompt = prompt_template.invoke({"language": "Japanese", "text": "hi!"})
prompt
ChatPromptValue(messages=[SystemMessage(content='Translate the following from English into Japanese', additional_kwargs={}, response_metadata={}), HumanMessage(content='Hello, how are you?', additional_kwargs={}, response_metadata={})])
ChatPromptValueは、二つのメッセージからなることがわかる。
また、フォーマットされたプロンプトでチャットモデルを呼び出すこともできる。
response = model.invoke(prompt)
print(response.content)
こんにちは、お元気ですか?
まとめ
このチュートリアルでは、以下のようなことを学んだ。
- LangSmithを使うことで、LLMアプリケーションのオブザーバビリティを高めることができる
- LLMプロバイダごとにモデルの呼び出し方が異なるため、それぞれのベンダーに合わせたライブラリが用意されている(例:
langchain[openai]) - LMStudioはOpenAI形式でAPIサーバーを建てることができるので、OpenAI形式のライブラリを使用する(APIキーは不要、base_urlの指定が必須など、少し異なる点もある)
- LangChainにはRunnable Interfaceと呼ばれる、すべてのコンポーネントを共通した方法で呼び出すためのインターフェースが実装されているため、これを利用してよびだす(
.invoke) - LLMへの入力は、誰からのメッセージかを表すroleを含める必要がある。LangChainではSystemMessageやHumanMessageクラスを使うことでそれらを
invoke()時に指定することができる - プロンプトテンプレートを使えば、カスタムのプロンプトテンプレートを作成できる。
