企业级演示自动化平台设计

引言

企业每年产出数以千计的演示文稿——销售提案、季度汇报、客户方案、培训教材。这些文稿大多遵循固定的品牌规范和内容模式,却仍依赖个人手工制作,导致质量参差不齐、品牌一致性难以保证。企业级演示自动化平台(Presentation Automation Platform)旨在将这一过程工程化、标准化和智能化。

一、平台架构

┌───────────────────────────────────────────────────┐
│                 用户界面层                          │
│  Web 编辑器 │ API 接口 │ CLI 工具 │ 插件(Office/WPS) │
├───────────────────────────────────────────────────┤
│                 业务逻辑层                          │
│  内容引擎    │  设计引擎   │  模板引擎  │  合规引擎    │
│  (LLM)      │  (布局/配色) │  (CRUD)   │  (品牌审核)  │
├───────────────────────────────────────────────────┤
│                 渲染层                              │
│  PPTX 渲染器 │ PDF 渲染器 │ Web 渲染器 │ 图片渲染器   │
├───────────────────────────────────────────────────┤
│                 基础设施层                          │
│  对象存储 │ 数据库 │ 消息队列 │ CDN │ LLM API       │
└───────────────────────────────────────────────────┘

二、核心模块设计

2.1 模板管理系统

# 模板数据模型
class PresentationTemplate:
    id: str
    name: str
    category: str              # sales, report, training, proposal
    industry: str              # tech, finance, healthcare, general
    brand_id: str              # 关联品牌配置
    slide_layouts: list[dict]  # 布局定义
    design_tokens: dict        # 设计令牌
    variables: list[dict]      # 可替换变量
    thumbnail: str             # 缩略图 URL
    version: int
    status: str                # draft, published, archived

    # 元数据
    created_by: str
    created_at: datetime
    usage_count: int
    avg_rating: float

# 模板 CRUD API
class TemplateService:
    async def create(self, template: PresentationTemplate) -> str:
        """创建新模板"""
        # 验证设计令牌完整性
        self._validate_design_tokens(template.design_tokens)
        # 渲染缩略图
        template.thumbnail = await self._render_thumbnail(template)
        # 保存
        return await self.repo.save(template)

    async def list_by_category(self, category: str,
                                brand_id: str = None) -> list:
        """按类别和品牌筛选模板"""
        filters = {"category": category, "status": "published"}
        if brand_id:
            filters["brand_id"] = brand_id
        return await self.repo.find(filters, sort="-usage_count")

    async def clone_and_customize(self, template_id: str,
                                   customizations: dict) -> str:
        """克隆模板并应用定制"""
        template = await self.repo.get(template_id)
        new_template = template.copy()
        new_template.design_tokens.update(customizations)
        return await self.create(new_template)

2.2 品牌合规引擎

class BrandComplianceEngine:
    """品牌规范合规检查引擎"""

    def __init__(self, brand_config: dict):
        self.config = brand_config

    def check(self, presentation_path: str) -> dict:
        """对演示文稿进行品牌合规检查"""
        prs = Presentation(presentation_path)
        violations = []

        for i, slide in enumerate(prs.slides):
            slide_violations = []

            for shape in slide.shapes:
                if shape.has_text_frame:
                    for para in shape.text_frame.paragraphs:
                        # 字体检查
                        if para.font.name and para.font.name not in self.config["allowed_fonts"]:
                            slide_violations.append({
                                "type": "font",
                                "detail": f"Non-brand font: {para.font.name}",
                                "severity": "warning"
                            })

                        # 颜色检查
                        if para.font.color.rgb:
                            color_hex = str(para.font.color.rgb)
                            if color_hex not in self.config["allowed_colors"]:
                                slide_violations.append({
                                    "type": "color",
                                    "detail": f"Non-brand color: #{color_hex}",
                                    "severity": "warning"
                                })

                # Logo 位置检查
                if shape.shape_type == 13:  # Picture
                    if self._is_logo(shape):
                        if not self._logo_position_ok(shape, slide):
                            slide_violations.append({
                                "type": "logo",
                                "detail": "Logo position non-compliant",
                                "severity": "error"
                            })

            if slide_violations:
                violations.append({"slide": i + 1, "issues": slide_violations})

        score = max(0, 100 - sum(
            len(v["issues"]) for v in violations
        ) * 5)

        return {
            "compliant": score >= 80,
            "score": score,
            "violations": violations,
            "total_issues": sum(len(v["issues"]) for v in violations)
        }

2.3 内容引擎(LLM 驱动)

class ContentEngine:
    """LLM 驱动的内容生成引擎"""

    def __init__(self, model: str = "gpt-4o"):
        self.client = OpenAI()
        self.model = model

    async def generate_from_brief(self, brief: dict) -> dict:
        """从创意简报生成演示内容"""
        system_prompt = self._build_system_prompt(brief)

        response = await self.client.chat.completions.create(
            model=self.model,
            messages=[
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": json.dumps(brief, ensure_ascii=False)}
            ],
            response_format={"type": "json_object"},
            temperature=0.7
        )

        content = json.loads(response.choices[0].message.content)

        # 质量检查
        quality = self._check_content_quality(content, brief)
        if quality["score"] < 70:
            # 自动修正
            content = await self._refine_content(content, quality["issues"])

        return content

    async def generate_from_document(self, document_text: str,
                                      num_slides: int = 15) -> dict:
        """从长文档提取关键内容生成演示"""
        # 分段总结
        summaries = await self._summarize_sections(document_text)

        # 结构化为演示格式
        content = await self._structure_for_presentation(
            summaries, num_slides
        )
        return content

    async def generate_speaker_notes(self, slides: list[dict],
                                      speaking_time: int = 30) -> list[dict]:
        """为每页生成演讲稿"""
        time_per_slide = speaking_time * 60 / len(slides)

        for slide in slides:
            slide["speaker_notes"] = await self._generate_notes(
                slide, int(time_per_slide)
            )
            slide["estimated_duration"] = f"{int(time_per_slide)}s"

        return slides

三、API 设计

3.1 REST API 规范

# 演示文稿 API
POST /api/v1/presentations
  Request:
    topic: string
    template_id: string (optional)
    brand_id: string (optional)
    num_slides: integer (default: 10)
    audience: string
    tone: string
    language: string (default: "zh")
  Response:
    id: string
    status: "generating" | "completed" | "failed"
    estimated_time: integer (seconds)

GET /api/v1/presentations/{id}
  Response:
    id: string
    status: string
    content: object (slides array)
    download_urls:
      pptx: string
      pdf: string
      web: string
    compliance:
      score: integer
      issues: array

POST /api/v1/presentations/{id}/regenerate
  Request:
    slide_indices: array[integer]  # 重新生成指定页
    feedback: string               # 修改意见
  Response:
    updated_slides: array

# 模板 API
GET /api/v1/templates
  Query: category, industry, brand_id
  Response: array[Template]

POST /api/v1/templates
  Request: Template object
  Response: { id: string }

# 品牌 API
POST /api/v1/brands/{id}/check
  Request: { presentation_id: string }
  Response: ComplianceReport

3.2 异步生成流程

from fastapi import FastAPI, BackgroundTasks
from celery import Celery

app = FastAPI()
celery = Celery("presentation_tasks", broker="redis://localhost:6379/0")

@app.post("/api/v1/presentations")
async def create_presentation(request: CreateRequest,
                               background_tasks: BackgroundTasks):
    """创建演示文稿(异步)"""
    job_id = str(uuid.uuid4())

    # 入队异步任务
    celery.send_task("generate_presentation", args=[job_id, request.dict()])

    return {
        "id": job_id,
        "status": "generating",
        "estimated_time": estimate_generation_time(request),
        "poll_url": f"/api/v1/presentations/{job_id}"
    }

@celery.task(name="generate_presentation")
def generate_presentation(job_id: str, request: dict):
    """后台生成任务"""
    try:
        # 1. 生成内容
        content = content_engine.generate_from_brief(request)

        # 2. 选择/加载模板
        template = template_service.get(request.get("template_id"))

        # 3. 渲染 PPTX
        pptx_path = renderer.render(content, template)

        # 4. 品牌合规检查
        compliance = brand_engine.check(pptx_path)

        # 5. 生成 PDF
        pdf_path = pdf_renderer.render(pptx_path)

        # 6. 上传到对象存储
        pptx_url = storage.upload(pptx_path)
        pdf_url = storage.upload(pdf_path)

        # 7. 更新状态
        db.update(job_id, {
            "status": "completed",
            "download_urls": {"pptx": pptx_url, "pdf": pdf_url},
            "compliance": compliance
        })

    except Exception as e:
        db.update(job_id, {"status": "failed", "error": str(e)})

四、设计令牌系统

4.1 Design Token 规范

{
  "brand": {
    "name": "灵阙科技",
    "logo_url": "https://cdn.example.com/logo.svg",
    "logo_position": "top-left",
    "logo_size": {"width": 120, "height": 40}
  },
  "colors": {
    "primary": "#1A56DB",
    "secondary": "#6B7280",
    "accent": "#F59E0B",
    "success": "#059669",
    "warning": "#D97706",
    "error": "#DC2626",
    "background": {
      "light": "#FFFFFF",
      "dark": "#0F172A",
      "section": "#F1F5F9"
    },
    "text": {
      "heading": "#1F2937",
      "body": "#374151",
      "muted": "#9CA3AF",
      "inverse": "#F8FAFC"
    }
  },
  "typography": {
    "heading": {
      "family": "Microsoft YaHei",
      "sizes": {"h1": 44, "h2": 32, "h3": 24},
      "weight": "bold"
    },
    "body": {
      "family": "Microsoft YaHei",
      "sizes": {"large": 20, "normal": 16, "small": 12},
      "weight": "regular",
      "line_height": 1.6
    }
  },
  "spacing": {
    "page_margin": {"top": 40, "bottom": 40, "left": 50, "right": 50},
    "section_gap": 32,
    "element_gap": 16,
    "grid_columns": 12,
    "grid_gutter": 16
  },
  "shapes": {
    "corner_radius": 8,
    "shadow": "none",
    "border_width": 0
  }
}

五、部署架构

5.1 微服务架构

┌─────────────┐     ┌─────────────┐
│  Web 前端    │     │  API 网关    │
│  (React)    │────→│  (Kong)     │
└─────────────┘     └──────┬──────┘
                           │
              ┌────────────┼────────────┐
              ↓            ↓            ↓
       ┌────────────┐ ┌──────────┐ ┌──────────┐
       │ 内容服务    │ │ 模板服务  │ │ 渲染服务  │
       │ (Python)   │ │ (Python) │ │ (Python) │
       └────────────┘ └──────────┘ └──────────┘
              │            │            │
              ↓            ↓            ↓
       ┌────────────┐ ┌──────────┐ ┌──────────┐
       │ LLM API    │ │ PostgreSQL│ │ MinIO    │
       │ (OpenAI)   │ │          │ │ (S3)     │
       └────────────┘ └──────────┘ └──────────┘

5.2 性能指标

指标 目标 实际
10 页 PPT 生成时间 < 30s 15-25s
并发生成能力 50 个/分钟 根据 LLM API 配额
PPTX 文件大小 < 10MB 2-8MB
API 响应时间 < 500ms(提交) 200ms
系统可用性 99.5% -

六、商业模式

6.1 定价策略

方案 月费 生成量 模板 品牌
Free 0 5 份/月 基础
Pro $29 50 份/月 全部 1 个
Team $99 200 份/月 全部 + 自定义 3 个
Enterprise 定制 无限 全部 + 自定义 无限

6.2 核心价值主张

对个人用户:
  痛点:花 3 小时做 PPT → 解决:3 分钟生成
  价值:时间节省 98%

对企业用户:
  痛点:品牌不一致、质量参差 → 解决:模板 + 合规检查
  价值:品牌一致性 100%,制作成本降低 80%

对销售团队:
  痛点:每个客户方案都重新做 → 解决:数据驱动批量生成
  价值:方案产出效率提升 10 倍

总结

企业级演示自动化平台的核心价值在于三个"化":标准化(品牌合规保证)、智能化(LLM 驱动内容生成)、规模化(API 驱动批量生产)。技术栈选型建议 python-pptx + FastAPI + Celery + OpenAI API。关键设计决策:以 Design Token 系统为纽带连接品牌规范和渲染引擎,以异步任务架构支撑并发生成需求。


Maurice | maurice_wen@proton.me