JSON 파싱 삽질은 이제 그만합시다. 1000줄의 코드에 담긴 Hugging Face 'Smolagents'의 뼈 때리는 반격
“LLM이 생성한 JSON 형식이 깨졌습니다.”
현업에서 에이전트(Agent) 기반 시스템을 운영해 본 개발자라면 이 에러 로그 하나에 얼마나 많은 주말을 날렸는지 아실 겁니다. LangChain이나 AutoGen 같은 거대한 프레임워크를 도입해서 멋지게 ‘자율형 AI’를 구축했다고 생각했는데, 현실은 어떤가요? 모델이 뱉어낸 기형적인 JSON 텍스트를 파싱하느라 정규식(Regex)을 떡칠하고, 툭하면 발생하는 무한 루프를 막기 위해 예외 처리에만 수백 줄을 할애하고 있지 않나요?
솔직히 말씀드리면, 기존의 ‘Tool Calling’ 아키텍처는 구조적으로 심각한 결함을 안고 있습니다. LLM에게 “네가 쓸 도구의 이름과 파라미터를 JSON으로 예쁘게 포장해서 줘”라고 강요하는 방식 자체가 너무 부자연스럽기 때문이죠. 그런 와중에 Hugging Face에서 툭 던져놓은 하나의 라이브러리가 제 시선을 완전히 사로잡았습니다. 프레임워크의 추상화를 극한으로 걷어내고, 고작 1,000줄 남짓한 코드로 에이전트의 본질을 재정의한 Smolagents입니다. 요즘 해외 엔지니어들 사이에서 “이게 진짜 에이전트지”라며 난리가 났는데, 과연 이 녀석이 우리의 고질적인 문제들을 어떻게 박살 내는지 그 밑바닥을 뜯어보겠습니다.
TL;DR: The Core
Smolagents의 핵심 패러다임 변화: LLM이 행동을 JSON으로 선언(Declarative)하게 만드는 대신, 실제 Python 코드를 작성하여 직접 실행(Imperative)하게 만듭니다. 복잡한 추상화를 걷어낸 이 1,000줄짜리 초경량 프레임워크는 에이전트 아키텍처를 ‘도구 호출(Tool Calling)’에서 ‘코드 실행(Code Execution)’의 시대로 강제로 견인하고 있습니다.
Deep Dive: Under the Hood
Smolagents의 진가는 그 이름처럼 ‘작다(Smol)’는 데서 나오지 않습니다. 이 프레임워크가 무서운 이유는 아키텍처의 근본적인 접근 방식을 뒤집어버렸다는 점입니다.
기존 프레임워크(OpenAI의 Function Calling이나 LangChain 기반)들은 대부분 ToolCallingAgent 구조를 따릅니다. 모델이 어떤 행동을 할지 JSON 블롭(Blob)으로 내뱉으면, 프레임워크가 이를 파싱해서 실제 함수에 매핑해 주죠. 반면 Smolagents는 CodeAgent라는 개념을 1급 시민(First-class citizen)으로 밀어붙입니다. LLM이 행동을 파이썬 코드로 직접 작성하게 놔두고, 이를 샌드박스 환경에서 곧바로 실행해 버리는 겁니다.
왜 이게 그렇게 중요할까요? 아래 표를 통해 아키텍처적 차이를 극명하게 확인해 보시죠.
| 구분 | JSON 기반 Tool Calling (기존) | Python 코드 기반 CodeAgent (Smolagents) |
|---|---|---|
| 제어 흐름 | LLM → JSON 생성 → 파서 → 함수 실행 → LLM 복귀 | LLM → Python 코드 작성 (루프, 조건문 포함) → 즉시 실행 |
| 복합 작업 | 여러 도구를 쓰려면 LLM과 서버 간 왕복(Round-trip) 호출 다수 발생 | for, if문 등 Python 자체의 제어 구조를 통해 단 한 번에 처리 |
| 오류 처리 | JSON 문법 오류, 스키마 불일치 등 프레임워크 단의 파싱 에러 다발 | 표준 Python 런타임 에러(Traceback) 발생 → LLM이 스스로 디버깅 가능 |
| 추상화 두께 | 매우 두꺼움 (무거운 프레임워크 종속성, 블랙박스화) | 매우 얇음 (1,000줄 남짓의 투명한 코어 로직) |
LLM은 방대한 텍스트와 더불어 GitHub의 수많은 코드를 학습했습니다. 즉, LLM에게 있어 “어떤 함수의 인자를 JSON 스키마에 맞게 끼워 맞추는 것”보다 “파이썬의 for 루프와 if-else 문을 활용해 자연스러운 로직을 짜는 것”이 훨씬 더 본질적이고 능숙한 행위입니다.
백문이 불여일견이죠. Smolagents가 내부적으로 어떻게 도구를 엮어내는지 코드로 직접 살펴봅시다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from smolagents import CodeAgent, HfApiModel, tool
# 1. 매우 직관적인 도구(Tool) 정의 - 데코레이터 하나면 끝납니다.
@tool
def fetch_user_data(user_id: int) -> dict:
"""
Fetch user details from the legacy database.
Args:
user_id: The unique identifier of the user.
"""
# 실제 실무라면 여기에 Spring/Node.js 백엔드 API 호출 로직이 들어갑니다.
return {"user_id": user_id, "status": "active", "tier": "premium"}
@tool
def calculate_discount(tier: str, base_price: float) -> float:
"""
Calculate the discount based on user tier.
Args:
tier: User's membership tier.
base_price: The original price of the item.
"""
discount_rates = {"premium": 0.2, "standard": 0.05}
return base_price * (1 - discount_rates.get(tier, 0))
# 2. CodeAgent 초기화 및 샌드박스 설정
# 보안을 위해 E2B나 Modal 같은 샌드박스 환경 내에서 코드를 실행할 수 있습니다.
agent = CodeAgent(
tools=[fetch_user_data, calculate_discount],
model=HfApiModel("meta-llama/Llama-3.3-70B-Instruct"),
additional_authorized_imports=["datetime", "math"] # 허용할 패키지 명시
)
# 3. 에이전트 실행
response = agent.run("유저 ID 1042의 정보를 가져오고, 50,000원짜리 상품에 대한 최종 할인가를 계산해 줘.")
print(response)
이 코드를 실행할 때 내부에서 일어나는 일이 예술입니다. 기존 방식이라면 1) fetch_user_data JSON 호출 2) 결과값 반환 3) calculate_discount JSON 호출 4) 결과값 반환… 이런 식으로 네트워크 핑퐁이 일어났겠죠. 하지만 Smolagents의 CodeAgent는 다음과 같은 단일 파이썬 스크립트를 내부적으로 생성해 버립니다.
1
2
3
4
5
# LLM이 스스로 작성하고 실행하는 내부 코드 스니펫 예시
user_info = fetch_user_data(user_id=1042)
if user_info["status"] == "active":
final_price = calculate_discount(tier=user_info["tier"], base_price=50000)
print(f"최종 가격은 {final_price}원입니다.")
네트워크 통신 비용 감소, 토큰 절약, 그리고 무엇보다 조건문(if)을 통한 논리적 분기 처리를 에이전트가 단 한 번의 추론으로 끝내버립니다. 프레임워크의 두꺼운 추상화를 벗겨내고, Python이라는 가장 강력하고 튜링 완전(Turing-complete)한 언어를 에이전트의 제어어로 격상시킨 것. 이것이 Smolagents가 보여주는 아키텍처의 핵심입니다.
Pragmatic Use Cases
그렇다면 실무 기획자와 개발자 관점에서 이 녀석을 어디에 투입해야 가장 파괴적인 효율을 낼 수 있을까요? 뻔한 장난감 예제 말고, 실제 인프라 환경을 가정해 보겠습니다.
대규모 데이터 파이프라인의 동적 제어 (Dynamic ETL) 데이터 엔지니어링 파트에서 수백 개의 API 엔드포인트에서 데이터를 긁어와야 하는 상황을 떠올려 보죠. 기존 JSON 기반 에이전트는 페이지네이션(Pagination) 처리를 위해 “페이지 넘기는 도구 호출 -> 결과 확인 -> 또 호출” 이라는 바보 같은 루프를 반복해야 했습니다. 토큰 비용이 눈덩이처럼 불어나죠. Smolagents를 사용하면 LLM이
while루프가 포함된 Python 코드를 작성하도록 유도할 수 있습니다. 에이전트에게 “API 끝에 도달할 때까지 계속 가져와서 결과를 요약해”라고 지시하면, LLM은 스스로 루프 로직을 짜서 한 번의 샌드박스 실행으로 모든 데이터를 추출하고 요약본만 텍스트로 반환합니다. 트래픽 스파이크나 지연 시간(Latency) 문제를 극적으로 줄일 수 있는 대목이죠.기존 레거시 백엔드(Spring, Node.js)와의 마찰 없는 결합 현업에서는 이미 수많은 비즈니스 로직이 Spring Boot나 Node.js 기반의 MSA로 쪼개져 있습니다. AI를 도입하겠다고 이 로직을 다 파이썬으로 포팅할 수는 없는 노릇입니다. Smolagents의 철저한 ‘Tool-agnostic(도구 불가지론)’ 특성을 활용하면, 각 마이크로서비스를 호출하는 얇은 Python 래퍼(Wrapper) 함수들만 만들어
@tool로 등록해주면 됩니다. 에이전트는 내부적으로 이 래퍼들을 조합하여 복잡한 비즈니스 워크플로우(예: “재고 확인 후 -> 결제 API 찌르고 -> 실패 시 롤백 API 호출”)를 하나의 트랜잭션처럼 묶어내는 코드를 생성합니다. 사실상 지능형 API 오케스트레이터(Orchestrator) 역할을 수행하게 되는 셈이죠.극단적인 보안 격리가 필요한 사내 환경 ‘LLM이 짠 코드를 내 서버에서 돌린다고?’ 시니어 개발자라면 여기서 뒷목을 잡아야 정상입니다.
os.system("rm -rf /")라도 실행하면 어떡할 건가요? Smolagents는 이 문제를 회피하지 않고 정면 돌파합니다. Local Python Interpreter 모드에서는 허용된 모듈(예:requests,pandas) 외의 import를 원천 차단하고, 연산 횟수를 제한하여 무한 루프(CPU 고갈)를 막습니다. 더 나아가 E2B, Modal, Docker 같은 클라우드 샌드박스 기술과 네이티브로 연동됩니다. “코드를 믿지 말고 샌드박스를 믿어라”라는 현업 보안의 대원칙을 프레임워크 단에서 강제하는 것이죠.
Honest Review & Trade-offs
아무리 찬양받는 기술도 은총알(Silver Bullet)은 아닙니다. 실제로 제가 Smolagents를 테스트베드에 올려놓고 굴려보면서 느낀 치명적인 단점과 트레이드오프는 다음과 같습니다.
- 스몰 모델(SLM)에서는 폭망할 확률이 높다:
CodeAgent의 가장 큰 딜레마입니다. JSON 스키마를 채워 넣는 것은 8B, 14B 수준의 작은 오픈소스 모델들도 꽤 잘합니다. 하지만 “작동하고 문법에 맞는 Python 코드”를 백지상태에서 작성하는 것은 차원이 다른 지능을 요구합니다. Llama 3나 Claude 3.5 Sonnet, GPT-4o 같은 헤비급 모델이 아니면, 에이전트가IndentationError나 변수명 오타 같은 어처구니없는 에러를 뱉어내며 자멸하는 꼴을 보게 될 겁니다. - 디버깅의 패러다임 전환이 주는 고통: 에러가 발생하면, 문제는 ‘우리가 작성한 시스템 로직’에 있는 게 아니라 ‘LLM이 런타임에 동적으로 짜낸 코드’에 있습니다. Traceback 로그를 까봐도 그 코드는 디스크에 존재하는 파일이 아니라 메모리상에서 잠시 살다 간 유령 같은 녀석입니다. 물론 LLM이 에러를 읽고 자가 수정(Self-correction)을 시도하긴 하지만, 프롬프트 엔지니어링만으로 이 디버깅 과정을 통제하는 건 실무에서 상당한 스트레스를 유발합니다.
- 보안 인프라 벤더 락인(Vendor Lock-in) 리스크: E2B나 Modal 같은 외부 샌드박스 서비스에 과도하게 의존하게 될 위험이 있습니다. 사내 폐쇄망(On-premise) 환경에서 이 정도 수준의 안전한 코드 실행 격리 환경(Pyodide, Deno 등)을 자체 구축하는 것은, 고작 1,000줄짜리 프레임워크를 쓰기 위해 배보다 배꼽이 커지는 인프라 공사를 의미할 수도 있습니다.
Closing Thoughts
솔직히 처음 이 아키텍처를 봤을 땐 의구심이 들었습니다. “다시 코드를 직접 실행하게 한다고? 과거로의 회귀 아닌가?” 하지만 코드를 뜯어보고 직접 레거시 API들과 엮어보면서 깨달았습니다. 우리는 그동안 AI에게 너무 많은 ‘인간의 규격(JSON)’을 강요하고 있었던 겁니다.
Smolagents는 무거운 추상화 계층(Abstraction Layer)으로 비즈니스를 하는 기존 AI 프레임워크 시장에 던지는 Hugging Face의 통렬한 일침입니다. “본질은 거대한 프레임워크가 아니라, 강력한 모델과 그 모델이 노는 가장 자연스러운 언어(Code)다”라고 말하고 있죠.
이 기술이 당장 내일 여러분의 모든 시스템을 대체하진 않겠지만, 적어도 ‘에이전트를 설계하는 방식’에 대한 우리의 굳은 사고방식을 산산조각 내기엔 충분합니다. 매번 JSON 파싱 에러와 사투를 벌이며 프레임워크의 숨겨진 코드를 뒤적이고 계시다면, 이번 주말엔 단 1,000줄의 코드로 구현된 이 날것의 지능을 한 번 맛보시길 권합니다. 어쩌면 우리가 찾던 자율형 AI의 미래는, 화려한 UI나 복잡한 다이어그램이 아니라 터미널 위에서 묵묵히 for 루프를 돌고 있는 파이썬 스크립트 안에 있을지도 모르니까요.
References
- https://huggingface.co/docs/smolagents
- https://github.com/huggingface/smolagents
- https://www.deeplearning.ai/short-courses/building-code-agents-with-hugging-face-smolagents
- https://medium.com/@zennura26/exploring-smolagents-building-intelligent-agents-with-hugging-face-c45de65373aa
