dodo-agent

PPT 生成智能体

在前面的课程中,我们已经系统分析了当前市面上 主流的 PPT 生成智能体实现方式,并且重点拆解了 豆包 PPT 的生成流程,了解了一个成熟产品在需求澄清、内容规划、素材准备以及最终生成阶段的大致工作方式。 而从这一节课开始,我们将不…

TL;DR

在前面的课程中,我们已经系统分析了当前市面上 主流的 PPT 生成智能体实现方式,并且重点拆解了 豆包 PPT 的生成流程,了解了一个成熟产品在需求澄清、内容规划、素材准备以及最终生成阶段的大致工作方式。 而从这一节课开始,我们将不…

在前面的课程中,我们已经系统分析了当前市面上 主流的 PPT 生成智能体实现方式,并且重点拆解了 豆包 PPT 的生成流程,了解了一个成熟产品在需求澄清、内容规划、素材准备以及最终生成阶段的大致工作方式。 而从这一节课开始,我们将不再只是停留在需求分析层面,而是进入真正的工程实现阶段。 本节课的核心目标是: 实现一个完整的 PPT 生成智能体,并深入理解在工程系统中,如何构建一个稳定、可编辑、可扩展、并且支持断点恢复的 PPT 生成 Agent。 在这一节课中,我们将重点关注以下几个问题: - 一个 PPT 生成 Agent 的整体架构应该如何设计 - 为什么要采用 状态机 + 策略模式 的设计 - 如何实现 创建 PPT、修改 PPT、断点恢复 三种意图能力

PPTBuilderAgent 整体架构

在整个系统中,PPTBuilderAgent 是 PPT 生成智能体的主入口。所有来自用户的 PPT 生成请求,最终都会进入这个 Agent,由它统一调度后续的执行流程。 系统架构如下:

PPTBuilderAgent
        │
        │
        ├──
        │        │
        │        └── 判断用户意图
        │             CREATE
        │
        ├──
        │        │
        │        └── 根据状态选择策略
        │
        ├──
        │        │
        │        └── 管理任务生命周期
        │
        └── 各状态策略
                 │
                 ├──
                 ├──
                 ├──
                 ├──
                 ├──
                 ├──
                 ├──
                 └──

从职责上来看,PPTBuilderAgent 并不会直接去完成 PPT 的生成工作,而是更像一个 流程调度中心。它的主要任务是:解析用户意图、管理任务生命周期,并根据当前状态选择合适的策略来推进整个 PPT 的生成流程。 首先是解析用户意图。在真实的对话场景中,用户可能提出不同类型的需求,例如首次生成 PPT、对已有 PPT 进行修改,或者在任务中断后继续执行。因此 Agent 在接收到请求后,会先通过 IntentRecognizer 判断用户当前的真实意图,再决定系统应该进入哪一条执行路径,这一步本质上就是整个系统的入口路由逻辑。 其次是驱动 PPT 生成流程的状态流转。PPT 的生成并不是一次完成的,而是被拆分为多个阶段,例如需求分析、模板选择、大纲生成、内容设计以及最终渲染。PPTBuilderAgent 会根据当前任务所处的状态,通过 PptStateStrategyFactory 选择对应的策略执行,并在完成后推进到下一个阶段,从而形成一个完整的状态机执行流程。 第三个职责是管理任务生命周期。由于 PPT 生成通常会涉及多次模型调用、素材搜索以及文件渲染等操作,整个任务可能持续较长时间。为了避免同一会话出现多个任务同时运行的问题,系统引入 AgentTaskManager 来统一管理任务,例如任务注册、任务取消以及任务完成后的清理,从而保证系统在复杂场景下依然稳定运行。 基于以上设计,整个系统的核心思想可以概括为一句话: Agent = 状态机 + 策略模式 也就是说,PPT 的生成流程被抽象为一个状态机,每个阶段对应一个明确的状态;而每个状态具体的执行逻辑,则由对应的策略类完成。通过这种方式,可以将复杂流程拆分成多个独立模块,使系统结构更加清晰,同时也更容易扩展和维护。

执行流程

在整个 PPT 生成系统中,我们并没有把所有逻辑写在一个流程里一次性执行,而是将整个生成过程拆分为多个明确的阶段,每一个阶段对应一个状态。系统通过状态机的流转来逐步推进任务执行。 状态机定义如下:

public


}

整个任务的执行流程如下:

INIT
 ↓
REQUIREMENT
 ↓
SEARCH
 ↓
TEMPLATE
 ↓
OUTLINE
 ↓
SCHEMA
 ↓
RENDER
 ↓
SUCCESS

如果在执行过程中出现异常,则任务会进入失败状态:

任意状态
   ↓
FAILED

之所以采用这种阶段化的执行流程,主要是因为 PPT 的生成本质上是一个多步骤、多模型调用的复杂任务。例如需求分析、大纲生成、模板选择以及最终渲染,本身就属于不同类型的处理逻辑,如果全部耦合在一起,不仅代码复杂度会迅速增加,也很难进行维护和扩展。 通过将流程拆分为多个状态,每一个阶段只负责完成自己的一部分任务,系统结构会更加清晰。同时这种设计在工程上还有几个非常重要的优势。 首先是支持断点恢复。由于任务的状态会被持久化保存,如果系统中途出现异常或者服务重启,只需要读取当前状态,就可以从上一次执行的位置继续运行,而不需要重新生成整个 PPT。 其次是便于监控和排查问题。当任务执行失败时,可以很容易地定位到底是在哪一个阶段出现了问题,例如是在信息收集阶段失败,还是在渲染阶段失败,这对于线上系统的稳定性非常重要。 最后是方便扩展能力。如果未来需要增加新的处理步骤,例如自动生成图表、自动生成讲稿,或者增加素材处理阶段,只需要在状态机中新增一个状态,并接入对应的执行策略即可,而不需要重写整个流程。 因此,通过这种状态驱动的执行流程设计,我们不仅让 PPT 生成逻辑更加清晰,也为系统的稳定性、可维护性以及未来扩展能力打下了基础。

Agent 主入口

PPTBuilderAgent 的核心入口方法:

public

执行流程: 1. 检查是否已有任务在执行 2. 注册任务到 AgentTaskManager 3. 识别用户意图 4. 根据意图进入不同流程 核心代码逻辑:

public


            pptIntentRecognizer


}

意图识别设计

目前根据PPT的操作行为,预判用户的意图,支持三种运行模式: | 模式 | 说明 | | --- | --- | | CREATE_PPT | 新建 PPT | | MODIFY_PPT | 修改 PPT | | RESUME_PPT | 断点恢复 |

意图识别逻辑:

public


}

这样做的最大的好处就是:虽然 PPT 是一个 workflow,但是依然可以像 真实助手一样持续对话。 例如:

用户:生成一个 AI 技术分享 PPT
Agent:生成完成

用户:把最后一些的标题换成“谢谢观看”
Agent:修改 PPT

创建 PPT 执行流程

创建流程:

CREATE_PPT
  ↓
创建实例
  ↓
启动状态机

核心代码:

private


            pptInstService


}

第一步是 创建 PPT 任务实例。 系统会为当前会话创建一条 AiPptInst 记录,用于保存整个任务的执行状态,例如当前状态、用户需求、生成结果等信息。后续所有的执行流程,都会围绕这个实例展开。第二步是 启动状态机执行流程。 通过 PptStateStrategyFactory 调用 executeNextState,系统会根据当前状态选择对应的执行策略,并开始推进整个生成流程。 在创建完成之后,系统会自动进入第一个执行阶段。

策略模式

为了让每个阶段的逻辑保持独立,我为所有执行阶段定义了统一的策略接口:

public


}

这个接口定义了每一个策略必须实现的两个核心能力。 execute 执行当前阶段的核心逻辑,例如生成大纲、搜索资料或渲染 PPT。所有状态的具体业务逻辑都在这里实现。 getTargetStatus 定义当前阶段执行完成后要进入的下一个状态,从而驱动状态机继续向前推进。 通过这个接口,我们将每一个阶段的逻辑都封装成了一个独立的策略类,例如: - RequirementStrategy - SearchStrategy - TemplateStrategy - OutlineStrategy - SchemaStrategy - RenderStrategy 这样每个策略只需要关注自己的业务逻辑,而不需要关心整个流程的流转。

策略工厂

为了根据状态选择正确的策略,实现了一个 策略工厂。

public


        STRATEGY_MAP
        STRATEGY_MAP
        STRATEGY_MAP
        STRATEGY_MAP
        STRATEGY_MAP
        STRATEGY_MAP
        STRATEGY_MAP
        STRATEGY_MAP


}

策略工厂的作用非常简单: 根据当前状态,从 Map 中取出对应的策略并执行。 例如: - 当前状态是 REQUIREMENT → 执行 RequirementStrategy - 当前状态是 OUTLINE → 执行 OutlineStrategy - 当前状态是 RENDER → 执行 RenderStrategy 通过这种方式,整个系统就形成了一个非常清晰的执行结构:

状态机
   ↓
策略工厂
   ↓
具体策略执行

这种设计有几个非常明显的优势。 第一,状态和行为解耦。 状态机只负责流程控制,而具体逻辑由策略实现。 第二,扩展能力强。 如果未来需要增加新的阶段,例如自动生成图表,只需要新增一个状态和策略即可。 第三,代码结构清晰。 每个策略文件都只负责一个阶段的逻辑,不会都融合在一起。

需求澄清(RequirementStrategy)

创建流程启动后,第一个执行阶段就是 需求澄清。 在真实场景中,用户给出的需求往往是比较模糊的,例如:

帮我做一个 AI 行业趋势的 PPT

但一个完整的 PPT 其实还需要很多信息,例如: - 目标受众 - 页数 - 样式风格 因此在 RequirementStrategy 中,系统会调用大模型对用户需求进行结构化整理,并生成一个完整的 PPT 需求描述。 这一阶段的核心目标是: 将用户的自然语言需求,转化为结构化的 PPT 生成需求。

/**


public
        ## 角色
        你是专业的PPT需求澄清助手。名字叫做:豆豆,英文名叫dodo,你的责任是根据上下文及历史会话,帮助用户澄清他们的需求,确保所有必要信息都被收集。

        ## 任务
        分析用户需求,判断信息是否足够生成PPT:
        至少包含:


        ## 输出要求

这些信息会被保存到 AiPptInst 中,作为后续生成流程的重要输入。 当需求澄清完成后,状态机会自动推进到下一阶段,核心代码如下,就是状态机的推进:

/**


private


}

而他的目标节点就是在类中定义的: 也就是 信息收集阶段。

信息收集(SearchStrategy)

在需求澄清完成之后,系统就进入了下一阶段:信息收集。 这一阶段的目标非常明确:为后续 PPT 内容生成准备足够的资料和背景信息。 很多时候,用户虽然已经给出了明确的主题,例如:

生成一个 AI 行业趋势的 PPT

但如果直接让大模型生成 PPT 内容,往往会出现两个问题: - 内容过于 泛化 - 缺乏 最新数据和真实案例 因此,在真正生成大纲和内容之前,我们会先进行一次 信息搜集,让模型先“做功课”,获取与主题相关的资料。这些资料可以使用我们前面使用过的搜索引擎工具来实现。这样在后续生成 PPT 大纲和内容时,就可以基于真实资料进行总结,而不是完全依赖模型的参数知识。 那么我们的要求就是根据前置环节生成的requirement,来去搜集相关的内容,核心提示词如下:

public

            ## 角色
            你是专业的信息收集助手。

            ## 任务
            根据以下PPT主题,使用tavily搜索工具收集相关信息,并整理成简洁但是全面的总结。

            ## PPT主题


            ## 输出要求


}

这个地方值得一提的是,因为我们需要的是全流式输出,而SpringAI在进行流式输出+工具调用的时候,稳定性很差,有一些bug,导致工具无法调用,所以这边直接使用我们自己开发SimpleReactAgent来做联网搜索并流式输出,并且收集最终的结果,保存至AiPptInst ,作为后续环节的输入。

// 获取 SimpleReactAgent 并注入 tavily 搜索工具
SimpleReactAgent


// 流式输出搜索过程
StringBuilder

Disposable

              searchResultBuffer
              sink


              log


              searchResult
              context
              sink
              context


              log

              context

同时,完成后,继续执行状态机。

模板选择(TemplateStrategy)

在完成 信息收集 之后,系统就进入了下一阶段:模板选择。 这一阶段的目标非常明确:根据用户需求,从系统已有模板中选择一个最合适的 PPT 模板。 在企业级 PPT 生成系统中,模板其实是非常关键的一部分。因为企业通常都会有自己的 统一设计规范,例如: - Logo 固定位置 - 页面布局结构 - 字体与配色规范 - 首页、章节页、内容页样式 如果完全让模型自由生成页面结构,很容易出现 设计风格不统一、排版混乱 等问题。还有就是为什么要把模板选择放在这个流程位置上,主要是因为,后续的内容大纲生成必须是要按照模板的格式来准备的,否则可能就会导致,生成内容的不兼容,因此在我们的实现中采用了一种非常典型的 ToB 设计方式: 先选择模板,再根据模板结构生成内容。 这样既能够保证生成的 PPT 符合模板规范,同时不同模板也能够提供一定的 样式多样性。 在 TemplateStrategy 中,系统首先会从数据库中获取所有可用模板:

List

每一个模板都会包含一些基础信息,例如: - templateCode:模板唯一标识 - templateName:模板名称 - styleTags:适用风格标签 - slideCount:模板页数 - templateDesc:模板说明 随后系统会将这些模板信息整理成一段文本,提供给大模型进行分析:

StringBuilder
for
    templatesInfo

                    template_code
                    模板名称
                    适用风格
                    模板页数
                    模板说明

            template
            template
            template
            template
            template

}

接着系统会构造模板选择提示词,将 用户需求 与 模板信息 一起提供给模型:

String
        requirement
        templatesInfo
)

模型的任务就是根据用户需求,从这些模板中选择一个最合适的模板,并返回对应的 templateCode。为了保证模型输出的稳定性,这里使用了 结构化解析工具BeanOutputConverter 来解析模型返回结果:

BeanOutputConverter

随后调用模型进行模板选择:

String


TemplateSelectionResult

模型最终会返回类似这样的结构化结果:

templateCode: tech_blue
reason: 该模板适合科技行业报告

得到模板结果之后,系统会将选择结果保存到当前 PPT 实例中:

context

继续执行状态机。

大纲生成(OutlineStrategy)

在完成 模板选择 之后,系统就进入了下一阶段:大纲生成。 这一阶段的目标就是: 根据用户需求、搜索资料以及模板,生成整个 PPT 的章节大纲。 在实际制作 PPT 的过程中,大纲是非常关键的一步。因为 PPT 的整体结构、章节顺序以及内容逻辑,基本都是在这一阶段确定的。后续每一页 PPT 的具体内容,实际上都是在这个大纲基础上的进一步展开。 因此在 OutlineStrategy 中,系统会综合利用前面几个阶段生成的信息: - 需求澄清阶段生成的 requirement - 信息收集阶段得到的 searchInfo - 模板选择阶段确定的 template 这些信息会共同作为大纲生成的输入。

/**


public

            ## 角色
            你是专业的PPT内容大纲生成专家。你根据PPT的生成需求、选定模板的结构以及收集的相关信息,生成详细的PPT内容大纲。

            ## 任务
            请根据需求、模板结构和搜索信息生成PPT内容大纲。模板结构定义了可用的页面类型和字段,你需要根据这些来规划大纲。充分利用搜索到的信息来丰富大纲内容。

            ## PPT需求


            ## 搜索相关信息(可用于补充)


            ## 选定模板
            模板名称:

            ## 模板结构


            ## 输出要求
            输出详细的PPT大纲结构,包括每页的主题和要点。
            使用清晰的结构化格式,每页内容以
            每页应包含:


            页面类型说明:


            示例格式:

            类型:COVER
            标题:演示文稿名称
            副标题:副标题或说明
            作者:作者姓名


            类型:CATALOG
            标题:目录


            类型:CONTENT
            标题:内容标题


            ## 要求:
            不要有任何其他解释性的内容,只输出内容大纲。

}

可以看到提示词中有一个比较重要的提示: CONTENT: 内容页,展示主要内容(可以重复使用,根据用户的页数需求来选择复制多份) 这是什么意思?其实目的就是说,我们的模板可能只准备了5页:封面、目录、内容1、内容2、结尾,那我生成的PPT用户要求是10页、20页怎么办?那是不是内容页应当是可以赋值使用的,只是其中的文本和配图的差异,这个设计就是出于这个来考虑的。 接着就利用大模型,根据requirement、searchInfo以及template来生成出每页的详细内容,这样模型在生成大纲时,就能够同时考虑 内容逻辑 和 模板结构,从而生成更合理的 PPT 页面规划。

String
        requirement
        templateSchema
        template
        searchInfo
)

当模型生成完成之后,会执行完成回调:

.
    log

    context


    sink

    context
}

这里主要完成三件事情: 1. 保存生成的大纲内容 2. 更新任务状态为 SCHEMA 3. 继续推进状态机 随后系统就会进入下一阶段。 后面就是这个智能体最核心的两个环节,分别是Schema生成(SchemaStrategy)和PPT渲染(RenderStrategy),我们单独在下一章节进行介绍。

版本提示

模型、框架与接口会持续变化。涉及版本号、参数与生产配置时,请在实践前对照对应官方文档。

LLMentor系统化学习大模型应用工程

内容来自个人课程知识库备份,并经过结构化整理。技术版本持续演进,生产使用前请结合官方文档验证。