前段时间频繁刷到 claude skills 的技术分享,大都是说这玩意有多牛逼。于是周日我打算学一学。花了点时间看了官方的文档后,打算结合业务实际演练下。先是网上各种找实战分享,不是官方文档翻译就是想看更多卖课的。突然想到自己最近在研发如何整理发票,那能不能做一个根据商品名称推荐发票分类的 ai agent。说干咱就干!
看完这篇博客你将学到:
- claude skills 的实际应用
你需要有的技能:
使用过 claude code
一、方案演进
首先我拥有的材料:
- 从国家税务总局下载的【商品和服务税收分类编码表】excel 格式,如下:
- 把 excel 的 的分类按照编码层级整理成了一个 json 树categories.json 格式,如下:
方案一:直接询问
要实现我们的需求,其实也很简单,直接基于 claude code 文件询问,如下:
方案一的缺点:
- category.json 文件有 64486 个字符,每次询问至少消耗的 token 已经被文件字符数锁定,这不符合我们出方案的基础规则:控制问题规模。category.json也可能是 60w 个字符,600w 个字符,打到模型的上下文吞不下。
方案二:使用Claude skills,将发票查询打包成技能
先不要问 skills 有什么用,那个看官方文档就可以,当然也可能看了官方文档也不清楚,跟着做,做完就清楚了!
这里我只会将这个项目中用到的相关知识,各种毛细知识可以查看 官网文档,中文的
- claude code 的 skills 定义在项目目录的".claude/skills"文件夹,比如下面我这里有两个 skills:
- 第一个是画图表的 skill
- 第二个skill 就是根据商品名称查分类的 skill
- 现在我们手工新建一个 skill
mkdir -p .claude/skills/product-category-matcher
2.1 编写 SKILL.md
---
name: product-category-matcher
description: 从用户输入的自然语言中识别出商品或商品分类,匹配 发票分类树,返回匹配的Top3分类,适用场景:(1) 用户描述想要采购的物品,(2) 用户提及某类产品需要查询。触发词包括:查找分类、匹配分类、什么分类、属于哪类、推荐分类等。
---
# 商品发票分类匹配器
根据用户输入的自然语言,从发票分类树中推荐最匹配的 Top3 个分类,严格按照输出格式输出。
## 数据结构
分类树
references/
├── categories.json #发票分类树json
文件结构:
[
{
"code": "1000000",
"name": "货物",
"level": "pian",
"children": [...]
}
]
层级:pian(篇) > lei(类) > zhang(章) > jie(节)
## 匹配流程
1. **提取关键词**:从用户输入中提取商品关键词
2. **加载发票分类树**:加载 categories.json 文件
3. **精确匹配**:在数据中搜索最匹配的分类
4. **返回结果**:输出 Top3 个推荐分类
## 匹配策略
按优先级排序:
1. **精确匹配**:分类名称与用户输入完全一致
2. **包含匹配**:分类名称包含用户关键词,或用户输入包含分类名称
3. **语义匹配**:基于语义理解的相似度匹配(如"电脑"匹配"计算机")
4. **上下文推断**:根据行业、场景推断可能的分类
## 输出格式
{
"recommendations": [
{
"rank": 1,
"code": "1020304",
"name": "分类名称",
"path": "货物 > 大类 > 中类 > 小类",
"confidence": "high|medium|low",
"reason": "匹配原因说明"
}
]
}
2.2 创建references文件夹
2.3 将categories.json文件放入references文件夹。
2.4 完成!
现在我们来使用这个技能,如图,我们输入“查询可口可乐的发票分类” claude code 发现并询问我们是否使用这个 skill,选 2:Yes, and don't ask again...
claude code加载categories 文件并输出的结果。
总结:
- 我们知道了 claude skills 其实就是沉淀我们一个已经成熟的技能,这个技能可以是“发票分类查询”,也可以是“项目整理架构介绍”,也可以是“使用mermaid绘制流程图(中文纠错)”,我们描述清楚 skill 的使用场景和使用方法,claude code 会在需要的时候主动调用工具。
- skill 是工具主动调用的,调用的好不好有的时候与取决于模型的能力。
- 我演示的时候使用的模型是 glm4.6
方案三:优化,使用渐进式加载节省 token,提高稳定性
方案二相对于方案一,就是把方案一打包成的技能,但是在 token 使用上并没有节省。本方案我们来优化下:
我们观察,我们的数据是高度结构化、层次化的,这是我们优化的基础。
我们发现可以把唯一的category.json按照二级分类来拆分为不同的分片文件。然后写一个前两级分类的索引文档.MD,让 ai 先根据较小的 索引文档.MD 确定分类,然后加载命中的具体文件识别具体的分类。
这个步骤甚至都不需要自己做,既然你使用了 claude code,让它写一个脚本,按这种规则拆分就行。比如我拆分成如下结构
代码分享在最后
_index.md文件的内容
# 分类分片索引
本目录包含按二级分类(类)拆分的 JSON 文件。
> 去重统计:原始 390 个叶子节点,去重后 359 个,移除 31 个重复项
## 使用方法
1. 根据用户输入的关键词,在下表中找到匹配的分类
2. 加载对应的 JSON 文件进行精确匹配
## 分片列表
### 货物
| 编码 | 名称 | 文件 | 节点数 | 关键词 |
| ------- | ---------------- | ----------------------- | ------ | ------------------------------------------------------------ |
| 1010000 | 农产品 | `农产品.json` | 32 | 农业产品、动物及其产品、林业产品、油料、活家禽、活牲畜 |
| 1020000 | 矿产品 | `矿产品.json` | 22 | 原油、原盐、天然气、有色金属矿石、液化天然气、煤炭 |
| 1030000 | 食品类产品 | `食品类产品.json` | 39 | 农副食品、方便食品、烟草制品、焙烤食品、糖果类食品、薯豆加工品 |
| 1040000 | 纺织服装皮革产品 | `纺织服装皮革产品.json` | 9 | 帽子、服装、服装鞋帽、皮革毛皮制品、纺织产品、鞋 |
| 1050000 | 木制品 | `木制品.json` | 8 | 家具、家具及配件、家具配件、木制品、棕藤草制品、竹制品 |
| 1060000 | 纸印刷品文教产品 | `纸印刷品文教产品.json` | 25 | 保健休闲用品、印刷品、墨水、工艺品、教具、文具 |
| 1070000 | 石化医药产品 | `石化医药产品.json` | 46 | 中药饮片、人造能源、化学原料及制品、化学纤维、化学药品制剂、化学药品原药 |
| 1080000 | 金属及非金属制品 | `金属及非金属制品.json` | 19 | 有色金属冶炼压延品、核燃料、稀有稀土金属、贵金属、金属制品、非金属矿物制品 |
| 1090000 | 机械设备 | `机械设备.json` | 155 | 专用设备、交通运输设备、仪器仪表办公用机械、其他机械设备、发动机、发电机 |
| 1100000 | 电力热力水燃气 | `电力热力水燃气.json` | 9 | 冷气、发电及供电、水冰雪、热力、燃气、电力热力冷气 |
### 劳务
| 编码 | 名称 | 文件 | 节点数 | 关键词 |
| ------- | ---- | ----------- | ------ | ------ |
| 2010000 | 劳务 | `劳务.json` | 2 | 劳务 |
### 销售服务
| 编码 | 名称 | 文件 | 节点数 | 关键词 |
| ------- | -------- | --------------- | ------ | ------------------------------------------------------------ |
| 3010000 | 运输服务 | `运输服务.json` | 3 | 运输服务 |
| 3020000 | 邮政服务 | `邮政服务.json` | 4 | 邮政服务 |
| 3030000 | 电信服务 | `电信服务.json` | 3 | 电信服务 |
| 3040000 | 现代服务 | `现代服务.json` | 24 | 人力资源服务、企业管理服务、会展服务、信息技术服务、商务辅助服务、广告服务 |
| 3050000 | 建筑服务 | `建筑服务.json` | 2 | 建筑服务 |
| 3060000 | 金融服务 | `金融服务.json` | 6 | 保险服务、金融服务 |
| 3070000 | 生活服务 | `生活服务.json` | 9 | 住宿服务、旅游服务、生活服务、餐饮服务 |
### 无形资产
| 编码 | 名称 | 文件 | 节点数 | 关键词 |
| ------- | -------- | --------------- | ------ | -------- |
| 4010000 | 无形资产 | `无形资产.json` | 4 | 无形资产 |
### 不动产
| 编码 | 名称 | 文件 | 节点数 | 关键词 |
| ------- | ------ | ------------- | ------ | ------ |
| 5010000 | 不动产 | `不动产.json` | 4 | 不动产 |
### 不征税项目
| 编码 | 名称 | 文件 | 节点数 | 关键词 |
| ------- | ---------------------------- | ----------------------------------- | ------ | ------ |
| 6010000 | 预付卡销售 | `预付卡销售.json` | 1 | - |
| 6020000 | 房地产预收款 | `房地产预收款.json` | 1 | - |
| 6030000 | 补开营业税发票 | `补开营业税发票.json` | 1 | - |
| 6040000 | 代收印花税 | `代收印花税.json` | 1 | - |
| 6050000 | 代收车船使用税 | `代收车船使用税.json` | 1 | - |
| 6060000 | 融资性售后回租承租方出售资产 | `融资性售后回租承租方出售资产.json` | 1 | - |
| 6070000 | 资产重组涉及的不动产 | `资产重组涉及的不动产.json` | 1 | - |
| 6080000 | 资产重组涉及的土地使用权 | `资产重组涉及的土地使用权.json` | 1 | - |
| 6090000 | 代理进口免税货物货款 | `代理进口免税货物货款.json` | 1 | - |
| 6100000 | 有奖发票奖金 | `有奖发票奖金.json` | 1 | - |
| 6110000 | 不征税自来水 | `不征税自来水.json` | 1 | - |
| 6120000 | 建筑服务预收款 | `建筑服务预收款.json` | 1 | - |
---
SKILL.md 文件加上加载索引的说明
---
name: product-category-matcher
description: 从用户输入的自然语言中识别出商品或商品分类,匹配 发票分类树,返回匹配的Top3分类,适用场景:(1) 用户描述想要采购的物品,(2) 用户提及某类产品需要查询。触发词包括:查找分类、匹配分类、什么分类、属于哪类、推荐分类等。
---
# 商品发票分类匹配器
根据用户输入的自然语言,从发票分类树中推荐最匹配的 Top3 个分类,严格按照输出格式输出。
## 数据结构
分类数据按二级分类(lei)拆分存储,减少单次加载的数据量:
references/
├── categories/
│ ├── _index.md # 分片索引(必读)
│ ├── _manifest.json # 分片清单
│ ├── 1010000-农产品.json # 按二级分类拆分的数据
│ ├── 1020000-食品饮料.json
│ └── ...
└── categories-index.md # 总体概览索引
分片文件结构:
{
"parent": {"code": "1000000", "name": "货物", "level": "pian"},
"category": {
"code": "1010000",
"name": "农产品",
"level": "lei",
"children": [...]
}
}
层级:pian(篇) > lei(类) > zhang(章) > jie(节)
## 匹配流程
1. **提取关键词**:从用户输入中提取标的物关键词
2. **查阅分片索引**:读取 `references/categories/_index.md`,根据关键词定位可能的二级分类
3. **加载目标分片**:只加载匹配的 1-3 个分片 JSON 文件
4. **精确匹配**:在分片数据中搜索最匹配的分类
5. **返回结果**:输出 TopN 个推荐分类
**重要**:不要一次性加载所有分片,只加载与用户查询相关的分片文件。
## 匹配策略
按优先级排序:
1. **精确匹配**:分类名称与用户输入完全一致
2. **包含匹配**:分类名称包含用户关键词,或用户输入包含分类名称
3. **语义匹配**:基于语义理解的相似度匹配(如"电脑"匹配"计算机")
4. **上下文推断**:根据行业、场景推断可能的分类
## 输出格式
{
"query": "用户原始输入",
"keywords": ["提取的关键词"],
"recommendations": [
{
"rank": 1,
"code": "1020304",
"name": "分类名称",
"path": "货物 > 大类 > 中类 > 小类",
"confidence": "high|medium|low",
"reason": "匹配原因说明"
}
]
}
## 示例
**输入**:我想采购一批办公用的激光打印机
**输出**:
{
"query": "我想采购一批办公用的激光打印机",
"keywords": ["激光打印机", "办公", "打印机"],
"recommendations": [
{
"rank": 1,
"code": "1090504",
"name": "打印机",
"path": "货物 > 办公消耗用品及类似物品 > 办公设备 > 打印机",
"confidence": "high",
"reason": "精确匹配'打印机'"
},
{
"rank": 2,
"code": "1090501",
"name": "办公设备及配件",
"path": "货物 > 办公消耗用品及类似物品 > 办公设备 > 办公设备及配件",
"confidence": "medium",
"reason": "上级分类,覆盖更广"
}
]
}
## 注意事项
- 优先推荐最细粒度(jie 节)的分类
- 同时提供上级分类作为备选,增加容错性
- 当匹配不确定时,说明置信度为 low 并解释原因
- 如用户输入过于模糊,可请求补充信息
再次执行,发现 ai 已经按需加载文件了。
总结:
- 这里我们主要是要打开思路,对结构化的上下文进行分片和按需加载,后续碰到大上下文的场景时候都可以考虑能不能用这样的方式处理。
- 没一个新的事物产生,这些事物的推崇者都会热衷于创造一些新词,这些新词其实就是新瓶装旧酒。不要被它吓到。
github 链接:github.com/roylee1024/…