RareJob Tech Blog

レアジョブテクノロジーズのエンジニア・デザイナーによる技術ブログです

LangChainのふんいき(LangChain触ってみたN番煎じ)

導入

こんにちは。

プロダクト開発部 PROGOS・SMART Method 開発グループの奥山です。

レアジョブテクノロジーズでは月1回、知見発表会があります。
月毎に部署が割り当てられ、各々が知見を発表していきます。
この会で紹介する内容に縛りはなく、最近自分が遊んだ技術を紹介することも可能です。
今回は、この知見発表会でゆるく話したLangChainのスライドをそのままだとネタスライドがあって出せないのでブログ記事に改変してお送りします。

何が書いてあるか?

LangChainの小さなコードを積み上げていって、1回だけ質問に答えてくれるコードを書きます。

読む意味はあるか?

私の理解はLangChainのドキュメントの土地勘がちょっと生えたレベルです。
よって、LangChainドキュメントの土地勘を生やすのに少し役立つかもしれません。
網羅的に扱っていないため、全てを解説したドキュメントをお探しの場合は公式をご覧ください。

注意点

ゆるいです。

概要

  1. LangChainのイメージをゆるく説明します
  2. 環境構築します
  3. コードを小さく積み上げていきます

LangChainのゆるい説明

ウェルカムページにはこう書かれてます。

LangChain is a framework for developing applications powered by language models. We believe that the most powerful and differentiated applications will not only call out to a language model

言語モデルを利用して開発する際に利用するフレームワークとして開発されたようです。

言語モデルといえば、OpenAIのGPT系などでしょうか。

フレームワークといえば、開発する際に色々と便利なツールや機能のカタマリ、みたいなイメージでしょうか。

言語モデルを用いた開発が手軽にできるんだな〜というイメージを一旦持っておくことにします。

https://python.langchain.com/en/latest/index.html

環境構築

python3系が入っていることが前提です。 自分は下記で構築しました。

pip install langchain
pip install openai
export OPENAI_API_KEY="..."

OPENAI_API_KEYはご自身でOpenAIのアカウントを作成し入手してください。

おすすめの読み方

ドキュメントの土地勘をふんわり掴むことを念頭に置いてるので、貼られたリンクを踏みながら目次の場所を眺めます。
そのあとサンプルコードをコピペか写経するかして動かしてみてください。

Step1 Language Modelsを利用する

公式はこちらです。
https://python.langchain.com/docs/modules/model_io/models/

Language Modelsの項目にはLLMsとChatModelsが見えていると思います。

LangChain Language Models 目次
両方とも、言語モデルを提供しているAPIをラップしています。 実際にこのStepで紹介するOpenAIクラスを覗いてみます。

OpenAIクラス(597行目)の実装を見てみると、BaseOpenAIクラス(123行目)を継承してるので、そこを読みにいきます。 https://github.com/hwchase17/langchain/blob/e9877ea8b1d301efafb7d2fda5f9105b005774b8/langchain/llms/openai.py

利用している言語モデルはtext-davinci-003。

model_name: str = "text-davinci-003"

エンドポイントはcompletionのようです。

values["client"] = openai.Completion

https://platform.openai.com/docs/api-reference/completions

では、LLMsを実際に利用します。

from langchain.llms import OpenAI

llm = OpenAI(temperature=0.9)

prompt = "英会話サービス運営企業のバックオフィス用に開発したチャットボットに名前をつけてください。"
print(llm(prompt))

結果例

NiceTalker.

LangChainにはLLMsの他に、ChatModelsも用意されてます。

https://python.langchain.com/docs/modules/model_io/models/chat/

今回利用するChatOpenAIクラスは、gpt3.5-turboモデルがデフォで、chat/completionsをラップしているようです。

ChatModelクラスは下記の通りに利用できます。

from langchain.chat_models import ChatOpenAI
from langchain.schema import HumanMessage

chat = ChatOpenAI(temperature=0.0)

prompt = "英会話サービス運営企業のバックオフィス用に開発したチャットボットに名前をつけてください。"

result = chat([HumanMessage(content=prompt)])

print(result)

結果例

content='「ChatPal」という名前を提案します。' additional_kwargs={} example=False

また、LLMsもChatModelsも、引数temperatureを0に設定すると、何度実行しても同じ結果が戻ります。

Step2 PromptTemplateを利用する

公式ドキュメントはこちらです。 https://python.langchain.com/docs/modules/model_io/prompts/prompt_templates/

説明するよりサンプルをご覧になった方が早いかと思います。

from langchain.prompts import PromptTemplate

prompt = PromptTemplate.from_template("{product}の名前は何が良いでしょうか?")
resutl1 = prompt.format(product="子供向け英会話サービス")

prompt = PromptTemplate.from_template("{adjective}{product}の名前は何が良いでしょうか?")
result2 = prompt.format(adjective="かわいい", product="子供向け英会話サービス")

print(resutl1)
print(result2)

結果例

子供向け英会話サービスの名前は何が良いでしょうか?
かわいい子供向け英会話サービスの名前は何が良いでしょうか?

プロンプトの一部を虫食い状態にできるので、プロンプトを抽象化して利用可能であることがポイントです。

Step3 Chainsを利用する

公式ドキュメントはこちらです。

https://python.langchain.com/docs/modules/chains/

このサンプルではLLMChainというクラスを利用します。 引数にModelsとTemplateを渡して動かします。 Modelsは、単に私がChatOpenAIクラスをなんとなく好きなので利用してます。

from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from langchain.chat_models import ChatOpenAI

prompt = PromptTemplate(
    input_variables=["product", "something"],
    template="{product}の名前は何が良いでしょうか?また、{something}も考えてください。",
)

llm = ChatOpenAI(temperature=0.9)

chain = LLMChain(llm=llm, prompt=prompt)

print(chain.run({
    "product": "必ず喋れることをコンセプトにした英会話サービス", 
    "something": "コンセプト"
    }))

結果例

1. "SureSpeak": コンセプトは、自信を持って会話ができることを強調し、利用者に自信と満足感を与えること。
2. "TalkMaster":コンセプトは、利用者が英語のマスターになり、自在に会話ができることを強調。
3. "FluentChat":コンセプトは、流暢な英会話ができることを強調し、利用者が自然な会話を楽しめること。
4. "ConverseEase":コンセプトは、利用者が英会話を簡単に行えることを強調し、利用者の学習体験をスムーズにすること。
5. "TalkSure":コンセプトは、利用者が自信を持って英会話ができることを強調し、利用者の自己表現能力向上を支援すること。

ModelsとPromptTemplatesをChainsのコンストラクタ引数として渡すことで利用可能です。

Step4 Document Loadersを利用する

公式ドキュメントはこちらです。

https://python.langchain.com/docs/modules/data_connection/document_loaders/

名前の通り、ドキュメントをロードします。
まずは利用方法を見てみます。
今回はWebからドキュメントを取得してみます。
弊社の主力プロダクト、レアジョブ英会話のLPから取得してみます。

www.rarejob.com

下準備として、下記コマンドを打っておきます。 このコマンドは、今回利用するPlaywrightURLLoaderのドキュメントから拝借してます。

https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/url

pip install "playwright"
pip install "unstructured"
playwright install
from langchain.document_loaders import PlaywrightURLLoader

urls = [
    "https://www.rarejob.com/"
]
loader = PlaywrightURLLoader(urls=urls, remove_selectors=["header", "footer"])
data = loader.load()

print(data)

出力例

[Document(page_content='オンライン英会話No.1 レアジョブ英会話\n\n無料登録\n\nログイン\n\n法人向け|\n\n学校向け|\n\n初めての方へ\n                        体験レッスンとは\n                        講師について\n                        ご利用の流れ\n                        レッスンルームについて\n                        お客様の声\n\n料金プラン\n\nサービス\n                        日常英会話コース\n                        ビジネス英会話コース\n                        中学・高校生コース\n                        スピーキングテスト\n                        レッスンチケット\n                        英会話コーチングのスマートメソッド®\n\n教材\n\n予約・講師検索\n\n初めての方へ\n                    \n                        体験レッスンとは\n                        講師について\n                        ご利用の流れ\n                        レッスンルームについて\n                        お客様の声\n\n料金プラン\n\nサービス\n                    \n                        オンライン英会話サービス\n                        日常英会話コース\n                        ビジネス英会話コース\n                        中学・高校生コース\n                        スピーキングテスト\n

(以下省略)

Document Loadersの魅力は、色々なドキュメントを扱えるよう整備されていることです。

Document Loaders 目次

他にもConfluenceやGoogle Driveもサポートしてます。
個人的な感想ですが、こうやって気楽に色々なドキュメントを読み込ませて、LLM利用の可能性を探っていきやすいのが魅力的だなと思います。

Step 5 Text Splitterを利用する

公式ドキュメントはこちらです。 https://python.langchain.com/docs/modules/data_connection/document_transformers/

先ほどロードしたドキュメントを分割するのに利用します。

from langchain.document_loaders import PlaywrightURLLoader
from langchain.text_splitter import CharacterTextSplitter

urls = [
    "https://www.rarejob.com/"
]
loader = PlaywrightURLLoader(urls=urls, remove_selectors=["header", "footer"])
docs = loader.load()

text_splitter = CharacterTextSplitter(        
    separator = "\n",
    chunk_size = 1000,
    chunk_overlap = 200
)

texts = text_splitter.split_documents(docs)

print('=======')
print(texts[0])
print('=======')
print(texts[1])

出力例

=======
page_content='オンライン英会話No.1 レアジョブ英会話\n無料登録\nログイン\n法人向け|\n学校向け|\n初めての方へ\n                        体験レッスンとは\n                        講師について\n                        ご利用の流れ\n                        レッスンルームについて\n                        お客様の声\n料金プラン\nサービス\n                        日常英会話コース\n                        ビジネス英会話コース\n                        中学・高校生コース\n                        スピーキングテスト\n                        レッスンチケット\n                        英会話コーチングのスマートメソッド®\n教材\n予約・講師検索\n初めての方へ\n                    \n                        体験レッスンとは\n                        講師について\n                        ご利用の流れ\n                        レッスンルームについて\n                        お客様の声\n料金プラン\nサービス\n                    \n                        オンライン英会話サービス\n                        日常英会話コース\n                        ビジネス英会話コース\n                        中学・高校生コース\n                        スピーキングテスト\n                        レッスンチケット\n                        英会話コーチングサービス\n                        スマートメソッド®\n教材\n予約・講師検索\nまずは無料体験レッスンへ\n叶えたいのは、英語が話せること' metadata={'source': 'https://www.rarejob.com/'}
=======
page_content='スピーキングテスト\n                        レッスンチケット\n                        英会話コーチングサービス\n                        スマートメソッド®\n教材\n予約・講師検索\nまずは無料体験レッスンへ\n叶えたいのは、英語が話せること\n本当に英会話力が上がる英会話サービスを作る\nレアジョブ英会話にはその使命があります\n週に1回しか通えない教室より毎日使えるオンライン\n楽しいだけでなく、適切なフィードバックをできるプロの講師\n日々研究する中で15年間の集大成が詰まっています\nレアジョブ英会話をうまく使って英会話力を伸ばしてください\nレアジョブ英会話だから実現した最適な学習サイクル\nオンラインで毎日話せるからこそ、英会話力は伸びます。さらにレアジョブ英会話では、レッスンを提供するだけでなく、「迷わず学べる・話せるようになる」学習体験を実現しました。\n詳細を見る\n継続するなら「話せるようになる」レアジョブ英会話へ\nレッスンを受けたいと思ったそのときに、外出先でも予約ができます。あなたのライフスタイルに合わせて、いつでも気軽に英会話を学べます。\n5,000教材から選べる\n                        \n                        view more\n朝6時~深夜1時毎日開校\n                        \n                        view more\n日本人講師によるレッスン・学習相談\n                        \n                        view more\nスマホアプリで受けられる\n                        \n                        view more\n自動録音機能で復習ができる\nインプット学習をアプリで「ソロトレ」\n※Webとスマートフォンアプリでは一部の仕様が異なります。\n※自動録音機能は一部の講師のみとなります。\n※自動録音機能は、一部の環境で音声が再生できない場合があります。詳細は\n                    こちらをご確認ください。\n質の違い、体験でわかります' metadata={'source': 'https://www.rarejob.com/'}

Step6 EmbeddingsとVectorStoreを利用する

Embeddingsの説明について、LangChainのドキュメントの説明を引用します。

https://python.langchain.com/docs/modules/data_connection/text_embedding/

The Embeddings class is a class designed for interfacing with text embedding models. There are lots of embedding model providers (OpenAI, Cohere, Hugging Face, etc) - this class is designed to provide a standard interface for all of them. Embeddings create a vector representation of a piece of text. This is useful because it means we can think about text in the vector space, and do things like semantic search where we look for pieces of text that are most similar in the vector space.

テキストをベクトルで表現する際に利用するモデルのことをEmbedding Modelsと呼んでます。
Embeddings Models自体も同様に、さまざまなプロバイダーから提供されているようです。
今回はOpenAIのEmbedding Modelsを利用します。

https://python.langchain.com/docs/modules/data_connection/text_embedding/integrations/openai

実際に動かしてみます。

from langchain.embeddings import OpenAIEmbeddings

embedding_model = OpenAIEmbeddings()

embeddings = embedding_model.embed_documents(
    [
        "牛丼",
        "カツ丼",
        "豚丼",
        "たまご丼",
        "親子丼",
    ]
)

embedded_query = embedding_model.embed_query("豚")

print(embedded_query)

出力例

[-0.012570846825838089, -0.015062803402543068, -0.005081093404442072, -0.02415601722896099, -0.025752535089850426, 0.002058120444417, -0.03506787493824959, -0.015479287132620811, -0.04137064889073372, -0.029237110167741776, -0.0027921719010919333, 0.029181579127907753, 0.005153977777808905, -0.018200309947133064, -0.019283166155219078, 0.03720581904053688, 0.022184664383530617, 0.006268070079386234, 0.03512340411543846,

(省略)

VectorStoreの説明をします。

https://python.langchain.com/docs/modules/data_connection/vectorstores/

VectorStoreの説明をLangChainの公式ドキュメントから引用します。

One of the most common ways to store and search over unstructured data is to embed it and store the resulting embedding vectors, and then at query time to embed the unstructured query and retrieve the embedding vectors that are 'most similar' to the embedded query. A vector store takes care of storing embedded data and performing vector search for you.

与えられた非構造化データをベクトルに変換して保存する、クエリもベクトル化することで近いベクトルを検索できる、というふんわりとした理解で行きます。

今回はなんとなくFAISSを利用します。

https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/faiss

github.com

下記コマンドで下準備します。

pip install faiss-cpu

下記サンプルは
①レアジョブ英会話のLPからドキュメントを取得する
②テキストを分割してEmbeddingsを利用してベクトル変換し、VectorStoreに格納する
③「レアジョブ英会話」という単語で打った際、どんなドキュメントが出てくるか観察する
というコードになります。

from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.text_splitter import CharacterTextSplitter
from langchain.vectorstores import FAISS
from langchain.document_loaders import PlaywrightURLLoader

urls = [
    "https://www.rarejob.com/"
]
loader = PlaywrightURLLoader(urls=urls, remove_selectors=["header", "footer"])
data = loader.load()

text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
docs = text_splitter.split_documents(data)

embeddings = OpenAIEmbeddings()

# FAISSにOpenAI Embeddingsを用いて取得したベクトルを格納する
db = FAISS.from_documents(docs, embeddings)
db.save_local("rarejob-eikaiwa")

# 格納した先を開く
new_db = FAISS.load_local("faiss_index", embeddings)

# レアジョブ英会話と距離が近い値を持つドキュメントを取得する
query = "レアジョブ英会話"
docs = new_db.similarity_search(query)

print(docs[0].page_content)

結果例

本当に英会話力が上がる英会話サービスを作る

レアジョブ英会話にはその使命があります

週に1回しか通えない教室より毎日使えるオンライン

楽しいだけでなく、適切なフィードバックをできるプロの講師

日々研究する中で15年間の集大成が詰まっています

レアジョブ英会話をうまく使って英会話力を伸ばしてください

レアジョブ英会話だから実現した最適な学習サイクル

オンラインで毎日話せるからこそ、英会話力は伸びます。さらにレアジョブ英会話では、レッスンを提供するだけでなく、「迷わず学べる・話せるようになる」学習体験を実現しました。

(省略)

Step7 1回だけ質問に答えてくれるコードを書く

今まで
・LLMs、ChatModels
・Prompt Templates
・Chains
・Document Loaders
・Text Splitters
・Embeddings
・VectorStore
とやってきました。

最後に、Chainsの1つであるload_qa_with_sources_chainに、レアジョブ英会話のLPから取得したテキストを渡して、 レアジョブ英会話について教えてもらうことにします。

https://python.langchain.com/docs/use_cases/question_answering/

from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import FAISS
from langchain.chat_models import ChatOpenAI
from langchain.chains.qa_with_sources import load_qa_with_sources_chain

embeddings = OpenAIEmbeddings()
llm = ChatOpenAI(temperature=0.9)

new_db = FAISS.load_local("faiss_index", embeddings)
docs = new_db.similarity_search("レアジョブ英会話", k=3, return_only_outputs=True)

chain = load_qa_with_sources_chain(llm=llm, chain_type="stuff")
question = "レアジョブ英会話の特徴について教えてください。日本語で回答してください。"
answer = chain({"input_documents": docs, "question": question}, return_only_outputs=True)
print(answer)

出力例

1回目

{'output_text': 'レアジョブ英会話の特徴は以下の通りです:\n- 週に1回しか通えない教室よりも、毎日使えるオンライン英会話サービスです。\n- プロの講師による楽しいレッスンを提供し、適切なフィードバックも受けることができます。\n- 15年間の研究に基づいた集大成が詰まったプログラムです。\n- オンラインで毎日話すことで英会話力が伸びる特長があります。\n- 自動録音機能により、復習を行うこともできます。\n- 講師の厳しい審査と専門的な研修により、高品質なレッスンを提供しています。\n- 日本人講師とフィリピン人講師の両方がおり、学習者のニーズに合わせたレッスンが選べます。\n- レベルや目的に合わせたオリジナル教材が提供されています。\n\nソース: https://www.rarejob.com/\n'}

2回目

{'output_text': 'レアジョブ英会話の特徴は以下の通りです。\n- 週に1回しか通えない教室より毎日使えるオンライン\n- 適切なフィードバックをできるプロの講師\n- 15年間の研究成果が詰まった英会話サービス\n- レアジョブ英会話を使って英会話力を伸ばすことができる\n- 実現した最適な学習サイクル\n- スマホアプリで受けられる\n- 自動録音機能で復習ができる\n- 無料体験レッスンがある\n- 教材が選べる\n- TESOLに基づいた研修を監修している講師陣がいる\n- フィリピン人講師と日本人講師がいる\nSOURCES: https://www.rarejob.com/'}

3回目

{'output_text': 'レアジョブ英会話の特徴は以下のとおりです。\n- 週に1回しか通えない教室より毎日使えるオンラインサービスです。\n- 適切なフィードバックをできるプロの講師が提供されます。\n- 15年間の研究を経て集大成された教材があります。\n- 自動録音機能で復習ができます。\n- 日本人講師によるレッスンと学習相談が受けられます。\n- スマホアプリで受講可能です。\n- 無料体験レッスンが用意されています。\nSOURCES: https://www.rarejob.com/'}

どれも公式見解ではないのでご注意ください。

終わりに

気楽にLLMで遊ぶにはもってこいのツールなので、ご興味ある方は是非触ってみてください。

採用

現在、弊社は一緒に働いてくださるエンジニアを大募集しています。
穏やかな紳士・淑女の多い職場です。
ご興味ありましたら、是非カジュアル面談にお越しください。 rarejob-tech.co.jp