PRODСуверенная европейская платформа BaaSОткрыть панель управления →

Родной ИИ · 10 минута чтения

Безопасный агент Postgres с вызовом функций (учебник)

Affane Daylami · Fondateur · 21 марта 2026 г.

Вернуться в блог

Агент, отправляющий запрос Postgres, перед первой строкой кода задает специальный контрольный вопрос: какую функцию вы предоставляете модели? Если инструмент, который может вызвать LLM, напрямую выполняет написанный им сам SQL, неоднозначного вопроса или подсказки достаточно, чтобы прочитать любую таблицу в проекте.

Этот текст на английском языке был создан автоматически на основе французского оригинала и еще не проверялся.
Эта страница была переведена автоматически. Английская версия является авторитетной.

В этом руководстве создается агент с вызовом функций, при котором инструмент, доступный к модели, никогда не выполняет произвольный SQL. Он сочетает в себе два механизма, уже проверенных в коде Aurabase: валидатор NL2SQL и транзакцию Postgres только для чтения, два блока собственного искусственного интеллекта, интегрированные в бэкэнд. Предварительные требования: проект Aurabase, его ключ service_roleи учетная запись у одного из трех собственных поставщиков LLM (OpenAI, Anthropic, Gemini).

Самое необходимое

  • Настоящий риск заключается не в вызове самой функции, а в инструменте, доступном модели: необработанный execute_sql(query) дает ей полный доступ к SQL.
  • Безопасная архитектура предоставляет инструмент query_database(question), который делегирует проверку синтаксического дерева (только SELECT, ограниченный LIMIT, изолированная схема), а не прямое выполнение.
  • Aurabase предоставляет этот валидатор изначально (/nl2sql): повторное использование его в качестве реализации инструмента позволяет избежать необходимости самостоятельно перекодировать проверку SQL.
  • Зафиксированный SQL затем выполняется через aura.db.sql() в режиме readOnly: true, фактической транзакции Postgres только для чтения, а не простого текстового фильтра.
  • Собственная конечная точка /chat Aurabase пока не принимает ни роль tool, ни параметр tools (проверено в коде): цикл агента в настоящее время выполняется через SDK поставщика LLM, а не через прокси-сервер Aurabase.
  • Ключ service_role намеренно обходит RLS: он никогда не должен покидать ваш сервер, а агент наследует более широкий доступ, чем обычный аутентифицированный пользователь.
#
Цель

Что вы построите

Вы создадите агент, который отвечает на вопросы на естественном языке о данных в проекте Postgres, не позволяя модели писать SQL, который выполняется как есть. Модель вызывает инструмент с именем query_database, этот инструмент переводит вопрос в SQL, проверенный через NL2SQL, затем выполняет этот SQL только для чтения и возвращает строки в модель, чтобы она сформулировала свой ответ.

Информация

В этом руководстве используется JavaScript SDK @aurabase/aurabase-js на стороне сервера (никогда на стороне браузера, ключ service_role не должен быть доступен клиенту) и API вызова функции OpenAI для цикла агента. Тот же принцип применяется к Anthropic или Gemini SDK.

#
Под капотом

Чем опасен инструмент «запустите этот SQL»

В большинстве руководств по агентам Postgres, включая некоторые официальные руководства, определяется один инструмент: функция execute_sql, которая принимает строку SQL в качестве аргумента и выполняет ее как есть. Модель сама записывает эту строку на основе вопроса пользователя и схемы, заданной ей в контексте.

tool-schema-dangereux.json (антишаблон)json
{
  "name": "execute_sql",
  "parameters": {
    "query": { "type": "string" }  // модель пишет SQL напрямую
  }
}

Этот выбор перекладывает на модель ответственность, которую она не может надежно выполнить. Внедрение подсказки в вопрос может привести к деструктивному SQL-коду, который инструмент выполняет без разбора, поскольку он не имеет представления о том, как должен выглядеть «законный» запрос. В нашей специальной статье подробно описан этот вектор атаки: защита NL2SQL от SQL-инъекций.

Альтернатива, созданная в этом руководстве, предоставляет более узкий инструмент query_database(question). Модель больше не может писать SQL напрямую: она может только задавать вопросы при вызове собственного инструмента. Это механизм Aurabase NL2SQL, который переводит этот вопрос в SQL, прежде чем передать его через валидатор синтаксического дерева (только SELECT, без подзапросов, десять разрешенных функций, ограниченный LIMIT).

Системный запрос не является проверкой безопасности

Инструмент execute_sql(query: string) предоставляет модели полный доступ к SQL, независимо от того, насколько хороша ваша системная подсказка. Инструкция («выполняет только SELECT») остается инструкцией, которой модель может следовать, неверно интерпретировать или считать ее обходной с помощью инъекции, подставленной в вопрос пользователя.

#
Шаг 1

Определите схему инструмента, представленного в модели.

Три собственных поставщика LLM Aurabase (OpenAI, Anthropic, Gemini) принимают таблицу определений инструментов в формате JSON Schema. Для этого агента достаточно одного инструмента: query_database, который принимает вопрос на естественном языке и ничего больше. Модель не видит ни схемы SQL, ни поля query, которое она могла бы заполнить сама.

lib/agent-tools.tstypescript
export const tools = [
  {
    type: 'function',
    function: {
      name: 'query_database',
      description:
        "Interroge les données du projet en langage naturel. N'accepte pas de SQL : posez une question.",
      parameters: {
        type: 'object',
        properties: {
          question: {
            type: 'string',
            description: 'Question en français sur les données du projet.'
          },
        },
        required: ['question'],
        additionalProperties: false
      },
    },
  },
]
#
Шаг 2

Внедрите инструмент: NL2SQL будет доступен только для чтения.

Обработчик инструмента запускается на вашем сервере, а не в браузере. Он содержит ключ проекта service_role, который по своей конструкции обходит RLS и поэтому никогда не должен быть доступен клиенту. Он выполняет два вызова Aurabase SDK.

Первый вызов переводит вопрос в SQL, проверенный через aura.ai.nl2sql(): только SELECT, ограничено LIMIT, нет доступа к системному каталогу. Второй выполняет этот SQL, уже проверенный через aura.db.sql(), с опцией readOnly: true: сам Postgres затем отказывается от любой записи в этой транзакции, независимо от текстовой проверки, уже примененной вышестоящим NL2SQL.

server/tools/query-database.tstypescript
// Клиент инициализируется с помощью ключа service_role, а не на стороне браузера.
import { aura } from '@/lib/aurabase'

export async function queryDatabase(question: string) {
  const { data: validated, error } = await aura.ai.nl2sql(
    question,
    undefined,
    { limit: 50 },
  )
  if (error) return { error: error.message }

  const { data: rows, error: execError } = await aura.db.sql(
    validated.sql,
    [],
    { readOnly: true },
  )
  if (execError) return { error: execError.message }

  return { sql: validated.sql, rows }
}
Астуце

readOnly: true запускает реальную транзакцию Postgres только для чтения: движок отказывается от записи, это не фильтр, применяемый к тексту запроса. В сочетании с проверкой только SELECT в NL2SQL агент имеет два независимых уровня: если в одном есть дефект, другой все еще сохраняется.

#
Шаг 3

Цикл агента: вызов функции на стороне 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 внутри этого цикла.

server/agent.tstypescript
import OpenAI from 'openai'
import { tools } from './lib/agent-tools'
import { queryDatabase } from './tools/query-database'

const openai = new OpenAI()

export async function askAgent(question: string) {
  const messages = [{ role: 'user', content: question }]

  const first = await openai.chat.completions.create({
    model: 'gpt-4.1', messages, tools,
  })

  const call = first.choices[0].message.tool_calls?.[0]
  if (!call) return first.choices[0].message.content

  const args = JSON.parse(call.function.arguments)
  const result = await queryDatabase(args.question)

  const second = await openai.chat.completions.create({
    model: 'gpt-4.1',
    messages: [
      ...messages,
      first.choices[0].message,
      { role: 'tool', tool_call_id: call.id, content: JSON.stringify(result) },
    ],
  })

  return second.choices[0].message.content
}

Принцип остается тем же, если вы организуете агент с помощью LangChain или такой службы, как Azure AI Agent: инструмент, объявленный в платформе, должен оставаться тем же query_database, а не просто исполнителем SQL. Подробное сравнение, где LangChain и LlamaIndex обеспечивают реальную ценность Postgres, а где они особенно усложняют: Агенты Postgres с LangChain или LlamaIndex.

#
Шаг 4

Тест с реальным вопросом

Вопрос отправлен агенту: «Сколько премиальных клиентов разместили заказ в этом месяце?» ". Шаблон вызывает query_database с этим вопросом как есть, даже не видя и не записывая никакого SQL. Вот результат двух внутренних вызовов, инициированных инструментом.

результат инструмента (выдержка)json
{
  "sql": "SELECT count(*) FROM orders WHERE customer_plan = 'premium' AND created_at >= date_trunc('month', now()) LIMIT 50",
  "rows": [{ "count": 128 }]
}

Окончательный ответ модели основан на этих реальных линиях, а не на догадках. Если инструмент возвращает ноль строк, числовые галлюцинации становятся значительно менее вероятными, чем в случае с моделью, которая реагирует без проверенных данных.

#
Безопасность

Защитите агент перед запуском в производство

  • Ключ service_role никогда не покидает ваш сервер: ни в приглашении, отправленном в модель, ни в журнале, ни в переменной среды на стороне клиента.
  • readOnly: true остается активным на aura.db.sql() для этого конкретного инструмента, даже если ваш проект требует записи в другом месте приложения.
  • service_role намеренно обходит RLS. Если агент должен отвечать по-разному в зависимости от пользователя, задающего вопрос, явно отфильтруйте SQL или вернитесь к классическим конечным точкам PostgREST, которые соблюдают RLS. См. раздел многопользовательской изоляции RLS.
  • Регистрируйте каждый вызов инструмента (заданный вопрос, проверка SQL, количество строк): это единственная полезная трассировка, если вопрос дает неожиданный результат.
  • Ограничение скорости и ежемесячная квота Aurabase уже применяются к каждому проекту на /nl2sql: разговорчивый агент не может молча превысить ваш бюджет на ИИ.
#
Честность

Текущие ограничения, о которых следует знать

Инструмент query_database наследует все ограничения валидатора NL2SQL: отсутствие подзапросов, отсутствие CTE/WITH, отсутствие UNION и закрытый список из десяти функций SQL. Вопрос, который, естественно, требует подзапроса («клиенты, которые никогда не заказывали»), должен быть переформулирован или обработан вторым специальным инструментом, а не принудительно перенесен в NL2SQL.

Сегодня внутри прокси-сервера Aurabase /chat не существует никакой оркестрации вызовов инструментов: описанный здесь цикл агента находится в коде вашего приложения, а не в управляемом сервисе. Если агенту необходимо объединить несколько инструментов (например, базу данных и документацию RAG), именно ваш бэкэнд организует эти два вызова.

#
Иди дальше

RAG и вызов функций вместе

В этом руководстве рассматриваются структурированные вопросы по реляционным данным. Для вопросов о неструктурированном контенте (документы, заявки, заметки) тот же агент может использовать второй инструмент, подключенный к встроенному RAG Aurabase (pgvector, поиск HNSW). Эти две возможности и их сочетание подробно описаны на странице Собственный искусственный интеллект в Postgres.

#
Часто задаваемые вопросы

Часто задаваемые вопросы

Могу ли я предоставить агенту доступ на запись (INSERT/UPDATE)?+
Технически да, удалив опцию readOnly и указав на отдельный инструмент, но это не то, что NL2SQL делает сегодня: валидатор разрешает только запросы SELECT, независимо от варианта выполнения, выбранного на стороне клиента. Агент записи запрашивает отдельный валидатор со своим собственным белым списком функций и, возможно, с подтверждением человека перед выполнением.
Совместимо ли оно с LangChain, LlamaIndex или такой службой, как Azure AI Agent?+
Да: эти платформы организуют цикл вызова функций за вас, но реализация инструмента остается за вами. Тот же обработчик (NL2SQL, затем выполнение только для чтения) связывает себя как функцию инструмента, объявленного в LangChain или в агенте Azure, вместо того, чтобы позволять им выполнять необработанный SQL.

ГОТОВЫ К РАЗВЕРТЫВАНИЮ?

Ваш бэкэнд за пять минут.

Кредитная карта не требуется · 500 МБ бесплатно · 50 000 MAU