PDF 분석과 채팅

PDF 분석과 채팅

Docker와 생성형 AI를 사용해 PDF 문서를 파싱하고 응답을 생성하는 PDF 봇을 만드는 방법을 배워볼게요.

출처: 문서

본문

GenAI(생성형 AI) 가이드는 Docker를 사용해 기존 GenAI 어플리케이션을 컨테이너화하는 방법을 알려줘요. 이 가이드에서 배울 내용:

  • Python 기반 GenAI 어플리케이션 컨테이너화 및 실행하기
  • 개발용으로 전체 GenAI 스택을 로컬에서 실행할 로컬 환경 설정하기

먼저 기존 GenAI 어플리케이션을 컨테이너화하는 것부터 시작해볼게요.

생성형 AI 어플리케이션 컨테이너화하기

사전 요구사항

참고: GenAI 어플리케이션은 종종 GPU 가속의 혜택을 받을 수 있어요. 현재 Docker Desktop은 Windows에서만 WSL2 백엔드 로 GPU 가속을 지원해요. Linux 사용자도 Docker Engine 의 네이티브 설치로 GPU 가속에 접근할 수 있어요.

  • 최신 버전의 Docker Desktop 을 설치했거나, Linux 사용자이고 GPU 가속을 사용할 계획이라면 Docker Engine 을 설치했어야 해요. Docker는 정기적으로 새 기능을 추가하므로 이 가이드의 일부 부분은 최신 버전의 Docker Desktop에서만 작동할 수 있어요.
  • git 클라이언트 가 있어야 해요. 이 섹션의 예제는 명령줄 기반 git 클라이언트를 사용하지만, 어떤 클라이언트든 사용할 수 있어요.

개요

이 섹션은 Docker Desktop을 사용해 생성형 AI(GenAI) 어플리케이션을 컨테이너화하는 과정을 안내해요.

참고: 컨테이너화된 GenAI 어플리케이션의 더 많은 샘플은 GenAI Stack 데모 어플리케이션 에서 볼 수 있어요.

샘플 어플리케이션 가져오기

이 가이드에서 사용하는 샘플 어플리케이션은 GenAI Stack 데모 어플리케이션 의 PDF Reader 어플리케이션의 수정 버전이에요. 이 어플리케이션은 PDF 파일에 대해 질문할 수 있게 해주는 풀스택 Python 어플리케이션이에요.

이 어플리케이션은 오케스트레이션에 LangChain , UI에 Streamlit , LLM 실행에 Ollama , 벡터 저장에 Neo4j 를 사용해요.

샘플 어플리케이션을 클론해주세요. 터미널을 열고 작업할 디렉터리로 이동한 뒤, 다음 명령으로 리포지토리를 클론해주세요:

$ git clone https://github.com/craig-osterhout/docker-genai-sample

이제 docker-genai-sample 디렉터리에 다음 파일이 있어야 해요.

├── docker-genai-sample/
│   ├── .gitignore
│   ├── app.py
│   ├── chains.py
│   ├── env.example
│   ├── requirements.txt
│   ├── util.py
│   ├── LICENSE
│   └── README.md

Docker 자산 만들기

이제 어플리케이션이 있으니 컨테이너화하는 데 필요한 Docker 자산을 만들 수 있어요.

팁: Gordon 은 내 어플리케이션에 맞춘 .dockerignore 를 제안해줘요.

docker-genai-sample 디렉터리에 다음 파일들을 만드세요.

Dockerfile

# syntax=docker/dockerfile:1
# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Dockerfile reference guide at
# https://docs.docker.com/go/dockerfile-reference/
ARG PYTHON_VERSION=3.11.4
FROM python:${PYTHON_VERSION}-slim as base
# Prevents Python from writing pyc files.
ENV PYTHONDONTWRITEBYTECODE=1
# Keeps Python from buffering stdout and stderr to avoid situations where
# the application crashes without emitting any logs due to buffering.
ENV PYTHONUNBUFFERED=1
WORKDIR /app
# Create a non-privileged user that the app will run under.
# See https://docs.docker.com/go/dockerfile-user-best-practices/
ARG UID=10001
RUN adduser \
  --disabled-password \
  --gecos "" \
  --home "/nonexistent" \
  --shell "/sbin/nologin" \
  --no-create-home \
  --uid "${UID}" \
  appuser
# Download dependencies as a separate step to take advantage of Docker's caching.
# Leverage a cache mount to /root/.cache/pip to speed up subsequent builds.
# Leverage a bind mount to requirements.txt to avoid having to copy them into
# into this layer.
RUN --mount=type=cache,target=/root/.cache/pip \
  --mount=type=bind,source=requirements.txt,target=requirements.txt \
  python -m pip install -r requirements.txt
# Switch to the non-privileged user to run the application.
USER appuser
# Copy the source code into the container.
COPY . .
# Expose the port that the application listens on.
EXPOSE 8000
# Run the application.
CMD ["streamlit", "run", "app.py", "--server.address=0.0.0.0", "--server.port=8000"]

compose.yaml

# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Docker Compose reference guide at
# https://docs.docker.com/go/compose-spec-reference/
# Here the instructions define your application as a service called "server".
# This service is built from the Dockerfile in the current directory.
# You can add other services your application may depend on here, such as a
# database or a cache. For examples, see the Awesome Compose repository:
# https://github.com/docker/awesome-compose
services:
  server:
    build:
      context: .
    ports:
      - 8000:8000
# The commented out section below is an example of how to define a PostgreSQL
# database that your application can use. `depends_on` tells Docker Compose to
# start the database before your application. The `db-data` volume persists the
# database data between container restarts. The `db-password` secret is used
# to set the database password. You must create `db/password.txt` and add
# a password of your choosing to it before running `docker compose up`.
# depends_on:
#   db:
#     condition: service_healthy
# db:
#   image: postgres
#   restart: always
#   user: postgres
#   secrets:
#     - db-password
#   volumes:
#     - db-data:/var/lib/postgresql/data
#   environment:
#     - POSTGRES_DB=example
#     - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
#   expose:
#     - 5432
#   healthcheck:
#     test: [ "CMD", "pg_isready" ]
#     interval: 10s
#     timeout: 5s
#     retries: 5
# volumes:
#   db-data:
# secrets:
#   db-password:
#     file: db/password.txt

.dockerignore

# Include any files or directories that you don't want to be copied to your
# container here (e.g., local build artifacts, temporary files, etc.).
#
# For more help, visit the .dockerignore file reference guide at
# https://docs.docker.com/go/build-context-dockerignore/
**/.DS_Store
**/__pycache__
**/.venv
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/bin
**/charts
**/docker-compose*
**/compose.y*ml
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md

이제 docker-genai-sample 디렉터리에 다음 내용이 있어야 해요.

├── docker-genai-sample/
│   ├── .dockerignore
│   ├── .gitignore
│   ├── app.py
│   ├── chains.py
│   ├── compose.yaml
│   ├── env.example
│   ├── requirements.txt
│   ├── util.py
│   ├── Dockerfile
│   ├── LICENSE
│   └── README.md

이 파일들에 대해 더 알려면 Dockerfile , .dockerignore , compose.yaml 을 참고하세요.

어플리케이션 실행하기

docker-genai-sample 디렉터리 안에서 터미널에 다음 명령을 실행해주세요.

$ docker compose up --build

Docker가 어플리케이션을 빌드하고 실행해요. 네트워크 연결에 따라 모든 의존성을 다운로드하는 데 몇 분이 걸릴 수 있어요. 어플리케이션이 실행 중일 때 터미널에서 다음처럼 생긴 메시지를 보게 돼요.

server-1 | You can now view your Streamlit app in your browser.
server-1 |
server-1 | URL: http://0.0.0.0:8000
server-1 |

브라우저를 열고 http://localhost:8000 에서 어플리케이션을 확인해주세요. 간단한 Streamlit 어플리케이션이 보여야 해요. 어플리케이션이 임베딩 모델을 다운로드하는 데 몇 분 걸릴 수 있어요. 다운로드가 진행되는 동안 오른쪽 상단에 Running 이 나타나요.

어플리케이션이 작동하려면 Neo4j 데이터베이스 서비스와 LLM 서비스가 필요해요. Docker 밖에서 실행한 서비스에 접근할 수 있다면 연결 정보를 지정하고 시도해보세요. 서비스가 실행되고 있지 않다면 이 가이드를 계속 진행해 Docker로 이 서비스들의 일부 또는 전부를 실행하는 방법을 배워보세요.

터미널에서 ctrl + c 를 눌러 어플리케이션을 중지해주세요.

요약

이 섹션에서 Docker로 GenAI 어플리케이션을 컨테이너화하고 실행하는 방법을 배웠어요.

다음 단계

다음 섹션에서는 어플리케이션, 데이터베이스, LLM 서비스를 모두 Docker로 로컬에서 실행하는 방법을 배워요.

생성형 AI 개발에 컨테이너 사용하기

사전 요구사항

생성형 AI 어플리케이션 컨테이너화하기 를 완료하세요.

개요

이 섹션에서는 생성형 AI(GenAI) 어플리케이션이 필요로 하는 모든 서비스에 접근할 개발 환경을 설정하는 방법을 배워요. 여기에는 다음이 포함돼요:

  • 로컬 데이터베이스 추가
  • 로컬 또는 원격 LLM 서비스 추가

참고: 컨테이너화된 GenAI 어플리케이션의 더 많은 샘플은 GenAI Stack 데모 어플리케이션 에서 볼 수 있어요.

로컬 데이터베이스 추가

컨테이너로 데이터베이스 같은 로컬 서비스를 설정할 수 있어요. 이 섹션에서는 데이터베이스 서비스를 정의하도록 compose.yaml 파일을 업데이트해요. 또한 매번 수동으로 정보를 입력하는 대신 데이터베이스 연결 정보를 로드할 환경 변수 파일을 지정할 거예요.

데이터베이스 서비스를 실행하려면:

클론된 리포지토리의 디렉터리에서 env.example 파일 이름을 .env 로 바꿔주세요. 이 파일은 컨테이너가 사용할 환경 변수를 담아요.

클론된 리포지토리의 디렉터리에서 IDE 또는 텍스트 편집기로 compose.yaml 파일을 열어주세요.

compose.yaml 파일에 다음을 추가해주세요:

  • Neo4j 데이터베이스를 실행하는 지침 추가
  • 연결용 환경 변수를 전달하기 위해 server 서비스 아래에 환경 파일 지정

다음은 업데이트된 compose.yaml 파일이에요. 모든 주석이 제거됐어요.

services:
  server:
    build:
      context: .
    ports:
      - 8000:8000
    env_file:
      - .env
    depends_on:
      database:
        condition: service_healthy
  database:
    image: neo4j:5.11
    ports:
      - "7474:7474"
      - "7687:7687"
    environment:
      - NEO4J_AUTH=${NEO4J_USERNAME}/${NEO4J_PASSWORD}
    healthcheck:
      test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider localhost:7474 || exit 1"]
      interval: 5s
      timeout: 3s
      retries: 5

참고: Neo4j에 대해 더 알려면 Neo4j 공식 Docker 이미지 를 참고하세요.

어플리케이션을 실행해주세요. docker-genai-sample 디렉터리 안에서 터미널에 다음 명령을 실행해주세요.

$ docker compose up --build

어플리케이션에 접근하세요. 브라우저를 열고 http://localhost:8000 에서 어플리케이션을 확인해주세요. 간단한 Streamlit 어플리케이션이 보여야 해요. .env 파일에 지정된 LLM 서비스가 아직 실행되고 있지 않으므로 PDF에 질문하면 어플리케이션이 실패한다는 점에 주의하세요.

어플리케이션을 중지하세요. 터미널에서 ctrl + c 를 눌러 어플리케이션을 중지해주세요.

로컬 또는 원격 LLM 서비스 추가

샘플 어플리케이션은 Ollama 와 OpenAI 를 모두 지원해요. 이 가이드는 다음 시나리오에 대한 지침을 제공해요:

  • 컨테이너에서 Ollama 실행
  • 컨테이너 밖에서 Ollama 실행
  • OpenAI 사용

모든 플랫폼이 이전 시나리오 중 아무거나 사용할 수 있지만, 성능과 GPU 지원은 다를 수 있어요. 적절한 옵션을 선택하는 데 도움이 되는 지침을 사용할 수 있어요:

  • Linux에서 네이티브 Docker Engine 설치를 사용하거나 Windows 10/11에서 Docker Desktop을 사용하고, CUDA 지원 GPU가 있으며, 시스템에 최소 8GB RAM이 있다면 컨테이너에서 Ollama를 실행하세요.
  • Apple silicon Mac에 있다면 컨테이너 밖에서 Ollama를 실행하세요.
  • 이전 두 시나리오가 적용되지 않는다면 OpenAI를 사용하세요.

LLM 서비스에 대해 다음 옵션 중 하나를 선택하세요.

컨테이너에서 Ollama 실행

컨테이너에서 Ollama를 실행할 때는 CUDA 지원 GPU가 있어야 해요. 지원 GPU 없이도 컨테이너에서 Ollama를 실행할 수는 있지만 성능이 만족스럽지 않을 수 있어요. GPU 접근을 지원하는 것은 Linux와 Windows 11뿐이에요.

컨테이너에서 Ollama를 실행하고 GPU 접근을 제공하려면:

사전 요구사항을 설치하세요.

  • Linux의 Docker Engine에서는 NVIDIA Container Toolkit 을 설치하세요.
  • Windows 10/11의 Docker Desktop에서는 최신 NVIDIA 드라이버 를 설치하고 WSL2 백엔드 를 사용하고 있는지 확인하세요.

compose.yaml 에 Ollama 서비스와 볼륨을 추가해주세요. 다음은 업데이트된 compose.yaml 이에요:

services:
  server:
    build:
      context: .
    ports:
      - 8000:8000
    env_file:
      - .env
    depends_on:
      database:
        condition: service_healthy
  database:
    image: neo4j:5.11
    ports:
      - "7474:7474"
      - "7687:7687"
    environment:
      - NEO4J_AUTH=${NEO4J_USERNAME}/${NEO4J_PASSWORD}
    healthcheck:
      test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider localhost:7474 || exit 1"]
      interval: 5s
      timeout: 3s
      retries: 5
  ollama:
    image: ollama/ollama:latest
    ports:
      - "11434:11434"
    volumes:
      - ollama_volume:/root/.ollama
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
volumes:
  ollama_volume:

참고: Compose 지침에 대한 자세한 내용은 Docker Compose로 GPU 접근 켜기 를 참고하세요.

compose.yaml 파일에 ollama-pull 서비스를 추가해주세요. 이 서비스는 GenAI Stack의 pull_model.Dockerfile 에 기반한 docker/genai:ollama-pull 이미지를 사용해요. 이 서비스는 Ollama 컨테이너용 모델을 자동으로 내려받아요. 다음은 compose.yaml 파일의 업데이트된 섹션이에요:

services:
  server:
    build:
      context: .
    ports:
      - 8000:8000
    env_file:
      - .env
    depends_on:
      database:
        condition: service_healthy
      ollama-pull:
        condition: service_completed_successfully
  ollama-pull:
    image: docker/genai:ollama-pull
    env_file:
      - .env
# ...

컨테이너 밖에서 Ollama 실행

호스트 머신에 Ollama 를 설치 하고 실행해주세요.

.env 파일의 OLLAMA_BASE_URL 값을 http://host.docker.internal:11434 로 업데이트해주세요.

다음 명령으로 모델을 Ollama에 내려받아주세요.

$ ollama pull llama2

OpenAI 사용

중요: OpenAI를 사용하려면 OpenAI 계정 이 필요해요. OpenAI는 타사 호스팅 서비스이며 요금이 부과될 수 있어요.

.env 파일의 LLM 값을 gpt-3.5 로 업데이트해주세요.

.env 파일의 OPENAI_API_KEY 값을 내 OpenAI API 키 로 주석 해제하고 업데이트해주세요.

GenAI 어플리케이션 실행하기

이 시점에서 Compose 파일에 다음과 같은 서비스가 있어요:

  • 메인 GenAI 어플리케이션용 서버 서비스
  • Neo4j 데이터베이스에 벡터를 저장하는 데이터베이스 서비스
  • (선택) LLM을 실행하는 Ollama 서비스
  • (선택) Ollama 서비스용 모델을 자동으로 내려받는 Ollama-pull 서비스

모든 서비스를 실행하려면 docker-genai-sample 디렉터리에서 다음 명령을 실행해주세요:

$ docker compose up --build

Compose 파일에 ollama-pull 서비스가 있다면 ollama-pull 서비스가 모델을 내려받는 데 몇 분이 걸릴 수 있어요. ollama-pull 서비스는 콘솔에 상태를 계속 업데이트해요. 모델을 내려받은 후 ollama-pull 서비스 컨테이너가 중지되고 어플리케이션에 접근할 수 있어요.

어플리케이션이 실행되면 브라우저를 열고 http://localhost:8000 에서 어플리케이션에 접근해주세요.

PDF 파일(예: Docker CLI Cheat Sheet )을 업로드하고 PDF에 대해 질문해주세요.

시스템과 선택한 LLM 서비스에 따라 응답하는 데 몇 분이 걸릴 수 있어요. Ollama를 사용 중이고 성능이 만족스럽지 않다면 OpenAI를 사용해보세요.

요약

이 섹션에서 GenAI 어플리케이션이 필요로 하는 모든 서비스에 접근할 개발 환경을 설정하는 방법을 배웠어요.

관련 정보:

  • Dockerfile 참조
  • Compose 파일 참조
  • Ollama Docker 이미지
  • Neo4j 공식 Docker 이미지
  • GenAI Stack 데모 어플리케이션

다음 단계

GenAI Stack 데모 어플리케이션 에서 더 많은 GenAI 어플리케이션 샘플을 확인해보세요.

더 알아보기 (Learn more)

  • GenAI Stack
  • LangChain
  • Ollama / Neo4j