AI 에이전트를 위한 가드레일(Guardrails): 구현 방법 (코드 포함)
요약
AI 에이전트가 예기치 않은 위험한 동작을 수행하지 않도록 제한하는 '가드레일(Guardrails)'의 개념과 구현 방법을 설명합니다. 입력, 실행, 출력의 세 단계에서 안전 경계를 설정하는 방식을 다룹니다.
핵심 포인트
- 가드레일은 에이전트의 지능을 유지하면서 위험한 동작을 차단하는 안전 경계선임
- 입력 단계: 사용자 요청이 에이전트에게 전달되기 전 검토 및 차단
- 실행 단계: 에이전트가 수행하려는 작업의 권한과 범위를 제한
- 출력 단계: 에이전트의 결과물이 안전한지 최종 확인
AI 어시스턴트에게 "프로젝트의 임시 파일들을 삭제해줘"라고 요청한다고 상상해 보세요. 어시스턴트는 어떤 파일인지 알고 있다고 생각하지만, 틀렸습니다... 그리고 당신에게 필요한 설정 데이터가 담긴 중요한 파일(.env.local)을 삭제해 버립니다. 데이터는 유실됩니다.
왜 이런 일이 발생했을까요? 어시스턴트가 그것이 안전한지 여부를 생각하지 않고, 당신의 명령에서 이해한 그대로를 정확히 수행했기 때문입니다.
**가드레일 (Guardrails)**은 이런 일이 발생하는 것을 방지하는 안전 경계선입니다. 이는 위험한 구역에 울타리를 치는 것과 같습니다. 어시스턴트는 계속 작업을 수행하지만, 해를 끼칠 수 있는 구역에는 들어갈 수 없습니다.
코드 없이 비유를 통해 가드레일이 무엇인지, 그리고 왜 필요한지를 먼저 이해하고 싶으신가요? 그렇다면 개념 가이드부터 시작하세요. 여기서는 바로 구현 단계로 넘어갑니다.
시작하기 전에
AI 에이전트 (AI agent)란 무엇인가요? 인공지능(ChatGPT 등)을 사용하여 다음을 수행하는 프로그램입니다:
- 당신의 지시를 받음
- 무엇을 할지 결정함
- 동작을 실행함 (파일 삭제, 이메일 전송 등)
코드 예제는 TypeScript로 작성되었지만, 전문 프로그래머가 아니더라도 따라올 수 있습니다. 쉬운 용어로 설명해 드릴 것입니다.
가드레일이란 무엇인가
울타리가 쳐진 놀이터를 생각해 보세요. 울타리는 아이들이 어디로 갈지 또는 어떻게 놀지를 결정하지 않습니다. 단지 아이들이 도로 쪽으로 달려 나가는 것을 방지할 뿐입니다.
AI 가드레일은 기본적으로 사전에 정의한 경계선입니다. 어시스턴트는 지능을 유지하며 결정을 내리지만, 당신이 무엇을 요청하든 할 수 없는 특정 동작들이 존재합니다.
예시:
-
"설정 파일을 삭제할 수 없습니다"
-
"고객 데이터베이스에 접근할 수 없습니다"
-
"계좌에서 돈을 이체할 수 없습니다"
가드레일(Guardrails)을 배치할 수 있는 위치는 세 곳이 있습니다:
1. 이전 단계 (입력, input): 어시스턴트(assistant)에게 전달하기 전에 사용자의 요청을 검토합니다.
- 예시: 누군가 "모두 삭제해"라고 작성하면, 시스템은 어시스턴트가 시도하기 전에 이를 차단합니다.
2. 진행 단계 (실행, execution): 어시스턴트가 실행할 수 있는 작업에 제한을 둡니다.
- 예시: 어시스턴트는 파일을 읽을 수 있지만, 파일을 삭제하려고 할 때 시스템이 다음과 같이 확인합니다: "이 파일이 허용된 폴더에 있는가? 보호된 파일은 아닌가?"
3. 이후 단계 (출력, output): 어시스턴트가 반환한 내용을 사용하기 전에 검토합니다.
- 예시: 어시스턴트가 실행할 명령어를 생성하면, 실행하기 전에 해당 명령어가 안전한지 확인합니다.
가드레일이 없는 에이전트가 실패하는 이유
문제는 AI가 나쁘다는 것이 아닙니다. 문제는 AI가 그것이 안전한지 의문을 제기하지 않고, 당신의 명령에서 이해한 그대로를 정확히 수행한다는 점입니다.
예시: 당신이 "애플리케이션을 더 빠르게 만들어"라고 말하면, 어시스턴트는 데이터베이스가 느리다고 판단하여 이를 삭제할 수 있습니다. 기술적으로는 당신의 명령을 따른 것입니다. 하지만 그 결과는 재앙적입니다.
세 가지 일반적인 문제:
1. 확인 없는 되돌릴 수 없는 작업 (Irreversible actions without confirmation)
어시스턴트가 확인 절차 없이 위험한 명령(파일 삭제, 설정 변경 등)을 즉시 실행합니다. 이는 안전한지 확인하지 않고 모든 명령을 수행하는 작업자와 같습니다.
2. 공유해서는 안 될 정보의 공유 (Sharing information that shouldn't be shared)
어시스턴트가 민감한 정보(비밀번호, 고객 데이터)에 접근할 수 있으며, 공유해서는 안 됨에도 불구하고 응답에 이를 포함합니다. 이는 악의적인 의도가 아니라, 단지 답변을 위해 자신이 가진 모든 정보를 사용하는 것뿐입니다.
3. 무한 루프 (Infinite loops)
어시스턴트가 오류를 감지하고 이를 수정하려 시도하지만, 그 과정에서 또 다른 오류가 발생하고 다시 시도하는 식으로... 스스로 멈추지 않는 무한한 순환에 빠집니다.
근본적인 원인: 가드레일이 없다면, 어시스턴트는 당신의 특정 상황에서 무엇이 허용되고 무엇이 허용되지 않는지 알지 못합니다.
실무에서의 구현 방법
가장 단순한 것부터 가장 강력한 것까지 네 가지 단계의 보호 수준이 있습니다. 처음부터 모든 단계가 필요한 것은 아니므로, 첫 번째 단계부터 시작하세요.
레벨 1: 규칙서 (The rulebook) 📋
이것은 무엇인가요? 에이전트에게 주는 초기 지침입니다. "이것은 되고, 저것은 안 된다"라고 명시된 매뉴얼과 같습니다. 이는 시스템 프롬프트 (System Prompt)의 시작 부분에 위치하며, 시작부터 기대치를 설정합니다.
실제 사례: 작업자에게 "문서는 읽어도 되지만, 금고에는 절대 손대지 마세요"라고 말하는 것과 같습니다. 이는 모든 결정의 지침이 되는 황금률입니다.
const systemPrompt = `
You are a code assistant.
...
에이전트는 수신하는 각 요청을 해석하기 위해 이 지침을 사용합니다. 만약 누군가 "모든 것을 삭제해"라고 말한다면, 에이전트는 파일을 삭제할 수 없다는 것을 알고 있으므로 이를 거부해야 합니다.
장점: 구현이 빠릅니다. 이를 통해 거의 모든 일반적인 사고를 차단할 수 있습니다. 이는 에이전트의 심리적 필터 역할을 합니다.
단점: 끈질긴 사용자는 당신이 이 규칙들을 무시하도록 유도할 수도 있습니다. 에이전트는 순종하도록 훈련되었기 때문에, 적절한 말을 사용하면 매뉴얼과 모순되는 명령을 따르려고 시도할 수 있습니다. 이것이 다른 단계들이 존재하는 이유입니다.
레벨 2: 안전 필터 (The safety filter) 🛡️
이것은 무엇인가요? 에이전트가 사용자의 메시지를 받기 전에, 당신의 코드가 "이것이 안전해 보이는가?"를 확인합니다. 이 필터는 사용자와 에이전트 사이에서 작동하며, 위험한 텍스트 패턴을 분석합니다.
실제 사례: 문 앞에 경비원을 두어 누군가 무기를 들고 들어오려 하는지 확인하는 것과 같습니다. 누군가 위험한 키워드를 말하면, 그들은 건물에 들어올 수조차 없습니다.
function isSecure(message: string): boolean {
const dangers = [
/delete.*database/i,
...
이 방식의 장점은 에이전트가 위험한 메시지를 절대 처리하지 않는다는 것입니다. 메시지가 여기서 차단되면, AI 모델은 해당 메시지를 처리하려고 시도하거나 왜 이를 수행해야 하는지 정당화하려는 시도조차 하지 않습니다.
커스터마이징 옵션 (Customization options):
메시지가 메인 에이전트에 도달하기 전에 이를 평가하는 **경량 분류 모델 (lightweight classification model)**을 구현할 수도 있습니다. 작고 빠른 모델을 다음과 같은 용도로 사용할 수 있습니다:
- 의도 분류 (Intent classification): 요청이 허용된 동작을 요구하는지 여부를 결정
- 안전성 점수 산정 (Safety scoring): 각 메시지에 위험 수준(낮음, 중간, 높음)을 할당
- 콘텐츠 필터링 (Content filtering): 정규 표현식 (regex) 패턴보다 더 정확하게 민감한 주제나 패턴을 탐지
import Anthropic from "@anthropic-ai/sdk";
async function classifyMessage(message: string): Promise<{
...
이 접근 방식은 패턴 매칭 (pattern matching)보다 더 정확하지만 약간 더 느립니다. 다음 중 하나를 선택하세요:
- 패턴 기반 필터링 (Pattern-based filtering) (regex): 속도와 단순함을 위해 선택
- 모델 기반 분류 (Model-based classification) (경량 AI 모델): 정확도와 뉘앙스를 위해 선택
장점 (Pro): 에이전트가 악성 메시지를 절대 보지 못합니다. 입력이 에이전트에 도달하지 않으므로 메시지를 이상하게 해석하려고 시도할 수도 없습니다. 모델 기반 분류는 단순한 패턴 규칙을 우회하려는 정교한 시도를 잡아냅니다.
단점 (Con): (패턴 기반인 경우) 패턴의 목록에 의존합니다. 패턴을 트리거하지 않고 동일한 내용을 작성하는 새로운 방법을 찾아내는 사람이 항상 존재합니다 (예: "delete production" 대신 "remove production data" 사용). 모델 기반 분류는 지연 시간 (latency)과 비용을 추가하지만, 작고 빠른 모델을 사용하면 이 두 가지를 모두 최소화할 수 있습니다.
레벨 3: 각 도구에 대한 제한 (Limits on each tool) 🔒
이것은 무엇인가요? 에이전트가 수행할 수 있는 모든 동작은 코드 내에 자체적인 가드레일 (guardrail)을 가집니다. 에이전트가 무언가를 요청하면, 이를 실행하기 전에 허용되는지 여부를 검증합니다. 이는 함수 수준의 보호 (function-level protection)입니다.
실제 사례: 물을 서빙할 수 있는 웨이터가 있지만, 병 시스템이 물만 허용하고 알코올은 허용하지 않는 것과 같습니다. 웨이터가 바(bar)에서 무언가를 요청하더라도, 기계는 물만 제공합니다.
async function deleteFile(path: string) {
// 허용된 폴더에 있는가?
if (!path.includes('/tmp/')) {
...
이 단계는 제한 사항이 에이전트의 해석이 아닌 코드 내에 존재하기 때문에 가장 강력합니다. 에이전트가 규칙을 우회하려고 시도하더라도, 코드 자체가 이를 방지합니다.
장점 (Pro): 에이전트가 무엇을 시도하든 코드가 허용하지 않습니다. 단어나 패턴에 의존하는 것이 아니라 순수 로직에 기반하기 때문에 우회하기가 매우 어렵습니다.
단점 (Con): 각 중요한 동작마다 이 코드를 작성해야 합니다. 작업량이 더 많지만, 실무에서 가장 효과적인 방법입니다.
레벨 4: 출력 검토 (Output review) 👀
이것은 무엇인가요? 에이전트가 생성한 응답이나 명령을 사용하기 전에, 여러분의 코드가 이를 검토합니다. 만약 에이전트가 위험한 SQL 명령어를 생성한다면, 실행하기 전에 이를 차단합니다. 이것은 최종 필터입니다.
실제 사례: 문서를 출판하기 전에 검토하는 편집자와 같습니다: "이 단락은 출판하지 않습니다." 또는 각 행동이 일어나기 전에 검증하는 보안 책임자와 같습니다.
function isSQLSafe(sql: string): boolean {
if (sql.includes('DROP') || sql.includes('TRUNCATE')) {
return false; // 실행 전 차단
...
이 단계는 에이전트가 실행될 코드나 명령을 생성할 때 특히 유용합니다. 에이전트가 이전의 모든 필터를 통과했더라도, 이 단계는 곧 일어날 일이 정말로 안전한지 확인하는 마지막 단계입니다.
장점 (Pro): 여러분이 최종 필터가 됩니다. 다른 모든 것이 실패하더라도 이 단계에서 잡아낼 수 있습니다. 에이전트가 SQL 명령어, 실행 가능한 코드, 또는 실제 시스템에 영향을 미치는 동작을 생성하는 경우 특히 중요합니다.
단점 (Con): 검토해야 할 콘텐츠가 많을 경우 사용자의 대기 시간이 길어집니다. 또한, 실행 전에 각 출력을 분석하기 위한 추가적인 노력이 필요합니다.
요약:
| 레벨 | 비유하자면 | 속도 | 보안 |
|---|---|---|---|
| 1️⃣ 수동 (Manual) | "만지지 마시오"라고 적힌 표지판 | 매우 빠름 | 기본 (Basic) |
| ... |
얼마나 많이 필요한가요? 처음 두 가지 단계부터 시작하세요. 만약 에이전트가 위험한 행동(삭제, 이메일 전송 등)을 수행한다면 세 번째 단계를 추가하세요. 만약 결과값이 코드를 실행하는 데 사용된다면 네 번째 단계를 추가하세요.
흔한 실수들 (Common mistakes)
시스템 프롬프트(System prompt)만 신뢰하는 것
시스템 프롬프트는 시작점일 뿐, 완전한 시스템이 아닙니다. 만약 에이전트가 데이터를 삭제할 수 있는 도구(Tools)를 가지고 있다면, "아무것도 삭제하지 마세요"라는 지침만으로는 충분하지 않습니다. 코드 수준의 제한(Code-level limits)은 모델의 해석에 의존하지 않으며 우회하기가 더 어렵습니다.
너무 많은 것을 차단하는 가드레일
필터가 너무 공격적이어서 거의 모든 요청을 거부하는 에이전트는 쓸모가 없습니다. 목표는 에이전트를 무용지물로 만드는 것이 아니라, 예측 가능하게(Predictable) 만드는 것입니다. 적은 수의 가드레일로 시작하여 실제로 발생한 문제를 해결하는 것들만 추가하세요.
거부(Rejections) 로그를 남기지 않는 것
가드레일이 무언가를 차단할 때, 로그를 남겨두세요. 그 기록은 에이전트(또는 사용자)가 무엇을 하려고 시도했는지, 얼마나 자주 발생하는지, 그리고 귀하의 가드레일이 잘 조정(Calibrated)되어 있는지 아니면 너무 제한적인지를 알려줍니다. 로그가 없다면 눈을 가리고 비행하는 것과 같습니다. 다만 거부 로그를 남길 때는, 사용자 메시지에 민감한 데이터가 포함되어 있을 수 있으므로 전체 메시지를 저장하는 것은 피하세요. 차단을 유발한 패턴과 타임스탬프(Timestamp)만 저장하세요.
클라이언트(Client)에만 적용된 가드레일
에이전트가 API를 호출하거나 서버에서 코드를 실행한다면, 가드레일은 서버에도 있어야 합니다. 클라이언트에만 적용된 가드레일은 엔드포인트(Endpoint)를 직접 호출함으로써 우회될 수 있습니다.
구현 체크리스트 (Implementation checklist)
무한 수정 루프(Infinite correction loops)를 방지하기 위해, 시도 횟수 제한(Attempt limit)은 작동하는 가장 간단한 방법입니다:
// 무한 수정 루프를 방지하기 위한 시도 횟수 제한
const MAX_RETRIES = 3;
let attempts = 0;
...
-
시스템 프롬프트(System Prompt)에 에이전트가 할 수 있는 일과 할 수 없는 일을 명시적으로 나열함
-
모델에 요청을 보내기 전에 입력 유효성 검사(Input Validation)를 수행함
-
파괴적인 효과(삭제, 수정, 전송)를 가진 각 도구(Tool)는 구현 단계에서 자체적인 가드레일(Guardrail)을 가짐
-
가드레일에 의한 거부(Rejection) 사항이 로그(Log)에 기록됨
-
중요한 유효성 검사는 클라이언트(Client)뿐만 아니라 서버(Server)에서 수행됨
-
무한 수정 루프를 방지하기 위한 시도 횟수 제한(Attempt Limit)이 있음
자주 묻는 질문 (Frequently Asked Questions)
가드레일이 에이전트의 속도를 느리게 만드나요?
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기