見出し画像

LangChain の HOW-TO EXAMPLES (2) - プロンプト

「LangChain」の「プロンプト」が提供する機能を紹介する HOW-TO EXAMPLES をまとめました。

前回

1. プロンプトの機能

プロンプトの機能について説明します。

1-1. PromptTemplate

「PromptTemplate」は、最も単純なプロンプトテンプレートで、任意の数の入力変数を受け取り、プロンプトを生成します。

(1) 入力変数のないプロンプトテンプレートの準備。

from langchain.prompts import PromptTemplate

# 入力変数のないプロンプトテンプレートの準備
no_input_prompt = PromptTemplate(
    input_variables=[], 
    template="かっこいい動物といえば?"
)
print(no_input_prompt.format())
かっこいい動物といえば?

(2) 1つの入力変数を持つプロンプトテンプレートの準備。

from langchain.prompts import PromptTemplate

# 1つの入力変数を持つプロンプトテンプレートの準備
one_input_prompt = PromptTemplate(
    input_variables=["adjective"], 
    template="{adjective}動物といえば?"
)
print(one_input_prompt.format(adjective="かっこいい"))
かっこいい動物といえば?

(3) 複数の入力変数を持つプロンプトテンプレートの準備。

from langchain.prompts import PromptTemplate

# 複数の入力変数を持つプロンプトテンプレートの準備
multiple_input_prompt = PromptTemplate(
    input_variables=["adjective", "content"],
    template="{adjective}{content}といえば?"
)
print(multiple_input_prompt.format(adjective="かっこいい", content="動物"))
かっこいい動物といえば?

(4) jinja2を使ったプロンプトテンプレートの準備。

from langchain.prompts import PromptTemplate

# テンプレートの準備 (jinja2)
template = """
{% for item in items %}
Q: {{ item.question }}
A: {{ item.answer }}
{% endfor %}
"""

# 入力変数の準備
items=[
    {"question": "foo", "answer": "bar"},
    {"question": "1", "answer": "2"}
]

# jinja2を使ったプロンプトテンプレートの準備
jinja2_prompt = PromptTemplate(
    input_variables=["items"],
    template=template,
    template_format="jinja2"
)
print(jinja2_prompt.format(items=items))
Q: foo
A: bar

Q: 1
A: 2

1-2. FewShotPromptTemplate

「FewShotPromptTemplate」は、いくつかの例を含むプロンプトのテンプレートです。タスクの実行方法の例をいくつか収集した場合は、このクラスが役立ちます。

(1) FewShotPromptTemplateの準備。

from langchain.prompts import FewShotPromptTemplate

# 例の準備
examples = [
    {"input": "明るい", "output": "暗い"},
    {"input": "おもしろい", "output": "つまらない"},
]

# 例のプロンプト
example_prompt = PromptTemplate(
    input_variables=["input","output"],
    template="入力: {input}\n出力: {output}",
)

# FewShotPromptTemplateの準備
prompt_from_string_examples = FewShotPromptTemplate(
    examples=examples, # 例
    example_prompt=example_prompt,  # 例のフォーマット
    prefix="すべての入力の反意語を与えてください",  # プレフィックス
    suffix="入力: {adjective}\n出力:",  # サフィックス
    input_variables=["adjective"],  # 入力変数
    example_separator="\n\n"  # プレフィックスと例とサフィックスを結合する文字

)
print(prompt_from_string_examples.format(adjective="大きい"))
すべての入力の反意語を与えてください

入力: 明るい
出力: 暗い

入力: おもしろい
出力: つまらない

入力: 大きい
出力:

1-3. ExampleSelector

「BaseExampleSelector」 は、多数の例がある場合、使用するものを選択できるセレクターです。

・LengthBasedExampleSelector
・SemanticSimilarityExampleSelector
・MaxMarginalRelevanceExampleSelector

◎ LengthBasedExampleSelector
「LengthBasedExampleSelector」は、長さを基に使用する例を選択します。 これは、コンテキストウィンドウの長さを超えるプロンプトの作成が心配な場合に役立ちます。より長い入力の場合、含める例は少なく選択されますが、短い入力の場合はより多くの例が選択されます。

(1) LengthBasedExampleSelectorの準備。

from langchain.prompts import FewShotPromptTemplate
from langchain.prompts.example_selector import LengthBasedExampleSelector

# 例の準備
examples = [
    {"input": "明るい", "output": "暗い"},
    {"input": "おもしろい", "output": "つまらない"},
    {"input": "エネルギッシュ", "output": "無気力"},
    {"input": "高い", "output": "低い"},
    {"input": "明るい", "output": "暗い"},
    {"input": "早い", "output": "遅い"},
]

# 例のプロンプト
example_prompt = PromptTemplate(
    input_variables=["input","output"],
    template="入力: {input}\n出力: {output}",
)

# LengthBasedExampleSelectorの準備
example_selector = LengthBasedExampleSelector(
    examples=examples,  # 例
    example_prompt=example_prompt,  # 例のフォーマット
    max_length=10,  # 最大長
)

# FewShotPromptTemplateの準備
prompt_from_string_examples = FewShotPromptTemplate(
    example_selector=example_selector,  # ExampleSelector
    example_prompt=example_prompt,  # 例のフォーマット
    prefix="すべての入力の反意語を与えてください",  # プレフィックス
    suffix="入力: {adjective}\n出力:",  # サフィックス
    input_variables=["adjective"],  # 入力変数
    example_separator="\n\n"  # プレフィックスと例とサフィックスを結合する文字

)
print(prompt_from_string_examples.format(adjective="大きい"))
入力の反意語を与えてください

入力: 明るい
出力: 暗い

入力: おもしろい
出力: つまらない

入力: エネルギッシュ
出力: 無気力

入力: 大きい
出力:

◎ SemanticSimilarityExampleSelector「SemanticSimilarityExampleSelector」は、どの例が入力に最も類似しているかを基に例を選択します。これは、入力とのコサイン類似度が最大の埋め込みを使用して例を見つけます。

(1) SemanticSimilarityExampleSelectorの準備。

from langchain.prompts.example_selector import SemanticSimilarityExampleSelector
from langchain.vectorstores import FAISS
from langchain.embeddings import OpenAIEmbeddings
from langchain.prompts import FewShotPromptTemplate

# 例の準備
examples = [
    {"input": "明るい", "output": "暗い"},
    {"input": "おもしろい", "output": "つまらない"},
    {"input": "エネルギッシュ", "output": "無気力"},
    {"input": "高い", "output": "低い"},
    {"input": "明るい", "output": "暗い"},
    {"input": "早い", "output": "遅い"},
]

# 例のプロンプト
example_prompt = PromptTemplate(
    input_variables=["input","output"],
    template="入力: {input}\n出力: {output}",
)

# SemanticSimilarityExampleSelectorの準備
example_selector = SemanticSimilarityExampleSelector.from_examples(
    examples,  # 例
    OpenAIEmbeddings(),  # 埋め込み生成のためのクラス
    FAISS,  # 埋め込みを保存し、類似検索するためのVectorStoreクラス
    k=1  # 作成する例の数
)

# FewShotPromptTemplateの準備
prompt_from_string_examples = FewShotPromptTemplate(
    example_selector=example_selector,  # ExampleSelector
    example_prompt=example_prompt,  # 例のフォーマット
    prefix="すべての入力の反意語を与えてください",  # プレフィックス
    suffix="入力: {adjective}\n出力:",  # サフィックス
    input_variables=["adjective"],  # 入力変数
    example_separator="\n\n"  # プレフィックス・例・サフィックスを結合する文字

)
print(prompt_from_string_examples.format(adjective="大きい"))
すべての入力の反意語を与えてください

入力: 高い
出力: 低い

入力: 大きい
出力:

◎ MaxMarginalRelevanceExampleSelector「MaxMarginalRelevanceExampleSelector」は、多様性を最適化しながら、どの例が入力に最も類似しているかの組み合わせに基づいて例を選択します。これは、入力とのコサインの類似性が最も高い埋め込みを使用して例を見つけ、それらを繰り返し追加しながら、既に選択されている例に近いことをペナルティとして追加することによって行われます。

(1) MaxMarginalRelevanceExampleSelectorの準備。

from langchain.prompts.example_selector import MaxMarginalRelevanceExampleSelector
from langchain.vectorstores import FAISS
from langchain.embeddings import OpenAIEmbeddings
from langchain.prompts import FewShotPromptTemplate

# 例の準備
examples = [
    {"input": "明るい", "output": "暗い"},
    {"input": "おもしろい", "output": "つまらない"},
    {"input": "エネルギッシュ", "output": "無気力"},
    {"input": "高い", "output": "低い"},
    {"input": "明るい", "output": "暗い"},
    {"input": "早い", "output": "遅い"},
]

# 例のプロンプト
example_prompt = PromptTemplate(
    input_variables=["input","output"],
    template="入力: {input}\n出力: {output}",
)

# MaxMarginalRelevanceExampleSelectorの準備
example_selector = MaxMarginalRelevanceExampleSelector.from_examples(
    examples,  # 例
    OpenAIEmbeddings(),  # 埋め込み生成のためのクラス
    FAISS,  # 埋め込みを保存し、類似検索するためのVectorStoreクラス
    k=2 # 作成する例の数
)

# FewShotPromptTemplateの準備
prompt_from_string_examples = FewShotPromptTemplate(
    example_selector=example_selector,  # ExampleSelector
    example_prompt=example_prompt,  # 例のフォーマット
    prefix="すべての入力の反意語を与えてください",  # プレフィックス
    suffix="入力: {adjective}\n出力:",  # サフィックス
    input_variables=["adjective"],  # 入力変数
    example_separator="\n\n"  # プレフィックス・例・サフィックスを結合する文字

)
print(prompt_from_string_examples.format(adjective="大きい"))
すべての入力の反意語を与えてください

入力: 高い
出力: 低い

入力: おもしろい
出力: つまらない

入力: 大きい
出力:

2. プロンプトのシリアライズ

プロンプトの情報は、ファイル (json or yaml) で保持することができます。

2-1. プロンプトファイルの読み込み

プロンプトファイルの読み込み手順は、次のとおりです。

(1) プロンプトファイルの準備。

・my_prompt.json

{
    "input_variables": ["adjective", "content"],
    "template_path": "my_template.txt"
}

・my_template.txt

{adjective}{content}は何?

(2) プロンプトファイルの読み込み。

from langchain.prompts import load_prompt

# プロンプトファイルの読み込み
prompt = load_prompt("my_prompt.json")

3. カスタムExampleSelector

カスタムExampleSelectorの実装手順は、次のとおりです。

(1) カスタムExampleSelectorの定義。

from langchain.prompts.example_selector.base import BaseExampleSelector
from typing import Dict, List
import numpy as np

# カスタムExampleSelectorの定義
class CustomExampleSelector(BaseExampleSelector):

    # 初期化
    def __init__(self, examples: List[Dict[str, str]]):
        self.examples = examples

    # 例の初期化
    def add_example(self, example: Dict[str, str]) -> None:
        self.examples.append(example)

    # 例の選択
    def select_examples(self, input_variables: Dict[str, str]) -> List[dict]:
        return np.random.choice(self.examples, size=2, replace=False)  # ランダムに2つ選択

(2) カスタムExampleSelectorの定義の利用。

from langchain.prompts import FewShotPromptTemplate
from langchain.prompts.example_selector import LengthBasedExampleSelector

# 例の準備
examples = [
    {"input": "明るい", "output": "暗い"},
    {"input": "おもしろい", "output": "つまらない"},
    {"input": "エネルギッシュ", "output": "無気力"},
    {"input": "高い", "output": "低い"},
    {"input": "明るい", "output": "暗い"},
    {"input": "早い", "output": "遅い"},
]

# 例のプロンプト
example_prompt = PromptTemplate(
    input_variables=["input","output"],
    template="入力: {input}\n出力: {output}",
)

# カスタムExampleSelectorの利用
example_selector = CustomExampleSelector(examples)

# FewShotPromptTemplateの準備
prompt_from_string_examples = FewShotPromptTemplate(
    example_selector=example_selector,  # ExampleSelector
    example_prompt=example_prompt,  # 例のフォーマット
    prefix="すべての入力の反意語を与えてください",  # プレフィックス
    suffix="入力: {adjective}\n出力:",  # サフィックス
    input_variables=["adjective"],  # 入力変数
    example_separator="\n\n"  # プレフィックスと例とサフィックスを結合する文字

)
print(prompt_from_string_examples.format(adjective="大きい"))
すべての入力の反意語を与えてください

入力: エネルギッシュ
出力: 無気力

入力: 早い
出力: 遅い

入力: 大きい
出力:

4. カスタムPromptTemplate

カスタムPromptTemplateの実装手順は、次のとおりです。

(1) カスタムPromptTemplateの定義。

from langchain.prompts import BasePromptTemplate
from pydantic import BaseModel

# カスタムPromptTemplateの定義
class CustomPromptTemplate(BasePromptTemplate, BaseModel):
    template: str

    # フォーマット
    def format(self, **kwargs) -> str:
        capitalized_kwargs = {k: v.upper() for k, v in kwargs.items()} # 大文字化
        return self.template.format(**capitalized_kwargs)

(2) カスタムPromptTemplateの利用。

# カスタムPromptTemplateの利用
prompt = CustomPromptTemplate(input_variables=["foo"], template="Capitalized: {foo}")
print(prompt.format(foo="lowercase"))
Capitalized: LOWERCASE

次回



いいなと思ったら応援しよう!