architect-python-uv-fastapi-sqlalchemy

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Architect: Python + uv + FastAPI + SQLAlchemy

架构模板:Python + uv + FastAPI + SQLAlchemy

Use this skill when the user wants a production API scaffold in Python with modern packaging (
uv
), database migrations, containerization, and CI. Docker is required by default for this runnable base architect skill. Only allow
NO_DOCKER=yes
when the user explicitly asks for a local-only exception.
当用户需要使用现代包管理工具(
uv
)、数据库迁移、容器化和CI的Python生产API脚手架时使用本技能。 默认情况下这个可运行的基础架构技能需要Docker,仅当用户明确要求仅本地运行的例外情况时才允许设置
NO_DOCKER=yes

Inputs

输入

Collect these values first:
  • PROJECT_NAME
    : kebab-case repository/folder name.
  • MODULE_NAME
    : import-safe module name (usually snake_case).
  • PYTHON_VERSION
    : default
    3.12
    .
  • DATABASE_URL
    : runtime DB URL.
  • NO_DOCKER
    : default
    no
    . Set
    yes
    only when user explicitly opts out of containerization.
Use these version defaults unless user requests otherwise:
  • uv
    latest stable via
    astral-sh/setup-uv
  • Python
    3.12
  • postgres:16-alpine
首先收集以下参数:
  • PROJECT_NAME
    :短横线分隔命名的仓库/文件夹名称。
  • MODULE_NAME
    :可安全导入的模块名(通常为蛇形命名)。
  • PYTHON_VERSION
    :默认
    3.12
  • DATABASE_URL
    :运行时数据库URL。
  • NO_DOCKER
    :默认
    no
    ,仅当用户明确选择不使用容器化时设置为
    yes
除非用户另有要求,否则使用以下默认版本:
  • 通过
    astral-sh/setup-uv
    安装最新稳定版
    uv
  • 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:
  • production-default
    : create and validate container artifacts (
    NO_DOCKER=no
    ).
  • local-no-docker
    : skip container files only when user explicitly sets
    NO_DOCKER=yes
    .
  • offline-smoke
    : tools/network constrained; scaffold structure and report verification limits.
Production-default contract:
  • Must create
    Dockerfile
    ,
    .dockerignore
    , and
    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

脚手架搭建工作流

  1. Initialize project:
bash
uv init --package {{PROJECT_NAME}}
cd {{PROJECT_NAME}}
  1. 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
  1. Create source layout:
text
src/{{MODULE_NAME}}/
  api/routes/health.py
  core/config.py
  db/base.py
  db/session.py
  main.py
tests/
  1. Initialize Alembic and wire metadata:
bash
uv run alembic init alembic
  • Set
    alembic.ini
    sqlalchemy.url
    to
    ${DATABASE_URL}
    .
  • In
    alembic/env.py
    , import
    Base.metadata
    from
    src/{{MODULE_NAME}}/db/base.py
    .
  1. Add infrastructure and CI files (
    NO_DOCKER=no
    ):
  • Dockerfile
  • .dockerignore
  • docker-compose.yml
  • .github/workflows/ci.yml
    If
    NO_DOCKER=yes
    , document this exception in project notes and keep non-container validation.
  1. 初始化项目:
bash
uv init --package {{PROJECT_NAME}}
cd {{PROJECT_NAME}}
  1. 安装运行时和开发依赖:
bash
uv add fastapi "uvicorn[standard]" sqlalchemy asyncpg alembic pydantic-settings
uv add -d pytest pytest-asyncio httpx ruff mypy
  1. 创建源码目录结构:
text
src/{{MODULE_NAME}}/
  api/routes/health.py
  core/config.py
  db/base.py
  db/session.py
  main.py
tests/
  1. 初始化Alembic并绑定元数据:
bash
uv run alembic init alembic
  • alembic.ini
    中的
    sqlalchemy.url
    设置为
    ${DATABASE_URL}
  • alembic/env.py
    中,从
    src/{{MODULE_NAME}}/db/base.py
    导入
    Base.metadata
  1. 添加基础设施和CI文件(
    NO_DOCKER=no
    时):
  • Dockerfile
  • .dockerignore
  • docker-compose.yml
  • .github/workflows/ci.yml
    如果
    NO_DOCKER=yes
    ,在项目说明中记录该例外情况并保留非容器验证逻辑。

Required File Templates

必填文件模板

src/{{MODULE_NAME}}/core/config.py

src/{{MODULE_NAME}}/core/config.py

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()
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.py

python
from sqlalchemy.orm import DeclarativeBase


class Base(DeclarativeBase):
    pass
python
from sqlalchemy.orm import DeclarativeBase


class Base(DeclarativeBase):
    pass

src/{{MODULE_NAME}}/db/session.py

src/{{MODULE_NAME}}/db/session.py

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)
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.py

python
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.py

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)
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

Dockerfile

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 --from=build /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 --from=build /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

gitignore
.git
.venv
__pycache__
.pytest_cache
.mypy_cache
.ruff_cache
*.pyc
dist
build
gitignore
.git
.venv
__pycache__
.pytest_cache
.mypy_cache
.ruff_cache
*.pyc
dist
build

docker-compose.yml

docker-compose.yml

yaml
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
ports: - "8000:8000"
on the
api
service so the host can reach the app. If documenting direct
docker run
usage for the API image, include
-p 8000:8000
and the required
DATABASE_URL
;
docker compose
remains the default local path because it provides the database too.
yaml
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
服务上保留
ports: - "8000:8000"
配置,以便主机可以访问应用。如果要说明API镜像的直接
docker run
用法,需要添加
-p 8000:8000
和必填的
DATABASE_URL
参数;
docker compose
仍然是默认的本地运行方案,因为它同时提供了数据库服务。

.github/workflows/ci.yml

.github/workflows/ci.yml

yaml
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=max
yaml
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=max

Guardrails

约束规则

  • 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.
  • Prefer
    docker compose
    in docs and scripts.
  • Treat
    NO_DOCKER=yes
    as an explicit exception, not default behavior.
  • Ensure
    uv.lock
    is committed before Docker build; the Dockerfile copies it explicitly for deterministic
    uv sync --frozen
    installs.
  • 生成代码的文档约定:
    • Python:为模块、公共类、方法和函数编写模块文档字符串。
    • Next.js/TypeScript:为导出的组件、hooks、工具函数和路由处理函数编写JSDoc。
    • 仅为非显而易见的逻辑、不变量或安全约束添加简明的理由注释。
    • 即使使用下方的模板片段也要遵守本约定,按需扩展模板。
  • 保持异步数据库栈一致(
    sqlalchemy.ext.asyncio
    +
    asyncpg
    )。
  • 绝不硬编码密钥,使用环境变量和
    .env
    文件。
  • 确保Alembic迁移已生成并提交。
  • 确保容器以非root用户运行。
  • 文档和脚本中优先使用
    docker compose
  • NO_DOCKER=yes
    视为明确例外,而非默认行为。
  • 确保Docker构建前已提交
    uv.lock
    ;Dockerfile会显式复制该文件以实现确定性的
    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/healthz
local-no-docker
(
NO_DOCKER=yes
):
bash
uv run ruff check .
uv run ruff check . --select D
uv run mypy src
uv run pytest -q
uv run alembic upgrade head
Fallback (
offline-smoke
):
bash
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/healthz
local-no-docker
NO_DOCKER=yes
)场景:
bash
uv run ruff check .
uv run ruff check . --select D
uv run mypy src
uv run pytest -q
uv run alembic upgrade head
回退场景(
offline-smoke
):
bash
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.
  • 每个非平凡决策都必须包含具体理由。
  • 记录考虑过的替代方案以及被拒绝的原因。
  • 说明所选方案的权衡和残留风险。
  • 如果缺少说明,视为任务未完成,并将其标记为阻塞项。