首页
/ Langchain-Chatchat 代码贡献工作流:Poetry 依赖管理、本地开发环境与 ruff 格式化/Lint 实践

Langchain-Chatchat 代码贡献工作流:Poetry 依赖管理、本地开发环境与 ruff 格式化/Lint 实践

2026-09-05 09:36:24作者:殷蕙予

本篇围绕 Langchain-Chatchat 仓库的贡献指南文档 docs/contributing/code.md,系统讲解作为外部贡献者参与该项目代码开发的完整技术链路:如何通过 Fork & PR 流程提交代码、如何用 Poetry 正确管理依赖与本地开发环境、如何用 ruff 完成代码格式化与增量格式化、以及如何本地复现 CI 的 lint 与测试检查。读完本文,你可以独立完成从克隆仓库到提交 PR 的全流程,并理解仓库中 Makefilepyproject.toml 与 CI 工作流之间的对应关系。

贡献流程总览

按照 docs/contributing/code.md 的说明,Langchain-Chatchat 的代码贡献遵循标准的 Fork & Pull Request 流程:除非你是项目维护者,否则不要直接向主分支提交代码,应通过 fork 后再发起 PR 的方式贡献。

提交 PR 之前有两个硬性要求:

  1. 按 PR 模板操作,并预期 CI 系统会自动运行 linting 和测试,确保代码符合项目标准;
  2. 保持单元测试与文档同步更新
    • 添加新功能时,更新受影响的操作文档;
    • 修复 bug 时,尽可能添加一个单元测试,放置在 tests/integration_teststests/unit_tests 目录中。

对应地,仓库中确实存在这两个测试目录(位于 libs/chatchat-server/tests 下,包含 integration_tests/unit_tests/ 以及针对知识库向量库、文档加载器等模块的专项测试),并配有共享测试配置 conftest.py。贡献指南的总入口在 docs/contributing/README.md,其中还说明了 Issue 管理约定(15 天无回复的 Issue 会被关闭)以及遇到问题时可联系维护者协商规则调整。

依赖管理:Poetry 与虚拟环境策略

本项目使用 Poetry 管理依赖。这一点在仓库结构中可以直接印证:仓库根目录和两个子项目(服务端 libs/chatchat-server/pyproject.toml、SDK libs/python-sdk)各自带有 pyproject.tomlpoetry.toml

安装 Poetry 前的环境准备

贡献文档给出了两条关键提示:

  • 如果你使用 Conda,建议先创建并激活一个独立的 Conda 环境再安装 Poetry,例如:
conda create -n chatchat python=3.9

需要注意一个版本约束差异:上述命令是文档中的示例写法,而 libs/chatchat-server/pyproject.toml 中对服务端的实际 Python 约束为 python = ">=3.10,<3.12,!=3.9.7",因此创建环境时建议实际选用 3.10 或 3.11,避免安装后因版本不符而无法解析依赖。

  • 如果没有其他用 Poetry 管理的项目,pipx 或 pip 都可以完成 Poetry 的安装(Poetry 官方安装文档支持 pipx、pip、独立安装脚本等多种方式)。

让 Poetry 复用 Conda/Pyenv 的 Python

贡献文档中的第三条 Note 非常实用:如果你用 Conda 或 Pyenv 作为环境/包管理器,安装 Poetry 后应执行:

poetry config virtualenvs.prefer-active-python true

这条配置让 Poetry 优先使用当前已激活的 Python(如 Conda 环境中的 Python),而不是再单独找一个 virtualenv 解释器,从而避免环境割裂。

另外,从仓库中的 libs/chatchat-server/poetry.toml 可以看到项目自身的 Poetry 约定:

[virtualenvs]
in-project = true

[plugins]
[plugins.pypi_mirror]
url = "https://pypi.tuna.tsinghua.edu.cn/simple"

即虚拟环境创建在项目目录内(.venv),且默认配置了国内 PyPI 镜像加速源;libs/chatchat-server/pyproject.toml 中的 [[tool.poetry.source]] 也将清华源声明为 primary 源。

本地开发环境安装

贡献文档给出的两步操作如下:

  • 选择主项目目录(服务端代码位于 libs/chatchat-server,而不是仓库根目录):
cd Langchain-Chatchat/libs/chatchat-server/
  • 安装 chatchat 依赖(用于运行 chatchat 的 lint 与 tests):
poetry install --with lint,test

这里 --with lint,test 的含义可以直接在 pyproject.toml 中找到依据:linttest 都是标记为 optional = true 的依赖组。其中:

  • lint 组L170-L175):ruff = "^0.1.5"
  • test 组L148-L167):pytestpytest-covpytest-dotenvpytest-watcherfreezegunresponsespytest-asynciopytest-mockpytest-socketsyrupyrequests-mock 等,注释明确说明该组只收录运行测试所需的依赖;
  • 此外还有可选的 codespell 组(拼写检查)和 dev 组(jupyter、setuptools)。

pyproject.toml[tool.poetry.dependencies] 部分列出了核心运行时依赖:langchain 0.1.17fastapi ~0.109.2streamlit 1.34.0faiss-cpu ~1.7.4SQLAlchemy ~2.0.25 等,并通过 [tool.poetry.extras] 提供 xinferencezhipuaiollama 等可选扩展(L74-L77)。

开发环境如何被“安装”:direct_url.json 机制

贡献文档还特别解释了 poetry install 的一个底层行为,这对理解 editable 安装很有价值:

Poetry install 后会在你的 site-packages 安装一个 chatchat-<version>.dist-info 文件夹,带有 direct_url.json 文件,这个文件指向你的开发环境。

其原理是:该包在 pyproject.toml 中声明了 packages = [{include = "chatchat"}, {include = "langchain_chatchat"}] 两个本地包,Poetry 对本地包默认做 editable(可编辑)安装,direct_url.json 中的 url 字段记录了指向本仓库路径的信息,因此你在本地修改 chatchat/langchain_chatchat/ 下的源码后,运行环境中的行为会即时生效,无需重复安装。这也是贡献者可以一边改代码一边跑 lint/test 的基础。

代码格式化:ruff 与 make format

贡献文档指出本项目使用 ruff 进行代码格式化,并给出两条命令:

# 对整个库进行格式化
cd {chatchat-server}
make format
# 仅格式化当前分支相对主分支已修改的文件
make format_diff

这两条命令的真实实现见 Makefile

format format_diff:
	[ "$(PYTHON_FILES)" = "" ] || poetry run ruff format $(PYTHON_FILES)
	[ "$(PYTHON_FILES)" = "" ] || poetry run ruff --select I --fix $(PYTHON_FILES)

可以看到 format 实际执行两个动作:ruff format(代码风格格式化)和 ruff --select I --fix(import 排序自动修复)。两者的差异在于 PYTHON_FILES 变量:

  • make formatPYTHON_FILES=.,作用于整个项目;
  • make format_diff 时(Makefile L48),PYTHON_FILESgit diff 动态生成——取当前分支相对 master 分支已变更(且未删除)的 .py.ipynb 文件列表:
lint_diff format_diff: PYTHON_FILES=$(shell git diff --relative=libs/langchain --name-only --diff-filter=d master | grep -E '\.py$$|\.ipynb$$')

这正是贡献文档强调的使用场景:当你只修改了项目的一部分,希望确保改动部分格式正确、而不触碰代码库其余部分时make format_diff 可以最小化 diff,避免 PR 中混入大量与功能无关的格式变更,也降低了与主线合并时的冲突概率。

ruff 的具体规则集在 pyproject.toml 中声明:

[tool.ruff.lint]
select = [
    "E",    # pycodestyle
    "F",    # pyflakes
    "I",    # isort
    "T201", # print
]

即启用 pycodestyle 风格规则、pyflakes 错误检测、import 排序,并将 print 语句(T201)标记为问题——这对一个以 FastAPI + Streamlit 构建的服务端项目是合理的约束(服务端日志应走 loguru 而非 print)。同时 tool.ruff 排除了个别非 UTF-8 的测试样例文件。

Lint 检查:make lint 的完整检查链

虽然贡献文档主体聚焦格式化,但它明确要求 CI 会自动运行 linting 和测试,因此贡献者需要了解 make lint 实际执行了哪些检查。Makefile L53-L59 中,lint(及其 lint_diff/lint_package/lint_tests 变体)按顺序执行五层检查:

lint lint_diff lint_package lint_tests:
	./scripts/check_pydantic.sh .
	./scripts/lint_imports.sh
	poetry run ruff .
	[ "$(PYTHON_FILES)" = "" ] || poetry run ruff format $(PYTHON_FILES) --diff
	[ "$(PYTHON_FILES)" = "" ] || poetry run ruff --select I $(PYTHON_FILES)
	[ "$(PYTHON_FILES)" = "" ] || mkdir -p $(MYPY_CACHE) && poetry run mypy $(PYTHON_FILES) --cache-dir $(MYPY_CACHE)

前两个脚本是本仓库特有的两条硬性约束,值得单独说明:

1. scripts/check_pydantic.sh:用 git grep 搜索以 import pydanticfrom pydantic 开头的行,一旦发现即报错退出,并提示改为从 langchain_core.pydantic_v1 导入(例如把 from pydantic import BaseModel 替换为 from langchain_core.pydantic_v1 import BaseModel)。这条规则的背景是:项目同时兼容 pydantic v1/v2 两套 API(仓库中可见 chatchat/pydantic_v1.pychatchat/pydantic_v2.py 两个兼容层),直接依赖 pydantic 具体版本会破坏兼容性,因此统一收敛导入路径。贡献新功能时如果触发了该报错,按脚本提示替换导入即可。

2. scripts/lint_imports.sh:用 git grep 检查是否存在以 from chatchat. 开头的导入行,若发现则以非零状态退出。从源码结构看,项目对外发布的是 chatchatlangchain_chatchat 两个顶层包(见 pyproject.tomlpackages 声明),该脚本用于约束代码内部的导入方式,避免引入不符合约定的子模块导入路径。

其后是通用的 ruff 检查、ruff format --diff(只打印 diff 不落地修改,用于校验格式是否合规)、ruff --select I(校验 import 排序)以及 mypy 类型检查。mypy 的全局配置在 pyproject.toml L207-L210ignore_missing_imports = "True"disallow_untyped_defs = "True",即要求函数定义带类型注解;lint_diff/lint_tests 分别使用独立的 mypy 缓存目录(.mypy_cache_test)以避免污染。

Makefile 还提供了按范围执行的变体,方便局部修改时聚焦检查:

  • make lint_package:仅检查 chatchat 包目录;
  • make lint_tests:仅检查 tests 目录;
  • make lint_diff:仅检查相对 master 的变更文件。

此外 Makefile 还包含拼写检查目标 spell_check/spell_fix(基于 codespell,配置见 pyproject.toml L242-L249 的 skip 与 ignore-words-list)。

本地运行测试:与 CI 对齐的测试命令

贡献文档要求修复 bug 时“尽可能在 tests/integration_teststests/unit_tests 中添加单元测试”。本地验证测试时,可以使用 Makefile 中定义的一组目标:

TEST_FILE ?= tests/unit_tests/

test tests:
	poetry run pytest --disable-socket --allow-unix-socket $(TEST_FILE)

coverage:
	poetry run pytest --cov --cov-config=.coveragerc \
		--cov-report xml --cov-report term-missing:skip-covered $(TEST_FILE)

integration_tests:
	poetry run pytest tests/integration_tests

要点:

  • TEST_FILE 可覆盖make test TEST_FILE=tests/unit_tests/test_xxx.py 可只跑单个测试文件,适合开发循环中快速反馈;
  • --disable-socket --allow-unix-socket:由 pyproject.toml 中的 pytest-socket 插件提供,强制单测禁止发起真实网络请求(仅允许 Unix socket),保证测试的确定性与离线可运行——这与“修复 bug 需可测试”的贡献要求相呼应;
  • make coverage 生成覆盖率报告,tool.coverage.run 中已排除 tests/*

pyproject.toml[tool.pytest.ini_options] 还定义了全局测试行为:--strict-markers --strict-config --durations=5 --snapshot-warn-unused -svv,注册了三个自定义 marker(requiresscheduledcompile),并启用 asyncio_mode = "auto" 以支持 asyncio 测试。

tests/conftest.py 进一步实现了两套自定义机制,理解它们有助于编写符合约定的测试:

  • --only-extended / --only-core 选项L15-L26):配合 [tool.poetry.extras] 中定义的大型 extended_testing 依赖组(pyproject.toml L79-L146,包含 beautifulsoup4datasetspandas 周边等大量可选包)区分核心测试与扩展测试;
  • requires markerL55-L93):标注为 @pytest.mark.requires("pkg") 的测试在依赖包未安装时自动跳过,在 --only-extended 模式下则会直接失败以提醒补齐依赖。新写的测试如果依赖可选包,应使用该 marker 而非硬编码 import。

CI 检查如何复现:GitHub Actions 工作流

贡献文档提到“CI 系统会自动运行 linting 和测试”。具体实现见 ​.github/workflows/_test.yml

  • 固定使用 Poetry 1.7.1env.POETRY_VERSION),并通过自定义 action .github/actions/poetry_setup 完成 Python + Poetry 安装与缓存;
  • ubuntu-latest / windows-latest / macos-latest 三平台 × Python 版本矩阵上并行执行;
  • 核心步骤依次为:poetry install --with testL41)→ make testL46);
  • 最有特色的一步是测试纯净性检查L64-L75):测试结束后执行 git status 并断言输出包含 nothing to commit, working tree clean,即测试代码不允许在仓库中产生任何未跟踪文件(如临时生成的 tests/unit_tests/config/chatchat/ 目录会被显式清理)。

这意味着本地写测试时也应遵循同样纪律:不要在被测代码路径或仓库目录中生成落盘文件,或确保 fixture 能自清理。该工作流还支持 workflow_dispatch 手动触发,并允许通过 working-directory 输入切换到其他子项目(默认 ./libs/chatchat-server)。

小结

本文沿 docs/contributing/code.md 的主线,覆盖了 Langchain-Chatchat 代码贡献的完整技术闭环:

  1. 流程:Fork & PR、按 PR 模板操作、新功能更新文档、修 bug 补充 tests/integration_teststests/unit_tests 中的单测;
  2. 依赖:Poetry 管理依赖,Conda/Pyenv 用户建议 poetry config virtualenvs.prefer-active-python true,注意服务端 Python 约束为 >=3.10,<3.12,!=3.9.7
  3. 环境cd Langchain-Chatchat/libs/chatchat-server/ 后执行 poetry install --with lint,test,editable 安装通过 direct_url.json 指向本地源码;
  4. 格式化make format 全量、make format_diff 仅针对相对 master 的变更文件(ruff format + ruff --select I --fix);
  5. Lintmake lint 五层检查链(pydantic 导入约束、from chatchat. 导入约束、ruff、格式 diff 校验、mypy 类型注解要求);
  6. 测试与 CImake test 禁用 socket 网络请求,本地保持工作树干净以通过 CI 的纯净性断言。

按照以上链路完成本地验证后,再发起 PR,即可显著降低 CI 失败与评审往返的成本。

登录后查看全文
热门项目推荐
相关项目推荐