왜 코드를 직접 쓰는가
대부분의 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다. 여기서 두 구체 구현이 파생된다.
| CodeAgent | ToolCallingAgent | |
|---|---|---|
| 행동 표현 | Python 코드 스니펫 생성 | JSON 도구 호출 딕셔너리 생성 |
| 유연성 | 루프, 조건문, 다중 도구 호출 자유로움 | 단일 도구 호출 구조로 제한 |
| 안정성 | LLM이 유효한 Python을 생성해야 함 | 모델이 fine-tune된 형식과 일치 |
| 스텝 효율 | 30% 적은 LLM 호출 | 더 많은 왕복 필요 |
| 적합한 시나리오 | 복잡한 멀티스텝, 동적 오케스트레이션 | 간단한 단일 도구, 높은 안정성 요구 |
CodeAgent의 실행 흐름은 단순하다. agent.run(prompt) → LLM이 Python 코드 블록 생성 → 실행기(Executor)가 코드 실행 → 결과를 다음 LLM 호출의 맥락으로 추가 → 최종 답변 반환. 루프는 에이전트가 "최종 답변"(final_answer())을 생성하거나 최대 스텝 수에 도달할 때 종료된다.
실행 아키텍처: Executor 계층
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로 구분하는 것도 가능하다.
운영자 체크리스트
배포 전 필수 확인
- [ ] 사용자 입력이 에이전트 프롬프트에 포함되면 반드시 격리 Executor(Docker, E2B, Modal, Blaxel)를 사용한다.
- [ ] LocalPythonExecutor의
additional_authorized_imports목록을 최소화한다. 불필요한 모듈을 포함하면 공격 표면이 커진다. - [ ] v1.25 이전 버전이라면 원격 Executor의 바인딩 주소와 인증 설정을 수동으로 확인한다.
- [ ] DockerExecutor 사용 시 컨테이너에 호스트
/etc,/root, 비밀 파일 경로를 마운트하지 않는다.
모델 선택
| 모델 특성 | 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
- smolagents GitHub Repository: https://github.com/huggingface/smolagents
- smolagents v1.25.0 Release Notes (GitHub): https://github.com/huggingface/smolagents/releases/tag/v1.25.0
- smolagents v1.26.0 Release Notes (GitHub): https://github.com/huggingface/smolagents/releases/tag/v1.26.0
- smolagents on PyPI (latest version history): https://pypi.org/project/smolagents/
- Introducing smolagents: simple agents that write actions in code (HuggingFace Blog): https://huggingface.co/blog/smolagents
- Exploring the smolagents Library: CodeAgent and ToolCallingAgent (Medium): https://kargarisaac.medium.com/exploring-the-smolagents-library-a-deep-dive-into-multistepagent-codeagent-and-toolcallingagent-03482a6ea18c
- Shipping Agents That Think in Code (Cohorte Blog): https://cohorte.co/blog/shipping-agents-that-think-in-code-a-practical-opinionated-guide-to-hugging-face-smolagents
- Deploy SmolAgents on GPU Cloud (Spheron Blog, 2026): https://www.spheron.network/blog/deploy-smolagents-gpu-cloud/
- Secure Code Execution in AI Agents (Medium): https://medium.com/@saurabh-shukla/secure-code-execution-in-ai-agents-d2ad84cbec97