見出し画像

ChatGPT勉強日記(#10) LangChainの総復習-第1回

Open AIにどっぷり浸かった案件が終わり、しばらく中規模のWebサービスやモバイルサービスを設計・開発していたため、何ヶ月かLLMから離れてしまっていた。
再びLLMを使った仕事を始める前に、久しぶりにLangChainのバージョンヒストリーを見るとエグいほどバージョンが変わっていた。。
これはベーシックなところから一度おさらいをする必要があると思い、LangChainの総復習をすることにした。

The LangChain CookBook

昨年、LangChainを始めた触ったときに最初意味がわからなくて、いろいろとブログやチュートリアルを見た結果、一番わかり易いのが下のGreg Kamradt氏のYoutube Videoだった。

ものすごく超特急で説明していくのだが、説明のセンスが素晴らしく、LangChainでできることや動作の仕組みがすっと頭に入ってきたのである。

再びKamradt氏のビデオと、下のチュートリアルを見ておさらいすることにした。

このビデオもサンプルコードも1年ほど前に作られたものであり、おそらく今のLangChainと互換性があるものはないであろう。ただ、それでもこのビデオは見るに値する。
このビデオを見ながら、氏のサンプルコードを最新のLangChainで動くように改良していきながら復習してみようと考えた。

Chat Messages

まず、Jupyter Notebookを開き、必要なライブラリのインストールを行った。

!pip install python-dotenv
!pip install langchain
!pip install -U langchain-openai

インストールしたlangchainモジュールのバージョンは0.1.20
後述するが、最新のLangChainになってlangchainモジュールだけだけじゃなくて、langchain-openaiというモジュールが新たに必要になるので、ここでインストールしておく。

環境変数の読み込み部分は特に同じコードで問題はない。

from dotenv import load_dotenv
import os

load_dotenv()

openai_api_key=os.getenv('OPENAI_API_KEY', 'your_api_key')

(your_api_keyのところにOpen AIのAPI Keyを入れる)

(ここからはチュートリアル形式になるので、説明が「ですます」調になります。)

ChatOpenAIオブジェクトの生成

CookbookではlangchainモジュールからChatOpenAIクラスを参照していましたが、langchainじゃなくて、langchain_openaiモジュールからChatOpenAIを使うように変更が必要です。

from langchain_openai import ChatOpenAI
from langchain.schema import HumanMessage, SystemMessage, AIMessage

# This it the language model we'll use. We'll talk about what we're doing below in the next section
chat = ChatOpenAI(temperature=.7, openai_api_key=openai_api_key)

チャットの送信部分(OpenAIのモデルに、SystemMessageとHumanMessageを送信する部分)は下のようになりました。

chat.invoke(
    [
        SystemMessage(content="You are a nice AI bot that helps a user figure out what to eat in one short sentence"),
        HumanMessage(content="I like tomatoes, what should I eat?")
    ]
)

以前は、以下のようにcallableオブジェクトの関数を呼ぶ形式でチャットを送っていましたが、

chat(
    [
        HumanMessage(content="What day comes after Thursday?")
    ]
)

最新のLangChainでは、下のようにinvokeメソッドをつかってSystemMessageなどのメッセージをOpen AI APIに送信します。

chat.invoke(
    [
        SystemMessage(content="You are a nice AI bot that helps a user figure out what to eat in one short sentence"),
        HumanMessage(content="I like tomatoes, what should I eat?")
    ]
)

(SystemMessageがシステムテンプレート、Human Messageが我々”人間”からの入力です。)

LLMモデルの取得

Cookbookでは、langchain.llmモジュールからOpenAIクラスをインポートしていましたが、その後、このクラスはlangchain_openaiモジュールには移動したようです。

from langchain_openai import OpenAI

llm = OpenAI(model_name="gpt-3.5-turbo-instruct", openai_api_key=openai_api_key)

また、model_nameは"text-ada-001"がduplicatedになっているので、したのドキュメントを参考にしgpt-3.5-turbo-instructを選びました。

llm("What day comes after Friday?")



-> '\n\nSaturday'

が返ってきて動作が確認できました。

ただ、このgpt-3.5-turbo-instructのモデルを確認してみると、https://platform.openai.com/docs/models/gpt-3-5-turbo

下のように書かれていて

Similar capabilities as GPT-3 era models. Compatible with legacy Completions endpoint and not Chat Completions.

このモデルが、"Completions endpoint"というレガシーのOpen AI APIに対応したモデルだということがわかりました。

from langchain_openai import OpenAI

llm = OpenAI(model_name="gpt-3.5-turbo-0125", openai_api_key=openai_api_key)
llm("What day comes after Friday?")

のように、gpt-3.5-turbo-instructよりも新しいgpt-3.5-turbo-0125に置き換えてみると、

NotFoundError: Error code: 404 - {'error': {'message': 'This is a chat model and not supported in the v1/completions endpoint. Did you mean to use v1/chat/completions?', 'type': 'invalid_request_error', 'param': 'model', 'code': None}}

このようなエラーが返ってきました。どうやらlangchain_openaiモジュールのOpenAIクラスは新しいモデルでは使えないようです。

再び、ChatOpenAIクラス

ChatOpenAIクラスに戻って、modelに`gpt-3.5-turbo-0125`, `gpt-4-turbo`, `gpt-4o`などのモデルを入れてみると、動作するので、こちらはレガシーではなく、新しいAPIにつながっているようです。

chat = ChatOpenAI(model='gpt-4o', temperature=.7, openai_api_key=openai_api_key)
chat.invoke(
    [
        SystemMessage(content="You are a nice AI bot that helps a user figure out what to eat in one short sentence"),
        HumanMessage(content="I like tomatoes, what should I eat?")
    ]
)

今後はOpenAIクラスではなく、ChatOpenAIクラスを使ってOpen AI APIとインタラクションする事になりそうです。

Function Calling

ユーザーのチャットから特定ののパラメータ値を抜き取るためのOpen AI APIのFunction Callingの機能ですが、こちらはCookbookのコードがそのまま動作しました。

import json
chat = ChatOpenAI(model='gpt-4o', temperature=1, openai_api_key=openai_api_key)

output = chat(messages=
     [
         SystemMessage(content="You are an helpful AI bot"),
         HumanMessage(content="What’s the weather like in Tokyo right now? What degrees celsicus now?")
     ],
     functions=[{
         "name": "get_current_weather",
         "description": "Get the current weather in a given location",
         "parameters": {
             "type": "object",
             "properties": {
                 "location": {
                     "type": "string",
                     "description": "The city and state, e.g. San Francisco, CA"
                 },
                 "unit": {
                     "type": "string",
                     "enum": ["celsius", "fahrenheit"]
                 }
             },
             "required": ["location"]
         }
     }
     ]
)

function_call = output.additional_kwargs.get('function_call')
if function_call:
    arguments = function_call.get('arguments')
    if arguments:
        arguments_dict = json.loads(arguments)
        print("Parsed Arguments:", arguments_dict)
    else:
        print("No arguments found.")
else: 
    print("No function_call found.", output)

後半のコードを改造して、outputからfunction callingで抜き取ったパラメータを抽出して表示するようにしました。
結果はこのようになっていて、"location"と"celsius"という2つのパラメータが抜き取れたことがわかります。

Parsed Arguments: {'location': 'Tokyo, Japan', 'unit': 'celsius'}

function callingを色々と試していると、HumanMessage(つまりユーザーからのインプット)が質問形式、問い合わせ形式になっていないとうまく動かないケースがあることがわかります。function callingの中身が質問のカテゴリを読み取るためにある程度の最適化が行われたものになっていそうです。

この例だと日本人にはわかりにくいので、次のような例を考えてみました。

import json
chat = ChatOpenAI(model='gpt-4o', temperature=1, openai_api_key=openai_api_key)

output = chat(messages=
     [
         SystemMessage(content="You are at the customer support center of an electric appliance shop."),
         HumanMessage(content="I have a question about an issue with my mobile phone: the display is blinking.")
     ],
     functions=[{
         "name": "get_product_type_and_issue",
         "description": "Get the product type and the symptoms of the issue the user is experiencing.",
         "parameters": {
             "type": "object",
             "properties": {
                 "product": {
                     "type": "string",
                     "description": "The type of product (e.g., mobile phone, TV, shaver)."
                 },
                 "symptom": {
                     "type": "string",
                     "description": "The symptoms of the issue the user is experiencing."
                 }
             },
             "required": ["location"]
         }
     }
     ]
)

function_call = output.additional_kwargs.get('function_call')
if function_call:
    arguments = function_call.get('arguments')
    if arguments:
        arguments_dict = json.loads(arguments)
        print("Parsed Arguments:", arguments_dict)
    else:
        print("No arguments found.")
else: 
    print("No function_call found.", output)

これはカスターサポートのチャットボットを想定したfunction callingです。

下のように、descriptionに
"Get the product type and the symptoms of the issue the user is experiencing."
抜き出したいパラメータを以下のように設定しました。
HumanMessage(content="I have a question about an issue with my mobile phone: the display is blinking.")
のようなHumanMessageを投げると、問い合わせ内容から、
- 問い合わせ対象の製品
- 製品に起きている故障の症状
を抜き出してくれて、このようなJSONが返ってきます。

{'product': 'mobile phone', 'symptom': 'display is blinking'}

function callingをつかう最大の利点は、結果を他のAPIにつなげることが容易になることです。
上のJSONを自社の別のシステムのAPIに投げて、mobile phone専用の受付処理にユーザーを招待したり、カスターサポートデータベースに保存して、あとでmobile phone専用の対応部署からメールを送ったりするような使い方ができます。またその際にはカスタマーの対応内容全文ではなく、symptom(症状)のみを別の絡むに保存しておけると素早い対応システムが作れるかもしれません。
このように自社の既存のシステムや独自のシステムとLLMをつなげるのにfunction callingは非常に有効です。


さて、ここまでさわってみて、OpenAIクラスが最新のモデルと互換性がなくなっていることが飲み込めずどう対応したらよいのか調べながら右往左往してしまいましたが、ここまでのサンプルをどうにか動かすことができました。

次回は、引き続きKamradt氏のCookbookの残りのサンプルを見ていきます。

続く。


このブログに関する質問や、弊社(Goldrush Computing)へのOpenAI API、LLM、LangChain関連の開発案件の依頼は↓↓↓からお願いします。

mizutori@goldrushcomputing.com


次回は↓↓↓


この記事が気に入ったらサポートをしてみませんか?