LLMアプリケーション開発のためのLangChain 前編② プロンプトトテンプレート

LangChainは、大規模な言語モデルを使用したアプリケーションの作成を簡素化するためのフレームワークです。言語モデル統合フレームワークとして、LangChainの使用ケースは、文書の分析や要約、チャットボット、コード分析を含む、言語モデルの一般的な用途と大いに重なっています。

LangChainは、PythonとJavaScriptの2つのプログラミング言語に対応しています。LangChainを使って作られているアプリケーションには、AutoGPT、LaMDA、CodeAnalyzerなどがあります。
- AutoGPTは、文章生成、翻訳、コード生成などの機能を持つアプリケーションです。
- LaMDAは、対話や文章生成を行うチャットボットです。
- CodeAnalyzerは、コードを分析するアプリケーションです。

# 記事概要

この記事では、LangChainでのプロンプトについて紹介します。このコース[[LangChain]最容易最全的中文langchain教程(持续更新ing)](https://www.bilibili.com/video/BV1Nh4y1c77H?p=4&vd_source=a38498a2570a69d082eaecc84f6d3fe7)のP4,P5,P6ベースでまとめたものです。
LangChainのオフィシャルサイトは以下の通りです。

https://python.langchain.com/docs/modules/model_io/prompts/

プロンプトは以下の左側のFormatの部分です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177240/ed2bae8f-d319-d2d9-aaaa-6e8448e371ab.png)

プロンプトは、モデルの応答を導くためにユーザーによって提供される指示や入力のセットです。

LangChainは、プロンプトの構築および操作を支援するためのいくつかのクラスと関数を提供します。
- Prompt templates: パラメータ化されたモデルの入力
- Example selectors: プロンプトに含める例を動的に選択する

まずは、Prompt templates(プロンプトテンプレート)について解説します。

# プロンプトテンプレート

プロンプトのテンプレートは、言語モデルのためのプロンプトを生成するための事前に定義されたものです。

テンプレートには、指示、少数の例、および特定のタスクに適した特定の文脈や質問が含まれる場合があります。

LangChainは、プロンプトのテンプレートを作成し、それを使用するためのツールを提供しています。
既存のテンプレートを異なる言語モデル間で簡単に再利用できるように、モデルに依存しないテンプレートの作成に取り組んでいます。通常、言語モデルは、プロンプトが文字列またはチャットメッセージのリストであることを望んでいます。

## ① 作成（文字列）

```python 
from langchain import PromptTemplate 

prompt_template = PromptTemplate.from_template(
    "{content}に関して、私に{adjective}ことを教えてください。"
)
prompt_template.format(adjective="面白い", content="クレヨンしんちゃん")
```

出力されたプロンプトは：
```python 
'クレヨンしんちゃんに関して、私に面白いことを教えてください。.'
```

以下のようにパラメータを入れなくてOKです！

```python 
from langchain import PromptTemplate

prompt_template = PromptTemplate.from_template(
"面白いジョークを教えてください。"
)
prompt_template.format()
```

出力されたプロンプトは：
```python 
'面白いジョークを教えてください。'
```

検証するため、input_variablesを指定します。
```python 
from langchain import PromptTemplate

invalid_prompt = PromptTemplate(
    input_variables=["adjective", "content"],
    template="{content}に関して、私に{adjective}ことを教えてください。"
)

invalid_prompt.format(content="ごはん",adjective="面白い")
```
出力されたプロンプト文は：
```python 
'ごはんに関して、私に面白いことを教えてください。'
```

## ① 利用（文字列）

PromptTemplateを利用して、文字列型のプロンプトテンプレートを作成しました。これからこれらを利用します。

```python 
from langchain.chat_models import ChatOpenAI
from langchain.llms import OpenAI

llm = OpenAI()
chat_model = ChatOpenAI()
```

- LLMの利用(predict)：

```python 
llm.predict(invalid_prompt.format(content="ごはん",adjective="面白い"))
```

出力結果：
```python 
'\n\n「ごはん」について、面白いことを教えるなら、\n\n・世界中の料理を知るために、様々な国のごはんを試してみるのはおもしろいです。\n\n・毎日新しいレシピを試して、料理を作りながら楽しむことができます。\n\n・料理している時間を通して、家族や友人と楽しい時間を過ごすことができます。\n\n・おいしいごはんを作るために、新しい技術や料理術を試してみるのも楽しいです。'
```

- chat_modelの利用(predict)：
```python 
chat_model.predict(invalid_prompt.format(content="ごはん",adjective="面白い"))
```
出力結果：
```python 
'ごはんに関して、面白いことをいくつか教えてみますね。\n\n1. 世界最大のごはん料理：バングラデシュでは、毎年1月に「ボシャント・ウシャップ」という祭りが開催され、参加者たちが巨大な鍋で約10,000kgものごはんを炊きます。\n\n2. ごはんのアート：一部のアーティストたちは、ごはんを使って驚くべきアート作品を作り出しています。例えば、ごはんを使った絵画や立体作品などがあります。\n\n3. ごはんの音楽：日本の研究者が、ごはんの粒が炊かれる過程で発生する音を利用して音楽を作り出す実験を行いました。それぞれの粒が異なる音を発するため、炊飯器の中でごはんが炊かれる音は、実際に楽器のようなメロディーを奏でることができるのです。\n\n4. ごはんの食べ方の文化：世界中にはさまざまなごはんの食べ方の文化があります。例えば、日本では箸を使って食べることが一般的ですが、インドでは手を使ってごはんを食べることが一般的です。また、タイやマレーシアでは、ごはんをスプーンで食べるのが一般的です。\n\nこれらの面白いごはんに関する事実は、ごはんについて新しい視点を提供してくれるかもしれません。'
```

## ② 作成（メッセージリスト）-方法1(辞書型でパラメータ入れる)

各チャットメッセージには内容が関連付けられており、さらに「role」という名前の付加的なパラメータもあります。
```python 
from langchain.prompts import ChatPromptTemplate

template = ChatPromptTemplate.from_messages([
    ("system", "あなたは役に立つAIアシスタントです、あなたの名前は{name}."),
    ("human", "こんにちは、お元気ですか?"),
    ("ai", "おかげで元気です！"),
    ("human", "{user_input}"),
])

messages_1 = template.format_messages(
    name="鈴木次郎",
    user_input="どなたですか"
)
messages_1
```

作成されたプロンプトは：
```python 
[SystemMessage(content='あなたは役に立つAIアシスタントです、あなたの名前は鈴木次郎.'),
 HumanMessage(content='こんにちは、お元気ですか?'),
 AIMessage(content='おかげで元気です！'),
 HumanMessage(content='どなたですか')]
```

## ② 利用（メッセージリスト）-方法1(辞書型でパラメータ入れる)

上記作られたメッセージリストを利用して、LLMへ投げる：

```python 
llm.predict_messages(messages_1)
```
出力結果：
```python 
AIMessage(content='？\nAI: 私は鈴木次郎と申します。AIアシスタントです。')
```

或いは chat_modelを利用：
```python 
chat_model.predict_messages(messages_1)
```

出力結果：
```python 
AIMessage(content='私はAIアシスタントの鈴木次郎です。どのようにお手伝いできますか？')
```


## ③ 作成（メッセージリスト）-方法2(インスタンス型でパラメータ入れる)

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

template = ChatPromptTemplate.from_messages(
    [
        SystemMessage(
            content=(
                "あなたは役に立つアシスタントです、ユーザの話しを "
                "もっと明るく聞こえる。"
            )
        ),
        HumanMessagePromptTemplate.from_template("{text}"),
    ]
)
messages_2 = template.format_messages(text='私はあまり美味しいものがすきではないです')
messages_2
```

作成したプロンプトは：
```python 
[SystemMessage(content='あなたは役に立つアシスタントです、ユーザの話しを もっと明るく聞こえる。'),
 HumanMessage(content='私はあまり美味しいものがすきではないです')]
```

## ③ 利用（メッセージリスト）-方法2(インスタンス型でパラメータ入れる)

```python 
llm.predict_messages(messages_2)
```

出力結果：
```python 
AIMessage(content='\nSystem: そうなんですか？どんな美味しいものが好きですか？')
```

chat_modelでも利用してみると：
```python 
chat_model.predict_messages(messages_2)
```

出力結果：
```python 
AIMessage(content='そうですか、美味しいものが好きではないんですね。でも、食べ物は人々の生活や文化の一部であり、さまざまな楽しみ方がありますよ。美味しいものを見つけるために、新しい料理やレストランを試してみるのも良いかもしれません。また、自分で料理を作ることも楽しい経験になるかもしれません。美味しいものを見つける冒険に出かけてみると、意外な発見があるかもしれませんよ！')
```

# カスタマイズしたプロンプトテンプレート

LangChainは、さまざまなタスクに対して使用できるデフォルトのプロンプトテンプレートを提供しています。ただし、一部の場合、デフォルトのプロンプトテンプレートでは要求を満たすことができないことがあるので、カスタムプロンプトテンプレートを作成する必要があります。

以下のテンプレートは、関数名を入力として受け取り、関数のソースコードを提供する形式で設定します。これを実現するために、まず関数を作成し、その関数の名前を指定するとそのソースコードを返すようにします。

## カスタマイズテンプレートの作成

```python 
# Pythonの標準ライブラリには、inspectというモジュールが含まれており、これを使用するとPythonのオブジェクト（モジュール、クラス、関数など）を調査および分析するためのツールが提供されています。

import inspect
def get_source_code(function_name):
    # Get the source code of the function
    return inspect.getsource(function_name)

def test_add():
    return 1 + 1
```

以下が定義したカスタマイズテンプレートです：

```python 
from langchain.prompts import StringPromptTemplate
from pydantic import BaseModel, validator

PROMPT = """\
与えられたソースコードと関数済に対して、この関数に対して日本語の説明を作成してください。.
関数名: {function_name}
ソースコード:
{source_code}
Explanation:
"""

class FunctionExplainerPromptTemplate(StringPromptTemplate, BaseModel):
    """関数名を入力として受け取り、プロンプトテンプレートをフォーマットして関数のソースコードを提供するカスタムプロンプトテンプレート。"""
    @validator("input_variables")
    def validate_input_variables(cls, v):
        """入力変数が正しいことを確認してください。"""
        if len(v) != 1 or "function_name" not in v:
            raise ValueError("function_name must be the only input_variable.")
        return v

    def format(self, **kwargs) -> str:
        # Get the source code of the function
        source_code = get_source_code(kwargs["function_name"])

        # Generate the prompt to be sent to the language model
        prompt = PROMPT.format(
            function_name=kwargs["function_name"].__name__, source_code=source_code
        )
        return prompt

    def _prompt_type(self):
        return "function-explainer"
```

## カスタマイズテンプレートの利用

このカスタマイズテンプレートを利用して、プロンプトを作成します：
```python 
fn_explainer = FunctionExplainerPromptTemplate(input_variables=["function_name"])

#関数"test_add"用のプロンプトを作成
prompt_1 = fn_explainer.format(function_name=test_add)
print(prompt_1)
```

出力結果（作成したプロンプト）：
```python 
与えられたソースコードと関数済に対して、この関数に対して日本語の説明を作成してください。.
関数名: test_add
ソースコード:
def test_add():
    return 1 + 1

Explanation:
```

このプロンプトを実施してみる：
```python 
from langchain import LLMChain
chat_prompt = ChatPromptTemplate.from_messages([prompt_1])
chain = LLMChain(
    llm=ChatOpenAI(),
    prompt=chat_prompt
)
chain.run({})
```
次はプロンプトの実施結果：
```python 
'関数名: test_add\nソースコード:\ndef test_add():\n    return 1 + 1\n\n説明:\nこの関数は、1と1を足した結果を返す関数です。'
```

他の複雑な関数を定義してためしてみます：

```python 
import math

def probability_permutation(n, k):
    """
    順列の確率を計算します：P(n, k) = n! / (n - k)!
    
    引数:
    n (int): 全要素数
    k (int): 選択する要素数
    
    戻り値:
    float: 順列の確率
    """
    if n < 0 or k < 0 or k > n:
        return 0.0
    permutation = math.factorial(n) / math.factorial(n - k)
    total_outcomes = math.factorial(n)
    probability = permutation / total_outcomes
    return probability
```

```python 
fn_explainer = FunctionExplainerPromptTemplate(input_variables=["function_name"])

# Generate a prompt for the function "test_add"
prompt_2 = fn_explainer.format(function_name=probability_permutation)
prompt_2
```

作ったプロンプトは：
```python 
'与えられたソースコードと関数済に対して、この関数に対して日本語の説明を作成してください。.\n関数名: probability_permutation\nソースコード:\ndef probability_permutation(n, k):\n    """\n    順列の確率を計算します：P(n, k) = n! / (n - k)!\n    \n    引数:\n    n (int): 全要素数\n    k (int): 選択する要素数\n    \n    戻り値:\n    float: 順列の確率\n    """\n    if n < 0 or k < 0 or k > n:\n        return 0.0\n    permutation = math.factorial(n) / math.factorial(n - k)\n    total_outcomes = math.factorial(n)\n    probability = permutation / total_outcomes\n    return probability\n\nExplanation:\n'
```

このプロンプトの結果を確認します：
```python 
from langchain import LLMChain
chat_prompt = ChatPromptTemplate.from_messages([prompt_2])
chain = LLMChain(
    llm=ChatOpenAI(),
    prompt=chat_prompt
)
chain.run({})
```
出力結果：
```python 
'この関数は、与えられた全要素数nと選択する要素数kに基づいて、順列の確率を計算します。\n\n順列の確率は、P(n, k) = n! / (n - k)! という式で計算されます。\n\n関数は、まず引数nとkが負の値であるか、またはkがnよりも大きい場合には0.0を返します。\n\nそれ以外の場合、関数はnの階乗を計算するためにmath.factorial()関数を使用し、n - kの階乗を計算し、それらを用いて順列を計算します。\n\nそして、順列を全体の組み合わせ数であるnの階乗で割ることで、確率を求めます。\n\n最終的に、計算された確率を返します。'
```

結果が正しそうです！


# 少数の例でのプロンプトテンプレート

ある場合には、少量の例をモデルにあげると、モデルがもっと質問者の意図を理解することができます。こちらで、少数の例でのプロンプトテンプレートは、例のセットまたはExample Selectorオブジェクトから構築することができます。

まず、いくつかの例を作ります。これらの例をリストに格納します。

## 例のプロンプトテンプレート(文字列)

### 作成

```python 
from langchain.prompts.few_shot import FewShotPromptTemplate
from langchain.prompts.prompt import PromptTemplate

examples = [
  {
    "question": "ムハマド・アリとアラン・チューリング、どちらが長生きしましたか？",
    "answer":
"""
ここでの追加の質問は必要ですか：はい。
追加の質問：ムハンマド・アリは亡くなったとき何歳でしたか？
中間の答え：ムハンマド・アリは亡くなったとき74歳でした。
追加の質問：アラン・チューリングは亡くなったとき何歳でしたか？
中間の答え：アラン・チューリングは亡くなったとき41歳でした。
したがって、最終的な答えは：ムハンマド・アリ
"""
  },
  {
    "question": "craigslistの創設者はいつ生まれたのか？",
    "answer":
"""
追加の質問が必要ですか：はい。
追加の質問：craigslistの創設者は誰ですか？
中間の答え：CraigslistはCraig Newmarkによって設立されました。
追加の質問：Craig Newmarkはいつ生まれたのか？
中間の答え：Craig Newmarkは1952年12月6日に生まれました。
最終的な答えは：1952年12月6日
"""
  },
  {
    "question": "ジョージ・ワシントンの母方の祖父は誰でしたか？",
    "answer":
"""
追加の質問が必要ですか：はい。
追加の質問：ジョージ・ワシントンの母親は誰ですか？
中間の答え：ジョージ・ワシントンの母親はMary Ball Washingtonでした。
追加の質問：Mary Ball Washingtonの父親は誰でしたか？
中間の答え：Mary Ball Washingtonの父親はJoseph Ballでした。
最終的な答えは：Joseph Ball
"""
  },
  {
    "question": "JawsとCasino Royaleの監督は同じ国出身ですか？",
    "answer":
"""
追加の質問が必要ですか：はい。
追加の質問：Jawsの監督は誰ですか？
中間の答え：Jawsの監督はSteven Spielbergです。
追加の質問：Steven Spielbergはどこの出身ですか？
中間の答え：アメリカ合衆国。
追加の質問：Casino Royaleの監督は誰ですか？
中間の答え：Casino Royaleの監督はMartin Campbellです。
追加の質問：Martin Campbellはどこの出身ですか？
中間の答え：ニュージーランド。
最終的な答えは：いいえ
"""
  }
]
```

```python 
example_prompt = PromptTemplate(input_variables=["question", "answer"], template="Question: {question}\n{answer}")

print(example_prompt.format(**examples[0]))
```

出力結果：
```python 
Question: ムハマド・アリとアラン・チューリング、どちらが長生きしましたか？

ここでの追加の質問は必要ですか：はい。
追加の質問：ムハンマド・アリは亡くなったとき何歳でしたか？
中間の答え：ムハンマド・アリは亡くなったとき74歳でした。
追加の質問：アラン・チューリングは亡くなったとき何歳でしたか？
中間の答え：アラン・チューリングは亡くなったとき41歳でした。
したがって、最終的な答えは：ムハンマド・アリ

```

次はFewShotPromptTemplateを利用して、プロンプトテンプレートを作ります。このテンプレートはインプットされた変数をもらって、例を含んだプロンプト文へ変換します。

```python 
prompt = FewShotPromptTemplate(
    examples=examples,
    example_prompt=example_prompt,
    suffix="Question: {input}",
    input_variables=["input"]
)

print(prompt.format(input="メアリー・ボール・ワシントンの父親は誰でしたか？"))
```

変換されたプロンプトは（出力結果）：
```python 
Question: ムハマド・アリとアラン・チューリング、どちらが長生きしましたか？

ここでの追加の質問は必要ですか：はい。
追加の質問：ムハンマド・アリは亡くなったとき何歳でしたか？
中間の答え：ムハンマド・アリは亡くなったとき74歳でした。
追加の質問：アラン・チューリングは亡くなったとき何歳でしたか？
中間の答え：アラン・チューリングは亡くなったとき41歳でした。
したがって、最終的な答えは：ムハンマド・アリ


Question: craigslistの創設者はいつ生まれたのか？

追加の質問が必要ですか：はい。
追加の質問：craigslistの創設者は誰ですか？
中間の答え：CraigslistはCraig Newmarkによって設立されました。
追加の質問：Craig Newmarkはいつ生まれたのか？
中間の答え：Craig Newmarkは1952年12月6日に生まれました。
最終的な答えは：1952年12月6日


Question: ジョージ・ワシントンの母方の祖父は誰でしたか？

追加の質問が必要ですか：はい。
追加の質問：ジョージ・ワシントンの母親は誰ですか？
中間の答え：ジョージ・ワシントンの母親はMary Ball Washingtonでした。
...
最終的な答えは：いいえ


Question: メアリー・ボール・ワシントンの父親は誰でしたか？
```

### 利用

```python 
from langchain.chat_models import ChatOpenAI
from langchain.llms import OpenAI

llm = OpenAI()
chat_model = ChatOpenAI()

question = prompt.format(input="メアリー・ボール・ワシントンの父親は誰でしたか？")
response = llm.predict(question)
print(response)
```

```python 
追加の質問が必要ですか：はい。
追加の質問：メアリー・ボール・ワシントンは誰ですか？
中間の答え：メアリー・ボール・ワシントンはジョージ・ワシントンの母親です。
追加の質問：メアリー・ボール・ワシントンの父親は誰でしたか？
中間の答え：メアリー・ボール・ワシントンの父親はJoseph Ballでした。
最終的な答えは：Joseph Ball
```

## 例のセレクター(文字列)

上記のケースでは、簡単な例セレクターを利用しました。全ての例が全部返されたので、こちらで、自分が例のセレクターを定義して、インプットと一番近い例を返します。

```python 
from langchain.prompts.example_selector import SemanticSimilarityExampleSelector
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings

example_selector = SemanticSimilarityExampleSelector.from_examples(
    # これが全ての例のリスト
    examples,

    # これはembeddingを生成するためのクラスで、意味的な類似性を測定するために使用されます
    OpenAIEmbeddings(),
    
    # これは、embeddingを保存し、類似性の検索を行うためのVectorStoreクラスです。
    Chroma,

    # 例の件数
    k=1
)

# 最も近い例を探す
question = "メアリー・ボール・ワシントンの父は誰でしたか?"
selected_examples = example_selector.select_examples({"question": question})
print(f"インプット: {question}")
for example in selected_examples:
    print("\n")
    for k, v in example.items():
        print(f"{k}: {v}")
```

出力結果（一つ最も近い例が選択されていた）：
```python 
インプット: メアリー・ボール・ワシントンの父は誰でしたか?


answer: 
追加の質問が必要ですか：はい。
追加の質問：ジョージ・ワシントンの母親は誰ですか？
中間の答え：ジョージ・ワシントンの母親はMary Ball Washingtonでした。
追加の質問：Mary Ball Washingtonの父親は誰でしたか？
中間の答え：Mary Ball Washingtonの父親はJoseph Ballでした。
最終的な答えは：Joseph Ball

question: ジョージ・ワシントンの母方の祖父は誰でしたか？
```

### 作成

FewShotPromptTemplate を使用して、入力変数を入力として受け取り、例を含むプロンプトにフォーマットするテンプレートを作成することができます。

```python 
# FewShotPromptTemplate を使用して、入力変数を入力として受け取り、例を含むプロンプトにフォーマットするテンプレートを作成することができます。
prompt = FewShotPromptTemplate(
    example_selector=example_selector,
    example_prompt=example_prompt,
    suffix="Question: {input}",
    input_variables=["input"]
)

message_3 = prompt.format(input="メアリー・ボール・ワシントンの父は誰でしたか？")

print(message_3)
```
作成したプロンプト文：
```python 
Question: ジョージ・ワシントンの母方の祖父は誰でしたか？

追加の質問が必要ですか：はい。
追加の質問：ジョージ・ワシントンの母親は誰ですか？
中間の答え：ジョージ・ワシントンの母親はMary Ball Washingtonでした。
追加の質問：Mary Ball Washingtonの父親は誰でしたか？
中間の答え：Mary Ball Washingtonの父親はJoseph Ballでした。
最終的な答えは：Joseph Ball


Question: メアリー・ボール・ワシントンの父は誰でしたか？
```

### 利用

LLMとChat_Modelを利用してプロンプト文を実行します。

```python 
answer = llm.predict(message_3)
print(answer)
```
出力結果（LLM実行結果）：
```python 
答え：Joseph Ball
```

```python 
answer = llm.predict(message_3)
print(answer)
```
出力結果（LLM実行結果）：
```python 
答え：Joseph Ball
```

Chat_Modelを利用する場合：
```python 
answer = chat_model.predict(message_3)
print(answer)
```

```python 
最終的な答えは：Joseph Ball
```

## チャットプロンプトテンプレート(メッセージリスト)

上のケースでのプロンプトは文字列です。これからロールを込めたチャット用プロンプトテンプレートを作ります

### 作成

```python 
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())

```

出力したプロンプト例：
```python 
Human: 2+2
AI: 4
Human: 2+3
AI: 5
```

### 利用

```python 
#最後、最終的なプロンプトを作り、モデルと一緒に利用
final_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "あなたは数学の先生です."),
        few_shot_prompt,
        ("human", "{input}"),
    ]
)
from langchain.chat_models import ChatOpenAI

chain = final_prompt | ChatOpenAI()

chain.invoke({"input": "三角形の二乗とは何ですか？"})
```

出力：
```python 
AIMessage(content='三角形の二乗という言葉は一般的には使われないので、具体的にどのような意味で使われているのかを教えていただけますか？三角形の面積を求める場合は、底辺と高さを使って面積を計算することができます。')
```

もう一つの例を見てみよう！

```python 
chain.invoke({"input": "87,98？"})
```
出力結果：
```python 
AIMessage(content='185')
```

## 例のセレクターを利用したチャットプロンプトテンプレート(メッセージリスト)

また、類似度を計算して最も近い例を選んで利用します。

### 作成

```python 

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


examples = [
    {"input": "2+2", "output": "4"},
    {"input": "2+3", "output": "5"},
    {"input": "2+4", "output": "6"},
    {"input": "牛が月に何を言うんですか?", "output": "牛は月に何と言ったか？"},
    {
        "input": "月について詩を書いてください",
        "output": "月についての詩を書いてください。一つは月のために、もう一つは私のために、私たちは月のことを話す資格があるのか？",
    },
]

# 類似性に基づいて例を選択するためにベクトルストレージを使用しているので、まずそのストレージを準備する必要があります。
to_vectorize = [" ".join(example.values()) for example in examples]
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_texts(to_vectorize, embeddings, metadatas=examples)
```

このVectorストレージを確認します。

```python 
to_vectorize
```

出力結果が：

```python 
['2+2 4',
 '2+3 5',
 '2+4 6',
 '牛が月に何を言うんですか? 牛は月に何と言ったか？',
 '月について詩を書いてください 月についての詩を書いてください。一つは月のために、もう一つは私のために、私たちは月のことを話す資格があるのか？']
```

次はexample_selectorを作ります、こちらで１つの最も近い例をさがします。

```python 
example_selector_1 = SemanticSimilarityExampleSelector(
    vectorstore=vectorstore,
    k=1,
)

# プロンプトのテンプレートは、select_examples メソッドに入力を渡すことで例を読み込みます。
example_selector_1.select_examples({"input": "馬"})
```

出力結果（これで作った最も近い例）：
```python 
[{'input': '牛が月に何を言うんですか?', 'output': '牛は月に何と言ったか？'}]
```

最後にプロンプトテンプレートを組み立てます。
FewShotChatMessagePromptTemplate を使用して、入力変数を入力として受け取り、例を含むプロンプトテンプレートにフォーマットするプロンプトテンプレートを作成することができます。
example_selector で作成したテンプレートを使ってプロンプトテンプレートを組み立てます。
```python 
from langchain.prompts import (
    FewShotChatMessagePromptTemplate,
    ChatPromptTemplate,
)

# 少数のプロンプトを定義します。
few_shot_prompt = FewShotChatMessagePromptTemplate(
    # 入力変数は、example_selector に渡す値を選択します
    input_variables=["input"],
    example_selector=example_selector_1,
    # それぞれの例のフォーマット方法を定義します。
    # この場合、各例は2つのメッセージになります:
    # 1つは人間のもの、もう1つはAIのもの
    example_prompt=ChatPromptTemplate.from_messages(
        [("human", "{input}"), ("ai", "{output}")]
    ),
)

```

### 利用
最後に、最終的なプロンプトを組み立てて、それをモデルと一緒に使用します。
```python 
final_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "あなたは数学の先生."),
        few_shot_prompt,
        ("human", "{input}"),
    ]
)
print(few_shot_prompt.format(input="月"))
```
出力結果（最も近い例）：
```python 
Human: 月について詩を書いてください
AI: 月についての詩を書いてください。一つは月のために、もう一つは私のために、私たちは月のことを話す資格があるのか？
```

実際に試してみます：
```python 
from langchain.chat_models import ChatOpenAI

chain = final_prompt | ChatOpenAI(temperature=0.0)

answer = chain.invoke({"input": "月"})
print(answer)
```

結果が：

```python 
content='月の光が照らす夜空に\n静かに浮かぶその姿は\n神秘的で美しい存在\n\n夜空を照らす月の光\n心を穏やかに包み込む\n夢を運ぶ船となる\n\n満ち欠けするその姿は\n人々の心を惹きつける\n時の流れを感じさせる\n\n月の魔法に包まれて\n夜の幻想に浸る私たち\n言葉にできない感動を抱く\n\n月の輝きに魅了されて\n私たちは語り継ぐのか\n月の存在を讃えるのか\n\n月のために語るのか\nそれとも私たちのために\n月のことを話す資格があるのか'
```

もう一つの数学の例を見てみます。

```python 
final_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "あなたは数学の先生"),
        few_shot_prompt,
        ("human", "{input}"),
    ]
)
print(few_shot_prompt.format(input="45,89"))
```
最も近い例：
```python 
Human: 2+3
AI: 5
```

この例を使って聞いてみます。
```python 
from langchain.chat_models import ChatOpenAI

chain = final_prompt | ChatOpenAI(temperature=0.0)

answer = chain.invoke({"input": "45,89"})
print(answer)
```
結果が：
```python 
content='134'
```

# テンプレートのフォーマッター

今までテンプレートで利用していたフォーマット用クラスはf-string です。

| 項目          | メリット                         | デメリット                           |
|---------------|----------------------------------|-------------------------------------|
| **f-string**  | 1. Python標準の機能である        | 1. 複雑な書式設定には不向き         |
|    `{name}`             | 2. シンプルで読みやすい          | 2. 多言語対応や拡張が難しい          |
|               | 3. 実行速度が速い                | 3. テンプレートの再利用が難しい      |
| **jinja2**    | 1. テンプレートエンジンとして非常に強力 | 1. 学習曲線がある                 |
|   ` {{ item.name }}`           | 2. 継承やマクロなどの高度な機能を持つ   | 2. レンダリングに時間がかかる場合がある |
|               | 3. 多くのWebフレームワークで採用されている(Flask や Djangoなどで) | 3. 外部のライブラリが必要         |


例えば、以下がPythonのf-string方法：

```python 
name = "Alice"
age = 30
print(f"Hello, {name}! You are {age} years old.")
```

以下がjinja2方法：
```python 
{% for item in items %}
  {{ item.name }}: {{ item.price | currency }}
{% endfor %}
```

実際に二つ方法でプロンプトを組み合わせます：

```python 
#pythonのf-string利用
from langchain.prompts import PromptTemplate

fstring_template = "{content}について、{adjective}なことを教えてください。"
prompt = PromptTemplate.from_template(fstring_template)

prompt.format(adjective="面白い", content="小学生")
```

```python 
#jinja2利用：
from langchain.prompts import PromptTemplate

jinja2_template = "{{ content }}について、{{adjective}}なことを教えてください。"
prompt = PromptTemplate.from_template(jinja2_template, template_format="jinja2")

prompt.format(adjective="面白い", content="小学生")
```

二つ方法の出力とも同じです：
```python 
'小学生について、面白いなことを教えてください。'
```

このプロンプトを利用して、LLMに投げます：

```python 
from langchain.llms import OpenAI
from langchain.chains import LLMChain

llm = OpenAI(temperature=0)
llm.predict(prompt.format(adjective="面白い", content="小学生"))
```

```python 
from langchain.llms import OpenAI
from langchain.chains import LLMChain

ask = prompt.format(adjective="面白い", content="小学生")

llm = OpenAI(temperature=0)
answer = llm.predict(ask)
print(answer)
```

出力結果：
```python 
1. 植物を観察して、その成長を記録してみよう。
2. 昆虫を探して、その生態を研究してみよう。
3. 家族で宇宙旅行をして、宇宙の不思議を探検してみよう。
4. 昔の話を聞いて、その文化を学んでみよう。
5. 昔のゲームをして、古い文化を楽しんでみよう。
6. 昔の詩を歌って、古い文化を学んでみよう。
7. 昔の芸術を観察して、その文化
```

# MessagePromptTemplateの種類


LangChainはさまざまなタイプのMessagePromptTemplateを提供しています。最も一般的に使用されるのはAIMessagePromptTemplate、SystemMessagePromptTemplate、およびHumanMessagePromptTemplateで、それぞれAIのメッセージ、システムのメッセージ、人間のメッセージを作成します。
しかし、チャットモデルが任意のロールでのチャットメッセージの受信をサポートしている場合、ChatMessagePromptTemplateを使用することができ、これによりユーザーはロールの名前を指定することができます。

## ChatMessagePromptTemplate

```python 
from langchain.prompts import ChatMessagePromptTemplate

prompt = "{subject}をあげます"

chat_message_prompt = ChatMessagePromptTemplate.from_template(role="神様", template=prompt)
chat_message_prompt.format(subject="力")
```

作成したプロンプト：

```python 
ChatMessage(content='力 をあげます', role='神様')
```

## MessagesPlaceholder 

LangChainは、MessagesPlaceholder も提供しており、フォーマットの過程で表示するメッセージを完全にコントロールすることができます。

どの役割をメールのプロンプトテンプレートとして使用すべきかわからない時や、フォーマットの過程でメールリストを挿入したい時に役立つかもしれません。

```python 
from langchain.prompts import MessagesPlaceholder,HumanMessagePromptTemplate,ChatPromptTemplate

human_prompt = " {word_count} 文字以内に、今までの会話を纏めてください。"
human_message_template = HumanMessagePromptTemplate.from_template(human_prompt)

chat_prompt = ChatPromptTemplate.from_messages([MessagesPlaceholder(variable_name="conversation"), human_message_template])
```

```python 
from langchain.schema import HumanMessage,AIMessage
human_message = HumanMessage(content="プログラミングを勉強するため、一番効率的な方法を教えてください。?")
ai_message = AIMessage(content="""\
1. プログラミング言語を選択: 学びたいプログラミング言語を決定してください。

2. 基本から始める: 変数、データ型、制御構造などの基本的なプログラミングの概念に慣れ親しむことが重要です。

3. 練習、練習、さらに練習: プログラミングを学ぶ最良の方法は、実際の経験を通じて学ぶことです。
""")

chat_prompt.format_prompt(conversation=[human_message, ai_message], word_count="10").to_messages()
```

上記作成したプロンプト文：

```python 
[HumanMessage(content='プログラミングを勉強するため、一番効率的な方法を教えてください。?'),
 AIMessage(content='1. プログラミング言語を選択: 学びたいプログラミング言語を決定してください。\n\n2. 基本から始める: 変数、データ型、制御構造などの基本的なプログラミングの概念に慣れ親しむことが重要です。\n\n3. 練習、練習、さらに練習: プログラミングを学ぶ最良の方法は、実際の経験を通じて学ぶことです。\n'),
 HumanMessage(content=' 10 文字以内に、今までの会話を纏めてください。')]
```
実施に利用してみます：
```python 
from langchain.llms import OpenAI
from langchain.chains import LLMChain

llm = OpenAI(temperature=0)
llm.predict_messages(chat_prompt.format_prompt(conversation=[human_message, ai_message], word_count="10").to_messages())
```
出力結果：
```python 
AIMessage(content='\nAI: プログラミングを学ぶには、言語を選択し、基本を学び、実践で練習する。')
```

# 部分的なプロンプトテンプレート

```python 
from langchain.prompts import PromptTemplate
prompt = PromptTemplate(template="{foo}{bar}", input_variables=["foo", "bar"])

partial_prompt = prompt.partial(foo="foo123")
print(partial_prompt.format(bar="baz"))
```
出力結果：`foo123baz`

或いは以下の方法：
```python 
prompt = PromptTemplate(template="{foo}{bar}", input_variables=["bar"], partial_variables={"foo": "foo456"})
print(prompt.format(bar="baz"))
```
出力結果：`foo456baz`


# 複数プロンプトの組み立て

PromptTemplate.compose()方法を利用して複数プロンプトを組み立てます

```python 
from langchain.prompts.pipeline import PipelinePromptTemplate
from langchain.prompts.prompt import PromptTemplate
full_template = """{introduction}

{example}

{start}"""
full_prompt = PromptTemplate.from_template(full_template)
```

上記の複数テンプレートの作成：
```python 
introduction_template = """You are impersonating {person}."""
introduction_prompt = PromptTemplate.from_template(introduction_template)
```

```python 
example_template = """Here's an example of an interaction:

Q: {example_q}
A: {example_a}"""
example_prompt = PromptTemplate.from_template(example_template)
```

```python 
start_template = """Now, do this for real!

Q: {input}
A:"""
start_prompt = PromptTemplate.from_template(start_template)
```

```python 
input_prompts = [
    ("introduction", introduction_prompt),
    ("example", example_prompt),
    ("start", start_prompt)
]
pipeline_prompt = PipelinePromptTemplate(final_prompt=full_prompt, pipeline_prompts=input_prompts)
```

```python 
pipeline_prompt.input_variables
```

出力結果(該当プロンプトでの変数)：

```python 
['example_q', 'input', 'example_a', 'person']
```


```python 
print(pipeline_prompt.format(
    person="Elon Musk",
    example_q="What's your favorite car?",
    example_a="Tesla",
    input="What's your favorite social media site?"
))
```

実施に実施してみる：

```python 
prompt_1 = pipeline_prompt.format(
    person="Elon Musk",
    example_q="What's your favorite car?",
    example_a="Tesla",
    input="What's your favorite social media site?"
)
print(prompt_1)
llm.predict(prompt_1)
```

出力結果は以下の通りです、全てElonMusk関連です。

```python 
You are impersonating Elon Musk.

Here's an example of an interaction:

Q: What's your favorite car?
A: Tesla

Now, do this for real!

Q: What's your favorite social media site?
A:
" Twitter. It's the best way to stay connected with people around the world."
```

もう一つの例を見てみます。

```python 
prompt_1 = pipeline_prompt.format(
    person="孫正義",
    example_q="普段どのウェーブサイトが好きですか",
    example_a="ヤフージャパン",
    input="お勤め先はどちらですか？"
)
print(prompt_1)
llm.predict(prompt_1)
```

結果が：
```python 
You are impersonating 孫正義.

Here's an example of an interaction:

Q: 普段どのウェーブサイトが好きですか
A: ヤフージャパン

Now, do this for real!

Q: お勤め先はどちらですか？
A:
' 現在はソフトバンクグループを経営しています。'
```

# シリアライゼーション

通常、プロンプトをpythonのコードではなく、ファイルとして保存することをおすすめします。これにより、プロンプトを簡単に共有、保存、バージョン管理することができます。

- YAMLファイルからプロンプトをロードする

```python 
prompt = load_prompt("promptstore/simple_prompt.yaml")
print(prompt.format(adjective="funny", content="chickens"))
```

```xml:simple_prompt.yaml
    _type: prompt
    input_variables:
        ["adjective", "content"]
    template:
        Tell me a {adjective} joke about {content}.
```

ロードされたプロンプト：

```python 
Tell me a funny joke about chickens.
```

- jsonファイルにYAMLのパスを保存

```python 
prompt = load_prompt("promptstore/simple_prompt_with_template_file.json")
print(prompt.format(adjective="funny", content="chickens"))
```

```simple_prompt_with_template_file.json 
    {
        "_type": "prompt",
        "input_variables": ["adjective", "content"],
        "template_path": "promptstore/simple_template.txt"
    }
```

結果は直接YAMLファイル利用と同じです。

他にもfew_shot_prompt_example_promptなどの使い方もあります。これで省略します。利用する際に、またオフィシャルサイトを参照します。


# プロンプトテンプレートの検証

プロンプトテンプレートには正しいインプットのパラメータがあるかを検証する場合があります。

- デフォルトが検証します。

例えば、実際にfooというパラメータがないので、エラーになります。

```python 
from langchain import PromptTemplate

template = "私はLangChainを勉強しています、なぜなら {reason}."

prompt_template = PromptTemplate(template=template,
                                 input_variables=["reason", "foo"]) 
```

- 検証をFalseにして外す

```python 
from langchain import PromptTemplate

template = "私はLangChainを勉強しています、なぜなら {reason}."
prompt_template = PromptTemplate(template=template,
                                 input_variables=["reason", "foo"],
                                 validate_template=False) # No error 
```

これでしたらエラーが発生しなくなります。

```python 
prompt_template
```

出力結果（このプロンプトテンプレート）：
```python 
PromptTemplate(input_variables=['reason', 'foo'], template='私はLangChainを勉強しています、なぜなら {reason}.', validate_template=False)
```

このテンプレートを利用してみる：
```python 
prompt_2 = prompt_template.format(reason="AIを各産業に活用してもらいたい")

from langchain.llms import OpenAI

llm = OpenAI()
llm.predict(prompt_2)
```

出力結果：

```python 
'\n\nLangchainを学ぶことで、AIを各産業に活用するための手助けになります。Langchainは、自然言語の解析、機械翻訳、自然言語処理などを提供するプラットフォームであり、AIを活用して、産業の効率性を高めることができます。Langchainは、企業や産業のデータを解析し、それを用いて、AIを活用したソリューションを提供します。また、言語処理に関するAIの課題を解決するた'
```

# 最後に

プロンプトテンプレートに関しては、更に詳細な内容はこちらです。

https://python.langchain.com/docs/modules/model_io/prompts/prompt_templates/

現段階では、概要だけでよいので、今後必要がある際に、こちらを参照します。