🤖

LangChainで対話テキストからイベント情報をまとめる

2022/12/31に公開

「LangChain」を使い対話テキストからイベント情報をまとめる機能を試作しました。
記事では具体例として飲み会としましたが、広義のイベント情報を対話テキストから抽出することを目的としています。

前提

LLMの力を活用して、エージェントのようにふるまうAIを実現してみたく、トライアルとしてチャレンジしました。
私の普段の仕事はデータアナリティクスでプログラミングではないので、解説もそれほど専門的ではありません。この点ご容赦ください。
先にインプットとアウトプットをお見せします。興味があれば読み進めていただきコメントなどいただけるとうれしいです。

# インプットする対話テキスト

"""
Aさん:今度の花見はいつにしますか。
Bさん:私は1月下旬がちょうどいいです。
Cさん:私は1月だと1/20か、1/30がちょうどいいです。
Dさん:私は1月だと参加は難しそうです。
Aさん:じゃあいったん1/30にしましょうか。
Bさん:わかりました。時間は何時にします?
Aさん:そうですね、じゃあ18:00にしましょうか。
Cさん:ちょっと早くないですか?19:00はどうでしょう。
Aさん:了解です。じゃあ19時で!場所はいつもの黒木屋にしましょうか。
Cさん:了解です。楽しみにしてます。
Dさん:楽しんできてください。いけそうだったらまた連絡します。
"""

# アウトプットされるイベント情報
"イベントは花見で、開催日は1/30、開始時間は19:00、開催場所は黒木屋、参加者はAさん、Bさん、Cさんの予定です。"

LangChain

LangChainは、LLM(大規模言語モデル)を活用したアプリケーション開発支援を目的としたライブラリです。

LangChainの基本機能


LLM とプロンプト

LLMへの命令です。通常のLLMを利用する際は毎回自然言語のプロンプトを記述しますが、LangChainではこれを簡略化・効率化するための「テンプレートプロンプト」が用意されています。

チェーン

テンプレートプロンプトを含む、複数の「ツール(検索、データベース等)」を連鎖させるための仕組みです。

データ拡張生成

トレーニング済データ以外のデータに対する処理をサポートしています。データの分割、単語のベクトル表現(Embeddings)、Embeddingsを保存するVectorstore等。

エージェント

「エージェント」は複数のツールにアクセスし実行できるクラスです。ユーザーの入力をLLMで解釈し、どのツールをどの順序で実行するかを決定できる強力な存在です。

メモリー

デフォルトではチェーンとエージェントはステートレスです。つまり受信した各クエリを個別に処理しますが、一部のアプリケーション (チャットボット等) では、以前のやり取りを覚えておくことが非常に重要です。「メモリー」はこれを支援する機能です。


インストール

実行環境としてはGoogle Colabを利用しています。

1.ライブラリをインストール

!pip install langchain
!pip install openai

2.環境変数を設定

OPENAI_API_KEYは、OpenAIから登録して取得できます。APIはフリートライアル($18)分を使い切ると有料に移行するようです。(2022/12/31時点)

import os
os.environ["OPENAI_API_KEY"] =<あなたのOPENAI_API_KEY>

LLM呼び出し

LangChainの基本的な機能であるLLM呼び出しを実行します。LLMに任意の入力を与えることで、回答が得られます。

from langchain.llms import OpenAI

llm = OpenAI(temperature=0.9)
text = "カラフルな靴下を作る会社の社名にふさわしいのは?"

print(llm(text))

LLMからの回答。

・カラフルソックス・ソックスタイム・トゥインクルソックス・ファンタジーソックス・ソックスワールドなど

LLMチェーン

LLMをアプリケーションで使うことを考えると「LLM呼び出し」のように毎回ユーザーが文章を送ることは効率的ではありません。
そこで、キーとなる情報だけをユーザーから取得し、LangChainがプロンプトを作成してLLMに送信します。これが以下のPromptTemplateです。

from langchain.prompts import PromptTemplate

prompt = PromptTemplate(
    input_variables=["product"],
    template="{product}を作る会社の社名にふさわしいのは?",
)

PromptTemplateをもとに、ユーザー入力からプロンプトを作成しLLMに送信するチェーンを作ることができます。

from langchain.chains import LLMChain
chain = LLMChain(llm=llm, prompt=prompt)

チェーンを作ることで、製品名を指定するだけで実行できるようになります。

chain.run("カラフルな靴下")
chain.run('料理のVRゲーム')
chain.run('力士用ボディクリーム')

シーケンシャルチェーン

LangChainを使ったイベント情報抽出

ここからが本noteのメインです。

準備

まず準備としてライブラリをインポートしLLMを準備します。

# ライブラリのインポート
from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain

# LLMの準備
llm = OpenAI(temperature=0.9)

情報を抽出する対話テキストを用意します。

mytalk = """Aさん:今度の花見はいつにしますか。
Bさん:私は1月下旬がちょうどいいです。
Cさん:私は1月だと1/20か、1/30がちょうどいいです。
Dさん:私は1月だと参加は難しそうです。
Aさん:じゃあいったん1/30にしましょうか。
Bさん:わかりました。時間は何時にします?
Aさん:そうですね、じゃあ18:00にしましょうか。
Cさん:ちょっと早くないですか?19:00はどうでしょう。
Aさん:了解です。じゃあ19時で!場所はいつもの黒木屋にしましょうか。
Cさん:了解です。楽しみにしてます。
Dさん:楽しんできてください。いけそうだったらまた連絡します。
"""

LLMチェーン作成

対話テキストから、イベント名を抽出するためのチェーンを作成します。まずはPromptTemplateから。

event_prompt = PromptTemplate(
    input_variables=["talk","type"],
    template="""次の文章の中から開催されるイベント名を{type}型で返してください。不明な場合はNULLを返してください。
    {talk}
    """
)

イベント名抽出用プロンプトをもとに、イベント名抽出チェーンを作成。

event_chain = LLMChain(
    llm=yllm, 
    prompt=event_prompt
    )

試しにチェーンを実行。

event_ans = event_chain.run({
    'talk':mytalk,
    'type':"str"
})

print(event_ans)

以下の結果が得られています。

'花見'

同様に、日にち、時間、場所、参加者についてプロンプトとチェーンを作成します。実行は最後にまとめて行います。

# 日にちチェーン(prompt, chain)準備
date_prompt = PromptTemplate(
    input_variables=["talk","type"],
    template="""次の文章の中から開催されるイベントの開催日を{type}型で返してください。不明な場合はNULLを返してください。
    {talk}
    """
)
date_chain = LLMChain(
    llm=llm, 
    prompt=date_prompt
    )



# 時間チェーン(prompt, chain)準備
time_prompt = PromptTemplate(
    input_variables=["talk", "type"],
    template="""次の文章の中から開催されるイベントの開始時間を{type}型で返してください。不明な場合はNULLを返してください。
    {talk}
    """
)
time_chain = LLMChain(
    llm=llm, 
    prompt=time_prompt
    )



# 開催場所チェーン(prompt, chain)準備
place_prompt = PromptTemplate(
    input_variables=["talk","type"],
    template="""次の文章の中から開催されるイベントの開催場所を{type}型で返してください。不明な場合はNULLを返してください。
    {talk}
    """
)
place_chain = LLMChain(
    llm=llm, 
    prompt=place_prompt
    )



# 参加者チェーン(prompt, chain)準備
sanka_prompt = PromptTemplate(
    input_variables=["talk"],
    template="""次の文章の中から開催されるイベントの参加者を返してください。不明な場合はNULLを返してください。
    {talk}
    """
)
sanka_chain = LLMChain(
    llm=llm, 
    prompt=sanka_prompt
    )

LLMチェーン実行

チェーンを順番に実行していきます。見ての通り、このあたりの記述は冗長なので、シーケンシャルチェーンを使うとよりスマートに記述できます。

# イベントチェーン実行
event_ans = event_chain.run({
    'talk':mytalk,
    'type':"str"
})

# 日にちチェーン実行
date_ans = date_chain.run({
    'talk':mytalk,
    'type':"str"
})

# 時間チェーン実行
time_ans = time_chain.run({
    'talk':mytalk,
    'type':"str"
})

# 開催場所チェーン実行
place_ans = place_chain.run({
    'talk':mytalk,
    'type':"str"
})

# 参加者チェーン実行
sanka_ans = sanka_chain.run({
    'talk':mytalk
})

実行結果

結果を見てみましょう。

print(event_ans, date_ans, time_ans, place_ans, sanka_ans)
'花見' 
'1/30' 
"19:00" 
'黒木屋'  Aさん、Bさん、Cさん

うまく情報は抽出できています。しかしクォーテーションや不要な改行などが混じっているので、これらを除去します。

event_ans = event_ans.replace('\n', '').replace('\'', '').replace('\"', '')
date_ans = date_ans.replace('\n', '').replace('\'', '').replace('\"', '')
time_ans = time_ans.replace('\n', '').replace('\'', '').replace('\"', '')
place_ans = place_ans.replace('\n', '').replace('\'', '').replace('\"', '')
sanka_ans = sanka_ans.replace('\n', '').replace('\'', '').replace('\"', '')

最後に1つの文章として整形して完了です。

print('イベントは'+event_ans+'で、開催日は'+date_ans+'、開始時間は'+time_ans+'、開催場所は'+place_ans+'、参加者は'+sanka_ans+'の予定です。')
イベントは花見で、開催日は1/30、開始時間は19:00、開催場所は黒木屋、参加者は Aさん、Bさん、Cさんの予定です。

感想

  • LangChainを使うことで、LLMを効率的に活用できそう。

  • 機能のコアである情報抽出の部分はすべてLLMまかせのブラックボックスなので、不安がある。

  • とはいえ、既存の自然言語処理手法だとかなり複雑な記述が必要と思われるタスクを、プロンプト一つで実現できるのは脅威。

  • 特に「参加者の特定」は、既存の自然言語処理手法だとかなり難しいのではないか(詳しい方コメントください)。

  • 全体としてLLM含む大規模モデルの威力はすさまじいので、不安はありつつも活用は不可逆的に急速に進むだろう。

関連情報

npaka様のLnagChain情報を大変参考にさせていただきました。

今後もエージェント的にふるまうAIについて発信していきたいと思います。ありがとうございました。

Discussion