ADR-014 — 实现语言与核心运行时技术栈(Python 追认 + 多语言演进触发器与兜底)
草案(M0 L8 整改期补治理洞)。 起因:owner 发现整个架构与工程化落地默认采纳 Python 却从未留下选型评估痕迹——这是"文档理据洞",不是"实质未决"(选择早已做出且 M0 已跑在其上)。本 ADR 不是重新选型 bake-off,而是:① 追认已成事实的核心语言;② 把"何时必须重新评估、如何让未来多语言化保持线性成本"钉死,回应 owner 关切——改造不能拖到成本指数化才做。
§0 决策简述
- 核心运行时语言 = Python 3.13(追认):FinBayes 认知/编排 spine 用 Python(+ uv / ruff / pytest / Pydantic v2 / asyncio / SQLite)。
- 只锁 spine,不锁全栈:客户端层、性能敏感模块、规模化数据层显式不锁定 Python(部署形态另归 arch-rewrite/ADR-006 待写)。
- 多语言以"边界抽取"演进,非"整体重写":未来引入 TS/Rust/Go 时,从干净边界抽取单个组件,Python 始终是认知 spine——把改造成本结构性地压成线性而非指数。
- 现在就钉死重评估触发器 + 里程碑兜底(§3):保证"按需触发"可执行,且有硬兜底防无限推迟到"太晚"。
§1 上下文与取证
- 语言一直是隐式默认:architecture.md 通篇假定 Python(asyncio §12、src layout §27、Pydantic 数据对象、click/typer CLI、SQLite binding、keyring),M0 代码已全在 Python、60 测试绿。
- 既有 ADR 都不覆盖语言选型:ADR-003 工程实施栈与协作讲的是 OpenSpec+Archon+Claude/Codex 工具/协作栈;arch-rewrite/ADR-005(待写)是 Python 内部并发原语;arch-rewrite/ADR-006(待写)是部署形态。无任何 ADR 评估"为何 Python 而非 TS/Rust/Go"。
- owner 关切(2026-05-30):后续需在合适阶段做更全面的语言/栈评估(Python→TS?性能敏感模块 Rust/Go?),可按需触发,但不能太晚,避免改造成本指数增大。本 ADR 即为此关切提供机制。
§2 决策
D1 · 核心运行时语言 = Python 3.13(追认,低后悔)
理据:
- 领域强绑定:FinBayes 是 LLM 认知层,Python 是 LLM 生态母语——provider SDK、Pydantic 结构化输出、eval/ML 工具链全 Python-first,换语言是逆生态。
- 已落地无沉没成本理由:M0 已跑通,现在换=扔可用代码换零 M0 价值。
- 无性能热点:当前无逼迫上 Rust/Go 的算力瓶颈。
D2 · 锁定范围(只锁 spine)
| 部分 | 是否锁 Python | 归属 |
|---|---|---|
| 核心认知 / 任务编排 / 状态管理 runtime | 锁(本 ADR) | 本 ADR |
| 客户端 / 前端层 | 不锁(未来可 TS) | 触发后另评(§3) |
| 性能敏感模块 | 不锁(未来可 Rust/Go) | 触发后另评(§3) |
| 规模化数据层 | 不锁(SQLite → ? 按规模) | 部署形态 arch-rewrite/ADR-006 |
D3 · 成本遏制设计规则(现在就做的便宜保险)
要让未来抽取保持线性,现在零成本/低成本做两件事:
- 容器边界 contract 保持可序列化:子系统间接口的数据对象(Pydantic)设计上对 JSON/protobuf 友好,使任一边界未来可升级为跨进程/跨语言序列化边界,无需重构内部。
- 热点逻辑隔离在接口后:任何潜在性能敏感逻辑收口到清晰接口,便于将来整块抽走换语言,不与编排逻辑缠绕。
这是回应"成本指数化"的结构性答案:永远不"迁移整个系统",只在干净边界"抽取单个组件",成本随组件数线性增长。
D4 · 多语言演进方式 = 边界抽取,不整体重写
- Python 永远是认知 / 编排 spine。
- TS 若进入,进客户端层(加法,Python 后端 + TS 前端是常规组合,非迁移)。
- Rust/Go 若进入,进隔离的性能模块(PyO3 扩展 或 sidecar 服务),只换该模块。
§3 重评估触发器 + 兜底("不能太晚"的硬保证)
事件型触发器(leading indicators,早响应而非滞后)
任一触发即启动对应范围的深度评估(非全栈,仅触发范围):
| # | 触发条件(可观测、前瞻) | 评估范围 |
|---|---|---|
| T1 | 产品从 CLI 走向真实 GUI/Web 客户端 | 客户端层选 TS / 其他 |
| T2 | 某模块实测 perf 不达 SLO,且 profiler 把瓶颈归因到语言/运行时(非算法/IO) | 该模块抽取为 Rust/Go |
| T3 | 多用户并发下 GIL / 单线程事件循环成可测瓶颈 | 并发模型 / 服务化 / 局部换语言 |
| T4 | 关键能力只在他栈生态存在(Python 无对等) | 该能力模块选型 |
里程碑兜底检查点(防无限推迟)
在 M2 规模化 / 首次多用户部署之前,无论上述触发器是否已响,强制做一次"技术栈适配性 review"——这是"不能太晚"的硬兜底:把语言/栈问题在成本仍线性的最晚安全点强制摆上台面,不允许默默拖过。
设计意图:触发器负责"该早就早",兜底检查点负责"再晚不能晚过 M2 规模化前"。两者合起来杜绝"拖到代码全耦合才发现要改"。
§4 与相邻决议关系
- ADR-003 工程实施栈与协作:工具/协作栈(OpenSpec+Archon+Claude/Codex),与本 ADR(实现语言)正交。
- arch-rewrite/ADR-005(待写):Python 内部并发原语(asyncio),是本 ADR D1 之下的细化。
- arch-rewrite/ADR-006(待写):部署形态(本地/远程/混合),承接本 ADR D2 中"规模化数据层 / 服务化"。
- ADR-008 LLM Provider 接口抽象:provider 适配已是清晰边界,符合 D3 边界抽取原则。
§5 留待触发时深评(本 ADR 不现在定)
- 各模块的具体 perf SLO 阈值(T2/T3 判定线)。
- 跨语言边界序列化格式(JSON vs protobuf vs gRPC)。
- TS / Rust / Go 之间的具体选型(触发时按场景 bake-off)。
- 服务化拆分粒度(与 arch-rewrite/ADR-006 部署形态合并评估)。
§6 变更记录
- 2026-05-30(草案):补语言选型治理洞——追认 Python core spine + 成本遏制设计规则 + 多语言边界抽取演进 + 重评估触发器 T1–T4 + M2 前里程碑兜底检查点。回应 owner"按需触发但不能太晚"关切。
- 2026-05-31(accepted):owner 签字。Python 核心 spine 追认生效;TS/Go 按 T1–T4 触发 + M2 前强制复审引入,非现在 bake-off。ADR-005(asyncio 并发)作为本 ADR D1 之下的细化同步 accepted。