后端依赖管理实战:以 pyproject.toml 为唯一事实来源、用 uv 统一解析与锁定的完整工作流)
danswerOnyx后端依赖管理实战以 pyproject.toml 为唯一事实来源、用 uv 统一解析与锁定的完整工作流【免费下载链接】danswerOpen Source AI Platform - AI Chat with advanced features that works with every LLM项目地址: https://gitcode.com/GitHub_Trending/da/danswer本篇技术指南围绕 backend/requirements/README.md 展开系统讲解 danswerOnyxAI 平台后端在 Python 依赖管理上采用的pyproject.toml uv.lock 导出 requirements.txt混合方案为什么放弃手工维护多个 requirements 文件、如何划分依赖分组、如何借助 pre-commit 钩子自动化「改一处、全同步」以及 Docker 构建与本地开发环境如何基于同一套锁定版本获得可复现的安装结果。读完你可以直接在自己的 danswer 分支上安全地增删依赖也能把这一套「单一事实来源 统一锁文件 哈希校验导出」的工程实践迁移到其他 Python 项目中。一、方案概览为什么用 pyproject.toml 而非直接改 requirements.txtbackend/requirements/目录在仓库中的定位是「为兼容既有 Docker 构建而保留的遗留产物」原文档原话This directory is kept for backwards compatibility with existing Docker builds。真正的依赖管理以仓库根目录的 pyproject.toml 为唯一事实来源single source of truth配合统一的 uv.lock 锁定所有已解析版本。原文档明确列出了这一设计带来的收益单一事实来源所有依赖只定义在pyproject.toml中一处维护无重复跨环境共享的依赖只声明一次不随环境重复列举统一锁文件所有版本一起解析天然保证互相兼容快速uv 的解析与安装速度远快于传统 pip-tools 工作流原文档给出的量级为 10–100 倍可复现构建锁文件钉死了全部直接与传递依赖易更新改pyproject.toml、提交、完成。需要说明的是uv 是 Astral 团队Rust 实现的 Python 包管理与解析器推出的工具它同时承担 pip、pip-tools、virtualenv、poetry 等工具的职能本仓库只把它用于依赖解析、锁定、导出与同步安装。二、文件结构与职责边界原文档给出的目录结构如下这也与仓库实际布局完全一致pyproject.toml # 事实来源 —— 改这里 uv.lock # 统一锁文件全部版本 backend/ └── requirements/ # 遗留 .txt 文件兼容 Docker 构建 ├── default.txt # 共享 backend 组 ├── dev.txt # 共享 dev 组 ├── ee.txt # 共享 ee 组 ├── model_server.txt # 共享 model_server 组 └── combined.txt # 聚合其余所有文件主要用于测试各文件职责对照仓库实况文件内容来源实际用途pyproject.toml手工维护依赖声明、工具链ruff/ty/basedpyright配置、uv 行为配置uv.lockuv lock生成跨平台、跨 Python 版本解析后的完整版本图default.txtuv export --group backendbackend 主镜像安装ee.txtuv export --group eeEnterprise Edition 附加依赖posthogmodel_server.txtuv export --group model_server模型服务镜像ML 依赖dev.txtuv export --group dev开发与测试工具链combined.txt手工-r聚合测试环境一次性安装全部依赖以 combined.txt 为例它本身只包含四条-r指令-r default.txt -r ee.txt -r model_server.txt -r dev.txt文件头注释明确说明它「combines all the other requirements filesPrimarily for testing」并建议实际运行时按需只安装对应部分这与原文档「按环境分组导出」的思路一脉相承。导出文件的哈希校验特性打开任意导出的.txt例如 default.txt可以看到文件头会记录生成它的完整命令且每个钉死的产物都带--hashsha256:...条目# This file was autogenerated by uv via the following command: # uv export --no-emit-project --no-default-groups --group backend -o backend/requirements/default.txt agent-client-protocol0.7.1 \ --hashsha256:4ffe999488f2b23db26f09becdfaa2aaae6529f0847a52bca61bc2c628001c0f \ --hashsha256:8d7031209e14c3f2f987e3b95e7d9c3286158e7b2af1bf43d6aae5b8a429249f # via onyx这正是原文档强调的安装安全边界Docker 构建与 CI 使用uv pip install --require-hashes安装任何版本号或产物哈希未出现在锁定文件中的包都会被拒绝安装因此实际部署只能落到uv.lock解析出的版本上杜绝了「构建时悄悄漂移到未锁定版本」的风险。三、pyproject.toml 中的依赖分组详解原文档指出新增依赖时应写入合适的 section这里结合仓库根目录 pyproject.toml 的真实结构逐一说明各组语义[project.dependencies]共享依赖backend 与 model_server共同使用的运行时依赖例如fastapi0.133.1、pydantic2.12.5、openai2.38.0、litellm[google]1.93.0、sentry-sdk2.14.0、uvicorn0.49.0等。文件内注释还解释了某些包被放进共享组的原因比如python-json-logger是为了让 model_server 镜像同样支持LOG_FORMATjson结构化日志。[dependency-groups.backend]backend 独有体量最大的一组覆盖连接器Slack、Confluence、Jira、Google Drive、Notion、SharePoint 等、异步框架aiohttp、celery、数据库SQLAlchemy2.0.50、asyncpg、psycopg2-binary、文件解析markitdown[pdf, docx, pptx, xlsx, xls]0.1.2且注释提醒更新前必须了解get_markitdown_converter的补丁行为等。组内还常见「冻结版本 原因注释」的写法例如openpyxl3.0.10因上游 issue 冻结、libpass1.9.3替代已停止维护且不兼容 Python 3.13 的 passlib。[dependency-groups.dev]开发工具pytest 全家桶pytest9.0.3、pytest-asyncio、pytest-xdist、pytest-playwright等、ruff0.16.1、pre-commit3.2.2、hatchling、matplotlib以及大量types-*类型桩。注释中强调部分版本需与.pre-commit-config.yaml中隔离安装的 hook 版本保持一致。[dependency-groups.ee]企业版特性目前仅posthog3.7.4服务于企业版埋点统计。[dependency-groups.model_server]ML 依赖accelerate、einops、numpy、sentence-transformers、torch2.9.1、transformers5.14.1等重 ML 包仅随 model_server 安装。额外分组[dependency-groups.zizmor]CI 单独同步的安全审计工具避免拖入整套 dev 工具链、[dependency-groups.loadtest]Locust 压测运行时刻意排除在默认组之外避免正常uv sync拉入 gevent/flask 等重依赖。此外[tool.uv]一节还定义了两个对本工作流至关重要的行为[tool.uv] # uv 仅用于依赖管理。onyx project 永不构建或安装为包 # Docker 镜像拷贝源码树并从 requirements 导出安装本地导入靠 cwd / PYTHONPATH 约定。 package false default-groups [backend, dev, ee, model_server]package false项目只作为依赖清单使用不会被pip install -e .安装成包仓库根 pyproject.toml 注释中说明 Docker 镜像直接拷贝源码树并按 requirements 导出安装本地导入依赖backend/pytest.ini的 cwd/PYTHONPATH 约定default-groups默认同步时会安装 backend、dev、ee、model_server 四组全部依赖正好对应原文档「uv sync为开发常用场景」的描述。[tool.uv]下还有override-dependencies用于放宽 mitmproxy 等包的过紧上限如tornado6.5.0附带 CVE 修复说明保证解析器保留 backend 需要的更新版本。四、完整工作流从安装 uv 到日常增删依赖1. 安装 uv未安装 uv 时原文档给出的官方安装方式macOS/Linuxcurl -LsSf https://astral.py/uv/install.sh | sh安装完成后可用uv --version验证。Windows 用户可参考 uv 官方文档的 PowerShell 安装方式仓库文档未展开这里不做断言。2. 添加 / 更新依赖只改 pyproject.toml原文档给出了强约束绝对不要直接编辑.txt文件。正确步骤是编辑 pyproject.toml按第二节的语义把依赖放入合适的 section[project.dependencies]backend 与 model_server 共享[dependency-groups.backend]backend 独有[dependency-groups.dev]开发工具[dependency-groups.ee]企业版特性[dependency-groups.model_server]ML 包提交变更 —— 提交时 pre-commit 钩子会自动重新生成锁文件与 requirements。仓库中.pre-commit-config.yaml的写法印证了第 3 步它引入astral-sh/uv-pre-commit仓库的uv-sync、uv-lock与三条uv-export钩子每个导出钩子都带有与手工命令完全一致的参数--no-emit-project --no-default-groups --group 组名 -o backend/requirements/对应文件并且只在这些文件变化时才触发files: ^(pyproject\.toml|uv\.lock|backend/requirements/.*\.txt)$。也就是说一次 commit 即可完成「改声明 → 重新解析 → 重新导出哈希校验 requirements」的闭环。3. 手动重新生成锁文件与 requirements若需要手动触发例如 CI 环境或临时核对原文档给出的命令为uv lock uv export --no-emit-project --no-default-groups --group backend -o backend/requirements/default.txt uv export --no-emit-project --no-default-groups --group dev -o backend/requirements/dev.txt uv export --no-emit-project --no-default-groups --group ee -o backend/requirements/ee.txt uv export --no-emit-project --no-default-groups --group model_server -o backend/requirements/model_server.txt参数含义补充说明--no-emit-project导出时不把项目自身onyx作为依赖项输出只输出第三方包--no-default-groups不包含default-groups定义的那一组从而让每条命令只导出--group指定的单一分组这与 .pre-commit-config.yaml 中钩子参数一一对应每条导出命令产出的.txt都会包含全部共享依赖 该组独有依赖这正是 default.txt 这类文件动辄上千行、且每个包都带 sha256 哈希的原因。4. 安装依赖uv sync 分组安装原文档把安装命令分成了三种场景# 开发常用 —— 安装 共享 backend dev ee uv sync # backend 生产 —— 仅 共享 backend uv sync --no-default-groups --group backend # model server —— 仅 共享 model_server不含任何 backend 依赖 uv sync --no-default-groups --group model_server其中默认uv sync的完整集合共享 backend dev ee model_server正是由[tool.uv] default-groups决定的。如果开启了uv-syncpre-commit 钩子切换分支或拉取新变更时依赖会自动同步安装无需手动干预。原文档也提示该钩子「If enabled」由 .pre-commit-config.yaml 顶部的default_install_hook_types中包含post-checkout、post-merge、post-rewrite可知这是 hook 安装阶段决定的行为。5. 升级依赖原文档给出的升级路径依然围绕「改声明 → 交给钩子」修改pyproject.toml中的版本约束提交pre-commit 钩子自动重新生成uv.lock与 requirements 文件。同时原文档特别提醒提交前务必仔细审查变更。从源码注释可以看到仓库对此相当谨慎——多处以注释冻结版本并说明原因如gpt4all在 Mac 与 slim-bookworm 镜像上的兼容问题、markitdown与文件抽取逻辑的耦合说明版本升级在本项目中往往是需要人工复核正确性的操作。五、Docker 构建中的实际落地--require-hashes 强制校验原文档强调「Docker builds and CI install withuv pip install --require-hashes」这一点在 backend/Dockerfile 中得到直接印证COPY ./requirements/default.txt /tmp/requirements.txt COPY ./requirements/ee.txt /tmp/ee-requirements.txt # requirements/*.txt 是完全预解析的锁文件导出--no-deps 原样安装 RUN uv pip install --system --no-cache-dir --no-deps --require-hashes \ -r /tmp/requirements.txt \ -r /tmp/ee-requirements.txt ...关键点拆解--no-deps导出文件已经是完整解析结果含全部传递依赖无需再次解析依赖树--require-hashes强制每个被安装的包都要在 requirements 中出现对应哈希否则拒绝安装——这正是「只能安装uv.lock内版本」的安全保证--systembackend 镜像直接在系统 Python 环境安装。model_server 镜像的处理更有代表性。查看 backend/Dockerfile.model_server 可以看到构建分两步先用awk从model_server.txt中筛出torch、nvidia-*、triton等重量级 ML 包单独安装便于分层缓存再安装剩余全部依赖两步都使用了--require-hashes与指向/app/.venv/bin/python的--python参数。由于导出文件把长哈希写成了\续行格式awk 需要按行首^[[:alnum:]]判断包条目才能正确筛选——这是锁定文件格式与构建脚本耦合的一个典型细节。六、工程实践要点小结综合原文档与仓库源码这套依赖管理方案的实践要点可以归纳为唯一入口所有依赖变更只发生在 pyproject.toml.txt是生成物而非编辑对象分组清晰共享 / backend / dev / ee / model_server 五组职责互斥配合default-groups与--no-default-groups --group实现「按需安装」避免 ML 依赖污染普通开发环境钩子自动化.pre-commit-config.yaml 中的uv-lock、uv-export、uv-sync让锁文件与导出文件始终与声明保持一致同时uv-sync覆盖分支切换、拉取等场景哈希强校验导出文件携带全部 sha256Docker/CI 用--require-hashes安装构建结果被严格钉死在锁定版本上遗留兼容backend/requirements/与combined.txt的存在是为了兼容历史 Docker 构建与测试场景新代码不应再依赖手工编辑它们。这套「单一事实来源 统一锁文件 分组导出 钩子自动同步 哈希强制校验」的组合既保留了 uv 快速、可复现的优点又兼容了既有镜像构建是大型 Python 单体项目如本仓库这种连接器众多、前后端与模型服务共存的架构中值得参考的依赖治理模板。【免费下载链接】danswerOpen Source AI Platform - AI Chat with advanced features that works with every LLM项目地址: https://gitcode.com/GitHub_Trending/da/danswer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考