#!/usr/bin/env python3 # -*- coding: utf-8 -*- """作业 9 · 生成 16:9 HTML 幻灯片(浏览器全屏即 PPT)。""" from __future__ import annotations import html from pathlib import Path from typing import Any import _bootstrap # noqa: F401 from 作业9_ppt_data import data_dir, fmt_int, fmt_pct, repo_root def e(s: Any) -> str: return html.escape("" if s is None else str(s), quote=True) def svg_bars(daily: list[dict[str, Any]]) -> str: if not daily: return "

暂无趋势数据

" vals = [int(d.get("traces") or 0) for d in daily] mx = max(vals) or 1 w, h, pad = 720, 220, 28 gap = 6 n = len(vals) bw = max(8, (w - pad * 2 - gap * (n - 1)) / n) parts = [ '' % (w, h) ] parts.append( '' % (w, h) ) for i, d in enumerate(daily): v = int(d.get("traces") or 0) bh = (v / mx) * (h - pad * 2 - 18) x = pad + i * (bw + gap) y = h - pad - 14 - bh parts.append( '' % (x, y, bw, bh) ) label = (d.get("date") or "")[5:] if i % max(1, n // 7) == 0 or i == n - 1: parts.append( '%s' % (x + bw / 2, h - 8, e(label)) ) parts.append("") return "".join(parts) def render_html(deck: dict[str, Any]) -> str: core = deck.get("core") or {} biz = deck.get("business") or {} cost = deck.get("cost") or {} nxt = deck.get("next") or {} mom = core.get("mom") or {} daily = list(core.get("daily") or []) goals = "".join("
  • %s
  • " % e(x) for x in (nxt.get("goals") or [])) res = "".join("
  • %s
  • " % e(x) for x in (nxt.get("resources") or [])) risks = "".join("
  • %s
  • " % e(x) for x in (nxt.get("risks") or [])) wow_t = fmt_pct(mom.get("traces_wow_pct") or 0, signed=True) wow_k = fmt_pct(mom.get("tokens_wow_pct") or 0, signed=True) return f""" {e(deck.get("title"))} · PPT 模版
    {e(deck.get("org"))} · 管理层汇报模版

    {e(deck.get("title"))}

    {e(deck.get("period"))}

    汇报人:{e(deck.get("presenter"))}

    ← → 翻页 · F 全屏 · 数字为模版可替换项 + 平台实测

    1. 平台核心指标(一页纸)

    总请求量(近 14 日 Trace){e(fmt_int(core.get("total_requests") or 0))}环比 {e(wow_t)}
    Token 消耗{e(fmt_int(core.get("total_tokens") or 0))}环比 {e(wow_k)}
    平台可用性{e(core.get("availability_pct"))}%目标 ≥ 99.9%
    延迟 P99{e(core.get("ttft_p99"))}s / {e(core.get("full_p99"))}s首 Token / 完整响应
    请求量环比趋势(按日 Trace)
    {svg_bars(daily)}
    数据来源

    请求 / Token:{e(core.get("langfuse_source"))}(Langfuse)

    可用性 / 延迟:{e(core.get("sla_source"))}(作业8 SLA)

    前半窗口 vs 后半窗口作为「环比」。业务数字在后续页用模版占位,可改 overrides.json。

    2. 业务价值(重点)

    客服效率

    +{e(biz.get("cs_efficiency_pct"))}%

    {e(biz.get("cs_note"))}

    研发编码效率

    +{e(biz.get("dev_efficiency_pct"))}%

    {e(biz.get("dev_note"))}

    知识检索时间

    {e(biz.get("kb_before_minutes"))} 分钟 → {e(biz.get("kb_after_seconds"))} 秒

    {e(biz.get("kb_note"))}

    3. 成本优化

    {e(cost.get("period_label") or "本季度")}通过量化 / 缓存 / 模型降级节省

    ¥ {e(cost.get("saved_wan"))} 万元
    量化 {e(cost.get("quantize_pct"))}%
    缓存 {e(cost.get("cache_pct"))}%
    模型降级 {e(cost.get("downgrade_pct"))}%

    {e(cost.get("note"))}

    4. 下一步规划

    下季度目标

      {goals}

    资源需求

      {res}

    风险预警

      {risks}

    谢谢

    问答 · 大盘 http://192.168.110.207:3001/d/ai-platform-sla

    改业务数字:data/homework9/overrides.json 后重跑生成脚本

    {e(deck.get("org"))}1 / 6
    ← → 翻页 · F 全屏
    """ def write_html(deck: dict[str, Any]) -> list[Path]: text = render_html(deck) outs = [ data_dir() / "AI平台经营汇报.html", repo_root() / "docs" / "html_cursor" / "作业9_AI平台经营汇报幻灯片_cursor.html", ] written: list[Path] = [] for p in outs: p.parent.mkdir(parents=True, exist_ok=True) p.write_text(text, encoding="utf-8") written.append(p) return written