
「ragy」 로컬 RAG 및 자기 수복 에이전트 개발 환경 구축과 구현 【기술 상세·구현 편】
요약
로컬 RAG 및 자기 수복 에이전트 개발 환경인 「ragy」의 기술적 구현 상세를 다룹니다. LiteLLM, Dify, Docker를 활용한 하이브리드 LLM 게이트웨이 구축과 실시간 데이터 동기화 및 에러 자동 수정 프로세스를 설명합니다.
핵심 포인트
- LiteLLM을 활용한 OpenAI 호환 하이브리드 LLM 게이트웨이 구축
- Dify API와 watchdog을 이용한 실시간 RAG 데이터 동기화 구현
- FastMCP와 Redis 기반의 시맨틱 캐시를 포함한 자작 MCP 서버 개발
- 에러 검지 및 자동 수정 패치 적용을 위한 자기 수복 에이전트 구현
본 기사는 필자가 개인 개발용으로 구축한 로컬 RAG 및 에이전트 개발 환경 「ragy」의 기술적인 상세 내용과 구체적인 구현 절차·설정을 정리한 【기술 상세·구현 편】입니다.
이전의 「설계·실패담 편」에서 소개한 전체 시스템을 실제로 로컬에서 구동하기 위한 설정 파일과 코드에 대한 해설을 진행합니다.
기본이 되는 아키텍처 설계(컴포넌트 간의 연결 및 방어책)는 개발자 스스로 주도하였으며, 실제 코드 작성 및 구현은 AI 에이전트(Antigravity)와의 페어 프로그래밍(Pair Programming)을 통해 폭속으로 구축했습니다. 그 과정에서 직면한 OS 레이어의 장애와 그 기술적 해결 방법에 대해서도 공유합니다.
하이브리드 LLM 게이트웨이: Docker Compose + LiteLLM을 통한 API 통합 및 SSRF 프록시(Squid)를 통한 보호 -
컨트롤 CLI 「ragy」: 프로젝트 초기화(Dify Dataset 자동 생성 및 IDE용 템플릿 전개) 자동화 -
전자동 RAG 동기화: Python의 watchdog 라이브러리와 Dify API를 조합한 실시간 동기화 스크립트(sync_docs.py) -
자작 MCP 서버: FastMCP와 Redis를 조합한 시맨틱 캐시(Semantic Cache) 포함 RAG 검색 도구(mcp_server.py) -
외부 에이전트(pi 또는 Claude Code)와의 SKILLS 연계: 커맨드 라인을 통해 RAG를 직접 호출하기 위한 검색 스크립트(dify_search.py) -
자기 수복 에이전트: google.antigravity SDK를 이용한 에러 검지·자동 수정 패치 적용 및 GitHub PR 자동 전송(agent_healer.py)
에디터 측(Continue나 Aider)에서 클라우드 모델(Gemini)과 로컬 모델(Ollama 상의 Qwen2.5-Coder, multilingual-e5-large)을 투명하게 다룰 수 있도록 하기 위해, LiteLLM을 사용하여 OpenAI 호환 API 서버로서 게이트웨이화합니다.
LiteLLM은 OpenAI API와 완전한 호환성을 가지고 있기 때문에, Continue나 Aider에 국한되지 않고 OpenAI 호환 엔드포인트를 지원하는 최근 화제인 pi나 opencode 등 모든 외부 애플리케이션 및 에이전트로부터 심리스(Seamless)하게 접속할 수 있습니다.
먼저 인프라(LiteLLM, Dify, Squid 등)를 실행하기 위한 docker-compose.yml의 관련 부분입니다. Dify 컨테이너가 로컬 네트워크 내의 프라이빗 IP로 부정 액세스하는 것을 방지하기 위해 SSRF 프록시(Squid)를 배치했습니다.
version: '3.8'
services:
# LiteLLM Proxy
...
주력 코딩용 gemini-3.5-flash, 로컬 개발용 qwen2.5-coder, 그리고 RAG용 임베딩 모델 multilingual-e5-large를 일괄 정의하고 있습니다.
model_list:
- model_name: gemini-3.5-flash
litellm_params:
...
본 환경에서는 각 서비스의 기동 및 프로젝트 초기화를 원활하게 수행하기 위해 컨트롤 CLI 도구 ./ragy를 쉘 스크립트(Shell Script)로 구현했습니다. 특히 ragy init 커맨드는 신규 프로젝트에서 RAG 환경을 즉시 구축하기 위한 자동화 로직입니다.
Dataset ID를 지정하지 않고 ragy init을 실행할 경우, Dify API(/datasets)를 자동으로 호출하여 현재 디렉토리 이름과 동일한 이름의 데이터셋을 신규 생성하고, 반환된 Dataset ID를 sync_config.json에 기록합니다.
RAG 시스템이 감시하는 물리적인 실체 폴더($PROJECT_DIR/docs/<project_name>)를 생성하고, 프로젝트 작업 폴더(현재 디렉토리) 직하에 ./docs로서 심볼릭 링크(Symbolic Link)를 자동으로 연결합니다. 이를 통해 개발자는 프로젝트 내의 ./docs/에 문서를 넣는 것만으로 동기화가 실행되도록 됩니다.
Aider용 .aider.conf.yml 및 Continue용 .continue/config.json
미리 준비된 템플릿으로부터 전개하여, 로컬 LLM 모델명이나 LiteLLM 게이트웨이의 URL(http://localhost:4000/v1)을 동적으로 주입합니다. 이를 통해 개발 경험 (DX)이 극적으로 향상됩니다.
로컬 docs/ 디렉터리 하위에 있는 .md 파일의 신규 생성·편집·삭제를 감시하여, Dify의 지식 (Dataset) API로 실시간 동기화합니다.
Dify의 문서 생성/업데이트 API는 통상적으로 multipart/form-data에 여러 파라미터를 평탄하게 나열하여 전송하면 유효성 검사 (Validation) 에러가 발생합니다.
올바른 방법은 설정 내용을 JSON 문자열로 만들어 data라는 단일 폼 필드(Form field)에 묶어서 넣어야 합니다.
# sync_docs.py 의 업로드 처리 발췌
import os
import json
...
이 사양에 대응하여, watchdog.events.FileSystemEventHandler를 상속받아 감시 이벤트(on_created, on_modified, on_deleted)를 핸들링합니다.
에디터 측 (Aider 등)에서 Dify RAG 검색을 심리스하게 호출할 수 있도록, Model Context Protocol (MCP) 서버를 Python의 FastMCP로 구현합니다.
또한, 동일하거나 유사한 쿼리에 대해 RAG로의 불필요한 API 호출을 억제하기 위해, Redis를 이용한 시맨틱 캐시 (Semantic Cache, 벡터 유사도 검색)를 구현하고 있습니다.
외부 라이브러리 (NumPy 등)에 대한 의존성을 최대한 배제하고, 순수 Python만으로 코사인 유사도 (Cosine Similarity)를 계산하여 캐시 판정을 수행합니다.
import math
# 코사인 유사도 계산
def cosine_similarity(v1, v2):
...
@mcp.tool() 데코레이터를 사용하여, AI 에이전트로부터 호출 가능한 RAG 검색 도구를 공개합니다.
@mcp.tool()
def search_dify_knowledge(query: str) -> str:
"""
...
Aider나 Continue와 같이 MCP (Model Context Protocol)를 지원하는 클라이언트 외에도, 최근 화제가 되고 있는 pi나 Claude Code처럼 독자적인 커스텀 커맨드 (SKILLS)를 읽어 들여 실행할 수 있는 AI 에이전트 도구와 로컬 RAG를 연동하기 위한 접근 방식입니다.
MCP처럼 프로세스 간 통신 (IPC)으로 접속하는 것이 아니라, AI 에이전트에게 "표준 출력을 해석하기 위한 검색 커맨드"를 스킬로서 등록합니다. 이 용도를 위해 CLI에서 직접 동작하는 RAG 검색 스크립트 dify_search.py를 구현했습니다.
현재 디렉터리 이름으로부터 프로젝트를 동적으로 특정하고, Dify의 API를 호출하여 검색 결과를 포맷팅한 뒤 표준 출력으로 내보내는 심플한 스크립트입니다.
import sys
import json
import os
...
이 dify_search.py를 AI 에이전트 (pi 등)의 커스텀 스킬 (커스텀 도구) 정의 파일에 등록합니다.
예를 들어, 다음과 같은 설정 (또는 SKILL.md나 도구 매니페스트)을 통해 에이전트에게 읽히게 합니다.
도구 이름 / 커맨드 이름: search_dify_knowledge
설명: "로컬의 Dify 지식 베이스로부터 프로젝트와 관련된 기술 문서나 사양서를 검색하여 컨텍스트로 가져온다"
실행 커맨드: python3 /path/to/My-RAG-Agent-System/scripts/dify_search.py "{{query}}"
이를 통해 AI 에이전트 (pi)가 자발적으로 "이 에러의 해결책이 문서에 있는가?", "이 API의 사양은 무엇인가?"라고 판단했을 때, 백그라운드에서 dify_search.py를 호출하여 로컬 RAG를 검색하고, 얻어진 표준 출력을 컨텍스트로 읽어 들여 정확한 코드를 생성할 수 있게 됩니다.
에러 로그를 백그라운드에서 모니터링하고, 예외 트레이스백(Traceback)을 감지한 시점에 대상 소스 코드를 특정하여, AI 에이전트(Google Antigravity SDK)를 기동해 자동 수정 및 GitHub의 PR(Pull Request)까지 완결합니다.
Gemini의 구조화된 출력(Structured Output) 기능을 이용하여, AI로부터 "수정 대상 파일 경로", "해설", "수정된 소스 코드"를 스키마 정의에 따라 깔끔하게 가져옵니다.
import pydantic
from google.antigravity import Agent, LocalAgentConfig
# AI로부터 가져올 수정 제안의 데이터 구조 정의
...
이 스크립트를 tail -f와 같이 로그 파일의 끝에 추가된 데이터를 폴링 모니터링(monitor_log_file)하게 하여, 비동기 태스크(Asynchronous Task)로서 상주 시킵니다.
구현의 대부분을 AI(Antigravity)에게 맡겨 폭속 개발을 진행하는 과정에서, AI가 혼자서 해결하지 못해 인간과 밀접하게 대화하며 해결했던, 가장 중요한 "OS·프로세스 관리 레이어"의 버그 해결 사례입니다.
백그라운드 동기화 스크립트나 Webhook 리시버를 기동할 때, Unix 계열 OS의 일반적인 데몬화(Daemonization) 기법(Double Fork)을 AI가 자동으로 생성했습니다. 하지만 macOS (Darwin)에서는 멀티스레드 프로세스에서 os.fork()를 호출하려고 하면, 시스템 라이브러리(CoreFoundation)의 안전 제한에 저촉되어 프로세스가 SIGABRT로 즉시 크래시(Crash)됩니다.
이를 해결하기 위해, os.fork()에 의존하지 않고 subprocess.Popen을 사용하여 새로운 세션으로서 프로세스를 띄우는 설계를 적용했습니다.
# deploy_listener.py 에서의 프로세스 기동 처리 발췌
# start_new_session=True 를 통해, 완전히 독립된 백그라운드 프로세스를 안전하게 기동한다
subprocess.Popen(
...
deploy_listener.py가 GitHub Webhook을 트리거로 ./ragy restart를 실행하려고 할 때, ragy 내부에서 오래된 리시버 프로세스를 kill하면, OS가 해당 프로세스 트리 전체에 시그널(SIGHUP/SIGPIPE)을 전파하여 실행 중인 배포 스크립트 자체도 함께 종료되어 버리는 결함이 발생했습니다.
이를 회피하기 위해, AI와 함께 검토하여 다음과 같은 3중 대책을 구현함으로써 자동 배포 파이프라인의 완전 자율화를 실현했습니다.
- Webhook 리시버의 조기 자율 종료: 리시버(FastAPI) 자신이 GitHub에 200 OK 응답을 반환한 직후,
os._exit(0)로 스스로 정상 종료하여 포트를 신속하게 해제한다. - 자동 배포 플래그를 통한 Kill 처리 스킵: 배포 스크립트에서 재기동을 실행할 때, 환경 변수
AUTO_DEPLOY=1을 지정하여ragy스크립트를 구동함으로써 Webhook 리시버에 대한 중복 Kill 명령을 바이패스(Bypass)한다. - PID 소멸 대기 루프:
ragy스크립트의 정지 처리 측에, 프로세스가 OS 레벨에서 완전히 소멸할 때까지 최대 10초간 대기하는 루프를 도입하여, 포트 충돌(Address already in use)로 인한 재기동 실패를 완전히 방지한다.
v1.1.0의 밀결합(RAG의 정지가 에이전트의 크래시로 직결되는 과제)을 해결하고, 나아가 RAG의 답변 정밀도를 극한까지 높이기 위해, 다음과 같은 로드맵으로 v2.0 업데이트를 계획하고 있습니다.
Aider나 Continue에서 MCP 서버로의 요청을 직접 Dify API로 동기 전송하는 것이 아니라, 일단 Redis의 큐(Redis Streams)에 태스크로서 스택(Stack)합니다. 백그라운드 워커(Worker)가 순차적으로 처리함으로써, Dify 컨테이너의 재기동이나 일시적인 네트워크 단절이 발생하더라도 에디터 측의 에이전트가 타임아웃으로 인해 함께 크래시되는 것을 방지합니다.
RAG의 정밀도 향상을 위해, LangGraph와 같은 무거운 프레임워크를 풀 스크래치(Full-scratch)로 도입하는 대신, Dify의 "워크플로우 기능"을 활용합니다.
Dify의 워크플로우 상에서 다음과 같은 루프를 정의하고, MCP 서버에서는 해당 워크플로우 API를 한 번 호출하기만 하면 되는 구성입니다.
- 쿼리 재생성 (Query Regeneration): 사용자의 질문으로부터 검색에 최적화된 키워드나 문맥을 LLM이 추출 및 쿼리화한다.
- 하이브리드 검색 (Hybrid Search): 지식 베이스(Weaviate)로부터 검색을 실행한다.
- Evaluator에 의한 자기 평가 (Self-Evaluation): 취득한 문서가 질문에 대해 충분한 정보를 포함하고 있는지 별도의 LLM (Evaluator)이 판정한다.
- 자기 수정 루프 (Self-Correction Loop): 정보가 부족하다고 판정될 경우, 부족한 요소를 채우기 위한 새로운 검색 쿼리를 생성하여 재검색을 실행한다 (최대 N회).
이와 같이 문서 파싱이나 벡터 DB 관리 등의 인프라 부분은 Dify의 혜택을 받으면서, 외곽에는 Redis 큐를, 내부에는 Dify의 루프 처리를 조합하는 "하이브리드 구성"을 채택함으로써, 개발 비용을 최소한으로 억제하면서도 매우 견고하고 똑똑한 로컬 AI 환경을 실현해 나갑니다.
본 구축을 통해 다음과 같은 개발 사이클을 로컬 환경에서 완결할 수 있었습니다.
docs/에 문서를 두는 것만으로 자동 벡터 인덱싱 (RAG)이 수행된다.- 에디터나 CLI (Aider/Continue)에서 코드를 작성할 때, 자체 제작한 MCP를 통해 문서의 문맥을 고려한 매우 정밀도 높은 제안을 (시맨틱 캐시 (Semantic Cache)의 빠른 응답과 함께) 얻을 수 있다.
- 만일 백그라운드 동기화나 배포 과정에서 예외가 발생하더라도, 자기 수복 에이전트가 백그라운드에서 동작하여 수정 PR을 자동으로 생성해 준다.
각 파츠에는 Dify API 파라미터의 특이사항 회피, 독자적인 유사도 계산을 통한 캐시 시스템, Google Antigravity SDK를 이용한 인텔리전트한 자동 수복 등, 실무적인 노하우가 곳곳에 녹아 있습니다.
로컬 개발 환경의 추가적인 효율화를 목표로 하는 분들에게 참고가 되기를 바랍니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기