AI工程从零到一:工单助手落地全流程解析

📅 发布时间:2026/10/5 1:03:26
AI工程从零到一:工单助手落地全流程解析
我一直觉得ai-engineering-from-scratch这个标题挺有意思的乍看像一门课程的名字其实它最能概括我最近做实操项目时的状态把一个业务问题从头到尾用AI技术解决掉中间没有任何人帮我兜底。去年组里接了个任务客服工单要自动分类、自动摘要还要给出建议处理方案。我当时以为这事很简单写一段prompt调一下模型API就完事了。真正动手才发现AI工程和传统开发完全是两种玩法你要面对的不确定性太多了模型输出会漂、格式会裂、上下文会爆你需要的不是调通一次而是设计一个即使模型表现不稳定、结果依然可用的系统。这篇文章我打算把这套东西完整拆一遍从怎么判断需求适不适合用AI到模型选型、上下文构建、提示词工程再到评估、上线、成本控制完整讲清楚一个AI功能从零到落地的全过程。不管你是在做AI测试、AI编程辅助还是想搞Agent、自动化工单这套思路都可以直接套用。1. 必须先把AI工程这个概念掰开1.1 AI工程不是调模型也不是写提示词很多人把AI工程理解成写一段能调用模型的代码。这属于还没入门的状态。跑通一次调用就像你点燃了一个打火机但你没法用它稳稳地烧完一顿饭。AI工程真正要解决的是在模型输出带有随机性、甚至偶尔完全跑偏的情况下依然把产品功能做得稳定、可度量、可维护。我自己最大的体会是AI工程的核心资产不是模型而是可控性。模型是别人的平台随时可能调整版本提示词是你写的但同一个提示词每次输出可能不同上下文是你拼的但拼多了模型就不听你的话。你得在这么多不确定因素里找出一个确定性的框架让业务结果整体在可接受范围内。这就是AI工程和普通后端开发最本质的区别传统代码是输入确定、逻辑确定、输出确定AI工程是输入带噪声、模型带概率、输出带误差而你要为这套概率系统设计质量护栏。1.2 AI工程和传统软件工程的差异到底在哪举个具体的例子。你写一个普通的工单查询接口用户传一个工单号你查数据库返回结果这个结果你用单元测试就能锁定。但AI工单分类不一样用户传入的工单文本千奇百怪有错别字、有口语、有夹杂截图说明模型可能今天把手机进水分到硬件故障明天分到售后服务而且就算模型分错了程序也不会报错它只会安安静静地给你一个错误结果。所以传统工程可以依赖断言和异常来保证质量AI工程必须换成评估和兜底。你没法断言模型一定输出合法分类你只能通过校验和重试来降低非法输出的概率你没法保证摘要100%覆盖所有关键信息你只能通过评估集定期验证并让业务方接受一个可容忍的错误率。这种思路的转变对很多老开发来说是最难的一关。代码写错了可以修bug模型输出错了你连bug都没法定位只能从输入-输出-上下文三者关系里一点点排查。1.3 AI工程的核心组成五个环节缺一不可我习惯把一个AI工程拆成五块模型、上下文、提示词、流程编排、评估反馈。这五个词听起来各自独立实际上是一个闭环。模型负责真正理解和生成的部分它决定能力上限。上下文你提供给模型的背景信息、历史数据、知识库片段它决定模型能不能答到点子上。提示词让模型知道怎么组织输出它决定结果的形式和可用性。流程编排包括前置条件判断、后置校验、多步调用顺序、工具调用它决定系统的复杂度和稳定性。评估反馈包括评估集、指标、日志、人工反馈回流它决定你能不能持续优化。这五块里最容易忽略的是最后一个。我见过太多团队把精力花在调提示词上调了一周准确率看着还行一上线就崩。原因很简单没有评估集你的看着还行只是猜有了评估集你才知道每次修改到底是变好还是变差。所以我的习惯是评估先行提示词后调。先花时间攒一批评估数据再开始调效率翻倍。2. 从零到一的核心设计先解决什么问题2.1 需求判断你的问题适不适合用AI解决做AI工程最容易犯的错就是拿着锤子找钉子。不是所有问题都需要大模型也不是所有问题用传统规则就做不好。我自己的判断标准有三个语义理解是否占主导。工单分类、情感判断、文本摘要、意图识别这些都是读一段话、理解意思的任务适合AI。反过来如果你需要精确计算库存、判断金额是否超限用规则和代码就够了。输入是否高度可变。如果输入完全可以通过枚举穷举比如固定下拉框那你不需要AI如果输入是自由文本、语音转文字、图片OCR结果形态千变万化那才需要考虑AI。业务对错误率的容忍度。AI永远做不到100%准确。如果你的业务不允许任何误判比如医疗诊断、金融风控的最终决策那AI只能做辅助必须有人工审核兜底。能接受百分之一到百分之几的错误率且可以通过流程弥补的才适合直接上AI。拿我自己做的工单系统来说分类错误可以靠人工复核兜底摘要不完整不影响后续处理所以这个场景适合AI。如果你要做一个绝对不许算错的财务系统就别硬塞大模型进去用公式和数据库约束更靠谱。2.2 模型选型别一上来就冲参数最大的模型选型是个成本工程。很多人一说AI就往最强模型上想但实际项目里没必要。大模型确实聪明但贵、慢、还有可能过度发散输出自说自话。小模型便宜、快但理解能力弱复杂任务容易翻车。我用过一个比较实用的分层策略简单分类任务比如工单一级分类类别数量不超过十个可以先用轻量级模型试比如量化过的小参数模型成本低、延迟低够用就好。我们实测下来大部分工单分类只要类别定义清晰中小模型的表现足够好。中等任务多级分类、信息抽取、轻量摘要选中等规模的模型加上好的few-shot示例效果能追上大模型的大部分场景。复杂推理任务多步骤规划、代码生成、长文档综合理解这个才需要顶级大模型不要省这个钱。选模型还有一个隐藏点同一个任务你可以用一个便宜模型做粗筛判断是否真的需要上大模型。比如先让规则或小模型把明显简单的工单处理掉只有复杂的才转发给大模型。这叫前置路由后面成本控制部分我也会细讲。模型选型看的是性价比不是刷榜分数这一点千万记住。2.3 形态选择单次调用、工作流还是Agent同样是AI功能实现形态差别很大。选型逻辑是任务的确定性越高用的形态越简单。单次调用一个输入一次模型调用一个输出。适合摘要、翻译、单分类。结构最简单问题最容易排查。工作流Workflow多个步骤按固定顺序编排每步一个明确任务。比如先判断工单类型再抽取关键实体最后生成摘要。每一步都单独校验失败可以单独重试。适合流程很清晰的任务。Agent由模型自己决定下一步干什么、调用什么工具路径不可预知。适合开放任务比如帮我排查这个系统故障原因模型可能需要读日志、查文档、执行命令。我在工单项目里用的就是工作流形态没有硬上Agent。原因很简单分类和摘要的顺序是固定的你不需要模型自己决定步骤。很多人把用Agent当成工程高级感但Agent意味着你放弃了部分控制权排查难度指数上升。能用工作流解决的不要硬上Agent这一点在工程上真的非常重要。2.4 上下文工程决定模型能不能答对的隐藏变量同样的模型、同样的提示词上下文里塞的东西不同输出效果天差地别。上下文工程说白了就是两件事什么东西该塞进去什么东西不该塞。该塞的是什么用户的核心诉求、必要的背景数据、相关的历史记录、业务知识库里命中片段。不该塞的是什么无关的闲聊、大段的原文引用、已经过期的信息、重复的历史对话。上下文窗口是有限的塞满了模型注意力会被稀释还可能顾头不顾尾完全忽略你放在前面或后面的关键指令。我在做工单摘要时最初的做法是把整条工单的长文本和全部历史对话都丢进去结果模型生成的摘要总是带着对话记录的废话客户说客户说反复出现关键问题反而没提炼出来。后来改成预处理先用规则把工单里的日志记录、无用格式符清理掉再用一个轻量模型把历史对话压成三句话的过程摘要最后再传给主力模型做最终摘要。效果立刻上来了token消耗还降了一半。上下文工程不是多给信息模型就更聪明而是给对信息模型才靠谱。3. 实操一个工单助手从需求到上线的完整链路3.1 业务需求拆解与技术方案设计工单助手的业务需求其实不复杂但拆解完会发现每一步都有讲究第一步对工单做一级分类咨询、故障、投诉、其他第二步从工单里抽取关键信息产品型号、报修时间、用户期望、联系方式第三步生成一段简明摘要给客服或技术员快速浏览第四步给出建议处理方案。对应到技术上我拆成四个模块文本清洗、分类模型调用、信息抽取调用、摘要与建议生成调用。这四个模块是串行的工作流每一步的输出都作为下一步的输入。为了让每一环都能独立排查问题我没有做成一个大prompt全包而是每步单独一个函数、单独一套提示词、单独的日志记录。这样哪个环节出了问题看日志就能定位不用把整条链路翻个底朝天。3.2 评估集建设最枯燥但最值得花时间的一步没有评估集后面所有优化都等于开车不看路。我的做法是这样从历史工单里随机抽了200条覆盖五个业务大类每条单独做人工标注标注内容包括正确分类、关键信息点、标准摘要。标注完把数据按8:2分成调试集和测试集。调试集是我平时改提示词用的测试集是动都不敢动的用来做最终验证。指标上分类任务看准确率和混淆矩阵摘要和信息抽取任务看信息召回率和格式通过率也就是模型输出能否被程序正确解析。这个数据集看起来只有200条但对我们的实际场景已经够了因为工单类型相对集中。如果你要做的是开放领域文本处理建议至少500到1000条起步。数据少有一个问题就是你很容易在调试集上过拟合——调提示词调到在调试集上准确率95%一上真实数据就掉到80%。原因就是调试集覆盖不到真实世界的多样写法。所以后来我又专门加了一条规则每周从生产环境抽20条未标注工单先人工标注入库再跑一次离线评估用新数据检验模型在真实分布上的表现。3.3 提示词工程的关键设计从写话术到写接口文档很多人写提示词像是在跟模型聊天写一大段自然语言让模型好好做。我的经验是提示词应该写得像接口文档一样严谨。我常用的结构是角色定义一句话说明模型扮演什么角色回答问题的范围和边界是什么。任务说明明确要完成的具体任务分类还是抽取还是摘要。输入格式给模型定义好输入内容的JSON结构说明。输出格式明确要求输出JSON并给出完整的JSON Schema包含每个字段的含义和可选项。示例放2到3个输入输出对作为few-shot示例。注意示例不是越多越好我实测下来示例太多了模型容易从示例里学习照抄而不是理解任务而且会消耗大量token。3到5个高质量的示例足够覆盖绝大多数场景。兜底规则告诉模型遇到不确定的情况怎么处理比如无法分类时输出其他并说明原因。我最看重的是输出格式约束。工单助手工序有四个模块要串联最怕的就是模型输出一堆长文本加个序号导致下游程序解析不了。所以每个模块我都明确要求结构化输出并要求后置校验。有一个很典型的例子第一版分类模块我在提示词里写输出格式JSON包含category字段。结果模型经常输出一大堆解释所谓JSON还是用Markdown块包起来的。后来我改成了明确写只输出JSON对象不要输出任何解释、不要使用Markdown代码块然后在代码里再加一道JSON区域提取的预处理双重保险才算彻底稳定下来。3.4 工程实现细节解析、校验、重试与缓存提示词写得再好工程实现不严谨一样会炸。我把工单助手的核心链路拆成几段最小可运行版本大概是这样的结构import json import re from tenacity import retry, stop_after_attempt, wait_exponential def extract_json(text: str) - dict: # 模型偶尔会在JSON外裹代码块或解释文字先把JSON块抠出来 match re.search(r\{.*\}, text, re.DOTALL) if not match: raise ValueError(没有找到 JSON 对象) return json.loads(match.group()) retry(stopstop_after_attempt(3), waitwait_exponential(multiplier1, max10)) def call_model_with_retry(messages, model_name, temperature0): # 实际项目里这里替换成你用的模型SDK调用 response model_client.chat(messagesmessages, modelmodel_name, temperaturetemperature) content response[choices][0][message][content] data extract_json(content) validate_schema(data) # 用JSON Schema校验必填字段和枚举值 return data # 分类模块调用 def classify_ticket(cleaned_text: str) - dict: messages build_classification_messages(cleaned_text) return call_model_with_retry(messages, model_namecheap-fast-model)有几个细节我必须单独拎出来讲temperature设为0分类和抽取这类有明确答案的任务不需要模型发挥创造性temperature开得越低越稳定。闲聊才需要高temperature。重试必须有模型服务有概率返回超时或异常也可能返回格式错误。如果解析JSON失败使用指数退避重试一般重试两三次就能拿到合法输出。必须做Schema校验程序里不仅要有JSON解析还要校验category是否在允许的枚举值内、summary字段是否非空。校验不过就按模型输出异常处理打进异常工单队列不要硬着头皮往下游传。缓存机制如果工单内容高度相似比如同一个用户反复反馈同类问题可以在调用模型前先算文本哈希命中缓存就直接返回历史结果能省不少成本。这个优化我上线后第二周加的直接省了大概三成的模型调用费用。我这里用的回调函数风格是示意实际项目里你可能用函数调用Function Calling方式来做结构化输出原理一样让模型生成一个结构化的参数对象比你让它自由写JSON要稳定得多。3.5 上线观测与反馈闭环工程能不能活下去的关键上线不是结束而是评估工作的开始。我在工单助手里放了三层观测第一层是日志层每次模型调用记录输入文本摘要、模型名称、输出结果、耗时、token数。这层日志的成本很低但对排查问题价值巨大。第二层是抽样层从生产流量里随机抽5%到10%的工单做人工复检复检结果会回流到评估集里用来持续更新标注数据。第三层是反馈层客服看到摘要后可以点有帮助/没帮助这个反馈数据用来发现高频错误类别后续针对性地调模型或加规则。有了这三层你才真正有资格说自己在做AI工程。否则你只是写了一段调用代码产品和业务根本不知道它在线上表现如何也不知道下个版本要不要改、怎么改。4. 问题排查、成本控制与避坑经验4.1 常见故障现象与排查方法速查表做到现在我遇到的坑基本都能归类到下面这张表里故障现象常见原因排查步骤分类结果不稳定同样的输入两次结果不同temperature设置过高提示词歧义先降到0再检查类别定义是否互相重叠JSON解析经常失败模型能力弱上下文太多影响指令遵循检查是否有无关内容压缩了指令换用结构化输出方式输出内容被截断上下文超长max_tokens设置过小检查token统计压缩上下文调大输出token上限摘要信息不完整漏重点上下文被无关内容淹没提示词没有强调关键字段精简输入内容用规则前置清洗在提示词中显式列出摘要需覆盖的字段响应延迟过长模型模型大上下文长重试次数多加缓存用前置路由把简单请求分给小模型降低重试上限成本快速上涨把大量无关数据塞进上下文没有缓存监控token消耗做上下文压缩引入缓存排查的第一步永远是看日志里的输入-输出-耗时-模型版本四件套。没有这四样你就是在盲人摸象。4.2 几个让我印象很深的实战坑第一个坑是输出格式约束失效。有一版分类模块我自信满满提示词里要求必须输出JSON不要输出额外解释技术栈里也做了JSON解析。结果发现模型偶尔还是会输出一段类似好的我来为您分类的话再接JSON。原因不是提示词写得不好而是模型有时自作主张。最终解决方法是两层提示词强调代码里先做抠JSON块预处理再解析。这个先抠再解的套路直接消灭了80%的解析异常。第二个坑是上下文被工单全文撑爆。历史工单动辄几百字加上对话记录可以达到上千token。一开始我把全文直接塞给摘要模块模型输出时明显失焦。后来我加了一步预处理先按规则把无意义的重复句子、系统日志、时间戳清理掉再用小模型把长对话压缩成一句事态进展。这一步做完摘要准确率提升token消耗还下降了。这就是上下文工程的典型价值。第三个坑是评估集过拟合。我前面说过在200条调试集上把准确率调到95%上线后真实分布上只有80%。原因很简单真实工单有大量调试集里没见过的说法、错别字和情绪化表达。解决思路就是前面提过的生产环境抽样回流机制。从那之后我再也不信调试集上的数字只看抽样回流后的数字。4.3 成本与延迟的平衡技巧做一个AI功能如果成本失控业务一样撑不住。我总结了几条实用的平衡策略能不用大模型就不用大模型。一级分类用中小模型复杂摘要才用大模型这叫分级调用。能缓存就缓存。相同或相似输入的请求命中缓存直接返回。工单这类业务尤其适合因为反复反馈同一个问题的情况非常多。能做前置路由就做前置路由。先用规则或文本特征判断复杂度简单任务直接走一条轻量路径复杂任务才走完整模型链路。我在工单助手里加了一条规则工单字数少于20且关键词命中装不了、打不开、闪退的直接分类为故障不用调模型。这类规则不一定覆盖很多场景但能省掉一部分高成本调用。合理设置超时与重试。重试不是越多越好每次重试都增加延迟和成本。我的习惯是重试上限设3次间隔按指数递增。这个平衡说白了就是让小模型和规则承担确定性高的部分把大模型留给真正需要理解力的部分。这是AI工程成本优化的核心思路比任何调参优化prompt都见效。5. 我踩坑之后的一些个人体会做这个工单助手项目我最深的感受是AI工程其实是一门用成本换确定性的生意。你不可能让模型输出100%稳定但可以通过上下文工程、提示词约束、后置校验、人工兜底让系统整体达到业务能接受的准确率。工程的意义不是消灭不确定性而是把不确定性管理在一个可控范围内。最后分享一个我现在一定会遵守的习惯提示词必须像代码一样做版本管理。每次修改提示词我都带上版本号、模型版本、评估结果记录使用统一的文件保存。因为AI工程里最常见的情况不是模型代码出bug而是有人在某个版本里改了一句话效果莫名其妙变差了又找不出是什么时候改的。有了版本管理这种情况可以少掉一大半。AI工程从零到一技术上不难难的是把每个细节都当成工程问题来对待。这也是ai-engineering-from-scratch这个标题最打动我的地方从零开始但每一步都走扎实。