Hugging Face의 smolagents를 사용하여 SQL 데이터베이스와 "대화"하는 AI 에이전트 구축하기
요약
Hugging Face의 smolagents 라이브러리를 활용하여 자연어로 SQL 데이터베이스와 상호작용하는 Text-to-SQL AI 에이전트 구축 방법을 소개합니다. 단순한 LLM 파이프라인의 한계를 넘어, 코드를 직접 작성하고 실행하며 오류를 스스로 수정하는 에이전트 방식의 장점을 설명합니다.
핵심 포인트
- 단순 LLM 호출 방식은 의미론적 오류에 취약함
- smolagents의 CodeAgent는 Python 코드를 직접 작성하여 실행함
- 생각-행동-관찰-반복 사이클을 통해 자기 수정(Autocorrective) 가능
- Text-to-SQL 작업에서 에이전트 방식이 더 높은 신뢰성을 제공함
서론
SQL을 직접 작성하는 것은 의도치 않게 많은 사람을 데이터로부터 소외시키는 기술 중 하나입니다. 프로덕트 매니저(Product Manager), 지원 에이전트, 심지어 주니어 개발자들조차 데이터베이스에 무엇을 물어보고 싶은지는 정확히 알지만, 이를 SQL로 어떻게 표현해야 하는지는 모르는 경우가 많습니다. 이것이 바로 Text-to-SQL 시스템이 해결하고자 하는 문제입니다. 즉, 자연어(영어 또는 스페인어)로 질문을 작성하면 실제 데이터베이스에서 실제 데이터를 얻어내는 것입니다.
이 글에서는 Hugging Face의 smolagents 라이브러리를 사용하여 작지만 완전한 Text-to-SQL AI 에이전트를 구축하고, 왜 _에이전트(Agent)_가 "LLM이 SQL을 작성하고 우리가 실행한다"는 단순한 파이프라인(Pipeline)보다 더 신뢰할 수 있는지 설명하며, 실제 코드를 살펴보고, 이러한 시스템을 프로덕션(Production) 데이터베이스에 연결하기 전에 고려해야 할 보안 사항들을 다룹니다.
이 프로젝트의 전체 소스 코드는 이 글의 끝에 링크된 공개 저장소(Repository)에서 확인할 수 있습니다.
왜 단순히 LLM에게 SQL을 요청하면 안 될까요?
가장 단순한 형태의 Text-to-SQL 파이프라인은 다음과 같습니다:
- 데이터베이스 스키마(Schema) + 사용자의 질문을 LLM에 전송합니다.
- LLM에게 SQL 쿼리(Query)를 반환하도록 요청합니다.
- 해당 쿼리를 실행하고 결과를 보여줍니다.
이 방식은... 작동을 멈출 때까지는 잘 작동합니다. 문제는 단 한 번의 LLM 호출은 **취약(Fragile)**하다는 점입니다. 모델은 구문론적으로는 유효하지만 의미론적으로는 잘못된 쿼리(잘못된 조인(Join), 잘못된 필터, 잘못된 집계 등)를 생성할 수 있으며, 그럼에도 불구하고 어떠한 결과는 반환할 것입니다. 단지 그것이 정확한 결과가 아닐 뿐입니다. 응답을 검증하거나 다시 시도할 수 있는 내장된 메커니즘이 없습니다.
이것이 바로 smolagents 문서가 시작하며 던지는 바로 그 "황금 질문"입니다. 왜 단일 프롬프트(single prompt)로 간단하게 유지하지 않는가? 그 이유는 추론하고, 도구(tool)를 호출하며, 결과를 관찰하고, 다시 시도해야 할지 결정할 수 있는 능력인 **에이전트 (agent)**가 이러한 유형의 작업에 본질적으로 더 강력하기 때문입니다.
smolagents란 무엇이며 "코드로 생각하기"는 무엇을 의미하는가?
smolagents는 Hugging Face의 가벼운 에이전트 프레임워크로, 유지 관리자들은 이를 "코드로 생각하는 에이전트를 위한 미니멀리스트 라이브러리"라고 설명합니다. 모델에게 경직된 JSON 형식으로 도구 호출(tool call)을 생성하도록 강제하는 대신, CodeAgent는 추론 사이클의 일부로 실제 Python 코드를 작성하고 실행합니다:
- 생각하기 (Think) — 모델이 다음에 무엇을 할지 추론합니다.
- 행동하기 (Act) — 제공된 도구 중 하나(이 경우에는 SQL 실행 도구)를 호출하는 Python 코드 조각을 작성합니다.
- 관찰하기 (Observe) — 해당 코드의 결과(쿼리 출력값)가 모델의 컨텍스트로 다시 통합됩니다.
- 반복 또는 응답하기 (Repeat or Respond) — 에이전트가 응답하기에 충분한 정보가 있는지, 아니면 쿼리를 수정하여 다시 시도해야 하는지 결정합니다.
이 사이클이 에이전트를 자기 수정(autocorrective) 가능하게 만드는 핵심입니다. 만약 쿼리가 의심스러운 결과(빈 결과, 명백히 잘못된 숫자 등)를 반환하면, 모델은 잘못된 답변을 자신 있게 내놓는 대신 쿼리를 다시 작성할 수 있습니다.
프로젝트 구축하기: "영수증"을 위한 작은 SQLite 데이터베이스
예제를 쉽게 유지하기 위해, smolagents의 공식 Text-to-SQL 예제에서 사용된 것과 동일한 구조를 가진 두 개의 작은 테이블이 포함된 인메모리(in-memory) SQLite 데이터베이스를 사용합니다.
1. 데이터베이스 설정하기
from sqlalchemy import (
create_engine, MetaData, Table, Column,
String, Integer, Float, insert, inspect, text
...
2. 에이전트를 위한 스키마(schema) 기술하기
에이전트는 유용한 SQL을 작성하기 전에 어떤 컬럼들이 존재하는지 알아야 합니다. 우리는 이를 수동으로 작성하는 대신 동적으로 생성합니다:
inspector = inspect(engine)
columns_info = [(col["name"], col["type"]) for col in inspector.get_columns("receipts")]
...
3. SQL 도구 (tool) 정의하기
이것이 핵심 요소입니다. @tool 데코레이터가 붙은 단일 Python 함수로, 에이전트가 호출할 수 있도록 허용됩니다. 이 함수의 docstring(독스트링)은 모델이 이 도구를 어떻게, 언제 사용할지 이해하기 위해 읽는 문서 _그 자체_이므로, 매우 정확해야 합니다.
from smolagents import tool
@tool
...
4. 에이전트 생성하기
from smolagents import CodeAgent, InferenceClientModel
agent = CodeAgent(
...
내부적으로 에이전트는 다음과 유사한 코드를 작성합니다:
result = sql_engine(query="SELECT customer_name FROM receipts ORDER BY price DESC LIMIT 1")
print(result)
...결과를 관찰한 뒤, 그제서야 자연어로 된 응답을 생성합니다: "Woodrow Wilson이 $53.43로 가장 비싼 영수증을 보유하고 있습니다."
5. 확장하기: 두 테이블 간의 조인 (join)
두 번째 테이블(waiters)을 도입하면, 도구의 docstring을 확장하여 두 테이블을 모두 설명하고 더 강력한 추론 모델로 변경하기만 하면 됩니다:
updated_description = """테이블에 대한 SQL 쿼리를 수행할 수 있습니다. 이 도구의 출력은 실행 결과의 텍스트 표현임을 유의하세요.
다음 테이블들을 사용할 수 있습니다:"""
...
이제 에이전트는 우리가 SQL을 수동으로 작성하지 않아도 receipts와 waiters 사이의 조인(join)에 대해 추론하고, 이를 실행하며, 결과를 해석해야 합니다.
LLM에 직접 전달하는 프롬프트(Prompt) vs. 자신의 작업을 검증하는 에이전트
| LLM 직접 전달 → SQL | smolagents를 사용한 에이전트 | |
|---|---|---|
| 쿼리 생성 | 피드백 없는 단일 시도 | 반복적이며, 재시도 가능 |
| ... |
핵심 결론: 에이전트가 오류를 완전히 없애주는 것은 아니지만, 시스템이 잘못된 숫자를 자신 있게 반환하는 대신 오류를 _감지(notar)_하고 그에 _반응(reaccionar)_할 수 있는 기회를 제공합니다.
보안: 이 섹션을 건너뛰지 마세요
AI 모델이 실제 데이터베이스에 대해 SQL을 생성하고 실행하도록 허용하는 것은 강력하지만, 통제되지 않은 상태로 방치하면 위험합니다. 이러한 시스템을 출시하기 전에 반드시 다음 사항을 처리해야 합니다:
-
읽기 전용 액세스 (Read-only access). 도구는 오직
SELECT문만 실행할 수 있어야 합니다.DROP,DELETE,UPDATE,INSERT,ALTER와 같은 명령은 프롬프트에서 -
분석 대시보드 (Dashboards de analítica) — 가능한 모든 질문에 대해 각각의 그래프를 만드는 대신, 사용자가 "지난주 지역별 신규 레코드는 몇 개인가요?"라고 질문하면 즉석에서 집계 쿼리 (aggregation query)를 생성하도록 합니다.
-
고객 지원 도구 (Herramientas de soporte al cliente) — 지원 에이전트는 티켓 시스템의 스키마 (schema)를 알 필요 없이 "이 사용자의 최근 티켓 5개를 보여줘"라고 요청할 수 있습니다.
-
내부 비즈니스 인텔리전스 (Inteligencia de negocio interna) — 기술적 지식이 없는 사람들도 에이전트가 조인 (join)과 집계 (aggregation)를 처리하도록 하여 판매, 재고 또는 사용 데이터를 대화형으로 탐색할 수 있습니다.
-
개발자 도구 (Herramientas para desarrolladores) — 일회성 SQL 스크립트를 수동으로 작성할 필요 없이 디버깅 (debugging) 중에 데이터를 빠르게 탐색할 수 있습니다.
결론
Text-to-SQL은 "단순히 LLM을 호출하는 것"만으로는 충분하지 않은 사례의 훌륭한 예시입니다. 단일 시도 프롬프트 (single-attempt prompt)와 진정한 에이전트 루프 (agent cycle) 사이의 차이는 데모 수준과 실제 데이터를 믿고 맡길 수 있는 수준 사이의 차이와 같습니다. smolagents의 "코드 우선 (code-first)" 접근 방식은 해당 루프를 투명하고 검사 가능하게 만들며, 적절한 보호 조치가 있다면 진정으로 프로덕션 (production) 환경에서 실행 가능하게 합니다.
저장소 (Repositorio): 이 프로젝트의 완전하고 기능적인 코드(데이터베이스 설정, 도구 정의 및 에이전트 설정)는 여기에서 확인할 수 있습니다: github.com/iovargasjeff/text-to-sql-smolagents-demo
시연 영상 (Video demostrativo): 예제 데이터베이스에 대해 자연어로 질문에 답하는 에이전트를 보여주는 짧은 영상이 위에 링크되어 있습니다.
참고 문헌: Hugging Face smolagents 문서 – Text-to-SQL, smolagents GitHub 저장소
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기