당신의 Django 앱에는 수년간의 데이터가 쌓여 있습니다. AI 에이전트가 이를 실제로 활용하게 만드는 방법
요약
Django 앱의 관계형 데이터를 AI 에이전트가 활용할 수 있도록 ORM 그래프를 기반으로 벡터화하는 django-graph-search 라이브러리를 소개합니다. 기존 스키마 변경 없이 설정만으로 객체의 관계적 문맥을 포함한 풍부한 임베딩을 생성할 수 있습니다.
핵심 포인트
- ORM 관계 그래프를 탐색하여 객체의 전체 문맥을 포착
- 기존 Django 모델 및 스키마 변경 없이 즉시 적용 가능
- FK, M2M, 역관계를 포함한 풍부한 의미론적 벡터 생성
- post_save 시그널을 통한 실시간 벡터 인덱스 동기화
모든 Django 개발자가 알고 있는 문제
당신은 제품, 기사, 주문, 사용자 등 수년간의 데이터가 쌓인 Django 앱을 보유하고 있습니다. 사용자들은 검색창에 자연어 쿼리 (natural language queries)를 입력하지만, 결과가 아예 없거나 키워드 매칭 방식의 쓸모없는 정보만을 받게 됩니다.
더 심각한 문제는, 이 데이터에 대해 질문에 답할 수 있는 AI 에이전트 (AI agent)를 연결하고 싶을 때 발생합니다. 하지만 모든 데이터는 관계형 테이블 (relational tables) 안에 잠겨 있습니다. 이를 LLM (Large Language Model)에 공급하려면 데이터베이스를 통째로 덤프하거나, 커스텀 ETL 파이프라인을 작성하거나, 별도의 벡터 스토어 (vector store)를 구축한 뒤 ORM (Object-Relational Mapping)과 수동으로 동기화해야 합니다.
저는 정확히 이 문제에 직면했습니다. 그래서 단 하나의 설정 파일로 이 문제를 해결하는 라이브러리를 만들었습니다.
Django ORM과 AI 사이의 간극
전형적인 Django 검색 솔루션들은 '검색'이라는 단 하나의 문제만을 해결합니다. 하지만 그 모든 것에는 대가가 따릅니다:
| 솔루션 | 필요한 사항 | 문제점 |
|---|---|---|
| Haystack + Elasticsearch | 별도의 서버, 수동 필드 매핑 | 엄청난 양의 보일러플레이트 (boilerplate), 의미론적 정보 부재 |
| ... |
그 어떤 것도 진짜 질문에 답하지 못합니다: 아키텍처를 재작성하지 않고 어떻게 하면 내 Django 앱의 실시간 데이터와 AI 에이전트가 함께 작동하게 만들 수 있을까?
핵심 아이디어: 벡터 소스로서의 ORM 그래프
django-graph-search는 단순히 개별 모델 필드를 벡터화하는 것에 그치지 않습니다. 이 라이브러리는 ORM 관계 그래프 (relation graph)를 탐색하여 객체의 전체 문맥 (context)을 포착하는 풍부한 문서를 생성합니다.
간단한 Product 모델을 예로 들어보겠습니다:
Product(pk=42)
├── name: "Pixel 8"
├── description: "Camera-first Android phone with Tensor G3"
...
이 모든 정보가 하나의 텍스트 문서로 병합되어 임베딩 모델 (embedding model)로 전달됩니다. 결과로 생성된 벡터는 의미론적으로 풍부합니다. 즉, 객체 자체에 대한 정보뿐만 아니라 객체의 전체적인 관계적 문맥에 대한 정보까지 담고 있습니다.
이는 GraphResolver를 통해 수행됩니다. 이 클래스는 _meta.get_fields()를 재귀적으로 탐색하며, FK (Foreign Key), M2M (Many-to-Many), 역관계 (reverse relations)를 처리하고, visited 집합을 통해 순환 참조 (cycles)를 추적합니다.
마이그레이션 제로. 스키마 변경 제로.
가장 중요한 부분은 이 모든 것이 당신의 기존 (existing) Django 애플리케이션 위에 추가된다는 점입니다. 모델을 변경하거나, 새로운 테이블을 생성하거나, 뷰 (views)를 건드릴 필요가 없습니다.
모든 설정은 settings.py에 위치합니다:
INSTALLED_APPS = [
...,
"django_graph_search",
...
명령어 하나로 벡터 인덱스 (vector index)가 구축됩니다:
python manage.py build_search_index
당신의 Django 앱은 이전과 정확히 동일하게 작동합니다. 데이터는 PostgreSQL에 그대로 유지됩니다. 벡터 (Vectors)는 ChromaDB에 나란히 저장됩니다. post_save 시그널 (signals)이 객체가 변경될 때마다 인덱스를 자동으로 최신 상태로 유지합니다.
벡터화할 항목 제어하기
데이터베이스의 모든 항목이 벡터로 변환될 필요는 없습니다. slug, created_at과 같은 기술적인 필드나 내부 관리용 메모 등은 검색 품질을 저하시키는 노이즈 (noise)가 됩니다.
weight_fields를 통해 정밀한 제어가 가능합니다:
"weight_fields": {
"title": 2.0, # 두 번 반복됨 — 임베딩 (embedding)이 이를 더 잘 "기억"함
"description": 1.0, # 표준 가중치
...
이것은 단순한 필터링이 아닙니다. 내부적으로 GraphResolver._apply_weight() 메서드는 문서 내의 텍스트 조각들을 가중치에 비례하여 반복합니다. 가중치가 2.0인 필드는 연결된 문자열 내에서 두 번 나타나며, 벡터 공간 (vector space)에서 임베딩 중심점 (embedding centroid)을 해당 개념 쪽으로 이동시킵니다.
Django 데이터 기반의 RAG: 진정한 패턴
이제 핵심적인 내용입니다. 이것이 AI 에이전트 (AI agents)와 어떻게 연결되는지에 대한 설명입니다.
표준 RAG (Retrieval-Augmented Generation, 검색 증강 생성) 패턴은 다음과 같은 과정을 요구합니다: 사용자 질의 (query) 수신 → 관련 컨텍스트 (context) 검색 → LLM에 전달. 보통 "컨텍스트 검색"은 별도의 인프라를 의미합니다. 하지만 django-graph-search를 사용하면 단 세 줄로 가능합니다:
from django_graph_search import search
def build_llm_context(user_question: str) -> str:
...
핵심적인 세부 사항: results[*]["text"]는 단순히 str(instance)가 아닙니다. 이는 모든 관련 필드와 모든 가중치가 적용된, 벡터 구축에 사용된 전체 텍스트 문서입니다. LLM은 단순한 객체 이름이 아닌, **풍부하고 관계적인 컨텍스트 (rich, relational context)**를 전달받게 됩니다.
LangGraph 파이프라인: 메모리를 가진 에이전트
더 까다로운 유스케이스 (use cases)를 위해, 이 라이브러리는 선택 사항인 LangGraph 파이프라인을 제공합니다. 이는 다음 기능들을 추가합니다:
- 쿼리 확장 (Query expansion) — LLM이 2~3개의 의미론적 재구성 (semantic reformulations)을 생성하며, 모든 변형에 대해 검색을 수행하고 결과를 병합합니다.
- 재순위화 (Reranking) — 상위 K개의 후보군 (top-K candidates)을 실제 관련성에 따라 LLM이 다시 정렬합니다.
- 스트리밍 (Streaming) — 클라이언트는 SSE 또는 NDJSON을 통해 실시간 진행 상황을 확인할 수 있습니다.
GRAPH_SEARCH = {
"LANGGRAPH": {
"ENABLED": True,
...
중요: langgraph는 **선택적 의존성 (optional dependency)**입니다. 패키지가 설치되어 있지 않으면, 파이프라인은 자동으로 동일한 동작을 수행하는 내장 _FallbackGraph로 전환됩니다. 애플리케이션 코드는 변경할 필요가 없습니다.
대화형 엔드포인트 (conversational endpoint)는 요청 간에 상태 유지 메모리 (stateful memory)를 추가합니다:
사용자: "500달러 미만의 스마트폰을 보여줘"
에이전트: [목록 반환]
사용자: "삼성 제품만"
...
프로덕션으로 가는 길 — 동일한 설정, 다른 백엔드
벡터 백엔드 (vector backends) 간의 마이그레이션은 단 한 줄만 변경하면 됩니다:
개발 (Dev): ChromaDB → 로컬, 서버 없음, 디스크에 파일 저장
스테이징 (Staging): FAISS → 빠름, CPU 전용, 모든 데이터를 메모리에 유지
프로덕션 (Production): pgvector → 이미 PostgreSQL을 사용 중이라면 — 새로운 서버가 전혀 필요 없음
...
임베딩 모델 (Embedding models)도 동일한 방식으로 교체 가능합니다: 로컬 sentence-transformers, OpenAI, Cohere — 모두 EMBEDDINGS.BACKEND를 통해 이루어집니다. 코드 변경은 전혀 없습니다.
이것의 실체
django-graph-search는 단순한 또 다른 검색 라이브러리가 아닙니다. 이것은 **기존 Django ORM 위에 구축된 벡터 레이어 (vector layer)**로, 다음과 같은 역할을 수행합니다:
- 데이터베이스 스키마 (database schema) 변경이 필요하지 않습니다.
- 모델 관계 (model relations)를 탐색하여 풍부한 문서 (rich documents)를 자동으로 생성합니다.
- 표준 RAG 패턴을 통해 애플리케이션 데이터를 AI 에이전트가 사용할 수 있도록 만듭니다.
- 리팩토링 없이 로컬 ChromaDB에서 프로덕션용 Qdrant까지 확장 가능합니다.
pip install django-graph-search[chromadb]
당신의 데이터는 이미 그곳에 있습니다. 단지 벡터 레이어가 필요할 뿐입니다.
GitHub: svalench/django_graph_search · PyPI: django-graph-search
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기