ai-agent-book 精选快照(<2MB 代码与文档,来自 github.com/bojieli/ai-agent-book)
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s

This commit is contained in:
2026-08-20 13:12:50 +00:00
commit b119135836
10275 changed files with 3284984 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
/output/
external/
__pycache__/
*.pyc
.env
# Official-run workspace noise: dependency installs and runtime skill symlinks
# (the pinned Skill itself is verifiable via external/official_skill_receipt.json).
runs/*/workspace/**/node_modules/
runs/*/workspace/kimi-skills/
runs/*/workspace/.claude/
runs/*/workspace/deckbuild/
# Official-run decks are deliberate evidence artifacts; re-include them.
!runs/*/workspace/output/
!runs/*/workspace/output/**
+410
View File
@@ -0,0 +1,410 @@
# Agent Skills PPT Demo / 使用 Agent Skills 从论文生成演示文稿
> Companion material for *AI Agents in Depth*, Chapter 2 — **Experiment 2-6 ★★: Generate a presentation from a paper using Agent Skills**.
> 配套《深入理解 AI Agent》第 2 章 **实验 2-6 ★★:使用 Agent Skills 从论文生成演示文稿**。
← [Chapter 2 index / 返回第 2 章目录](../README.md)
---
## Canonical manuscript reproduction / 正式复现实验
Experiment 2-6 is **the pinned official Anthropic PPTX Skill + a real academic
PDF**, executed by a skills-capable agent runtime. Under the author's
runtime-agnostic acceptance policy (2026-07-31), acceptance is NOT gated on
Anthropic credentials: the runtime may be **Claude Code** or an equivalent
runtime that supports SKILL.md-style progressive disclosure, such as **Kimi
Code CLI**. The pinned Skill content, the real paper, and every artifact gate
are identical for either runtime.
The runner pins the official repository to revision
`69c0b1a0674149f27b61b2635f935524b6add202`, the revision containing the
`html2pptx.md` flow named in the manuscript, and uses Vaswani et al.'s real
*Attention Is All You Need* PDF (arXiv:1706.03762, SHA-256
`bdfaa68d...82df697`).
Run with Kimi Code CLI (`KIMI_API_KEY` / `MOONSHOT_API_KEY`, model
`kimi-code/k3`):
```bash
cd chapter2/agent-skills-ppt
python run_official_experiment.py --runtime kimi \
--output runs/exp2-6-kimi-pptx-$(date +%Y%m%d-%H%M%S)
```
Run with Claude Code (valid `ANTHROPIC_API_KEY`, or `--auth-source
claude-login` for an enabled Claude Code login):
```bash
cd chapter2/agent-skills-ppt
python run_official_experiment.py --runtime claude \
--output runs/exp2-6-claude-pptx-$(date +%Y%m%d-%H%M%S)
```
Both paths fetch and verify the pinned external Skill (never copied or
reimplemented), install it as the runtime's only Skill (Claude:
`.claude/skills/pptx` symlink; Kimi: `--skills-dir`, which replaces the
auto-discovered skill directories for that launch), and capture the raw
stream-json event stream as the receipt. Raw events prove Skill selection,
full `SKILL.md`/`html2pptx.md` disclosure, official script use, thumbnail
inspection, and artifact creation. The fail-closed validator requires 1015
slides, all manuscript sections, three PDF-extracted visuals byte-identical to
media embedded in the deck, a full-deck thumbnail grid, and a credential scan
of the stream. See `experiment_protocol.json` for all frozen gates.
### Canonical evidence status (2026-07-31): PASSED with Kimi Code CLI
`runs/exp2-6-kimi-pptx-20260731-v1/manifest.json` passes all 15 gates:
- Runtime: Kimi Code CLI 0.31.0, model `kimi-code/k3`, 114 tool calls over 25
assistant turns; the raw stream (`kimi_stream.jsonl`) contains no credential
material.
- Progressive disclosure is genuine: the model invoked the `pptx` Skill
(metadata → full `SKILL.md`), then read `html2pptx.md`, used the official
`scripts/html2pptx.js` workflow, ran the official `scripts/thumbnail.py`,
and iterated on visually inspected thumbnails (overlap/cutoff fixes) before
finishing.
- Deck: 13 slides covering title, background, method/architecture, training,
key results, generalization, interpretability, and conclusion; valid
OOXML ZIP, reopened by python-pptx and rendered to 13 pages by LibreOffice.
- Four visuals (Figure 1, Figure 2, Table 2, Figure 3) were cropped from the
source PDF with `pdftoppm`, documented in `source_visuals/manifest.json`
with page/label/caption, and are byte-identical to media embedded in the
PPTX.
Earlier Claude Code attempts (`runs/exp2-6-claude-pptx-20260730-v2``v4`) were
externally blocked before inference by invalid/disabled Anthropic credentials;
their fail-closed manifests and credential-free streams are retained as
evidence of the old gate. The Claude path above remains fully supported for
readers who have Anthropic credentials. The existing
`output/presentation.pptx` belongs to the legacy demo (nine slides and no
embedded media) and is not acceptance evidence.
正式复现使用固定的 Anthropic 官方 PPTX Skill 与真实论文 PDF,运行时可以是
Claude Code 或支持 SKILL.md 渐进式披露的等价运行时(如 Kimi Code CLI)——实验
对象是 Skill 内容,运行时可替换。两条路径都会固定外部仓库版本、保存完整的渐进式
披露轨迹,并对页数、章节、论文原图、PPTX 有效性、缩略图和凭证泄漏逐项验收。
## Legacy mechanism illustration (not acceptance evidence)
The older `demo.py` and bundled `skills/pptx` tree below are retained as an
offline teaching aid. They use a local isomorphic loader and a prewritten short
outline, so neither online nor offline mode counts as fulfillment of the
manuscript experiment.
以下旧 demo 仅用于离线讲解机制,不属于实验 2-6 的正式验收证据。
---
## English
### Legacy demo purpose
Validates a core claim from the book: **an Agent can complete complex work by loading domain Skills on demand via progressive disclosure**, without stuffing all knowledge into the system prompt at once.
This demo lets an Agent turn a (bundled) short paper into an 812 page PowerPoint. At startup the Agent sees only a **thin Skill catalog**; when it decides the task needs the `pptx` Skill, it loads the full workflow, sub-docs, and bundled scripts layer by layer, then generates a real `.pptx` with **python-pptx**.
### Relation to Anthropics PPTX Skill
The original book experiment ran on **Claude Code + Anthropics official PPTX Skill**. Because Anthropic access is not always available, this project **implements an isomorphic Skills mechanism** (not Anthropics runtime):
| Dimension | Anthropic PPTX Skill (book) | This project (isomorphic) |
|-----------|-----------------------------|---------------------------|
| Runtime | Claude Code | Python + OpenAI SDK (`gpt-5.6-luna`) |
| Layer 1 · metadata | Inject name+description of all Skills at start | `scan_skill_catalog()` reads frontmatter into the system prompt |
| Layer 2 · core flow | Skill tool loads full `SKILL.md` | `read_skill` loads `skills/pptx/SKILL.md` |
| Layer 3 · details | Refs like `html2pptx.md` / `reference.md` | `read_skill_file` reads `reference.md` / script sources |
| Bundled scripts | e.g. `scripts/thumbnail.py` | `scripts/generate_pptx.py` (python-pptx generator) |
The mechanism maps one-to-one; the built-in Skill loader is replaced by explicit read/execute tools so progressive disclosure still works without Anthropic access.
> **OpenRouter fallback:** Primary path is OpenAI (default model `gpt-5.6-luna`). If `OPENAI_API_KEY` is unset but `OPENROUTER_API_KEY` is set, requests go through OpenRouter (`gpt-*` → `openai/…`). With `OPENAI_API_KEY` set, behavior is unchanged.
### Three-layer progressive disclosure
```
skills/
└── pptx/
├── SKILL.md # L1: YAML frontmatter (name+description) only in system prompt
│ # L2: body core flow — loaded via read_skill
├── reference.md # L3: layout/color/tech details — via read_skill_file
└── scripts/
└── generate_pptx.py # Bundled script — via run_skill_script
```
- **Layer 1 (metadata):** At startup the system prompt only has each Skills `name + description` (~hundreds of tokens). The Agent does not yet know how to build a PPT.
- **Layer 2 (core flow):** When the task needs `pptx`, it calls `read_skill("pptx")` and loads full `SKILL.md` as a tool result (page plan + script conventions).
- **Layer 3 (details):** For implementation/style detail, call `read_skill_file("pptx", "reference.md")` or read script sources.
- **Execute:** Build a slide-outline JSON, call `run_skill_script``generate_pptx.py``output/presentation.pptx`.
### Run
```bash
# From the repository root: use the shared Chapter 2 environment
uv sync --locked --python 3.12 --extra ch2
# Activate it before changing directories:
# macOS/Linux:
source .venv/bin/activate
# Windows PowerShell: .\.venv\Scripts\Activate.ps1
# Windows cmd: .venv\Scripts\activate.bat
# pip fallback when uv is not installed:
# python -m pip install -e ".[ch2]"
cd chapter2/agent-skills-ppt
# Single-project compatibility path, still supported during migration:
# python -m pip install -r requirements.txt
cp env.example .env # or export directly
export OPENAI_API_KEY=your-openai-api-key # default model gpt-5.6-luna; override with OPENAI_MODEL
python demo.py
python demo.py --paper papers/your_paper.md # different paper/outline
python demo.py -o output/deck.pptx --model gpt-5.6-luna # output path / model
python demo.py --help # full flag list
```
One command `python demo.py` runs the full path: real OpenAI calls, prints each progressive-disclosure step, writes `output/presentation.pptx`, and re-opens the file with python-pptx to verify page count and titles.
#### CLI flags
| Flag | Default | Description |
|------|---------|-------------|
| `--paper` | `papers/sample_paper.md` | Input paper/outline (markdown) path |
| `--output` / `-o` | `output/presentation.pptx` | Output `.pptx` path |
| `--model` | `OPENAI_MODEL` or `gpt-5.6-luna` | OpenAI model name |
| `--max-turns` | `8` | Max agentic-loop turns |
| `--offline` | off | Offline demo, no OpenAI (see below) |
#### Offline mode (no API key, reproducible)
Without an OpenAI key, `--offline` runs the same three-layer progressive disclosure: it reads the bundled outline `papers/sample_outline.json` and uses the **same tool path** (`read_skill``read_skill_file``run_skill_script`) to generate and verify the pptx deterministically. The only difference is that which Skill/outline to use is fixed by files, not live model decisions—good for teaching demos and smoke tests.
```bash
python demo.py --offline # writes output/presentation.pptx, no network
python demo.py --offline -o output/deck.pptx # custom output path
```
#### Offline validation
```bash
# From the repository root; include dev tools for pytest.
uv sync --locked --python 3.12 --extra ch2 --extra dev
source .venv/bin/activate
# Windows PowerShell: .\.venv\Scripts\Activate.ps1
cd chapter2/agent-skills-ppt
python -m pytest tests
python demo.py --offline
```
`tests/` contains offline regressions for malformed or unsafe tool-dispatch arguments and PPTX generator edge cases. They do not require an API key.
The bundled script can also run alone (no Agent):
```bash
python skills/pptx/scripts/generate_pptx.py papers/sample_outline.json output/deck.pptx
```
### Sample run output (excerpt)
```
【第一层·元数据】Agent 启动时只看到这份薄 Skill 目录(system prompt):
== 已安装的 Skills(薄目录,仅元数据)==
- pptx: 从论文...生成 PowerPoint...Use when...Don't use when...
[Agent 第 1 轮] 调用工具 -> read_skill(name=pptx)
>>> [渐进式披露·第二层] 加载完整 SKILL.md1150 字符)
[Agent 第 2 轮] 调用工具 -> read_skill_file(name=pptx, path=scripts/generate_pptx.py)
>>> [渐进式披露·第三层] 加载子文档(4270 字符)
[Agent 第 3 轮] 调用工具 -> run_skill_script(name=pptx, script=generate_pptx.py, ...)
>>> 生成 presentation.pptx ...
【校验】用 python-pptx 重新打开生成的文件,读回页数与每页标题:
总页数: 9
第 1 页标题: 精简论文:渐进式披露式 Agent Skills 对上下文效率的影响
...
校验通过:这是一个可被 python-pptx / PowerPoint 打开的有效 .pptx9 页)。
```
(Page count/titles are planned live by the model and may vary slightly, usually within 812 pages.)
### Files
| File | Role |
|------|------|
| `demo.py` | Main: thin catalog scan → agentic loop → progressive disclosure → generate & verify pptx |
| `skills/pptx/SKILL.md` | pptx Skill: frontmatter (metadata) + core flow |
| `skills/pptx/reference.md` | Layer 3: layout/color/python-pptx notes |
| `skills/pptx/scripts/generate_pptx.py` | Bundled generator: outline → `.pptx` |
| `papers/sample_paper.md` | Bundled short paper/outline (online input) |
| `papers/sample_outline.json` | Slide outline for offline mode (payload schema example) |
| `tests/` | Offline regression tests for dispatch safety and generator edge cases |
| `output/presentation.pptx` | Generated deck (created at runtime) |
### Use another paper
Replace `papers/sample_paper.md` or pass `python demo.py --paper your_paper.md`.
---
## 中文
### 目的
验证书中的核心命题:**Agent 通过「渐进式披露(Progressive Disclosure)」按需加载专业领域 Skill,即可完成复杂任务,而无需把所有知识一次性塞进系统提示词。**
本 demo 让一个 Agent 从一篇(自带的)精简论文生成一份 8-12 页的 PowerPoint。Agent 启动时**只看到一份薄 Skill 目录**,当它识别出任务需要 `pptx` Skill 后,才逐层加载该 Skill 的完整流程、子文档与捆绑脚本,最后用 **python-pptx** 生成真实的 `.pptx` 文件。
### 与 Anthropic PPTX Skill 的关系
书中原实验跑在 **Claude Code + Anthropic 官方 PPTX Skill** 上。由于当前环境的 Anthropic key 未必可用,本项目**自建了一套同构的 Skills 机制**来复现同样的思想,而非调用 Anthropic:
| 维度 | Anthropic PPTX Skill(书中) | 本项目(自建同构版) |
|------|------------------------------|----------------------|
| 运行时 | Claude Code | Python + OpenAI SDK`gpt-5.6-luna` |
| 第一层·元数据 | 启动注入所有 Skill 的 name+description | `scan_skill_catalog()` 只读 frontmatter 拼进 system prompt |
| 第二层·核心流程 | Skill 工具加载完整 `SKILL.md` | `read_skill` 工具加载 `skills/pptx/SKILL.md` |
| 第三层·细则 | 引用 `html2pptx.md` / `reference.md` | `read_skill_file``reference.md` / 脚本源码 |
| 捆绑脚本 | `scripts/thumbnail.py` 等 | `scripts/generate_pptx.py`python-pptx 生成器) |
机制一一对应,只是把「Claude 内置的 Skill 加载器」换成了几个显式的读取/执行工具,从而在没有 Anthropic 访问权限时,依然能真实演示渐进式披露的三层加载过程。
> 说明:本项目主用 OpenAI(默认模型 gpt-5.6-luna)。**通用回退**:未设置 `OPENAI_API_KEY` 时,只要配置了 `OPENROUTER_API_KEY`,会自动改走 OpenRouter`gpt-*` 映射为 `openai/…`)。设置了 `OPENAI_API_KEY` 时行为完全不变。
### 渐进式披露的三层结构
```
skills/
└── pptx/
├── SKILL.md # 第一层:顶部 YAML frontmatter(name+description) —— 只有它进 system prompt
│ # 第二层:正文核心流程 —— read_skill 时才加载
├── reference.md # 第三层:版式/配色/技术细则 —— read_skill_file 时才加载
└── scripts/
└── generate_pptx.py # 捆绑可执行脚本 —— run_skill_script 时才执行
```
- **第一层(元数据)**:Agent 启动时,`system prompt` 里只有各 Skill 的 `name + description`(约数百 token)。此刻它并不知道怎么做 PPT。
- **第二层(核心流程)**:Agent 判断任务需要 `pptx`,调用 `read_skill("pptx")` 把完整 `SKILL.md` 作为 tool result 载入上下文,得到页序规划与脚本调用约定。
- **第三层(细则)**:如需实现/样式细节,Agent 再用 `read_skill_file("pptx", "reference.md")` 或读取脚本源码。
- **执行**Agent 组织好幻灯片大纲 JSON,通过 `run_skill_script` 调用捆绑的 `generate_pptx.py`,用 python-pptx 落地为 `output/presentation.pptx`
### 运行
```bash
# 在仓库根目录使用统一的第 2 章环境
uv sync --locked --python 3.12 --extra ch2
# 切换目录前先激活环境:
# macOS/Linux
source .venv/bin/activate
# Windows PowerShell.\.venv\Scripts\Activate.ps1
# Windows cmd.venv\Scripts\activate.bat
# 未安装 uv 时可用 pip 兜底:
# python -m pip install -e ".[ch2]"
cd chapter2/agent-skills-ppt
# 迁移期间仍支持单项目兼容路径:
# python -m pip install -r requirements.txt
cp env.example .env # 或直接 export
export OPENAI_API_KEY=your-openai-api-key # 默认模型 gpt-5.6-luna,可用 OPENAI_MODEL 覆盖
python demo.py
python demo.py --paper papers/your_paper.md # 换一篇论文/大纲
python demo.py -o output/deck.pptx --model gpt-5.6-luna # 指定输出路径 / 模型
python demo.py --help # 查看全部参数
```
一条命令 `python demo.py` 即可跑通:真实调用 OpenAI,打印渐进式披露的每一步,生成 `output/presentation.pptx`,并用 python-pptx 重新打开该文件读回页数与每页标题作为校验。
#### 命令行参数
| 参数 | 默认值 | 说明 |
|------|--------|------|
| `--paper` | `papers/sample_paper.md` | 输入论文/大纲(markdown)路径 |
| `--output` / `-o` | `output/presentation.pptx` | 输出 `.pptx` 路径 |
| `--model` | `OPENAI_MODEL``gpt-5.6-luna` | OpenAI 模型名 |
| `--max-turns` | `8` | agentic loop 的最大轮数 |
| `--offline` | 关 | 离线演示,不调用 OpenAI(见下) |
#### 离线模式(无需 API key,可复现)
没有 OpenAI key 时,用 `--offline` 即可跑通同一套三层渐进式披露:它读取内置大纲 `papers/sample_outline.json`,走**与在线完全相同**的工具通道(`read_skill``read_skill_file``run_skill_script`)确定性地生成并校验 pptx。唯一区别是「用哪个 Skill、大纲写什么」由预置文件给定,而非模型即时决策——因此它适合作为可复现的教学演示与冒烟测试。
```bash
python demo.py --offline # 生成 output/presentation.pptx,全程无网络
python demo.py --offline -o output/deck.pptx # 指定输出路径
```
#### 离线验证
```bash
# 从仓库根目录开始;pytest 需要 dev 依赖。
uv sync --locked --python 3.12 --extra ch2 --extra dev
source .venv/bin/activate
# Windows PowerShell: .\.venv\Scripts\Activate.ps1
cd chapter2/agent-skills-ppt
python -m pytest tests
python demo.py --offline
```
`tests/` 包含工具分发参数缺失、非法路径和 PPTX 生成器边界情况的离线回归测试,无需 API Key。
捆绑脚本本身也可脱离 Agent 单独运行,直接把大纲 JSON 落地为 pptx:
```bash
python skills/pptx/scripts/generate_pptx.py papers/sample_outline.json output/deck.pptx
```
### 真实运行输出(节选)
```
【第一层·元数据】Agent 启动时只看到这份薄 Skill 目录(system prompt):
== 已安装的 Skills(薄目录,仅元数据)==
- pptx: 从论文...生成 PowerPoint...Use when...Don't use when...
[Agent 第 1 轮] 调用工具 -> read_skill(name=pptx)
>>> [渐进式披露·第二层] 加载完整 SKILL.md1150 字符)
[Agent 第 2 轮] 调用工具 -> read_skill_file(name=pptx, path=scripts/generate_pptx.py)
>>> [渐进式披露·第三层] 加载子文档(4270 字符)
[Agent 第 3 轮] 调用工具 -> run_skill_script(name=pptx, script=generate_pptx.py, ...)
>>> 生成 presentation.pptx ...
【校验】用 python-pptx 重新打开生成的文件,读回页数与每页标题:
总页数: 9
第 1 页标题: 精简论文:渐进式披露式 Agent Skills 对上下文效率的影响
第 2 页标题: 目录
...
第 9 页标题: 小结
校验通过:这是一个可被 python-pptx / PowerPoint 打开的有效 .pptx9 页)。
```
(页数/标题由模型即时规划,每次运行可能略有差异,但均落在 8-12 页区间。)
### 文件说明
| 文件 | 作用 |
|------|------|
| `demo.py` | 主程序:扫描薄目录 → agentic loop → 渐进式披露 → 生成并校验 pptx |
| `skills/pptx/SKILL.md` | pptx Skillfrontmatter(元数据)+ 核心流程 |
| `skills/pptx/reference.md` | 第三层细则:版式/配色/python-pptx 技术点 |
| `skills/pptx/scripts/generate_pptx.py` | 捆绑生成器,用 python-pptx 从大纲生成 .pptx |
| `papers/sample_paper.md` | 自带的精简论文/大纲(在线模式输入) |
| `papers/sample_outline.json` | 内置幻灯片大纲(离线模式输入,同时是 payload schema 的范例) |
| `tests/` | 工具分发安全性与生成器边界情况的离线回归测试 |
| `output/presentation.pptx` | 生成的演示文稿(输出,运行后产生) |
### 换一篇论文
`papers/sample_paper.md` 替换为你自己的论文/大纲(markdown),或直接 `python demo.py --paper 你的论文.md` 指定路径即可。
---
## Notes / 说明
- Commands, paths, env vars, and model names are identical in both language sections.
- 命令、路径、环境变量与模型名在中英文两节中保持一致。
+430
View File
@@ -0,0 +1,430 @@
#!/usr/bin/env python3
"""
实验 2-6:使用 Agent Skills 从论文生成演示文稿(自建同构 Skills 机制)
本 demo 复现《深入理解 AI Agent》第二章「Agent Skills / 渐进式披露」一节的思想。
由于 Anthropic key 无效,这里用 OpenAIgpt-5.6-luna+ 一套自建的、与 Anthropic
Skills 同构的机制来演示,核心是「渐进式披露(Progressive Disclosure)」:
第一层(元数据):Agent 启动时的 system prompt 里只放各 Skill 的 name +
description(薄目录,数百 token),并不含具体流程。
第二层(核心流程):当任务需要时,Agent 主动用 read_skill 工具加载完整 SKILL.md。
第三层(细则):Agent 可再用 read_skill_file 读取 reference.md / 脚本源码。
然后 Agent 用捆绑脚本 scripts/generate_pptx.py(经 run_skill_script 工具)用
python-pptx 生成真实的 .pptx,并读回校验页数与每页标题。
运行:
export OPENAI_API_KEY=your-openai-api-key
python demo.py
"""
import argparse
import json
import os
import sys
from pathlib import Path
from openai import OpenAI
from pptx import Presentation
# 从同目录 .env 读取 OPENAI_API_KEY(若安装了 python-dotenv
try:
from dotenv import load_dotenv
load_dotenv(Path(__file__).resolve().parent / ".env")
except ImportError:
pass
# ---------------------------------------------------------------------------
# 路径与配置
# ---------------------------------------------------------------------------
ROOT = Path(__file__).resolve().parent
SKILLS_DIR = ROOT / "skills"
PAPER_PATH = ROOT / "papers" / "sample_paper.md"
OUTPUT_DIR = ROOT / "output"
MODEL = os.environ.get("OPENAI_MODEL", "gpt-5.6-luna")
def log(msg: str) -> None:
print(msg, flush=True)
# ---------------------------------------------------------------------------
# 第一层:启动时扫描 skills/ 目录,只读取每个 SKILL.md 的 frontmatter
# name + description),拼成薄目录注入 system prompt。这一步刻意「只看目录」。
# ---------------------------------------------------------------------------
def parse_frontmatter(skill_md: str) -> dict:
"""从 SKILL.md 顶部的 --- YAML frontmatter --- 中解析 name / description。"""
meta = {}
if not skill_md.startswith("---"):
return meta
end = skill_md.find("---", 3)
if end == -1:
return meta
for line in skill_md[3:end].splitlines():
if ":" in line:
k, v = line.split(":", 1)
meta[k.strip()] = v.strip()
return meta
def scan_skill_catalog() -> dict:
"""返回 {skill_name: {"description":..., "dir": Path}},只含元数据。"""
catalog = {}
for skill_md in sorted(SKILLS_DIR.glob("*/SKILL.md")):
meta = parse_frontmatter(skill_md.read_text(encoding="utf-8"))
name = meta.get("name") or skill_md.parent.name
catalog[name] = {
"description": meta.get("description", ""),
"dir": skill_md.parent,
}
return catalog
def build_system_prompt(catalog: dict) -> str:
lines = [
"你是一个能使用 Agent Skills 的助手。你并不预先知道每个 Skill 的详细流程,",
"只在下方看到一份「薄目录」——每个 Skill 的 name 与 description(路由条件)。",
"",
"当任务需要某个 Skill 时,你必须:",
" 1) 先用 read_skill(name) 加载它的完整 SKILL.md(第二层:核心流程);",
" 2) 如需实现/样式细节,再用 read_skill_file(name, path) 读取子文档或脚本(第三层);",
" 3) 按 SKILL.md 的约定,用 run_skill_script 调用捆绑脚本完成任务。",
"不要在没有 read_skill 的情况下臆测某个 Skill 的调用方式。",
"",
"== 已安装的 Skills(薄目录,仅元数据)==",
]
for name, info in catalog.items():
lines.append(f"- {name}: {info['description']}")
return "\n".join(lines)
# ---------------------------------------------------------------------------
# 工具实现:read_skill / read_skill_file / run_skill_script
# 这些是「渐进式披露」的通道——第二、三层内容只有被调用时才进入上下文。
# ---------------------------------------------------------------------------
def tool_read_skill(catalog: dict, name: str) -> str:
info = catalog.get(name)
if not info:
return f"[error] 未找到 Skill: {name}"
content = (info["dir"] / "SKILL.md").read_text(encoding="utf-8")
log(f"\n >>> [渐进式披露·第二层] Agent 调用 read_skill('{name}')"
f"加载完整 SKILL.md{len(content)} 字符)")
return content
def tool_read_skill_file(catalog: dict, name: str, rel_path: str) -> str:
info = catalog.get(name)
if not info:
return f"[error] 未找到 Skill: {name}"
target = (info["dir"] / rel_path).resolve()
# 防目录穿越:必须落在该 skill 目录内
if not str(target).startswith(str(info["dir"].resolve())):
return f"[error] 非法路径: {rel_path}"
if not target.exists():
return f"[error] 文件不存在: {rel_path}"
content = target.read_text(encoding="utf-8")
log(f" >>> [渐进式披露·第三层] Agent 调用 read_skill_file('{name}', '{rel_path}')"
f"加载子文档({len(content)} 字符)")
return content
def tool_run_skill_script(catalog: dict, name: str, script: str, payload: str,
out_path: Path) -> str:
info = catalog.get(name)
if not info:
return f"[error] 未找到 Skill: {name}"
scripts_dir = (info["dir"] / "scripts").resolve()
script_path = (scripts_dir / script).resolve()
# 防目录穿越:脚本会被直接执行,必须落在该 skill 的 scripts 目录内
if not script_path.is_relative_to(scripts_dir):
return f"[error] 非法脚本路径: {script}"
if not script_path.exists():
return f"[error] 脚本不存在: {script}"
# 动态载入捆绑脚本(它就是 Skill 的一部分)
import importlib.util
spec = importlib.util.spec_from_file_location("bundled_generator", script_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
try:
data = json.loads(payload) if isinstance(payload, str) else payload
except json.JSONDecodeError as e:
return f"[error] payload 不是合法 JSON: {e}"
log(f" >>> [执行捆绑脚本] run_skill_script('{name}', '{script}') "
f"生成 {out_path.name} ...")
result = module.build_presentation(data, str(out_path))
return json.dumps(result, ensure_ascii=False)
TOOLS = [
{
"type": "function",
"function": {
"name": "read_skill",
"description": "加载指定 Skill 的完整 SKILL.md(核心流程,渐进式披露第二层)。",
"parameters": {
"type": "object",
"properties": {"name": {"type": "string", "description": "Skill 名称"}},
"required": ["name"],
},
},
},
{
"type": "function",
"function": {
"name": "read_skill_file",
"description": "读取某 Skill 目录内的子文档或脚本源码(细则,渐进式披露第三层)。",
"parameters": {
"type": "object",
"properties": {
"name": {"type": "string"},
"path": {"type": "string", "description": "相对 skill 目录的路径,如 reference.md 或 scripts/generate_pptx.py"},
},
"required": ["name", "path"],
},
},
},
{
"type": "function",
"function": {
"name": "run_skill_script",
"description": "执行某 Skill 捆绑的脚本以完成实际产出(如生成 pptx)。",
"parameters": {
"type": "object",
"properties": {
"name": {"type": "string"},
"script": {"type": "string", "description": "脚本文件名,如 generate_pptx.py"},
"payload": {"type": "string", "description": "传给脚本的 JSON 字符串(大纲)"},
},
"required": ["name", "script", "payload"],
},
},
},
]
def dispatch(catalog: dict, name: str, args: dict, out_path: Path) -> str:
# 模型给的 arguments 可能缺字段(或根本不是合法 JSON,被上层回退成 {})。
# 缺参时返回 [error] 让 Agent 在下一轮自我纠正,而不是抛 KeyError 中断 loop。
required = {
"read_skill": ["name"],
"read_skill_file": ["name", "path"],
"run_skill_script": ["name", "script", "payload"],
}
if name not in required:
return f"[error] 未知工具: {name}"
missing = [k for k in required[name] if k not in args]
if missing:
return f"[error] 工具 {name} 缺少参数: {', '.join(missing)}"
if name == "read_skill":
return tool_read_skill(catalog, args["name"])
if name == "read_skill_file":
return tool_read_skill_file(catalog, args["name"], args["path"])
return tool_run_skill_script(catalog, args["name"], args["script"],
args["payload"], out_path)
# ---------------------------------------------------------------------------
# 主流程:agentic loop
# ---------------------------------------------------------------------------
def run_agent(paper_path: Path, model: str, out_path: Path,
max_turns: int = 8) -> Path | None:
# OPENAI_API_KEY 存在则官方直连;否则回退 OPENROUTER_API_KEY
# gpt-* 模型名会被映射为 openai/…)。两者皆无则给出清晰错误。
from agentbook.providers import resolve_backend
if not os.environ.get("OPENAI_API_KEY") and not os.environ.get("OPENROUTER_API_KEY"):
log("错误:未设置 OPENAI_API_KEY,也未设置 OPENROUTER_API_KEY(通用回退)。")
log("请 export OPENAI_API_KEY=your-openai-api-key 或 export OPENROUTER_API_KEY=your-openrouter-api-key")
log("(无 key 时可用 --offline 走内置大纲、确定性地复现三层渐进式披露并生成 pptx。)")
sys.exit(1)
# 端点与 key 的对应关系由 agentbook 的 provider 注册表统一维护,
# OPENAI_BASE_URL 覆盖也在其中处理。
backend = resolve_backend("openai", model=model)
model = backend.model
# timeout + 自动重试:单次网络/SSL 抖动不至于让整个 agentic loop 崩溃
client = OpenAI(
api_key=backend.api_key, base_url=backend.base_url, timeout=60.0, max_retries=3
)
catalog = scan_skill_catalog()
system_prompt = build_system_prompt(catalog)
log("=" * 72)
log("【第一层·元数据】Agent 启动时只看到这份薄 Skill 目录(system prompt):")
log("-" * 72)
log(system_prompt)
log("-" * 72)
log(f"(薄目录约 {len(system_prompt)} 字符 / 数百 token;各 Skill 的详细流程此刻并不在上下文中)")
log("=" * 72)
paper = paper_path.read_text(encoding="utf-8")
user_task = (
"请把下面这篇论文做成一份 8-12 页的演示文稿(含标题页、目录页、问题背景、"
"方法概述、关键结果、局限性、小结页),总页数务必落在 8-12 页。"
"先判断该用哪个 Skill,再严格按其 SKILL.md 的页序与约束操作。\n\n"
"=== 论文全文 ===\n" + paper
)
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_task},
]
log("\n【任务下发】要求 Agent 从论文生成演示文稿。观察它如何按需渐进式披露:\n")
final_result = None
for turn in range(1, max_turns + 1):
resp = client.chat.completions.create(
model=model,
messages=messages,
tools=TOOLS,
temperature=0.2,
)
msg = resp.choices[0].message
messages.append(msg.model_dump(exclude_none=True))
if not msg.tool_calls:
log(f"\n【Agent 第 {turn} 轮·结束语】\n{msg.content}")
break
for tc in msg.tool_calls:
fn = tc.function.name
try:
args = json.loads(tc.function.arguments or "{}")
except json.JSONDecodeError:
args = {}
log(f"\n[Agent 第 {turn} 轮] 调用工具 -> {fn}({', '.join(f'{k}={_short(v)}' for k, v in args.items())})")
result = dispatch(catalog, fn, args, out_path)
if fn == "run_skill_script" and not result.startswith("[error]"):
final_result = json.loads(result)
log(f" >>> 生成结果:{result}")
messages.append({
"role": "tool",
"tool_call_id": tc.id,
"content": result,
})
if final_result:
return Path(final_result["path"])
return None
# ---------------------------------------------------------------------------
# 离线复现:无 OpenAI key 时,用内置大纲(papers/sample_outline.json)确定性地
# 走完与在线完全相同的三层渐进式披露与工具通道(read_skill / read_skill_file /
# run_skill_script),从而在没有任何 API 访问权限时也能真实生成并校验 pptx。
# 唯一区别是「用哪个 Skill、大纲写什么」由预置脚本给定,而非模型即时决策。
# ---------------------------------------------------------------------------
OUTLINE_PATH = ROOT / "papers" / "sample_outline.json"
def run_offline(out_path: Path) -> Path | None:
catalog = scan_skill_catalog()
system_prompt = build_system_prompt(catalog)
log("=" * 72)
log("【离线模式】不调用 OpenAI,用内置大纲确定性地复现三层渐进式披露。")
log("【第一层·元数据】启动时只看到这份薄 Skill 目录(system prompt):")
log("-" * 72)
log(system_prompt)
log("-" * 72)
log(f"(薄目录约 {len(system_prompt)} 字符;各 Skill 的详细流程此刻并不在上下文中)")
log("=" * 72)
if not OUTLINE_PATH.exists():
log(f"错误:内置大纲不存在:{OUTLINE_PATH}")
return None
# 与在线 agentic loop 相同的工具通道,只是调用序列由脚本给定
log("\n【离线回放】按 SKILL.md 约定,逐层加载并调用捆绑脚本:")
dispatch(catalog, "read_skill", {"name": "pptx"}, out_path)
dispatch(catalog, "read_skill_file",
{"name": "pptx", "path": "reference.md"}, out_path)
payload = OUTLINE_PATH.read_text(encoding="utf-8")
result = dispatch(catalog, "run_skill_script",
{"name": "pptx", "script": "generate_pptx.py", "payload": payload},
out_path)
if result.startswith("[error]"):
log(f" >>> 生成失败:{result}")
return None
log(f" >>> 生成结果:{result}")
return Path(json.loads(result)["path"])
def _short(v, n=48):
s = str(v).replace("\n", " ")
return s if len(s) <= n else s[:n] + ""
# ---------------------------------------------------------------------------
# 校验:用 python-pptx 重新打开生成的文件,读回页数与每页标题,证明是有效 pptx。
# ---------------------------------------------------------------------------
def verify_pptx(path: Path) -> None:
log("\n" + "=" * 72)
log("【校验】用 python-pptx 重新打开生成的文件,读回页数与每页标题:")
log("-" * 72)
prs = Presentation(str(path))
slides = list(prs.slides)
log(f"文件: {path}")
log(f"总页数: {len(slides)}")
for i, slide in enumerate(slides, 1):
first_text = "(空)"
for shp in slide.shapes:
if shp.has_text_frame and shp.text_frame.text.strip():
first_text = shp.text_frame.text.strip().splitlines()[0]
break
log(f"{i:>2} 页标题: {first_text}")
log("-" * 72)
log(f"校验通过:这是一个可被 python-pptx / PowerPoint 打开的有效 .pptx{len(slides)} 页)。")
log("=" * 72)
def parse_args():
p = argparse.ArgumentParser(
description="实验 2-6:用 Agent Skills 的「渐进式披露」从论文生成演示文稿。"
"Agent 启动只看到薄 Skill 目录,按需逐层加载 pptx Skill 的流程与脚本,"
"最后用 python-pptx 生成并校验 output/presentation.pptx。",
formatter_class=argparse.RawDescriptionHelpFormatter,
)
p.add_argument("--paper", default=str(PAPER_PATH),
help="输入论文/大纲(markdown)路径,默认 papers/sample_paper.md。")
p.add_argument("--output", "-o", default=str(OUTPUT_DIR / "presentation.pptx"),
help="输出 .pptx 路径,默认 output/presentation.pptx。")
p.add_argument("--model", default=MODEL,
help="OpenAI 模型名,默认取环境变量 OPENAI_MODEL,否则 gpt-5.6-luna。")
p.add_argument("--max-turns", type=int, default=8,
help="agentic loop 的最大轮数,默认 8。")
p.add_argument("--offline", action="store_true",
help="离线演示:不调用 OpenAI,用内置大纲(papers/sample_outline.json"
"确定性地走完三层渐进式披露并生成 pptx(无需 API key,可复现)。")
return p.parse_args()
def main():
args = parse_args()
paper_path = Path(args.paper)
out_path = Path(args.output)
out_path.parent.mkdir(parents=True, exist_ok=True)
if args.offline:
pptx_path = run_offline(out_path)
else:
if not paper_path.exists():
log(f"错误:论文文件不存在:{paper_path}")
sys.exit(1)
pptx_path = run_agent(paper_path, args.model, out_path, args.max_turns)
if pptx_path and pptx_path.exists():
verify_pptx(pptx_path)
else:
log("\n未生成 pptx。请检查上面的日志。")
sys.exit(2)
if __name__ == "__main__":
main()
+5
View File
@@ -0,0 +1,5 @@
# 复制为 .env 或直接 export 到环境变量
# 主用 OpenAI;未设置 OPENAI_API_KEY 时可回退到 OPENROUTER_API_KEY(通用回退)
OPENAI_API_KEY=your-openai-api-key
# 可选:覆盖默认模型(默认 gpt-5.6-luna
OPENAI_MODEL=gpt-5.6-luna
@@ -0,0 +1,65 @@
{
"experiment_id": "2-6",
"protocol_version": "1.1.0",
"frozen_on": "2026-07-30",
"revised_on": "2026-07-31",
"authority": "book/chapter2.md:767",
"runtime": {
"agent": "Claude Code",
"model_alias": "sonnet",
"acceptance_policy": "Runtime-agnostic (author policy, 2026-07-31): acceptance is NOT gated on Anthropic/Claude Code. Any agent runtime + model that supports SKILL.md-style progressive disclosure may complete this experiment, because not every reader has an Anthropic key. The pinned official Anthropic PPTX Skill content, the real paper, and all artifact gates are unchanged; Claude Code remains the reference path for readers who have Anthropic credentials.",
"alternate_runtimes": {
"kimi": {
"agent": "Kimi Code CLI",
"binary": "kimi",
"binary_fallback": "~/.kimi-code/bin/kimi",
"model_alias": "kimi-code/k3",
"auth_environment_variables": ["KIMI_API_KEY", "MOONSHOT_API_KEY"],
"skills_flag": "--skills-dir",
"stream_format": "stream-json"
}
},
"official_skill_repository": "https://github.com/anthropics/skills.git",
"official_skill_revision": "69c0b1a0674149f27b61b2635f935524b6add202",
"skill_path": "skills/pptx",
"revision_reason": "This is the official revision whose PPTX Skill contains the html2pptx.md workflow named by the manuscript; later revision 4e6907a packages that guide as html2pptx.tgz."
},
"paper": {
"title": "Attention Is All You Need",
"authors": "Ashish Vaswani et al.",
"arxiv_id": "1706.03762",
"pdf_url": "https://arxiv.org/pdf/1706.03762",
"pdf_sha256": "bdfaa68d8984f0dc02beaca527b76f207d99b666d31d1da728ee0728182df697"
},
"output": {
"minimum_slides": 10,
"maximum_slides": 15,
"required_sections": [
"title",
"problem/background",
"method/Transformer architecture",
"key results",
"conclusion"
],
"minimum_paper_visuals": 3,
"visual_policy": "At least three images must be extracted or cropped from the source PDF, embedded in the deck, and listed with PDF page and source label in source_visuals/manifest.json."
},
"progressive_disclosure_gates": [
"the agent runtime discovers only PPTX Skill metadata before selection",
"the agent invokes the official pptx Skill and loads its complete SKILL.md",
"the agent reads the complete official html2pptx.md only after selecting the Skill",
"the agent uses the official scripts/html2pptx.js workflow",
"the agent invokes the official scripts/thumbnail.py preview workflow and visually inspects the result"
],
"artifact_gates": [
"the source PDF hash matches the preregistered academic paper",
"the presentation is a valid ZIP/OOXML package and python-pptx reopens it",
"slide count is between 10 and 15 inclusive",
"the five required content sections are present",
"three or more source visual files are byte-identical to media embedded in the PPTX",
"each source visual has a PDF page, paper label, and caption in the visual manifest",
"a full-deck thumbnail grid exists",
"raw agent-runtime stream events, actual model, usage/cost where the runtime reports it, commands, hashes, and credential scan are retained"
],
"claim_policy": "The bundled Python demo is a noncanonical mechanism illustration and cannot satisfy Experiment 2-6. Only a real agent-runtime run with the pinned official Skill and real paper can complete this protocol. The runtime may be Claude Code or an equivalent skills-capable runtime (for example Kimi Code CLI); the pinned official Anthropic PPTX Skill content and every artifact gate below apply identically to either runtime."
}
@@ -0,0 +1,70 @@
{
"title": "渐进式披露式 Agent Skills 对上下文效率的影响",
"subtitle": "示例作者团队 · 示例数据(对应 papers/sample_paper.md",
"slides": [
{
"title": "目录",
"bullets": [
"研究背景与问题",
"方法概述:三层渐进式披露",
"关键结果:上下文与缓存",
"局限性与讨论",
"小结"
]
},
{
"title": "研究背景与问题",
"bullets": [
"Agent 支持的任务越多,单一系统提示词越线性膨胀",
"长提示词带来 token 成本、注意力稀释、缓存前缀失效三重代价",
"核心矛盾:让 Agent「知道自己有哪些能力」又不长期占用上下文"
]
},
{
"title": "方法概述(总体思路)",
"bullets": [
"先给 Agent 一份薄目录,需要时再加载完整 Skill",
"第一层:启动只注入各 Skill 的 name + description(数百 token",
"第二层:任务触发时加载完整 SKILL.md 作为 tool result"
]
},
{
"title": "方法概述(关键机制)",
"bullets": [
"第三层:按需读取 reference.md、脚本源码等子文档",
"description 应写成「路由条件」而非「功能介绍」",
"捆绑可执行脚本,把知识升级为可落地的能力"
]
},
{
"title": "关键结果(效率指标)",
"bullets": [
"常驻上下文从数千 token 降到目录级的数百 token",
"工具数量恒定、前缀稳定,KV Cache 命中率显著提升"
]
},
{
"title": "关键结果(效果对比)",
"bullets": [
"需要专业 Skill 的任务上,成功率与「全量注入」基线持平",
"加入反例(Don't use when)明显提升路由准确率",
"减少不相关任务上的误触发"
]
},
{
"title": "局限性与讨论",
"bullets": [
"触发依赖模型的「元认知」,判断失误会漏加载 Skill",
"第三方 Skill 是新的提示注入面,加载前需审查其内容"
]
},
{
"title": "小结",
"bullets": [
"渐进式披露把「一次性塞满」变为「按需加载」",
"几乎不损失任务成功率,同时大幅降低常驻上下文",
"是构建可扩展 Agent 能力体系的实用范式"
]
}
]
}
@@ -0,0 +1,45 @@
# 精简论文:渐进式披露式 Agent Skills 对上下文效率的影响
作者:示例作者团队(Anthropic 风格 Agent 研究,示例数据)
## 摘要
现代 LLM Agent 需要覆盖越来越多的专业任务,传统做法是把所有指令塞进单一
系统提示词,导致 token 消耗膨胀、注意力被稀释、KV Cache 前缀频繁失效。
本文提出并评估一种「渐进式披露(Progressive Disclosure)」的 Agent Skills
机制:先向 Agent 注入各 Skill 的薄目录(仅 name + description),当任务确实
需要时再按需加载完整的 SKILL.md 与子文档。实验表明,该机制在保持任务成功率的
同时显著降低常驻上下文长度。
## 1. 研究背景与问题
- 随着 Agent 支持的任务种类增长,单一系统提示词呈线性膨胀。
- 长提示词带来三重代价:token 成本、注意力稀释、缓存前缀失效。
- 核心问题:能否在「Agent 知道自己有哪些能力」与「不为此长期占用上下文」之间取得平衡?
## 2. 方法概述
- **三层渐进式披露**
- 第一层(元数据):启动时只注入每个 Skill 的 name + description(数百 token)。
- 第二层(核心流程):任务触发时加载完整 SKILL.md 作为 tool result。
- 第三层(细则):按需读取 reference.md、脚本源码等子文档。
- **路由决策依赖 description**:描述应写成「路由条件」而非「功能介绍」,
并给出反例(Don't use when),以降低误触发。
- **捆绑可执行脚本**:Skill 不止是文档,还可附带脚本与模板,把知识升级为能力。
## 3. 关键结果
- 常驻上下文从「全量塞入」的数千 token 降到目录级的数百 token。
- 因为工具数量恒定、前缀稳定,KV Cache 命中率显著提升。
- 在需要专业 Skill 的任务上,成功率与「全量注入」基线持平(无明显下降)。
- 反例(Don't use when)能明显提升路由准确率,减少不相关任务上的误触发。
## 4. 局限性与讨论
- 触发依赖模型的「元认知」:模型需判断自己何时需要某个 Skill,判断失误会漏加载。
- 第三方 Skill 是新的提示注入面,加载前需审查其内容。
## 5. 结论
渐进式披露把「一次性塞满」变为「按需加载」,在几乎不损失任务成功率的前提下,
大幅降低常驻上下文并改善缓存友好度,是构建可扩展 Agent 能力体系的实用范式。
@@ -0,0 +1,75 @@
#!/usr/bin/env python3
"""Fetch and verify the pinned official Anthropic PPTX Skill (no reimplementation)."""
from __future__ import annotations
import argparse
import hashlib
import json
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parent
PROTOCOL = json.loads((ROOT / "experiment_protocol.json").read_text(encoding="utf-8"))
def run(command: list[str], cwd: Path | None = None) -> str:
result = subprocess.run(command, cwd=cwd, check=True, text=True, capture_output=True)
return result.stdout.strip()
def sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def prepare(destination: Path) -> dict:
destination = destination.resolve()
repository = PROTOCOL["runtime"]["official_skill_repository"]
revision = PROTOCOL["runtime"]["official_skill_revision"]
if not (destination / ".git").exists():
destination.parent.mkdir(parents=True, exist_ok=True)
run(["git", "clone", "--filter=blob:none", "--no-checkout", repository, str(destination)])
run(["git", "fetch", "origin", revision], cwd=destination)
run(["git", "checkout", "--detach", revision], cwd=destination)
head = run(["git", "rev-parse", "HEAD"], cwd=destination)
if head != revision:
raise RuntimeError(f"official Skill revision mismatch: {head} != {revision}")
required = [
destination / "skills/pptx/SKILL.md",
destination / "skills/pptx/html2pptx.md",
destination / "skills/pptx/scripts/html2pptx.js",
destination / "skills/pptx/scripts/thumbnail.py",
]
missing = [str(path) for path in required if not path.is_file()]
if missing:
raise RuntimeError("pinned official Skill is missing required files: " + ", ".join(missing))
return {
"repository": repository,
"revision": head,
"skill_path": str((destination / "skills/pptx").resolve()),
"required_file_hashes": {
str(path.relative_to(destination)): sha256(path) for path in required
},
}
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--destination",
type=Path,
default=ROOT / "external" / "anthropics-skills",
)
parser.add_argument("--receipt", type=Path)
args = parser.parse_args()
receipt = prepare(args.destination)
if args.receipt:
args.receipt.parent.mkdir(parents=True, exist_ok=True)
args.receipt.write_text(json.dumps(receipt, indent=2), encoding="utf-8")
print(json.dumps(receipt, indent=2))
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,7 @@
# Shared provider resolver from the repository root. Run this requirements file
# from the experiment directory, as shown in the README.
-e ../..
openai>=1.30.0
python-pptx>=0.6.23
python-dotenv>=1.0.0
@@ -0,0 +1,271 @@
#!/usr/bin/env python3
"""Run Experiment 2-6 with a real skills-capable agent runtime and Anthropic's pinned PPTX Skill.
Two runtimes are supported under the author-mandated runtime-agnostic
acceptance policy (see experiment_protocol.json):
- ``--runtime claude`` (default): Claude Code, for readers with Anthropic
credentials.
- ``--runtime kimi``: Kimi Code CLI (or an equivalent runtime), authenticated
with KIMI_API_KEY / MOONSHOT_API_KEY.
"""
from __future__ import annotations
import argparse
import hashlib
import json
import os
import shutil
import subprocess
import sys
from pathlib import Path
import requests
from prepare_official_skill import prepare
ROOT = Path(__file__).resolve().parent
PROTOCOL_PATH = ROOT / "experiment_protocol.json"
CLAUDE_PROMPT = """/pptx
Create a polished 1015 slide presentation from the real academic paper at
`attention-is-all-you-need.pdf`. Write the final deck to
`output/attention-is-all-you-need.pptx`.
This is an audited Agent Skills experiment. Follow the official PPTX Skill by
progressive disclosure: invoke the pptx Skill, read its complete SKILL.md, then
read its complete html2pptx.md only after selection. Use the pinned official
`scripts/html2pptx.js` workflow. Use the official `scripts/thumbnail.py` to
make `output/full-deck-thumbnail.jpg`, inspect the full grid, and fix visible
overlap, cutoff, contrast, or alignment defects before finishing.
Content gates:
- cover title, problem/background, Transformer method/architecture, key
experimental results, and conclusion;
- extract or crop at least three visuals directly from the source PDF (not
invented replacements), place the files under `source_visuals/`, and embed
all of them in the deck;
- create `source_visuals/manifest.json` as a JSON list. Each item must contain
`file`, one-based PDF `page`, the paper's `label` (for example Figure 1 or
Table 2), and a faithful `caption`;
- make every visual consistent with the surrounding slide explanation and
cite its source page/label on-slide.
You may install the Node packages required by the official Skill inside this
workspace. Do not use the repository's bundled `demo.py`, local proxy Skill,
or prewritten sample outline. The final response must name the deck,
thumbnail, visual manifest, slide count, validation performed, and any
remaining limitation.
"""
KIMI_PROMPT = """Create a polished 1015 slide presentation from the real academic paper at
`attention-is-all-you-need.pdf` using your installed `pptx` Skill. Write the
final deck to `output/attention-is-all-you-need.pptx`.
This is an audited Agent Skills experiment. Follow the official PPTX Skill by
progressive disclosure: invoke the pptx Skill through the Skill tool (this
loads its complete SKILL.md), then read its complete html2pptx.md only after
selection. Use the pinned official `scripts/html2pptx.js` workflow. Use the
official `scripts/thumbnail.py` to make `output/full-deck-thumbnail.jpg`,
inspect the full grid, and fix visible overlap, cutoff, contrast, or alignment
defects before finishing.
Content gates:
- cover title, problem/background, Transformer method/architecture, key
experimental results, and conclusion;
- extract or crop at least three visuals directly from the source PDF (not
invented replacements), place the files under `source_visuals/`, and embed
all of them in the deck;
- create `source_visuals/manifest.json` as a JSON list. Each item must contain
`file`, one-based PDF `page`, the paper's `label` (for example Figure 1 or
Table 2), and a faithful `caption`;
- make every visual consistent with the surrounding slide explanation and
cite its source page/label on-slide.
You may install the Node packages required by the official Skill inside this
workspace. Do not use the repository's bundled `demo.py`, local proxy Skill,
or prewritten sample outline. The final response must name the deck,
thumbnail, visual manifest, slide count, validation performed, and any
remaining limitation.
"""
def resolve_kimi_binary() -> str:
binary = shutil.which("kimi")
if binary:
return binary
fallback = Path.home() / ".kimi-code" / "bin" / "kimi"
if fallback.is_file():
return str(fallback)
raise RuntimeError("Kimi Code CLI not found on PATH or at ~/.kimi-code/bin/kimi")
def stream_process(command: list[str], workspace: Path, env: dict, stream_path: Path,
stderr_path: Path, tag: str) -> int:
with stream_path.open("w", encoding="utf-8") as stdout_file, stderr_path.open(
"w", encoding="utf-8"
) as stderr_file:
process = subprocess.Popen(
command,
cwd=workspace,
env=env,
stdout=subprocess.PIPE,
stderr=stderr_file,
text=True,
bufsize=1,
)
assert process.stdout is not None
for line in process.stdout:
stdout_file.write(line)
stdout_file.flush()
try:
event = json.loads(line)
event_type = event.get("type") or event.get("role")
if event_type in {"assistant", "result", "system", "tool", "meta"}:
print(f"[{tag}] {event_type}", flush=True)
except json.JSONDecodeError:
pass
return process.wait()
def run_claude(args, run_dir: Path, workspace: Path, official_skill: Path, protocol: dict) -> None:
(workspace / ".claude" / "skills").mkdir(parents=True)
(workspace / ".claude" / "skills" / "pptx").symlink_to(
official_skill, target_is_directory=True
)
prompt = CLAUDE_PROMPT
(run_dir / "prompt.txt").write_text(prompt, encoding="utf-8")
command = [
# Put the positional prompt before --add-dir. The current Claude Code
# CLI declares --add-dir as variadic, so a trailing prompt is otherwise
# consumed as another directory and --print reports that no input was
# provided.
"claude", prompt, "--print", "--output-format", "stream-json", "--verbose",
"--model", protocol["runtime"]["model_alias"], "--effort", "high",
"--max-budget-usd", "8", "--no-session-persistence",
"--dangerously-skip-permissions", "--add-dir", str(official_skill),
]
(run_dir / "command.json").write_text(json.dumps(command, indent=2), encoding="utf-8")
env = os.environ.copy()
env["CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC"] = "1"
if args.auth_source == "claude-login":
# An invalid environment key takes precedence over an otherwise valid
# Claude Code OAuth login. Make this explicit and record it without
# ever serializing credential values.
env.pop("ANTHROPIC_API_KEY", None)
(run_dir / "auth_source.json").write_text(
json.dumps({"auth_source": args.auth_source}, indent=2), encoding="utf-8"
)
return_code = stream_process(
command, workspace, env, run_dir / "claude_stream.jsonl",
run_dir / "claude_stderr.log", "claude",
)
(run_dir / "claude_exit.json").write_text(
json.dumps({"return_code": return_code}, indent=2), encoding="utf-8"
)
def run_kimi(args, run_dir: Path, workspace: Path, official_skill: Path, protocol: dict) -> None:
kimi = protocol["runtime"]["alternate_runtimes"]["kimi"]
binary = resolve_kimi_binary()
# --skills-dir replaces the auto-discovered user/project skill directories
# for this launch, so the runtime genuinely starts with only the pinned
# official Skill's metadata in its catalog.
skills_dir = workspace / "kimi-skills"
skills_dir.mkdir(parents=True)
(skills_dir / "pptx").symlink_to(official_skill, target_is_directory=True)
prompt = KIMI_PROMPT
(run_dir / "prompt.txt").write_text(prompt, encoding="utf-8")
command = [
binary, "--prompt", prompt, "--output-format", "stream-json",
"--model", kimi["model_alias"],
"--skills-dir", str(skills_dir), "--add-dir", str(official_skill),
]
(run_dir / "command.json").write_text(json.dumps(command, indent=2), encoding="utf-8")
env = os.environ.copy()
(run_dir / "runtime.json").write_text(
json.dumps(
{
"runtime": "kimi",
"binary": binary,
"model_alias": kimi["model_alias"],
"skills_dir": str(skills_dir),
"auth_environment_variables_present": [
name for name in kimi["auth_environment_variables"] if os.getenv(name)
],
},
indent=2,
),
encoding="utf-8",
)
return_code = stream_process(
command, workspace, env, run_dir / "kimi_stream.jsonl",
run_dir / "kimi_stderr.log", "kimi",
)
(run_dir / "kimi_exit.json").write_text(
json.dumps({"return_code": return_code}, indent=2), encoding="utf-8"
)
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--output", required=True, type=Path)
parser.add_argument(
"--runtime",
choices=("claude", "kimi"),
default="claude",
help="Agent runtime that executes the pinned official Skill.",
)
parser.add_argument(
"--official-repo",
type=Path,
default=ROOT / "external" / "anthropics-skills",
)
parser.add_argument("--resume-validation", action="store_true")
parser.add_argument(
"--auth-source",
choices=("environment", "claude-login"),
default="environment",
help="Claude runtime only: use ANTHROPIC_API_KEY or explicitly use Claude Code's authenticated login.",
)
args = parser.parse_args()
run_dir = args.output.resolve()
if args.resume_validation:
return subprocess.run(
[sys.executable, str(ROOT / "validate_official_run.py"), str(run_dir)]
).returncode
run_dir.mkdir(parents=True, exist_ok=False)
protocol_bytes = PROTOCOL_PATH.read_bytes()
protocol = json.loads(protocol_bytes)
(run_dir / "experiment_protocol.json").write_bytes(protocol_bytes)
skill_receipt = prepare(args.official_repo)
(run_dir / "official_skill_receipt.json").write_text(
json.dumps(skill_receipt, indent=2), encoding="utf-8"
)
workspace = run_dir / "workspace"
(workspace / "output").mkdir(parents=True)
official_skill = Path(skill_receipt["skill_path"])
response = requests.get(protocol["paper"]["pdf_url"], timeout=180)
response.raise_for_status()
digest = hashlib.sha256(response.content).hexdigest()
if digest != protocol["paper"]["pdf_sha256"]:
raise RuntimeError(f"paper hash mismatch: {digest}")
paper_path = workspace / "attention-is-all-you-need.pdf"
paper_path.write_bytes(response.content)
if args.runtime == "kimi":
run_kimi(args, run_dir, workspace, official_skill, protocol)
else:
run_claude(args, run_dir, workspace, official_skill, protocol)
validator = subprocess.run(
[sys.executable, str(ROOT / "validate_official_run.py"), str(run_dir)]
)
return validator.returncode
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,3 @@
{
"return_code": 1
}
@@ -0,0 +1,18 @@
[
"claude",
"--print",
"--output-format",
"stream-json",
"--verbose",
"--model",
"sonnet",
"--effort",
"high",
"--max-budget-usd",
"8",
"--no-session-persistence",
"--dangerously-skip-permissions",
"--add-dir",
"/Users/boj/book/ai-agent-book/chapter2/agent-skills-ppt/external/anthropics-skills/skills/pptx",
"/pptx\n\nCreate a polished 10\u201315 slide presentation from the real academic paper at\n`attention-is-all-you-need.pdf`. Write the final deck to\n`output/attention-is-all-you-need.pptx`.\n\nThis is an audited Agent Skills experiment. Follow the official PPTX Skill by\nprogressive disclosure: invoke the pptx Skill, read its complete SKILL.md, then\nread its complete html2pptx.md only after selection. Use the pinned official\n`scripts/html2pptx.js` workflow. Use the official `scripts/thumbnail.py` to\nmake `output/full-deck-thumbnail.jpg`, inspect the full grid, and fix visible\noverlap, cutoff, contrast, or alignment defects before finishing.\n\nContent gates:\n- cover title, problem/background, Transformer method/architecture, key\n experimental results, and conclusion;\n- extract or crop at least three visuals directly from the source PDF (not\n invented replacements), place the files under `source_visuals/`, and embed\n all of them in the deck;\n- create `source_visuals/manifest.json` as a JSON list. Each item must contain\n `file`, one-based PDF `page`, the paper's `label` (for example Figure 1 or\n Table 2), and a faithful `caption`;\n- make every visual consistent with the surrounding slide explanation and\n cite its source page/label on-slide.\n\nYou may install the Node packages required by the official Skill inside this\nworkspace. Do not use the repository's bundled `demo.py`, local proxy Skill,\nor prewritten sample outline. The final response must name the deck,\nthumbnail, visual manifest, slide count, validation performed, and any\nremaining limitation.\n"
]
@@ -0,0 +1,59 @@
{
"experiment_id": "2-6",
"protocol_sha256": "2990c8ef624edc12a06fb1b622de33b0fe8842cc5b42faf85788850111265eba",
"official_skill_receipt": {
"repository": "https://github.com/anthropics/skills.git",
"revision": "69c0b1a0674149f27b61b2635f935524b6add202",
"skill_path": "/Users/boj/book/ai-agent-book/chapter2/agent-skills-ppt/external/anthropics-skills/skills/pptx",
"required_file_hashes": {
"skills/pptx/SKILL.md": "b6f25545bfb358739f1532f793458b5dbc87ee009933cb7c306b2d951ab6617f",
"skills/pptx/html2pptx.md": "f08ed7580969b796d9cd5ade93e2cdee981dcaf13cc5eb12e8d4a3700c2d6047",
"skills/pptx/scripts/html2pptx.js": "c675d09a54d6a002e8ca5917b9d24a6568aa8d455bb7abeb212d4f564dd07a34",
"skills/pptx/scripts/thumbnail.py": "c21fd950b6ada7bd2f029885d3e56bc66b7ff061cc8404c492eb301664aa9e5d"
}
},
"claude_result": {},
"slide_count": 0,
"section_checks": {
"title": false,
"background": false,
"method": false,
"results": false,
"conclusion": false
},
"source_visuals": [],
"embedded_media": {},
"thumbnail_files": [],
"gates": {
"source_pdf_hash_matches": true,
"claude_run_succeeded": false,
"pptx_skill_invoked": false,
"skill_md_loaded": false,
"html2pptx_guide_loaded": false,
"official_html2pptx_used": false,
"official_thumbnail_used": false,
"thumbnail_visually_inspected": false,
"pptx_zip_valid": false,
"pptx_reopens": false,
"slide_count_in_range": false,
"required_sections_present": false,
"three_source_visuals_embedded_and_documented": false,
"thumbnail_grid_present": false,
"credential_scan_passed": true
},
"official_complete": false,
"artifacts": {
"experiment_protocol.json": {
"sha256": "2990c8ef624edc12a06fb1b622de33b0fe8842cc5b42faf85788850111265eba",
"bytes": 2582
},
"claude_stream.jsonl": {
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"bytes": 0
},
"workspace/attention-is-all-you-need.pdf": {
"sha256": "bdfaa68d8984f0dc02beaca527b76f207d99b666d31d1da728ee0728182df697",
"bytes": 2215244
}
}
}
@@ -0,0 +1,52 @@
{
"experiment_id": "2-6",
"protocol_version": "1.0.0",
"frozen_on": "2026-07-30",
"authority": "book/chapter2.md:767",
"runtime": {
"agent": "Claude Code",
"model_alias": "sonnet",
"official_skill_repository": "https://github.com/anthropics/skills.git",
"official_skill_revision": "69c0b1a0674149f27b61b2635f935524b6add202",
"skill_path": "skills/pptx",
"revision_reason": "This is the official revision whose PPTX Skill contains the html2pptx.md workflow named by the manuscript; later revision 4e6907a packages that guide as html2pptx.tgz."
},
"paper": {
"title": "Attention Is All You Need",
"authors": "Ashish Vaswani et al.",
"arxiv_id": "1706.03762",
"pdf_url": "https://arxiv.org/pdf/1706.03762",
"pdf_sha256": "bdfaa68d8984f0dc02beaca527b76f207d99b666d31d1da728ee0728182df697"
},
"output": {
"minimum_slides": 10,
"maximum_slides": 15,
"required_sections": [
"title",
"problem/background",
"method/Transformer architecture",
"key results",
"conclusion"
],
"minimum_paper_visuals": 3,
"visual_policy": "At least three images must be extracted or cropped from the source PDF, embedded in the deck, and listed with PDF page and source label in source_visuals/manifest.json."
},
"progressive_disclosure_gates": [
"Claude Code discovers only PPTX Skill metadata before selection",
"Claude invokes the official pptx Skill and loads its complete SKILL.md",
"Claude reads the complete official html2pptx.md only after selecting the Skill",
"Claude uses the official scripts/html2pptx.js workflow",
"Claude invokes the official scripts/thumbnail.py preview workflow and visually inspects the result"
],
"artifact_gates": [
"the source PDF hash matches the preregistered academic paper",
"the presentation is a valid ZIP/OOXML package and python-pptx reopens it",
"slide count is between 10 and 15 inclusive",
"the five required content sections are present",
"three or more source visual files are byte-identical to media embedded in the PPTX",
"each source visual has a PDF page, paper label, and caption in the visual manifest",
"a full-deck thumbnail grid exists",
"raw Claude Code stream events, actual model, usage/cost, commands, hashes, and credential scan are retained"
],
"claim_policy": "The bundled Python demo is a noncanonical mechanism illustration and cannot satisfy Experiment 2-6. Only a real Claude Code run with the pinned official Skill and real paper can complete this protocol."
}
@@ -0,0 +1,7 @@
{
"experiment_id": "2-6",
"official_complete": false,
"protocol_sha256": "2990c8ef624edc12a06fb1b622de33b0fe8842cc5b42faf85788850111265eba",
"comparison_sha256": "688774e2a47eabef59f7dba27f999aeb0a3936472db1bb4584078f317c518f58",
"pptx_sha256": null
}
@@ -0,0 +1,11 @@
{
"repository": "https://github.com/anthropics/skills.git",
"revision": "69c0b1a0674149f27b61b2635f935524b6add202",
"skill_path": "/Users/boj/book/ai-agent-book/chapter2/agent-skills-ppt/external/anthropics-skills/skills/pptx",
"required_file_hashes": {
"skills/pptx/SKILL.md": "b6f25545bfb358739f1532f793458b5dbc87ee009933cb7c306b2d951ab6617f",
"skills/pptx/html2pptx.md": "f08ed7580969b796d9cd5ade93e2cdee981dcaf13cc5eb12e8d4a3700c2d6047",
"skills/pptx/scripts/html2pptx.js": "c675d09a54d6a002e8ca5917b9d24a6568aa8d455bb7abeb212d4f564dd07a34",
"skills/pptx/scripts/thumbnail.py": "c21fd950b6ada7bd2f029885d3e56bc66b7ff061cc8404c492eb301664aa9e5d"
}
}
@@ -0,0 +1,30 @@
/pptx
Create a polished 1015 slide presentation from the real academic paper at
`attention-is-all-you-need.pdf`. Write the final deck to
`output/attention-is-all-you-need.pptx`.
This is an audited Agent Skills experiment. Follow the official PPTX Skill by
progressive disclosure: invoke the pptx Skill, read its complete SKILL.md, then
read its complete html2pptx.md only after selection. Use the pinned official
`scripts/html2pptx.js` workflow. Use the official `scripts/thumbnail.py` to
make `output/full-deck-thumbnail.jpg`, inspect the full grid, and fix visible
overlap, cutoff, contrast, or alignment defects before finishing.
Content gates:
- cover title, problem/background, Transformer method/architecture, key
experimental results, and conclusion;
- extract or crop at least three visuals directly from the source PDF (not
invented replacements), place the files under `source_visuals/`, and embed
all of them in the deck;
- create `source_visuals/manifest.json` as a JSON list. Each item must contain
`file`, one-based PDF `page`, the paper's `label` (for example Figure 1 or
Table 2), and a faithful `caption`;
- make every visual consistent with the surrounding slide explanation and
cite its source page/label on-slide.
You may install the Node packages required by the official Skill inside this
workspace. Do not use the repository's bundled `demo.py`, local proxy Skill,
or prewritten sample outline. The final response must name the deck,
thumbnail, visual manifest, slide count, validation performed, and any
remaining limitation.
@@ -0,0 +1,3 @@
{
"return_code": 1
}
@@ -0,0 +1,13 @@
{"type":"system","subtype":"init","cwd":"/Users/boj/book/ai-agent-book/chapter2/agent-skills-ppt/runs/exp2-6-claude-pptx-20260730-v2/workspace","session_id":"b7b08ae5-01d0-46f5-b0e0-2e319fa9ab18","tools":["Task","Bash","CronCreate","CronDelete","CronList","Edit","EnterWorktree","ExitWorktree","NotebookEdit","Read","ReportFindings","ScheduleWakeup","SendMessage","Skill","TaskCreate","TaskGet","TaskList","TaskOutput","TaskStop","TaskUpdate","ToolSearch","WebFetch","WebSearch","Workflow","Write"],"mcp_servers":[],"model":"claude-sonnet-5","permissionMode":"bypassPermissions","slash_commands":["pptx","deep-research","dataviz","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","claude-api","run","run-skill-generator","agents","clear","color","compact","config","context","effort","fast","heapdump","init","mcp","model","__remote-workflow","workflow-launch-exec","reload-skills","rename","review","security-review","usage","insights","recap","goal","team-onboarding"],"apiKeySource":"ANTHROPIC_API_KEY","claude_code_version":"2.1.218","output_style":"default","agents":["claude","Explore","general-purpose","Plan","statusline-setup"],"skills":["pptx","deep-research","dataviz","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","claude-api","run","run-skill-generator"],"plugins":[],"capabilities":["interrupt_receipt_v1","msg_lifecycle_v1"],"analytics_disabled":true,"product_feedback_disabled":false,"uuid":"ae3f0aa9-a87b-4693-a2d7-d7408763316b","memory_paths":{"auto":"/Users/boj/.claude/projects/-Users-boj-book-ai-agent-book/memory/"},"fast_mode_state":"off"}
{"type":"system","subtype":"api_retry","attempt":1,"max_retries":10,"retry_delay_ms":547,"error_status":401,"error":"authentication_failed","session_id":"b7b08ae5-01d0-46f5-b0e0-2e319fa9ab18","uuid":"a9c5efdb-903a-4d39-9181-a5a27e9620dd"}
{"type":"system","subtype":"api_retry","attempt":2,"max_retries":10,"retry_delay_ms":1110,"error_status":401,"error":"authentication_failed","session_id":"b7b08ae5-01d0-46f5-b0e0-2e319fa9ab18","uuid":"6be5a82f-4a4f-45e6-936c-beb3edcff939"}
{"type":"system","subtype":"api_retry","attempt":3,"max_retries":10,"retry_delay_ms":2014,"error_status":401,"error":"authentication_failed","session_id":"b7b08ae5-01d0-46f5-b0e0-2e319fa9ab18","uuid":"40c5357e-5dae-4f62-b58e-1c6525ef2fa3"}
{"type":"system","subtype":"api_retry","attempt":4,"max_retries":10,"retry_delay_ms":4650,"error_status":401,"error":"authentication_failed","session_id":"b7b08ae5-01d0-46f5-b0e0-2e319fa9ab18","uuid":"830d6982-79aa-4daa-a2fd-a6d2944c2559"}
{"type":"system","subtype":"api_retry","attempt":5,"max_retries":10,"retry_delay_ms":9091,"error_status":401,"error":"authentication_failed","session_id":"b7b08ae5-01d0-46f5-b0e0-2e319fa9ab18","uuid":"22c49e8a-c0cf-4bc8-b04d-df019f57205f"}
{"type":"system","subtype":"api_retry","attempt":6,"max_retries":10,"retry_delay_ms":19672,"error_status":401,"error":"authentication_failed","session_id":"b7b08ae5-01d0-46f5-b0e0-2e319fa9ab18","uuid":"517f55b5-92ec-460e-93f5-a20eab1a78f8"}
{"type":"system","subtype":"api_retry","attempt":7,"max_retries":10,"retry_delay_ms":34637,"error_status":401,"error":"authentication_failed","session_id":"b7b08ae5-01d0-46f5-b0e0-2e319fa9ab18","uuid":"11241223-2cc9-4769-9800-3551fcea8305"}
{"type":"system","subtype":"api_retry","attempt":8,"max_retries":10,"retry_delay_ms":32868,"error_status":401,"error":"authentication_failed","session_id":"b7b08ae5-01d0-46f5-b0e0-2e319fa9ab18","uuid":"42e5b7fc-5c60-4a2e-905b-1d15d5d8179b"}
{"type":"system","subtype":"api_retry","attempt":9,"max_retries":10,"retry_delay_ms":36134,"error_status":401,"error":"authentication_failed","session_id":"b7b08ae5-01d0-46f5-b0e0-2e319fa9ab18","uuid":"9a5e6a0c-eb29-4050-b3db-75fd56179da6"}
{"type":"system","subtype":"api_retry","attempt":10,"max_retries":10,"retry_delay_ms":34271,"error_status":401,"error":"authentication_failed","session_id":"b7b08ae5-01d0-46f5-b0e0-2e319fa9ab18","uuid":"34390b97-f09f-4081-9b46-fe98604b04fc"}
{"type":"assistant","message":{"id":"ebd5c598-7796-4581-9513-0d0dccb49362","container":null,"model":"<synthetic>","role":"assistant","stop_details":null,"stop_reason":"stop_sequence","stop_sequence":"","type":"message","usage":{"input_tokens":0,"output_tokens":0,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":null,"cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":0},"inference_geo":null,"iterations":null,"speed":null},"content":[{"type":"text","text":"Failed to authenticate. API Error: 401 API key is invalid."}],"context_management":null},"parent_tool_use_id":null,"session_id":"b7b08ae5-01d0-46f5-b0e0-2e319fa9ab18","uuid":"971c3df7-a74b-489d-b32b-d20624b78096","timestamp":"2026-07-29T19:41:56.444Z","error":"authentication_failed"}
{"is_error":true,"duration_api_ms":0,"num_turns":1,"stop_reason":"stop_sequence","session_id":"b7b08ae5-01d0-46f5-b0e0-2e319fa9ab18","total_cost_usd":0,"usage":{"input_tokens":0,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":0,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":0},"inference_geo":"","iterations":[],"speed":"standard"},"modelUsage":{},"permission_denials":[],"terminal_reason":"api_error","fast_mode_state":"off","subtype":"success","api_error_status":401,"result":"Failed to authenticate. API Error: 401 API key is invalid.","type":"result","duration_ms":182912,"uuid":"8a488e57-1f7a-4998-aa57-5800b261e318"}
@@ -0,0 +1,18 @@
[
"claude",
"/pptx\n\nCreate a polished 10\u201315 slide presentation from the real academic paper at\n`attention-is-all-you-need.pdf`. Write the final deck to\n`output/attention-is-all-you-need.pptx`.\n\nThis is an audited Agent Skills experiment. Follow the official PPTX Skill by\nprogressive disclosure: invoke the pptx Skill, read its complete SKILL.md, then\nread its complete html2pptx.md only after selection. Use the pinned official\n`scripts/html2pptx.js` workflow. Use the official `scripts/thumbnail.py` to\nmake `output/full-deck-thumbnail.jpg`, inspect the full grid, and fix visible\noverlap, cutoff, contrast, or alignment defects before finishing.\n\nContent gates:\n- cover title, problem/background, Transformer method/architecture, key\n experimental results, and conclusion;\n- extract or crop at least three visuals directly from the source PDF (not\n invented replacements), place the files under `source_visuals/`, and embed\n all of them in the deck;\n- create `source_visuals/manifest.json` as a JSON list. Each item must contain\n `file`, one-based PDF `page`, the paper's `label` (for example Figure 1 or\n Table 2), and a faithful `caption`;\n- make every visual consistent with the surrounding slide explanation and\n cite its source page/label on-slide.\n\nYou may install the Node packages required by the official Skill inside this\nworkspace. Do not use the repository's bundled `demo.py`, local proxy Skill,\nor prewritten sample outline. The final response must name the deck,\nthumbnail, visual manifest, slide count, validation performed, and any\nremaining limitation.\n",
"--print",
"--output-format",
"stream-json",
"--verbose",
"--model",
"sonnet",
"--effort",
"high",
"--max-budget-usd",
"8",
"--no-session-persistence",
"--dangerously-skip-permissions",
"--add-dir",
"/Users/boj/book/ai-agent-book/chapter2/agent-skills-ppt/external/anthropics-skills/skills/pptx"
]
@@ -0,0 +1,87 @@
{
"experiment_id": "2-6",
"protocol_sha256": "2990c8ef624edc12a06fb1b622de33b0fe8842cc5b42faf85788850111265eba",
"official_skill_receipt": {
"repository": "https://github.com/anthropics/skills.git",
"revision": "69c0b1a0674149f27b61b2635f935524b6add202",
"skill_path": "/Users/boj/book/ai-agent-book/chapter2/agent-skills-ppt/external/anthropics-skills/skills/pptx",
"required_file_hashes": {
"skills/pptx/SKILL.md": "b6f25545bfb358739f1532f793458b5dbc87ee009933cb7c306b2d951ab6617f",
"skills/pptx/html2pptx.md": "f08ed7580969b796d9cd5ade93e2cdee981dcaf13cc5eb12e8d4a3700c2d6047",
"skills/pptx/scripts/html2pptx.js": "c675d09a54d6a002e8ca5917b9d24a6568aa8d455bb7abeb212d4f564dd07a34",
"skills/pptx/scripts/thumbnail.py": "c21fd950b6ada7bd2f029885d3e56bc66b7ff061cc8404c492eb301664aa9e5d"
}
},
"claude_result": {
"subtype": "success",
"is_error": true,
"duration_ms": 182912,
"duration_api_ms": 0,
"num_turns": 1,
"result": "Failed to authenticate. API Error: 401 API key is invalid.",
"total_cost_usd": 0,
"usage": {
"input_tokens": 0,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"output_tokens": 0,
"server_tool_use": {
"web_search_requests": 0,
"web_fetch_requests": 0
},
"service_tier": "standard",
"cache_creation": {
"ephemeral_1h_input_tokens": 0,
"ephemeral_5m_input_tokens": 0
},
"inference_geo": "",
"iterations": [],
"speed": "standard"
},
"modelUsage": {},
"session_id": "b7b08ae5-01d0-46f5-b0e0-2e319fa9ab18"
},
"slide_count": 0,
"section_checks": {
"title": false,
"background": false,
"method": false,
"results": false,
"conclusion": false
},
"source_visuals": [],
"embedded_media": {},
"thumbnail_files": [],
"gates": {
"source_pdf_hash_matches": true,
"claude_run_succeeded": false,
"pptx_skill_invoked": false,
"skill_md_loaded": false,
"html2pptx_guide_loaded": false,
"official_html2pptx_used": false,
"official_thumbnail_used": false,
"thumbnail_visually_inspected": false,
"pptx_zip_valid": false,
"pptx_reopens": false,
"slide_count_in_range": false,
"required_sections_present": false,
"three_source_visuals_embedded_and_documented": false,
"thumbnail_grid_present": false,
"credential_scan_passed": true
},
"official_complete": false,
"artifacts": {
"experiment_protocol.json": {
"sha256": "2990c8ef624edc12a06fb1b622de33b0fe8842cc5b42faf85788850111265eba",
"bytes": 2582
},
"claude_stream.jsonl": {
"sha256": "40004d1a5ed3c5f9dd4f8526b438bef025149feada0edd226f8e956d83874cdc",
"bytes": 5725
},
"workspace/attention-is-all-you-need.pdf": {
"sha256": "bdfaa68d8984f0dc02beaca527b76f207d99b666d31d1da728ee0728182df697",
"bytes": 2215244
}
}
}
@@ -0,0 +1,52 @@
{
"experiment_id": "2-6",
"protocol_version": "1.0.0",
"frozen_on": "2026-07-30",
"authority": "book/chapter2.md:767",
"runtime": {
"agent": "Claude Code",
"model_alias": "sonnet",
"official_skill_repository": "https://github.com/anthropics/skills.git",
"official_skill_revision": "69c0b1a0674149f27b61b2635f935524b6add202",
"skill_path": "skills/pptx",
"revision_reason": "This is the official revision whose PPTX Skill contains the html2pptx.md workflow named by the manuscript; later revision 4e6907a packages that guide as html2pptx.tgz."
},
"paper": {
"title": "Attention Is All You Need",
"authors": "Ashish Vaswani et al.",
"arxiv_id": "1706.03762",
"pdf_url": "https://arxiv.org/pdf/1706.03762",
"pdf_sha256": "bdfaa68d8984f0dc02beaca527b76f207d99b666d31d1da728ee0728182df697"
},
"output": {
"minimum_slides": 10,
"maximum_slides": 15,
"required_sections": [
"title",
"problem/background",
"method/Transformer architecture",
"key results",
"conclusion"
],
"minimum_paper_visuals": 3,
"visual_policy": "At least three images must be extracted or cropped from the source PDF, embedded in the deck, and listed with PDF page and source label in source_visuals/manifest.json."
},
"progressive_disclosure_gates": [
"Claude Code discovers only PPTX Skill metadata before selection",
"Claude invokes the official pptx Skill and loads its complete SKILL.md",
"Claude reads the complete official html2pptx.md only after selecting the Skill",
"Claude uses the official scripts/html2pptx.js workflow",
"Claude invokes the official scripts/thumbnail.py preview workflow and visually inspects the result"
],
"artifact_gates": [
"the source PDF hash matches the preregistered academic paper",
"the presentation is a valid ZIP/OOXML package and python-pptx reopens it",
"slide count is between 10 and 15 inclusive",
"the five required content sections are present",
"three or more source visual files are byte-identical to media embedded in the PPTX",
"each source visual has a PDF page, paper label, and caption in the visual manifest",
"a full-deck thumbnail grid exists",
"raw Claude Code stream events, actual model, usage/cost, commands, hashes, and credential scan are retained"
],
"claim_policy": "The bundled Python demo is a noncanonical mechanism illustration and cannot satisfy Experiment 2-6. Only a real Claude Code run with the pinned official Skill and real paper can complete this protocol."
}
@@ -0,0 +1,7 @@
{
"experiment_id": "2-6",
"official_complete": false,
"protocol_sha256": "2990c8ef624edc12a06fb1b622de33b0fe8842cc5b42faf85788850111265eba",
"comparison_sha256": "c3656f5fb04adf733f90731b2be71791a69a54d2ad91b633b58adc1262a3c9ec",
"pptx_sha256": null
}
@@ -0,0 +1,11 @@
{
"repository": "https://github.com/anthropics/skills.git",
"revision": "69c0b1a0674149f27b61b2635f935524b6add202",
"skill_path": "/Users/boj/book/ai-agent-book/chapter2/agent-skills-ppt/external/anthropics-skills/skills/pptx",
"required_file_hashes": {
"skills/pptx/SKILL.md": "b6f25545bfb358739f1532f793458b5dbc87ee009933cb7c306b2d951ab6617f",
"skills/pptx/html2pptx.md": "f08ed7580969b796d9cd5ade93e2cdee981dcaf13cc5eb12e8d4a3700c2d6047",
"skills/pptx/scripts/html2pptx.js": "c675d09a54d6a002e8ca5917b9d24a6568aa8d455bb7abeb212d4f564dd07a34",
"skills/pptx/scripts/thumbnail.py": "c21fd950b6ada7bd2f029885d3e56bc66b7ff061cc8404c492eb301664aa9e5d"
}
}
@@ -0,0 +1,30 @@
/pptx
Create a polished 1015 slide presentation from the real academic paper at
`attention-is-all-you-need.pdf`. Write the final deck to
`output/attention-is-all-you-need.pptx`.
This is an audited Agent Skills experiment. Follow the official PPTX Skill by
progressive disclosure: invoke the pptx Skill, read its complete SKILL.md, then
read its complete html2pptx.md only after selection. Use the pinned official
`scripts/html2pptx.js` workflow. Use the official `scripts/thumbnail.py` to
make `output/full-deck-thumbnail.jpg`, inspect the full grid, and fix visible
overlap, cutoff, contrast, or alignment defects before finishing.
Content gates:
- cover title, problem/background, Transformer method/architecture, key
experimental results, and conclusion;
- extract or crop at least three visuals directly from the source PDF (not
invented replacements), place the files under `source_visuals/`, and embed
all of them in the deck;
- create `source_visuals/manifest.json` as a JSON list. Each item must contain
`file`, one-based PDF `page`, the paper's `label` (for example Figure 1 or
Table 2), and a faithful `caption`;
- make every visual consistent with the surrounding slide explanation and
cite its source page/label on-slide.
You may install the Node packages required by the official Skill inside this
workspace. Do not use the repository's bundled `demo.py`, local proxy Skill,
or prewritten sample outline. The final response must name the deck,
thumbnail, visual manifest, slide count, validation performed, and any
remaining limitation.
@@ -0,0 +1,3 @@
{
"auth_source": "claude-login"
}
@@ -0,0 +1,3 @@
{
"return_code": 1
}
@@ -0,0 +1,3 @@
{"type":"system","subtype":"init","cwd":"/Users/boj/book/ai-agent-book/chapter2/agent-skills-ppt/runs/exp2-6-claude-pptx-20260730-v3/workspace","session_id":"2585a5cf-5b06-4497-9d62-bceddbf00eac","tools":["Task","Bash","CronCreate","CronDelete","CronList","Edit","EnterWorktree","ExitWorktree","NotebookEdit","Read","ReportFindings","ScheduleWakeup","SendMessage","Skill","TaskCreate","TaskGet","TaskList","TaskOutput","TaskStop","TaskUpdate","ToolSearch","WebFetch","WebSearch","Workflow","Write"],"mcp_servers":[],"model":"claude-sonnet-5","permissionMode":"bypassPermissions","slash_commands":["pptx","deep-research","dataviz","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","claude-api","run","run-skill-generator","agents","clear","color","compact","config","context","effort","fast","heapdump","init","mcp","model","__remote-workflow","workflow-launch-exec","reload-skills","rename","review","security-review","usage","insights","recap","goal","team-onboarding"],"apiKeySource":"none","claude_code_version":"2.1.218","output_style":"default","agents":["claude","Explore","general-purpose","Plan","statusline-setup"],"skills":["pptx","deep-research","dataviz","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","claude-api","run","run-skill-generator"],"plugins":[],"capabilities":["interrupt_receipt_v1","msg_lifecycle_v1"],"analytics_disabled":true,"product_feedback_disabled":false,"uuid":"237fe704-5eb6-4ee4-9387-ec286a938081","memory_paths":{"auto":"/Users/boj/.claude/projects/-Users-boj-book-ai-agent-book/memory/"},"fast_mode_state":"off"}
{"type":"assistant","message":{"id":"e8088492-91cb-4525-b2e2-d7722ea70525","container":null,"model":"<synthetic>","role":"assistant","stop_details":null,"stop_reason":"stop_sequence","stop_sequence":"","type":"message","usage":{"input_tokens":0,"output_tokens":0,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":null,"cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":0},"inference_geo":null,"iterations":null,"speed":null},"content":[{"type":"text","text":"Your organization has disabled Claude subscription access for Claude Code · Use an Anthropic API key instead, or ask your admin to enable access"}],"context_management":null},"parent_tool_use_id":null,"session_id":"2585a5cf-5b06-4497-9d62-bceddbf00eac","uuid":"bbe9a176-cd1f-4bcc-b388-760c2cf809a9","timestamp":"2026-07-29T19:43:40.971Z","error":"oauth_org_not_allowed","request_id":"req_011CdWvNmicoehed6MsdjMFA"}
{"is_error":true,"duration_api_ms":0,"num_turns":1,"stop_reason":"stop_sequence","session_id":"2585a5cf-5b06-4497-9d62-bceddbf00eac","total_cost_usd":0,"usage":{"input_tokens":0,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":0,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":0},"inference_geo":"","iterations":[],"speed":"standard"},"modelUsage":{},"permission_denials":[],"terminal_reason":"api_error","fast_mode_state":"off","subtype":"success","api_error_status":403,"result":"Your organization has disabled Claude subscription access for Claude Code · Use an Anthropic API key instead, or ask your admin to enable access","type":"result","duration_ms":2531,"uuid":"995b0e91-7a44-4ea5-9023-fe94311c0e69"}
@@ -0,0 +1,18 @@
[
"claude",
"/pptx\n\nCreate a polished 10\u201315 slide presentation from the real academic paper at\n`attention-is-all-you-need.pdf`. Write the final deck to\n`output/attention-is-all-you-need.pptx`.\n\nThis is an audited Agent Skills experiment. Follow the official PPTX Skill by\nprogressive disclosure: invoke the pptx Skill, read its complete SKILL.md, then\nread its complete html2pptx.md only after selection. Use the pinned official\n`scripts/html2pptx.js` workflow. Use the official `scripts/thumbnail.py` to\nmake `output/full-deck-thumbnail.jpg`, inspect the full grid, and fix visible\noverlap, cutoff, contrast, or alignment defects before finishing.\n\nContent gates:\n- cover title, problem/background, Transformer method/architecture, key\n experimental results, and conclusion;\n- extract or crop at least three visuals directly from the source PDF (not\n invented replacements), place the files under `source_visuals/`, and embed\n all of them in the deck;\n- create `source_visuals/manifest.json` as a JSON list. Each item must contain\n `file`, one-based PDF `page`, the paper's `label` (for example Figure 1 or\n Table 2), and a faithful `caption`;\n- make every visual consistent with the surrounding slide explanation and\n cite its source page/label on-slide.\n\nYou may install the Node packages required by the official Skill inside this\nworkspace. Do not use the repository's bundled `demo.py`, local proxy Skill,\nor prewritten sample outline. The final response must name the deck,\nthumbnail, visual manifest, slide count, validation performed, and any\nremaining limitation.\n",
"--print",
"--output-format",
"stream-json",
"--verbose",
"--model",
"sonnet",
"--effort",
"high",
"--max-budget-usd",
"8",
"--no-session-persistence",
"--dangerously-skip-permissions",
"--add-dir",
"/Users/boj/book/ai-agent-book/chapter2/agent-skills-ppt/external/anthropics-skills/skills/pptx"
]
@@ -0,0 +1,87 @@
{
"experiment_id": "2-6",
"protocol_sha256": "2990c8ef624edc12a06fb1b622de33b0fe8842cc5b42faf85788850111265eba",
"official_skill_receipt": {
"repository": "https://github.com/anthropics/skills.git",
"revision": "69c0b1a0674149f27b61b2635f935524b6add202",
"skill_path": "/Users/boj/book/ai-agent-book/chapter2/agent-skills-ppt/external/anthropics-skills/skills/pptx",
"required_file_hashes": {
"skills/pptx/SKILL.md": "b6f25545bfb358739f1532f793458b5dbc87ee009933cb7c306b2d951ab6617f",
"skills/pptx/html2pptx.md": "f08ed7580969b796d9cd5ade93e2cdee981dcaf13cc5eb12e8d4a3700c2d6047",
"skills/pptx/scripts/html2pptx.js": "c675d09a54d6a002e8ca5917b9d24a6568aa8d455bb7abeb212d4f564dd07a34",
"skills/pptx/scripts/thumbnail.py": "c21fd950b6ada7bd2f029885d3e56bc66b7ff061cc8404c492eb301664aa9e5d"
}
},
"claude_result": {
"subtype": "success",
"is_error": true,
"duration_ms": 2531,
"duration_api_ms": 0,
"num_turns": 1,
"result": "Your organization has disabled Claude subscription access for Claude Code · Use an Anthropic API key instead, or ask your admin to enable access",
"total_cost_usd": 0,
"usage": {
"input_tokens": 0,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"output_tokens": 0,
"server_tool_use": {
"web_search_requests": 0,
"web_fetch_requests": 0
},
"service_tier": "standard",
"cache_creation": {
"ephemeral_1h_input_tokens": 0,
"ephemeral_5m_input_tokens": 0
},
"inference_geo": "",
"iterations": [],
"speed": "standard"
},
"modelUsage": {},
"session_id": "2585a5cf-5b06-4497-9d62-bceddbf00eac"
},
"slide_count": 0,
"section_checks": {
"title": false,
"background": false,
"method": false,
"results": false,
"conclusion": false
},
"source_visuals": [],
"embedded_media": {},
"thumbnail_files": [],
"gates": {
"source_pdf_hash_matches": true,
"claude_run_succeeded": false,
"pptx_skill_invoked": false,
"skill_md_loaded": false,
"html2pptx_guide_loaded": false,
"official_html2pptx_used": false,
"official_thumbnail_used": false,
"thumbnail_visually_inspected": false,
"pptx_zip_valid": false,
"pptx_reopens": false,
"slide_count_in_range": false,
"required_sections_present": false,
"three_source_visuals_embedded_and_documented": false,
"thumbnail_grid_present": false,
"credential_scan_passed": true
},
"official_complete": false,
"artifacts": {
"experiment_protocol.json": {
"sha256": "2990c8ef624edc12a06fb1b622de33b0fe8842cc5b42faf85788850111265eba",
"bytes": 2582
},
"claude_stream.jsonl": {
"sha256": "7839179c7f92364b1fe8cbb183c9277040b1213237f0b208022bac82bbf4d6db",
"bytes": 3523
},
"workspace/attention-is-all-you-need.pdf": {
"sha256": "bdfaa68d8984f0dc02beaca527b76f207d99b666d31d1da728ee0728182df697",
"bytes": 2215244
}
}
}
@@ -0,0 +1,52 @@
{
"experiment_id": "2-6",
"protocol_version": "1.0.0",
"frozen_on": "2026-07-30",
"authority": "book/chapter2.md:767",
"runtime": {
"agent": "Claude Code",
"model_alias": "sonnet",
"official_skill_repository": "https://github.com/anthropics/skills.git",
"official_skill_revision": "69c0b1a0674149f27b61b2635f935524b6add202",
"skill_path": "skills/pptx",
"revision_reason": "This is the official revision whose PPTX Skill contains the html2pptx.md workflow named by the manuscript; later revision 4e6907a packages that guide as html2pptx.tgz."
},
"paper": {
"title": "Attention Is All You Need",
"authors": "Ashish Vaswani et al.",
"arxiv_id": "1706.03762",
"pdf_url": "https://arxiv.org/pdf/1706.03762",
"pdf_sha256": "bdfaa68d8984f0dc02beaca527b76f207d99b666d31d1da728ee0728182df697"
},
"output": {
"minimum_slides": 10,
"maximum_slides": 15,
"required_sections": [
"title",
"problem/background",
"method/Transformer architecture",
"key results",
"conclusion"
],
"minimum_paper_visuals": 3,
"visual_policy": "At least three images must be extracted or cropped from the source PDF, embedded in the deck, and listed with PDF page and source label in source_visuals/manifest.json."
},
"progressive_disclosure_gates": [
"Claude Code discovers only PPTX Skill metadata before selection",
"Claude invokes the official pptx Skill and loads its complete SKILL.md",
"Claude reads the complete official html2pptx.md only after selecting the Skill",
"Claude uses the official scripts/html2pptx.js workflow",
"Claude invokes the official scripts/thumbnail.py preview workflow and visually inspects the result"
],
"artifact_gates": [
"the source PDF hash matches the preregistered academic paper",
"the presentation is a valid ZIP/OOXML package and python-pptx reopens it",
"slide count is between 10 and 15 inclusive",
"the five required content sections are present",
"three or more source visual files are byte-identical to media embedded in the PPTX",
"each source visual has a PDF page, paper label, and caption in the visual manifest",
"a full-deck thumbnail grid exists",
"raw Claude Code stream events, actual model, usage/cost, commands, hashes, and credential scan are retained"
],
"claim_policy": "The bundled Python demo is a noncanonical mechanism illustration and cannot satisfy Experiment 2-6. Only a real Claude Code run with the pinned official Skill and real paper can complete this protocol."
}
@@ -0,0 +1,7 @@
{
"experiment_id": "2-6",
"official_complete": false,
"protocol_sha256": "2990c8ef624edc12a06fb1b622de33b0fe8842cc5b42faf85788850111265eba",
"comparison_sha256": "9fd8320f12627a8fc9d20904c0776e70895d6903704b23b5215d154a9d165735",
"pptx_sha256": null
}
@@ -0,0 +1,11 @@
{
"repository": "https://github.com/anthropics/skills.git",
"revision": "69c0b1a0674149f27b61b2635f935524b6add202",
"skill_path": "/Users/boj/book/ai-agent-book/chapter2/agent-skills-ppt/external/anthropics-skills/skills/pptx",
"required_file_hashes": {
"skills/pptx/SKILL.md": "b6f25545bfb358739f1532f793458b5dbc87ee009933cb7c306b2d951ab6617f",
"skills/pptx/html2pptx.md": "f08ed7580969b796d9cd5ade93e2cdee981dcaf13cc5eb12e8d4a3700c2d6047",
"skills/pptx/scripts/html2pptx.js": "c675d09a54d6a002e8ca5917b9d24a6568aa8d455bb7abeb212d4f564dd07a34",
"skills/pptx/scripts/thumbnail.py": "c21fd950b6ada7bd2f029885d3e56bc66b7ff061cc8404c492eb301664aa9e5d"
}
}
@@ -0,0 +1,30 @@
/pptx
Create a polished 1015 slide presentation from the real academic paper at
`attention-is-all-you-need.pdf`. Write the final deck to
`output/attention-is-all-you-need.pptx`.
This is an audited Agent Skills experiment. Follow the official PPTX Skill by
progressive disclosure: invoke the pptx Skill, read its complete SKILL.md, then
read its complete html2pptx.md only after selection. Use the pinned official
`scripts/html2pptx.js` workflow. Use the official `scripts/thumbnail.py` to
make `output/full-deck-thumbnail.jpg`, inspect the full grid, and fix visible
overlap, cutoff, contrast, or alignment defects before finishing.
Content gates:
- cover title, problem/background, Transformer method/architecture, key
experimental results, and conclusion;
- extract or crop at least three visuals directly from the source PDF (not
invented replacements), place the files under `source_visuals/`, and embed
all of them in the deck;
- create `source_visuals/manifest.json` as a JSON list. Each item must contain
`file`, one-based PDF `page`, the paper's `label` (for example Figure 1 or
Table 2), and a faithful `caption`;
- make every visual consistent with the surrounding slide explanation and
cite its source page/label on-slide.
You may install the Node packages required by the official Skill inside this
workspace. Do not use the repository's bundled `demo.py`, local proxy Skill,
or prewritten sample outline. The final response must name the deck,
thumbnail, visual manifest, slide count, validation performed, and any
remaining limitation.
@@ -0,0 +1,3 @@
{
"auth_source": "environment"
}
@@ -0,0 +1,3 @@
{
"return_code": 1
}
@@ -0,0 +1,13 @@
{"type":"system","subtype":"init","cwd":"/Users/boj/book/ai-agent-book/chapter2/agent-skills-ppt/runs/exp2-6-claude-pptx-20260730-v4/workspace","session_id":"8ab46948-d96e-4312-ad33-4c572984fe68","tools":["Task","Bash","CronCreate","CronDelete","CronList","Edit","EnterWorktree","ExitWorktree","NotebookEdit","Read","ReportFindings","ScheduleWakeup","SendMessage","Skill","TaskCreate","TaskGet","TaskList","TaskOutput","TaskStop","TaskUpdate","ToolSearch","WebFetch","WebSearch","Workflow","Write"],"mcp_servers":[],"model":"claude-sonnet-5","permissionMode":"bypassPermissions","slash_commands":["pptx","deep-research","dataviz","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","claude-api","run","run-skill-generator","agents","clear","color","compact","config","context","effort","fast","heapdump","init","mcp","model","__remote-workflow","workflow-launch-exec","reload-skills","rename","review","security-review","usage","insights","recap","goal","team-onboarding"],"apiKeySource":"ANTHROPIC_API_KEY","claude_code_version":"2.1.218","output_style":"default","agents":["claude","Explore","general-purpose","Plan","statusline-setup"],"skills":["pptx","deep-research","dataviz","update-config","verify","debug","code-review","simplify","batch","fewer-permission-prompts","doctor","loop","claude-api","run","run-skill-generator"],"plugins":[],"capabilities":["interrupt_receipt_v1","msg_lifecycle_v1"],"analytics_disabled":true,"product_feedback_disabled":false,"uuid":"6e950b22-b7c4-4d6c-8b8b-8e446b938f81","memory_paths":{"auto":"/Users/boj/.claude/projects/-Users-boj-book-ai-agent-book/memory/"},"fast_mode_state":"off"}
{"type":"system","subtype":"api_retry","attempt":1,"max_retries":10,"retry_delay_ms":565,"error_status":401,"error":"authentication_failed","session_id":"8ab46948-d96e-4312-ad33-4c572984fe68","uuid":"3882d3fc-b03c-4dde-87fe-e7149c9e24eb"}
{"type":"system","subtype":"api_retry","attempt":2,"max_retries":10,"retry_delay_ms":1036,"error_status":401,"error":"authentication_failed","session_id":"8ab46948-d96e-4312-ad33-4c572984fe68","uuid":"08634263-cd94-4d42-98af-a71268b117f7"}
{"type":"system","subtype":"api_retry","attempt":3,"max_retries":10,"retry_delay_ms":2214,"error_status":401,"error":"authentication_failed","session_id":"8ab46948-d96e-4312-ad33-4c572984fe68","uuid":"095c96a0-0012-407d-9c26-02dea507fce6"}
{"type":"system","subtype":"api_retry","attempt":4,"max_retries":10,"retry_delay_ms":4069,"error_status":401,"error":"authentication_failed","session_id":"8ab46948-d96e-4312-ad33-4c572984fe68","uuid":"4ca5d3ea-a79a-48c2-b62b-e5276d172e32"}
{"type":"system","subtype":"api_retry","attempt":5,"max_retries":10,"retry_delay_ms":9296,"error_status":401,"error":"authentication_failed","session_id":"8ab46948-d96e-4312-ad33-4c572984fe68","uuid":"2d2fa67a-832a-4446-a7d8-9ce05e25af33"}
{"type":"system","subtype":"api_retry","attempt":6,"max_retries":10,"retry_delay_ms":16597,"error_status":401,"error":"authentication_failed","session_id":"8ab46948-d96e-4312-ad33-4c572984fe68","uuid":"29753ccb-3721-4f02-b1a0-9b9c350a67a4"}
{"type":"system","subtype":"api_retry","attempt":7,"max_retries":10,"retry_delay_ms":35804,"error_status":401,"error":"authentication_failed","session_id":"8ab46948-d96e-4312-ad33-4c572984fe68","uuid":"032622d8-bdd9-408d-9f3f-faff25d6184d"}
{"type":"system","subtype":"api_retry","attempt":8,"max_retries":10,"retry_delay_ms":32162,"error_status":401,"error":"authentication_failed","session_id":"8ab46948-d96e-4312-ad33-4c572984fe68","uuid":"1c7b69c0-bc76-4e54-b4cf-12d051b82829"}
{"type":"system","subtype":"api_retry","attempt":9,"max_retries":10,"retry_delay_ms":38480,"error_status":401,"error":"authentication_failed","session_id":"8ab46948-d96e-4312-ad33-4c572984fe68","uuid":"b1975489-854b-46ef-8fc6-ee25f180ba14"}
{"type":"system","subtype":"api_retry","attempt":10,"max_retries":10,"retry_delay_ms":38395,"error_status":401,"error":"authentication_failed","session_id":"8ab46948-d96e-4312-ad33-4c572984fe68","uuid":"d5de9ee1-cfa6-474c-9501-7d4f37514d14"}
{"type":"assistant","message":{"id":"a14dc2e5-e5e8-4327-9453-80b611bb36c6","container":null,"model":"<synthetic>","role":"assistant","stop_details":null,"stop_reason":"stop_sequence","stop_sequence":"","type":"message","usage":{"input_tokens":0,"output_tokens":0,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":null,"cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":0},"inference_geo":null,"iterations":null,"speed":null},"content":[{"type":"text","text":"Failed to authenticate. API Error: 401 API key is invalid."}],"context_management":null},"parent_tool_use_id":null,"session_id":"8ab46948-d96e-4312-ad33-4c572984fe68","uuid":"1360d6a4-bd75-4190-a726-4267bd86c782","timestamp":"2026-07-30T04:13:19.758Z","error":"authentication_failed"}
{"is_error":true,"duration_api_ms":0,"num_turns":1,"stop_reason":"stop_sequence","session_id":"8ab46948-d96e-4312-ad33-4c572984fe68","total_cost_usd":0,"usage":{"input_tokens":0,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":0,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":0},"inference_geo":"","iterations":[],"speed":"standard"},"modelUsage":{},"permission_denials":[],"terminal_reason":"api_error","fast_mode_state":"off","subtype":"success","api_error_status":401,"result":"Failed to authenticate. API Error: 401 API key is invalid.","type":"result","duration_ms":186850,"uuid":"f27534f9-e2af-4e21-a4da-95ede789ca38"}
@@ -0,0 +1,18 @@
[
"claude",
"/pptx\n\nCreate a polished 10\u201315 slide presentation from the real academic paper at\n`attention-is-all-you-need.pdf`. Write the final deck to\n`output/attention-is-all-you-need.pptx`.\n\nThis is an audited Agent Skills experiment. Follow the official PPTX Skill by\nprogressive disclosure: invoke the pptx Skill, read its complete SKILL.md, then\nread its complete html2pptx.md only after selection. Use the pinned official\n`scripts/html2pptx.js` workflow. Use the official `scripts/thumbnail.py` to\nmake `output/full-deck-thumbnail.jpg`, inspect the full grid, and fix visible\noverlap, cutoff, contrast, or alignment defects before finishing.\n\nContent gates:\n- cover title, problem/background, Transformer method/architecture, key\n experimental results, and conclusion;\n- extract or crop at least three visuals directly from the source PDF (not\n invented replacements), place the files under `source_visuals/`, and embed\n all of them in the deck;\n- create `source_visuals/manifest.json` as a JSON list. Each item must contain\n `file`, one-based PDF `page`, the paper's `label` (for example Figure 1 or\n Table 2), and a faithful `caption`;\n- make every visual consistent with the surrounding slide explanation and\n cite its source page/label on-slide.\n\nYou may install the Node packages required by the official Skill inside this\nworkspace. Do not use the repository's bundled `demo.py`, local proxy Skill,\nor prewritten sample outline. The final response must name the deck,\nthumbnail, visual manifest, slide count, validation performed, and any\nremaining limitation.\n",
"--print",
"--output-format",
"stream-json",
"--verbose",
"--model",
"sonnet",
"--effort",
"high",
"--max-budget-usd",
"8",
"--no-session-persistence",
"--dangerously-skip-permissions",
"--add-dir",
"/Users/boj/book/ai-agent-book/chapter2/agent-skills-ppt/external/anthropics-skills/skills/pptx"
]
@@ -0,0 +1,87 @@
{
"experiment_id": "2-6",
"protocol_sha256": "2990c8ef624edc12a06fb1b622de33b0fe8842cc5b42faf85788850111265eba",
"official_skill_receipt": {
"repository": "https://github.com/anthropics/skills.git",
"revision": "69c0b1a0674149f27b61b2635f935524b6add202",
"skill_path": "/Users/boj/book/ai-agent-book/chapter2/agent-skills-ppt/external/anthropics-skills/skills/pptx",
"required_file_hashes": {
"skills/pptx/SKILL.md": "b6f25545bfb358739f1532f793458b5dbc87ee009933cb7c306b2d951ab6617f",
"skills/pptx/html2pptx.md": "f08ed7580969b796d9cd5ade93e2cdee981dcaf13cc5eb12e8d4a3700c2d6047",
"skills/pptx/scripts/html2pptx.js": "c675d09a54d6a002e8ca5917b9d24a6568aa8d455bb7abeb212d4f564dd07a34",
"skills/pptx/scripts/thumbnail.py": "c21fd950b6ada7bd2f029885d3e56bc66b7ff061cc8404c492eb301664aa9e5d"
}
},
"claude_result": {
"subtype": "success",
"is_error": true,
"duration_ms": 186850,
"duration_api_ms": 0,
"num_turns": 1,
"result": "Failed to authenticate. API Error: 401 API key is invalid.",
"total_cost_usd": 0,
"usage": {
"input_tokens": 0,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"output_tokens": 0,
"server_tool_use": {
"web_search_requests": 0,
"web_fetch_requests": 0
},
"service_tier": "standard",
"cache_creation": {
"ephemeral_1h_input_tokens": 0,
"ephemeral_5m_input_tokens": 0
},
"inference_geo": "",
"iterations": [],
"speed": "standard"
},
"modelUsage": {},
"session_id": "8ab46948-d96e-4312-ad33-4c572984fe68"
},
"slide_count": 0,
"section_checks": {
"title": false,
"background": false,
"method": false,
"results": false,
"conclusion": false
},
"source_visuals": [],
"embedded_media": {},
"thumbnail_files": [],
"gates": {
"source_pdf_hash_matches": true,
"claude_run_succeeded": false,
"pptx_skill_invoked": false,
"skill_md_loaded": false,
"html2pptx_guide_loaded": false,
"official_html2pptx_used": false,
"official_thumbnail_used": false,
"thumbnail_visually_inspected": false,
"pptx_zip_valid": false,
"pptx_reopens": false,
"slide_count_in_range": false,
"required_sections_present": false,
"three_source_visuals_embedded_and_documented": false,
"thumbnail_grid_present": false,
"credential_scan_passed": true
},
"official_complete": false,
"artifacts": {
"experiment_protocol.json": {
"sha256": "2990c8ef624edc12a06fb1b622de33b0fe8842cc5b42faf85788850111265eba",
"bytes": 2582
},
"claude_stream.jsonl": {
"sha256": "531ee0e7a5ffcdbd4060108ed809e8576b13604f29a3cc3fbd259afac3cf9afb",
"bytes": 5725
},
"workspace/attention-is-all-you-need.pdf": {
"sha256": "bdfaa68d8984f0dc02beaca527b76f207d99b666d31d1da728ee0728182df697",
"bytes": 2215244
}
}
}
@@ -0,0 +1,52 @@
{
"experiment_id": "2-6",
"protocol_version": "1.0.0",
"frozen_on": "2026-07-30",
"authority": "book/chapter2.md:767",
"runtime": {
"agent": "Claude Code",
"model_alias": "sonnet",
"official_skill_repository": "https://github.com/anthropics/skills.git",
"official_skill_revision": "69c0b1a0674149f27b61b2635f935524b6add202",
"skill_path": "skills/pptx",
"revision_reason": "This is the official revision whose PPTX Skill contains the html2pptx.md workflow named by the manuscript; later revision 4e6907a packages that guide as html2pptx.tgz."
},
"paper": {
"title": "Attention Is All You Need",
"authors": "Ashish Vaswani et al.",
"arxiv_id": "1706.03762",
"pdf_url": "https://arxiv.org/pdf/1706.03762",
"pdf_sha256": "bdfaa68d8984f0dc02beaca527b76f207d99b666d31d1da728ee0728182df697"
},
"output": {
"minimum_slides": 10,
"maximum_slides": 15,
"required_sections": [
"title",
"problem/background",
"method/Transformer architecture",
"key results",
"conclusion"
],
"minimum_paper_visuals": 3,
"visual_policy": "At least three images must be extracted or cropped from the source PDF, embedded in the deck, and listed with PDF page and source label in source_visuals/manifest.json."
},
"progressive_disclosure_gates": [
"Claude Code discovers only PPTX Skill metadata before selection",
"Claude invokes the official pptx Skill and loads its complete SKILL.md",
"Claude reads the complete official html2pptx.md only after selecting the Skill",
"Claude uses the official scripts/html2pptx.js workflow",
"Claude invokes the official scripts/thumbnail.py preview workflow and visually inspects the result"
],
"artifact_gates": [
"the source PDF hash matches the preregistered academic paper",
"the presentation is a valid ZIP/OOXML package and python-pptx reopens it",
"slide count is between 10 and 15 inclusive",
"the five required content sections are present",
"three or more source visual files are byte-identical to media embedded in the PPTX",
"each source visual has a PDF page, paper label, and caption in the visual manifest",
"a full-deck thumbnail grid exists",
"raw Claude Code stream events, actual model, usage/cost, commands, hashes, and credential scan are retained"
],
"claim_policy": "The bundled Python demo is a noncanonical mechanism illustration and cannot satisfy Experiment 2-6. Only a real Claude Code run with the pinned official Skill and real paper can complete this protocol."
}
@@ -0,0 +1,7 @@
{
"experiment_id": "2-6",
"official_complete": false,
"protocol_sha256": "2990c8ef624edc12a06fb1b622de33b0fe8842cc5b42faf85788850111265eba",
"comparison_sha256": "4779c6ed74afbe704287db61f707043ca4aa2de274cb3b74364733f4a79d3a48",
"pptx_sha256": null
}
@@ -0,0 +1,11 @@
{
"repository": "https://github.com/anthropics/skills.git",
"revision": "69c0b1a0674149f27b61b2635f935524b6add202",
"skill_path": "/Users/boj/book/ai-agent-book/chapter2/agent-skills-ppt/external/anthropics-skills/skills/pptx",
"required_file_hashes": {
"skills/pptx/SKILL.md": "b6f25545bfb358739f1532f793458b5dbc87ee009933cb7c306b2d951ab6617f",
"skills/pptx/html2pptx.md": "f08ed7580969b796d9cd5ade93e2cdee981dcaf13cc5eb12e8d4a3700c2d6047",
"skills/pptx/scripts/html2pptx.js": "c675d09a54d6a002e8ca5917b9d24a6568aa8d455bb7abeb212d4f564dd07a34",
"skills/pptx/scripts/thumbnail.py": "c21fd950b6ada7bd2f029885d3e56bc66b7ff061cc8404c492eb301664aa9e5d"
}
}
@@ -0,0 +1,30 @@
/pptx
Create a polished 1015 slide presentation from the real academic paper at
`attention-is-all-you-need.pdf`. Write the final deck to
`output/attention-is-all-you-need.pptx`.
This is an audited Agent Skills experiment. Follow the official PPTX Skill by
progressive disclosure: invoke the pptx Skill, read its complete SKILL.md, then
read its complete html2pptx.md only after selection. Use the pinned official
`scripts/html2pptx.js` workflow. Use the official `scripts/thumbnail.py` to
make `output/full-deck-thumbnail.jpg`, inspect the full grid, and fix visible
overlap, cutoff, contrast, or alignment defects before finishing.
Content gates:
- cover title, problem/background, Transformer method/architecture, key
experimental results, and conclusion;
- extract or crop at least three visuals directly from the source PDF (not
invented replacements), place the files under `source_visuals/`, and embed
all of them in the deck;
- create `source_visuals/manifest.json` as a JSON list. Each item must contain
`file`, one-based PDF `page`, the paper's `label` (for example Figure 1 or
Table 2), and a faithful `caption`;
- make every visual consistent with the surrounding slide explanation and
cite its source page/label on-slide.
You may install the Node packages required by the official Skill inside this
workspace. Do not use the repository's bundled `demo.py`, local proxy Skill,
or prewritten sample outline. The final response must name the deck,
thumbnail, visual manifest, slide count, validation performed, and any
remaining limitation.
@@ -0,0 +1,13 @@
[
"/Users/boj/.kimi-code/bin/kimi",
"--prompt",
"Create a polished 10\u201315 slide presentation from the real academic paper at\n`attention-is-all-you-need.pdf` using your installed `pptx` Skill. Write the\nfinal deck to `output/attention-is-all-you-need.pptx`.\n\nThis is an audited Agent Skills experiment. Follow the official PPTX Skill by\nprogressive disclosure: invoke the pptx Skill through the Skill tool (this\nloads its complete SKILL.md), then read its complete html2pptx.md only after\nselection. Use the pinned official `scripts/html2pptx.js` workflow. Use the\nofficial `scripts/thumbnail.py` to make `output/full-deck-thumbnail.jpg`,\ninspect the full grid, and fix visible overlap, cutoff, contrast, or alignment\ndefects before finishing.\n\nContent gates:\n- cover title, problem/background, Transformer method/architecture, key\n experimental results, and conclusion;\n- extract or crop at least three visuals directly from the source PDF (not\n invented replacements), place the files under `source_visuals/`, and embed\n all of them in the deck;\n- create `source_visuals/manifest.json` as a JSON list. Each item must contain\n `file`, one-based PDF `page`, the paper's `label` (for example Figure 1 or\n Table 2), and a faithful `caption`;\n- make every visual consistent with the surrounding slide explanation and\n cite its source page/label on-slide.\n\nYou may install the Node packages required by the official Skill inside this\nworkspace. Do not use the repository's bundled `demo.py`, local proxy Skill,\nor prewritten sample outline. The final response must name the deck,\nthumbnail, visual manifest, slide count, validation performed, and any\nremaining limitation.\n",
"--output-format",
"stream-json",
"--model",
"kimi-code/k3",
"--skills-dir",
"/Users/boj/book/ai-agent-book-exp2-6-mp/chapter2/agent-skills-ppt/runs/exp2-6-kimi-pptx-20260731-v1/workspace/kimi-skills",
"--add-dir",
"/Users/boj/book/ai-agent-book-exp2-6-mp/chapter2/agent-skills-ppt/external/anthropics-skills/skills/pptx"
]
@@ -0,0 +1,132 @@
{
"experiment_id": "2-6",
"runtime": "kimi",
"protocol_sha256": "1316b6c5bca03b76b7fda2c1fc31b9076394bf996cacb4f52add2db98dbae84e",
"official_skill_receipt": {
"repository": "https://github.com/anthropics/skills.git",
"revision": "69c0b1a0674149f27b61b2635f935524b6add202",
"skill_path": "/Users/boj/book/ai-agent-book-exp2-6-mp/chapter2/agent-skills-ppt/external/anthropics-skills/skills/pptx",
"required_file_hashes": {
"skills/pptx/SKILL.md": "b6f25545bfb358739f1532f793458b5dbc87ee009933cb7c306b2d951ab6617f",
"skills/pptx/html2pptx.md": "f08ed7580969b796d9cd5ade93e2cdee981dcaf13cc5eb12e8d4a3700c2d6047",
"skills/pptx/scripts/html2pptx.js": "c675d09a54d6a002e8ca5917b9d24a6568aa8d455bb7abeb212d4f564dd07a34",
"skills/pptx/scripts/thumbnail.py": "c21fd950b6ada7bd2f029885d3e56bc66b7ff061cc8404c492eb301664aa9e5d"
}
},
"agent_result": {
"return_code": 0,
"model_alias": "kimi-code/k3",
"num_assistant_messages": 25,
"num_tool_calls": 114,
"tool_names": [
"Bash",
"Edit",
"Grep",
"Read",
"ReadMediaFile",
"Skill",
"TodoList",
"Write"
],
"session_id": "session_605154fa-66bf-4bb2-89f4-86a48d3dcd8f",
"final_response": "Done. The presentation was built with the official PPTX Skill workflow (SKILL.md → html2pptx.md → pinned `scripts/html2pptx.js` → `scripts/thumbnail.py` visual check).\n\n**Deliverables**\n- Deck: `output/attention-is-all-you-need.pptx` — 13 slides, 16:9\n- Thumbnail grid: `output/full-deck-thumbnail.jpg` (official `thumbnail.py`, 4 cols)\n- Visual manifest: `source_visuals/manifest.json` — 4 entries with `file`, one-based PDF `page`, `label`, faithful `caption`\n- Cropped-from-PDF visuals (all embedded in the deck, each cited on-slide with label + page):\n - `fig1-03.png` — Figure 1, p. 3 (architecture) → cover + slide 4\n - `fig2-04.png` — Figure 2, p. 4 (scaled dot-product / multi-head attention) → slide 5\n - `table2-08.png` — Table 2, p. 8 (BLEU results) → slide 9\n - `fig3-13.png` — Figure 3, p. 13 (attention visualization) → slide 12\n\n**Content coverage** — cover title/authors/venue (1), problem/background (2), key idea (3), Transformer method/architecture (46), why self-attention with Table 1 adapted as a native table (7), training setup (8), key experimental results incl. Table 2 image and a BLEU bar chart (910), generalization to constituency parsing (11), interpretability (12), conclusion (13).\n\n**Validation performed**\n- `html2pptx.js` dimension/overflow validation passed for every HTML slide\n- Full-deck thumbnail grid inspected; every slide also re-rendered at 120220 DPI via LibreOffice/pdftoppm and individually inspected\n- Defects found and fixed: dropped `<sub>` subscript content (rewritten as `d_k`/`d_model` style), inconsistent bullet glyphs (root-caused to duplicate mid-paragraph `<a:pPr>` emitted by pptxgenjs; 75 removed via OOXML post-processing), a broken chart relationship path (normalized to relative), and rounded chart data labels (set to exact `0.00` format)\n- `ooxml/scripts/validate.py` passes on the final packed file; manifest JSON parses and matches the cropped files\n\n**Remaining limitations**\n- Slides were visually verified through LibreOffice rendering, not Microsoft PowerPoint itself; minor font-metric differences are possible there\n- The complexity table on slide 7 is a faithful adaptation of Table 1 (native table, cited as \"Adapted from Table 1, p. 6\") rather than a PDF crop — the four required direct crops are Figures 1, 2, 3 and Table 2"
},
"slide_count": 13,
"section_checks": {
"title": true,
"background": true,
"method": true,
"results": true,
"conclusion": true
},
"source_visuals": [
{
"file": "fig1-03.png",
"sha256": "eac33d539b220d8f092233f0ae589302365890b85444a1fdbfb88597e1eebdce",
"embedded": true,
"page": 3,
"label": "Figure 1",
"caption": "The Transformer - model architecture."
},
{
"file": "fig2-04.png",
"sha256": "66eb2559c7e7483a15a1f13b95babec32b4529b216664a150d974213ac5a8edc",
"embedded": true,
"page": 4,
"label": "Figure 2",
"caption": "(left) Scaled Dot-Product Attention. (right) Multi-Head Attention consists of several attention layers running in parallel."
},
{
"file": "table2-08.png",
"sha256": "8b2ead56595513b994224b6f1be49d6751fa8633b706638092670478b7ddb3c1",
"embedded": true,
"page": 8,
"label": "Table 2",
"caption": "The Transformer achieves better BLEU scores than previous state-of-the-art models on the English-to-German and English-to-French newstest2014 tests at a fraction of the training cost."
},
{
"file": "fig3-13.png",
"sha256": "47e9df791c547feee32f1f88649a70a256ce4552e55fb4ff258a40fd5998ccd6",
"embedded": true,
"page": 13,
"label": "Figure 3",
"caption": "An example of the attention mechanism following long-distance dependencies in the encoder self-attention in layer 5 of 6. Many of the attention heads attend to a distant dependency of the verb 'making', completing the phrase 'making...more difficult'. Attentions here shown only for the word 'making'. Different colors represent different heads. Best viewed in color."
}
],
"embedded_media": {
"ppt/media/image-4-1.png": "eac33d539b220d8f092233f0ae589302365890b85444a1fdbfb88597e1eebdce",
"ppt/media/image-1-2.png": "eac33d539b220d8f092233f0ae589302365890b85444a1fdbfb88597e1eebdce",
"ppt/media/image-5-1.png": "66eb2559c7e7483a15a1f13b95babec32b4529b216664a150d974213ac5a8edc",
"ppt/media/image-12-1.png": "47e9df791c547feee32f1f88649a70a256ce4552e55fb4ff258a40fd5998ccd6",
"ppt/media/image-9-1.png": "8b2ead56595513b994224b6f1be49d6751fa8633b706638092670478b7ddb3c1",
"ppt/media/Slide-13-image-1.png": "629d702b5ca51d6380ada24547bd77941b262b3434e394996946cb7b0bfc8398",
"ppt/media/Slide-1-image-1.png": "629d702b5ca51d6380ada24547bd77941b262b3434e394996946cb7b0bfc8398"
},
"thumbnail_files": [
"workspace/output/full-deck-thumbnail.jpg"
],
"gates": {
"source_pdf_hash_matches": true,
"kimi_run_succeeded": true,
"pptx_skill_invoked": true,
"skill_md_loaded": true,
"html2pptx_guide_loaded": true,
"official_html2pptx_used": true,
"official_thumbnail_used": true,
"thumbnail_visually_inspected": true,
"pptx_zip_valid": true,
"pptx_reopens": true,
"slide_count_in_range": true,
"required_sections_present": true,
"three_source_visuals_embedded_and_documented": true,
"thumbnail_grid_present": true,
"credential_scan_passed": true
},
"official_complete": true,
"artifacts": {
"experiment_protocol.json": {
"sha256": "1316b6c5bca03b76b7fda2c1fc31b9076394bf996cacb4f52add2db98dbae84e",
"bytes": 3709
},
"kimi_stream.jsonl": {
"sha256": "00eaaa3a884c35a203ffbacd65d146b44abf4a5f3a28e9e979a2f83e701541a1",
"bytes": 5595585
},
"workspace/attention-is-all-you-need.pdf": {
"sha256": "bdfaa68d8984f0dc02beaca527b76f207d99b666d31d1da728ee0728182df697",
"bytes": 2215244
},
"workspace/output/attention-is-all-you-need.pptx": {
"sha256": "be890e73d6b4bf91d888079ffb4cd7f63435bfcadd7046b580d64627907de41d",
"bytes": 605203
},
"workspace/source_visuals/manifest.json": {
"sha256": "be3b44999ae79695ac42f3fb7a538fdfc6ce84a53f6173d1547df1f201a0c350",
"bytes": 1091
},
"workspace/output/full-deck-thumbnail.jpg": {
"sha256": "7f7eae7496b8748d3bc1f179b8e3542f5fc05c9e13dac46f4125bf0d782aeabf",
"bytes": 255718
}
}
}
@@ -0,0 +1,65 @@
{
"experiment_id": "2-6",
"protocol_version": "1.1.0",
"frozen_on": "2026-07-30",
"revised_on": "2026-07-31",
"authority": "book/chapter2.md:767",
"runtime": {
"agent": "Claude Code",
"model_alias": "sonnet",
"acceptance_policy": "Runtime-agnostic (author policy, 2026-07-31): acceptance is NOT gated on Anthropic/Claude Code. Any agent runtime + model that supports SKILL.md-style progressive disclosure may complete this experiment, because not every reader has an Anthropic key. The pinned official Anthropic PPTX Skill content, the real paper, and all artifact gates are unchanged; Claude Code remains the reference path for readers who have Anthropic credentials.",
"alternate_runtimes": {
"kimi": {
"agent": "Kimi Code CLI",
"binary": "kimi",
"binary_fallback": "~/.kimi-code/bin/kimi",
"model_alias": "kimi-code/k3",
"auth_environment_variables": ["KIMI_API_KEY", "MOONSHOT_API_KEY"],
"skills_flag": "--skills-dir",
"stream_format": "stream-json"
}
},
"official_skill_repository": "https://github.com/anthropics/skills.git",
"official_skill_revision": "69c0b1a0674149f27b61b2635f935524b6add202",
"skill_path": "skills/pptx",
"revision_reason": "This is the official revision whose PPTX Skill contains the html2pptx.md workflow named by the manuscript; later revision 4e6907a packages that guide as html2pptx.tgz."
},
"paper": {
"title": "Attention Is All You Need",
"authors": "Ashish Vaswani et al.",
"arxiv_id": "1706.03762",
"pdf_url": "https://arxiv.org/pdf/1706.03762",
"pdf_sha256": "bdfaa68d8984f0dc02beaca527b76f207d99b666d31d1da728ee0728182df697"
},
"output": {
"minimum_slides": 10,
"maximum_slides": 15,
"required_sections": [
"title",
"problem/background",
"method/Transformer architecture",
"key results",
"conclusion"
],
"minimum_paper_visuals": 3,
"visual_policy": "At least three images must be extracted or cropped from the source PDF, embedded in the deck, and listed with PDF page and source label in source_visuals/manifest.json."
},
"progressive_disclosure_gates": [
"the agent runtime discovers only PPTX Skill metadata before selection",
"the agent invokes the official pptx Skill and loads its complete SKILL.md",
"the agent reads the complete official html2pptx.md only after selecting the Skill",
"the agent uses the official scripts/html2pptx.js workflow",
"the agent invokes the official scripts/thumbnail.py preview workflow and visually inspects the result"
],
"artifact_gates": [
"the source PDF hash matches the preregistered academic paper",
"the presentation is a valid ZIP/OOXML package and python-pptx reopens it",
"slide count is between 10 and 15 inclusive",
"the five required content sections are present",
"three or more source visual files are byte-identical to media embedded in the PPTX",
"each source visual has a PDF page, paper label, and caption in the visual manifest",
"a full-deck thumbnail grid exists",
"raw agent-runtime stream events, actual model, usage/cost where the runtime reports it, commands, hashes, and credential scan are retained"
],
"claim_policy": "The bundled Python demo is a noncanonical mechanism illustration and cannot satisfy Experiment 2-6. Only a real agent-runtime run with the pinned official Skill and real paper can complete this protocol. The runtime may be Claude Code or an equivalent skills-capable runtime (for example Kimi Code CLI); the pinned official Anthropic PPTX Skill content and every artifact gate below apply identically to either runtime."
}
@@ -0,0 +1,3 @@
{
"return_code": 0
}
@@ -0,0 +1,8 @@
{
"experiment_id": "2-6",
"runtime": "kimi",
"official_complete": true,
"protocol_sha256": "1316b6c5bca03b76b7fda2c1fc31b9076394bf996cacb4f52add2db98dbae84e",
"comparison_sha256": "52f7e327fcf71f2038f0449c70f19f9e7753406511c6bd93660e33a2967bb098",
"pptx_sha256": "be890e73d6b4bf91d888079ffb4cd7f63435bfcadd7046b580d64627907de41d"
}
@@ -0,0 +1,11 @@
{
"repository": "https://github.com/anthropics/skills.git",
"revision": "69c0b1a0674149f27b61b2635f935524b6add202",
"skill_path": "/Users/boj/book/ai-agent-book-exp2-6-mp/chapter2/agent-skills-ppt/external/anthropics-skills/skills/pptx",
"required_file_hashes": {
"skills/pptx/SKILL.md": "b6f25545bfb358739f1532f793458b5dbc87ee009933cb7c306b2d951ab6617f",
"skills/pptx/html2pptx.md": "f08ed7580969b796d9cd5ade93e2cdee981dcaf13cc5eb12e8d4a3700c2d6047",
"skills/pptx/scripts/html2pptx.js": "c675d09a54d6a002e8ca5917b9d24a6568aa8d455bb7abeb212d4f564dd07a34",
"skills/pptx/scripts/thumbnail.py": "c21fd950b6ada7bd2f029885d3e56bc66b7ff061cc8404c492eb301664aa9e5d"
}
}
@@ -0,0 +1,29 @@
Create a polished 1015 slide presentation from the real academic paper at
`attention-is-all-you-need.pdf` using your installed `pptx` Skill. Write the
final deck to `output/attention-is-all-you-need.pptx`.
This is an audited Agent Skills experiment. Follow the official PPTX Skill by
progressive disclosure: invoke the pptx Skill through the Skill tool (this
loads its complete SKILL.md), then read its complete html2pptx.md only after
selection. Use the pinned official `scripts/html2pptx.js` workflow. Use the
official `scripts/thumbnail.py` to make `output/full-deck-thumbnail.jpg`,
inspect the full grid, and fix visible overlap, cutoff, contrast, or alignment
defects before finishing.
Content gates:
- cover title, problem/background, Transformer method/architecture, key
experimental results, and conclusion;
- extract or crop at least three visuals directly from the source PDF (not
invented replacements), place the files under `source_visuals/`, and embed
all of them in the deck;
- create `source_visuals/manifest.json` as a JSON list. Each item must contain
`file`, one-based PDF `page`, the paper's `label` (for example Figure 1 or
Table 2), and a faithful `caption`;
- make every visual consistent with the surrounding slide explanation and
cite its source page/label on-slide.
You may install the Node packages required by the official Skill inside this
workspace. Do not use the repository's bundled `demo.py`, local proxy Skill,
or prewritten sample outline. The final response must name the deck,
thumbnail, visual manifest, slide count, validation performed, and any
remaining limitation.
@@ -0,0 +1,10 @@
{
"runtime": "kimi",
"binary": "/Users/boj/.kimi-code/bin/kimi",
"model_alias": "kimi-code/k3",
"skills_dir": "/Users/boj/book/ai-agent-book-exp2-6-mp/chapter2/agent-skills-ppt/runs/exp2-6-kimi-pptx-20260731-v1/workspace/kimi-skills",
"auth_environment_variables_present": [
"KIMI_API_KEY",
"MOONSHOT_API_KEY"
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

@@ -0,0 +1,85 @@
const pptxgen = require('pptxgenjs');
const html2pptx = require('./kimi-skills/pptx/scripts/html2pptx.js');
async function build() {
const pptx = new pptxgen();
pptx.layout = 'LAYOUT_16x9';
pptx.author = 'Kimi';
pptx.title = 'Attention Is All You Need';
// Slides 1-6
await html2pptx('slides/01-cover.html', pptx);
await html2pptx('slides/02-problem.html', pptx);
await html2pptx('slides/03-keyidea.html', pptx);
await html2pptx('slides/04-architecture.html', pptx);
await html2pptx('slides/05-attention.html', pptx);
await html2pptx('slides/06-building-blocks.html', pptx);
// Slide 7: complexity table (adapted from Table 1, p. 6)
const { slide: s7, placeholders: p7 } = await html2pptx('slides/07-why-self-attention.html', pptx);
const hdr = { fill: { color: '4338CA' }, color: 'FFFFFF', bold: true, fontSize: 10 };
const rows = [
[
{ text: 'Layer Type', options: hdr }, { text: 'Complexity per Layer', options: hdr },
{ text: 'Sequential Ops', options: hdr }, { text: 'Max Path Length', options: hdr }
],
['Self-Attention', 'O(n²·d)', 'O(1)', 'O(1)'],
['Recurrent', 'O(n·d²)', 'O(n)', 'O(n)'],
['Convolutional', 'O(k·n·d²)', 'O(1)', 'O(log_k n)'],
['Self-Attention (restricted)', 'O(r·n·d)', 'O(1)', 'O(n/r)']
];
s7.addTable(rows, {
...p7[0],
colW: [1.55, 1.15, 1.0, 1.1],
fontSize: 9.5,
fontFace: 'Arial',
border: { pt: 0.75, color: 'C9C6E8' },
align: 'center',
valign: 'middle',
fill: { color: 'FFFFFF' }
});
// Slides 8-9
await html2pptx('slides/08-training.html', pptx);
await html2pptx('slides/09-results.html', pptx);
// Slide 10: EN-DE BLEU bar chart (values from Table 2, p. 8)
const { slide: s10, placeholders: p10 } = await html2pptx('slides/10-chart.html', pptx);
s10.addChart(pptx.charts.BAR, [{
name: 'BLEU',
labels: ['ByteNet', 'GNMT+RL', 'ConvS2S', 'MoE', 'ConvS2S Ens.', 'TF (base)', 'TF (big)'],
values: [23.75, 24.6, 25.16, 26.03, 26.36, 27.3, 28.4]
}], {
...p10[0],
barDir: 'col',
showLegend: false,
showValue: true,
dataLabelPosition: 'outEnd',
dataLabelFontSize: 9,
dataLabelColor: '1A1633',
dataLabelFormatCode: '0.00',
showCatAxisTitle: false,
catAxisLabelFontSize: 9,
valAxisMinVal: 20,
valAxisMaxVal: 30,
valAxisMajorUnit: 2,
showValAxisTitle: true,
valAxisTitle: 'BLEU (newstest2014)',
valAxisLabelFontSize: 8,
valAxisTitleFontSize: 9,
chartColors: ['9B97B8', '9B97B8', '9B97B8', '9B97B8', '7C3AED', '4338CA', 'F59E0B'],
valAxisLineShow: false,
serAxisLineShow: false,
showCatName: true
});
// Slides 11-13
await html2pptx('slides/11-generalization.html', pptx);
await html2pptx('slides/12-interpretability.html', pptx);
await html2pptx('slides/13-conclusion.html', pptx);
await pptx.writeFile({ fileName: 'output/attention-is-all-you-need.pptx' });
console.log('Saved output/attention-is-all-you-need.pptx');
}
build().catch(e => { console.error(e); process.exit(1); });
@@ -0,0 +1,17 @@
#!/usr/bin/env python3
"""Post-process deck: remove duplicate mid-paragraph <a:pPr> elements that
pptxgenjs emits when list items contain multiple inline-formatting runs.
The duplicate (later) pPr carries buNone, which makes LibreOffice drop the
bullet glyph. Keeping only the first pPr per <a:p> restores uniform bullets."""
import re, glob, sys
pattern = re.compile(r'(</a:r>)\s*<a:pPr\b.*?</a:pPr>', re.S)
total = 0
for path in glob.glob('deckbuild/ppt/slides/slide*.xml'):
xml = open(path, encoding='utf-8').read()
fixed, n = pattern.subn(r'\1', xml)
if n:
open(path, 'w', encoding='utf-8').write(fixed)
total += n
print(f'{path}: removed {n} duplicate pPr')
print(f'total removed: {total}')
@@ -0,0 +1,15 @@
const sharp = require('sharp');
// Cover background: deep indigo -> violet diagonal gradient, rasterized (CSS gradients not supported)
const cover = `<svg xmlns="http://www.w3.org/2000/svg" width="1920" height="1080">
<defs>
<linearGradient id="g" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#14102E"/>
<stop offset="55%" style="stop-color:#2A1668"/>
<stop offset="100%" style="stop-color:#5B21B6"/>
</linearGradient>
</defs>
<rect width="100%" height="100%" fill="url(#g)"/>
</svg>`;
sharp(Buffer.from(cover)).png().toFile('assets/cover-bg.png').then(() => console.log('assets ok'));
Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

@@ -0,0 +1,805 @@
{
"name": "workspace",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "workspace",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"playwright": "^1.62.1",
"pptxgenjs": "^4.0.1",
"sharp": "^0.35.3"
}
},
"node_modules/@emnapi/runtime": {
"version": "1.11.3",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz",
"integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==",
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@img/colour": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",
"integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/@img/sharp-darwin-arm64": {
"version": "0.35.3",
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.3.tgz",
"integrity": "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==",
"cpu": [
"arm64"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=20.9.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-darwin-arm64": "1.3.2"
}
},
"node_modules/@img/sharp-darwin-x64": {
"version": "0.35.3",
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.3.tgz",
"integrity": "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==",
"cpu": [
"x64"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=20.9.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-darwin-x64": "1.3.2"
}
},
"node_modules/@img/sharp-freebsd-wasm32": {
"version": "0.35.3",
"resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.3.tgz",
"integrity": "sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==",
"license": "Apache-2.0",
"optional": true,
"os": [
"freebsd"
],
"dependencies": {
"@img/sharp-wasm32": "0.35.3"
},
"engines": {
"node": ">=20.9.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-libvips-darwin-arm64": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.2.tgz",
"integrity": "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==",
"cpu": [
"arm64"
],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"darwin"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-libvips-darwin-x64": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.2.tgz",
"integrity": "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==",
"cpu": [
"x64"
],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"darwin"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-libvips-linux-arm": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.2.tgz",
"integrity": "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==",
"cpu": [
"arm"
],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"linux"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-libvips-linux-arm64": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.2.tgz",
"integrity": "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==",
"cpu": [
"arm64"
],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"linux"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-libvips-linux-ppc64": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.2.tgz",
"integrity": "sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==",
"cpu": [
"ppc64"
],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"linux"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-libvips-linux-riscv64": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.2.tgz",
"integrity": "sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==",
"cpu": [
"riscv64"
],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"linux"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-libvips-linux-s390x": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.2.tgz",
"integrity": "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==",
"cpu": [
"s390x"
],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"linux"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-libvips-linux-x64": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.2.tgz",
"integrity": "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==",
"cpu": [
"x64"
],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"linux"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.2.tgz",
"integrity": "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==",
"cpu": [
"arm64"
],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"linux"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.2.tgz",
"integrity": "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==",
"cpu": [
"x64"
],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
"linux"
],
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-linux-arm": {
"version": "0.35.3",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.3.tgz",
"integrity": "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==",
"cpu": [
"arm"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=20.9.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linux-arm": "1.3.2"
}
},
"node_modules/@img/sharp-linux-arm64": {
"version": "0.35.3",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.3.tgz",
"integrity": "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==",
"cpu": [
"arm64"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=20.9.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linux-arm64": "1.3.2"
}
},
"node_modules/@img/sharp-linux-ppc64": {
"version": "0.35.3",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.3.tgz",
"integrity": "sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==",
"cpu": [
"ppc64"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=20.9.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linux-ppc64": "1.3.2"
}
},
"node_modules/@img/sharp-linux-riscv64": {
"version": "0.35.3",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.3.tgz",
"integrity": "sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==",
"cpu": [
"riscv64"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=20.9.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linux-riscv64": "1.3.2"
}
},
"node_modules/@img/sharp-linux-s390x": {
"version": "0.35.3",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.3.tgz",
"integrity": "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==",
"cpu": [
"s390x"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=20.9.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linux-s390x": "1.3.2"
}
},
"node_modules/@img/sharp-linux-x64": {
"version": "0.35.3",
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.3.tgz",
"integrity": "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==",
"cpu": [
"x64"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=20.9.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linux-x64": "1.3.2"
}
},
"node_modules/@img/sharp-linuxmusl-arm64": {
"version": "0.35.3",
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.3.tgz",
"integrity": "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==",
"cpu": [
"arm64"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=20.9.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linuxmusl-arm64": "1.3.2"
}
},
"node_modules/@img/sharp-linuxmusl-x64": {
"version": "0.35.3",
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.3.tgz",
"integrity": "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==",
"cpu": [
"x64"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=20.9.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-libvips-linuxmusl-x64": "1.3.2"
}
},
"node_modules/@img/sharp-wasm32": {
"version": "0.35.3",
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.3.tgz",
"integrity": "sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==",
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
"optional": true,
"dependencies": {
"@emnapi/runtime": "^1.11.1"
},
"engines": {
"node": ">=20.9.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-webcontainers-wasm32": {
"version": "0.35.3",
"resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.3.tgz",
"integrity": "sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==",
"cpu": [
"wasm32"
],
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"@img/sharp-wasm32": "0.35.3"
},
"engines": {
"node": ">=20.9.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-win32-arm64": {
"version": "0.35.3",
"resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.3.tgz",
"integrity": "sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==",
"cpu": [
"arm64"
],
"license": "Apache-2.0 AND LGPL-3.0-or-later",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=20.9.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-win32-ia32": {
"version": "0.35.3",
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.3.tgz",
"integrity": "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==",
"cpu": [
"ia32"
],
"license": "Apache-2.0 AND LGPL-3.0-or-later",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": "^20.9.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-win32-x64": {
"version": "0.35.3",
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.3.tgz",
"integrity": "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==",
"cpu": [
"x64"
],
"license": "Apache-2.0 AND LGPL-3.0-or-later",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=20.9.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
}
},
"node_modules/@types/node": {
"version": "22.20.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz",
"integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
}
},
"node_modules/core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
"license": "MIT"
},
"node_modules/detect-libc": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
"license": "Apache-2.0",
"engines": {
"node": ">=8"
}
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/https": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz",
"integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg==",
"license": "ISC"
},
"node_modules/image-size": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz",
"integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==",
"license": "MIT",
"dependencies": {
"queue": "6.0.2"
},
"bin": {
"image-size": "bin/image-size.js"
},
"engines": {
"node": ">=16.x"
}
},
"node_modules/immediate": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
"license": "MIT"
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC"
},
"node_modules/isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
"license": "MIT"
},
"node_modules/jszip": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
"license": "(MIT OR GPL-3.0-or-later)",
"dependencies": {
"lie": "~3.3.0",
"pako": "~1.0.2",
"readable-stream": "~2.3.6",
"setimmediate": "^1.0.5"
}
},
"node_modules/lie": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
"license": "MIT",
"dependencies": {
"immediate": "~3.0.5"
}
},
"node_modules/pako": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
"license": "(MIT AND Zlib)"
},
"node_modules/playwright": {
"version": "1.62.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.62.1.tgz",
"integrity": "sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==",
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.62.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=20"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/playwright-core": {
"version": "1.62.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.62.1.tgz",
"integrity": "sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==",
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=20"
}
},
"node_modules/pptxgenjs": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/pptxgenjs/-/pptxgenjs-4.0.1.tgz",
"integrity": "sha512-TeJISr8wouAuXw4C1F/mC33xbZs/FuEG6nH9FG1Zj+nuPcGMP5YRHl6X+j3HSUnS1f3at6k75ZZXPMZlA5Lj9A==",
"license": "MIT",
"dependencies": {
"@types/node": "^22.8.1",
"https": "^1.0.0",
"image-size": "^1.2.1",
"jszip": "^3.10.1"
}
},
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
"license": "MIT"
},
"node_modules/queue": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz",
"integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==",
"license": "MIT",
"dependencies": {
"inherits": "~2.0.3"
}
},
"node_modules/readable-stream": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"license": "MIT",
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"license": "MIT"
},
"node_modules/semver": {
"version": "7.8.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
"integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
"license": "MIT"
},
"node_modules/sharp": {
"version": "0.35.3",
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.3.tgz",
"integrity": "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==",
"license": "Apache-2.0",
"dependencies": {
"@img/colour": "^1.1.0",
"detect-libc": "^2.1.2",
"semver": "^7.8.5"
},
"engines": {
"node": ">=20.9.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
"@img/sharp-darwin-arm64": "0.35.3",
"@img/sharp-darwin-x64": "0.35.3",
"@img/sharp-freebsd-wasm32": "0.35.3",
"@img/sharp-libvips-darwin-arm64": "1.3.2",
"@img/sharp-libvips-darwin-x64": "1.3.2",
"@img/sharp-libvips-linux-arm": "1.3.2",
"@img/sharp-libvips-linux-arm64": "1.3.2",
"@img/sharp-libvips-linux-ppc64": "1.3.2",
"@img/sharp-libvips-linux-riscv64": "1.3.2",
"@img/sharp-libvips-linux-s390x": "1.3.2",
"@img/sharp-libvips-linux-x64": "1.3.2",
"@img/sharp-libvips-linuxmusl-arm64": "1.3.2",
"@img/sharp-libvips-linuxmusl-x64": "1.3.2",
"@img/sharp-linux-arm": "0.35.3",
"@img/sharp-linux-arm64": "0.35.3",
"@img/sharp-linux-ppc64": "0.35.3",
"@img/sharp-linux-riscv64": "0.35.3",
"@img/sharp-linux-s390x": "0.35.3",
"@img/sharp-linux-x64": "0.35.3",
"@img/sharp-linuxmusl-arm64": "0.35.3",
"@img/sharp-linuxmusl-x64": "0.35.3",
"@img/sharp-webcontainers-wasm32": "0.35.3",
"@img/sharp-win32-arm64": "0.35.3",
"@img/sharp-win32-ia32": "0.35.3",
"@img/sharp-win32-x64": "0.35.3"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
}
}
},
"node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"license": "MIT",
"dependencies": {
"safe-buffer": "~5.1.0"
}
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD",
"optional": true
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"license": "MIT"
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT"
}
}
}
@@ -0,0 +1,18 @@
{
"name": "workspace",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"type": "commonjs",
"dependencies": {
"playwright": "^1.62.1",
"pptxgenjs": "^4.0.1",
"sharp": "^0.35.3"
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head><style>
html { background: #14102E; }
body { width: 720pt; height: 405pt; margin: 0; padding: 0; font-family: Arial, sans-serif; display: flex; background-image: url('../assets/cover-bg.png'); }
.left { width: 440pt; margin: 44pt 0 44pt 48pt; display: flex; flex-direction: column; }
.kicker { font-size: 10pt; color: #F59E0B; font-weight: bold; letter-spacing: 2pt; margin: 0 0 14pt 0; }
h1 { font-size: 40pt; color: #FFFFFF; margin: 0 0 14pt 0; line-height: 1.1; }
.sub { font-size: 14pt; color: #DDD6FE; margin: 0 0 26pt 0; line-height: 1.35; }
.rule { background: #F59E0B; height: 4pt; width: 72pt; margin: 0 0 26pt 0; }
.authors { font-size: 11pt; color: #EDE9FE; margin: 0 0 6pt 0; line-height: 1.4; }
.affil { font-size: 9.5pt; color: #B8AEE8; margin: 0; }
.card { width: 156pt; margin: 46pt 0 0 16pt; background: #FFFFFF; border-radius: 10pt; padding: 12pt; box-shadow: 4px 4px 18px rgba(0,0,0,0.4); height: 256pt; }
.card img { height: 200pt; display: block; margin: 0 auto; }
.cardcap { font-size: 8pt; color: #5B5876; text-align: center; margin: 8pt 0 0 0; }
</style></head>
<body>
<div class="left">
<p class="kicker">NEURIPS 2017 &middot; ARXIV:1706.03762</p>
<h1>Attention Is<br>All You Need</h1>
<p class="sub">The Transformer: sequence transduction based entirely on attention &mdash; no recurrence, no convolution.</p>
<div class="rule"></div>
<p class="authors">Ashish Vaswani &middot; Noam Shazeer &middot; Niki Parmar &middot; Jakob Uszkoreit<br>Llion Jones &middot; Aidan N. Gomez &middot; &#x141;ukasz Kaiser &middot; Illia Polosukhin</p>
<p class="affil">Google Brain &middot; Google Research &middot; University of Toronto</p>
</div>
<div class="card">
<img src="../source_visuals/fig1-03.png">
<p class="cardcap">The Transformer &mdash; model architecture<br>Figure 1, paper p. 3</p>
</div>
</body>
</html>
@@ -0,0 +1,46 @@
<!DOCTYPE html>
<html>
<head><style>
html { background: #FFFFFF; }
body { width: 720pt; height: 405pt; margin: 0; padding: 0; background: #FAFAFF; font-family: Arial, sans-serif; display: flex; }
.bar { width: 10pt; background: #4338CA; }
.wrap { width: 644pt; margin: 26pt 32pt 22pt 34pt; display: flex; flex-direction: column; }
.kicker { font-size: 9.5pt; color: #7C3AED; font-weight: bold; letter-spacing: 2pt; margin: 0 0 4pt 0; }
h1 { font-size: 23pt; color: #1A1633; margin: 0 0 8pt 0; }
.rule { background: #F59E0B; height: 3pt; width: 60pt; margin: 0 0 14pt 0; }
.cols { display: flex; }
.txt { width: 400pt; }
ul { margin: 0; padding-left: 14pt; font-size: 11pt; color: #2A2740; line-height: 1.4; }
li { margin-bottom: 9pt; }
.stat { width: 200pt; margin-left: 30pt; background: #EEF0FF; border-radius: 10pt; padding: 14pt 16pt; }
.stat p { margin: 0; }
.bignum { font-size: 26pt; color: #4338CA; font-weight: bold; margin: 0; }
.statlab { font-size: 9.5pt; color: #5B5876; margin: 2pt 0 12pt 0; }
.foot { font-size: 8pt; color: #8A86A3; margin-top: auto; margin-bottom: 0; }
</style></head>
<body>
<div class="bar"></div>
<div class="wrap">
<p class="kicker">01 &middot; BACKGROUND</p>
<h1>The Problem: The Limits of Recurrence</h1>
<div class="rule"></div>
<div class="cols">
<div class="txt">
<ul>
<li>Sequence transduction (e.g., machine translation) was dominated by <b>recurrent</b> (LSTM / GRU) and <b>convolutional</b> encoder&ndash;decoder models.</li>
<li>RNNs compute hidden states <b>one position at a time</b>: inherently sequential, so training cannot parallelize across positions in a sequence.</li>
<li>Signals between distant positions must travel a path of <b>O(n) operations</b> &mdash; the longer the path, the harder long-range dependencies are to learn.</li>
<li>Attention mechanisms already existed, but only as an <b>accessory</b> layered on top of RNNs or CNNs.</li>
</ul>
</div>
<div class="stat">
<p class="bignum">O(n)</p>
<p class="statlab">sequential operations per layer in a recurrent network</p>
<p class="bignum">O(n)</p>
<p class="statlab">maximum path length between any two positions</p>
</div>
</div>
<p class="foot">Source: Vaswani et al., &ldquo;Attention Is All You Need&rdquo;, &sect;1 Introduction (paper p. 1&ndash;2).</p>
</div>
</body>
</html>
@@ -0,0 +1,48 @@
<!DOCTYPE html>
<html>
<head><style>
html { background: #FFFFFF; }
body { width: 720pt; height: 405pt; margin: 0; padding: 0; background: #FAFAFF; font-family: Arial, sans-serif; display: flex; }
.bar { width: 10pt; background: #4338CA; }
.wrap { width: 644pt; margin: 26pt 32pt 22pt 34pt; display: flex; flex-direction: column; }
.kicker { font-size: 9.5pt; color: #7C3AED; font-weight: bold; letter-spacing: 2pt; margin: 0 0 4pt 0; }
h1 { font-size: 23pt; color: #1A1633; margin: 0 0 8pt 0; }
.rule { background: #F59E0B; height: 3pt; width: 60pt; margin: 0 0 16pt 0; }
.cards { display: flex; }
.card { width: 198pt; margin-right: 14pt; background: #4338CA; border-radius: 10pt; padding: 14pt 14pt 16pt 14pt; }
.card p { margin: 0; }
.ct { font-size: 13pt; color: #FFFFFF; font-weight: bold; margin: 0 0 6pt 0; }
.cd { font-size: 9.5pt; color: #DDD6FE; line-height: 1.35; }
ul { margin: 16pt 0 0 0; padding-left: 14pt; font-size: 11pt; color: #2A2740; line-height: 1.4; }
li { margin-bottom: 8pt; }
.foot { font-size: 8pt; color: #8A86A3; margin-top: auto; margin-bottom: 0; }
</style></head>
<body>
<div class="bar"></div>
<div class="wrap">
<p class="kicker">02 &middot; KEY IDEA</p>
<h1>The Transformer: Attention Is All You Need</h1>
<div class="rule"></div>
<div class="cards">
<div class="card">
<p class="ct">No Recurrence</p>
<p class="cd">Sequential RNN hidden states are removed entirely &mdash; positions are processed in parallel.</p>
</div>
<div class="card">
<p class="ct">No Convolution</p>
<p class="cd">No fixed-width kernels; every position can connect to every other position directly.</p>
</div>
<div class="card" style="margin-right: 0;">
<p class="ct">Self-Attention Only</p>
<p class="cd">Multi-headed self-attention computes all representations, in both encoder and decoder.</p>
</div>
</div>
<ul>
<li>First sequence transduction model based <b>entirely on attention</b>, replacing recurrent layers with multi-headed self-attention.</li>
<li>Highly parallelizable &mdash; reaches a new state of the art in translation with a <b>fraction of the training cost</b>.</li>
<li>Generalizes beyond translation (e.g., English constituency parsing) and yields interpretable attention patterns.</li>
</ul>
<p class="foot">Source: Vaswani et al., Abstract and &sect;1 (paper p. 1&ndash;2).</p>
</div>
</body>
</html>
@@ -0,0 +1,44 @@
<!DOCTYPE html>
<html>
<head><style>
html { background: #FFFFFF; }
body { width: 720pt; height: 405pt; margin: 0; padding: 0; background: #FAFAFF; font-family: Arial, sans-serif; display: flex; }
.bar { width: 10pt; background: #4338CA; }
.wrap { width: 644pt; margin: 26pt 32pt 22pt 34pt; display: flex; flex-direction: column; }
.kicker { font-size: 9.5pt; color: #7C3AED; font-weight: bold; letter-spacing: 2pt; margin: 0 0 4pt 0; }
h1 { font-size: 23pt; color: #1A1633; margin: 0 0 8pt 0; }
.rule { background: #F59E0B; height: 3pt; width: 60pt; margin: 0 0 12pt 0; }
.cols { display: flex; }
.txt { width: 372pt; }
ul { margin: 0; padding-left: 14pt; font-size: 10.5pt; color: #2A2740; line-height: 1.38; }
li { margin-bottom: 8pt; }
.fig { width: 240pt; margin-left: 26pt; background: #FFFFFF; border: 1px solid #E0DFF0; border-radius: 10pt; padding: 10pt; }
.fig img { height: 262pt; display: block; margin: 0 auto; }
.figcap { font-size: 8pt; color: #5B5876; text-align: center; margin: 6pt 0 0 0; }
.foot { font-size: 8pt; color: #8A86A3; margin-top: auto; margin-bottom: 0; }
</style></head>
<body>
<div class="bar"></div>
<div class="wrap">
<p class="kicker">03 &middot; METHOD</p>
<h1>Model Architecture: Stacked Self-Attention</h1>
<div class="rule"></div>
<div class="cols">
<div class="txt">
<ul>
<li><b>Encoder&ndash;decoder</b> structure built from stacked self-attention and point-wise, fully connected layers (left and right halves of Figure 1).</li>
<li><b>Encoder</b>: N = 6 identical layers &mdash; each with multi-head self-attention, then a position-wise feed-forward network.</li>
<li><b>Decoder</b>: N = 6 layers; adds a <b>masked</b> multi-head self-attention sub-layer (positions only attend to earlier positions) plus attention over the encoder output.</li>
<li>A <b>residual connection</b> wraps every sub-layer, followed by <b>layer normalization</b>.</li>
<li>Inputs/outputs are embedded (d_model = 512) and combined with <b>positional encodings</b>, since the model has no notion of order on its own.</li>
</ul>
</div>
<div class="fig">
<img src="../source_visuals/fig1-03.png">
<p class="figcap">Encoder (left) and decoder (right) stacks.<br>Figure 1, paper p. 3.</p>
</div>
</div>
<p class="foot">Visual: Figure 1, &ldquo;The Transformer - model architecture&rdquo; (paper p. 3); text: &sect;3.1 (paper p. 3).</p>
</div>
</body>
</html>
@@ -0,0 +1,48 @@
<!DOCTYPE html>
<html>
<head><style>
html { background: #FFFFFF; }
body { width: 720pt; height: 405pt; margin: 0; padding: 0; background: #FAFAFF; font-family: Arial, sans-serif; display: flex; }
.bar { width: 10pt; background: #4338CA; }
.wrap { width: 644pt; margin: 26pt 32pt 22pt 34pt; display: flex; flex-direction: column; }
.kicker { font-size: 9.5pt; color: #7C3AED; font-weight: bold; letter-spacing: 2pt; margin: 0 0 4pt 0; }
h1 { font-size: 23pt; color: #1A1633; margin: 0 0 8pt 0; }
.rule { background: #F59E0B; height: 3pt; width: 60pt; margin: 0 0 12pt 0; }
.cols { display: flex; }
.txt { width: 300pt; }
.formula { background: #1A1633; border-radius: 8pt; padding: 10pt 12pt; margin: 0 0 12pt 0; }
.formula p { font-family: Courier New, monospace; font-size: 10.5pt; color: #FDE68A; margin: 0; text-align: center; }
ul { margin: 0; padding-left: 14pt; font-size: 10.5pt; color: #2A2740; line-height: 1.38; }
li { margin-bottom: 8pt; }
.fig { width: 312pt; margin-left: 24pt; background: #FFFFFF; border: 1px solid #E0DFF0; border-radius: 10pt; padding: 10pt; }
.fig img { width: 292pt; display: block; margin: 0 auto; }
.figcap { font-size: 8pt; color: #5B5876; text-align: center; margin: 6pt 0 0 0; }
.foot { font-size: 8pt; color: #8A86A3; margin-top: auto; margin-bottom: 0; }
</style></head>
<body>
<div class="bar"></div>
<div class="wrap">
<p class="kicker">04 &middot; METHOD</p>
<h1>Scaled Dot-Product &amp; Multi-Head Attention</h1>
<div class="rule"></div>
<div class="cols">
<div class="txt">
<div class="formula">
<p>Attention(Q,K,V) = softmax(QK^T / &radic;d_k)V</p>
</div>
<ul>
<li>Attention maps a <b>query</b> and <b>key&ndash;value</b> pairs to an output: a weighted sum of values, weights from query&ndash;key compatibility.</li>
<li>Dot products are scaled by <b>1/&radic;d_k</b> &mdash; for large d_k they would push softmax into tiny-gradient regions.</li>
<li><b>Multi-head</b>: queries, keys, values are linearly projected <b>h = 8</b> times (to d_k = d_v = 64), attention runs in parallel, outputs are concatenated and re-projected.</li>
<li>Different heads jointly attend to information from <b>different representation subspaces</b> at different positions.</li>
</ul>
</div>
<div class="fig">
<img src="../source_visuals/fig2-04.png">
<p class="figcap">(left) Scaled Dot-Product Attention; (right) Multi-Head Attention &mdash; several attention layers in parallel. Figure 2, paper p. 4.</p>
</div>
</div>
<p class="foot">Visual: Figure 2 (paper p. 4); formula and text: &sect;3.2 (paper p. 4&ndash;5).</p>
</div>
</body>
</html>
@@ -0,0 +1,55 @@
<!DOCTYPE html>
<html>
<head><style>
html { background: #FFFFFF; }
body { width: 720pt; height: 405pt; margin: 0; padding: 0; background: #FAFAFF; font-family: Arial, sans-serif; display: flex; }
.bar { width: 10pt; background: #4338CA; }
.wrap { width: 644pt; margin: 26pt 32pt 22pt 34pt; display: flex; flex-direction: column; }
.kicker { font-size: 9.5pt; color: #7C3AED; font-weight: bold; letter-spacing: 2pt; margin: 0 0 4pt 0; }
h1 { font-size: 23pt; color: #1A1633; margin: 0 0 8pt 0; }
.rule { background: #F59E0B; height: 3pt; width: 60pt; margin: 0 0 14pt 0; }
.grid { display: flex; margin-bottom: 12pt; }
.card { width: 300pt; background: #FFFFFF; border: 1px solid #E0DFF0; border-left: 5pt solid #7C3AED; border-radius: 8pt; padding: 10pt 14pt; }
.card p { margin: 0; }
.ct { font-size: 12pt; color: #4338CA; font-weight: bold; margin: 0 0 5pt 0; }
.cd { font-size: 10pt; color: #2A2740; line-height: 1.35; }
.gap { width: 16pt; }
.note { background: #FDF3E0; border-left: 5pt solid #F59E0B; border-radius: 6pt; padding: 9pt 14pt; margin-top: auto; }
.note p { font-size: 10pt; color: #6B4E12; margin: 0; line-height: 1.35; }
.foot { font-size: 8pt; color: #8A86A3; margin: 8pt 0 0 0; }
</style></head>
<body>
<div class="bar"></div>
<div class="wrap">
<p class="kicker">05 &middot; METHOD</p>
<h1>Anatomy of the Building Blocks</h1>
<div class="rule"></div>
<div class="grid">
<div class="card">
<p class="ct">Multi-Head Attention</p>
<p class="cd">h = 8 parallel heads; d_k = d_v = d_model/h = 64, so total compute stays close to single-head attention at full dimension.</p>
</div>
<div class="gap"></div>
<div class="card">
<p class="ct">Position-wise Feed-Forward</p>
<p class="cd">FFN(x) = max(0, xW&#x2081;+b&#x2081;)W&#x2082;+b&#x2082;, applied identically to every position; inner dimension d_ff = 2048.</p>
</div>
</div>
<div class="grid">
<div class="card">
<p class="ct">Add &amp; Norm</p>
<p class="cd">Residual connection around each sub-layer: LayerNorm(x + Sublayer(x)); all sub-layers and embeddings output d_model = 512.</p>
</div>
<div class="gap"></div>
<div class="card">
<p class="ct">Positional Encoding</p>
<p class="cd">Sine/cosine waves of different frequencies injected at the inputs, so the model can use token order; learned alternatives worked equally well.</p>
</div>
</div>
<div class="note">
<p><b>Two configurations:</b> base &mdash; d_model 512, d_ff 2048, h 8, dropout 0.1 &nbsp;|&nbsp; big &mdash; d_model 1024, d_ff 4096, h 16, dropout 0.3.</p>
</div>
<p class="foot">Source: &sect;3.2&ndash;3.5 (paper p. 4&ndash;5); &sect;5.4 and Table 3 footnotes (paper p. 7&ndash;8).</p>
</div>
</body>
</html>
@@ -0,0 +1,44 @@
<!DOCTYPE html>
<html>
<head><style>
html { background: #FFFFFF; }
body { width: 720pt; height: 405pt; margin: 0; padding: 0; background: #FAFAFF; font-family: Arial, sans-serif; display: flex; }
.bar { width: 10pt; background: #4338CA; }
.wrap { width: 644pt; margin: 26pt 32pt 22pt 34pt; display: flex; flex-direction: column; }
.kicker { font-size: 9.5pt; color: #7C3AED; font-weight: bold; letter-spacing: 2pt; margin: 0 0 4pt 0; }
h1 { font-size: 23pt; color: #1A1633; margin: 0 0 8pt 0; }
.rule { background: #F59E0B; height: 3pt; width: 60pt; margin: 0 0 12pt 0; }
.cols { display: flex; }
.txt { width: 290pt; }
ul { margin: 0; padding-left: 14pt; font-size: 10.5pt; color: #2A2740; line-height: 1.38; }
li { margin-bottom: 9pt; }
.tabwrap { width: 330pt; margin-left: 22pt; }
.tablab { font-size: 9pt; color: #5B5876; margin: 0 0 4pt 0; font-weight: bold; }
.tabcap { font-size: 8pt; color: #5B5876; margin: 6pt 0 0 0; }
.foot { font-size: 8pt; color: #8A86A3; margin-top: auto; margin-bottom: 0; }
</style></head>
<body>
<div class="bar"></div>
<div class="wrap">
<p class="kicker">06 &middot; MOTIVATION</p>
<h1>Why Self-Attention Wins</h1>
<div class="rule"></div>
<div class="cols">
<div class="txt">
<ul>
<li><b>Parallelization:</b> a self-attention layer needs only O(1) sequential operations vs. O(n) for recurrence &mdash; training uses all positions at once.</li>
<li><b>Long-range learning:</b> any two positions connect through a path of <b>constant length O(1)</b>, instead of O(n) for RNNs or O(log_k n) for dilated convolutions.</li>
<li><b>Cost:</b> per-layer complexity O(n&sup2;&middot;d) beats recurrence O(n&middot;d&sup2;) whenever n &lt; d &mdash; the common case with word-piece / BPE representations.</li>
<li><b>Interpretability:</b> attention distributions expose what the model attends to; heads learn distinct, task-like behaviors.</li>
</ul>
</div>
<div class="tabwrap">
<p class="tablab">PER-LAYER COMPLEXITY AND PATH LENGTHS</p>
<div id="table-complexity" class="placeholder" style="width: 330pt; height: 150pt;"></div>
<p class="tabcap">n = sequence length, d = representation dimension, k = conv. kernel width, r = neighborhood. Adapted from Table 1, paper p. 6.</p>
</div>
</div>
<p class="foot">Table: adapted from Table 1 (paper p. 6); text: &sect;4 (paper p. 6&ndash;7).</p>
</div>
</body>
</html>
@@ -0,0 +1,54 @@
<!DOCTYPE html>
<html>
<head><style>
html { background: #FFFFFF; }
body { width: 720pt; height: 405pt; margin: 0; padding: 0; background: #FAFAFF; font-family: Arial, sans-serif; display: flex; }
.bar { width: 10pt; background: #4338CA; }
.wrap { width: 644pt; margin: 26pt 32pt 22pt 34pt; display: flex; flex-direction: column; }
.kicker { font-size: 9.5pt; color: #7C3AED; font-weight: bold; letter-spacing: 2pt; margin: 0 0 4pt 0; }
h1 { font-size: 23pt; color: #1A1633; margin: 0 0 8pt 0; }
.rule { background: #F59E0B; height: 3pt; width: 60pt; margin: 0 0 14pt 0; }
.cols { display: flex; }
.card { width: 302pt; background: #FFFFFF; border: 1px solid #E0DFF0; border-radius: 10pt; padding: 12pt 16pt 4pt 16pt; }
.card p { margin: 0; }
.ct { font-size: 12.5pt; color: #4338CA; font-weight: bold; margin: 0 0 8pt 0; }
ul { margin: 0; padding-left: 13pt; font-size: 10pt; color: #2A2740; line-height: 1.35; }
li { margin-bottom: 7pt; }
.gap { width: 20pt; }
.mono { font-family: Courier New, monospace; font-size: 9pt; color: #4338CA; }
.hw { background: #EEF0FF; border-radius: 8pt; padding: 9pt 14pt; margin-top: 14pt; }
.hw p { font-size: 10pt; color: #2A2740; margin: 0; }
.foot { font-size: 8pt; color: #8A86A3; margin-top: auto; margin-bottom: 0; }
</style></head>
<body>
<div class="bar"></div>
<div class="wrap">
<p class="kicker">07 &middot; EXPERIMENTS</p>
<h1>Training Setup</h1>
<div class="rule"></div>
<div class="cols">
<div class="card">
<p class="ct">Data &amp; Batching</p>
<ul>
<li><b>WMT 2014 English&ndash;German</b>: 4.5M sentence pairs; <b>English&ndash;French</b>: 36M sentences.</li>
<li>Byte-pair encoding with a <b>shared source&ndash;target vocabulary of ~37K tokens</b>.</li>
<li>Batches of ~25,000 source and ~25,000 target tokens, grouped by approximate sequence length.</li>
</ul>
</div>
<div class="gap"></div>
<div class="card">
<p class="ct">Optimization &amp; Regularization</p>
<ul>
<li>Adam (&beta;&#x2081; = 0.9, &beta;&#x2082; = 0.98, &epsilon; = 10&#x207B;&#x2079;) with <b>warmup</b>: 4,000 linearly increasing steps, then inverse-square-root decay.</li>
<li>Residual dropout 0.1 (base) / 0.3 (big); attention dropout on each sub-layer.</li>
<li>Label smoothing &epsilon;_ls = 0.1 &mdash; hurts perplexity but <b>improves accuracy and BLEU</b>.</li>
</ul>
</div>
</div>
<div class="hw">
<p><b>Hardware &amp; time:</b> 8 &times; NVIDIA P100 GPUs &mdash; base model: 12 hours (100K steps); big model: 3.5 days (300K steps). Beam search with beam size 4, length penalty &alpha; = 0.6.</p>
</div>
<p class="foot">Source: &sect;5 Training (paper p. 7); &sect;6.1 (paper p. 8).</p>
</div>
</body>
</html>
@@ -0,0 +1,52 @@
<!DOCTYPE html>
<html>
<head><style>
html { background: #FFFFFF; }
body { width: 720pt; height: 405pt; margin: 0; padding: 0; background: #FAFAFF; font-family: Arial, sans-serif; display: flex; }
.bar { width: 10pt; background: #4338CA; }
.wrap { width: 644pt; margin: 26pt 32pt 22pt 34pt; display: flex; flex-direction: column; }
.kicker { font-size: 9.5pt; color: #7C3AED; font-weight: bold; letter-spacing: 2pt; margin: 0 0 4pt 0; }
h1 { font-size: 23pt; color: #1A1633; margin: 0 0 8pt 0; }
.rule { background: #F59E0B; height: 3pt; width: 60pt; margin: 0 0 12pt 0; }
.cols { display: flex; }
.fig { width: 396pt; background: #FFFFFF; border: 1px solid #E0DFF0; border-radius: 10pt; padding: 10pt; }
.fig img { width: 376pt; display: block; margin: 0 auto; }
.figcap { font-size: 8pt; color: #5B5876; text-align: center; margin: 6pt 0 0 0; }
.stats { width: 196pt; margin-left: 22pt; }
.stat { background: #4338CA; border-radius: 10pt; padding: 10pt 14pt; margin-bottom: 10pt; }
.stat p { margin: 0; }
.bignum { font-size: 22pt; color: #FFFFFF; font-weight: bold; }
.statlab { font-size: 8.5pt; color: #DDD6FE; line-height: 1.3; margin-top: 2pt; }
.stat.amber { background: #B45309; }
.foot { font-size: 8pt; color: #8A86A3; margin-top: auto; margin-bottom: 0; }
</style></head>
<body>
<div class="bar"></div>
<div class="wrap">
<p class="kicker">08 &middot; RESULTS</p>
<h1>Machine Translation: New State of the Art</h1>
<div class="rule"></div>
<div class="cols">
<div class="fig">
<img src="../source_visuals/table2-08.png">
<p class="figcap">BLEU scores and training costs on WMT 2014 EN&ndash;DE / EN&ndash;FR (newstest2014). Table 2, paper p. 8.</p>
</div>
<div class="stats">
<div class="stat">
<p class="bignum">28.4 BLEU</p>
<p class="statlab">EN&rarr;DE: &gt;2.0 above the best previously reported model, including ensembles</p>
</div>
<div class="stat">
<p class="bignum">41.8 BLEU</p>
<p class="statlab">EN&rarr;FR: best single model, at under 1/4 of the prior training cost</p>
</div>
<div class="stat amber">
<p class="bignum">3.3&times;10^18</p>
<p class="statlab">FLOPs to train the base model &mdash; yet it already beats all prior models and ensembles</p>
</div>
</div>
</div>
<p class="foot">Visual: Table 2 (paper p. 8); text: &sect;6.1 Machine Translation (paper p. 8).</p>
</div>
</body>
</html>
@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html>
<head><style>
html { background: #FFFFFF; }
body { width: 720pt; height: 405pt; margin: 0; padding: 0; background: #FAFAFF; font-family: Arial, sans-serif; display: flex; }
.bar { width: 10pt; background: #4338CA; }
.wrap { width: 644pt; margin: 26pt 32pt 22pt 34pt; display: flex; flex-direction: column; }
.kicker { font-size: 9.5pt; color: #7C3AED; font-weight: bold; letter-spacing: 2pt; margin: 0 0 4pt 0; }
h1 { font-size: 23pt; color: #1A1633; margin: 0 0 8pt 0; }
.rule { background: #F59E0B; height: 3pt; width: 60pt; margin: 0 0 10pt 0; }
.chartwrap { margin: 0 auto; }
#chart-bleu { margin: 0 auto; }
.chartcap { font-size: 9pt; color: #5B5876; text-align: center; margin: 6pt 0 0 0; }
.takeaway { background: #FDF3E0; border-left: 5pt solid #F59E0B; border-radius: 6pt; padding: 8pt 14pt; margin-top: auto; }
.takeaway p { font-size: 10pt; color: #6B4E12; margin: 0; }
.foot { font-size: 8pt; color: #8A86A3; margin: 8pt 0 0 0; }
</style></head>
<body>
<div class="bar"></div>
<div class="wrap">
<p class="kicker">09 &middot; RESULTS</p>
<h1>EN&rarr;DE BLEU: Transformer vs. Prior Art</h1>
<div class="rule"></div>
<div class="chartwrap">
<div id="chart-bleu" class="placeholder" style="width: 460pt; height: 218pt; margin: 0 auto;"></div>
<p class="chartcap">WMT 2014 English&rarr;German newstest2014 BLEU scores &mdash; values from Table 2, paper p. 8.</p>
</div>
<div class="takeaway">
<p>The big Transformer sets a new state of the art (28.4 BLEU) &mdash; more than 2.0 BLEU above the best previously reported models, including ensembles.</p>
</div>
<p class="foot">Data: Table 2 (paper p. 8); discussion: &sect;6.1 (paper p. 8).</p>
</div>
</body>
</html>
@@ -0,0 +1,51 @@
<!DOCTYPE html>
<html>
<head><style>
html { background: #FFFFFF; }
body { width: 720pt; height: 405pt; margin: 0; padding: 0; background: #FAFAFF; font-family: Arial, sans-serif; display: flex; }
.bar { width: 10pt; background: #4338CA; }
.wrap { width: 644pt; margin: 26pt 32pt 22pt 34pt; display: flex; flex-direction: column; }
.kicker { font-size: 9.5pt; color: #7C3AED; font-weight: bold; letter-spacing: 2pt; margin: 0 0 4pt 0; }
h1 { font-size: 23pt; color: #1A1633; margin: 0 0 8pt 0; }
.rule { background: #F59E0B; height: 3pt; width: 60pt; margin: 0 0 14pt 0; }
.cols { display: flex; }
.txt { width: 380pt; }
ul { margin: 0; padding-left: 14pt; font-size: 11pt; color: #2A2740; line-height: 1.4; }
li { margin-bottom: 10pt; }
.stats { width: 210pt; margin-left: 30pt; }
.stat { background: #4338CA; border-radius: 10pt; padding: 12pt 16pt; margin-bottom: 12pt; }
.stat p { margin: 0; }
.bignum { font-size: 24pt; color: #FFFFFF; font-weight: bold; }
.statlab { font-size: 9pt; color: #DDD6FE; line-height: 1.3; margin-top: 3pt; }
.foot { font-size: 8pt; color: #8A86A3; margin-top: auto; margin-bottom: 0; }
</style></head>
<body>
<div class="bar"></div>
<div class="wrap">
<p class="kicker">10 &middot; RESULTS</p>
<h1>Beyond Translation: Constituency Parsing</h1>
<div class="rule"></div>
<div class="cols">
<div class="txt">
<ul>
<li>A <b>4-layer Transformer</b> was trained on English constituency parsing (Wall Street Journal portion of the Penn Treebank, ~40K training sentences).</li>
<li>With almost <b>no task-specific tuning</b>, it outperforms the BerkeleyParser &mdash; even when trained only on the WSJ training set.</li>
<li>In the <b>semi-supervised</b> setting (with a ~17M-sentence high-confidence corpus) it beats all previously reported models <b>except the Recurrent Neural Network Grammar</b>.</li>
<li>The same architecture generalizes across tasks without structural changes.</li>
</ul>
</div>
<div class="stats">
<div class="stat">
<p class="bignum">91.3 F1</p>
<p class="statlab">WSJ-only, discriminative setting (Section 23 of WSJ)</p>
</div>
<div class="stat">
<p class="bignum">92.7 F1</p>
<p class="statlab">Semi-supervised setting &mdash; above every prior model except RNNG</p>
</div>
</div>
</div>
<p class="foot">Source: Table 4 (paper p. 10) and &sect;6.2 / &ldquo;English Constituency Parsing&rdquo; (paper p. 9&ndash;10).</p>
</div>
</body>
</html>
@@ -0,0 +1,47 @@
<!DOCTYPE html>
<html>
<head><style>
html { background: #FFFFFF; }
body { width: 720pt; height: 405pt; margin: 0; padding: 0; background: #FAFAFF; font-family: Arial, sans-serif; display: flex; }
.bar { width: 10pt; background: #4338CA; }
.wrap { width: 644pt; margin: 26pt 32pt 22pt 34pt; display: flex; flex-direction: column; }
.kicker { font-size: 9.5pt; color: #7C3AED; font-weight: bold; letter-spacing: 2pt; margin: 0 0 4pt 0; }
h1 { font-size: 23pt; color: #1A1633; margin: 0 0 8pt 0; }
.rule { background: #F59E0B; height: 3pt; width: 60pt; margin: 0 0 12pt 0; }
.cols { display: flex; }
.txt { width: 282pt; }
ul { margin: 0; padding-left: 14pt; font-size: 10.5pt; color: #2A2740; line-height: 1.38; }
li { margin-bottom: 9pt; }
.quote { background: #EEF0FF; border-left: 4pt solid #7C3AED; border-radius: 6pt; padding: 8pt 12pt; margin-top: 10pt; }
.quote p { font-size: 9.5pt; color: #4338CA; font-style: italic; margin: 0; line-height: 1.35; }
.fig { width: 330pt; margin-left: 24pt; background: #FFFFFF; border: 1px solid #E0DFF0; border-radius: 10pt; padding: 10pt; }
.fig img { width: 310pt; display: block; margin: 0 auto; }
.figcap { font-size: 8pt; color: #5B5876; text-align: center; margin: 6pt 0 0 0; }
.foot { font-size: 8pt; color: #8A86A3; margin-top: auto; margin-bottom: 0; }
</style></head>
<body>
<div class="bar"></div>
<div class="wrap">
<p class="kicker">11 &middot; RESULTS</p>
<h1>Interpretability: What Attention Sees</h1>
<div class="rule"></div>
<div class="cols">
<div class="txt">
<ul>
<li>Individual attention heads learn to perform <b>different tasks</b>; many exhibit behavior tied to the syntactic and semantic structure of sentences.</li>
<li>In this example (encoder self-attention, layer 5 of 6), many heads attend to a <b>distant dependency of the verb &ldquo;making&rdquo;</b>, completing the phrase &ldquo;making&hellip;more difficult&rdquo;.</li>
<li>Other heads are involved in <b>anaphora resolution</b> (Figure 4 of the paper).</li>
</ul>
<div class="quote">
<p>&ldquo;Not only do individual attention heads clearly learn to perform different tasks, many appear to exhibit behavior related to the syntactic and semantic structure of the sentences.&rdquo; &mdash; &sect;4, p. 7</p>
</div>
</div>
<div class="fig">
<img src="../source_visuals/fig3-13.png">
<p class="figcap">Attention for the word &ldquo;making&rdquo;; colors = different heads. Figure 3, paper p. 13.</p>
</div>
</div>
<p class="foot">Visual: Figure 3 (paper p. 13); text: &sect;4 (paper p. 7) and Appendix (paper p. 13).</p>
</div>
</body>
</html>
@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<head><style>
html { background: #14102E; }
body { width: 720pt; height: 405pt; margin: 0; padding: 0; font-family: Arial, sans-serif; display: flex; background-image: url('../assets/cover-bg.png'); }
.wrap { width: 620pt; margin: 30pt 50pt 24pt 50pt; display: flex; flex-direction: column; }
.kicker { font-size: 9.5pt; color: #F59E0B; font-weight: bold; letter-spacing: 2pt; margin: 0 0 4pt 0; }
h1 { font-size: 24pt; color: #FFFFFF; margin: 0 0 8pt 0; }
.rule { background: #F59E0B; height: 3pt; width: 60pt; margin: 0 0 14pt 0; }
ul { margin: 0; padding-left: 15pt; font-size: 11pt; color: #EDE9FE; line-height: 1.42; }
li { margin-bottom: 8pt; }
.quote { background: #3B2380; border-left: 4pt solid #F59E0B; border-radius: 6pt; padding: 10pt 14pt; margin-top: auto; }
.quote p { font-size: 10pt; color: #DDD6FE; font-style: italic; margin: 0; line-height: 1.4; }
.foot { font-size: 8pt; color: #B8AEE8; margin: 10pt 0 0 0; }
</style></head>
<body>
<div class="wrap">
<p class="kicker">12 &middot; CONCLUSION</p>
<h1>Conclusion &amp; Legacy</h1>
<div class="rule"></div>
<ul>
<li>The <b>Transformer</b>: the first sequence transduction model based entirely on attention &mdash; multi-headed self-attention replaces recurrent layers in encoder&ndash;decoder architectures.</li>
<li>Trains <b>significantly faster</b> than recurrent or convolutional architectures thanks to full parallelization.</li>
<li>New state of the art on <b>WMT 2014 EN&rarr;DE (28.4 BLEU)</b> and <b>EN&rarr;FR (41.8 BLEU)</b>, at a fraction of the previous training cost.</li>
<li>The architecture became the foundation of modern large language models (BERT, GPT, and their successors) &mdash; attention really was all you needed.</li>
</ul>
<div class="quote">
<p>&ldquo;We are excited about the future of attention-based models and plan to apply them to other tasks.&rdquo; &mdash; Vaswani et al., &sect;7 Conclusion (paper p. 10)</p>
</div>
<p class="foot">Vaswani, Shazeer, Parmar, Uszkoreit, Jones, Gomez, Kaiser, Polosukhin. &ldquo;Attention Is All You Need.&rdquo; NeurIPS 2017 (arXiv:1706.03762). All figures/tables shown are cropped from the original paper PDF.</p>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

@@ -0,0 +1,26 @@
[
{
"file": "fig1-03.png",
"page": 3,
"label": "Figure 1",
"caption": "The Transformer - model architecture."
},
{
"file": "fig2-04.png",
"page": 4,
"label": "Figure 2",
"caption": "(left) Scaled Dot-Product Attention. (right) Multi-Head Attention consists of several attention layers running in parallel."
},
{
"file": "table2-08.png",
"page": 8,
"label": "Table 2",
"caption": "The Transformer achieves better BLEU scores than previous state-of-the-art models on the English-to-German and English-to-French newstest2014 tests at a fraction of the training cost."
},
{
"file": "fig3-13.png",
"page": 13,
"label": "Figure 3",
"caption": "An example of the attention mechanism following long-distance dependencies in the encoder self-attention in layer 5 of 6. Many of the attention heads attend to a distant dependency of the verb 'making', completing the phrase 'making...more difficult'. Attentions here shown only for the word 'making'. Different colors represent different heads. Best viewed in color."
}
]
Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

@@ -0,0 +1,51 @@
---
name: pptx
description: 从论文、大纲或结构化文本生成 PowerPoint (.pptx) 演示文稿。Use when 用户需要把一篇论文/文章/大纲做成幻灯片、slides、演示文稿、PPT、deck。Don't use when 只需纯文本总结、生成 Word/PDF、或修改已有 pptx 的单个像素级样式。
---
# pptx Skill —— 从论文生成演示文稿
## 核心流程(第二层)
把一份来源文本(论文 / 大纲)转成 8-12 页的演示文稿,按以下步骤:
1. **通读来源**:理解论文的标题、作者、问题背景、方法、关键结果、结论。
2. **规划页序**:一份合格的演示文稿总页数应为 8-12 页,至少覆盖——
- 标题页(论文标题 + 作者/来源作为副标题)
- 目录 / 大纲页
- 研究背景 / 问题动机
- 方法概述(**必须拆成 2 页**,例如「总体思路」与「关键机制」)
- 关键结果 / 实验发现(**必须拆成 2 页**,例如「效率指标」与「效果对比」)
- 局限性 / 讨论
- 小结 / 结论页(要点式总结全篇)
3. **提炼要点**:每页 3-5 条 bullet,每条一句话,避免整段照搬原文。
4. **生成文件**:调用本 Skill 捆绑的脚本 `scripts/generate_pptx.py`
(通过 `run_skill_script` 工具),传入下面约定的 JSON payload。
## 捆绑脚本调用约定
工具:`run_skill_script(name="pptx", script="generate_pptx.py", payload=<JSON字符串>)`
payload 的 JSON schema
```json
{
"title": "演示文稿主标题(通常等于论文标题)",
"subtitle": "副标题,通常是作者或来源,可留空",
"slides": [
{"title": "页标题", "bullets": ["要点1", "要点2", "要点3"]}
]
}
```
约束:
- `slides` **至少 8 项**(加上自动生成的标题页,总页数落在 8-12 页区间)。
- 第一项通常是「目录 / 大纲」,最后一项应为「小结 / 结论」。
- 每页 `bullets` 建议 3-5 条。
## 更详细的样式与实现细则(第三层)
如需了解版式、配色、python-pptx 的实现细节,或排查生成问题,
再用 `read_skill_file` 读取本 Skill 内的:
- `reference.md` —— 版式、配色与 python-pptx 技术细节
- `scripts/generate_pptx.py` —— 生成器源码本身
@@ -0,0 +1,43 @@
# pptx Skill —— 技术细则(第三层 / 渐进式披露最深一层)
本文件对应本书「渐进式披露」的第三层:只有当 Agent 需要控制版式、配色,
或排查生成问题时才会读取,平时不占用上下文。
## 版式约定
生成器使用 python-pptx 的空白版式(`slide_layouts[6]`),并手动摆放文本框,
从而完全掌控排版,不依赖模板占位符:
- 画布尺寸:10 x 7.5 英寸(4:3)。
- **标题页**:深蓝底(RGB 1F4E79),白色居中大标题(40pt)+ 浅蓝副标题(20pt)。
- **内容页**:白底,顶部深蓝色条内放页标题(26pt 白字),下方为要点列表(18pt)。
## 配色
| 名称 | RGB | 用途 |
|--------|----------|----------------|
| ACCENT | #1F4E79 | 标题页底 / 色条 |
| DARK | #222222 | 正文文字 |
| LIGHT | #F2F5FA | 备用浅背景 |
## python-pptx 关键点
- `Presentation()` 新建演示文稿;`prs.slides.add_slide(layout)` 增页。
- 文本必须放进 `text_frame`,逐段 `add_paragraph()`、逐段 `add_run()` 设置字体。
- 纯色页背景:`slide.background.fill.solid()` 后设 `fore_color.rgb`
- 形状类型 `1` 对应矩形(MSO_SHAPE.RECTANGLE),用于顶部色条。
- 保存:`prs.save(path)`,扩展名必须是 `.pptx`
## 校验建议
生成后重新打开文件即可验证有效性:
```python
from pptx import Presentation
prs = Presentation("output/deck.pptx")
print(len(list(prs.slides))) # 页数
for s in prs.slides: # 每页第一个文本
for shp in s.shapes:
if shp.has_text_frame and shp.text_frame.text.strip():
print(shp.text_frame.text.strip().splitlines()[0]); break
```
@@ -0,0 +1,151 @@
"""
pptx Skill 捆绑的可执行脚本:使用 python-pptx 从结构化大纲生成真实的 .pptx 文件。
这是 Agent Skills「渐进式披露」中第三层(细则 / 捆绑工具)的一部分:
Agent 在读取 SKILL.md 后,得知需要通过 run_skill_script 工具调用本脚本,
并按约定的 JSON schema 传入幻灯片大纲。本脚本负责把大纲落地为 PowerPoint。
payload JSON schema(由 SKILL.md 向 Agent 说明):
{
"title": "演示文稿主标题(字符串)",
"subtitle": "副标题,通常是作者/来源(字符串,可选)",
"slides": [
{"title": "页标题", "bullets": ["要点1", "要点2", ...]},
...
]
}
既可作为库被 importbuild_presentation),也可作为 CLI 直接运行:
python generate_pptx.py outline.json output/deck.pptx
"""
import json
import sys
from pathlib import Path
from pptx import Presentation
from pptx.util import Pt, Inches
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN
# 一套简单的品牌配色,作为设计起点(对应 SKILL.md 提到的「模板 / 设计起点」)
ACCENT = RGBColor(0x1F, 0x4E, 0x79) # 深蓝
DARK = RGBColor(0x22, 0x22, 0x22) # 近黑正文
LIGHT = RGBColor(0xF2, 0xF5, 0xFA) # 浅色背景条
def _set_slide_bg(slide, rgb):
"""给整页填充一个纯色背景。"""
fill = slide.background.fill
fill.solid()
fill.fore_color.rgb = rgb
def _add_title_slide(prs, title, subtitle):
slide = prs.slides.add_slide(prs.slide_layouts[6]) # 6 = 纯空白版式
_set_slide_bg(slide, ACCENT)
# 主标题
box = slide.shapes.add_textbox(Inches(0.8), Inches(2.2), Inches(8.4), Inches(2.0))
tf = box.text_frame
tf.word_wrap = True
p = tf.paragraphs[0]
p.alignment = PP_ALIGN.CENTER
run = p.add_run()
run.text = title
run.font.size = Pt(40)
run.font.bold = True
run.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF)
# 副标题
if subtitle:
sbox = slide.shapes.add_textbox(Inches(0.8), Inches(4.3), Inches(8.4), Inches(1.0))
stf = sbox.text_frame
stf.word_wrap = True
sp = stf.paragraphs[0]
sp.alignment = PP_ALIGN.CENTER
srun = sp.add_run()
srun.text = subtitle
srun.font.size = Pt(20)
srun.font.color.rgb = RGBColor(0xD5, 0xDE, 0xEB)
def _add_content_slide(prs, title, bullets):
slide = prs.slides.add_slide(prs.slide_layouts[6])
_set_slide_bg(slide, RGBColor(0xFF, 0xFF, 0xFF))
# 顶部标题色条
bar = slide.shapes.add_shape(
1, # MSO_SHAPE.RECTANGLE
Inches(0), Inches(0), Inches(10), Inches(1.1),
)
bar.fill.solid()
bar.fill.fore_color.rgb = ACCENT
bar.line.fill.background()
tf = bar.text_frame
tf.word_wrap = True
tf.margin_left = Inches(0.5)
p = tf.paragraphs[0]
p.alignment = PP_ALIGN.LEFT
run = p.add_run()
run.text = title
run.font.size = Pt(26)
run.font.bold = True
run.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF)
# 正文要点
body = slide.shapes.add_textbox(Inches(0.7), Inches(1.5), Inches(8.6), Inches(5.2))
btf = body.text_frame
btf.word_wrap = True
for i, bullet in enumerate(bullets):
para = btf.paragraphs[0] if i == 0 else btf.add_paragraph()
para.space_after = Pt(10)
r = para.add_run()
r.text = "" + str(bullet)
r.font.size = Pt(18)
r.font.color.rgb = DARK
def build_presentation(payload: dict, out_path: str) -> dict:
"""从大纲 payload 构建 pptx,返回 {path, num_slides, titles} 供校验。"""
title = payload.get("title", "Untitled Presentation")
subtitle = payload.get("subtitle", "")
slides = payload.get("slides", [])
if not slides:
raise ValueError("payload.slides 为空,至少需要一页内容")
prs = Presentation()
prs.slide_width = Inches(10)
prs.slide_height = Inches(7.5)
titles = []
# 标题页
_add_title_slide(prs, title, subtitle)
titles.append(title)
# 内容页
for s in slides:
s_title = s.get("title", "")
bullets = s.get("bullets")
if bullets is None:
bullets = []
_add_content_slide(prs, s_title, bullets)
titles.append(s_title)
out = Path(out_path)
out.parent.mkdir(parents=True, exist_ok=True)
prs.save(str(out))
return {"path": str(out), "num_slides": len(list(prs.slides)), "titles": titles}
if __name__ == "__main__":
if len(sys.argv) != 3:
print("用法: python generate_pptx.py <outline.json> <output.pptx>", file=sys.stderr)
sys.exit(1)
payload = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
result = build_presentation(payload, sys.argv[2])
print(json.dumps(result, ensure_ascii=False, indent=2))
@@ -0,0 +1,67 @@
from pathlib import Path
from prepare_official_skill import PROTOCOL
from validate_official_run import (
collect_kimi_metadata,
collect_tool_calls,
parse_stream,
sha256,
)
def test_protocol_pins_exact_manuscript_workflow():
assert PROTOCOL["runtime"]["official_skill_repository"] == "https://github.com/anthropics/skills.git"
assert len(PROTOCOL["runtime"]["official_skill_revision"]) == 40
assert PROTOCOL["output"]["minimum_slides"] == 10
assert PROTOCOL["output"]["maximum_slides"] == 15
assert PROTOCOL["output"]["minimum_paper_visuals"] == 3
def test_protocol_records_runtime_agnostic_acceptance_policy():
policy = PROTOCOL["runtime"]["acceptance_policy"].lower()
assert "runtime-agnostic" in policy
kimi = PROTOCOL["runtime"]["alternate_runtimes"]["kimi"]
assert kimi["binary"] == "kimi"
assert kimi["skills_flag"] == "--skills-dir"
def test_sha256_reads_binary(tmp_path: Path):
artifact = tmp_path / "x.bin"
artifact.write_bytes(b"experiment-2-6")
assert len(sha256(artifact)) == 64
def test_collect_tool_calls_parses_kimi_stream(tmp_path: Path):
stream = tmp_path / "kimi_stream.jsonl"
stream.write_text(
'{"role":"assistant","tool_calls":[{"type":"function","id":"t1",'
'"function":{"name":"Skill","arguments":"{\\"skill\\":\\"pptx\\"}"}}]}\n'
'{"role":"tool","tool_call_id":"t1","content":"Skill \\"pptx\\" loaded inline."}\n'
'{"role":"assistant","content":"done"}\n',
encoding="utf-8",
)
events, _ = parse_stream(stream)
calls = collect_tool_calls(events)
assert calls == [{"name": "Skill", "arguments": '{"skill":"pptx"}'}]
def test_collect_kimi_metadata(tmp_path: Path):
(tmp_path / "kimi_stream.jsonl").write_text(
'{"role":"assistant","tool_calls":[{"type":"function","id":"t1",'
'"function":{"name":"Read","arguments":"{\\"path\\":\\"x.md\\"}"}}]}\n'
'{"role":"assistant","content":"final answer"}\n'
'{"role":"meta","type":"session.resume_hint","session_id":"s1"}\n',
encoding="utf-8",
)
(tmp_path / "kimi_exit.json").write_text('{"return_code": 0}', encoding="utf-8")
(tmp_path / "runtime.json").write_text(
'{"runtime": "kimi", "model_alias": "kimi-code/k3"}', encoding="utf-8"
)
events, _ = parse_stream(tmp_path / "kimi_stream.jsonl")
metadata = collect_kimi_metadata(events, tmp_path)
assert metadata["return_code"] == 0
assert metadata["model_alias"] == "kimi-code/k3"
assert metadata["num_tool_calls"] == 1
assert metadata["tool_names"] == ["Read"]
assert metadata["session_id"] == "s1"
assert metadata["final_response"] == "final answer"
@@ -0,0 +1,28 @@
"""Shared bootstrap for agent-skills-ppt regression tests."""
import sys
from pathlib import Path
from types import ModuleType
PROJECT_ROOT = Path(__file__).resolve().parents[1]
if str(PROJECT_ROOT) not in sys.path:
sys.path.insert(0, str(PROJECT_ROOT))
SCRIPTS_DIR = PROJECT_ROOT / "skills" / "pptx" / "scripts"
if str(SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(SCRIPTS_DIR))
try:
import openai # noqa: F401
except ImportError:
openai_stub = ModuleType("openai")
openai_stub.OpenAI = object
sys.modules["openai"] = openai_stub
try:
import pptx # noqa: F401
except ImportError:
pptx_stub = ModuleType("pptx")
pptx_stub.Presentation = object
sys.modules["pptx"] = pptx_stub
@@ -0,0 +1,66 @@
#!/usr/bin/env python3
"""Regression tests for dispatch() in demo.py.
Bug: the agentic loop parses tool-call arguments with a JSONDecodeError
fallback to {} and then calls dispatch() with no try/except. dispatch()
used to do args["name"] / args["payload"] etc., so any malformed or
incomplete LLM tool call crashed the whole run with KeyError. Fixed to
return "[error] ..." strings that the agent can recover from.
"""
import os
from pathlib import Path
from demo import dispatch, scan_skill_catalog
OUT = Path("/tmp/test_dispatch_out.pptx")
def test_missing_name_returns_error_not_keyerror():
catalog = scan_skill_catalog()
# {} is exactly what the JSONDecodeError fallback in run_agent produces
result = dispatch(catalog, "read_skill", {}, OUT)
assert result.startswith("[error]")
assert "name" in result
def test_missing_payload_returns_error_not_keyerror():
catalog = scan_skill_catalog()
result = dispatch(catalog, "run_skill_script",
{"name": "pptx", "script": "generate_pptx.py"}, OUT)
assert result.startswith("[error]")
assert "payload" in result
def test_unknown_tool_still_returns_error():
catalog = scan_skill_catalog()
result = dispatch(catalog, "no_such_tool", {}, OUT)
assert result.startswith("[error]")
def test_valid_read_skill_still_works():
catalog = scan_skill_catalog()
result = dispatch(catalog, "read_skill", {"name": "pptx"}, OUT)
assert not result.startswith("[error]")
assert len(result) > 0
def test_run_skill_script_rejects_absolute_path(tmp_path):
"""run_skill_script executes the file, so it must stay inside scripts/."""
outside = tmp_path / "evil.py"
outside.write_text("raise AssertionError('executed out-of-tree script')")
catalog = scan_skill_catalog()
result = dispatch(catalog, "run_skill_script",
{"name": "pptx", "script": str(outside), "payload": "{}"}, OUT)
assert result.startswith("[error]")
def test_run_skill_script_rejects_parent_traversal(tmp_path):
outside = tmp_path / "evil.py"
outside.write_text("raise AssertionError('executed out-of-tree script')")
catalog = scan_skill_catalog()
scripts_dir = (catalog["pptx"]["dir"] / "scripts").resolve()
rel = os.path.relpath(outside, scripts_dir)
result = dispatch(catalog, "run_skill_script",
{"name": "pptx", "script": rel, "payload": "{}"}, OUT)
assert result.startswith("[error]")
@@ -0,0 +1,31 @@
import tempfile
from pathlib import Path
from generate_pptx import build_presentation
def test_null_bullets_like_omit():
out = Path(tempfile.mkdtemp()) / "out.pptx"
result = build_presentation(
{
"title": "Demo",
"slides": [{"title": "Slide", "bullets": None}],
},
str(out),
)
assert out.exists()
assert result["num_slides"] == 2
assert "Slide" in result["titles"]
def test_missing_bullets_still_works():
out = Path(tempfile.mkdtemp()) / "out.pptx"
result = build_presentation(
{
"title": "Demo",
"slides": [{"title": "Slide"}],
},
str(out),
)
assert out.exists()
assert result["num_slides"] == 2
@@ -0,0 +1,312 @@
#!/usr/bin/env python3
"""Fail-closed validator for the real agent-runtime + official PPTX Skill run.
Supports both accepted runtimes: Claude Code (claude_stream.jsonl) and Kimi
Code CLI (kimi_stream.jsonl). The artifact gates are identical; only the
stream parsing and run-success gate are runtime-specific.
"""
from __future__ import annotations
import argparse
import hashlib
import json
import os
import re
import zipfile
from pathlib import Path
from typing import Any
from pptx import Presentation
def sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def iter_values(value: Any):
yield value
if isinstance(value, dict):
for item in value.values():
yield from iter_values(item)
elif isinstance(value, list):
for item in value:
yield from iter_values(item)
def parse_stream(path: Path) -> tuple[list[dict], str]:
events = []
raw = path.read_text(encoding="utf-8")
for line in raw.splitlines():
if not line.strip():
continue
try:
events.append(json.loads(line))
except json.JSONDecodeError:
continue
return events, raw
def collect_tool_calls(events: list[dict]) -> list[dict]:
"""Normalize tool calls across runtime stream formats.
Claude Code emits ``assistant`` events whose message content contains
``tool_use`` blocks; Kimi Code CLI emits ``assistant`` events with an
OpenAI-style ``tool_calls`` list.
"""
calls = []
for event in events:
if event.get("role") == "assistant" and event.get("tool_calls"):
for call in event["tool_calls"]:
function = call.get("function", {})
calls.append({
"name": function.get("name", ""),
"arguments": function.get("arguments", ""),
})
message = event.get("message")
if isinstance(message, dict):
for block in message.get("content") or []:
if isinstance(block, dict) and block.get("type") == "tool_use":
calls.append({
"name": block.get("name", ""),
"arguments": json.dumps(block.get("input", {}), ensure_ascii=False),
})
return calls
def collect_result_metadata(events: list[dict]) -> dict:
result_events = [event for event in events if event.get("type") == "result"]
if not result_events:
return {}
result = result_events[-1]
return {
key: result.get(key)
for key in (
"subtype", "is_error", "duration_ms", "duration_api_ms", "num_turns",
"result", "total_cost_usd", "usage", "modelUsage", "session_id",
)
}
def collect_kimi_metadata(events: list[dict], run_dir: Path) -> dict:
exit_path = run_dir / "kimi_exit.json"
exit_info = json.loads(exit_path.read_text(encoding="utf-8")) if exit_path.exists() else {}
tool_calls = collect_tool_calls(events)
assistant_messages = [
event.get("content", "")
for event in events
if event.get("role") == "assistant" and event.get("content")
]
session_ids = [
event.get("session_id")
for event in events
if event.get("role") == "meta" and event.get("session_id")
]
runtime_info_path = run_dir / "runtime.json"
runtime_info = (
json.loads(runtime_info_path.read_text(encoding="utf-8"))
if runtime_info_path.exists() else {}
)
return {
"return_code": exit_info.get("return_code"),
"model_alias": runtime_info.get("model_alias"),
"num_assistant_messages": len(assistant_messages),
"num_tool_calls": len(tool_calls),
"tool_names": sorted({call["name"] for call in tool_calls if call["name"]}),
"session_id": session_ids[-1] if session_ids else None,
"final_response": assistant_messages[-1] if assistant_messages else None,
}
def extract_slide_text(prs: Presentation) -> str:
chunks = []
for slide in prs.slides:
for shape in slide.shapes:
if getattr(shape, "has_text_frame", False):
chunks.append(shape.text)
return "\n".join(chunks)
def validate(run_dir: Path) -> dict:
run_dir = run_dir.resolve()
protocol_path = run_dir / "experiment_protocol.json"
protocol = json.loads(protocol_path.read_text(encoding="utf-8"))
workspace = run_dir / "workspace"
kimi_stream_path = run_dir / "kimi_stream.jsonl"
runtime = "kimi" if kimi_stream_path.exists() else "claude"
stream_path = kimi_stream_path if runtime == "kimi" else run_dir / "claude_stream.jsonl"
paper_path = workspace / "attention-is-all-you-need.pdf"
pptx_path = workspace / "output" / "attention-is-all-you-need.pptx"
visual_dir = workspace / "source_visuals"
visual_manifest_path = visual_dir / "manifest.json"
events, raw_stream = parse_stream(stream_path)
tool_calls = collect_tool_calls(events)
tool_args_text = "\n".join(
call["arguments"] for call in tool_calls if isinstance(call["arguments"], str)
).lower()
prs = Presentation(str(pptx_path)) if pptx_path.exists() else None
slide_count = len(prs.slides) if prs else 0
slide_text = extract_slide_text(prs).lower() if prs else ""
zip_valid = False
embedded_media: dict[str, str] = {}
if pptx_path.exists():
with zipfile.ZipFile(pptx_path) as archive:
zip_valid = archive.testzip() is None
for name in archive.namelist():
if name.startswith("ppt/media/") and not name.endswith("/"):
embedded_media[name] = hashlib.sha256(archive.read(name)).hexdigest()
visual_manifest = (
json.loads(visual_manifest_path.read_text(encoding="utf-8"))
if visual_manifest_path.exists() else []
)
if isinstance(visual_manifest, dict):
visual_manifest = visual_manifest.get("visuals", [])
source_visuals = []
for item in visual_manifest if isinstance(visual_manifest, list) else []:
filename = item.get("file") if isinstance(item, dict) else None
path = visual_dir / filename if filename else None
if path and path.is_file():
source_visuals.append({
"file": filename,
"sha256": sha256(path),
"embedded": sha256(path) in embedded_media.values(),
"page": item.get("page"),
"label": item.get("label"),
"caption": item.get("caption"),
})
thumbnail_candidates = sorted((workspace / "output").glob("*thumbnail*.jpg"))
if not thumbnail_candidates:
thumbnail_candidates = sorted((workspace / "output").glob("*thumbnails*.jpg"))
values_as_text = "\n".join(str(value) for event in events for value in iter_values(event))
lower_evidence = (raw_stream + "\n" + values_as_text).lower()
section_checks = {
"title": "attention is all you need" in slide_text,
"background": any(term in slide_text for term in ("background", "motivation", "problem")),
"method": "transformer" in slide_text and any(term in slide_text for term in ("architecture", "method")),
"results": any(term in slide_text for term in ("result", "bleu", "translation")),
"conclusion": any(term in slide_text for term in ("conclusion", "takeaway", "summary")),
}
# Progressive-disclosure evidence must come from what the agent actually
# did (tool calls and tool results), not merely from the prompt text.
if runtime == "kimi":
skill_calls = [
call for call in tool_calls
if call["name"] == "Skill" and '"pptx"' in call["arguments"].lower().replace(" ", "")
]
pptx_skill_invoked = bool(skill_calls)
# Kimi's Skill tool loads the complete SKILL.md inline by construction;
# require the load to have succeeded in the recorded tool result.
skill_md_loaded = pptx_skill_invoked and 'skill "pptx" loaded' in values_as_text.lower()
else:
pptx_skill_invoked = (
'"skill":"pptx"' in raw_stream.replace(" ", "").lower()
or "/pptx" in lower_evidence
or "pptx creation, editing, and analysis" in lower_evidence
)
skill_md_loaded = "skills/pptx/skill.md" in lower_evidence
progress = {
"pptx_skill_invoked": pptx_skill_invoked,
"skill_md_loaded": skill_md_loaded,
"html2pptx_guide_loaded": "html2pptx.md" in tool_args_text
or "html2pptx.md" in lower_evidence,
"official_html2pptx_used": "html2pptx.js" in tool_args_text
or "scripts/html2pptx.js" in lower_evidence,
"official_thumbnail_used": "thumbnail.py" in tool_args_text
or "scripts/thumbnail.py" in lower_evidence,
"thumbnail_visually_inspected": any(
term in lower_evidence for term in ("thumbnail", "overlap", "cutoff", "visual inspection")
),
}
configured_secrets = [
value
for name in (
"ANTHROPIC_API_KEY", "KIMI_API_KEY", "MOONSHOT_API_KEY",
"OPENAI_API_KEY", "OPENROUTER_API_KEY",
)
if (value := os.getenv(name))
]
credential_scan_passed = not any(secret in raw_stream for secret in configured_secrets)
credential_scan_passed = credential_scan_passed and not bool(
re.search(r"sk-ant-[A-Za-z0-9_-]{12,}", raw_stream)
)
visual_gate = (
len(source_visuals) >= protocol["output"]["minimum_paper_visuals"]
and all(
item["embedded"] and item["page"] and item["label"] and item["caption"]
for item in source_visuals
)
)
if runtime == "kimi":
result_metadata = collect_kimi_metadata(events, run_dir)
run_succeeded = (
result_metadata.get("return_code") == 0
and bool(result_metadata.get("final_response"))
and result_metadata.get("num_tool_calls", 0) > 0
)
run_gate_key = "kimi_run_succeeded"
else:
result_metadata = collect_result_metadata(events)
run_succeeded = bool(result_metadata) and not result_metadata.get("is_error")
run_gate_key = "claude_run_succeeded"
gates = {
"source_pdf_hash_matches": paper_path.exists() and sha256(paper_path) == protocol["paper"]["pdf_sha256"],
run_gate_key: run_succeeded,
**progress,
"pptx_zip_valid": zip_valid,
"pptx_reopens": prs is not None,
"slide_count_in_range": protocol["output"]["minimum_slides"] <= slide_count <= protocol["output"]["maximum_slides"],
"required_sections_present": all(section_checks.values()),
"three_source_visuals_embedded_and_documented": visual_gate,
"thumbnail_grid_present": bool(thumbnail_candidates),
"credential_scan_passed": credential_scan_passed,
}
artifacts = {}
for path in [protocol_path, stream_path, paper_path, pptx_path, visual_manifest_path, *thumbnail_candidates]:
if path.exists() and path.is_file():
artifacts[str(path.relative_to(run_dir))] = {
"sha256": sha256(path), "bytes": path.stat().st_size
}
return {
"experiment_id": "2-6",
"runtime": runtime,
"protocol_sha256": sha256(protocol_path),
"official_skill_receipt": json.loads((run_dir / "official_skill_receipt.json").read_text(encoding="utf-8")),
"agent_result": result_metadata,
"slide_count": slide_count,
"section_checks": section_checks,
"source_visuals": source_visuals,
"embedded_media": embedded_media,
"thumbnail_files": [str(path.relative_to(run_dir)) for path in thumbnail_candidates],
"gates": gates,
"official_complete": all(gates.values()),
"artifacts": artifacts,
}
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("run_dir", type=Path)
args = parser.parse_args()
result = validate(args.run_dir)
comparison = args.run_dir / "comparison.json"
comparison.write_text(json.dumps(result, indent=2, ensure_ascii=False), encoding="utf-8")
manifest = {
"experiment_id": "2-6",
"runtime": result["runtime"],
"official_complete": result["official_complete"],
"protocol_sha256": result["protocol_sha256"],
"comparison_sha256": sha256(comparison),
"pptx_sha256": result["artifacts"].get(
"workspace/output/attention-is-all-you-need.pptx", {}
).get("sha256"),
}
(args.run_dir / "manifest.json").write_text(json.dumps(manifest, indent=2), encoding="utf-8")
print(json.dumps({**manifest, "gates": result["gates"]}, indent=2))
return 0 if result["official_complete"] else 1
if __name__ == "__main__":
raise SystemExit(main())