🎃

LlamaIndex v0.6.4 を LangChain の Tools として使ってみた

2023/05/11に公開2

想定読者

  • v0.6.0 より大きな変更があった LlamaIndex を LangChain でどのように使うか知りたい方
  • 独自のデータを利用しつつ、過去のやりとりを記憶した AI ツールを作りたい方

LlamaIndex v0.6 について

LlamaIndex が v0.6.0 より大きな変更がありました。クエリロジックをカスタマイズしやすくなったりする一方で、コード実行方法も変更になりました。変更内容の詳細や LlamaIndex 単体の使い方を知りたい方には、以下の npaka さんの記事をおすすめします。

https://note.com/npaka/n/n4254fc549dc0
https://note.com/npaka/n/n50475d6c3118

LangChain との連携方法も変わるか

LangChain の Tools として利用するのは変わりませんが、コード実行方法に少し変更があるようです。今回は以下の公式ドキュメントも参考にしながら実際に連携を試してみたいと思います。

https://gpt-index.readthedocs.io/en/latest/how_to/integrations/using_with_langchain.html
https://gpt-index.readthedocs.io/en/latest/reference/langchain_integrations/base.html

やってみた

Google Colab を使ってやっていきます。

ドキュメントの準備

LlamaIndex のリポジトリにあるダミーデータを利用します。

https://github.com/jerryjliu/llama_index/blob/main/examples/paul_graham_essay/data/paul_graham_essay.txt

Google Colab を開きます。左袖から以下を作成します。

  • フォルダ:data
  • ファイル:paul_graham_essay.txt (データは上記ダミーデータをコピペ)

パッケージのインストール

!pip install langchain
!pip install openai
!pip install tiktoken
!pip install llama-index==0.6.4

環境変数の設定

sk-xxxxxxxxxxxxには OpenAI API のトークン(有料)を設定します。

import os

os.environ["OPENAI_API_KEY"] = "sk-xxxxxxxxxxxx"

https://openai.com/product

コードの準備

import openai
from langchain.agents import AgentType, Tool, initialize_agent
from langchain.chat_models import ChatOpenAI
from langchain.memory import ConversationSummaryBufferMemory
from llama_index import GPTVectorStoreIndex, SimpleDirectoryReader


documents = SimpleDirectoryReader("./data/").load_data()
index = GPTVectorStoreIndex.from_documents(documents=documents)

tools = [
    Tool(
        name="LlamaIndex",
        func=lambda q: str(index.as_query_engine().query(q)),
        description="useful for when you need to answer questions about graham",
        return_direct=True,
    ),
]
llm = ChatOpenAI(
    temperature=0,
    client=openai,
)
memory = ConversationSummaryBufferMemory(
    llm=llm,
    memory_key="chat_history",
    max_token_limit=1000,
)
llm = ChatOpenAI(temperature=0)
prefix = """Anser the following questions as best you can, but speaking Japanese. You have access to the following tools:"""
suffix = """Begin! Remember to speak Japanese when giving your final answer."""

agent_chain = initialize_agent(
    tools,
    llm,
    agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION,
    memory=memory,
    prefix=prefix,
    suffix=suffix,
    verbose=True,
)

実行

LlamaIndex のインデックスを参照した回答ができるか

agent_chain.run(input="What did graham do growing up?")
> Entering new AgentExecutor chain...
Thought: Do I need to use a tool? Yes
Action: LlamaIndex
Action Input: Graham
Observation: chose to work on Bel because...?

Graham chose to work on Bel because he wanted to create a Lisp interpreter that could be written in itself, and he was intrigued by the challenge of making it work. He also wanted to explore the potential of macros and embedded languages, which he felt had been largely unexplored. Finally, he wanted to prove to himself that he could complete a difficult project, and he was motivated by the idea of being able to make art and be independent without having to rely on a boss or research funding.

> Finished chain.
chose to work on Bel because...?

Graham chose to work on Bel because he wanted to create a Lisp interpreter that could be written in itself, and he was intrigued by the challenge of making it work. He also wanted to explore the potential of macros and embedded languages, which he felt had been largely unexplored. Finally, he wanted to prove to himself that he could complete a difficult project, and he was motivated by the idea of being able to make art and be independent without having to rely on a boss or research funding.

TOOL として LlamaIndex が選択され、ダミーデータのインデックスを元に回答できました。

LangChain を使って過去のやりとりができるか

agent_chain.run(input='私の名前は筧です。')
> Entering new AgentExecutor chain...
Thought: Do I need to use a tool? No
AI: こんにちは、筧さん!私はあなたのお手伝いができます。何かお困りのことはありますか?

> Finished chain.
こんにちは、筧さん!私はあなたのお手伝いができます。何かお困りのことはありますか?

ダミーデータに関係ない質問だったので、TOOL は利用しないで回答できました。

agent_chain.run(input='私の名前を覚えていますか?')
> Entering new AgentExecutor chain...
Thought: Do I need to use a tool? No
AI: はい、覚えています。あなたの名前は筧さんですね。

> Finished chain.
はい、覚えています。あなたの名前は筧さんですね。

直近のやりとりを覚えており、それを踏まえて回答できました。

おわりに

最後まで読んでいただきありがとうございました。LlamaIndex の大きな変更があったものの、公式ドキュメントには使い方の説明があり、すぐに使うことができました。参考になった場合は、Like やバッジを贈っていただけると励みになります!それではまた!

Discussion

若山祐紀憲若山祐紀憲

LlamaIndexの事を知りたくて、ド素人ですが、「LlamaIndex google colab」と検索してこの記事に辿り着きました。
とりあえず、内容の通りコピペして実行するのですが、以下のようなエラー?が出てきます。
何がダメなんでしょう(;^_^A
GPTに質問しましたが良く分からずorz

Entering new AgentExecutor chain...
DEBUG:openai:message='Request to OpenAI API' method=post path=https://api.openai.com/v1/chat/completions
DEBUG:openai:api_version=None data='{"messages": [{"role": "user", "content": "Assistant is a large language model trained by OpenAI.\n\nAssistant is designed to be able to assist with a wide range of tasks, from answering simple questions to providing in-depth explanations and discussions on a wide range of topics. As a language model, Assistant is able to generate human-like text based on the input it receives, allowing it to engage in natural-sounding conversations and provide responses that are coherent and relevant to the topic at hand.\n\nAssistant is constantly learning and improving, and its capabilities are constantly evolving. It is able to process and understand large amounts of text, and can use this knowledge to provide accurate and informative responses to a wide range of questions. Additionally, Assistant is able to generate its own text based on the input it receives, allowing it to engage in discussions and provide explanations and descriptions on a wide range of topics.\n\nOverall, Assistant is a powerful tool that can help with a wide range of tasks and provide valuable insights and information on a wide range of topics. Whether you need help with a specific question or just want to have a conversation about a particular topic, Assistant is here to assist.\n\nTOOLS:\n------\n\nAssistant has access to the following tools:\n\n> LlamaIndex: useful for when you need to answer questions about graham\n\nTo use a tool, please use the following format:\n\n\\nThought: Do I need to use a tool? Yes\\nAction: the action to take, should be one of [LlamaIndex]\\nAction Input: the input to the action\\nObservation: the result of the action\\n\n\nWhen you have a response to say to the Human, or if you do not need to use a tool, you MUST use the format:\n\n\\nThought: Do I need to use a tool? No\\nAI: [your response here]\\n\n\nBegin!\n\nPrevious conversation history:\n\n\nNew input: What did graham do growing up?\n"}], "model": "gpt-3.5-turbo", "max_tokens": null, "stream": false, "n": 1, "temperature": 0.0, "stop": ["\nObservation:", "\n\tObservation:"]}' message='Post details'
DEBUG:urllib3.connectionpool:https://api.openai.com:443 "POST /v1/chat/completions HTTP/1.1" 200 None
DEBUG:openai:message='OpenAI API response' path=https://api.openai.com/v1/chat/completions processing_ms=2679 request_id=553111570f50c8d3c065f7372c3336c5 response_code=200
Thought: Do I need to use a tool? Yes
Action: LlamaIndex
Action Input: Graham


AttributeError Traceback (most recent call last)
<ipython-input-35-bf4c1f72580d> in <cell line: 1>()
----> 1 agent_chain.run(input="What did graham do growing up?")

9 frames
/usr/local/lib/python3.10/dist-packages/langchain/chains/base.py in run(self, callbacks, *args, **kwargs)
237
238 if kwargs and not args:
--> 239 return self(kwargs, callbacks=callbacks)[self.output_keys[0]]
240
241 if not kwargs and not args:

/usr/local/lib/python3.10/dist-packages/langchain/chains/base.py in call(self, inputs, return_only_outputs, callbacks)
138 except (KeyboardInterrupt, Exception) as e:
139 run_manager.on_chain_error(e)
--> 140 raise e
141 run_manager.on_chain_end(outputs)
142 return self.prep_outputs(inputs, outputs, return_only_outputs)

/usr/local/lib/python3.10/dist-packages/langchain/chains/base.py in call(self, inputs, return_only_outputs, callbacks)
132 try:
133 outputs = (
--> 134 self._call(inputs, run_manager=run_manager)
135 if new_arg_supported
136 else self._call(inputs)

/usr/local/lib/python3.10/dist-packages/langchain/agents/agent.py in _call(self, inputs, run_manager)
951 # We now enter the agent loop (until it returns something).
952 while self._should_continue(iterations, time_elapsed):
--> 953 next_step_output = self._take_next_step(
954 name_to_tool_map,
955 color_mapping,

/usr/local/lib/python3.10/dist-packages/langchain/agents/agent.py in _take_next_step(self, name_to_tool_map, color_mapping, inputs, intermediate_steps, run_manager)
818 tool_run_kwargs["llm_prefix"] = ""
819 # We then call the tool on the tool input to get an observation
--> 820 observation = tool.run(
821 agent_action.tool_input,
822 verbose=self.verbose,

/usr/local/lib/python3.10/dist-packages/langchain/tools/base.py in run(self, tool_input, verbose, start_color, color, callbacks, **kwargs)
253 except (Exception, KeyboardInterrupt) as e:
254 run_manager.on_tool_error(e)
--> 255 raise e
256 run_manager.on_tool_end(str(observation), color=color, name=self.name, **kwargs)
257 return observation

/usr/local/lib/python3.10/dist-packages/langchain/tools/base.py in run(self, tool_input, verbose, start_color, color, callbacks, **kwargs)
247 tool_args, tool_kwargs = self._to_args_and_kwargs(parsed_input)
248 observation = (
--> 249 self._run(*tool_args, run_manager=run_manager, **tool_kwargs)
250 if new_arg_supported
251 else self._run(*tool_args, **tool_kwargs)

/usr/local/lib/python3.10/dist-packages/langchain/tools/base.py in _run(self, run_manager, *args, **kwargs)
348 )
349 if new_argument_supported
--> 350 else self.func(*args, **kwargs)
351 )
352

<ipython-input-33-d8a0ef5efb88> in <lambda>(q)
12 Tool(
13 name="LlamaIndex",
---> 14 func=lambda q: str(index.as_query_engine().query(q)),
15 description="useful for when you need to answer questions about graham",
16 return_direct=True,

/usr/local/lib/python3.10/dist-packages/llama_index/indices/query/base.py in query(self, str_or_query_bundle)
18 if isinstance(str_or_query_bundle, str):
19 str_or_query_bundle = QueryBundle(str_or_query_bundle)
---> 20 return self._query(str_or_query_bundle)
21
22 async def aquery(self, str_or_query_bundle: QueryType) -> RESPONSE_TYPE:

AttributeError: 'RetrieverQueryEngine' object has no attribute 'callback_manager'

若山祐紀憲若山祐紀憲

pythonのバージョンを3.10にするとできました‼
お騒がせしましたm(__)m