# -*- coding: utf-8 -*-
"""
生成《销售型生产企业 AI 数字化建设方案》策划案 PPT (.pptx)
- 两张图示(architecture.png / dataflow.png) 用 matplotlib 渲染
- 正文用【占位符】当可复用模板；演讲者备注含"示例填充 + 讲解话术"
- 成本页仅含硬件/云成本，不含服务费报价
"""
import os
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.patches import FancyBboxPatch
import matplotlib.font_manager as fm

# ---------- 路径 ----------
BASE = r"C:\Users\Administrator\Desktop\AI落地公司企划项目"
ASSETS = os.path.join(BASE, "assets")
os.makedirs(ASSETS, exist_ok=True)
ARCH_PNG = os.path.join(ASSETS, "architecture.png")
FLOW_PNG = os.path.join(ASSETS, "dataflow.png")
OUT_PPTX = os.path.join(BASE, "销售型生产企业AI数字化建设方案.pptx")

# ---------- matplotlib 中文字体 ----------
def setup_cjk_font():
    for p in [r"C:/Windows/Fonts/msyh.ttc", r"C:/Windows/Fonts/simhei.ttf",
              r"C:/Windows/Fonts/simsun.ttc", r"C:/Windows/Fonts/msyhbd.ttc"]:
        if os.path.exists(p):
            try:
                fm.fontManager.addfont(p)
            except Exception:
                pass
    cands = ["Microsoft YaHei", "SimHei", "SimSun", "Noto Sans CJK SC",
             "WenQuanYi Micro Hei", "Source Han Sans SC"]
    avail = {f.name for f in fm.fontManager.ttflist}
    chosen = next((c for c in cands if c in avail), None)
    if chosen:
        plt.rcParams["font.sans-serif"] = [chosen]
    plt.rcParams["axes.unicode_minus"] = False

setup_cjk_font()

# ---------- 图1：四层技术架构栈 ----------
def draw_architecture(path):
    fig, ax = plt.subplots(figsize=(12.5, 9))
    ax.set_xlim(0, 12.2)
    ax.set_ylim(0, 9.2)
    ax.axis("off")
    bands = [
        ("触达层", "第四层", "#2E93F7", ["Web 看板 (Streamlit)", "微信/钉钉/飞书推送", "经营简报(图文)"]),
        ("智能调度层", "第三层", "#1E6FB8", ["Hermes Agent", "Web 管理面板", "工具调用·消息网关"]),
        ("模型层", "第二层", "#15538C", ["Qwen3.6-35B (Ollama)", "DeepSeek V4 Flash 备用", "bge-m3 向量嵌入"]),
        ("数据层", "第一层", "#0E3D66", ["SQLite 数据库", "Python 清洗脚本", "Chroma 向量知识库"]),
        ("硬件与系统", "底层", "#0B2545", ["EVO-X2 128GB", "Ubuntu 24.04 LTS", "2×2TB NVMe·Tailscale"]),
    ]
    n = len(bands)
    bh, gap, bottom = 1.45, 0.22, 0.55
    for i, (name, tag, color, chips) in enumerate(bands):
        y = bottom + i * (bh + gap)
        band = FancyBboxPatch((0.4, y), 11.4, bh,
                              boxstyle="round,pad=0.02,rounding_size=0.12",
                              linewidth=0, facecolor=color, alpha=0.95)
        ax.add_patch(band)
        ax.text(0.7, y + bh/2 + 0.18, name, color="white", fontsize=17,
                fontweight="bold", va="center", ha="left")
        ax.text(0.7, y + bh/2 - 0.32, tag, color="#BBD6F2", fontsize=10,
                va="center", ha="left")
        cx, cw, sp = 3.3, 2.55, 0.16
        for j, c in enumerate(chips):
            x = cx + j * (cw + sp)
            chip = FancyBboxPatch((x, y + 0.30), cw, bh - 0.60,
                                  boxstyle="round,pad=0.02,rounding_size=0.08",
                                  linewidth=0, facecolor="white", alpha=0.94)
            ax.add_patch(chip)
            ax.text(x + cw/2, y + bh/2, c, color="#0B2545", fontsize=10.5,
                    fontweight="bold", va="center", ha="center")
    ax.annotate("", xy=(0.4, bottom + n*(bh+gap) - 0.15), xytext=(0.4, bottom + 0.15),
                arrowprops=dict(arrowstyle="-|>", color="#9FB6CE", lw=2))
    ax.text(0.2, bottom + n*(bh+gap)/2, "数据流\n自下而上", color="#9FB6CE",
            fontsize=9, va="center", ha="center", rotation=90)
    ax.text(6.1, 9.0, "整体技术架构（四层 + 底层硬件）", color="#0B2545",
            fontsize=14, fontweight="bold", va="center", ha="center")
    plt.tight_layout()
    fig.savefig(path, dpi=200, bbox_inches="tight", facecolor="white")
    plt.close(fig)

# ---------- 图2：数据流转全链路 ----------
def draw_dataflow(path):
    fig, ax = plt.subplots(figsize=(14, 7))
    ax.set_xlim(0, 14)
    ax.set_ylim(0, 7)
    ax.axis("off")
    nodes = [
        (1.0, 4.4, "每月原始数据\n(Excel/CSV)", "#0E3D66"),
        (3.3, 4.4, "清洗脚本\n(pandas)", "#15538C"),
        (5.6, 4.4, "SQLite\n数据库", "#1E6FB8"),
        (8.0, 4.4, "Hermes Agent\n调度", "#2E93F7"),
        (10.4, 4.4, "看板 / 报表", "#2E93F7"),
        (12.6, 4.4, "微信推送\n→ 老板决策", "#FF8A3D"),
    ]
    models = [
        (7.0, 1.7, "本地 Ollama\nQwen3.6", "#FF8A3D"),
        (8.6, 1.7, "DeepSeek API\n(备用)", "#FF8A3D"),
        (10.2, 1.7, "向量知识库\nChroma", "#FF8A3D"),
    ]
    def box(x, y, text, color, w=1.7, h=1.0):
        b = FancyBboxPatch((x - w/2, y - h/2), w, h,
                           boxstyle="round,pad=0.02,rounding_size=0.12",
                           linewidth=0, facecolor=color, alpha=0.96)
        ax.add_patch(b)
        ax.text(x, y, text, color="white", fontsize=10.5, fontweight="bold",
                va="center", ha="center")
    for x, y, t, c in nodes:
        box(x, y, t, c)
    for x, y, t, c in models:
        box(x, y, t, c, w=1.7, h=0.95)
    acolor = "#7A94B0"
    for i in range(len(nodes) - 1):
        x1, x2 = nodes[i][0], nodes[i+1][0]
        ax.annotate("", xy=(x2 - 0.85, 4.4), xytext=(x1 + 0.85, 4.4),
                    arrowprops=dict(arrowstyle="-|>", color=acolor, lw=2))
    for mx, _, _, _ in models:
        ax.annotate("", xy=(8.0, 3.9), xytext=(mx, 2.17),
                    arrowprops=dict(arrowstyle="-|>", color="#FF8A3D", lw=1.8))
    ax.text(8.6, 2.6, "模型层支撑", color="#FF8A3D", fontsize=9,
            va="center", ha="center")
    ax.text(7.0, 6.5, "数据流转全链路：只出不进，分析结果闭环到老板手机",
            color="#0B2545", fontsize=14, fontweight="bold", va="center", ha="center")
    plt.tight_layout()
    fig.savefig(path, dpi=200, bbox_inches="tight", facecolor="white")
    plt.close(fig)

draw_architecture(ARCH_PNG)
draw_dataflow(FLOW_PNG)
print("diagrams done")

# ============ 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.shapes import MSO_SHAPE

BG_DARK   = RGBColor(0x0B, 0x25, 0x45)
PRIMARY   = RGBColor(0x13, 0x31, 0x5C)
ACCENT    = RGBColor(0x2E, 0x93, 0xF7)
HIGHLIGHT = RGBColor(0xFF, 0x8A, 0x3D)
LIGHT     = RGBColor(0xF4, 0xF7, 0xFB)
GRAY      = RGBColor(0x6B, 0x7A, 0x8F)
WHITE     = RGBColor(0xFF, 0xFF, 0xFF)
TEXTDARK  = RGBColor(0x1A, 0x24, 0x33)

prs = Presentation()
SW = Inches(13.333)
SH = Inches(7.5)
prs.slide_width = SW
prs.slide_height = SH
BLANK = prs.slide_layouts[6]

def add_bg(slide, color=BG_DARK):
    s = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, SW, SH)
    s.fill.solid(); s.fill.fore_color.rgb = color
    s.line.fill.background(); s.shadow.inherit = False
    return s

def add_title(slide, title, page=None, subtitle=None):
    bar = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, SW, Inches(1.15))
    bar.fill.solid(); bar.fill.fore_color.rgb = PRIMARY
    bar.line.fill.background(); bar.shadow.inherit = False
    stripe = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, Inches(1.15), SW, Inches(0.06))
    stripe.fill.solid(); stripe.fill.fore_color.rgb = ACCENT
    stripe.line.fill.background(); stripe.shadow.inherit = False
    tb = slide.shapes.add_textbox(Inches(0.55), Inches(0.16), SW - Inches(1.1), Inches(0.95))
    tf = tb.text_frame; tf.word_wrap = True
    p = tf.paragraphs[0]; r = p.add_run(); r.text = title
    r.font.size = Pt(25); r.font.bold = True; r.font.color.rgb = WHITE
    if subtitle:
        p2 = tf.add_paragraph(); r2 = p2.add_run(); r2.text = subtitle
        r2.font.size = Pt(12); r2.font.color.rgb = RGBColor(0xBB, 0xD6, 0xF2)
    if page is not None:
        ft = slide.shapes.add_textbox(Inches(0.55), SH - Inches(0.4), SW - Inches(1.6), Inches(0.3))
        fp = ft.text_frame.paragraphs[0]; fr = fp.add_run()
        fr.text = "销售型生产企业 AI 数字化建设方案  ·  【客户名称】"
        fr.font.size = Pt(9); fr.font.color.rgb = GRAY
        pg = slide.shapes.add_textbox(SW - Inches(1.05), SH - Inches(0.4), Inches(0.6), Inches(0.3))
        pp = pg.text_frame.paragraphs[0]; pr = pp.add_run(); pr.text = str(page)
        pr.font.size = Pt(9); pr.font.color.rgb = GRAY; pp.alignment = PP_ALIGN.RIGHT

def add_bullets(slide, items, left, top, width, height, size=15, color=TEXTDARK):
    tb = slide.shapes.add_textbox(left, top, width, height)
    tf = tb.text_frame; tf.word_wrap = True
    first = True
    for it in items:
        text, lvl = it if isinstance(it, tuple) else (it, 0)
        p = tf.paragraphs[0] if first else tf.add_paragraph()
        first = False
        p.level = lvl
        p.space_after = Pt(9)
        prefix = "•  " if lvl == 0 else "    –  "
        r = p.add_run(); r.text = prefix + text
        r.font.size = Pt(size if lvl == 0 else size - 2)
        r.font.color.rgb = color
    return tb

def add_table(slide, data, left, top, width, height, col_widths=None):
    rows = len(data); cols = len(data[0])
    gtbl = slide.shapes.add_table(rows, cols, left, top, width, height)
    table = gtbl.table
    if col_widths:
        tot = sum(col_widths)
        for i, cw in enumerate(col_widths):
            table.columns[i].width = Emu(int(width * cw / tot))
    for ri, row in enumerate(data):
        for ci, val in enumerate(row):
            cell = table.cell(ri, ci)
            cell.margin_left = Inches(0.08); cell.margin_right = Inches(0.08)
            cell.margin_top = Inches(0.04); cell.margin_bottom = Inches(0.04)
            cell.vertical_anchor = MSO_ANCHOR.MIDDLE
            tf = cell.text_frame; tf.word_wrap = True
            p = tf.paragraphs[0]; r = p.add_run(); r.text = val
            r.font.size = Pt(11)
            if ri == 0:
                cell.fill.solid(); cell.fill.fore_color.rgb = PRIMARY
                r.font.color.rgb = WHITE; r.font.bold = True
            else:
                cell.fill.solid()
                cell.fill.fore_color.rgb = WHITE if ri % 2 else LIGHT
                r.font.color.rgb = TEXTDARK
                if ci == 0:
                    r.font.bold = True
    return table

def add_image(slide, path, left, top, width):
    slide.shapes.add_picture(path, left, top, width=width)

def set_notes(slide, text):
    slide.notes_slide.notes_text_frame.text = text

def new_content(title, page, subtitle=None):
    s = prs.slides.add_slide(BLANK)
    add_bg(s, LIGHT)
    add_title(s, title, page=page, subtitle=subtitle)
    return s

# ---------- 1. 封面 ----------
cov = prs.slides.add_slide(BLANK)
add_bg(cov, BG_DARK)
# 左侧强调条
acc = cov.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, Inches(0.22), SH)
acc.fill.solid(); acc.fill.fore_color.rgb = ACCENT; acc.line.fill.background(); acc.shadow.inherit = False
# 主标题
t = cov.shapes.add_textbox(Inches(0.9), Inches(1.9), Inches(11.5), Inches(1.6))
tf = t.text_frame; tf.word_wrap = True
p = tf.paragraphs[0]; r = p.add_run(); r.text = "销售型生产企业"
r.font.size = Pt(46); r.font.bold = True; r.font.color.rgb = WHITE
p2 = tf.add_paragraph(); r2 = p2.add_run(); r2.text = "AI 数字化建设方案"
r2.font.size = Pt(46); r2.font.bold = True; r2.font.color.rgb = ACCENT
# 副标题
st = cov.shapes.add_textbox(Inches(0.9), Inches(3.7), Inches(11.5), Inches(0.6))
sp = st.text_frame.paragraphs[0]; sr = sp.add_run()
sr.text = "软硬件一体化  ·  私有本地部署  ·  数据不出厂"
sr.font.size = Pt(18); sr.font.color.rgb = RGBColor(0xBB, 0xD6, 0xF2)
# 一句话价值
vt = cov.shapes.add_textbox(Inches(0.9), Inches(4.5), Inches(11.5), Inches(0.6))
vp = vt.text_frame.paragraphs[0]; vr = vp.add_run()
vr.text = "把“脏数据”变成“干净资产”，让 AI 替你看盘、盯异常、做决策"
vr.font.size = Pt(15); vr.font.color.rgb = WHITE
# 底部占位信息
info = cov.shapes.add_textbox(Inches(0.9), Inches(6.0), Inches(11.5), Inches(1.0))
ip = info.text_frame; ip.word_wrap = True
for i, line in enumerate([
    "客户：【客户名称】       行业：【如 母婴/食品/五金 网店工厂】",
    "服务商：【您的公司 / 顾问姓名】       日期：【提交日期】"]):
    pp = ip.paragraphs[0] if i == 0 else ip.add_paragraph()
    rr = pp.add_run(); rr.text = line
    rr.font.size = Pt(13); rr.font.color.rgb = RGBColor(0x9F, 0xB6, 0xCE)
set_notes(cov,
    "【示例填充】客户：杭州某母婴用品网店工厂；行业：母婴用品；服务商：XX 数字化工作室；日期：2026-09-01\n"
    "【讲解话术】老板您好，这份方案不卖您系统、不碰您现有进销存，只在您办公室放一台小主机，"
    "把散落各处的销售数据收拢成'一本账'，再用本地 AI 帮您看盘、盯异常、给决策。数据全在您自己厂里，不出门。")

# ---------- 2. 目录 ----------
s = new_content("目录", 1)
add_bullets(s, [
    "一、为什么现在要做（痛点与现状）",
    "二、方案定位与整体技术架构",
    "三、四层技术架构详解（硬件 / 数据 / 模型 / 调度 / 触达）",
    "四、数据流转全链路",
    "五、三期建设路线（资产化 → 智能化 → 自动化）",
    "六、成本估算与客户配合事项",
    "七、服务价值与下一步行动",
], Inches(0.9), Inches(1.6), Inches(11.5), Inches(5.0), size=18)
set_notes(s, "【讲解话术】我按'先理清家底→再发现问题→最后自动决策'三步走，今天重点讲清每一步给您带来什么、要投多少。")

# ---------- 3. 执行摘要 ----------
s = new_content("执行摘要", 2)
add_bullets(s, [
    "一期（打基础，1–2 月）：把分散数据收拢成“一本账”，让老板看明白去年赚在哪、亏在哪。",
    "二期（提效率，2–3 月）：本地 AI 自动出周/月报、盯异常，释放人力。",
    "三期（增利润，3–6 月）：AI 预测备货与资金，真正帮老板省钱。",
    "零风险落地：不碰客户现有 ERP/进销存，旁路部署，数据只出不进。",
    "数据不出厂：全部模型跑在客户内网 EVO-X2 主机，敏感数据不离开办公室。",
    "一期一付、做一期看一期效果，老板决策压力小、回款节奏稳。",
], Inches(0.9), Inches(1.6), Inches(11.5), Inches(5.0), size=15)
set_notes(s,
    "【示例填充】以母婴客户为例：一期后老板能随时看'去年 11 月双11 哪个 SKU 最赚'；"
    "二期后每周一手机收到经营简报；三期后 AI 建议'年后少备 A 款、多备 B 款'。\n"
    "【讲解话术】您不用一次性投很多，先做一期看效果，满意再继续。")

# ---------- 4. 客户痛点与现状 ----------
s = new_content("一、为什么现在要做：客户痛点与现状", 3)
add_bullets(s, [
    "数据散落各处：网店后台、Excel 台账、微信记录各管各的，对不上账。",
    "看不清赚亏：想看“去年哪月卖得好、哪个 SKU 最赚”要人工拼表，慢且易错。",
    "人力耗在重复劳动：财务/运营每周手动导出、透视、做图，没空做分析。",
    "库存与现金流焦虑：缺货断供、积压占资金，老板心里没底。",
    "想用 AI 但不会用：担心数据上云泄露、怕采购昂贵系统、怕折腾业务。",
], Inches(0.9), Inches(1.6), Inches(11.5), Inches(5.0), size=15)
set_notes(s,
    "【示例填充】某母婴用品网店工厂，年销约 3000 万，3 个平台店铺，SKU 200+，"
    "老板最关心库存周转和回款，财务每周花 1.5 天做表。\n"
    "【讲解话术】这些痛点您是不是都有？这套方案就是专门冲着'看不清、做表累、心里没底'来的。")

# ---------- 5. 方案定位 ----------
s = new_content("二、方案定位：旁路部署，零风险落地", 4)
add_bullets(s, [
    "旁路部署：在原有业务系统“旁边”独立搭一套 AI 数据分析与自动化辅助系统。",
    "数据只出不进：从原系统（网店/进销存）只读导出，分析结果不回写，不影响现有流程。",
    "私有本地运行：模型与数据库部署在客户内网主机，敏感经营数据不出厂。",
    "轻量可演进：不强制替换 ERP，先见效再扩展，风险可控、投入可控。",
    "管理层触达：看板 + 微信推送，老板手机即可看报告、收预警。",
], Inches(0.9), Inches(1.6), Inches(11.5), Inches(5.0), size=15)
set_notes(s,
    "【讲解话术】一句话：我不在您现有系统上动刀，只在旁边加一台'AI 数据助理'，"
    "您原来的进销存该怎么用还怎么用，零风险。")

# ---------- 6. 四层技术架构总览 ----------
s = new_content("三、整体技术架构（四层 + 底层硬件）", 5)
add_image(s, ARCH_PNG, Inches(2.0), Inches(1.35), Inches(9.3))
set_notes(s,
    "【讲解话术】从下往上看：最底下是您办公室一台 EVO-X2 小主机；往上是数据层、模型层、"
    "调度层，最上面是您手机能看到的看板和微信推送。数据从原系统只进到最底层，结果从最上层出来。\n"
    "注：具体型号（EVO-X2 / Qwen3.6 / DeepSeek 等）以交付时主流为准。")

# ---------- 7. 底层：硬件与系统 ----------
s = new_content("底层：硬件与系统部署方案", 6, subtitle="核心逻辑：2 万预算内本地流畅跑 30B 级模型，功耗约 100W，适合 24h 开机")
add_table(s, [
    ["项目", "配置", "说明"],
    ["主机", "GMKtec EVO-X2（128GB 版）", "迷你主机，不占空间、静音，适合办公室"],
    ["内存", "128GB LPDDR5X 统一内存（可分 96GB 给 GPU）", "本地跑 35B MoE + 向量库 + 数据库够用"],
    ["存储", "2TB NVMe SSD ×2（系统盘/数据盘分离）", "系统装系统模型，数据盘存库与日志"],
    ["操作系统", "Ubuntu 24.04 LTS", "对 AMD ROCm 支持好，AI 性能高 20–30%"],
    ["网络", "内网固定 IP + Tailscale", "远程巡检与系统更新"],
    ["备用方案", "DeepSeek V4 Flash API", "复杂任务自动 fallback 到云端"],
], Inches(0.7), Inches(1.7), Inches(11.9), Inches(4.6), col_widths=[1.3, 3.6, 4.0])
set_notes(s,
    "【示例填充】以母婴客户为例：一台 EVO-X2 放财务室，插网线，固定内网 IP，我用 Tailscale 远程维护。\n"
    "【讲解话术】这台机器比一台台式机还小，静音不发热，24 小时开着电费忽略不计，但能本地跑大模型。")

# ---------- 8. 第一层：数据层 ----------
s = new_content("第一层：数据层方案", 7, subtitle="核心逻辑：SQLite 单文件易备份；Markdown 是 AI 理解准确率最高的文本格式")
add_bullets(s, [
    "数据库：SQLite 3，轻量零配置，单文件易备份迁移。",
    "清洗脚本：Python（pandas + openpyxl），每月定时运行 Excel→清洗→入库。",
    "中间格式：Markdown 表格，AI 理解准确率最高的文本格式，便于人工核对。",
    "向量知识库：Chroma（轻量），存产品手册/会议纪要，供 RAG 检索。",
    "定时任务：cron（Linux 自带），每月 1 号自动清洗，无需人工干预。",
], Inches(0.9), Inches(1.7), Inches(11.5), Inches(4.8), size=15)
set_notes(s,
    "【讲解话术】您每月把网店导出的 Excel 丢进一个文件夹，系统自己洗好存进数据库，"
    "不用您再手动拼表。")

# ---------- 9. 第二层：模型层 ----------
s = new_content("第二层：模型层方案", 8, subtitle="核心逻辑：Qwen3.6 为 MoE，激活仅 3B，在 EVO-X2 上 25–35 Token/s，数据不出厂")
add_table(s, [
    ["组件", "技术选型", "说明"],
    ["本地推理引擎", "Ollama", "一行命令部署，兼容 OpenAI API，管理方便"],
    ["主模型", "Qwen3.6-35B-A3B（4bit 量化）", "智商≈Claude 3.5 Sonnet，约 20GB，速度 25–35 Token/s"],
    ["备用云端模型", "DeepSeek V4 Flash API", "复杂推理按量付费，月均 < 200 元"],
    ["嵌入模型", "bge-m3（本地）", "知识库向量化，纯本地不依赖外网"],
    ["模型格式", "GGUF（llama.cpp 兼容）", "CPU+GPU 混合推理，显存不足可 fallback 内存"],
], Inches(0.7), Inches(1.7), Inches(11.9), Inches(4.6), col_widths=[1.8, 3.2, 4.0])
set_notes(s,
    "【讲解话术】95% 的分析都在您这台机器上本地算，只有极少量复杂任务才花几块钱走云端，"
    "您的客户名单和金额永远不会出办公室。")

# ---------- 10. 第三层：智能调度层 ----------
s = new_content("第三层：智能调度层方案", 9, subtitle="核心逻辑：Hermes Agent 国产开源，原生支持微信接入与工具调用，适合 ToB")
add_bullets(s, [
    "智能体框架：Hermes Agent，连接模型/工具/消息平台，系统“大脑”。",
    "Web 管理面板：可视化配置模型、工具、知识库，查看 Token 消耗与日志。",
    "工具定义：Python 函数注册为 Tools，让 AI 能查库、跑脚本、发报告。",
    "消息网关：iLink Bot API（微信），AI 主动推送报告、接收指令。",
    "国产开源、部署简单，适合 ToB 场景。",
], Inches(0.9), Inches(1.7), Inches(11.5), Inches(4.8), size=15)
set_notes(s,
    "【讲解话术】这一层就是让 AI '会干活'：它能自己查数据库、自己生成报告、自己发到您微信上。")

# ---------- 11. 第四层：触达层 ----------
s = new_content("第四层：触达层方案", 10, subtitle="核心逻辑：Streamlit 快速搭看板；微信推送让老板手机收报告，无需装 APP")
add_bullets(s, [
    "数据看板：Streamlit（Python），展示销售趋势、SKU 排行、区域分布、异常预警。",
    "报表推送：Hermes Agent + 微信 Bot，每周一自动推送《经营简报》图文。",
    "多通道：飞书/钉钉/企业微信可选，走同一消息通道。",
    "人工巡检：每月 2 次远程巡检，查日志、备份库、确认推送正常。",
], Inches(0.9), Inches(1.7), Inches(11.5), Inches(4.8), size=15)
set_notes(s,
    "【示例填充】母婴客户用微信：每周一早 9 点，老板微信收到上周经营简报，含销量异动和库存预警。\n"
    "【讲解话术】您不用学新软件，报告直接发您微信，像收一条工作消息一样简单。")

# ---------- 12. 数据流转全链路 ----------
s = new_content("四、数据流转全链路", 11)
add_image(s, FLOW_PNG, Inches(1.1), Inches(1.35), Inches(11.1))
set_notes(s,
    "【讲解话术】整条链路是'只进不出'：原始数据进来，洗干净存库，AI 分析完把结论推到您手机，"
    "结论不回写您的业务系统，所以绝对不影响您现有流程。")

# ---------- 13. 第一期 ----------
s = new_content("五之一、第一期：数据资产化（1–2 个月）", 12, subtitle="核心任务：打通数据孤岛，建立统一数据看板")
add_bullets(s, [
    "数据清洗与入库：导出近 1–2 年网店销售（订单/SKU/区域/金额），清洗入 SQLite。",
    "建立核心指标：固定口径（销售额、退货率、客单价、库存周转天数）。",
    "搭建基础看板：Web 看板展示月度趋势、SKU 排行、区域分布。",
    ("交付物：数据表结构文档、在线看板、《公司数据资产盘点报告》", 0),
    ("验收：老板随时登录查任意月份数据且准确", 0),
], Inches(0.9), Inches(1.7), Inches(11.5), Inches(4.8), size=15)
set_notes(s,
    "【示例填充】母婴客户一期交付：一张看板，能点开看'2025 各月销售额''TOP20 SKU''华东/华南占比'。\n"
    "【讲解话术】一期结束，您就拥有了全公司统一的'一本账'。")

# ---------- 14. 第二期 ----------
s = new_content("五之二、第二期：分析智能化（2–3 个月）", 13, subtitle="核心任务：接入 AI，实现自动化分析与预警")
add_bullets(s, [
    "本地模型部署：EVO-X2 内部署 Qwen3.6 等小模型，数据不出厂。",
    "自动化周/月报：AI 读库，每周一生成《经营简报》推送微信。",
    "异常归因：SKU 销量暴跌时，AI 关联缺货/竞品跟价，给“可能原因”。",
    ("交付物：本地推理服务、推送机器人、异常预警配置文档", 0),
    ("验收：每月初手机收到完整经营分析，重点异常清晰标注", 0),
], Inches(0.9), Inches(1.7), Inches(11.5), Inches(4.8), size=15)
set_notes(s,
    "【示例填充】母婴客户二期：某 SKU 周销量跌 40%，AI 简报标注'疑似断货+竞品降价'，并附建议。\n"
    "【讲解话术】这期相当于给您配了个 24 小时不休息的数据分析师。")

# ---------- 15. 第三期 ----------
s = new_content("五之三、第三期：决策自动化（3–6 个月）", 14, subtitle="核心任务：从“发现问题”升级到“给出方案”并介入业务")
add_bullets(s, [
    "库存预测与补货：按历史+季节因子预测需求，自动生成采购建议单。",
    "现金流模拟：输入支出与回款周期，模拟健康度、预警资金缺口。",
    "选品与定价：结合外部大盘，输出《SKU 优化建议》（淘汰/主推/清仓）。",
    ("交付物：智能库存预警采购系统、现金流压力测试看板、SKU 生命周期报告", 0),
    ("验收：参考 AI 建议做至少一次库存调整，产生可量化资金占用下降", 0),
], Inches(0.9), Inches(1.7), Inches(11.5), Inches(4.8), size=15)
set_notes(s,
    "【示例填充】母婴客户三期：AI 预测年后 B 款需求涨，建议提前备货；同时提示 C 款滞销建议清仓，"
    "释放占用资金约 30 万。\n"
    "【讲解话术】这期是'商业大脑'，不仅告诉您哪有问题，还告诉您怎么办、办了会怎样。")

# ---------- 16. 三期节奏总览 ----------
s = new_content("五、三期节奏与投入对比总览", 15)
add_table(s, [
    ["阶段", "周期", "核心目标", "关键交付", "老板获得感"],
    ["一期 数据资产化", "1–2 月", "理清家底", "统一看板 + 数据资产报告", "看明白赚亏"],
    ["二期 分析智能化", "2–3 月", "提效率", "本地 AI + 自动周报 + 预警", "AI 分析师盯盘"],
    ["三期 决策自动化", "3–6 月", "增利润", "预测补货 + 现金流模拟", "商业大脑给方案"],
], Inches(0.7), Inches(1.8), Inches(11.9), Inches(3.6), col_widths=[2.0, 1.2, 1.8, 3.6, 2.3])
set_notes(s,
    "【讲解话术】三步走、每一步都有明确交付和验收，您随时可以停在任意一期，风险完全可控。")

# ---------- 17. 成本估算（仅硬件/云） ----------
s = new_content("六之一、总体成本估算（仅硬件 / 云，不含服务费）", 16)
add_table(s, [
    ["项目", "预估金额（人民币）", "备注"],
    ["主机硬件（EVO-X2 128GB）", "15,000 – 18,000 元", "一次投入，含 2TB SSD"],
    ["备用配件（额外 SSD 等）", "1,000 – 2,000 元", "数据备份与冗余"],
    ["云端 API（DeepSeek Flash）", "100 – 200 元/月", "仅复杂任务触发，日常用本地"],
    ["网络/域名（如需公网）", "0 – 500 元/年", "可选项，非必需"],
], Inches(0.7), Inches(1.8), Inches(11.9), Inches(3.6), col_widths=[3.2, 3.0, 3.7])
set_notes(s,
    "【示例填充】母婴客户一次性硬件约 1.7–2.0 万，之后每月云端 API 不到 200 元。\n"
    "【讲解话术】这是您要承担的'材料费'，一次性投入小、月费可忽略；我的实施服务费咱们线下另谈，"
    "按一期一付、验收后付。")

# ---------- 18. 客户需配合准备 ----------
s = new_content("六之二、客户需配合准备清单", 17)
add_table(s, [
    ["序号", "内容", "说明"],
    ["1", "近 1–2 年网店销售数据（Excel/CSV）", "用于清洗和导入数据库"],
    ["2", "产品手册/商品台账（Excel）", "补充 SKU 维度（品类/成本/建议售价）"],
    ["3", "一台能联网的电脑 / 一个不用的手机号", "微信 Bot 登录 + 远程维护通道"],
    ["4", "内网 WiFi 或有线网络", "固定内网 IP 或 Tailscale 替代"],
], Inches(0.7), Inches(1.8), Inches(11.9), Inches(3.8), col_widths=[0.8, 4.2, 4.0])
set_notes(s,
    "【讲解话术】您这边要做的事很简单：把数据导出给我、准备一个不用的手机号和能联网，"
    "其余安装部署都是我来做。")

# ---------- 19. 服务价值卖点 + 复制扩张 ----------
s = new_content("七之一、服务价值卖点（三句话）", 18)
add_bullets(s, [
    "第一期：我把你家散落各处的数据收拢成“一本账”，让你看明白钱花哪去了。",
    "第二期：我给你配个“AI 数据分析师”，24 小时盯着数据，出问题第一时间告诉你。",
    "第三期：我给你装上“AI 商业大脑”，不仅告诉哪有问题，还告诉你怎么办、模拟办了会怎样。",
    "复制扩张：样板跑通后，同一套架构可快速复制到同类销售型生产企业。",
    "商务模式：一期一付、做一期看一期效果，决策压力小、回款稳。",
], Inches(0.9), Inches(1.7), Inches(11.5), Inches(4.8), size=15)
set_notes(s,
    "【讲解话术】您跟同行聊的时候，就这三句话，谁都听得懂、谁都想要。样板跑通，"
    "后面复制给其他厂非常快。")

# ---------- 20. 下一步行动 / CTA ----------
s = new_content("七之二、下一步行动", 19)
add_bullets(s, [
    "建议从一期起步：先交付“一本账”看板，2 个月内见效。",
    "我方接下来：① 拉取贵司近 1–2 年销售数据做样例清洗；② 部署 EVO-X2 演示环境；③ 输出首版看板。",
    "贵司配合：提供销售数据/台账、准备一台不用的手机、确认内网接入。",
    "确认即启动：签订一期合作，按里程碑验收付款。",
    "联系人：【您的姓名 / 微信】",
], Inches(0.9), Inches(1.7), Inches(11.5), Inches(4.8), size=15)
set_notes(s,
    "【讲解话术】今天聊完，如果您认可，咱们就先签一期。两周内您能看到第一版看板demo，"
    "觉得有用再继续二期、三期。")

prs.save(OUT_PPTX)
print("PPTX saved:", OUT_PPTX, "slides:", len(prs.slides._sldIdLst))
