做智能硬件售后这两年,最让我头疼的其实不是硬件本身,而是每天涌入客服后台那一堆措辞各异的用户反馈。“手表连不上蓝牙咋办”“表盘怎么不刷新了”“充满电两天就没了”……同一类问题,一百个人能有一百种说法。后来我接了大模型API做自动化分类,把这些问题按类别自动打标,再接上工单路由和问题热度统计,整个售后数据一下就顺了。这篇就用“智能手表用户问题自动分类”这个例子,把从分类体系设计、提示词编写到批量调用落地的完整过程,连同我踩过的坑一起分享出来。适合正在做智能硬件客服系统、售后数据分析,或者想用大模型API做文本分类的开发者参考。
1. 智能手表售后单自动分类:先想清楚为什么
1.1 用户反馈长什么样:口语化、碎片化、主题混杂
智能手表的用户问题来源很杂:App内置反馈入口、电商平台评价、客服聊天记录、社区帖子、公众号留言。这些文本的共同特征,我总结成三个字:糙、散、混。
“糙”是口语化严重。专业客服会写“设备连接异常”,用户只会写“又断了”“连不上”“搜不到手表”。“散”是信息碎片化,有人一句话带过“表盘不动了”,有人会扯一大段今天几点做了什么运动然后才说“数据没同步”。“混”是最麻烦的,一条消息里往往同时包含多个主题:“手表最近特别费电,而且微信消息一直收不到,是不是系统更新闹的?”这里面既有battery(续航充电)、notify(通知提醒),又有system(系统软件)。人工要拆开处理,模型也要学会抓主要焦点。
如果靠人工逐条看,一天几百条还能扛,到几千条时分类标准就开始漂移。上午把“续航短”归到“电池故障”,下午可能就归到“性能问题”。更糟糕的是,不同客服对同一条问题的归类可能完全不一样,导致月底统计各类问题的占比时,数据连自己人都不太信。我见过某团队做月度质量报告,光是“连接类”问题的占比,两个客服复核同一批工单就差出十个百分点,根源就是分类口径没有统一。
所以自动化分类的第一价值,不是省掉一个客服,而是把分类标准固定下来,让数据口径一致。同样一批问题,今天跑和三个月后跑,分出来的类别是一样的。只有保证口径稳定,才能去做趋势对比、问题热度排名、故障归因这类下游分析——那才是售后数据真正值钱的地方。
1.2 为什么不用关键词规则或传统分类模型
先聊关键词规则的局限。它确实适合“蓝牙”“充电”“表盘”这种强标志词,但用户表达是无穷尽的。我见过一个真实例子:“手表莫名其妙自己重启”和“用着用着就黑屏了,然后又亮了”,前者你能靠“重启”命中开关机异常,后者没有“重启”两个字,很难靠关键词覆盖。规则表会越维护越长,后面一个词可能要挂七八个否定语境,误判率反而拉高,维护成本也上去了。关键词规则还扛不住口语里的变形,比如“连不上”和“连部上”“连步上”这种错别字组合,规则匹配基本束手无策。
再说传统文本分类模型。智能手表这类硬件是典型的“问题和类目快速变化”场景,这周出一个新固件,下周可能就多一类“升级后数据丢失”问题。传统方案需要先标注训练集,比如8个类别每类至少几百条样本,标注成本先不提,训练、验证、上线的周期也不短。等你模型训好,用户的关注点可能已经变了,又要重新标注,整个迭代节奏根本跟不上硬件产品的发版速度。
大模型API的优势在于零样本和少样本分类。你把分类体系写清楚,扔两三个示例,它就能理解语义并输出结构化结果。不需要提前准备训练样本,不维护规则表,改分类体系只需要改提示词,一次改动全量生效。代价也明确:按token计费,单条有毫秒到秒级延迟,输出偶尔不稳定需要校验。但从落地角度看,对每天几百到几千条工单的规模,大模型API在开发效率和分类质量上都是很划算的选择。三种方案我放在一起比较过:
| 方案 | 开发成本 | 维护成本 | 语义理解 | 迭代速度 | 适合场景 |
|---|---|---|---|---|---|
| 关键词规则 | 低 | 高 | 弱 | 慢 | 问题模式高度固定 |
| 传统分类模型 | 高 | 中 | 中 | 慢 | 样本量大且类目稳定 |
| 大模型API | 低 | 低 | 强 | 快 | 类目变化快、语义复杂 |
问题越接近“表达自由无序”,越适合大模型方案。智能手表用户提问恰恰就是这种自由无序的状态。
2. 分类体系与提示词设计:决定效果上限
2.1 8个一级类别的设计与互斥原则
分类体系是整个方案的灵魂。大部分分类不准的问题,根源不在模型,而在分类定义本身有歧义。最初我把类别拆得很细,搞了十几个:蓝牙问题单独一类、充电问题单独一类、心率问题单独一类……结果模型和我自己都晕,两个类别的描述乍一看都沾边,输出就开始随机了。
后来收敛成8个一级类别。这个数量对模型来说是舒适的——候选空间太大,判断容易出歧义;候选空间太小,又失去了分类的区分度。8个类别正好覆盖智能手表售后最常见的主题域。
| 类别编码 | 类别名称 | 典型问题示例 |
|---|---|---|
| conn | 连接与通信 | 蓝牙配对失败、频繁断连、Wi-Fi连不上 |
| battery | 续航与充电 | 耗电太快、充不进电、充电器不识别 |
| health | 健康监测 | 心率/血氧/睡眠数据异常、运动记录缺失 |
| notify | 通知与提醒 | 消息不推送、闹钟不响、来电提醒失灵 |
| system | 系统与软件 | App闪退、固件升级失败、表盘不刷新 |
| hardware | 硬件故障 | 屏幕碎裂、按键失灵、表带断裂、震动异响 |
| purchase | 购买与售后 | 退换货、配件购买、保修期查询、发票 |
| other | 其他 | 无法归入以上类别的问题 |
设计时三个原则很重要。第一是互斥:类别之间尽量不重叠,比如“充电触点生锈导致充不进电”既沾battery又沾hardware,我会在提示词里加优先级规则,明确“焦点”的裁量方式。第二是兜底:other类别不是摆设,它让模型拿不准时可以不必硬选,减少强行分类带来的噪声,后续把这些other数据定期捞出来看,往往能发现新的问题主题。第三是类别描述不要只给四个字,要带上高频场景词,相当于帮模型缩小候选空间,比如conn后面跟着“蓝牙、Wi-Fi、配对、断连、搜索不到”。
分类体系的版本迭代也要纳入工作流。每一两周把复核记录里被纠正的样本拉出来,如果某类问题反复被纠正,就说明类别定义或优先级需要调整。分类体系不是一次定死的,它是活的东西,会随着产品迭代和用户行为变化而演进。
2.2 稳定输出JSON的提示词写法
提示词我改过很多版,最终固定成三个部分:角色设定、分类规则、输出要求。
角色设定是“你是一个智能手表售后工单分类助手”,作用是给模型一个语境锚点,让它之后的判断角度和用词都贴近售后场景。分类规则部分写清每个类别的含义、典型场景,以及上面的互斥和优先级原则。输出要求是核心,明确写“只输出JSON对象,不要解释”,包含category、confidence、reason三个字段。
我最初没有要求结构化输出,模型会先分析一番再解释,然后甩给你一大段文字,解析起来很折腾。改成“只输出JSON对象”之后好很多。但要注意,部分接口的模型可能在JSON外面套markdown代码块,或者输出多余换行,所以解析函数里必须做容错,先把外层反引号剥掉再json.loads。这个防御式解析不是可选项,我在后面代码部分会细说。
还有两个细节直接影响稳定性。一个是temperature必须显式设为0,分类任务要的是确定性,不是创造性。哪怕设到0.3,同一句话跑两次都可能出现不同的类别归属;设到0之后,只要提示词不变,结果基本是确定的,这对后续做可复现测试和置信度分析非常重要。另一个是few-shot示例不要追求多,两三条就够,但要有代表性:一条明确归属、一条容易混淆、一条多主题需要裁决。比如“手表能充电但两天就没电”归battery,“充不进电而且充电口有烧焦味”按优先级归hardware。模型从这两个例子里学到的,是你处理边界问题的裁决偏好。
3. 从请求封装到批量落库:完整代码实践
3.1 环境准备:用requests直接调兼容接口
依赖只需要两个,装包命令:
pip install requests python-dotenvrequests用于调用API,python-dotenv用于读取环境变量。我特意不用厂商SDK,直接用requests构造HTTP请求。原因是现在主流大模型API基本都兼容同一套对话接口格式,直接写请求的方式换平台只改base_url和key就行,不绑定某一家,也少一层SDK升级带来的兼容问题。对文本分类这种简单场景,裸请求反而最省心,出问题的时候也更方便抓包排查。
配置写在.env文件里:
LLM_API_BASE=https://your-api-endpoint.example/v1 LLM_API_KEY=your-api-key LLM_MODEL=fast-modelLLM_API_BASE的路径形态要注意,有些平台要求写完整到/v1,有些后端本身带了路径,建议在代码里统一用rstrip("/")再拼接,避免出现双斜杠导致404。另外不要把Key写进代码里,更不要提交到仓库,环境变量是最基本的底线。
3.2 单条分类函数与JSON防御式解析
单条分类是整个流程最基础的单元。把system prompt、用户问题和参数封装成一个函数:
import os import json import time import requests from dotenv import load_dotenv load_dotenv() API_BASE = os.getenv("LLM_API_BASE").rstrip("/") API_KEY = os.getenv("LLM_API_KEY") MODEL = os.getenv("LLM_MODEL", "fast-model") SYSTEM_PROMPT = """ 你是一个智能手表售后工单分类助手。请把用户反馈的问题归入以下类别之一: - conn: 连接与通信(蓝牙配对、断开、Wi-Fi连接、设备搜索不到) - battery: 续航与充电(耗电快、充不进电、充电器异常、电池不耐用) - health: 健康监测(心率、血氧、睡眠、压力、运动数据异常) - notify: 通知与提醒(消息推送收不到、闹钟不响、来电提醒失灵) - system: 系统与软件(App闪退、固件升级失败、表盘不刷新、设置异常) - hardware: 硬件故障(屏幕碎裂、按键失灵、表带断裂、震动异常、充电口损坏) - purchase: 购买与售后(退换货、配件购买、保修查询、发票问题) - other: 其他 注意事项: 1. 当问题同时涉及多个类别时,选择用户表达焦点的那个类别。 2. 如果同时涉及硬件损坏和功能异常,焦点偏向"坏了"时归hardware,偏向"用不了"时归对应功能类别。 3. 用户问题中含情绪化表达不影响分类,只根据事实判断。 输出要求:只输出JSON对象,不要解释,格式如下: {"category": "类别编码", "confidence": 0.0到1.0之间的数字, "reason": "一句话判断理由"} 示例: 用户问题:昨晚刚买的手表,今天蓝牙一直断,能不能换一台 输出:{"category": "purchase", "confidence": 0.7, "reason": "问题提到刚购买且询问换货,焦点在售后而非连接"} 用户问题:充一夜电早上还是没电,充电器指示灯也不亮 输出:{"category": "battery", "confidence": 0.9, "reason": "描述充电无效果,属于续航充电问题"} """ def parse_llm_json(content): """解析模型输出,兼容代码块包裹、多余换行等情况""" content = content.strip() if content.startswith("```"): lines = content.splitlines() content = "\n".join(lines[1:]) if content.endswith("```"): content = content[:-3] return json.loads(content) VALID_CATEGORIES = {"conn", "battery", "health", "notify", "system", "hardware", "purchase", "other"} def normalize_result(result, question): """字段校验和兜底,保证下游拿到的数据是干净结构""" category = result.get("category", "other") if category not in VALID_CATEGORIES: category = "other" try: confidence = float(result.get("confidence", 0)) except (TypeError, ValueError): confidence = 0.0 if confidence < 0 or confidence > 1: confidence = 0.0 reason = str(result.get("reason", ""))[:200] return {"category": category, "confidence": confidence, "reason": reason} def classify_question(question, timeout=15, retries=3): payload = { "model": MODEL, "messages": [ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": f"用户问题:{question}"} ], "temperature": 0, "response_format": {"type": "json_object"} } headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json" } url = f"{API_BASE}/chat/completions" for attempt in range(retries): try: resp = requests.post(url, json=payload, headers=headers, timeout=timeout) resp.raise_for_status() content = resp.json()["choices"][0]["message"]["content"] result = parse_llm_json(content) return normalize_result(result, question) except Exception as e: print(f"第{attempt + 1}次请求失败: {e}, 问题原文: {question}") time.sleep(2 * (attempt + 1)) return {"category": "other", "confidence": 0.0, "reason": "API调用失败"}几个细节值得展开。
response_format设置为json_object,很多兼容接口都支持,能显著提升JSON输出稳定性。如果你的接口不支持这个字段,删掉不影响主流程,但解析函数必须做容错。
模型输出内容里,可能有代码块包裹、前后空格、多余换行,甚至偶尔在JSON后面跟一句废话。parse_llm_json只处理了最常见的代码块场景,更脏的情况建议在真实数据上跑一轮,把异常样本收集起来,再针对性补处理逻辑。normalize_result做白名单和范围校验,宁可把脏数据修正成other,也不能让它污染下游统计。
3.3 批量任务调度:限速、截断、断点续跑
批量处理不能无脑for循环发请求,要有三个基本控制:限速、超时、断点。
def batch_classify(questions, delay=0.2, output_path="classify_results.csv"): import csv fieldnames = ["id", "category", "confidence", "reason", "question"] with open(output_path, "w", newline="", encoding="utf-8-sig") as f: writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() for idx, q in enumerate(questions): if not q or not isinstance(q, str): row = {"id": idx, "category": "other", "confidence": 0.0, "reason": "输入为空", "question": q} writer.writerow(row) continue q = q.strip()[:500] result = classify_question(q) row = {"id": idx, "category": result["category"], "confidence": result["confidence"], "reason": result["reason"], "question": q} writer.writerow(row) time.sleep(delay) if (idx + 1) % 50 == 0: print(f"已处理 {idx + 1}/{len(questions)}")这段代码把三个关键点都覆盖了。
delay控制请求节奏。单线程下给0.2秒也就是每秒5条左右,先跑稳再逐渐加并发。千万不要一上来就开几十个线程,我遇到过接口返回限流后全批重试,反而比单线程慢几倍,白白烧了不少token。
截断逻辑放在调用之前。用户问题可能粘贴一大段日志或者无限重复字符,超出上下文限制会直接报错。500字符是我按智能手表售后问题特性定的经验值,足够覆盖完整描述,又不会浪费token。截断位置在字符串层面是最简单的,如果你懂一点分词,按语义边界截效果更好,但对这个场景差别不大。
断点续跑的实现方式就是“边跑边写”。每处理完一条就写进CSV,而不是最后一次性写文件。跑三千条数据如果在中途被限流打断,已处理的两千条都在文件里,补跑只需要重新处理剩余部分,不会前功尽弃。这个习惯我是在丢过一次三千条结果之后才养成的。
3.4 结果落库与热点聚合统计
分类完的数据不要只躺在内存里,我建议落成一张表,每条记录至少包含这些字段:id、分类、置信度、判断理由、问题原文。CSV用utf-8-sig编码,Excel直接打开不会乱码,团队里负责数据分析的同事拿过去就能用。
如果团队有数仓,这张表直接导入即可。没有数仓的话,一个小型SQLite或PostgreSQL表也够用。分类结果表最忌讳的是只存分类不存原文本,因为你迟早要回去看某个分类下的用户到底说了什么,没有原文本就只能重新调API。
有了这张表,第一个下游应用是类别分布统计,直接按category分组就能算。第二个更有价值的是热点问题聚合。同一类别下用户表达太散,直接按原文聚合看不出来趋势,我会额外让模型给每条问题生成一个3到5个字的“核心主题”,比如“蓝牙断连”“充电不进”“推送收不到”,再按核心主题做计数排序。这一步每条约多花十几个token,但能直接看出售后服务团队当前最该处理什么问题。
不过要提醒:热点聚合的结果仍然存在同义表述偏差,“收不到微信”“微信收不到消息”“微信不推送”可能是三行不同文本。如果要做更精确的趋势分析,可以对核心主题做一层简单的关键词归并,或者把核心主题也拿去让模型分类到预定义的热点清单里。简单场景做到核心主题这层就够用了,别为了精确把流程搞得过于复杂。
4. 实测效果、成本估算与翻车记录
4.1 一组真实问题的分类结果核对
拿一批脱敏后的智能手表用户问题做小规模测试,我人工核对了一遍,结果大致是这样:
| 用户原话 | 模型分类 | 置信度 | 我的核对结论 |
|---|---|---|---|
| 手表突然连不上手机蓝牙了,重启也没用 | conn | 0.96 | 正确 |
| 你们这血氧测得也太不准了吧,躺着不动都报警 | health | 0.93 | 正确 |
| 为了省电把息屏关闭了,结果抬腕亮屏也不行了 | system | 0.72 | 正确,焦点在设置功能 |
| 昨天激活的,今天发现表带裂开了,能补发一条吗 | hardware | 0.81 | 有争议,用户诉求是补发配件,更应归purchase |
| 刚用了三天,屏幕就出现一条竖线,看着闹心 | hardware | 0.97 | 正确 |
| 手机收不到微信通知,所有权限都开了 | notify | 0.95 | 正确 |
| 手表充不进电,而且充电器插上去没反应 | battery | 0.92 | 正确 |
| 升级完固件之后,抬手亮屏感觉变慢了 | system | 0.68 | 正确,关注的是升级后的表现 |
整体看,语义明确的单问题场景准确率很高;出错的多数集中在多意图混合和“关注点模糊”。比如表带裂开那条,模型按“损坏”归到hardware,但用户真实诉求是补发配件,从工单路由角度归purchase更合适。这类问题靠提示词优先级能改善一部分,但不能完全消除,所以低置信度人工复核是必要的兜底。
模型给出reason字段的价值就在这时候体现——哪怕分类不完全符合你的业务预期,你至少能看到它为什么这么分,是优先级规则没覆盖到,还是它理解错了焦点。我给reason加了200字符的长度限制,别让它在理由上写小作文,够看就行。
4.2 成本怎么算:单条token × 总量 × 单价
成本是很多人关心的点,我给出一个不绑定任何平台价格的通用估算方法。
先估单条token消耗。一条典型的智能手表售后问题文本平均80个字符,换算成token大约50个。系统提示词里类别规则加示例大概150到200个token,往高按200记。模型输出一个JSON结果,category加confidence加reason,平均约60个token。加起来单条约310个token。
这个估算是粗糙的,实际输入token受字符编码影响,中英文混排场景下波动很大。我跑过一批中文为主的数据,实际单条输入加输出平均在350到400个token之间。一万条问题就是350万到400万token。然后把所用API的输入单价和输出单价分别乘对应的token数量,相加就是总成本。以我碰到的常见量级来看,用快速小模型跑一万条大致在几十元人民币量级,远低于一个客服手工分类这些工单的人力成本,而且分类速度是分钟级的。
几个省钱的实用技巧一起说。第一,系统提示词精简,few-shot示例两三条就够,因为每条请求都会重复计费这部分token。第二,输入做硬截断,超长粘贴一律截到500字符以内。第三,相同问题加缓存,用户反馈重复率非常高,“连不上蓝牙”这类问题一个活动期能来几百条,按文本hash缓存分类结果,重复问题直接命中不请求API。第四,离线批量任务可以错峰跑,有些平台闲时价格更低,把非实时需求排到闲时段,成本能再降一截。
4.3 我踩过的四个典型坑
第一个坑是温度参数忘了设0。最初我沿用文本生成的默认配置,结果同一批数据跑两次,类别分布居然差了好几个百分点。排查了半天才发现是temperature没改。分类任务不是写作,温度越低越稳定,这个参数必须显式写上,不能依赖默认值。现在我的配置全局写死temperature=0,谁改配置谁自己负责回归验证。
第二个坑是JSON解析炸在markdown上。某次批量跑到一半,大量结果报JSON解析失败,打开原始输出发现模型在JSON外面包了三个反引号。从那以后我写死了清洗逻辑,并且在解析失败时把原始content原样打印出来,方便事后排查而不是直接丢数据。解析失败本身是重要信号,它往往意味着提示词或接口配置出了问题,不能只当异常忽略。
第三个坑是批次任务没有断点续跑。三千条数据跑了一千条,接口突然限流,程序一退出全白跑。虽然结果都打印在日志里,重新组织却很痛苦。后来改为每50条写一次CSV的边跑边写模式,再也没被这个问题坑过。写文件是廉价操作,多写几次不会有多少性能损失,换来的是进度可见和失败可恢复。
第四个坑是“问题焦点”的判断。用户说“手表进水了,屏幕花了,还能保修吗”,这涉及硬件故障和售后政策,不同的人分类可能都不一样。我现在的做法是给这类问题打“需要人工复核”标记,配合置信度阈值兜底,而不是强行让模型选一个。分类的目的是让大多数问题自动流转,少数存疑问题留给人工,这是设计目标,不是缺陷。别指望模型在模糊场景下能完美理解业务意图,学会让出必要的模糊地带才能减少整条链路的不确定性。
5. 从自动分类到智能工单处理
5.1 置信度阈值与人工复核兜底
置信度不是摆设。我实践下来的做法是三档处理:confidence大于等于0.85的结果直接进入自动统计和工单路由;0.6到0.85的进入待复核队列,由客服人工确认;低于0.6的在界面上标红,优先处理。复核确认的结果定期回灌成难例样本,用来迭代提示词,形成闭环。
阈值怎么定,取决于对误分类的容忍度。如果只是做问题热度统计,阈值可以放宽,因为个别错分不影响整体趋势。如果要接自动回复或工单路由,阈值就要收紧,错分意味着用户被转到错误流程,体验很差。最靠谱的定法是先拿历史数据画置信度分布曲线,看样本量在哪个区间出现明显拐点,再定阈值。这个动作建议上线前就做,比上线后发现分类不稳再回头调要省事得多。
置信度本身也不是完全可信的。有些模型对长文本、复杂文本会给出偏高的置信度,这时候reason字段能帮忙筛掉一部分“高置信度但判断离谱”的情况。我见过一条问题只写了“差评”两个字,模型给了0.9置信度归到other,理由是“无法从文本中识别具体问题”——这个判断本身没错,但如果客服系统把“差评”当成无关内容略过,就错过了用户不满的信号。所以对短文本要单独设一条规则:少于5个字符的问题强制进人工复核。
5.2 用难例持续迭代提示词
运行时最重要的优化手段不是换模型,而是收集难例迭代提示词。我每两周会看一次复核记录里被纠正的案例,找出模型反复出错的模式,然后把这类模式的处理规则写进提示词的注意事项里。
举一个实际例子。最初“手表进水”类问题模型经常归到hardware,但这类问题的售后处理路径是走进水检测和维修报价,跟普通硬件故障不一样。我在提示词里加了一条:“如果用户提到进水、摔坏、人为损坏等关键词,归入hardware,但reason里必须标注‘疑似人为损坏’。”之后再跑,这部分问题的分类一致性和可用性都提高了很多,下游审核的人不用每条都点开看原因。
这个迭代过程要注意控制提示词膨胀。规则越加越多,到某个阶段提示词会变得很长,每条请求的token成本上升、响应变慢,而且规则之间可能冲突。我给自己定了一条线:提示词超过1200个token时,必须做一次精简重构,把低频的规则挪到单独的后处理脚本里,而不是继续堆在提示词中。分类规则写进提示词是为了让模型理解语义边界,而不是把所有业务判断都塞给模型。
建议给提示词做版本管理。每次修改记录一下改动内容、触发改动的案例、前后效果对比。这个版本历史看起来很麻烦,但对排查“为什么这周结果和上周不一样”特别有用。我也见过团队用简单的Markdown文档记录每次prompt版本变更,效果不比专业工具差。
5.3 自动回复模板与问题热度预警
分类结果稳定之后,能做的事就不止统计了。同一类别的常见问题,可以接一个自动回复模板。比如conn类别命中“蓝牙配对失败”主题,自动回复先引导重新配对并清理旧配对记录;notify类别命中“微信收不到推送”,自动回复引导检查通知权限和电池优化白名单。这一步省下的客服时间非常可观,而且因为分类是稳定的,自动回复的话术边界也清晰,不太容易出现答非所问。
再往前一步是问题热度预警。按类别和核心主题做滚动统计,某主题问题数量在24小时内超过设定阈值就报警。我在实际项目中靠这个在“某次固件升级后表盘不刷新”的问题爆发初期就收到警报,在事态扩大之前把升级开关回退了。这种基于用户反馈的实时监测,比等投诉单累积起来再发现要快得多,这也是把自动化分类工程化之后最大的红利之一。
预警阈值也要动态调整。新品首发期用户问题基数大,静态阈值容易被刷爆;平稳期又容易因为阈值太高错过小波动。可以按近7天平均值的倍数来设定阈值,比如某主题当日数量超过近7日均值的3倍就报警,这样既适应产品周期,又不会太敏感。
我个人做这个项目最大的体会是:这类自动化分类的成败,七成在分类体系和提示词设计,三成在工程容错。很多人一开始盯着API调用代码调来调去,结果发现分类不准的原因是类别定义互相重叠;也有不少人只关注模型选型,忽略了输出解析和阈值兜底,导致线上跑几天全是脏数据。如果你想做类似的事,我建议从最简版本起步:8个类别、一个稳定的提示词、单线程批量调用、CSV落库,先跑一周人工核对结果,再决定要不要加并发、加缓存、加自动回复。等基础流程稳住了,再谈智能化程度也不迟。