feat(skill): plot_pub 吸收 nature-figure 投稿级复合图设计纪律 + bump 0.12.15

调研 nature-figure skill(MIT)后只迁移可复用设计 IP,不整包移植
(避免与 plot_pub 重叠、R/生物内容不适配、多文件结构破坏单 SKILL.md 约定)。

- style.py: 补 svg.fonttype='none'(原只设 PDF Type 42,漏了 SVG 可编辑)
  + SEMANTIC_COLORS 语义色表 + clean_spines() + ablation_alphas()
- SKILL.md: 新增「投稿级多 panel 复合图」段(五点 figure contract /
  语义配色 / 信息架构 + spine 纪律 / 导出纪律),示例改建材领域
- SKILL_LIST.md / PROGRESS.md 同步;纯 Python 零新依赖,保留中文字体

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-06-15 16:54:32 +08:00
parent 5caa3db62e
commit ec27fcae3e
5 changed files with 136 additions and 5 deletions

View File

@ -2,7 +2,7 @@
> 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9` > 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9`
最后更新:2026-06-15(文件预览修滚动穿透 + 图片 Ctrl+滚轮缩放) 最后更新:2026-06-15(plot_pub 吸收 nature-figure 投稿级复合图设计纪律)
--- ---
@ -21,6 +21,11 @@
## 已完成关键能力 ## 已完成关键能力
### 2026-06-15 / plot_pub 吸收 nature-figure 投稿级复合图设计纪律
- 联网调研 `nature-figure` skill(MIT,github.com/Yuan1z0825/nature-skills):双层 manifest 路由 + Python/R 双后端 + 生物医学 gallery。判断不整包移植 —— 与已有 plot_pub 高度重叠、R/单细胞/在体内容跟建材院领域不沾边、多文件结构破坏 zcbot 单 SKILL.md 约定。
- 只迁移可复用的设计 IP,折进 `skills/plot_pub`:`style.py` 补 `svg.fonttype='none'`(可编辑矢量,原本只设了 PDF Type 42 漏了 SVG)+ `SEMANTIC_COLORS` 语义色表 + `clean_spines()` spine 纪律 + `ablation_alphas()` 同色变 alpha;`SKILL.md` 新增「投稿级多 panel 复合图」段(五点 figure contract / 语义配色 / 信息架构 / 导出纪律),示例全改建材领域。纯 Python、零新依赖、保留中文字体。bump 0.12.14 → 0.12.15。
### 2026-06-15 / 消息分页:尾部窗口 + 向上滚动加载更早(切 task 提速) ### 2026-06-15 / 消息分页:尾部窗口 + 向上滚动加载更早(切 task 提速)
- 痛点:切 task 卡顿 —— `/v1/tasks/{id}/messages` 无分页一次拉全量,前端 `renderMessages` 又对每条跑 markdown+highlight+media 全量渲 DOM,消息多时两段成本都线性涨。 - 痛点:切 task 卡顿 —— `/v1/tasks/{id}/messages` 无分页一次拉全量,前端 `renderMessages` 又对每条跑 markdown+highlight+media 全量渲 DOM,消息多时两段成本都线性涨。

View File

@ -1,7 +1,7 @@
# zcbot Skill 清单 # zcbot Skill 清单
服务对象:中国建筑材料科学研究总院 —— 无机非金属材料 R&D(水泥 / 混凝土 / 玻璃 / 陶瓷 / 耐火 / 新型建材) 服务对象:中国建筑材料科学研究总院 —— 无机非金属材料 R&D(水泥 / 混凝土 / 玻璃 / 陶瓷 / 耐火 / 新型建材)
最后更新:2026-06-11 最后更新:2026-06-15
Skill 总数:15 Skill 总数:15
zcbot 的"skill"是一份可加载的工作流脚本(`skills/<name>/SKILL.md` + 配套 templates / scripts / Python helper),模型在识别用户意图后挂载对应 skill,按其内置的阶段化流程产出可交付物。本文档面向**使用方 / 协作方**,按"做什么、什么时候用、什么时候别用、典型产物"组织。 zcbot 的"skill"是一份可加载的工作流脚本(`skills/<name>/SKILL.md` + 配套 templates / scripts / Python helper),模型在识别用户意图后挂载对应 skill,按其内置的阶段化流程产出可交付物。本文档面向**使用方 / 协作方**,按"做什么、什么时候用、什么时候别用、典型产物"组织。
@ -19,7 +19,7 @@ zcbot 的"skill"是一份可加载的工作流脚本(`skills/<name>/SKILL.md` +
| 科研写作 | [patent](#patent) | 写发明专利技术交底书(供代理师转写) | | 科研写作 | [patent](#patent) | 写发明专利技术交底书(供代理师转写) |
| 科研写作 | [review](#review) | 审稿 / 润色 / 校对(中英文,长文档分段深审) | | 科研写作 | [review](#review) | 审稿 / 润色 / 校对(中英文,长文档分段深审) |
| 演示出图 | [ppt](#ppt) | 生成 PowerPoint 演示稿(商务红主题,大纲对齐后一脚本整建) | | 演示出图 | [ppt](#ppt) | 生成 PowerPoint 演示稿(商务红主题,大纲对齐后一脚本整建) |
| 演示出图 | [plot_pub](#plot_pub) | 出版级 matplotlib 学术图(中文 + viridis + 矢量) | | 演示出图 | [plot_pub](#plot_pub) | 出版级 matplotlib 学术图(中文 + viridis + 矢量 + 投稿级复合图设计纪律) |
| 文献检索 | [research](#research) | 查 paper_server(OpenAlex 元数据 + Sci-Hub 下载) | | 文献检索 | [research](#research) | 查 paper_server(OpenAlex 元数据 + Sci-Hub 下载) |
| 文献检索 | [documents](#documents) | 查内部 7 学科材料知识库(21W+ 论文,跨语言检索;host-side tool 持 key) | | 文献检索 | [documents](#documents) | 查内部 7 学科材料知识库(21W+ 论文,跨语言检索;host-side tool 持 key) |
| 科研计算 | [pymatgen](#pymatgen) | 晶体结构 / XRD 模拟 / 相图 / Materials Project(host-side tool 持 key) | | 科研计算 | [pymatgen](#pymatgen) | 晶体结构 / XRD 模拟 / 相图 / Materials Project(host-side tool 持 key) |
@ -197,6 +197,8 @@ zcbot 的"skill"是一份可加载的工作流脚本(`skills/<name>/SKILL.md` +
**默认配色**:viridis(jet 是现代审稿雷区)。 **默认配色**:viridis(jet 是现代审稿雷区)。
**投稿级复合图**:要投高影响期刊的 figure 1 时,内置一套设计纪律(移植自 nature-figure skill,MIT)—— 五点 figure contract(核心结论 / 证据链 / 图原型 / 后端 / 期刊契约)、语义配色(蓝=本方法 / 绿=提升 / 红=baseline / 灰=参照,消融用同色变 alpha)、spine 纪律(`clean_spines` 只留左+下)、可编辑 SVG(`svg.fonttype='none'`)。
--- ---
## 文献检索 ## 文献检索

View File

@ -1,3 +1,3 @@
# zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。 # zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。
# 改版本只动这一行。 # 改版本只动这一行。
__version__ = "0.12.14" __version__ = "0.12.15"

View File

@ -1,6 +1,6 @@
--- ---
name: plot_pub name: plot_pub
description: 出版级 matplotlib 绘图(论文 / 报告 / 申报书用,中文字体 + viridis 配色 + dpi 设定一键到位)。✅ 触发:用户要画 XRD 谱、TG-DSC 曲线、应力-应变曲线、SEM 标注、多 panel 学术图、要 SVG/PDF 矢量图给论文。⛔ 不触发:PPT 内嵌图(走 `ppt` skill 的 matplotlib 配图流程);只要快速看一眼数据(直接 `df.plot()` 即可,不用本 skill)。 description: 出版级 matplotlib 绘图(论文 / 报告 / 申报书用,中文字体 + viridis 配色 + dpi 设定一键到位)。✅ 触发:用户要画 XRD 谱、TG-DSC 曲线、应力-应变曲线、SEM 标注、多 panel 学术图、投稿级 / Nature 级复合图、要 SVG/PDF 矢量图给论文。⛔ 不触发:PPT 内嵌图(走 `ppt` skill 的 matplotlib 配图流程);只要快速看一眼数据(直接 `df.plot()` 即可,不用本 skill)。
--- ---
# plot_pub # plot_pub
@ -139,6 +139,73 @@ fig.savefig("fig1_characterization.pdf", bbox_inches="tight")
plt.close(fig) plt.close(fig)
``` ```
## 投稿级多 panel 复合图(Nature-grade composite)
要投高影响期刊、要一张"封面级"的多 panel figure 1 时,光排版正确不够 —— 还要**让图讲一个故事**。下面这套设计纪律移植自 `nature-figure` skill(MIT,[github.com/Yuan1z0825/nature-skills](https://github.com/Yuan1z0825/nature-skills)),砍掉其 R / 单细胞 / 在体生物那一套,只留可迁移的部分,适配建材领域。
### 动手前:五点 figure contract
画复合图前先在心里(或跟用户)把这五条对齐,**先想清楚再下笔**,避免画完六张图才发现讲不成一个故事:
1. **核心结论**:这张图要捍卫的**一句话**论断是什么?(例:"复合掺合料把 28d 强度提升 18% 且不牺牲流动度")
2. **证据链**:每个 panel 对应**唯一**一条证据,删掉冗余 panel —— 同一份数据别用两种图形再画一遍,同一组指标别排两次序
3. **图原型**:归类为 ① 定量网格(全是量化对比图)② schematic 主导(机理图 + 验证小图)③ 图像板 + 定量(SEM/光学 plate + 旁证曲线)④ 非对称混合;先定原型再排版
4. **后端**:本 skill 纯 matplotlib(Python),所有 panel / 预览 / 导出统一一套后端出
5. **期刊契约**:开画前定好尺寸(单栏 ~89mm / 双栏 ~183mm)、可编辑矢量(已由 `apply_pub_style``svg.fonttype='none'`)、source data、统计标注、导出格式
> 一句话原则:**图为科学逻辑服务**,美化 / 套模板 / 复杂排版都是次要的。
### 语义配色(比 viridis 更进一步)
单组数据用 viridis 没问题;但**多组对比 / 方法 vs 基线**时,颜色要承载语义,而不是随机区分。`style.py` 已内置一张语义色表:
```python
from skills.plot_pub.style import apply_pub_style, SEMANTIC_COLORS, clean_spines, ablation_alphas
apply_pub_style()
# 蓝=本方法/主角 绿=提升 红=baseline/对照 灰=参照 橙=少量强调
ax.bar(x0, y_base, color=SEMANTIC_COLORS["baseline"], label="基准配方")
ax.bar(x1, y_method, color=SEMANTIC_COLORS["method"], label="复合掺合料")
```
原则:**family consistency beats maximal hue separation** —— 相关的几条基线归一个色系,本方法的几个变体归另一个色系,别把颜色撒得到处都是。消融 / 梯度对比用**同色变 alpha**(0.25→1.0)而不是换色相:
```python
colors = ablation_alphas(len(掺量梯度)) # 同蓝色由浅到深
for (label, y), c in zip(掺量梯度.items(), colors):
ax.plot(x, y, color=c, label=label)
```
### 信息架构 + spine 纪律
- 多 panel 要**作为一个故事读**,不是六张互不相干的图拼一起
- 有 schematic / hero panel 时给它视觉主导,旁边的验证小图别喧宾夺主
- 每个 panel 调 `clean_spines(ax)` —— 只留左 + 下边框,去掉上 + 右,信噪比立刻上来
- legend 无框(`apply_pub_style` 已设)、网格抑制(只在需要时留稀疏 y 网格)
```python
fig, axes = plt.subplots(1, 3, figsize=(7.2, 2.6), constrained_layout=True) # 双栏宽 ~183mm
axes[0].bar(...) # (a) 概览:堆叠/分组柱
axes[1].imshow(...) # (b) 偏差:z-score 热图
axes[2].scatter(...) # (c) 关系:散点/气泡 —— 三级递进,各答一个不同的科学问题
for ax in axes:
clean_spines(ax)
for ax, letter in zip(axes, "abc"):
ax.text(-0.12, 1.02, f"({letter})", transform=ax.transAxes, fontweight="bold", va="bottom")
fig.savefig("fig1_composite.svg", bbox_inches="tight") # SVG 主格式,文字可编辑
fig.savefig("fig1_composite.png", dpi=300, bbox_inches="tight") # PNG 仅作 raster 预览
plt.close(fig)
```
### 导出纪律
- **SVG 为主格式**(文字可编辑,编辑部/作者能改),PDF 矢量并行,PNG 300dpi 仅作预览
- 别拿 PNG 当投稿正图(见反模式 #6)
## 中文字体配置(Windows 注意) ## 中文字体配置(Windows 注意)
`apply_pub_style(chinese=True)` 默认按以下顺序找字体: `apply_pub_style(chinese=True)` 默认按以下顺序找字体:

View File

@ -109,7 +109,64 @@ def apply_pub_style(
rc["pdf.fonttype"] = 42 rc["pdf.fonttype"] = 42
rc["ps.fonttype"] = 42 rc["ps.fonttype"] = 42
# ---- SVG 文字可编辑(投稿级要求:导出后编辑部/作者能在 AI 里改文字) ----
# 'none' = 文字以 <text> 保留,不转 path;配 PDF Type 42 一起,矢量两路都可编辑
rc["svg.fonttype"] = "none"
def reset_style() -> None: def reset_style() -> None:
"""还原 matplotlib 默认 rcParams(测试 / 切换主题时用)。""" """还原 matplotlib 默认 rcParams(测试 / 切换主题时用)。"""
matplotlib.rcdefaults() matplotlib.rcdefaults()
# ============================================================
# Nature 级复合图辅助:语义配色 + spine 纪律
# 思路源自 nature-figure skill(MIT, github.com/Yuan1z0825/nature-skills),
# 砍掉 R / 生物 gallery,只留可迁移的设计纪律,改为纯 Python + 建材领域。
# ============================================================
# 语义配色:颜色承载"科学语义"而非随机区分。同族基线归一个冷色系,
# 本方法/主角归一个暖/蓝主色系 —— family consistency beats maximal hue separation。
SEMANTIC_COLORS = {
"method": "#1f5fa8", # 蓝 = 本工作 / 提出的方法(主角)
"gain": "#2a8f5e", # 绿 = 提升 / 增益 / 正向
"baseline": "#c0392b", # 红 = 对照 / baseline / 退化
"neutral": "#8a8f99", # 灰 = 参照 / 背景 / 次要
"accent": "#e08a1e", # 橙 = 强调 / 高亮少量点
}
def clean_spines(ax, keep=("left", "bottom")) -> None:
"""
出版图 spine 纪律:只留指定边框(默认左 + ),去掉上 +
复合图每个子 panel 都调一次,视觉更干净信噪比更高
Args:
ax: matplotlib Axes
keep: 保留哪几条 spine,默认 ("left", "bottom")
"""
for side in ("top", "right", "left", "bottom"):
ax.spines[side].set_visible(side in keep)
# 刻度只画在保留的边上
ax.tick_params(
top="top" in keep, right="right" in keep,
bottom="bottom" in keep, left="left" in keep,
)
def ablation_alphas(n: int, base_color: str = None):
"""
消融 / 梯度对比:同一颜色变 alpha(0.25 1.0),而不是换色相
返回 n (color, alpha) 不便用,这里直接返回 n RGBA
Args:
n: 系列数
base_color: 基色,默认用 SEMANTIC_COLORS["method"]
"""
import matplotlib.colors as mcolors
import numpy as np
base = base_color or SEMANTIC_COLORS["method"]
rgb = mcolors.to_rgb(base)
alphas = np.linspace(0.25, 1.0, n)
return [(rgb[0], rgb[1], rgb[2], a) for a in alphas]