簡単にLangChainを使う

# LangChainとは

LangChainとは、LLMを用いたアプリケーション開発を効率的に行うためのライブラリです。AgentやToolといった多くのライブラリが含まれています。便利で機能が豊富な反面、取っ付きづらくなっているかもしれません。

# pytool-directory

簡単にLangChainを体験できるライブラリの1つが pytool-directory です。DialogPlayディレクトリーに事前登録されたAPIの情報を利用して、必要に応じて簡単に外部APIを呼び出してくれます。

DialogPlayディレクトリーにはOpenAPI仕様書（openapi.yaml）と統合仕様書（integration.yaml）が公開されていて、APIを呼び出すためのインターフェースを自然言語処理で吸収するため、利用者はAPI仕様をほとんど意識する必要がありません。

同様の仕組みはChatGPTのプラグインでも利用されていますが、ChatGPTのプラグインはクローズドです。

PyPI - pytool-directory

https://pypi.org/project/pytool-directory/

https://github.com/dialogplay/pytool-directory

DialogPlayディレクトリー

https://tool-directory.dialogplay.jp


# サクッと使ってみる

### 前提

- Python 3.9以上
- 使用するAPIのAPI KEYを準備する

### インストール

```sh:
pip install pytool-directory
```

### 実行（今日の天気予報を尋ねる）

通常、ChatGPTは今日の天気は答えてくれませんが、toolでOpenWeatherMAPを使うように指定すると、APIを呼び出して答えてくれます。

```python:
import os
import langchain
from langchain.chat_models import ChatOpenAI
from langchain.agents import initialize_agent, load_tools, AgentType
from tool_directory import ToolLoader

# OpenAIのAPI KEY
os.environ['OPENAI_API_KEY'] = '<OpenAIのAPI KEYをここに指定>'

# 使用したいtool
tools = [];
tools.extend(ToolLoader('openweather').get_tools(parameters={'appid': '<OpenWeatherMAPのAPI KEYをここに指定>'}))

# 初期化
llm = ChatOpenAI(temperature=0, model_name='gpt-3.5-turbo')
agent = initialize_agent(tools, llm, agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION, verbose=True)

# 実行
answer = agent('今日の東京の天気を教えて。')
```

実行ログ

```
> Entering new AgentExecutor chain...
Action:
``
{
  "action": "GET https://api.openweathermap.org/data/2.5/weather",
  "action_input": {
    "q": "Tokyo",
    "units": "metric"
  }
}
``
Observation: {'coord': {'lon': 139.6917, 'lat': 35.6895}, 'weather': [{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04n'}], 'base': 'stations', 'main': {'temp': 24.22, 'feels_like': 24.26, 'temp_min': 23.12, 'temp_max': 24.89, 'pressure': 1015, 'humidity': 60}, 'visibility': 10000, 'wind': {'speed': 5.66, 'deg': 140}, 'clouds': {'all': 75}, 'dt': 1696326517, 'sys': {'type': 2, 'id': 2001249, 'country': 'JP', 'sunrise': 1696279017, 'sunset': 1696321434}, 'timezone': 32400, 'id': 1850144, 'name': 'Tokyo', 'cod': 200}
Thought:東京の天気は、曇りです。現在の気温は24.22℃で、湿度は60%です。風速は5.66m/sです。

> Finished chain.

```

### 実行（今の時刻を尋ねる）

通常、ChatGPTは時刻は答えてくれませんが、toolでtimeapiを使うように指定すると、APIを呼び出して答えてくれます。

```python:
import os
import langchain
from langchain.chat_models import ChatOpenAI
from langchain.agents import initialize_agent, load_tools, AgentType
from tool_directory import ToolLoader

# OpenAIのAPI KEY
os.environ['OPENAI_API_KEY'] = '<OpenAIのAPI KEYをここに指定>'

# 使用したいtool
tools = [];
tools.extend(ToolLoader('timeapi').get_tools())

# 初期化
llm = ChatOpenAI(temperature=0, model_name='gpt-3.5-turbo')
agent = initialize_agent(tools, llm, agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION, verbose=True)

# 実行
answer = agent('日本のタイムゾーンで今の時刻を教えて。')
```

実行ログ

```
> Entering new AgentExecutor chain...
Action:
``
{
  "action": "GET https://timeapi.io/api/Time/current/zone",
  "action_input": {
    "timeZone": "Asia/Tokyo"
  }
}
``
Observation: {'year': 2023, 'month': 10, 'day': 3, 'hour': 19, 'minute': 15, 'seconds': 33, 'milliSeconds': 996, 'dateTime': '2023-10-03T19:15:33.9969742', 'date': '10/03/2023', 'time': '19:15', 'timeZone': 'Asia/Tokyo', 'dayOfWeek': 'Tuesday', 'dstActive': False}
Thought:日本のタイムゾーンでの現在の時刻は、2023年10月3日19時15分です。

> Finished chain.
```

### 実行（複数のtoolの連携。現在地の天気を尋ねる）

天気の取得には現在地の指定が必要ですが、複数のtoolを連携して、IPアドレスから現在地を特定してから答えてくれます。

```python:
import os
import langchain
from langchain.chat_models import ChatOpenAI
from langchain.agents import initialize_agent, load_tools, AgentType
from tool_directory import ToolLoader

# OpenAIのAPI KEY
os.environ['OPENAI_API_KEY'] = '<OpenAIのAPI KEYをここに指定>'

# 使用したいtool
tools = [];
tools.extend(ToolLoader('openweather').get_tools(parameters={'appid': '<OpenWeatherMAPのAPI KEYをここに指定>'}))
tools.extend(ToolLoader('ipinfo').get_tools(parameters={'token': '<IPinfoのAPI KEYをここに指定>'}))

# 初期化
llm = ChatOpenAI(temperature=0, model_name='gpt-3.5-turbo')
agent = initialize_agent(tools, llm, agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION, verbose=True)

# 実行
answer = agent('日本のタイムゾーンで今の時刻を教えて。')
```

実行ログ

```
> Entering new AgentExecutor chain...
Action:
``
{
  "action": "GET https://ipinfo.io/",
  "action_input": {}
}
``
Observation: {'ip': '192.168.1.1', 'hostname': 'p10000001-ipoe.ipoe.ocn.ne.jp', 'city': 'Chōfu', 'region': 'Tokyo', 'country': 'JP', 'loc': '35.6592,139.5484', 'org': 'AS4713 NTT Communications Corporation', 'postal': '182-0000', 'timezone': 'Asia/Tokyo'}
Thought:日本のタイムゾーンでの現在の時刻を教えるために、まずIP情報を取得しました。IP情報にはタイムゾーンの情報も含まれています。次に、そのタイムゾーンに基づいて現在の時刻を取得します。

Action:
``
{
  "action": "Final Answer",
  "action_input": "現在の時刻はAsia/Tokyoのタイムゾーンで取得する必要があります。"
}
``



> Finished chain.


> Entering new AgentExecutor chain...
Action:
``
{
  "action": "GET https://ipinfo.io/",
  "action_input": {}
}
``
Observation: {'ip': '192.168.1.1', 'hostname': 'p10000001-ipoe.ipoe.ocn.ne.jp', 'city': 'Chōfu', 'region': 'Tokyo', 'country': 'JP', 'loc': '35.6592,139.5484', 'org': 'AS4713 NTT Communications Corporation', 'postal': '182-0000', 'timezone': 'Asia/Tokyo'}
Thought:現在の位置情報は東京都調布市です。天気予報を取得するために、OpenWeatherMap APIを使用します。

Action:
``
{
  "action": "GET https://api.openweathermap.org/data/2.5/weather",
  "action_input": {
    "q": "Chōfu,JP",
    "units": "metric"
  }
}
``


Observation: {'coord': {'lon': 139.5522, 'lat': 35.6556}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04n'}], 'base': 'stations', 'main': {'temp': 23.49, 'feels_like': 23.36, 'temp_min': 22.29, 'temp_max': 24.93, 'pressure': 1016, 'humidity': 56}, 'visibility': 10000, 'wind': {'speed': 4.85, 'deg': 151, 'gust': 5.35}, 'clouds': {'all': 99}, 'dt': 1696328380, 'sys': {'type': 2, 'id': 2033467, 'country': 'JP', 'sunrise': 1696279049, 'sunset': 1696321469}, 'timezone': 32400, 'id': 1864518, 'name': 'Chōfu', 'cod': 200}
Thought:現在の位置、東京都調布市の天気は、曇りです。気温は23.49℃で、湿度は56%です。

> Finished chain.
```

# まとめ

APIの仕様をほとんど意識することなく、自然な対話で外部のAPIを呼び出すことができました。