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

15 KiB
Raw Permalink Blame History

API Contracts — 函数签名与数据接口

本文档是 ppt-station 所有公开 API 的精确合约。编码前必读。


1. create_combo_chart()

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个字段全部必需

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

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

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

LegendConfig(
    position=LegendConfig.TOP,   # TOP/BOTTOM/LEFT/RIGHT/CORNER
    font_size_pt=9,
    font_name="黑体",
)

CategoryAxisConfig

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

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

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
  • 图例框边框/填充 → 移除

手动使用:

from ppt_station.chart_builder.cleaner import clean_chart, ChartJunkCleaner
clean_chart(chart)  # 便捷函数
# 或
ChartJunkCleaner(chart).clean()  # 完整控制

4.2 DeckLinter保存时自动运行

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

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 引用用 sourcedatasource

default_layout_configJob 级)

{
  "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

{"title": "标题", "subtitle": "副标题"}  # subtitle 可选

kpi_cards

{
    "title": "页面标题",
    "footnote": "来源: Wind, 公司年报",  # 可选auto-footer 渲染
    "cards": [
        {"label": "指标名", "value": "数值", "change": "+5.9%"},  # change/note 可选
    ]
}

常见错误:用 title 代替 label,用 unit 作为单独字段(应把单位写进 value)。

bullet_points

{
    "title": "页面标题",
    "footnote": "来源: 行业报告",  # 可选auto-footer 渲染
    "items": [
        {"icon": "01", "label": "要点标题", "desc": "要点描述"},
    ]
}

常见错误:用 title 代替 label,用 body 代替 desc

chart_full / chart_text

{
    "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 中dfsource/datasource 字段解析,不直接传 DataFrame。

two_charts50/50 双图)

{
    "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上下双图

{
    "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左图右表

{
    "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

{
    "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

{"title": "章节名", "number": "01"}

conclusion_dark左右分栏布局

{
    "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 代替 itemsconclusions(左侧要点)和 items(右侧指标)是不同字段。


6. 完整示例:双轴组合图(自定义页面)

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