This tutorial builds an agent with function calling where the tool exposed to the model never executes arbitrary SQL. It combines two mechanisms already verified in the Aurabase code: the NL2SQL validator and a read-only Postgres transaction, two bricks of thenative AI integrated into thebackend. Prerequisites: an Aurabase project, its service_rolekey, and an account with one of the three native LLM providers (OpenAI, Anthropic, Gemini).
要点
- 真正的风险不是函数调用本身,而是暴露给模型的工具:原始的
execute_sql(query)赋予它完整的 SQL 访问权限。 - 安全架构公开了一个
query_database(question)工具,该工具委托给语法树验证器(仅限 SELECT、有界 LIMIT、隔离模式)而不是直接执行。 - Aurabase 本机公开此验证器 (
/nl2sql):将其重新用作工具的实现,避免了必须自己重新编码 SQL 验证。 - 然后,提交的 SQL 通过
readOnly: true模式下的aura.db.sql()执行,这是一个实际的只读 Postgres 事务,而不是简单的文本过滤器。 - Aurabase 的本机
/chat端点尚不接受tool角色,也不接受tools参数(在代码中验证):代理循环当前通过 LLM 提供商的 SDK 运行,而不是通过 Aurabase 代理运行。 service_role密钥在设计上绕过了 RLS:它绝不能离开您的后端,并且代理继承比典型的经过身份验证的用户更广泛的访问权限。
你将构建什么
您将构建一个代理来回答有关 Postgres 项目中数据的自然语言问题,而无需让模型编写按原样执行的 SQL。该模型调用一个名为 query_database的工具,该工具将问题转换为通过 NL2SQL 验证的 SQL,然后执行此只读 SQL 并将行返回给模型,以便模型制定答案。
本教程在服务器端使用 @aurabase/aurabase-js JavaScript SDK(切勿在浏览器端,service_role 密钥不得暴露给客户端)和用于代理循环的 OpenAI 函数调用 API。同样的原理也适用于 Anthropic 或 Gemini SDK。
为什么“运行此 SQL”工具很危险
大多数 Postgres 代理教程(包括一些官方指南)都定义了一个工具:一个 execute_sql 函数,它将 SQL 字符串作为参数并按原样执行。模型根据用户的问题和上下文中给出的模式自行编写此字符串。
这种选择将一项无法可靠履行的责任转移给模型。问题中的提示注入可能会产生破坏性的 SQL,该工具会不加区别地执行该 SQL,因为它不知道“合法”查询应该是什么样子。我们的专门文章详细介绍了此攻击向量:保护 NL2SQL 免受 SQL 注入。
本教程中构建的替代方案公开了一个更窄的工具 query_database(question)。该模型不能再直接编写 SQL:它只能在自己的工具调用中提出问题。 Aurabase NL2SQL 引擎 将此问题转换为 SQL,然后将其传递给语法树验证器(仅 SELECT,无子查询,授权的 10 个函数,有限的 LIMIT)。
无论您的系统提示有多好,execute_sql(query: string) 工具都可以为模型提供完整的 SQL 访问权限。指令(“仅执行 SELECT”)仍然是模型可以遵循、误解或被用户问题中的注入所规避的指令。
定义暴露给模型的工具的架构
Aurabase 的三个本地 LLM 提供商(OpenAI、Anthropic、Gemini)接受 JSON Schema 格式的工具定义表。对于这个代理来说,一个工具就足够了: query_database,它只接受自然语言的问题。该模型既看不到 SQL 模式,也看不到它可以自行填充的 query 字段。
实现工具:NL2SQL 然后只读
工具处理程序在您的后端运行,而不是在浏览器中运行。它带有项目密钥 service_role,该密钥在设计上绕过了 RLS,因此永远不应该暴露给客户端。它对 Aurabase SDK 进行两次调用。
第一个调用将问题转换为通过 aura.ai.nl2sql()验证的 SQL:仅限 SELECT,LIMIT 受限,无法访问系统目录。第二个执行已经通过 aura.db.sql()和 readOnly: true 选项验证的 SQL:然后 Postgres 本身拒绝此事务中的任何写入,独立于 NL2SQL 上游已经应用的文本验证。
readOnly: true 触发真正的只读 Postgres 事务:引擎拒绝写入,它不是应用于请求文本的过滤器。与 NL2SQL 的仅 SELECT 验证相结合,代理具有两个独立的层:如果一个层有缺陷,另一层仍然有效。
代理循环:调用供应商SDK端的函数
Aurabase 公开了三个本机 LLM 提供程序,但其 /chat 端点尚未中继 tools 参数或 tool角色。 ChatOptions 仅携带 temperature、 max_tokens 和 model,接受的角色仅限于 system、 user 和 assistant (在 llm/mod.rs 和 handlers/chat.rs中验证)。因此,函数调用循环现在直接通过提供商的 SDK 运行,而不是通过 Aurabase 代理。
只要 Aurabase 本身不编排工具调用,您的后端就必须使用 OpenAI、Anthropic 或 Gemini SDK 管理循环本身。 NL2SQL 和 SQL 执行仍然是该循环内的经典 Aurabase 调用。
如果您使用 LangChain 或 Azure AI Agent 之类的服务编排代理,则原则保持不变:框架中声明的工具必须保持相同 query_database,而不是原始 SQL 执行器。我们的比较详细介绍了 LangChain 和 LlamaIndex 在 Postgres 上提供真正价值的地方,以及它们特别增加复杂性的地方: 使用 LangChain 或 LlamaIndex的 Postgres 代理。
用真题进行测试
发送给代理商的问题:“本月有多少优质客户下了订单?” ”。模板按原样调用 query_database 来解决这个问题,没有看到或编写任何 SQL。这是该工具触发的两个内部调用的结果。
模型的最终答案是基于这些实际的线条,而不是猜测。如果该工具返回零行,则与在没有经过验证的数据的情况下响应的模型相比,出现数字幻觉的可能性要小得多。
在投入生产之前确保代理安全
service_role键永远不会离开您的后端:既不会出现在发送到模型的提示中,也不会出现在日志中,也不会出现在客户端环境变量中。- 即使您的项目需要在应用程序的其他地方编写,
readOnly: true在此特定工具的aura.db.sql()上仍然保持活动状态。 service_role在设计上绕过了 RLS。如果代理应根据提出问题的用户做出不同的响应,请在 SQL 中显式过滤或回退到遵循 RLS 的经典 PostgREST 端点。请参阅 多租户 RLS 隔离。- 记录每个工具调用(提出的问题、SQL 验证、行数):如果问题产生意外结果,这是唯一可用的跟踪。
- Aurabase 的速率限制和每月配额已适用于
/nl2sql上的每个项目:健谈的代理无法悄悄超出您的 AI 预算。
需要注意的当前限制
query_database 工具继承了 NL2SQL 验证器的所有限制:没有子查询、没有 CTE/WITH、没有 UNION 以及十个 SQL 函数的封闭列表。自然需要子查询(“从未订购过的客户”)的问题必须重新表述或由第二个专用工具处理,而不是强制进入 NL2SQL。
目前,Aurabase /chat 代理内部不存在工具调用编排:此处描述的代理循环存在于您的应用程序代码中,而不是托管服务中。如果代理必须链接多个工具(例如数据库和文档 RAG),则您的后端将协调这两个调用。
RAG 和函数调用相结合
本教程涵盖有关关系数据的结构化问题。对于有关非结构化内容(文档、票据、注释)的问题,同一代理可以公开连接到 Aurabase 的本机 RAG(pgvector、HNSW 搜索)的第二个工具。 Native AI on Postgres页面详细介绍了这两种功能及其关联。