에이전트가 가진 최악의 쿼리는 읽기 전용이다
요약
AI 에이전트의 위험성은 단순히 삭제 권한에만 국한되지 않습니다. 읽기 전용 쿼리조차도 전체 스캔이나 비효율적인 조인으로 인해 시스템 자원을 고갈시키고 다른 서비스에 심각한 영향을 줄 수 있습니다. 따라서 Lagaam과 같은 비용 책정 메커니즘을 도입하여, SQL 실행 전에 예상되는 데이터 처리량(바이트 및 행 개수)을 검증하고 예산 초과 시 쿼리를 거부하는 것이 중요합니다.
핵심 포인트
- 에이전트의 위험은 삭제가 아닌 자원 고갈(읽기 전용 쿼리)에서 발생할 수 있다.
- 비효율적인 쿼리는 시스템 전체를 느리게 만들고 파이프라인을 마비시킬 수 있다.
- Lagaam과 같은 도구는 SQL 실행 전에 스캔 바이트와 최대 행 개수를 예측하여 비용을 책정해야 한다.
- 예산 초과 시 에이전트에게 명확한 거부 메시지를 제공하여 스스로 쿼리를 수정하도록 유도하는 것이 효과적이다.
지난 4월, 한 AI 에이전트가 회사의 프로덕션 데이터베이스를 삭제했고 HN은 이 사건에 대해 1,000개의 댓글을 달았습니다. 세부 사항이 중요합니다: 해당 에이전트는 Cursor 내에서 작동했으며, 스테이징(staging)과 프로덕션(production) 모두에 영향을 줄 수 있는 API 토큰을 발견했고, 그 결과 하나의 curl 명령으로 볼륨 전체를 삭제했습니다. 백업 데이터도 같은 볼륨에 있었습니다.
HN의 결론은 '모델 자체가 나빴다'가 아니었습니다. 그것은 다음과 같았습니다: 당신은 에이전트에게 안전성을 프롬프트로 지시할 수 없다. 한계는 에이전트가 말로 우회할 수 없는 무언가에 존재해야 한다. 범위 지정된 자격 증명(Scoped credentials), 권한 계층(permission layer), 잠금장치가 걸린 문과 같습니다.
이 교훈에는 HN처럼 사람들의 관심을 끌지 못하는, 하지만 더 조용한 SQL 버전이 있습니다. 왜냐하면 아무것도 삭제되지 않기 때문입니다.
위험한 쿼리는 SELECT이다
에이전트에게 읽기 전용 사용자 권한을 가진 공유 Trino 클러스터(Trino cluster)를 연결해 주면, 헤드라인에서 다루는 위험은 처리한 것입니다: DROP도 없고, DELETE도 없습니다. 남은 것은 완벽하게 유효하고 완벽하게 읽기 전용인 SQL입니다:
- 전체 스캔(full scan)으로 변질되어 모든 워커를 점유하는 누락된 파티션 필터(missing partition filter);
- 잘못된 컬럼 또는 아예 컬럼 없이 조인(join)하여, 다른 사람들의 쿼리가 뒤에 줄 서서 기다리는 동안 워커 메모리에 수억 개의 행을 생성하는 경우;
- 첫 번째 시도가 '시간 초과(timed out)'로 끝났기 때문에 다시 실행되는 재시도 루프(retry loop).
자체 호스팅된 Trino에서는 이런 일에 대해 500달러 청구서를 받지 않습니다. 발생하는 상황은 설명하기 더 나쁩니다: 대시보드가 느려지고, 다른 팀의 파이프라인이 제때 작동하지 못하며, 그 원인은 누군가 챗봇에게 질문한 것이었습니다.
Trino 자체 계획에 따르면 676,575 bytes를 읽으므로, 0.7 MB를 초과하는 모든 스캔 제한은 통과됩니다. 이는 902,625,000 rows를 구축할 것입니다. LIMIT 10으로는 도움이 되지 않습니다. 행(rows)은 어떤 것이 반환되기 전에 미리 구축되기 때문입니다.
쿼리 실행 전 비용 책정하기
저는 Lagaam을 이 문에 걸린 자물쇠로 만들었습니다. 이것은 MCP 서버이며, 에이전트가 엔진에 도달할 수 있는 유일한 방법은 세 가지 도구(list_catalogs, describe_table, query_data)를 통해서입니다. SQL이 실행되기 전에 Lagaam은 엔진에게 비용을 문의합니다:
- 스캔할 바이트에 대한
EXPLAIN (TYPE IO), - **어떤 단계가 구축할 수 있는 가장 넓은 행 개수(widest row count)**에 대한
EXPLAIN (TYPE LOGICAL). 이는 크로스 조인(cross join)이 폭발시키고LIMIT로는 숨길 수 없는 숫자입니다.
예산 초과 시, 쿼리는 거부됩니다. 위 쿼리에 대해 에이전트가 받는 응답은 다음과 같습니다:
이 쿼리는 가장 넓은 단계에서 902,625,000 rows를 구축할 것이며, 이는 귀하의 예산인 50,000,000을 초과합니다. 이것들은 엔진이 내부적으로 자료화(materializes)하는 행이며 반환되는 행이 아니므로, LIMIT는 도움이 되지 않습니다 — 더 많은 고유 값을 가진 컬럼으로 조인하거나 조인 전에 각 측면에서 필터링하십시오.
마지막 문장이 핵심입니다. 에이전트는 거부 메시지를 읽고, SQL을 수정하여 다시 시도할 수 있습니다. 이것은 새벽 2시에 사람이 해독해야 하는 스택 트레이스(stack trace)가 아닙니다.
나머지는 어차피 원하겠지만 지루한 게이트입니다:
- 구문 트리에서 읽기 전용(Read-only). 정확히 하나의
SELECT만 허용하며, DDL/DML은 금지되고,SELECT *도 금지되며, 누락된 경우LIMIT가 주입됩니다. 검증된 SQL은 다시 렌더링되므로, 실행되는 것은 확인된 것과 정확히 같습니다. - 에이전트별 테이블 권한(per-agent table grant). 외부 테이블은 거부되고, 권한 없이 시작되지 않습니다.
- 폐쇄 실패(Fail closed). 테이블 통계가 없으면 추정치가 없고, 추정치가 없으면 쿼리가 없습니다.
- 호출당 감사 라인(audit line per call): 누가 무엇을 요청했고, 허용되었는지 거부되었는지, 그리고 그 이유입니다.
Apache Pinot도 동일한 게이트를 사용합니다. Pinot의 플래너는 비용 추정치가 없기 때문에 더 어려웠습니다 (모든 스캔이 rowcount 100이라고 말하기 때문). 따라서 Pinot 견적은 세그먼트 메타데이터와 브로커 자체의 세그먼트 가지치기(segment-pruning) 카운트를 기반으로 구축됩니다. 이것은 별도의 게시물 주제입니다.
에이전트가 실제로 작성하는 내용을 잡아낼까요?
저는 에이전트가 그럴듯하게 보낼 수 있는 쿼리 11가지(전체 스캔, SELECT *, DROP 및 DELETE, 다중 구문 주입 (multi-statement injection), CTE에 숨겨진 쓰기 작업, 과도하고 자체 복제되는 조인 (oversized and self-doubling joins), 권한 외부의 테이블)와 잘 범위가 지정된 제어 쿼리 1개를 작성했습니다. SQL을 Trino로 전달하는 원시 MCP Wrapper는 이 모든 것을 제출합니다. Lagaam은 실행 전에 11개 중 11개를 차단하며, 제어 쿼리는 여전히 실행됩니다. 테이블 및 재현 스크립트는 리포에 있습니다.
기능하지 않는 부분
- 프로덕션 데이터베이스 전체를 저장하지는 못했습니다: 그것은 SQL이 아니라 API 토큰이었습니다. Lagaam은 쿼리 경로를 보호하며, 자격 증명(credentials) 문제는 여전히 사용자 문제입니다.
- 플래너(planner)로부터 가격을 책정하므로, 통계 정보만큼만 유용합니다.
ANALYZE를 실행하세요. 통계가 없는 테이블은 추측하는 대신 거부됩니다. - 이것은 스케줄러가 아니라 게이트입니다: 모든 것이 실제로 실행되는 경우에 대한 백업책으로 리소스 그룹(resource group)을 유지하십시오.
- 스스로 재작성하거나 재시도하지 않습니다. 에이전트가 거부에 대해 무엇을 할지 결정합니다.
사용해 보세요
PyPI와 공식 MCP 레지스트리(io.github.lagaam-ai/lagaam)에서 사용할 수 있습니다:
TRINO_HOST=your-trino LAGAAM_ALLOWED_TABLES=hive.sales.orders uvx lagaam
Claude Code에서는 플러그인으로 설치하세요:
/plugin marketplace add lagaam-ai/lagaam
/plugin install lagaam@lagaam
또는 모든 MCP 클라이언트에서:
{ "mcpServers": { "lagaam": { "command": "uvx", "args": ["lagaam"], "env": { "TRINO_HOST": "your-trino", "LAGAAM_ALLOWED_TABLES": "hive.sales.orders" } } } }
Apache 2.0, 자체 호스팅, Python입니다. 에이전트가 공유 Trino 또는 Pinot 클러스터 근처에서 작동하도록 허용한다면, 무엇이 고장 났는지 듣고 싶습니다. 이슈나 DM은 환영합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기