데이터베이스를 삭제하지 않는 AI 에이전트를 구축하는 방법
요약
AI 에이전트가 데이터베이스를 삭제하거나 잘못된 작업을 수행하는 것을 방지하기 위한 아키텍처 설계 방안을 제시합니다. 프롬프트에 의존하기보다 읽기 전용 권한 부여와 인간 참여(Human-in-the-Loop) 게이트를 통한 물리적 가드레일 구축의 중요성을 강조합니다.
핵심 포인트
- 에이전트의 기본 권한은 반드시 읽기 전용(Read-Only)이어야 함
- 프롬프트는 안전망이 될 수 없으므로 아키텍처 수준의 제어가 필요함
- 쓰기 작업은 별도의 승인 서비스와 인간 참여 프로세스를 거쳐야 함
- 단순 확인 버튼 대신 사용자의 직접 입력을 요구하는 패턴 권장
저는 테스트용 에이전트가 PostgreSQL 테이블을 삭제하려고 시도하는 것을 목격했습니다. 에이전트는 자격 증명(credentials)을 가지고 있었고, 의도(intent)도 있었습니다. 이를 막을 수 있었던 유일한 것은 시스템 프롬프트(system prompt)에 적힌 "당신은 읽기 전용 어시스턴트입니다"라는 단 한 줄의 문장이었습니다.
그 문장은 효과가 있었습니다. 하지만 저는 프롬프트가 안전망(safety net)이 될 수 없다는 것을 알 만큼 충분히 많은 아슬아슬한 상황들을 목격해 왔습니다.
매주 새로운 공포 이야기가 들려옵니다. 운영 데이터베이스(production database)를 날려버린 에이전트, 10,000명의 고객에게 잘못된 메시지를 발송한 에이전트, 한 시간 만에 50,000달러의 API 비용을 발생시킨 에이전트 이야기 말입니다. 이들의 공통점은 악의적인 의도가 아닙니다. 바로 LLM(Large Language Model)을 강력하지만 신뢰할 수 없는 인턴이 아닌, 신뢰할 수 있는 운영자로 취급하는 아키텍처(architecture) 문제입니다.
정답은 "에이전트를 사용하지 마라"가 아닙니다. 정답은 "적절한 가드레일(guardrails)과 함께 에이전트를 사용하라"입니다. 저는 운영 규모(production scale)에서 점수 매기기(scoring), 생성(generation), 구조화된 추출(structured extraction)을 위해 LLM을 사용하는 시스템을 구축해 왔습니다. 에이전트가 피해를 입히지 않도록 방지하는 패턴들을 소개합니다.
기본값은 읽기 전용 (Default to Read-Only)
제가 구축하는 모든 에이전트는 읽기 전용 시스템으로 시작합니다. 에이전트는 관찰하고, 분석하고, 보고할 수 있습니다. 하지만 쓰기, 삭제 또는 실행은 할 수 없습니다.
이것은 나중에 추가하는 기능이 아닙니다. 이것은 기본 상태(default state)여야 합니다. 에이전트에게 쓰기 권한을 부여하는 것은, 에이전트에게 그것이 필요하다는 것을 증명하고 에이전트가 무엇을 쓰는지 제어할 수 있음을 확인했을 때만 수행하는 승격(promote) 과정입니다.
구현 방법은 간단합니다. 에이전트에게 읽기 전용 자격 증명(read-only credentials)이 있는 데이터베이스 연결을 제공하십시오. 모든 쓰기 작업(write operations)은 명시적인 승인이 필요한 별도의 서비스 뒤에 래핑(wrap)하십시오. 에이전트가 뮤테이션 API(mutation API)를 직접 호출하게 해서는 절대 안 됩니다.
OpenAI 함수 호출(function calling)을 사용했을 때 실제 구현 모습은 다음과 같습니다:
const readOnlyFunctions = [
{
name: "query_database",
...
에이전트는 쓰기를 요청할 수는 있지만, 직접 수행할 수는 없습니다. 이는 단순히 프롬프트 수준이 아니라 아키텍처 수준에서 인간 참여(human-in-the-loop) 게이트를 강제합니다.
실제로 작동하는 인간 참여(Human-in-the-Loop) 게이트
전형적인 "인간 참여(human in the loop)" 방식은 예/아니오(yes/no) 확인 대화창입니다. 이는 사용자가 피로감을 느껴 읽지도 않고 "예"를 클릭하기 시작할 때까지는 작동합니다. 저는 실제로 그런 일이 일어나는 것을 보았습니다.
더 나은 패턴은 인간이 자신의 언어로 의도를 다시 표현하도록 요구하는 것입니다. "이 작업을 승인합니다"라고 하는 대신, 요약을 보여주고 사용자에게 특정 변경 사항을 직접 입력하거나 확인하도록 요청하십시오. 예를 들어, "사용자 1234 삭제"라고 적힌 버튼을 보여주는 대신, 어떤 일이 일어날지에 대한 미리보기를 보여주고 진행을 위해 사용자가 "사용자 1234 삭제"를 직접 입력하도록 요구하는 방식입니다.
위험도가 높은 작업의 경우, 쿨다운 기간 (cooldown period)을 추가하십시오. 에이전트는 특정 시간 범위 내에서 동일한 작업을 두 번 실행할 수 없습니다. 이는 에이전트가 동일한 요청으로 승인 대기열을 가득 채우는 폭주 루프 (runaway loop)를 방지합니다.
대화에 자율적으로 답장하는 소셜 미디어 참여 에이전트를 생각해 보십시오. 에이전트에게 답장을 생성할 수 있는 능력은 부여하되, 게시할 수 있는 능력은 부여하지 않는다고 가정해 봅시다. 사람이 생성된 각 답장을 검토하고, 필요에 따라 편집한 뒤, 게시 버튼을 클릭합니다. 이 추가적인 단계는 환각 (hallucinations), 톤 불일치 (tone mismatches), 그리고 LLM이 재미있다고 생각하는 부적절한 농담 같은 문제들을 잡아냅니다. 에이전트는 게시 API에 전혀 손을 대지 않습니다. 이러한 분리 (separation)가 당신을 구원합니다.
멱등적 작업 (Idempotent Actions) 및 안전한 재시도
에이전트는 실패합니다. 타임아웃 (time out)이 발생합니다. 재시도합니다. 당신이 할 수 있는 최악의 행동은 에이전트가 비멱등적 (non-idempotent) 작업을 재시도하게 두는 것입니다.
에이전트가 수행하는 모든 변이 (mutation) 작업은 두 번 호출해도 안전해야 합니다. 멱등성 키 (idempotency keys)를 사용하십시오. 각 작업에 대해 고유한 키를 생성하면, 에이전트가 동일한 키로 재시도할 경우 시스템이 이를 중복 제거 (deduplicate)합니다.
다음은 지원서를 제출해야 하는 구직 에이전트를 위해 제가 사용하는 패턴입니다:
async function submitApplication(application: Application, idempotencyKey: string) {
// 이 키가 이미 처리되었는지 확인
const existing = await db.applications.findUnique({
...
에이전트는 항상 멱등성 키를 전달합니다. 만약 API 호출이 타임아웃되고 에이전트가 재시도하더라도, 두 번째 호출은 아무 작업도 수행하지 않는 no-op이 됩니다. 중복 지원, 이중 결제, 이중 삭제가 발생하지 않습니다.
에이전트 환경의 샌드박스화 (Sandbox the Agent's Environment)
에이전트는 운영 데이터베이스 (production database)나 운영 API (production API)에 직접 접근해서는 안 됩니다. 이는 타협할 수 없는 원칙입니다.
에이전트와 모든 외부 시스템 사이에 프록시 계층 (proxy layer)을 생성하십시오. 이 프록시는 정책 (policy)에 따라 모든 요청을 검증합니다. 정책은 에이전트가 무엇을 할 수 있는지, 어떤 데이터를 볼 수 있는지, 그리고 무엇을 건드릴 수 없는지를 정의합니다.
데이터베이스의 경우, 저는 읽기 전용이며 지연 (lag)을 허용하는 복제본 (replica)을 대상으로 에이전트를 실행합니다. 에이전트는 실시간에 가까운 데이터를 볼 수 있지만, 데이터에 쓸 수는 없습니다. 만약 에이전트가 쓰기 작업을 수행해야 한다면, 모든 변경 사항 (mutation)을 기록하고 속도 제한 (rate limits)을 강제하는 프록시를 거쳐야 합니다.
외부 API의 경우, 프록시는 위험한 기능들을 제거합니다. 만약 에이전트가 CRM API를 호출하고 있다면, 프록시는 사용 가능한 경로 (routes)에서 DELETE 엔드포인트 (endpoint)를 제거합니다. 에이전트는 전체 API 표면 (API surface)을 결코 볼 수 없습니다. 에이전트는 오직 프록시가 노출하는 것만을 봅니다.
연쇄 반응 없는 오류 복구 (Error Recovery Without Cascading)
에이전트의 작업이 실패할 때, 기본 동작은 맹목적으로 재시도하는 것이 아니라 중단하는 것이어야 합니다. 여기에는 서킷 브레이커 (circuit breaker) 패턴이 효과적입니다. 예를 들어, 에이전트가 동일한 서비스로부터 연속으로 세 번의 오류를 받는다고 가정해 봅시다. 그러면 서킷이 열립니다 (circuit opens). 에이전트는 사람이 이를 재설정할 때까지 해당 서비스를 다시 호출할 수 없습니다.
이는 단 하나의 오작동하는 에이전트가 장애 발생 중에 다운스트림 시스템 (downstream system)을 계속해서 타격하는 것을 방지합니다. 또한 에이전트가 실패한 작업을 파괴적인 대안으로 "수정"하려고 시도함으로써 상황을 악화시키는 것도 방지합니다.
저는 또한 전체 추론 체인 (reasoning chain)을 포함하여 에이전트가 수행하는 모든 작업을 기록합니다. 무언가 잘못되었을 때, 저는 에이전트의 의사 결정 과정을 재현하여 정확히 어느 지점에서 경로를 벗어났는지 확인할 수 있습니다. 이는 디버깅 (debugging)과 가드레일 (guardrails)을 개선하는 데 매우 귀중합니다.
확장 가능한 아키텍처 (The Architecture That Scales)
제가 설계한 모든 에이전트 시스템에 적용되는 패턴은 다음과 같습니다: 에이전트는 실행 엔진 (execution engine)이 아니라 제안 엔진 (proposal engine)이어야 합니다. 에이전트는 아이디어, 논거, 그리고 계획을 생성합니다. 별도로 엄격하게 제어되는 실행 계층 (execution layer)이 이를 실행할지 여부를 결정합니다.
에이전트는 프로덕션 데이터베이스 (production database)를 절대 건드리지 않습니다. 뮤테이션 API (mutation API)를 직접 호출하지도 않습니다. 관리자 권한 (admin credentials) 또한 전혀 가지고 있지 않습니다. 에이전트에게는 업무 수행에 필요한 최소한의 권한 (minimum permissions)만 부여되며, 이러한 권한은 프롬프트 수준 (prompt level)이 아닌 인프라 수준 (infrastructure level)에서 강제됩니다.
만약 여러분의 팀이 AI 에이전트의 안전성 문제로 골머리를 앓고 있거나, 에이전트가 무엇을 할지 모른다는 두려움 때문에 배포 속도가 늦어지고 있다면, 그것이 바로 제가 도움을 드리는 분야입니다. 저는 유용할 만큼 강력하면서도, 두려움 없이 배포할 수 있을 만큼 안전한 프로덕션 AI 시스템을 구축합니다. 함께 의견을 나누고 싶다면 언제든 환영합니다.
작성자: Abdul Rehman, 프로덕션 SaaS, MVP 및 AI 자동화를 구축하는 풀스택 AI 엔지니어. 더 많은 정보는 PrimeStrides에서 확인하세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기