[LLM] LLM 애플리케이션 개발하기: RAG

이보석·2025년 4월 25일

※ 본 글은 허정준 저자의 『LLM을 활용한 실전 AI 애플리케이션 개발』(책만, 2024)을 기반으로 학습한 내용을 정리한 것입니다.

해당 도서의 공식 GitHub 실습 코드 저장소는 다음과 같습니다:​
Github: https://github.com/onlybooks/llm​

9.1 검색 증강 생성(RAG)

RAG란?

  • 비정형 데이터로 학습한 LLM의 원치 않는 답을내는 Hallucination에 대한 해결방안
  • Retrieval(되찾아옴) Augmented(증강하는) Generation(생성)
  • 정답
  • 정보 검색 → 프롬프트 보강(증강) → 생성(추론)
  • 팩트를 LLM에게 전달해서 더 나아진 답을 생성하게 하는 것

9.1 검색 증강 생성(RAG) - 워크플로

  • 워크플로 A(저장): 검색할 데이터를 벡터 데이터베이스에 저장
  • 워크플로 B(검색): 사용자의 인터페이스를 통해 들어온 사용자의 요청에 관련된 정보를 벡터DB에서 검색(저장한 문서를 검색)
  • 워크플로 C(반영): 사용자의 요청과 결합해 프롬프트를 완성

9.1 검색 증강 생성(RAG) - LLM 오케스트레이션

LLM 오케스트레이션 도구란?

  • LLM 기반 애플리케이션의 구축과 관리를 간소화하는 종합적인 툴(프레임워크)
  • 사용자 인터페이스, 임베딩모델, 벡터 데이터베이스등
  • 라마인덱스, 랭체인, 캐노피 등

9.1.1 데이터 저장

  • 데이터소스의 텍스트를 임베딩 모델을 사용해 임베딩 벡터로 변환
  • 벡터 데이터베이스에 저장

임베딩 모델

  • 비정형 데이터 입력시 그 의미를 담은 임베딩 벡터로 변환하는 모델
  • OpenAI의 text-embedding-ada-002
  • Sentence-Transformers 라이브러리(오픈소스)

벡터 데이터베이스

  • 벡터 사이의 거리를 기준으로 검색하는 특수 DB
  • 벡터가 Index/PK가 될 수 있다.
  • Chroma, Milvus(오픈소스)
  • Pinecone, Weaviate
  • PostgreSQL(RDB에 벡터 검색 기능 도입)

텍스트 데이터를 임베딩 벡터로 만들어 벡터 DB에 저장하고 검색하는 과정

	1. 문서를 임베딩 모델을 통해 임베딩 벡터로 변환하고 벡터DB에 저장
    2. 벡터DB 내부에 각각의 벡터를 저장
    3. 특정 문장(검색 쿼리)으로 검색을 수행하는 경우 임베딩 모델을 통해 검색 쿼리도 벡터로 변환
    4. 벡터DB에서 위치를 찾고 쿼리 임베딩과 가장 가까운 벡터를 찾음
    5. 일반적으로 유클리드 거리, 코사인 유사도 활용하여 거리 계산

요약 정리

  • 비정형 데이터 -> 임베딩 모델 -> 벡터화 -> 벡터DB 저장
  • 벡터는 검색을 위한 유사도 비교의 대상
  • 쿼리도 벡터화해서 유사도 기반 검색 수행
  • 비슷한 벡터는 벡터 공간상 가까운 위치에 있어 클러스터처럼 동작

9.1.2 프롬프트에서 검색 결과 통합

  • LLM은 결과 생성시 프롬프트만 입력으로 받음

  • 검색과정에서 임베딩 벡터를 보냄

  • 검색 결과는 프롬프트 모델에서 사용자의 요청과 하나로 통합됨

    Example

    1. Context정보 -> 임베딩 모델 -> 문서 임베딩으로 변환 -> 벡터DB 저장
    2. Question을 쿼리 임베딩으로 변환해 검색했을때 두 임베딩이 가까워 검색 결과로 반환
    3. 프롬프트에 추가

9.1.3 실습: 라마인덱스로 RAG 구현하기

  • 라마인덱스: 대표적인 LLM 오케스트레이션 라이브러리
  • KLUE MRC 데이터셋 활용 질문 답변 RAG구현
  1. KLUE MRC 데이터셋 내려받기
  2. OpenAI 임베딩 모델 API키 설정
  3. dataset 라이브러리의 load_dataset 함수에 데이터셋 이름, 서브셋 이름 입력
  4. 허깅페이스 데이터셋 허브가 KLUE 데이터셋 자동 다운로드

9.2 LLM 캐시

  • LLM추론을 수행할 때 사용자의 요청과 생성 결과 기록
  • 동일하거나, 비슷한 요청 들어오면 새롭게 텍스트 생성 X
  • 이전의 생성 결과를 가져와 바로 응답
  • LLM 생성 요청 줄임
  • 비용, 지연시간 감소
  • LLM 애플리케이션 효율적 운영 가능

9.2.1 LLM 캐시 작동 원리

  • 프롬프트 통합과 LLM 생성 사이에 위치
  • 일치 캐시 exact match
    - 문자열 그대로 동일하지를 판단
    • 자료구조에 프롬프트와 그에 대한 응답 저장
    • 새로운 요청이 들어왔을 때 딕셔너리 키에 동일한 프롬프트가 있는지 확인
  • 유사 검색 캐시 similar search
    - 문자열을 임베딩 모델을 통해 변환한 임베딩 벡터를 비교
    워크플로
    1. 요청
    2. 요청을 임베딩 모델로 임베딩 벡터로 변환
    3. 벡터 데이터베이스에 유사한 요청 있었는지 검색
      3-1 있으면 저장된 텍스트 반환
      3-2 없으면 LLM으로 새롭게 텍스트 생성 응답
      3-2-1 벡터DB에 요청의 임베딩 벡터와 생성 결과 저장

9.2.2 실습: OpenAI API 캐시 구현

실습 준비

  • 파이썬 딕셔너리, 크로마사용
  • OpenAI 클라이언트: 언어 모델과 임베딩 모델 사용가능
  • 크로마 벡터DB 클라이언트: 임베딩 벡터를 저장하고 검색할 때 사용

LLM 캐시를 사용하지 않았을 때 동일한 요청 처리에 걸린 시간 확인

일치캐시

  • 일치캐시 구현
    1. init 메서드에서 파이썬 딕셔너리로 Prompt와 그 응답을 저장할 일치 LLM 캐시(self.cache) 생성
    1. 입력받은 prompt가 self.cache에 없으면 OpenAICache 클래스에서 generate 메서드 사용해 새로운 텍스트 생성.
    2. 생성 결과를 이후 사용 위해 self.cache에 저장
    3. 캐시에 동일한 prompt 있으면 캐시에 저장된 응답 그대로 반환

유사 검색 캐시

  • 유사검색캐시 구현
    1. 일치캐시를 통해 동일한 프롬프트가 있는지 확인
    1. 벡터DB에 등록된 임베딩 모델 사용해 텍스트를 임베딩으로 변환 후 검색 수행
    2. 검색 결과 존재 확인, 검색한 문서와 검색 결과 문서 사이의 거리가 가까운지 확인. 조건 만족시 검색된 문서 반환
    3. 조건 불만족시 새로운 결과 생성
    4. 이후 LLM 캐시에서 활용할 수 있도록 일치캐시와 유사검색캐시에 저장

9.3 데이터 검증

데이터 검증이란?

벡터 검색 결과나 LLM 생성 결과에 포함되지 않아야 하는 데이터를 필터링하고 답변을 피해야 하는 요청을 선별함으로써 LLM 애플리케이션이 생성한 텍스트로 인해 생길 수 있는 문제를 줄이는 방법

  • 생성형 AI 서비스의 경우 사용자의 요청이 다양 --> LLM의 생성 결과 예측 어려움
  • 사용자의 요청 중에 적절하지 않은 요청 응답 거부
  • 검색 결과나 LLM의 생성 결과에 적절하지 않은 내용 포함됐는지 확인하는 절차 필요

9.3.1 데이터 검증 방식

LLM 생성 데이터가 형식에 맞는지, 회사의 정책이나 가이드라인과 충돌하는 내용은 없는지 확인 필요.

확인 방법 네가지

  1. 규칙 기반
    문자열 매칭이나 정규 표현식 활용해 데이터 확인
    ex) 개인 정보 중 휴대폰 번호 삭제시 --> 패턴 찾아 변환

  2. 분류 또는 회귀 모델
    명확한 문자열 패턴이 없는 경우
    ex) 지나치게 부정적인 생성 결과 피하고 싶다면 긍/부정 분류 모델 만들어 활용
    --> 부정 스코어가 일정 점수 이상이면 다시 생성하도록 로직 개발

  3. 임베딩 유사도 기반
    특정 테마와 관련된 텍스트를 임베딩 벡터로 만들어 요청의 임베딩과 유사한 경우 답변 회피

  4. LLM 활용
    요청이나 응답에 특정 테마에 관한 내용이 포함돼 있다면 예/아니오로 답해서 LLM에 전달
    응답에 따라 해당 내용을 다시 생성 or 삭제

9.3.2 데이터 검증 실습

NeMo-Guardrails 라이브러리(by. 엔비디아) 활용 특정 주제에 대한 답변을 피하는 기능 구현

  1. 상황(문장) 정의
  2. NeMo-Guardrails가 문장을 임베딩 벡터로 변환해서 저장. 유사한 요청시 내용 판단
  3. 요청에 대한 응답을 bot과 행동으로 정의
  4. 사용자의 요청과 봇의 응답을 하나로 묶어 어떤 요청에 어떤 응답 반환 할지 정의
    • NeMo-Guardrails 흐름과 요청/응답 정의
    • 요리에 대한 응답 피하기
    • 사용자의 요청에 악의적 목적이 있는지 검증하고 대응

9.4 데이터 로깅

  • 사용자의 입력과 LLM이 생성한 출력을 기록
  • 입력이 동일해도 출력이 달라질 수 있기 때문에 어떤 입력에서 어떤 출력을 반환했는지 반드시 기록해야함.
  • 서비스 운영, 애플리케이션 개선 및 고도화에 사용
  • 대표적 로깅 도구: W&B(Weight and Bias), MLflow, PromptLayer등
  • 여러 도구가 비슷한 기능 제공, 그러므로 도구 선택시 기존에 사용하고 있는 도구인지, 오픈소스로 직접 구축, 유료 서비스 사용등 고려해 선택.

9.4 데이터 로깅 - W&B

9.4.1 OpenAI API 로깅

상업용 LLM API 로깅하기 ( OpenAI )

  • 프롬프트, query를 OpenAI 클라이언트의 채팅 모델에 전달해 텍스트 생성
  • Trace 클래스에 OpenAI 요청의 입력, 생성결과, 생성 성공여부, 생성에 사용한 설정값등을 입력해 기록할 데이터 생성
  • Trace 클래스의 log 메서드를 사용해 로그를 W&B에 전달

예외 처리가 포함된 전체 예시 코드는 W&B 프롬프트 로깅 공식 문서에서 확인 가능.

https://docs.wandb.ai/ko/guides/prompts/quickstart/ (한국어)

9.4.2 라마인덱스 로깅

라마인덱스의 RAG과정을 W&B에 기록하기

  1. 라마인덱스에서 RAG 수행
  2. 라마인덱스 내부에서 W&B에 로그 전송하도록 설정: set_global_handler 함수 → 라마인덱스에서 내부적으로 LLM API 호출시 W&B에 기록을 남김
  3. 요청의 입력과 출력, 사용된 체인의 종류, 실행된 요소의 타임라인 등 정보 확인 가능

9.5 정리

RAG, LLM캐시, 데이터 검증, 데이터 로깅

  • LLM을 활용해 서비스를 개발할 때 많은 요소가 필요함.
  • LLM의 환각현상 및 학습 비용이 높은점이 문제
  • 벡터DB 활용한 RAG기술 적용 필요
  • LLM 캐시: LLM 추론에 많은 시간과 비용이 들어가므로 비슷한 요청은 캐시 사용
  • 싱글턴, 멀티턴 문제: 가능하면 싱글턴으로
  • 데이터 검증: 개인정보, 민감정보(정치, 사회문제등)
  • 데이터 로깅: 서비스에 들어온 요청, LLM이 생성한 응답 기록

0개의 댓글