平日、ちょっと触れていない間にLangchain のバージョンが 0.0.101 から 0.0.104 に上がっていました。Langchain への ChatGPT(GTP-3.5)の API の影響も大きいみたいですね(そりゃそうか)。
詳細は分からないですが、GPT-3.5-turbo API に投げられるメッセージが、101以前は単一のテキストメッセージだったのがリスト化できるようになったみたいです。前回苦労した辺りですね。
ということはもしかしたら、同じく前回解決できなかった {llm_output} エラーが解消できるかもしれません。
参考:
新しい Langchain でコーディングしてみる
from langchain.chat_models import ChatOpenAI
from langchain import PromptTemplate, LLMChain
from langchain.prompts.chat import (
# 汎用のメッセージテンプレート(将来の拡張に備えているのかも)
ChatPromptTemplate,
# System メッセージテンプレート
SystemMessagePromptTemplate,
# assistant メッセージテンプレート
AIMessagePromptTemplate,
# user メッセージテンプレート
HumanMessagePromptTemplate,
)
from langchain.schema import (
# それぞれ GPT-3.5-turbo API の assistant, user, system role に対応
AIMessage,
HumanMessage,
SystemMessage
)
# env に読み込ませるAPIキーの類
import key
# 環境変数にAPIキーを設定
import os
os.environ["OPENAI_API_KEY"] = key.OPEN_API_KEY
# モデル作成
chat = ChatOpenAI(temperature=0)
こんなもんコピペでいいですからね。
現在の(2023/3/9)最新の ChatGPT ではメッセージテンプレートおよびメッセージが GPT-3.5-turbo API の role に対応しています。
オリジナルの方では、 role と メッセージのペアになっていましたが、Langchain では
- SystemMessage → system role
- AIMessage → assistant role
- HumanMessage → user role
という対応になっています。また、ChatMessage というものも定義されていますが、通常は使わないと公式ドキュメントでも書かれています。これは将来の拡張に備えられているか、分岐をしやすいためにあると考えられます。
ただ、prompt injection の対策などを考えると、特別な理由がなければ使わないというポリシーでいいと思います。
前々回は prefix_messages という形で role と それに対応する content を与えていました。また、 OpenAIChatのコンストラクタに prefix_messagesを入れる必要がありました。つまり、prompt中にsystem role のメッセージを導入するなどはできませんでした
しかし、今回はメッセージのリストをコンストラクタの実行後に与えられます。
messages = [
# System role のメッセージ
SystemMessage(content="あなたは一歩一歩考えて回答する親切なアシスタントです"),
# user role(チャットのプロンプトの入力欄)のメッセージ
HumanMessage(content="中小企業診断士について教えて。")
]
# メッセージ送信
chat(messages)
結果:
content='中小企業診断士とは、中小企業の経営改善や事業承継、海外進出などの支援を行う専門家です。中小企業診断士は、中小企業庁が認定する資格であり、国家資格ではありません。\n\n中小企業診断士は、中小企業の経営課題を分析し、改善策を提案することが主な仕事です。具体的には、財務分析や市場調査、人材育成などの支援を行います。また、事業承継やM&Aなどの企業再編にも携わることがあります。\n\n中小企業診断士は、中小企業の経営者や経営幹部、または経' additional_kwargs={}
動作していますね。ただし、中小企業診断士は国家資格です。ChatGPTにも間違えられるなんて……。
また、ChatGPTとして公開されている WebGUI 版と同様に長い日本語のメッセージは切れてしまうようです。SystemMessage は英語で節約して、Token量を削減するといった工夫もいるかもしれませんね。
PromptTemplate を使ってみる
(バッチメッセージは使いどころがあんまり思いつかなかったのと簡単なので割愛)
MessagePromptTemplateを使用して、メッセージのテンプレート化できます。1つ以上の MessagePromptTemplate から ChatPromptTemplate を構築することができます。
ChatPromptTemplate の format_prompt を利用してLLMやチャットモデルの入力に使用可能です。
ちょっと何を言っているか分かりにくいですね。つまり、上で見たようにOpenAIChatで使われるChatタイプのモジュールでは、複数のメッセージをリストにして(system や user などのロールを加えて)送信できます。
MessagePromptTemplate は これらの個別の1行・role と content のペアのオブジェクトで、ChatPromptTemplate はそれらを束ねて1回のリクエストとして送信するテンプレートになります。
またこれらの定義の利便性のため、from_templateメソッドがあります。
ということでコードを見てみます。
# テンプレートの元となる文字列
template="You are a helpful assistant that translates {input_language} to {output_language}."
# Sytem role のテンプレートに
system_message_prompt = SystemMessagePromptTemplate.from_template(template)
# ユーザーからの入力はそのまま。
human_template="{text}"
# User role のテンプレートに
human_message_prompt = HumanMessagePromptTemplate.from_template(human_template)
ユーザーから入力された文字列は特別、テンプレートとして他の文字列とは組み合わせません。しかし、 system role としては翻訳者としての指示を与えますが、input_language と output_language を後の実行時に設定できるようにしています。
丁度、DeepL で入力欄と出力欄の言語を選ぶとそれぞれ input_language と outputlanguage に設定されるイメージでいいでしょう。翻訳アプリやボットを自社ツールに組み込む場合などが想定されますね。
# 先に定義したテンプレートをまとめて、ひとつのChatTemplateに
chat_prompt = ChatPromptTemplate.from_messages([system_message_prompt, human_message_prompt])
# OpenAI の API を叩いて結果を取得する。引数にはテンプレートの文字列で指示した{}の中身を指定する
# to_messagesメソッドでテンプレートからメッセージに変換・出力する
print(chat(chat_prompt.format_prompt(input_language="Japanese", output_language="English", text="5000兆円欲しい!").to_messages()))
結果:
content='"I want 5000 trillion yen!"' additional_kwargs={}
終わりに
全体的にスマートに、記述できるようになっていますね。
なによりありがたいのが、prefix_message ではなく message として system や user などの role を指定したメッセージを指示できることです。
もちろん、LLMChain や Memory にも対応しているみたいなので、旧バージョンでは期待通りの動作がさせられなかった Google との連携(LLMChain利用)といった使い方ができると期待できます。System role で GPT に期待する役割を切り替えながらの実装などもできそうですね。
LLMChain の実験までやりたかったですが、こちらはまた後日実験してみます。