【初投稿】ChatGPTを最強にするはじめの一歩 〜 OpenAI API + LangChain 〜

# はじめに
皆さん、初めまして！
当アカウント（BlueberryAIxR）での初投稿となる今回は、ChatGPTを最強にするはじめの一歩として、LangChainをテーマにお話してみたいと思います。

# ChatGPTとLLMモデル
皆さんがよく使っているChatGPTというのは、LLM（大規模言語モデル）を使ったWebアプリです。ユーザは実際にはLLMを直接使っているわけではなく、あくまでChatGPTというWebアプリが提供している機能を使っているに過ぎません。
![スクリーンショット 2023-10-04 14.37.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319271/44ea68f3-f5a3-fbad-97ed-fc0226c5d893.png)

これが例えば、OpenAI APIを使っている場合には、このWebアプリを経由する事なく直接（厳密にはAPIサーバ経由で）LLMを利用する事が可能です。しかし、ネット検索ができなくて2021年9月（GPT-4は2022年1月）以降の新しい情報には対応できないとか、ちょっと複雑な計算になると簡単に間違った回答をしてしまう等の問題はOpenAI APIを使っても解決しません。それらの問題を解決するのがLangChainです。

# LangChain
[LangChain](https://www.langchain.com/)は、LLMを利用したアプリケーション用ライブラリー／フレームワークです。Lang（言語）＋Chain（鎖）の造語からも分かる通り、様々な機能／サービス／API／ツール／チェーン／LLMを連結／連携させる事が簡単にできる事が最大の特徴です。
![スクリーンショット 2023-10-04 15.00.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319271/8eeeaabc-d597-5e18-bb0c-2dcabeffdcaf.png)

実際にどんなものなのか、これ以降簡単なサンプルを使ってご紹介します。なお、今回はサンプルの開発・実行環境として[VScode](https://azure.microsoft.com/ja-jp/products/visual-studio-code)を使っています。VScodeのdevcontainerでPython環境を構築、VScodeのJupyter拡張でJupyterノートブックを利用していますが、Pythonが動作する環境さえあれば何でも構いません。

Python環境を構築するのも面倒な方は、Google Colab（Google Colaboratory）を利用するのが無料で一番手っ取り早いと思います。Google Colabについては、[解説サイト](https://blog.kikagaku.co.jp/google-colab-howto)などをご参照下さい。

LangChainのインストールは、pipコマンドで行います。

```python
# OpenAI APIも使うので、openaiとlangchainをインストール
pip install openai==0.28.1 langchain
```

:::note warn
2023年11月上旬にOpenAI APIがv1.0.0にアップデートされました。v1.0.0はインターフェースが変更されており、現時点（2023年11月14日）のLangChainは、OpenAI API v1.0.0以上に対応できていません。LangChainからOpenAI APIを使用する場合、OpenAI API v1.0.0より前で最新のv0.28.1をインストールして下さい。
:::

OpenAI APIの使用には、APIキーが必要です。OpenAIの[公式サイト](https://platform.openai.com/)でアカウントを作成し、管理画面でAPIキーを作成して環境変数OPENAI_API_KEYに設定します。OS側の設定を使っても構いません（以下同様）が、Pythonから設定する場合は下記コードを実行します。

```python:set_openai_apikey.py
# OpenAIのAPIキーを設定
import os
os.environ['OPENAI_API_KEY'] = 'OpenAIのAPIキー'
```

なお、OpenAI APIは入出力したトークン数（文字数みたいなもの）により課金されます。使用料の詳細は[公式サイト](https://openai.com/pricing)でご確認下さい。

# SerpAPI（ネット検索ツール）
まずは、LLMが自由にネット検索できるように、ネット検索ツール「SerpAPI」と連携させてみましょう。

SerpAPIを使用するには、SerpAPIの[公式サイト](https://serpapi.com/)で無料アカウントを作成する必要があります。無料アカウントの場合、1ヶ月に100回までのネット検索が可能です。作成したアカウントの管理画面でAPIキーを作成し、環境変数SERPAPI_API_KEYに設定します。

```python:set_serpapi_apikey.py
# SerpAPIのAPIキーを設定
import os
os.environ['SERPAPI_API_KEY'] = 'SerpAPIのAPIキー'
```

また、SerpAPIを使用するには、google-search-resultsパッケージをインストールする必要があります。

```
pip install google-search-results
```

以上でプログラミングの準備は整いましたので、次のコードを実行します。

```python:tool_serpapi.py
from langchain.llms import OpenAI

from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.agents import AgentType

llm = OpenAI(model='gpt-3.5-turbo-instruct', temperature=0)
tools = load_tools(["serpapi"], llm=llm) # ツールの準備
agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True) # エージェントの準備

agent.run("リコリスリコイルの主人公は？")
```

```verbose=True```で実行すると、下記のように最終回答に至るまでのLLMの思考過程がログ出力されます。```verbose=False```または```verbose```自体を指定しないとログ出力無しで最終回答だけ出力されます。

```
> Entering new AgentExecutor chain...
 I should search for information about the anime or manga series "リコリスリコイル".
Action: Search
Action Input: "リコリスリコイル 主人公"
Observation: 錦木千束はアニメ「リコリス・リコイル」の主人公で、ボブカットで明るい色の髪に、赤いリボンがトレードマーク。 自称「喫茶リコリコの看板娘」です。 アニメでは、第1話にて、喫茶リコリコに転属となったセカンドリコリス・井ノ上たきなのバディとして登場します。
Thought: I should search for more information about the character 錦木千束.
Action: Search
Action Input: "錦木千束 リコリスリコイル"
Observation: ['Chisato Nishikigi is one of the two main characters of the Japanese anime television series Lycoris Recoil, created by Spider Lily and Asaura.', 'Chisato Nishikigi type: TV character.', 'Chisato Nishikigi main_tab_text: Overview.', 'Chisato Nishikigi kgmid: /g/11tm_bknn1.', 'Chisato Nishikigi first_appearance: Lycoris Recoil Episode 1 "Easy does it" (2022).', '実は歴代最強と称されるリコリス。 DAが対応しないような民間の困りごとを、楽しんで解決している。 喫茶リコリコでは、自称・看板娘として明るく元気に働いている。']
Thought: I now know the final answer
Final Answer: 錦木千束

> Finished chain.

'錦木千束'
```

せっかくなので、LLMの思考過程を見てみましょう。

"リコリスリコイルの主人公は？"という人間からの質問に対して、まず、LLMは"リコリスリコイル"というアニメまたはマンガについての情報収集の必要性を感じています。そのために、ActionとしてSearch（SerpAPIの利用）に取りかかります。SerpAPIを利用する際、検索キーワードとして"リコリスリコイル 主人公"をInputしています。検索結果は、Observation欄に書かれてある通りです。

検索結果によって、錦木千束というキャラクターの存在が分かりましたが、彼女が主人公かどうかはこの検索結果から分かりませんでした。そこで、次のThought欄によると、錦木千束というキャラクターの情報をもっと収集する必要があると考えます。

その結果、再びActionとしてSearchを行います。今回の検索キーワードは"錦木千束 リコリスリコイル"を使っています。その検索結果（Observation）から、錦木千束がこのアニメの主人公2人の内の1人である事が分かります。これにより、最終回答にたどり着いた事を認識し、'錦木千束'という回答を人間にしています。

最終回答だけ見ると、まるでAIが知っていたかのように見えますが、実はネット検索を2回使って回答していたのですね。こうして、AIがGoogleを使って何回か検索しながら絞込検索している過程を見ていると、人間味を感じて親しみを持つ方もいるかもしれません。

# llm-math（計算ツール）
ChatGPTは、計算が苦手です。Plusサブスクリプションのユーザしか利用できないGPT-4でさえ、「17の8乗は？」という質問に対して平気で嘘をつきます。ちなみに正解は6,975,757,441です。LLMは学習した言葉やその意味類似性などから後に続く言葉を推論しているだけで、計算能力自体は無いので当然の結果ですが。

![スクリーンショット 2023-10-10 15.33.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319271/84b04ed0-80a8-dc22-acc8-a4f4784840e6.png)

プロンプトを工夫することで正しい回答をするようにはなりますが、AIにも計算機を貸してあげればこのような間違いをなくせます。

![スクリーンショット 2023-10-10 15.34.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1319271/846646fd-e84f-274f-beec-3098ee1e06f0.png)

AIにとっての計算機の1つが、llm-mathというツールです。では、実際にAIに計算機（llm-math）を与えて同じ質問をしてみます。SerpAPIとの違いは、```load_tools()```の最初の引数のみです。

```python:tool_llm-math.py
from langchain.llms import OpenAI

from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.agents import AgentType

llm = OpenAI(model='gpt-3.5-turbo-instruct', temperature=0)
tools = load_tools(["llm-math"], llm=llm) # ツールの準備
agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True) # エージェントの準備

agent.run("17の8乗は？")
```

実行ログを見てみると、この問題解決のために計算機を使うべきだと考えて、ActionとしてCalculator（llm-math）を利用しています。計算機に```17^8```をInputして、計算機からの答えとして6975757441という結果をObservation（観察）しています。

これにより、最終回答にたどり着いた事を認識し、'6975757441'という正しい回答を人間にしています。これも、```verbose=True```を指定しなれば、AIがまるで暗算したかのように見えますが、実はこっそり計算機を使っていたのです。

```
> Entering new AgentExecutor chain...
 I should use the calculator to solve this problem.
Action: Calculator
Action Input: 17^8
Observation: Answer: 6975757441
Thought: I now know the final answer.
Final Answer: 6975757441

> Finished chain.

'6975757441'
```

# 複数ツールの組み合わせ
ここまでツールを一度に1つしか与えていませんが、もちろん一度に複数与えることも可能です。```load_tools()```の最初の引数（list型）に複数のツールを指定するだけです。どのツールをどの順番で使用するかはLLMが考えてくれます。

```some_tools.py
from langchain.llms import OpenAI

from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.agents import AgentType

llm = OpenAI(model='gpt-3.5-turbo-instruct', temperature=0)
tools = load_tools(["serpapi", "llm-math"], llm=llm) # 複数ツールの準備
agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True) # エージェントの準備

agent.run("リコリスリコイルの主人公は誰？彼女の年齢を8乗するといくつになるか？")
```

実行結果（思考過程）は下記となります。実際には千束は17歳という設定ですが、なぜか第8話で18歳になったという誤情報を引いちゃったようです。我々人間もGoogleの検索結果を鵜呑みにしないよう気をつけないといけないですね。ちなみに、18の8乗の計算は合っています。

```
> Entering new AgentExecutor chain...
 I should use a search engine to find information about the main character of Licorice Recoil and her age.
Action: Search
Action Input: Licorice Recoil main character
Observation: ['Chisato is the all-time strongest agent at Lycoris, alongside the cool headed talented-but-mysterious Takina; they work together at a café serving coffee, sweets, providing childcare, shopping, and teaching Japanese.', 'Lycoris Recoil type: 2022 ‧ Girls with guns ‧ 1 season.', 'Lycoris Recoil main_tab_text: Overview.', 'Lycoris Recoil kgmid: /g/11smc2wtbw.', 'Lycoris Recoil first_episode_date: July 2, 2022 (Japan).', 'Lycoris Recoil genres: Girls with guns, Action fiction.', 'Lycoris Recoil created_by: - Spider Lily (original work); Asaura (original story);.', 'Lycoris Recoil demographic: Seinen.', 'Lycoris Recoil directed_by: Shingo Adachi.', 'Kurumi. Kusunoki. L. Category:Lycoris. M. Majima · Category:Male. Mika.']
Thought: I should refine my search to find information specifically about the main character's age.
Action: Search
Action Input: Licorice Recoil main character age
Observation: ["Japanese. 錦木 千束 ; Romaji. Nishikigi Chisato ; Gender. Female ; Age. 18 (As of episode 8) 17 (As of episode 1) ; Height. 162cm (5'3) ..."]
Thought: I should use a calculator to find the answer to the second part of the question.
Action: Calculator
Action Input: 18^8
Observation: Answer: 11019960576
Thought: I now know the final answer.
Final Answer: Chisato Nishikigi is the main character of Licorice Recoil and her age is 18. When her age is raised to the 8th power, she would be 11,019,960,576 years old.

> Finished chain.

'Chisato Nishikigi is the main character of Licorice Recoil and her age is 18. When her age is raised to the 8th power, she would be 11,019,960,576 years old.'
```


# 最後に
お疲れ様でした。今回はLangChainに親しみを持ってもらうために、Agentの話には深入りせずに簡単な例に絞って紹介してみました。でも、これだけでも、ChatGPTの、最新情報には対応できないとか、計算は苦手といった問題は解決できます。ツールはこれ以外にも[たくさん](https://book.st-hakky.com/docs/agents-of-langchain/)ありますし、自作したカスタムツールをLLMに使ってもらう事も可能です。もしニーズがあれば近い将来ご紹介する機会があるかもしれません。

さて、当アカウントでの初投稿、いかがだったでしょうか？気に入ってもらえると嬉しいです。今後もChatGPT、OpenAI API、LangChainだけでなく、大好きなAI/XRなどいろいろなテーマで記事を書いていきたいと思っています。どうぞよろしくお願いします！