[SmartHee] Phase 1 로컬 실행 삽질기 — Python 3.13 환경에서 FastAPI + LangGraph 세팅하기
📌 들어가며
학습을 보다 체계적으로 진행하기 위해 멀티에이전트 오케스트레이션 플랫폼 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을 선택한 결정적인 이유는 두 가지다.
asyncpg비동기 드라이버: FastAPI + SQLAlchemy의 비동기 처리와 궁합이 가장 좋으며, 고성능 비동기 I/O를 기본으로 지원한다.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.0이 numpy<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.x와 langchain-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.py는 backend/ 안에 있는데 루트에서 실행한 경우.
해결: 반드시 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 화면이 나타난다.

루트(/)는 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