让AI做PPT,一场与缩放逻辑的持久战
AI Agent 做 PPT 已经不错了,但要让控件、图表、缩放逻辑全部正常工作,需要和 AI 经过数轮对话才能收敛到理想的状态。

为什么要让AI做PPT?
上学期做 Python 基础课的 PPT,试了一下 WPS AI——直接把课程大纲丢给它,说这堂课要讲什么,中间过了什么步骤我不知道,它就生成了。做出来看起来有模有样,但文字没有逻辑,深度不够。模版套出来的东西,永远差一口气。
这学期英语 pre 不一样——既有 speech script,又有 data dashboard。于是我决定走一条更野的路:直接让 opencode + deepseekv4 生成 HTML,然后截图转 PPT。
听起来很美。做起来全是坑。
第一章:V1 — 初版诞生
deepseekv4 根据 speech script + data dashboard 生成了初版 HTML。15 页,有标题有图表,能翻页。
但说真的——第一次打开它的时候,我愣了三秒。Georgia 标题 + Segoe UI 正文,配色灰扑扑的,看起来像学生期末作业。我看了一眼工具栏,15 页。还行,至少内容都在。

这个版本用的是 responsive 布局(94vw max-width 1100px)。我在浏览器里拉伸窗口,内容会自己缩放,看起来挺灵活。
但当我放大的一瞬间——布局全乱了。字体变大,边距变宽,原本刚好一页的内容现在溢出了。"responsive"在普通窗口里是好事,在放大下就是灾难。全屏呢?字体大小不动,但是窗口大小动了,布局完全混乱。
我关掉了全屏。开始重新思考。
第二章:V2 — 质变
"不要 responsive 了,"我对 AI 说,"固定尺寸。设计尺寸就是 1200×675。"
V2 是一次大改。所有 slide 锁定为 1200×675 像素,加入 Inter 字体(Google Fonts),正文换成 Palatino Linotype。

打开的那一刻,视觉上一下子"学术"了很多。但真正的改动不在表面——AI 写了一个叫 updateScale() 的函数,检测窗口大小变化时对整个 Canvas 做 transform scale。这是第一版缩放逻辑。
我试了试全屏,满意了。又试了试拉伸窗口,也还行。我以为缩放逻辑已经稳了。
后来才知道,这只是一个开始。V2 只是让我产生了一切可控的错觉。
第三章:V3-V4 — 缝隙里的修补
V3 只改了一个数字。
参考文献 18 条。在 675px 高度的 slide 里,用正常的字号无论如何也塞不下。AI 把 font-size 调到 14.86px,line-height 1.25——分毫不差,刚好塞进去。这个数字精确到小数点后两位,现在想想,那是一个诅咒。
V4 加入了第一条 zoom 防线:maximum-scale=1.0, user-scalable=no。理由是"防止移动端缩放破坏布局"。
这两版视觉上几乎没有变化。我在正常浏览器窗口里看 Reference 页:18 条,都能显示。好,过了。
但那个时候我完全没有意识到,真正的敌人根本不是字体大小。它在暗处等着我。
第四章:V5 — 夭折的完美方案
"如果每个 slide 直接用 SVG 渲染呢?矢量图,永远清晰,永远不变形。"
这个想法让我兴奋了一整个下午。我让 AI 把整个演示文稿重写了一遍——所有 slide 都用 1200×675 的 SVG,每个字都是矢量路径。

打开的那一刻,我被震撼了。每一个字符都清晰到像是印刷出来的,边缘没有一丝模糊。矢量缩放无懈可击。没有 Chart.js 截图空白的问题,没有字体不一致的问题——一切都完美。
但完美只持续了十分钟。
"把这段文字改一下",我说。AI 沉默了五秒钟,然后开始修改 SVG 里的 <text> 元素坐标。每改一个字,都要重新调整 x、y、font-size、text-anchor。改了三行之后我放弃了。
文字不可选中。后期编辑是噩梦。控件栏被隐藏了,因为没有合适的位置放。这根本不是一条可持续的路。
我关掉了 V5,做了深呼吸。纯 HTML 路线才是对的,SVG 只配做图表。
第五章:V6 — 错误的岔路
回到 HTML。V6 引入了三层结构:
canvas-wrapper → canvas-center → presentation-container
这就是"地图模型"——Canvas 固定 1200×675,视窗只能看到一部分,超出部分被裁剪,用户通过滚动浏览不同区域。
我试了一会儿。放大,出现滚动条。缩小,还是有滚动条。我左右滚了一下,页面一卡一卡的。
"不对,"我突然意识到,"**这是一个 presentation,不是一个地图。**谁能接受在演讲的时候还要手动滚动页面?"
地图模型要求 Canvas 固定不动、靠滚动导航。但我真正想要的,是 Canvas 自动缩放到视窗内——不管窗口多大,它都整整齐齐地出现在正中间。
我走错了路。V6 是一个漂亮的错误。
第六章:V7 — 轮回
"抛弃地图模型,"我对 AI 说。"全部推倒。我们做自适应模型。"
V7 的核心是两个 JS 函数:
updateAutoScale():窗口小于 Canvas 时缩小适配updateFullscreenUI():全屏时放大填满屏幕
还有一个关键——wasInFullscreen 标志位。全屏退出时,auto-scale 会检测窗口大小并试图叠加另一个 transform。如果没有这个标志位,每一次退出全屏都是一场灾难。
改完缩放逻辑,我松了口气。V7 终于稳定了。我关了电脑,准备睡觉。
那天晚上,我睡前打开了浏览器。
Reference 页。 16 条。
我猛地坐起来。之前在电脑上明明看到 18 条,怎么只有 16 条?我重新打开电脑,试了试:普通窗口 16 条,全屏 18 条,resize 一下变回 14 条。
这个 bug 是活的。
Reference 页的幽灵
我花了一整个晚上跟 AI 讨论这个问题。
"子像素四舍五入,"AI 说,"不同缩放级别下每行高度有微小差异,累计起来导致条目进出边界。"
我被说服了。算了一下:30 行 × ±0.44px/行 ≈ ±13px。但这只能解释 0-1 条的变化,而为什么我看到了 4 条的差异?
直觉告诉我,AI 错了。
"你再想想,"我对 AI 说,"13px 能解释 4 条的变化吗?"
AI 沉默了。然后说:"……不能。"
我们重新开始排查。两个小时后,一个数字引起了我的注意——presContainer 的宽度。
| 模式 | presContainer 宽 | 文本宽 |
|---|---|---|
| 普通模式(有 padding) | 1120px | 1048px |
| auto-scale(JS 强制) | 1200px | 1128px |
| 全屏(padding 移除) | 1200px | 1128px |
80px 的宽度差。
回到 Reference 页。80px 的宽度差意味着每个长条目可以少 wrap 一行。7-8 个长条目,每行 17.25px——总差异约 138px。
138px ≈ 8 行 ≈ 4 条。
不是子像素。不是 rounding error。是宽度不一致。
解决方案就这么简单:所有模式下 presContainer 宽度统一为 1200px。
改完之后,我在三个浏览器里分别试了全屏、resize、zoom in、zoom out。18 条。永远是 18 条。不动了。
那个夜晚,我终于睡着了。
尾声
最终版本是这个样子:

18 条参考文献,任你怎么缩放、全屏、resize,都不动了。还加上了中文字体(Noto Serif SC)、彻底禁用了浏览器 zoom(maximum-scale + Ctrl+wheel 阻止)、适配了翻页笔键映射(PageUp/PageDown)。
从 V1 到 V7,七次版本迭代,没有换过一次模板、没有手动调整过一次像素位置。但每一次修改→截图→反馈→再修改的循环,都需要和 AI 经过数轮对话。
如果 AI 能自己看到结果就好了——如果模型具备识图能力,能对自己生成的 HTML 进行负反馈调节,形成 ReAct 闭环。目前这个闭环是人在做。每次我截图、贴回去、说"这里不对"——都是人在替 AI 执行视觉反馈。
个人感觉AI做PPT需要模型提供商对HTML做RL,同时要求模型至少具备识图能力,这样才能对生成出来的PPT进行负反馈调节,形成ReAct闭环。
如果有必要,顶尖模型提供商还可以对AI进行word、pdf、excel、PPT的单独训练优化,让AI更深远地惠及大众办公,而不止于普通的问答。
不训练顶尖大模型的公司仅仅通过prompt engineering、LoRA、RAG、提供API等等方式,很难让AI具备生产日常办公可交付结果的能力。
没有AI的自主负反馈调节,模型再强,也很难收敛到优质的结果。
我不打算放弃。V7 不是终点。
相关经验与技术文档戳👉这里
本文所有截图均来自实际生成的 HTML 文件。项目地址及 AGENTS.md 见第二篇。
AI 生成声明:本文由 deepseek-v4-flash 模型生成,借助 opencode(AI Agent 框架)完成 HTML 调试、截图录制、版本迭代与文档撰写。全过程人机协作:人类把控需求与方向,AI 负责代码实现与文档输出。
Comments 2 条评论
?!强强 !?
@蔡徐坤 💗