关于 power-bi-build
power-bi-build 用于构建带 IPPF 品牌元素的 Power BI 成品。它可创建 DAX 度量值和主题文件,并将页面方案存为 JSON 供手动应用;另提供从 Excel 构建完整 .pbip 仪表板的流程。
主要功能
- 01通过 pbi CLI 创建 DAX 度量值
- 02将 IPPF 主题写入 .pbip
- 03以 JSON 暂存页面方案
- 04从 Excel 生成完整 .pbip 项目
适用场景
- 01为现有 .pbip 模型添加度量值和品牌元素
- 02准备页面布局,供 Power BI Desktop 手动创建
- 03从 Excel 工作簿构建完整仪表板
MCP 相关
此技能使用 pbi CLI,该 CLI 封装了用于语义模型操作的 Power BI MCP 服务,包括度量值、表、列、关系和 DAX 执行。该接口不支持报表页面和视觉对象。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ gasserane/ personal-skills --skill power-bi-build安装后在 Claude Code 中输入/power-bi-build确认已加载。
附带文件CHANGELOG.md
详细介绍
/power-bi-build
构建带有 IPPF 品牌样式的 Power BI 构件。v1 范围:通过 pbi CLI 将 DAX 度量值写入语义模型;直接将 IPPF 主题 JSON 写入 .pbip 文件夹;将页面配方(指标仪表板、公平性、方法论)保存为 JSON 暂存构件,由用户在 Power BI Desktop 中手动应用。完整的页面和视觉对象自动化属于 v2。
工具现状
pbi CLI(软件包 pbi-cli-tool,命令名为 pbi)封装了一个 Power BI MCP 服务,但仅用于语义模型操作:
- ✅
pbi measure create— 添加 DAX 度量值。 - ✅
pbi table、pbi column、pbi relationship— 模型结构。 - ✅
pbi dax— 执行和验证 DAX。 - ❌ 没有
pbi theme命令。主题 JSON 直接写入.pbip文件夹。 - ❌ 没有
pbi page或pbi visual命令。页面和视觉对象需在 Power BI Desktop 中手动创建,并以暂存配方 JSON 为参考。
上游工具添加报表层命令后,此技能将纳入这些功能。v1 仅提供上述范围。
两种路径。 下方的 v1 流程(使用 pbi CLI 添加度量值 + 主题 + 暂存页面配方)适用于用户仍要手动继续构建的现有模型。当源文件是 Excel 工作簿,且用户希望交付一份完整可用的仪表板(已加载数据、度量值、主题和报表页面均已构建)时,请改用下方专门章节中的完整 .pbip 编写路径。该路径已通过端到端验证(CERV Portfolio Dashboard,2026-06-20)。
预检
按顺序运行这些检查。首次失败时,停止并显示指定消息。
-
已安装
pbi。- 运行:
pipx list | grep pbi-cli-tool。 - 如果结果为空:停止。显示:"尚未安装 pbi CLI。运行:
pipx install pbi-cli-tool && pipx ensurepath && pbi skills install。打开新的 shell,让 PATH 更新生效。"
- 运行:
-
Power BI Desktop 正在运行,且有活动项目,并已命名连接。
- 运行一次
pbi connect(不带参数),从 "Auto-detected Power BI Desktop on localhost:" 行读取端口。断开连接:pbi disconnect。 - 使用与 MCP 服务内部标签匹配的显式名称重新连接:
pbi connect -d localhost:<port> -n PBIDesktop-<pbip-basename>-<port>。必须使用从模型派生的名称 — pbi-cli 默认的保存名称localhost-<port>会被 MCP 服务的度量值/表操作拒绝,并返回"Connection 'localhost-<port>' not found"。 - 如果
pbi connect报告没有活动项目:停止。显示:"打开 Power BI Desktop 并加载.pbip项目,然后重新运行。" - 如果 Analysis Services 端口在会话期间发生变化(Power BI Desktop 有时会在重新打开文件、切换到模型视图等操作时重启引擎),请使用新端口重新执行此步骤。
- 运行一次
-
活动项目是
.pbip(文本格式),而不是旧版.pbix二进制格式。pbi connect输出中包含项目路径。如果路径以.pbix结尾:停止。显示:"请先将项目另存为.pbip— 文件 → 另存为 → 将保存类型设为 Power BI Project。此技能仅适用于基于文本的 PBIP 格式。"
-
品牌层可导入。
- 验证
${WORK_FOLDER_ROOT}/ane_package/reporting/powerbi_dashboard/__init__.py是否存在。 - 如果不存在:停止。显示:"未在
${WORK_FOLDER_ROOT}/ane_package/reporting/powerbi_dashboard/找到品牌层。请检查 OneDrive 同步。"
- 验证
确定意图
根据提示和本次会话中最近的分析构件,确定:
- 指标 —
MEL_DAX_LIBRARY中的键。如果不明确,询问 Ane:"要使用哪些指标?可选项:{sorted(MEL_DAX_LIBRARY.keys())}"。 - 受众层级 — 默认使用 Tier 1 工作简报;只有提示中明确提及时才使用 Tier 2 发布版。
- 页面集 — 每个指标默认使用
(indicator_dashboard, equity_disaggregation, methodology_card)。 - 来源说明 — 从先前的构件中推导;如果没有,则询问一次。
- 暂存目录 — 默认为
${PWD}/powerbi-build-output/。存放本次运行的 theme.json、measures.json 和页面配方。
构建(Python — 在内存中生成所有构件)
import json
from pathlib import Path
from ane_package.reporting.powerbi_dashboard import (
MEL_DAX_LIBRARY,
build_equity_disaggregation_page,
build_indicator_dashboard_page,
build_ippf_theme,
build_methodology_card_page,
)
theme = build_ippf_theme()
measures = {k: MEL_DAX_LIBRARY[k] for k in selected_indicators}
pages = []
for indicator in selected_indicators:
pages.append(build_indicator_dashboard_page(indicator, target, source))
pages.append(build_equity_disaggregation_page(
indicator, ("age", "gender", "geography", "wgss_disability"), source))
pages.append(build_methodology_card_page(method_note, evidence_base_sources, glossary_terms))
staging = Path("powerbi-build-output")
staging.mkdir(exist_ok=True)
(staging / "theme.json").write_text(json.dumps(theme, indent=2))
(staging / "measures.json").write_text(json.dumps(measures, indent=2))
for i, page in enumerate(pages):
(staging / f"page_{page['page_id']}.json").write_text(json.dumps(page, indent=2))
应用
1. 通过 pbi measure create 创建 DAX 度量值
对于每个度量值,根据 DAX 表达式中的 [方括号] 引用推断表(启发式规则:表达式中的第一个 Table[Column] 引用;如果表达式仅引用其他度量值,则使用 _Measures)。
pbi measure create <measure-key> \
--table <inferred-table> \
--expression "<dax-expression>"
度量值名称是位置参数(不是 --name 标志)。已通过 pbi-cli-tool 0.5.6 验证。
原样显示任何错误。切勿重试——pbi 失败通常意味着模型中不存在所引用的表或列。pbi-cli 遇到任何“未找到表”错误都会回滚事务;度量值不会以红色图标的形式写入(与旧版 Power BI 的行为相反)。请预先创建一个占位表,或在重试前修正 DAX。
1a. 保存规范 — 每次 pbi 模型写入后都必须执行
每次执行 pbi table / pbi measure / pbi column / pbi relationship 写入后,都要告知 Ane 切换到 Power BI Desktop 并立即按 Ctrl+S。外部 pbi 写入仅保存在 Analysis Services 引擎的内存中;磁盘上的 .pbip 只有在 Power BI Desktop 中选择“文件 → 保存”后才会更新。如果引擎在保存前重启(端口切换、重新打开文件,有时也包括切换模型视图),写入内容会丢失。
2. 主题 JSON — 直接写入 .pbip 文件夹
.pbip 是文本格式的项目。主题 JSON 应作为自定义主题放入报表文件夹。路径布局(Power BI Desktop 2.140+):
<project>.pbip
<project>.Report/
StaticResources/
SharedResources/
BaseThemes/
ippf-visual-identity-2025.json ← write theme here
RegisteredResources/
ippf-visual-identity-2025.json ← also write here
definition/
report.json ← register the theme (see below)
将主题 JSON 写入这两个路径。然后更新 report.json 以注册主题(Power BI Desktop 从 report.json 元数据中读取主题名称;如果未注册,主题会显示在“主题”库中,但不会自动应用)。
如果用户尚未在 Power BI Desktop 中启用 PBIP 报表格式预览(文件 → 选项 → 预览功能 → Power BI Project (.pbip) 源代码管理),主题路径可能会有所不同。如果 <project>.Report/ 不存在,请明确告知用户。
重试时避开缓存。 Power BI Desktop 会按文件名缓存已注册的主题,即使导入失败也是如此——重新导入同名文件会再次显示旧的验证错误,即使文件已修正也是如此。验证失败后重试时,请使用带版本号的名称写入新的主题 JSON(ippf-visual-identity-2025-v2.json、-v3 等),以便 Power BI 将其作为新文件解析。
3. 页面方案 — v1 阶段仅暂存
将每个页面方案以 JSON 格式保存到暂存目录。v1 中不要尝试将 PBIR 页面文件写入 .pbip 文件夹(PBIR 格式仍处于预览阶段,架构尚不稳定)。
在聊天输出中,准确告知 Ane 每个页面方案存放在哪个文件中,以及需要手动创建哪些视觉对象:
Pages saved to staging — apply manually in Power BI Desktop:
powerbi-build-output/page_cyp_total-dashboard.json
→ New page "cyp_total Dashboard"
→ Card visual: measure [cyp_total], position (40, 40, 280, 120)
→ Line chart: x=Date[Month], y=[cyp_total], position (360, 40, 880, 320)
→ Bar chart: x=Geography[country], y=[cyp_total], position (40, 380, 1200, 280)
(repeat for each page)
v2 将在上游 pbi-cli 添加报表层支持后自动创建页面,或在我们接入直接 PBIR 写入器后实现自动创建。
使用 Excel 全面创作 .pbip(完整交付方案)
当源文件是 Excel 工作簿,且用户需要完整可用的仪表板而非暂存方案时使用。通过Python 生成器(一份可重复运行的脚本)创作整个 .pbip,因为 pbi CLI 无法导入 Excel 数据(DataSourceOperationsTool 在 PowerBI 兼容模式下会被跳过),且没有报表页面/视觉对象 API。参考构建脚本:${WORK_FOLDER_ROOT}/scripts/gen_cerv_dashboard_pbip.py。
如果目标 .pbip 已存在,请遵循 mel_wiki/wiki/concepts/edit-preservation-protocol.md。 如果用户已打开目标项目,请在新的项目文件夹中创作(以避免文件锁);用户关闭项目但不保存,然后重新打开。
步骤:
- 镜像架构。 读取现有空
.pbip的骨架,并原样复制其版本(compatibilityLevel 1600;report.json 3.3.0;visual 2.9.0;page 2.1.0;pbism 4.2)。要求在 Desktop 中启用 PBIP + PBIR 预览功能。 - 检查 Excel,使用 openpyxl 检查工作表名称、精确的表头字符串(不要去除空格——末尾空格会导致列引用失效)、表头行位置,以及各列的数据类型。将预期汇总值与工作簿中现有的仪表板选项卡进行核对。
- 编写语义模型 TMDL(使用制表符缩进):
database.tmdl、model.tmdl(包含ref table X行和relationship块)、cultures/,以及tables/*.tmdl(列 + 度量值 + Mpartition)。M 块的缩进要比source =更深。 - 编写报表 PBIR:
definition.pbir(使用指向 SemanticModel 的相对byPath——此链接在文件夹移动后仍然有效)、report.json(注册 IPPF 自定义主题 + 基础主题)、pages/<id>/page.json,以及pages/<id>/visuals/<id>/visual.json。 - 将度量值与源汇总值进行核对,然后让用户打开并刷新。加载后,通过 CLI 验证:
pbi connect,然后执行pbi dax execute "EVALUATE <Table>"并读取RowCount=(CLI 不会打印单元格值;使用表格 EVALUATE 并读取行数)。
Power Query M 注意事项(每项都会导致一次刷新错误循环——请提前处理):
Excel.Workbook(File.Contents(path), null, true)会删除开头完全为空的行,因此固定的Table.Skip(n)会导致表头错位。请动态定位表头:HeaderRow = List.PositionOf(Sheet[Column1], "<key header>"),然后执行Table.Skip(Sheet, HeaderRow),再执行Table.PromoteHeaders(.., [PromoteAllScalars=true])。- 应使用
List.PositionOf(列表 + 值),而不是Table.PositionOf(表 + 记录)——后者会报错 "cannot convert List to Table"。 - 仅对仪表板需要的列设置固定类型(金额 →
type number,计数 →Int64.Type)。对计算所得列或 "(auto)" 列设置类型时,若待填充行返回非数值("" / "Pending"),每个单元格都会报 "N errors"。这些列应保持未指定类型。 - 多行表头 / 合并表头工作表:跳过 PromoteHeaders;使用位置参数
Table.Range+Table.SelectColumns({"Column1",...})+ 重命名。 - M 文件路径是字面值(无需转义反斜杠)。在共享表达式中只定义一次源路径,或始终以内联方式一致地填写。
PBIR 视觉对象 JSON(盲写,均可正确渲染): 最小结构为 visual.query.queryState.<role>.projections[{field:{Measure|Column:{Expression:{SourceRef:{Entity}},Property}}, queryRef:"Entity.Prop", nativeQueryRef:"Prop"}]。角色:card → Values;clusteredColumnChart/clusteredBarChart → Category + Y(多度量值图表省略 Category);tableEx → Values(列表)。省略标题对象——让 IPPF 主题和自动标题统一设置样式。其他内容:切片器多选 + 全选 = objects.selection[{properties:{singleSelect:{Literal false}, selectAllCheckboxEnabled:{Literal true}}}];导航栏 = visualType pageNavigator;徽标 = visualType image,使用 ResourcePackageItem(RegisteredResources) + 将 PNG 复制到 StaticResources/RegisteredResources/ + 在 report.json 中注册;页眉色带 = 带有 visualContainerObjects.background 的文本框。
易读布局(1280×720): 紧凑的全宽切片器栏(切片器会呈现为下拉菜单)、KPI 卡片行,然后是横跨全宽的大图表——避免使用右侧空出大片区域的半宽切片器行。将页面 displayOption 设为 FitToWidth,使页面填满显示器宽度并可垂直滚动。
分发与许可(向用户提供建议)
.pbip是一种开发/源代码管理格式——刷新前打开时内容为空,不适合新手。- 对于新手查看者,请提供一个
.pbix(文件 → 另存为 →.pbix会嵌入数据,打开后内容完整,且无需许可证)。只有 Power BI Desktop 能写入.pbix;命令行或脚本都无法做到,而且嵌入数据前必须先进行一次实时刷新。 - 发布到 Power BI Service 后,只有当工作区位于 Premium(P SKU)或 Fabric F64+ 容量上时,免费版同事才能查看;否则,每位查看者也需要 Pro/PPU。对于敏感数据(SRHR/财务/次级受资助方),切勿使用“发布到 Web”——该方式会公开数据。
- 页面导航器和按钮在阅读视图/已发布状态下单击即可导航,在编辑视图下按 Ctrl 并单击才可导航(这是 Power BI 固有行为,并非错误)。
- 通过绝对本地路径/OneDrive 路径引用的 Excel 源文件,移至 SharePoint 后会失效。要实现云端计划刷新,请通过 SharePoint 连接器重新指定源;每次刷新都会重新读取源文件。
输出(一级工作简报)
Power BI build complete (v1 scope: measures + theme + page recipes).
Measures applied to semantic model: {N} ({list of keys}).
Theme written to: {project}.Report/StaticResources/SharedResources/BaseThemes/ippf-visual-identity-2025.json
Page recipes staged at powerbi-build-output/:
- page_<id>.json — {N} visuals each. Apply manually per the recipe.
Source: {derived source line}.
常见错误
| 错误 | 可能原因 | 修复方法 |
|---|---|---|
安装后出现 pbi: command not found | PATH 尚未更新 | 运行 pipx ensurepath;打开新的 shell。 |
pbi connect 提示没有会话 | Power BI Desktop 未运行,或未打开 .pbip | 打开 Power BI Desktop 并加载 .pbip。 |
pbi measure create 失败:“找不到表” | DAX 度量值引用了模型中不存在的表 | 确认数据模型符合度量值的假设;添加该表或调整 DAX。 |
指标不在 MEL_DAX_LIBRARY 中 | 正在添加新指标 | 将其添加到 dax_library.py,并附上对应的 wiki 条目;向 anework-package 提交 PR。 |
| Power BI Desktop 未应用主题 | PBIP 预览功能已禁用,或主题未在 report.json 中注册 | 启用 PBIP 预览(File → Options → Preview features);检查 report.json 是否包含主题名称。 |
| 页面配方中的 visualType 是用户找不到的类型 | Power BI Desktop UI 的命名与配方中的 type 不同 | 使用可视化对象库:card = “Card”;lineChart = “Line chart”;barChart / stackedBarChart = “Stacked bar chart”;slicer = “Slicer”;table = “Table”;textbox = “Text box”。 |
pbi measure create 失败:Connection 'localhost-<port>' not found | pbi-cli 自动保存的名称与 MCP 服务的内部名称不同 | 使用 -n PBIDesktop-<pbip-basename>-<port> 重新连接——参见准备步骤 2。 |
pbi 模型写入的内容在命令之间消失 | AS 引擎在 File → Save 之前重启 | 每次使用 pbi 写入后,都在 Power BI Desktop 中按 Ctrl+S。参见应用步骤 1a。 |
主题导入在修复后仍因引用相同键而出现 oneOf 验证错误 | Power BI Desktop 按文件名缓存了上一次解析失败的结果 | 将主题 JSON 重写为 -v2(或 -v3)文件名,然后重新浏览选择。参见应用步骤 2。 |
pbi table refresh 失败并显示 Invalid operation: Refresh | pbi-cli 0.5.6 发送 Refresh 操作名称;MCP 服务只接受 REFRESHWITHXMLA / REFRESHWITHAPI | pbi-cli 上游 bug。解决方法:在 Power BI Desktop 中手动触发刷新(Home → Refresh)。 |
pbi measure get 或 pbi table export-tmdl 失败并显示 References is required | pbi-cli 0.5.6 未将 References 参数传递给 MCP 服务 | pbi-cli 上游 bug。使用 pbi measure list --table <name> 确认度量值是否存在;使用 Power BI Desktop 的 Tabular Editor 视图检查 TMDL。 |
| 刷新:“未找到表的列 ‘’”(提升标题的表) | Excel.Workbook 删除了开头的空行,导致固定的 Table.Skip 与标题错位 | 动态查找标题:Table.Skip(Sheet, List.PositionOf(Sheet[Column1], "<key>"))。参见完整创作流程。 |
| 刷新:“无法将 List 类型的值转换为 Table 类型” | 对列使用了 Table.PositionOf | 使用 List.PositionOf(list, value),而不是 Table.PositionOf(table, record)。 |
| 刷新:某个表显示“已加载 N 行。出现 M 个错误” | 类型化的计算列 / “(auto)” 列对尚未填充的行返回非数值 | 移除该列的类型转换;仅为仪表板需要的列指定类型。 |
| 报表已打开,但刷新后所有可视化对象均为空白 | 导入模型加载了 0 行(M 失败)——这不是绑定 bug | 使用 pbi 连接,运行 pbi dax execute "EVALUATE <Table>",读取 RowCount=;修复 M,而不是可视化对象。 |
收件人打开 .pbip 后看到空报表 | .pbip 不存储数据;打开后会显示为空,直到刷新 | 分发包含数据的 .pbix,或发布到 Service。参见分发与许可。 |