Skip to content

Instantly share code, notes, and snippets.

@peterroelants
Last active October 4, 2023 07:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save peterroelants/f3508b85b5574576b5f85cd90c721a9d to your computer and use it in GitHub Desktop.
Save peterroelants/f3508b85b5574576b5f85cd90c721a9d to your computer and use it in GitHub Desktop.
Simple example of OpenAI Function Calling in ReAct Loop
"""
Simple example of OpenAI Function Calling in ReAct Loop.
Functions are described in JSON with each parameter the function accepts described as a JSON Schema object.
More info:
- OpenAI Function Calling
- https://openai.com/blog/function-calling-and-other-api-updates
- https://platform.openai.com/docs/guides/gpt/function-calling
- https://platform.openai.com/docs/api-reference/chat/create#chat/create-functions
- JSON Schema
- https://json-schema.org/
- https://json-schema.org/specification-links#2020-12
- ReAct
- https://arxiv.org/abs/2210.03629
- https://peterroelants.github.io/posts/react-repl-agent/
"""
import inspect
import json
import openai
# Functions ########################################################
class StopException(Exception):
"""
Stop Execution (Task is Finished)
"""
...
def finish(answer):
"""Answer the user's question, and finish the conversation."""
raise StopException(answer)
def get_current_location():
"""Get the current location of the user"""
# Mocked for the sake of the example
return json.dumps(
{
"latitude": 50.9326,
"longitude": 5.34260,
}
)
def get_current_weather(latitude, longitude):
"""Get the current weather in a given location"""
# Mocked for the sake of the example
weather_info = {
"location": (latitude, longitude),
"temperature": "72",
"unit": "fahrenheit",
"forecast": "sunny",
}
return json.dumps(weather_info)
def calculate(formula):
"""Calculate the result of a given formula"""
return str(eval(formula))
name_to_function_map = {
get_current_location.__name__: get_current_location,
get_current_weather.__name__: get_current_weather,
calculate.__name__: calculate,
finish.__name__: finish,
}
# The parameters the functions accepts, described as a JSON Schema object.
functions = [
{
"name": get_current_location.__name__,
"description": inspect.getdoc(get_current_location).strip(),
"parameters": {
"type": "object",
"properties": {},
"required": [],
},
},
{
"name": get_current_weather.__name__,
"description": inspect.getdoc(get_current_weather).strip(),
"parameters": {
"type": "object",
"properties": {
"latitude": {"type": "number"},
"longitude": {"type": "number"},
},
"required": ["latitude", "longitude"],
},
},
{
"name": calculate.__name__,
"description": inspect.getdoc(calculate).strip(),
"parameters": {
"type": "object",
"properties": {
"formula": {
"type": "string",
"description": "Formula to compute the result of, in Python syntax.",
},
},
"required": ["formula"],
},
},
{
"name": finish.__name__,
"description": inspect.getdoc(finish).strip(),
"parameters": {
"type": "object",
"properties": {
"answer": {
"type": "string",
"description": "Text to answer the user with.",
},
},
"required": ["answer"],
},
},
]
# Function Calling in loop #########################################
# Initial "chat" messages
messages = [
{
"role": "system",
"content": "You are a helpful assistant that can answer multi-step questions by sequentially calling functions. Follow a pattern of THOUGHT (reason step-by-step about which function to call next), ACTION (call a function to get closer to the answer), OBSERVATION (output of the function).",
},
{
"role": "user",
"content": "What's the current weather for my location? Give me the temperature in degrees Celsius.\n\nReason step by step which actions to take to get to the answer.",
},
]
# Run in loop
max_iterations = 20
for i in range(max_iterations):
print(f"\n\n# Iteration {i}:")
# Call OpenAI's API to get the next message
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=messages,
functions=functions,
function_call="auto",
)
response_message = response["choices"][0]["message"]
# Extend conversation with assistant's reply
messages.append(response_message.to_dict())
# Check if GPT wanted to call a function
if response_message.get("function_call"):
function_call_message = response_message["function_call"]
print(f"function_call_message={json.dumps(function_call_message, indent=2)}")
# Call the function
function_name = function_call_message["name"]
function_to_call = name_to_function_map[function_name]
function_args = function_call_message["arguments"]
try:
function_args_dict = json.loads(function_args)
except json.JSONDecodeError as exc:
# JSON decoding failed
print(f"Error decoding function arguments: {exc}")
messages.append(
{
"role": "function",
"name": function_name,
"content": f"Error decoding function arguments {function_args!r}! Error: {exc}",
}
)
continue
try:
function_response = function_to_call(**function_args_dict)
except StopException as exc:
# Agent wants to stop the conversation (Expected)
print(f"Stopping conversation: {exc}")
break
except Exception as exc:
# Unexpected error calling function
print(f"Error calling function {function_name}: {exc}")
messages.append(
{
"role": "function",
"name": function_name,
"content": f"Error calling function {function_name}: {exc}!",
}
)
continue
print(f"{function_response=}")
# Extend conversation with function response
messages.append(
{
"role": "function",
"name": function_name,
"content": function_response,
}
)
print(f"messages: {json.dumps(messages, indent=2)}")
# [
# {
# "role": "system",
# "content": "You are a helpful assistant that can answer multi-step questions by sequentially calling functions. Follow a pattern of THOUGHT (reason step-by-step about which function to call next), ACTION (call a function to get closer to the answer), OBSERVATION (output of the function)."
# },
# {
# "role": "user",
# "content": "What's the current weather for my location? Give me the temperature in degrees Celsius.\n\nReason step by step which actions to take to get to the answer."
# },
# {
# "role": "assistant",
# "content": "To get the current weather for the user's location, we need to follow these steps:\n\n1. Get the user's current location.\n2. Retrieve the latitude and longitude coordinates from the user's location.\n3. Use the coordinates to get the current weather.\n4. Extract the temperature from the weather data.\n5. Convert the temperature to degrees Celsius.\n\nNow, let's take these steps one by one and call the appropriate functions to get the answer.",
# "function_call": {
# "name": "get_current_location",
# "arguments": "{}"
# }
# },
# {
# "role": "function",
# "name": "get_current_location",
# "content": "{\"latitude\": 50.9326, \"longitude\": 5.3426}"
# },
# {
# "role": "assistant",
# "content": null,
# "function_call": {
# "name": "get_current_weather",
# "arguments": "{\n \"latitude\": 50.9326,\n \"longitude\": 5.3426\n}"
# }
# },
# {
# "role": "function",
# "name": "get_current_weather",
# "content": "{\"location\": [50.9326, 5.3426], \"temperature\": \"72\", \"unit\": \"fahrenheit\", \"forecast\": \"sunny\"}"
# },
# {
# "role": "assistant",
# "content": "The current weather for your location is sunny with a temperature of 72 degrees Fahrenheit. \n\nTo convert the temperature to Celsius, we can use the formula: \n\nCelsius = (Fahrenheit - 32) * 5/9\n\nLet's calculate it.",
# "function_call": {
# "name": "calculate",
# "arguments": "{\n \"formula\": \"(72 - 32) * 5/9\"\n}"
# }
# },
# {
# "role": "function",
# "name": "calculate",
# "content": "22.22222222222222"
# },
# {
# "role": "assistant",
# "content": "The current temperature in your location is approximately 22.22 degrees Celsius."
# },
# {
# "role": "assistant",
# "content": null,
# "function_call": {
# "name": "finish",
# "arguments": "{\n \"answer\": \"The current weather for your location is sunny with a temperature of 22.22 degrees Celsius.\"\n}"
# }
# }
# ]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment