docs: add P5 PR report

This commit is contained in:
potaser
2026-10-15 19:35:51 +08:00
parent 7ced30fa96
commit f5a951f47a
@@ -0,0 +1,131 @@
# P5 科研汇报助手 — PR 报告(2026-10-14,v4 更新 2026-10-15)
## 概要
新增「科研汇报助手」(P5):论文 PDF/粘贴文本 → vstore 解析 → map-reduce LLM 大纲
(5 场景 × 4 时长 × **14 版式**,逐条证据可溯)→ 逐页编辑 → python-pptx 本地生成
16:9 PPTX 初稿(论文真图卡片化嵌入、分隔页主副标题、讲者备注携带证据引用)。
全程只新增 `review_service/` 内代码 + BFF 既有 `REVIEW_ENABLED` 条件反代,
禁区(RAG 核心、既有服务、前端 router/db)零改动。
**v4 迭代(2026-10-15)**:① 副标题风格数改为动态读取 `GET /ppt/styles`(缺数据回落
「多风格·图文混排」)② 章节分隔页加副标题(prompt 强制 `subtitle`、渲染 40pt 主标题 +
20pt muted 副标题 + 底部 accent 装饰线、编辑器可改)③ 论文白底原图与页面底色融合
(surface_2 圆角卡片 + 图注 + 宽/高上限 + 单图页居中留白)。
## 修改内容
### 后端(`review_service/`)
| 类别 | 文件 | 说明 |
| --- | --- | --- |
| 改 | `app/config.py` | +`ppt_jobs_dir` / `ppt_styles_dir` 及 abs 属性(+14 行) |
| 改 | `app/main.py` | 导入 ppt 路由、lifespan `ppt_ensure_table()`、挂载(+8 行) |
| 改 | `tooling/requirements.lock` | **+3 行**:`pillow==12.3.0` / `python-pptx==1.0.2` / `xlsxwriter==3.2.9`(三者均已实装于统一 venv) |
| 改 | `.gitignore` | +9 行:`ppt_jobs/` 运行数据、`prewarm.log`、`data/compile_jobs/`、`scripts/`、`data/review.db` |
| 新 | `app/routers/ppt.py`(317 行) | **9 端点**:`GET /styles`、`POST /jobs`、`POST /jobs/pdf`、`GET/PATCH /jobs/{id}`、`POST /jobs/{id}/build`、`GET /jobs/{id}/pptx`、`GET /jobs/{id}/preview/{page}`、`DELETE /jobs/{id}` |
| 新 | `app/services/ppt_outline.py`(917 行) | map-reduce 大纲核心(索引/配额/重验/裁剪/膨胀回修/分隔页 subtitle 规则) |
| 新 | `app/services/ppt_build.py`(1176 行) | python-pptx 生成器(14 版式 token、字体栈、真图卡片容器、分隔页主副标题、讲者备注) |
| 新 | `app/services/ppt_figure_index.py`(264 行) | 论文真图索引(para/图号 → 图片落盘与状态) |
| 新 | `app/services/ppt_preview.py`(230 行) | 逐页 PNG 预览(供面板缩略图,失败不阻断) |
| 新 | `app/services/ppt_store.py`(155 行) | `ppt_jobs` SQLite 持久化(约定 DDL 逐字实现) |
| 新 | `data/ppt_styles/*.json`(14 个) | 版式 token 源码资产(随 git 走,`GET /styles` 的唯一数据源) |
| 新 | `tests/test_ppt_*.py`(6 个 / 102 例) | outline / build / figure_index / preview / router / store |
### 前端(`ui/` + `tests/`)
| 类别 | 文件 | 说明 |
| --- | --- | --- |
| 改 | `ui/src/api/review.js`(+50 行) | PPT API 段 + `pptStyles()` |
| 改 | `ui/src/stores/edu.js`(+147 行) | `ppt` 状态块 + actions + `pptLoadStyles` + `SECTIONS` 增列 |
| 改 | `ui/src/components/edu/EduSideNav.vue` / `EduWorkspace.vue` | 菜单项 + PptPanel 注册(各 +2 行) |
| 新 | `ui/src/components/edu/panels/PptPanel.vue`(578 行) | 生成表单 + 编辑器(动态风格数、分隔页副标题输入、逐页预览) |
| 新 | `ui/src/components/edu/panels/ppt-styles.css`(40 行) | 面板样式 |
| 新 | `ui/src/assets/ppt-style-cards/*.png`(14 个,约 21KB/个) | 版式卡面截图 |
| 新 | `tests/frontend/unit/edu/ppt.panel.test.js`(12 例)/ `ppt.store.test.js`(7 例) | P5 前端单测 |
| 改 | `tests/frontend/unit/b2/components.test.js`、`b3/benchpanel.test.js`、`bioflow/benchpanel.bioflow.test.js`、`tests/e2e/bench.spec.js` | **预存失败清零**:① tab 文案「提取任务」→「上传文献」(对齐 `51532a3`)② 模式 pill 断言改为「已随 `43a7c6c` 移除」 |
**依赖偏差声明**:`python-pptx==1.0.2`(PyPI 最高 1.0.2;需求中 1.1.2 实为
python-docx 版本,系记混,已在实现时更正)。`xlsxwriter==3.2.9` 当前无代码直接引用,
属统一 venv `pip freeze` 的真实记录,一并同步锁文件——**合并前仍需人工确认这 3 行锁变更**。
**DDL**(与约定逐字一致):
```sql
ppt_jobs(id, status /*outline_ready|building|done|error*/, source_kind, filename,
raw_text, scenario, minutes, style_id, pages_json, pptx_path,
warnings_json, created_at, updated_at)
```
**API 前缀** `/api/review/ppt`(9 端点;业务错误一律 200 信封
`{ok:false, error:{code,message,degraded}}`;二进制下载缺失时回同款信封 JSON)。
### 关键渲染约定(v4)
- **分隔页**:`section_bg` 底(缺则 `surface_2`→`cover_bg`→`bg`),40pt 居中粗体主标题 +
20pt muted 副标题(`bullets[0]` → `subtitle` → 该章第一页 title 三级兜底,浅底自动换深字)+
底部 30% 宽 accent 短线。
- **真图卡片**:`surface_2` 圆角矩形(8px)容器 + 5mm 内边距,图居中、图注
`Fig N(论文 S?P?)` 10–12pt muted、与图区留 3mm;混排页图区 ≤55% 页宽且 ≤60% 页高,
单独图页居中、上下留白 ≥5%。
## 测试结果(2026-10-15)
```
pytest(review_service 全库): 215 passed in 13.7s (P5 新增 102 → 113 + 102)
vitest edu(tests/frontend/unit/edu): 43 passed (P5 新增 19:panel 12 + store 7)
vitest 全量(tests/frontend/unit): 32 files / 421 passed / 0 failed
—— 修掉 b2/b3/bioflow 共 5 个预存失败(文案改名 + 模式按钮移除)
npm run build: ✓ built(chunk > 500kB 警告为预存)
版式目检: 3 风格 × 10 页预览 PNG 渲染核对(分隔页主副标题/对比度回退、
混排图卡 ≤55%、单独图页居中留白)均符合预期
```
## 真实 e2e(v1 阶段记录:官方 MDPI 模板实例 PDF,16813 字符,真实 DeepSeek)
```
PDF 模式创建: 21.77s → 12 页(目标 [11,14])warnings=0 llm_calls=7 unverified_evidence=0
PATCH 编辑: 改写要点 + 新增无证据行 → evidence 同长重验、结构清洗 ✓
build: 0.13s → 12 slides / 64157 B / 16:9(13.33×7.50in)逐页讲者备注 ✓
python-pptx 回读: 12 页,S3 首条 = 人工改写文本 ✓
编辑后下载: ok=false NOT_FOUND(盘上旧文件存在但拒发,pptx_path 清空生效)✓
text 模式: 15.0s → 9 页(目标 [8,10])group_meeting / 10min / botanic-forest ✓
短文本: 200 信封 VALIDATION「正文过短(<300 字),无法生成汇报大纲」✓
清理: 双 DELETE → ppt_jobs 0 行、产物目录空 ✓
```
## 部署验证步骤
1. 依赖:同步 `tooling/requirements.lock`(+3 行)并安装到统一 venv。
2. 重启服务:`cd review_service && bash restart.sh`(8100)。
3. 健康检查:`curl http://127.0.0.1:8100/health` → `status=ok`、`vstore connected=true`。
4. 路由:`curl .../openapi.json` 确认 9 个 `/api/review/ppt/*` 路径在列(含 `/styles`)。
5. BFF:`sdk/.env` 确认 `REVIEW_ENABLED=1`,走 8090 反代冒烟
`POST /api/review/ppt/jobs`(短文本应得 200 VALIDATION 信封,不打 LLM)。
6. 真实链路:上传一篇论文 PDF → 出大纲 → 编辑分隔页副标题并保存 → build → 下载 PPTX →
用 python-pptx 核对页数、16:9 尺寸与分隔页副标题文本。
7. 回归:`cd review_service && <venv>/bin/python -m pytest -q`;
`cd ui && npx vitest run` + `npm run build`。
## 部署决策
**建议:只合并,下次批量部署。** 本次改动对存量服务零影响(纯新增模块 +
开关化反代),无数据迁移、无 schema 破坏性变更;单点重启 8100 即可生效,
不值得为它单独发版,随下一次批量部署一并上即可。
## 风险与回滚
| 风险 | 影响 | 缓解 / 回滚 |
| --- | --- | --- |
| `python-pptx==1.0.2` 版本与需求(1.1.2)不一致 | 依赖锁未按原文落库 | 已声明偏差,合并前人工确认 3 行锁变更;如需回退直接还原 lock |
| `xlsxwriter==3.2.9` 无代码引用 | 锁文件带入未用依赖 | venv 实况记录,可保留;若要求最小化可单独剔除该行 |
| LLM 大纲偶发超时/无效 JSON | 创建接口报 `LLM_ERROR`/`LLM_INVALID` | 已分片 reduce + 单次膨胀回修 + 警告降级;前端可重试,无脏数据落库 |
| quote 反查失败残留 evidence(2 条) | 个别要点 `unverified` | **Phase 2**:最长公共子串(≥20 字符)匹配补 `para_id`,失败保持 unverified(边际价值低,v4 不做) |
| 编辑后旧 PPTX 残留盘上 | 无(`pptx_path` 清空后拒发,仅占磁盘) | 删除 job 时 `rmtree` 一并清理 |
| 回滚 | — | 反向还原 12 个已跟踪文件改动 + 删除 44 个新增文件(后端 26 + 前端 18)即可;`ppt_jobs` 表与产物目录可留可删,不被其他模块引用 |
## 与 `feature/p3b-compile-ui#4f9c077` 的关系
v1 版报告曾以同一路径提交于该分支(`docs: add P5 PR report`),内容停留在 3 版式 /
7 端点 / 208 pytest 阶段。本文件为 v4 权威版本;若两分支先后合入,同路径会产生一次
文本冲突,取本文件(v4)内容即可。