跳到主内容
快讯直播
AI智模界
教程

AI 生成教学 PPT:从教案到成品的完整流程

一套课件的制作时间,大头往往不在"想讲什么",而在"把想讲的东西摆到页面上":调字号、对齐、配图、分页、统一版式。把其中机械的部分交给脚本和模型,教师只需要提供教学内容与最后校对,这是本教程要落地的目标。下面这套流程不依赖任何特定厂商的模型,换成网页版对话工具,把生成的 JSON 复制出来同样适用。

这套流程能做出什么

先说成果。走完下面的步骤,你会得到:

  • 一个可编辑的 .pptx 文件,不是图片拼贴,文字、表格、色块都能在 PowerPoint 或 WPS 里继续改;
  • 结构完整的课件骨架:封面、学习目标、情境导入、知识点分页、例题、课堂练习、小结、作业;
  • 每页都有备注区,可以放讲稿提示,放映时自己看得到、学生看不到;
  • 一套可复用的 JSON 数据格式,下次换一节课,只改输入不改脚本。

以初中物理《浮力》为例,输入一份两三百字的备课要点,输出的页序大致是:封面 → 学习目标 3 条 → 情境导入(为什么木头浮、铁块沉)→ 浮力的方向与施力物体 → 阿基米德原理 → 三种状态对比表 → 例题 2 道 → 课堂练习 3 题 → 小结与作业。

对于一节常规课,熟练之后从输入要点到拿到可编辑成品,通常能压进半小时以内;剩余时间留给内容校对和个性化调整。重复性排版工作的耗时下降更明显,因为版式由母版统一控制,脚本只负责填内容。

前置条件清单

开始前确认这几样东西就位:

1. Python 环境:本地能跑 python --version。版本以官方文档当前版本为准,3.x 系列即可。

2. python-pptx 库:用于读写 pptx 文件。安装命令见下。

3. 一份母版文件:自己学校或自己常用的 .pptx,留出空白版式即可。有统一模板,生成的成品才有辨识度。

4. 一个能稳定输出结构化文本的大模型入口:网页版、API 都行。要求它按 JSON 输出。

5. 素材来源:图片、图表建议自制或使用明确授权的素材,避免课堂展示时出现版权争议。

6. 一份待备课的教案要点:不需要写得很细,知识点、重难点、例题方向写清楚就够。

安装依赖:

```bash

pip install python-pptx

```

如果国内网络下载慢,可自行配置镜像源,具体地址以所用镜像站官方说明为准。

第一步:把备课要点写成结构化提示词

提示词的质量直接决定输出能不能用。核心是两件事:明确要求输出 JSON,明确每一页的字段含义。

可以直接套用下面这段,把方括号内容替换成自己的课:

```text

你是一位[初中物理]教师,请为一节[40]分钟的课设计课件大纲。

课题:[浮力]

学情:[学生已学过力的三要素和二力平衡,尚未接触阿基米德原理]

重难点:[重点:浮力的方向与阿基米德原理;难点:理解排开液体的重力]

可用例题方向:[弹簧测力计称重法测浮力、漂浮与悬浮的受力分析]

要求:

1. 只输出 JSON,不要任何解释文字,不要 Markdown 代码围栏。

2. 幻灯片总数控制在 16 到 22 页之间。

3. 每页正文条目不超过 5 条,每条不超过 30 个字,避免文字堆砌。

4. 数值、公式、史实必须准确;不确定的内容省略,不要编造。

5. JSON 结构严格遵循下面这个示例,字段名与层级不要改动:

{在此粘贴第二步的 JSON 示例}

```

把 JSON 示例贴在提示词里,是减少字段漂移的有效办法。模型看到具体的键名,比只看到文字描述更不容易跑偏。

第二步:固定 JSON 中间格式

脚本和模型之间的契约就是这份 JSON。字段定得越死,后面越省事。

```json

{

"meta": {

"subject": "初中物理",

"grade": "八年级",

"title": "浮力",

"periods": 1

},

"slides": [

{

"type": "cover",

"title": "浮力",

"subtitle": "初中物理 · 八年级下册"

},

{

"type": "objectives",

"title": "学习目标",

"items": [

"知道浮力的方向与施力物体",

"会用弹簧测力计测量浮力大小",

"能说出阿基米德原理的内容"

]

},

{

"type": "bullets",

"title": "浮力的方向",

"bullets": [

"浸在液体中的物体受到向上的托力",

"施力物体是液体",

"方向总是竖直向上"

],

"note": "可让学生用手压水中的空瓶感受浮力"

},

{

"type": "table",

"title": "三种状态的受力关系",

"headers": ["状态", "受力关系", "常见实例"],

"rows": [

["漂浮", "F浮 = G", "木块"],

["悬浮", "F浮 = G", "悬浮的鸡蛋"],

["下沉", "F浮 < G", "铁块"]

]

},

{

"type": "practice",

"title": "课堂练习",

"items": [

"一物体重 5 N,浸没在水中时弹簧测力计读数为 3 N,求浮力大小",

"同一物体分别浸没在水和酒精中,哪次受到的浮力大?为什么?"

]

},

{

"type": "summary",

"title": "小结",

"items": ["浮力方向竖直向上", "阿基米德原理", "浮沉条件"]

}

]

}

```

type 字段是脚本的分发依据,先定 cover、objectives、bullets、table、practice、summary 六种,覆盖大多数课型。后面想加"实验步骤""思维导图"页,再加一个分支就行。

第三步:准备母版并确认版式索引

打开自己常用的 pptx 文件,删掉正文页,保留母版和版式,另存为 template.pptx。脚本里通常用"空白"版式,然后手动加文本框,这样做的好处是排版完全由脚本控制,不受占位符位置限制。

版式索引在不同模板中不一样,不要凭记忆写死。先跑一段代码把索引打出来:

```python

from pptx import Presentation

prs = Presentation("template.pptx")

for i, layout in enumerate(prs.slide_layouts):

print(i, layout.name)

for i, master in enumerate(prs.slide_masters):

print("master", i, master.name)

```

输出里名字类似"空白""标题幻灯片"的那一项,就是你要用的索引。记下来填到下一步的代码里。

第四步:写生成脚本

新建 build_ppt.py,把 JSON 读进来,按 type 分发到不同的渲染函数。下面是一份可直接改用的骨架:

```python

import json

from pptx import Presentation

from pptx.util import Cm, Pt

from pptx.dml.color import RGBColor

from pptx.enum.text import PP_ALIGN, MSO_ANCHOR

from pptx.oxml.ns import qn

FONT = "微软雅黑"

DARK = (0x33, 0x33, 0x33)

ACCENT = (0x1F, 0x5C, 0xA8)

BLANK_LAYOUT_INDEX = 6 # 以第三步打印出的实际索引为准

def style_run(run, size, bold=False, color=DARK):

"""统一设置字号、颜色和中英文字体"""

run.font.size = Pt(size)

run.font.bold = bold

run.font.color.rgb = RGBColor(*color)

rPr = run._r.get_or_add_rPr()

font.name 只覆盖拉丁字母,中文需要额外写 a:ea

for tag in ("a:latin", "a:ea", "a:cs"):

for old in rPr.findall(qn(tag)):

rPr.remove(old)

el = rPr.makeelement(qn(tag), {"typeface": FONT})

rPr.append(el)

def add_paragraph(tf, text, size, bold=False, color=DARK, first=False):

p = tf.paragraphs[0] if first else tf.add_paragraph()

p.text = text

for run in p.runs:

style_run(run, size, bold, color)

return p

def add_title(slide, text, width):

box = slide.shapes.add_textbox(Cm(1.5), Cm(1.0), width, Cm(1.6))

tf = box.text_frame

tf.word_wrap = True

tf.vertical_anchor = MSO_ANCHOR.MIDDLE

add_paragraph(tf, text, 32, bold=True, color=ACCENT, first=True)

return box

def add_body(slide, items, width, top=Cm(3.4), size=20):

box = slide.shapes.add_textbox(Cm(1.5), top, width, Cm(11))

tf = box.text_frame

tf.word_wrap = True

for i, item in enumerate(items):

add_paragraph(tf, "· " + item, size, first=(i == 0))

tf.paragraphs[-1].space_after = Pt(10)

return box

def add_table(slide, headers, rows, width, top=Cm(3.4)):

shape = slide.shapes.add_table(

len(rows) + 1, len(headers), Cm(1.5), top, width, Cm(1.0) * (len(rows) + 1)

)

table = shape.table

for c, h in enumerate(headers):

cell = table.cell(0, c)

cell.text = h

for p in cell.text_frame.paragraphs:

for run in p.runs:

style_run(run, 18, bold=True)

for r, row in enumerate(rows, start=1):

for c, val in enumerate(row):

cell = table.cell(r, c)

cell.text = val

for p in cell.text_frame.paragraphs:

for run in p.runs:

style_run(run, 16)

return table

def build(json_path, template_path, out_path):

with open(json_path, encoding="utf-8") as f:

data = json.load(f)

prs = Presentation(template_path)

blank = prs.slide_layouts[BLANK_LAYOUT_INDEX]

body_width = prs.slide_width - Cm(3.0)

for s in data["slides"]:

slide = prs.slides.add_slide(blank)

t = s.get("type", "bullets")

if t == "cover":

box = slide.shapes.add_textbox(Cm(2.0), Cm(7.0), body_width, Cm(3.0))

tf = box.text_frame

tf.word_wrap = True

add_paragraph(tf, s.get("title", ""), 44, bold=True, color=ACCENT, first=True)

add_paragraph(tf, s.get("subtitle", ""), 20)

elif t == "table":

add_title(slide, s.get("title", ""), body_width)

add_table(slide, s["headers"], s["rows"], body_width)

else:

add_title(slide, s.get("title", ""), body_width)

items = s.get("items") or s.get("bullets") or []

add_body(slide, items, body_width)

备注区放讲稿提示

if s.get("note"):

slide.notes_slide.notes_text_frame.text = s["note"]

prs.save(out_path)

print("已生成:", out_path)

if __name__ == "__main__":

build("lesson.json", "template.pptx", "浮力.pptx")

```

运行:

```bash

python build_ppt.py

```

打开生成的 pptx,重点检查三处:文字有没有溢出文本框、表格列宽是否合理、中文字体是否按预期显示。

第五步:处理公式、图片和留白

课件里绕不开三类特殊内容,各有各的处理方式。

公式。python-pptx 直接写数学公式的能力有限,实践中有两种做法:一是用纯文本近似表达,例如 F浮 = ρ液 g V排,配合上下标不明显的场景够用;二是先用公式编辑器或绘图工具导出透明背景 PNG,再用 slide.shapes.add_picture() 贴进去。脚本里可以预留占位:

```python

from pptx.util import Cm

from pptx.enum.shapes import MSO_SHAPE

def add_image_slot(slide, top=Cm(4.0), left=Cm(14.0)):

shape = slide.shapes.add_shape(

MSO_SHAPE.ROUNDED_RECTANGLE, left, top, Cm(10.0), Cm(7.0)

)

shape.text_frame.text = "此处插入实验示意图"

for p in shape.text_frame.paragraphs:

for run in p.runs:

style_run(run, 14, color=(0x99, 0x99, 0x99))

return shape

```

表格。前面脚本里已经封装好。列数控制在 3 到 4 列,行数控制在 6 行以内,投影到教室后排才看得清。

留白。一页塞太多字是课件通病。在提示词里限定"每条不超过 30 字""每页不超过 5 条",比事后手动删要省力。

第六步:批量生成与导出

一学期几十节课,逐节手工点太慢。把每节课的 JSON 放在 lessons/ 目录下,用一个循环跑完:

```python

from pathlib import Path

from build_ppt import build

for jf in sorted(Path("lessons").glob("*.json")):

build(str(jf), "template.pptx", f"out/{jf.stem}.pptx")

```

需要交付 PDF 版本时,可以用 LibreOffice 命令行转换(具体参数以官方文档为准):

```bash

soffice --headless --convert-to pdf --outdir out out/浮力.pptx

```

如果本机没有安装 LibreOffice,也可以直接用 PowerPoint 或 WPS 的"导出为 PDF"功能。

备选路线:Markdown 直出幻灯片

有些老师更习惯写 Markdown。这条路线的工具链更轻,适合讲义型课件:

```bash

pandoc lesson.md -o lesson.pptx --reference-doc=template.pptx

```

--reference-doc 指定母版,pandoc 会继承其中的字体和配色。用 Marp 则更适合直接投屏:

```bash

npx @marp-team/marp-cli lesson.md --pdf -o lesson.pdf

```

两条路线的取舍:需要精细控制每页版式、要嵌表格和公式图片,用 python-pptx;内容以文字为主、追求写作速度,用 Markdown 工具链。

常见坑与排错

版式索引对不上,生成的页面版式错乱。 不同模板的 slide_layouts 顺序不同,务必按第三步把索引打印出来再写进脚本。

中文显示成宋体或出现方框。 python-pptx 的 run.font.name 只设置拉丁字体,中文字体要写到 a:ea 节点上,也就是脚本里 style_run 那段。另一条更省事的路径是在母版里就把中文字体设好,脚本只调字号。

生成的 JSON 带 Markdown 围栏或前后废话。 解析前做一次清洗:截取第一个 { 到最后一个 } 之间的内容再 json.loads。仍然失败就重试一次,并在提示词里强化"只输出 JSON"。

文字溢出文本框。 自动缩放只在 PowerPoint 打开文件时才重新计算,脚本阶段看不出来。稳妥做法是控制每页条数和单条字数,并把文本框宽度按幻灯片宽度减去页边距来算。生成后逐页翻一遍仍是必要步骤。

学科内容出错。 模型可能把单位写错、把历史年代记混。物理单位、化学方程式、数学符号、史实年代这几类内容必须人工过一遍,这一步不能省。

图片版权。 优先使用自制图表、拍摄照片或明确标注可商用的素材,来源不明的网络图片不要直接放进课堂课件。

学生隐私。 学生姓名、成绩、家庭信息不要粘贴到外部服务里。做分层练习时,用"基础组、提升组"这类抽象标签替代真实姓名。

保存失败。 目标 pptx 正在被 PowerPoint 打开时,脚本会报权限错误。关闭文件再跑一次即可。

放映时字体变了。 换电脑演示前,用"嵌入字体"或在演示机上确认字体可用,否则行距和换行会跟原稿不一样。

下一步建议

流程跑通之后,可以从这几个方向继续打磨:

1. 沉淀模板库。按学科和课型分别准备母版,新授课、复习课、实验课的版式需求差别很大。

2. 沉淀提示词库。把每种课型的提示词存成文本文件,每次只替换课题和学情两段,比每次重写省时间。

3. 积累 JSON 样例。把生成效果好的 JSON 留下来,下次作为提示词里的示例,输出质量会稳定不少。

4. 配套讲稿。备注区已经预留了位置,可以让模型顺带生成逐字稿和板书设计,讲稿与课件分开放,改起来互不影响。

5. 接入分层练习。同一份知识点,生成基础、提升两套练习题,课后按学情发放。

6. 版本管理。用 Git 或网盘保留每学期的 JSON 与 pptx,第二年只需要更新学情和例题,不必从头再来。

课件本身只是载体,真正的价值在于把教师从排版里释放出来,把时间还给教学设计和课堂互动。

AI 生成本文由 AI 基于公开信息自动生成,仅供参考。