创建时间: 2026-08-28最后更新: 2026-08-28

1. 依赖

依赖是项目运行或开发时需要安装的其他 Python 包。当前 FastAPI 服务会导入这些模块:

imports.py
1
from fastapi import FastAPI
2
from langchain.agents import create_agent
3
from langchain_openai import ChatOpenAI
4
from pydantic_settings import BaseSettings

这些导入能否成功,取决于对应的包是否已经安装在当前 Python 环境中。pyproject.toml 负责声明项目依赖,uv 负责解析并安装它们:

pyproject.toml
1
[project]
2
dependencies = [
3
"fastapi[standard]>=0.141.1",
4
"langchain>=1.3.16",
5
"langchain-openai>=1.6.0",
6
"pydantic-settings>=2.10.1",
7
]

依赖名称与导入名称可能不同:

安装名称导入名称
langchain-openailangchain_openai
pydantic-settingspydantic_settings
python-dotenvdotenv

安装名称由包发布到 Python 包索引时决定;导入名称由包内部模块结构决定。遇到 ModuleNotFoundError 时,不要仅凭导入名猜安装命令,应先查看该库的官方安装说明。

依赖还分为直接依赖和传递依赖。我们主动声明 fastapi,它内部还需要 Starlette、Pydantic 等包,这些就是传递依赖。项目应把代码直接使用的包声明为直接依赖,不必把依赖树中的每个包都手工复制到 [project].dependencies。反过来,如果代码直接导入了一个传递依赖,也应把它提升为直接依赖,避免上游调整依赖树后项目突然失效。

2. 运行依赖

[project].dependencies 保存项目正常运行必须存在的包。uv 会把它们安装到项目虚拟环境:

runtime-dependencies.toml
1
[project]
2
dependencies = [
3
"fastapi[standard]>=0.141.1",
4
"langchain>=1.3.16",
5
"langchain-openai>=1.6.0",
6
"pydantic-settings>=2.10.1",
7
]

如果生产代码会导入某个包,它通常就应该是运行依赖。不能因为开发电脑已经全局安装,就省略声明。

当前项目通过 pydantic-settings 读取 .env,而 pydantic-settings 已经把 python-dotenv 声明为自己的依赖,因此不需要重复添加。只有项目直接导入 dotenv,或者确实需要独立约束它的版本时,才应把 python-dotenv 提升为直接依赖。

fastapi[standard] 中的方括号表示安装 FastAPI 提供的 standard extra。extra 是一个包公开的可选功能集合。FastAPI 的标准 extra 会带上 CLI、Uvicorn 及常见标准依赖,因此当前项目可以直接运行:

fastapi-dev.bash
1
uv run fastapi dev

当前版本的 standard extra 还会带上 FastAPI Cloud CLI。如果只需要常用标准依赖而不需要这个云部署命令,可以改用 fastapi[standard-no-fastapi-cloud-cli]。

extra 不是版本范围。下面三个片段分别表示包名、可选功能和版本约束:

dependency-shape.txt
1
fastapi [standard] >=0.141.1
2
包名 extra 版本约束

有些依赖看起来功能相似,但并不重复。例如:

  • fastapi[standard] 提供 Web 框架和常用服务端依赖;
  • langchain 提供 Agent、消息和 Runnable 等核心抽象;
  • langchain-openai 提供 OpenAI 与 OpenAI 兼容接口的模型实现;
  • pydantic-settings 负责把环境变量和配置文件转换成类型安全的 Settings。

判断一个包是否应该保留,应该检查代码是否直接依赖它以及 extra 是否已经覆盖需求,不能只看包名是否相似。

3. 版本

依赖字符串可以包含版本约束:

写法含义
fastapi允许解析器选择任意兼容版本
fastapi>=0.141.1不低于指定版本
fastapi>=0.141.1,<1位于指定区间
fastapi==0.141.1只允许一个精确版本
fastapi~=0.141.1等价于 >=0.141.1,<0.142.0,允许补丁升级
fastapi!=0.142.0排除某个已知问题版本

应用项目通常不需要把 [project].dependencies 全部锁成 ==。可以声明项目愿意支持的最低版本和必要上限,再由 uv.lock 锁定当前环境的精确结果。

需要注意,>=0.141.1 是一项兼容性承诺,不表示 uv 已经替你测试过 0.141.1。uv 默认倾向选择范围内的较新版本,锁文件只能记录并复现这次选中的组合。如果项目必须保证最低版本可用,还需要通过最低版本解析或单独的测试矩阵进行验证。

dependency-layers.txt
1
pyproject.toml:这个项目允许使用哪些版本?
2
uv.lock:这一次实际安装哪些精确版本?

如果只写宽泛范围却不提交锁文件,不同机器可能在不同时间解析出不同结果。如果把每个传递依赖都写死在 pyproject.toml,升级和维护又会变得困难。

环境标记

依赖可以根据 Python 版本或操作系统条件生效:

environment-markers.toml
1
[project]
2
dependencies = [
3
"uvloop>=0.22; sys_platform != 'win32'",
4
"colorama>=0.4; sys_platform == 'win32'",
5
]

分号右侧是环境标记。只有条件成立时才安装对应依赖。平台差异确实存在时再使用,普通纯 Python 依赖不需要额外添加条件。

直接来源

标准依赖字符串主要面向包索引。Git 仓库、本地目录和私有索引等来源,uv 会通过 [tool.uv.sources] 等配置补充。它们会在下一篇 uv 配置中单独讲解。

4. 分组

并非所有依赖都应该随生产应用安装。测试、格式化、类型检查只在开发或 CI 中使用,可以放进标准的 [dependency-groups]:

dependency-groups.toml
01
[dependency-groups]
02
dev = [
03
{ include-group = "lint" },
04
{ include-group = "test" },
05
]
06
lint = [
07
"ruff>=0.12",
08
"mypy>=1.17",
09
]
10
test = [
11
"pytest>=8.4",
12
"pytest-asyncio>=1.1",
13
"coverage>=7.10",
14
"httpx>=0.28",
15
]

这里有三个组:lint、test 和 dev。dev 通过 include-group 包含另外两个组,因此本地开发可以一次安装完整工具链,CI 也可以只安装自己需要的组。

uv 默认会包含 dev 组。常用命令包括:

dependency-group-commands.bash
1
uv sync # 同步项目及默认依赖组
2
uv sync --no-dev # 不包含 dev 组
3
uv sync --no-default-groups --group test # 项目、运行依赖和 test 组
4
uv sync --only-group lint # 只同步 lint,不安装项目和运行依赖
5
uv sync --all-groups # 同步全部组

--group test 会在项目和默认依赖组之外追加 test。但当前示例中的默认 dev 已经包含 test,单独追加不会改变结果,因此上面先用 --no-default-groups 关闭默认组。--only-group lint 的含义更严格:只安装这个组,当前项目及其运行依赖也会被省略。

--no-dev 只是关闭名为 dev 的组。如果 [tool.uv] 还配置了其他默认组,它们仍会安装;生产环境需要排除全部默认组时,应使用 --no-default-groups。

还要区分依赖组与可选依赖:

配置面向谁是否进入发布元数据
[project].dependencies所有运行用户是
[project.optional-dependencies]安装项目并选择额外功能的用户是
[dependency-groups]当前项目开发、测试和文档流程否

例如,一个可发布的库可以提供 PostgreSQL extra:

optional-dependencies.toml
1
[project.optional-dependencies]
2
postgres = [
3
"psycopg[binary]>=3.2",
4
]

当前项目可以通过 uv sync --extra postgres 启用这个 extra;其他项目依赖它时,可以执行 uv add "your-package[postgres]"。但当前 FastAPI 应用的 pytest、Ruff 不属于对外功能,应放进依赖组而不是 optional dependencies。

旧版 uv 曾使用 [tool.uv].dev-dependencies。当前项目应优先采用已经标准化的 [dependency-groups],不要在新文章和新项目中继续复制旧写法。

5. uv 命令

虽然可以手动编辑依赖数组,但日常增删依赖更适合使用 uv 命令。默认情况下,uv 会更新 pyproject.toml 和 uv.lock,并同步项目虚拟环境;只有显式使用 --no-sync 时才跳过环境同步:

dependency-commands.bash
1
uv add sqlalchemy
2
uv add "psycopg[binary]>=3.2"
3
uv add --dev pytest
4
uv add --group lint ruff
5
uv remove sqlalchemy

命令中的引号由 Shell 使用,防止 >、<、方括号等字符被错误解释。写入 TOML 后,依赖仍然是普通字符串。

几个常用命令的职责如下:

命令作用
uv add声明依赖,更新锁文件并同步环境
uv remove移除直接依赖,重新解析并同步环境
uv lock创建或更新锁文件,不同步环境
uv lock --check检查锁文件是否与项目声明一致
uv sync让虚拟环境与项目及锁文件一致
uv run在项目环境中执行命令,必要时先同步
uv tree查看直接和传递依赖关系

uv run 不是简单地使用系统 Python。它会找到项目、虚拟环境和锁文件,然后在对应环境中执行命令:

run-python.bash
1
uv run python -c "import fastapi; print(fastapi.__version__)"

使用 python main.py 能运行,不代表依赖声明正确;它可能意外使用系统环境。项目统一使用 uv run,可以减少「我的电脑能运行,别人电脑不能运行」的问题

6. 锁文件

uv.lock 是 uv 自动维护的跨平台锁文件。它包含精确版本、包来源、哈希值和依赖关系。通常应该提交到应用项目仓库,让开发、CI 和部署基于同一解析结果。

不要手工编辑 uv.lock。需要升级依赖时,应修改声明或使用 uv 命令:

lock-commands.bash
1
uv lock --upgrade-package fastapi
2
uv lock --upgrade
3
uv lock --check
4
uv sync --locked --no-default-groups

--upgrade-package fastapi 只把 FastAPI 作为升级目标,并尽量保留其他已锁定版本;如果新版 FastAPI 对传递依赖提出了新要求,相关包仍可能随之变化。--upgrade 会尝试升级整个依赖图,适合计划明确的集中升级。

uv lock --check 只检查锁文件,uv sync --locked 则会在同步环境前检查。如果 pyproject.toml 中的依赖修改使锁文件过期,它们会失败,而不是自动改写锁文件。示例中的 --no-default-groups 进一步排除了 dev 等默认组,适合只安装生产运行依赖。

--frozen 的含义不同:它会跳过新旧检查,直接把现有锁文件当作事实来源。如果 pyproject.toml 新增了依赖而锁文件尚未更新,新依赖可能不会被安装。这个选项适合明确需要信任现有锁文件的场景,例如 Docker 分层构建时尚未复制全部项目文件,但不应把它当作 CI 的一致性检查开关。

升级后至少需要检查:

  1. FastAPI 服务能否启动;
  2. 请求和响应校验是否一致;
  3. LangChain 模型调用和流式事件是否变化;
  4. LangGraph checkpointer 和数据库驱动是否兼容;
  5. 测试、类型检查和格式化是否通过。

锁文件能固定版本,不能证明版本一定正确。验证仍然要由测试和运行检查完成。

常见排查

声明了依赖仍无法导入

先确认命令是否通过 uv run 执行,再检查安装名称和导入名称是否混淆:

inspect-package.bash
1
uv run python -c "import langchain_openai; print(langchain_openai.__file__)"
2
uv tree

开发依赖进入生产环境

检查包是否误放进 [project].dependencies。生产同步时可以使用 uv sync --locked --no-default-groups,而不是在构建机器上复用开发虚拟环境。

依赖解析冲突

先阅读 uv 输出,确认是哪两个范围无法同时成立。不要立刻删除所有上限或执行强制安装。冲突通常来自:项目支持范围过宽、某个集成包版本过旧,或不同依赖组确实不兼容。

只更新了 pyproject.toml

手工改完依赖后运行 uv lock 或 uv sync,并同时提交锁文件。代码审查时,依赖声明与锁文件应出现在同一次变更中。

把模型名当作 Python 依赖

deepseek-v4-flash 是远程模型标识,不是需要 uv add 的 Python 包。模型调用需要 langchain-openai 等客户端包,而模型名应进入应用配置。

正在验证登录状态
请稍候,验证完成后将继续显示文章内容