File size: 10,080 Bytes
04b2d94
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# -*- coding: utf-8 -*-
"""openai_function_calling_sample_code.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1FBRyZ2hTn04o_nvNDaZFtroebmN4jiz-

# Function Calling  について LangChain Agent と比較しながら試してみた

こちらの記事のサンプルコードになります:https://qiita.com/yu-Matsu/items/12b686fe4cab343f50b3

## 必要なライブラリのインストール
"""

# !pip install openai==0.27.6
# !pip install langchain==0.0.205

"""## 一部ライブラリのインポート、APIキーの設定"""

import json
import os
import sys
import openai
import dotenv

dotenv.load_dotenv(".env")
openai.api_key = os.environ.get("OPENAI_API_KEY")
# print("OPENAI_API_KEY: ", openai.api_key)

print("""## 普通にChatGPTに天気を聞いてみる""")

def chat_gpt_without_functions(text):
    response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo-0613",
        messages=[
            {"role": "user", "content": text},
        ]
    )
    
    content = response["choices"][0]["message"]["content"]
    print(content)

    return content

print("""# 挨拶は普通に返ってくるが...""")
chat_gpt_without_functions("こんにちは")

print("""# 天気を聞いても返してくれない""")
chat_gpt_without_functions("東京の天気を教えて")

print("""## 今回利用する天気を返す関数""")

print("""# locationに応じて固定値で天気を返すだけの簡単な関数""")
def weather_function(location):
    match location:
      case "東京" | "Tokyo":
        weather = "晴れ"
      case "大阪" | "Osaka":
        weather = "曇り"
      case "北海道" | "Hokkaido":
        weather = "雪"
      case _ :
        weather = "不明"

    weather_answer = [
        {"天気": weather}
    ]

    return json.dumps(weather_answer)

print("""# 実際に Function callingを動かしてみる""")

def chat_gpt_with_function(text):
    # AIが呼び出せる関数の定義
    functions = [
        # 何をする関数かについて記述
        {
            "name": "weather",
            "description": "天気を調べる",
            "parameters": {
                "type": "object",
                "properties": {
                    # location引数についての情報を記述
                    "location": {
                        "type": "string",
                        "description": "天気を知りたい場所を入力。例: 東京",
                    },
                },
                "required": ["location"],
            },
        }
    ]

    # ユーザーの入力から、Functions Callingが必要かどうか判断する
    response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo-0613",
        messages=[
            {"role": "user", "content": text},
        ],
        functions=functions,
        function_call="auto",
    )

    # Function Callingが必要なければ、そのままAIの回答になる
    message = response["choices"][0]["message"]

    # Functions Callingが必要な場合は、messageに関数名と引数を格納されている
    if message.get("function_call"):


        print("------function_callの中身------")
        print(message["function_call"])
        print("--------------------------------------")

        # messageから実行する関数と引数を取得
        function_name = message["function_call"]["name"]
        arguments = json.loads(message["function_call"]["arguments"])

        # 関数を実行
        if function_name == "weather":
            function_response = weather_function(
                location=arguments.get("location"),
            )

        # 関数の実行結果を元にAIの回答を生成
        second_response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo-0613",
            messages=[
                {"role": "user", "content": text},
                message,
                {
                    "role": "function",
                    "name": function_name,
                    "content": function_response,
                },
            ],
        )

        content = "AIの回答: " + second_response.choices[0]["message"]["content"]
    else:
        content = "AIの回答: " + message["content"]
    
    print(content)
    return content

print("""# 挨拶をした場合は、Functionを呼び出す必要がないと判断される""")
chat_gpt_with_function("こんにちは")

print("""# 天気を聞いた場合、Functionを呼び出す必要があると判断され、関数名と引数の情報が「function_call」に格納されている""")
chat_gpt_with_function("東京の天気を教えて")

print("""## LangChain の Agent の場合""")

from langchain.llms import OpenAI
from langchain.agents import initialize_agent, Tool
from langchain.agents.mrkl import prompt

def lang_chain_agent(text):
    llm = OpenAI(model_name='gpt-3.5-turbo-0613')
    # toolsに利用したいToolを格納する。LangChainで用意されたToolを利用することも出来る。
    # 代表的なTool一覧:https://book.st-hakky.com/docs/agents-of-langchain/
    tools = [
        Tool(
            name = "Weather",
            func=weather_function,
            description="天気を知りたい場所を入力。例: 東京",
        )
    ]

    # エージェントの準備
    agent = initialize_agent(
        tools,
        llm,
        agent="zero-shot-react-description",
        agent_kwargs=dict(suffix='Answer should be in Japanese.' + prompt.SUFFIX),
        verbose=True,
        return_intermediate_steps=True)

    response = agent({"input": text})

    return response

print("""
# 東京の天気について質問すると、toolsで用意した「Weather」が利用され、その結果を元にAIの回答が生成されていることが分かる
# 動作が安定しない可能性があるため、失敗する場合は何度か実行してみて下さい
lang_chain_agent("東京の天気を教えて")
""")

print("""## LangChain で Function Calling を利用してみる""")

from langchain.schema import (
    AIMessage,
    HumanMessage,
    FunctionMessage
)
from langchain.chat_models import ChatOpenAI

def lang_chain_with_function_calling(text):
    # AIが利用する関数の定義
    functions = [
        # 何をする関数かについて記述
        {
            "name": "weather",
            "description": "天気を調べる",
            "parameters": {
                "type": "object",
                "properties": {
                    # location引数についての情報を記述
                    "location": {
                        "type": "string",
                        "description": "天気を知りたい場所を入力。例: 東京",
                    },
                },
                "required": ["location"],
            },
        }
    ]

    # ユーザーの入力をmessagesに格納
    messages=[HumanMessage(content=text)]

    llm=ChatOpenAI(model_name='gpt-3.5-turbo-0613')

    # ユーザーの入力内容から、Functions Callingが必要かどうか判断する
    # Function Callingが必要なければ、こちらの結果がAIの回答になる
    message = llm.predict_messages(
        messages, functions=functions
    )

    print("==================")
    print(message)
    print("==================")

    # Functions Callingが必要な場合は、additional_kwargsに関数名と引数を格納されている
    if message.additional_kwargs:

        # messageから実行する関数と引数を取得
        function_name = message.additional_kwargs["function_call"]["name"]
        arguments = json.loads(message.additional_kwargs["function_call"]["arguments"])

        # 関数を実行
        function_response = weather_function(
            location=arguments.get("location"),
        )

        # 実行結果をFunctionMessageとしてmessagesに追加
        function_message = FunctionMessage(name=function_name, content=function_response)
        messages.append(function_message)

        # FuncitonMessageを元に、AIの回答を取得
        second_response = llm.predict_messages(
            messages=messages, functions=functions
        )
        content = "AIの回答: " + second_response.content
    else:
        content = "AIの回答: " + message.content
    
    print(content)
    return content

print("""# 挨拶の場合は、contentにAIのレスポンスが格納されており、Functionの情報は含まれていない""")
lang_chain_with_function_calling("こんにちは")

print("""# 天気を聞いた場合、contentにAIのレスポンスは含まれず、additional_kwgargsに呼び出すFunctionの情報が格納されている""")
lang_chain_with_function_calling("東京の天気を教えて")

print("""# Agent にも対応""")

from langchain.agents import AgentType

def lang_chain_agent_with_function_calling(text):
    llm = ChatOpenAI(model_name='gpt-3.5-turbo-0613')
    # カスタムツールの登録は従来のAgentと同様
    tools = [
        Tool(
            name = "Weather",
            func=weather_function,
            description="天気を知りたい場所を入力。例: 東京"
        )
    ]

    # エージェントの準備
    agent = initialize_agent(
        tools,
        llm,
        agent=AgentType.OPENAI_FUNCTIONS,  # ここで、AgentType.OPENAI_FUNCTIONSを指定する
        verbose=True,
        return_intermediate_steps=True)

    response = agent({"input": text})
    print(response)
    return response

print("""# 挨拶の場合はFunctionが利用されていないことが実行結果(intermediate_steps)から分かる""")
lang_chain_agent_with_function_calling("こんにちは")

print("""# 天気を聞いた場合、intermediate_stepsにてAIMessageとしてFunctionが呼び出されていることが分かる""")
lang_chain_agent_with_function_calling("今日の東京の天気を教えて")