AI 生成 PPTX:11 页课件编译出 429 个形状

📅 发布时间:2026/10/11 4:20:16
AI 生成 PPTX:11 页课件编译出 429 个形状
AI 生成 PPTX11 页课件编译出 429 个形状2026 年「让 Agent 写 PPT」成了显学官方 pptx skill 收敛到「先大纲、再出结构化中间表示、交给渲染器出文件」Marp、Slidev 这类 code-first 方案也被翻出来。我用同一思路做了份 11 页试讲课件73.8 KiB 源码编译出 429 个原生形状。一、先看产物一条流水线吐出了什么整套东西不是「一句话生成幻灯片」那种黑盒而是一条能反复改、能版本回滚的工程流水线。实测产物清单如下。产物数量体积说明slides/*.slide课件源码11 个 / 1068 行73.8 KiB类 JSX 的声明式 DSLDSL 组件节点Box 367 Text 257 CodeBlock 5 SVG 6—6 个 SVG 全部手写ppt/slides/*.xml编译产物11 页435.5 KiB未压缩是源码的5.90 倍打包后 PPTX429 个形状68.0 KiBzip 把 XML 压到 9.9%preview/出图11 张 PNG566.7 KiB平均每页 52.8 KiB用于人眼验收STORY.md设计文档11 页规则表7.6 KiB节奏、版式、字数预算试讲逐字稿11 段 / 340 s8.3 KiB与每页一一对应一句话概括体量关系73.8 KiB 源码 → 435.5 KiB OOXML → zip 压到 44.3 KiB → 落盘 68.0 KiB 的 PPTX。二、技术选型三条出片路线怎么选路线代表做法产物文字可编辑我的判断图像合成每页整张图再拼进 PPTX位图否好看但字号改不了、文字选不中教学课件要反复改直接否决命令式代码手写 PptxGenJS / python-pptx逐个addShape原生形状是可控但所有坐标要人肉算11 页 429 个形状会写疯声明式 DSL写BoxText编译器负责布局与出形状原生形状是选它写的是意图不是坐标决定性的差别在最后一步能不能改。试讲课件要按评委反馈反复调字号、挪色块图像路线每改一次就重出一遍图而 DSL 路线改一行源码重新编译即可。三、核心原理一DSL 节点到 OOXML 形状的映射这是整条链路最值得讲的一层。我把 11 页逐个拆开数了一遍页Box带 background 的 BoxTextSVGp:spp:pic文本字符P1203191261187P23614240420292P33314220370352P42711172292248P54813331511419P62813250410523P75614430650320P83815260480396P9269200320374P103616210380340P1119672142118合计367128257642363569三条实测规律1 个Text换 1 个可编辑文本框。11 页里 8 页的「带文字形状数」与 DSL 的Text数量完全相等P6 / P8 / P9 多出的 1~3 个来自CodeBlock内部的行标签不是误差。367 个 Box 里只有 128 个34.9%会落成矩形判定条件是有没有写background。剩下 239 个Box style{{ height: 20 }} /是纯粹的布局占位撑高度/撑间距编译后一个形状都不留。6 个手写 SVG 原样以.svg进ppt/media/不是先光栅化成 PNG矢量保真并且以 STORED 方式存包原始字节 压缩后字节因为 SVG 本身是文本再压一次收益极小。整体折算下来平均每 2.49 行 DSL 产出 1 个形状每个形状烧掉约 1040 字节 XML每页平均 39.0 个形状。顺带看一眼样式侧的落地情况。全篇几何体只有rect和roundRect两种roundRect共 90 个对应 DSL 里 85 处borderRadius圆角没有丢。border也是一样DSL 里 42 处描边声明OOXML 里恰好 42 条带属性的a:ln一条不差另外 125 个无边框形状补的是空a:ln/节点。字体只用三款——Microsoft YaHei1184 次、Menlo204 次代码块、Consolas80 次数字与变量名没有出现字体回退失败导致的方块字。颜色共 26 种、填充 927 次用得最多的是正文深灰#1A2230153 次。四、核心原理二同一份尺寸要走三套单位DSL 里写的是前端熟悉的 px落到 OOXML 全部换单位这三套换算一旦记错手写 XML 修图必然全歪。维度DSL 里写的OOXML 里的值换算关系画布宽width: 1280pxa:ext cx121920001 px 9525 EMU画布高height: 720pxa:ext cy6858000同上字号fontSize: 58sz43501 px 0.75 ptsz 单位是 1/100 pt即 px × 75字距letterSpacing: 3spc225同上px × 75所以 DSL 里的 58px 大标题在 PowerPoint 里显示为 43.5 pt。编译器还会按字符类型切 run全篇 628 个 run 中langzh-CN349 个、langen-US279 个中文与英文/数字分开打语言标记字体回退和拼写检查才不会串。五、核心原理三5.9 倍膨胀两成来自源码内嵌435.5 KiB / 73.8 KiB 5.90 倍这个数一开始让我以为是形状骨架太重。查下去发现另有原因每页的完整 DSL 源码被 HTML 转义后塞进了p:cNvPr id1 descr...。11 页descr合计 87,356 字节占全部 slide XML 的19.6%单页转义后是原文的 1.15~1.18 倍lt;gt;#xA;这些实体在吃体积。扣掉descr后 XML 仍是源码的 4.75 倍剩下的才是形状骨架税。这么干是有道理的descr让 PPTX 自带可反解的源码下一轮 Agent 接着改时能读回原始意图不用猜哪个形状对应哪段 DSL。代价就是下面这条坑。六、验收闭环渲染成图再让「另一双眼睛」看一遍这条流水线一共分四层缺任何一层质量都会塌大纲层先写STORY.md把受众、目标、页数、每页功能和节奏定下来人工过一遍内容层把大纲翻译成slides/*.slide这一步只做内容决策不碰坐标编译层确定性渲染器把 DSL 变成 OOXML不做任何「自由发挥」验收层每页渲成 PNG 放preview/由人或另一个不看生成上下文的 Agent对照清单挑毛病只修出问题的页。第 4 层是最容易省、也最不该省的一层。原因很实在文字溢出、元素重叠、对比度不够这三类缺陷只有在渲染成像素之后才存在看 DSL 源码永远发现不了——源码里height: 54和fontSize: 19两个数单独看都合理但真渲出来字可能顶到边框。这次 11 页全部出图合计 566.7 KiB平均每页 52.8 KiB最大的 P3 是 59,740 B最小的 P11 是 33,202 B。还有一条纪律生成者和验收者必须是两边。写这页的对象脑子里已经有「它应该长什么样」会本能地给自己的排版缺陷找理由换一个没有生成上下文的校验者才会老老实实按清单逐条查。这也是 2026 年几套成熟方案共同的做法。七、关键代码结束页的 DSL 长这样注意svg是内联手写的Box有background才会变成矩形// slides/11.slide —— 结束页 Slide style{{ width: 1280px, height: 720px, background: #FFFFFF, padding: 0, fontFamily: Microsoft YaHei }} {/* 背景装饰低透明循环环SVG 手写编译后保留矢量 */} Box style{{ position: absolute, top: 30, left: 660, opacity: 0.07 }} svg width{620} height{620} viewBox0 0 620 620 path dM 310 60 A 250 250 0 1 1 486.8 133.2 fillnone stroke#1E4FA8 strokeWidth36 strokeLinecapround / polygon points486.8,133.2 441.5,122.0 475.5,88.0 fill#1E4FA8 / /svg /Box Text style{{ fontSize: 58, fontWeight: bold, color: #0E3F8C }} 请各位评委老师批评指正 /Text {/* 只有带 background 的 Box 会落成 PPT 里的矩形其余都是布局占位 */} Box style{{ width: 100, height: 3, background: #1E4FA8 }} / /Slide「执行过程追踪」那张表也是 Box Text 一行行拼出来的没有用任何表格原语// slides/07.slide —— 追踪表的一行斑马纹靠手写 background 交替 Box style{{ width: 100%, height: 54, background: #F0F5FC, flexDirection: row, alignItems: center, borderBottomWidth: 1, borderBottomStyle: solid, borderBottomColor: #E5E7EB }} Box style{{ width: 200, paddingLeft: 20 }} Text style{{ fontSize: 19, color: #4A5568 }}循环开始前/Text /Box Box style{{ width: 150 }} Text style{{ fontSize: 19, color: #8B97A8, fontFamily: Consolas }}—/Text /Box Box style{{ width: 200 }} Text style{{ fontSize: 19, fontWeight: bold, color: #1E4FA8, fontFamily: Consolas }}0/Text /Box /Box代码演示页用CodeBlock组件它带自己的语法主题与等宽字体不是普通Text// slides/06.slide —— 代码块组件 CodeBlock code{# 例计算 1 2 ... 100 total 0 # ① 建累加器清零 for i in range(1, 101): # ② 取i 依次是 1..100 total total i # 加把当前的 i 累加进去 print(total) # ③ 用循环结束后再取结果} languagepython width{744} fontSize{21} themegithub-dark /增量修订靠.slidep/state.json记每页的版本与 commit这是能反复改的基础{filePath:...\\Python循环结构-试讲课件.pptx,lastCommit:{01:{revisionId:103cd8dc...,version:1,at:2026-09-08 12:36:59.786},10:{revisionId:44b21f5c...,version:10,at:2026-09-08 12:46:51.559},11:{revisionId:7a0bcb39...,version:12,at:2026-09-08 12:48:00.375}}}上面所有数字都是把 PPTX 当 zip 打开后数出来的脚本核心就这几行# tools/_measure_pptworker.py节选—— 所有结论均来自真实文件importzipfile,re zzipfile.ZipFile(PPTX)xz.read(ppt/slides/slide11.xml).decode(utf-8,ignore)print(sp 形状:,len(re.findall(rp:sp,x)),pic 图片:,len(re.findall(rp:pic,x)),原生表格:,len(re.findall(ra:tbl,x)))# 实测为 0descrre.search(rp:cNvPr id1 name descr(.*?)/,x,re.S).group(1)print(源码内嵌:,len(descr),B)八、把设计规则写成可校验的表STORY.md最有价值的地方是它把「排版感觉」写成了能跑校验的规则。实测下来全部命中hero 页视觉重页3 个P1 / P3 / P11占 3/11 27.3%落在 20–30% 预算区间节奏序列 peak 4 个、valley 5 个、transition 2 个最长连续 valley 2没出现连续三页低谷任意两个 hero 之间至少隔 1 页实测相邻 hero 对数 0全部 11 页的关键数字都在文档里留了手算校验5050、4950、2550、50005000九、踩坑记录1. 「表格」根本不是表格。全篇a:tbl数量是0p:graphicFrame是 0图表引用也是 0。P7 那张追踪表是 43 个独立文本框拼出来的P7 的 XML 也因此成为最重的一页58,641 B。后果是在 PowerPoint 里没法整表选中、改列宽要一个个拖、排序完全做不了。想导出成真表格得在 DSL 层加Table原语。2. 纯布局 Box 选不中。367 个 Box 里 239 个没有background编译后消失。有次我想给某块区域加底色在 PowerPoint 里怎么点都选不到它——因为它压根不存在得回 DSL 里补background再编译。3. 单位换算按 100 倍算会全错。第一次手改 XML 时我把fontSize: 58写成了sz5800实际是4350。记住px × 75不是 × 1001 px 0.75 pt。4.descr与形状会脱节。因为源码被内嵌进 PPTX一旦有人在 PowerPoint 里手工拖过形状descr里的 DSL 就和实际布局不一致了下一轮 Agent 读回源码再编译会把手工改动冲掉。要么全程只改 DSL要么接受手工改动会在下次编译时丢失。5. 版本号断号。.slidep/state.json记了 11 页版本号范围是 1…12但11 是缺失的——中间有一次提交被覆盖。想做「回退到上一版」的功能时按version - 1取会取空。6. SVG 缓存留了孤儿。.cache/media/images/下有 7 个 SVG只有 6 个被打进 PPTX剩下的5e7acd15….svg176 B成了孤儿。清理产物时要按引用表删不能一股脑拷目录。7. 逐字稿时间预算超标。表格里 11 段相加是340 秒5:40而文档标题写的是「总计 5:00」超了 40 秒、13.3%。试讲卡时严格的话必须按文档提示把 P2、P7 当提纲页快速带过。十、实测数据汇总指标实测值源码 → slide XML 膨胀5.90 倍435.5 KiB / 73.8 KiB其中descr内嵌占比19.6%87,356 Bzip 压缩率9.9%435.5 KiB → 44.3 KiB形状总数 / 平均每页429 / 39.0文本形状占比59.9%257 个产出效率每 2.49 行 DSL → 1 个形状颜色种类 / 填充总次数26 种 / 927 次字号档位21 档13 px ~ 38 px编译节奏11 分 01 秒出 11 页平均 60.1 s/页顺带一个提醒全篇最小字号是 13 px9.75 pt只出现 1 次。在 1280 px 画布上 13 px 只占 1.0% 宽度投到教室投影基本看不清试讲课件建议正文不低于 18 px。十一、小结与下一步回看这套做法真正省时间的不只是「生成」而是把课件变成了一份可 diff、可回滚、可校验的工程文件设计规则写在STORY.md里能跑校验每页改动记在state.json里能定位出图放在preview/里能人眼验收。对要自己搭这套流程的同学我的建议是按顺序做三件事别一上来就追求「一句话出片」先把规则写出来hero 占比、连续低谷页数、每页字数预算、配色数量上限写成一张能跑校验的表比事后靠眼睛挑有效得多再把单位换算刻进肌肉px × 9525 得 EMU、px × 75 得 sz这两个数错了后面所有手工微调都是白工最后一定留出验收位渲染成图、换一双眼睛看这一步的成本远低于在 PPT 里一个个拖形状。这三条做完「AI 生成 PPT」才从演示效果变成能反复用的生产方式。我下一步想补的是Table表格原语解决第 1 条坑以及把descr拆成外部 sidecar 文件把那 19.6% 的体积还回来。完整工程已整理好含 11 页 DSL 源码、STORY.md 设计文档、5 分钟逐字稿和上文这个实测脚本需要的同学评论区扣「源码」我看到会一一回复也欢迎关注我后面会把课件工程化这个系列继续更下去。