使用自然语言查询数据库

本页介绍了 AlloyDB Omni 提供的预览版功能,可让您尝试使用自然语言查询数据库。

概览

您可以使用 AlloyDB Omni 预览一组实验性功能,这些功能可让数据库驱动的应用更安全地执行来自应用用户的自然语言查询,例如“我的包裹在哪里?”或“每个部门的收入最高的是谁?”AlloyDB Omni 会将自然语言输入转换为特定于您数据库的 SQL 查询,并将结果限制为仅显示应用用户有权查看的内容。

自然语言查询的强大功能和风险

借助大语言模型(例如 Gemini Pro),您的应用可以根据应用最终用户创建的自然语言查询运行数据库查询。例如,具有对应用数据库架构访问权限的模型可以接受最终用户输入,如下所示:

What are the cheapest direct flights from Boston to Denver in July?

并将其转换为如下所示的 SQL 查询:

SELECT flight.id, flight.price, carrier.name, [...]
FROM [...]
WHERE [...]
ORDER BY flight.price ASC 
LIMIT 10

自然语言查询可以为您的应用提供强大的工具来为用户提供服务。不过,在允许最终用户对数据库表运行任意查询之前,您必须考虑此技术带来的明显安全风险。即使您已将应用配置为以有限访问权限的只读数据库用户身份连接到数据库,但允许自然语言查询的应用也可能容易受到以下攻击:

  • 恶意用户可以提交提示注入攻击,试图操纵底层模型以揭示应用有权访问的所有数据。
  • 模型本身可能会生成范围过大的 SQL 查询,即使在响应用户的善意查询时,也会泄露敏感数据。

使用参数化安全视图对查询进行排错

为了帮助降低上一部分中所述的风险,Google 开发了参数化安全视图,这是一种实验性功能,您可以使用本页中介绍的技术预览该功能。

借助参数化安全视图,您可以明确定义自然语言查询可以从中提取数据的表和列,并对单个应用用户可用的行范围添加其他限制。借助这些限制,您可以严格控制应用用户可以通过自然语言查询看到的数据,无论用户如何表述这些查询。

如果您启用此预览版,则可以使用 Google 开发的实验性扩展程序 alloydb_ai_nlparameterized_views

parameterized_views 扩展程序提供以下功能:

  • 参数化安全视图,是 SQL 视图的一种变体,用于限制查询可以访问的数据范围。
  • execute_parameterized_views() 函数,可让您查询参数化安全视图。

alloydb_ai_nl 扩展程序提供以下功能:

  • google_get_sql_current_schema() 函数,用于将自然语言查询转换为当前架构中表和视图的 SQL 查询。

以下部分介绍了如何使用这些功能,并演示了它们如何协同工作。

准备工作

安装 AlloyDB Omni 15.5.1 或更高版本,包括 AI 模型集成。如需了解详情,请参阅安装 AlloyDB Omni with AlloyDB for PostgreSQL AI

为参数化安全视图设置数据库

  1. 使用 psql 连接到 AlloyDB Omni 集群。

  2. 修改 /var/alloydb/config/postgresql.conf 的内容,使 shared_preload_libraries 指令的值包含 alloydb_ai_nlparameterized_views。修改后的指令应类似于以下内容:

    shared_preload_libraries='g_stats,google_job_scheduler,google_insights,pg_stat_statements,google_db_advisor,google_columnar_engine,alloydb_ai_nl,parameterized_views'
    
  3. 停止 AlloyDB Omni

  4. 启动 AlloyDB Omni

  5. 启用 alloydb_ai_nlparameterized_views 扩展程序:

    CREATE EXTENSION google_ml_integration;
    ALTER SYSTEM SET google_ml_integration.enable_model_support=on;
    ALTER SYSTEM SET alloydb_ai_nl.enabled=on;
    ALTER SYSTEM SET parameterized_views.enabled=on;
    SELECT pg_reload_conf();
    CREATE EXTENSION alloydb_ai_nl CASCADE;
    CREATE EXTENSION parameterized_views;
    
  6. 使用模型端点管理功能,根据 Gemini Pro API 注册新的语言模型:

    CALL google_ml.create_model(
        model_id => 'MODEL_ID',
        model_request_url => 'https://us-central1-aiplatform.googleapis.com/v1/projects/PROJECT_ID/locations/us-central1/publishers/google/models/gemini-pro:streamGenerateContent',
        model_provider => 'google',
        model_auth_type => 'alloydb_service_agent_iam');
    

    替换以下内容:

  7. 创建新的数据库用户。请勿向其授予任何权限或角色。此过程的后续步骤会向用户授予所需的权限。

参数化安全视图

参数化安全视图的运作方式与普通 PostgreSQL 安全视图非常相似:本质上就是一个存储的 SELECT 语句。此外,使用参数化安全视图时,您还可以要求在查询视图时向其传递一个或多个命名参数值,这有点类似于使用普通数据库查询绑定变量。

例如,假设您运行的应用的数据库会跟踪向客户发货的情况。用户在查询 Where is my package? 中使用 ID 为 12345 的类型登录了此应用。使用参数化安全视图,您可以确保以下要求适用于 AlloyDB for PostgreSQL 执行此查询的方式:

  • 该查询只能读取您在数据库的参数化安全视图中明确列出的数据库列。在本例中,这些列可能是 itemsusersshipments 表中的某些列。
  • 该查询只能读取与发出查询的用户关联的数据库行。在这种情况下,这可能需要返回的行与 users 表行(其 id 列值为 12345)之间存在数据关系。

创建带参数的安全视图

如需创建带有参数的安全视图,请使用 PostgreSQL CREATE VIEW DDL 命令,并附带以下属性:

  • 使用 security_barrier 选项创建视图。
  • 如需限制应用用户仅查看他们有权查看的行,请在 WHERE 子句中使用 $@PARAMETER_NAME 语法添加所需参数。一个常见用例是使用 WHERE COLUMN = $@PARAMETER_NAME 检查列的值。

以下示例中的参数化安全视图允许访问名为 users 的表中的三列,并将结果限制为仅包含 users.id 与所需参数匹配的行:

CREATE VIEW user_psv WITH (security_barrier) AS 
SELECT 
  username,
  full_name,
  birthday
FROM 
  users
WHERE 
  users.id = $@user_id; 

参数化安全视图核心的 SELECT 语句可以与普通 PostgreSQL 视图允许的语句一样复杂。

创建视图后,您必须向之前创建的用户授予对该视图运行 SELECT 查询的权限:

GRANT SELECT ON VIEW_NAME TO NL_DB_USER;

替换以下内容:

  • VIEW_NAME:您在上一步中创建的视图的名称。
  • NL_DB_USER:您指定执行自然语言查询的数据库用户的名称。

查询参数化安全视图

虽然它们与普通 PostgreSQL 视图相似,但您无法直接查询参数化安全视图。而是使用 parameterized_views 扩展提供的 execute_parameterized_query() 函数。该函数的语法如下所示:

SELECT * FROM
parameterized_views.execute_parameterized_query(
    query => SQL_QUERY
    param_names => ARRAY [PARAMETER_NAMES],
    param_values => ARRAY [PARAMETER_VALUES]
)

替换以下内容:

  • SQL_QUERY:一个 SQL 查询,其 FROM 子句引用一个或多个参数化安全视图。
  • PARAMETER_NAMES:要传入的参数名称列表(以字符串形式)。
  • PARAMETER_VALUES:要传入的参数值列表。此列表的大小必须与 param_names 列表相同。值的顺序与名称的顺序一致。

该函数会返回一个 JSON 对象表。表中的每一行都等同于原始查询结果行的 row_to_json() 值。

在典型用例中,query 参数的值不是由您自己的代码生成的,而是由您与 AlloyDB for PostgreSQL 数据库集成的 AI 模型生成的。

以下示例展示了如何在 Python 中查询参数化安全视图,然后显示其结果。它基于上一部分中的 user_psv 示例视图构建:

# Assume a get_user_id() function that returns the ID of the current
# application user.
app_user_id = get_user_id()

pool = await asyncpg.create_pool(
    host=INSTANCE_IP
    user=NL_DB_USER
    password=NL_DB_PASSWORD
    database=DB_NAME
)

table_name = "user_psv"

query = f"""
    SELECT 
      full_name,
      birthday
    FROM 
      {table_name} 
"""
params = {
    "user_id": app_user_id
}

param_query = f"""
SELECT * FROM parameterized_views.execute_parameterized_query(
  query => '{query}',
  param_names => $1,
  param_values => $2
);
"""

sql_results = await pool.execute(
    param_query, 
    params.keys(), 
    params.values()
)

for row in sql_results:
    print(json.loads(row))

执行自然语言查询

使用参数化安全视图执行自然语言查询是一个两步流程:

  1. 作为只能SELECT访问适当的参数化安全视图的数据库用户,使用大语言模型将自然语言查询转换为 SQL。
  2. 使用 execute_parameterized_query() 函数处理 SQL,将其绑定到与当前用户会话相适应的参数值。

下面几个部分将详细介绍这些步骤。

将自然语言转换为 SQL

如需将自然语言输入转换为 SQL,请使用参数化安全视图技术预览版附带的 google_get_sql_current_schema() 函数:

SELECT alloydb_ai_nl.google_get_sql_current_schema(
  sql_text => 'NL_TEXT',
  model_id => 'MODEL_ID',
  prompt_text => 'HINT_TEXT'
);

替换以下内容:

  • NL_TEXT:要转换为 SQL 查询的自然语言文本。
  • MODEL_ID:您在为参数化安全视图设置数据库时向模型目录注册的模型的 ID。
  • HINT_TEXT:有关数据库架构的其他信息,以自然语言表示。这样,您就可以向模型提供有关架构重要方面(仅通过分析表、列和关系结构可能无法提取)的其他提示。例如:When joining flights and seats, be sure to join on flights.id = seats.flight_id.

该函数的输出是一个包含 SQL 查询的字符串。

使用参数运行转换后的 SQL

将自然语言查询转换为 SQL 后,您可以按照本页上文所述调用 execute_parameterized_views(),并传入参数化安全视图可能需要的任何参数。

如果您在给定查询中向该函数传递的参数数量多于其所需的参数数量,该函数就会正常运行,因此您可以使用应用具有值的所有参数化安全视图使用的所有参数调用该函数。如果该函数尝试运行需要未定义参数的查询,则会抛出异常。

执行自然语言查询的示例

本部分演示了从自然语言输入到 SQL 结果集的完整流程。代码示例展示了应用运行的底层 SQL 查询和函数。

对于此示例流程,假设您的应用具有以下特点:

  • 您的数据库驱动型应用会跟踪向客户配送的商品。
  • 您已在模型目录中注册了名为 my-gemini-model 的基于 Gemini Pro 的模型。
  • 您已在名为 shipment_view 的数据库中定义了一个参数化安全视图。
    • 该视图会从多个与向客户发货相关的表中选择数据。
    • 该视图需要 user_id 参数,其值为应用的最终用户的 ID。
  1. 应用用户 ID 为 12345 的最终用户在您的 Web 应用中输入“我的文件包在哪里?”
  2. 您的应用调用 google_get_sql_current_schema() 将输入转换为 SQL:

    SELECT alloydb_ai_nl.google_get_sql_current_schema(
      sql_text => 'Where is my package?'
      model_id => 'my-gemini-model'
    );
    

    此调用会返回一个包含单个 SQL SELECT 查询的字符串。该查询仅限于您创建的用于处理参数化安全视图的数据库用户可见的参数化安全视图。

    通过 Where is my package? 生成的 SQL 可能类似于以下内容:

    SELECT current_location, ship_date, ship_eta FROM shipment_view;
    

    由于 shipment_view 是一个参数化安全视图,而不是普通的 PostgreSQL 视图,因此您的应用必须使用 execute_parameterized_views() 安全地运行包含所需 user_id 参数的查询,如下一步所示。

  3. 您的应用将 SQL 以及限制输出的参数传递给 execute_parameterized_views()。在我们的示例中,这是提供输入的应用最终用户的 ID:

    SELECT * FROM
    parameterized_views.execute_parameterized_views(
        query => 'SELECT current_location, ship_date, ship_eta FROM shipment_view',
        param_names => ['user_id'],
        param_values => ['12345']
    );
    
    

    输出是 SQL 结果集,表示为 JSON 数据。

  4. 您的应用会根据需要处理 JSON 数据。

适用于自然语言处理的数据库设计

此技术预览版提供的 google_get_sql_current_schema() 函数主要用于演示参数化安全视图的功能,让您有机会尽早试用这项正在开发的技术。与任何预览版一样,您不应将此函数应用于生产环境中的应用。

因此,在对 google_get_sql_current_schema() 进行实验时,您可以应用本部分中的建议来提高 google_get_sql_current_schema() 输出的质量。

设计易于人类理解的架构

一般来说,请为数据库结构提供清晰的名称和注释,以便普通开发者推断出其表、列和关系的用途。这样清晰的说明有助于大型语言模型根据您的架构生成更准确的 SQL 查询。

使用描述性名称

请为表、列和关系使用描述性名称。避免使用缩写词或首字母缩写词。例如,与名为 u 的表相比,该模型在名为 users 的表中运行效果更好。

如果无法重命名现有数据结构,请在调用 google_get_sql_current_schema() 时使用 prompt_text 参数向模型提供提示。

使用特定数据类型

如果您为列使用更具体的数据类型,模型可以更好地推断您的数据。例如,如果您将某个列专用于存储真或假值,请使用 boolean 数据类型(与 truefalse 搭配使用)而非 integer(与 10 搭配使用)。

启用预览后,请谨慎回滚

如果您已在数据库上启用参数化安全视图技术预览版,但随后决定将 AlloyDB Omni 回滚到 15.5.0 之前的版本,则必须先执行一些手动清理步骤,然后才能降级。

如果您不执行这些步骤,则任何尝试查询、修改或删除参数化安全视图的操作都会导致 SQL 错误。这包括对数据库视图目录的查询,如果不进行此更改,则其结果中会包含参数化安全视图,例如 SELECT * FROM pg_views

如需在 AlloyDB Omni 回滚之前从数据库中彻底移除此技术预览版,请按以下步骤操作:

  1. psql 中,使用 DROP VIEW 命令删除数据库中的每个参数化安全视图。

  2. psql 中,使用 DROP EXTENSION 命令停用数据库上的 alloydb_ai_nlparameterized_views 扩展。

  3. postgresql.conf 文件中,从 shared_preload_libraries 指令中移除对 alloydb_ai_nlparameterized_views 的引用。

如需详细了解如何回滚 AlloyDB Omni 安装,请参阅回滚升级