7 minute read

📌 들어가며

학습을 보다 체계적으로 진행하기 위해 멀티에이전트 오케스트레이션 플랫폼 SmartHee 구축 과정을 Phase 1부터 Phase 4까지 총 4단계로 나누어 진행하였다.

이 포스트에서는 그중 첫 번째 단계인 Phase 1을 로컬에서 실행하면서 겪은 시행착오를 기록한다. Python 3.13 환경에서 LangChain/LangGraph 계열 패키지를 설치하는 과정에서 생각보다 많은 문제가 발생했고, 이 기록이 같은 환경에서 시작하는 분들께 도움이 되길 바란다.

환경 정보

  • OS: Windows 10 (10.0.26200)
  • Python: 3.13
  • Docker Desktop 설치됨
  • OpenAI API 키 보유

📁 프로젝트 구조

본격적인 백엔드 마이크로서비스 및 에이전트 연동 실습을 위해 프로젝트 구조를 아래와 같이 구성하였다.

Phase1/                        
├── .env.example
├── docker-compose.yml
├── README.md
├── backend/
│   ├── main.py
│   ├── requirements.txt
│   ├── core/
│   ├── agents/
│   ├── api/
│   └── models/
└── frontend/
    └── src/                   ← 비어있음 (Phase 1은 백엔드만)

참고: frontend/src는 비어있는 게 정상이다. Phase 1은 FastAPI 백엔드 중심이며 프론트엔드는 이후 단계에서 추가할 계획이다. 지금은 http://localhost:8000/docs (Swagger UI)로 API를 테스트한다.


�️ 왜 PostgreSQL을 선택했나?

수많은 RDBMS 중에서 PostgreSQL을 선택한 주요 이유는 다음과 같다.

항목 PostgreSQL MySQL SQLite
JSON/JSONB 지원 ✅ 네이티브 지원 (인덱싱 포함) 제한적
비동기 드라이버 asyncpg (고성능) aiomysql
확장성 풍부한 확장 (pgvector 등) 제한적
트랜잭션 안정성 ACID 완전 준수 제한적
실무 사용 빈도 매우 높음 높음 소규모에 적합

특히 AgentForge 프로젝트에서 PostgreSQL을 선택한 결정적인 이유는 두 가지다.

  1. asyncpg 비동기 드라이버: FastAPI + SQLAlchemy의 비동기 처리와 궁합이 가장 좋으며, 고성능 비동기 I/O를 기본으로 지원한다.
  2. pgvector 확장 호환성: 이후 Phase에서 벡터 검색(RAG) 기능을 추가할 때 PostgreSQL의 pgvector 확장을 그대로 활용할 수 있어, 별도의 벡터 DB 없이도 구현이 가능하다.

�🚀 Step 1 — Docker로 PostgreSQL + Redis 실행

# 루트 폴더(docker-compose.yml 있는 곳)에서 실행
docker-compose up -d postgres redis

문제 1: Docker Desktop이 꺼져있었다

unable to get image 'postgres:16-alpine': error during connect
open //./pipe/dockerDesktopLinuxEngine: The system cannot find the file specified.

원인: Docker Desktop 프로그램 자체가 실행되지 않은 상태.

해결: 시작 메뉴에서 Docker Desktop을 먼저 실행하고, 트레이 아이콘의 고래🐋가 정지 상태가 된 후 다시 시도한다.

문제 2: docker-compose version 경고

the attribute `version` is obsolete, it will be ignored

원인: 최신 Docker Compose는 version 속성을 더 이상 사용하지 않음.

해결: 경고일 뿐 오류가 아니므로 무시해도 된다. 컨테이너가 정상적으로 뜨는지만 확인하면 된다.

docker ps
# postgres, redis 컨테이너 2개가 Up 상태이면 정상

💻 Step 2 — Python 가상환경 생성

# backend/ 폴더에서 실행
cd backend
python -m venv venv
venv\Scripts\activate
python -m pip install --upgrade pip

📦 Step 3 — 패키지 설치 (가장 삽질이 많았던 구간)

문제 3: numpy 빌드 실패 (Python 3.13 핵심 문제)

ERROR: Unknown compiler(s): [['icl'], ['cl'], ['cc'], ['gcc'], ['clang'], ...]
numpy==1.26.4 → Python 3.13용 pre-built wheel 없음 → C 컴파일러 필요 → 실패

원인: langchain==0.3.0numpy<2.0.0을 요구하는데, numpy 1.x는 Python 3.13을 지원하지 않는다. Python 3.13용 wheel이 없으니 소스 빌드를 시도하다가 컴파일러가 없어서 실패.


문제 4: asyncpg, psycopg2-binary 빌드 실패

ERROR: Failed building wheel for asyncpg
ERROR: Failed to build 'psycopg2-binary'

원인: 동일하게 Python 3.13용 pre-built wheel이 없는 구버전 명시.


문제 5: ResolutionImpossible — 버전 충돌

ERROR: ResolutionImpossible: for help visit https://pip.pypa.io/...

원인: 패키지 간 의존하는 하위 패키지 버전이 서로 충돌. 예를 들어 langchain 1.xlangchain-openai 0.x가 요구하는 langchain-core 버전이 다름.


최종 해결: 버전 고정 해제

버전을 명시할수록 충돌이 계속 발생했다. 결론은 버전 고정을 해제하고 pip가 호환 가능한 버전 조합을 자동으로 찾게 하는 것이다.

# requirements.txt (최종 버전)

# Core
fastapi
uvicorn[standard]
python-dotenv
pydantic
pydantic-settings

# LangChain / LangGraph
langchain
langchain-openai
langchain-anthropic
langchain-community
langgraph

# Database
sqlalchemy
alembic
asyncpg
psycopg2-binary
redis

# Tools
tavily-python
httpx
pip install -r requirements.txt

설치 완료 후 실제 설치된 버전을 잠금 파일로 저장해두면 나중에 재현할 때 유용하다.

pip freeze > requirements_lock.txt

팁: requirements.txt는 버전 미지정(유연)으로 유지하고, requirements_lock.txt는 실제 설치된 버전 고정 용도로 분리해서 관리하는 것이 실무 방식이다.


⚙️ Step 4 — .env 파일 설정

문제 6: .env 파일 위치 혼동

.env.example은 루트 폴더에 있다. backend/ 안에서 copy하면 경로 오류가 발생한다.

# 반드시 루트 폴더(Phase1/)에서 실행
cd ..        # backend에서 상위로 이동
copy .env.example .env

문제 7: DATABASE_URL 중복 입력

.env를 수동으로 편집하다가 아래처럼 키가 두 번 들어가는 실수를 했다.

# 잘못된 경우
DATABASE_URL=DATABASE_URL=postgresql+asyncpg://agentforge:<password>@localhost:5432/agentforge

# 올바른 경우
DATABASE_URL=postgresql+asyncpg://agentforge:<password>@localhost:5432/agentforge

확인 방법:

type .env | findstr DATABASE_URL

문제 8: config.py 기본값 문제

backend/core/config.py에 DATABASE_URL 기본값이 user:<password>로 하드코딩되어 있었다. .env 파일이 제대로 읽히지 않으면 이 기본값이 사용되어 인증 실패가 발생한다.

asyncpg.exceptions.InvalidPasswordError: password authentication failed for user "user"

해결: .env 파일을 backend/ 폴더 안에도 복사해준다. uvicorn이 backend/에서 실행되기 때문에 해당 위치에서 .env를 찾는다.

# 루트에서
copy .env backend\.env

또는 config.py의 기본값을 직접 수정한다.

# core/config.py
DATABASE_URL: str = "postgresql+asyncpg://agentforge:<password>@localhost:5432/agentforge"

최종 .env 구성

# LLM
OPENAI_API_KEY=sk-...      # 실제 키 입력
DEFAULT_MODEL=openai/gpt-4o

# Database
DATABASE_URL=postgresql+asyncpg://agentforge:<password>@localhost:5432/agentforge
REDIS_URL=redis://localhost:6379/0

# Tools (없으면 비워도 됨)
TAVILY_API_KEY=

# Observability (Phase 2에서 사용)
LANGFUSE_PUBLIC_KEY=
LANGFUSE_SECRET_KEY=

# App
SECRET_KEY=local-dev-secret
DEBUG=true

⚠️ 보안 주의: API 키를 채팅, 이슈, 커밋 메시지 등 외부에 절대 노출하지 말 것. 노출됐다면 즉시 해당 키를 Revoke하고 재발급한다.


🏃‍♂️ Step 5 — 서버 실행

문제 9: 루트 폴더에서 uvicorn 실행

ERROR: Error loading ASGI app. Could not import module "main".

원인: main.pybackend/ 안에 있는데 루트에서 실행한 경우.

해결: 반드시 backend/ 폴더 안에서 실행한다.

cd backend
uvicorn main:app --reload --port 8000

문제 10: venv 비활성화 상태에서 실행

ModuleNotFoundError: No module named 'asyncpg'

원인: venv가 활성화되지 않은 상태에서 실행하면 시스템 Python을 사용하게 되어 설치한 패키지를 못 찾는다.

해결: 항상 venv 활성화 후 실행한다.

venv\Scripts\activate
# 프롬프트 앞에 (venv)가 붙어야 정상
uvicorn main:app --reload --port 8000

🎉 최종 성공

INFO:main:🚀 AgentForge 서버 시작
INFO:sqlalchemy.engine.Engine:BEGIN (implicit)
...
INFO:     Application startup complete.

Swagger UI 접속: http://127.0.0.1:8000/docs

정상적으로 서버가 구동되고 문서에 접속하면 아래와 같이 AgentForge API에 대한 Swagger 화면이 나타난다.

Swagger UI 1 Swagger UI 2

루트(/)는 Not Found가 뜨는 게 정상이다. /docs로 접속해야 API 테스트 UI가 나온다.


� API 엔드포인트 상세 설명

Swagger UI에서 확인할 수 있는 AgentForge API의 전체 엔드포인트 목록과 각 기능에 대한 설명이다.

🤖 Agents — 에이전트 빌드 및 실행

메서드 경로 설명
POST /api/v1/builder/create 에이전트 생성 — 목적(goal), 사용할 툴, 시스템 프롬프트 등을 정의하여 새로운 에이전트 설정(agent_config)을 DB에 저장한다.
POST /api/v1/builder/refine 에이전트 개선 — 기존 에이전트 설정을 바탕으로 프롬프트나 툴 구성을 개선한 새 버전을 생성한다.
POST /api/v1/agents/run 에이전트 실행 — 저장된 에이전트 설정 ID와 입력값(input)을 받아 LangGraph를 통해 LLM을 호출하고, 결과를 agent_runs 테이블에 저장한다.
GET /api/v1/agents/run/{run_id} 실행 결과 조회 — 특정 run_id에 해당하는 에이전트 실행 결과(output, status, 비용 등)를 조회한다.
GET /api/v1/tools 사용 가능한 툴 목록 조회 — 에이전트에 연결할 수 있는 툴(예: Tavily 웹검색 등) 목록을 반환한다.
GET /api/v1/models 사용 가능한 모델 목록 조회 — 에이전트에 연결 가능한 LLM 모델(예: openai/gpt-4o) 목록을 반환한다.

📊 Evaluation — LLM Judge 평가

Phase 1 코드에 라우트 파일(evaluation.py)이 이미 포함되어 있어 Swagger UI에 표시된다. 실제 평가 로직과 Langfuse 연동은 Phase 2에서 구현된다.

메서드 경로 설명
POST /api/v1/eval/run/{run_id} 에이전트 실행 결과 평가 — 특정 run에 대해 LLM Judge가 응답 품질(정확성, 관련성 등)을 자동으로 평가하고 점수를 저장한다.
POST /api/v1/eval/abtest A/B 테스트 — 두 개의 에이전트 설정(또는 모델)을 동일한 입력으로 실행하고 결과를 비교 평가한다.
GET /api/v1/eval/agent/{agent_id}/stats 에이전트 통계 조회 — 특정 에이전트의 누적 실행 횟수, 평균 점수, 평균 비용 등의 통계를 반환한다.

🔧 default

메서드 경로 설명
GET /health 헬스체크 — 서버가 정상적으로 실행 중인지 확인하는 엔드포인트. {"status": "ok"} 형태로 응답한다.

📐 Schemas — 요청/응답 데이터 구조

스키마 설명
BuildRequest 에이전트 생성 요청 바디 — goal, tools, system_prompt 등 포함
RefineRequest 에이전트 개선 요청 바디 — 기존 agent_config_id 및 개선 방향 포함
RunRequest 에이전트 실행 요청 바디 — agent_config_id, input 포함
ABTestRequest A/B 테스트 요청 바디 — 두 개의 agent_config_id 및 공통 input 포함
HTTPValidationError FastAPI 표준 유효성 검증 오류 응답
ValidationError 세부 필드 유효성 검증 오류

�📝 전체 실행 순서 정리

# 1. Docker Desktop 실행 (GUI)

# 2. PostgreSQL + Redis 컨테이너 실행 (Phase1 루트에서)
docker-compose up -d postgres redis
docker ps   # 두 컨테이너 Up 상태 확인

# 3. 가상환경 생성 및 패키지 설치 (backend/에서)
cd backend
python -m venv venv
venv\Scripts\activate
python -m pip install --upgrade pip
pip install -r requirements.txt

# 4. .env 설정
cd ..
copy .env.example .env
# .env 편집: API 키, DATABASE_URL 등 입력
copy .env backend\.env   # backend 폴더에도 복사

# 5. 서버 실행
cd backend
venv\Scripts\activate
uvicorn main:app --reload --port 8000

# 6. 접속
# http://127.0.0.1:8000/docs

💡 트러블슈팅 요약

문제 원인 해결
numpy 빌드 실패 langchain이 numpy<2.0 강제 → Python 3.13용 wheel 없어 빌드 실패 requirements.txt 버전 고정 해제 (pip 자동 해결)
asyncpg/psycopg2 빌드 실패 Python 3.13 wheel 없는 구버전 버전 미지정으로 pip 자동 해결
ResolutionImpossible 패키지 간 버전 충돌 전체 버전 고정 해제
Docker 연결 실패 Docker Desktop 미실행 Docker Desktop 먼저 실행
.env 위치 혼동 backend/에서 copy 시도 루트에서 복사 후 backend/에도 복사
DATABASE_URL 파싱 실패 키 이름 중복 입력 type .env \| findstr DATABASE_URL로 확인
InvalidPasswordError config.py 기본값 user: .env를 backend/에도 복사
main 모듈 못 찾음 루트에서 uvicorn 실행 backend/ 폴더에서 실행
asyncpg ModuleNotFoundError venv 비활성화 상태 venv\Scripts\activate 후 실행

핵심 교훈: Python 3.13은 아직 일부 패키지의 pre-built wheel 지원이 불완전하다. 버전을 엄격하게 고정하기보다는 pip에게 해결을 맡기고, 설치 완료 후 pip freeze로 실제 버전을 기록하는 방식이 효율적이다.


🎯 다음 단계

Phase 2에서는 Langfuse 트레이싱LLM Judge 기반 평가 시스템을 추가할 예정이다.

Comments