AI PPT与文档自动生成完全教程

教程简介

本教程全面讲解AI驱动的PPT与文档自动生成核心技术,涵盖Gamma/Beautiful.ai/Tome等主流AI PPT工具对比、python-pptx程序化生成、LLM内容结构化与大纲生成、模板系统与主题设计、图表与数据可视化自动嵌入、图片AI生成与插入、多格式导出(PPT/PDF/HTML)、Word文档AI生成、报告自动化流水线、API集成与批量生成等核心内容,通过完整的季度报告自动生成系统案例帮助开发者掌握AI文档生成技术。

AI PPT与文档自动生成完全教程

从零掌握AI驱动的演示文稿与文档自动化生成技术,打造企业级报告自动化流水线

一、前言

在企业日常运营中,PPT演示文稿和文档报告是信息传递的核心载体。传统的文档制作流程——收集数据、撰写内容、设计排版、反复修改——往往耗费大量人力和时间。随着大语言模型(LLM)和AI生成技术的成熟,文档自动化生成已经从"模板填充"进化到"智能创作"阶段。

本教程将系统讲解AI PPT与文档自动生成的核心技术栈,涵盖从商业工具对比到程序化生成、从内容智能排版到完整流水线搭建的全链路方案。无论你是需要批量生成周报月报的开发者,还是希望为产品集成文档生成能力的工程师,都能从中找到实用的解决方案。

二、主流AI PPT工具全景对比

2.1 工具概览

目前市场上主流的AI PPT生成工具各有侧重:

工具 核心优势 适用场景 API支持 价格
Gamma 输入一句话生成完整PPT,设计感强 快速原型、会议演示 免费版+Pro $10/月
Beautiful.ai 智能排版引擎,自动调整布局 商业汇报、销售提案 $12/月起
Tome AI叙事驱动,适合讲故事 创意展示、产品发布 免费版+Pro $16/月
SlidesGPT 纯文本输入,快速生成 快速草稿、头脑风暴 按次付费
Canva AI 设计资源丰富,模板多样 营销材料、社交媒体 有限 免费版+Pro $13/月

2.2 Gamma API 集成示例

Gamma提供了REST API,可以通过代码调用AI生成演示文稿:

import requests
import json
import time

class GammaAPI:
    """Gamma AI PPT生成客户端"""
    
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.base_url = "https://api.gamma.app/v1"
        self.headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json"
        }
    
    def create_presentation(self, topic: str, num_slides: int = 10, 
                           style: str = "professional") -> dict:
        """创建AI演示文稿"""
        payload = {
            "inputText": topic,
            "numCards": num_slides,
            "style": style,
            "language": "zh-CN",
            "format": "presentation"
        }
        
        response = requests.post(
            f"{self.base_url}/generations",
            headers=self.headers,
            json=payload,
            timeout=60
        )
        response.raise_for_status()
        return response.json()
    
    def poll_status(self, generation_id: str, max_wait: int = 300) -> dict:
        """轮询生成状态"""
        start = time.time()
        while time.time() - start < max_wait:
            resp = requests.get(
                f"{self.base_url}/generations/{generation_id}",
                headers=self.headers
            )
            data = resp.json()
            if data.get("status") == "completed":
                return data
            elif data.get("status") == "failed":
                raise RuntimeError(f"生成失败: {data.get('error')}")
            time.sleep(5)
        raise TimeoutError("生成超时")
    
    def export_pptx(self, generation_id: str, output_path: str):
        """导出为PPTX格式"""
        resp = requests.get(
            f"{self.base_url}/generations/{generation_id}/export/pptx",
            headers=self.headers,
            stream=True
        )
        resp.raise_for_status()
        with open(output_path, 'wb') as f:
            for chunk in resp.iter_content(chunk_size=8192):
                f.write(chunk)


# 使用示例
# gamma = GammaAPI(api_key="your-api-key")
# result = gamma.create_presentation("2024年Q4销售业绩报告", num_slides=12)
# final = gamma.poll_status(result["id"])
# gamma.export_pptx(final["id"], "sales_report.pptx")

2.3 工具选择建议

  • 快速原型验证:Gamma(一句话生成,速度最快)
  • 企业级批量生成:python-pptx + LLM(完全可控,无API费用)
  • 设计质量优先:Beautiful.ai(排版引擎最强)
  • 叙事型内容:Tome(AI讲故事能力突出)

三、python-pptx 程序化PPT生成

对于需要完全控制输出格式、批量生成或与内部系统集成的场景,python-pptx 是最成熟的Python库。

3.1 环境安装与基础操作

pip install python-pptx
from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.enum.chart import XL_CHART_TYPE
from pptx.chart.data import CategoryChartData

def create_basic_presentation():
    """创建基础演示文稿"""
    prs = Presentation()
    
    # 设置幻灯片尺寸(16:9)
    prs.slide_width = Inches(13.333)
    prs.slide_height = Inches(7.5)
    
    # 使用空白布局
    blank_layout = prs.slide_layouts[6]
    
    # 添加标题页
    slide = prs.slides.add_slide(blank_layout)
    
    # 添加标题文本框
    left = Inches(1.5)
    top = Inches(2.5)
    width = Inches(10)
    height = Inches(1.5)
    
    title_box = slide.shapes.add_textbox(left, top, width, height)
    title_frame = title_box.text_frame
    title_frame.word_wrap = True
    
    p = title_frame.paragraphs[0]
    p.text = "2024年第四季度销售业绩报告"
    p.font.size = Pt(36)
    p.font.bold = True
    p.font.color.rgb = RGBColor(0x1A, 0x36, 0x5D)
    p.alignment = PP_ALIGN.CENTER
    
    # 添加副标题
    subtitle_box = slide.shapes.add_textbox(
        Inches(2), Inches(4.2), Inches(9), Inches(1)
    )
    subtitle_frame = subtitle_box.text_frame
    p2 = subtitle_frame.paragraphs[0]
    p2.text = "销售部 · 2024年12月"
    p2.font.size = Pt(20)
    p2.font.color.rgb = RGBColor(0x66, 0x66, 0x66)
    p2.alignment = PP_ALIGN.CENTER
    
    prs.save("basic_report.pptx")
    print("基础演示文稿已创建: basic_report.pptx")

create_basic_presentation()

3.2 高级版式与样式系统

from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.oxml.ns import qn
import copy

class PresentationTheme:
    """演示文稿主题配置"""
    
    def __init__(self):
        # 颜色方案
        self.primary_color = RGBColor(0x1A, 0x36, 0x5D)      # 深蓝
        self.secondary_color = RGBColor(0x2B, 0x57, 0x9A)    # 中蓝
        self.accent_color = RGBColor(0xE8, 0x6C, 0x00)       # 橙色
        self.text_color = RGBColor(0x33, 0x33, 0x33)          # 深灰
        self.light_text = RGBColor(0x99, 0x99, 0x99)          # 浅灰
        self.background = RGBColor(0xFF, 0xFF, 0xFF)          # 白色
        
        # 字体配置
        self.title_font = "微软雅黑"
        self.body_font = "宋体"
        self.code_font = "Consolas"
        
        # 尺寸配置
        self.slide_width = Inches(13.333)
        self.slide_height = Inches(7.5)


class SlideFactory:
    """幻灯片工厂 - 快速生成各类版式"""
    
    def __init__(self, theme: PresentationTheme = None):
        self.theme = theme or PresentationTheme()
        self.prs = Presentation()
        self.prs.slide_width = self.theme.slide_width
        self.prs.slide_height = self.theme.slide_height
    
    def _add_textbox(self, slide, left, top, width, height, 
                     text, font_size=18, bold=False, 
                     color=None, alignment=PP_ALIGN.LEFT,
                     font_name=None):
        """通用文本框添加方法"""
        box = slide.shapes.add_textbox(Inches(left), Inches(top),
                                        Inches(width), Inches(height))
        frame = box.text_frame
        frame.word_wrap = True
        
        p = frame.paragraphs[0]
        p.text = text
        p.font.size = Pt(font_size)
        p.font.bold = bold
        p.font.color.rgb = color or self.theme.text_color
        p.font.name = font_name or self.theme.body_font
        p.alignment = alignment
        
        return box, frame
    
    def add_title_slide(self, title: str, subtitle: str = ""):
        """添加标题页"""
        slide = self.prs.slides.add_slide(self.prs.slide_layouts[6])
        
        # 背景色块
        bg = slide.shapes.add_shape(
            1,  # MSO_SHAPE.RECTANGLE
            Inches(0), Inches(0),
            self.theme.slide_width, self.theme.slide_height
        )
        bg.fill.solid()
        bg.fill.fore_color.rgb = self.theme.primary_color
        bg.line.fill.background()
        
        # 标题
        self._add_textbox(slide, 1.5, 2.2, 10, 1.5, title,
                         font_size=40, bold=True,
                         color=RGBColor(0xFF, 0xFF, 0xFF),
                         alignment=PP_ALIGN.CENTER,
                         font_name=self.theme.title_font)
        
        # 副标题
        if subtitle:
            self._add_textbox(slide, 2, 4.0, 9, 1, subtitle,
                             font_size=22,
                             color=RGBColor(0xCC, 0xDD, 0xEE),
                             alignment=PP_ALIGN.CENTER)
        
        return slide
    
    def add_content_slide(self, title: str, bullet_points: list):
        """添加内容页(标题+要点列表)"""
        slide = self.prs.slides.add_slide(self.prs.slide_layouts[6])
        
        # 顶部色条
        bar = slide.shapes.add_shape(
            1, Inches(0), Inches(0),
            self.theme.slide_width, Inches(0.08)
        )
        bar.fill.solid()
        bar.fill.fore_color.rgb = self.theme.accent_color
        bar.line.fill.background()
        
        # 标题
        self._add_textbox(slide, 0.8, 0.4, 11, 0.8, title,
                         font_size=28, bold=True,
                         color=self.theme.primary_color,
                         font_name=self.theme.title_font)
        
        # 内容列表
        content_box = slide.shapes.add_textbox(
            Inches(1.0), Inches(1.6), Inches(11), Inches(5)
        )
        frame = content_box.text_frame
        frame.word_wrap = True
        
        for i, point in enumerate(bullet_points):
            if i == 0:
                p = frame.paragraphs[0]
            else:
                p = frame.add_paragraph()
            
            p.text = f"• {point}"
            p.font.size = Pt(18)
            p.font.color.rgb = self.theme.text_color
            p.font.name = self.theme.body_font
            p.space_after = Pt(12)
        
        return slide
    
    def add_two_column_slide(self, title: str, left_content: list, 
                              right_content: list):
        """添加双栏内容页"""
        slide = self.prs.slides.add_slide(self.prs.slide_layouts[6])
        
        # 标题
        self._add_textbox(slide, 0.8, 0.4, 11, 0.8, title,
                         font_size=28, bold=True,
                         color=self.theme.primary_color)
        
        # 左栏
        left_box = slide.shapes.add_textbox(
            Inches(0.8), Inches(1.6), Inches(5.5), Inches(5)
        )
        left_frame = left_box.text_frame
        left_frame.word_wrap = True
        for i, item in enumerate(left_content):
            p = left_frame.paragraphs[0] if i == 0 else left_frame.add_paragraph()
            p.text = f"• {item}"
            p.font.size = Pt(16)
            p.space_after = Pt(10)
        
        # 分隔线
        line = slide.shapes.add_shape(
            1, Inches(6.5), Inches(1.6),
            Inches(0.02), Inches(4.5)
        )
        line.fill.solid()
        line.fill.fore_color.rgb = RGBColor(0xDD, 0xDD, 0xDD)
        line.line.fill.background()
        
        # 右栏
        right_box = slide.shapes.add_textbox(
            Inches(7.0), Inches(1.6), Inches(5.5), Inches(5)
        )
        right_frame = right_box.text_frame
        right_frame.word_wrap = True
        for i, item in enumerate(right_content):
            p = right_frame.paragraphs[0] if i == 0 else right_frame.add_paragraph()
            p.text = f"• {item}"
            p.font.size = Pt(16)
            p.space_after = Pt(10)
        
        return slide
    
    def add_data_slide(self, title: str, chart_type: str,
                       categories: list, series_data: dict):
        """添加数据图表页"""
        slide = self.prs.slides.add_slide(self.prs.slide_layouts[6])
        
        # 标题
        self._add_textbox(slide, 0.8, 0.4, 11, 0.8, title,
                         font_size=28, bold=True,
                         color=self.theme.primary_color)
        
        # 图表数据
        chart_data = CategoryChartData()
        chart_data.categories = categories
        for series_name, values in series_data.items():
            chart_data.add_series(series_name, values)
        
        # 图表类型映射
        chart_types = {
            'bar': XL_CHART_TYPE.BAR_CLUSTERED,
            'column': XL_CHART_TYPE.COLUMN_CLUSTERED,
            'line': XL_CHART_TYPE.LINE,
            'pie': XL_CHART_TYPE.PIE,
            'area': XL_CHART_TYPE.AREA,
        }
        
        chart = slide.shapes.add_chart(
            chart_types.get(chart_type, XL_CHART_TYPE.COLUMN_CLUSTERED),
            Inches(1.0), Inches(1.8), Inches(11), Inches(5),
            chart_data
        ).chart
        
        chart.has_legend = True
        chart.legend.include_in_layout = False
        
        return slide
    
    def save(self, path: str):
        """保存文件"""
        self.prs.save(path)
        print(f"演示文稿已保存: {path}")


# 使用示例
factory = SlideFactory()
factory.add_title_slide("AI技术年度总结", "技术部 · 2024年度")
factory.add_content_slide("核心成果", [
    "完成大语言模型平台搭建,支撑10+业务线",
    "AI助手日活用户突破5万",
    "自动化报告系统节省人工80%",
    "模型推理成本降低60%"
])
factory.add_two_column_slide(
    "技术栈对比",
    ["PyTorch 2.0 — 训练效率提升40%", "vLLM — 推理吞吐量提升3x", "LangChain — RAG准确率92%"],
    ["TensorRT — 延迟降低50%", "ONNX Runtime — 跨平台部署", "Milvus — 向量检索P99<10ms"]
)
factory.add_data_slide("季度营收趋势", "column",
    ["Q1", "Q2", "Q3", "Q4"],
    {"2023": [1200, 1500, 1800, 2100], "2024": [1800, 2200, 2600, 3200]}
)
factory.save("ai_annual_report.pptx")

四、LLM驱动的内容结构化与大纲生成

4.1 基于LLM的PPT大纲自动生成

利用大语言模型将用户输入的主题自动扩展为结构化的PPT大纲:

import json
from openai import OpenAI

class PPTOutlineGenerator:
    """基于LLM的PPT大纲生成器"""
    
    def __init__(self, api_key: str, base_url: str = None, model: str = "gpt-4"):
        self.client = OpenAI(api_key=api_key, base_url=base_url)
        self.model = model
    
    def generate_outline(self, topic: str, num_slides: int = 10,
                        style: str = "professional",
                        audience: str = "企业管理层") -> dict:
        """生成PPT大纲"""
        
        system_prompt = """你是一个专业的演示文稿大纲生成专家。请根据用户输入的主题,
生成结构化的PPT大纲,输出为JSON格式。

输出格式要求:
{
  "title": "演示文稿主标题",
  "subtitle": "副标题",
  "slides": [
    {
      "slide_number": 1,
      "type": "title|content|two_column|data|summary",
      "title": "幻灯片标题",
      "content": ["要点1", "要点2", ...],
      "left_content": ["左栏内容"], // 仅two_column类型
      "right_content": ["右栏内容"], // 仅two_column类型
      "chart_hint": "图表类型和数据建议", // 仅data类型
      "speaker_notes": "演讲者备注"
    }
  ]
}"""
        
        user_prompt = f"""请为以下主题生成PPT大纲:
- 主题:{topic}
- 幻灯片数量:{num_slides}页
- 演示风格:{style}
- 目标受众:{audience}

要求:
1. 开头要有引人入胜的开场
2. 中间部分逻辑清晰、层层递进
3. 包含数据支撑的页面
4. 结尾有总结和行动建议
5. 每页内容精炼,不超过5个要点"""

        response = self.client.chat.completions.create(
            model=self.model,
            messages=[
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": user_prompt}
            ],
            temperature=0.7,
            response_format={"type": "json_object"}
        )
        
        return json.loads(response.choices[0].message.content)
    
    def expand_bullet_points(self, topic: str, bullet_points: list,
                            detail_level: str = "medium") -> list:
        """扩展要点为详细内容"""
        
        prompt = f"""请将以下PPT要点扩展为详细的讲解内容(每条100-200字):
主题:{topic}
要点:
{json.dumps(bullet_points, ensure_ascii=False, indent=2)}

要求:
- 语言专业但不晦涩
- 包含具体数据或案例
- 适合口头讲解
- 详细程度:{detail_level}

输出为JSON数组,每个元素包含原始要点和扩展内容。"""

        response = self.client.chat.completions.create(
            model=self.model,
            messages=[{"role": "user", "content": prompt}],
            temperature=0.7,
            response_format={"type": "json_object"}
        )
        
        return json.loads(response.choices[0].message.content)


# 使用示例
# generator = PPTOutlineGenerator(api_key="your-key")
# outline = generator.generate_outline(
#     "2024年AI技术趋势与企业应用",
#     num_slides=12,
#     style="professional",
#     audience="CTO和VP级别管理者"
# )
# print(json.dumps(outline, ensure_ascii=False, indent=2))

4.2 智能内容提取与结构化

从业务数据、会议记录、研究报告等非结构化文本中提取PPT所需内容:

import json
from dataclasses import dataclass, field, asdict
from typing import Optional

@dataclass
class SlideContent:
    """幻灯片内容数据结构"""
    title: str
    slide_type: str = "content"
    bullet_points: list = field(default_factory=list)
    key_data: dict = field(default_factory=dict)
    speaker_notes: str = ""
    priority: int = 0  # 用于排序

class ContentExtractor:
    """从非结构化文本提取PPT内容"""
    
    def __init__(self, llm_client, model: str = "gpt-4"):
        self.client = llm_client
        self.model = model
    
    def extract_from_report(self, text: str, max_slides: int = 10) -> list:
        """从报告文本提取PPT内容"""
        prompt = f"""请从以下报告文本中提取适合制作PPT的核心内容,
最多提取{max_slides}个关键点:

{text[:5000]}  # 截断避免token超限

输出JSON格式:
{{
  "slides": [
    {{
      "title": "幻灯片标题",
      "slide_type": "content/data/two_column",
      "bullet_points": ["要点1", "要点2"],
      "key_data": {{"metric_name": "value"}},
      "priority": 1
    }}
  ]
}}

提取原则:
1. 优先提取有数据支撑的内容
2. 按重要性排序
3. 每个幻灯片不超过5个要点
4. 识别适合用图表展示的数据"""

        response = self.client.chat.completions.create(
            model=self.model,
            messages=[{"role": "user", "content": prompt}],
            response_format={"type": "json_object"}
        )
        
        result = json.loads(response.choices[0].message.content)
        return [SlideContent(**item) for item in result.get("slides", [])]
    
    def extract_from_meeting_notes(self, notes: str) -> dict:
        """从会议纪要提取行动项和决策"""
        prompt = f"""请从以下会议纪要中提取:
1. 关键决策(decisions)
2. 行动项(action_items)含负责人和截止日期
3. 待解决问题(open_questions)

会议纪要:
{notes[:3000]}

输出JSON格式。"""

        response = self.client.chat.completions.create(
            model=self.model,
            messages=[{"role": "user", "content": prompt}],
            response_format={"type": "json_object"}
        )
        
        return json.loads(response.choices[0].message.content)

五、图表与数据可视化自动嵌入

5.1 基于matplotlib的图表自动生成

import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('Agg')  # 无GUI模式
import numpy as np
from io import BytesIO

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False

class ChartGenerator:
    """图表生成器 - 自动生成PPT所需的图表图片"""
    
    def __init__(self, style: str = "seaborn-v0_8-whitegrid"):
        try:
            plt.style.use(style)
        except OSError:
            plt.style.use('default')
        self.color_palette = [
            '#1A365D', '#2B579A', '#E86C00', '#28A745',
            '#DC3545', '#6F42C1', '#17A2B8', '#FFC107'
        ]
    
    def bar_chart(self, categories: list, values: list,
                  title: str = "", ylabel: str = "") -> BytesIO:
        """生成柱状图"""
        fig, ax = plt.subplots(figsize=(10, 6))
        
        bars = ax.bar(categories, values, color=self.color_palette[:len(categories)],
                      width=0.6, edgecolor='white', linewidth=0.5)
        
        # 在柱子上方显示数值
        for bar, val in zip(bars, values):
            ax.text(bar.get_x() + bar.get_width() / 2, bar.get_height() + 0.5,
                    f'{val:,.0f}', ha='center', va='bottom', fontsize=11)
        
        ax.set_title(title, fontsize=16, fontweight='bold', pad=20)
        ax.set_ylabel(ylabel, fontsize=12)
        ax.spines['top'].set_visible(False)
        ax.spines['right'].set_visible(False)
        
        plt.tight_layout()
        
        buf = BytesIO()
        fig.savefig(buf, format='png', dpi=150, bbox_inches='tight')
        buf.seek(0)
        plt.close(fig)
        return buf
    
    def line_chart(self, x_data: list, series: dict,
                   title: str = "", ylabel: str = "") -> BytesIO:
        """生成折线图"""
        fig, ax = plt.subplots(figsize=(10, 6))
        
        for i, (name, values) in enumerate(series.items()):
            color = self.color_palette[i % len(self.color_palette)]
            ax.plot(x_data, values, marker='o', label=name,
                    color=color, linewidth=2, markersize=6)
        
        ax.set_title(title, fontsize=16, fontweight='bold', pad=20)
        ax.set_ylabel(ylabel, fontsize=12)
        ax.legend(fontsize=11)
        ax.grid(True, alpha=0.3)
        ax.spines['top'].set_visible(False)
        ax.spines['right'].set_visible(False)
        
        plt.tight_layout()
        
        buf = BytesIO()
        fig.savefig(buf, format='png', dpi=150, bbox_inches='tight')
        buf.seek(0)
        plt.close(fig)
        return buf
    
    def pie_chart(self, labels: list, values: list,
                  title: str = "") -> BytesIO:
        """生成饼图"""
        fig, ax = plt.subplots(figsize=(8, 8))
        
        colors = self.color_palette[:len(labels)]
        wedges, texts, autotexts = ax.pie(
            values, labels=labels, colors=colors,
            autopct='%1.1f%%', startangle=90,
            textprops={'fontsize': 12}
        )
        
        for autotext in autotexts:
            autotext.set_fontsize(11)
            autotext.set_fontweight('bold')
        
        ax.set_title(title, fontsize=16, fontweight='bold', pad=20)
        plt.tight_layout()
        
        buf = BytesIO()
        fig.savefig(buf, format='png', dpi=150, bbox_inches='tight')
        buf.seek(0)
        plt.close(fig)
        return buf
    
    def add_chart_to_slide(self, slide, chart_buf: BytesIO,
                           left: float = 1.0, top: float = 1.8,
                           width: float = 10, height: float = 5):
        """将图表插入幻灯片"""
        from pptx.util import Inches
        slide.shapes.add_picture(
            chart_buf, Inches(left), Inches(top),
            Inches(width), Inches(height)
        )


# 使用示例
chart_gen = ChartGenerator()

# 生成柱状图
buf = chart_gen.bar_chart(
    ['北京', '上海', '广州', '深圳', '杭州'],
    [3200, 2800, 1900, 2100, 1500],
    title='各区域Q4销售额(万元)',
    ylabel='销售额(万元)'
)

# 生成折线图
buf2 = chart_gen.line_chart(
    ['1月', '2月', '3月', '4月', '5月', '6月'],
    {'2023': [100, 120, 135, 148, 160, 175], '2024': [130, 155, 170, 195, 210, 240]},
    title='月度用户增长趋势',
    ylabel='用户数(万)'
)

5.2 动态数据绑定

import pandas as pd
from datetime import datetime

class DataDrivenSlideBuilder:
    """数据驱动的幻灯片构建器"""
    
    def __init__(self, factory: SlideFactory, chart_gen: ChartGenerator):
        self.factory = factory
        self.chart_gen = chart_gen
    
    def build_from_dataframe(self, df: pd.DataFrame, title: str,
                             chart_type: str = "column"):
        """从DataFrame自动生成数据幻灯片"""
        
        # 自动识别数据列类型
        numeric_cols = df.select_dtypes(include=['number']).columns.tolist()
        category_cols = df.select_dtypes(include=['object', 'category']).columns.tolist()
        
        if not category_cols or not numeric_cols:
            raise ValueError("DataFrame需包含至少一个分类列和一个数值列")
        
        # 构建图表数据
        categories = df[category_cols[0]].tolist()
        series_data = {}
        for col in numeric_cols[:4]:  # 最多4个系列
            series_data[col] = df[col].tolist()
        
        # 添加数据幻灯片
        self.factory.add_data_slide(title, chart_type, categories, series_data)
    
    def build_summary_slide(self, metrics: dict, title: str = "关键指标总览"):
        """构建指标汇总页"""
        slide = self.factory.prs.slides.add_slide(
            self.factory.prs.slide_layouts[6]
        )
        
        # 标题
        self.factory._add_textbox(slide, 0.8, 0.4, 11, 0.8, title,
                                  font_size=28, bold=True,
                                  color=self.factory.theme.primary_color)
        
        # 指标卡片网格(2行3列)
        positions = [
            (0.8, 1.8), (4.8, 1.8), (8.8, 1.8),
            (0.8, 4.2), (4.8, 4.2), (8.8, 4.2),
        ]
        
        for i, (name, value) in enumerate(list(metrics.items())[:6]):
            if i >= len(positions):
                break
            left, top = positions[i]
            
            # 卡片背景
            card = slide.shapes.add_shape(
                1, Inches(left), Inches(top),
                Inches(3.5), Inches(2)
            )
            card.fill.solid()
            card.fill.fore_color.rgb = RGBColor(0xF5, 0xF7, 0xFA)
            card.line.fill.background()
            
            # 指标名称
            self.factory._add_textbox(slide, left + 0.3, top + 0.3,
                                     3, 0.5, name,
                                     font_size=14,
                                     color=self.factory.theme.light_text)
            
            # 指标值
            self.factory._add_textbox(slide, left + 0.3, top + 0.9,
                                     3, 0.8, str(value),
                                     font_size=32, bold=True,
                                     color=self.factory.theme.primary_color)

六、多格式导出系统

6.1 PPT转PDF

import subprocess
import os

class DocumentExporter:
    """多格式文档导出器"""
    
    @staticmethod
    def pptx_to_pdf_libreoffice(input_path: str, output_dir: str = None):
        """使用LibreOffice将PPTX转换为PDF"""
        if output_dir is None:
            output_dir = os.path.dirname(input_path)
        
        cmd = [
            'libreoffice', '--headless', '--convert-to', 'pdf',
            '--outdir', output_dir, input_path
        ]
        
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
        if result.returncode != 0:
            raise RuntimeError(f"转换失败: {result.stderr}")
        
        pdf_name = os.path.splitext(os.path.basename(input_path))[0] + '.pdf'
        return os.path.join(output_dir, pdf_name)
    
    @staticmethod
    def pptx_to_html(input_path: str, output_path: str):
        """将PPTX转换为HTML(简化版)"""
        from pptx import Presentation
        
        prs = Presentation(input_path)
        
        html_parts = ['''<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Presentation</title>
    <style>
        body { font-family: 'Microsoft YaHei', sans-serif; margin: 0; background: #f0f0f0; }
        .slide { width: 960px; height: 540px; margin: 20px auto; background: white; 
                 box-shadow: 0 2px 10px rgba(0,0,0,0.1); padding: 40px; 
                 box-sizing: border-box; position: relative; overflow: hidden; }
        .slide-number { position: absolute; bottom: 10px; right: 20px; color: #999; font-size: 12px; }
        h1 { color: #1A365D; font-size: 28px; }
        p { color: #333; font-size: 16px; line-height: 1.8; }
    </style>
</head>
<body>''']
        
        for i, slide in enumerate(prs.slides, 1):
            html_parts.append(f'<div class="slide" id="slide-{i}">')
            
            for shape in slide.shapes:
                if shape.has_text_frame:
                    for para in shape.text_frame.paragraphs:
                        text = para.text.strip()
                        if text:
                            if any(run.font.bold for run in para.runs if run.font.bold):
                                html_parts.append(f'  <h1>{text}</h1>')
                            else:
                                html_parts.append(f'  <p>{text}</p>')
                elif hasattr(shape, 'image'):
                    import base64
                    img_data = base64.b64encode(shape.image.blob).decode()
                    ext = shape.image.content_type.split('/')[-1]
                    html_parts.append(
                        f'  <img src="data:image/{ext};base64,{img_data}" '
                        f'style="max-width:100%;max-height:400px;">'
                    )
            
            html_parts.append(f'  <div class="slide-number">{i}</div>')
            html_parts.append('</div>')
        
        html_parts.append('</body></html>')
        
        with open(output_path, 'w', encoding='utf-8') as f:
            f.write('\n'.join(html_parts))
        
        return output_path
    
    @staticmethod
    def generate_thumbnail(input_path: str, output_path: str, 
                          slide_number: int = 0):
        """生成幻灯片缩略图(需要pdf2image)"""
        from pdf2image import convert_from_path
        
        # 先转PDF
        pdf_path = DocumentExporter.pptx_to_pdf_libreoffice(input_path)
        
        # PDF转图片
        images = convert_from_path(pdf_path, first_page=slide_number+1,
                                   last_page=slide_number+1, dpi=150)
        if images:
            images[0].save(output_path, 'PNG')
            return output_path
        return None

七、Word文档AI生成

7.1 python-docx程序化生成

from docx import Document
from docx.shared import Inches, Pt, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT

class ReportGenerator:
    """Word报告生成器"""
    
    def __init__(self, title: str, author: str = ""):
        self.doc = Document()
        self.title = title
        self.author = author
        self._setup_styles()
    
    def _setup_styles(self):
        """设置文档样式"""
        # 设置默认字体
        style = self.doc.styles['Normal']
        style.font.name = '宋体'
        style.font.size = Pt(12)
        style.paragraph_format.line_spacing = 1.5
        
        # 设置标题样式
        for level in range(1, 4):
            heading_style = self.doc.styles[f'Heading {level}']
            heading_style.font.name = '微软雅黑'
            heading_style.font.color.rgb = RGBColor(0x1A, 0x36, 0x5D)
    
    def add_cover_page(self, date: str = None):
        """添加封面页"""
        if date is None:
            from datetime import datetime
            date = datetime.now().strftime("%Y年%m月")
        
        # 空行
        for _ in range(6):
            self.doc.add_paragraph()
        
        # 标题
        title_para = self.doc.add_paragraph()
        title_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
        run = title_para.add_run(self.title)
        run.font.size = Pt(36)
        run.font.bold = True
        run.font.color.rgb = RGBColor(0x1A, 0x36, 0x5D)
        
        # 日期
        date_para = self.doc.add_paragraph()
        date_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
        run = date_para.add_run(date)
        run.font.size = Pt(16)
        run.font.color.rgb = RGBColor(0x66, 0x66, 0x66)
        
        if self.author:
            author_para = self.doc.add_paragraph()
            author_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
            run = author_para.add_run(self.author)
            run.font.size = Pt(14)
        
        self.doc.add_page_break()
    
    def add_table(self, headers: list, rows: list, title: str = None):
        """添加表格"""
        if title:
            self.doc.add_heading(title, level=2)
        
        table = self.doc.add_table(rows=len(rows)+1, cols=len(headers))
        table.style = 'Light Grid Accent 1'
        table.alignment = WD_TABLE_ALIGNMENT.CENTER
        
        # 表头
        for i, header in enumerate(headers):
            cell = table.rows[0].cells[i]
            cell.text = header
            for paragraph in cell.paragraphs:
                for run in paragraph.runs:
                    run.font.bold = True
        
        # 数据行
        for row_idx, row_data in enumerate(rows, 1):
            for col_idx, value in enumerate(row_data):
                table.rows[row_idx].cells[col_idx].text = str(value)
        
        self.doc.add_paragraph()  # 表格后空行
    
    def add_chart_image(self, image_path: str, width: float = 6.0,
                       caption: str = ""):
        """添加图表图片"""
        self.doc.add_picture(image_path, width=Inches(width))
        
        if caption:
            cap_para = self.doc.add_paragraph()
            cap_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
            run = cap_para.add_run(caption)
            run.font.size = Pt(10)
            run.font.color.rgb = RGBColor(0x99, 0x99, 0x99)
    
    def save(self, path: str):
        """保存文档"""
        self.doc.save(path)
        print(f"报告已保存: {path}")


# 使用示例
report = ReportGenerator("2024年第四季度销售业绩报告", "销售部")
report.add_cover_page("2024年12月")

report.doc.add_heading("一、执行摘要", level=1)
report.doc.add_paragraph(
    "本季度销售额达到3200万元,同比增长35.8%。其中华东区域表现最为突出,"
    "贡献了总销售额的42%。新客户获取成本较上季度降低18%,客户留存率提升至92.5%。"
)

report.add_table(
    headers=["区域", "Q3销售额", "Q4销售额", "环比增长"],
    rows=[
        ["华东", "1,200万", "1,344万", "+12%"],
        ["华南", "800万", "880万", "+10%"],
        ["华北", "600万", "720万", "+20%"],
        ["西部", "400万", "256万", "-36%"],
    ],
    title="各区域业绩对比"
)

report.save("Q4_sales_report.docx")

八、完整案例:季度报告自动化生成系统

8.1 系统架构

"""
季度报告自动化生成系统
整合LLM内容生成、图表生成、PPT/Word导出的完整流水线
"""

import json
import os
from datetime import datetime
from dataclasses import dataclass, field

@dataclass
class ReportConfig:
    """报告配置"""
    company_name: str
    report_period: str
    department: str
    template_path: str = None
    output_dir: str = "./output"
    include_pptx: bool = True
    include_docx: bool = True
    include_pdf: bool = True
    include_html: bool = True
    theme_colors: dict = field(default_factory=lambda: {
        'primary': '#1A365D',
        'secondary': '#2B579A',
        'accent': '#E86C00'
    })


class QuarterlyReportPipeline:
    """季度报告自动化流水线"""
    
    def __init__(self, config: ReportConfig, llm_api_key: str,
                 llm_base_url: str = None):
        self.config = config
        self.outline_gen = PPTOutlineGenerator(llm_api_key, llm_base_url)
        self.chart_gen = ChartGenerator()
        self.exporter = DocumentExporter()
        
        os.makedirs(config.output_dir, exist_ok=True)
    
    def run(self, data_source: dict, supplementary_text: str = ""):
        """执行完整报告生成流水线"""
        print(f"{'='*50}")
        print(f"开始生成 {self.config.report_period} 季度报告")
        print(f"{'='*50}")
        
        # Step 1: LLM生成大纲
        print("\n[1/6] 生成报告大纲...")
        outline = self.outline_gen.generate_outline(
            f"{self.config.company_name} {self.config.report_period} "
            f"{self.config.department}季度报告",
            num_slides=12,
            style="professional"
        )
        
        # Step 2: 生成图表
        print("[2/6] 生成数据图表...")
        charts = self._generate_charts(data_source)
        
        # Step 3: 构建PPT
        if self.config.include_pptx:
            print("[3/6] 构建PPT演示文稿...")
            pptx_path = self._build_pptx(outline, charts, data_source)
        else:
            pptx_path = None
        
        # Step 4: 构建Word报告
        if self.config.include_docx:
            print("[4/6] 构建Word报告...")
            docx_path = self._build_docx(outline, charts, data_source)
        else:
            docx_path = None
        
        # Step 5: 转换格式
        print("[5/6] 导出多格式文件...")
        outputs = {'pptx': pptx_path, 'docx': docx_path}
        
        if pptx_path and self.config.include_pdf:
            try:
                pdf_path = self.exporter.pptx_to_pdf_libreoffice(pptx_path)
                outputs['pdf'] = pdf_path
                print(f"  ✓ PDF: {pdf_path}")
            except Exception as e:
                print(f"  ✗ PDF转换失败: {e}")
        
        if pptx_path and self.config.include_html:
            html_path = os.path.join(self.config.output_dir, 'report.html')
            self.exporter.pptx_to_html(pptx_path, html_path)
            outputs['html'] = html_path
            print(f"  ✓ HTML: {html_path}")
        
        # Step 6: 生成摘要
        print("[6/6] 生成执行摘要...")
        summary = self._generate_summary(outline, data_source)
        
        print(f"\n{'='*50}")
        print("报告生成完成!")
        for fmt, path in outputs.items():
            if path:
                print(f"  {fmt.upper()}: {path}")
        print(f"{'='*50}")
        
        return outputs
    
    def _generate_charts(self, data: dict) -> dict:
        """生成所有图表"""
        charts = {}
        
        # 营收趋势图
        if 'revenue' in data:
            charts['revenue_trend'] = self.chart_gen.line_chart(
                data['revenue']['months'],
                data['revenue']['series'],
                title='月度营收趋势',
                ylabel='营收(万元)'
            )
        
        # 区域分布饼图
        if 'regional' in data:
            charts['regional_dist'] = self.chart_gen.pie_chart(
                data['regional']['labels'],
                data['regional']['values'],
                title='区域销售分布'
            )
        
        # 产品对比柱状图
        if 'products' in data:
            charts['product_compare'] = self.chart_gen.bar_chart(
                data['products']['names'],
                data['products']['sales'],
                title='产品线销售对比',
                ylabel='销售额(万元)'
            )
        
        return charts
    
    def _build_pptx(self, outline: dict, charts: dict, data: dict) -> str:
        """构建PPT文件"""
        theme = PresentationTheme()
        factory = SlideFactory(theme)
        
        # 封面
        factory.add_title_slide(
            outline.get('title', f'{self.config.report_period}季度报告'),
            outline.get('subtitle', f'{self.config.department} · {self.config.company_name}')
        )
        
        # 内容页
        for slide_data in outline.get('slides', []):
            slide_type = slide_data.get('type', 'content')
            
            if slide_type == 'content':
                factory.add_content_slide(
                    slide_data['title'],
                    slide_data.get('content', [])
                )
            elif slide_type == 'two_column':
                factory.add_two_column_slide(
                    slide_data['title'],
                    slide_data.get('left_content', []),
                    slide_data.get('right_content', [])
                )
            elif slide_type == 'data':
                # 尝试嵌入图表
                chart_key = slide_data.get('chart_hint', '')
                if chart_key in charts:
                    slide = factory.prs.slides.add_slide(
                        factory.prs.slide_layouts[6]
                    )
                    factory._add_textbox(slide, 0.8, 0.4, 11, 0.8,
                                        slide_data['title'], font_size=28,
                                        bold=True, color=theme.primary_color)
                    factory.chart_gen = self.chart_gen
                    self.chart_gen.add_chart_to_slide(slide, charts[chart_key])
        
        # 结尾页
        factory.add_title_slide("谢谢", f"Q&A · {self.config.company_name}")
        
        path = os.path.join(self.config.output_dir, 
                           f'{self.config.report_period}_report.pptx')
        factory.save(path)
        return path
    
    def _build_docx(self, outline: dict, charts: dict, data: dict) -> str:
        """构建Word文档"""
        report = ReportGenerator(
            outline.get('title', f'{self.config.report_period}季度报告'),
            author=self.config.department
        )
        
        report.add_cover_page(self.config.report_period)
        
        # 目录占位
        report.doc.add_heading("目录", level=1)
        report.doc.add_paragraph("(请在Word中右键更新目录域)")
        report.doc.add_page_break()
        
        # 内容
        for i, slide_data in enumerate(outline.get('slides', []), 1):
            report.doc.add_heading(f"{i}. {slide_data['title']}", level=1)
            
            for point in slide_data.get('content', []):
                report.doc.add_paragraph(point, style='List Bullet')
            
            # 添加相关图表
            chart_key = slide_data.get('chart_hint', '')
            if chart_key and chart_key in charts:
                chart_path = os.path.join(self.config.output_dir,
                                         f'chart_{chart_key}.png')
                charts[chart_key].seek(0)
                with open(chart_path, 'wb') as f:
                    f.write(charts[chart_key].read())
                charts[chart_key].seek(0)
                report.add_chart_image(chart_path, caption=slide_data['title'])
        
        path = os.path.join(self.config.output_dir,
                           f'{self.config.report_period}_report.docx')
        report.save(path)
        return path
    
    def _generate_summary(self, outline: dict, data: dict) -> str:
        """生成执行摘要"""
        # 简化版:从大纲中提取关键信息
        key_points = []
        for slide in outline.get('slides', []):
            if slide.get('priority', 0) <= 3:
                key_points.extend(slide.get('content', [])[:2])
        
        summary = "执行摘要:\n\n"
        for i, point in enumerate(key_points[:5], 1):
            summary += f"{i}. {point}\n"
        
        summary_path = os.path.join(self.config.output_dir, 'summary.txt')
        with open(summary_path, 'w', encoding='utf-8') as f:
            f.write(summary)
        
        return summary


# ========== 使用示例 ==========

def demo_quarterly_report():
    """演示:生成季度报告"""
    
    # 配置
    config = ReportConfig(
        company_name="智远科技",
        report_period="2024-Q4",
        department="销售部",
        output_dir="./quarterly_report_output",
        include_pptx=True,
        include_docx=True,
        include_pdf=True,
        include_html=True
    )
    
    # 模拟业务数据
    data = {
        'revenue': {
            'months': ['10月', '11月', '12月'],
            'series': {
                '2023': [950, 1050, 1200],
                '2024': [1200, 1350, 1650]
            }
        },
        'regional': {
            'labels': ['华东', '华南', '华北', '西部', '海外'],
            'values': [35, 25, 20, 12, 8]
        },
        'products': {
            'names': ['AI平台', '数据服务', '智能硬件', '咨询'],
            'sales': [1800, 650, 480, 270]
        }
    }
    
    # 运行流水线(需要有效的API Key)
    # pipeline = QuarterlyReportPipeline(
    #     config, 
    #     llm_api_key="your-api-key",
    #     llm_base_url="https://api.openai.com/v1"
    # )
    # outputs = pipeline.run(data)
    
    print("季度报告自动化生成系统已配置完成")
    print(f"输出目录: {config.output_dir}")
    print(f"生成格式: PPTX={config.include_pptx}, DOCX={config.include_docx}, "
          f"PDF={config.include_pdf}, HTML={config.include_html}")

demo_quarterly_report()

九、API集成与批量生成

9.1 REST API封装

from fastapi import FastAPI, HTTPException, BackgroundTasks
from pydantic import BaseModel
from typing import Optional
import uuid

app = FastAPI(title="AI文档生成服务")

class GenerateRequest(BaseModel):
    topic: str
    num_slides: int = 10
    style: str = "professional"
    format: list = ["pptx"]  # pptx, docx, pdf, html
    data: Optional[dict] = None

class GenerateResponse(BaseModel):
    task_id: str
    status: str
    message: str

# 任务存储
tasks = {}

def generate_task(task_id: str, request: GenerateRequest):
    """后台生成任务"""
    try:
        tasks[task_id]['status'] = 'processing'
        
        config = ReportConfig(
            company_name="自动生成",
            report_period=request.topic,
            department="",
            output_dir=f"./output/{task_id}",
            include_pptx='pptx' in request.format,
            include_docx='docx' in request.format,
            include_pdf='pdf' in request.format,
            include_html='html' in request.format
        )
        
        # pipeline = QuarterlyReportPipeline(config, os.environ['LLM_API_KEY'])
        # outputs = pipeline.run(request.data or {})
        
        tasks[task_id]['status'] = 'completed'
        tasks[task_id]['files'] = {'pptx': f'./output/{task_id}/report.pptx'}
        
    except Exception as e:
        tasks[task_id]['status'] = 'failed'
        tasks[task_id]['error'] = str(e)

@app.post("/api/generate", response_model=GenerateResponse)
async def generate_report(request: GenerateRequest, 
                         background_tasks: BackgroundTasks):
    """提交报告生成任务"""
    task_id = str(uuid.uuid4())[:8]
    tasks[task_id] = {'status': 'queued'}
    
    background_tasks.add_task(generate_task, task_id, request)
    
    return GenerateResponse(
        task_id=task_id,
        status='queued',
        message='报告生成任务已提交'
    )

@app.get("/api/task/{task_id}")
async def get_task_status(task_id: str):
    """查询任务状态"""
    if task_id not in tasks:
        raise HTTPException(status_code=404, detail="任务不存在")
    return tasks[task_id]

# 启动命令: uvicorn main:app --host 0.0.0.0 --port 8000

9.2 批量生成脚本

import csv
import os
from concurrent.futures import ThreadPoolExecutor, as_completed

class BatchGenerator:
    """批量报告生成器"""
    
    def __init__(self, pipeline_factory, max_workers: int = 4):
        self.pipeline_factory = pipeline_factory
        self.max_workers = max_workers
    
    def generate_from_csv(self, csv_path: str, template_config: dict):
        """从CSV批量生成报告"""
        
        with open(csv_path, 'r', encoding='utf-8-sig') as f:
            reader = csv.DictReader(f)
            tasks = list(reader)
        
        print(f"读取到 {len(tasks)} 个生成任务")
        
        results = []
        with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
            futures = {}
            for i, row in enumerate(tasks):
                config = ReportConfig(
                    company_name=row.get('company', '未命名'),
                    report_period=row.get('period', f'Task-{i}'),
                    department=row.get('department', ''),
                    output_dir=f"./batch_output/{i}",
                    **template_config
                )
                
                pipeline = self.pipeline_factory(config)
                future = executor.submit(pipeline.run, json.loads(row.get('data', '{}')))
                futures[future] = row
            
            for future in as_completed(futures):
                row = futures[future]
                try:
                    outputs = future.result()
                    results.append({
                        'period': row.get('period'),
                        'status': 'success',
                        'files': outputs
                    })
                    print(f"  ✓ {row.get('period')} 完成")
                except Exception as e:
                    results.append({
                        'period': row.get('period'),
                        'status': 'failed',
                        'error': str(e)
                    })
                    print(f"  ✗ {row.get('period')} 失败: {e}")
        
        return results

# 使用示例CSV格式:
# company,period,department,data
# 智远科技,2024-Q1,销售部,"{""revenue"": {...}}"
# 智远科技,2024-Q2,销售部,"{""revenue"": {...}}"
# 智远科技,2024-Q3,销售部,"{""revenue"": {...}}"

十、最佳实践与常见问题

10.1 性能优化建议

  1. 缓存LLM结果:相同主题的大纲生成结果应缓存,避免重复调用API
  2. 并行图表生成:多个图表可以使用ThreadPoolExecutor并行生成
  3. 模板预加载:将常用的PPT模板预加载为Presentation对象,避免重复解析
  4. 图片压缩:插入PPT前对图片进行压缩,减小文件体积

10.2 常见问题

Q: 中文字体在不同系统上显示不一致? A: 在代码中指定通用字体(如"微软雅黑"、"宋体"),或在PPT中嵌入字体:

# python-pptx暂不支持字体嵌入,需在模板中预设
# 或使用LibreOffice转换时指定字体

Q: 生成的图表中文显示为方块? A: 确保系统安装了中文字体,并正确设置matplotlib:

import matplotlib.font_manager as fm
# 查看可用字体
for font in fm.fontManager.ttflist:
    if 'Hei' in font.name or 'Song' in font.name:
        print(font.name, font.fname)

Q: LibreOffice转换PDF失败? A: 确保安装了LibreOffice:

# Ubuntu/Debian
sudo apt-get install libreoffice-impress

# CentOS/RHEL
sudo yum install libreoffice-impress

Q: 批量生成时API限流? A: 实现指数退避重试:

import time

def call_with_retry(func, max_retries=3, base_delay=1):
    for attempt in range(max_retries):
        try:
            return func()
        except Exception as e:
            if 'rate_limit' in str(e).lower() and attempt < max_retries - 1:
                delay = base_delay * (2 ** attempt)
                print(f"限流,{delay}秒后重试...")
                time.sleep(delay)
            else:
                raise

十一、总结

AI PPT与文档自动生成技术已经从概念验证走向生产可用。核心要点:

  1. 工具选择:快速原型用Gamma,企业级批量生成用python-pptx + LLM
  2. 内容结构化:LLM负责内容生成和结构化,代码负责排版和渲染
  3. 模板系统:建立统一的主题/样式系统,确保输出一致性
  4. 多格式输出:PPTX + PDF + HTML + Word满足不同分发场景
  5. 流水线化:将数据获取→内容生成→图表制作→文档组装串联为自动化流水线
  6. API化服务:封装为REST API支持按需调用和批量任务

掌握这些技术,你可以为团队构建一个"输入数据,输出报告"的全自动文档工厂,将原本数小时的文档制作工作压缩到分钟级别。


本教程配套完整代码已开源,欢迎Star和PR。

内容声明

本文内容为AI技术学习教程,仅供学习参考。如涉及技术问题,欢迎通过 xurj005@163.com 与我们交流。

目录