#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""第一期培训 PPT：一起养虾，一起打造自己的数字员工"""

from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.enum.shapes import MSO_SHAPE

FONT = "Noto Sans CJK SC"
NAVY = RGBColor(0x1B, 0x2A, 0x4A)
BLUE = RGBColor(0x2E, 0x5C, 0x9A)
ACCENT = RGBColor(0xD9, 0x7A, 0x2B)
GREEN = RGBColor(0x2E, 0x7D, 0x5B)
GREY = RGBColor(0x5A, 0x62, 0x70)
MUTE = RGBColor(0x94, 0x9C, 0xAA)
LIGHT = RGBColor(0xF2, 0xF4, 0xF7)
LINE = RGBColor(0xD8, 0xDD, 0xE5)
WHITE = RGBColor(0xFF, 0xFF, 0xFF)
RED = RGBColor(0xB3, 0x3A, 0x3A)
CREAM = RGBColor(0xFD, 0xF4, 0xEC)
CREAM_L = RGBColor(0xE8, 0xC4, 0x9E)

W, H = Inches(13.333), Inches(7.5)
prs = Presentation()
prs.slide_width, prs.slide_height = W, H
BLANK = prs.slide_layouts[6]


def tb(slide, x, y, w, h, align=PP_ALIGN.LEFT, anchor=MSO_ANCHOR.TOP):
    box = slide.shapes.add_textbox(x, y, w, h)
    tf = box.text_frame
    tf.word_wrap = True
    tf.vertical_anchor = anchor
    tf.margin_left = tf.margin_right = tf.margin_top = tf.margin_bottom = 0
    tf.paragraphs[0].alignment = align
    return tf


def put(tf, text, size=13, color=GREY, bold=False, sb=0, sa=4,
        align=PP_ALIGN.LEFT, first=False, line=1.25):
    p = tf.paragraphs[0] if first else tf.add_paragraph()
    p.alignment = align
    p.space_before = Pt(sb)
    p.space_after = Pt(sa)
    p.line_spacing = line
    r = p.add_run()
    r.text = text
    r.font.size = Pt(size)
    r.font.bold = bold
    r.font.color.rgb = color
    r.font.name = FONT
    return p


def rect(slide, x, y, w, h, fill=None, lc=None, lw=0.75):
    sh = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, x, y, w, h)
    if fill is None:
        sh.fill.background()
    else:
        sh.fill.solid()
        sh.fill.fore_color.rgb = fill
    if lc is None:
        sh.line.fill.background()
    else:
        sh.line.color.rgb = lc
        sh.line.width = Pt(lw)
    sh.shadow.inherit = False
    return sh


def header(slide, tag, title, sub=None):
    rect(slide, Inches(0), Inches(0), W, Inches(1.08), fill=NAVY)
    t = tb(slide, Inches(0.62), Inches(0.3), Inches(1.9), Inches(0.5))
    put(t, tag, 12, RGBColor(0xE0, 0xB6, 0x8A), bold=True, first=True)
    t = tb(slide, Inches(2.42), Inches(0.2), Inches(8.6), Inches(0.7))
    put(t, title, 23, WHITE, bold=True, first=True, sa=0)
    if sub:
        put(t, sub, 11, RGBColor(0xA8, 0xBA, 0xD8), sb=3)


def table(slide, x, y, rows, col_w, font=11, row_h=Inches(0.42),
          head_h=Inches(0.44), col_align=None, head=NAVY):
    cx = x
    for j, cw in enumerate(col_w):
        rect(slide, cx, y, cw, head_h, fill=head)
        tf = tb(slide, cx + Inches(0.09), y, cw - Inches(0.18), head_h,
                anchor=MSO_ANCHOR.MIDDLE)
        put(tf, rows[0][j], font, WHITE, bold=True, first=True,
            align=(col_align or {}).get(j, PP_ALIGN.LEFT), sa=0)
        cx += cw
    yy = y + head_h
    for i in range(1, len(rows)):
        cx = x
        f = LIGHT if i % 2 == 1 else WHITE
        for j, cw in enumerate(col_w):
            rect(slide, cx, yy, cw, row_h, fill=f, lc=LINE, lw=0.5)
            tf = tb(slide, cx + Inches(0.09), yy, cw - Inches(0.18), row_h,
                    anchor=MSO_ANCHOR.MIDDLE)
            txt, c, bd = rows[i][j], GREY, False
            if txt[:1] == "!":
                txt, c, bd = txt[1:], ACCENT, True
            elif txt[:1] == "^":
                txt, c, bd = txt[1:], GREEN, True
            elif txt[:1] == "~":
                txt, c = txt[1:], MUTE
            elif txt[:1] == "#":
                txt, c, bd = txt[1:], RED, True
            put(tf, txt, font, c, bold=bd, first=True,
                align=(col_align or {}).get(j, PP_ALIGN.LEFT), sa=0, line=1.05)
            cx += cw
        yy += row_h
    return yy


def band(slide, y, title, body, h=Inches(0.82), tone="light"):
    """底部强调条"""
    if tone == "light":
        rect(slide, Inches(0.62), y, Inches(12.1), h, fill=LIGHT, lc=LINE)
        rect(slide, Inches(0.62), y, Inches(0.06), h, fill=ACCENT)
        tc, bc = NAVY, GREY
    elif tone == "warn":
        rect(slide, Inches(0.62), y, Inches(12.1), h, fill=CREAM, lc=CREAM_L)
        tc, bc = RED, GREY
    else:
        rect(slide, Inches(0.62), y, Inches(12.1), h, fill=NAVY)
        tc, bc = RGBColor(0xE0, 0xB6, 0x8A), WHITE
    tf = tb(slide, Inches(0.94), y + Inches(0.13), Inches(11.5),
            h - Inches(0.24))
    put(tf, title, 13.5, tc, bold=True, first=True, sa=3)
    if body:
        put(tf, body, 11, bc, line=1.28)


# =========================================================
# P1 封面
# =========================================================
s = prs.slides.add_slide(BLANK)
rect(s, Inches(0), Inches(0), W, H, fill=NAVY)
rect(s, Inches(0), Inches(0), Inches(0.16), H, fill=ACCENT)

tf = tb(s, Inches(1.15), Inches(1.85), Inches(11), Inches(0.4))
put(tf, "华腾 · 产研团队 · 第一期", 13, RGBColor(0x8E, 0xA5, 0xCC),
    bold=True, first=True)

tf = tb(s, Inches(1.15), Inches(2.38), Inches(11.3), Inches(1.9))
put(tf, "一起养虾", 46, WHITE, bold=True, first=True, sa=6)
put(tf, "打造属于你自己的数字员工", 30, RGBColor(0xD5, 0xDF, 0xEF),
    bold=True, sa=10)

rect(s, Inches(1.15), Inches(4.62), Inches(2.2), Inches(0.035), fill=ACCENT)

tf = tb(s, Inches(1.15), Inches(5.0), Inches(11), Inches(1.1))
put(tf, "今天讲清三件事", 14, RGBColor(0xE0, 0xB6, 0x8A), bold=True,
    first=True, sa=8)
put(tf, "数字员工是什么　·　怎么让它懂你　·　你回去做什么", 16,
    RGBColor(0xB8, 0xC8, 0xE4), line=1.3)

tf = tb(s, Inches(1.15), Inches(6.5), Inches(11), Inches(0.5))
put(tf, "90 分钟　|　全员参加，无需提前准备", 12, RGBColor(0x8E, 0xA5, 0xCC),
    first=True)


# =========================================================
# P2 议程
# =========================================================
s = prs.slides.add_slide(BLANK)
header(s, "开场", "今天怎么安排", "听完你会带走一份自己的配置文件，和一件回去要做的事")

items = [
    ("01", "什么是数字员工", "15 min", "概念 + 现场演示，看它真干活"),
    ("02", "数据红线", "10 min", "先立规矩，再教怎么用"),
    ("03", "工具怎么装", "15 min", "全员装 WorkBuddy，OpenClaw 我演示"),
    ("04", "给它做入职培训", "30 min", "本场核心：怎么让 AI 变成懂你的同事"),
    ("05", "回去做什么", "15 min", "作业 + 报名 + 答疑安排"),
]
y = Inches(1.4)
for n, t, m, d in items:
    rect(s, Inches(0.62), y, Inches(12.1), Inches(0.94), fill=WHITE, lc=LINE)
    c = ACCENT if n == "04" else BLUE
    rect(s, Inches(0.62), y, Inches(0.06), Inches(0.94), fill=c)
    tf = tb(s, Inches(1.02), y + Inches(0.24), Inches(0.7), Inches(0.5))
    put(tf, n, 19, c, bold=True, first=True)
    tf = tb(s, Inches(1.85), y + Inches(0.15), Inches(4.2), Inches(0.66))
    put(tf, t, 15.5, NAVY, bold=True, first=True, sa=2, line=1.1)
    put(tf, d, 11, GREY, line=1.15)
    tf = tb(s, Inches(11.4), y + Inches(0.3), Inches(1.1), Inches(0.4),
            align=PP_ALIGN.RIGHT)
    put(tf, m, 12, MUTE, bold=True, first=True)
    y += Inches(1.03)

band(s, Inches(6.6), "第 4 章是今天唯一无法自学的部分",
     "装软件看文档就行；但「怎么把 AI 变成懂你业务的同事」，没人会自己想到。", tone="light")


# =========================================================
# P3 什么是数字员工
# =========================================================
s = prs.slides.add_slide(BLANK)
header(s, "第 1 章", "什么是数字员工", "先分清三层东西，后面才不会乱")

rows = [
    ["", "是什么", "举例", "它的角色"],
    ["大模型", "会思考的大脑", "GPT　Claude　豆包　DeepSeek", "~原材料"],
    ["AI 工具", "会聊天的助手", "各类对话产品、网页版", "~能问，不能干"],
    ["!数字员工", "!能动手干活的同事", "!OpenClaw　WorkBuddy　Codex",
     "!有工具、有记忆、有权限"],
]
table(s, Inches(0.62), Inches(1.44), rows,
      [Inches(1.9), Inches(2.9), Inches(4.5), Inches(2.8)],
      font=12, row_h=Inches(0.6))

# 核心区别
rect(s, Inches(0.62), Inches(3.6), Inches(12.1), Inches(1.22), fill=NAVY)
tf = tb(s, Inches(0.94), Inches(3.78), Inches(11.5), Inches(0.95),
        align=PP_ALIGN.CENTER)
put(tf, "问答型 AI 给你答案，数字员工给你结果", 24, WHITE, bold=True,
    first=True, sa=8, align=PP_ALIGN.CENTER)
put(tf, "前者告诉你「这个 docx 可以用 Python 解析」；"
        "后者直接把 32MB 的白皮书拆完，给你一份带出处的问题清单。",
    12.5, RGBColor(0xB8, 0xC8, 0xE4), align=PP_ALIGN.CENTER)

# 三个特征
feats = [
    ("有工具", "能读写文件、跑命令、调接口、查资料——不只是聊天"),
    ("有记忆", "记得你是谁、在做什么、你的规矩；而且会自己纠错"),
    ("有权限", "哪些事它自己拍板，哪些必须先问你——边界由你定"),
]
x = Inches(0.62)
cw = Inches(3.93)
for i, (t, d) in enumerate(feats):
    rect(s, x, Inches(5.06), cw, Inches(1.32), fill=WHITE, lc=LINE)
    rect(s, x, Inches(5.06), cw, Inches(0.05), fill=ACCENT)
    tf = tb(s, x + Inches(0.2), Inches(5.28), cw - Inches(0.4), Inches(1.0))
    put(tf, t, 15, NAVY, bold=True, first=True, sa=5, line=1.1)
    put(tf, d, 11, GREY, line=1.28)
    x += cw + Inches(0.155)

band(s, Inches(6.58), "三者缺一，就退化成聊天机器人",
     None, h=Inches(0.5), tone="light")


# =========================================================
# P4 什么活用什么工具
# =========================================================
s = prs.slides.add_slide(BLANK)
header(s, "第 1 章", "什么活用什么工具", "不是选一个碾压其他，是分层用")

rows = [
    ["能力维度", "ChatGPT / 豆包类", "Codex / CodeBuddy IDE", "WorkBuddy", "OpenClaw"],
    ["装完就能用", "^是", "^是", "^是", "#要配环境"],
    ["读写本地文件、跑命令", "~否", "^是", "部分", "^是"],
    ["接入微信 / 飞书", "~否", "~否", "^是", "^是"],
    ["跨会话记住你", "部分", "项目内", "^是", "^是"],
    ["不开口也主动干活", "~否", "~否", "^自动任务", "^是"],
    ["关掉电脑仍在跑", "~—", "~否", "~否", "^跑在服务器"],
    ["!数据留在公司内", "#否", "#否", "#否 · 腾讯云", "!是 · 自己服务器"],
    ["成本", "免费~订阅", "按用量", "¥0~700/月", "服务器 + API"],
]
table(s, Inches(0.62), Inches(1.36), rows,
      [Inches(2.85), Inches(2.15), Inches(2.75), Inches(2.15), Inches(2.2)],
      font=10.5, row_h=Inches(0.4), head_h=Inches(0.46),
      col_align={1: PP_ALIGN.CENTER, 2: PP_ALIGN.CENTER,
                 3: PP_ALIGN.CENTER, 4: PP_ALIGN.CENTER})

band(s, Inches(5.28), "最后两行才是真正的分水岭",
     "「数据在哪」决定这个工具能用在哪类项目上——这条比其他所有行加起来都重要。",
     h=Inches(0.8), tone="light")

# 分层结论
lanes = [
    ("全员", "WorkBuddy", "免部署，装完在企微/飞书里就能用", BLUE),
    ("研发", "+ Codex 类", "写代码、读老模块", BLUE),
    ("种子", "+ OpenClaw", "重活、涉密、深定制", ACCENT),
]
x = Inches(0.62)
cw = Inches(3.93)
for who, tool, d, c in lanes:
    rect(s, x, Inches(6.24), cw, Inches(0.86), fill=WHITE, lc=LINE)
    rect(s, x, Inches(6.24), Inches(0.055), Inches(0.86), fill=c)
    tf = tb(s, x + Inches(0.2), Inches(6.38), cw - Inches(0.4), Inches(0.6))
    put(tf, f"{who}　→　{tool}", 13, NAVY, bold=True, first=True, sa=2,
        line=1.1)
    put(tf, d, 10.5, GREY, line=1.15)
    x += cw + Inches(0.155)


# =========================================================
# P5 高价值场景 + 演示
# =========================================================
s = prs.slides.add_slide(BLANK)
header(s, "第 1 章", "它能干什么：四类高价值场景", "共同点：做错了不出门，重跑一次就行")

scenes = [
    ("文档分析",
     "几十兆的白皮书 / 招标文件，几分钟给出结构问题、前后矛盾、缺失项、"
     "无出处的承诺。",
     "现场演示这一条"),
    ("材料整理",
     "会议纪要 → 事项规范；需求文档 → 用例清单；散乱记录 → 结构化表格。",
     None),
    ("代码与系统理解",
     "一个没人敢碰的老模块，读完给你结构说明 + 改造方案 + 风险点。",
     None),
    ("跨校复配",
     "A 校已配好的事项 + B 校差异说明 → B 校配置方案。重复劳动最重的一环。",
     "最容易出漂亮数字"),
]
x = Inches(0.62)
cw = Inches(2.955)
for i, (t, d, tagtxt) in enumerate(scenes):
    rect(s, x, Inches(1.4), cw, Inches(2.5), fill=WHITE, lc=LINE)
    rect(s, x, Inches(1.4), cw, Inches(0.05),
         fill=ACCENT if tagtxt else BLUE)
    tf = tb(s, x + Inches(0.19), Inches(1.62), cw - Inches(0.38), Inches(2.1))
    put(tf, t, 15, NAVY, bold=True, first=True, sa=7, line=1.1)
    put(tf, d, 11, GREY, line=1.3, sa=8)
    if tagtxt:
        put(tf, "▸ " + tagtxt, 10.5, ACCENT, bold=True, line=1.2)
    x += cw + Inches(0.145)

# 为什么不讲自动化
rect(s, Inches(0.62), Inches(4.08), Inches(12.1), Inches(1.42),
     fill=CREAM, lc=CREAM_L)
tf = tb(s, Inches(0.94), Inches(4.24), Inches(11.5), Inches(1.14))
put(tf, "今天不讲「自动收发邮件 / 自动运维重启服务 / 自动发公众号」", 14,
    RED, bold=True, first=True, sa=6)
put(tf, "这三类动作有一个共同点：不可撤回，而且署名是人不是 AI。"
        "发错的邮件收不回来，重启错的服务是生产事故，责任在操作人。", 11.5,
    GREY, sa=4, line=1.3)
put(tf, "自动化能力留到第三期——等大家有判断力之后，"
        "并且必须配「哪些动作必须人确认」的规则一起讲。", 11.5, GREY, line=1.3)

# 底部一句
rect(s, Inches(0.62), Inches(5.72), Inches(12.1), Inches(1.24), fill=NAVY)
tf = tb(s, Inches(0.94), Inches(5.98), Inches(11.5), Inches(0.8),
        align=PP_ALIGN.CENTER)
put(tf, "同样的活不干第二遍 —— 这才是效率的来源", 26, WHITE, bold=True,
    first=True, align=PP_ALIGN.CENTER, sa=6)
put(tf, "AI 打字快省不了多少时间；把一次性经验变成可复用资产，才是真的省。",
    12, RGBColor(0xB8, 0xC8, 0xE4), align=PP_ALIGN.CENTER)


# =========================================================
# P6 数据红线
# =========================================================
s = prs.slides.add_slide(BLANK)
header(s, "第 2 章", "先立规矩：数据红线", "在教怎么用之前讲，不是之后")

rows = [
    ["级别", "内容", "可用工具", "举例"],
    ["L0 公开", "已公开的产品介绍、通用技术资料", "^全部工具", "官网文案、公开标准"],
    ["L1 内部", "内部文档、非客户相关设计稿", "^全部工具", "内部规范、原型草图"],
    ["!L2 客户相关", "!项目方案、投标文件、客户需求文档",
     "!仅本地 / 私有化", "!标书、需求确认单"],
    ["#L3 敏感", "#高校真实业务数据、源码仓库、账号凭据",
     "#禁止进任何外部工具", "#学生数据、代码、密钥"],
]
table(s, Inches(0.62), Inches(1.4), rows,
      [Inches(1.95), Inches(4.5), Inches(2.85), Inches(2.8)],
      font=11, row_h=Inches(0.64))

rect(s, Inches(0.62), Inches(4.5), Inches(12.1), Inches(1.36),
     fill=CREAM, lc=CREAM_L)
tf = tb(s, Inches(0.94), Inches(4.66), Inches(11.5), Inches(1.08))
put(tf, "为什么这一页排在最前面", 14, RED, bold=True, first=True, sa=6)
put(tf, "WorkBuddy 官网的主打功能之一是「自动搜集资料并生成完整的 PPT」——"
        "这个功能太顺手，不提醒一定有人把投标文件拖进去。", 11.5, GREY, sa=4,
    line=1.3)
put(tf, "而且它接了微信 / 企微 / 飞书，入口离工作数据极近。"
        "方便和越界是同一件事的两面。", 11.5, GREY, line=1.3)

rect(s, Inches(0.62), Inches(6.06), Inches(12.1), Inches(0.9), fill=NAVY)
tf = tb(s, Inches(0.94), Inches(6.22), Inches(11.5), Inches(0.62))
put(tf, "一句话记住：客户数据、源码、投标文件，不进外部工具。", 17, WHITE,
    bold=True, first=True, sa=4)
put(tf, "违规按信息安全事故处理。拿不准的，先问再用。", 11.5,
    RGBColor(0xE0, 0xB6, 0x8A), bold=True)


# =========================================================
# P7 工具怎么装
# =========================================================
s = prs.slides.add_slide(BLANK)
header(s, "第 3 章", "工具怎么装", "今天你只需要装一个，另一个看我演示")

# 左：全员
rect(s, Inches(0.62), Inches(1.4), Inches(5.95), Inches(4.44), fill=WHITE,
     lc=LINE)
rect(s, Inches(0.62), Inches(1.4), Inches(5.95), Inches(0.06), fill=BLUE)
tf = tb(s, Inches(0.94), Inches(1.66), Inches(5.35), Inches(0.7))
put(tf, "全员：装 WorkBuddy", 19, NAVY, bold=True, first=True, sa=4)
put(tf, "免部署，安装即用。今天现场跟着装。", 12, GREY)

steps = [
    ("1", "官网下载客户端", "macOS 12.0+ / Windows 10+"),
    ("2", "登录，选体验版", "限时免费，每月 500 积分"),
    ("3", "接入企微或飞书", "扫码即可，在聊天里直接用"),
    ("4", "配置你的第一个助理", "下一章要用，先留着"),
]
y = Inches(2.5)
for n, t, d in steps:
    tf = tb(s, Inches(0.98), y, Inches(0.35), Inches(0.4))
    put(tf, n, 15, BLUE, bold=True, first=True)
    tf = tb(s, Inches(1.44), y, Inches(4.9), Inches(0.68))
    put(tf, t, 13, NAVY, bold=True, first=True, sa=2, line=1.1)
    put(tf, d, 10.5, GREY, line=1.15)
    y += Inches(0.78)

rect(s, Inches(0.94), Inches(5.24), Inches(5.31), Inches(0.44), fill=CREAM,
     lc=CREAM_L)
tf = tb(s, Inches(1.1), Inches(5.32), Inches(5.0), Inches(0.3))
put(tf, "积分会用完，省着点试；用光了告诉我", 10.5, RED, bold=True, first=True)

# 右：演示
rect(s, Inches(6.77), Inches(1.4), Inches(5.95), Inches(4.44), fill=NAVY)
tf = tb(s, Inches(7.09), Inches(1.66), Inches(5.35), Inches(0.7))
put(tf, "OpenClaw：我装，你先看", 19, WHITE, bold=True, first=True, sa=4)
put(tf, "要服务器、要命令行、要改配置文件。", 12, RGBColor(0xA8, 0xBA, 0xD8))

demo = [
    ("看它是常驻服务", "跑在服务器上，关掉自己电脑它还在干活"),
    ("在微信里让它干件真事", "一句话指令，看它自己规划、自己执行"),
    ("打开它的工作区目录", "里面就是几个 markdown 文件——下一章的主角"),
]
y = Inches(2.56)
for i, (t, d) in enumerate(demo, 1):
    tf = tb(s, Inches(7.13), y, Inches(0.35), Inches(0.4))
    put(tf, str(i), 15, RGBColor(0xE0, 0xB6, 0x8A), bold=True, first=True)
    tf = tb(s, Inches(7.59), y, Inches(4.9), Inches(0.7))
    put(tf, t, 13, WHITE, bold=True, first=True, sa=2, line=1.1)
    put(tf, d, 10.5, RGBColor(0xA8, 0xBA, 0xD8), line=1.2)
    y += Inches(0.82)

rect(s, Inches(7.09), Inches(5.14), Inches(5.31), Inches(0.56),
     fill=RGBColor(0x2A, 0x3D, 0x63))
tf = tb(s, Inches(7.25), Inches(5.24), Inches(5.0), Inches(0.4))
put(tf, "想自己装的，会后单独找我 —— 今天不现场敲", 11, WHITE, bold=True,
    first=True, sa=2)
put(tf, "10 个人会卡在 8 个不同的地方，一起敲只会浪费大家时间。", 9.5,
    RGBColor(0xA8, 0xBA, 0xD8))

band(s, Inches(6.06), "两只龙虾，别搞混",
     "WorkBuddy 官方也叫「腾讯龙虾」，OpenClaw 本身就是只龙虾。"
     "一只是买来即用的，一只是自己养的——用途不同，今天都会讲到。",
     h=Inches(0.9), tone="light")


# =========================================================
# P8 入职培训 = 几个文件
# =========================================================
s = prs.slides.add_slide(BLANK)
header(s, "第 4 章", "给你的数字员工做入职培训", "本场核心：这一章决定你四周后是「用了个工具」还是「有了个同事」")

tf = tb(s, Inches(0.62), Inches(1.32), Inches(12.1), Inches(0.4))
put(tf, "大多数人用 AI 的问题：每次都要从零解释一遍背景。"
        "而记忆不是玄学，就是几个文件。", 13.5, NAVY, bold=True, first=True)

rows = [
    ["文件", "相当于新员工的", "里面写什么"],
    ["AGENTS.md", "员工手册", "工作规范、红线、遇到什么情况怎么办"],
    ["SOUL.md", "性格与价值观", "说话风格、是否敢反驳你、要不要客套"],
    ["!USER.md", "!认识你这个老板", "!你是谁、做什么、偏好什么、雷区在哪"],
    ["MEMORY.md", "工作日志与经验", "长期沉淀，它自己维护、自己纠错"],
    ["TOOLS.md", "工位设备清单", "你环境里特有的东西：服务器、账号、习惯"],
]
table(s, Inches(0.62), Inches(1.86), rows,
      [Inches(2.5), Inches(3.1), Inches(6.5)],
      font=12, row_h=Inches(0.56))

band(s, Inches(5.3), "换个说法：你不是在写配置，你在带新人",
     "新同事入职第一周，你会告诉他公司规矩、你的工作方式、哪些事不能碰。"
     "这几个文件就是把那件事写下来——写一次，以后每次都生效。",
     h=Inches(0.92), tone="light")

rect(s, Inches(0.62), Inches(6.42), Inches(12.1), Inches(0.56), fill=NAVY)
tf = tb(s, Inches(0.94), Inches(6.53), Inches(11.5), Inches(0.36))
put(tf, "WorkBuddy 用户：对应的是「助理」配置，逻辑完全一样，只是换个地方填。",
    12.5, WHITE, bold=True, first=True)


# =========================================================
# P9 两个反直觉演示
# =========================================================
s = prs.slides.add_slide(BLANK)
header(s, "第 4 章", "两个反直觉的现场演示", "这两件事不看一遍，大概不会相信")

# 演示一
rect(s, Inches(0.62), Inches(1.38), Inches(5.95), Inches(2.55), fill=WHITE,
     lc=LINE)
rect(s, Inches(0.62), Inches(1.38), Inches(5.95), Inches(0.06), fill=ACCENT)
tf = tb(s, Inches(0.94), Inches(1.64), Inches(5.35), Inches(2.1))
put(tf, "演示一：让它敢反驳你", 17, NAVY, bold=True, first=True, sa=7)
put(tf, "AI 爱拍马屁不是天性，是没人告诉它别这样。", 12, GREY, sa=8, line=1.25)
put(tf, "现场对比：同一个问题，问两次。", 12, NAVY, bold=True, sa=5)
put(tf, "第一次：不写任何规则", 11, MUTE, sa=2, line=1.2)
put(tf, "第二次：文件里写上「禁止奉承，我判断有问题必须拦我」", 11, ACCENT,
    bold=True, line=1.25)

# 演示二
rect(s, Inches(6.77), Inches(1.38), Inches(5.95), Inches(2.55), fill=WHITE,
     lc=LINE)
rect(s, Inches(6.77), Inches(1.38), Inches(5.95), Inches(0.06), fill=ACCENT)
tf = tb(s, Inches(7.09), Inches(1.64), Inches(5.35), Inches(2.1))
put(tf, "演示二：记忆是「写」，不是「查」", 17, NAVY, bold=True, first=True, sa=7)
put(tf, "很多人以为「有记忆」= 把历史对话塞进去让它搜。那是查资料。", 12, GREY,
    sa=8, line=1.25)
put(tf, "真实例子：它记错了我的名字。", 12, NAVY, bold=True, sa=5)
put(tf, "它做的不是「记住新名字」，而是改掉那一行，"
        "并标注「旧的是错的，勿再沿用」。", 11, ACCENT, bold=True, line=1.25)

# 对比说明
rows = [
    ["", "查资料式「记忆」", "真正的记忆"],
    ["机制", "~把历史内容检索出来", "^主动决定写什么、改什么、删什么"],
    ["纠错", "#下次还会犯同样的错", "^标注错误来源，不再重复"],
    ["结果", "~像个健忘的实习生", "^像个越用越顺手的老同事"],
]
table(s, Inches(0.62), Inches(4.12), rows,
      [Inches(1.5), Inches(5.0), Inches(5.6)],
      font=11.5, row_h=Inches(0.52))

band(s, Inches(6.36), "这就是「工具」和「同事」的分界线",
     None, h=Inches(0.5), tone="dark")


# =========================================================
# P10 三个实操作业
# =========================================================
s = prs.slides.add_slide(BLANK)
header(s, "第 4 章", "现在动手：三件事，20 分钟", "不用装 OpenClaw 也能做，WorkBuddy 助理里一样填")

tasks = [
    ("1", "写你自己的 USER.md",
     "你是谁 · 负责什么 · 手上在做的项目 · 你的偏好 · 你的雷区",
     "例：我负责测试，主要做流程引擎模块。给我结论先行，"
     "别写长段落。不确定的地方必须标出来。"),
    ("2", "定三条你自己的规矩",
     "写进文件，以后每次都生效",
     "例：① 回复用中文　② 先给结论再给理由　"
     "③ 你不确定的必须说不确定，不许猜"),
    ("3", "让它读一份你手上的真实文档",
     "看它是不是真懂了你的业务",
     "注意：只用 L0 / L1 级别的文档。"
     "这一条最容易出现「哇」的时刻，因为那是你自己的东西。"),
]
y = Inches(1.42)
for n, t, sub, ex in tasks:
    rect(s, Inches(0.62), y, Inches(12.1), Inches(1.55), fill=WHITE, lc=LINE)
    rect(s, Inches(0.62), y, Inches(0.06), Inches(1.55),
         fill=ACCENT if n == "3" else BLUE)
    tf = tb(s, Inches(1.02), y + Inches(0.52), Inches(0.6), Inches(0.5))
    put(tf, n, 22, ACCENT if n == "3" else BLUE, bold=True, first=True)
    tf = tb(s, Inches(1.75), y + Inches(0.2), Inches(10.7), Inches(1.2))
    put(tf, t, 16, NAVY, bold=True, first=True, sa=3, line=1.1)
    put(tf, sub, 11.5, GREY, sa=6, line=1.2)
    put(tf, ex, 11, MUTE, line=1.28)
    y += Inches(1.66)

band(s, Inches(6.5), "带走的东西",
     "散会时，你手上应该有一份写着你自己名字、你自己规矩的配置文件。"
     "这就是你的数字员工的第一天。", h=Inches(0.72), tone="light")


# =========================================================
# P11 实践任务（题库）
# =========================================================
s = prs.slides.add_slide(BLANK)
header(s, "第 5 章", "回去做什么：实践题目", "全员一道，报名者进重点轨")

tf = tb(s, Inches(0.62), Inches(1.3), Inches(12.1), Inches(0.4))
put(tf, "全员作业：完成刚才那三件事，两周后带一个真实场景来分享。", 14, NAVY,
    bold=True, first=True)

tf = tb(s, Inches(0.62), Inches(1.82), Inches(12.1), Inches(0.34))
put(tf, "报名者（重点轨）：每人认领一道题，四周只打这一道", 13.5, ACCENT,
    bold=True, first=True)

rows = [
    ["角色", "题目", "输入 → 输出", "产出的资产"],
    ["产品", "需求转事项规范", "会议纪要 / 原始需求 → 字段清单 + 流程节点 + 权限矩阵",
     "规范生成 prompt + 模板"],
    ["设计", "设计稿转组件映射", "页面设计稿 → 现有组件可覆盖部分 + 需新开发清单",
     "映射规则 + 占比实测"],
    ["!研发", "!事项规范转 schema", "!事项规范 → amis schema + 流程定义",
     "!转换 skill（最高价值）"],
    ["测试", "规范转测试用例集", "事项规范 → 正常 / 边界 / 异常 / 权限用例",
     "用例生成 skill"],
    ["!实施", "!同类事项跨校复配", "!A 校已配事项 + B 校差异 → B 校配置方案",
     "!复配 skill（最易出数字）"],
]
table(s, Inches(0.62), Inches(2.24), rows,
      [Inches(1.2), Inches(2.65), Inches(5.15), Inches(3.1)],
      font=10.5, row_h=Inches(0.62))

band(s, Inches(5.58), "为什么每人只打一道",
     "打三道会三道都浅。宁可一道打透，产出一个别人能直接拿去用的东西。",
     h=Inches(0.72), tone="light")

rect(s, Inches(0.62), Inches(6.44), Inches(12.1), Inches(0.62), fill=NAVY)
tf = tb(s, Inches(0.94), Inches(6.56), Inches(11.5), Inches(0.4))
put(tf, "设计与研发的题目锁定 amis 这条线 —— 前端有多套技术栈，一套规则通吃不了。",
    12.5, WHITE, bold=True, first=True)


# =========================================================
# P12 三条规则
# =========================================================
s = prs.slides.add_slide(BLANK)
header(s, "第 5 章", "三条规则，比题目本身更重要", "不讲清这三条，四周后我们手上什么都没有")

rules = [
    ("先做一次「不用 AI」的基线",
     "在开始用 AI 之前，先照老办法做一次，记下花了多久。",
     "没有基线，四周后只有感觉，没有数字。"
     "登记三个字段：是否使用 AI / 实际工时 / 是否需要研发协助。",
     ACCENT),
    ("允许宣布失败",
     "有些题目现在的 AI 确实做不好。诚实说「这条路不通，原因是 X」，"
     "价值等于成功。",
     "它省下其他人的时间，也告诉公司智能体工厂该往哪里补。"
     "分享会上会有「翻车奖」——说实话是被鼓励的。",
     GREEN),
    ("交作业交「件」，不交心得",
     "可以直接复制运行的东西：skill / 工作流 / prompt / 模板。",
     "附一句话：解决什么场景、省多少时间。"
     "心得体会类文档不计入资产。",
     BLUE),
]
y = Inches(1.44)
for i, (t, d, note, c) in enumerate(rules, 1):
    rect(s, Inches(0.62), y, Inches(12.1), Inches(1.62), fill=WHITE, lc=LINE)
    rect(s, Inches(0.62), y, Inches(0.06), Inches(1.62), fill=c)
    tf = tb(s, Inches(1.02), y + Inches(0.56), Inches(0.6), Inches(0.5))
    put(tf, str(i), 22, c, bold=True, first=True)
    tf = tb(s, Inches(1.75), y + Inches(0.22), Inches(10.7), Inches(1.24))
    put(tf, t, 16, NAVY, bold=True, first=True, sa=4, line=1.1)
    put(tf, d, 11.5, GREY, sa=5, line=1.25)
    put(tf, note, 11, MUTE, line=1.28)
    y += Inches(1.73)

band(s, Inches(6.68), "一句话：我们要的不是「大家都试过了」，是「省下的时间能被算出来」",
     None, h=Inches(0.52), tone="dark")


# =========================================================
# P13 会后机制
# =========================================================
s = prs.slides.add_slide(BLANK)
header(s, "收尾", "散会之后", "第一期最大的损耗不在会上，在会后三天")

items = [
    ("一个群", "卡住随时问", "把安装、配置、用法的问题都扔进去，别自己憋着。", BLUE),
    ("两个固定答疑时段", "每周二、周四下午各 30 分钟",
     "想装 OpenClaw 的、积分用光的、题目卡住的，来这两个时段。", BLUE),
    ("一个 deadline", "两周后第一次分享",
     "每人带一个真实场景。没有 deadline 的培训等于没培训。", ACCENT),
]
y = Inches(1.44)
for t, sub, d, c in items:
    rect(s, Inches(0.62), y, Inches(12.1), Inches(1.2), fill=WHITE, lc=LINE)
    rect(s, Inches(0.62), y, Inches(0.06), Inches(1.2), fill=c)
    tf = tb(s, Inches(1.02), y + Inches(0.22), Inches(4.0), Inches(0.8))
    put(tf, t, 17, NAVY, bold=True, first=True, sa=3, line=1.1)
    put(tf, sub, 12, c, bold=True, line=1.15)
    tf = tb(s, Inches(5.4), y + Inches(0.38), Inches(7.0), Inches(0.6))
    put(tf, d, 12, GREY, first=True, line=1.3)
    y += Inches(1.3)

# 报名
rect(s, Inches(0.62), Inches(5.42), Inches(12.1), Inches(1.02), fill=CREAM,
     lc=CREAM_L)
tf = tb(s, Inches(0.94), Inches(5.58), Inches(11.5), Inches(0.75))
put(tf, "现在举手：谁想进重点轨，配 OpenClaw、认领题目？", 17, RED, bold=True,
    first=True, sa=4)
put(tf, "当场登记。散会之后再收报名，人数会掉一半。", 11.5, GREY)

rect(s, Inches(0.62), Inches(6.6), Inches(12.1), Inches(0.66), fill=NAVY)
tf = tb(s, Inches(0.94), Inches(6.72), Inches(11.5), Inches(0.44),
        align=PP_ALIGN.CENTER)
put(tf, "同样的活不干第二遍", 19, WHITE, bold=True, first=True,
    align=PP_ALIGN.CENTER)


out = "/home/ubuntu/.openclaw/workspace/output/第一期培训-一起养虾-20260727.pptx"
prs.save(out)
print("saved:", out)
