diff --git a/CHANGELOG.md b/CHANGELOG.md index da6d71e..1ef1216 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ > 所以不是每个版本号都有条目。条目格式 `## <版本> — <日期>`,新条目加在最上面。 > 工程口径的完整记录见 `PROGRESS.md` / git log。 +## 0.60.29 — 2026-08-04 + +- 生成 PPT 时,如果没有说明所属机构,模板候选会固定包含中国建材总院红模板;提到总院、中国建材、中建材、建材集团或 CNBM 模板时会直接选用该模板。明确属于其他机构或已经指定其他模板时不会强制加入,同时移除了容易混淆的通用商务红模板。 + ## 0.60.28 — 2026-08-04 - 对话中的 HTML 产物预览现在会充分利用可用宽度,图片和视频预览也适度放大;文件附件改为更清晰的偏方形卡片,可直接辨认文件类型、名称和预览入口。 diff --git a/PROGRESS.md b/PROGRESS.md index 50c3b79..09989c1 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -2,7 +2,7 @@ > 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9`。 -最后更新:2026-08-04(对话产物预览与文件 chip 优化,bump 0.60.28) +最后更新:2026-08-04(PPT 总院红候选与别名路由收敛,bump 0.60.29) --- @@ -23,6 +23,8 @@ ### 2026-08-04 +- **08-04 / 0.60.29 / PPT 总院红候选硬门 + 别名直达**:新建 PPT 未说明所属机构时,模板 / 品牌 / visual_style 候选必须包含 `zongyuan_red`;明确外部机构或其他唯一模板时不强制加入。总院、中国建材、中建材、建材集团、CNBM 等“机构词 × 模板/风格词”表达直接归一到总院红,不再追问具体模板;删除易混淆的通用 `business-red` 品牌预设,普通商务红仅保留为即时配色候选。新增路由回归测试,相关 5 项 unittest、品牌索引 JSON 校验及 diff 检查通过;无 schema、migration、HTTP API、依赖或运行方式变化。 + - **08-04 / 0.60.28 / 对话产物宽版预览 + 文件 chip 优化**:包含 HTML 产物的助手消息改用接近占满对话区的宽版布局,HTML 画布取消 760px 上限;内联视频上限提升至 720px,图片上限提升至 560px 并保持原比例。文件 chip 从小胶囊调整为偏方形附件卡,直接展示扩展名类型块、文件名和预览入口,粘贴附件同步统一样式并保留键盘焦点反馈。Node 前端 15 项、JavaScript 语法及 diff 检查通过;无 schema、migration、HTTP API 或依赖变化。 ### 2026-08-03 diff --git a/SKILL_LIST.md b/SKILL_LIST.md index 5e7538d..c9f8161 100644 --- a/SKILL_LIST.md +++ b/SKILL_LIST.md @@ -1,7 +1,7 @@ # zcbot Skill 清单 服务对象:中国建筑材料科学研究总院 —— 无机非金属材料 R&D(水泥 / 混凝土 / 玻璃 / 陶瓷 / 耐火 / 新型建材) -最后更新:2026-07-27(videogen 新增 Seedance 单图首帧与最多 9 张多图参考输入) +最后更新:2026-08-04(ppt 默认机构候选包含总院红,明确外部机构除外,建材集团别名直达) Skill 总数:18 zcbot 的"skill"是一份可加载的工作流脚本(`skills//SKILL.md` + 配套 templates / scripts / Python helper),模型在识别用户意图后挂载对应 skill,按其内置的阶段化流程产出可交付物。本文档面向**使用方 / 协作方**,按"做什么、什么时候用、什么时候别用、典型产物"组织。 @@ -20,7 +20,7 @@ zcbot 的"skill"是一份可加载的工作流脚本(`skills//SKILL.md` + | 科研写作 | [patent](#patent) | 写发明专利技术交底书(供代理师转写) | | 科研写作 | [review](#review) | 审稿 / 润色 / 校对(中英文,长文档分段深审;含英文 SCI 润色子模式) | | 科研写作 | [rebuttal](#rebuttal) | 回复审稿意见(修回):逐点回复信 + 稿件修改清单 + 修回 cover letter | -| 演示出图 | [ppt](#ppt) | 生成可编辑 PowerPoint(SVG-first:逐页手写 SVG → 转原生 DrawingML;19 种视觉风格 + 模板库) | +| 演示出图 | [ppt](#ppt) | 生成可编辑 PowerPoint(SVG-first:逐页手写 SVG → 原生 DrawingML;总院红品牌模板可选) | | 演示出图 | [plot_pub](#plot_pub) | 出版级 matplotlib 学术图(中文 + viridis + 矢量 + 投稿级复合图设计纪律) | | 文献检索 | [research](#research) | 查 paper_server(OpenAlex 元数据 + Sci-Hub 下载) | | 文献检索 | [documents](#documents) | 查内部 7 学科材料知识库(100W+ 论文,跨语言检索;host-side tool 持 key) | @@ -209,7 +209,7 @@ zcbot 的"skill"是一份可加载的工作流脚本(`skills//SKILL.md` + **定向模型**:frontmatter 定向 `glm.pro52`(GLM-5.2)—— 对话中 load 本 skill 时自动切换到该模型(不限档位,不自动切回;后续消息沿用,可在顶栏下拉手动切走)。 -**默认主题 —— 自由设计**(content-driven):按内容+受众+选定 visual_style 派生配色版式,spec 阶段给 ≥3 套候选挑;商务红/品牌色作为候选之一,用户点名或素材有 brand guideline 才锁定。 +**默认主题 —— 自由设计 + 总院红条件必选候选**:用户未说明所属机构时,新建 PPT 的候选列表必须包含 `templates/layouts/zongyuan_red/`;用户明确说明属于其他机构 / 品牌或提供其他唯一模板时不强制加入。用户明确说“总院模板、建材集团模板、中国建材模板、中建材模板、CNBM 模板”等同义表达时,直接视为已选中总院红。普通商务红只保留为即时配色方向,不再提供独立品牌模板。 **八条对齐**(spec 阶段定稿,a–h):画布 / 页数 / 受众+核心信息+投递目的 / mode+visual_style / 配色 / 图标库 / 字体+字号 / 配图。确认后产出两份引擎契约:`design_spec.md`(人读叙事)+ `spec_lock.md`(机读执行锁,executor 每页重读、抗长 deck 漂移)。 diff --git a/core/__init__.py b/core/__init__.py index a3afb0f..67892ea 100644 --- a/core/__init__.py +++ b/core/__init__.py @@ -1,3 +1,3 @@ # zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。 # 改版本只动这一行。 -__version__ = "0.60.28" +__version__ = "0.60.29" diff --git a/skills/ppt/SKILL.md b/skills/ppt/SKILL.md index 5b185e7..95f4de3 100644 --- a/skills/ppt/SKILL.md +++ b/skills/ppt/SKILL.md @@ -65,11 +65,12 @@ model: glm.pro52 ## 默认主题 — 自由设计(content-driven) -**默认不锁死配色**:策略阶段根据**内容 + 受众 + 选定的 visual_style** 派生一套协调配色与版式(在 spec 阶段给用户 ≥3 个配色/风格候选挑)。模板是地板也是天花板 —— 默认自由设计让版面跟着内容走,而非被固定语汇框死。 +**默认不锁死配色**:策略阶段根据**内容 + 受众 + 选定的 visual_style**派生一套协调配色与版式(在 spec 阶段给用户 ≥3 个配色/风格候选挑)。模板是地板也是天花板 —— 默认自由设计让版面跟着内容走,而非被固定语汇框死。 -- 商务红 `#C00000` / 中建材等品牌色,作为**候选之一**;**中文政企/集团/科研商务汇报默认就把商务红列进 ≥3 配色候选**(见 strategist.md §e)。用户点名("做成蓝色 / 用我们公司紫色")或素材里有 brand guideline → 按其锁定。 -- 现成一款 **`business-red` 商务红品牌预设**(`templates/brands/business-red/`:#C00000 全色表 + 宋体标题 + 实心图标);用户说"红色 / 商务红 / 中建材风"→ 指给他按路径 opt-in,或直接锁其配色。其它品牌/模板同理:**用户给 `templates/` 下明确路径才触发**(见 strategist.md 模板分发),不主动猜、不模糊匹配。 -- **例外(主动提示):中国建材总院系汇报** —— 受众/素材/用户机构指向 **中国建筑材料科学研究总院 · 中国建材(CNBM)系**(工作汇报/立项/项目评审/**职称评审**/品牌宣讲)时,策略阶段**主动**把整套品牌模板 `templates/layouts/zongyuan_red/`(八边形 logo + 品牌红 `#D7000E` + 总部大楼实景铺底,5 页齐)作为候选点名给用户,用户点头再按明确路径套入(见 strategist.md §e "中国建材总院" 提示)。这是唯一鼓励主动提模板的场景;其余仍等明确路径。 +- 商务红 `#C00000` 可作为普通中文政企 / 集团 / 科研商务汇报的**配色候选之一**,但它只是一套即时派生的配色方案,不对应内置品牌模板。用户点名其他颜色或素材里有 brand guideline → 按其锁定。 +- 其它品牌 / 模板仍按明确路径 opt-in(见 strategist.md 模板分发),不主动猜、不模糊匹配。 +- **候选硬门**:zcbot 默认服务于中国建筑材料科学研究总院。用户未说明所属机构时,只要新建 PPT 进入模板 / 品牌 / visual_style 候选流程,候选列表中**必须包含** `templates/layouts/zongyuan_red/`,并以“中国建材总院红模板”单独列出,与其他候选一起等用户确认;不要静默锁定。用户明确说明 PPT 属于其他机构 / 品牌,或提供现有 PPT / 明确指定其他唯一模板时,不强制加入 `zongyuan_red`。模板锁定的是品牌身份 + 页面骨架,正文仍按内容扩展,不机械限制为 5 页。 +- **总院模板别名直接归一**:机构词“总院 / 中国建筑材料科学研究总院 / 中国建材 / 中建材 / CNBM / 中国建材集团 / 建材集团”,与意图词“模板 / 红模板 / 品牌模板 / 风格 / 品牌风”任意组合,都视为用户已选中 `templates/layouts/zongyuan_red/`。包括但不限于“总院红、中国建材模板、中建材模板、CNBM 模板、中国建材集团模板、建材集团模板、集团红模板、中国建材风、中建材风、按集团模板、用建材集团风格”;直接锁定该明确路径,不再追问“是哪一套模板”。在本项目总院语境下,单说“集团模板 / 集团红模板”也按此处理。用户提供的现有 PPT / 其他明确模板仍然优先。 --- diff --git a/skills/ppt/references/strategist.md b/skills/ppt/references/strategist.md index 3e01876..5fbae17 100644 --- a/skills/ppt/references/strategist.md +++ b/skills/ppt/references/strategist.md @@ -119,11 +119,11 @@ Proactively provide a color scheme (HEX values) based on content characteristics | Technology / Internet | `#1565C0` Bright Blue | Innovative, energetic | | Healthcare / Health | `#00796B` Teal Green | Professional, reassuring | | Government / Public Sector | `#C41E3A` Red | Authoritative, dignified | -| 中文政企 / 集团 / 研究院所 商务汇报 | `#C00000` 商务红 | 庄重、权威、结论先行(中建材系商务红) | +| 中文政企 / 集团 / 研究院所 商务汇报 | `#C00000` 商务红 | 庄重、权威、结论先行 | -> **商务红 default candidate.** For Chinese state-enterprise / group / research-institute business decks (工作汇报 / 述职 / 方案评审 / 年度总结 / 科技成果), offer **商务红 `#C00000`** as one of the ≥3 color candidates by default — it is the conventional, expected business color in this context. Anchor a red-dominant scheme on it: pair with a 鎏金 `#BF9B5F` *or* 沉靛蓝 `#2B4C7E` accent (red-gold or red-blue, pick one as the point accent), warm off-white surface (`#FBF5F5`), and reserve solid `#C00000` for headings / key data / one color block — never a full-canvas red. A ready-made bundle of exactly this (palette + serif-title typography + filled-icon style) ships as the **`business-red` brand preset** (`templates/brands/business-red/`); when the user wants "红色 / 商务红 / 中建材风" point them at it (apply by explicit path per Step 3) or lock its palette inline. +> **商务红 default color candidate.** For Chinese state-enterprise / group / research-institute business decks (工作汇报 / 述职 / 方案评审 / 年度总结 / 科技成果), offer **商务红 `#C00000`** as one of the ≥3 color candidates by default — it is a color direction, not a built-in brand template. Anchor a red-dominant scheme on it: pair with a 鎏金 `#BF9B5F` *or* 沉靛蓝 `#2B4C7E` accent (red-gold or red-blue, pick one as the point accent), warm off-white surface (`#FBF5F5`), and reserve solid `#C00000` for headings / key data / one color block — never a full-canvas red. -> **中国建材总院 → 主动提 `zongyuan_red` 整套模板.** 当受众 / 素材 / 用户机构指向 **中国建筑材料科学研究总院 · 中国建材(CNBM)系**(工作汇报 / 述职 / 立项 / 项目评审 / **职称评审** / 品牌宣讲)时,除商务红候选外,**主动在 a–h 预览里把 `templates/layouts/zongyuan_red/` 作为一个候选点名给用户**(一句话:"你们总院有现成品牌模板 `zongyuan_red`(八边形 logo + 品牌红 `#D7000E` + 总部大楼实景铺底,封面/目录/章节/内容/尾页 5 页齐),要不要直接套?")。与 `business-red` 的区别:business-red 只是**配色+字体身份预设**(自由设计上皮用);zongyuan_red 是**带真实 logo/实景照片的整套 5 页版式**,要品牌一致、省事就用它。仍是 opt-in —— 用户点头后按 Step 3 以**明确路径**套入锁定其 identity+structure;用户要另起风格 / 换配色就走自由设计,不强推。 +> **候选硬门 + 别名直达 `zongyuan_red`.** zcbot 默认服务于中国建筑材料科学研究总院。用户未说明所属机构时,只要新建 PPT 进入模板 / 品牌 / visual_style 候选流程,候选列表中必须包含 `templates/layouts/zongyuan_red/`,以“中国建材总院红模板”单独列出。用户明确说明 PPT 属于其他机构 / 品牌,或提供现有 deck / 明确指定其他唯一模板时,不强制加入 `zongyuan_red`。别名按“机构词 × 意图词”归一:机构词包括“总院 / 中国建筑材料科学研究总院 / 中国建材 / 中建材 / CNBM / 中国建材集团 / 建材集团”,意图词包括“模板 / 红模板 / 品牌模板 / 风格 / 品牌风”;任意组合都表示用户已明确选中 `zongyuan_red`,包括但不限于“总院红、中国建材模板、中建材模板、CNBM 模板、中国建材集团模板、建材集团模板、集团红模板、中国建材风、中建材风、按集团模板、用建材集团风格”。在本项目总院语境下,单说“集团模板 / 集团红模板”也按此处理。此时直接按路径锁定 identity + structure,不再追问具体模板。`zongyuan_red` 是中国建材相关表述唯一对应的内置红色品牌模板,带真实 logo、总部实景和封面/目录/章节/内容/尾页 5 类骨架。模板锁定后保留品牌恒定色,补锁 1–2 个内容辅助色并按实际页数扩展正文版式。 **Color rules**: 60-30-10 rule (primary 60%, secondary 30%, accent 10%); text contrast ratio >= 4.5:1; no more than 4 colors per page. diff --git a/skills/ppt/templates/brands/brands_index.json b/skills/ppt/templates/brands/brands_index.json index c1bb571..37995c0 100644 --- a/skills/ppt/templates/brands/brands_index.json +++ b/skills/ppt/templates/brands/brands_index.json @@ -6,9 +6,5 @@ "google": { "summary": "Google brand identity — multi-product corporate decks, developer events (Google I/O style), education and training in the Google ecosystem", "primary_color": "#4285F4" - }, - "business-red": { - "summary": "商务红 (Business Red) — 中文政企/集团/研究院所商务汇报、述职、方案评审、年度总结;商务红 #C00000(中建材系同色),庄重结论先行,红金/红蓝配,实心图标,宋体标题", - "primary_color": "#C00000" } } diff --git a/skills/ppt/templates/brands/business-red/design_spec.md b/skills/ppt/templates/brands/business-red/design_spec.md deleted file mode 100644 index 6a16563..0000000 --- a/skills/ppt/templates/brands/business-red/design_spec.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -brand_id: business-red -kind: brand -summary: 商务红 (Business Red) identity — 中文政企/科研/集团商务汇报,商务红 #C00000(中建材系商务红同色);庄重、结论先行、红金/红蓝配 -primary_color: "#C00000" ---- - -# 商务红 Business-Red Brand Specification - -> Identity-only preset (no SVG page roster — pages are composed freely under these constraints). -> A **generic** Chinese business / state-enterprise / research-institute identity anchored on -> 商务红 `#C00000` (the PowerPoint "Dark Red", also 中建材系商务红). Not tied to one organization -> — reuse for 总院 / 集团 / 政企 / 科研 decks. To bind a real organization, drop its logo into this -> directory and fill §IV (see Logo note). - -## I. Brand Overview - -| Property | Value | -|---|---| -| Brand Name | 商务红 (Business Red) | -| Use Cases | 政企 / 集团 / 研究院所 工作汇报、述职、方案评审、年度总结、科技成果汇报、对外宣讲 | -| Tone | 庄重、严谨、结论先行、克制;权威而不浮夸(红为主、金/蓝点缀,避免大面积纯红刺眼) | - -## II. Color Scheme - -| Role | HEX | Provenance | Notes | -|---|---|---|---| -| primary | `#C00000` | fact | 商务红 — 主色(= PowerPoint 标准"深红",中建材系商务红同色)。标题强调、主色块、主图表系列 | -| primary-deep | `#8A0000` | approx | 深绛红 — 主红暗阶。表头、深色页底、强调描边、悬停/选中态 | -| neutral-dark | `#1A1A1A` | approx | 近黑 — 正文、深色面、图表基线/坐标轴 | -| bg | `#FFFFFF` | fact | 纯白页面背景 | -| accent (gold) | `#BF9B5F` | approx | 鎏金/古铜 — 点缀、金线分隔、奖项/关键数据高亮。红金为政企/官方经典搭配,显厚重 | -| accent (info) | `#2B4C7E` | approx | 沉靛蓝 — 流程/链接/可交互/第二数据系列。冷色平衡暖红,红蓝是另一套稳重搭配 | -| accent (positive) | `#2E7D32` | approx | 墨绿 — 推荐项、正向、达标、增长 | -| accent (alert) | `#E08A1E` | approx | 琥珀橙 — 风险/提醒/预警(刻意避开与主红撞色,不要再用一种红表示警示) | -| surface | `#FBF5F5` | approx | 暖调微红的卡片底/面板,比纯白略沉,承托内容 | -| border | `#ECDCDC` | approx | 暖浅灰 — 卡片边、分隔线、表格栏线 | -| muted-text | `#6E6260` | approx | 暖灰 — 次要文字、注释、图表标签、页码/出处 | - -第 1–4 行(primary / primary-deep / neutral-dark / bg)是身份骨架;gold / info / positive / alert / -surface / border / muted 是其上的演示约定色,按政企商务气质派生。语义三元组(info=流程 / positive= -推荐 / alert=风险)用于区分内容类型。**纪律**:红为主、金或蓝二选一作点缀主调(不要红金蓝同台喧宾); -大面积底色用 bg/surface,纯 `#C00000` 只压在标题条、关键数据、主色块等"少而重"的位置 —— 避免满屏 -红的廉价感。Strategist 可按页轮换主色支配比例。 - -## III. Typography - -| Role | Family | Weight | -|---|---|---| -| title | `"Source Han Serif SC", "Noto Serif CJK SC", "Songti SC", "SimSun", serif` | 600–700 | -| body | `"Source Han Sans SC", "Noto Sans CJK SC", "Microsoft YaHei", "PingFang SC", sans-serif` | 400 | - -> 宋体系标题给政企/官方的庄重感,黑体正文保清晰。两条字体栈尾都收在 **预装字体**(`SimSun` / -> `Microsoft YaHei`,见 shared-standards §字体),无运行时回退也不会掉成系统默认。若部署机装了思源 -> 宋体/黑体(沙箱镜像的 `fonts-noto-cjk` 即 Noto CJK),会优先吃到更优字形。纯数字/西文标号可另配 -> 一款衬线数字体(如 `Georgia`)增强财务/数据页质感,非必须。 - -## IV. Logo - -**本预设不绑定具体机构 logo**(通用商务红身份)。默认用机构中文全称作**文字 wordmark**: -- 封面:机构全称用 title 衬线体,`primary`(`#C00000`)或 `neutral-dark`,压在左下/居中;副标题用 body。 -- 页眉/页脚:可放机构简称小字 + 一条 `primary` 细色条作版式锚点;不要每页堆砌。 -- 要绑真实机构:把其 logo 存成 `./_logo.svg` 放进本目录,在此表补一行(File / Form / Usage), - 并按 anthropic 预设的 Logo 段写清留白(≥0.5× logo 高)、用色(单色优先 `primary` 或随页文字色)、 - 封面 vs 页内的取用规则。 - -## V. Voice & Tone - -- 正式度:正式、书面、政企汇报口吻;结论先行。 -- 人称:我们 / 各位(中文);单位自称用机构简称。 -- Emoji:避免(政企/官方场合不用)。 -- 缩略语 / 术语:首次出现给中文全称(必要时附英文/缩写),后文再用简称。 - -## VI. Icon Style - -- Preference: filled(实心) - -> 政企/官方汇报偏稳重厚实,实心图标比线性更压得住场。用 `templates/icons/` 时优先 `tabler-filled` / -> `chunk-filled` 实心库,不要 outline 线性库(太轻,与庄重调性不符)。图标用色走 `primary` / -> `primary-deep` / `neutral-dark`,点缀可用 `gold`;一套库到底,不混库。 diff --git a/tests/test_ppt_skill_policy.py b/tests/test_ppt_skill_policy.py new file mode 100644 index 0000000..807333d --- /dev/null +++ b/tests/test_ppt_skill_policy.py @@ -0,0 +1,47 @@ +"""PPT skill 的总院品牌候选与别名路由回归测试。""" +import json +from pathlib import Path +import unittest + + +ROOT = Path(__file__).resolve().parents[1] + + +class TestPptSkillBrandPolicy(unittest.TestCase): + def test_zongyuan_red_is_candidate_with_alias_direct_route(self): + skill = (ROOT / "skills" / "ppt" / "SKILL.md").read_text(encoding="utf-8") + strategist = ( + ROOT / "skills" / "ppt" / "references" / "strategist.md" + ).read_text(encoding="utf-8") + + self.assertIn("候选硬门", skill) + self.assertIn("候选列表中**必须包含**", skill) + self.assertIn("用户未说明所属机构", skill) + self.assertIn("用户明确说明 PPT 属于其他机构 / 品牌", skill) + self.assertIn("总院模板别名直接归一", skill) + self.assertIn("建材集团模板", skill) + self.assertIn("机构词", skill) + self.assertIn("中国建材风", skill) + self.assertIn("候选硬门 + 别名直达", strategist) + self.assertIn("用户已明确选中 `zongyuan_red`", strategist) + self.assertNotIn("唯一默认入口", skill) + self.assertNotIn("business-red", skill) + self.assertNotIn("business-red", strategist) + self.assertFalse( + (ROOT / "skills" / "ppt" / "templates" / "brands" / "business-red").exists() + ) + brands_index = json.loads( + ( + ROOT + / "skills" + / "ppt" + / "templates" + / "brands" + / "brands_index.json" + ).read_text(encoding="utf-8") + ) + self.assertNotIn("business-red", brands_index) + + +if __name__ == "__main__": + unittest.main()