GPT 操作库:SQL 数据库

简介

这是为开发者提供的指南,旨在让 ChatGPT 能够使用 GPT Action 查询 SQL 数据库。在阅读本指南之前,请先熟悉以下内容:

本指南概述了通过中间件应用程序将 ChatGPT 连接到 SQL 数据库所需的工作流程。在本示例中,我们将使用 PostgreSQL 数据库,但对于所有 SQL 数据库(MySQL、MS SQL Server、Amazon Aurora、Google Cloud 上的 SQL Server 等)来说,该过程应该类似。本文档概述了创建能够执行以下操作的 GPT Action 所需的步骤:

  • 对 SQL 数据库执行读取查询
  • 通过文本响应返回记录
  • 通过 CSV 文件返回记录

价值 + 示例业务用例

价值:用户现在可以利用 ChatGPT 的自然语言能力来回答有关 SQL 数据库中数据的问题:

  • 业务用户无需编写 SQL 或向分析师提交请求,即可访问 SQL 数据库中包含的信息
  • 数据分析师可以通过提取数据并使用 ChatGPT 进行分析,执行超出 SQL 查询能力的复杂分析

示例用例

  • 业务用户需要回答有关其销售渠道的问题
  • 数据分析师需要对大型数据集执行回归分析

应用程序设计注意事项

鉴于大多数托管的 SQL 数据库不提供用于提交查询的 REST API,您需要一个中间件应用程序来执行以下功能:

  1. 通过 REST API 请求接受数据库查询
  2. 将查询转发到集成的 SQL 数据库
  3. 将数据库响应转换为 CSV 文件
  4. 将 CSV 文件返回给请求者

在设计第一个功能时,有两种主要方法:

  1. 中间件支持一种方法来接收 GPT 生成的任意 SQL 查询,并将其转发到数据库。此方法的优点包括:
    1. 开发简易性
    2. 灵活性(无需预测用户将进行的查询类型)
    3. 低维护(无需更新 API 模式以响应数据库更改)
  2. 中间件支持与特定允许查询相对应的多种方法。此方法的优点包括:
    1. 更多控制
    2. 减少 GPT 在生成 SQL 时出错的可能性

本指南将重点介绍选项 1。对于有兴趣了解选项 2 的人,可以考虑实施 PostgRESTHasura 等服务来简化该过程。

应用程序架构图,描绘了用户、GPT、中间件和数据库之间的交互 应用程序架构图

中间件注意事项

开发人员可以构建自定义中间件(通常作为具有 AWS、GCP 或 MS Azure 等 CSP 的无服务器函数进行部署),也可以使用第三方解决方案(如 Mulesoft AnypointRetool Workflows)。使用第三方中间件可以加速您的开发过程,但灵活性不如自己构建。

构建自己的中间件可以让你更好地控制应用程序的行为。有关自定义中间件的示例,请参阅我们的 Azure Functions 食谱

本指南将重点介绍中间件与 GPT 和 SQL 数据库的接口,而不是中间件设置的具体细节。

工作流程步骤

1) GPT 生成 SQL 查询

GPT 非常擅长根据用户的自然语言提示编写 SQL 查询。您可以通过以下方式之一向 GPT 提供数据库模式来提高其查询生成能力:

  1. 指示 GPT 首先查询数据库以检索模式(此方法在我们 BigQuery 食谱 中有更详细的演示)。
  2. 在 GPT 指令中提供模式(最适合小型、静态模式)

以下是包含简单数据库模式信息的示例 GPT 指令:

# Context
You are a data analyst. Your job is to assist users with their business questions by analyzing the data contained in a PostgreSQL database.

## Database Schema

### Accounts Table
**Description:** Stores information about business accounts.

| Column Name  | Data Type      | Constraints                        | Description                             |

| Column Name  | Data Type      | Constraints                        | Description                             |
|--------------|----------------|------------------------------------|-----------------------------------------|
| account_id   | INT            | PRIMARY KEY, AUTO_INCREMENT, NOT NULL | Unique identifier for each account      |
| account_name | VARCHAR(255)   | NOT NULL                           | Name of the business account            |
| industry     | VARCHAR(255)   |                                    | Industry to which the business belongs  |
| created_at   | TIMESTAMP      | NOT NULL, DEFAULT CURRENT_TIMESTAMP | Timestamp when the account was created  |

### Users Table
**Description:** Stores information about users associated with the accounts.

| Column Name  | Data Type      | Constraints                        | Description                             |

| Column Name  | Data Type      | Constraints                        | Description                             |
|--------------|----------------|------------------------------------|-----------------------------------------|
| user_id      | INT            | PRIMARY KEY, AUTO_INCREMENT, NOT NULL | Unique identifier for each user         |
| account_id   | INT            | NOT NULL, FOREIGN KEY (References Accounts(account_id)) | Foreign key referencing Accounts(account_id) |
| username     | VARCHAR(50)    | NOT NULL, UNIQUE                   | Username chosen by the user             |
| email        | VARCHAR(100)   | NOT NULL, UNIQUE                   | User's email address                    |
| role         | VARCHAR(50)    |                                    | Role of the user within the account     |
| created_at   | TIMESTAMP      | NOT NULL, DEFAULT CURRENT_TIMESTAMP | Timestamp when the user was created     |

### Revenue Table
**Description:** Stores revenue data related to the accounts.

| Column Name  | Data Type      | Constraints                        | Description                             |

| Column Name  | Data Type      | Constraints                        | Description                             |
|--------------|----------------|------------------------------------|-----------------------------------------|
| revenue_id   | INT            | PRIMARY KEY, AUTO_INCREMENT, NOT NULL | Unique identifier for each revenue record |
| account_id   | INT            | NOT NULL, FOREIGN KEY (References Accounts(account_id)) | Foreign key referencing Accounts(account_id) |
| amount       | DECIMAL(10, 2) | NOT NULL                           | Revenue amount                          |
| revenue_date | DATE           | NOT NULL                           | Date when the revenue was recorded      |

# Instructions:

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.
2. Write a PostgreSQL-compatible query and submit it using the `databaseQuery` API method.
3. Use the response data to answer the user's question.
4. If necessary, use code interpreter to perform additional analysis on the data until you are able to answer the user's question.

2) GPT 将 SQL 查询发送到中间件

为了让我们的 GPT 与我们的中间件进行通信,我们将配置一个 GPT Action。中间件需要提供一个接受 SQL 查询字符串的 REST API 端点。您可以通过几种方式设计此接口。以下是一个简单的端点 OpenAPI 模式示例,该模式在 POST 操作中接受“q”参数:

openapi: 3.1.0
info:
  title: PostgreSQL API
  description: API for querying a PostgreSQL database
  version: 1.0.0
servers:

  - url: https://my.middleware.com/v1
    description: middleware service
paths:
  /api/query:
    post:
      operationId: databaseQuery
      summary: Query a PostgreSQL database
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                q:
                  type: string
                  example: select * from users
      responses:
        "200":
          description: database records
          content:
            application/json:
              schema:
                type: object
                properties:
                  openaiFileResponse:
                    type: array
                    items:
                      type: object
                      properties:
                        name:
                          type: string
                          description: The name of the file.
                        mime_type:
                          type: string
                          description: The MIME type of the file.
                        content:
                          type: string
                          format: byte
                          description: The content of the file in base64 encoding.
        "400":
          description: Bad Request. Invalid input.
        "401":
          description: Unauthorized. Invalid or missing API key.
      security:

        - ApiKey: []
components:
  securitySchemes:
    ApiKey:
      type: apiKey
      in: header
      name: X-Api-Key
  schemas: {}

关于身份验证的说明: 上面示例中的 API 接口接受一个系统级 API 密钥,该密钥与 GPT 的配置一起存储,并用于验证所有 GPT 用户的请求。GPT Actions 还支持 OAuth 身份验证,它支持用户级别的身份验证和授权。了解有关 GPT Action 身份验证选项的更多信息

由于用户是通过 OAuth 流程与中间件进行身份验证,而不是直接与底层数据库进行身份验证,因此强制执行用户级别的访问(表或行级别权限)需要更多的努力。但是,对于用户对底层数据库具有不同访问级别的 GPT 来说,这可能是必需的。

为了强制执行用户级别的权限,您的中间件应:

  1. 接收 IdP 在 OAuth 流程期间提供的用户元数据并提取其标识信息
  2. 查询数据库以检索用户的数据库权限
  3. 向数据库发出命令,在会话剩余时间内强制执行相关权限

为了保持良好的用户体验,您需要动态检索每个用户可用的数据库模式,而不是直接将模式数据包含在 GPT 指令中。这确保了 GPT 只能访问它可以代表当前用户查询的表。

3) 中间件将 SQL 查询转发到数据库

您的中间件将实现数据库驱动程序或客户端库,使其能够直接查询 PostgreSQL 数据库。如果您使用的是第三方中间件,中间件供应商应提供 SQL 数据库的原生连接器。如果您正在构建自己的中间件,您可能需要实现数据库供应商或第三方提供的客户端库。例如,以下是 PostgreSQL 社区维护的客户端库列表:https://wiki.postgresql.org/wiki/List_of_drivers

在此工作流程步骤中,中间件应用程序需要从 GPT 收到的请求中提取 SQL 字符串,并使用客户端库提供的方法将其转发到数据库。

关于只读权限的说明: 鉴于此设计模式会导致数据库处理任意 AI 生成的 SQL 查询,您应确保中间件应用程序对数据库具有只读权限。这可以确保 AI 生成的查询无法插入新数据或修改现有数据。如果您的用例需要写入访问权限,请考虑部署特定于操作的端点,而不是接受任意 SQL。

4) 数据库将记录返回给中间件

根据您实现的客户端库,您的中间件可能会以各种格式接收记录。一种常见的模式是您的中间件接收一个 JSON 对象数组,每个对象代表一个与查询匹配的数据库记录:

[
  {
    "account_id": 1,
    "number_of_users": 10,
    "total_revenue": 43803.96,
    "revenue_per_user": 4380.40
  },
  {
    "account_id": 2,
    "number_of_users": 12,
    "total_revenue": 77814.84,
    "revenue_per_user": 6484.57
  },
  ...
]

5) 中间件将记录转换为 base64 编码的 CSV 文件

为了让 ChatGPT 能够分析大量记录,它需要访问 CSV 格式的数据。GPT Actions 接口允许 GPT 接收 base64 编码的文件,大小最多为 10mb。

您的中间件需要执行两个操作:

将记录转换为 CSV 格式

许多编程语言都包含用于处理 CSV 文件的本机库(例如,Python 的 csv 库)。

以下是您的中间件如何将 JSON 对象数组转换为 CSV 文件的示例:

import json
import csv

# Sample JSON array of objects
json_data = '''
[
    {"account_id": 1, "number_of_users": 10, "total_revenue": 43803.96, "revenue_per_user": 4380.40}, 
    {"account_id": 2, "number_of_users": 12, "total_revenue": 77814.84, "revenue_per_user": 6484.57}
]
'''

# Load JSON data
data = json.loads(json_data)

# Define the CSV file name
csv_file = 'output.csv'

# Write JSON data to CSV
with open(csv_file, 'w', newline='') as csvfile:
    # Create a CSV writer object
    csvwriter = csv.writer(csvfile)

    # Write the header (keys of the first dictionary)
    header = data[0].keys()
    csvwriter.writerow(header)

    # Write the data rows
    for row in data:
        csvwriter.writerow(row.values())

print(f"JSON data has been written to {csv_file}")

Base64 编码 CSV 文件

许多编程语言都包含用于处理 base64 编码的本机库(例如,Python 的 base64 库)。

以下是您的中间件如何对上一步生成的 CSV 文件进行 base64 编码的示例:

import base64 

# Base64 encode the CSV file
encoded_string = base64.b64encode(open('output.csv', 'rb').read()).decode('utf-8')

print("Base64 Encoded CSV:")
print(encoded_string)

6) 中间件将 base64 编码的 CSV 文件返回给 GPT

为了让 GPT Actions 接口能够处理 base-64 编码的 CSV 文件,您的中间件返回的响应必须包含 openaiFileResponse 参数。提供的值必须是文件对象或文件链接的数组(有关更多详细信息,请参阅 Actions 文档)。在本示例中,我们将使用文件对象数组。

以下是有效响应体的外观示例:

{
  "openaiFileResponse": [
    {
      "name": "output.csv",
      "mime_type": "text/csv",
      "content": "ImFjY291bn...NC41NyI="
    }
  ]
}

7) GPT 处理返回的文件

一旦您的 GPT 收到 base64 编码的 CSV 文件,它将自动解码该文件并处理它以回答用户的问题。这可能涉及使用 代码解释器对 CSV 文件执行其他分析,这与用户通过提示上传 CSV 文件的方式相同。

注意: 如果您想对返回的文件执行其他分析,则必须在 GPT 中启用“代码解释器和数据分析”功能。

结论

GPT Actions 提供了一个灵活的框架,用于从 SQL 数据库等外部源检索数据。让 ChatGPT 能够查询数据库可以极大地扩展其作为知识助手和分析师的能力。

您希望我们优先集成哪些集成?我们的集成是否存在错误?在我们的 GitHub 上提交 PR 或 issue,我们将进行查看。