# PRD — ephron.ren 全项目代码阅读后的完整优化清单 ## 说明 本清单基于对 `ephron.ren` **远端 `origin/main`** 的系统性源码阅读整理,不以当前本地工作树为准。 之所以强调这一点,是因为本地仓库当前状态与远端并不一致: - 本地 `HEAD` 与 `origin/main` 存在 ahead/behind 分叉 - 本地还存在不在 `origin/main` 中的文件(例如 `prompt/tests/test_settings_validation_regression.py`) 因此,本 PRD 的结论全部以 **`origin/main` 可验证代码** 为依据,避免把本地漂移状态误判为正式项目现状。 本次阅读重点覆盖: - 顶层入口与项目运行骨架 - `shared/` 全部核心 Python 模块 - 五个服务的 `src/` 关键入口、routes、services - 全局 tests 与各服务代表性 tests - 必要的 scripts 层职责分布 阅读方式不是逐个文档回顾,而是**以 Python 代码为主、以测试和脚本为辅助**,用于形成项目层面的结构优化结论。 --- ## 一、执行摘要 当前 `ephron.ren` 项目已经从早期多服务 MVP 演进到一个拥有: - 五个 FastAPI 服务(home/auth/blog/canvas/prompt) - 一层共享模块(shared) - 一批管理脚本(scripts) - 混合测试体系(全局 + 各服务) 的中型单仓项目。 它已经具备一定工程化基础: - 统一启动器 - shared 安全头 / cookie / CSRF / 审计 / ports / rate limit 等公共模块 - app factory / config base 共享化的第一轮落地 - service token / audit event / RBAC 等基础治理能力 但通读代码后可以明确判断: > 这个项目当前的主要矛盾已经不是“功能缺失”,而是**结构进入半统一状态后,边界没有完全收紧**。 也就是说: - shared 已经出现,但没有真正成为单一权威层 - 服务之间已经开始收敛,但仍保留大量复制变体 - 测试已经很多,但测试结构并不稳定 - 运维脚本已经存在,但项目契约在代码、文档、脚本三层之间有漂移 因此,本清单的核心不是列零散 bug,而是给出 **整站层面需要收口的工程性优化项**。 --- ## 二、代码规模与结构概览 基于 `origin/main` 的粗略统计: - `shared/`:17 个 Python 文件,约 1492 行 - `auth/src/`:17 个 Python 文件,约 4369 行 - `auth/tests/`:9 个 Python 文件,约 1851 行 - `auth/scripts/`:7 个 Python 文件,约 1384 行 - `blog/src/`:21 个 Python 文件,约 5734 行 - `blog/tests/`:8 个 Python 文件,约 913 行 - `blog/scripts/`:8 个 Python 文件,约 973 行 - `canvas/src/`:10 个 Python 文件,约 2208 行 - `canvas/tests/`:3 个 Python 文件,约 702 行 - `home/src/`:9 个 Python 文件,约 1007 行 - `home/tests/`:2 个 Python 文件,约 400 行 - `home/scripts/`:2 个 Python 文件,约 362 行 - `prompt/src/`:18 个 Python 文件,约 4293 行 这说明项目已经明显超出“小型 MVP”范畴,进入了: - 多服务 - 多存储模型 - 多种认证形态 - 多种测试风格 - 多种运维入口 并存的复杂系统阶段。 此时最重要的不是继续叠功能,而是**统一边界、清理漂移、建立工程约束**。 --- ## 三、总体问题地图 通读代码后,当前整站问题可以归纳为六大类: 1. **架构边界问题**:shared 化进行到一半,责任分层尚不稳定 2. **数据与模式归属问题**:数据库 schema ownership 漂移,跨服务边界模糊 3. **认证与权限问题**:跨服务 auth/helper 重复实现,guard 逻辑分散 4. **测试体系问题**:测试风格混杂,很多测试在替结构问题兜底 5. **运维与部署契约问题**:代码、README、scripts、依赖链之间存在漂移 6. **性能与运行时语义问题**:部分启动逻辑、缓存、健康检查和 IO 设计仍偏“功能优先”而非“系统优先” 下面按优先级展开。 --- ## 四、P0:必须优先处理的系统性问题 ## P0-1. Shared 层已经成为基础设施核心,但还没有真正成为单一权威层 ### 现象 项目已经有大量 shared 模块: - `shared/security_headers.py` - `shared/limiter.py` - `shared/health.py` - `shared/csrf.py` - `shared/cookie_utils.py` - `shared/request_ip.py` - `shared/service_tokens.py` - `shared/auth_users.py` - `shared/redirect_codec.py` - `shared/audit_log.py` - `shared/audit_events.py` - `shared/config_base.py` - `shared/error_handlers.py` - `shared/app_factory.py` 这已经说明 shared 不是辅助目录,而是事实上的基础设施层。 但代码同时又显示出明显的“半统一态”: - 各服务仍自己写 path hack - 各服务仍各自保留 auth helper 变体 - 各服务仍自行拼装部分 guard / token / DB 逻辑 - 测试大量依赖服务内 patch / import 注入来运行 ### 问题本质 项目已经决定走 shared 化,但还没有把这个决定贯彻为**稳定架构边界**。 现在的 shared 更像: - “可复用模块集合” 而不是: - “明确的公共协议层和基础设施层” ### 优化建议 1. 把 shared 的职责重新定义清楚,至少分三层: - **shared/core**:ports、config、health、sqlite、templating 等基础设施 - **shared/security**:cookie、csrf、redirect、request_ip、network_safety、service_tokens - **shared/governance**:audit_log、audit_events、auth_users 2. 明确服务层只允许: - 组合 shared - 实现服务自己的业务逻辑 3. 禁止服务层重复实现 shared 已经承担的协议逻辑 4. 为 shared 层补一个“模块边界文档 + import 约束清单” ### 价值 这一步不是美化目录,而是让项目从“共享了一些代码”变成“建立了共享层权威”。 --- ## P0-2. 数据库 schema ownership 已经漂移,Prompt 服务承担了不属于自己的数据库中枢职责 ### 现象 `prompt/src/services/db.py` 不仅初始化 prompt 自己的表: - `prompts` - `prompt_versions` - `collections` - `collection_items` - `settings` - `test_audit_log` 还在同一个模块里初始化: - `blog_collections` - `blog_collection_items` ### 这为什么严重 这不是简单的“顺手建表”,而是**服务边界被数据库初始化逻辑穿透了**。 直接后果: 1. Prompt 服务在事实层面成了跨域 schema bootstrap owner 2. Blog 的集合表不再由 Blog 自己的模块负责 3. 后续如果 blog collections 结构要演进,维护者必须去 prompt/db 里改 4. 新人很难从目录结构直觉判断“哪个服务拥有哪张表” 5. 迁移/部署/排障都会误导 ### 优化建议 1. 重新定义数据库模式归属: - `auth` 拥有用户/角色/权限/邀请/service account 相关表 - `prompt` 拥有 prompts / prompt_versions / prompt collections / settings / prompt audit - `blog` 拥有 blog_collections 等 blog 相关表 - 全局共享表必须明确标注为 shared schema 2. 不允许某个服务的 `db.py` 去创建另一个服务的业务表 3. 建立统一的 migration / schema registration 机制: - 可以是根级 `scripts/migrate_database.py` 调度各服务注册器 - 或 shared schema registry 4. 把“建表逻辑”和“运行时 connection helper”拆开 - `db.py` 负责连接和轻量 helper - schema 初始化由独立 migration 层负责 ### 价值 这是整站层面最需要收口的一条,不然项目继续增长后,数据库会先于代码架构失控。 --- ## P0-3. 认证/权限协议在跨服务间被重复实现,风险在于未来持续漂移 ### 现象 多个服务都存在自己的 auth service: - `auth/src/services/auth.py` - `blog/src/services/auth.py` - `canvas/src/services/auth.py` - `home/src/services/auth.py` - `prompt/src/services/auth.py` 虽然它们都围绕同一 cookie/token 协议,但实现并不完全统一。 常见重复内容包括: - token verify - get_auth_user - require_admin_role - require_permission - service actor 识别 - admin guard / redirect 逻辑 ### 问题 这不是“代码重复”这么简单,而是**认证协议重复实现**。 一旦协议升级,比如: - token 字段变化 - current DB role 解释变化 - service token 行为变化 - handoff_to_human 规则变化 多个服务必须同步修改,否则就会出现行为漂移。 ### 优化建议 1. 抽出一层 shared auth protocol helper: - token verify - current user resolve - current role/permission resolve 2. 抽 shared admin guard skeleton: - 登录跳转 - redirect 编解码 - service token 能否访问 admin 的统一策略 3. 服务层只保留: - 各自业务资源上的权限要求 - 各自页面跳转的 UI 差异 4. 统一 `service actor` 的语义,不要每个 service_api 都自己再包一层近似实现 ### 价值 认证和权限如果不先统一协议层,将来每个服务都能“看起来能用,但行为不完全一致”。这是最难排查的线上问题类型之一。 --- ## P0-4. 当前测试体系已经在为结构债务兜底,必须先治理测试边界 ### 现象 测试中出现多种明显信号: 1. 大量 `sys.path.insert(...)` 2. 大量 `sys.modules['src.config'] = mock_config` 3. 既有真实入口测试,也有内存 SQLite 模拟测试 4. 一些 tests 更像脚本式 smoke file,而不是稳定单测 5. 有些测试在 patch import path 后才跑得起来 ### 问题本质 这说明测试体系并不只是“多样”,而是: > 结构不稳定,测试在替结构问题兜底。 当测试必须大量: - 改路径 - 改模块缓存 - patch 配置模块 - patch 连接层 才能运行时,往往意味着: - 模块边界不清晰 - 初始化副作用太强 - 配置/数据库/依赖注入方式不够健康 ### 优化建议 1. 重新分层测试: - **unit**:纯函数/纯 service helper - **contract**:shared 协议、entrypoint 装配、cookie/redirect/security headers 合同 - **integration**:服务路由 + 临时 DB / 文件系统 - **script smoke**:脚本独立,不混在核心单测里 2. 把脚本式测试文件从核心 pytest 回归层剥离 3. 减少 `sys.modules['src.config']` 风格 patch,改为显式配置注入或 fixture 4. 建立统一测试 helper: - temp DB - temp content dir - service runtime bootstrap 5. 明确哪些 tests 允许 mock,哪些必须走真实入口 ### 价值 如果不先治理测试边界,后续任何重构都会因为“测试全在 patch 系统内部结构”而变得高风险。 --- ## 五、P1:高优先级结构优化 ## P1-1. `sys.path.insert(...)` 仍然是整站普遍现象,说明模块边界和运行契约都不稳定 ### 现象 根入口、五个服务 main/config、routes、services、tests、scripts 中广泛存在 `sys.path.insert(...)`。 这意味着项目当前默认运行模型仍然是: - 依赖 cwd - 依赖相对路径 - 依赖手动往 `sys.path` 注入仓库根或服务根 ### 问题 这会导致: - 测试和运行环境对 cwd 高度敏感 - 一旦目录结构调整,影响面极大 - 局部脚本行为难预测 - 本地与 CI、服务与脚本之间的 import 语义不稳定 ### 优化建议 1. 先收敛核心入口(根 `main.py` + 5 个服务 `main.py/config.py`) 2. 再收敛服务内 routes/services 的 path 注入 3. 最后收敛 tests/scripts 4. 推荐建立统一 bootstrap helper 或显式包运行约定 5. 长期建议迁移到更稳定的 package/import 结构 ### 价值 这是整站工程可维护性的基础设施问题,不解决的话 shared 化永远会停在“看起来统一”。 --- ## P1-2. Root `main.py` 已承担启动器职责,但与服务/文档/环境管理之间的契约仍旧脆弱 ### 现象 根 `main.py`: - 负责 5 个服务统一拉起 - 自己处理 Windows / conda / venv / Python 路径探测 - 自己检查 `.env` - 通过 subprocess + uvicorn 启动每个服务 这是一个“越来越重的启动器”。 ### 问题 1. 环境选择逻辑混在业务启动器里 2. 文档、服务真实依赖、启动脚本之间的契约分散 3. 统一启动器既是开发工具,又试图兼容生产模式 4. 一旦某个服务需要特殊启动参数,根 `main.py` 会继续膨胀 ### 优化建议 1. 明确 root `main.py` 的角色: - 仅作为开发/本地一键启动器 - 不承担生产部署入口的抽象责任 2. 把环境探测逻辑拆出去 - 例如单独的 runtime/bootstrap helper 3. 用配置声明服务列表,而不是在代码里硬编码越来越多的服务元数据 4. README 明确区分: - 开发启动 - 测试启动 - 生产部署 ### 价值 避免根 `main.py` 继续演变成“全能脚本”。 --- ## P1-3. `shared/health.py` 当前统一检查 sqlite,但没有表达“每个服务的真实健康语义” ### 现象 `shared/health.py` 当前统一返回: - service - uptime - database status 而所有服务都走这一套。 ### 问题 这在格式统一上是好的,但语义上过度简化: - `canvas` 主要是文件存储,但 health 只看 DB - `blog` 的 search index / content dir 状态不在 health 中 - `prompt` 对外部 LLM provider 的依赖也不在 health 中 - `home` 的 content state 也不在 health 中 也就是说,health 现在更像: - “进程 + sqlite 基本可用” 而不是: - “服务核心依赖健康” ### 优化建议 1. 保留 shared health response 骨架 2. 允许每个服务注册自己的 health probes 3. 将 health 结构设计为: - shared baseline - service-specific checks 4. 区分: - `ok` - `degraded` - `error` ### 价值 这能显著提升运维判断质量,避免“health 绿了但服务关键功能其实坏了”。 --- ## P1-4. Service API 在 blog/canvas/prompt 三个服务中存在明显的重复模式,应该抽共享骨架 ### 现象 三个 service API 都有相似结构: - bearer token 识别 - actor 权限检查 - own draft / manageable state 判断 - 审计记录 - create / edit / delete / publish / unpublish 其中大量逻辑只是资源字段不同,本质模式一致。 ### 问题 这类“高度相似但不完全相同”的复制是最危险的: - 现在看还能维护 - 再迭代 2~3 次就会出现细节漂移 - 测试也会不断复制 ### 优化建议 1. 抽 service API shared skeleton: - require_service_actor - permission check helpers - audit emit helper - draft/manageable state helper interface 2. 资源特有逻辑通过回调/策略注入 3. 保持每个服务自己的 resource serializer ### 价值 这不是为了“抽象好看”,而是为了避免 blog/canvas/prompt 在未来演化出三套近似但不兼容的 draft workflow。 --- ## P1-5. README / docs / 代码现状之间存在持续漂移,需要把“文档同步”从手工习惯变成约束 ### 现象 顶层 README 仍然保留部分旧叙述,例如: - `home/.env` 使用 `ENVIRONMENT`,其他服务使用 `ENV` 而代码实际上已经进入: - shared config base - `home` 兼容 `ENVIRONMENT`,但系统已有 `ENV` 主路径 类似漂移在多服务项目中会越来越常见。 ### 优化建议 1. 把运行契约文档集中到一份单一权威说明 2. README 只保留高层入口,不重复易漂移细节 3. 与代码强耦合的配置/依赖契约,尽量让测试检查而不是只写文档 4. 把“更新 README”纳入涉及运行契约改动的 PR checklist ### 价值 减少“文档看起来对,但代码其实已经变了”的维护成本。 --- ## 六、P2:中优先级改进项 ## P2-1. 启动时副作用偏多,部分初始化逻辑应继续外移 ### 现象 示例: - blog 启动时会检查并构建 search index - prompt 启动时初始化 DB - auth 启动时初始化 DB ### 问题 这种模式在开发期方便,但会导致: - 启动时间与副作用不可预测 - 失败原因混在应用启动里 - 多进程/并发启动场景下更脆弱 ### 优化建议 1. 区分“必须阻塞启动的初始化”和“可外移的预热任务” 2. search index 这类工作更适合显式脚本 / 后台任务 / 管理命令 3. DB migration 与 table bootstrap 应从服务启动逻辑继续拆离 --- ## P2-2. 审计体系方向正确,但日志职责还可进一步统一 ### 现象 项目已经有: - `shared/audit_log.py` - `shared/audit_events.py` - service API 中的记录逻辑 - prompt service test audit log ### 问题 目前审计能力分成: - 传统文本审计日志 - 结构化 event 记录 - 各服务局部 audit 表 这三类并存,但归属和用途界限还不够清楚。 ### 优化建议 1. 明确区分: - 运维审计日志 - 结构化安全事件 - 业务行为记录 2. 不同用途采用不同存储模型,但命名/入口统一 3. 避免某服务自己再长出第四套审计语义 --- ## P2-3. Script 层数量已经不小,但还缺少明确分类 ### 现象 根 scripts + 各服务 scripts 中,已经有相当多的管理脚本。 ### 问题 这些脚本有的负责: - DB init - 迁移 - backup - health - audit rotate - cache clear - 校验工具 但当前更多是“文件集合”,不是“清晰操作面”。 ### 优化建议 1. 给脚本分层: - init - migrate - maintenance - diagnose - admin 2. 为每类脚本建立统一 CLI 约定 3. 减少“功能可用,但脚本发现与使用成本高”的问题 --- ## P2-4. 多服务配置约定仍有历史兼容包袱,建议逐步统一为显式配置模型 ### 现象 - `home` 仍兼容 `ENVIRONMENT` - 其他服务走 `ENV` - 各服务 config summary 字段不完全一致 - 部分服务仍自己决定目录存在性和默认值策略 ### 建议 1. 保留兼容,但把兼容行为写进测试 2. 明确哪些配置是全局契约,哪些是服务局部契约 3. 配置模块尽量只做“读取 + 校验”,不做过多隐式推导 --- ## 七、按维度汇总的完整优化清单 下面给出可以直接交给开发的总清单版本。 ## A. 架构与边界 1. 重构 shared 目录为更清晰的核心/安全/治理分层 2. 定义 shared 为公共权威层,而不是可选工具箱 3. 清理服务内重复的 auth/token/helper 骨架 4. 统一 service API shared skeleton 5. 建立更清晰的 schema ownership 归属关系 6. 把 DB migration/bootstrap 从单个服务 `db.py` 中抽离 7. 收敛根 `main.py` 的职责,只保留开发启动器定位 ## B. 数据与存储 8. 让各服务只初始化自己拥有的业务表 9. 建立统一 migration 入口和注册机制 10. 区分 shared 表、auth 表、blog 表、prompt 表等 ownership 11. 明确文件存储服务(canvas/blog content)与 sqlite 的边界 ## C. 认证与权限 12. 抽 shared token verify / current user resolve helper 13. 抽 shared admin guard skeleton 14. 统一 service token 行为解释 15. 统一 redirect / login jump / unauthorized response 约定 16. 让服务只实现资源级权限,不重复协议级权限逻辑 ## D. 测试 17. 将测试划分为 unit / contract / integration / script smoke 18. 把脚本式测试从核心 pytest 回归集合中分离 19. 减少 `sys.modules['src.config']` 类测试补丁 20. 建立统一 fixture / runtime helper 21. 新增服务入口装配合同测试 22. 新增配置兼容合同测试 23. 新增 shared/service API 行为合同测试 ## E. 运维与部署 24. 统一依赖声明,确保 shared factory 新增依赖覆盖开发/测试/部署链路 25. 统一 README 与真实代码契约 26. 明确开发启动、测试启动、生产启动三种模式 27. 给 scripts 建立分类和统一调用方式 28. 让 health 支持服务特有探针 ## F. 可维护性 29. 分阶段清理 `sys.path.insert(...)` 30. 统一 config summary 骨架,但不强行统一业务字段含义 31. 降低 import-time 副作用 32. 将启动阶段的重任务外移 33. 统一 shared 与服务层的异常处理约定 ## G. 性能与运行时语义 34. review blog 的 search index 启动构建策略,考虑外移 35. review health response 语义,避免“数据库好=服务好”的误判 36. review缓存/内容读取路径,减少重复 IO 和重复解析热点 37. 评估 comments/likes/views/search 的存储与索引策略是否继续扩张会失控 --- ## 八、建议实施顺序 整站层面不建议一次性全做,建议按风险和价值排序: ### 第一组:先收边界 1. schema ownership / migration 归属梳理 2. shared auth/service API/admin guard 协议层抽象 3. 收敛核心入口 path hack ### 第二组:再稳测试 4. 入口合同测试 5. 测试分类与 fixture 统一 6. 减少 patch 配置/patch module 风格测试 ### 第三组:再治运行契约 7. 依赖链统一 8. README / scripts / 启动器 对齐 9. health probe 模型升级 ### 第四组:最后处理性能与脚本治理 10. 启动重任务外移 11. 脚本层分类 12. 次级 IO/缓存/索引优化 --- ## 九、完成定义 当以下条件同时成立时,才能说 `ephron.ren` 从“多服务功能可用”进入“整站工程结构稳定”: 1. shared 成为清晰的公共权威层 2. 数据库 schema ownership 明确,单服务不再越界建表 3. 认证/权限协议不再跨服务重复实现 4. 测试体系不再依赖大量结构性 patch 才能跑起来 5. 启动、依赖、文档、脚本的运行契约一致 6. health / migration / scripts / shared app factory 都有明确职责边界 --- ## 十、最终结论 如果只看功能,这个项目已经能跑很多东西;但如果看长期维护,它最需要的不是再堆功能,而是: > 把已经出现的“shared、RBAC、service API、统一启动、统一测试”这些好方向,真正收敛成稳定边界。 现在最大的风险不是“缺模块”,而是: - 模块已经有了,但边界还不够硬 - shared 已经有了,但权威性还不够强 - 测试已经很多了,但结构还不够稳 这也是为什么本清单的重点会落在: - 架构边界 - schema ownership - auth 协议统一 - 测试分层 - 运维契约对齐 而不是零散功能修补。 从整站视角看,只要先把这几条收住,后续新功能开发成本会明显下降,回归风险也会显著降低。