制作或修改 Excel 表格(.xlsx)时使用:从零搭建数据表、明细表、汇总表、预算表、财务模型、 测算表、报价单、台账、看板底表;把整理好的数据导出成带公式和格式的工作簿;读取、清洗、 改写用户上传的 .xlsx/.xls/.csv。当用户说到「Excel」「表格」「工作簿」「xlsx」「报表」「台账」 「预算表」
复制下面这句话,粘贴给 Claude Code、Codex、Cursor 等 AI 编程工具,它会读取安装说明并在你确认后完成安装。
请阅读 https://ai.atlankj.com/install/asset/gh-bisheng-xlsx-8e5a071d2302 ,按照其中的说明把「bisheng-xlsx」安装到你(当前 AI 工具)中。执行前先告诉我将运行的命令和写入的位置,等我确认。
查看 AI 将读取的安装说明正在读取 GitHub 原文…
内容来自 GitHub 原始文件,由原作者维护。在 GitHub 查看
这一轮只读文档,不要在同一轮里并行调用别的工具。 读完本文件(必要时再读 references)之后, 下一轮才开始动手。把「读 SKILL.md」和「产出交付物」放进同一轮并行调用,等于技能没读。
本技能要求已勾选代码执行器(bisheng_code_interpreter)。 没有它就无法生成 .xlsx ——
直接告诉用户「请在工具里勾选代码执行器后重试」,不要用 Markdown 表格顶替。
| 项 | 事实 |
|---|---|
| 生成方式 | 只有 openpyxl(后端 pyproject.toml 的正式依赖),import openpyxl 直接可用 |
| 公式重算 | 靠 LibreOffice(soffice)。openpyxl 写出的公式没有值,不重算等于交了张空表 |
| 不存在的东西 | markitdown、Node/npm、defusedxml、pdftoppm、zip/unzip 命令 |
xlsxwriter | 在,但只能新建、不能读改已有文件,也不能和 openpyxl 混用 → 一律用 openpyxl |
| 其它可用库 | pandas/numpy、Pillow、PyMuPDF(fitz)、matplotlib、python-docx、python-pptx、lxml |
| 禁止 | pip install、npm install、任何联网假设(生产多为离线内网) |
| 工作目录 | 执行器 cwd = 工作区根,一律用相对路径 |
output/ | 唯一交付区。每次运行自动创建 |
scratch/ | 中间产物区,不会交付,需自己 os.makedirs;跨轮追问时会丢 |
uploads/ | 用户上传的原件在这里。跨轮追问时和 output/ 一起被带进新会话 |
skills/bisheng-xlsx/ | 本技能包,脚本和参考资料在这里,只读 |
| 绝对禁止 | 写 /output/xxx.xlsx 这种带前导斜杠的路径 —— 文件被静默丢弃,用户拿不到 |
| 单次执行上限 | 本地执行器 600 秒;E2B 沙箱 300 秒。构建、重算、体检分多次调用 |
| 日志规则 | 二选一:returncode != 0 只回 stderr(stdout 整段丢弃);returncode == 0 只回 stdout(stderr 整段丢弃)→ 一切诊断用 print(),且子进程的 stdout 和 stderr 都要各自打印一遍 |
| 可见性 | exitcode 0 时执行器把本轮新建/修改的文件同步进工作区,之后 ls/read_file 一般能看到。但判断是否产出以 exitcode 0 + 日志为准,不要因为一次 ls 没看到就重做 |
| 轮次 | 最后两轮工具收窄到 write_file/edit_file/导出,代码执行器会被拦掉 → 表要尽早产出,不要拖到收尾 |
| E2B 沙箱 | 沙箱模式下 skills/ 结构性不可见(copy-in 快照早于技能物化)→ 调包内脚本必 FileNotFoundError。一旦报这个错,就改走纯 openpyxl 内联写法:不调本包脚本,公式改成 Python 算好写数值,并在收尾里说明没做重算与体检 |
import shutil
try:
import openpyxl
print("openpyxl OK", openpyxl.__version__)
except ImportError:
print("openpyxl MISSING")
print("soffice:", shutil.which("soffice") or shutil.which("libreoffice") or "无 —— 公式无法重算,见 §4 降级方案")
openpyxl MISSING:正常部署不会出现。不要 pip install(共享的离线环境,装了会污染所有租户)。
如实告诉用户环境缺依赖,需要运维补装。soffice 没有、或重算时报「没有 Calc 组件」:见 §4 的降级方案,不要假装重算过了。| 情况 | 做法 |
|---|---|
| 用户给了数据(或让你先查再整理),要一张新表 | §3 从零构建 |
| 用户上传了 .xlsx 要改内容 / 填数 | §5 改已有文件(先读懂它的约定,再动手) |
| 用户上传了 .csv/.xls 要清洗成规范表 | pandas 读进来清洗 → 按 §3 写出 |
| 用户要的是图表图片、Word、PPT | 不属于本技能 |
第 1 步 · 定结构。先想清楚分几个表、每个表的列。复杂测算把假设单独放一个表。
草稿写 scratch/,不要写进 output/ —— 交付物按类型排序,.md/.docx/.pdf/.html 排在
.xlsx 前面,所以 output/ 里只要有一个 .md,它就永远压过你的表成为用户看到的头条交付物。
不是「同一轮才会」,是只要在就会。
第 2 步 · 写构建脚本。用 write_file 把完整脚本写到 scratch/build_sheet.py,
不要把整段代码塞进代码执行器的参数里 —— 参数过长会被截断,导致反复重试却总差一截。
写法读 /skills/bisheng-xlsx/references/openpyxl-cookbook.md(表格、样式、数字格式、公式、
图表、批注都有可直接抄的片段),规范读 /skills/bisheng-xlsx/references/design-zh.md。
第 3 步 · 执行:
import subprocess, sys
r = subprocess.run([sys.executable, "scratch/build_sheet.py"], capture_output=True, text=True)
print(r.stdout or "(no stdout)")
print(r.stderr[-2000:] if r.stderr else "(no stderr)")
为什么不直接写
python scratch/build_sheet.py:PATH 里的python未必是后端那个解释器, 用sys.executable才能保证跑在装了 openpyxl 的环境里。下面所有脚本调用都用这个写法。
第 4 步 · 重算(有公式就必做),见 §4。
第 5 步 · 体检并返修,见 §6。返修用 edit_file 定点改 scratch/build_sheet.py 再重跑,
不要每次重写整份脚本。
openpyxl 写出的公式只是字符串,没有结果。不重算的话,pandas、data_only=True、
以及多数预览器读到的全是空 —— 用户打开看到的是一张有公式没数字的表。
import subprocess, sys
r = subprocess.run([sys.executable, "skills/bisheng-xlsx/scripts/recalc_check.py", "output/xxx.xlsx"],
capture_output=True, text=True)
print(r.stdout or "(no stdout)")
print(r.stderr[-2000:] if r.stderr else "(no stderr)")
LibreOffice 会算完所有公式、就地重写文件,然后脚本回读并列出所有错误单元格。
一次重算连冷启动约 10–30 秒;超时就加 --timeout 120。
看结论:
重算完成 —— 公式都能算出结果。这不是交付条件,还要过 §6 的体检。重算未通过 + 错误单元格清单 —— 逐个改完再重算。[未重算] —— 环境问题(没有 soffice、或只装了 writer 没装 calc、或超时)。这时不要硬撑:
改成在 Python 里把数算好直接写数值,并在表里用一列文字说明计算口径,
同时如实告诉用户「本环境无法重算公式,已改为写入计算结果,修改输入不会自动重算」。能算 ≠ 算对。 区间差一行、引用错行,照样是一张干净的错数字表。 铺开整张表之前,先写 2–3 个关键公式,重算一次,肉眼核对结果符不符合预期。
import subprocess, sys
r = subprocess.run([sys.executable, "skills/bisheng-xlsx/scripts/inspect_workbook.py",
"uploads/原表.xlsx", "--content-only"], capture_output=True, text=True)
print(r.stdout or "(no stdout)")
print(r.stderr[-2000:] if r.stderr else "(no stderr)")
它按单元格坐标打印内容和公式(这是 markitdown 在本环境的替代品,而且比它多给坐标,
所以可以据此规划改哪一格)。然后:
load_workbook(path) 打开(不要加 data_only=True,那样保存会把公式全变成字面量)。.xlsm 要 keep_vba=True,否则宏全丢。output/,不要覆盖 uploads/ 里的原件。import subprocess, sys
r = subprocess.run([sys.executable, "skills/bisheng-xlsx/scripts/inspect_workbook.py", "output/xxx.xlsx"],
capture_output=True, text=True)
print(r.stdout or "(no stdout)")
print(r.stderr[-2000:] if r.stderr else "(no stderr)")
输出分两段:
_xlfn. 前缀伪装)、
_xlfn. 缺前缀、跨表引用引号、百分比存成文本、百分比存整数、数字带单位存成文本、
公式无缓存值、重算后仍是错误值、缺表头、列宽不足导致 ###、合并区丢值、年份千分位、
残留占位符、硬编码系数、除法未保护。
同一条规则在同一列命中多次时只列 3 例 + 一句「同类还有 N 处」,按同样办法一次改完。改完重新生成 → 重算 → 再体检,直到最后一行是 结论: 通过。
只有 ERROR 挡交付;结论: 通过 之后仍要把 WARN 逐条看一遍,确实无碍再交付。
XLOOKUP / XMATCH / SORT / FILTER / UNIQUE / SEQUENCE / LET / LAMBDA
—— openpyxl 写不出溢出元数据,LibreOffice 要么报 #NAME?,要么只填左上角一个值
而且重算报 0 错误。查表用 INDEX/MATCH;排序、去重、筛选在 Python 里做完再写值。
加 _xlfn. 前缀救不了这一组:=_xlfn.SORT(...) 实测照样 #NAME?,体检也照样拦。TEXTJOIN / CONCAT / IFS / SWITCH / MAXIFS / MINIFS 必须带 _xlfn. 前缀,
裸写 → #NAME?。='假设 输入'!$B$5。用 sheet_ref() 自动处理。set_pct(),它会在你传 15 时直接报错。output/ 里只放最终的 .xlsx。草稿、构建脚本、中间版本一律放 scratch/。output/2024年度经营分析.xlsx。markitdown 读表 —— 环境里没有,用 §5 的 --content-only。pip install 任何东西。/output/...。load_workbook(..., data_only=True) 之后 save() —— 会永久毁掉所有公式。ls 没看到刚生成的文件就重做一遍 —— exitcode 0 + 日志就是产出的凭据。"15%"、"1,250,000" 这种字符串 —— 不参与求和,体检会拦。