uv——Python 包管理的革命,AI 工程师为什么要换
uv——Python 包管理的革命,AI 工程师为什么要换
适读人群:AI 工程师、Python 开发者 | 阅读时长:约 12 分钟 | 核心价值:uv 完整迁移指南,以及为什么在 AI 项目里 uv 特别有价值
我在一台新 Mac 上设置 AI 开发环境,用 pip 装 LangChain 的全家桶,等了将近四分钟。
同样的事情用 uv 做,我掐表——19 秒。
这个对比让我当场就决定把手里所有 AI 项目迁移到 uv。
我不是那种追新工具的人。从 pip 换 conda,从 conda 换 poetry,每次迁移都是因为有具体的痛点。这次换 uv 也一样,不是因为它新,是因为它真的解决了 AI 项目里的几个顽固问题。
AI 项目的包管理为什么特别难
做普通 Python 项目,pip 其实还好。但 AI 项目有几个特点让包管理痛苦程度指数级上升:
依赖树特别深。 你装一个 transformers,它会带出来 torch、tokenizers、safetensors、huggingface_hub……每一个再带出来一堆。一个 AI 项目轻松几百个依赖包。
包特别大。 torch 单个就是 2-3GB。依赖解析的时候,pip 会去网络请求每个包的 metadata,这个过程在慢网络下是折磨。
版本冲突是家常便饭。 CUDA 版本、torch 版本、transformers 版本之间的兼容性是个矩阵,一不小心就装出来不兼容的组合。
多个项目并行。 我现在同时在维护一个用 openai SDK 的应用、一个用 langchain 的 RAG 项目、一个用 litellm 的代理服务。它们对各种库的版本要求不一样,必须用虚拟环境隔离。
pip 在这些场景下的问题:慢、解析逻辑简单容易冲突、虚拟环境管理不统一。
uv 是什么,为什么这么快
uv 是 Astral 公司(就是做 ruff 那家)用 Rust 写的 Python 包管理器和项目管理工具。
速度快的原因:
- 依赖解析算法是并发的,不是串行的
- 有本地包缓存,同一个包在不同项目里只下载一次
- 对 PyPI 的请求做了优化,减少网络 round-trip
- 整个工具链用 Rust 实现,没有 Python 自身的启动开销
但速度只是表面。uv 真正的价值是它把 pip、venv、pip-tools、pyenv 这些工具的功能整合进了一个工具,而且有合理的默认配置。
安装 uv
# macOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# 或者用 Homebrew
brew install uv
# Windows
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
# 验证安装
uv --version
# uv 0.5.x (...)核心命令速查
先把最常用的命令对比列出来,方便迁移:
pip 命令 uv 等价命令
─────────────────────────────────────────────────────────
pip install requests uv pip install requests
pip install -r req.txt uv pip install -r requirements.txt
pip uninstall requests uv pip uninstall requests
pip list uv pip list
pip freeze uv pip freeze
pip show requests uv pip show requests
python -m venv .venv uv venv
source .venv/bin/activate source .venv/bin/activate (同)
conda create -n myenv uv venv --python 3.11
conda activate myenv source .venv/bin/activate注意:uv 不需要"激活"虚拟环境就能工作——用 uv run 命令可以直接在虚拟环境里执行,不用 activate。
项目初始化:新建一个 AI 项目
# 创建新项目
uv init my-ai-project
cd my-ai-project
# 项目结构
# my-ai-project/
# ├── .python-version (Python 版本锁定)
# ├── pyproject.toml (项目配置和依赖声明)
# ├── uv.lock (锁定文件,所有依赖的精确版本)
# └── src/
# └── my_ai_project/
# └── __init__.py
# 添加依赖
uv add openai
uv add langchain langchain-openai
uv add --dev pytest pytest-asyncio # 开发依赖
# 查看 pyproject.toml(自动更新)
cat pyproject.tomlpyproject.toml 会自动更新:
[project]
name = "my-ai-project"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
"openai>=1.50.0",
"langchain>=0.3.0",
"langchain-openai>=0.2.0",
]
[tool.uv]
dev-dependencies = [
"pytest>=8.0.0",
"pytest-asyncio>=0.24.0",
]AI 项目常用场景
场景一:torch + CUDA 版本管理
这是 AI 工程师最头疼的问题。pip 装 torch 如果没指定 CUDA 版本,可能装出来 CPU 版本。
# 先确认你的 CUDA 版本
nvidia-smi | grep "CUDA Version"
# uv 安装指定 CUDA 版本的 torch
# 通过 --extra-index-url 指定 PyTorch 的 CUDA 专用源
uv add torch --extra-index-url https://download.pytorch.org/whl/cu121
# 或者在 pyproject.toml 里配置,不用每次手写在 pyproject.toml 里配置(推荐):
[project]
dependencies = [
"torch>=2.1.0",
"torchvision>=0.16.0",
]
[[tool.uv.index]]
name = "pytorch-cu121"
url = "https://download.pytorch.org/whl/cu121"
explicit = true
[tool.uv.sources]
torch = { index = "pytorch-cu121" }
torchvision = { index = "pytorch-cu121" }场景二:不同项目用不同 Python 版本
我有一个项目要求 Python 3.10(因为依赖的某个库还不支持 3.12),另一个项目跑在 Python 3.12 上。
# uv 可以直接管理 Python 版本(不需要 pyenv!)
uv python install 3.10
uv python install 3.12
# 列出已安装的 Python 版本
uv python list
# 为特定项目指定 Python 版本
cd project-old
uv venv --python 3.10
uv sync
cd project-new
uv venv --python 3.12
uv sync
# 项目根目录的 .python-version 文件会记录版本
# uv 会自动读取这个文件场景三:requirements.txt 项目迁移
已有项目用 requirements.txt,想切换到 uv:
cd existing-project
# 从 requirements.txt 导入依赖
uv init --no-readme
uv add $(cat requirements.txt | grep -v "^#" | grep -v "^$" | tr '\n' ' ')
# 或者更安全的方式:先用 uv pip 装,再生成 pyproject.toml
uv venv
uv pip install -r requirements.txt
uv pip freeze > /tmp/frozen.txt
# 然后手动整理 pyproject.toml,把核心依赖(非 transitive)加进去场景四:运行脚本不污染全局环境
这个功能我特别喜欢:临时运行一个脚本,不用先建虚拟环境。
# 直接运行,uv 会自动创建临时环境并安装依赖
uv run --with openai python my_script.py
# 在脚本里声明依赖(inline script metadata,PEP 723)
# my_script.py 开头:
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "openai>=1.0",
# "rich>=13.0",
# ]
# ///
uv run my_script.py # uv 自动读取脚本里的依赖声明并安装这个功能对于写一次性的 AI 脚本(比如批量处理数据)特别有用。
uv 的 lock 文件机制
这是 uv 和 pip 最大的区别之一:uv.lock 文件。
# 安装依赖(基于 uv.lock,保证所有人用同样的版本)
uv sync
# 更新所有依赖到最新兼容版本
uv lock --upgrade
# 更新特定包
uv lock --upgrade-package openai
# 检查是否有安全漏洞(需要 uv audit,目前在实验阶段)uv.lock 应该提交到 git,这样团队里所有人、CI 环境都用完全一致的依赖版本。这解决了 "在我机器上好好的" 问题。
和 Docker 集成
AI 项目的生产环境通常是 Docker,uv 在 Dockerfile 里的用法:
FROM python:3.11-slim
# 安装 uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
WORKDIR /app
# 先复制依赖文件(利用 Docker 层缓存)
COPY pyproject.toml uv.lock ./
# 安装依赖(--frozen 确保使用 lock 文件,不更新)
RUN uv sync --frozen --no-dev
# 复制代码
COPY src/ ./src/
# 使用 uv run 启动(不需要 activate 虚拟环境)
CMD ["uv", "run", "python", "-m", "src.main"]比用 pip 的 Dockerfile 快很多,特别是依赖没变化时缓存命中率高。
我的真实迁移体验
我迁移了三个 AI 项目:
项目一:OpenAI 应用(20 个左右的依赖)
- pip install 时间:约 45 秒
- uv sync 时间:约 4 秒(缓存热的情况下)
- 迁移时间:10 分钟,基本无痛
项目二:LangChain RAG 系统(80 多个依赖)
- pip install 时间:约 3 分半
- uv sync 时间:约 18 秒(首次,无缓存)
- 迁移时间:约 30 分钟,主要是整理 pyproject.toml
项目三:带 torch 的模型推理服务(依赖最复杂)
- 需要处理 CUDA 版本的 index 配置
- 迁移时间约 1 小时,主要卡在 torch 的 index 配置上
- 配好之后,复现环境的速度大幅提升
总体来说:对于纯 API 调用类的 AI 项目,迁移非常轻松。对于涉及 GPU 和本地模型的项目,要花一些时间配置 index,但配好之后受益更大。
一句话总结
uv 不是 pip 的替代品,是整个 Python 工具链的现代化重写。对于管理多个 AI 项目、需要频繁重建环境、团队协作的场景,它解决的问题是真实的。
换的成本很低,换完的收益很明显。没什么理由不换。
