LLM WikiAccess-protected knowledge portal

WIKI

smolagents 1.25: 코드를 생성해 도구를 부르는 에이전트의 실행 경계와 보안 설계

왜 코드를 직접 쓰는가 대부분의 LLM 에이전트 프레임워크는 LLM이 JSON 형태의 도구 호출을 생성하고, 런타임이 그 JSON을 파싱해 도구를 실행하는 방식을 택한다. Hugging Face가 2024년 말 공개한 smolagents는 다른 가정에서 출발했다. LLM이 Python 코드 자체를 생성하고, 그 코드를 직접 실행하면 어떨까. 이 접근의 이점은 측정 가능했다. smolagents 팀이 공개한 벤치마크에서 Code

경로human/study/content/ai-frontier/28-smolagents-code-agent-sandbox-security.md
카테고리Study
태그#agent #ai-review #airflow #code #sandbox #security #smolagents #study

왜 코드를 직접 쓰는가

대부분의 LLM 에이전트 프레임워크는 LLM이 JSON 형태의 도구 호출을 생성하고, 런타임이 그 JSON을 파싱해 도구를 실행하는 방식을 택한다. Hugging Face가 2024년 말 공개한 smolagents는 다른 가정에서 출발했다. LLM이 Python 코드 자체를 생성하고, 그 코드를 직접 실행하면 어떨까.

이 접근의 이점은 측정 가능했다. smolagents 팀이 공개한 벤치마크에서 CodeAgent는 JSON 기반 도구 호출 에이전트 대비 30% 적은 스텝으로 동일한 작업을 완료했다. Python 코드는 자연스럽게 루프, 조건문, 함수 합성을 지원하기 때문에, JSON 호출로는 여러 번 왕복해야 할 작업을 한 번의 코드 블록에 담을 수 있다.

2026년 5월, smolagents는 v1.25.0을 릴리스하면서 이 코드 실행 모델의 보안 경계를 재설계했다. 코드를 실행하는 에이전트는 강력하지만, 그만큼 실행 경계가 명확하지 않으면 위험하다.


두 가지 에이전트 유형: CodeAgent와 ToolCallingAgent

smolagents의 최상위 추상은 MultiStepAgent다. 여기서 두 구체 구현이 파생된다.

CodeAgentToolCallingAgent
행동 표현Python 코드 스니펫 생성JSON 도구 호출 딕셔너리 생성
유연성루프, 조건문, 다중 도구 호출 자유로움단일 도구 호출 구조로 제한
안정성LLM이 유효한 Python을 생성해야 함모델이 fine-tune된 형식과 일치
스텝 효율30% 적은 LLM 호출더 많은 왕복 필요
적합한 시나리오복잡한 멀티스텝, 동적 오케스트레이션간단한 단일 도구, 높은 안정성 요구

CodeAgent의 실행 흐름은 단순하다. agent.run(prompt) → LLM이 Python 코드 블록 생성 → 실행기(Executor)가 코드 실행 → 결과를 다음 LLM 호출의 맥락으로 추가 → 최종 답변 반환. 루프는 에이전트가 "최종 답변"(final_answer())을 생성하거나 최대 스텝 수에 도달할 때 종료된다.


실행 아키텍처: Executor 계층

smolagents 1.25 실행 경계와 Executor 옵션 CodeAgent / MultiStepAgent LLM 호출 → Python 코드 생성 → Executor 전달 → 결과 수집 → 다음 스텝 반복 max_steps 초과 또는 final_answer() 호출 시 루프 종료 ▼ Executor 선택 (보안 경계 결정) LocalPythonExecutor AST 파싱 + 허용 import 목록 에이전트 프로세스 내부에서 실행 ⚠ 보안 경계 아님 — 개발 전용 우회 가능, 프로덕션 배포 금지 허용 모듈 목록 명시 필요 DockerExecutor 로컬 Docker 컨테이너 격리 파일시스템 마운트 제어 가능 커스텀 Dockerfile 지원 v1.25: allow_origin 제거 토큰 기반 인증 추가 E2B Sandbox 원격 격리 컨테이너 로컬 환경 영향 없음 API 키 필요 신뢰할 수 없는 코드 실행에 가장 강한 격리 Modal Sandbox 원격 서버리스 실행 GPU 워크로드 지원 Modal 계정 필요 v1.25: allow_origin 제거 loopback-only 바인딩 Blaxel 원격 코드 실행 Blaxel 플랫폼 통합 지수 백오프 재시도 v1.24에서 추가됨 MCP 도구 파싱 지원 보안 경계 없음 (개발 전용) 프로덕션 배포 가능한 격리 실행 환경 멀티에이전트: ManagedAgent 패턴 오케스트레이터 CodeAgent Python으로 서브에이전트 호출 루프·조건문으로 위임 제어 ManagedAgent A 검색 전문 에이전트 tool로 등록 ManagedAgent B 코드 실행 전문 에이전트 독립 Executor 사용 가능 오케스트레이터 코드: result = sub_agent_a(query) if result: sub_agent_b(result)
smolagents 실행 아키텍처와 보안 경계

LocalPythonExecutor: AST 기반 실행과 그 한계

LocalPythonExecutor는 코드를 Python 인터프리터에 직접 넘기지 않는다. 대신 코드를 Abstract Syntax Tree(AST)로 파싱하고 노드를 하나씩 순회하며 실행한다. 허용되지 않은 import나 위험한 빌트인 함수를 AST 수준에서 차단하는 방식이다.

from smolagents import CodeAgent, HfApiModel, LocalPythonExecutor

# 허용할 import 모듈 목록을 명시해야 한다
executor = LocalPythonExecutor(
    additional_authorized_imports=["pandas", "numpy", "re"]
)

agent = CodeAgent(
    tools=[...],
    model=HfApiModel("Qwen/Qwen2.5-Coder-32B-Instruct"),
    executor=executor,
)

하지만 이 차단 방식은 완전하지 않다. 동적 코드 실행(exec, eval), 속성 체인을 통한 우회, __builtins__ 조작 등으로 제한을 벗어날 수 있다. smolagents 팀은 공식 문서에서 명확히 경고한다: "LocalPythonExecutor is not a security boundary. Do not use it when running untrusted code."


v1.25.0 보안 강화: 무엇을 고쳤나

2026년 5월 14일 릴리스된 v1.25.0은 원격 Executor의 심각한 보안 취약점을 패치했다. 변경 내용을 보면 그동안의 문제 범위를 짐작할 수 있다.

바인딩 주소 제한

이전 WasmExecutor는 0.0.0.0(모든 인터페이스)에 리슨했다. v1.25에서 loopback-only(127.0.0.1)로 전환됐다. 원격에서 Executor 서버에 직접 접근할 수 있는 경로가 닫혔다.

토큰 기반 인증 추가

DockerExecutor와 Modal은 이전까지 allow_origin 설정에 의존했다. v1.25에서는 allow_origin 옵션이 제거되고, Bearer 토큰 인증이 기본으로 추가됐다. Executor 서버와 에이전트 런타임 사이의 API 호출에 서명이 요구된다.

직렬화 레지스트리 패턴

importlib으로 모듈을 동적으로 불러와 에이전트와 모델을 역직렬화하던 방식이 레지스트리 패턴으로 교체됐다. 알려진 타입만 등록하고 그 외를 거부해, 임의 클래스 인스턴스화를 통한 공격을 차단한다.

pickle 처리 표준화

암묵적 pickle 역직렬화가 제거됐다. 명시적 prefix가 없는 직렬화 데이터는 거부된다.


샌드박스 선택 기준

운영 환경에 따라 적합한 Executor가 다르다.

시나리오권장 Executor이유
로컬 개발, 신뢰할 수 있는 코드LocalPythonExecutor가장 빠름, 설정 불필요
사내 서버, 의존성 관리 필요DockerExecutor컨테이너 격리 + 로컬 리소스 접근
외부 사용자 입력 실행E2B가장 강한 원격 격리
GPU 집약 코드, 서버리스Modal탄력적 GPU 환경
Blaxel 플랫폼 통합Blaxel플랫폼 생태계 활용

프로덕션 배포에서 LocalPythonExecutor를 사용하면 에이전트가 생성하는 코드가 호스트 프로세스에서 직접 실행된다. 사용자 입력이 에이전트 프롬프트에 포함되는 구조라면, 코드 인젝션으로 호스트 시스템 파일에 접근하거나 외부 서버에 연결하는 코드가 실행될 수 있다.


ManagedAgent: 멀티에이전트 위임

smolagents의 멀티에이전트 패턴은 단순하다. 서브에이전트를 ManagedAgent로 감싸면, 오케스트레이터 CodeAgent가 이를 Python 호출 가능한 도구처럼 사용할 수 있다.

from smolagents import CodeAgent, ManagedAgent, HfApiModel

model = HfApiModel("Qwen/Qwen2.5-Coder-32B-Instruct")

# 검색 전문 서브에이전트
search_agent = CodeAgent(tools=[web_search_tool], model=model)
managed_search = ManagedAgent(
    agent=search_agent,
    name="search_expert",
    description="인터넷에서 최신 정보를 검색한다. 입력: 검색 쿼리 문자열."
)

# 오케스트레이터: 서브에이전트를 Python에서 직접 호출
orchestrator = CodeAgent(
    tools=[managed_search],  # 서브에이전트가 도구로 등록됨
    model=model,
)

# 오케스트레이터가 생성하는 코드 예시
# result = search_expert("StarRocks 4.1 release notes")
# if "tablet" in result:
#     process_result(result)

JSON 도구 호출 에이전트와 달리, 오케스트레이터가 Python 제어 흐름으로 서브에이전트 호출을 조건화하거나 반복할 수 있다. 서브에이전트마다 독립적인 Executor를 설정할 수 있어, 보안이 더 엄격한 서브에이전트는 E2B로, 빠른 응답이 필요한 서브에이전트는 Local로 구분하는 것도 가능하다.


운영자 체크리스트

배포 전 필수 확인

모델 선택

모델 특성CodeAgent 적합도
Python 코드 생성에 fine-tune됨높음 (예: Qwen2.5-Coder, DeepSeek-Coder)
범용 instruction 모델중간 (코드 품질이 가변적)
작은 모델(< 7B)낮음 (Python 문법 오류 빈번)

비용과 안정성 트레이드오프

CodeAgent는 ToolCallingAgent보다 LLM 호출 횟수가 적다. 하지만 생성된 코드가 실행 오류를 낼 경우 재시도가 발생한다. 재시도 한도(max_steps)를 낮게 설정하면 비용을 통제할 수 있으나, 복잡한 작업에서 미완으로 끝날 수 있다.


smolagents를 한 문장으로

smolagents는 LLM이 Python 코드를 직접 쓰게 함으로써 도구 호출의 합성 비용을 낮추는 미니멀한 에이전트 프레임워크다. v1.25에서 보안 경계를 명확히 했지만, 코드를 실행하는 에이전트의 위험은 Executor 선택에 달려 있다.

References