Blog
ai-agentdata-catalogarchitecturellm

표준 라이브러리만으로 만든 데이터 카탈로그 AI 에이전트 — 3가지 실행 모드 아키텍처

외부 의존성 0으로 만든 Argus Catalog AI 에이전트의 아키텍처를 해부합니다. 배치·폴링 데몬·어시스턴트 서버 세 가지 실행 모드, 에이전트를 분리한 이유, 그리고 의존성 없는 설계 철학을 정리합니다.

Data Dynamics2026年6月10日10 min read
This post is not yet translated. The original Korean version is shown below.

데이터 카탈로그에 AI를 붙이는 가장 손쉬운 방법은 API 서버 안에 LLM 호출 코드를 끼워 넣는 것입니다. 하지만 운영을 길게 보면 이 방식은 GPU 자원, 데이터 주권, 스케일 단위가 모두 API 서버에 묶여버리는 부채가 됩니다. Argus Catalog Agent는 AI 메타데이터 기능을 독립 에이전트로 떼어내고, 외부 패키지 의존성을 0개로 유지하는 길을 택했습니다.

이 글은 그 에이전트의 아키텍처를 해부하는 5부작 시리즈의 첫 편입니다. 시리즈는 아키텍처 → 모델/LLM → 도구(Function Calling) → 통신·표시(SSE) → 거버넌스 순으로 이어집니다.

1. 한 패키지, 세 가지 실행 모드

argus-agent는 하나의 Python 패키지지만 세 가지 얼굴을 가집니다.

┌──────────────────────────────────────────────────────────────────┐
│  argus-agent  (Docker 또는 native — 표준 라이브러리만 사용)           │
│                                                                   │
│  [배치]  describe/.../generate-all   [데몬]  worker (주기 폴링)      │
│     │  ① 컨텍스트 읽기 (스키마·샘플·용어집)                            │
│     ├──── REST (admin 계정) ────▶ Argus Catalog API                │
│     │  ③ 결과 반입 (제안 → UI 에서 사람 승인)                          │
│     │  ② 생성                                                      │
│     └──── OpenAI 호환 API ────▶ ollama (qwen2.5:7b) / vLLM / ...   │
│                                                                   │
│  [서버]  serve (:8930) — AI 어시스턴트 tool-use 채팅                  │
│     ▲ SSE (사용자 토큰 위임)                                         │
│     │                        ┌─ 도구 실행 (사용자 권한) ─▶ 카탈로그 API │
│  백엔드 /ai/assistant/chat    └─ tool-use 루프 ─▶ LLM               │
│  (assistant.agent.url 설정 시 프록시)                                │
└──────────────────────────────────────────────────────────────────┘
모드명령성격
배치(one-shot)describe / summarize / columns / tags / pii / generate-all지정한 데이터셋에 메타데이터를 한 번 생성하고 종료
폴링 데몬(worker)worker --datasource-id ...설명이 비어 있는 데이터셋을 주기적으로 찾아 자동 생성
어시스턴트 서버(serve)serve --port 8930사용자와 tool-use 채팅을 주고받는 SSE HTTP 서버

배치와 워커는 관리자 계정으로 카탈로그를 읽고 결과를 되돌리는 데이터 파이프라인이고, serve는 사용자 토큰을 위임받아 그 사람 권한으로만 동작하는 대화형 서버입니다. 같은 도구·같은 LLM 레이어를 공유하되 진입점과 인증 모델이 다른 셈입니다.

2. 모듈 구성

기능이 셋이라고 코드가 흩어지지는 않습니다. 9개 모듈이 각자 한 가지 역할만 맡습니다.

모듈역할
config.py설정 — CLI 인자 > 환경변수 > 기본값 우선순위
catalog.py카탈로그 REST 클라이언트 (배치용 — admin 계정)
llm.pyOpenAI 호환 LLM — generate(단발)·chat(tool-calling)·chat_stream(스트리밍)
prompts.py메타데이터 생성용 한국어 프롬프트 빌더
tasks.py배치 작업 5종 (describe/summarize/columns/tags/pii)
tools.py어시스턴트 도구 레지스트리 (OpenAI 스키마 + 실행 함수)
assistant.pytool-use 루프 (LLM ↔ 도구 반복, SSE 이벤트 생성)
server.pyserve 모드 HTTP 서버 (/chat SSE, /health)
telemetry.py셀프-텔레메트리 — 레지스트리 자기등록 + 호출 지표 push

llm.pytools.py는 세 모드가 모두 공유하는 코어이고, tasks.py·prompts.py는 배치/워커 전용, assistant.py·server.py·telemetry.py는 serve 전용입니다.

3. 왜 에이전트로 분리했나

AI 코드를 API 서버 밖으로 꺼내면서 얻는 효과는 네 가지로 정리됩니다.

관점효과
자원 분리LLM(GPU)을 API 서버와 다른 머신에서 — 서버는 가볍게 유지
데이터 주권로컬 모델 사용 시 스키마·샘플이 외부로 나가지 않음 (PII 감지에 특히 중요)
독립 스케일대량 일괄 생성 시 에이전트만 따로 확장
운영 일관성기존 quality/ 배치와 동일한 외장 패턴 (API 읽기 → 처리 → 반입)

특히 데이터 주권은 단순한 명분이 아닙니다. PII(개인정보) 컬럼을 감지하려면 실제 샘플 데이터를 모델에 보여줘야 하는데, 이걸 외부 SaaS LLM에 보내는 순간 컴플라이언스 문제가 생깁니다. 로컬 ollama 모델을 기본으로 둔 이유가 여기에 있습니다.

4. 의존성 0 — dependencies = []

이 에이전트의 가장 눈에 띄는 결정은 pyproject.toml의 이 한 줄입니다.

[project]
name = "argus-agent"
requires-python = ">=3.10"
dependencies = []

HTTP 통신은 requests가 아니라 표준 라이브러리 urllib로, 서버는 flask가 아니라 http.server로, JSON은 json으로 처리합니다. 외부 패키지가 하나도 없습니다. 이렇게 한 이유는:

  • 설치·배포 마찰 제거pip install이 필요 없고, 폐쇄망·에어갭 환경에서도 파이썬만 있으면 돈다
  • 보안 감사 표면 최소화 — 의존성 트리가 없으니 CVE 추적 대상도 없다
  • 버전 충돌 제로 — 다른 서비스와 같은 호스트에 올려도 패키지가 부딪히지 않는다

requirements.txt-e . 한 줄만 있는 건 패키지 자신을 설치해 argus-agent CLI를 PATH에 등록하기 위한 것일 뿐, 런타임 의존성을 끌어오지 않습니다.

5. 설정 우선순위 — Docker와 native를 동시에

배치는 보통 CLI 인자로, Docker는 환경변수(.env)로 설정을 주는 게 자연스럽습니다. config.py는 둘 다 만족시키기 위해 명확한 우선순위를 둡니다 — CLI 인자 > 환경변수 > 기본값.

def pick(cli_value, env_key: str, default: str) -> str:
    # CLI 인자가 명시됐으면 최우선, 없으면 환경변수, 그래도 없으면 기본값
    if cli_value is not None:
        return str(cli_value)
    return os.environ.get(env_key, default)

덕분에 docker compose에서는 .env만 채우면 되고, 로컬에서 빠르게 테스트할 때는 --model·--api-url 같은 인자로 즉석에서 덮어쓸 수 있습니다.

6. Docker Compose 한 장으로 기동

운영 묶음은 ollama(모델 서버) + agent(배치) + assistant(serve) + worker(데몬)로 구성됩니다.

services:
  ollama:        # qwen2.5:7b 모델 서버
  ollama-pull:   # 최초 1회 모델 pull
  agent:         # 배치 — describe/generate-all 실행
  assistant:     # serve :8930 — 채팅 서버
  worker:        # 폴링 데몬 — 설명 없는 데이터셋 자동 생성
docker compose up -d ollama          # 모델 자동 pull (최초 1회 수 분)
docker compose run --rm agent describe --urn <URN>
docker compose up -d assistant worker

카탈로그 API가 호스트에서 돌고 있으면 host.docker.internal로 접근하도록 기본값이 잡혀 있어, .env 한 줄로 환경을 바꿀 수 있습니다.

마치며

핵심은 분리무의존입니다. AI 기능을 독립 에이전트로 떼어내 자원·데이터·스케일을 분리했고, 표준 라이브러리만으로 구현해 어디서든 마찰 없이 돌게 만들었습니다. 같은 코어(LLM 레이어·도구)를 세 가지 실행 모드가 공유하는 구조 덕분에, 배치 파이프라인과 대화형 어시스턴트가 한 패키지 안에 자연스럽게 공존합니다.

다음 편에서는 이 아키텍처의 심장인 LLM 레이어를 다룹니다 — provider 분기 코드 없이 ollama·vLLM·OpenAI를 하나의 클라이언트로 통합한 방법과, 7B 소형 모델을 안정적으로 다루는 기법을 살펴봅니다.