LangChainのAgentをLlamaIndexのインデックスでカスタマイズしてみた

これで必要に応じて自作インデックスを参照するChatBotが作成できます
2023.03.19

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんちには。

データアナリティクス事業本部 インテグレーション部 機械学習チームの中村です。

LangChainのAgentのカスタムツールとして、LlamaIndexのインデックスを使用する方法を紹介します。

LangChainとは

LlamdaIndexと同様、大規模言語モデル(LLM)をカスタマイズして使用するためのライブラリです。

カスタムされたチャットボットやエージェントを作成することが可能で、以下のような様々な外部リソース・サービスと連携可能です。

  • OpenAIのChatCompletion(要するにChatGPT)
  • Google検索やOpenSearchなどの検索サービス
  • Hugging Faceのエコシステム
  • Pythonのコーディングをするツール
    • 例:PyTorchでhogehogeな構成のサンプルコードを教えて、など
  • SQLデータベースなどのリソース
    • 例:hogehogeテーブルの概要を教えて、など

なおツールはカスタマイズすることも可能となっており、今回はこれを利用します。

LangChainの公式ドキュメントは以下を参照ください。

ツールの一覧は以下のページが分かりやすいかと思います。

LangChainのAgentとは

上記であげたリソース・サービスをツールとして与え、同時にdescriptionとして各ツールの説明を書いておきます。

そして要求されたクエリと、descriptionの記述内容を照らし合わせ、ツールを適切に選択して結果を取得、これらを繰り返しながら最終的な結論を得ます。

またツールは開発者が自作(カスタマイズ)して与えることも可能です。

先行して弊社の記事でも扱っているので、併せて参考ください。

今回は、カスタマイズツールに、LlamdaIndexのインデックスを使ってみる事例となります。

LlamaIndexとは

前回記事でかきましたが、入力プロンプトにコンテキストを埋め込むことで、大規模言語モデル(LLM)をカスタマイズして使用するためのライブラリです。

詳細は前回記事を参照ください。

またLlamdaIndexの公式ドキュメントは以下を参照ください。

実装してみた

使用環境

Google Colaboratoryを使います。ハードウェアアクセラレータは無し、ラインタイム仕様も標準です。

主なバージョン情報は以下です。

!python --version
Python 3.9.16
!pip freeze | grep -e "openai" -e "llama" -e "langchain"
langchain==0.0.115
llama-index==0.4.30
openai==0.27.2

LangChain、LlamdaIndexともに前回からバージョンが上がってます。

とても開発が活発ですのでご注意ください(記事執筆中にバージョンが上がってしまうことも多々あります)。

セットアップ

モジュールをインストールします。

!pip install llama-index
!pip install python-dotenv
!pip install html2text

このやり方にこだわる必要はないのですが、OPEN_AI_KEYを.envに書き込みます。

!echo 'OPENAI_API_KEY="あなたのOPENAIのAPIキー"' >.env

importは以下の通りです。

from dotenv import load_dotenv
load_dotenv()

from langchain import OpenAI
from llama_index import GPTSimpleVectorIndex, SimpleWebPageReader, LLMPredictor, OpenAIEmbedding
from langchain.agents import initialize_agent, Tool
from langchain.tools.python.tool import PythonREPLTool
from langchain.chains.conversation.memory import ConversationBufferMemory

indexの作成

まずはカスタムツールの元となるindexを作成します。

前回記事と同様、TranscribeとTranslateに関する記事をベースにしてみます。自身のブログ記事の情報を使ってみます。

transcribe_index = GPTSimpleVectorIndex(
    documents=SimpleWebPageReader(html_to_text=True).load_data([
        "https://dev.classmethod.jp/articles/reintro-managed-ml-transcribe/"
    ])
    , embed_model=OpenAIEmbedding(model="text-embedding-ada-002")
    , llm_predictor=LLMPredictor(llm=OpenAI(temperature=0, model_name="gpt-3.5-turbo"))
)

translate_index = GPTSimpleVectorIndex(
    documents=SimpleWebPageReader(html_to_text=True).load_data([
        "https://dev.classmethod.jp/articles/reintro-managed-ml-translate/"
    ])
    , embed_model=OpenAIEmbedding(model="text-embedding-ada-002")
    , llm_predictor=LLMPredictor(llm=OpenAI(temperature=0, model_name="gpt-3.5-turbo"))
)

カスタムツールの作成

カスタムツールの作成方法には、BaseToolのサブクラスとしてクラスを実装するか、Toolというデータクラスを使用するかの2種類があります。

今回は簡易なので、後者のToolというデータクラスを使用する方法で実装します。

transcribe_index_tool = Tool(
    name="Transcribe Index"
    , description="Amazon Transcribeについて特徴や料金を調べる際に利用することができます。"
    , func=transcribe_index.query
)
translate_index_tool = Tool(
    name="Translate Index"
    , description="Amazon Translateについて特徴や料金を調べる際に利用することができます。"
    , func=translate_index.query
)

ZeroShotAgentの作成

まずはZeroShotAgentを作成します。ZeroShotAgentは与えられたツールを使って回答を作成しようとします。

また以下のようにカスタムツールに加え、PythonREPLToolという既に準備されているツールを併せて使ってみます。

PythonREPLToolはPythonコードを生成したり、実行して結果を得たりしてくれます。

agent_executor = initialize_agent(
    tools=[
        PythonREPLTool()
        , transcribe_index_tool
        , translate_index_tool
    ]
    , llm=OpenAI(temperature=0)
    , agent="zero-shot-react-description", verbose=True
)

print(type(agent_executor))
print(type(agent_executor.agent))
<class 'langchain.agents.agent.AgentExecutor'>
<class 'langchain.agents.mrkl.base.ZeroShotAgent'>

またagent作成時に、verbose=Trueとすることで、Agentがツールを選択する過程を見ることができます。

OpenAItemperature=0となっているのは、回答に安定性を持たせるためです。

ZeroShotAgentへのクエリ実行

まずはPythonに関するクエリを投げてみましょう。

answer = agent_executor.run("Pythonでフィボナッチ数列を計算し、10番目の数字を教えてください。")

Entering new AgentExecutor chain...
フィボナッチ数列を計算するPythonコードを書く必要があります。
Action: Python REPL
Action Input: def fibonacci(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fibonacci(n-1) + fibonacci(n-2)
Observation:
Thought: 10番目の数字を取得する
Action: Python REPL
Action Input: print(fibonacci(10))
Observation: 55

Thought: I now know the final answer
Final Answer: 10番目のフィボナッチ数列の数字は55です。

Finished chain.

上記のような試行過程のログが表示されます。Python REPLが使われたことが分かりますね。

答えも文字列として取得されます。

print(answer)
10番目のフィボナッチ数列の数字は55です。

次にTranscribeについて聞いてみましょう。

answer = agent_executor.run("Amazon Transcribeの料金について要約し、日本語で回答してください。")

Entering new AgentExecutor chain...
Amazon Transcribeの料金を調べる必要がある。
Action: Transcribe Index
Action Input: Amazon Transcribeの料金
Observation: Amazon Transcribeの料金は、1か月あたりの文字起こしされた音声データの秒数に基づいた従量課金となります。少なくともTokyoリージョンでは、バッチとストリーミングに費用差はなく、1分あたり$0.024ドルからとなっています。ただし、いくぶんか無料枠もあるようですが、期間と時間に制限があるとのことです。
Thought: Amazon Transcribeの料金を要約した。
Final Answer: Amazon Transcribeの料金は、1か月あたりの文字起こしされた音声データの秒数に基づいた従量課金となり、Tokyoリージョンでは1分あたり$0.024ドルからとなっています。また、いくぶんかの無料枠もあるようですが、期間と時間に制限があるとのことです。

Finished chain.

カスタマイズしたTranscribe Indexが使われています。

(anserのprintは、Final Answerにも記載されているため省略)

最後にTranslateについても聞いてみましょう。

answer = agent_executor.run("Amazon Translateとは何か、その特徴を日本語で回答してください。")

Entering new AgentExecutor chain...
Amazon Translateについて調べる必要がある
Action: Translate Index
Action Input: Amazon Translate
Observation: Amazon Translate is a service provided by Amazon that allows for language translation. The article discusses various ways to use Amazon Translate and notes that while it is primarily designed for Japanese language support, there are some limitations in the Tokyo region. The article concludes by stating that it hopes to be a helpful resource for those looking to utilize Amazon Translate in the future.
Thought: Amazon Translateの特徴を理解した
Final Answer: Amazon Translateは、Amazonが提供する言語翻訳サービスです。このサービスを使用する方法について記載されており、主に日本語サポートを目的としていますが、東京地域ではいくつかの制限があることが指摘されています。この記事は、将来Amazon Translateを利用するための有用なリソースとなることを期待しています。

Finished chain.

こちらもカスタマイズしたTranslate Indexが使われています。

ConversationalAgentの作成

次にChatBotに特化したConversationalAgentを作成してみます。

ConversationalAgentは通常の会話はツールを使わず、必要に応じてツールを選択して回答するといった動作が可能です。

ツールはZeroShotAgentと同様のものを指定してみます。文脈を保持するために、memoryを指定しておきます。

agent_executor = initialize_agent(
    tools=[
        PythonREPLTool()
        , transcribe_index_tool
        , translate_index_tool
    ]
    , llm=OpenAI(temperature=0)
    , agent="conversational-react-description"
    , verbose=True
    , memory=ConversationBufferMemory(memory_key="chat_history")
)

print(type(agent_executor))
print(type(agent_executor.agent))
<class 'langchain.agents.agent.AgentExecutor'>
<class 'langchain.agents.conversational.base.ConversationalAgent'>

ConversationalAgentへのクエリ実行

まずは挨拶してみます。

agent_executor.run("こんにちは!今日は寒いですね。")

Entering new AgentExecutor chain...

Thought: Do I need to use a tool? No
AI: こんにちは!はい、今日はとても寒いですね。

Finished chain.

あいさつは、ツールを使わずに応答していることが分かります。

唐突にTranscribeについて聞いてみましょう。

agent_executor.run("寒いと言えば、Transcribeというサービスを知っていますか?")

Entering new AgentExecutor chain...

Thought: Do I need to use a tool? No
AI: はい、Amazon Transcribeというサービスをご存知ですか?Amazon Transcribeは、音声をテキストに変換するサービスです。音声をテキストに変換することで、文章を理解しやすくなります。

Finished chain.

まだツールは使ってくれませんでした。もう少し突っ込んで料金を聞いてみます。

agent_executor.run("そのサービスの料金について教えてください。")

Entering new AgentExecutor chain...

Thought: Do I need to use a tool? Yes
Action: Transcribe Index
Action Input: 料金
Observation: Amazon Transcribeの料金は、1時間の通話で標準的には60 x $0.024 = $1.44です。ただし、PII Redaction機能やCLMなどの追加機能を使用する場合は、それぞれ標準のおよそ1/10や1/4が追加費用としてかかります。また、特定の用途のTranscribeには、無印のTranscribeよりも価格が割高なものもあります。Custom language modelsやRedactionなどの機能を使用する場合は、追加の料金が発生します。具体的な料金については、Amazon Transcribeの公式サイトを参照する必要があります。
Thought: Do I need to use a tool? No
AI: Amazon Transcribeの料金は、1時間の通話で標準的には60 x $0.024 = $1.44です。ただし、PII Redaction機能やCLMなどの追加機能を使用する場合は、それぞれ標準のおよそ1/10や1/4が追加費用としてかかります。また、特定の用途のTranscribeには、無印のTranscribeよりも価格が割高なものもあります。Custom language modelsやRedactionなどの機能を使用する場合は、追加の料金が発生します。具体的な料金については、

Finished chain.

今度は、Transcribe Indexを使って回答してくれました!続きがありそうなので聞いてみましょう。

agent_executor.run("続きをお願いします。")

Entering new AgentExecutor chain...

AI: Amazon Transcribeの料金について、詳しくは、Amazon Transcribeのウェブサイトをご覧ください。また、Amazon Transcribeの料金を比較するために、Amazon Transcribeの料金表を参照することもできます。Amazon Transcribeの料金表を参照することで、Amazon Transcribeの料金を比較し、最適な料金を選択することができます。

Finished chain.

無事に通常の会話とツールを使い分けさせることができました。

まとめ

いかがでしたでしょうか。

色々試してみたのですが、descriptionの書き方次第では意図通りにツールを使ってくれなかったりなどの試行錯誤が必要でした。

descriptionには用途をある程度明確にして指定する必要があると感じました。

  • 具体的な例 : Amazon Transcribeについて特徴や料金を調べる際に利用することができます。
  • 少し曖昧な例 : Amazon Transcribeについて調べる際に利用することができます。

また、本記事ではChatBotの動作についてはChatGPTとは異なるものが使用され、モデルもgpt-3.5-turboではなくtext-davinci-003が使用されているため注意が必要です。

ChatGPTと同様の動作をさせるためには、AgentExecutorを以下に沿って作成する必要がありそうです。

こちらについては機会があればもう少し詳しく調べたいと思います。(書く人募集中)

本記事が、今後LangChainやLlamaIndexを活用される方の参考になれば幸いです。

参考記事