语义打标签,不是关键词匹配
给 CSV / Excel 的某一列做语义打标签的 Claude Code Skill。 企微文档的智能表格、飞书的智能表格,都内置了"按某一列给行打标签"的能力 —— 可一旦你把同样的活交给 Claude 或 Claude Code,它几乎总是退化成关键词匹配:快是快,但准不起来。Column Classify 把这件事扳回正轨 —— 让模型真正读懂每一行的意思,再打标签。
不写脚本去 requests.post 调 API,不配 API key / base_url / model,不绑 provider。分类交给 Claude Code 会话里正在用的那个模型,按 batch 切片、并行派发子 agent 跑,跑完精确回写。整个 skill 只剩一个纯 I/O 助手脚本负责读写文件。
| 语义,不是关键词 | 判断"这段 query 是不是追问"、"这条 feedback 是正面还是负面"、"这个需求算不算开发类" —— 这些靠 contains("追问") 永远做不对。这里让模型逐行理解文本含义再判定。 |
| 零 API 配置 | 子 agent 用的是 Claude Code 当前会话的模型,跟着你的 /model 走。没有 token、没有 base_url、没有环境变量要配,换模型零改动。 |
| 并行批处理 | split 切片 → 一条消息里并发派发 N 个子 agent → 各自回吐 JSONL → cat 拼回。几百上千行不再串行磨。 |
| CSV & Excel 通吃 | 基于 pandas,.csv / .xlsx / .xls 原生支持,多列综合判断也行(--column A B)。 |
| 断点续跑 | --skip-column 只导出还没标完的行,中断了重跑不重复劳动、不覆盖已有结果。 |
| 精确对位回写 | 用原始行号 _row 对位,多批并行后回写不串行、不丢行。 |
| 单批退路 | ≤ 一批(~40 行)时不派子 agent,主线程直接内联分类,省一轮开销。 |
智能表格里的"自动打标签",底层几乎都是规则引擎 —— 包含某词、匹配某正则、命中某枚举。它适合结构稳定、措辞收敛的场景(订单状态、渠道来源)。但你要标的往往是自由文本:
| query | 关键词匹配 | 语义匹配 |
|---|---|---|
上面那个能改成降序吗 |
❌ 没关键词 → 漏 | ✅ 追问 |
这个颜色太深了,调浅一点 |
❌ 没关键词 → 漏 | ✅ 追问 |
帮我看一下这段报错是什么意思 |
✅ 独立需求 | |
帮我写一个快速排序算法 |
❌ 没关键词 → 漏 | ✅ 独立需求 |
关键词匹配的"准确率",本质是你事先穷举词表的能力 —— 而你永远穷举不完。语义匹配把这件事交给模型,一次判定标准,覆盖所有变体。
io.py extract → JSONL 行流(_row + 待分析列)
↓
split 切 batch(~40 行/批)
↓
并行派发 N 个子 agent(Agent 工具,一条消息并发)
↓ 每个 agent:判定标准 + 一批 JSONL → 只回 JSONL
汇总所有 agent 的 JSONL 输出
↓
io.py apply → 按 _row 精确写回结果列
子 agent 看不到你的文件系统,所以每个 batch 的 JSONL 是整段内联进 Agent 工具的 prompt 参数,不是传文件路径 —— 这是设计上最容易踩的点,SKILL.md 里写死了。
把整个文件夹放进 Claude Code 的 skills 目录:
git clone https://github.com/Wechat-ggGitHub/column-classify.git \
~/.claude/skills/column-classify
pip install pandas openpyxl # openpyxl 仅 Excel 需要装完在 Claude Code 里说一句"帮我分析 xxx.csv 里 query 列,找出哪些是追问",skill 会自动触发。也可以直接 /column-classify。
"帮我分析
feedback.csv的content列,给每条打情感标签(positive/neutral/negative),结果写进sentiment列。"
Claude Code 会按 skill 流程走:确认判定标准 → 导出 → 切批 → 并行子 agent → 回写 → 报分布。
# 导出未标注行
python3 ~/.claude/skills/column-classify/scripts/io.py extract \
--file data.csv --column query --skip-column label > rows.jsonl
# 切批
split -l 40 -d -a 1 rows.jsonl batch_
# (Claude Code)逐个读出 batch_*,连同判定标准 + 子 agent 模板填进
# 每条 Agent 工具调用的 prompt,一条消息并发派出。
# 每个 agent 回吐一段 JSONL,cat 在一起。
# 写回
cat results.jsonl | python3 ~/.claude/skills/column-classify/scripts/io.py apply \
--file data.csv --output-column label60 行带 ground truth 的中文 query 数据集(30 独立需求 / 30 追问):
| 方案 | 准确率 | good 召回 | bad 召回 |
|---|---|---|---|
关键词基线(朴素 5 词:追问/上面/刚才/再/改成) |
73.3% (44/60) | 100% | 47% |
| 关键词基线(针对性调到 15 词,盯着错例加词) | 96.7% (58/60) | 97% | 97% |
| Column Classify(语义,无歧义条款) | 96.7% (58/60) | 93% | 100% |
| Column Classify(语义,加一句歧义消解) | 98.3% (59/60) | 100% | 97% |
读法:朴素关键词只能抓到一半的追问(bad 召回 47%)—— 想追平语义,你得盯着错例不断往词表里加词(5→15),而且每来一种新说法就得再加一次。语义匹配用一条判定标准直接到顶。工程链路两轮零故障:JSONL 对位、行数完整、子 agent 输出干净可直接管道。复现脚本在 examples/reproduce_eval.py。
这个 skill 的准确率天花板,几乎完全取决于你写的判定标准写得多清楚。两条原则(写进了 SKILL.md):
- 先约定 label 取值集合(
good/bad还是独立需求/追问),别让子 agent 各自发挥。 - 消解歧义表达。同一个词在不同语境归不同 label 的,必须在判定标准里讲明 —— 比如"这段"既可指用户自己粘贴的内容(→ 独立需求),也可指前文 AI 的产出(→ 追问)。不写清楚,子 agent 会按字面倾向统一误判一整类样本。
column-classify/
├── SKILL.md # Claude Code 加载的 skill 定义(流程 + 子 agent 模板)
├── scripts/
│ └── io.py # 纯 I/O 助手:extract 导出 JSONL / apply 写回列,不调 API
└── examples/ # 测试集与复现脚本
欢迎 issue 和 PR。特别欢迎:
- 更多语言 / 领域的判定标准示例
- 更刁钻的歧义 case(带 ground truth)
- I/O 助手对其他格式(Parquet、JSON 数组、Google Sheets)的支持
MIT — 见 LICENSE。