0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Memory対応したOpenAI Function Agentで実現する天気予報対応チャットアプリの実現方法

Posted at

はじめに

以前OpenAIのFunction Callingを利用方法を確認した。今回はFunction Callingの機能をサポートしたChatGPTのようなチャットアプリの実現方法について検討した。

結論

以下の3点からLangChainを利用したChatGPTのようなチャットアプリの実現方法について確認した。

  1. ConversationBufferMemoryを使用してチャット履歴を管理する
    LangChainでは、ConversationBufferMemoryを利用してチャット履歴を管理することができる。ユーザーとの対話の過去のやり取りを記憶し、チャットの流れを維持することが可能となる。

  2. OpenAI Function AgentでOpenAI APIのFunction Calling機能を利用する
    OpenAI Function Agentは、OpenAI APIのFunction Calling機能を利用することができる特殊なエージェントだ。これにより、カスタム関数を定義してOpenAI APIを呼び出すことが可能となる。例えば、天気予報を取得するカスタム関数を作成し、Function Agentを使用してOpenAI APIを介して呼び出すことができる。

  3. OpenAI Function AgentにMemoryを追加する
    OpenAI Function Agentは、さらにMemoryを追加することができます。このMemoryには、必要な情報を保存しておくことができる。例えば、天気予報を取得する前に、ユーザーの都市情報をMemoryに保存しておき、それを利用して天気予報の回答を行うことができる。

以上の3つの要素を組み合わせて、LangChainを使用したチャットアプリでOpenAI Function Agentを活用し、チャット履歴に対応した天気予報回答プログラムを実現した。ユーザーとの自然な対話が可能となり、過去のやり取りを覚えているかのような対話体験を提供することができる。

ChatGPTのようなチャットアプリとは?

下記のサイトで紹介されているような過去のチャット履歴の文脈に基づいて回答が返ってくるChatGPTクローンのようなアプリのことだ。

過去のチャット履歴を活用して回答する方法は、OpenAIのようなニューラルネットワークには記憶機能がないため、履歴データを別途提供する必要がある。これは少し力技のように見えるが、学習中でない限り、ニューラルネットワーク自体は情報を覚えることができないためだ。

履歴データを手動で管理するのは簡単ではないため、LangChainはMemory機能を活用して実装する。特にConversationBufferMemoryという機能は、チャットに特化しており、ChatGPTのようなアプリケーションをより簡単に開発できるようになっている。これを利用することで、過去の会話を効果的に活用しながらよりスムーズな対話型のAIアプリを作成することができる。

LangChain AgentでFunction Callingを利用する

LangChainを利用するのであれば、OpenAI APIのFunction Calling機能もLangChainから利用するのが良いだろう。LangChainにはFunction Callingのような機能を独自に実現するAgentがある。Function CallingがOpenAIでサポートされたのに合わせてOpenAI Functions Agentが実装された。

OpenAI Functions Agentを利用するには最新のLangChainのライブラリが必要になる。ライブラリのアップデートをしておきたい。

$ pip install -U langchain

AgentにMemoryを追加する

OpenAI Functions AgentへのMemoryの追加方法は、これまでのAgentへのMemoryの追加とは手法が異なるため注意が必要だ。

Custom Agentを使ってAgentにMemoryを追加する

AgentへMemoryを追加する場合、通常はCustom Agentを作成して実現する。しかし、この方法はOpenAI Functions Agentには適用できない。

OpenAI Functions AgentにはCustom Agentを作成する手法が利用できない

OpenAI Functions AgentにMemoryを追加する

OpenAI Functions AgentにMemoryを追加するには下記のように行う。

  1. エージェントの設定情報を格納するagent_kwargsにConversationBufferMemoryへの参照を設定
  2. チャット履歴を管理するConversationBufferMemoryを作成する
  3. AgentType.OPENAI_FUNCTIONSを指定してエージェントを初期化
agent_kwargs = {
    "extra_prompt_messages": [MessagesPlaceholder(variable_name="memory")],
}

memory = ConversationBufferMemory(memory_key="memory", return_messages=True)

agent = initialize_agent(
    tools,
    llm,
    agent=AgentType.OPENAI_FUNCTIONS,
    verbose=True,
    agent_kwargs=agent_kwargs,
    memory=memory,
)
agent.run("hi")

チャットで天気予報を回答させる

LangChainを使って、履歴管理とFunction Callingを組み合わせて利用する方法を確認した。ここでは、実際にチャットで天気予報を回答させるプログラムを実行して結果を確認する。

確認用プログラム

Google Colab
!pip install langchain
!pip install openai
import os
os.environ["OPENAI_API_KEY"] = "<Your OpenAI API KEY>"

from langchain.agents import Tool
from langchain.chains.conversation.memory import ConversationBufferMemory
from langchain import OpenAI
from langchain.utilities import GoogleSearchAPIWrapper
from langchain.agents import initialize_agent
from langchain.agents import AgentType
from langchain.chat_models import ChatOpenAI
from langchain.schema import SystemMessage
from langchain import OpenAI, LLMChain, PromptTemplate

# 気象データをapi.open-meteo.comから取得する
import requests
import json
def get_weather_info(latitude, longitude):
    base_url = "https://api.open-meteo.com/v1/forecast"
    parameters = {
        "latitude": latitude,
        "longitude": longitude,
        #        "current_weather": "true",
        "hourly": "temperature_2m,relativehumidity_2m",
        "forecast_days": "3",
        "timezone": "Asia/Tokyo"
    }
    response = requests.get(base_url, params=parameters)
    if response.status_code == 200:
        data = response.json()
        return json.dumps(data)
    else:
        return None

#
# OpenAI Functions Agent に get_weather_info の使い方を教える
#
from typing import Type
from pydantic import BaseModel, Field
from langchain.tools import BaseTool

class WeatherInfoInput(BaseModel):
  latitude: int = Field(descption = "latitude")
  longitude: int = Field(descption = "longitude")

class WeatherInfo(BaseTool):
  name = 'get_weather_info'
  description = "This is useful when you want to know the weather forecast. Enter longitude in the latitude field and latitude in the longitude field."
  args_schema: Type[BaseModel] = WeatherInfoInput
  def _run(self, latitude: int, longitude: int):
    return get_weather_info(latitude, longitude)
  def _arun(self, ticker: str):
    raise NotImplementedError("get_stock_performance does not support async")

#
# MemoryとFunction Callingに対応した Agent を用意する
#
from langchain.prompts import MessagesPlaceholder
from langchain.memory import ConversationBufferMemory

llm = ChatOpenAI(temperature=0, model="gpt-3.5-turbo-16k")

tools = [
  WeatherInfo(),
]

def OpenAIFunctionsAgent(tools= tools, llm = llm, verbose=False):
  agent_kwargs = {
      "extra_prompt_messages": [MessagesPlaceholder(variable_name="memory")],
  }
  memory = ConversationBufferMemory(memory_key="memory", return_messages=True)
  # Add the user's prompt to the memory if need
  prompt = """
  The following is a friendly conversation between a human and an AI.
  The AI is talkative and provides lots of specific details from its context.
  If the AI does not know the answer to a question, it truthfully says it does not know.
  """
  memory.save_context({"input": prompt}, {"ouput": "understand!"})
  return initialize_agent(
      tools=tools,
      llm=llm,
      agent=AgentType.OPENAI_FUNCTIONS,
      verbose=True,
      agent_kwargs=agent_kwargs,
      memory=memory,
  )

agent_chain = OpenAIFunctionsAgent(tools = tools, llm = llm, verbose = True)

プログラムの実行結果

チャット履歴機能が上手に働いている事を確認するために、挨拶から開始する。

agent_chain.run(input="こんにちは")
> Entering new AgentExecutor chain...
こんにちはお元気ですか何かお手伝いできますか

> Finished chain.

AIアシスタントのように挨拶した

agent_chain.run(input="わたしの名前はシャンシャンです")
> Entering new AgentExecutor chain...
こんにちはシャンシャンさん素敵な名前ですね私はAIアシスタントですどのようにお手伝いできますか

> Finished chain.
こんにちはシャンシャンさん素敵な名前ですね私はAIアシスタントですどのようにお手伝いできますか

名前を名乗ってみた。AIアシスタントは名前を覚えただろうか?

agent_chain.run("私の名前がわかりますか?")
 new AgentExecutor chain...
はい先ほどおっしゃった通りあなたの名前はシャンシャンさんですね私は会話の中で情報を覚えていますのでお名前も覚えていますよどのようなお話をしましょうか

> Finished chain.
はい先ほどおっしゃった通りあなたの名前はシャンシャンさんですね私は会話の中で情報を覚えていますのでお名前も覚えていますよどのようなお話をしましょうか

名前を回答できている。

print(agent_chain.run(input="横浜の今日の天気は?人がどのように感じる天気なのかと、ドライブしているユーザーが今日どのように過ごすと良いか提案を含めてアナウンサー口調で答えよ"))
> Entering new AgentExecutor chain...
Invoking: `get_weather_info` with `{'latitude': 35.4437, 'longitude': 139.638}`
{"latitude": 35.0, "longitude": 139.0, "generationtime_ms": 0.10704994201660156, "utc_offset_seconds": 32400, "timezone": "Asia/Tokyo", "timezone_abbreviation": "JST", "elevation": 234.0, "hourly_units": {"time": "iso8601", "temperature_2m": "\u00b0C", "relativehumidity_2m": "%"}, "hourly": {"time": ["2023-07-29T00:00", "2023-07-29T01:00", "2023-07-29T02:00", "2023-07-29T03:00", "2023-07-29T04:00", "2023-07-29T05:00", "2023-07-29T06:00", "2023-07-29T07:00", "2023-07-29T08:00", "2023-07-29T09:00", "2023-07-29T10:00", "2023-07-29T11:00", "2023-07-29T12:00", "2023-07-29T13:00", "2023-07-29T14:00", "2023-07-29T15:00", "2023-07-29T16:00", "2023-07-29T17:00", "2023-07-29T18:00", "2023-07-29T19:00", "2023-07-29T20:00", "2023-07-29T21:00", "2023-07-29T22:00", "2023-07-29T23:00", "2023-07-30T00:00", "2023-07-30T01:00", "2023-07-30T02:00", "2023-07-30T03:00", "2023-07-30T04:00", "2023-07-30T05:00", "2023-07-30T06:00", "2023-07-30T07:00", "2023-07-30T08:00", "2023-07-30T09:00", "2023-07-30T10:00", "2023-07-30T11:00", "2023-07-30T12:00", "2023-07-30T13:00", "2023-07-30T14:00", "2023-07-30T15:00", "2023-07-30T16:00", "2023-07-30T17:00", "2023-07-30T18:00", "2023-07-30T19:00", "2023-07-30T20:00", "2023-07-30T21:00", "2023-07-30T22:00", "2023-07-30T23:00", "2023-07-31T00:00", "2023-07-31T01:00", "2023-07-31T02:00", "2023-07-31T03:00", "2023-07-31T04:00", "2023-07-31T05:00", "2023-07-31T06:00", "2023-07-31T07:00", "2023-07-31T08:00", "2023-07-31T09:00", "2023-07-31T10:00", "2023-07-31T11:00", "2023-07-31T12:00", "2023-07-31T13:00", "2023-07-31T14:00", "2023-07-31T15:00", "2023-07-31T16:00", "2023-07-31T17:00", "2023-07-31T18:00", "2023-07-31T19:00", "2023-07-31T20:00", "2023-07-31T21:00", "2023-07-31T22:00", "2023-07-31T23:00"], "temperature_2m": [23.3, 23.5, 22.8, 22.4, 22.0, 21.6, 22.9, 24.9, 26.6, 28.3, 29.6, 30.6, 30.5, 30.8, 30.7, 30.4, 29.9, 28.7, 26.9, 25.2, 24.4, 23.6, 23.2, 22.8, 22.5, 22.5, 22.2, 22.1, 22.3, 22.0, 22.7, 24.5, 26.4, 28.0, 29.4, 31.0, 31.8, 32.0, 32.1, 31.6, 30.8, 29.2, 27.5, 26.3, 25.7, 25.1, 24.7, 24.3, 24.1, 24.2, 24.0, 23.6, 23.5, 23.4, 23.9, 25.3, 26.8, 28.0, 29.0, 29.4, 30.8, 30.7, 29.4, 29.7, 28.1, 27.4, 26.8, 25.9, 25.3, 25.2, 25.1, 25.0], "relativehumidity_2m": [92, 88, 90, 89, 91, 92, 90, 85, 81, 70, 62, 59, 64, 63, 64, 62, 60, 62, 70, 82, 82, 88, 90, 92, 94, 93, 92, 92, 90, 90, 89, 84, 75, 68, 62, 55, 54, 55, 55, 58, 61, 68, 76, 82, 87, 91, 90, 90, 91, 90, 91, 92, 94, 93, 92, 87, 79, 73, 69, 66, 57, 54, 60, 61, 71, 77, 81, 85, 90, 89, 87, 85]}}横浜の今日の天気は気温は23.3から32.0の間で変動します朝の気温は23.3昼間に最高気温の32.0になります湿度は92%から54%の範囲で変動し朝は92%の湿度がありますが昼間には54%まで下がります
このような天気の中ドライブを楽しむには朝の涼しい時間帯や夕方の日が傾く頃がおすすめです気温が高くなる昼間はエアコンを効かせた車内で快適に過ごすことができますまた日差しを避けるために帽子や日焼け止めを使用することも大切です
横浜の天気は変わりやすいので天気予報をチェックしながら計画を立てることをおすすめします楽しいドライブをお楽しみください

> Finished chain.
横浜の今日の天気は気温は23.3から32.0の間で変動します朝の気温は23.3昼間に最高気温の32.0になります湿度は92%から54%の範囲で変動し朝は92%の湿度がありますが昼間には54%まで下がります
このような天気の中ドライブを楽しむには朝の涼しい時間帯や夕方の日が傾く頃がおすすめです気温が高くなる昼間はエアコンを効かせた車内で快適に過ごすことができますまた日差しを避けるために帽子や日焼け止めを使用することも大切です
横浜の天気は変わりやすいので天気予報をチェックしながら計画を立てることをおすすめします楽しいドライブをお楽しみください

横浜の天気について問い合わせたところ、get_weather_info関数を呼び出して気象データを入手し天気予報を回答した。

print(agent_chain.run(input="北海道は?"))
> Entering new AgentExecutor chain...
[省略...]

> Finished chain.
北海道の今日の天気は気温は19.3から24.8の間で変動します朝の気温は19.3昼間に最高気温の24.8になります湿度は96%から72%の範囲で変動し朝は96%の湿度がありますが昼間には72%まで下がります
このような天気の中ドライブを楽しむには涼しい時間帯や湿度が低い時間帯がおすすめです朝の涼しい時間帯や夕方の日が傾く頃にドライブをすると快適に過ごすことができますまた北海道の景色を楽しむために美しい自然や観光地を訪れるのも良いでしょう
天気は変わりやすいので天気予報をチェックしながら計画を立てることをおすすめします楽しいドライブをお楽しみください

「北海道は?」という問いに対して、「北海道の天気は?」という問いだと予測して北海道の天気を回答した。チャット履歴とFunction Callingの組み合わせに対応したChatGPTのようなチャットアプリの実現できそうだ。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?