見出し画像

[LangChain]謎機能のチャット版FewShotChatMessagePromptTemplateを分かりやすく解説

前回これを解説しました。今回はその亜種のチャット版です。
前回と違い、OpenAIで通信することを想定して、最終出力物を適切なものにしています。

↓ 公式ドキュメント


・これなに?

複数のあらかじめ用意した文章と、類似した意味の文章を検索するモジュールを組み合わせると、新規のワードとそれに似た文章を合体させてくれる機能です。

例えば、
・「昨日はハンバーグ食べた」「おいしそうですね」
・「1+1はなんですか?」「2です」
みたいな色んなバリエーションの会話を蓄えておいた場合に、
「2+2はなに?」
と聞くと、
「1+1はなんですか?」
「2です」
「2+2はなに?」
の3つを1セットにして取得できます。

これの何がうれしいかというと、プロンプトに過去の会話を含めると回答の精度が上昇したり、一定のテンプレに合わせた回答に誘導できたりします。

ただ、そういう論文を読んだわけではないので、本当に有用なのかはちょっと自信ないです。

・Chat prompt templateについて

本題の前に、事前知識としてChat prompt templateについて学ぶ必要があります。なぜ?僕の知る限りだとFewShotChatMessagePromptTemplateだけだとOpanAIとAPI通信するのにあまり使い勝手がよくないからです。

とはいえ、そこまで難しいものではありません。
基本的には文字置換機能があるだけです。
サンプルを貼ります。


from langchain.prompts import ChatPromptTemplate

template = ChatPromptTemplate.from_messages([
     ("system", "あなたは役に立つ AI ボットです。あなたの名前は {name} です。"),
     ("human","こんにちは、調子はどうですか?"),
     ("ai","元気だよ、ありがとう!"),
     ("human", "{user_input}")
])

messages = template.format_messages(
    name="Bob",
    user_input="あなたの名前は?"
)

メッセージの中に、{name}と{user_input}がありますが、
その下のtemplate.format_messages関数で同じ名前の部分に言葉を入れると、それに置換してくれます。
上記だと、こうなります。

 ("system", "あなたは役に立つ AI ボットです。あなたの名前は Bob です。"),
 ("human","こんにちは、調子はどうですか?"),
 ("ai","元気だよ、ありがとう!"),
 ("human", "あなたの名前は?")

で、ここからが大事で、ChatPromptTemplateには文章だけでなくPromptやMessage系のクラスもいくつかそのまま入れることができます。
例えばこんな感じに。

from langchain.prompts import ChatPromptTemplate
from langchain.prompts.chat import SystemMessage, HumanMessagePromptTemplate

template = ChatPromptTemplate.from_messages(
    [
        SystemMessage(
            content=(
               "あなたは、ユーザーのテキストをより前向きに書き換える、役立つアシスタントです。"
            )
        ),
        HumanMessagePromptTemplate.from_template("{text}"),
    ]
)

from langchain.chat_models import ChatOpenAI

llm = ChatOpenAI()
llm(template.format_messages(text='私はおいしいものを食べるのが好きではありません。'))

SystemMessageやHumanMessagePromptTemplateはまだ知らない方もいると思いますが、これらはLangChainが用意したプロンプト用のクラスで、基本的にはチャットはこれらクラスを使います。

・AIMessage (AIのメッセージに使う)
・HumanMessage (ユーザーのメッセージに使う)
・SystemMessage (AIが従うべき命令に使う)
・ChatMessage (役割を自由に指定できるメッセージ)

だいたいは、「AIMessage 」「HumanMessage 」「SystemMessage」の3つと、〇〇PromptTemplate系が使われます。

で、話を戻しますが、このChatPromptTemplateにはFewShotChatMessagePromptTemplateも含めることが出来ます。
このように。

final_prompt = ChatPromptTemplate.from_messages(
    [
        SystemMessage(content="あなたは素晴らしい数学の教師です"),
        few_shot_prompt,  ←FewShotChatMessagePromptTemplate
        HumanMessagePromptTemplate.from_template("{input}"),
    ]
)

なので最終的には、ChatPromptTemplateの中にFewShotChatMessagePromptTemplateを含めた上で、OpenAIのAPIに渡すことになります。

・FewShotChatMessagePromptTemplateについて

基本的には前回の記事と同じです。

例えばこのようなスクリプトを書いたとします。

from langchain.prompts import (
    FewShotChatMessagePromptTemplate,
    ChatPromptTemplate,
)

examples = [
    {"input": "2+2", "output": "4"},
    {"input": "2+3", "output": "5"},
]

example_prompt = ChatPromptTemplate.from_messages(
    [
        ("human", "{input}"),
        ("ai", "{output}"),
    ]
)
few_shot_prompt = FewShotChatMessagePromptTemplate(
    example_prompt=example_prompt,
    examples=examples,
)

print(few_shot_prompt.format())

するとexampleがexample_promptというフォーマッターを通して展開されるので、出力はこのようになります。

Human: 2+2
AI: 4
Human: 2+3
AI: 5

これを調整して、SystemMessage等を使いたい場合はこのような感じになります。

from langchain.prompts import (
    FewShotChatMessagePromptTemplate,
    ChatPromptTemplate,
)
from langchain.prompts.chat import SystemMessage, HumanMessagePromptTemplate
from langchain.schema import (
    SystemMessage,
)


examples = [
    {"input": "2+2", "output": "4"},
    {"input": "2+3", "output": "5"},
]

example_formatter = ChatPromptTemplate.from_messages(
    [
        ("human", "{input}"),
        ("ai", "{output}"),
    ]
)
few_shot_prompt = FewShotChatMessagePromptTemplate(
    example_prompt=example_formatter,
    examples=examples,
)

final_prompt = ChatPromptTemplate.from_messages(
    [
        SystemMessage(content="あなたは素晴らしい数学の教師です"),
        few_shot_prompt,
        HumanMessagePromptTemplate.from_template("{input}"),
    ]
)

print(final_prompt.format_messages(input="6+6?"))

処理の流れとしては、
1, 複数の文章を用意する。
2, フォーマッターで"human":"2+2", "ai":"4"のような形に整形する。
3, その前後に任意でSystemメッセージや、Humanメッセージを加える。
4,最後に、format_messagesでチャット用の形に変換する。
すると、final_promptはこのような内容になります。

SystemMessage(content='あなたは素晴らしい数学の教師です', additional_kwargs={}),
HumanMessage(content='2+2', additional_kwargs={}, example=False),
AIMessage(content='4', additional_kwargs={}, example=False),
HumanMessage(content='2+3', additional_kwargs={}, example=False),
AIMessage(content='5', additional_kwargs={}, example=False),
HumanMessage(content='6+6?', additional_kwargs={}, example=False)

これにひと手間加えて、FewShotChatMessagePromptTemplateの中身を必要なものだけ、つまり意味が類似してるものだけ使用するようにできれば、使い勝手が良くなりそうですね。

そこで、SemanticSimilarityExampleSelectorを使用しますが、詳しくは前回の記事で解説しているので見てください。

使用したサンプルはこちらです。

from langchain.prompts import SemanticSimilarityExampleSelector
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.chat_models import ChatOpenAI

from langchain.prompts import (
    FewShotChatMessagePromptTemplate,
    ChatPromptTemplate,
)
from langchain.prompts.chat import SystemMessage, HumanMessagePromptTemplate
from langchain.schema import (
    SystemMessage,
)


examples = [
    {"input": "2+2", "output": "4"},
    {"input": "2+3", "output": "5"},
    {"input": "こんにちは", "output": "はい、こんにちは"},
    {"input": "牛は月に何と言った?", "output": "何もありません"},
]

example_formatter = ChatPromptTemplate.from_messages(
    [
        ("human", "{input}"),
        ("ai", "{output}"),
    ]
)


example_selector = SemanticSimilarityExampleSelector.from_examples(
    examples,
    OpenAIEmbeddings(),
    Chroma,
    k=1
)

few_shot_prompt = FewShotChatMessagePromptTemplate(
    input_variables=["input"],
    example_prompt=example_formatter,
    example_selector=example_selector,
)

final_prompt = ChatPromptTemplate.from_messages(
    [
        SystemMessage(content="あなたは素晴らしい数学の教師です"),
        few_shot_prompt,
        HumanMessagePromptTemplate.from_template("{input}"),
    ]
)

chat = ChatOpenAI()
print(chat(final_prompt.format_messages(input="6+6?")).content)

examplesに余計なものを足して、本当に必要なものだけを取得しているか分かりやすくしているのと、OpenAIのAPIを使用するようにしています。
APIの設定をしていない人は通信できないかもしれません。需要があれば設定方法をいつか書きます。

APIで通信した内容はこちらになります。

SystemMessage(content='あなたは素晴らしい数学の教師です', additional_kwargs={}),
HumanMessage(content='2+3', additional_kwargs={}, example=False),
AIMessage(content='5', additional_kwargs={}, example=False),
HumanMessage(content='6+6?', additional_kwargs={}, example=False)

応答は以下でした。

12


この記事が気に入ったらサポートをしてみませんか?