1장 · 환경 준비¶
목표¶
빈 폴더에서 시작해서, 의존성이 설치된 실행 가능한 Python 프로젝트를 만듭니다. 이 장이 끝나면 uv run python으로 FastAPI를 import할 수 있는 상태가 됩니다.
준비물¶
| 도구 | 확인 명령 | 비고 |
|---|---|---|
| Python 3.10+ | python3 --version |
3.10·3.11·3.12 전 테스트 통과 검증. 3.9 이하 불가(X \| Y 타입 문법) |
| uv | uv --version |
없으면: curl -LsSf https://astral.sh/uv/install.sh \| sh |
| git | git --version |
uv가 뭔가요? Python의 패키지·가상환경 관리자입니다. Java 세계의 Maven/Gradle에 해당하며, uv sync 한 번으로 격리된 환경(.venv/) 생성과 의존성 설치를 끝냅니다. 자세한 대응표는 05 §1을 참고하세요.
따라하기¶
1. 프로젝트 폴더 생성¶
저장소 루트에서:
2. pyproject.toml 작성¶
Maven의 pom.xml에 해당하는 프로젝트 정의 파일입니다.
[project]
name = "prepay-agent"
version = "0.1.0"
description = "카드 선결제 에이전트 — 챗봇 WAS와 LLM 사이의 에이전틱 처리 계층"
requires-python = ">=3.10"
dependencies = [
"fastapi>=0.115",
"uvicorn[standard]>=0.30",
"pydantic>=2.7",
"pydantic-settings>=2.3",
]
[dependency-groups]
dev = [
"pytest>=8",
"httpx>=0.27",
]
[tool.uv]
package = false
[tool.pytest.ini_options]
pythonpath = ["."]
testpaths = ["tests"]
3. 의존성 설치¶
.venv/(격리된 실행 환경)와 uv.lock(잠금 파일)이 생깁니다. uv.lock은 커밋하고 .venv/는 커밋하지 않습니다 — Gradle의 lockfile은 커밋하고 빌드 산출물은 안 하는 것과 같습니다.
4. .env.example 작성¶
# 복사해서 .env 로 사용: cp .env.example .env
# .env 는 .gitignore 대상 — API key 를 절대 커밋하지 말 것 (docs/05 §5)
# LLM 연결 — 개발: OpenRouter / 운영: 사내 서빙 (Q9 확정 후 교체)
LLM_BASE_URL=https://openrouter.ai/api/v1
LLM_API_KEY=sk-or-여기에-본인-키
LLM_MODEL=openai/gpt-oss-120b
# Redis — Step 4 부터 사용 (접속 정보는 Q6 확정 시 갱신)
REDIS_URL=redis://localhost:6379/0
각자 cp .env.example .env 후 본인 키를 채웁니다. .env는 절대 커밋하지 않습니다 — 저장소 루트 .gitignore에 이미 등록되어 있습니다.
코드 해설 — pyproject.toml 한 줄씩¶
| 부분 | 의미 | Spring이라면 |
|---|---|---|
dependencies 4종 |
fastapi(웹 프레임워크) / uvicorn(서버) / pydantic(DTO+검증) / pydantic-settings(설정) | spring-web / 내장 Tomcat / Bean Validation / @ConfigurationProperties |
[dependency-groups] dev |
테스트에만 필요한 의존성 | Maven의 <scope>test</scope> |
[tool.uv] package = false |
이 프로젝트는 배포용 라이브러리가 아닌 애플리케이션이라는 선언 — 패키지 빌드 없이 의존성만 관리 | jar를 Maven Central에 올릴 게 아니라는 뜻 |
[tool.pytest.ini_options] pythonpath = ["."] |
테스트 실행 시 프로젝트 루트를 import 경로에 추가 — from app... import가 가능해짐 |
클래스패스 설정 |
LangGraph·openai 의존성이 아직 없는 게 눈에 띌 텐데, 의도적입니다. Step에 필요해지는 시점에 추가합니다(4장에서 langgraph, 5장에서 openai). 처음부터 다 넣지 않는 이유는, 각 의존성이 왜 필요한지를 추가하는 순간에 이해하기 위해서입니다.
동작 확인¶
이 장의 완성 코드 전문¶
아래 접이식 블록은 저장소 최종본과 자동 동기화됩니다 — 코드가 바뀌면 agent/ 에서 uv run python scripts/refresh_guide_code.py 가 문서를 다시 쓰고, 불일치는 tests/test_guide_sync.py 가 테스트 실패로 잡습니다(3장의 "문서의 예시 = fixture" 전략의 확장). 최종본에는 이후 장의 내용이 이미 반영되어 있습니다 — 예를 들어 pyproject 에는 4·5·6장에서 추가하는 langgraph·openai·checkpoint-redis 가 이미 들어 있습니다. 본문의 단계별 서사와 다른 부분은 각 장이 설명합니다.
전문 · agent/pyproject.toml
[project]
name = "prepay-agent"
version = "0.1.0"
description = "카드 선결제 에이전트 — 챗봇 WAS와 LLM 사이의 에이전틱 처리 계층"
requires-python = ">=3.10" # 하한은 3.10 (X | Y 타입 문법). 3.10·3.11·3.12 전 테스트 통과 검증됨
dependencies = [
"fastapi>=0.115",
"uvicorn[standard]>=0.30",
"pydantic>=2.7",
"pydantic-settings>=2.3",
"langgraph>=1.2.7",
"openai>=2.44.0",
"langgraph-checkpoint-redis>=0.5.0",
]
[dependency-groups]
dev = [
"pytest>=8",
"httpx>=0.27",
]
[tool.uv]
package = false
[tool.pytest.ini_options]
pythonpath = ["."]
testpaths = ["tests"]
전문 · agent/.env.example
# 복사해서 .env 로 사용: cp .env.example .env
# .env 는 .gitignore 대상 — API key 를 절대 커밋하지 말 것 (docs/05 §5)
# 서버 기동 (표준 기동법: uv run python -m app) — 포트는 CLI 가 아니라 여기서 관리
APP_HOST=127.0.0.1
APP_PORT=8800
APP_RELOAD=true
# LLM 구현 선택 — mock(가짜 규칙, 오프라인) | openai(실제 모델, Step 3 부터)
LLM_PROVIDER=openai
# LLM 요청/응답 JSONL 저장 위치 (빈 값이면 저장 안 함) — 프롬프트 튜닝·장애 분석용
LLM_LOG_DIR=logs/llm
# structured output(response_format) 사용 여부 — OpenRouter 는 하위 제공자들의
# constrained decoding 버그(응답 오염)가 관측되어 false 권장. 사내 서빙에선 true 로 재검증 (Q1)
LLM_STRUCTURED_OUTPUT=false
# LLM 연결 — 개발: OpenRouter / 운영: 사내 서빙 (Q9 확정 후 교체)
LLM_BASE_URL=https://openrouter.ai/api/v1
LLM_API_KEY=sk-or-여기에-본인-키
LLM_MODEL=openai/gpt-oss-120b
# Redis — 대화 상태(체크포인트) 저장소. 접속 정보·TTL 정책은 Q6 확정 시 갱신
# 로컬 개발: docker run -d --name prepay-redis -p 127.0.0.1:6379:6379 redis:8
REDIS_URL=redis://localhost:6379/0
REDIS_TTL_MINUTES=60
이 장에서 배운 것¶
- uv = Maven/Gradle.
uv sync= 의존성 설치,uv run <명령>= 프로젝트 환경 안에서 실행 pyproject.toml=pom.xml. 앱 프로젝트는package = false- 비밀값은
.env(커밋 금지), 템플릿은.env.example(커밋)