자율형 에이전트 시제품을 며칠 돌려보면 당황스러운 상황을 맞이한다. 에이전트 몇 개가 서로 대화를 주고받았을 뿐인데 클라우드 API 비용이 수백 달러씩 청구된다. 에이전트 특유의 '인식-추론-행동-반성' 루프 탓에 단발성 질의보다 토큰을 훨씬 많이 쓰기 때문이다. 여기에 사내 DB 정보나 API 키가 외부 서버로 그대로 흘러 들어갈 위험까지 생각하면 실무 적용은 아득해진다.
비용 폭증과 보안 위반은 엔지니어링 통제선만 제대로 구축해도 대부분 해결할 수 있다.
에이전트 간 무한 대화 차단과 소프트 랜딩 구현
에이전트끼리 "확인했습니다", "감사합니다" 같은 무의미한 인사말을 주고받는 현상은 예산을 고갈시키는 주범이다. 프레임워크의 기본 재귀 제한 설정에만 의존하면 한도 초과 시 서버가 500 에러를 내며 그냥 터져버린다.
서비스 중단 없이 답변을 건지려면 3단계 라우팅 제어가 필요하다.
- 프레임워크의 상태 객체 안에 스텝 카운터 변수를 하나 둔다.
- 카운터가 지정한 횟수(예: 5회)에 도달했는지 확인하는 라우터 함수를 실행한다.
- 임계값을 넘기면 예외를 던지는 대신 폴백 노드로 흐름을 우회시켜 지금까지의 결과를 요약하고 종료한다.
시스템 프롬프트에는 무의미한 텍스트 생성을 막는 통제문을 직접 넣어야 한다.
[SYSTEM INSTRUCTION: COMMUNICATION PROTOCOL]
DO NOT generate conversational filler, pleasantries, or acknowledgments (e.g., "Hello", "Thank you", "I understand", "Great job").
Output strictly the requested schema or technical answer.
If you have verified the counterpart's output and no further modifications are required, you MUST include the exact string "TERMINATE_WORKFLOW" and stop asking questions.
If a correction is requested more than twice for the same issue, stop execution and output "REQUIRES_HUMAN_INTERVENTION".
민감 데이터 마스킹 미들웨어와 망 분리
퍼블릭 API로 데이터를 보낼 때 구간 암호화가 없으면 개인정보나 API 키가 그대로 노출된다. 프록시 전단에 패턴 기반 마스킹 미들웨어를 두고 가짜 토큰으로 변환해 보내야 안전하다.
- AWS 키, Bearer 토큰, 이메일, IP 주소를 탐지하는 정규식 스캐너를 구현한다.
- 스캔한 민감 정보는 가짜 토큰으로 바꿔 외부로 보내고, 진짜 매핑 정보는 온프레미스 Redis DB에 짧은 유효기간(TTL)을 걸어 보관한다.
- 외부 프록시 게이트웨이(LiteLLM Proxy 등) 설정에서 메세지 로깅을 끄고 로그 보존 주기를 1일로 제한해 평문 로그가 디스크에 남지 않게 만든다.
네트워크 격리도 함께 가져가야 한다.
| 영역 | 포함 컴포넌트 | 통신 및 제약 조건 |
|---|---|---|
| 사내 Private VPC | 핵심 DB, 내부 메시지 버스 | 외부 인터넷 전면 차단, mTLS 내부 통신만 허용 |
| 전처리 미들웨어 | 데이터 마스킹 엔진, Redis | 사내 DB 및 에이전트 런타임과 양방향 통신 |
| Isolated DMZ | 외부 에이전트 런타임 | 사내 VPC 직접 접근 차단, 지정된 외부 API만 허용 |
디렉토리 접근 제한과 승인 게이트
에이전트에게 쉘 명령어 실행 권한을 줄 때 상위 디렉토리 참조(../)나 파괴적인 명령어를 방치하면 로컬 시스템 전체가 위험해진다.
작업 공간을 특정 경로 하위로 묶어두는 패스 검증 로직을 작성해야 한다.
import os
from pathlib import Path
BASE_SANDBOX_DIR = Path("/workspace/sandbox").resolve()
def validate_safe_path(target_path_str: str) -> Path:
target_path = (BASE_SANDBOX_DIR / target_path_str).resolve()
if not str(target_path).startswith(str(BASE_SANDBOX_DIR)):
raise PermissionError(f"허용되지 않은 디렉토리 접근 시도: {target_path_str}")
return target_path
def safe_write_file(relative_path: str, content: str):
safe_path = validate_safe_path(relative_path)
os.makedirs(safe_path.parent, exist_ok=True)
with open(safe_path, "w", encoding="utf-8") as f:
f.write(content)
명령어와 코드 반영에는 추가 보호막을 친다.
- Bash 툴 실행부에 필터를 둬서
rm -rf,chmod 777같은 위험 명령어 패턴을 차단한다. - 커밋 노드 직전에 일시 정지 함수(
interrupt())를 호출해 런타임 상태를 저장하고 대기한다. - 사람(개발팀 리더)이 코드 변경점을 확인하고 승인 신호를 보낼 때만 Git 커밋이 실행되도록 만든다.
예산 한도와 모델 캐스케이딩
프론티어 모델이 추론 과정에서 만드는 내부 생각 토큰(Reasoning Tokens)은 전부 출력 토큰 비용으로 계산된다. 툴 스키마 수십 개를 매번 컨텍스트에 집어넣는 구조도 비용을 끌어올린다.
비용 통제 체계는 다음과 같이 구성한다.
- LiteLLM 프록시에서 에이전트별 가상 키를 발급하고 월별 예산 한도를 설정한다. 한도를 넘기면 HTTP 429 응답을 내보내며 차단한다.
- 메인 모델 예산이 바닥났을 때 전체 시스템이 멈추지 않도록 백업 체인을 설정해 저비용 모델로 자동 전환되게 한다.
- 매번 JSON 스키마를 통째로 전달하는 대신, 에이전트가 직접 필요한 코드를 작성해 호출하는 방식을 도입해 입력 컨텍스트를 줄인다.
