conda 与 uv 环境管理完全指南:从概念到实战避坑
适用项目:LangChain-agent(后端 FastAPI + LangChain,前端 React + Vite) 一句话结论 :本项目是纯 Python 项目,用 uv 管理即可,不需要 conda 。 但理解三种组合(只用 conda / 只用 uv / conda + uv 配合)的启动命令差异,能避免"明明装了依赖却报
ModuleNotFoundError"这个经典坑。
一、关键命令(TL;DR)
| 管理方案 | 后端怎么起 | 前端怎么起 | 关键原则 |
|---|---|---|---|
| 只用 conda | conda activate xxx 后 python -m uvicorn ... |
npm run dev |
激活后 python 就是环境内的,裸跑即可 |
| 只用 uv | uv 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都指向该环境
bash
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)
bash
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 相同,但更轻更快。
验证你跑在哪个环境(隔离的本质):
powershell
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):
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 / Agent | uv(推荐) |
| 需要 CUDA、PyTorch GPU、MKL | conda |
| 多语言 / 科研复现 | conda |
| 追求安装速度 + lock 文件 | uv |
| 已有 conda 基础环境 | conda 内用 uv(见下) |
结论很清晰:纯 Python 项目优先 uv,需要系统级二进制依赖再上 conda。
四、四种使用模式与对应启动命令
下面按实际组合给出可直接抄的启动命令 。前端都是 Node 项目,与 Python 环境无关,统一用 npm run dev。
模式 A:只用 conda
依赖装进名为 myenv 的 conda 环境,没有 .venv。
powershell
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 内执行。
powershell
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 并运行。
powershell
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 解释器:
bash
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 这种系统解释器跑,不激活任何环境。
powershell
python -m uvicorn app.main:app --port 8000 # ❌ 容易缺依赖
为什么危险 :系统 Python 的 site-packages 通常没装项目依赖(conda / uv 都没往里装),一运行就 ModuleNotFoundError。本项目就因此踩过坑(见下)。
五、实战:本项目标准启动流程
powershell
# 终端 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
关系链:
scss
pyproject.toml ──uv sync──▶ .venv (运行环境 + 锁定版本)
.env ──load_dotenv()──▶ 代码读取配置 / 密钥
frontend(5173) ──Vite proxy /api──▶ backend(8000)
三种真实项目对比
| 项目 | 环境 / 依赖管理 | 依赖声明文件 | 启动核心差异 |
|---|---|---|---|
| RAG-agent | 只用 conda | 无(手动 pip 装) | conda activate + 裸 python |
| LangChain-agent | 只用 uv | pyproject.toml + uv.lock |
uv sync + uv run |
| Agent-project | conda + uv 混用 | requirements.txt |
conda 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 字节,发现后端返回:
vbnet
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 环境启动:
powershell
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 不生效 |
没初始化 shell | conda init powershell 后重开终端 |
八、端口冲突排查(Windows 实战)
联调时常遇到 OSError: [WinError 10048] 通常每个套接字地址只允许使用一次。
诊断谁占了端口(PowerShell):
powershell
netstat -ano | findstr ":8000" # 看占用 PID
Get-NetTCPConnection -LocalPort 8000 -State Listen # 精确查监听进程
taskkill /PID <pid> /F # 强杀
⚠️ 坑:Get-NetTCPConnection 有时会显示已退出父进程的 PID(socket 残留),taskkill 报"找不到进程"。这时真正持有 socket 的是它的子进程(multiprocessing fork 出来的)。用下面命令找真实持有者:
powershell
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