
前阵子有朋友问我“你天天在终端里泡着用OpenCode跑各种分析任务那如果给它一个xlsx文件它能直接分析吗”我的回答是能但别指望裸奔的模型能稳定搞定。原因很简单xlsx这种格式表面上看是“一个Excel文件”实际上是一个用ZIP压缩过的XML容器里面塞着工作表、样式表、共享字符串、公式缓存一堆东西。让通用对话模型凭空去读一个多sheet、带合并单元格、甚至还有几列Excel日期序列号的表格一次两次可以十次八次必然在某个细节上翻车。不是模型智商不够是这种二进制格式的边界情况太多了。后来我把整套需求固化成了skills——一个专门处理xlsx分析的技能包。从“读取文件”到“清洗数据”再到“输出统计结论”全部沉淀成可复用的流程。现在再扔任何xlsx给我OpenCode会主动加载这个skill按照固定流程走完结果稳定得多了。这篇文章就把这套玩法完整拆开为什么需要skills、SKILL.md怎么写、配套的Python脚本怎么设计、实操中会踩哪些坑。1. 需求拆解xlsx分析“难”在哪1.1 二进制容器带来的第一道坎很多人以为xlsx就是“表格的另一种格式”和CSV差不多。实际差别大了。CSV是纯文本模型睁眼就能读xlsx则是一个二进制包裹的压缩包解压之后才是XML结构。这意味着模型没法直接“看”文件内容必须先经过一层解析。就算把xlsx解析成了表格后面还有一连串暗坑sheet多一个文件可能有十几个工作表模型很容易只看第一个就下结论。样式和内容分离字体、配色、列宽这些样式信息存放在单独的XML里真正的数据在另一个文件里解析时必须知道去哪儿找。日期和数字格式Excel底层存日期用的是“距离1899年12月30日的天数”比如2024年1月1日存成45292。模型如果不知道这层规则看到45292会当成普通数字处理。合并单元格表头经常跨行跨列合并pandas读出来之后合并区域要么只有左上角有值要么全是NaN。隐藏行、空行、公式缓存明明看着有几十行数据实际有效行数可能只有一半。给个生活化的类比xlsx就像一个贴了标签的档案柜抽屉sheet里面装着文件夹XML文件夹里面才是真正的纸张数据。AI要是不知道开锁的规矩就只能在外面干瞪眼。1.2 skills补上的“稳定性”短板讲道理这些坑每个都能单独绕过但问题是你不可能每次分析都让模型从头开始探索一遍。模型是无状态的这次问它“帮我看看这个Excel”它会自己摸索下次再问同样的问题它又从头摸索可能换一条完全不同的路径。结果就是同样一个文件第一次跑得好好的第二次跑就换了办法中间还可能踩进某个没处理过的格式化陷阱。skills解决的就是这个问题。它的本质是一套“按需加载的操作手册”用户在配置目录里放好一组指令和脚本文档当模型识别到任务匹配时就把这套指令注入当前对话告诉模型“遇到xlsx就按这个流程走”。相当于给模型发了一本固定的SOP手册不允许它临场发挥。这种模式的好处非常直接可复现同一个skill跑出来的结果不会有太大偏差。省token不需要每次都让模型反复试错指令和脚本一次性加载。可维护Excel解析规则变了、库更新了改skill一个地方就行不用去每条对话里叮嘱。2. 环境准备OpenCode与skills的安装和目录规范2.1 安装OpenCode并确认Shell环境先搞定基础环境。OpenCode的安装方式主要有两种——npm全局安装或者用官方的一键脚本。# npm 方式 npm install -g opencode # 脚本方式 curl -fsSL https://opencode.ai/install | bash装完验证一下opencode --version这里有个常见的坑如果你用的是Windows自带的CMD安装完输入opencode可能会提示“不是内部或外部命令”因为npm的全局bin目录没有加入PATH。解决办法是重开终端或者手动把npm的全局路径加进环境变量。实测下来Windows上最省心的方式是装一个WSL2在里面跑OpenCode文件处理、路径兼容、shell体验都顺滑很多。如果你不想上WSL2至少装个PowerShell 7别在旧版CMD里折腾。2.2 配置模型接入OpenCode本身是个壳真正干活的是底层模型。进入OpenCode交互界面后第一次运行会让你配置模型供应商。刚才跑起来的第一件事是执行opencode auth login按提示完成认证。如果你有自建网关或者本地推理服务也可以走配置文件方式接入。配置完成后输入opencode进入交互界面随便问一句“11等于几”能够正常回答就说明链路通了。一个实操细节如果模型返回报错先分清是“客户端到服务端”的问题还是“服务端到模型”的问题。比如有些免费档位只能用客户端内部触发外部直接调用就会报provider相关错误。遇到这种就先确认模型来源是否被允许、密钥是否有效别急着改配置。2.3 skills目录结构与创建方式OpenCode的skills体系沿用了社区常见的SKILL.md规范核心概念是一个skill对应一个目录目录里至少包含一个带元信息的SKILL.md文件还可以附带脚本、模板等资源文件。目录位置通常在你的用户配置目录下以常见约定为例~/.config/opencode/skill/xlsx-analysis/ ├── SKILL.md ├── analyze_xlsx.py ├── requirements.txt └── samples/Windows上这个路径通常是C:\Users\你的用户名\.config\opencode\skill\WSL2环境下就是~/.config/opencode/skill/。创建好目录后用opencode自带的命令可以查看当前已安装的skills。有些版本支持通过命令从仓库安装opencode skills install 某个skill名不过我更推荐手动创建后面每一步都好控制。3. 为xlsx分析专门设计一个skill包3.1 先定义能力边界写skill之前先想清楚这个技能要管哪些事别贪多。我给xlsx-analysis这个skill定的能力边界是读取xlsx输出文件整体概况sheet列表、每个sheet的行列数、列名、数据类型。数据清洗缺失值统计、重复行检测、类型转换、日期解析。基础统计数值列的均值、中位数、极值、分位数分类列的唯一值数量和TOP频次。汇总输出按用户需求做分组聚合、生成简要洞察报告。超出这个范围的比如复杂的机器学习建模、图表可视化我不做而是让模型在完成基础分析后建议下一步方案。这样skill的指令可以写得非常聚焦模型执行起来也不会精神分裂。3.2 SKILL.md的完整写法SKILL.md是整个技能包的核心。文件最前面是YAML格式的元信息后面是正式的指令正文。--- name: xlsx-analysis description: 用于分析和处理 .xlsx / .xlsm Excel 文件。当用户要求“分析Excel”“读取xlsx”“查看表格数据”“统计表格”“做数据透视”时触发。可读取多sheet、统计概况、清洗载入数据、生成汇总报告。 agent: code model: strong --- # xlsx 文件分析流程 ## 第一步检查文件信息 1. 先用 python3 analyze_xlsx.py info 文件路径 获取文件概况。 2. 输出内容包含sheet 名称、各 sheet 行数列数、列名、缺失值情况。 3. 除非用户指定sheet否则默认分析第一个非空 sheet。 ## 第二步数据清洗 1. 删除全空行、全空列。 2. 重复行先查后删删除前向用户确认。 3. 日期列统一通过 pandas.to_datetime 解析如果出现“1900-01-01”这样的默认值标记为可疑数据。 4. 数值列如果存在文本型数字先转 numeric。 ## 第三步统计分析 1. 数值列输出count、mean、min、25%/50%/75%分位、max。 2. 分类列输出唯一值数量、出现次数最多的前10个值。 3. 按用户要求做分组聚合时优先使用 pandas.groupby保留结果精度到两位小数。 ## 第四步输出报告 1. 用 Markdown 表格呈现统计结果。 2. 给结论时必须引用具体数字不得模糊描述。 3. 如用户需要将清洗后数据另存为 _cleaned.xlsx不要覆盖原文件。 ## 执行要求 - 所有脚本调用统一走 python3 $SKILL_DIR/analyze_xlsx.py 绝对路径方式。 - 不要在不运行脚本的情况下猜测数据。 - 遇到解析异常先查看 traceback再尝试修复。几个容易被忽略的点description的写法决定了触发率。模型识别任务是靠匹配description的重合度所以这里要堆满用户可能会说的词分析、Excel、xlsx、表格、统计、透视、sheet。词不够skill就静默不触发这个问题后面还会细讲。model字段可以指定强模型或快模型比如复杂任务用strong简单任务用fast能省不少token。agent字段限定了这个skill对哪类代理生效。如果你只需要写代码场景生效就写code。3.3 配套Python脚本的两个关键设计analyze_xlsx.py是skill真正干体力活的家伙。写这个脚本有两个关键设计。第一个是“双子命令”结构。我给它设计了info和analyze两个子命令前者只输出概况后者做深度清洗和统计。为什么要拆开因为模型在执行任务时经常只需要先确认文件概貌压根没必要把所有数据都载入内存算一遍。拆开后效率高也省token。import sys import json import pandas as pd from openpyxl import load_workbook def get_info(path): wb load_workbook(path, read_onlyTrue, data_onlyTrue) sheets [] for ws in wb.worksheets: sheets.append({ sheet: ws.title, rows: ws.max_row, cols: ws.max_column, first_row: [cell.value for cell in next(ws.iter_rows(max_row1))] }) print(json.dumps(sheets, ensure_asciiFalse, indent2)) def analyze(path, sheet_nameNone): df pd.read_excel(path, sheet_namesheet_name, engineopenpyxl) # 清洗去空行空列 df df.dropna(howall).dropna(axis1, howall) # 数值列描述统计 numeric_summary df.describe(includenumber).T.to_dict() # 分类列统计 categorical_summary {} for col in df.select_dtypes(includeobject).columns: categorical_summary[col] { unique: df[col].nunique(), top10: df[col].value_counts().head(10).to_dict() } output {shape: df.shape, numeric: numeric_summary, categorical: categorical_summary} print(json.dumps(output, ensure_asciiFalse, indent2, defaultstr)) if __name__ __main__: cmd, path sys.argv[1], sys.argv[2] if cmd info: get_info(path) elif cmd analyze: sheet sys.argv[3] if len(sys.argv) 3 else None analyze(path, sheet)第二个关键设计是“永远把脚本放在skill目录里通过$SKILL_DIR引用”。为什么因为skill目录会跟着opencode配置走不管在哪个项目路径下打开终端都能稳定找到脚本。如果你把脚本丢在某个项目文件夹里换一个项目就得重写路径skill就无法复用了。另外脚本输出格式选择了JSON。原因是模型处理JSON比处理自由格式文本稳得多字段可预测后续转成Markdown表格也方便。4. 实操全过程用skill分析一份xlsx4.1 发起任务让模型自动命中skill到这一步环境、技能包、脚本都齐了开跑。我拿一份模拟的“销售明细.xlsx”做演示里面有3个sheet订单明细、客户信息、退货记录。先在xlsx所在目录启动OpenCodecd /path/to/xlsx opencode然后在对话里直接说用xlsx-analysis这个技能帮我分析一下销售明细.xlsx重点看订单明细这个sheet看看最近的销售趋势和TOP10客户。留意我的措辞我不仅说了“分析”还点名了xlsx-analysis这个skill。这样做的好处是双保险——即使description触发失败模型也能根据直接指令找到skill目录。模型收到请求后会先定位skill并读取SKILL.md然后按指令第一步执行python3 ~/.config/opencode/skill/xlsx-analysis/analyze_xlsx.py info 销售明细.xlsx返回的JSON会明确列出3个sheet名称、每个sheet的行数和列数。模型看完概况后自己会判断“订单明细”这个sheet有18000行、16列符合用户预期于是进入下一步。4.2 数据概况、清洗、统计的完整链路接着模型执行清洗逻辑。用脚本里的analyze命令跑了一遍发现这几点问题订单日期列有35个缺失值模型先把这些行标记出来没有直接删而是提示用户确认。金额列有12个值存成了文本型数字脚本做了强制转换并保留了原始值供排查。存在7行完全重复的订单数据模型向用户确认后删除。这段交互非常关键。以前没有skill的时候模型可能会自作主张把缺失值填掉、把重复行删掉用户根本不知道数据被动过手脚。现在SOP里明确写了一句“删除前先确认”模型就会停下来问。这是一个很小的设计但真实场景里能救你很多次。清洗完成后模型继续跑统计。订单明细表的金额列统计结果是总销售额约327万客单价215.4元订单量15200单。TOP10客户贡献了32.6%的销售额。月度趋势上12月销量在全年中的占比明显高于其他月份。模型把这些结论全部用具体数字呈现在Markdown里没有一句“看起来增长了”这种模糊表述。这也要归功于SKILL.md里的那句“必须引用具体数字不得模糊描述”。4.3 结果输出与人工复核分析完模型生成了一份完整的Markdown报告包含数据概览表sheet名、行列数、缺失值清洗日志删了几行、改了几列类型关键指标表销售额、订单量、客单价TOP10客户排序表月度趋势说明用户如果要求把清洗后的数据存成新文件模型会调用脚本逻辑用openpyxl写回一个新的销售明细_cleaned.xlsx原文件完全不动。最后也是最重要的一步人工复核日期字段。我在实操中发现xlsx里的日期一旦被pandas解析成时间戳显示格式和时区经常有偏差。比如2024年1月2日有时候解析出来变成2024-01-01 23:30:00原因是Excel日期序列号的时区换算。这种问题模型自己发现不了你得抽查几行原始数据对比一下。5. 常见问题与排查技巧5.1 问题速查表下面这些坑都是我实际跑过之后总结出来的按出现频率排序现象可能原因解决办法脚本报ModuleNotFoundError: openpyxl没安装依赖库pip install openpyxl pandasskill没有被触发模型直接瞎猜description关键词覆盖不够在请求里明确提到skill名或扩充description中的触发词日期列全部变成数字序列号Excel日期底层就是序列号用pd.to_datetime转换别在read_excel阶段慌合并单元格造成表头NaNpandas默认不填充合并区域在清洗步骤里加ffill逻辑按行向下填充读取.xlsm提示安全警告xlsm包含宏read_excel读取受限如果只是数据可以另存为xlsx再用别直接分析宏Windows路径带反斜杠导致报错反斜杠在Shell里被转义用正斜杠/或对路径加引号文件很大但内存飙高xlsx里有大量XML样式、公式缓存read_excel时用usecols限定列或先用info确认规模模型把数值四舍五入后失真统计结果精度不足在SKILL.md里注明保留两位小数必要时原样输出输入opencode命令无效安装目录未加入PATH重开终端或手动添加npm全局路径免费档位调模型报provider错误模型服务不允许外部直接调用确认模型渠道来源以及是否必须在opencode内触发5.2 经验心得除了上面的速查表再说几条“常规文档里看不到”的经验。第一skill目录里务必备一份requirements.txt并把安装命令写进SKILL.md里。换个机器跑的时候模型看到SKILL.md里的说明会自己去装依赖不用你手动折腾。我一般在SKILL.md末尾加一行“如果遇到依赖缺失执行pip install -r $SKILL_DIR/requirements.txt”。第二别让skill包办所有事。我一开始的版本把“生成图表”也做进了skill里结果模型一会儿用matplotlib一会儿用plotly样式飘忽不定。后来我把图表相关指令从SKILL.md里删掉只保留“如果需要可视化建议使用matplotlib并输出png文件”反而效果更好。skill的指令越聚焦模型的发挥越稳定。第三警惕“过度清洗”。数据分析新手总巴不得把数据洗得干干净净再动手但真实业务数据往往带着脏而且有些“缺失值”本身就是信息——比如“退货记录”sheet里没有退货日期说明这批货还没退回来。我刚开始写skill时命令模型“补齐所有缺失值”结果把大量实际含义为“无”的空白格填成了0差点导致统计结论错误。现在的SOP改成了“先分类缺失原因业务层面缺失还是录入层面缺失再决定填充或删除”。第四xlsx存储膨胀的问题值得单独说一句。你可能会遇到一个文件没多少数据体积却有几十MB的情况。这不是数据量大而是XML内部不干净——大量单元格样式、条件格式、公式缓存、历史格式记录塞在容器里。分析之前不要解析整个文件先用脚本的info命令读结构再决定是否需要限定usecols或按块读取。处理完干净数据另存为一个新xlsx体积立刻能缩到原来的几十分之一。6. 扩展建议这套玩法还能往哪里走xlsx分析这个skill跑通之后很容易横向扩展出一整套数据处理技能包。我现在自己维护的skill目录里除了xlsx-analysis还挂了几个关联技能csv-cleaner专门处理CSV的编码、分隔符、大小写清洗活得比Excel还频繁。>