Dify에서 로컬 RAG 구현하기: 워크플로우 구축 및 답변 품질 테스트
요약
Dify를 사용하여 로컬 환경에서 RAG(검색 증강 생성) 파이프라인을 구축하고 워크플로우를 설계하는 실무 가이드입니다. 문서 파싱부터 임베딩, 벡터 DB 활용, 답변 품질 테스트까지의 전 과정을 다룹니다.
핵심 포인트
- Dify를 활용한 로컬 RAG 워크플로우 구축 방법
- 문서 파싱, 임베딩, 벡터 DB 등 RAG 구성 요소 이해
- 검색 설정 변화에 따른 답변 품질 비교 및 테스트 방법
- 로컬 배포 시 보안 및 인프라 구성 주의사항
Local RAG in Dify: Build a Workflow and Test Answer Quality | Agent Lab Journal
Agent Lab Journal
Guides
...
실무 가이드 · 중급
Dify에서 로컬 RAG 구현하기: 워크플로우 구축 및 답변 품질 테스트
전체 서비스 인프라를 처음부터 구축할 필요 없이, 자신의 머신에 Dify를 배포하고, 로컬 모델을 연결하며, 자체 문서를 인덱싱하고, 검색 워크플로우 (retrieval workflow)를 조립하고, 설정을 비교해 보세요.
레벨: 중급
읽기 및 실습 시간: 60분
결과물: 로컬 Dify, 작동하는 RAG 파이프라인, 검색 비교표
이 실습의 유용한 결과물은 단순히 사용자의 파일을 알고 있는 것처럼 보이는 챗봇이 아닙니다. 여러분은 재현 가능한 로컬 검색 증강 생성 (RAG, Retrieval-Augmented Generation) 서비스를 만들고, 그 답변이 실제로 소스 문서에 의해 뒷받침되는지 테스트하며, 검색 설정이 결과에 어떻게 변화를 주는지 기록하게 될 것입니다.
우리가 해결하려는 문제
한 팀이 지원 핸드북, 사고 절차서, 서비스 정책과 같은 소규모의 내부 운영 문서 컬렉션을 보유하고 있습니다. 사람들은 “사고는 언제 에스컬레이션(escalation)되어야 하는가?” 또는 “인수인계 보고서에는 어떤 필드가 필수적인가?”와 같은 질문을 반복해서 던집니다. 일반 목적의 모델 (general-purpose model)은 규칙이 해당 파일들에만 존재하기 때문에 신뢰할 수 있는 답변을 제공할 수 없습니다.
개별 구성 요소로부터 프로덕션 검색 서비스를 구축하려면 문서 파싱 (document parsing), 임베딩 모델 (embedding model), 벡터 데이터베이스 (vector database), 검색 코드 (retrieval code), 프롬프트 조립 (prompt assembly), 모델 서빙 (model serving), 인터페이스, 로깅 및 배포 작업이 필요합니다. Dify는 이러한 조각들을 조립할 수 있는 시각적 레이어를 제공하므로, 우리는 문서 준비, 검색 및 평가에 집중할 수 있습니다.
이 가이드는 구체적이지만 고객 사례가 아닌 경우를 사용합니다: 여러분이 직접 만드는 가상의 운영 핸드북입니다. 답변 품질에 대한 수치는 사전에 제공되지 않습니다. 여러분은 자신의 설치 환경을 측정하고 관찰된 결과를 비교표에 입력하게 됩니다.
대상 아키텍처
사용자 질문
│
▼
...
대규모 언어 모델 (LLM)이 응답을 작성합니다. 모델이 직접 소스 파일을 검색하는 것은 아닙니다. Dify는 먼저 관련 구절을 찾아 모델 프롬프트 (prompt)에 삽입합니다. 이러한 단계들을 분리하여 유지하는 것이 중요합니다. 품질이 낮은 답변은 검색 (retrieval) 실패, 불충분한 프롬프트, 또는 생성 모델 (generation model) 때문에 발생할 수 있기 때문입니다.
여기서 "로컬 (local)"의 의미
Dify와 이를 지원하는 서비스들은 사용자의 머신 위 컨테이너 (containers)에서 실행됩니다. 생성 (generation) 및 임베딩 (embedding) 모델은 로컬 모델 런타임 (model runtime)에 의해 제공됩니다. 문서는 일반적인 사용 과정 동안 머신에 그대로 남아 있습니다. 초기 설치 시에는 리포지토리 (repositories) 클론, 컨테이너 이미지 다운로드, 모델 제공자 (model-provider) 구성 요소 설치, 그리고 모델 파일 다운로드를 위해 여전히 인터넷 접속이 필요할 수 있습니다.
로컬 배포가 자동으로 보안 배포를 의미하는 것은 아닙니다. 노출된 포트 (ports)를 제한하고, 계정을 보호하며, 로그를 검토하고, 업로드된 문서가 선택한 머신에 저장되는 것을 허용할지 여부를 결정해야 합니다.
요구 사항 및 규모
다음이 필요합니다:
- Git 및 Compose 명령어를 사용할 수 있는 Docker
- Ollama와 같은 로컬 모델 런타임 (model runtime)
- Dify 컨테이너, 데이터베이스, 소스 파일 및 모델 가중치 (model weights)를 위한 충분한 여유 디스크 공간
- Dify 설치 환경에서 지원되는 최소 하나 이상의 텍스트 처리 가능 생성 모델 (generation model) 및 임베딩 모델 (embedding model)
- 터미널 (terminal) 및 최신 브라우저
CPU 전용 추론 (inference)만으로도 파이프라인 (pipeline)을 검증하기에는 충분하지만, 생성 속도가 느릴 수 있습니다. 지원되는 GPU를 사용하면 일반적으로 반복 속도가 향상됩니다. 메모리 요구 사항은 모델 크기와 양자화 (quantization)에 크게 좌우되므로, 대규모 모델을 다운로드하기 전에 모델 런타임을 확인하십시오.
재현 가능한 첫 번째 테스트를 위해서는, 머신이 간신히 실행할 수 있는 가장 큰 모델보다는 적당한 크기의 지시 모델 (instruction model)을 권장합니다. 안정적인 응답 시간은 구성 비교를 더 쉽게 만들어 줍니다.
1. 통제된 문서 세트 생성
익숙한 문서로 테스트하는 것도 유용하지만, 통제된 미니 코퍼스 (mini-corpus)를 사용하면 실패 원인을 진단하기가 더 쉽습니다. Dify 소스 트리 외부의 디렉토리를 생성하십시오:
mkdir -p rag-lab/documents
cd rag-lab/documents
세 개의 일반 텍스트(plain-text) 또는 Markdown 파일을 생성하십시오. 아래의 사실들은 의도적으로 허구로 작성되었으며, 오직 이 실습(lab)을 위해서만 존재합니다.
incident-policy.md
# Incident policy (장애 대응 정책)
## Severity Amber (심각도 Amber)
...
handover-checklist.md
# Shift handover checklist (교대 근무 인수인계 체크리스트)
모든 인수인계 기록에는 다음 사항이 포함되어야 합니다:
...
support-boundaries.md
# Support boundaries (지원 범위)
지원 팀은 승인 없이 공개 상태 워커(public status worker)를 재시작할 수 있습니다.
...
비교가 완료될 때까지 이 소스 파일들을 변경하지 마십시오. 코퍼스(corpus)가 변경되면 인덱싱된 구절(indexed passages)이 더 이상 동일하지 않게 되므로 구성 비교(configuration comparison) 결과가 무효화됩니다.
인덱싱 전 질문 세트 구축하기
파일 내에 근거가 명확히 존재하는 질문들을 생성하십시오. 직접적인 질문, 다중 부분 질문, 오도하는 질문, 그리고 답변 불가능한 질문을 포함하십시오:
-
Amber 등급의 장애는 얼마나 빨리 검토되어야 합니까?
-
Amber 등급의 장애는 언제 Red 등급이 될 수 있습니까?
-
티켓 수가 많으면 자동으로 장애 등급이 Red가 됩니까?
-
모든 인수인계 기록에는 무엇이 포함되어야 합니까?
-
지원 팀은 요청자를 확인한 후 운영 환경(production) 접근 권한을 부여할 수 있습니까?
-
지원 팀이 승인 없이 수행할 수 있는 작업은 무엇입니까?
-
비용 환급은 누가 승인합니까?
-
Amber 등급의 검토 시간은 얼마이며, Red 상태 업데이트는 얼마나 자주 요구됩니까?
질문 7은 의도적으로 답변이 불가능하도록 설계되었습니다. 안전한 RAG 서비스라면 제공된 문서에 답변이 포함되어 있지 않다고 말해야 합니다. 만약 서비스가 비용 정책을 지어낸다면, 환각 (hallucination) 현상을 감지한 것입니다.
2. 로컬 모델 런타임 시작하기
사용 중인 운영 체제에 적합한 방법으로 Ollama를 설치한 다음, 서비스가 실행 중인지 확인하십시오:
ollama --version
ollama list
사용자의 사양에 맞는 명령 수행 모델(instruction model) 하나와 임베딩 모델(embedding model) 하나를 가져오기(pull) 하십시오. 다음 이름들은 Ollama에서 흔히 사용되는 예시입니다. 설치된 런타임 및 Dify 프로바이더(provider)가 지원하는 모델로 교체하여 사용하십시오:
ollama pull qwen2.5:7b
ollama pull nomic-embed-text
Dify와 독립적으로 생성 여부를 확인합니다:
ollama run qwen2.5:7b "Reply with exactly: local model ready"
HTTP 서비스가 응답하는지 확인합니다:
curl http://127.0.0.1:11434/api/tags
응답에는 모델 목록이 포함되어야 합니다. 만약 이 요청이 실패한다면, 다음 단계로 진행하기 전에 런타임 (runtime)을 수정하십시오. Dify는 사용 불가능한 모델 엔드포인트 (model endpoint)를 스스로 복구할 수 없습니다.
컨테이너에서 런타임에 접근 가능하도록 설정하기
컨테이너 내부의 127.0.0.1은 호스트 (host)가 아니라 해당 컨테이너 자신을 가리킵니다. Docker Desktop의 경우, 호스트는 보통 다음과 같이 접근할 수 있습니다:
Linux의 경우, Dify는 관련 Compose 서비스에 명시적인 호스트 게이트웨이 (host-gateway) 매핑이 필요할 수 있습니다:
extra_hosts:
- "host.docker.internal:host-gateway"
Linux의 또 다른 옵션은 Docker 브리지 게이트웨이 (Docker bridge gateway) 주소를 사용하는 것이지만, 반복 가능한 실습을 위해서는 안정적인 호스트 매핑을 사용하는 것이 더 명확합니다. 모델 서버 또한 Docker에서 접근 가능한 인터페이스 (interface)에서 리스닝 (listen)해야 합니다. 단순히 컨테이너 연결성 문제를 해결하기 위해 모델 서버를 신뢰할 수 없는 네트워크에 노출하지 마십시오.
3. Docker Compose로 Dify 배포하기
공식 Dify 리포지토리 (repository)를 클론 (clone)하고, 선택한 릴리스 (release)에 포함된 배포 파일을 사용하십시오:
git clone https://github.com/langgenius/dify.git
cd dify/docker
cp .env.example .env
...
재현성 (reproducibility)이 중요한 경우에는 릴리스 태그 (release tag)나 커밋 (commit)을 고정하십시오. 단순히 "latest"로만 기록하면, Dify의 기본값, 프로바이더 (providers), 파서 (parsers), 인터페이스 레이블 (interface labels) 등이 변경될 수 있기 때문에 나중에 비교할 때 모호해질 수 있습니다.
컨테이너 상태를 확인합니다:
docker compose ps
서비스가 비정상적이거나 반복적으로 재시작되는 경우 최근 로그를 검사하십시오:
docker compose logs --tail=150
배포 설정에 표시된 로컬 Dify 주소를 열고, 초기 관리자 설정을 완료한 후 프로젝트 계정을 생성하십시오. 스크린샷, 셸 히스토리 (shell history), 공유 노트 또는 이 실습의 비교 테이블에 실제 자격 증명 (credentials)을 기입하지 마십시오.
환경 기록하기
기본값을 변경하기 전에 작은 실행 기록을 생성하십시오:
Dify 릴리스 또는 커밋:
Docker 버전:
운영 체제 (Operating system):
...
문서 디렉토리에서 소스 해시 (source hashes)를 계산할 수 있습니다:
sha256sum *.md
sha256sum이 없는 시스템에서는 해당 플랫폼의 상응하는 SHA-256 명령어를 사용하십시오. 이 해시들을 통해 다른 사람이 동일한 코퍼스 (corpus)로 테스트되었는지 확인할 수 있습니다.
4. 로컬 모델 프로바이더 (model providers) 설정
Dify에서 모델 프로바이더 (model-provider) 설정을 열고 로컬 런타임 (runtime)에서 사용하는 프로바이더를 활성화하십시오. Dify 릴리스 버전에 따라 프로바이더가 이미 사용 가능할 수도 있고, 프로바이더 마켓플레이스 (marketplace)에서 설치해야 할 수도 있습니다.
생성 모델 (generation model)을 다음과 같이 설정하십시오:
-
Base URL: http://host.docker.internal:11434
-
Model name:
ollama list명령에 의해 반환되는 정확한 이름 -
Model type: 프로바이더가 요구하는 대로 텍스트 생성 (text generation) 또는 채팅 (chat)
-
Context size: 선택한 모델이 지원하고 사용 가능한 메모리 내에 있는 값
임베딩 모델 (embedding model)은 별도로 설정하십시오:
-
Base URL: 접근 가능한 동일한 Ollama 엔드포인트 (endpoint)
-
Model name: 정확한 임베딩 모델 이름
-
Model type: 텍스트 임베딩 (text embedding)
로컬 런타임이 요청을 인증하지 않더라도, 일부 프로바이더 양식에서는 구문상 애플리케이션 프로그래밍 인터페이스 (API) 키가 필요할 수 있습니다. 프로바이더의 UI 요구 사항을 따르되, 실제 비밀 키를 자리 표시자 (placeholder)로 재사용하지 마십시오.
연결 확인 (Connection verification)
제공되는 경우 Dify의 프로바이더 테스트를 사용하십시오. 테스트에서 네트워크 오류가 보고되면 모델 파라미터를 변경하기 전에 연결 상태를 점검하십시오:
-
호스트에서 Ollama가 응답하는지 확인합니다.
-
프로바이더 URL이 컨테이너 로컬인
127.0.0.1을 사용하지 않는지 확인합니다. -
필요한 경우 호스트 게이트웨이 (host-gateway) 매핑이 존재하는지 확인합니다.
-
태그를 포함한 모델 이름이
ollama list와 일치하는지 확인합니다. -
방화벽 규칙이 Docker로부터의 트래픽을 차단하지 않는지 확인합니다.
5. 지식 베이스 (knowledge base) 생성 및 인덱싱
Dify의 지식 (Knowledge) 영역을 열고 rag-lab-operations라는 이름의 데이터셋을 생성합니다. 제어된 세 개의 문서를 업로드하십시오.
인터페이스에서 선택 옵션을 제공할 경우 더 높은 품질의 인덱싱 (indexing) 방법을 선택하십시오. 구성한 로컬 임베딩 (embedding) 모델을 선택합니다. 임베딩 모델을 변경하면 일반적으로 코퍼스 (corpus)를 다시 인덱싱해야 하므로 원본 파일을 유지하십시오.
초기 세그멘테이션 (segmentation) 전략 선택
청크 (chunk)는 하나의 검색 가능한 단위로 저장된 구절입니다. 적절한 최대 길이를 시작점으로 잡고, 가능한 경우 단락 경계를 보존하며, 작은 오버랩 (overlap)을 사용하십시오. 정확한 단위는 Dify 버전과 파서 (parser)에 따라 다르므로, 해당 값과 인터페이스가 글자 수 (characters)를 측정하는지 토큰 (tokens)을 측정하는지를 기록하십시오.
설정 (Setting)
초기값 (Initial value)
이유 (Reason)
...
인덱싱을 완료하기 전에 파싱된 세그먼트 (segments)를 미리 보기 하십시오. 다음 사항을 확인하십시오:
-
헤딩 (headings)이 수식하는 지침에 부착되어 있는지
-
리스트 (lists)가 읽기 쉬운 상태를 유지하는지
-
파싱 후 빈 파일이 없는지
-
Amber-to-Red 에스컬레이션 (escalation) 규칙이 조건문으로부터 분리되지 않았는지
-
인수인계 리스트가 관련 없는 한 줄짜리 구절들로 파편화되지 않았는지
인덱싱 완료 보고가 나올 때까지 기다리십시오. 부분적인 코퍼스를 조용히 테스트하기보다는 실패한 문서들을 기록하십시오.
6. 애플리케이션 구축 전 검색 (retrieval) 테스트
데이터셋의 검색 테스트 (retrieval-test) 인터페이스를 사용하십시오. 다음 문구로 검색합니다:
When does an Amber incident become Red?
(언제 Amber 사고가 Red가 됩니까?)
관련 구절에는 실패한 워크아라운드 (failed-workaround) 또는 핵심 워크플로우 (critical-workflow) 조건이 포함되어 있어야 합니다. 그다음, 의역된 문구로 검색합니다:
Is a surge in ticket volume enough to raise the severity?
(티켓 볼륨의 급증이 심각도를 높이기에 충분합니까?)
예상되는 근거는 높은 티켓 수만으로는 심각도가 변경되지 않는다는 문장입니다. 이 테스트는 정확한 키워드 매칭 (keyword matching)과 의미론적 검색 (semantic retrieval)을 구분해 줍니다.
각 쿼리 (query)에 대해 구절 자체를 검사하십시오. 유사도 점수 (similarity score)를 관련성의 증거로 취급하지 마십시오. 높은 점수를 받은 구절이라도 결정적인 조건을 누락할 수 있으며, 점수는 서로 다른 임베딩 모델 간에 항상 비교 가능한 것은 아닙니다.
베이스라인 검색 설정 (Baseline retrieval settings)
시맨틱 검색 (Semantic retrieval) 또는 벡터 검색 (Vector retrieval)을 시작점으로 삼고, top-k 값을 3으로 설정하며, 기본 점수 임계값 (Score threshold)은 비활성화하거나 보수적으로 설정하세요. top-k는 다음 단계로 반환되는 후보 구절 (Candidate passages)의 최대 개수입니다.
만약 정답 구절이 누락되어 있다면, 생성 (Generation) 단계에서 이를 안정적으로 복구할 수 없습니다. 프롬프트 (Prompt)를 튜닝하기 전에 데이터 수집 (Ingestion) 또는 검색 (Retrieval) 과정을 먼저 수정하세요.
7. RAG 워크플로우 구축
설치된 환경에서 사용 가능한 워크플로우 (Workflow) 또는 챗플로우 (Chatflow) 유형을 사용하여 새로운 Dify 애플리케이션을 생성하세요. 이름은 'Local Operations RAG'로 지정합니다.
다음과 같은 최소한의 그래프를 구성하세요:
시작 (Start) / 사용자 입력 (User Input)
│
▼
...
시작 노드 (Start node)
'question'이라는 이름의 텍스트 입력을 추가합니다. 만약 애플리케이션 유형에서 이미 사용자 쿼리 (User query)를 노출하고 있다면, 중복해서 생성하지 말고 해당 변수를 일관되게 사용하세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기