AI에게 안전해 달라고 애원하지 마세요: 제약 엔지니어링의 필요성
Source: Dev.to
저는 안전 전략으로서의 “Prompt Engineering”에 지쳤습니다.
만약 여러분이 데이터베이스를 조회하고, 파일을 이동하거나, 이메일을 보내는 등 실제로 작업을 수행할 수 있는 자율 에이전트 AI를 구축하고 있다면, 그 불안감을 느꼈을 것입니다. 다음과 같은 프롬프트를 작성하죠:
“사용자 수를 가져오는 SQL 쿼리를 생성해 주세요. IMPORTANT: 테이블을 삭제하지 마세요. 제발, 간청합니다, 데이터베이스를 삭제하지 마세요.”
그런 다음 여러분은 손가락을 교차하며 LLM의 확률적 수학이 여러분의 정중한 요청을 존중해 주길 바랍니다.
이것은 광기입니다. 전통적인 소프트웨어 엔지니어링에서는 사용자 입력에 “SQL 인젝션이 되지 않게 해 주세요”라고 요청하지 않습니다. 우리는 입력을 정화하고, 방화벽을 사용하며, 엄격한 타입을 사용합니다. 하지만 AI 에이전트와 함께라면 결정론적 시스템의 기본을 잊어버린 듯합니다.
저는 최근에 Constraint Engine이라 부르는 모듈을 만들었고, 이 모듈은 AI 에이전트를 신뢰하는 방식을 완전히 바꾸어 놓았습니다. 이제 안전을 위해 프롬프트를 중단하고 코딩으로 해결해야 하는 이유를 설명합니다.
철학: 뇌 vs. 손
핵심 문제는 우리가 LLM을 뇌(계획, 추론)와 손(실행) 두 역할을 동시에 맡기고 있다는 점입니다.
- 뇌 (LLM): 계획을 생성합니다 (예: “공간을 확보하기 위해 임시 파일을 삭제하겠습니다”).
- 방화벽 (제약 엔진): 정규식, 허용 목록, 비용 제한 등 하드 규칙에 따라 계획을 검사하는 결정론적 Python 스크립트입니다.
- 손 (실행기): 방화벽이
True를 반환할 때에만 계획을 실행합니다.
“인간은 벽을 만들고, AI는 그 안에서 놀아요.”
“Logic Firewall” 구현
구현은 놀라울 정도로 간단합니다. AI를 검사하기 위해 또 다른 AI를 사용하지 않으며(그렇게 하면 비용과 불확실성이 더 늘어납니다). 표준적인, 지루한 파이썬을 사용합니다.
from dataclasses import dataclass
from typing import Any, Dict
@dataclass
class ConstraintViolation:
rule_name: str
severity: str # CRITICAL, HIGH, MEDIUM, LOW
message: str
blocked_action: str
class ConstraintEngine:
def validate_plan(self, plan: Dict[str, Any]) -> bool:
"""
Loop through rules and return approval status.
If CRITICAL or HIGH severity violations exist, BLOCK.
"""
# (Implementation of rule checking goes here)
...
규칙은 어리석다 (그리고 그게 좋은 이유)
우리는 AI가 데이터베이스 삭제가 왜 나쁜지 “이해”할 필요가 없습니다. 우리는 단지 구문을 잡아내기만 하면 됩니다.
예시: SQLInjectionRule
import re
class SQLInjectionRule:
DANGEROUS_PATTERNS = [
r'\bDROP\s+TABLE\b',
r'\bDELETE\s+FROM\b.*\bWHERE\s+1\s*=\s*1\b',
r';\s*DROP\b', # Command chaining
]
def validate(self, plan):
query = plan.get("query", "")
for pattern in self.DANGEROUS_PATTERNS:
if re.search(pattern, query, re.IGNORECASE):
return ConstraintViolation(
rule_name="SQLInjectionRule",
severity="CRITICAL",
message=f"Dangerous SQL detected: {pattern}",
blocked_action=query,
)
return None
이것이 원시적인가요? 예. 표준 DROP TABLE 명령에 대해 100 % 효과적인가요? 역시 예. 정규식은 문맥이 아니라 구문만을 검사합니다.
역설: 안전이 창의성을 가능하게 함
보통 AI 에이전트를 안전하게 만들고 싶을 때는 temperature(무작위성)를 0.0으로 낮춰 로봇처럼 예측 가능하게 합니다. 이렇게 하면 AI가 영리한 해결책을 고안할 수 있는 능력이 사라집니다.
Constraint Engine을 사용하면 temperature를 올릴 수 있습니다(예: 0.9 또는 1.0). 모델이 자유롭게 아이디어를 생성하도록 한 뒤, 방화벽이 강력한 제한을 적용합니다.
- 시나리오 A (방화벽 없음): AI가
rm -rf /를 떠올립니다. 서버가 삭제됩니다. - 시나리오 B (방화벽 사용): AI가
rm -rf /를 떠올립니다.FileOperationRule이 이를 차단합니다. 에이전트는 오류를 받습니다: “Action Blocked: root deletion not allowed.” AI는 스스로 수정합니다: “아, 죄송합니다. 대신 특정 로그 파일을 삭제하겠습니다.”
방화벽은 놀이터의 경계 역할을 하여, 창의성을 허용하면서 시스템을 안전하게 유지합니다.
SQL을 넘어: 비용 및 범위
같은 접근 방식은 예산 제어에도 적용됩니다. CostLimitRule은 미리 정의된 금액 한도를 초과하는 계획을 차단할 수 있습니다.
class CostLimitRule:
def __init__(self, max_cost: float):
self.max_cost = max_cost
def validate(self, plan):
if plan.get('estimated_cost', 0) > self.max_cost:
return ConstraintViolation(
rule_name="CostLimitRule",
severity="HIGH",
message="Cost exceeds authorized limit.",
blocked_action=str(plan),
)
return None
이는 에이전트가 비용이 많이 드는 API를 호출하는 루프에 갇혀 “무한 루프 파산” 시나리오가 발생하는 것을 방지합니다.
요약
우리는 AI가 더 이상 단순한 챗봇이 아니라 실행자가 되는 시대에 진입하고 있습니다. 시스템 프롬프트를 통한 AI의 “자기‑제어”에 의존해 인프라를 보호하는 것은 부주의한 행동입니다.
- Intercept 실행 전에 계획을 차단합니다.
- Validate 하드 로직(정규식, 수학, 화이트리스트)으로 검증합니다.
- Execute 통과한 것만 실행합니다.
구걸을 그만하고, 엔지니어링을 시작하라.