"This notebook covers how to use the Chat Completions API in combination with external functions to extend the capabilities of GPT models.\n",
"\n",
"## How to use functions\n",
"\n",
"`functions` is an optional parameter in the ChatCompletion API which can be used to provide function specifications. The purpose of this is to enable models to generate outputs which adhere to function input schemas. Note that the API will not actually execute any function calls. It is up to developers to execute function calls using model outputs.\n",
"\n",
"If the `functions` parameter is provided then by default the model will decide when it is appropriate to use one of the functions. The API can also be forced to use a specific function by setting the `function_call` parameter to `{\"name\": \"<insert-function-name>\"}`. If a function is used, the output will contain `\"finish_reason\": \"function_message\"` in the response, as well as a `function_call` object that has the name of the function and the generated function arguments.\n",
"\n",
"Functions are specified with the following fields:\n",
"\n",
"- **Name:** The name of the function.\n",
"- **Description:** A description of what the function does. The model will use this to decide when to call the function.\n",
"- **Parameters:** The parameters object contains all of the input fields the function requires. These inputs can be of the following types: String, Number, Boolean, Object, Null, AnyOf. Refer to the [API reference docs](https://platform.openai.com/docs/api-reference/chat) for details.\n",
"- **Required:** Which of the parameters are required to make a query. The rest will be treated as optional.\n",
"\n",
"You can chain function calls by executing the function and passing the output of the function execution directly back to the assistant. This can lead to _infinite loop_ behaviour where the model continues calling functions indefinitely, however guardrails can be put in place to prevent this.\n",
"\n",
"## Walkthrough\n",
"\n",
"This cookbook takes you through the following workflow:\n",
"\n",
"- **Basic concepts:** Creating an example function and getting the API to use it if appropriate.\n",
"- **Integrating API calls with function execution:** Creating an agent that uses API calls to generate function arguments and then executes the function.\n",
"- **Using multiple functions:** Allowing multiple functions to be called in sequence before responding to the user.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "80e71f33",
"metadata": {
"pycharm": {
"is_executing": true
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Requirement already satisfied: scipy in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (1.10.1)\n",
"Requirement already satisfied: numpy<1.27.0,>=1.19.5 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from scipy) (1.24.3)\n",
"Requirement already satisfied: tenacity in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (8.2.2)\n",
"Requirement already satisfied: tiktoken in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (0.4.0)\n",
"Requirement already satisfied: regex>=2022.1.18 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from tiktoken) (2023.6.3)\n",
"Requirement already satisfied: requests>=2.26.0 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from tiktoken) (2.31.0)\n",
"Requirement already satisfied: charset-normalizer<4,>=2 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from requests>=2.26.0->tiktoken) (3.1.0)\n",
"Requirement already satisfied: idna<4,>=2.5 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from requests>=2.26.0->tiktoken) (3.4)\n",
"Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from requests>=2.26.0->tiktoken) (2.0.2)\n",
"Requirement already satisfied: certifi>=2017.4.17 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from requests>=2.26.0->tiktoken) (2023.5.7)\n",
"Requirement already satisfied: termcolor in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (2.3.0)\n",
"Requirement already satisfied: openai in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (0.27.7)\n",
"Requirement already satisfied: requests>=2.20 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from openai) (2.31.0)\n",
"Requirement already satisfied: tqdm in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from openai) (4.65.0)\n",
"Requirement already satisfied: aiohttp in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from openai) (3.8.4)\n",
"Requirement already satisfied: charset-normalizer<4,>=2 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from requests>=2.20->openai) (3.1.0)\n",
"Requirement already satisfied: idna<4,>=2.5 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from requests>=2.20->openai) (3.4)\n",
"Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from requests>=2.20->openai) (2.0.2)\n",
"Requirement already satisfied: certifi>=2017.4.17 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from requests>=2.20->openai) (2023.5.7)\n",
"Requirement already satisfied: attrs>=17.3.0 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from aiohttp->openai) (23.1.0)\n",
"Requirement already satisfied: multidict<7.0,>=4.5 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from aiohttp->openai) (6.0.4)\n",
"Requirement already satisfied: async-timeout<5.0,>=4.0.0a3 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from aiohttp->openai) (4.0.2)\n",
"Requirement already satisfied: yarl<2.0,>=1.0 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from aiohttp->openai) (1.9.2)\n",
"Requirement already satisfied: frozenlist>=1.1.1 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from aiohttp->openai) (1.3.3)\n",
"Requirement already satisfied: aiosignal>=1.1.2 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from aiohttp->openai) (1.3.1)\n",
"Requirement already satisfied: requests in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (2.31.0)\n",
"Requirement already satisfied: charset-normalizer<4,>=2 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from requests) (3.1.0)\n",
"Requirement already satisfied: idna<4,>=2.5 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from requests) (3.4)\n",
"Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from requests) (2.0.2)\n",
"Requirement already satisfied: certifi>=2017.4.17 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from requests) (2023.5.7)\n",
"Requirement already satisfied: arxiv in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (1.4.7)\n",
"Requirement already satisfied: feedparser in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from arxiv) (6.0.10)\n",
"Requirement already satisfied: sgmllib3k in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from feedparser->arxiv) (1.0.0)\n",
"Requirement already satisfied: pandas in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (2.0.2)\n",
"Requirement already satisfied: python-dateutil>=2.8.2 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from pandas) (2.8.2)\n",
"Requirement already satisfied: pytz>=2020.1 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from pandas) (2023.3)\n",
"Requirement already satisfied: tzdata>=2022.1 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from pandas) (2023.3)\n",
"Requirement already satisfied: numpy>=1.20.3 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from pandas) (1.24.3)\n",
"Requirement already satisfied: six>=1.5 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from python-dateutil>=2.8.2->pandas) (1.16.0)\n",
"Requirement already satisfied: PyPDF2 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (3.0.1)\n",
"Requirement already satisfied: typing_extensions>=3.10.0.0 in /Users/joe/.virtualenvs/openai-cookbook-internal/lib/python3.9/site-packages (from PyPDF2) (4.6.3)\n"
"Next we'll create a specification for a function called ```get_current_weather```. Later we'll pass this function specification to the API in order to generate function arguments that adhere to the specification."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "d2e25069",
"metadata": {},
"outputs": [],
"source": [
"functions = [\n",
" {\n",
" \"name\": \"get_current_weather\",\n",
" \"description\": \"Get the current weather\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"location\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"The city and state, e.g. San Francisco, CA\",\n",
" },\n",
" \"format\": {\n",
" \"type\": \"string\",\n",
" \"enum\": [\"celsius\", \"fahrenheit\"],\n",
" \"description\": \"The temperature unit to use. Infer this from the users location.\",\n",
" },\n",
" },\n",
" \"required\": [\"location\", \"format\"],\n",
" },\n",
" }\n",
"]\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "131d9a33",
"metadata": {},
"outputs": [],
"source": [
"conversation = Conversation()\n",
"conversation.add_message(\"user\", \"what is the weather like today\")\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "12ed2515",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'role': 'assistant', 'content': 'Where are you currently located?'}"
"## Integrating API calls with function execution\n",
"\n",
"In our next example, we'll demonstrate how to execute functions whose inputs are model-generated, and use this to implement an agent that can answer questions for us about a database. For simplicity we'll use the [Chinook sample database](https://www.sqlitetutorial.net/sqlite-sample-database/).\n",
"\n",
"*Note:* SQL generation use cases are high-risk in a production environment - models can be unreliable when generating consistent SQL syntax. A more reliable way to solve this problem may be to build a query generation API that takes the desired columns as input from the model."
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "f7654fef",
"metadata": {},
"source": [
"### Pull SQL Database Info\n",
"\n",
"First let's define some helpful utility functions to extract data from a SQLite database."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "30f6b60e",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Opened database successfully\n"
]
}
],
"source": [
"import sqlite3\n",
"\n",
"conn = sqlite3.connect(\"data/Chinook.db\")\n",
"print(\"Opened database successfully\")\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "abec0214",
"metadata": {},
"outputs": [],
"source": [
"def get_table_names(conn):\n",
" \"\"\"Return a list of table names.\"\"\"\n",
" table_names = []\n",
" tables = conn.execute(\"SELECT name FROM sqlite_master WHERE type='table';\")\n",
"As before, we'll define a function specification for the function we'd like the API to generate arguments for. Notice that we are inserting the database schema into the function specification. This will be important for the model to know about."
" SQL query extracting info to answer the user's question.\n",
" SQL should be written using this database schema:\n",
" {database_schema_string}\n",
" The query should be returned in plain text, not in JSON.\n",
" \"\"\",\n",
" }\n",
" },\n",
" \"required\": [\"query\"],\n",
" },\n",
" }\n",
"]\n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "da08c121",
"metadata": {},
"source": [
"### SQL execution\n",
"\n",
"Now let's implement the function that the agent will use to query the database. We also need to implement utilities to integrate the calls to the Chat Completions API with the function it is calling."
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "65585e74",
"metadata": {},
"outputs": [],
"source": [
"def ask_database(conn, query):\n",
" \"\"\"Function to query SQLite database with provided SQL query.\"\"\"\n",
"Prepped query is {'query': 'SELECT Artist.Name, COUNT(Track.TrackId) AS NumberOfTracks FROM Artist JOIN Album ON Artist.ArtistId = Album.ArtistId JOIN Track ON Album.AlbumId = Track.AlbumId GROUP BY Artist.Name ORDER BY NumberOfTracks DESC LIMIT 5;'}\n",
"The top 5 artists by number of tracks in the Chinook database are:\n",
"Prepped query is {'query': 'SELECT Album.Title, COUNT(Track.TrackId) AS number_of_tracks FROM Album JOIN Track ON Album.AlbumId = Track.AlbumId GROUP BY Album.AlbumId ORDER BY number_of_tracks DESC LIMIT 1'}\n"
]
},
{
"data": {
"text/plain": [
"\"The album with the most tracks in the Chinook database is 'Greatest Hits' with a total of 57 tracks.\""
"Now let's construct a scenario in which we provide a model with more than one function to call. We'll create an agent that uses data from arXiv to answer questions about academic subjects. It has two new functions at its disposal:\n",
"- **get_articles**: A function that gets arXiv articles on a subject and summarizes them for the user with links.\n",
"- **read_article_and_summarize**: This function takes one of the previously searched articles, reads it in its entirety and summarizes the core argument, evidence and conclusions.\n",
"This will get you comfortable with a multi-function workflow that can choose from multiple services, and where some of the data from the first function is persisted to be used by the second."
"We'll first set up some utilities that will underpin our two functions.\n",
"\n",
"Downloaded papers will be stored in a directory (we use ```./data/papers``` here). We create a file ```arxiv_library.csv``` to store the embeddings and details for downloaded papers to retrieve against using ```summarize_text```."
"{'title': 'Proximal Policy Optimization and its Dynamic Version for Sequence Generation',\n",
" 'summary': 'In sequence generation task, many works use policy gradient for model\\noptimization to tackle the intractable backpropagation issue when maximizing\\nthe non-differentiable evaluation metrics or fooling the discriminator in\\nadversarial learning. In this paper, we replace policy gradient with proximal\\npolicy optimization (PPO), which is a proved more efficient reinforcement\\nlearning algorithm, and propose a dynamic approach for PPO (PPO-dynamic). We\\ndemonstrate the efficacy of PPO and PPO-dynamic on conditional sequence\\ngeneration tasks including synthetic experiment and chit-chat chatbot. The\\nresults show that PPO and PPO-dynamic can beat policy gradient by stability and\\nperformance.',\n",
" - The authors argue that Proximal Policy Optimization (PPO) and its dynamic version (PPO-dynamic) can effectively replace policy gradient for model optimization in sequence generation tasks.\n",
" - They propose a modification to the constraints of PPO to make it more dynamic and flexible, which further improves the training.\n",
" - The authors also argue that the fixed hyperparameter in PPO, which aims to bound the KL-divergence, is not consistent with the actual KL-divergence that depends on the old policy. They propose dynamic parameters that adjust the bound for better constraints.\n",
"\n",
"- Evidence:\n",
" - The paper demonstrates the efficacy of PPO and PPO-dynamic on conditional sequence generation tasks, including synthetic experiments and chit-chat chatbots.\n",
" - The authors tested their methods on a synthetic counting task and a chit-chat chatbot task, showing that both PPO and PPO-dynamic can stabilize training and generate more diverse outputs.\n",
" - The authors provide the pseudo code for PPO and PPO-dynamic, which is similar to the original PPO algorithm.\n",
" - They also analyze the distribution of the first output on a counting task, finding that using the PPO method generates a more scattered distribution.\n",
" - The authors use REINFORCE and PPO-dynamic algorithms to generate responses in a chatbot context, demonstrating the differences in their outputs.\n",
"\n",
"- Conclusions:\n",
" - The results show that PPO and PPO-dynamic outperform policy gradient in terms of stability and performance.\n",
" - PPO-dynamic also sped up the convergence.\n",
" - The authors conclude that PPO is a better method for sequence learning and that GAN-based sequence learning can use PPO for improved performance.\n",
" - They also conclude that a shorter input length should correspond to a higher variance in the context of a chatbot, and vice versa.\n"
"We'll now create 2 function specifications for functions that provide access to the arXiv data. We'll also create some more utilities to integrate Chat Completions API calls with function execution."
]
},
{
"cell_type": "code",
"execution_count": 28,
"id": "a391cabe",
"metadata": {},
"outputs": [],
"source": [
"# Initiate our get_articles and read_article_and_summarize functions\n",
"arxiv_functions = [\n",
" {\n",
" \"name\": \"get_articles\",\n",
" \"description\": \"\"\"Use this function to get academic papers from arXiv to answer user questions.\"\"\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"query\": {\n",
" \"type\": \"string\",\n",
" \"description\": f\"\"\"\n",
" User query in JSON. Responses should be summarized and should include the article URL reference\n",
" \"\"\",\n",
" }\n",
" },\n",
" \"required\": [\"query\"],\n",
" },\n",
" \"name\": \"read_article_and_summarize\",\n",
" \"description\": \"\"\"Use this function to read whole papers and provide a summary for users.\n",
" You should NEVER call this function before get_articles has been called in the conversation.\"\"\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"query\": {\n",
" \"type\": \"string\",\n",
" \"description\": f\"\"\"\n",
" Description of the article in plain text based on the user's query\n",
"Proximal Policy Optimization (PPO) is a type of reinforcement learning algorithm that balances the benefits of other policy optimization methods: it can have a pace comparable to Stochastic Gradient Descent, is less complex to implement, has fewer hyperparameters to tune, and does not require a second-order optimization. \n",
"\n",
"In reinforcement learning, an agent learns to perform actions in an environment to maximize some notion of cumulative reward. PPO, designed by OpenAI, introduces a novel objective function that takes the best of both worlds: like Trust Region Policy Optimization (TRPO), it uses a trust region to ensure stable updates, but like Clipped Policy Gradient, it avoids the complexity associated with constraining the learning process within a certain region.\n",
"\n",
"If you would like a more detailed explanation or academic resources on PPO, I can look up some papers for you."
],
"text/plain": [
"<IPython.core.display.Markdown object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Add a user message\n",
"paper_conversation.add_message(\"user\", \"Hi, how does PPO reinforcement learning work?\")\n",
"Sure, here are summaries for some papers I found that focus on Proximal Policy Optimization (PPO) for sequence generation:\n",
"\n",
"1. Title: [Proximal Policy Optimization and its Dynamic Version for Sequence Generation](http://arxiv.org/abs/1808.07982v1)\n",
" The paper presents a method of replacing policy gradient with Proximal Policy Optimization (PPO) for sequence generation tasks. The authors introduce a dynamic approach to PPO (PPO-dynamic) and demonstrate its efficacy in conditional sequence generation tasks including synthetic experiments and chit-chat chatbots. The results show that both PPO and PPO-dynamic outperform policy gradient in terms of stability and performance.\n",
"\n",
"2. Title: [Neural PPO-Clip Attains Global Optimality: A Hinge Loss Perspective](http://arxiv.org/abs/2110.13799v4)\n",
" This paper provides a global convergence rate for the PPO-Clip algorithm under neural function approximation. The authors reinterpret PPO-Clip from the perspective of hinge loss, connecting policy improvement with solving a large-margin classification problem. The paper also proposes a two-step policy improvement scheme that helps with the convergence analysis.\n",
"\n",
"3. Title: [A2C is a special case of PPO](http://arxiv.org/abs/2205.09123v1)\n",
" The paper reveals an intriguing connection between Advantage Actor-Critic (A2C) and PPO algorithms. The authors argue that A2C can be viewed as a special case of PPO and provide theoretical justifications and pseudocode analysis to support their claim.\n",
"\n",
"4. Title: [Continuous-action Reinforcement Learning for Playing Racing Games: Comparing SPG to PPO](http://arxiv.org/abs/2001.05270v1)\n",
" This paper compares Sampled Policy Gradient (SPG) and Proximal Policy Optimization (PPO) in the context of a racing game environment. While the focus is not strictly on sequence generation, this might still provide some interesting insights on PPO's performance in continuous-action settings.\n",
"\n",
"Please confirm if you want more detailed summaries or if you would like to read them directly."
],
"text/plain": [
"<IPython.core.display.Markdown object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Add another user message to induce our system to use the second tool\n",
"paper_conversation.add_message(\n",
" \"user\",\n",
" \"Can you read the PPO sequence generation paper for me and give me a summary\",\n",