PRODSouveräne europäische BaaS-PlattformÖffnen Sie das Dashboard →

Native KI · 10 Min. Lesezeit

Sicherer Postgres-Agent mit Funktionsaufruf (Tutorial)

Affane Daylami · Fondateur · 21. März 2026

Zurück zum Blog

Ein Agent, der Postgres abfragt, stellt vor der ersten Codezeile eine bestimmte Sicherheitsfrage: Welche Funktion stellen Sie dem Modell zur Verfügung? Wenn das Tool, das das LLM aufrufen kann, das von ihm selbst geschriebene SQL direkt ausführt, reicht eine mehrdeutige Frage oder eine Eingabeaufforderung aus, um eine beliebige Tabelle im Projekt zu lesen.

Dieser englische Text wurde automatisch aus dem französischen Original generiert und wurde noch nicht überprüft.
Diese Seite wurde automatisch übersetzt. Maßgeblich ist die englische Version.

In diesem Tutorial wird ein Agent mit Funktionsaufruf erstellt, bei dem das dem Modell ausgesetzte Tool niemals beliebiges SQL ausführt. Es kombiniert zwei bereits im Aurabase-Code verifizierte Mechanismen: den NL2SQL-Validator und eine schreibgeschützte Postgres-Transaktion, zwei Bausteine ​​der nativen KI, die in das Backendintegriert ist. Voraussetzungen: ein Aurabase-Projekt, sein service_role-Schlüssel und ein Konto bei einem der drei nativen LLM-Anbieter (OpenAI, Anthropic, Gemini).

Das Wesentliche

  • Das eigentliche Risiko liegt nicht in der Funktion, die sich selbst aufruft, sondern in dem Werkzeug, das dem Modell zur Verfügung gestellt wird: Ein unformatiertes execute_sql(query) gewährt ihm vollständigen SQL-Zugriff.
  • Die sichere Architektur stellt ein query_database(question)-Tool bereit, das an einen Syntaxbaumvalidator delegiert (nur SELECT, begrenztes LIMIT, isoliertes Schema) und nicht direkt ausgeführt wird.
  • Aurabase stellt diesen Validator nativ bereit (/nl2sql): Durch die Wiederverwendung als Implementierung des Tools müssen Sie die SQL-Validierung nicht selbst neu codieren.
  • Das festgeschriebene SQL wird dann über aura.db.sql() im readOnly: true-Modus ausgeführt, eine tatsächliche schreibgeschützte Postgres-Transaktion, kein einfacher Textfilter.
  • Der native /chat-Endpunkt von Aurabase akzeptiert noch weder eine tool-Rolle noch einen tools-Parameter (im Code überprüft): Die Agentenschleife läuft derzeit über das SDK des LLM-Anbieters, nicht über den Aurabase-Proxy.
  • Der Schlüssel service_role umgeht RLS von Natur aus: Er darf Ihr Backend niemals verlassen und der Agent erbt einen umfassenderen Zugriff als ein typischer authentifizierter Benutzer.
#
Ziel

Was Sie bauen werden

Sie erstellen einen Agenten, der Fragen zu Daten in einem Postgres-Projekt in natürlicher Sprache beantwortet, ohne dass das Modell jemals SQL schreiben muss, das so ausgeführt wird, wie es ist. Das Modell ruft ein Tool namens query_databaseauf. Dieses Tool übersetzt die Frage in über NL2SQL validiertes SQL, führt dann dieses schreibgeschützte SQL aus und gibt die Zeilen an das Modell zurück, damit es seine Antwort formuliert.

Infos

Dieses Tutorial verwendet das JavaScript-SDK @aurabase/aurabase-js auf der Serverseite (niemals auf der Browserseite, der Schlüssel service_role darf dem Client nicht zugänglich gemacht werden) und die OpenAI-Funktionsaufruf-API für die Agentenschleife. Das gleiche Prinzip gilt für das Anthropic- oder Gemini-SDK.

#
Unter der Haube

Warum ein „Run this SQL“-Tool gefährlich ist

Die meisten Postgres-Agent-Tutorials, einschließlich einiger offizieller Leitfäden, definieren ein einziges Tool: eine execute_sql-Funktion, die einen SQL-String als Argument verwendet und ihn unverändert ausführt. Das Modell schreibt diese Zeichenfolge selbst, basierend auf der Frage des Benutzers und dem ihm im Kontext gegebenen Schema.

tool-schema-dangereux.json (Anti-Muster)json
{
  "name": "execute_sql",
  "parameters": {
    "query": { "type": "string" }  // Das Modell schreibt SQL direkt
  }
}

Durch diese Wahl wird dem Modell eine Verantwortung übertragen, die es nicht zuverlässig erfüllen kann. Eine in die Frage eingefügte Prompt-Injection kann destruktives SQL erzeugen, das das Tool wahllos ausführt, da es keine Vorstellung davon hat, wie eine „legitime“ Abfrage aussehen sollte. Unser spezieller Artikel beschreibt diesen Angriffsvektor: NL2SQL gegen SQL-Injection sichern.

Die in diesem Tutorial integrierte Alternative stellt ein engeres Tool zur Verfügung: query_database(question). Das Modell kann SQL nicht mehr direkt schreiben: Es kann nur noch in seinem eigenen Tool-Aufruf eine Frage stellen. Es ist die Aurabase NL2SQL-Engine, die diese Frage in SQL übersetzt, bevor sie durch einen syntaktischen Baumvalidator geleitet wird (SELECT allein, keine Unterabfragen, zehn Funktionen autorisiert, begrenztes LIMIT).

Die Systemaufforderung ist keine Sicherheitsüberprüfung

Ein execute_sql(query: string)-Tool gewährt dem Modell vollständigen SQL-Zugriff, unabhängig davon, wie gut Ihre Systemeingabeaufforderung ist. Eine Anweisung („führt nur SELECTs aus“) bleibt eine Anweisung, der das Modell folgen, die sie falsch interpretieren oder die durch eine in die Frage des Benutzers eingefügte Injektion umgangen werden kann.

#
Schritt 1

Definieren Sie das Schema des Werkzeugs, das dem Modell zur Verfügung gestellt wird

Die drei nativen LLM-Anbieter von Aurabase (OpenAI, Anthropic, Gemini) akzeptieren eine Tabelle mit Tooldefinitionen im JSON-Schemaformat. Für diesen Agenten reicht ein einziges Tool aus: query_database, das eine Frage in natürlicher Sprache beantwortet und sonst nichts. Das Modell sieht weder das SQL-Schema noch ein query-Feld, das es selbst ausfüllen könnte.

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

Implementieren Sie das Tool: NL2SQL, dann schreibgeschützt

Der Tool-Handler läuft auf Ihrem Backend, niemals im Browser. Es trägt den Projektschlüssel service_role, der RLS konstruktionsbedingt umgeht und daher niemals einem Client zugänglich gemacht werden sollte. Es werden zwei Aufrufe an das Aurabase SDK durchgeführt.

Der erste Aufruf übersetzt die Frage in SQL, validiert über aura.ai.nl2sql(): nur SELECT, LIMIT eingeschränkt, kein Zugriff auf den Systemkatalog. Der zweite führt dieses bereits über aura.db.sql()validierte SQL mit der Option readOnly: true aus: Postgres selbst verweigert dann jegliches Schreiben in dieser Transaktion, unabhängig von der Textvalidierung, die bereits von NL2SQL Upstream angewendet wurde.

server/tools/query-database.tstypescript
// Der Client wird mit dem Schlüssel „service_role“ initialisiert, niemals auf der Browserseite
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 }
}
Scharfsinn

readOnly: true löst eine echte schreibgeschützte Postgres-Transaktion aus: Die Engine verweigert das Schreiben, es handelt sich nicht um einen Filter, der auf den Anforderungstext angewendet wird. In Kombination mit der reinen SELECT-Validierung von NL2SQL verfügt der Agent über zwei unabhängige Schichten: Wenn eine einen Fehler aufweist, gilt die andere weiterhin.

#
Schritt 3

Die Agentenschleife: Funktionsaufruf auf der SDK-Seite des Lieferanten

Aurabase stellt drei native LLM-Anbieter zur Verfügung, sein /chat-Endpunkt gibt jedoch noch keinen tools-Parameter oder eine tool-Rolle weiter. ChatOptions trägt nur temperature, max_tokens und model, und akzeptierte Rollen sind auf system, user und assistant beschränkt (überprüft in llm/mod.rs und handlers/chat.rs). Die Funktionsaufrufschleife läuft daher heute direkt über das SDK des Anbieters, nicht über den Aurabase-Proxy.

Aktuelle Einschränkung, keine endgültige Wahl

Solange Aurabase Toolaufrufe nicht nativ orchestriert, muss Ihr Backend die Schleife selbst mit dem OpenAI, Anthropic oder Gemini SDK verwalten. NL2SQL und SQL-Ausführung bleiben klassische Aurabase-Aufrufe innerhalb dieser Schleife.

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
}

Das Prinzip bleibt dasselbe, wenn Sie den Agent mit LangChain oder einem Dienst wie Azure AI Agent orchestrieren: Das im Framework deklarierte Tool muss dasselbe bleiben query_database, niemals ein unformatierter SQL-Executor. Unser Vergleich zeigt, wo LangChain und LlamaIndex einen echten Mehrwert für Postgres bieten und wo sie insbesondere die Komplexität erhöhen: Postgres-Agenten mit LangChain oder LlamaIndex.

#
Schritt 4

Testen Sie mit einer echten Frage

An den Agenten gesendete Frage: „Wie viele Premium-Kunden haben diesen Monat eine Bestellung aufgegeben?“ ". Die Vorlage ruft query_database mit dieser Frage auf, ohne jemals SQL zu sehen oder zu schreiben. Hier ist das Ergebnis der beiden internen Aufrufe, die vom Tool ausgelöst wurden.

Werkzeugergebnis (Auszug)json
{
  "sql": "SELECT count(*) FROM orders WHERE customer_plan = 'premium' AND created_at >= date_trunc('month', now()) LIMIT 50",
  "rows": [{ "count": 128 }]
}

Die endgültige Antwort des Modells basiert auf diesen tatsächlichen Linien und nicht auf einer Vermutung. Wenn das Tool null Zeilen zurückgibt, ist eine Zahlenhalluzination deutlich unwahrscheinlicher als bei einem Modell, das ohne verifizierte Daten reagieren würde.

#
Sicherheit

Sichern Sie den Agenten, bevor Sie ihn in Produktion nehmen

  • Der Schlüssel service_role verlässt niemals Ihr Backend: weder in der an das Modell gesendeten Eingabeaufforderung, noch in einem Protokoll, noch in einer clientseitigen Umgebungsvariablen.
  • readOnly: true bleibt für dieses spezielle Tool auf aura.db.sql() aktiv, auch wenn Ihr Projekt an anderer Stelle in der Anwendung geschrieben werden muss.
  • service_role umgeht RLS absichtlich. Sollte der Agent je nach Fragesteller unterschiedlich reagieren, filtern Sie explizit im SQL oder greifen Sie auf klassische PostgREST-Endpunkte zurück, die RLS respektieren. Siehe mehrinstanzenfähige RLS-Isolation.
  • Protokollieren Sie jeden Tool-Aufruf (gestellte Frage, SQL-validiert, Anzahl der Zeilen): Dies ist der einzige verwendbare Trace, wenn eine Frage ein unerwartetes Ergebnis liefert.
  • Die Ratenbegrenzung und das monatliche Kontingent von Aurabase gelten bereits pro Projekt am /nl2sql: Ein gesprächiger Agent kann Ihr KI-Budget nicht stillschweigend überschreiten.
#
Ehrlichkeit

Aktuelle Grenzwerte, die Sie beachten sollten

Das Tool query_database übernimmt alle Einschränkungen des NL2SQL-Validators: keine Unterabfragen, kein CTE/WITH, keine UNION und eine geschlossene Liste von zehn SQL-Funktionen. Eine Frage, die natürlich eine Unterabfrage erfordert („Kunden, die noch nie bestellt haben“), muss umformuliert oder von einem zweiten dedizierten Tool verarbeitet werden, anstatt in NL2SQL gezwungen zu werden.

Derzeit gibt es im Aurabase /chat-Proxy keine Orchestrierung von Toolaufrufen: Die hier beschriebene Agentenschleife befindet sich in Ihrem Anwendungscode, nicht in einem verwalteten Dienst. Wenn der Agent mehrere Tools verketten muss (z. B. Datenbank- und Dokumentations-RAG), ist es Ihr Backend, das die beiden Aufrufe orchestriert.

#
Gehen Sie weiter

RAG und Funktionsaufruf kombiniert

Dieses Tutorial behandelt strukturierte Fragen zu relationalen Daten. Bei Fragen zu unstrukturierten Inhalten (Dokumente, Tickets, Notizen) kann derselbe Agent ein zweites Tool bereitstellen, das mit der nativen RAG von Aurabase verbunden ist (pgvector, HNSW-Suche). Die beiden Funktionen und ihre Artikulation werden auf der Seite Native AI auf Postgresdetailliert beschrieben.

#
Häufig gestellte Fragen

FAQs

Kann ich dem Agenten Schreibzugriff gewähren (INSERT/UPDATE)?+
Technisch gesehen ja, indem die readOnly-Option entfernt und auf ein separates Tool verwiesen wird, aber das ist nicht das, was NL2SQL heute tut: Der Validator lässt nur SELECT-Abfragen zu, unabhängig von der auf der Clientseite gewählten Ausführungsoption. Ein Schreibagent fordert einen separaten Validator mit einer eigenen Whitelist von Funktionen und wahrscheinlich einer menschlichen Bestätigung vor der Ausführung an.
Ist es mit LangChain, LlamaIndex oder einem Dienst wie Azure AI Agent kompatibel?+
Ja: Diese Frameworks orchestrieren die Funktionsaufrufschleife für Sie, aber die Implementierung des Tools bleibt Ihnen überlassen. Derselbe Handler (NL2SQL, dann schreibgeschützte Ausführung) verbindet sich selbst als Funktion des in LangChain oder im Azure-Agenten deklarierten Tools, anstatt ihnen die Ausführung von Roh-SQL zu ermöglichen.

BEREIT ZUM EINSATZ?

Ihr Backend in fünf Minuten.

Keine Kreditkarte erforderlich · 500 MB kostenlos · 50.000 MAU