1. 前言:为什么需要给工单打一条“合适的标签”
业务系统上线之后,客服、运维、研发每天都会碰同一个问题:工单越来越多,标签却越来越乱。同一个“App登录报错”的工单,客服可能标成“账号问题”,运维标成“认证异常”,研发标成“登录模块缺陷”。来源不同,口径不同,最后汇总报表的时候数据完全没法看。更麻烦的是,很多工单系统里标签是纯手填或半自动下拉,打标质量完全取决于当天值班人的心情。等到你想按标签统计趋势、定位高频缺陷、优化客服响应策略,才发现这些脏标签根本撑不起分析。
这几年大模型能力起来之后,很多人第一反应是“我直接把工单丢给通义千问提示它打标不就行了”。确实,通用模型的意图理解远比规则引擎灵活,它知道“登录失败”“密码不对”“验证码收不到”都属于认证相关。但真放到生产环境里,你会遇到几个问题:不同团队的标签体系是自定义的,通用模型未必知道你们的“P0-P3”意味着什么;模型输出格式不稳定,今天返回“账号异常:密码错误”,明天返回“账号异常-密码错误”,后天直接举一反三编一个你标签体系里根本不存在的类别;还有数据安全,工单里往往带用户电话、订单号,不能随便往外送。所以工程化的做法,是把一个小型语言模型在你们自己的工单历史数据上微调,用数据把“标签口径”和“输出格式”固定下来。
这篇文章记录的就是我近期跑通的完整流程:用阿里云百炼的 API 做指令微调,实现自动工单打标。从头到尾包括标签体系设计、训练数据构造、微调参数选择、模型部署、线上打标接入和效果评估,每一步都会说清楚我为什么这么选、踩过哪些坑。适合两类人看:一类是刚接手工单系统智能化改造、正在纠结“用通用大模型还是微调”的产品或研发;另一类是把大模型微调当黑盒、想找一份能直接照做的实操步骤,又不想自己从零搭训练集群的团队。
2. 项目整体设计与数据约束
2.1 需求拆解:从“打个标”到“可治理的工单打标”
工单打标表面上是分类问题,实际是一个“低容错 + 强规范”的文本处理任务。低容错体现在:打标的目的是后续统计和自动路由,标签错了,调度就错了,一个 P0 故障被标成 P3,响应时效就会出问题。强规范体现在:企业内部的标签体系必然是有限的、固定的,不会像开放问答那样可以自由发挥。
所以我在需求拆解阶段,没有先去找模型,而是先定了三件事:
- 标签体系必须扁平化,层级不超过两层,单层标签数量控制在 30 个以内;
- 每条工单的输出必须包含两个字段:
labels数组和confidence置信度; - 推理环境必须走 API,训练数据不能落私有化部署之外的地方。
选扁平化标签是为了降低训练难度。标签体系层级越深,模型需要记忆的边界条件越多,训练数据量不够时极易混淆。工单打标不像知识图谱分类需要极细粒度,大部分团队的业务场景,第一层分“前台用户问题 / 后台交易问题 / 财务问题 / 权限问题 / 系统性能问题”就已经足够路由,第二层再落到具体模块。30 个标签以内的规模,用几百到一两千条标注样本就可以微调出可用效果,部署成本和维护成本都可控。
输出带置信度也很关键。打标系统不是替代人,而是辅助人,置信度低的时候可以把工单转给人审。这个字段我一开始没设计,后来发现线上总会冒出来训练集没覆盖的工单类型,没有置信度兜底,系统会自信地乱标。加了这个字段之后,人工审核的目标就明确了:只查置信度小于阈值的工单。
2.2 为什么是微调而不是只改提示词
这个决策直接影响整个项目走向。最初我们试过用现成大模型的 prompt 打标,样例加了不少,短期效果也能接受。但上线三周后暴露了三个问题。
第一是判断标准漂移。通用模型的输出会随着对话上下文、prompt 先后顺序甚至服务端升级而悄悄变化。同一句话,上周标成“账号问题”,这周就可能标成“安全问题”,标签口径不稳定,报表就失真。第二是反例召回困难。工单里有大量看似相似但实际不同的场景,比如“登录成功后退出了”和“登录失败退出了”是两码事,通用模型更容易被字面相似误导。第三是私有标签体系语言化表达不一致。你们内部叫“结算差错”,模型可能输出“支付失败”“对账不平”“结算异常”,语义接近但无法直接入字段。
微调解决的正是这三件事。指令微调的核心思想,是让模型在固定输入输出模式上做大量针对性学习,把“你们公司的标签定义”写进模型参数里,而不是临时塞进上下文里。模型看到工单文本,输出会被训练数据约束到你们定义的标签集合里,格式也可以做到严格可控,输出“[结算差错]”就真的是你们系统中的“结算差错”。
什么情况不用微调?如果标签量很小、只有三五个大方向,而且对格式一致性和数据隔离没有硬性要求,那 prompt 方案更快。如果你们的工单内容高度标准化、字段也规范,规则引擎反而更省事。微调的价值是在语义复杂、标签量中等、需要稳定输出格式的场景中体现的,工单打标恰好就是这种场景。
2.3 方案选型:平台帮你省掉哪些事
做一次微调,传统路径要处理训练脚本、GPU 资源、模型保存、推理服务、兼容层和监控告警,光是把推理服务上线就要折腾好几周。这次我选择阿里云百炼,核心原因是它把“从数据到模型到 API”这段路程压缩成了几个 API 接口和几段配置。
模型层面,百炼提供了基于通义系模型的微调入口,数据格式兼容主流 JSONL messages 结构,工程师很快就能上手。基础设施层面,训练、模型托管、推理都不需要自己管理 GPU,平台负责资源调度和稳定性,团队可以把精力放在数据质量和业务效果上。成本层面,微调按 token 和训练时长度计费,工单打标属于短文本任务,训练量不大,整个迭代周期花费可控,远比自建训练集群经济。
当然,平台也不是万能的。百炼的模型版本升级、API 限流、训练任务的排队时长这些仍然要适应。我的经验是前期一定要在平台之外做两层防护:数据层做脱敏,输出层做校验。把平台当作“模型能力供应商”,而不是“业务系统本身”,这样无论是模型迭代还是切换基座,都不会影响工单主流程。
3. 数据集准备:打标任务的“食材”与“火候”
3.1 数据采集与清洗
微调效果的上限由数据质量决定。这句话我越来越认同。数据准备看起来枯燥,但项目成败基本就看这一步。
数据从哪里来?工单系统历史库里挑最近 6 到 12 个月的已关闭工单。选这段时间有三个原因:业务现状相对接近,标签口径有迹可循,且工单处置结果是已知的。这些样本里不全是高质量数据,当初人工打的标签本身可能就是错的,所以采集之后要做三层清理。
第一层是内容脱敏。工单文本里可能包含用户手机号、订单号、身份证、地址等敏感信息,训练前必须把这些字段替换成占位符。我当时的做法是写规则脚本先扫一遍手机号、邮箱、金额等模式,再人工复核一遍,确保训练语料不包含真实用户隐私。这一步不只是合规要求,也是模型学习效果的要求。如果模型把“订单号 A123”这种完全无意义的长串当成判断标签的依据,泛化能力一定会变差。
第二层是去重和冲突处理。同一个用户重复提交的工单,文本相似度很高,如果原样进数据集,模型会在这些样本上过拟合,压缩有效样本多样性。文本完全相同的工单只保留一条,语义重复但表述不同的工单如果标签一致可以保留,如果标签不一致则直接剔除,说明历史打标本身就有问题,不能当标准答案。
第三层是标准化正文格式。真实工单里会有大量无关内容,比如“你好”“在线等”“麻烦尽快处理”之类的寒暄语。我做了两个处理:把所有工单拼接成“问题描述 + 已尝试操作 + 期望结果”的固定结构,同时把无关字符清理掉。这样模型看到的训练样本结构一致,学习效率会高很多。实测下来,同样的数据量,清理前后微调效果差距非常明显。
3.2 标签体系的落地与去模糊化
标签不能直接拿工单系统里的原始字段用。很多团队现有标签是长出来的,不是设计出来的,同一类问题有七八种叫法。我建议在微调之前,把标签整理成一张规范表,每条包含四列:标签名、标签说明、包含场景、排除场景。
举个例子。对于“账号登录异常”这个标签,说明写清楚“用户无法登录、登录报错、验证码校验失败都属于此标签”;包含场景写“密码错误、验证码过期、账号锁定”;排除场景写“登录成功后的页面问题属于前端展示问题,不属于登录异常”。别小看这些说明,它们直接决定了训练数据的构造质量。模型学习的是“标签对应什么内容”,而不是“标签长什么样”。
标签数量上守住两个底线:单层不超过 30 个,整体不超过两层。如果现有标签体系超过 50 个,那就做一次归并,把低频标签合并到上级类别,或者归入“其他”。宁可损失一点粒度,也要保证每个标签有足够训练样本。我这次最终保留了 26 个二级标签,每个标签最少有 40 条样本,低于 20 条的一律归并或单独抽出来做 few-shot 测试。
多标签的情况也要提前想清楚。工单经常“既属于交易问题,也属于数据问题”,所以模型输出应该允许一个标签数组,而不是单选。但每个标签的判定逻辑要尽量互斥,如果两三个标签经常同时出现在同一张工单上,没有明显主从关系,训练时模型会很难收敛。这类重叠标签我会合并成一个组合标签,比如“结算差错—全额退款失败”,替代分别打两个标签,输出更稳定。
3.3 构造训练样本:角色分离与严格格式
数据文件格式我采用了标准的 JSONL,每一行是一轮对话样本。工单打标这类任务,样本结构固定为三段:
- system:给模型立规矩,描述工单打标任务和标签体系;
- user:放工单正文;
- assistant:放理想输出,也就是标签结果。
这是目前模型微调最通用的格式。尽量把每轮样本的 system 字段保持一致,只替换 user 和 assistant 部分。如果 system 提示词在训练集里来回变,模型会分不清该听哪一段,打标标准会涣散。
JSONL 样例如下:
{"messages":[{"role":"system","content":"你是工单打标助手。根据工单内容,从以下标签集合中选择最匹配的标签:P0紧急故障、P1严重故障、账号问题、支付问题、订单问题、前端展示问题、数据问题、性能问题、其他。输出格式为JSON:{\"labels\":[\"标签名\"],\"confidence\":0-1之间的数字},不要输出多余文字。"},{"role":"user","content":"用户反馈收货后订单状态一直显示待发货,实际包裹已到驿站。多次刷新无变化,后台显示物流单号已同步,前端未更新。"},{"role":"assistant","content":"{\"labels\":[\"订单问题\",\"前端展示问题\"],\"confidence\":0.92}"}]}assistant 输出必须严格遵守统一格式,所有训练样本统一用 JSON 结构。测试过的同学应该知道,如果一半样本输出普通文本、一半样本输出 JSON,微调后模型很容易在二者之间摇摆,线上调用时解析就很痛苦。宁可训练时多花一点精力把输出统一成 JSON,也不要在推理阶段写一堆正则去适配各种脏输出。
样本数量上,我的经验是短文本工单任务,800 到 1500 条已标注样本足够跑出可用模型。少于 300 条,模型的标签记忆会不稳定;超过 3000 条,边际收益明显下降,重点应该转向基座模型本身的规模。
3.4 数据划分与偏差检查
数据划分我用的是 80/10/10:8 成训练,1 成验证,1 成测试。验证集和测试集要按工单 ID 切,不能按行随机切,因为同一个工单的多轮文本很相似,如果一部分进训练一部分进验证,模型其实已经见过这道题,评估分数会虚高。
切完之后还要做一轮偏差检查。我会把验证集和测试集单独请求原来的通用模型打一次标,用人工确认这些样本是“通用模型也会做错的”,还是“只有知道我们内部标签定义才能做对的”。如果验证集大部分是后者,说明数据难度正常;如果通用模型也能做对,那这些样本对微调效果的评估作用有限。
再检查一遍标签分布,确保每个标签在训练集、验证集、测试集中的比例基本一致。很多项目没做这一步,验证集里恰好缺了几个低频标签,最后模型上线遇到这类工单时全线溃败。
4. 百炼API微调全流程实操
4.1 前置准备:账号、模型与接口凭证
开始调 API 之前,需要完成几项准备工作。首先是开通阿里云百炼服务,在控制台开通对应区域的模型服务能力,开通后系统会分配一个 API Key,后续所有调用都靠这个 Key 做鉴权。生产环境建议单独建一个子账号,只授模型训练和推理权限,不要用主账号的 Key 跑业务,防止权限过度。
然后是选定基座模型。百炼平台提供多种通义系模型,选择标准取决于任务复杂度和成本。工单打标属于短文本分类任务,不需要超大规模模型。我用的是一款适合文本理解任务的中小尺寸模型,上下文长度足够容纳工单正文,训练和推理成本都低。复杂任务可以应对高风险场景,拉高费用和响应延迟,在打标场景不划算。
如果你不确定选哪个模型,最简单的判断方法:先把 50 条验证集样本发给各备选模型的通用版本打标,看输出格式稳定性和标签命中率,选表现最稳的那个作为微调基座。这里不建议仅看分数,更要看错误模式。比如某模型总是把“订单状态不更新”归到“物流问题”而不是“订单问题”,那微调时要重点补充这类反例,而不是换基座。
4.2 启动训练:控制台和SDK两条路
百炼支持两种微调启动方式:控制台操作和 API 调用。如果你是第一次跑通流程,我建议先在控制台上传数据、点一遍页面按钮,所有配置项一目了然。流程跑通后再切到 API,方便后续批量提交训练任务和自动化迭代。
控制台的路径一般是:进入百炼模型 Tuner 或微调页面,上传数据文件,选择基座模型,填写训练参数,然后提交。其中几项关键参数我会重点说明。
- 学习率:建议从 1e-5 到 2e-5 开始。学习率过大会导致模型在训练集上快速收敛但是严重遗忘原有能力,学习率过小则收敛缓慢,需要更长训练时间。
- epochs:3 到 5 轮比较合适。工单样本量不大,超过 10 轮很容易过拟合,表现为训练损失很低但验证集分数下降。
- batch size:默认 8 到 16 即可。batch size 主要影响训练稳定性,不需要过度调参。
- 序列长度:根据最长工单设置,一般 512 到 1024 足够,过长会显著增加费用,过短会截断关键信息。
- 损失与保存策略:平台默认保存最优 checkp oft,但我会在训练结束后手动再确认一次验证集表现最好的那个快照。
如果你希望用 API 方式自动化,核心流程是三步:上传训练文件、创建微调任务、轮询任务状态。代码示意如下:
import time import dashscope from dashscope import FineTune dashscope.api_key = "your-api-key" # 1. 上传训练文件 # 此处为示意,实际以百炼官方上传接口为准 # upload_resp = FileUploader.upload(file_path="train.jsonl") # 2. 创建微调任务 resp = FineTune.create( model="qwen-class", # 替换为选定的基座模型名 training_file_ids=["file-xxx"], hyper_parameters={ "learning_rate": 1.5e-5, "epoch": 4, "batch_size": 8, } ) fine_tune_id = resp.id print(f"训练任务已创建: {fine_tune_id}") # 3. 轮询状态 while True: status = FineTune.get(fine_tune_id).status print("当前状态:", status) if status in ["SUCCEEDED", "FAILED", "CANCELED"]: break time.sleep(60)调用 API 前务必先去官方 SDK 文档核对最新的类名和参数名,平台版本更新比较快,以官方文档为准。把这段代码作为骨架,结合官方示例调整参数即可。
4.3 训练监控与结果判断
训练任务提交后,控制台一般会展示状态流转:排队中、准备数据、训练中、审核中、成功或失败。排队时间取决于平台繁忙程度,也受训练数据量影响。如果你看到训练损失在逐渐下降但验证损失反而上升,这就是过拟合的典型信号,可以提前停止,或者把后续迭代的学习率再调低一点。
训练完成后,不要急着部署,先做一次人工验收。把测试集样本批量请求刚训练好的模型,对比输出标签与真实标签的差异。重点观察几类错误:完全预测错,比如把“支付成功但未到账”标成“订单问题”而不是“支付问题”;输出格式错,比如输出了普通文本而不是 JSON;置信度错乱,比如明确匹配的样本置信度只有 0.5。
验收通过后再部署。部署动作本质上是把微调得到的模型参数固化为一个可被 API 调用的服务实例。这一步也需要选择实例数量和资源规格,早期跑通阶段一个实例就够,用起来后再逐步扩容。
4.4 部署配置与推理参数
部署完成后,系统会分配一个模型专属的推理 endpoint,线上代码通过这个 endpoint 传工单文本,拿回打标结果。调用参数中有几个地方值得注意。
- temperature:打标任务建议设 0.1 到 0.3。温度越高,输出越发散,标签稳定性就差。这不是创作任务,需要尽量确定性的输出。
- top_p:建议 0.6 到 0.8,与低温配合使用。这两个参数不是越高越好,尤其是在生产环境,确定性比多样性重要。
- max_tokens:设置为标签可能输出的最大长度即可,不需要给太长。如果模型默认输出很长,反而会增加响应时间和解析成本。
推理时的 prompt 我沿用了训练时的 system 提示词,这样可以最大化训练与推理环境的一致性。很多实践者会在部署后把提示词改得很复杂,结果模型效果明显下降,原因就是切换了系统提示词,训练时见过的模式被冲淡了。改提示词之后一定要重新做一次验证集抽测,别偷懒。
5. 推理、打标集成与服务化
5.1 调用微调模型并解析输出
模型部署完成后,写一个稳定的调用入口就行。工单打标是内部使用场景,并发量一般不大,可以直接用 SDK 调用。
我封装了一个打标函数,核心逻辑是:拼好 messages、请求模型、解析 JSON、兜底容错。示例如下:
import json import dashscope from dashscope import Generation dashscope.api_key = "your-api-key" SYSTEM_PROMPT = "你是工单打标助手。根据工单内容,从标签集合中选择最匹配的标签..." def classify_ticket(content: str): resp = Generation.call( model="your-fine-tuned-model-id", messages=[ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": content}, ], temperature=0.2, top_p=0.7, max_tokens=128, ) raw = resp["output"]["text"] try: data = json.loads(raw) labels = data.get("labels", []) confidence = float(data.get("confidence", 0.5)) except Exception: # 兜底:标注为低置信度,转人工 labels = [] confidence = 0.0 return {"labels": labels, "confidence": confidence, "raw": raw}这里兜底逻辑很关键。不论模型训练得多好,都有极小概率输出不合规文本。如果解析失败就把置信度置为 0,走人工审核通道,而不是崩溃或把脏文本直接写进数据库。工单打标是辅助功能,下游路由依赖标签,绝不能因为解析异常把整个路由链路打断。
5.2 批量打标与前端页面交互
工单打标可能有实时与批量两种触发方式。实时场景是新工单进来时同步调用模型,在客服工作台直接展示推荐标签。批量场景是历史遗留工单集中回打,或者在每天晚上跑一次全量缓存更新。
实时场景的延迟目标一般控制在 3 秒以内。我遇到过模型推理本身不慢,但调用代码里串行处理、每次重新初始化连接,导致接口总耗时高达 5 秒的情况。改成连接复用后降到 2 秒左右。如果工单系统峰值并发明显,建议加一层本地缓存,相同或相似工单在短时间内只请求一次模型。
批量场景要引入消息队列,比如把待打标工单 ID 推入队列,消费者逐条调用微调模型,写回工单系统。处理失败时要做重试,重试三次仍失败就跳过并记录到错误表。别忽略失败记录的意义,它往往暴露的是数据格式异常、工单被删除、标签体系调整等业务问题,不只是模型问题。
5.3 与工单系统字段映射
微调模型输出的标签名,和工单系统里的字段值之间要做一层映射。不要直接依赖模型输出的中文字符串去数据库里查匹配,一旦批处理时有人把标签改名,所有映射全部失效。
我的做法是给每个标签配一个稳定 ID。模型训练时,assistant 输出仍然用标签名,便于模型理解和约束。推理拿到标签名之后,再通过一个字典映射到工单系统字段 ID。这样即使标签从“账号问题”改名为“账号访问问题”,只需要更新字典,不需要重新训练模型。输出层的稳定性比训练层更容易被忽略,但往往是最容易埋雷的地方。线上改字段名时要意识到,改的不是一段配置,而是一整条数据链路的语义约定。
6. 效果评估与迭代策略
6.1 离线评估指标
工单打标模型上线前,至少先跑一遍离线评估,否则没法判断这次微调到底有没有进步。
单标签场景可以直接看准确率、精确率、召回率、F1。多标签场景建议看汉明损失或每个标签单独的 F1。我不建议只盯整体准确率。几类高频标签做得很好,整体准确率很高,但低频标签全错,这种模型上线后会制造大量人工返工。
除了模型指标,一定要统计“格式正确率”:模型输出能成功解析成合法 JSON 的比例。这个指标直接决定了工程接入的流畅程度,如果格式正确率低于 95%,下游解析代码会被迫写一堆兼容逻辑。
如果上线前有历史规则引擎,建议把规则引擎、通用模型、微调模型三者结果放一起对比。大多数情况下微调模型会在低频标签、长尾表达、格式稳定性上明显胜出,规则引擎优势只剩下确定性高。表格对比如下:
| 指标 | 规则引擎 | 通用模型+提示词 | 微调模型 |
|---|---|---|---|
| 格式一致性 | 高 | 中 | 高 |
| 理解语义能力 | 低 | 中高 | 高 |
| 私有标签体系适配 | 需手工维护 | 需每次写prompt | 训练固化 |
| 新增标签成本 | 低成本快速改规则 | 改prompt | 需补样本重训 |
| 故障定位能力 | 强 | 弱(黑盒) | 弱(黑盒) |
6.2 线上验证与人工闭环
离线分数只能说明模型在历史数据上表现不错,真实线上环境还会出现大量训练集没见过的表达。我建议灰度方式:先让新模型打标,但不直接写入核心字段,而是把模型输出放到“推荐标签”栏,由客服人员一键确认。确认后的结果回流到待标注样本池,每周补充一次训练数据。
这个闭环做起来之后,模型会越用越准,因为人工反馈的每一条都是一次真实情况下的纠偏。但我有一个重要提醒:回流数据必须经过质量校验,不能人点错了也照单全收。建议对回流的样本设置双重确认门槛,比如客服确认后还需要质检抽检,防止人工本身就打错、又在污染模型。
新标签需求的响应周期也要明确。大多数团队希望新增标签后马上生效,但微调的自然迭代周期至少是“攒数据一两天、训练一两小时、评估半天”。所以尽量在标签体系层面保持稳定,不要把“临时想一个标签就用一周”当成常态。
6.3 训练数据的版本管理
模型迭代多了以后,数据集也会出现版本问题。我见过团队训练了十几次模型,但没人说得清某个版本用的是哪些样本、标签定义是哪一版。这会导致排查线上误判时无从下手,也不知道该回滚到哪个版本。
我的做法是在项目里建三个目录:原始工单数据、清洗后训练数据、复现验证脚本。每次训练前打一个日期版本标签,训练记录里同步保存数据 hash、随机种子、学习率、epochs、测试集评分。别看这些记录琐碎,真正线上出了问题,这就是唯一的排查线索。微调效果不好时,你会感谢自己当初留下了这些信息。
7. 常见问题与避坑清单
7.1 高频问题速查表
| 现象 | 大概率原因 | 解决思路 |
|---|---|---|
| API 返回 400 参数错误 | 请求体格式字段名不符 | 对照官方 SDK 最新文档核对模型名、训练文件、messages 结构 |
| 训练任务一直排队 | 平台资源繁忙或训练数据量大 | 选择非高峰时段提交,或调小训练数据量与序列长度 |
| 训练损失下降但验证集分数差 | 过拟合 | 调低学习率、减少 epochs、增加数据多样性 |
| 线上模型总是输出非 JSON 文本 | 训练样本格式不统一、temperature 过高 | 统一 assistant 输出格式,降低 temperature 至 0.2 以下 |
| 多个相似标签互相混淆 | 标签定义重叠 | 合并重叠标签,用“排除场景”制造更清晰的反例 |
| 资深客服熟悉的场景但模型不会 | 训练样本缺少该场景真实语料 | 补充该场景正反例样本,单独再做一轮微调 |
| 成本比预期高 | 序列长度设太长、实例规格过大 | 压缩工单正文至 512 token 内,调小实例配置再观察 |
7.2 我这个项目里的几个细节心得
微调任务刚开始时,我也犯了一个典型错误:把所有注意力放在模型参数调优上,数据却只是粗粗清洗一遍。结果第一版模型训练损失降得很快,验证集 F1 只有 0.72。后来我花了一周时间重做数据结构,给每条样本补齐“排除场景”的反例,第二版 F1 直接到 0.88。所以参数很重要,但数据质量永远是第一位。
另外一个容易被忽略的细节是 system 提示词不要写太长太复杂。你不需要把整个标签规则都写进 system 字段,标签规则训练数据已经学过了,system 只要保证任务身份和输出格式稳定即可。写得越长,模型调度注意力越分散,格式错误率反而升高。
还有一点关于置信度阈值。阈值不是拍脑袋定的,我建议去测试集上算出不同阈值下的误标记率和漏标记率,找一个平衡点。我是以 0.75 作为人工审核起点,置信度低于 0.75 的工单会被标记为“待人工确认”,进入质检队列。这个数字在你们的业务场景里不一定一样,一定要看自己的数据分布。
训练数据里一定要留一小批“纯干扰项”,就是那些看起来像正常工单但无法明确归类的文本,assistant 输出统一给“其他”。没有这类样本,模型会在线上一遇到边缘情况就强行猜一个高置信度标签,那比不猜更危险。有了“其他”这个出口,边缘工单会被顺利分流到人工,而不是污染统计报表。
这个项目做下来,我的感受是工单打标微调并不是很难的“大模型研究”,更像一次精细的数据工程。只要标签体系定义清楚、训练样本格式统一、推理输出有兜底,再用百炼这样的托管平台服务化上线,从数据到模型的闭环是完全可以自主建立的。微调带来的收益不只是打标准确率提升,更重要的是打标口径从“人的随机判断”变成了“模型与规则共同约束的确定性行为”。后续如果你们要把这套能力扩展到工单自动分派、相似工单聚类、服务质量分析,这个打标模型就是现成的底座,只需要在标签层面继续丰富和迭代即可。