8.conda 与 uv 环境管理完全指南:从概念到实战避坑

314 阅读8分钟

conda 与 uv 环境管理完全指南:从概念到实战避坑

适用项目:LangChain-agent(后端 FastAPI + LangChain,前端 React + Vite) 一句话结论:本项目是纯 Python 项目,用 uv 管理即可,不需要 conda。 但理解三种组合(只用 conda / 只用 uv / conda + uv 配合)的启动命令差异,能避免"明明装了依赖却报 ModuleNotFoundError"这个经典坑。


一、关键命令(TL;DR)

管理方案后端怎么起前端怎么起关键原则
只用 condaconda activate xxx 后 python -m uvicorn ...npm run dev激活后 python 就是环境内的,裸跑即可
只用 uvuv sync 后 uv run uvicorn ...npm run dev必须用 uv run,裸 python 会找不到依赖
conda + uv 配合conda activate xxx 后 uv run ...(uv 用 conda 解释器时)或 python -m ...npm run dev确认 uv 的 .venv 用的是哪个解释器,别混

最坑的一点:用系统 Python(python -m uvicorn)启动时,它既不属于 conda 也不属于 uv 的 .venv,所以读不到任何环境的依赖 → 一请求就 No module named 'xxx'。本项目就踩过这个坑(详见第五节实战记录)。


二、核心概念扫盲

在讲怎么用之前,先把几个最容易混淆的概念厘清。

2.1 conda 负责什么

conda 是一个跨语言的环境 + 包管理器:

  • 创建隔离的 Python 环境(独立解释器 + 站点包)
  • 能装非 Python 的二进制依赖:CUDA、MKL、PyTorch GPU 版、R 语言包等
  • conda activate myenv 切换环境,激活后命令行里的 python / pip 都指向该环境
conda create -n langchain-agent python=3.11
conda activate langchain-agent
conda install numpy          # 或 pip install numpy

适用场景:需要 GPU / 系统级库、多语言项目、已有 conda 工作流。

2.2 uv 负责什么(重点:它不只是包管理器)

uv 是纯 Python 的极速环境 + 包管理器(Rust 实现,可替代 pip + venv + pip-tools):

  • 创建虚拟环境(.venv,uv sync 自动建)
  • 安装 Python 包,比 pip 快 10–100 倍
  • 依赖锁定:uv.lock 保证多机器可复现
  • 自带 Python 版本管理:本机没有对应解释器时自动下载(如本项目自动用 F:\uv-python\cpython-3.13)
uv sync                       # 按 pyproject.toml 建 .venv 并装依赖
uv run python -m app.main     # 在 .venv 里运行
uv add fastapi                # 加依赖并写入 pyproject.toml

⚠️ 常见误解纠正:uv 不是"单纯的包管理器",它和 conda 一样也做环境隔离(.venv 就是隔离出来的环境,只是默认放在项目目录内)。所以"uv 只是装包的"这个说法是错误的——uv venv 等价于 python -m vvenv,uv run 等价于"激活 .venv 再执行"。

适用场景:纯 Python 项目、Web 服务、AI / LLM 应用(本项目就是)。

2.3 .venv 是什么

项目私有的 Python 环境文件夹,含:

  • 独立的 Python 解释器副本(项目/.venv/Scripts/python.exe)
  • 独立的 site-packages/
  • Scripts/activate(Windows 激活脚本)

它与全局 Python 完全隔离,通常放进 .gitignore(不提交,靠 uv sync 重建),作用和 conda 的 envs/myenv 相同,但更轻更快。

验证你跑在哪个环境(隔离的本质):

uv run python -c "import sys; print(sys.executable)"
# 输出: ...\项目\backend\.venv\Scripts\python.exe  ← 证明跑在 .venv 隔离环境

一句话:uv run / 激活 .venv 后跑的所有 python 命令只在 .venv 这个"小房间"里生效,装错包、版本冲突都被关在里面。换项目前务必确认激活的是哪个环境。

2.4 .env 是什么(与 .venv 完全不同!)

.venv.env
是什么隔离的 Python 环境(文件夹)配置文件(文本)
存什么解释器 + 依赖包环境变量 / 密钥:OPENAI_API_KEY=xxx
谁读uv / Python 解释器代码(python-dotenv)
提交 git❌ 忽略❌ 忽略(含密钥!)
模板无.env.example 提交,.env 不提交

示例(本项目 backend/.env):

OPENAI_API_KEY=sk-xxxx
OPENAI_BASE_URL=https://api.deepseek.com
MODEL_NAME=deepseek-chat

代码通过 python-dotenv 的 load_dotenv() 读取(backend/app/config.py)。


三、怎么选:conda 还是 uv?

情况用哪个
纯 Python 后端 / Web / Agentuv(推荐)
需要 CUDA、PyTorch GPU、MKLconda
多语言 / 科研复现conda
追求安装速度 + lock 文件uv
已有 conda 基础环境conda 内用 uv(见下)

结论很清晰:纯 Python 项目优先 uv,需要系统级二进制依赖再上 conda。


四、四种使用模式与对应启动命令

下面按实际组合给出可直接抄的启动命令。前端都是 Node 项目,与 Python 环境无关,统一用 npm run dev。

模式 A:只用 conda

依赖装进名为 myenv 的 conda 环境,没有 .venv。

conda activate myenv
cd backend
python -m uvicorn app.main:app --host 0.0.0.0 --port 8000

为什么:conda activate 后 python 直接指向 envs/myenv/python.exe,依赖都在那里,裸 python 即可,不需要 uv,也没有 .venv。

模式 B:只用 uv(本项目采用)

uv sync 读 pyproject.toml 在 .venv 装依赖,uv run 确保命令在 .venv 内执行。

cd backend
uv sync                              # 建 .venv 并装依赖(首次需联网)
cp .env.example .env                 # 第一次需创建 .env 并填 API Key
uv run uvicorn app.main:app --host 0.0.0.0 --port 8000

为什么:uv 自己管理一切。注意 必须写 uv run,不能写 python -m uvicorn——后者调用的是系统 Python,读不到 .venv 里的包(见第五节实战坑)。本项目入口是 app.main:app(FastAPI 实例),故用 uv run uvicorn app.main:app。

模式 C:conda + uv 配合(conda 管解释器,uv 管依赖)

conda 提供基础解释器,uv 在其上用 requirements.txt / pyproject.toml 装依赖到 .venv 并运行。

conda activate myenv
cd backend
uv run python -m uvicorn app.main:app --reload --host 127.0.0.1 --port 8000

为什么:「conda 管解释器 + uv 管包」的组合。uv run 默认会创建自己的 .venv;若想让它直接用 conda 环境需加 --system 或先 uv venv --python $(which python),否则可能装了两份依赖。

让 uv 用当前 conda 解释器:

conda activate myenv
uv venv --python $(which python)    # .venv 基于 conda 解释器
uv sync

风险点:

  • 一般无冲突:uv 装到自己的 .venv,conda 装到 envs/myenv,互不干扰
  • 最大风险:忘了激活正确环境装错地方;或 conda 激活了但 uv 又自建了 .venv,运行时必须用 uv run 才能用对那个环境
  • 推荐:二选一,不要混。混用仅在「conda 管 Python 版本 + uv 管 Python 包」时才值得

模式 D:系统 Python(❌ 不推荐,最易踩坑)

直接用 F:\software download\Python\python.exe 这种系统解释器跑,不激活任何环境。

python -m uvicorn app.main:app --port 8000   # ❌ 容易缺依赖

为什么危险:系统 Python 的 site-packages 通常没装项目依赖(conda / uv 都没往里装),一运行就 ModuleNotFoundError。本项目就因此踩过坑(见下)。


五、实战:本项目标准启动流程

# 终端 1:后端(uv 环境)
cd backend
uv sync                         # ① pyproject.toml + uv.lock → 建 .venv 并装依赖
cp .env.example .env            # ② 创建 .env(若已存在可跳过),填入真实 API Key
uv run uvicorn app.main:app --host 0.0.0.0 --port 8000

# 终端 2:前端(Node,与 Python 环境无关)
cd frontend
npm install                     # 首次
npm run dev                     # Vite 启动,默认 5173,代理 /api → localhost:8000

关系链:

pyproject.toml ──uv sync──▶ .venv (运行环境 + 锁定版本)
.env          ──load_dotenv()──▶ 代码读取配置 / 密钥
frontend(5173) ──Vite proxy /api──▶ backend(8000)

三种真实项目对比

项目环境 / 依赖管理依赖声明文件启动核心差异
RAG-agent只用 conda无(手动 pip 装)conda activate + 裸 python
LangChain-agent只用 uvpyproject.toml + uv.lockuv sync + uv run
Agent-projectconda + uv 混用requirements.txtconda activate + uv run

运行时解释器位置对照(隔离层在哪一目了然):

项目运行时解释器位置
RAG-agent(conda)F:\Miniconda\envs\rag-agent\python.exe(conda 即隔离层,无 .venv)
LangChain-agent(uv)项目/backend/.venv/Scripts/python.exe
Agent-project(conda+uv)项目/Backend/.venv/Scripts/python.exe

六、实战踩坑记录:为什么必须用 uv run

现象:用户用 F:\software download\Python\python.exe 直接 python -m uvicorn app.main:app --port 8000,后端启动正常,但前端一提问就"没输出"。

排查:用 uv run python 直接 POST /api/chat 抓原始 SSE 字节,发现后端返回:

event: error
data: {"code": "STREAM_ERROR", "message": "No module named 'langchain_mcp_adapters'"}

模型调用需要 MCP 适配器,而系统 Python 的 site-packages 里没有(依赖只在 uv 的 .venv 中)。

根因:裸 python -m uvicorn 用的是系统 Python,既非 conda 也非 uv 的 .venv,读不到任何项目依赖。

解决:改用 uv 环境启动:

cd backend
uv run uvicorn app.main:app --host 0.0.0.0 --port 8000

重启后 SSE 正常返回 delta → done,前端恢复输出。

教训:本项目后端必须用 uv run 启动,不能裸 python -m uvicorn。这也印证了模式 B 的原则。


七、常见错误速查

现象原因解决
ModuleNotFoundError: xxx用系统 Python / 没激活环境跑改用 uv run 或先 conda activate
WinError 10048 端口占用上次进程没退出 / 残留子进程见第八节,杀真实持有进程
前端提问"没输出"后端 SSE 返回 event: error,前端没显示错误看后端日志,多半是 No module named ... → 用了错环境
uv run 很慢首次解析 / 下载解释器正常,后续快
conda activate 不生效没初始化 shellconda init powershell 后重开终端

八、端口冲突排查(Windows 实战)

联调时常遇到 OSError: [WinError 10048] 通常每个套接字地址只允许使用一次。

诊断谁占了端口(PowerShell):

netstat -ano | findstr ":8000"                          # 看占用 PID
Get-NetTCPConnection -LocalPort 8000 -State Listen      # 精确查监听进程
taskkill /PID <pid> /F                                  # 强杀

⚠️ 坑:Get-NetTCPConnection 有时会显示已退出父进程的 PID(socket 残留),taskkill 报"找不到进程"。这时真正持有 socket 的是它的子进程(multiprocessing fork 出来的)。用下面命令找真实持有者:

Get-CimInstance Win32_Process -Filter "ProcessId=<显示出的pid>" | Select-Object CommandLine
# 若 CommandLine 含 "multiprocessing.spawn ... parent_pid=xxx",则去查那个子进程并杀掉它

本项目曾因 uv run python xxx 的父进程残留子进程占着 8000,导致重启 uvicorn 失败,杀掉残留子进程后端口才释放。


九、一句话总结

  • .venv = 隔离的运行环境(装包的地方,含独立解释器副本 + site-packages)
  • uv sync = 把依赖装进 .venv 的命令
  • uv.lock = 保证依赖版本可复现的锁文件
  • uv run = "在 .venv 里执行命令"的快捷方式(替代手动激活)
  • conda activate = 切换到 conda 隔离环境,之后裸 python 即用该环境
  • .env = 放密钥 / 配置的文件,跟环境无关,只被代码读取
  • 铁律:跑项目前先确认"此刻的 python 到底属于哪个环境",否则就是下一个 ModuleNotFoundError