OK,OK,大家好,欢迎大家来到大鹏 AI 教育,我是张大鹏。
我平时没少跟 PPT 打交道,开直播要做课件,上课要做讲义,一套幻灯片改到半夜是常有的事。所以当 AI 能"一句话生成一整套 PPT"的时候,我是真兴奋。这是我RuyiCourse 项目实战课系列的第一篇,我会带你从一个真实的开源项目出发,先把它的架构拆到骨头,再讲清楚一件我最近一直在琢磨的事:怎么把一个"AI 辅助"的产品,改造成一个"纯 AI 驱动"的产品。
这篇不讲玄学,每一个论断我都对着源码一行行核对过。
一、我为什么挑中了 Presenton
市面上做 AI 生成 PPT 的产品不少,Gamma、Beautiful.ai、Decktopus 都很能打,但它们有一个共同的问题:闭源、SaaS、把你的数据和模型选择权一起锁死。
Presenton 是这条赛道里少见的开源选手,Apache 2.0 协议,我把它的几个核心卖点列一下,你就知道我为什么看上它了:
- 模型无关(Model-Agnostic):OpenAI、Anthropic、Google、DeepSeek、Azure、Bedrock、Vertex、OpenRouter、Fireworks、Together、Cerebras,以及本地的 Ollama / LM Studio,十几个 provider 随便换,甚至能混着用。
- 本地优先、隐私优先:可以纯本地跑(配合 Ollama),数据不出内网,适合气隙环境。
- 三种形态一套代码:Docker 自托管、Electron 桌面应用、浏览器云端,全都是同一套代码。
- 自带生成 API 和 MCP Server:不只是个网页,它本身就是一个可以被程序调用的演示文稿生成引擎。
对我来说,最关键的不是"它现在能干什么",而是它的架构天生就为 AI 驱动留好了接口。这一点,拆完代码你就懂了。
二、把它的架构拆到骨头
Presenton 是一个标准的多进程、前后端分离架构。顶层就三块:
RuyiCourse/
├── servers/
│ ├── fastapi/ # Python 后端:真正的 PPT 生成引擎
│ └── nextjs/ # Next.js 前端:UI、编辑器、模板渲染
├── electron/ # 桌面外壳:把上面两个进程打包成原生 App
└── docker-compose.yml
我一个一个讲。
2.1 后端 FastAPI:生成引擎在这里
后端是 Python + FastAPI + SQLModel(默认 SQLite,可换 PostgreSQL,用 Alembic 做迁移)。核心生成接口是:
POST /api/v1/ppt/presentation/generate
我顺着 servers/fastapi/utils/llm_calls/ 这个目录把整条生成流水线摸了一遍,它的精妙之处在于:生成不是"一句话变一个 PPT"的黑盒,而是被拆成了几个清晰、各自独立、各自可控的 LLM 调用阶段。看目录里的文件名你就能猜到流程:
generate_presentation_outlines.py # ① 内容 → 大纲(标题 + 每页要点)
generate_presentation_structure.py # ② 大纲 → 结构(每页该用哪种布局)
generate_slide_content.py # ③ 结构 → 每页的结构化内容
edit_slide.py / edit_slide_html.py # ④ 单页编辑
generate_web_search_query.py # 联网搜索增强(可选)
端到端的数据流是这样的:
content(你的输入)
└─① generate_ppt_outline() → 大纲 JSON:{ title, slides:[{content}] }
└─② generate_presentation_structure() → 给每页分配布局类型
└─③ get_slide_content_from_type_and_outline() → 按布局 schema 填充内容
└─④ process_slide + 图像/图标资源(并发抓取/生成)
└─⑤ 存库(presentations / slides 表)
└─⑥ export_presentation() → PPTX / PDF
这里有两个工程细节我很欣赏:第三步生成每页内容时是分批并发(asyncio.gather)的,不是一页一页串着等;联网搜索做成了可插拔的(SearXNG / Tavily / Exa 等)。
2.2 模型无关,是怎么做到的
很多人以为"支持多个模型"就是写一堆 if-else。Presenton 的做法干净得多,值得学。
抽象分三层(自上而下):
业务逻辑(endpoints / llm_calls)
↓ 只管"我要生成大纲",不管用的是谁家模型
llmai 统一客户端(from llmai import get_client)
↓
ClientConfig 工厂(utils/llm_config.py)
↓ 用 match-case 按 LLM 环境变量造出对应 provider 的配置
具体 provider(OpenAI / Anthropic / Ollama …)
关键就在 utils/llm_config.py 里的一个工厂函数,它读 LLM 这个环境变量,match 出对应的 ClientConfig:
def get_llm_config() -> ClientConfig:
llm_provider = get_llm_provider() # 读环境变量 LLM
match llm_provider:
case LLMProvider.OPENAI: return OpenAIClientConfig(api_key=...)
case LLMProvider.ANTHROPIC: return AnthropicClientConfig(api_key=...)
case LLMProvider.OLLAMA: return ... # 十几个 provider
业务代码只跟 llmai 这个统一抽象层打交道,换模型 = 改一个环境变量,零代码改动。所有可配置项都收敛在 utils/get_env.py 里。这套设计意味着我后面要接任何新模型(包括国产模型),成本极低。
2.3 前端 + 模板系统:整个架构的"七寸"
前端是 Next.js 16 + React 19 + Tailwind 3。但真正让我眼前一亮的,是它的模板系统。
模板放在 servers/nextjs/app/presentation-templates/ 下,每个主题一个文件夹(modern、pitch-deck、Education、Code、neo-modern……)。我打开 modern/ 看,里面是一堆按布局命名的 React 组件:
modern/
├── IntroSlideLayout.tsx
├── BulletsWithIconsDescriptionGrid.tsx
├── ChartOrTableWithDescription.tsx
├── ImageAndDescriptionLayout.tsx
├── TableOfContentsLayout.tsx
└── ...
随便翻开一个 .tsx,它的结构是这样的,这是整篇文章最重要的一段代码,请你盯着看:
import * as z from "zod";
import { IconSchema } from "../defaultSchemes";
export const layoutId = "bullet-with-icons-description-grid";
export const layoutName = "Bullet With Icons Description Grid";
export const layoutDescription = "……这个布局适用于……"; // 给 AI 看的说明
// 用 Zod 定义这一页"能装什么内容",带长度约束和字段说明
const Schema = z.object({
title: z.string().min(3).max(25).meta({ description: "本页主标题" }),
items: z.array(z.object({
title: z.string().min(3).max(30).meta({ description: "条目标题" }),
description: z.string().min(5).max(70).meta({ description: "条目描述" }),
icon: IconSchema.optional(),
})),
});
export { Schema };
export type SlideData = z.infer<typeof Schema>;
看懂这段,你就看懂了整个产品的设计哲学:
设计(布局、样式)和内容(文字、数据)被一个带类型约束的 Zod Schema 干净地解耦。
- 布局组件负责"长什么样",这是人类设计师/前端的活儿;
- Schema 负责"能装什么、装多少",
min(3).max(25) 这种约束,本质上是在给 AI 划边界,防止它生成一个塞不进版面的超长标题; .meta({ description }) 和 layoutDescription,本质上就是写给模型看的提示词;- AI 要做的,只是产出一段符合这个 Schema 的 JSON,React 组件拿到就能渲染。
这就是所谓的结构化生成(Structured Generation)。它把"AI 写 PPT"这个模糊任务,变成了"AI 填一张有严格类型校验的表单",可控、可校验、可重试。这一点,是它区别于"让大模型直接吐 HTML"那类玩具项目的根本。
2.4 导出与桌面化
- 导出:
services/export_task_service.py 用 Puppeteer 起一个无头 Chromium,把前端渲染好的幻灯片页面截成图,再组装成 PPTX / PDF。所以导出结果和你在编辑器里看到的像素级一致,因为本来就是同一套 React 组件渲染的。 - 桌面化:
electron/ 的主进程把 FastAPI 和 Next.js 作为两个子进程拉起来,再用一个 BrowserWindow 加载本地前端。它通过环境变量把 LLM 凭证、数据库路径、PRESENTON_ELECTRON=true 这些注入进去。 - 还自带一个 MCP Server(
servers/fastapi/mcp_server.py),把生成 API 暴露成 Model Context Protocol 工具,这个细节,是我下面要重点讲的改造支点之一。
三、它现在是"AI 辅助",还不是"AI 驱动"
拆完架构,我得说句实话:Presenton 现在的形态,本质上是一个"AI 辅助"的工具,而不是"AI 驱动"的系统。
区别在哪?现在的主流程是这样的:
人来到网页 → 人输入主题 → AI 生成一版 → 人进编辑器逐页改 → 人点导出
AI 在这里是一个"强力的初稿生成器",但方向盘始终在人手里:是人在判断好不好、是人在决定改哪里、是人在点下一步。整个系统的中心,是那个给人用的图形编辑器。
拿我上课打个比方:现在的 AI 就像一个很能干的助教,能飞快帮我把这节课的讲义初稿打出来,可哪一页讲得对不对、要不要推翻重做,还得我一页页过。它确实帮我省了体力,但这堂课终归还是我在上,它没法替我把控。
而我想要的"纯 AI 驱动",是把这个流程改成:
人给一个目标和素材 → Agent 自主完成 生成→自检→修订→定稿 的闭环 → 人只在最后验收
中心从"给人用的编辑器"变成"Agent 的自主工作循环",人退到目标设定者和验收者的位置。
好消息是:Presenton 的架构,恰恰为这种改造留好了所有接口。 下面是我的改造蓝图。
四、改造蓝图:六层,从 AI 辅助走向 AI 自主
我把改造拆成六层,从最容易落地的开始,越往后越接近"完全自主"。
第一层:把"API"升级成"Agent",让 MCP 成为主入口
Presenton 已经有了三样东西:一个 generate REST API、一套拆解清晰的 llm_calls 流水线、一个现成的 MCP Server。
改造第一步不是写新功能,而是换中心:把 MCP Server 从"一个附属接口"提升为"系统的主入口"。让一个外部 Agent(比如 Claude)通过 MCP 直接驱动整个流水线:
- 把
generate_outline、generate_structure、generate_slide_content、edit_slide 这些已经存在的阶段,分别暴露成独立的 MCP 工具; - Agent 不再"一把梭"调
generate,而是像人一样分步骤决策:先生成大纲 → 自己审一遍大纲合不合理 → 不行就重来 → 满意了再往下走。
这一步几乎不用改核心代码,只是把已有能力重新编排成工具集。但意义重大:系统的驱动权,从"人点按钮"变成了"Agent 调工具"。
第二层:自我批判闭环,这是"自主"的灵魂
这是整个改造里我认为最关键、也最有意思的一层。
现在的系统生成完就结束了,好不好全靠人看。要做到自主,就得给它装一个自我批判(Self-Critic)的反馈回路。而 Presenton 的导出管线,恰好为此提供了天然的"眼睛":
①生成幻灯片 → ②Puppeteer 渲染截图 → ③多模态模型"看图"打分
↑ │
└────────── ⑤按批评意见定点修订 ←── ④给出可执行的批评 ──┘
(循环,直到达标或到上限)
具体说:
- 它本来就用 Puppeteer 把每一页渲染成图,这正好可以喂给视觉模型;
- 让一个视觉模型扮演"挑剔的设计总监",按一套评分标准(信息密度够不够?标题有没有溢出?图文比例?配色对不对?)给每一页打分并指出具体问题;
- 把这些批评翻译成对某一页 Schema 字段的定向修改,调第一层的
edit_slide 工具去改; - 改完再渲染、再看、再打分,直到达标或触达迭代上限。
打个我最熟的比方:这套流程,就跟我每次直播完回看录像一模一样,我从"主播"切换成"观众",把自己那场直播重新看一遍,发现哪里卡壳了、哪张图没讲清楚,记下来下次改。区别只在于,这里"看回放、挑毛病、改稿"这三件事,全甩给模型自己循环去做了,不用我盯着。
注意,这个闭环之所以能成立,全靠第二节讲的那个 Zod Schema,因为内容是结构化的,"把标题改短一点"这种批评才能被精确地落到"改 title 字段、且必须满足 max(25)"这个可执行动作上。如果内容是一坨自由 HTML,这个闭环根本闭不起来。
做到这一层,人就可以从"逐页审查"里解放出来了。
第三层:让 AI 自己造模板,把护城河交给 AI
现在模板(那些 .tsx + Zod Schema)是人写的,这是产能瓶颈:布局种类有限,风格固定。
但既然模板就是"React 组件 + Zod Schema"这种高度结构化的代码,它就完全可以由 AI 生成:
- 给视觉模型一张设计稿截图(或者一个 Dribbble 链接),让它反向生成对应的
layoutId + Zod Schema + Tailwind 组件;
这样系统的"设计能力"也变成 AI 驱动、可自我扩展的,而不是被人写模板的速度卡住。这是把它从"工具"变成"会自己进化的系统"的关键一跃。
第四层:用 Mem0 做个性化记忆,让它越用越懂你
Presenton 集成了 Mem0(本地 Qdrant + SQLite,记忆按演示文稿隔离)。目前它主要记录单次生成的上下文。
往 AI 驱动改,就要把记忆从"单次"升级成"跨次的用户画像":
- 下次生成时,Agent 先检索"这个人一贯怎么做 PPT",把它作为生成约束。
人不需要每次都重复交代偏好,系统自己记得。自主的另一面,是个性化。
第五层:多模态、多源输入,喂给它一切
现在的输入以文字和文档为主(靠 LiteParse 解析)。要让 Agent 真正自主,就得让它能"自己找米下锅":
- 联网搜索(已有基础)从"可选增强"变成 Agent 的主动调研工具,它自己决定要不要去查、查什么。
输入越丰富,人需要喂的就越少,自主度就越高。
第六层:连开发和运维本身,也交给 AI
这一层有点"元",但我认为是这个系列课最想传递的东西。
前五层讲的是让产品变成 AI 驱动。但你有没有想过,改造这个项目的过程本身,也可以是纯 AI 驱动的?
说个真实的:我把这个 1.1GB 的仓库弄到本地、诊断 git 克隆为什么卡死、初始化版本库、建私有仓库、推送代码、再到现在这篇拆解文章,全程是我指挥 AI 完成的,我自己没敲几行命令。 这本身就是"纯 AI 驱动开发"的一次小演练。
把这个思路延伸到整个项目的演进:
- 需求 → Agent 读代码、给方案、改代码、自测、提 PR;
- 文档、发版说明、这种博客,都由 Agent 维护;
这才是我理解的"纯 AI 驱动项目"的完整含义,产品是 AI 驱动的,建造产品的过程也是 AI 驱动的。
五、写在最后
我挑 Presenton,不是因为它"是个 AI 生成 PPT 的工具",而是因为它在三个地方做对了,让它成为一个绝佳的"被改造对象":
- 流水线拆得够细,每个阶段都是独立、可单独驱动的 LLM 调用;
- 内容用 Schema 结构化,这是自我批判闭环能成立的地基;
- 导出即渲染,天然给了 AI 一双"能看自己作品"的眼睛。
有了这三点,从"AI 辅助"到"AI 自主"的改造,就不是推倒重来,而是顺着它的骨架往上长。
接下来的课程里,我会带着大家把上面这六层一层层真正落地,不是 PPT 上的架构图,是能跑起来、能自己改、能自我迭代的代码。
这是第一篇,先把地基讲清楚。我们下一篇见。
本文所有架构论断均基于 Presenton 源码逐一核对(FastAPI 后端、Next.js + Zod Schema 模板、Electron 外壳、llmai 抽象层、Puppeteer 导出、MCP Server、Mem0 记忆)。源码快照来自项目 main 分支。