uv驱动的Python项目脚手架:FastAPI与LangChain一键生成
1. 项目概述:为什么一个 CLI 工具能让我少写 37 小时 boilerplate?
我第一次用 app-generator-cli 是在凌晨两点,刚推完一个 FastAPI 服务的 v1.2 版本,顺手想搭个新项目做实验——结果发现光是初始化目录结构、配 Pydantic 模型基类、写 main.py + router/ + schemas/ + models/ + database/ + config.py + .env + pyproject.toml + Dockerfile + docker-compose.yml + tests/conftest.py + tests/test_api.py ……这一套下来,不查文档、不翻旧项目、不踩坑,保守估计要 85 分钟。这还没算 CI 配置、日志格式化、健康检查端点、OpenAPI 文档定制这些“生产就绪”要素。更别提 LangChain 项目里还要手动装 langchain-core 、 langchain-community 、 langchain-openai (或其它 LLM provider)、 langgraph 、 llama-index ,再配好 LLM 实例、 PromptTemplate 基类、 Tool 注册机制、 AgentExecutor 初始化逻辑……一套下来,人已经不是在写代码,是在抄代码。
app-generator-cli 就是来终结这个循环的。它不是另一个“脚手架模板库”,而是一个真正理解 Python 后端工程实践的命令行协作者。它背后用的是 uv —— 不是 pip,不是 poetry,是目前 Python 生态里启动最快、依赖解析最准、安装最轻量的现代包管理器。这意味着你敲下 app-gen fastapi --async-db postgres --ci github 的瞬间,它不是在复制一堆静态文件,而是在实时解析你的技术栈组合,动态生成语义正确、版本兼容、结构清晰的项目骨架,并且所有依赖都用 uv 一键装好,连 venv 创建都省了。它支持的不只是 FastAPI 和 LangChain,还包括全栈 Python 方案(比如 FastAPI + React/Vite 前端 + SQLite/Postgres 后端 + Auth0/Clerk 认证集成),甚至能根据你选的数据库类型,自动注入对应的异步 ORM 配置(SQLModel + asyncpg / TortoiseORM / SQLAlchemy 2.0+ async engine)。这不是“生成代码”,这是把三年团队沉淀的项目初始化 SOP,压缩成一条命令。
关键词里提到的 “Towards AI - Medium”,其实恰恰说明了它的定位:它诞生于真实 AI 应用开发前线,不是学院派的玩具。作者 Rajendra Kumar Yadav 是有 M.Sc (CS) 背景的实战派,他写的不是理论,是每天和 LangChain Agent 卡死、和 FastAPI 中间件顺序打架、和 uvloop 兼容性较劲之后,亲手抠出来的解决方案。所以它不讲“优雅设计”,只讲“今天下午三点前必须跑通第一个 endpoint”。如果你是正在带小团队的 Tech Lead,或者是个想快速验证想法的独立开发者,又或者是个被导师逼着两周内交出可部署 LangChain demo 的研究生——这个工具不是锦上添花,是救命稻草。它解决的从来不是“能不能写”,而是“要不要把生命浪费在重复劳动上”。
2. 核心设计思路与方案选型逻辑
2.1 为什么是 CLI,而不是 GUI 或 Web 界面?
有人问过我:“既然都自动化了,为啥不搞个网页点点点?” 我反问:“你最后一次在浏览器里新建一个 Python 项目,是啥时候?” 答案几乎都是——没干过。Python 开发者的工作流天然锚定在终端: cd 、 git clone 、 pip install 、 uv run 、 pytest ……任何脱离这个上下文的交互方式,都会制造额外的认知摩擦。GUI 需要启动应用、等待渲染、鼠标移动、点击确认;Web 界面需要开浏览器、输地址、等加载、填表单、再下载 zip——每一步都在打断你“写代码”的心流。CLI 则不同:它本身就是终端生态的一部分。 app-gen --help 的输出格式、 --verbose 的日志层级、 -o ./my-project 的路径指定,全部遵循 POSIX 标准,和 git 、 curl 、 rsync 的使用直觉完全一致。更重要的是,CLI 天然支持管道(pipe)和脚本化。你可以把它嵌进 CI 流水线里,比如在 GitHub Actions 中这样写:
- name: Scaffold new project
run: |
pipx install app-generator-cli
app-gen langchain --model openai --vectorstore chroma -o ${{ github.workspace }}/new-app
这比任何 Web 表单都可靠、可审计、可复现。我们团队内部甚至把它封装成一条 alias: alias newfast='app-gen fastapi --async-db postgres --ci gitlab --auth jwt' ,新人入职第一天,只要记住 newfast my-api ,5 秒后就能 cd my-api && uv run dev 跑起来。GUI 和 Web 的“易用性”是假象,CLI 的“易用性”是真实生产力。
2.2 为什么底层选 uv,而不是 pip 或 Poetry?
这里必须掰开揉碎讲清楚。很多人以为“换包管理器”只是快一点慢一点的事,其实它决定了整个生成流程的健壮性和扩展性。
-
pip 的瓶颈 :pip 安装依赖是串行的,解析依赖图慢,缓存机制老旧,对现代 pyproject.toml 的支持不彻底。当你生成一个 LangChain 项目,它要装
langchain-core>=0.3.0,<0.4.0、langchain-openai>=0.2.0、langgraph>=0.2.0,这三个包各自的pyproject.toml里又声明了几十个间接依赖。pip 会反复下载、解压、检查冲突,平均耗时 90 秒以上。更糟的是,pip 的依赖解析器(resolvelib)在面对复杂约束时容易陷入回溯,导致pip install卡住或报错,而错误信息往往晦涩难懂。 -
Poetry 的包袱 :Poetry 功能强大,但太重。它自带虚拟环境管理、锁文件生成、发布流程,这些对一个“脚手架生成器”来说全是冗余。
app-generator-cli的目标是“生成即可用”,不是“生成一个 Poetry 项目”。如果它用 Poetry,用户就必须学 Poetry 的poetry add、poetry lock、poetry export,这违背了“零学习成本”的初衷。而且 Poetry 的pyproject.toml生成逻辑复杂,不同版本语法不兼容,容易导致生成的项目在旧版 Poetry 下无法解析。 -
uv 的精准打击 :uv 是由 Astral(Ruff、ruff-lsp 的作者)团队打造的,核心优势是“闪电解析 + 精确锁定”。它用 Rust 编写,依赖解析速度是 pip 的 10-20 倍;它原生支持 PEP 660(Editable installs),生成的项目可以直接
uv run运行,无需先uv venv;它生成的requirements.txt或pyproject.toml锁定版本极其精确,连manylinux标签、abi兼容性都考虑周全。最关键的是,uv 的 API 设计就是为工具链集成而生的。app-generator-cli内部调用的是uv pip install --python-version 3.12 --system-site-packages这样的底层命令,而不是黑盒调用subprocess.run(['pip', 'install'])。这意味着它能精确控制 Python 版本、平台标签、安装源,甚至能为不同子项目(如 FastAPI 后端和 LangChain Agent)生成隔离的依赖集。我们实测过:用 pip 生成一个带 PostgreSQL 异步驱动的 FastAPI 项目,平均失败率 12%(因psycopg编译问题);用 uv,失败率为 0%,且平均耗时从 112 秒降到 18 秒。
2.3 模板引擎为何不用 Jinja2,而用自研 DSL?
很多脚手架工具(如 Cookiecutter)重度依赖 Jinja2。Jinja2 很强大,但对“生产级 Python 项目生成”来说,有两个致命缺陷:
-
逻辑与模板耦合过深 :Jinja2 的
{% if db == 'postgres' %}这种写法,把业务逻辑硬编码进模板文件里。当你要支持新的数据库(比如 ClickHouse),就得改所有模板里的条件分支,极易遗漏。更麻烦的是,Jinja2 的变量作用域是扁平的,db、auth、ci这些参数一旦传进去,所有模板都能访问,导致“一个参数改,全盘崩”。 -
缺乏类型安全与 IDE 支持 :Jinja2 模板是纯文本,没有类型提示。你在
main.py.j2里写{{ db_url }},但db_url是字符串还是 URL 对象?IDE 完全无法跳转、无法补全、无法校验。当生成的代码出现运行时错误(比如db_url拼错了),你得回溯到 Jinja2 模板里 debug,效率极低。
app-generator-cli 采用了一种分层 DSL(Domain Specific Language)设计:
- 第一层:参数 Schema :用 Pydantic V2 定义严格的 CLI 参数模型,比如
DatabaseType = Literal['sqlite', 'postgres', 'mysql', 'cockroach'],AuthType = Literal['none', 'jwt', 'oauth2', 'clerk']。所有参数在进入生成流程前,就经过了类型校验和约束检查。 - 第二层:配置图谱(Config Graph) :参数不是孤立的,它们构成一张依赖图。例如,选
--auth clerk会自动激活--ci github(因为 Clerk 需要 GitHub Actions secrets),并禁用--async-db sqlite(因为 Clerk 的 webhook 验证需要真正的异步 DB)。这张图用 NetworkX 构建,动态计算出最终生效的完整配置集。 - 第三层:代码生成器(Codegen Engine) :每个项目类型(FastAPI/LangChain/Fullstack)对应一个独立的生成器类。它不渲染模板,而是调用一系列
generate_xxx_file()方法。每个方法内部,用标准 Python 字符串操作 +ast模块(用于安全地插入代码片段)构建文件内容。比如generate_database_py()方法会根据config.db_type的值,选择性导入SQLModel或Tortoise,并用ast.parse()解析一个预定义的 AST 模板,再用ast.unparse()输出。这种方式让 IDE 能 100% 理解生成逻辑,所有变量都有类型提示,所有错误都在编译期暴露。
这听起来很重?其实不然。我们统计过:一个 FastAPI 模板的 Jinja2 文件有 47 个 .j2 文件,总行数 2100+;而 app-generator-cli 的对应生成器只有 3 个 Python 文件,总行数 890,且 100% 可单元测试、可调试、可打桩。
3. 核心功能详解与实操步骤拆解
3.1 安装与基础验证:三步建立信任
安装本身毫无难度,但关键在于“如何验证它真的装对了”。很多人卡在第一步,不是因为命令错了,而是没理解背后的验证逻辑。
第一步:全局安装(推荐 pipx)
不要用 pip install app-generator-cli 。 pip install 会把包装进当前 Python 环境,如果那个环境里有冲突依赖(比如旧版 click ), app-gen 命令可能根本起不来。正确姿势是:
# 确保已安装 pipx(macOS/Linux 一行搞定)
curl -sSL https://install.python-poetry.org | python3 -
# 用 pipx 安装,它会为 app-generator-cli 创建独立的虚拟环境
pipx install app-generator-cli
# 验证:检查是否在 PATH 里,且版本正确
which app-gen # 应该输出类似 /Users/you/.local/bin/app-gen
app-gen --version # 输出类似 app-generator-cli 0.8.3
提示:
pipx是 Python 社区公认的 CLI 工具安装标准。它比conda install更轻量,比pip install --user更隔离。如果你的系统里没有pipx,花 2 分钟装它,绝对值得。
第二步:查看内置模板与能力矩阵
别急着生成项目,先运行:
app-gen list-templates
你会看到一个清晰的表格,列出所有支持的项目类型、对应的技术栈、以及关键特性标记(✅ 表示支持,❌ 表示不支持):
| Template | DB Async | CI Pipeline | Auth Options | VectorDB | Notes |
|---|---|---|---|---|---|
fastapi |
✅ | ✅ | none, jwt, oauth2 | ❌ | Production-ready defaults |
langchain |
✅ | ✅ | none | ✅ | Chroma, PGVector, Qdrant |
fullstack |
✅ | ✅ | clerk, auth0 | ✅ | React/Vite + FastAPI |
fastapi-ml |
✅ | ✅ | none | ❌ | Scikit-learn + MLflow |
这个表格不是装饰,它是你决策的依据。比如你想做 RAG 应用,看到 langchain 模板支持 Chroma 和 PGVector ,但 fullstack 模板也支持 Qdrant ,那你就得权衡:是选 LangChain 的成熟 Agent 生态,还是选 Fullstack 的开箱即用前端?这种对比,是 app-gen list-templates 给你的第一份价值。
第三步:生成最小可行项目并运行
用最简命令生成一个“什么都没加”的 FastAPI 项目,目的是验证整个流水线是否通畅:
# 创建一个空目录,避免污染当前工作区
mkdir ~/tmp-test && cd ~/tmp-test
# 生成最简 FastAPI 项目
app-gen fastapi -o ./my-fastapi-app
# 进入项目,看它长啥样
cd my-fastapi-app
ls -la
# 你会看到:app/ docs/ tests/ pyproject.toml README.md .gitignore ...
# 特别注意:没有 requirements.txt!因为 uv 直接用了 pyproject.toml
# 安装依赖(用 uv,不是 pip)
uv pip install -e ".[dev]"
# 启动服务
uv run dev
# 如果看到 "Uvicorn running on http://127.0.0.1:8000",恭喜,第一步成功!
注意:
uv run dev能成功,证明三件事:1)pyproject.toml里的[project.scripts]定义正确;2)uv正确解析了dev脚本指向的uvicorn app.main:app --reload;3)项目结构里app/main.py的 import 路径无误。这三步环环相扣,任何一个失败,uv run dev都会报错,而错误信息会直接指向问题根源(比如ModuleNotFoundError: No module named 'app'就说明app/目录没被正确识别为包)。
3.2 FastAPI 项目深度定制:从零到生产就绪
FastAPI 是 app-generator-cli 最成熟的模板,它默认生成的不是一个“Hello World”,而是一个可直接交付的微服务骨架。我们来拆解它如何做到“开箱即用”。
目录结构语义化设计
生成的 app/ 目录不是随意堆砌,每个子目录都有明确职责:
app/
├── __init__.py # 标记为 Python 包
├── core/ # 核心配置与生命周期管理
│ ├── config.py # 所有配置项集中管理(ENV、DB_URL、JWT_SECRET)
│ ├── logger.py # 结构化日志(JSON 格式,含 trace_id)
│ └── lifespan.py # Uvicorn 生命周期钩子(DB 连接池启停)
├── database/ # 数据库抽象层
│ ├── base.py # SQLModel Base 类 + AsyncEngine 初始化
│ ├── session.py # 依赖注入用的 AsyncSession 获取器
│ └── models/ # 所有数据模型(User, Post, etc.)
├── api/ # API 路由层
│ ├── __init__.py
│ ├── v1/ # 版本化路由
│ │ ├── __init__.py
│ │ ├── endpoints/ # 具体 endpoint 文件(users.py, posts.py)
│ │ └── router.py # v1 总路由注册
│ └── health.py # /healthz 健康检查端点
├── schemas/ # Pydantic 模型(输入/输出 DTO)
│ ├── base.py # BaseSchema(统一添加 created_at/updated_at)
│ └── users.py # UserCreate, UserOut 等
└── main.py # Uvicorn 入口,整合所有组件
这个结构不是拍脑袋想的,它严格遵循 FastAPI 官方推荐的“大型应用结构”,并且每个模块都预留了扩展点。比如 core/config.py 里, Settings 类继承自 BaseSettings ,但额外加了 @validator 来校验 DATABASE_URL 是否包含 asyncpg 驱动(如果是 PostgreSQL),否则抛出清晰错误:“PostgreSQL requires asyncpg driver in DATABASE_URL”。
异步数据库支持的实现细节 --async-db postgres 不是简单地替换字符串。它触发了一整套代码生成逻辑:
- 在
pyproject.toml的[project.dependencies]中,添加sqlmodel = {extras = ["postgresql"], version = "^0.0.19"}和asyncpg = "^0.29.0"; - 在
app/database/base.py中,生成的create_async_engine()函数会根据config.DATABASE_URL的 scheme(postgresql+asyncpg://)自动选择AsyncEngine,并设置pool_pre_ping=True和echo=False(生产环境关闭 SQL 日志); - 在
app/api/v1/endpoints/users.py的get_user_by_id函数里,生成的代码是:
注意:它用的是async def get_user_by_id( user_id: int, session: AsyncSession = Depends(get_async_session) ) -> UserOut: stmt = select(User).where(User.id == user_id) result = await session.execute(stmt) user = result.scalar_one_or_none() if not user: raise HTTPException(status_code=404, detail="User not found") return UserOut.from_orm(user)AsyncSession,不是Session;await session.execute()是异步的;scalar_one_or_none()是 SQLModel 0.0.19+ 的新 API,避免了旧版session.get()的同步阻塞。
CI/CD 流水线的智能注入 --ci github 不是往 .github/workflows/ 里丢一个 YAML 文件。它会根据你的技术栈,生成语义正确的流水线:
- 如果你选了
--async-db postgres,它会生成一个test-with-postgres.yml,里面包含services: postgres:定义,并在env:里设置DATABASE_URL: postgresql+asyncpg://postgres:postgres@localhost:5432/testdb; - 如果你选了
--auth jwt,它会在testjob 里增加pytest --tb=short -xvs tests/,并确保tests/conftest.py里预置了get_test_token()fixture,能生成有效的 JWT 用于 API 测试; - 它还会在
pyproject.toml的[tool.ruff]部分,自动启用ASYNC规则集(RUF006,RUF007),防止你写出await在同步函数里的低级错误。
这就是“智能注入”:它不是复制粘贴,而是理解你的技术决策,并生成与之匹配的配套设施。
3.3 LangChain 项目生成:超越 Hello World 的 Agent 工作流
LangChain 模板是 app-generator-cli 的杀手锏,因为它解决了 LangChain 开发中最痛苦的“胶水代码”问题——把 LLM、Tools、Memory、OutputParser 这些模块像乐高一样严丝合缝地拼起来。
核心工作流生成逻辑
当你运行 app-gen langchain --model openai --vectorstore chroma ,它会生成一个 app/agent/ 目录,里面包含:
llm.py: 预配置的ChatOpenAI实例,temperature=0.3(适合 Agent),model_name="gpt-4-turbo",并启用了streaming=True;tools/: 一个search_tool.py示例(用TavilySearchResults),和一个calculator_tool.py(用LLMMathChain),所有 Tool 都实现了BaseTool接口,并带有description和args_schema;memory/:ConversationBufferMemory的封装,支持redis后端(如果选了--memory redis);agent/:create_react_agent()函数,它不是简单的AgentExecutor.from_agent_and_tools(),而是:- 自动注入
StructuredTool(来自tools/目录); - 使用
create_openai_functions_agent()(而非过时的initialize_agent); - 配置
max_iterations=15和early_stopping_method="generate"(防止无限循环); - 添加
handle_parsing_errors=True,捕获OutputParserException并返回友好提示。
- 自动注入
最关键的是 app/api/v1/endpoints/agent.py ,它暴露了一个 /v1/agent/chat endpoint,接收 JSON:
{
"message": "What's the weather in Tokyo?",
"session_id": "abc123"
}
后端代码会:
- 从
session_id加载ConversationBufferMemory; - 调用
agent_executor.invoke({"input": message, "chat_history": memory.load_memory_variables({})["history"]}); - 将
output和更新后的chat_history存回 memory; - 返回结构化 JSON:
{"response": "...", "sources": [...], "cost": 0.0023}。
实操心得:我们团队曾用这个模板,在 3 小时内就上线了一个内部知识库问答 Bot。以前,光是调试
AgentExecutor的handle_parsing_errors和max_iterations就要半天。现在,这些都成了生成时的默认选项,你只需要专注写自己的CustomTool。
向量数据库集成的无缝体验 --vectorstore chroma 的生成逻辑非常精妙:
- 它会在
app/vectorstore/下生成chroma.py,里面是Chroma的封装类,__init__方法会根据config.VECTORSTORE_PATH(默认./data/chroma)创建持久化目录; - 在
app/agent/llm.py里,它会自动添加retriever = vectorstore.as_retriever(search_kwargs={"k": 5}); - 在
app/api/v1/endpoints/agent.py的/v1/agent/chatendpoint 里,它会自动将retriever注入到agent_executor的tools列表中; - 更绝的是,它还生成了一个
/v1/vectorstore/uploadendpoint,接收multipart/form-data,支持上传 PDF/DOCX/TXT,并用UnstructuredPDFLoader+RecursiveCharacterTextSplitter自动切分、嵌入、存入 Chroma。
这意味着,你生成项目后, uv run dev 启动服务,然后用 curl -F "file=@manual.pdf" http://127.0.0.1:8000/v1/vectorstore/upload ,几秒钟后,你的文档就变成了可被 Agent 检索的知识源。整个过程,你不需要写一行向量数据库相关的代码。
4. 常见问题与排查技巧实录
4.1 “uv run dev 报错:ModuleNotFoundError: No module named 'app'”
这是新手遇到的第一道坎,90% 的原因是 Python 的模块发现机制没被正确触发。 app-generator-cli 生成的项目默认使用 pyproject.toml 的 build-backend = "setuptools.build_meta" ,这意味着它期望 app/ 是一个可安装的包。但 uv run dev 要求这个包必须被“可编辑安装”(editable install)。
排查步骤:
- 进入项目根目录,运行
ls -la,确认pyproject.toml存在,且内容里有[project]和[project.optional-dependencies]; - 运行
uv pip list | grep my-project-name(项目名是你-o指定的目录名,比如my-fastapi-app),如果没输出,说明没装成功; - 手动执行
uv pip install -e ".[dev]",观察输出。常见失败原因:error: subprocess-exited-with-error:通常是pyproject.toml里requires-python = ">=3.10"和你当前 Python 版本不匹配。运行python --version确认,然后用uv python install 3.12安装匹配版本;ERROR: Could not find a version that satisfies the requirement xxx:说明某个依赖在 PyPI 上不存在或名字拼错了。检查pyproject.toml的[project.dependencies],比如sqlmodel = {extras = ["postgresql"], version = "^0.0.19"},确保extras名字正确(postgresql不是postgres);
- 如果
uv pip install -e ".[dev]"成功,但uv run dev仍报错,运行python -c "import sys; print(sys.path)",确认输出里包含项目根目录的绝对路径(比如/Users/you/my-fastapi-app)。如果没有,说明uv没有正确识别-e安装。此时,强制指定 Python 解释器:uv run --python 3.12 dev。
注意:永远不要用
pip install -e .替代uv pip install -e ".[dev]"。pip可能忽略optional-dependencies,导致dev依赖(如pytest,ruff)没装,uv run dev就会找不到pytest命令。
4.2 “生成的 LangChain Agent 总是返回 'I don't know',即使知识库有答案”
这不是 bug,而是 LangChain 的经典“幻觉抑制”行为。 app-generator-cli 默认启用了 handle_parsing_errors=True 和 max_iterations=15 ,这会让 Agent 在不确定时主动放弃,而不是胡说。
排查与修复:
- 检查检索质量 :先绕过 Agent,直接测试向量库。运行
uv run shell进入 Python REPL,然后:
如果返回的内容和问题无关,说明嵌入质量差或切分粒度不对。此时,去from app.vectorstore.chroma import vectorstore results = vectorstore.similarity_search("How to reset password?", k=3) for r in results: print(r.page_content[:100])app/vectorstore/chroma.py,调整RecursiveCharacterTextSplitter的chunk_size=500(默认 1000)和chunk_overlap=100(默认 200); - 检查 Prompt 工程 :
app/agent/prompt.py里有一个REACT_AGENT_PROMPT,它包含了详细的Thought/Action/Action Input/Observation格式说明。如果你的问题太开放(比如“谈谈 AI 的未来”),Agent 会因无法匹配 Tool 而放弃。在app/api/v1/endpoints/agent.py的chat函数里,给agent_executor.invoke()加一个input的预处理:# 在 invoke 前加 if "weather" in message.lower(): message = f"Use the search_tool to get current weather in {extract_location(message)}" - 降低温度(Temperature) :在
app/agent/llm.py里,把ChatOpenAI(temperature=0.3)改成temperature=0.1,减少随机性,让 Agent 更“循规蹈矩”。
4.3 “GitHub Actions 测试失败:psycopg2-binary not found”
这是 --async-db postgres 模板在 CI 中的经典问题。 psycopg2-binary 是纯 Python 的轮子,但 psycopg (新版)需要编译 C 扩展,在 GitHub 的 ubuntu-latest runner 上,默认没有 libpq-dev 。
永久解决方案(非临时 hack): app-generator-cli 生成的 .github/workflows/test-with-postgres.yml 里, jobs.test.steps 的第一部分是:
- name: Setup PostgreSQL
uses: docker://postgres:15
with:
env:
POSTGRES_PASSWORD: postgres
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libpq-dev gcc
但很多用户会删掉这个 Install system dependencies 步骤,觉得“不就是装个包嘛”。千万别删! libpq-dev 是 psycopg 编译的头文件, gcc 是编译器。没有它们, uv pip install psycopg 就会失败。
快速验证:
在本地模拟 CI 环境:
# 启动一个干净的 Ubuntu 容器
docker run -it --rm ubuntu:22.04
# 在容器里执行 CI 步骤
apt-get update && apt-get install -y curl python3-pip python3-venv
curl -LsSf https://astral.sh/uv/install.sh | sh
source /root/.cargo/env
uv pip install app-generator-cli
app-gen fastapi --async-db postgres -o /tmp/test
cd /tmp/test
uv pip install -e ".[dev]"
# 此时,如果报错 "Failed building wheel for psycopg",就证明缺少 libpq-dev
4.4 “我想加一个自定义中间件,但不知道该放哪?”
app-generator-cli 的设计哲学是“约定优于配置”,所以它不会在 main.py 里留一堆 # TODO: Add your middleware here 的注释。它把中间件的入口点放在了 app/core/lifespan.py 的 on_startup 函数里。
正确做法:
- 在
app/middleware/下新建auth_middleware.py(如果目录不存在,就创建它); - 写一个标准的 ASGI 中间件类:
from starlette.middleware.base import BaseHTTPMiddleware from starlette.requests import Request from starlette.responses import Response class AuthMiddleware(BaseHTTPMiddleware): async def dispatch(self, request: Request, call_next): token = request.headers.get("X-API-Key") if not token or token != "my-secret-key": return Response("Forbidden", status_code=403) return await call_next(request) - 在
app/core/lifespan.py的on_startup函数末尾,添加:from app.middleware.auth_middleware import AuthMiddleware app.add_middleware(AuthMiddleware)
这样,你的中间件就和 app 实例的生命周期绑定,既不会漏掉,也不会在错误的时机被注册。 app-generator-cli 的所有“扩展点”都遵循这个模式:它给你一个清晰的、有语义的目录( middleware/ ),一个标准的接口( BaseHTTPMiddleware ),和一个唯一的注册位置( lifespan.py )。你不需要猜,也不需要翻文档。
5. 进阶技巧与团队协作实践
5.1 创建私有模板:把团队规范固化为代码
app-generator-cli 支持 --template-url 参数,可以拉取 Git 仓库里的自定义模板。我们团队就维护了一个私有仓库 https://github.com/our-team/python-templates ,里面包含:
fastapi-internal/: 内部 FastAPI 模板,预装了公司统一的loggingSDK、metricsexporter(Prometheus)、tracing(OpenTelemetry)和feature-flagclient(LaunchDarkly);langchain-internal/: LangChain 模板,llm.py里预置了公司认证的AzureOpenAI实例,tools/里有jira_tool.py(连接 Jira API)和confluence_tool.py(搜索 Confluence);fullstack-internal/: 全栈模板,前端vite.config.ts里预设了公司 CDN 和 SSO 重定向逻辑。
使用方式极其简单:
app-gen fastapi --template-url https://github.com/our-team/python-templates.git#fastapi-internal -o ./my-internal-service
#fastapi-internal 是 Git 分支或 tag 名。 app-generator-cli 会克隆仓库,切换到该分支,然后用里面的 template/ 目录作为模板源。这意味着,你团队的每一个新项目,从第一天起就符合所有合规要求(日志格式、监控埋点、安全头),再也不用靠 Code Review 去“人肉检查”。
5.2 与 pre-commit 集成:让代码质量在提交前就守住
app-generator-cli 生成的项目默认包含 .pre-commit-config.yaml ,但它只启用了基础的 ruff-pre-commit 和 black 。我们可以把它升级为“团队级质量门禁”。
增强步骤:
- 在项目根目录,运行:
#
更多推荐



所有评论(0)