先週家族全員がコロナに感染しまして、ひいふう言ってました。40度近くの熱が出たけどロキソニン飲めばなんとかなったので、体調がつらいとはあんまり思わなかったけど、子供が辛くてキーキー言っているのが一番精神的に参りました。
皆様お気を付けてください。
さて、この記事はLangchainの勉強記事です。ある程度知っている人はとてもじゃないけど役には立たたないのでスルーしてね。
LangChainってなんなのさ?
まずはGoogle SGEさんの回答。
LangChain は、大規模言語モデル (LLM) を使用してサービスを開発する際に役立つライブラリです。LLM と外部リソース
(データソース、言語処理系) を組み合わせて、より高度なアプリケーションやサービスの開発をサポートすることを目的としています。
LangChain の強みは、その幅広い統合と機能にあります。LangChain の主要な機能には、次のようなものがあります:
- 会話履歴の保存
- 外部データの利用
- プロンプトの管理、最適化、シリアル化
- ユーザーと対話した履歴を記憶する
LangChain は、ChatGPT のような AI とチャットできるサービスを開発する場合に便利です。
1行目に言いたいことが凝縮されている。
Chat UIを使う以外の選択肢を色々生んでくれるようなライブラリという理解をしておけば、まずは間違いなし。
機能面でいうと一番は「外部データの利用」という部分に着目かな。
手元にあるデータを活用することが出来るようになるので、LLMが持っている情報より新しい情報を使って検索をしたい、といったニーズを満たす時にそれをかなえてくれる。(もちろんそれなりの実装は必要そうだけど。)
Langchainにはどんな機能があるのか?
Moduleと呼ばれる単位で語るととても分かりやすい。概ね以下の6つの機能に分類される。
- Model I/O:LLMとのインタフェースの役割となる機能。主にプロンプトとかLLM用のAPIとか。
- Retrieval:外部データとのインタフェースになる機能。ドキュメントを処理したり埋め込み作ったりVector Store(Faissとかqdrant)の機能を提供したり。
- Chains:プロンプトなどの呼び出しを構築する機能。複数のプロンプトを1度(見た目)に実行することが出来る。LLMのプロンプトは基本的に指示を分けることがベストプラクティスとされているので少々面倒なところがあるが、それも解消してくれている。
- Agents:入力に対し、予め定義しておいた挙動を実行してくれる機能。Google検索を使ったりPython実行してくれたりと色々なことが出来る。入力に応じて定義済みの処理のうちどんな機能を実行するかを判定する機能もあるっぽい?
- Memory:Chain実行時のアプリケーション状態を保持する機能。Chatの履歴の受け渡しなんかは通常はやりづらいので、リスト化したりなどハンドリングが簡単にできるようになる。
- Callbacks:ログに記録したりする機能。
まぁ、文字で書かれてもなんのこっちゃ感はあるので、触って覚えましょうね、に尽きる。
このサイトは分かりやすくまとまっていて触りの理解にはとても有用。
Model I/O 基本的な使い方
プロンプトテンプレートを触ってみる。テンプレートにはPromptTemplateとChatPromptTemplateの2種類が存在する。
PromptTemplateは単純なテキストで構成されていて、ChatPromptTemplateはLangChain特有(?)の構造が作られる。
PromptTemplateはHuggingFaceから落としてきたllama2のモデルをインプットにできるけど、ChatPromptTemplateは対応していない。
厳密にいうとLangchainを使った呼び出しに対応が出来ていない。Ollamaを使えばChatOllamaというAPIが使えるようなんだけど、MacOS限定でしか使えなかったのでやむなくOpenAIのAPIを呼び出してお勉強をしました。トホホ・・・。
というわけで最初はPromptTemplateから。といっても簡単。
from langchain.llms import LlamaCpp
from langchain import PromptTemplate
model_path = "./llama/llama-2-7b-chat.ggmlv3.q8_0.bin"
llm = LlamaCpp(
model_path=model_path,
temperature=0.7,
max_tokens=2000,
top_p=1,
verbose=True
)
prompt_template = PromptTemplate.from_template(
"Tell me a {adjective} joke about {content}."
)
prompt = prompt_template.format(adjective="funny", content="chickens")
result = llm(prompt)
print(result)
PromptTemplateクラスでtemplateを作り、.formatで{}内の変数に必要な入力を入れてLLMに渡している。
PromptTemplateの中に直接書く、ベタ打ちをしているのでこれだけだと使うメリットが分かりづらいかもしれないけど、
プロンプト管理だとか、何かの制約をプロンプトにつけておきたい、みたいなときはこういったテンプレートが作れると柔軟性が広がりそうだね。
次にChatPromptTemplate。
template = ChatPromptTemplate.from_messages([
("system", "You are a helpful AI bot. Your name is {name}."),
("human", "Hello, how are you doing?"),
("ai", "I'm doing well, thanks!"),
("human", "{user_input}"),
])
messages = template.format_messages(
name="Bob",
user_input="What is your name?"
)
//メッセージの中身
[SystemMessage(content='You are a helpful AI bot. Your name is Bob.', additional_kwargs={}),
HumanMessage(content='Hello, how are you doing?', additional_kwargs={}, example=False),
AIMessage(content="I'm doing well, thanks!", additional_kwargs={}, example=False),
HumanMessage(content='What is your name?', additional_kwargs={}, example=False)
//実行
llm = ChatOpenAI()
llm(template.format_messages(text='What is your name?'))
//結果
AIMessage(content='My name is Bob. How can I assist you today?', additional_kwargs={}, example=False)
テンプレートの構造とか使い方は最初のやつおほぼ同じ。{}で動的に入力させたい変数を設定しておいて、format_messagesでその変数に値を入力すればOK。PromptTemplateのときは「format」だったので少し違ってるね。
メッセージの中身として出力した内容については、
SystemMessage・・・LLMの挙動とかやってほしいことを定義するメッセージ。英語を日本語にするAIだよ!とか。
HumanMessage・・・人間の入力。ChatGPTのチャット入力欄に入力する文字のことをイメージしてもらえれば。
AIMessage・・・・・これはよくわからなかった。何のために存在しているのだろう?HumanMessageに対しての反応を無理くり作ってるのかな・・・?
最後にFewShotTemplate。これはちょっと実験付き。
まずはFewShotPromptが書かれたテンプレートをインプットにしてみる。
(公式Docのサンプルコードなので中略)
llm_text_llama = LlamaCpp(model_path='./llama/llama-2-7b-chat.ggmlv3.q8_0.bin')
llm_text_llama(prompt.format(input="Who was the father of Mary Ball Washington?"))
Are follow up questions needed here:Yes.
Follow up: What was the name of Mary Ball Washington's husband?
Intermediate answer: The husband of Mary Ball Washington was Joseph Ball.
So the final answer is: Joseph Ball
Question: Who was the director of
次に質問文をそのままLLMに渡してみる。
llm_text_llama("Who was the father of Mary Ball Washington?")
Mary Ball Washington, the mother of George Washington, was born in 1708 and died in 1789.
Her father's name is not definitively known, but there are several theories about his identity.
Some historians believe that her father may have been Joseph Ball, a planter from Lancaster County,
Virginia, while others suggest that he may have been George Eskridge,
a prominent lawyer and politician from nearby King George County.
Despite efforts to identify Mary Ball Washington's father with certainty,
the mystery surrounding his identity remains unsolved.
後者はMary Ball Washingtonの父親のことを知らない。と回答しているので、1つ目の事例においてFew Shotがきいていることが分かる。
もう少し簡単にしてみる。country : 国名 ⇒ capital:首都名、と返してほしいなぁというFew Shot Learning。
examples=[
{"country":"Japan","capital":"Tokyo"},
{"country":"United States","capital":"Washington"}
]
example_prompt = PromptTemplate(input_variables=["country", "capital"], template="country:{country} => capital:{capital}")
prompt = FewShotPromptTemplate(
examples=examples,
example_prompt=example_prompt,
suffix="country:{input}",
input_variables=["input"]
)
llm_text_llama(prompt.format(input="Korea"))
//結果
=> capital:Seoul
I hope this helps!
2例を与えることで、目的は達成することが出来た。
割愛するけど、素のプロンプトにすると会話形式のものが返ってくる。
一応↓のようにやれば、Few Shotを実現できなくもない。何もここまでどや顔しないでもってくらい返してくれる。
本当は1行目だけでいいのにね。
llm_text_llama("country:Japan => capital:Tokyo \n country: United States => capital:Washington \n country:Korea =>")
capital:Seoul
country:Canada => capital:Ottawa
country: Australia => capital:Canberra
country:Germany => capital:Berlin
country:France => capital:Paris
country:Italy => capital:Rome
country:Spain => capital:Madrid
country:United Kingdom => capital:London
country:Switzerland => capital:Bern
country:Norway => capital:Oslo
country:Denmark => capital:Copenhagen
country:Finland => capital:Helsinki
country:Russia => capital:Moscow
country:China => capital:Beijing
country:India => capital:New Delhi
country:Brazil => capital:Brasília
country:South Africa => capital:Pretoria
country:Mexico => capital:Mexico City
country:Argentina => capital:Buenos Aires
country:Chile => capital:Santiago
country:Colombia => capital:Bogotá
country:Peru => capital:
簡単な例なのでメリットを感じづらいと思うけど、ユーザ入力からFew Shot用のデータを作るアプリケーションなどを作ることを考えると、こういったFewShotTemplateがあると便利になりそうだね。
まずは一旦ここまで。