PRODSuwerenna europejska platforma BaaSOtwórz Panel →

Natywna sztuczna inteligencja · 10 min odczytu

Zabezpiecz agenta Postgres za pomocą wywołania funkcji (samouczek)

Affane Daylami · Fondateur · 21 marca 2026

Powrót do bloga

Agent wysyłający zapytanie do Postgres zadaje konkretne pytanie zabezpieczające przed pierwszą linijką kodu: Jaką funkcję udostępniasz modelowi? Jeśli narzędzie, które LLM może wywołać, bezpośrednio wykonuje SQL, który sam napisał, wystarczy niejednoznaczne pytanie lub szybki zastrzyk, aby odczytać dowolną tabelę w projekcie.

Ten tekst w języku angielskim został wygenerowany automatycznie na podstawie francuskiego oryginału i nie był jeszcze recenzowany.
Ta strona została przetłumaczona automatycznie. Wersja angielska jest miarodajna.

W tym samouczku budujemy agenta z wywołaniem funkcji, w przypadku którego narzędzie udostępnione modelowi nigdy nie wykonuje dowolnego kodu SQL. Łączy w sobie dwa mechanizmy już zweryfikowane w kodzie Aurabase: walidator NL2SQL i transakcję Postgres tylko do odczytu, dwie cegły natywnej sztucznej inteligencjizintegrowanej z backendem. Wymagania wstępne: projekt Aurabase, jego klucz service_rolei konto u jednego z trzech natywnych dostawców LLM (OpenAI, Anthropic, Gemini).

Najważniejsze

  • Prawdziwym ryzykiem nie jest samo wywołanie funkcji, ale narzędzie narażone na model: surowy execute_sql(query) zapewnia pełny dostęp do SQL.
  • Bezpieczna architektura udostępnia narzędzie query_database(question), które deleguje do modułu sprawdzania poprawności drzewa składni (tylko SELECT, ograniczony LIMIT, izolowany schemat), a nie do bezpośredniego wykonania.
  • Aurabase udostępnia ten walidator natywnie (/nl2sql): ponowne użycie go jako implementacji narzędzia pozwala uniknąć konieczności samodzielnego przekodowania walidacji SQL.
  • Zatwierdzony kod SQL jest następnie wykonywany poprzez aura.db.sql() w trybie readOnly: true, co jest rzeczywistą transakcją Postgres tylko do odczytu, a nie prostym filtrem tekstowym.
  • Natywny punkt końcowy /chat Aurabase nie akceptuje jeszcze roli tool ani parametru tools (zweryfikowanego w kodzie): pętla agenta działa obecnie za pośrednictwem pakietu SDK dostawcy LLM, a nie za pośrednictwem serwera proxy Aurabase.
  • Klucz service_role z założenia omija RLS: nie może nigdy opuścić backendu, a agent dziedziczy szerszy dostęp niż typowy uwierzytelniony użytkownik.
#
Cel

Co zbudujesz

Stworzysz agenta, który odpowiada na pytania w języku naturalnym dotyczące danych w projekcie Postgres, nigdy nie pozwalając modelowi na zapisanie kodu SQL, który będzie wykonywany w niezmienionej postaci. Model wywołuje narzędzie o nazwie query_database, narzędzie to tłumaczy pytanie na język SQL zweryfikowany przez NL2SQL, następnie wykonuje ten kod SQL tylko do odczytu i zwraca linie do modelu, aby sformułował odpowiedź.

Informacje

W tym samouczku zastosowano zestaw SDK JavaScript @aurabase/aurabase-js po stronie serwera (nigdy po stronie przeglądarki, klucz service_role nie może być widoczny dla klienta) oraz funkcję OpenAI wywołującą API dla pętli agenta. Ta sama zasada dotyczy pakietu Anthropic lub Gemini SDK.

#
Pod maską

Dlaczego narzędzie „uruchom ten SQL” jest niebezpieczne

Większość samouczków dotyczących agentów Postgres, w tym niektóre oficjalne przewodniki, definiuje jedno narzędzie: funkcję execute_sql, która przyjmuje ciąg SQL jako argument i wykonuje go w niezmienionej postaci. Model sam zapisuje ten ciąg na podstawie pytania użytkownika i schematu podanego mu w kontekście.

tool-schema-dangereux.json (antywzór)json
{
  "name": "execute_sql",
  "parameters": {
    "query": { "type": "string" }  // model zapisuje bezpośrednio SQL
  }
}

Wybór ten przenosi na model odpowiedzialność, której nie jest w stanie rzetelnie spełnić. Natychmiastowy zastrzyk włożony w pytanie może spowodować destrukcyjny kod SQL, który narzędzie wykonuje bezkrytycznie, ponieważ nie ma pojęcia, jak powinno wyglądać „uprawnione” zapytanie. W naszym dedykowanym artykule szczegółowo opisano ten wektor ataku: zabezpieczanie NL2SQL przed wstrzyknięciem SQL.

Alternatywa wbudowana w tym samouczku udostępnia węższe narzędzie, query_database(question). Model nie może już bezpośrednio pisać kodu SQL: może jedynie zadawać pytania w ramach własnego wywołania narzędzia. To silnik Aurabase NL2SQL tłumaczy to pytanie na SQL przed przekazaniem go przez walidator drzewa syntaktycznego (sam SELECT, bez podzapytań, autoryzowanych dziesięć funkcji, ograniczony LIMIT).

Monit systemowy nie jest kontrolą bezpieczeństwa

Narzędzie execute_sql(query: string) zapewnia modelowi pełny dostęp do SQL, niezależnie od tego, jak dobry jest znak zachęty systemu. Instrukcja („wykonuje tylko SELECT”) pozostaje instrukcją, którą model może wykonać, błędnie zinterpretować lub którą można obejść poprzez wstrzyknięcie w pytanie użytkownika.

#
Krok 1

Zdefiniuj schemat narzędzia wystawionego na model

Trzej natywni dostawcy LLM Aurabase (OpenAI, Anthropic, Gemini) akceptują tabelę definicji narzędzi w formacie schematu JSON. Dla tego agenta wystarczy jedno narzędzie: query_database, które zadaje pytanie w języku naturalnym i nic więcej. Model nie widzi schematu SQL ani pola query, które mógłby sam wypełnić.

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
      },
    },
  },
]
#
Krok 2

Zaimplementuj narzędzie: NL2SQL następnie tylko do odczytu

Moduł obsługi narzędzi działa na Twoim backendzie, nigdy w przeglądarce. Zawiera klucz projektu service_role, który z założenia omija RLS i dlatego nigdy nie powinien być udostępniany klientowi. Wykonuje dwa wywołania pakietu Aurabase SDK.

Pierwsze wywołanie tłumaczy pytanie na język SQL sprawdzany za pomocą aura.ai.nl2sql(): tylko SELECT, ograniczenie LIMIT, brak dostępu do katalogu systemowego. Drugi wykonuje ten SQL już zweryfikowany przez aura.db.sql(), z opcją readOnly: true: sam Postgres następnie odmawia jakiegokolwiek zapisu w tej transakcji, niezależnie od sprawdzania poprawności tekstu zastosowanego już przez NL2SQL wyższego szczebla.

server/tools/query-database.tstypescript
// Klient zainicjowany kluczem service_role, nigdy po stronie przeglądarki
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 }
}
Astus

readOnly: true wyzwala prawdziwą transakcję Postgres tylko do odczytu: silnik odmawia zapisu, nie jest to filtr zastosowany do tekstu żądania. W połączeniu z walidacją NL2SQL obejmującą tylko SELECT, agent ma dwie niezależne warstwy: jeśli jedna ma wadę, druga nadal działa.

#
Krok 3

Pętla agenta: wywołanie funkcji po stronie SDK dostawcy

Aurabase udostępnia trzech natywnych dostawców LLM, ale jego punkt końcowy /chat nie przekazuje jeszcze parametru tools ani roli tool. ChatOptions przenosi tylko temperature, max_tokens i model, a akceptowane role są ograniczone do system, user i assistant (zweryfikowane w llm/mod.rs i handlers/chat.rs). Dlatego pętla wywoływania funkcji działa obecnie bezpośrednio za pośrednictwem pakietu SDK dostawcy, a nie za pośrednictwem serwera proxy Aurabase.

Obecne ograniczenie, a nie ostateczny wybór

Dopóki Aurabase nie koordynuje natywnie wywołań narzędzi, Twój backend musi sam zarządzać pętlą za pomocą OpenAI, Anthropic lub Gemini SDK. Wykonywanie NL2SQL i SQL pozostaje klasycznymi wywołaniami Aurabase w tej pętli.

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
}

Zasada pozostaje taka sama, jeśli koordynujesz agenta za pomocą LangChain lub usługi takiej jak Azure AI Agent: narzędzie zadeklarowane w strukturze musi pozostać takie samo query_database, nigdy surowy moduł wykonujący SQL. Nasze szczegóły porównania, gdzie LangChain i LlamaIndex zapewniają prawdziwą wartość w Postgres i gdzie szczególnie zwiększają złożoność: Agenci Postgres z LangChain lub LlamaIndex.

#
Krok 4

Test z prawdziwym pytaniem

Pytanie wysłane do agenta: „Ilu klientów premium złożyło zamówienie w tym miesiącu?” ". Szablon wywołuje query_database z tym pytaniem w niezmienionej postaci, nigdy nie wyświetlając ani nie zapisując żadnego kodu SQL. Oto wynik dwóch wewnętrznych wywołań wywołanych przez narzędzie.

wynik narzędzia (wyciąg)json
{
  "sql": "SELECT count(*) FROM orders WHERE customer_plan = 'premium' AND created_at >= date_trunc('month', now()) LIMIT 50",
  "rows": [{ "count": 128 }]
}

Ostateczna odpowiedź modelu opiera się na tych rzeczywistych liniach, a nie na domysłach. Jeśli narzędzie zwróci zero wierszy, halucynacja liczbowa stanie się znacznie mniej prawdopodobna niż w przypadku modelu, który odpowiedziałby bez zweryfikowanych danych.

#
Bezpieczeństwo

Zabezpiecz agenta przed wejściem do produkcji

  • Klucz service_role nigdy nie opuszcza Twojego backendu: ani w podpowiedzi wysyłanej do modelu, ani w dzienniku, ani w zmiennej środowiskowej po stronie klienta.
  • readOnly: true pozostaje aktywny na aura.db.sql() dla tego konkretnego narzędzia, nawet jeśli projekt wymaga napisania w innym miejscu aplikacji.
  • service_role zgodnie z projektem omija RLS. Jeśli agent powinien reagować inaczej w zależności od użytkownika zadającego pytanie, jawnie odfiltruj kod SQL lub wróć do klasycznych punktów końcowych PostgREST, które uwzględniają RLS. Zobacz izolacja RLS dla wielu dzierżawców.
  • Rejestruj każde wywołanie narzędzia (zadane pytanie, walidacja SQL, liczba wierszy): jest to jedyny użyteczny ślad, jeśli pytanie daje nieoczekiwany wynik.
  • Limity stawek i miesięczny limit Aurabase obowiązują już dla każdego projektu w dniu /nl2sql: gadatliwy agent nie może po cichu przekroczyć budżetu AI.
#
Uczciwość

Aktualne ograniczenia, o których należy pamiętać

Narzędzie query_database dziedziczy wszystkie ograniczenia walidatora NL2SQL: brak podzapytań, brak CTE/WITH, brak UNION i zamkniętą listę dziesięciu funkcji SQL. Pytanie, które w naturalny sposób wymaga zapytania dodatkowego („klienci, którzy nigdy nie zamawiali”), należy przeformułować lub przetworzyć za pomocą drugiego dedykowanego narzędzia, a nie wtłaczać do NL2SQL.

Obecnie w serwerze proxy Aurabase /chat nie istnieje żadna orkiestracja wywołań narzędzi: opisana tutaj pętla agenta znajduje się w kodzie aplikacji, a nie w usłudze zarządzanej. Jeśli agent musi połączyć kilka narzędzi (na przykład bazę danych i dokument RAG), to Twój backend koordynuje te dwa połączenia.

#
Idź dalej

RAG i wywołanie funkcji połączone

W tym samouczku opisano pytania strukturalne dotyczące danych relacyjnych. W przypadku pytań dotyczących zawartości nieustrukturyzowanej (dokumenty, bilety, notatki) ten sam agent może udostępnić drugie narzędzie połączone z natywnym RAG Aurabase (pgvector, wyszukiwanie HNSW). Obie możliwości i ich artykulacja są szczegółowo opisane na stronie Natywna sztuczna inteligencja w Postgres.

#
Często zadawane pytania

Często zadawane pytania

Czy mogę przyznać agentowi prawo do zapisu (INSERT/UPDATE)?+
Technicznie rzecz biorąc, tak, usuwając opcję readOnly i wskazując osobne narzędzie, ale nie tym obecnie zajmuje się NL2SQL: walidator pozwala tylko na zapytania SELECT, niezależnie od opcji wykonania wybranej po stronie klienta. Agent zapisu żąda oddzielnego walidatora z własną białą listą funkcji i prawdopodobnie potwierdzeniem przez człowieka przed wykonaniem.
Czy jest kompatybilny z LangChain, LlamaIndex lub usługą taką jak Azure AI Agent?+
Tak: te frameworki koordynują za Ciebie pętlę wywoływania funkcji, ale implementacja narzędzia pozostaje Twoja. Ta sama procedura obsługi (NL2SQL, a następnie wykonanie tylko do odczytu) łączy się sama jako funkcja narzędzia zadeklarowanego w LangChain lub w agencie platformy Azure, zamiast pozwalać im na wykonywanie surowego kodu SQL.

GOTOWY DO WDROŻENIA?

Twój backend w pięć minut.

Karta kredytowa nie jest wymagana · 500 MB za darmo · 50 000 MAU