PROD주권 유럽 BaaS 플랫폼대시보드 열기 →

네이티브 AI · 10분 읽음

함수 호출로 Postgres 에이전트 보호(튜토리얼)

Affane Daylami · Fondateur · 2026년 3월 21일

블로그로 돌아가기

Postgres에 쿼리하는 에이전트는 코드의 첫 번째 줄 전에 특정 보안 질문을 묻습니다. 모델에 어떤 기능을 노출하고 있습니까? LLM이 호출할 수 있는 도구가 자신이 작성한 SQL을 직접 실행하는 경우 모호한 질문이나 프롬프트 삽입만으로도 프로젝트의 모든 테이블을 읽을 수 있습니다.

이 영어 텍스트는 프랑스어 원본에서 자동으로 생성되었으며 아직 검토되지 않았습니다.
이 페이지는 자동으로 번역되었습니다. 영어 버전은 권위가 있습니다.

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 액세스를 제공합니다.
  • 안전한 아키텍처는 직접 실행이 아닌 구문 트리 유효성 검사기(SELECT만, 제한된 LIMIT, 격리된 스키마)에 위임하는 query_database(question) 도구를 노출합니다.
  • Aurabase는 이 유효성 검사기를 기본적으로 노출합니다(/nl2sql). 이를 도구 구현으로 재사용하면 SQL 유효성 검사를 직접 다시 코딩할 필요가 없습니다.
  • 그런 다음 커밋된 SQL은 단순한 텍스트 필터가 아닌 실제 읽기 전용 Postgres 트랜잭션인 readOnly: true모드에서 aura.db.sql()을 통해 실행됩니다.
  • Aurabase의 기본 /chat 엔드포인트는 아직 tool 역할이나 tools 매개변수(코드에서 확인됨)를 허용하지 않습니다. 에이전트 루프는 현재 Aurabase 프록시가 아닌 LLM 공급자의 SDK를 통해 실행됩니다.
  • service_role 키는 설계상 RLS를 우회합니다. 즉, 백엔드를 벗어나면 안 되며 에이전트는 일반적인 인증된 사용자보다 더 광범위한 액세스 권한을 상속받습니다.
#
목표

무엇을 구축할 것인가

모델이 있는 그대로 실행되는 SQL을 작성하지 않고도 Postgres 프로젝트의 데이터에 대한 자연어 질문에 대답하는 에이전트를 구축하게 됩니다. 모델은 query_database라는 도구를 호출합니다. 이 도구는 질문을 NL2SQL을 통해 검증된 SQL로 변환한 다음 이 읽기 전용 SQL을 실행하고 해당 행을 모델에 반환하여 답을 공식화합니다.

정보

이 튜토리얼에서는 서버 측의 @aurabase/aurabase-js JavaScript SDK(브라우저 측에서는 절대 사용하지 않으며 service_role 키가 클라이언트에 노출되어서는 안 됨)와 에이전트 루프용 API를 호출하는 OpenAI 함수를 사용합니다. Anthropic 또는 Gemini SDK에도 동일한 원칙이 적용됩니다.

#
후드 아래

"이 SQL 실행" 도구가 위험한 이유

일부 공식 가이드를 포함한 대부분의 Postgres 에이전트 튜토리얼은 단일 도구, 즉 SQL 문자열을 인수로 사용하여 있는 그대로 실행하는 execute_sql 함수를 정의합니다. 모델은 사용자의 질문과 컨텍스트에 제공된 스키마를 기반으로 이 문자열 자체를 작성합니다.

tool-schema-dangereux.json (안티패턴)json
{
  "name": "execute_sql",
  "parameters": {
    "query": { "type": "string" }  // 모델이 SQL을 직접 작성합니다.
  }
}

이 선택은 안정적으로 수행할 수 없는 책임을 모델에 이전합니다. 질문에 신속한 삽입을 하면 도구가 "적법한" 쿼리가 어떻게 생겼는지에 대한 개념이 없기 때문에 무분별하게 실행되는 파괴적인 SQL이 생성될 수 있습니다. 당사의 전용 기사에서는 이 공격 벡터에 대해 자세히 설명합니다: SQL 주입으로부터 NL2SQL 보호.

이 튜토리얼에서 구축된 대안은 더 좁은 도구인 query_database(question)를 노출합니다. 모델은 더 이상 SQL을 직접 작성할 수 없으며 자체 도구 호출에서만 질문할 수 있습니다. Aurabase NL2SQL 엔진은 이 질문을 구문 트리 유효성 검사기(SELECT 단독, 하위 쿼리 없음, 10개 함수 승인, 제한된 LIMIT)를 통해 전달하기 전에 SQL로 변환합니다.

시스템 프롬프트는 보안 검사가 아닙니다.

execute_sql(query: string) 도구는 시스템 프롬프트가 얼마나 좋은지에 관계없이 모델에 전체 SQL 액세스를 제공합니다. 명령("SELECT만 실행")은 모델이 사용자의 질문에 삽입하여 따르거나 잘못 해석하거나 회피할 수 있는 명령으로 남아 있습니다.

#
1단계

모델에 노출된 도구의 스키마 정의

Aurabase의 세 가지 기본 LLM 제공업체(OpenAI, Anthropic, Gemini)는 JSON 스키마 형식의 도구 정의 테이블을 허용합니다. 이 에이전트에는 단일 도구로 충분합니다: 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 이후 읽기 전용

도구 핸들러는 백엔드에서 실행되며 브라우저에서는 실행되지 않습니다. 이는 설계상 RLS를 우회하므로 클라이언트에 절대 노출되어서는 안 되는 프로젝트 키 service_role를 전달합니다. Aurabase SDK를 두 번 호출합니다.

첫 번째 호출은 질문을 aura.ai.nl2sql()를 통해 검증된 SQL로 변환합니다. SELECT만 가능, LIMIT 제한됨, 시스템 카탈로그에 대한 액세스 불가. 두 번째는 readOnly: true 옵션을 사용하여 aura.db.sql()를 통해 이미 검증된 이 SQL을 실행합니다. 그런 다음 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 트랜잭션을 트리거합니다. 엔진은 쓰기를 거부하며 요청 텍스트에 적용되는 필터가 아닙니다. NL2SQL의 SELECT 전용 유효성 검사와 결합된 에이전트에는 두 개의 독립적인 계층이 있습니다. 하나에 결함이 있으면 다른 하나는 여전히 유지됩니다.

#
3단계

에이전트 루프: 공급자의 SDK 측에서 호출하는 함수

Aurabase는 세 가지 기본 LLM 공급자를 노출하지만 해당 /chat 엔드포인트는 아직 tools 매개변수 또는 tool역할을 전달하지 않습니다. ChatOptions는 temperature, max_tokens 및 model만 전달하며 허용되는 역할은 system, user 및 assistant(llm/mod.rs 및 handlers/chat.rs에서 확인됨)로 제한됩니다. 따라서 함수 호출 루프는 현재 Aurabase 프록시가 아닌 공급자의 SDK를 통해 직접 실행됩니다.

현재의 한계, 확실한 선택은 아니다

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 에이전트와 같은 서비스를 사용하여 에이전트를 오케스트레이션하는 경우 원칙은 동일하게 유지됩니다. 프레임워크에 선언된 도구는 원시 SQL 실행자가 아닌 동일한 query_database로 유지되어야 합니다. LangChain과 LlamaIndex가 Postgres에 실제 가치를 제공하는 부분과 특히 복잡성을 추가하는 부분에 대한 비교 세부 정보: LangChain 또는 LlamaIndex를 사용하는 Postgres 에이전트.

#
4단계

실제 질문으로 테스트하기

상담원에게 보낸 질문: "이번 달에 주문한 프리미엄 고객 수는 몇 명입니까?" ". 템플릿은 SQL을 보거나 작성하지 않고 있는 그대로 이 질문으로 query_database을 호출합니다. 다음은 도구에 의해 트리거된 두 내부 호출의 결과입니다.

도구 결과(추출)json
{
  "sql": "SELECT count(*) FROM orders WHERE customer_plan = 'premium' AND created_at >= date_trunc('month', now()) LIMIT 50",
  "rows": [{ "count": 128 }]
}

모델의 최종 답은 추측이 아닌 실제 선을 기반으로 합니다. 도구가 0개의 행을 반환하는 경우 확인된 데이터 없이 응답하는 모델을 사용할 때보다 숫자 환각이 발생할 가능성이 훨씬 줄어듭니다.

#
보안

프로덕션에 들어가기 전에 에이전트를 확보하세요

  • service_role 키는 백엔드를 떠나지 않습니다. 모델로 전송된 프롬프트나 로그, 클라이언트 측 환경 변수에서도 마찬가지입니다.
  • 프로젝트가 애플리케이션의 다른 곳에 작성해야 하는 경우에도 readOnly: true은 이 특정 도구에 대해 aura.db.sql()에서 활성 상태로 유지됩니다.
  • service_role는 설계상 RLS를 우회합니다. 에이전트가 질문하는 사용자에 따라 다르게 응답해야 하는 경우 SQL에서 명시적으로 필터링하거나 RLS를 존중하는 클래식 PostgREST 엔드포인트로 대체하세요. 다중 테넌트 RLS 격리를 참조하세요.
  • 각 도구 호출(질문, 검증된 SQL, 줄 수)을 기록합니다. 이는 질문이 예상치 못한 결과를 생성하는 경우 사용할 수 있는 유일한 추적입니다.
  • Aurabase의 속도 제한 및 월별 할당량은 이미 /nl2sql의 프로젝트별로 적용됩니다. 수다스러운 에이전트는 조용히 AI 예산을 초과할 수 없습니다.
#
정직

알아야 할 현재 제한 사항

query_database 도구는 NL2SQL 유효성 검사기의 모든 제한 사항(하위 쿼리 없음, CTE/WITH 없음, UNION 없음, 10개 SQL 함수의 폐쇄 목록)을 상속합니다. 자연스럽게 하위 쿼리를 요구하는 질문("주문한 적이 없는 고객")은 NL2SQL에 강제로 적용하기보다는 다시 공식화하거나 두 번째 전용 도구로 처리해야 합니다.

현재 Aurabase /chat 프록시 내부에는 도구 호출 오케스트레이션이 존재하지 않습니다. 여기에 설명된 에이전트 루프는 관리형 서비스가 아닌 애플리케이션 코드에 있습니다. 에이전트가 여러 도구(예: 데이터베이스 및 다큐멘터리 RAG)를 연결해야 하는 경우 두 통화를 조정하는 것은 백엔드입니다.

#
더 나아가

RAG와 함수 호출 결합

이 튜토리얼에서는 관계형 데이터에 대한 구조화된 질문을 다룹니다. 구조화되지 않은 콘텐츠(문서, 티켓, 메모)에 대한 질문의 경우 동일한 에이전트가 Aurabase의 기본 RAG(pgVector, HNSW 검색)에 연결된 두 번째 도구를 노출할 수 있습니다. 두 가지 기능과 해당 표현은 Postgres의 네이티브 AI페이지에 자세히 설명되어 있습니다.

#
자주 묻는 질문

자주 묻는 질문

에이전트에게 쓰기 액세스(INSERT/UPDATE)를 부여할 수 있나요?+
기술적으로 그렇습니다. readOnly 옵션을 제거하고 별도의 도구를 지정하면 됩니다. 하지만 현재 NL2SQL은 그렇지 않습니다. 유효성 검사기는 클라이언트 측에서 선택한 실행 옵션에 관계없이 SELECT 쿼리만 허용합니다. 쓰기 에이전트는 자체 기능 화이트리스트와 실행 전 사람의 확인을 통해 별도의 유효성 검사기를 요청합니다.
LangChain, LlamaIndex 또는 Azure AI Agent와 같은 서비스와 호환됩니까?+
예: 이러한 프레임워크는 함수 호출 루프를 조정하지만 도구 구현은 사용자의 몫입니다. 동일한 처리기(NL2SQL 이후 읽기 전용 실행)는 원시 SQL을 실행하도록 허용하는 대신 LangChain 또는 Azure 에이전트에 선언된 도구의 기능으로 자체적으로 연결됩니다.

배포할 준비가 되셨나요?

5분 만에 백엔드를 완성하세요.

신용카드 불필요 · 500MB 무료 · 50,000 MAU