mirror of
https://github.com/openai/openai-cookbook
synced 2024-11-11 13:11:02 +00:00
Adding docs for GPT Action - SQL Database (#1339)
Co-authored-by: Evan Weiss <evan.weiss@oai-evan.weiss> Co-authored-by: Aaron Wilkowitz <157151487+aaronwilkowitz-openai@users.noreply.github.com>
This commit is contained in:
parent
e8d6e6566d
commit
4ccce8ca18
@ -137,3 +137,8 @@ eszuhany-openai:
|
||||
name: "Eric Szuhany"
|
||||
website: "https://www.linkedin.com/in/szuhany/"
|
||||
avatar: "https://avatars.githubusercontent.com/u/164391912"
|
||||
|
||||
evanweiss-openai:
|
||||
name: "Evan Weiss"
|
||||
website: "https://www.linkedin.com/in/evanpweiss/"
|
||||
avatar: "https://avatars.githubusercontent.com/u/150647345"
|
@ -0,0 +1,434 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# GPT Action Library: SQL Database\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## Introduction\n",
|
||||
"\n",
|
||||
"This is a guide for developers seeking to give ChatGPT the ability to query a SQL database using a GPT Action. Before reading this guide, please familiarize yourself with the following content:\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"* [Introduction to GPT Actions](https://platform.openai.com/docs/actions)\n",
|
||||
"* [Introduction to GPT Actions Library](https://platform.openai.com/docs/actions/actions-library)\n",
|
||||
"* [Example of Building a GPT Action from Scratch](https://platform.openai.com/docs/actions/getting-started)\n",
|
||||
"\n",
|
||||
"This guide outlines the workflow required to connect ChatGPT to a SQL Database via a middleware application. We’ll use a PostgreSQL database for this example, but the process should be similar for all SQL databases (MySQL, MS SQL Server, Amazon Aurora, SQL Server on Google Cloud, etc.). This documentation outlines the steps required to create GPT Action which can:\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"* Execute read queries against a SQL Database\n",
|
||||
"* Return records via a text response\n",
|
||||
"* Return records via a CSV file\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"### Value + Example Business Use Cases\n",
|
||||
"\n",
|
||||
"**Value**: Users can now leverage ChatGPT's natural language capability to answer questions about data in a SQL database:\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"* Business users can access information contained in a SQL database without writing SQL or submitting a request to an analyst\n",
|
||||
"* Data analysts can perform complex analysis beyond what is possible with a SQL query by extracting data and analyzing it with ChatGPT\n",
|
||||
"\n",
|
||||
"**Example Use Cases**:\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"* A business user needs to answer questions about their sales funnel\n",
|
||||
"* A data analyst needs to perform a regression analysis on a large dataset\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## Application Design Considerations\n",
|
||||
"\n",
|
||||
"Given that most managed SQL databases do not provide REST APIs for submitting queries, you will need a middleware application to perform the following functions:\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"1. Accept database queries via REST API requests\n",
|
||||
"2. Forward queries to the integrated SQL database\n",
|
||||
"3. Convert database responses in to CSV files\n",
|
||||
"4. Return CSV files to the requestor\n",
|
||||
"\n",
|
||||
"There are two main approaches to designing the first function:\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"1. The middleware supports a single method for receiving arbitrary SQL queries generated by the GPT and forwards them to the database. The benefits of this approach include:\n",
|
||||
" 1. Ease of development\n",
|
||||
" 2. Flexibility (doesn’t require you to anticipate the types of queries users will make)\n",
|
||||
" 3. Low maintenance (doesn’t require you to update the API schema in response to database changes)\n",
|
||||
"2. The middleware supports a number of methods corresponding to specific allowed queries. The benefits of this approach include:\n",
|
||||
" 4. More control\n",
|
||||
" 5. Less opportunity for model error when generating SQL\n",
|
||||
"\n",
|
||||
"This guide will focus on option 1. For those interested in option 2, consider implementing a service like [PostgREST](https://github.com/PostgREST/postgrest) or [Hasura](https://hasura.io/) to streamline the process.\n",
|
||||
"\n",
|
||||
"<img src=\"../../../images/gptactions_sql_database_middleware.png\" alt=\"An application architecture diagram depicting the interaction between the user, GPT, middleware, and database\" width=\"75%\" />\n",
|
||||
"\n",
|
||||
"_Application architecture diagram_\n",
|
||||
"\n",
|
||||
"## Middleware Considerations\n",
|
||||
"\n",
|
||||
"Developers can either build custom middleware (commonly deployed as serverless functions with CSPs like AWS, GCP, or MS Azure) or use third-party solutions (like [Mulesoft Anypoint](https://www.mulesoft.com/platform/enterprise-integration) or [Retool Workflows](https://retool.com/products/workflows)). Using third-party middleware can accelerate your development process, but is less flexible than building it yourself.\n",
|
||||
"\n",
|
||||
"Building your own middleware gives you more control over the application’s behavior. For an example of custom middleware, see our [Azure Functions cookbook](https://cookbook.openai.com/examples/chatgpt/gpt_actions_library/gpt_middleware_azure_function).\n",
|
||||
"\n",
|
||||
"Rather than focusing on the specifics of middleware setup, this guide will focus on the middleware’s interface with the GPT and SQL database.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## Workflow Steps\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"### 1) GPT generates a SQL query\n",
|
||||
"\n",
|
||||
"GPTs are very good at writing SQL queries based on a user’s natural language prompt. You can improve the GPT’s query generation capabilities by giving it access to the database schema in one of the following ways:\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"1. Instruct the GPT to start by querying the database to retrieve the schema (this approach is demonstrated in more detail in our [BigQuery cookbook](https://cookbook.openai.com/examples/chatgpt/gpt_actions_library/gpt_action_bigquery#custom-gpt-instructions)).\n",
|
||||
"2. Provide the schema in the GPT instructions (works best for small, static schemata)\n",
|
||||
"\n",
|
||||
"Here are sample GPT instructions which include information about a simple database schema:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "plaintext"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Context\n",
|
||||
"You are a data analyst. Your job is to assist users with their business questions by analyzing the data contained in a PostgreSQL database.\n",
|
||||
"\n",
|
||||
"## Database Schema\n",
|
||||
"\n",
|
||||
"### Accounts Table\n",
|
||||
"**Description:** Stores information about business accounts.\n",
|
||||
"\n",
|
||||
"| Column Name | Data Type | Constraints | Description |\n",
|
||||
"|--------------|----------------|------------------------------------|-----------------------------------------|\n",
|
||||
"| account_id | INT | PRIMARY KEY, AUTO_INCREMENT, NOT NULL | Unique identifier for each account |\n",
|
||||
"| account_name | VARCHAR(255) | NOT NULL | Name of the business account |\n",
|
||||
"| industry | VARCHAR(255) | | Industry to which the business belongs |\n",
|
||||
"| created_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | Timestamp when the account was created |\n",
|
||||
"\n",
|
||||
"### Users Table\n",
|
||||
"**Description:** Stores information about users associated with the accounts.\n",
|
||||
"\n",
|
||||
"| Column Name | Data Type | Constraints | Description |\n",
|
||||
"|--------------|----------------|------------------------------------|-----------------------------------------|\n",
|
||||
"| user_id | INT | PRIMARY KEY, AUTO_INCREMENT, NOT NULL | Unique identifier for each user |\n",
|
||||
"| account_id | INT | NOT NULL, FOREIGN KEY (References Accounts(account_id)) | Foreign key referencing Accounts(account_id) |\n",
|
||||
"| username | VARCHAR(50) | NOT NULL, UNIQUE | Username chosen by the user |\n",
|
||||
"| email | VARCHAR(100) | NOT NULL, UNIQUE | User's email address |\n",
|
||||
"| role | VARCHAR(50) | | Role of the user within the account |\n",
|
||||
"| created_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | Timestamp when the user was created |\n",
|
||||
"\n",
|
||||
"### Revenue Table\n",
|
||||
"**Description:** Stores revenue data related to the accounts.\n",
|
||||
"\n",
|
||||
"| Column Name | Data Type | Constraints | Description |\n",
|
||||
"|--------------|----------------|------------------------------------|-----------------------------------------|\n",
|
||||
"| revenue_id | INT | PRIMARY KEY, AUTO_INCREMENT, NOT NULL | Unique identifier for each revenue record |\n",
|
||||
"| account_id | INT | NOT NULL, FOREIGN KEY (References Accounts(account_id)) | Foreign key referencing Accounts(account_id) |\n",
|
||||
"| amount | DECIMAL(10, 2) | NOT NULL | Revenue amount |\n",
|
||||
"| revenue_date | DATE | NOT NULL | Date when the revenue was recorded |\n",
|
||||
"\n",
|
||||
"# Instructions:\n",
|
||||
"1. When the user asks a question, consider what data you would need to answer the question and confirm that the data should be available by consulting the database schema.\n",
|
||||
"2. Write a PostgreSQL-compatible query and submit it using the `databaseQuery` API method.\n",
|
||||
"3. Use the response data to answer the user's question.\n",
|
||||
"4. If necessary, use code interpreter to perform additional analysis on the data until you are able to answer the user's question."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### 2) GPT sends SQL query to middleware\n",
|
||||
"\n",
|
||||
"In order for our GPT to communicate with our middleware, we’ll configure a GPT Action. The middleware needs to present a REST API endpoint which accepts a SQL query string. You can design this interface in several ways. Here is an example of an OpenAPI schema for a simple endpoint which accepts a “q” parameter in a POST operation:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "yaml"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"openapi: 3.1.0\n",
|
||||
"info:\n",
|
||||
" title: PostgreSQL API\n",
|
||||
" description: API for querying a PostgreSQL database\n",
|
||||
" version: 1.0.0\n",
|
||||
"servers:\n",
|
||||
" - url: https://my.middleware.com/v1\n",
|
||||
" description: middleware service\n",
|
||||
"paths:\n",
|
||||
" /api/query:\n",
|
||||
" post:\n",
|
||||
" operationId: databaseQuery\n",
|
||||
" summary: Query a PostgreSQL database\n",
|
||||
" requestBody:\n",
|
||||
" required: true\n",
|
||||
" content:\n",
|
||||
" application/json:\n",
|
||||
" schema:\n",
|
||||
" type: object\n",
|
||||
" properties:\n",
|
||||
" q:\n",
|
||||
" type: string\n",
|
||||
" example: select * from users\n",
|
||||
" responses:\n",
|
||||
" \"200\":\n",
|
||||
" description: database records\n",
|
||||
" content:\n",
|
||||
" application/json:\n",
|
||||
" schema:\n",
|
||||
" type: object\n",
|
||||
" properties:\n",
|
||||
" openaiFileResponse:\n",
|
||||
" type: array\n",
|
||||
" items:\n",
|
||||
" type: object\n",
|
||||
" properties:\n",
|
||||
" name:\n",
|
||||
" type: string\n",
|
||||
" description: The name of the file.\n",
|
||||
" mime_type:\n",
|
||||
" type: string\n",
|
||||
" description: The MIME type of the file.\n",
|
||||
" content:\n",
|
||||
" type: string\n",
|
||||
" format: byte\n",
|
||||
" description: The content of the file in base64 encoding.\n",
|
||||
" \"400\":\n",
|
||||
" description: Bad Request. Invalid input.\n",
|
||||
" \"401\":\n",
|
||||
" description: Unauthorized. Invalid or missing API key.\n",
|
||||
" security:\n",
|
||||
" - ApiKey: []\n",
|
||||
"components:\n",
|
||||
" securitySchemes:\n",
|
||||
" ApiKey:\n",
|
||||
" type: apiKey\n",
|
||||
" in: header\n",
|
||||
" name: X-Api-Key\n",
|
||||
" schemas: {}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**A note on authentication:** The API interface in the above example accepts a single system-level API key which is stored along with the GPT’s configuration and used to authenticate requests for all GPT users. GPT Actions also support OAuth authentication, which enables user-level authentication and authorization. [Learn more about GPT Action authentication options](https://platform.openai.com/docs/actions/authentication).\n",
|
||||
"\n",
|
||||
"Because the user is authenticating with middleware and not directly with the underlying database, enforcing user-level access (table or row-level permissions) requires more effort. However, it may be required for GPTs where users have different levels of access to the underlying database.\n",
|
||||
"\n",
|
||||
" In order to enforce user-level permissions, your middleware should:\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"1. Receive the user’s metadata provided by the IdP during the OAuth flow and extract their identifying information\n",
|
||||
"2. Query the database to retrieve the user’s database permissions\n",
|
||||
"3. Issue a command to the database to enforce the relevant permissions for the remainder of the session\n",
|
||||
"\n",
|
||||
"In order to maintain a good user experience, you’ll want to dynamically retrieve the available database schema for each user as opposed to including the schema data in the GPT instructions directly. This ensures that the GPT only has access to tables which it can query on behalf of the current user.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"### 3) Middleware forwards SQL query to database \n",
|
||||
"\n",
|
||||
"Your middleware will implement a database driver or client library to enable it to query the PostgreSQL database directly. If you are using third-party middleware, the middleware vendor should provide native connectors for SQL databases. If you are building your own middleware, you may need to implement a client library provided by the database vendor or a third-party. For example, here is a list of community-maintained client libraries for PostgreSQL: [https://wiki.postgresql.org/wiki/List_of_drivers](https://wiki.postgresql.org/wiki/List_of_drivers)\n",
|
||||
"\n",
|
||||
"During this workflow step, the middleware application needs to extract the SQL string from the request it received from the GPT and forward it to the database using the methods provided by the client library.\n",
|
||||
"\n",
|
||||
"**A note on read-only permissions:** Given that this design pattern results in your database processing arbitrary AI-generated SQL queries, you should ensure that the middleware application has read-only permissions on the database. This ensures that the AI-generated queries cannot insert new data or modify existing data. If write access is required for your use-case, consider deploying operation-specific endpoints rather than accepting arbitrary SQL.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"### 4) Database returns records to middleware\n",
|
||||
"\n",
|
||||
"Depending on the client library you have implemented, your middleware may receive records in a variety of formats. One common pattern is for your middleware to receive an array of JSON objects, each object representing a database record matching the query:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "json"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"[\n",
|
||||
" {\n",
|
||||
" \"account_id\": 1,\n",
|
||||
" \"number_of_users\": 10,\n",
|
||||
" \"total_revenue\": 43803.96,\n",
|
||||
" \"revenue_per_user\": 4380.40\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"account_id\": 2,\n",
|
||||
" \"number_of_users\": 12,\n",
|
||||
" \"total_revenue\": 77814.84,\n",
|
||||
" \"revenue_per_user\": 6484.57\n",
|
||||
" },\n",
|
||||
" ...\n",
|
||||
"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### 5) Middleware converts records into base64-encoded CSV file\n",
|
||||
"\n",
|
||||
"In order for ChatGPT to analyze large numbers of records, it needs access to data in a CSV format. The GPT Actions interface allows GPTs to [receive base64-encoded files](https://platform.openai.com/docs/actions/sending-files/returning-files) of up to 10mb in size.\n",
|
||||
"\n",
|
||||
"Your middleware needs to perform two actions:\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"#### Convert records into a CSV format\n",
|
||||
"\n",
|
||||
"Many programming languages include a native library for working with CSV files (the Python [csv](https://docs.python.org/3/library/csv.html) library, for example).\n",
|
||||
"\n",
|
||||
"Here’s an example of how your middleware could convert an array of JSON objects into a CSV file:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"import csv\n",
|
||||
"\n",
|
||||
"# Sample JSON array of objects\n",
|
||||
"json_data = '''\n",
|
||||
"[\n",
|
||||
" {\"account_id\": 1, \"number_of_users\": 10, \"total_revenue\": 43803.96, \"revenue_per_user\": 4380.40}, \n",
|
||||
" {\"account_id\": 2, \"number_of_users\": 12, \"total_revenue\": 77814.84, \"revenue_per_user\": 6484.57}\n",
|
||||
"]\n",
|
||||
"'''\n",
|
||||
"\n",
|
||||
"# Load JSON data\n",
|
||||
"data = json.loads(json_data)\n",
|
||||
"\n",
|
||||
"# Define the CSV file name\n",
|
||||
"csv_file = 'output.csv'\n",
|
||||
"\n",
|
||||
"# Write JSON data to CSV\n",
|
||||
"with open(csv_file, 'w', newline='') as csvfile:\n",
|
||||
" # Create a CSV writer object\n",
|
||||
" csvwriter = csv.writer(csvfile)\n",
|
||||
" \n",
|
||||
" # Write the header (keys of the first dictionary)\n",
|
||||
" header = data[0].keys()\n",
|
||||
" csvwriter.writerow(header)\n",
|
||||
" \n",
|
||||
" # Write the data rows\n",
|
||||
" for row in data:\n",
|
||||
" csvwriter.writerow(row.values())\n",
|
||||
"\n",
|
||||
"print(f\"JSON data has been written to {csv_file}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Base64-encode the CSV file\n",
|
||||
"\n",
|
||||
"Many programming languages include a native library for working with base64 encodings (the Python [base64](https://docs.python.org/3/library/base64.html) library, for example).\n",
|
||||
"\n",
|
||||
"Here’s an example of how your middleware could base64-encode the CSV file generated in the previous step:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import base64 \n",
|
||||
"\n",
|
||||
"# Base64 encode the CSV file\n",
|
||||
"encoded_string = base64.b64encode(open('output.csv', 'rb').read()).decode('utf-8')\n",
|
||||
"\n",
|
||||
"print(\"Base64 Encoded CSV:\")\n",
|
||||
"print(encoded_string)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### 6) Middleware returns base64-encoded CSV file to GPT\n",
|
||||
"\n",
|
||||
"In order for the GPT Actions interface to process the base-64 encoded CSV file, the response returned by your middleware must contain an `openaiFileResponse` parameter. The value provided must be an array of file objects or links to files (see the [Actions documentation](https://platform.openai.com/docs/actions/sending-files/returning-files) for more details). For the purposes of this example, we will work with an array of file objects.\n",
|
||||
"\n",
|
||||
"Here is an example of what a valid response body looks like:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "json"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"{\n",
|
||||
" \"openaiFileResponse\": [\n",
|
||||
" {\n",
|
||||
" \"name\": \"output.csv\",\n",
|
||||
" \"mime_type\": \"text/csv\",\n",
|
||||
" \"content\": \"ImFjY291bn...NC41NyI=\"\n",
|
||||
" }\n",
|
||||
" ]\n",
|
||||
"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### 7) GPT processes returned file\n",
|
||||
"\n",
|
||||
"Once your GPT receives the base64-encoded CSV file, it will automatically decode the file and process it to answer the user’s question. This may involve using [code interpreter to perform additional analysis](https://help.openai.com/en/articles/9213685-extracting-insights-with-chatgpt-data-analysis) against the CSV file, which happens the same way as if a user had uploaded the CSV file via the prompt.\n",
|
||||
"\n",
|
||||
"**Note:** You must enable the _Code Interpreter & Data Analysis_ capability in your GPT if you want to be able to perform additional analysis on the returned file.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## Conclusion\n",
|
||||
"\n",
|
||||
"GPT Actions provide a flexible framework for retrieving data from external sources like SQL databases. Giving ChatGPT the ability to query a database can substantially expand its capabilities as a knowledge assistant and analyst.\n",
|
||||
"\n",
|
||||
"_Are there integrations that you’d like us to prioritize? Are there errors in our integrations? File a PR or issue in our github, and we’ll take a look._"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
BIN
images/gptactions_sql_database_middleware.png
Normal file
BIN
images/gptactions_sql_database_middleware.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 183 KiB |
@ -1454,3 +1454,12 @@
|
||||
tags:
|
||||
- gpt-actions-library
|
||||
- chatgpt
|
||||
|
||||
- title: GPT Actions library - SQL Database
|
||||
path: examples/chatgpt/gpt_actions_library/gpt_action_sql_database.ipynb
|
||||
date: 2024-07-31
|
||||
authors:
|
||||
- evanweiss-openai
|
||||
tags:
|
||||
- gpt-actions-library
|
||||
- chatgpt
|
Loading…
Reference in New Issue
Block a user