简介面向产品经理、教师、运营人员及需要快速产出专业演示文稿的开发者这份可运行源码完整示范了“需求描述→大模型生成Markdown大纲→Kimi/通义千问/Coze一键转换并美化PPT”的自动化路径。压缩包共3个文件包含HTML页面、inscode项目配置和gitignore规则整体仅6KB轻量精简便于快速下载、查看页面逻辑和本地调试。目前已有241人学习下载。借助其中的核心代码与项目结构读者可以理解大模型提示词如何构造、Markdown的结构化表达如何与PPT转换工具衔接同时可直接替换主题内容生成自己的演示文稿源码中简洁的页面实现也为二次开发提供了便利。这套流程尤其适合需要批量产出课程讲义、产品方案、工作报告或汇报课件的用户能有效降低PPT制作的技术门槛与时间成本让零基础或入门学习者也能迅速上手。1. 大模型生成PPT指南一份能直接跑起来的源码包做方案、赶汇报、把会议纪要变成演示文稿这类需求几乎每周都会来一次。比起手动做版式让大模型生成PPT已经成了不少人的日常操作但要真正把“大模型返回的内容”变成“一个正常的 .pptx 文件”中间还隔着一层工程实现——模型吐出来的是文字不是文件。这份可运行源码包做的就是这件事你给一个大纲或一段主题文本它调用大模型接口解析返回结果在本地直接生成一份版式干净的PPT文件全程离线排版、可批量重复运行。适合产品经理、课程制作人和想二次开发的工程师新手跟着步骤能跑通熟手可以直接替换模型接入点。2. 两条技术路线对比为什么源码包选了 python-pptx 而不是 HTML 渲染2.1 python-pptx直接在 PPTX 层做排版输出最可控PPTX 格式本质上是一个 zip 压缩包内部全是 XML 描述的幻灯片结构、文本、样式和媒体对象。python-pptx 这个库的核心工作就是把这一层 XML 操作封装成 Python 对象让开发者不需要手写 XML 就能创建幻灯片、添加文本框、设定字体字号和坐标位置。我选择这条路线的理由很直接排版参数全部写死在代码里。比如标题字号 28 磅、正文 14 磅、左边距 0.8 英寸、顶部留白 0.5 英寸这些数值在代码中一目了然生成的每一页结构完全一致。对于需要批量产出、风格统一、不追求复杂动画的PPT场景这是最稳定的方案。另一个隐性优点是离线能力——调用大模型只需要一次网络请求拿到文本之后所有排版动作都在本地完成不依赖任何在线渲染服务。这套源码包在这个环节依赖三个核心库python-pptx 负责读写 PPTX 文件lxml 负责底层的 XML 处理通常作为 python-pptx 的依赖自动安装Pillow 用于处理封面图和配图。三个库加在一起安装体积不大Python 3.9 以上版本都能正常跑。2.2 HTML 浏览器转 PDF 的路线视觉上限高但环境坑多另一条常见路线是让大模型生成 HTML/CSS 幻灯片再用浏览器把 HTML 渲染成 PDF最后把 PDF 页面插入 PPT。这条路线在视觉表现力上确实更强CSS 可以做渐变、动画、复杂布局很多在线 AI 幻灯片工具用的是这个思路。但这条路线在工程落地时有几个绕不开的麻烦。第一大模型生成的 HTML/CSS 质量不稳定偶尔会输出不闭合的标签或非标准的 CSS 属性同一个模板这次渲染正常、下次就可能错位。第二浏览器渲染到 PDF 依赖本机安装了 Chromium 或 Chrome还需要处理页面尺寸、分页截断、字体嵌入一堆问题。第三PDF 转 PPT 后所有内容变成图片文本不可编辑后期想改一个字都比重新生成还麻烦。对于想拿源码包离线批量跑的人这套链路的环境差异太大了。一份源码如果需要在不同电脑上复现应该尽量降低对系统环境的依赖而不是把结果押注在“刚好装了某个版本的 Chrome”上。2.3 选型结论离线、批量、可复现优先所以我选 python-pptx对比维度python-pptxHTML 渲染转 PDF/PPT输出格式直接生成 .pptx文本可编辑PDF 转图内容不可编辑定制粒度坐标、字号、字体精确控制依赖 CSS 模板粒度粗运行依赖只用 Python 库跨平台一致需要 Chromium 等浏览器环境批量速度本地排版毫秒级生成每个文件走浏览器渲染秒级学习成本熟悉面向对象调用即可需要同时懂 CSS 和渲染配置我是从“内容与表现分离”的角度做的决定大模型负责输出结构化的内容python-pptx 负责把内容映射到固定的版式上。模型输出不稳定但本地排版是稳定的两者一组合整体质量就可控了。如果你已经部署了本地大模型或者有免费的 API 额度源码包的调用层可以直接切换过去这个到第 3 章会说具体接入方式。3. 最小可运行流程从安装依赖到生成第一份 3 页 PPT3.1 目录结构与依赖清单拿到源码包之后先按下面的结构理解文件组织。源码包的核心逻辑是有意拆开的llm_client.py负责网络请求parser.py负责解析大模型返回的文本builder.py负责生成 PPTXconfig.py保存模型参数和排版参数。这样拆分的好处是每一部分都可以单独替换换模型不用碰排版代码调样式也不用重新测试网络层。gpt-ppt/ ├── config.py # 模型参数、API配置、排版常量 ├── llm_client.py # 调用大模型API支持OpenAI兼容接口 ├── parser.py # 把markdown解析成页面对象 ├── builder.py # python-pptx 排版引擎生成PPTX ├── run.py # 入口读入大纲 → 生成PPT ├── requirements.txt # 依赖清单 └── examples/ └── outline.txt # 示例大纲直接可以喂给run.py# requirements.txt python-pptx0.6.23 lxml4.9.0 Pillow9.5.0 requests2.31.0逻辑说明python-pptx版本锁在 0.6.23 是因为这个版本在 Office 和 WPS 双端的兼容性表现最好更新的 1.x 版本引入了一些对 XML 空间的调整个别老模板会出兼容问题。lxml不锁死版本只要大于 4.9.0 就能配合 python-pptx 工作。3.2 先写一个最简单的生成脚本先不纠结复杂的排版逻辑直接跑通最小链路。源码包的run.py核心流程就五步读入大纲文件把大纲拼接成提示词请求大模型拿到结构化 markdown解析成页面数组最后交给 builder 逐页生成 PPT。# run.py 核心逻辑源码包按此结构实现 import os from llm_client import LLMClient from parser import parse_markdown_to_pages from builder import build_ppt # 1. 从环境变量读取API配置避免把密钥写进代码 api_key os.environ.get(LLM_API_KEY, sk-xxxx) base_url os.environ.get(LLM_BASE_URL, https://api.openai.com/v1) # 2. 初始化客户端temperature 调成 0.3 保证输出稳定 client LLMClient(api_keyapi_key, base_urlbase_url, temperature0.3) # 3. 读取大纲拼接提示词 with open(examples/outline.txt, r, encodingutf-8) as f: outline f.read() prompt build_prompt(outline) # 4. 请求大模型返回markdown格式的幻灯片内容 markdown_text client.get_response(prompt) # 5. 解析并生成PPT pages parse_markdown_to_pages(markdown_text) build_ppt(pages, output_filedemo.pptx) print(已生成 demo.pptx共 {} 页.format(len(pages)))逻辑说明temperature0.3是关键参数数值越低模型输出越保守、越不容易自由发挥格式。生成 PPT 这种场景不需要创造力控制住格式比控制内容重要。max_tokens在源码包里不是写死的而是根据大纲长度动态计算估算逻辑是“每个页面约 120 字再加上 30% 的冗余”这部分在config.py里有完整实现。参数说明LLM_API_KEY和LLM_BASE_URL是环境变量不是代码里写死的字符串。这么做是让源码包可以直接对接市面上绝大多数 OpenAI 兼容接口——包括各种开源模型的代理服务、国内模型的 API、本地部署的模型框架只需要改BASE_URL就能切换。3.3 运行与验证打开文件数页数依赖装好之后在源码包根目录执行下面的命令跑通流程# 安装依赖建议在虚拟环境里执行 pip install -r requirements.txt # 设置API密钥 export LLM_API_KEY你的密钥 # 运行生成流程 python run.py跑完会在当前目录生成demo.pptx。建议养成一个习惯不用直接双击打开文件先写一个小检查脚本确认页数和文本内容都是完整的。因为双击打开有视觉惯性很容易忽略“漏了一页”这种问题。# check_ppt.py 验证脚本 from pptx import Presentation prs Presentation(demo.pptx) print(总页数, len(prs.slides)) for idx, slide in enumerate(prs.slides): texts [] for shape in slide.shapes: if shape.has_text_frame: texts.append(shape.text_frame.text.replace(\n, )) print(第{}页{}.format(idx 1, | .join(texts)[:80]))参数说明这个脚本用Presentation()直接打开生成的.pptx文件逐个检查每页的文本内容是否和预期一致。如果发现某页文本是空的说明解析环节出了问题就该去查第 4 章说的容错逻辑了。3.4 本地大模型或免费 API 的接入方式源码包默认走 OpenAI 兼容接口但很多人实际用的是本地部署的模型框架或免费额度的 API。接入方式很简单环境变量已经预留了位置# 以常见的本地模型框架为例只需要改 BASE_URL export LLM_BASE_URLhttp://localhost:11434/v1 export LLM_API_KEYollama # 或者对接兼容OpenAI协议的免费API export LLM_BASE_URLhttps://api.example-free-llm.com/v1 export LLM_API_KEY你的免费密钥 python run.py逻辑说明这类本地模型框架通常都会提供一个 OpenAI 兼容的 REST 接口路径一样是/v1/chat/completions。源码包里的llm_client.py直接使用requests库调用这个标准接口所以换模型不需要改代码逻辑。需要注意的一点是本地小模型的指令遵循能力普遍不如商用大模型提示词里的格式要求可能要写得再强硬一些——这点在下一章的提示词设计里会展开。4. 结构化输出与容错解析从大模型返回的 markdown 到页面对象4.1 提示词设计让模型返回固定结构大模型生成 PPT 最容易出的问题不是内容不好而是格式不听话。有的模型喜欢给每一页加标题层级有的喜欢用列表嵌套列表有的偶尔在结尾加一段“总结”。源码包解决这个问题的思路是在提示词里给出极严格的输出格式约束用pages、title、bullets三个字段定义每个页面。# prompt_template.py 中的核心提示词模板 SYSTEM_PROMPT 你是一个PPT大纲生成器。我会给你一份文档提纲请你生成PPT内容。 输出要求 1. 严格输出 JSON 对象禁止输出任何解释文字 2. 顶层包含 pages 数组每个元素是一个页面对象 3. 页面对象格式{title: 页面标题不超过20字, bullets: [要点1, 要点2, 要点3]} 4. bullets 列表长度 2-5 条每条不超过 40 字 5. 总共生成 {num_pages} 页 6. 内容必须直接反映提纲不要添加提纲中没有的推论 提纲如下 {outline} 逻辑说明第 3 条和第 5 条是核心。固定字段名是为了后续解析简单限制字数是防止模型自由发挥写出超长句子导致 PPT 页面放不下。{num_pages}是动态填充的源码包根据提纲的行数估算页数估算公式是提纲行数加 2——这个数字是经验值太少页面塞不下内容太多页面每页很空。参数说明bullets长度限制在 2-5 条是有讲究的。PPT 不是文档一页超过 5 个要点字号就必须缩到 12 磅以下投影出来根本看不清。如果模型非要生成 7、8 条源码包的解析器会做截断而不是报错只取前 5 条宁缺毋滥。4.2 解析函数剥围栏、拆页面、兜底截断大模型返回的数据经常有意外。最常见的是在 JSON 外面套了 markdown 代码围栏即返回内容里带json 和标记。源码包的parser.py里第一个动作就是剥掉围栏第二步才是解析 JSON如果 JSON 都解析失败再做一次简单的文本兜底。# parser.py 中的解析与容错逻辑 import json import re def parse_markdown_to_pages(raw_text): # 1. 去掉 markdown 代码围栏兼容 json 前缀 cleaned raw_text.strip() cleaned re.sub(r^(?:json)?\s*, , cleaned) cleaned re.sub(r\s*$, , cleaned) # 2. 尝试解析 JSON try: data json.loads(cleaned) return data[pages] except (json.JSONDecodeError, KeyError): # 3. JSON 解析失败时降级为按标题行切分 pages [] current_title None current_bullets [] for line in cleaned.splitlines(): if line.startswith(#): if current_title: pages.append({title: current_title, bullets: current_bullets}) current_title line.lstrip(#).strip() current_bullets [] elif line.strip(): current_bullets.append(line.strip()) if current_title: pages.append({title: current_title, bullets: current_bullets}) return pages逻辑说明这套双层解析设计是关键——优先走 JSON 严格解析失败后不直接报错而是降级到按 markdown 标题结构切分。这样即使换了一个指令遵循能力较弱的模型最多损失一些格式精度不会让整个生成流程中断。参数说明re.sub的两个正则表达式分别处理开头和结尾的围栏。^(?:json)?匹配以三个反引号开头且后面可带 json 字样的字符串结尾的\s*$匹配最后一行。注意正则里用了\s*这样即使模型在围栏后面留了空行也能正确剥掉。4.3 文本清洗把控制字符和空白杂讯清掉大模型返回的文本偶尔会夹带一些意想不到的字符比如零宽空格\u200b、不换行空格\xa0、表情符号还有全角/半角混用的空格。这些直接写进 PPT 里轻则排版对不齐重则在某些字体渲染下显示成乱码方块。源码包在解析之后加了一道清洗工序。# parser.py 中的文本清洗函数 def sanitize_text(text): if not text: return # 去除零宽字符和控制字符 text re.sub(r[\u200b\u200c\u200d\u2060], , text) text re.sub(r[\x00-\x1f\x7f], , text) # 把不间断空格替换为普通空格 text text.replace(\xa0, ) # 压缩连续空格 text re.sub(r {2,}, , text) return text.strip()逻辑说明清洗函数放在解析之后、传给排版引擎之前。\u200b到\u2060这一串是 Unicode 的零宽字符区间肉眼看不见但会影响文本拼接和字号计算\x00-\x1f是 ASCII 控制字符区间部分接口返回内容里会在换行处残留这些字符。源码包的builder.py在写入每个文本框前都会调用这个清洗函数。4.4 一次请求还是逐页请求两种策略的适用边界源码包默认是单次请求生成全部页面这对 10 页以内的 PPT 完全够用。但如果你要生成的是 30 页以上的长文档单次请求很容易触发max_tokens上限截断导致后半部分内容丢失。这种情况下源码包支持一种“逐页请求”模式把大纲按章节拆开每次只让模型生成 3-5 页然后拼接起来。我在实际使用中的经验是短汇报型 PPT 用单次请求速度快、成本低课程讲义、操作手册这种长文档用逐页请求稳定性和内容质量都更好。切换方式在源码包run.py里通过一个--batch 5参数控制意思是每批生成 5 页批与批之间自动拼合。5. 大模型生成 PPT 的避坑记录五条实测翻车与修复方案5.1 中文全部变成方块现象生成的 PPT 里中文全部显示为小方块英文正常。原因python-pptx 默认的字体样式继承自空白模板而空白模板里中文字体映射通常是空的。代码里只设置了font.name Arial中文字符无法用该字体渲染就回退成了默认字体——在部分系统上这个默认字体不支持中文。解决设置英文字体的同时必须通过 XML 操作设置中文字体。源码包在builder.py里封装了set_font函数每次设置字体时同时写入latin和eaEast Asian两个字体属性# builder.py 中的字体设置 from pptx.oxml.ns import qn def set_font(run, name微软雅黑, size14, boldFalse, color000000): run.font.name name # 设置西文字体 run.font.size Pt(size) run.font.bold bold rPr run._r.get_or_add_rPr() ea rPr.find(qn(a:ea)) if ea is None: ea rPr.makeelement(qn(a:ea), {}) rPr.append(ea) ea.set(typeface, name) # 设置中文字体逻辑说明a:latin负责西文字符a:ea负责东亚字符。只设置前者中文字符就会被系统当成“未知字体”处理。源码包默认字体是“微软雅黑”这是 Windows 和 Office 环境覆盖率最高的中文字体如果你的目标环境是 macOS可以改为“PingFang SC”。5.2 返回内容里夹带 json 围栏现象解析时报json.JSONDecodeError: Expecting value生成流程中断。原因一些模型在系统提示词里看到了“JSON”字样会在输出里自动加上 markdown 代码围栏这在人工对话里是正常的但在程序调用里就成了多余字符。解决parser.py的第一条正则已经做了围栏剥离。但源码包还加了一道保险——在llm_client.py的返回值处理里如果检测到围栏字符串会先做一次替换再交给解析器# llm_client.py 中的预处理 def _strip_fence_if_present(text): if text.startswith(): # 去掉第一行 json 和最后一行 lines text.splitlines() return \n.join(lines[1:-1]) return text参数说明这段代码只在文本以开头时触发不匹配正常 JSON 内容所以不会误伤。逻辑上比正则更严格适合处理围栏和 JSON 混排的情况。5.3 响应被截断PPT 少了后三页现象生成的 PPT 一共只有 7 页但提示词要求生成 10 页内容到“总结”部分明显中断。原因max_tokens设置得太小模型输出到一半就撞上了上限后半部分内容根本没有生成。大模型对“生成完再停止”这件事没有精确控制输出长度受 token 限制非常严格。解决源码包在config.py里根据页数动态计算max_tokens。经验公式每页平均 120 字约等于 180 个 token再加上 500 token 的富余量最终值是页数乘以 180 再加 500。如果要生成 30 页建议直接把--batch 5参数加进去逐页请求不要指望单次请求一次性输出全部内容。5.4 并发调用免费 API 被限流现象批量生成 20 个 PPT跑到第 7 个突然报错429 Too Many Requests。原因绝大多数免费额度的 API 都有每分钟请求数限制源码包默认不做并发控制连续调用必然触发限流。解决源码包在llm_client.py里内置了简单的重试机制——遇到 429 或 5xx 状态码时等待 3 秒重试最多重试 3 次。如果你用的是免费额度建议把这个等待时间改长到 5 秒免费接口的限流窗口通常比较短等 5 秒再试的通过率明显更高。5.5 同一份代码在 Office 和 WPS 里显示不一致现象Office 里排版正常WPS 里文本框位置偏移了 0.3 厘米或者反过来WPS 正常Office 字体被替换。原因两个软件对 PPTX 里没有显式声明的样式属性有不同的默认值。python-pptx 生成的文本框中如果没有设置行距和边距Office 默认自动行距WPS 可能按固定值处理字体的autofit属性未设置时两边对溢出文本的压缩策略也不一样。解决源码包的builder.py里所有文本框都显式设置了三个参数——margin_left、margin_right、margin_top全部写死行距设置为固定值 1.2。设置之后不要用自动换行时的“压缩字体”选项这个选项在两个软件的实现差异最大。提醒一句任何人给你说“跨端渲染是玄学”基本上是因为参数没写全把边距、行距、字体这三个维度全部显式指定后两边差异会缩小到肉眼不可见。6. 进阶玩法把布局改成配置驱动一套提示词适配多种 PPT 风格走到这一步源码包的基础流程已经能稳定出片了。接下来的进阶方向不是让大模型更“聪明”而是把版式决策权从代码里拿出来做成配置驱动。源码包在config.py里预留了一个LAYOUTS配置字典用不同的布局名称区分版本风格。# config.py 中的布局配置 LAYOUTS { clean: { title_font: 微软雅黑, title_size: 28, body_font: 微软雅黑, body_size: 14, left_margin: 0.8, top_margin: 0.5, bullet_spacing: 1.2, }, compact: { title_font: 微软雅黑, title_size: 24, body_font: 微软雅黑, body_size: 12, left_margin: 0.6, top_margin: 0.3, bullet_spacing: 1.0, }, }逻辑说明clean是默认版式适合汇报场景字号大、留白多、每页信息量小compact适合课程讲义和技术文档字号小、单位面积内能容纳更多内容。生成时通过命令行参数--layout compact切换builder.py在排版时只读取这个字典不关心具体数值——这保证了版式的调整不需要改动任何页面生成逻辑。我做完这个改造后最大的感受是大模型负责内容组织本地配置负责视觉呈现两者完全解耦之后换风格的成本就从“修改排版代码”降到了“改一个 JSON 字典”。以前总觉得让模型一句话同时把控内容和版式是本事后来发现模型在版式把控上基本靠不住同样的提示词换一个模型版本排出来的间距可能差两倍。从那以后我每次生成 PPT 都强制走一遍“读大纲 → 定 layout → 调 API → 跑自检脚本”的流程不再在提示词里让模型自由发挥样式。自检脚本就按第 3 章的例子改只验证页数和每页文本是否非空跑通了再打开看视觉细节。这个习惯帮我避开了至少 3 次“生成完了才发现缺页”的尴尬希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?