本地用pptx和大模型生产PPT课件

先在智谱提问,让它给出PPT大纲。

以下是为您规划的西门子 WinCC 7.4 脚本编程基础 PPT 大纲,共 20 页。内容精简,重点突出,适合制作教程型 PPT。每页包含标题和核心内容要点。

第 1 页:封面

标题:西门子 WinCC 7.4 脚本编程基础

副标题:从入门到实战的 20 页教程

演讲人:您的名字

日期:202X 年 X 月 X 日

配图:WinCC 7.4 图标或 SCADA 系统界面截图

第 2 页:课程目标与适用对象

适用对象:

WinCC 工程师、自动化调试人员

有 PLC 基础但需提升 HMI 交互能力的技术人员

学习目标:

掌握 WinCC 7.4 脚本环境

熟悉基本语法与数据操作

能够编写常用逻辑控制脚本

了解调试与错误处理

课程时长:建议 45 分钟讲解 + 15 分钟练习

第 3 页:WinCC 7.4 脚本引擎概述

核心语言:基于 VBScript 的扩展语法

然后让它给写出python源代码

以下代码当前时间是调试成功的:

requirements.txt

powershell 复制代码
在这里插入代码片annotated-types==0.7.0
anyio==4.13.0
certifi==2026.5.20
colorama==0.4.6
distro==1.9.0
forge-guardrails==0.7.2
h11==0.16.0
httpcore==1.0.9
httpx==0.28.1
idna==3.17
jiter==0.15.0
lxml==6.1.1
openai==2.38.0
pillow==12.2.0
pydantic==2.13.4
pydantic_core==2.46.4
python-pptx==1.0.2
PyYAML==6.0.3
sniffio==1.3.1
tqdm==4.67.3
typing-inspection==0.4.2
typing_extensions==4.15.0
xlsxwriter==3.2.9

config.yaml

python 复制代码
# LLM 后端配置 (直连 llama-server)
llm:
  base_url: "https://example.com/v1"
  api_key: "api-key"
  model_name: "glm-5"
  temperature: 0.7
  max_tokens: 16000 # 确保足够长,防止大纲较长时截断

# 大纲文件路径
outline_file: "content.txt"

# PPT 渲染配置
ppt_settings:
  output_dir: "./output"
  slide_width: 13.333
  slide_height: 7.5
  font_name: "微软雅黑"
  title_font_size: 36
  body_font_size: 20
  paragraph_spacing: 10
  

可以用阿里、智谱等的免费额度,也可以用本地llama.cpp,本地测试Phi-4-mini-reasoning-Q5_K_M.gguf会失败,总是输出报错。

main.py

python 复制代码
import os
import re
import yaml
import json
from pydantic import BaseModel, Field, ValidationError
from typing import List
from openai import OpenAI
from lxml import etree

from pptx import Presentation
from pptx.util import Pt, Inches
from pptx.enum.text import PP_ALIGN
from pptx.oxml.ns import qn

# ================= 加载配置 =================
def load_config(config_path="config.yaml"):
    if not os.path.exists(config_path):
        raise FileNotFoundError(f"配置文件 {config_path} 不存在!")
    with open(config_path, 'r', encoding='utf-8') as f:
        return yaml.safe_load(f)

CONFIG = load_config()
# ==========================================

# ================= 定义 Pydantic 模型 =================
# 用于验证 LLM 输出的 JSON 结构是否符合预期
class SlideContent(BaseModel):
    title: str = Field(description="当前幻灯片的标题")
    points: List[str] = Field(description="当前幻灯片的要点列表,每个要点不超过30字")

class PPTGenerationParams(BaseModel):
    slides: List[SlideContent] = Field(description="包含所有幻灯片内容的列表")
# =====================================================

def read_outline(file_path: str) -> str:
    """读取大纲文件"""
    if not os.path.exists(file_path):
        raise FileNotFoundError(f"大纲文件 {file_path} 不存在!")
    with open(file_path, 'r', encoding='utf-8') as f:
        return f.read().strip()

def generate_ppt_content(outline_content: str) -> list:
    """调用本地 LLM 生成 PPT 结构化内容,带强容错提取"""
    print(f"🤖 正在让 Qwen 基于大纲生成结构化数据...")
    
    llm_cfg = CONFIG['llm']
    client = OpenAI(base_url=llm_cfg['base_url'], api_key=llm_cfg['api_key'], timeout=300.0)

    system_prompt = """你是一个专业的PPT内容策划师。用户会提供一份现有的PPT大纲。
你的任务是基于这份大纲,输出PPT的结构化数据。
严格要求:
1. 必须完全遵循用户提供的大纲结构和页数,不得自行增删页面。
2. 提取大纲中的核心内容作为 title 和 points。
3. 如果大纲中的要点过于简略,你可以适当润色扩写,但绝不能偏离原意。
4. 【格式要求】你必须且只能输出一个合法的 JSON 数组,不要输出任何解释性文字,绝对不要用 markdown 代码块包裹。
格式示例:
[
  {"title": "第一页标题", "points": ["要点1", "要点2"]},
  {"title": "第二页标题", "points": ["要点1", "要点2"]}
]"""

    user_prompt = f"这是我的PPT大纲:\n---\n{outline_content}\n---\n请基于以上大纲,生成完整的PPT结构化JSON数据。"

    try:
        response = client.chat.completions.create(
            model=llm_cfg['model_name'],
            messages=[
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": user_prompt}
            ],
            temperature=llm_cfg['temperature'],
            max_tokens=llm_cfg['max_tokens']
        )
        raw_content = response.choices[0].message.content.strip()

        # ================= 强容错解析 =================
        # 1. 清洗可能存在的 Markdown 代码块标记
        content = re.sub(r'^```json\s*', '', raw_content)
        content = re.sub(r'^```\s*', '', content)
        content = re.sub(r'\s*```$', '', content)
        
        # 2. 核心:强制截取第一个 '[' 和最后一个 ']' 之间的内容
        # 这样可以无视模型在 JSON 前后输出的任何废话、思维链标记或乱码
        start_idx = content.find('[')
        end_idx = content.rfind(']')
        
        if start_idx != -1 and end_idx != -1 and end_idx > start_idx:
            json_str = content[start_idx:end_idx+1]
        else:
            # 如果连方括号都找不到,说明模型完全没有输出结构,直接报错
            print("❌ 模型输出中未找到合法的 JSON 数组标志 '[' 和 ']'")
            print(f"原始输出:\n{raw_content}")
            return None

        # 3. (新增) 去除 JSON 字符串内部可能混入的控制字符和不可见字符
        # 防止类似 \u200b (零宽空格) 等字符藏在 JSON 键名里导致解析失败
        json_str = re.sub(r'[\x00-\x1f\x7f-\x9f]', '', json_str)

        # 3. 解析 JSON
        data_list = json.loads(json_str)

        # 4. 使用 Pydantic 进行结构校验
        validated_data = PPTGenerationParams(slides=data_list)
        print("✅ 内容生成并校验成功!")
        return [slide.model_dump() for slide in validated_data.slides]

    except json.JSONDecodeError as e:
        print(f"❌ JSON 解析失败,模型可能输出了非法格式: {e}")
        print(f"原始输出:\n{raw_content}")
        return None
    except ValidationError as e:
        print(f"❌ 数据结构校验失败,模型输出的字段不符合要求: {e}")
        return None
    except Exception as e:
        print(f"❌ 调用 LLM 失败: {e}")
        return None

def set_chinese_font(run, font_name, font_size):
    """为文本块设置中文字体 (使用 lxml 原生操作,适配所有 python-pptx 版本)"""
    run.font.name = font_name
    run.font.size = font_size
    
    # 获取底层 rPr (文本属性) 节点
    rPr = run._r.get_or_add_rPr()
    
    # 尝试查找是否已存在东亚字体节点 <a:ea>
    ea = rPr.find(qn('a:ea'))
    
    if ea is None:
        # 如果不存在,使用 lxml 原生方法直接创建子节点
        ea = etree.SubElement(rPr, qn('a:ea'))
    
    # 设置 typeface 属性为指定的中文字体
    ea.set('typeface', font_name)

def create_ppt_file(slides_data: list, filename: str):
    """将 JSON 数据渲染为 PPT 文件"""
    print(f"🎨 正在渲染 PPT 文件...")
    ppt_cfg = CONFIG['ppt_settings']
    font_name = ppt_cfg['font_name']
    title_size = Pt(ppt_cfg['title_font_size'])
    body_size = Pt(ppt_cfg['body_font_size'])
    spacing = Pt(ppt_cfg['paragraph_spacing'])

    prs = Presentation()
    prs.slide_width = Inches(ppt_cfg['slide_width'])
    prs.slide_height = Inches(ppt_cfg['slide_height'])

    for slide_info in slides_data:
        title = slide_info.get("title", "无标题")
        points = slide_info.get("points", [])

        # 使用"标题和内容"布局
        slide_layout = prs.slide_layouts[1]
        slide = prs.slides.add_slide(slide_layout)

        # 渲染标题
        title_shape = slide.shapes.title
        title_shape.text = ""
        p = title_shape.text_frame.paragraphs[0]
        p.alignment = PP_ALIGN.LEFT
        run = p.add_run()
        run.text = title
        set_chinese_font(run, font_name, title_size)

        # 渲染正文
        body_shape = slide.shapes.placeholders[1]
        tf = body_shape.text_frame
        tf.clear()

        for i, point in enumerate(points):
            if i == 0:
                p = tf.paragraphs[0]
            else:
                p = tf.add_paragraph()
            
            p.space_before = spacing
            p.space_after = spacing
            p.level = 0
            
            run = p.add_run()
            run.text = point
            set_chinese_font(run, font_name, body_size)

    output_dir = ppt_cfg['output_dir']
    os.makedirs(output_dir, exist_ok=True)
    filepath = os.path.join(output_dir, filename)
    prs.save(filepath)
    print(f"🎉 PPT 已成功保存至: {os.path.abspath(filepath)}")

if __name__ == "__main__":
    outline_file = CONFIG.get('outline_file', 'content.txt')
    outline_content = read_outline(outline_file)
    print(f"📄 已读取大纲文件,共 {len(outline_content)} 字符。")

    slides = generate_ppt_content(outline_content)
    
    if slides:
        safe_filename = re.sub(r'[\\/:*?"<>|]', '', "output") + ".pptx"
        create_ppt_file(slides, filename=safe_filename)
    else:
        print("生成中断,请检查上方错误信息后重试。")
		

测试时,content.txt里的内容尽量短少,生成PPT成功后再用全部内容,避免浪费。

生成PPT的效果

不咋样,就是糊弄糊弄差事,正儿八经的讲课得润色。

本地纯CPU llama.cpp 9t/s,还是Phi-4-mini 尺寸2.65G。如果本地

相关推荐
czq_268671948711 小时前
Python打卡第45天
开发语言·python
java_prinln11 小时前
9. Agent 的长短期记忆系统怎么做的?记忆是怎么存的?粒度是多少?怎么用的?
数据库·python·agent
FOAF-lambda11 小时前
Python实战:基于OpenCV与单调栈,精准定位PDF中最大的空白区域
python·opencv·pdf
terry60011 小时前
企业数据查询接口防爬实战:图形验证码服务端校验接入与限流设计(附Python)
python·信息与通信·图形验证码·反爬虫·人机验证·行为验证码·滑动拼图验证
量化炼金 (CodeAlchemy)11 小时前
【Python】pandas vs polars 简单性能对比测试
开发语言·python·pandas
学习论之费曼学习法11 小时前
无人值守AI内容矩阵实战(7):Playwright 发布器避坑——编辑器竞态、索引延迟与风控
python·线性代数·矩阵·编辑器·浏览器自动化·playwright·踩坑记录
wuyk55511 小时前
PyQt6-第06讲-主窗口框架
python·pyqt
ss.li11 小时前
rppg相关论文阅读笔记10.22
论文阅读·笔记·python
tryCbest11 小时前
Python之Flask项目部署到阿里云
python·阿里云·flask
liwulin050612 小时前
【PYTORCH】迷你DECODER-ONLY模型
pytorch·python·深度学习