skills/ppt-station-skill/reference/api-contracts.md
wangyitong a65adcc2e5 Initial commit: merged, deduplicated, and vetted skill collection
Sources: extracted from two upstream archives (skill-repo, skills-main),
merged with the following policy:

- 15 broken symlinks (pointing to /Users/jameslee/.cc-switch/skills or
  ../../.agents/skills on a foreign machine) discarded
- 3 real name collisions with identical content (ai-pair, ifind-http-api,
  zhipu-websearch) kept as one copy
- Functional overlaps deduped keeping the strongest variant:
  - docx family: kept docx (official, full toolchain) + docx-cn
    (GB/T 9704 Chinese official-document constants),
    dropped docx_writer (no scripts, name collided with docx)
  - humanizer family: kept humanizer-zh (6 zh reference docs),
    dropped humanizer (en, redundant for CN workflow)
- Skills that only ran in a foreign environment removed:
  ablemind-ops, app-publish, hlb-design-system, openclaw-adj-skill,
  claude-driver
- alphapai excluded from this public repo because its SKILL.md hard-coded
  live credentials

Result: 25 skills, 572 files, ~7.5 MB.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 14:47:12 +08:00

445 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# API Contracts — 函数签名与数据接口
> 本文档是 ppt-station 所有公开 API 的精确合约。编码前必读。
---
## 1. create_combo_chart()
```python
from ppt_station.chart_builder.api import create_combo_chart
create_combo_chart(
slide, # pptx Slide 对象
df, # pandas DataFrame
categories_col, # str — X轴列名
series_config, # List[Dict] — 见下方格式
position=(Inches(1), Inches(2)), # (left, top) 元组
size=(Inches(8), Inches(4.5)), # (width, height) 元组
style_config=None, # StyleConfig 实例
layout_config=None, # ChartLayoutConfig 实例
)
```
### series_config 格式4个字段全部必需
```python
series_config = [
{"key": "营收(亿)", "name": "营收(亿)", "type": "bar", "axis": "primary"},
{"key": "增长率", "name": "增长率", "type": "line", "axis": "secondary"},
]
```
| 字段 | 说明 | 可选值 |
|------|------|--------|
| `key` | DataFrame 列名 | 必须与 df 列名完全一致 |
| `name` | 图表图例显示名 | 任意字符串 |
| `type` | 图表类型 | `"bar"`, `"line"`, `"area"` |
| `axis` | 轴分配 | `"primary"` (左轴), `"secondary"` (右轴) |
**常见错误**:用 `column` 代替 `key`、用 `chart_type` 代替 `type`、缺少 `name` 字段。
---
## 2. StyleConfig
```python
from ppt_station.chart_builder.styles import StyleConfig
StyleConfig(
color_scheme="tech_blue", # 配色方案名(见 COLOR_SCHEMES
line_width_pt=2.0, # 线宽 pt: 0.5/0.75/1.0/1.5/2.0/2.25/3.0
marker_style="none", # "none"/"circle"/"square"/"diamond"/"triangle"
marker_size=5, # 标记点大小 pt
)
```
---
## 3. ChartLayoutConfig
```python
from ppt_station.chart_builder.layout import (
ChartLayoutConfig, LegendConfig, ValueAxisConfig, CategoryAxisConfig,
)
from ppt_station.chart_builder.date_axis import MONTHLY_TICKS # DateAxisConfig 预设
ChartLayoutConfig(
title=None, # str — 图表内标题(通常 None标题放页面上
legend_config=LegendConfig(...), # 图例
category_axis_config=CategoryAxisConfig(...), # 横轴
value_axis_config=ValueAxisConfig(...), # 主值轴(左)
secondary_value_axis_config=ValueAxisConfig(...), # 次值轴(右)
date_axis_config=MONTHLY_TICKS, # DateAxisConfig 日期轴(优先于 category_axis_config
)
```
**关键:`date_axis_config` 在 ChartLayoutConfig 上,不在 CategoryAxisConfig 上。**
### LegendConfig
```python
LegendConfig(
position=LegendConfig.TOP, # TOP/BOTTOM/LEFT/RIGHT/CORNER
font_size_pt=9,
font_name="黑体",
)
```
### CategoryAxisConfig
```python
CategoryAxisConfig(
is_date_axis=False, # 是否日期轴
major_unit_days=None, # 主刻度天数
font_name="黑体",
font_size_pt=10,
number_format=None, # 如 "yyyy-mm-dd"
)
```
**没有 `date_axis` 参数。** 日期轴预设传入 `ChartLayoutConfig(date_axis_config=...)`
### ValueAxisConfig
```python
ValueAxisConfig(
number_format="#,##0", # 数字格式
font_name="黑体",
font_size_pt=9,
)
```
### 日期轴预设
| 预设 | 间隔 | 适用 |
|------|------|------|
| `DAILY_TICKS` | 1天 | 1-30天 |
| `WEEKLY_TICKS` | 7天 | 1-6月 |
| `BIWEEKLY_TICKS` | 14天 | 季度 |
| `MONTHLY_TICKS` | 1月 | 年度 |
| `QUARTERLY_TICKS` | 3月 | 多年 |
| `YEARLY_TICKS` | 1年 | 长期 |
---
## 4. PageComposer
```python
from ppt_station.composer import PageComposer
composer = PageComposer(theme="jp_finance") # 主题名(从 theme 自动读取 slide_w/slide_h
# 方式一:注册布局
composer.add_page("layout_name", data_dict)
# 自动注入页眉add_page_header和页脚add_page_footer
# 封面/结论/分隔页除外title_dark, title_light, conclusion_dark, section_divider
# 页脚来源从 data["footnote"] 获取(兼容回退 data["source"]),页码自动计算
# 方式二:自定义渲染(无 data
composer.add_custom_page(render_fn)
# render_fn 签名: def render_fn(slide, theme) → None ← 只有 2 个参数
# 方式三:自定义渲染(带 data
composer.add_custom_page(render_fn, data=my_dict)
# render_fn 签名: def render_fn(slide, data, theme) → None ← 3 个参数
composer.save("output.pptx") # 自动运行 DeckLinter
composer.save("output.pptx", lint=False) # 跳过审计
```
**常见错误**`render_fn(slide, prs, theme)` — 没有 `prs` 参数。
---
## 4.1 ChartJunkCleaner自动无需手动调用
`ChartBuilder.build()` 完成后自动执行清洗:
- 移除图表外边框、plotArea 边框
- Y 轴网格线 → 极浅灰色虚线 (`#E8E8E8`, 0.5pt, dot)
- 坐标轴刻度线 → none
- 图例框边框/填充 → 移除
手动使用:
```python
from ppt_station.chart_builder.cleaner import clean_chart, ChartJunkCleaner
clean_chart(chart) # 便捷函数
# 或
ChartJunkCleaner(chart).clean() # 完整控制
```
---
## 4.2 DeckLinter保存时自动运行
```python
from ppt_station.qa.deck_linter import DeckLinter
linter = DeckLinter("output.pptx")
report = linter.run()
linter.print_summary(report) # 终端摘要
linter.to_json(report) # JSON 字符串
linter.write_to_notes(report) # 写入 PPT slide notes
```
检查规则:`header_missing`, `footer_missing`, `bounds_overflow`(动态阈值,适配 4:3/16:9, `title_size_overflow`, `chart_misaligned`, `insight_missing`(图表页无结论先行文本)
---
## 4.3 页眉/页脚 Helpers
```python
from ppt_station.composer.helpers import add_page_header, add_page_footer
add_page_header(slide, "页面标题", theme)
# 输出: 16pt 标题 + 全宽 primary 色分隔线 (使用 theme tokens: page_title_size, header_y, divider_y)
add_page_footer(slide, theme, source="Wind, 公司年报", page_num=3, brand="PPT Station")
# 输出: 左侧来源 + 右侧页码/品牌 (使用 theme tokens: footer_size, footer_y)
```
**注意**注册布局自动调用custom_page 不自动调用(需手动使用)。
---
## 4.4 PptEngine — Job JSON 渲染
### source / datasource / footnote 语义
| 字段 | 位置 | 语义 |
|------|------|------|
| `datasource` | 顶层或 left/right/top/bottom 嵌套 | **新 canonical key**,始终视为 datasource 引用 |
| `source` | 顶层或 left/right/top/bottom 嵌套 | 值匹配已知 datasource 时解析为 DataFrame 并 pop不匹配时保留为脚注文本兼容回退 |
| `footnote` | 顶层 | 页脚来源文本auto-footer 优先读取 |
**推荐**:顶层脚注用 `footnote`,嵌套 datasource 引用用 `source``datasource`
### default_layout_configJob 级)
```json
{
"default_layout_config": {
"legend_config": {"font_size_pt": 8, "font_name": "黑体"},
"value_axis_config": {"font_size_pt": 8, "font_name": "黑体"},
"secondary_value_axis_config": {"font_size_pt": 8}
}
}
```
- **作用**:所有含 `df` 的图表页(包括 left/right 嵌套)自动继承此配置
- **合并策略**浅合并per-page `layout_config` 的 key 覆盖默认 key整个子对象替换非字段级合并
- **无 `df` 的页面**kpi_cards, bullet_points 等)不注入
- **Optional**:不提供时不注入,完全兼容旧 JSON
---
## 5. 布局数据接口
### title_dark / title_light
```python
{"title": "标题", "subtitle": "副标题"} # subtitle 可选
```
### kpi_cards
```python
{
"title": "页面标题",
"footnote": "来源: Wind, 公司年报", # 可选auto-footer 渲染
"cards": [
{"label": "指标名", "value": "数值", "change": "+5.9%"}, # change/note 可选
]
}
```
**常见错误**:用 `title` 代替 `label`,用 `unit` 作为单独字段(应把单位写进 `value`)。
### bullet_points
```python
{
"title": "页面标题",
"footnote": "来源: 行业报告", # 可选auto-footer 渲染
"items": [
{"icon": "01", "label": "要点标题", "desc": "要点描述"},
]
}
```
**常见错误**:用 `title` 代替 `label`,用 `body` 代替 `desc`
### chart_full / chart_text
```python
{
"title": "页面标题",
"df": df, # pandas DataFrame引擎从 source/datasource 解析)
"categories_col": "日期", # X轴列名
"series_config": [...], # 同 create_combo_chart 的 series_config
"insight": "结论先行文本...", # 可选2-3行结论摘要header 和图表之间)
"footnote": "来源: Wind", # 可选auto-footer 渲染
"style_config": StyleConfig(...), # 可选
"layout_config": ChartLayoutConfig(...), # 可选(可由 default_layout_config 继承)
}
```
**Job JSON 中**`df` 由 `source`/`datasource` 字段解析,不直接传 DataFrame。
### two_charts50/50 双图)
```python
{
"title": "页面标题",
"left_title": "左图标题", # 可选
"right_title": "右图标题", # 可选
"insight": "结论先行文本...", # 可选header 和图表之间
"footnote": "来源: Wind", # 可选auto-footer 渲染
"left": {
"df": df1, "categories_col": "日期", "series_config": [...],
"style_config": ..., "layout_config": ...,
},
"right": {
"df": df2, "categories_col": "日期", "series_config": [...],
"style_config": ..., "layout_config": ...,
},
}
```
**Job JSON 中**left/right 内用 `source`/`datasource` 指向数据源,`footnote` 放顶层。
### two_charts_vertical上下双图
```python
{
"title": "页面标题",
"top_title": "上图标题", # 可选
"bottom_title": "下图标题", # 可选
"insight": "结论先行文本...", # 可选header 和上图之间
"footnote": "来源: Wind", # 可选auto-footer 渲染
"top": {
"df": df1, "categories_col": "日期", "series_config": [...],
"style_config": ..., "layout_config": ...,
},
"bottom": {
"df": df2, "categories_col": "日期", "series_config": [...],
"style_config": ..., "layout_config": ...,
},
}
```
**设计特点**:两个图表均使用全宽(最大化时间轴跨度),上下等高,中间 0.15" 间隔。适合共享日期轴的趋势对比。
**Job JSON 中**top/bottom 内用 `source`/`datasource` 指向数据源。
### chart_table左图右表
```python
{
"title": "页面标题",
"df": df, # 图表数据
"categories_col": "日期",
"series_config": [...],
"headers": ["指标", "值"], # 表格表头(可省略,从 table_df/df 自动提取)
"rows": [["A", "1"], ...], # 表格数据行(可省略,从 table_df/df 自动转换)
"table_df": table_df, # 可选,表格数据 DataFrame自动转为 headers/rows
"insight": "结论先行文本...", # 可选
"chart_ratio": 0.62, # 可选,图表宽度占比(默认 0.62
"footnote": "来源: Wind", # 可选
}
```
**设计特点**:左图表 (62%) + 右表格 (38%)表格使用投行级样式ib_style。适合趋势可视化 + 截面数据快照组合。
**Job JSON 中**:图表数据用 `source`/`datasource`,表格数据用 `table_df` 对应的 datasource 或直接传 `headers`/`rows`。
### comparison_table
```python
{
"title": "标题",
"headers": ["指标", "公司A", "公司B"],
"rows": [["营收", "100亿", "80亿"], ...],
"highlight_row": 0, # 可选,高亮行索引
"highlight_cols": [1, 2], # 可选,高亮列索引列表
"footnote": "来源: Wind, 公司年报", # 可选auto-footer 渲染
}
```
表格默认启用投行级样式(`ib_style=True`):无垂直边框、数值列自动右对齐、斑马纹。
**注意**footnote 统一由 auto-footer 渲染(不再在表格下方手动添加)。
### section_divider
```python
{"title": "章节名", "number": "01"}
```
### conclusion_dark左右分栏布局
```python
{
"verdict": "核心结论(必填)",
"title": "结论", # 可选,默认"结论"
"score": "8/10", # 可选,左侧顶部大字
"conclusions": [ # 可选左侧要点列表最多3条
{"title": "高壁垒", "desc": "品牌+渠道双重优势"},
{"title": "稳增长", "desc": "营收年化增速 20%+"},
],
"items": [{"label": "评分", "value": "8/10"}], # 可选,右侧指标卡片
"risk": "风险提示文字", # 可选,右侧底部
"footnote": "脚注", # 可选
}
```
布局结构:左侧 55%score + verdict + conclusions| 右侧 45%items 卡片 + risk
**常见错误**:用 `summary` 代替 `verdict`,用 `cards` 代替 `items`。`conclusions`(左侧要点)和 `items`(右侧指标)是不同字段。
---
## 6. 完整示例:双轴组合图(自定义页面)
```python
from pptx.util import Inches
from ppt_station.composer import PageComposer
from ppt_station.chart_builder.api import create_combo_chart
from ppt_station.chart_builder.styles import StyleConfig
from ppt_station.chart_builder.layout import (
ChartLayoutConfig, LegendConfig, ValueAxisConfig, CategoryAxisConfig,
)
from ppt_station.chart_builder.date_axis import MONTHLY_TICKS
def render_my_chart(slide, theme):
# 页眉(自动: 16pt 标题 + 全宽分隔线)
from ppt_station.composer.helpers import add_page_header, add_page_footer
add_page_header(slide, "股价走势2024-2026", theme)
# 图表 — style_config 可省略,自动使用主题配色
m = theme["margin"]
content_y = theme.get("content_y", 1.00)
footer_y = theme.get("footer_y", 7.10)
create_combo_chart(
slide, price_df, categories_col="日期",
series_config=[
{"key": "成交量", "name": "成交量", "type": "bar", "axis": "secondary"},
{"key": "收盘价", "name": "收盘价", "type": "line", "axis": "primary"},
],
position=(Inches(m), Inches(content_y)),
size=(Inches(12.133), Inches(footer_y - content_y - 0.3)),
# style_config 省略 — 自动使用 jp_finance 配色
layout_config=ChartLayoutConfig(
legend_config=LegendConfig(position=LegendConfig.TOP, font_size_pt=9, font_name="黑体"),
value_axis_config=ValueAxisConfig(number_format="#,##0.00", font_name="黑体", font_size_pt=9),
secondary_value_axis_config=ValueAxisConfig(number_format="#,##0", font_name="黑体", font_size_pt=9),
date_axis_config=MONTHLY_TICKS,
),
)
# 页脚(来源 + 页码)
add_page_footer(slide, theme, source="Tushare, Wind")
composer = PageComposer(theme="jp_finance")
composer.add_custom_page(render_my_chart)
composer.save("output.pptx") # 自动运行 DeckLinter
```