dodo-agent

如何输出稳定的PPT?

在上一节中,我们已经介绍了 PPT 自动生成流程中的几个关键环节,包括 需求澄清、信息收集、模板选择以及大纲生成。通过这些步骤,系统已经明确了用户的核心需求,并确定了 PPT 的整体结构与内容框架。 接下来,本节将进入 PPT 生成…

TL;DR

在上一节中,我们已经介绍了 PPT 自动生成流程中的几个关键环节,包括 需求澄清、信息收集、模板选择以及大纲生成。通过这些步骤,系统已经明确了用户的核心需求,并确定了 PPT 的整体结构与内容框架。 接下来,本节将进入 PPT 生成…

在上一节中,我们已经介绍了 PPT 自动生成流程中的几个关键环节,包括 需求澄清、信息收集、模板选择以及大纲生成。通过这些步骤,系统已经明确了用户的核心需求,并确定了 PPT 的整体结构与内容框架。 接下来,本节将进入 PPT 生成流程中最核心的部分:Schema 生成。在这一阶段,系统会根据前面生成的大纲和所选模板,构建出完整的 PPT 页面结构描述(JSON Schema)。

Schema 生成(SchemaStrategy)

在完成 PPT 大纲生成 之后,系统就进入了整个流程中最核心的阶段之一:Schema 生成。 这一阶段的目标是: 根据模板结构和大纲内容,生成完整的 PPT 页面结构 JSON。 如果把 PPT 生成流程类比为一个生产过程,那么: - 大纲阶段:确定 PPT 有多少页,每页大致的内容有哪些 - Schema 阶段:就是确定每一页 PPT 的具体内容,也就是每一页页面应该放什么内容、内容类型是什么、对应模板的哪个布局。

核心思想

在我们的系统中,并不是直接生成 .pptx 文件,而是先生成一个 PptSchema JSON 结构。例如:

{
  "slides": [
    {
      "pageType": "COVER",
      "pageDesc": "封面页",
      "templatePageIndex": 1,
      "data": {
        "title": {
          "type": "text",
          "content": "人工智能技术发展",
          "fontLimit": 7
        }
      }
    }
  ]
}

这个 Schema 会描述: - PPT 有多少页,对应slides的数组个数 - 每一页的类型是什么,COVER封面、CONTENT内容、END结尾等等 - 模板对应占位符的名称是什么 - 填充的类型是文字还是图片,如果是文字,字数限制又是多少 这样做有一个非常重要的工程优势: 生成逻辑和最终 PPT 渲染逻辑完全解耦。 Agent 只负责生成 结构化内容,而真正的 PPT 文件生成由渲染服务完成。

模板怎么做?

这边给出一个我制作好的PPT模板和Schema。比如我的PPT模板是5页。 打开我们的幻灯片模板ai.pptx,我们可以事先在准备这样一个PPT,可以看到这个PPT他的页面有很多元素。比如大标题、小标题、文本、图片、还有背景图,那我们要做的是什么?就是去将这些可变的元素进行替换填充到模板里去。 那如何精确的替换呢?答案就是占位符,我这边是直接使用的PPT提供的shape name来设置的。 点击开始→选择→选择窗格,就可以打开shape name设置的窗口。 不同颜色的箭头,表示一一对应的关系,这样为每页设置好shape name后,就ok了。 但是值得一提的是,这种替换是有要求的,就是你需要定位到最深层最原始的那个文本框才可以,因为PPT是很复杂的,里面可能会包含很多组合的元素,针对组合元素的文本,你的窗格即使配置上了,也没法替换成功。这个大家需要注意一下。

Schema模板怎么写?

他的Schema如下所示,首先明确他的作用是什么,他的作用就是使用JSON这种结构化语言,能够做到和PPT的映射关系,从而可以让大模型生成这样一个JSON后,由PYTHON-PPTX来解析渲染成具体的PPT。 接下来我为大家讲解一下具体怎么去配置这样一个模板Schema。

{
  "slides": [
    {
      "pageType": "COVER",
      "pageDesc": "封面页",
      "pageIndex": 1,
      "data": {
        "title": {
              "type":"text",
          "content": "大标题",
          "fontLimit": 7
        },
        "description": {
              "type":"text",
          "content": "一句话描述",
          "fontLimit": 30
        },
        "author": {
              "type":"text",
          "content": "作者姓名",
          "fontLimit": 10
        }
      }
    },

    {
      "pageType": "CATALOG",
      "pageDesc": "目录页",
      "pageIndex": 2,
      "data": {
        "catalog1": {
              "type":"text",
          "content": "目录1",
          "fontLimit": 9
        },
        "catalog2": {
              "type":"text",
          "content": "目录2",
          "fontLimit": 9
        },
        "catalog3": {
              "type":"text",
          "content": "目录3",
          "fontLimit": 9
        }
      }
    },

    {
      "pageType": "COMPARE",
      "pageDesc": "内容页,用于2者对比",
      "pageIndex": 3,
      "data": {
        "title": {
              "type":"text",
          "content": "大标题",
          "fontLimit": 9
        },
        "content1": {
          "type":"text",
          "content": "对比项1内容",
          "fontLimit": 60
        },
        "content2": {
              "type":"text",
          "content": "对比项2内容",
          "fontLimit": 60
        }
      }
    },

    {
      "pageType": "CONTENT",
      "pageDesc": "内容页",
      "pageIndex": 4,
      "data": {
        "title": {
              "type":"text",
          "content": "大标题",
          "fontLimit": 9
        },
        "subTitle": {
              "type":"text",
          "content": "子标题",
          "fontLimit": 4
        },
        "content": {
              "type":"text",
          "content": "内容描述",
          "fontLimit": 55
        },
        "image": {
              "type":"image",
          "content": "根据需求生成配图",
          "url": "图片URL地址"
        }
      }
    },

    {
      "pageType": "END",
      "pageDesc": "结束页",
      "pageIndex": 5,
      "data": {
        "title": {
              "type":"text",
          "content": "结束页大标题",
          "fontLimit": 5
        }
      }
    }
  ]
}

在 Schema 中,每一页 PPT 都由几个核心字段组成: - pageType:表示页面的类型,例如 COVER(封面页)、CONTENT(内容页)等 - pageIndex:表示该页面在模板中的页码,用于匹配模板中的具体版式 - data:表示该页面中需要填充的具体内容 其中,data 是一个 键值结构,每个 key 对应模板中某个 shape name(形状名称),用于精准定位 PPT 模板中的元素。 每个字段都会包含以下信息: - type:表示元素类型 - content:表示生成的内容 - url:用于图片资源地址(如果需要) 根据不同的 type,字段的含义也会有所不同:

文本类型

当 type = text 时,表示这是一个 文本类型的 shape: - content 表示最终填充到 PPT 中的文本内容 例如: - title:对应 PPT 中的 主标题 - description:对应主标题下方的 说明文本 对于文本类型,还需要额外增加一个 fontLimit(字数限制)。这是因为 PPT 模板中的 文本框大小和样式是固定的,如果生成的文本过长,超出文本框可承载的范围,就可能导致: - 文本换行异常 - 字体被压缩 - 页面排版错乱 因此需要为每个文本字段设置一个 合理的字数上限,用于约束大模型生成内容的长度。 不过需要注意的是,大模型生成内容本身具有一定的 随机性。即使设置了 fontLimit,模型生成的 content 在个别情况下仍然可能超过限制,因此实际系统中通常还需要通过后处理逻辑进一步控制文本长度。

图片类型

当 type = image 时,表示该字段是 配图元素。 此时: - content:表示 文生图的提示词(Prompt) - url:表示生成图片后的资源地址,初始默认为空 在后续流程中,系统会根据 content 的提示词调用 文生图服务生成图片,再将生成后的图片地址回填到 url 字段中。

背景类型

当 type = background 时,表示该字段用于生成 页面背景图。它的结构与 image 类似: - content:文生图提示词 - url:生成后的图片地址(初始为空) 在当前的模板中,为了保持整体视觉风格的统一,我并没有额外配置背景图,但系统本身是 支持背景图生成能力的。 这样设计 Schema 就可以做到: - 模板结构与内容生成解耦 - 不同元素类型可以统一管理 - 方便后续替换文本、图片、背景 同时也为后续的 PPT 自动渲染阶段提供了清晰、结构化的数据基础。配置完成后,我们就可以将JSON和PPT形成了一种映射的关系,当然我们的JSON Schema也要存储到表中:AiPptTemplate

执行流程

提示词生成

首先系统会获取前置阶段的关键数据:

String
AiPptTemplate
String
String

这里包含两个非常重要的输入: - templateSchema:模板字段定义 - outline:大纲结构 随后构造生成 Schema 的 Prompt,这边的Prompt约束指令比较多,因为生成出的JSON Schema效果,就直接会影响到我们后续生成PPT的效果。

/**


public

            ## 角色
            你是专业的PPT

            ## 任务
            根据模板

            ## 模板


            ## PPT大纲


            ## 输出格式要求
            输出JSON格式,结构如下:

              “slides”

                  “pageType”
                  “pageDesc”
                  “templatePageIndex”
                  “data”
                    “字段名”


}

调用模型生成 Schema

Prompt 构造完成后,系统调用大模型生成 JSON:

String

随后使用 BeanOutputConverter 将 JSON 转换为对象:

BeanOutputConverter


PptSchema

然后保存到数据库:

String

context

此时系统已经获得了 完整的 PPT 页面结构。

图片生成机制

Schema 生成完成之后,系统会自动检测是否存在需要生成的图片。 核心逻辑在:

processImageGeneration

这个方法会扫描所有Schema JSON的字段:

for

如果字段类型是:

image
background

并且 url 为空,就会触发图片生成。

图片生成流程

图片生成的流程如下: 调用文生图大模型

String
        context

获取Schema中的image或者background类型的,且url为空的字段的 content,也就是文生图提示词,调用文生图大模型,生成具体的图片,这边一般用一些低成品的文生图大模型就可以了,因为只是一些简单的配图,所以成品相对低很多。接口返回都是图片的URL,但是这种URL一般都是有时效性的。 下载图片并上传至对象存储

// 下载图片并上传到MinIO
byte

if

更新 Schema 最后更新字段 URL:

// 更新schema中的url为MinIO地址
task

这样 Schema 中的图片地址就变成了 真实可访问的资源地址。 当 Schema 中的内容全部准备完成后,系统会继续推进状态,进入最终的渲染阶段:

context.continueStateMachine(inst, sink, query, thinkingBuffer);
版本提示

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

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

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