PRD
April 12, 2026 · View on GitHub
Product: A library-first, CLI-second tool for AI agents (and humans) to inspect, edit, and render pptx decks with aggressive transparency.
Status: Draft v0.1 Last updated: 2026-04-11
1. Problem statement
AI agents 越来越多地被要求修改 pptx 文件 —— 写 slide、改 speaker notes、更新 agenda、插入图表、给客户改 proposal。今天的做法有三种,每一种都不好:
做法 A:AI 让用户手动改 —— AI 输出修改建议,用户自己打开 PowerPoint 一个个改。这种方式不具备可扩展性,每次修改都需要人类操作,且人类会引入错误或遗漏。
做法 B:AI 用 pandoc 或类似工具从头生成 pptx —— 把整个 deck 写成 markdown,用 pandoc -o deck.pptx 转。这种方式丧失了已有 deck 的 layout 和视觉设计,生成出来的 deck 模板化、不能直接用于客户交付,也不支持 "在已有 deck 上做 surgical 修改"。
做法 C:AI 直接写 python-pptx 脚本 —— 每次都现场写一段 Python 代码操作 pptx。这是最接近我们想要的方式,但有三个问题:
- python-pptx 的 API 是 OO 风格的,对 AI 不够 shell-friendly;AI 每次都要先做 "shape 树遍历" 才能找到目标
- 没有视觉反馈 —— AI 改完后无法 "看一眼" slide 真实长什么样。这意味着 AI 在开环模式下工作:能执行修改,但无法验证结果。每次改动都需要人类打开 PowerPoint 看一眼,把截图发给 AI,AI 才能判断是否需要进一步调整。人类变成了不可或缺的"渲染后端",AI 无法自主闭环完成一个任务。
- 没有 aggressive transparency —— 同一段代码在不同 deck 上可能有完全不同的效果,难以预测
我们需要的不仅是"能改 pptx 的工具",而是让 AI 能自主完成"改 → 看 → 调"闭环的工具:保留已有 deck 的所有设计,支持精细 surgical 修改,改完能渲染成图片自我检查,不满足就继续调,直到结果正确再交付给人类。
2. Goals
2.1 Primary goals
- 让 AI 能可靠地修改已有 pptx deck,每次修改的结果可预测、可解释、可审计
- 提供 shell-friendly 的 CLI,覆盖 80% 的高频修改任务,AI 一行命令搞定
- 提供 Python library,让 AI 能写 30 行脚本完成复杂的组合操作(批量修改、循环、条件判断)
- 提供渲染能力,让 AI 能自主闭环工作: 改完 slide 后渲染成 PNG,AI 自己"看一眼"判断效果,不满意就继续调,满意再交付。没有渲染,AI 只能开环工作 —— 每次改动都需要人类打开 PowerPoint 截图反馈,AI 才能知道结果对不对
- 贯彻 aggressive transparency: 每一步的 before/after 可见,错误不被吞,操作可复现
2.2 Secondary goals
- 字体归一化: 把 Calibri / PingFang 等平台专有字体替换成跨平台字体(Inter + Noto Sans SC),让 LibreOffice 渲染接近 PowerPoint 的视觉效果
- 字体度量 overflow 检查: 不依赖渲染,用字体度量数学精确判断文字会不会溢出 shape
- 结构化 dump: 把 slide 的 shape 树 dump 成 JSON / Markdown,让 AI 能做离线审计
- Escape hatch: 对于 python-pptx API 没覆盖的 OOXML 操作,提供 raw-xml-patch 直接改 XML
3. Non-goals
下面这些明确不做,是为了让 scope 收敛:
- 不做从 markdown 到 deck 的一键生成 —— 如果 AI 要从 md 生成 deck,让它自己写 loop 调库函数,不提供
generate-deck-from-md这种黑盒命令 - 不做 pixel-perfect 保真渲染 —— LibreOffice 的渲染和 PowerPoint 有差异(字体 fallback、阴影算法),render 输出是 "近似的 reality check",不是 "金标准"
- 不做复杂视觉效果的重建 —— SmartArt、Chart、3D effect、animation / transition、WordArt 非线性变形、OMML 公式都不支持。碰到这些告诉用户直接手动在 PowerPoint 改
- 不做 Keynote / Google Slides 格式 —— 只处理 pptx(OOXML)。keynote / gslides 导出的 pptx 可以读,但 roundtrip 质量依赖导出器
- 不做 pptx → md 的 "完美" 反向转换 ——
to-md只提供粗略 structured text 导出,供 AI 做快速内容扫描,不期望可以从 md 还原出原 deck - 不做网络调用 —— 所有操作都在本地文件上进行,不上传、不调外部 API
- 不做 GUI —— 只有 CLI 和 library
4. Users & use cases
4.1 Primary user: AI agent modifying an existing deck
Example: 用户要求 "把 slide 1 议程里 Part 4 的时长从 30 分钟改成 15 分钟,并把标题从 'Technical Demo · 实战案例现场演示' 改成 '两个实战案例 · 落地方式'"。
AI 应该能:
- 用
list-slides看到 deck 有多少 slide - 用
dump-slide 1 --format json看 slide 1 的所有 shape 和 text - 找到对应的两个 text shape 的 shape_id
- 用
set-text两次(或写一个 5 行 Python 脚本)完成修改 - 用
render-slide 1渲染成 PNG 检查视觉效果 - 完成
4.2 Secondary user: human developer writing a custom script
Example: 用户要在 deck 里批量插入 20 张 before/after 对比图。
用户(或 AI)应该能:
- 写一个 30 行 Python 脚本,
from pptx_skill import Deck - 打开 deck, loop 20 次调用
deck.add_slide(...)和slide.add_picture(...) - 保存
4.3 Tertiary user: human auditing AI's changes
Example: 用户给 AI 一个 deck 让它改,改完想看看具体改了什么。
用户应该能:
- 通过 diff
.bak和当前版本的 JSON dump 看到结构化 diff - 通过
render-slide的前后 PNG 做视觉对比 - 不需要安装 PowerPoint 就能审计
5. Success criteria
5.1 Functional 验收标准
- 可以用 CLI 在一分钟内修改一个已知 deck 的 slide 议程字段,且修改后的 deck 在 PowerPoint 里打开视觉上和手动修改没有明显差异
- 可以把 deck 的任一 slide 渲染成 PNG,AI 能从 PNG 中识别 slide 的 layout、主要文字、整体结构(即使字体 fallback 导致细节偏差)
- 可以通过 Python 脚本插入新 slide 并复制已有 slide 的 layout,新 slide 放在指定位置,不破坏其他 slide
- 可以 dump 任一 slide 成 JSON 并恢复到原文件的 shape 结构(roundtrip test)
5.2 Transparency 验收标准
- 每一个写操作在执行前后都打印 before / after 状态到 stderr
- 没有任何命令会 silently 修改 deck —— 修改前 stderr 宣告 "will modify X",修改后 stderr 打印 "saved"
- 错误信息保留原始细节 —— 如果 libreoffice 渲染失败,stderr 直接显示 soffice 的原始错误输出,不包装成 "rendering failed" 之类的笼统信息
- 可以用
--dry-run看每个写操作会改什么,但不实际写盘
5.3 Library 验收标准
- library 的每一个 public 函数都有对应的 CLI subcommand(1:1 映射)
- library 的调用可以在 Python REPL 里手工跑,不需要 CLI 做任何前置配置
- library 的所有函数都是 pure 函数或明确的 in-place 修改,没有隐藏 state 或全局副作用
5.4 Testability 验收标准
- 所有 write 操作都有 roundtrip test —— 改完之后 dump,和期望结构对比
- render 操作有 smoke test —— 能生成一个非零大小的 PNG,内容不空白(纯用 libreoffice,不做像素比对)
- 字体度量 heuristic 有 unit test —— 构造一段已知宽度的文字和 shape,验证 overflow 判断符合预期
- 所有测试在没有 LibreOffice 的环境下默认 pass(渲染测试走 pytest marker 跳过)
6. Feature list
按优先级排序。Phase 1 (MVP) 包括所有 P0 和 P1 功能,Phase 2 再考虑 P2。
Phase 1 / P0 · 基础读写
| 功能 | CLI 命令 | Library API | 说明 |
|---|---|---|---|
| 打开 deck 并验证可读 | doctor <deck> | Deck.open(path) | 健康检查,报告 slide 数、layout、依赖状态 |
| 列出 slide 概要 | list-slides <deck> | deck.slides | 每张 slide 的索引、layout 名、shape 数 |
| 列出某 slide 的 shape | list-shapes <deck> <slide_idx> | slide.shapes | shape 的 index、name、type、位置、大小、文字摘要 |
| Dump slide 完整结构 | dump-slide <deck> <slide_idx> --format json|md | slide.to_dict() / slide.to_markdown() | 给 AI 或人看的完整 structured 输出 |
| Dump 整个 deck | dump-deck <deck> --format json|md | deck.to_dict() | 一次性 dump 所有 slide |
| 读 shape 的文字 | get-text <deck> <slide_idx> --shape <id> | shape.text | 支持按 shape id / shape name 定位 |
| 读 speaker notes | get-notes <deck> <slide_idx> | slide.notes | |
| 写 shape 的文字 | set-text <deck> <slide_idx> --shape <id> --text "..." | shape.set_text(...) | 打印 before/after,单个 run 或整个 text frame |
| 写 speaker notes | set-notes <deck> <slide_idx> --text "..." | slide.set_notes(...) | 覆盖整个 notes |
| 保存 deck | (自动 in-place,或 --out) | deck.save(path) | 默认 in-place 保存,加 --out 另存 |
Phase 1 / P0 · 基础编辑
| 功能 | CLI 命令 | Library API | 说明 |
|---|---|---|---|
| 改 shape 的位置 | set-position <deck> <slide_idx> --shape <id> --left 1.5in --top 2in | shape.move(...) | 支持 in / cm / pt / px 单位 |
| 改 shape 的大小 | set-size <deck> <slide_idx> --shape <id> --width 4in --height 2in | shape.resize(...) | |
| 改 shape 的填充色 | set-fill <deck> <slide_idx> --shape <id> --color "#DC2626" | shape.set_fill(...) | 纯色 only,渐变走 raw-xml-patch |
| 改 text run 的字体属性 | set-font <deck> <slide_idx> --shape <id> --run <i> --name Inter --size 14 --bold --color "#FFFFFF" | run.set_font(...) |
Phase 1 / P0 · 渲染(AI 闭环的基础设施)
渲染是让 AI 从"能改"变成"能改完自己检查"的关键能力。没有渲染,AI 是开环的:改了 slide,不知道效果,只能等人类反馈。有了渲染,AI 可以自主完成 改 → 渲染 → 看图 → 调整 → 再渲染 的闭环,直到结果满意再交付。
| 功能 | CLI 命令 | Library API | 说明 |
|---|---|---|---|
| 渲染 slide 成 PNG | render-slide <deck> <slide_idx> --out /tmp/slide.png | slide.render_png(...) | 基于 LibreOffice headless,warning 说明保真度限制 |
| 渲染整个 deck 成 PNG | render-deck <deck> --out-dir /tmp/slides/ | deck.render_png(...) | 一次性生成所有 slide |
Phase 1 / P1 · 字体与 overflow
| 功能 | CLI 命令 | Library API | 说明 |
|---|---|---|---|
| 字体归一化 | normalize-fonts <deck> --en Inter --zh "Noto Sans SC" | deck.normalize_fonts(...) | 把 Calibri 等平台字体替换成跨平台字体 |
| Overflow heuristic 检查 | check-overflow <deck> --slide <idx> | slide.check_overflow() | 用 fonttools 读字体度量算宽度,判断会不会溢出 |
Phase 1 / P1 · 添加 / 删除 slide
| 功能 | CLI 命令 | Library API | 说明 |
|---|---|---|---|
| 添加新 slide | add-slide <deck> --after <idx> [--clone-from <idx>] | deck.add_slide(...) | 可以克隆已有 slide 作为 template |
| 删除 slide | delete-slide <deck> --slide <idx> | deck.delete_slide(...) | 危险操作,要求 --yes 确认 |
| 移动 slide 位置 | move-slide <deck> --from <idx> --to <idx> | deck.move_slide(...) |
Phase 1 / P1 · 图片操作
| 功能 | CLI 命令 | Library API | 说明 |
|---|---|---|---|
| 添加图片 | add-picture <deck> <slide_idx> --file img.png --left 1in --top 2in [--width 4in] | slide.add_picture(...) | 如果只给 width / height 一个,按原图比例算另一个 |
| 替换图片 | replace-picture <deck> <slide_idx> --shape <id> --file new.png | picture.replace(...) |
Phase 1 / P2 · Escape hatch
| 功能 | CLI 命令 | Library API | 说明 |
|---|---|---|---|
| 直接操作 OOXML | raw-xml-patch <deck> <slide_idx> --xpath "..." --xml-fragment "..." | slide.raw_xml_patch(...) | 给 AI 突破 python-pptx API 边界的最后手段 |
Phase 2 / 未来
- 多 backend 渲染: 支持
--backend=msoffice走 Mac AppleScript 调用真实 PowerPoint - Diff 功能:
diff <old_deck> <new_deck>产生 structured diff - 模板系统:
add-slide --from-template my_template.pptx从外部模板加 slide - 批量操作:
apply-patch <deck> <patch.yaml>读一个 YAML 描述的修改列表
7. Aggressive transparency 在 PPT skill 上的具体体现
"Aggressive transparency" 是本项目的核心设计原则。它在这个 skill 上有五个具体体现:
7.1 Before / after 可见
任何 write 命令在 stdout / stderr 打印:
[pptx-skill] set-text slide=0 shape-id=15
before: "Technical Demo · 实战案例现场演示"
after: "两个实战案例 · 落地方式"
[pptx-skill] saved deck.pptx (18 slides, 2.3 MB)
每一个写操作都有这三个 line,绝不 silent。
7.2 可预测的操作范围
命令只做它 name 明确说的事情。set-text 只改目标 shape,不 auto-fit 其他 shape 让它们 "看起来更好";set-position 只改 left/top 两个数,不做 snap-to-grid 或 auto-align。
如果 AI 要做复合操作(比如 "改文字的同时自动 resize 到适合的宽度"),它要自己写 script 组合两个命令。skill 不替 AI 做 "聪明" 决策。
7.3 错误透传
任何底层错误都原样抛出,不被 wrap 成笼统信息:
$ pptx-skill render-slide deck.pptx 3
[pptx-skill] render-slide slide=3
soffice error: conversion failed for /tmp/deck.pptx with reason:
com.sun.star.task.ErrorCodeIOException: SfxBaseModel::impl_store <.../out.png> failed: 0x81a(Error Area:Io Class:Access Code:26)
不包装成 rendering failed, please try again。AI 看到原始错误能立刻定位到是 LibreOffice 的 IO 权限问题。
7.4 Dry-run 支持
每个写命令都支持 --dry-run flag:
$ pptx-skill set-text deck.pptx 0 --shape 15 --text "New" --dry-run
[pptx-skill] DRY RUN set-text slide=0 shape-id=15
would change: "Technical Demo · 实战案例现场演示"
to: "New"
(no write performed)
AI 可以先 dry-run 确认要做什么,再真的执行。
7.5 结构化输出选项
每个读命令都支持 --format json 输出机器可解析结果,便于 AI 把结果 pipe 给下一个命令或 load 进 Python:
$ pptx-skill list-shapes deck.pptx 0 --format json | jq '.[] | select(.type == "AUTO_SHAPE")'
同时 stderr 和 stdout 不混 —— 结构化输出走 stdout,进度信息和 warning 走 stderr。AI pipe 时永远不会被进度文字污染 stdout。
8. Design constraints
8.1 LibreOffice 是 optional,不是 required
没装 LibreOffice 的系统上,除了 render-slide / render-deck 外,所有命令都能正常工作。doctor 命令会明确报告 "LibreOffice not found, render commands will fail",但不阻止其他命令运行。
8.2 不要和 python-pptx 竞争 API
python-pptx 是一个成熟的库,有它自己的 OO API 风格。pptx-skill 的 library 不是 re-implementing 它,而是在它之上加一层 "AI-friendly" 的包装:
- 扁平化 shape 查找: 提供按 id / name / text 的直接查找,不需要遍历 slide.shapes
- 统一单位: 所有位置 / 大小参数接受 "1.5in" / "4cm" / "100pt" 这种字符串,内部转换成 EMU
- Dict-like dump: 把 OO 对象树序列化成 dict,让 JSON dump 变成一行
底层所有真正的操作都委托给 python-pptx,pptx-skill 不重写 pptx 读写逻辑。
8.3 中英文字体混排
v5 deck 的一个关键观察是 shape 里同时包含中文和英文,用的是同一个 font.name (Calibri)。PowerPoint 会对中文做字体 fallback(通常到 PingFang SC 或 Microsoft YaHei)。
pptx-skill 的 set-font 命令应该支持两个参数:
--name <font>设置西文字体--zh-name <font>单独设置东亚字体
底层走 OOXML 的 <a:rFont> + <a:ea typeface="..."> 分离机制。
8.4 保存是 in-place + .bak 备份
默认每次 save 自动保留一个 .bak 文件(上次的版本)。--no-backup 显式关闭。这是为了避免 AI 跑错命令后无法回滚。
只保留最近一次 .bak(不搞多版本 snapshot),避免目录污染。
9. Risk & mitigation
9.1 LibreOffice render 保真度不够
风险: AI 根据 render 的 PNG 判断 "这段文字放进去 overflow 了",但实际 PowerPoint 打开不 overflow,结果 AI 做了错误的压缩决策。
缓解:
- render 命令在 stderr 明确警告 "font fallback may differ from PowerPoint",降低对 render 的信任度
- 提供独立的
check-overflow命令基于字体度量而非 render,给 AI 另一个数据源 - 提供
normalize-fonts命令,鼓励把 deck 字体换成跨平台字体以减小渲染差异
9.2 python-pptx API 覆盖不全
风险: AI 想做某个 OOXML 级别的操作(比如改 slide background、加 animation),python-pptx 不支持。
缓解:
- 提供
raw-xml-patch作为 escape hatch —— AI 可以直接写 OOXML XML 片段 doctor命令报告当前 python-pptx 版本和已知的功能缺失- README 和 AGENTS.md 明确列出 "做不到的事",让 AI 不浪费时间尝试
9.3 跨平台 font 和 layout 差异
风险: 一个 deck 在 Mac 上改,Windows 上打开 layout 变了。
缓解:
- 推荐
normalize-fonts预处理步骤 - 在
doctor里报告 deck 使用的所有字体,标记 "platform-specific" 的那些 - AGENTS.md 里写明这是 known trade-off,不是 skill 的 bug
9.4 AI 误改导致 deck 损坏
风险: AI 跑错命令(比如 delete 错了 slide),deck 被破坏,没 git 备份。
缓解:
- 默认每次保存自动生成
.bak - 危险命令(
delete-slide)要求--yes显式确认 - 所有写命令支持
--dry-run,AI 可以先试跑 doctor命令能验证 deck 可读性,失败时给出原始错误
10. Open questions
下面几个问题留给 RFC 或实现阶段解决,目前 PRD 层面不做决定:
- Shape ID 的稳定性: python-pptx 的 shape 没有稳定的 "id"。我们要用
shape_id(OOXML 的<p:sp>/<p:nvSpPr>/<p:cNvPr id="...">)还是 index inslide.shapes?前者在 deck 被编辑过之后更稳定,后者更直观。倾向用 shape_id,fallback 到 index - Text 的粒度: text frame 有
paragraphs和runs两层。set-text命令改哪一层?整个 text frame?某个 paragraph?某个 run?倾向默认改整个 text frame(最简单),--paragraph/--run是 opt-in - CLI 的 argparse vs click: 两个都是标准选择。倾向 argparse(标准库,没有额外依赖)
- Error exit code 的语义: 成功 = 0,argparse 错误 = 2,其他?倾向 simple: 任何 library raise = 1,argparse 错误 = 2,其他 = 3
- Logging vs print: 走
logging模块还是直接 print 到 stderr?倾向 print 到 stderr,因为每个命令的输出格式是 contract,不是 "日志" - Unit 解析:
1.5in/4cm/100pt的解析用哪个库?倾向自己写一个简单的 regex 解析,不引入新依赖
11. Related work
- python-pptx: 我们的底层依赖。https://python-pptx.readthedocs.io/
- pandoc: 另一种 pptx 生成路径,但不支持 surgical editing
- LibreOffice headless: 渲染后端 https://wiki.openoffice.org/wiki/API/Tutorials/PDF_export
- Aspose / Spire: 商业 pptx 库,保真度高但收费
12. Timeline 估计
这是一个 skill,不是一个正式产品,没有严格 deadline。估计:
- Day 1: Scaffolding(目录结构、pyproject、AGENTS)、PRD、RFC
- Day 2: Core library(Deck / Slide / Shape 抽象、读操作、基本写操作)、CLI 框架
- Day 3: Render、font normalize、overflow check、tests
- Day 4: 用 skill 真正改一个真实 deck 作为 end-to-end 验证,修复暴露的问题
- Day 5: 写 README / AGENTS.md 文档
Day 4 是一个关键节点: 如果 skill 能够真的改真实 deck 并且结果对,这个 skill 就 "working",可以开始用了。
Appendix · 程序化生成 deck 的兼容性
用代码生成的 deck(所有 shape 都是 RECTANGLE auto shape 或 PICTURE,没有 SmartArt / Chart / 3D / animation)100% 可以用 python-pptx 读写。
最常见的坑是字体:如果所有文字用平台专有字体(如 Calibri),中文靠 PowerPoint 自己 fallback 到 PingFang SC(Mac)或 Microsoft YaHei(Windows),LibreOffice 渲染时会 fallback 到 Liberation Sans,导致字宽差异。这也是为什么 normalize-fonts 是 P1 feature。