architect-python-uv-fastapi-sqlalchemy
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseArchitect: Python + uv + FastAPI + SQLAlchemy
架构模板:Python + uv + FastAPI + SQLAlchemy
Use this skill when the user wants a production API scaffold in Python with modern packaging (), database migrations, containerization, and CI.
Docker is required by default for this runnable base architect skill. Only allow when the user explicitly asks for a local-only exception.
uvNO_DOCKER=yes当用户需要使用现代包管理工具()、数据库迁移、容器化和CI的Python生产API脚手架时使用本技能。
默认情况下这个可运行的基础架构技能需要Docker,仅当用户明确要求仅本地运行的例外情况时才允许设置。
uvNO_DOCKER=yesInputs
输入
Collect these values first:
- : kebab-case repository/folder name.
PROJECT_NAME - : import-safe module name (usually snake_case).
MODULE_NAME - : default
PYTHON_VERSION.3.12 - : runtime DB URL.
DATABASE_URL - : default
NO_DOCKER. Setnoonly when user explicitly opts out of containerization.yes
Use these version defaults unless user requests otherwise:
- latest stable via
uvastral-sh/setup-uv - Python
3.12 postgres:16-alpine
首先收集以下参数:
- :短横线分隔命名的仓库/文件夹名称。
PROJECT_NAME - :可安全导入的模块名(通常为蛇形命名)。
MODULE_NAME - :默认
PYTHON_VERSION。3.12 - :运行时数据库URL。
DATABASE_URL - :默认
NO_DOCKER,仅当用户明确选择不使用容器化时设置为no。yes
除非用户另有要求,否则使用以下默认版本:
- 通过安装最新稳定版
astral-sh/setup-uvuv - Python
3.12 postgres:16-alpine
Preflight Checks
预检查
Run before scaffolding:
bash
command -v uv >/dev/null && uv --version || echo "uv-missing"
python3 --version
command -v docker >/dev/null && docker --version || echo "docker-missing"Execution modes:
- : create and validate container artifacts (
production-default).NO_DOCKER=no - : skip container files only when user explicitly sets
local-no-docker.NO_DOCKER=yes - : tools/network constrained; scaffold structure and report verification limits.
offline-smoke
Production-default contract:
- Must create ,
Dockerfile, and.dockerignore.docker-compose.yml - Must include CI image build check.
- Must run containerized smoke validation.
在搭建脚手架前运行以下命令:
bash
command -v uv >/dev/null && uv --version || echo "uv-missing"
python3 --version
command -v docker >/dev/null && docker --version || echo "docker-missing"执行模式:
- :创建并验证容器制品(
production-default)。NO_DOCKER=no - :仅当用户明确设置
local-no-docker时跳过容器相关文件。NO_DOCKER=yes - :工具/网络受限场景;仅搭建目录结构并报告验证限制。
offline-smoke
生产模式默认约定:
- 必须创建、
Dockerfile和.dockerignore。docker-compose.yml - 必须包含CI镜像构建检查。
- 必须运行容器化冒烟验证。
Scaffold Workflow
脚手架搭建工作流
- Initialize project:
bash
uv init --package {{PROJECT_NAME}}
cd {{PROJECT_NAME}}- Install runtime and dev dependencies:
bash
uv add fastapi "uvicorn[standard]" sqlalchemy asyncpg alembic pydantic-settings
uv add -d pytest pytest-asyncio httpx ruff mypy- Create source layout:
text
src/{{MODULE_NAME}}/
api/routes/health.py
core/config.py
db/base.py
db/session.py
main.py
tests/- Initialize Alembic and wire metadata:
bash
uv run alembic init alembic- Set
alembic.initosqlalchemy.url.${DATABASE_URL} - In , import
alembic/env.pyfromBase.metadata.src/{{MODULE_NAME}}/db/base.py
- Add infrastructure and CI files ():
NO_DOCKER=no
Dockerfile.dockerignoredocker-compose.yml- If
.github/workflows/ci.yml, document this exception in project notes and keep non-container validation.NO_DOCKER=yes
- 初始化项目:
bash
uv init --package {{PROJECT_NAME}}
cd {{PROJECT_NAME}}- 安装运行时和开发依赖:
bash
uv add fastapi "uvicorn[standard]" sqlalchemy asyncpg alembic pydantic-settings
uv add -d pytest pytest-asyncio httpx ruff mypy- 创建源码目录结构:
text
src/{{MODULE_NAME}}/
api/routes/health.py
core/config.py
db/base.py
db/session.py
main.py
tests/- 初始化Alembic并绑定元数据:
bash
uv run alembic init alembic- 将中的
alembic.ini设置为sqlalchemy.url。${DATABASE_URL} - 在中,从
alembic/env.py导入src/{{MODULE_NAME}}/db/base.py。Base.metadata
- 添加基础设施和CI文件(时):
NO_DOCKER=no
Dockerfile.dockerignoredocker-compose.yml- 如果
.github/workflows/ci.yml,在项目说明中记录该例外情况并保留非容器验证逻辑。NO_DOCKER=yes
Required File Templates
必填文件模板
src/{{MODULE_NAME}}/core/config.py
src/{{MODULE_NAME}}/core/config.pysrc/{{MODULE_NAME}}/core/config.py
src/{{MODULE_NAME}}/core/config.pypython
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8")
app_name: str = "api"
environment: str = "development"
database_url: str = "postgresql+asyncpg://app:app@localhost:5432/app"
settings = Settings()python
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8")
app_name: str = "api"
environment: str = "development"
database_url: str = "postgresql+asyncpg://app:app@localhost:5432/app"
settings = Settings()src/{{MODULE_NAME}}/db/base.py
src/{{MODULE_NAME}}/db/base.pysrc/{{MODULE_NAME}}/db/base.py
src/{{MODULE_NAME}}/db/base.pypython
from sqlalchemy.orm import DeclarativeBase
class Base(DeclarativeBase):
passpython
from sqlalchemy.orm import DeclarativeBase
class Base(DeclarativeBase):
passsrc/{{MODULE_NAME}}/db/session.py
src/{{MODULE_NAME}}/db/session.pysrc/{{MODULE_NAME}}/db/session.py
src/{{MODULE_NAME}}/db/session.pypython
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
from {{MODULE_NAME}}.core.config import settings
engine = create_async_engine(settings.database_url, pool_pre_ping=True)
SessionLocal = async_sessionmaker(bind=engine, expire_on_commit=False)python
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
from {{MODULE_NAME}}.core.config import settings
engine = create_async_engine(settings.database_url, pool_pre_ping=True)
SessionLocal = async_sessionmaker(bind=engine, expire_on_commit=False)src/{{MODULE_NAME}}/api/routes/health.py
src/{{MODULE_NAME}}/api/routes/health.pysrc/{{MODULE_NAME}}/api/routes/health.py
src/{{MODULE_NAME}}/api/routes/health.pypython
from fastapi import APIRouter
router = APIRouter()
@router.get("/healthz", tags=["health"])
async def healthcheck() -> dict[str, str]:
return {"status": "ok"}python
from fastapi import APIRouter
router = APIRouter()
@router.get("/healthz", tags=["health"])
async def healthcheck() -> dict[str, str]:
return {"status": "ok"}src/{{MODULE_NAME}}/main.py
src/{{MODULE_NAME}}/main.pysrc/{{MODULE_NAME}}/main.py
src/{{MODULE_NAME}}/main.pypython
from fastapi import FastAPI
from {{MODULE_NAME}}.api.routes.health import router as health_router
app = FastAPI(title="{{PROJECT_NAME}}")
app.include_router(health_router)python
from fastapi import FastAPI
from {{MODULE_NAME}}.api.routes.health import router as health_router
app = FastAPI(title="{{PROJECT_NAME}}")
app.include_router(health_router)Dockerfile
DockerfileDockerfile
Dockerfiledockerfile
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS build
WORKDIR /app
COPY pyproject.toml uv.lock ./
COPY src ./src
RUN uv sync --frozen --no-dev
FROM python:3.12-slim AS run
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
RUN useradd --create-home --shell /bin/bash app
COPY /app/.venv /app/.venv
COPY src ./src
COPY alembic ./alembic
COPY alembic.ini ./
ENV PATH="/app/.venv/bin:$PATH"
USER app
EXPOSE 8000
CMD ["uvicorn", "{{MODULE_NAME}}.main:app", "--host", "0.0.0.0", "--port", "8000"]dockerfile
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS build
WORKDIR /app
COPY pyproject.toml uv.lock ./
COPY src ./src
RUN uv sync --frozen --no-dev
FROM python:3.12-slim AS run
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
RUN useradd --create-home --shell /bin/bash app
COPY /app/.venv /app/.venv
COPY src ./src
COPY alembic ./alembic
COPY alembic.ini ./
ENV PATH="/app/.venv/bin:$PATH"
USER app
EXPOSE 8000
CMD ["uvicorn", "{{MODULE_NAME}}.main:app", "--host", "0.0.0.0", "--port", "8000"].dockerignore
.dockerignore.dockerignore
.dockerignoregitignore
.git
.venv
__pycache__
.pytest_cache
.mypy_cache
.ruff_cache
*.pyc
dist
buildgitignore
.git
.venv
__pycache__
.pytest_cache
.mypy_cache
.ruff_cache
*.pyc
dist
builddocker-compose.yml
docker-compose.ymldocker-compose.yml
docker-compose.ymlyaml
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: app
POSTGRES_USER: app
POSTGRES_PASSWORD: app
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 5s
timeout: 5s
retries: 20
volumes:
- pg_data:/var/lib/postgresql/data
api:
build: .
environment:
DATABASE_URL: postgresql+asyncpg://app:app@db:5432/app
depends_on:
db:
condition: service_healthy
ports:
- "8000:8000"
volumes:
pg_data:Keep on the service so the host can reach the app. If documenting direct usage for the API image, include and the required ; remains the default local path because it provides the database too.
ports: - "8000:8000"apidocker run-p 8000:8000DATABASE_URLdocker composeyaml
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: app
POSTGRES_USER: app
POSTGRES_PASSWORD: app
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 5s
timeout: 5s
retries: 20
volumes:
- pg_data:/var/lib/postgresql/data
api:
build: .
environment:
DATABASE_URL: postgresql+asyncpg://app:app@db:5432/app
depends_on:
db:
condition: service_healthy
ports:
- "8000:8000"
volumes:
pg_data:在服务上保留配置,以便主机可以访问应用。如果要说明API镜像的直接用法,需要添加和必填的参数;仍然是默认的本地运行方案,因为它同时提供了数据库服务。
apiports: - "8000:8000"docker run-p 8000:8000DATABASE_URLdocker compose.github/workflows/ci.yml
.github/workflows/ci.yml.github/workflows/ci.yml
.github/workflows/ci.ymlyaml
name: ci
on:
push:
pull_request:
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- uses: astral-sh/setup-uv@v5
- name: Install deps
run: uv sync --frozen --dev
- name: Ruff
run: uv run ruff check .
- name: Ruff Docstrings
run: uv run ruff check . --select D
- name: Mypy
run: uv run mypy src
- name: Pytest
run: uv run pytest -q
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
with:
context: .
push: false
tags: {{PROJECT_NAME}}:ci
cache-from: type=gha
cache-to: type=gha,mode=maxyaml
name: ci
on:
push:
pull_request:
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- uses: astral-sh/setup-uv@v5
- name: Install deps
run: uv sync --frozen --dev
- name: Ruff
run: uv run ruff check .
- name: Ruff Docstrings
run: uv run ruff check . --select D
- name: Mypy
run: uv run mypy src
- name: Pytest
run: uv run pytest -q
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
with:
context: .
push: false
tags: {{PROJECT_NAME}}:ci
cache-from: type=gha
cache-to: type=gha,mode=maxGuardrails
约束规则
-
Documentation contract for generated code:
- Python: write module docstrings and docstrings for public classes, methods, and functions.
- Next.js/TypeScript: write JSDoc for exported components, hooks, utilities, and route handlers.
- Add concise rationale comments only for non-obvious logic, invariants, or safety constraints.
- Apply this contract even when using template snippets below; expand templates as needed.
-
Keep async DB stack consistent (+
sqlalchemy.ext.asyncio).asyncpg -
Never hardcode secrets; use environment variables and.
.env -
Ensure Alembic migrations are generated and committed.
-
Ensure container runs as non-root user.
-
Preferin docs and scripts.
docker compose -
Treatas an explicit exception, not default behavior.
NO_DOCKER=yes -
Ensureis committed before Docker build; the Dockerfile copies it explicitly for deterministic
uv.lockinstalls.uv sync --frozen
-
生成代码的文档约定:
- Python:为模块、公共类、方法和函数编写模块文档字符串。
- Next.js/TypeScript:为导出的组件、hooks、工具函数和路由处理函数编写JSDoc。
- 仅为非显而易见的逻辑、不变量或安全约束添加简明的理由注释。
- 即使使用下方的模板片段也要遵守本约定,按需扩展模板。
-
保持异步数据库栈一致(+
sqlalchemy.ext.asyncio)。asyncpg -
绝不硬编码密钥,使用环境变量和文件。
.env -
确保Alembic迁移已生成并提交。
-
确保容器以非root用户运行。
-
文档和脚本中优先使用。
docker compose -
将视为明确例外,而非默认行为。
NO_DOCKER=yes -
确保Docker构建前已提交;Dockerfile会显式复制该文件以实现确定性的
uv.lock安装。uv sync --frozen
Validation Checklist
验证检查清单
- Confirm generated code includes required docstrings/JSDoc and rationale comments for non-obvious logic.
Run and fix failures before finishing:
bash
uv run ruff check .
uv run ruff check . --select D
uv run mypy src
uv run pytest -q
uv run alembic upgrade head
test -f uv.lock
docker build -t {{PROJECT_NAME}}:local .
docker compose up -d --build
docker compose ps
curl http://localhost:8000/healthzlocal-no-dockerNO_DOCKER=yesbash
uv run ruff check .
uv run ruff check . --select D
uv run mypy src
uv run pytest -q
uv run alembic upgrade headFallback ():
offline-smokebash
python3 -m compileall src
test -f Dockerfile || echo "docker-artifacts-missing-in-offline-smoke"- 确认生成的代码包含必填的文档字符串/JSDoc,以及非显而易见逻辑的理由注释。
在完成前运行以下命令并修复所有报错:
bash
uv run ruff check .
uv run ruff check . --select D
uv run mypy src
uv run pytest -q
uv run alembic upgrade head
test -f uv.lock
docker build -t {{PROJECT_NAME}}:local .
docker compose up -d --build
docker compose ps
curl http://localhost:8000/healthzlocal-no-dockerNO_DOCKER=yesbash
uv run ruff check .
uv run ruff check . --select D
uv run mypy src
uv run pytest -q
uv run alembic upgrade head回退场景():
offline-smokebash
python3 -m compileall src
test -f Dockerfile || echo "docker-artifacts-missing-in-offline-smoke"Decision Justification Rule
决策说明规则
- Every non-trivial decision must include a concrete justification.
- Capture the alternatives considered and why they were rejected.
- State tradeoffs and residual risks for the chosen option.
- If justification is missing, treat the task as incomplete and surface it as a blocker.
- 每个非平凡决策都必须包含具体理由。
- 记录考虑过的替代方案以及被拒绝的原因。
- 说明所选方案的权衡和残留风险。
- 如果缺少说明,视为任务未完成,并将其标记为阻塞项。