Files
daily-paper/DESIGN_REVIEW.md
T
Rain-Bus 21f16e6756 feat: refactor summarizer and PDF extraction pipeline
- Split summarizer into summary_generator and summary_persister modules
- Refactor pdf_image_extractor to two-phase pipeline with PicoDet layout detection
- Add layout_detector service for PicoDet-S_layout_3cls integration
- Add exceptions module with ConflictError and NotFoundError
- Improve admin dashboard with better statistics and task management
- Add design review document with system optimization suggestions
- Add new tests for crawler, pdf_downloader, pipeline, and summary_utils
- Update dependencies and configuration
- Clean up dead code and improve error handling
2026-06-13 13:16:47 +08:00

469 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 项目设计审查与优化建议
本文档汇总对当前项目的系统设计、流程设计和代码结构的审查结论。重点不在局部代码风格,而在后续稳定运行、失败恢复、数据一致性、可维护性和扩展性。
## 总体评价
项目当前结构整体清晰:FastAPI 路由层较薄,主要业务放在 `app/services/`;抓取、总结、搜索、个人数据、管理后台等能力已有模块化拆分;SQLite + FTS5 对本地个人论文导览站是合理选择。
主要风险集中在以下几类:
- 长任务生命周期没有统一抽象。
- Pipeline 不是显式状态机,阶段结果难以恢复和追踪。
- 数据库、文件系统、FTS5、ChromaDB 之间存在多处手工同步。
- Web、CLI、Scheduler 三个入口的业务策略存在分叉风险。
- 失败恢复和可观测性不足。
- 部分同步阻塞任务混入 async Web 流程。
## 高优先级问题
### 1. 缺少统一的任务系统
当前有三套入口都能触发重任务:
- Web 管理后台:`/admin/trigger-pipeline``/admin/summarize`
- APScheduler:每日自动 pipeline
- CLI`app.cli crawl/summarize`
它们共享部分 service,但没有统一的“任务创建、排队、运行、重试、取消、状态查询、失败恢复”模型。
影响:
- Web 请求会长时间挂住。
- CLI、Web、Scheduler 的行为可能逐渐分叉。
- 任务卡死后只能看到粗粒度的 running lock。
- 前端难以展示阶段进度。
- 后续增加重跑、取消、恢复、并发限制会越来越复杂。
建议:
引入统一 Job 模型:
```text
入口层 -> 创建 Job -> Worker 执行 Job -> JobEvent 记录进度 -> 页面/API 查询状态
```
建议任务类型:
- `crawl_daily`
- `summarize_one`
- `summarize_batch`
- `pipeline_daily`
- `refresh_upvotes`
- `delete_range`
- `reindex_fts`
- `reindex_chroma`
- `extract_images`
建议表结构方向:
- `jobs`: 任务主记录,包含 `id/type/status/owner/created_at/started_at/completed_at/error`
- `job_events`: 阶段事件,包含 `job_id/stage/status/message/payload_json/created_at`
- `paper_processing_runs`: 单篇论文处理记录,适合跟踪总结、图片提取、索引状态
### 2. Pipeline 不是显式状态机
当前 pipeline 基本是线性函数:
```text
crawl -> summarize -> cleanup
```
阶段输出没有被建模成可查询、可重放、可补偿的状态。
影响:
- crawl 成功但 summarize 部分失败时,整体状态表达不够精确。
- cleanup 失败是否影响 pipeline 成败语义不清。
- “今天无数据回退昨天”是隐式逻辑,不是可配置策略。
- 图片提取、ChromaDB 索引失败被吞掉,用户无法知道哪些增强能力缺失。
建议:
将 pipeline 改为显式阶段状态:
```text
created
-> crawling
-> crawled
-> summarizing
-> summarized | summarized_partial
-> postprocessing
-> completed | failed | cancelled
```
每个阶段记录:
- 输入参数
- 输出统计
- 错误类型
- 错误摘要
- 开始/结束时间
- 是否可重试
这样可以支持只重跑失败阶段,例如只重跑 ChromaDB 索引、只重跑图片提取、只重跑失败论文总结。
### 3. 数据生命周期没有清晰分层
项目中至少有四类数据:
- 主数据:`papers``authors``tags``summaries`、用户笔记/收藏/阅读状态
- 派生索引:FTS5、ChromaDB
- 长期资产:`data/papers/{arxiv_id}` 下的 summary、raw output、images
- 临时资产:`data/tmp/{arxiv_id}` 下的 PDF、源码、中间文件
现在这些数据由不同流程手动维护。删除、总结、重建索引、清理临时目录都分散在不同模块中。
影响:
- DB 与文件系统可能不一致。
- DB 与 FTS5/ChromaDB 可能不一致。
- 删除流程中一部分资源删除成功、一部分失败时难以恢复。
- 很难判断某篇论文是否“完整可用”。
建议:
明确数据权威源:
- DB 是权威源。
- FTS5、ChromaDB、summary.json、raw_output.txt、images 都应视为可重建派生物。
- 删除主数据优先保证 DB 状态正确,派生物清理可以异步补偿。
- 提供派生数据重建任务。
建议增加命令或任务:
```text
rebuild-derived --fts
rebuild-derived --chroma
rebuild-derived --files
rebuild-derived --images
```
### 4. 数据库与文件系统存在双写一致性风险
总结持久化流程会写文件、写 DB、更新状态、提取图片、写 ChromaDB。任一步失败都可能留下不一致状态。
典型风险:
- 文件已写入,但 DB 未提交。
- DB 标记 done,但 summary.json 缺失。
- DB done,但图片未提取或 ChromaDB 未索引。
- 重新总结时旧图片或旧 figures 残留。
建议:
- 将结构化总结以 DB 为准,JSON 文件作为导出/缓存。
- 文件写入使用临时文件加原子 rename。
- DB 中记录派生物状态,例如:
- `summary_persisted_at`
- `fts_indexed_at`
- `chroma_indexed_at`
- `images_extracted_at`
- `derived_error`
- 提供一致性检查任务,扫描 DB 与文件/索引差异并修复。
### 5. 失败恢复设计偏弱
当前失败主要依赖:
- `summary_status.status`
- `retry_count`
- `task_locks`
- `crawl_logs`
但对以下情况支持不足:
- 进程崩溃后 `processing` 永久残留。
- `task_locks` 中 running lock 永久残留。
- 任务执行到一半,部分文件或索引已经写入。
- 某些后处理失败但主任务显示成功。
建议:
- `task_locks` 增加 `heartbeat_at``expires_at`
- 应用启动时扫描 stale running task,标记为 `failed/stale`
- `summary_status` 增加更细阶段字段,例如:
- `downloading_pdf`
- `generating`
- `validating`
- `persisting`
- `extracting_images`
- `indexing`
- 区分主流程失败和派生增强失败。
### 6. 长任务直接跑在 Web 请求里
管理端触发 pipeline、批量总结、刷新 upvotes、删除数据时,会在请求生命周期内直接执行重任务。
影响:
- HTTP 请求容易超时。
- 用户关闭页面后任务状态不清晰。
- Web 进程被重任务占用。
- 后续很难做取消、进度展示和失败恢复。
建议:
- Web 只创建 job 并返回 `task_id`
- 后台 worker 执行任务。
- 管理页面轮询 `/admin/jobs/{id}` 或使用 SSE 展示进度。
## 中优先级问题
### 7. FTS5 索引手工维护,容易漂移
`papers_fts` 是独立虚拟表。插入论文、更新总结、删除论文时手动同步。
影响:
- 已有论文的 title、abstract、authors、tags 更新时可能不同步。
- AI 标签新增后,如果没有统一重建,搜索结果可能缺字段。
- 删除或回滚失败后索引可能残留。
建议:
- 封装统一的 `reindex_paper(db, paper_id)`
- 所有修改论文、标签、总结的路径最后调用它。
- 提供全量 `reindex_fts` 任务。
- 或用 SQLite trigger 维护基础字段,但总结字段仍建议通过统一函数更新。
### 8. ChromaDB 索引缺少系统级一致性策略
ChromaDB 当前是可选增强能力,索引失败会被日志吞掉,不影响总结主流程。
影响:
- 语义搜索可用性不透明。
- 某些论文 DB done,但未进入 ChromaDB。
- 删除或重建时可能出现残留索引。
建议:
- 将 ChromaDB 明确视为派生索引。
- DB 中记录 `chroma_indexed_at``chroma_error`
- 提供 `reindex_chroma` 任务。
- 搜索页或管理后台展示语义索引健康状态。
### 9. 同步阻塞操作混入 async 主流程
部分 async 函数内部调用同步阻塞操作,例如 PDF 下载使用 `requests`,图片提取和 PDF 解析也都是同步重任务。
影响:
- FastAPI event loop 可能被阻塞。
- 多篇并发总结时吞吐不可控。
- Web 服务响应受后台任务影响。
建议:
- 最佳方案:重任务移到 worker 进程。
- 过渡方案:对同步阻塞段使用 `asyncio.to_thread()`
- PDF 下载统一使用 `httpx.AsyncClient` 或明确放入同步 worker。
### 10. 调度策略和业务流程耦合
Scheduler 固定每日 pipeline 加 30 分钟 upvote refreshpipeline 内部固定“今天无数据回退昨天”。
影响:
- 策略不易测试和调整。
- CLI、Web、Scheduler 可能各自实现不同 fallback。
- 后续支持日期范围、补抓、只总结新增论文会变复杂。
建议将策略配置化:
- `crawl_target_policy`: `today` / `today_then_yesterday` / `date_range`
- `summarize_scope`: `new_only` / `pending_and_failed` / `date_range`
- `cleanup_policy`: `after_success` / `always` / `manual`
- `upvote_refresh_window_days`
- `pipeline_on_partial_failure`: `continue` / `stop`
### 11. 内嵌 APScheduler 对部署形态敏感
当前调度器嵌入 Web 进程,且多 worker 时只是打印警告。
影响:
- 多 worker、多进程、reload 模式下可能重复或漏跑任务。
- Web 进程重启会影响调度可靠性。
- 调度状态和任务执行状态耦合。
建议:
- 本地单机可以保留内嵌调度器。
- 长期运行建议拆为独立 scheduler 进程。
- scheduler 只负责创建 job,不直接执行重任务。
- Web 管理后台只展示 scheduler/job 状态。
### 12. 运行时迁移能力不足
当前 `_migrate()` 只支持补列。
影响:
- 无法可靠处理索引、约束、字段改名、数据回填。
- 数据库结构演进不可追踪。
- 生产或长期本地数据升级风险高。
建议:
- 引入 Alembic。
- 将 FTS5、索引、部分约束和数据回填纳入迁移脚本。
- 启动时不要静默做复杂迁移,改为显式执行迁移命令。
## 低到中优先级问题
### 13. 配置校验不足
当前配置字段主要是裸类型,缺少合法值和组合校验。
风险示例:
- `SUMMARY_BACKEND` 填错。
- `SUMMARY_PDF_MODE` 填错。
- `SCHEDULE_MINUTE + 30` 超过 59。
- `APP_WORKERS > 1``SCHEDULER_ENABLED=true`
- `CHROMA_ENABLED=true` 但 embedding 配置缺失。
建议:
- 使用 Pydantic validator 校验枚举值和组合条件。
- 对危险组合启动时报错,而不是只 warning。
### 14. 私有函数跨模块调用说明模块边界不稳定
例如 `summarizer.py` 调用 `_generate_with_retry``_persist_summary` 等下划线函数。
影响:
- 模块公开边界不清晰。
- 后续重构容易误伤。
- 测试和替换后端不方便。
建议:
- 为 summary 生成、持久化、后处理定义明确公开 API。
- 下划线函数保留为模块内部实现细节。
- 引入更明确的 service 类或函数边界,例如:
- `SummaryGenerator.generate()`
- `SummaryRepository.save()`
- `DerivedAssetBuilder.extract_images()`
### 15. 外部依赖缺少统一 adapter
项目依赖多个外部系统:
- HuggingFace API
- arXiv PDF/source 下载
- `pi` CLI
- `claude` CLI
- Embedding API
- ChromaDB
- ONNX layout detector
建议:
- 为每类外部依赖定义 adapter。
- 统一 timeout、retry、error classification、metrics。
- 测试中替换 adapter,而不是 patch 深层函数。
### 16. 删除流程事务边界不理想
删除流程中同时删除 FTS5、ChromaDB、本地文件、临时文件、ORM 数据。
影响:
- 文件删除成功后 DB rollback,会造成数据仍在但文件丢失。
- DB 删除成功后 ChromaDB 删除失败,会造成残留索引。
- 单篇失败时 rollback 可能影响之前 flush 的状态,逻辑不直观。
建议:
- 删除主数据与删除派生物分两阶段。
- DB 删除成功后创建派生清理 job。
- 派生物清理失败可重试,不影响主数据一致性。
## 建议目标架构
如果项目定位是个人本地工具,可以采用轻量目标架构:
```text
FastAPI Web
- 页面和 API
- 管理后台
- 创建 job
- 查询 job 状态
Scheduler
- 按策略创建 job
- 不直接跑重任务
Worker
- crawl
- summarize
- PDF download
- image extraction
- FTS/Chroma reindex
- cleanup/delete
Storage/Repository
- DB 权威数据
- 文件资产管理
- 派生索引重建
- 一致性检查
```
如果暂时不想引入独立 worker,也可以先在单进程内实现 Job 表和后台任务执行器。这样至少能统一任务状态和恢复机制。
## 推荐实施顺序
### 第一阶段:先解决任务与状态
目标:降低长任务不可控风险。
- 新增 `jobs``job_events` 表。
- Web 管理动作改为创建 job 并返回 task_id。
- Scheduler 改为创建 job。
- CLI 复用同一套 job runner。
- 加 stale running job 恢复逻辑。
### 第二阶段:统一派生数据管理
目标:降低 DB、文件、FTS、Chroma 不一致风险。
- 明确 DB 为权威源。
- 封装 `reindex_paper()`
- 增加 `reindex_fts``reindex_chroma` 任务。
- 增加派生数据状态字段或健康检查。
- 删除流程改为 DB 主删除 + 派生清理 job。
### 第三阶段:改造 pipeline 为状态机
目标:让任务可恢复、可补偿、可观测。
- 拆分 pipeline stages。
- 每个 stage 记录输入/输出/错误/耗时。
- 支持从失败阶段重跑。
- 将 fallback 策略配置化。
### 第四阶段:提升运行可靠性
目标:长期运行更稳。
- 引入 Alembic。
- 配置加 validator。
- 同步阻塞操作移出 Web event loop。
- 外部依赖 adapter 化。
- 管理后台展示任务、派生索引、失败类型、耗时统计。
## 最小可行改造方案
如果只做最小但收益最大的改动,建议优先做这三项:
1. 增加统一 Job 表和 job runner。
2. DB 作为权威源,FTS/Chroma/文件全部按派生物处理。
3. 增加 stale task 恢复和派生数据重建命令。
这三项能显著降低后续复杂度,也不会强迫项目马上拆成多个服务。