基于阿里云百炼的大模型微调:实现工单自动打标全流程实践
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/108 成训练1 成验证1 成测试。验证集和测试集要按工单 ID 切不能按行随机切因为同一个工单的多轮文本很相似如果一部分进训练一部分进验证模型其实已经见过这道题评估分数会虚高。切完之后还要做一轮偏差检查。我会把验证集和测试集单独请求原来的通用模型打一次标用人工确认这些样本是“通用模型也会做错的”还是“只有知道我们内部标签定义才能做对的”。如果验证集大部分是后者说明数据难度正常如果通用模型也能做对那这些样本对微调效果的评估作用有限。再检查一遍标签分布确保每个标签在训练集、验证集、测试集中的比例基本一致。很多项目没做这一步验证集里恰好缺了几个低频标签最后模型上线遇到这类工单时全线溃败。4. 百炼API微调全流程实操4.1 前置准备账号、模型与接口凭证开始调 API 之前需要完成几项准备工作。首先是开通阿里云百炼服务在控制台开通对应区域的模型服务能力开通后系统会分配一个 API Key后续所有调用都靠这个 Key 做鉴权。生产环境建议单独建一个子账号只授模型训练和推理权限不要用主账号的 Key 跑业务防止权限过度。然后是选定基座模型。百炼平台提供多种通义系模型选择标准取决于任务复杂度和成本。工单打标属于短文本分类任务不需要超大规模模型。我用的是一款适合文本理解任务的中小尺寸模型上下文长度足够容纳工单正文训练和推理成本都低。复杂任务可以应对高风险场景拉高费用和响应延迟在打标场景不划算。如果你不确定选哪个模型最简单的判断方法先把 50 条验证集样本发给各备选模型的通用版本打标看输出格式稳定性和标签命中率选表现最稳的那个作为微调基座。这里不建议仅看分数更要看错误模式。比如某模型总是把“订单状态不更新”归到“物流问题”而不是“订单问题”那微调时要重点补充这类反例而不是换基座。4.2 启动训练控制台和SDK两条路百炼支持两种微调启动方式控制台操作和 API 调用。如果你是第一次跑通流程我建议先在控制台上传数据、点一遍页面按钮所有配置项一目了然。流程跑通后再切到 API方便后续批量提交训练任务和自动化迭代。控制台的路径一般是进入百炼模型 Tuner 或微调页面上传数据文件选择基座模型填写训练参数然后提交。其中几项关键参数我会重点说明。学习率建议从 1e-5 到 2e-5 开始。学习率过大会导致模型在训练集上快速收敛但是严重遗忘原有能力学习率过小则收敛缓慢需要更长训练时间。epochs3 到 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_pathtrain.jsonl) # 2. 创建微调任务 resp FineTune.create( modelqwen-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( modelyour-fine-tuned-model-id, messages[ {role: system, content: SYSTEM_PROMPT}, {role: user, content: content}, ], temperature0.2, top_p0.7, max_tokens128, ) 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 输出统一给“其他”。没有这类样本模型会在线上一遇到边缘情况就强行猜一个高置信度标签那比不猜更危险。有了“其他”这个出口边缘工单会被顺利分流到人工而不是污染统计报表。这个项目做下来我的感受是工单打标微调并不是很难的“大模型研究”更像一次精细的数据工程。只要标签体系定义清楚、训练样本格式统一、推理输出有兜底再用百炼这样的托管平台服务化上线从数据到模型的闭环是完全可以自主建立的。微调带来的收益不只是打标准确率提升更重要的是打标口径从“人的随机判断”变成了“模型与规则共同约束的确定性行为”。后续如果你们要把这套能力扩展到工单自动分派、相似工单聚类、服务质量分析这个打标模型就是现成的底座只需要在标签层面继续丰富和迭代即可。