源文件:chapter5/paper-to-ppt/README.md 实验 5-4:基于论文的 PPT 自动生成(提议者-审核者机制) 配套《深入理解 AI Agent》第 5 章。把"做 PPT"重构为代码生成问题:用 Slidev(Markdown + HTML 定义幻灯片)框架,让 Agent 从一篇论文 自动生成演示文稿,并用提议者-审核者(Proposer-Reviewer)机制做视觉质量控制。 一句话结论 Proposer 只写 Slidev 代码、Reviewer 真正把每页渲染成 PNG 再用 Vision LLM 看图 挑毛病(文字溢出 / 内容拥挤 / 图片尺寸),Proposer 据结构化反馈迭代修订。
源文件:chapter5/paper-to-ppt/README.md
配套《深入理解 AI Agent》第 5 章。把"做 PPT"重构为代码生成问题:用
Slidev(Markdown + HTML 定义幻灯片)框架,让 Agent 从一篇论文
自动生成演示文稿,并用**提议者-审核者(Proposer-Reviewer)**机制做视觉质量控制。
Proposer 只写 Slidev 代码、Reviewer 真正把每页渲染成 PNG 再用 Vision LLM 看图
挑毛病(文字溢出 / 内容拥挤 / 图片尺寸),Proposer 据结构化反馈迭代修订。相比"单 Agent
自审"(把历次渲染图片都堆在同一上下文里),双 Agent 分工的上下文峰值显著更小——
因为 Proposer 全程不看图片、Reviewer 每轮只看最新一版截图。
Agent 写完 Slidev 代码时并不知道实际渲染效果:内容会不会太挤、文字会不会溢出、
图片尺寸是否合适——这些只有真正渲染成像素才看得出来。所以 Reviewer 接触到的是
Proposer 看不到的新信息(渲染结果),这正是本机制的价值所在。
| 角色 | 职责 | 上下文里有什么 |
|---|---|---|
Proposer(gpt-5.6-luna,纯文本) |
读论文 → 规划页面 → 生成/修订 slides.md |
论文正文 + 累积的结构化文字反馈(永不含图片) |
Reviewer(gpt-5.6-luna,Vision) |
看最新一版每页 PNG,输出结构化建议 JSON | 每轮全新调用,只含最新一版截图 |
Reviewer 的建议是结构化、可执行的,而非模糊的"不好看",包含字段:page(页码)、issue_type(text_overflow/overcrowded/image_size/readability/layout)、severity(high/medium/low)、suggestion(具体修改建议)、以及整份的 overall_score 与 pass。
Proposer 收到反馈 → 理解意图 → 修订代码 → 再次提交 Reviewer,循环直到 pass 或达最大轮数。
demo.py 同时跑两种方案,并用同一位独立 Vision 评委给两者的最终 PPT 打分(保证质量可比):
脚本打印每次调用的 prompt token 序列、总量、以及上下文峰值(单次 prompt token,
决定是否撑爆上下文窗口)。页数越多、迭代越多,方案 B 的峰值相对方案 A 越夸张。
# 1) Python 依赖 pip install -r requirements.txt # 2) Slidev + 渲染依赖(Node)。首次约 1-2 分钟: npm install # - @slidev/cli:Slidev 命令行 # - playwright-chromium:slidev export --format png 的底层浏览器 # - typescript:Slidev 的 twoslash 代码高亮所需(否则 export 会 ERR_MODULE_NOT_FOUND) # 若 npm install 没有自动装好 chromium 浏览器二进制,运行: # npx playwright install chromium # 3) 配置 Key cp env.example .env # 填入 OPENAI_API_KEY(未配置时设 OPENROUTER_API_KEY 自动改走 OpenRouter) # 4) 跑完整流程(生成 → 渲染 → Vision 审查 → 迭代 → 对比) python demo.py
python demo.py --help)一次完整运行会做数十次 gpt-5.6-luna Vision 调用,较慢较贵。下列参数提供更快的路径,并允许更换论文、输出目录与模型:
| 参数 | 作用 |
|---|---|
--paper PATH |
输入论文的 Markdown 路径(默认 paper/sample_paper.md)。换成你自己的论文即可。 |
--out-dir DIR |
产物输出目录(默认 output/):各轮 slides.md/review.json/comparison_summary.json。渲染 PNG 始终在 slidev_workspace/exports/。 |
--text-model NAME |
Proposer / 单 Agent 文本模型,覆盖 TEXT_MODEL 环境变量(默认 gpt-5.6-luna)。 |
--vision-model NAME |
Reviewer / 独立评委看图模型(须支持图像),覆盖 VISION_MODEL 环境变量(默认 gpt-5.6-luna)。 |
--mode {both,dual,single} |
只跑一种方案(dual=提议者-审核者,single=单 Agent 自审),省一半时间/费用;both(默认)才做跨方案对比。 |
--max-rounds N |
每种方案的最大迭代轮数(默认 3)。--max-rounds 1 只出首版、不修订,是最快的真实 LLM 冒烟。 |
--dry-run |
离线走通提议者-审核者循环:真实渲染两版脚本化 slides.md(拥挤初稿→拆页修订稿),用确定性启发式规则(按每页文字量判定,非 Vision LLM)扮演 Reviewer,完整展示“生成→渲染→审查→修订”闭环。不调用任何 LLM、无需 API Key。 |
--smoke |
只验证 Slidev 渲染链路(渲染一个两页 deck),不调用任何 LLM、无需 API Key。最快的“没搞坏渲染”自检。 |
python demo.py --smoke # 不花钱,验证 Node/Slidev/chromium 可用 python demo.py --dry-run # 不花钱,离线看清提议者-审核者闭环(真实渲染) python demo.py --mode dual --max-rounds 1 # 一次真实 LLM 冒烟(需 API Key) python demo.py --paper my_paper.md --out-dir run_my # 换论文、换输出目录
--dry-run里的两版slides.md是脚本化的(不是 LLM 生成),Reviewer 也只是按字符数判定拥挤的启发式规则、并非 Vision LLM——它只用来在没有 API Key 时把闭环结构跑通、产出真实渲染的 PNG。要看 gpt-5.6-luna 真的看像素审查,请用python demo.py(需OPENAI_API_KEY)。一次离线 dry-run 的真实结果:初稿 4 页(第 2/3/4 页被判 high 级 overcrowded、score=55、pass=False)→ 拆页修订稿 18 页(score=100、pass=True),渲染 PNG 见slidev_workspace/exports/dryrun_round*/。
| 文件 | 作用 |
|---|---|
demo.py |
主流程:跑两种方案、独立评委打分、打印 token 对比 |
agents.py |
Proposer / Reviewer / SelfReviewAgent 三个 Agent + TokenMeter 计量 |
renderer.py |
调 slidev export --format png 把 slides.md 渲染成逐页 PNG |
make_figures.py |
用 matplotlib 从论文数字复现 2 张图表,放进 Slidev public/ |
paper/sample_paper.md |
精简论文(FlashAttention,含标题/章节/表格/结果) |
package.json |
Slidev 与渲染依赖 |
output/ |
运行产物:各轮 slides.md、review.json、comparison_summary.json |
slidev_workspace/exports/ |
各轮渲染出的 PNG(dual_round1/、single_round1/ …) |
一次完整运行后,output/ 与 slidev_workspace/exports/ 下的真实产物(节选):
output/ ├── dual_round1_slides.md # 双 Agent 第 1 版 slidev 源码(首版故意很挤) ├── dual_round1_review.json # Reviewer 对第 1 版的结构化建议 JSON ├── dual_round2_slides.md # 据反馈修订后的第 2 版 ├── dual_round2_review.json ├── dual_round3_slides.md ├── single_round1_slides.md # 单 Agent 自审各版 ├── single_round2_slides.md ├── single_round3_slides.md └── comparison_summary.json # 两方案质量分 + token 消耗汇总 slidev_workspace/exports/ ├── dual_round1/1.png … 5.png # 首版渲染:段落太长、图表底部超出页面 ├── dual_round2/1.png … 8.png # 修订版:拆页后每页 8 张更干净 └── single_round1/1.png … # 单 Agent 各版渲染
说明:Slidev 的 PNG 导出是逐页一张 PNG(
1.png、2.png…),本实验不产出单一 PDF;
如需 PDF,可把renderer.py里的--format png改为--format pdf。comparison_summary.json里记录两方案的iteration_scores、final_quality与peak_context_prompt_tokens(上下文峰值),即书中的核心对比数据。
env.example)或命令行参数(优先级更高),代码无需改动。
OPENAI_API_KEY:密钥(必填其一;未配置时用 OPENROUTER_API_KEY 兜底,自动改走 OpenRouter)。OPENAI_BASE_URL:指向任何兼容 OpenAI 协议的端点(自建网关 / 其它供应商)。TEXT_MODEL / --text-model:Proposer / 单 Agent 文本部分用的模型(默认 gpt-5.6-luna)。VISION_MODEL / --vision-model:Reviewer / 独立评委看图用的模型,必须支持图像输入(默认 gpt-5.6-luna)。--paper PATH 指定论文、--out-dir DIR 指定产物目录(无需改代码);paper/sample_paper.md(保留 Markdown 章节结构即可)。若新论文有自己的make_figures.py 里的画图函数并更新 generate_all() 返回的 {文件名: 描述},node_modules/,其中包含@slidev/cli、playwright-chromium(slidev export --format png 的底层浏览器)、typescriptnode_modules/ 缺失或损坏,在本目录执行 npm install 重装;npx playwright install chromium。装好后先 python demo.py --smoke为了稳定复现"渲染 → 发现问题 → 修订"的闭环,agents.py 里让 Proposer/单 Agent 的
首版先把整篇论文塞进约 4 页、成段贴原文(一种常见的"先把内容倒进去"的初稿写法)。
这会产生真实的文字溢出与图表被裁切(见 slidev_workspace/exports/dual_round1/2.png:
段落太长、图表底部超出页面)。Reviewer 的问题都是视觉模型(默认 gpt-5.6-luna)看真实像素得出的,修订也是
真实的——不是预设脚本。若把首版指令改成"直接生成 8-12 页精简版",视觉模型往往一版就过关,
反而看不到迭代过程。一次真实运行的结果(会有随机波动):
双 Agent:round1 score=85 pass=False(4 个 medium:p2/p3/p4 overcrowded、p2 image_size) → Proposer 拆页精简 → round2 score=95 pass=True(+10 改善) 上下文峰值:双 Agent = 9308 tok,单 Agent 自审 = 14179 tok(单 Agent 图片累积:1640→8069→14179)
make_figures.py 从论文数字程序化复现,temperatureslidev export 依赖 playwright-chromium;无网络/无法装 chromium 的