ADR 0011: 采纳 python-project-standard;迁移到 src/ 布局,其余保持不变
采纳内部标准作为治理哲学;对齐唯一一条有界的结构不变量(src/ragspine/ 布局),并接受四项有记录的偏离。
状态:accepted · 日期:2026-06-18
不可变记录。豁免漂移追踪(无
covers)。只可被取代,不可编辑。
背景
一份外部内部标准("python-project-standard":mypy --strict + beartype claw 钩子 + 边界处 pydantic + ports//adapters/ Protocol 缝 + src/<pkg>/ 布局
- 一个
core/基础设施包 + 一条一键零警告门禁)被用来核查本仓库。
RAGSpine 依靠自身有记录的约定,已经满足该标准的意图及其大部分实质:mypy strict = true;src/ragspine/__init__.py 顶部的 beartype claw 钩子(受保护的 import、PEP 484 数值塔),且遵守 import 顺序纪律;每个外部依赖都置于 Protocol 之后,拥有零 SDK 的离线核心和默认 MockProvider;领域优先的深层布局;TDD;漂移守卫(scripts/check_doc_drift.py、scripts/check_docstring_refs.py);单一 CI 门禁(scripts/ci.sh);以及在代码中强制执行的防编造机制 / 来源溯源。
剩余差距纯属结构布局选择。其中,顶层包 vs src/<pkg>/ 是该标准视为硬性、机械化检查的不变量,也是迁移成本_有界_的一项:树内所有 import 已经使用绝对形式 from ragspine.…(零处 from src.…),因此移动包不会改动任何应用代码——只涉及打包配置、一处基于 __file__ 的相对根锚点、工具的路径参数,以及文档中的路径字符串。其余偏离项(rootutils、common/ vs core/、经由惰性 import 的 Protocol vs ports/+adapters/、稀疏使用 pydantic)会带来真实的横切成本,却没有任何行为收益。
决策
采纳 python-project-standard 作为治理哲学,并**对齐唯一一条有界、高价值的结构不变量:迁移到 src/ragspine/ 布局。**具体而言:
git mv ragspine src/ragspine;import ragspine通过 editable 安装继续可用(.pth现在指向src/)。应用代码零改动。- 配置跟进:hatchling 的
packages、mypy 的files以及[tool.ruff] src→src/ragspine/src;common/core.py中Path(__file__)…parents[N]锚点加一层;ci.sh/Makefile中 ruff 的路径参数;check_docstring_refs.py重新指向新根(其旧规则"任何src/…引用都是重组前的遗留死引用"被反转——src/ragspine/…现在是规范路径);文档及covers:中的路径字符串统一加上src/前缀。
保留以下偏离(成本 ≫ 收益——是被接受的选择,不是债务):
- 用
rootutils+.project-root做根解析(仅一处,common/core.py,import 期零副作用),而不用 editable 安装 +importlib.resources。 common/承担该标准分配给core/的横切基础设施角色。- SDK 隔离经由
Protocol+ 惰性 import(依 0009 / 0005),而不是ports/+adapters/的目录拆分。 - pydantic 只在已经使用的边界处使用,不做全面铺开。
check_conformance.py 未接入 ci.sh:它仍然断言存在 core/ 基础设施包、叶子级 core/settings.py 和 ports//adapters/ 目录,而本仓库刻意不设这些。scripts/ci.sh 仍是门禁。
考虑过的备选方案(已否决)
- 连顶层布局也保持不变(本 ADR 的最初立场):否决——
src/迁移是有界的(无应用代码翻动),且对齐了该标准唯一的硬性结构不变量,因此保护 (1)–(4) 的最小 diff 论据不适用于它。 - 完全字面遵从(同时增加
core/、拆分ports/+adapters/、全面 pydantic):否决——对一个成熟的 1078 测试代码库横切成本高昂,行为/安全收益为零。 - 把
check_conformance.py接入门禁:否决——它会在已接受的偏离 (1)–(4) 上永久失败,让绿色门禁因非问题而变红。
影响
- 包现在位于
src/ragspine/;editable 的.pth指向src/;所有文档和covers:路径字符串均为src/ragspine/…。ADR 0001–0010 不可变,保留其原有(现已成为历史的)路径引用。 check_docstring_refs.py的语义翻转:src/ragspine/…是规范路径;裸ragspine/…或旧的扁平src/<mod>.py视为死引用。- 新的全新兄弟项目仍按字面遵循该标准(从第一天起就有
core/、ports/+adapters/);本仓库只回补src/迁移这一项。 - 未来可能有一个低风险 PR 在
service/边界处加入 pydantic;除此之外 (1)–(4) 保持不变。
ADR 0010: 意图解析——确定性安全闸门 + 可插拔的 IntentParser
将始终开启、绝不可插拔的确定性安全闸门与可插拔的 IntentParser Protocol 解耦——在要紧处保持确定性,在安全处保持灵活。
ADR 0012: 采纳 corespine 的 LLM 缝——用 chat / ChatCompletion 取代 create_message / ProviderResponse
将 LLMProvider Protocol 移入家族共享核心(corespine),并采纳 OpenAI chat-completions 形态;经由 corespine 的 RateLimitedProvider 增加可选的主动 TPM 限流。