背景痛点:熬夜做 P版,不如写代码
临近开题答辩,实验室里最常见的场景是:
- 开题报告已经写完,却还要把 1.5 万字重新拆成 15 页 PPT
- 导师一句“格式按学院模板”让人陷入手动调字体、调行距、拼图表的循环
- 复制粘贴时,章节标题对不上,页脚日期忘记改,最后 5 分钟还在抢救页码
传统做法的时间分布大致是:搜集素材 20%、调格式 50%、真正思考“讲什么”只剩 30%。AI 辅助不是炫技,而是把 50% 的机械劳动拿回来,让毕业生把脑力留给“研究内容”与“创新点”。
技术选型:四件套够用,别堆料
| 工具 | 职责 | 选用理由 | 踩坑记录 |
|---|---|---|---|
| LangChain | 文档加载+链式提示 | 支持 Word/Markdown 解析器,社区文档全 | 版本迭代快,接口常改名 |
| LlamaIndex | 语义切块+检索 | 对长文本分段更细腻,可插本地向量库 | 依赖多,Windows 下装 GPU 驱动折腾 |
| python-pptx | 幻灯片渲染 | 纯 Python,无 COM 依赖,Linux 服务器友好 | 不支持 LaTeX,公式得先转图片 |
| Pandoc | 格式中转 | 把 Docx 一键转 Markdown,丢给 LangChain | 公式域在转换时易丢字号信息 |
结论:
- 文本简单、章节清晰 → LangChain 足够
- 报告里图表、公式多 → 先用 Pandoc 转 Markdown,再用 LlamaIndex 建索引,防止大模型“张冠李戴”
- 最终渲染统一用 python-pptx,方便在 CI 里跑 Docker,不依赖本机 Office
核心实现:一条流水线拆成 4 步
解析开题报告
用 LangChain 的UnstructuredWordLoader或MarkdownLoader读入文本,按“一级标题”切片,得到列表sections=[{title, content}, …]。构造“学术 PPT 提示词”
把每段内容塞进以下模板,让大模型生成 3 张幻灯片以内,控制字数 50~70 字/页:你是一位高校导师,正在帮学生把开题报告提炼为答辩 PPT。 输入章节标题:{title} 输入正文:{content} 要求: - 每页只列 3 行要点,每行≤15 字 - 保留关键数据,删除形容词 - 结尾给出一张图建议:坐标系/柱状图/思维导图 输出格式:{"slides":[{"title":"…","bullets":[…],"chart":"…"}]}结果校验
- 用 jsonschema 校验返回结构,缺失字段就让模型重试,最多 3 次
- 把“图建议”字段单独抽出来,丢给 matplotlib/plotly 生成 PNG,供后续占位
python-pptx 渲染
读取学院模板template.pptx,严格复用母版里的占位符,保证字体、配色、页脚日期与官方一致;把图表路径写进Picture对象,避免“贴图”漂移。
代码示例:Clean Code 版
以下脚本在 Python3.11 通过,依赖见注释。可直接python main.py --paper thesis.docx --template college.pptx一键出片。
#!/usr/bin/env python3 """ ai_ppt.py Generate defense slides from thesis Author: your_name """ import json, pathlib, argparse from langchain.document_loaders import UnstructuredWordDocumentLoader from langchain.chains import LLMChain from langchain.prompts import PromptTemplate from pptx import Presentation from pptx.util import Inches # ---------- 1. 命令行 ---------- def parse_args(): parser = argparse.ArgumentParser() parser.add_argument("--paper", required=True, help="开题报告路径") parser.add_argument("--template", required=True, help="学院模板路径") parser.add_argument("--output", default="defense.pptx") return parser.parse_args() # ---------- 2. 文本切片 ---------- def load_sections(paper_path: str): loader = UnstructuredWordDocumentLoader(paper_path, mode="elements") docs = loader.load() sections = [] for doc in docs: if doc.metadata.get("category") == "Title": sections.append({"title": doc.text, "content": ""}) elif sections and "content" in sections[-1]: sections[-1]["content"] += doc.text return sections # ---------- 3. 提示工程 ---------- prompt = PromptTemplate( input_variables=["title", "content"], template=""" 你是一位高校导师,正在帮学生把开题报告提炼为答辩 PPT。 输入章节标题:{title} 输入正文:{content} 要求: - 每页只列 3 行要点,每行≤15 字 - 保留关键数据,删除形容词 - 结尾给出一张图建议:坐标系/柱状图/思维导图 输出格式:{"slides":[{"title":"…","bullets":[…],"chart":"…"}]} """ ) def generate_slides(llm, title: str, content: str): chain = LLMChain(llm=llm, prompt=prompt) retry = 3 while retry: try: text = chain.run(title=title, content=content) data = json.loads(text) jsonschema.validate(data, SCHEMA) return data["slides"] except Exception as e: retry -= 1 return [] # ---------- 4. 渲染 ---------- def build_ppt(slides, template: str, output: str): prs = Presentation(template) blank = prs.slide_layouts[6] # 空白版式,自己占位 for s in slides: slide = prs.slides.add_slide(blank) left, top = Inches(1), Inches(1) txBox = slide.shapes.add_textbox(left, top, Inches(8), Inches(6)) tf = txBox.text_frame tf.text = s["title"] for b in s["bullets"]: p = tf.add_paragraph() p.text, p.level = b, 1 if s.get("chart"): slide.shapes.add_picture(s["chart"], Inches(1), Inches(4), width=Inches(4)) prs.save(output) # ---------- 5. main ---------- def main(): args = parse_args() sections = load_sections(args.paper) # 这里 llm 可用本地 llama-cpp-python 或 OpenAI API from langchain.llms import OpenAI llm = OpenAI(temperature=0.2, max_tokens=800) all_slides = [] for sec in sections: all_slides.extend(generate_slides(llm, sec["title"], sec["content"])) build_ppt(all_slides, args.template, args.output) print("Saved to", args.output) if __name__ == "__main__": main()要点注释:
- 用
jsonschema强制结构,过滤幻觉 - 模板母版决定字体,代码只写文本内容,防止“风格漂移”
- 图表路径提前生成,png 分辨率 300 dpi,投影不糊
准确性、合规性与安全性
准确性
- 让模型只“提炼”不“发明”,禁止出现未在原文出现的数据
- 对“研究基础”“创新点”等敏感章节,加入“请只使用输入文本原句”额外指令
学术合规
- 生成的要点需经学生本人通读,并在 PPT 备注栏手写讲解词,防止“AI 代写”嫌疑
- 图表引用自论文,则保留坐标轴、单位、参考文献,3 行字说明来源
本地化部署
- 学院内网常断,建议用 llama-cpp-python+4bit 量化,8G 显存可跑 7B 模型
- 把容器镜像放私有仓,代码走 GitLab CI,日志脱敏,不上传云端
生产环境避坑清单
模型幻觉控制
温度<0.3,top-p<0.8,并在提示尾部加“若信息不足请回复‘无’”。模板版本管理
母版变动后,把template.pptx纳入 Git LFS,文件名带日期;渲染脚本同步打 tag,保证历史答辩可复现。字体与公式
python-pptx 不支持 OTF 数学字体,公式提前用 Matplotlib 渲染为 PNG,DPI≥300,背景透明。多语言混排
中文段落+英文缩写,先跑pypinyin检测长度,超长自动换行,防止溢出。图表颜色
学院主色调 #004098,写进theme_color.py,每次生成前校验 RGB,避免“蓝得不一样”。
留给读者的思考
AI 把 50% 的体力活自动化后,剩下 50% 恰恰是学术训练的核心:问题意识、逻辑推演与批判性表达。我们可以让模型“写得快”,但不应让它“替我们思考”。下一次点击“生成”前,不妨先问自己——如果 AI 没读过这篇开题报告,我能用三句话把研究价值讲清楚吗?当答案肯定时,再让算法帮你把字调到格子里,这才是人机协同的边界,也是毕业生对学术诚信应守的责任。