
AI 에이전트를 위한 LLM 및 프레임워크를 선택하는 방법
요약
PostgreSQL 기반의 AI 에이전트를 구축하며 겪은 인프라와 기술 스택의 진화 과정을 다룹니다. 단순 RAG에서 시작하여 MCP(Model Context Protocol)를 활용한 ReAct 에이전트로 발전하는 과정과 컨텍스트 관리의 중요성을 설명합니다.
핵심 포인트
- 단순 RAG에서 MCP 기반의 도구 활용 에이전트로의 진화
- BGE-M3 임베딩과 희소 벡터를 결합한 하이브리드 검색 전략
- 모델 성능보다 컨텍스트 관리와 에이전트 아키텍처에 집중
- PostgreSQL 및 pgvector를 활용한 데이터 인덱싱 및 관리
지난 18개월 동안 저희 ML 팀은 매우 흥미로운 일들을 해왔습니다. 인프라가 진화하고 산업이 성숙해지며 품질에 대한 기대치가 계속 높아지는 가운데, PostgreSQL 기반의 AI 에이전트 (AI agents)를 구축해 왔습니다. 저희는 관리형 클라우드(managed cloud)의 단일 A100으로 시작하여 "RAG를 연결해 보자"와 같은 상당히 겸손한 작업부터 시작했습니다. 오늘날 저희는 프로덕션 어시스턴트 (production assistant)를 동시에 지원하고, 분석가 에이전트 (analyst agents)를 구축하며, 실험을 수행하고, 에이전트를 위한 자체 벤치마크 (benchmarks)를 생성하며, Qwen3-235B를 실행하고 더 작은 모델들에 대해 본격적인 파인튜닝 (fine-tuning) 작업을 수행할 수 있는 8x H200 서버로 이동할 준비를 하고 있습니다.
이 글은 저희가 해결하는 과제들, 하드웨어, 모델, 그리고 프레임워크 (frameworks)가 어떻게 진화했는지, 그리고 왜 저희가 어떤 모델이 "최고"인지에 대해서는 훨씬 덜 신경 쓰게 된 대신 컨텍스트 관리 (context management)와 에이전트 아키텍처 (agent architecture)에 훨씬 더 집중하게 되었는지에 대해 다룹니다. 기술된 모든 내용은 2025년 말에서 2026년 초 기준의 최신 정보입니다.
우리의 스택을 형성한 과제들
단일 LLM 인프라를 중심으로 일련의 애플리케이션들이 성장해 왔습니다. 이를 구체화하기 위해, 주요 방향은 다음과 같습니다:
-
문서 및 기타 소스에 대한 클래식 RAG 퓨전 (Classic RAG Fusion over documentation and other sources)
우리는 내부 문서, 기사, Q&A 쌍, SQL 예제, 도서 및 기타 텍스트를 pgvector에 인덱싱합니다. 임베딩 (Embeddings)을 위해 BGE-M3를 사용하며, BM25를 사용하는 대신 희소 벡터 (Sparse vectors)를 사용합니다. 즉, 희소 텍스트 표현 (Sparse text representations)을 구축하고 밀집 벡터 (Dense vectors)를 계산한 다음, 곱셈, 랭킹 (Ranking), 할인 (Discounting) 및 추가적인 휴리스틱 (Heuristics)을 통해 모든 것을 결합합니다. 일부 비정형 소스 (Unstructured-source) 처리는 LLM에 위임됩니다. -
웹 검색 및 SQL 실행기를 갖춘 MCP 에이전트로의 RAG 진화 (Evolving RAG into an MCP agent with web search and SQL executor)
클래식한 문서 채팅만으로는 충분하지 않게 되었습니다. 사용자들은 명확한 역할 모델 (Role model)과 다양한 도구 (Tools)를 갖춘 완전한 어시스턴트를 필요로 합니다. 따라서 RAG는 모델 컨텍스트 프로토콜 (Model Context Protocol, MCP)을 통해 PostgreSQL과 통신하고, 웹 검색을 호출하며, 하나의 추론 체인 (Reasoning chain) 내에서 소스들을 결합할 수 있는 ReAct 에이전트로 성장했습니다. -
분석을 위한 AI 에이전트 (An AI agent for analytics)
이는 분석 데이터에 대한 인터페이스입니다. 에이전트는 "지난 1년간 지역별 매출을 보여줘"와 같은 질문을 이해하고, SQL을 생성하며, 결과를 설명하고, 이전 단계로 돌아가거나 차트를 생성하며, 점진적으로 쿼리 복잡도를 높일 수 있습니다. -
대규모 C 코드베이스에 대한 그래프-RAG 에이전트 (AI 코파일럿) (Graph-RAG agent over a large C codebase (AI copilot))
PostgreSQL 코어 및 관련 프로젝트는 수백만 줄의 C 언어로 구성되어 있습니다. 우리는 Apache AGE에서 지식 그래프 (Knowledge graph)를 구축합니다. 정점 (Vertices)은 디렉토리, 파일, 함수, 구조체 (Structs), 매크로 (Macros), 변수 (Variables)이며, 간선 (Edges)은 의존성 (Dependencies)과 호출 (Calls)입니다. 에이전트는 동작이 어디에 구현되어 있는지, 어떤 변경이 문제를 일으킬 수 있는지에 대한 질문에 답하며, 원시 텍스트 검색 대신 지식 베이스로서 코드를 탐색할 수 있도록 돕습니다. -
SQL 생성기 (SQL generator)
우리가 EX/EM 지표 및 Spider/BIRD와 같은 벤치마크와 자체 스키마 (Schemas)를 사용하여 훈련하고 평가하는 완전한 텍스트-투-SQL (Text-to-SQL) 파이프라인입니다. 훈련을 위해 GRPO/SFT를 사용하여 Qwen-0.6B를 사용했습니다. 또한 순수 SFT를 통해 Qwen2.5-14B 주변의 모델들을 QLoRA 파인튜닝 (Fine-tuning)하는 실험도 진행했습니다. -
PostgreSQL을 위한 힌트 세트 생성기 (Hint set generator for PostgreSQL)
여기서의 핵심은 pg_hint_plan 확장 기능 (Extension)입니다.
모델은 쿼리(Query), 통계(Stats), 인덱스(Indexes)를 확인하고 힌트 세트(Hint-set) 형식으로 힌트를 제안합니다. 우리는 이를 GRPO로 학습시키며, pg_hint_plan이 이를 파싱(Parsing)하고 적용할 수 있도록 정형 문법(Formal grammar)으로 출력을 제한합니다.
-
DSL로부터 DB 스키마 및 비즈니스 로직 생성기
우리는 엔티티(Entities)뿐만 아니라 스키마 변이(Schema mutations)를 위한 트랜잭션 스키마를 기술하는 DSL (도메인 특화 언어, Domain-specific language)을 사용합니다. 스키마는 JTD (JSON Typedef) 내에서 계층적 데이터 구조를 가진 애그리거트(Aggregates)로 표현됩니다. 에이전트는 JTD 명세(Specification)를 따르면서 테이블, 관계, 트랜잭션 래퍼(Transaction wrappers), 그리고 비즈니스 로직 파편(Business-logic fragments)을 생성합니다. -
테스트 데이터 생성기
내부적으로는 미묘한 차이가 있는 "단순한" INSERT 생성기입니다. 에이전트는 그럴듯한 값 목록(도시, 이름, 주소)을 가져오고, 테이블 간의 관계를 고려하며, 쉬운 검증과 재현(Replay)을 위해 정형 문법(Formal grammar)으로 출력을 제한합니다. -
지원 티켓(Support-ticket) 요약 및 구조화
목표는 단순히 대화를 다시 말하는 것이 아니라 이를 구조화하는 것입니다: 누가 무엇을 했는지, 어떤 가설이 테스트되었는지, 어떤 명령어가 실행되었는지, 그리고 최종 결과가 무엇이었는지를 구조화합니다. 에이전트는 또한 기존 라벨(Labels)에 따라 티켓을 분류합니다. -
의심스러운 파편을 찾기 위한 대규모 코드베이스 스캐닝
LLM은 대규모 코드 코퍼스(Code corpora)를 거르며, 사전 정의된 기준에 따라 의심스러워 보이는 파편들을 플래그(Flag)로 표시합니다. 이는 수동 감사(Manual audits)를 대체하는 것이 아니라, 감사의 속도를 높이기 위한 도구입니다. -
자체 에이전트 벤치마크
우리는 "테스트되는 에이전트 + 테스트하는 에이전트 + 검증기(Validator)"라는 체계에 도달했습니다. 첫 번째는 도구를 사용하여 실제 운영 환경에서와 같이 작업을 해결합니다. 두 번째는 사용자의 역할을 수행하며, 제한된 컨텍스트(Context) 하에서 질문하고 명확하게 확인합니다. 세 번째(검증기)는 대화를 분석하여 목표가 달성되었는지, 테스트 조건이 충족되었는지를 결정합니다. 이는 모델의 IQ가 아니라 시스템의 동작을 테스트하는 것입니다.
메모리 및 형식 제약 연구 (Memory and formal constraint research)
동시에 우리는 에이전트의 단기 및 장기 메모리, 컨텍스트 관리 (단순한 윈도우 방식부터 요약(summarization), 마스킹(masking), 그래프(graphs)까지), 그리고 형식 문법(formal grammars)과 엄격한 구조화된 출력(structured output)이 품질, 속도 및 안정성에 어떤 영향을 미치는지 탐구합니다.
기사 전반에 걸쳐 저는 이러한 작업들을 계속해서 언급할 것입니다. 이것이 바로 우리의 하드웨어, 모델, 그리고 프레임워크를 움직인 동력이었습니다.
첫 번째 스택 반복: 1x A100, RAG, LangChain, 그리고 Ollama → vLLM
우리의 리소스 이야기는 매우 전형적입니다. 우리는 관리형 클라우드(managed cloud)에서 A100 80 GB 한 대를 사용하는 것으로 시작했으며, 이는 실험과 제품 데모를 모두 감당해야 했습니다. Ollama를 통해 Q6_K_M 양자화(quantization) 방식으로 30-70B 모델을 실행하고, 간단한 RAG 케이스를 구동하며, 초기 SQL 생성 프로토타입을 구축하기에는 충분했습니다.
나중에 우리는 A100 두 대가 장착된 내부 서버를 확보했습니다. 이는 훨씬 더 수월한 환경을 만들어 주었습니다. 그곳에서 프로덕션 실행이 이루어지고, 여러 환경이 존재하며, 안정적인 파이프라인을 유지하고 실험을 병렬로 진행할 수 있게 되었습니다. 그 시점에서 Ollama는 빠른 시작에는 훌륭하지만, 프로덕션을 위해서는 더 제어 가능하고 효율적인 추론 엔진(inference engine)이 필요하다는 점이 분명해졌습니다. 그렇게 해서 우리는 vLLM을 선택하게 되었습니다.
하지만 왜 더 높은 피크 성능(peak performance)을 보여주는 것으로 보이는 TensorRT-LLM이나 SGLang 대신 vLLM을 선택했을까요?
귀하의 경우에는 반드시 그렇지는 않을 것입니다. 인터넷에는 서로 모순되거나(서로 다른 하드웨어, 다른 모델, 다른 시점에 구축되었기 때문), 평균적으로 상대적인 동등함을 보여주는 차트와 표가 가득합니다:
요약하자면, 이는 흔히 볼 수 있는 벤치마크 전쟁입니다. 따라서 선택은 차트 상의 숫자가 아니라, 우리의 특정 사례에 대한 프레임워크 (Framework)의 적용 가능성에 달려 있습니다.
우리에게 필요했던 것은 무엇이었을까요? 구조화된 출력 (Structured output) 및 문맥 자유 문법 (Context-free grammars)에 대한 지원 — 세 엔진 모두 이를 지원합니다. Hugging Face의 다양한 모델들에 대한 지원 — 이 지점에서 TensorRT-LLM이 뒤처지기 시작하는데, 이는 지원하는 모델 제품군이 제한적이고 모델을 전환할 때마다 컴파일 (Compiling)이 필요하여 연구 개발 (R&D) 속도를 늦추고 복잡하게 만들기 때문입니다. 또한 우리는 CPU 추론 (Inference)을 원했는데, 이는 vLLM과 SGLang에는 존재하지만 TensorRT-LLM에는 없습니다. 결국 vLLM과 SGLang 사이의 최종 선택은 커뮤니티 규모에 따라 결정되었으며, vLLM이 승리했습니다.
이제 우리는 가장 무거운 모델들을 위한 진정한 플랫폼이라 할 수 있는 8x H200 서버를 기다리고 있습니다. 이를 통해 Qwen3-235B 및 그 주변 모델들을 실행하는 것이 현실적으로 가능해졌으며, 특정 작업을 위한 소형 모델들의 체계적인 학습 활동을 시작할 수 있게 되었습니다: SQL/Cypher/DSL 생성기, 의미론적 분류기 (Semantic classifiers), 벡터화 도구 (Vectorizers), 재순위화 도구 (Rerankers), 요약기 (Summarizers), 로그 분석기 (Log analyzers) 등입니다. A100에서도 이를 수행할 수는 있었겠지만, 양자화 (Quantization), 모델 크기, 그리고 학습 시간 측면에서 공격적인 타협을 감수해야 했을 것입니다.
프레임워크의 경우, 우리는 가장 단순한 세트인 LangChain, LangGraph, LangFuse, RAGAS, 그리고 여러 Qwen2.5 모델로 시작했습니다. 우리는 러시아어와 영어가 필요했기에, LLaMA 및 Gemma 제품군의 일부를 빠르게 제외했습니다. 우리의 작업과 코퍼스(Corpora) 측면에서 이들은 러시아어 성능이 Qwen에 비해 눈에 띄게 떨어졌고, 프롬프팅 (Prompting) 및 인스트럭션 튜닝 (Instruction tuning)도 더 어려웠기 때문입니다. 그 단계에서 우리의 구성은 다음과 같았습니다: RAG를 위한 LangChain, Qwen 기반의 초기 에이전트 그래프를 위한 LangGraph. 이 스택을 사용하여 우리는 프로토타입 어시스턴트를 구축하고, 베이스라인 RAG, SQL 생성기, 그리고 초기 실험적인 "생성기 + 비평가 + 검증기 (Generator + Critic + Validator)" 파이프라인을 테스트했습니다. 우리는 RAG의 반복적인 변경 사항 — 파이프라인/프롬프트/데이터 변경 및 베이스 모델 변경 모두 — 을 평가하기 위해 RAGAS를 사용했습니다. 목표 지표는 answer_relevancy(답변 관련성)와 answer_correctness(답변 정확성)였으며, 이를 바탕으로 변경 사항이 도움이 되었는지 혹은 해가 되었는지를 결정했습니다.

병행하여, 우리의 검색 (Retrieval) 접근 방식도 성숙해졌습니다. 일반적인 BM25 + 밀집 벡터 (Dense vectors) 조합 대신, 우리는 밀집(Dense) + 희소(Sparse) 표현을 모두 갖춘 BGE-M3에 승부수를 던졌고, 희소 벡터를 검색의 전문(Full-text) 부분으로 취급하기 시작했습니다. 우리의 텍스트 데이터에는 이 방식이 더 편리하고 운영하기 쉬웠습니다.
SQL 생성기 및 힌트 세트: 미세 조정 (Fine-tuning)이 진정으로 정당화되는 지점
"모델을 미세 조정했다"는 말은 자랑스럽게 들립니다. 하지만 대부분의 응용 작업에서 대규모 모델을 미세 조정하는 것은 필요성보다는 사치에 가깝다는 사실을 잊기 쉽습니다. 우리의 실무 경험은 매우 현실적입니다.
80B급 모델(특히 Qwen3-235B와 같은 모델)의 경우, 일반적인 작업 세트인 RAG, SQL, 코드, 도구(Tools)는 이미 기본적으로 해결되어 있습니다. 문제의 약 90%는 다음을 통해 해결됩니다:
- 견고한 컨텍스트 관리 (Context management);
- 적절한 RAG (BGE-M3, 우수한 인덱싱, 깨끗한 파싱);
- 파이프라인 아키텍처 (Generator, Critic, Validator, SGR);
- 엄격한 구조화된 출력 (Structured output).
미세 조정 (Fine-tuning)은 두 가지 경우에 정당화됩니다. 첫째, 강력한 GPU가 없는 고객을 위한 온프레미스 (on-prem) 환경처럼 특정 시나리오를 위한 작은 모델이 필요한 경우입니다. 둘째, 프롬프트 (prompt)만으로는 신뢰성 있게 얻기 어려운 매우 구체적인 출력 형식이 필요한 경우입니다.
기술적으로 우리는 고전적인 도구 상자를 사용합니다. 우리는 PEFT/LoRA/QLoRA, LLaMA-Factory 및 LMPO를 시도해 보았으나, 결국 사실상의 표준(de facto standard)인 TRL을 사용하여 SFT 및 RL 방식 (GRPO, GSPO 및 유사 방식)의 실험을 진행하게 되었습니다. SQL 생성기와 같은 복잡한 작업의 경우, 우리는 작은 모델 (예: Qwen-0.6B)에서 방법론을 먼저 테스트한 다음, 선택된 궤적 (trajectories)과 데이터를 더 큰 모델로 전이하는 방식을 선호합니다.
주요 필터는 간단합니다. 만약 우리에게 컴퓨팅 자원 (compute), 지표 (metrics)를 포함한 적절한 벤치마크 (benchmark), 또는 해당 작업을 위한 데이터가 없다면, 우리는 그 작업을 위해 미세 조정을 수행하지 않습니다. 먼저 컨텍스트 (context)를 통해 튜닝하고, 그다음 미세 조정을 수행합니다.
SQL 생성기는 우리가 "좋아, 여기서는 미세 조정이 가치가 있겠어"라고 생각한 첫 번째 사례였습니다. 우리는 모델이 유효한 SQL을 생성할 뿐만 아니라, 예측 가능하게 동작하고, PostgreSQL 방언 (dialect)의 뉘앙스를 이해하며, 일반적인 사용자 질의에 대한 추론 과정에서 스키마 연결 (schema linking)을 수행하기를 원했습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기


