2026/10/19 17:25:45

大模型应用开发全流程实战:从Prompt到Agent与微调

大模型应用开发全流程实战:从Prompt到Agent与微调 做了一年多大模型应用开发带过几个从零起步的项目也带过不少刚入门的同学。我发现大家遇到的最常见问题不是“API怎么调不对”而是“整个应用开发的流程到底怎么串起来”。这篇就把我从入门到搞明白的完整过程整理出来按照实际做项目的顺序覆盖环境搭建、Prompt设计、RAG检索增强、Function Calling与Agent、微调、评估上线以及排错经验。适合刚接触大模型应用开发的同学通读一遍建立全局认知也适合已经写完单体Demo、正打算往工程化方向走的人查漏补缺。1. 先想清楚大模型应用开发到底在做什么1.1 这活儿本质是“搭积木”大模型应用开发严格来说不是研究模型怎么训练出来的而是把现成的模型能力封装成业务功能。你的核心竞争力不在于会用某个推理接口而在于知道怎么设计Prompt、怎么管理上下文、怎么接外部工具、怎么评估效果以及上线之后怎么稳定运营。我经常打一个比方大模型就像一个知识广博但记性很差的外包员工。它懂很多通用知识但对你的业务细节一无所知它能听懂指令但你必须把指令说得足够清楚它不会主动去查数据库、调系统但如果你给了它“双手”工具调用它能指挥你把活干完。应用开发者的活就是当好这个外包员工的“项目经理”。理解这个本质之后很多迷茫就消失了。你不需要焦虑自己数学基础不够好、不懂模型内部原理就能做出有价值的应用但你也别以为写两个Prompt就完事了真实业务里的稳定性、成本、数据隐私、效果评估每一项都比“让模型说出一段漂亮话”难得多。1.2 两条路线怎么选商业API还是开源部署动手之前先选路线。目前主流有两条调用商业API和本地部署开源模型。两者各有明确适用场景不搞清就动手后面很容易返工。调用商业API的好处是上手快、链路稳定、按量付费不需要考虑显卡和运维。适合大多数业务场景尤其是团队小、迭代快、对数据不敏感的情况。代价是数据会经过第三方服务隐私敏感的行业需要评估合规风险另外随着调用量上涨成本控制是个长期课题。本地部署开源模型的好处是数据不出内网、可以任意微调、长期成本可能更低。代价也很直接你得有两块像样的显卡得处理推理框架、并发排队、版本升级这些问题。适合对数据安全要求高的场景或者业务本身重度依赖本地结构化数据。我的建议很简单没到非自部署不可的程度先用商业API把业务跑通等验证了需求、摸清了调用量和成本再决定要不要迁移到自部署。这就跟打车和买车的关系一样先别急着贷款买跑车。1.3 一个标准应用的基本骨架不管你做什么具体业务大模型应用的基本骨架都绕不开几个环节输入处理、Prompt构建、模型推理、输出解析、外部检索、工具调用、记忆管理、效果评估。输入处理解决的是用户消息怎么清洗、怎么脱敏、要不要分类Prompt构建是把业务要求翻译成模型能理解的指令模型推理就是真正花token的那一下输出解析则是把模型的文字结果转成结构化数据该转JSON转JSON该校验校验外部检索解决知识从哪里来工具调用让模型能操作业务系统记忆管理负责处理多轮对话和长上下文效果评估则回答“这么做到底行不行”。我第一次做项目时只写了“Prompt模型调用”就以为完事了结果线上问题一堆模型偶尔输出格式不对、知识库内容检索不到、用户多问两轮就“失忆”。后来才发现那些被忽略的环节才是真正决定应用能不能落地的部分。把这个骨架记在心里之后每一步都是在给它填肉。1.4 从入门到精通的分阶段路线总结我带人的经验大模型应用开发基本要经历五个阶段。第一阶段是跑通最小闭环搭好环境、能调用模型、写一个最简单的对话Demo搞清楚请求和响应的结构。第二阶段是Prompt工程入门能把模糊需求写成结构化指令控制输出格式让模型稳定完成单一任务。第三阶段是上下文与RAG学会管理多轮对话给模型外接知识库让它回答原本不知道的问题。第四阶段是工具调用与Agent让模型操作数据库、API、业务流程从“会说话”变成“会干活”。第五阶段是微调与工程化用微调解决Prompt解决不了的问题同时搭好评估、监控、缓存、安全体系。每个阶段都有关键技能要补。别跳阶段尤其是别没做好Prompt和RAG就直接冲微调微调不是灵丹妙药后边我会专门讲。2. 环境准备与模型接入从拿到Key到跑通第一个Demo2.1 开发环境Python、虚拟环境、依赖做这块开发基本都绕不开Python。建议直接用Python 3.9以上版本太低版本会遇到依赖兼容问题。装好Python之后给每个项目单独建虚拟环境这是避免依赖冲突最简单的手段。python -m venv .venv source .venv/bin/activate # Windows下是 .venv\Scripts\activate pip install requests很多人图省事直接全局装包等几个项目挤在一起就开始互相踩依赖了。虚拟环境是花十秒钟省一整天的事强烈建议养成习惯。刚开始开发依赖不用装太多一个requests就足够跑通流程了等后面用到向量库、框架再逐个加。API Key建议放到环境变量或者本地配置文件里不要commit到代码仓库。我见过不止一次有人把Key写死在代码里然后传到公共仓库几分钟内就被外部脚本扫走盗刷。2.2 最简接入写一个能对话的函数现在主流模型的API格式都很接近基本都采用“messages列表”结构区分system、user、assistant三种角色。这是整个大模型应用开发里最核心的交互模型system负责设定身份和规则user是用户输入assistant是模型回复多轮对话就是把历史消息按顺序追加进这个列表。下面这个函数是我每开一个新项目都会先写一遍的最小骨架直接能跑通import requests import os API_URL https://api.example.com/v1/chat/completions API_KEY os.environ.get(LLM_API_KEY) def chat(system_prompt, user_message): resp requests.post( API_URL, headers{Authorization: fBearer {API_KEY}}, json{ model: your-model-id, messages: [ {role: system, content: system_prompt}, {role: user, content: user_message}, ], temperature: 0.3, }, timeout30, ) resp.raise_for_status() data resp.json() return data[choices][0][message][content] if __name__ __main__: print(chat(你是一个严谨的技术助手, 请用三句话解释什么是RAG))注意这里故意没有用任何第三方封装库因为requests能看到最原始的请求和响应结构对理解整个链路最有帮助。等跑通了再考虑用各平台自己的SDK或者框架封装都是水到渠成的事。2.3 必须搞懂的四个交互参数调用模型时有四个参数几乎每次都会用到temperature、top_p、max_tokens、model。temperature控制随机性取值一般在0到1之间。数值越低输出越稳定、越确定适合代码生成、信息抽取、格式转换这类任务数值越高越发散、越有创意适合文案写作、头脑风暴。我的经验是需要复现的场景尽量压在0.2以下需要创造力的场景才放到0.7以上。网上很多“模型输出飘忽不定”的问题第一排查对象就是temperature没调低。top_p是核采样参数作用是控制候选词集合大小。现在很多平台推荐只调temperature或者只调top_p两者不要同时猛调否则输出会变得很难预测。我基本固定top_p为默认值只动temperature就已经能覆盖大多数场景了。max_tokens控制生成的最大长度超了就截断常常表现为“话说到一半断了”。设置时根据任务内容估算不是越长越好因为生成长度直接决定成本和延迟。model是模型选择同一个API下往往有多个规格。开发阶段用大模型调试上线时可以考虑“简单任务路由到小模型”这个后边在成本控制部分细聊。2.4 接入阶段最容易踩的坑接入阶段我踩过的最典型的坑有四个。第一个是超时。线上模型推理慢的时候几十秒不返回也是正常的如果你没设timeout请求就会一直挂在那里拖死应用线程。设了timeout又要考虑重试策略对网络抖动导致的小概率失败通常重试2到3次每次退避递增效果就很好了。第二个是限流。API基本都有每分钟调用次数限制并发一高就会收到限流错误。这时候别死磕做足重试退避同时在应用层加信号量控制并发比单纯靠库的重试机制可控得多。第三个是HTTP状态码之外的业务错误。有些平台返回200但body里带着错误码直接解析choices就可能KeyError。健壮写法是先看返回结构里有没有error字段再做响应解析。这个坑不遇到一次很难想起来防御。第四个是消息结构的细节。比如有些平台要求assistant消息不能连续出现有些平台对messages列表总长度有硬限制。调多轮对话时尤为明显后面上下文管理专门讲。3. Prompt工程决定应用上限的隐藏工程3.1 为什么Prompt本身就是工程很多人觉得Prompt就是“写几句话让模型干活”。真做起来你会发现同样的需求Prompt写得好不好效果差距能到天壤之别。原因在于大模型本身不读心它只能根据你给的信息做大概率补全。指令模糊它就按自己最舒服的方式猜猜的结果往往不是你要的。Prompt工程的核心目标只有一个降低模型“猜错”的概率。办法无非是把任务目标、背景信息、输入材料、输出格式、约束条件全部讲清楚。你越想让它稳定干活指令就越不能只靠自己“感觉说清楚了”而是要写到换一个人来读也不会理解错的程度。给个直观对比。弱Prompt是“帮我写一个活动通知。”模型到底写校园活动、促销活动还是技术沙龙语气正式还是轻松要不要给具体时间地点全是猜。强Prompt则会把场景、目标、要点、语气、长度、格式一一写明模型几乎没有自由发挥余地自然更稳定。3.2 一套可以直接抄的结构化模板我实际项目里一直在用一套模板核心结构是五段式任务定义、背景说明、输入数据、输出要求、约束条件。任务根据用户评论生成一条客服回复草稿。 背景我们是一家提供在线存储服务的公司回复需要维护品牌形象同时解决用户疑虑。 用户评论{这里放用户评论原文} 输出要求 1. 以“感谢您的反馈”开头。 2. 语气温和不得承诺无法兑现的事项。 3. 全文不超过80字。 4. 直接输出回复内容不要输出任何解释。这套模板看起来简单但每个部分都有实际作用。任务定义让模型知道“做什么”背景让模型知道“以什么身份、为了什么目标做”输入数据是原料输出要求定义了考核标准约束条件堵住了模型想自由发挥的漏洞。真正遇到复杂任务时每个部分还可以再拆细比如背景里加用户画像约束里加禁用词列表。写Prompt有个很实用的自我检查方法把模板里的模型替换成实习生想象一下你把这个需求交给一个靠谱但完全不了解前因后果的实习生他能交回来合格的活吗如果不能就是Prompt还不够清楚。3.3 few-shot与思维链小投入大回报结构化模板之上提升效果最明显的两个技巧就是few-shot和思维链。few-shot就是在Prompt里给模型几个输入输出示例。模型虽然不能真正“理解”示例但示例能极大地约束输出风格和格式。比如你要求模型把用户问题分类成“售前、售中、售后、其他”四类光写一句“请分类”不够稳但你给两个精确的分类例子再让它分准确率立刻上一个台阶。示例数量不用多我常试2到5个覆盖典型情况和边界情况就够了。思维链Chain-of-Thought是要求模型在做复杂推理时先把推理过程写出来再给最终答案。典型例子是数学题、多条件判断、需要逐步拆解的任务。直接问“这个任务能不能安排在今天”模型可能跳步如果要求它“先列出任务清单、估计每项耗时、再判断是否排得开”答案的可靠程度会明显提升。思维链不一定非要把“我们一步步思考”这句话写出来更有效的方式是在任务说明里直接要求“给出计算过程再在最后一行输出结论”。这样既能拿到推理依据又方便后一步解析结果。不过要注意简单任务不要强行加思维链否则白白增加token消耗和延迟收益却不明显。3.4 输出格式管理的实战技巧业务系统要消费模型的输出就不能容忍模型自由发挥必须把输出格式牢牢钉死。最常用的做法是要求JSON输出。直接说“输出JSON”往往不够稳模型的JSON里可能带markdown代码块标记、可能字段名跟你要的对不上、可能夹杂解释文字。更靠谱的写法是把期望的JSON结构直接写进Prompt附上示例值并加上“只输出JSON对象不要输出解释”。比如请从用户问题中提取关键信息输出JSON对象字段如下 { intent: query_order | cancel_order | other, order_id: 字符串如果没有则为null, needs_human: 布尔值是否必须转人工 } 只输出JSON不要输出任何其他内容。即使这样仍不能100%保证模型按格式输出。所以在代码侧要做两件事第一解析时用宽松策略比如先把markdown代码块标记剥掉再用正则从文本里抽第一个JSON片段第二解析失败时重试一次把“上次输出格式错误请严格按照要求重新输出”作为追加消息传回去。这套组合拳能让格式稳定性从90%拉到99%以上。温度参数在这里也是关键。凡是涉及输出解析的任务temperature直接调到0到0.2之间能明显减少格式飘忽。4. 上下文与RAG让模型查得到、记得住4.1 先理解模型的“记忆力”到底有多差大模型虽然看起来知识渊博但存在两个硬伤。第一它的训练知识有截止时间最新的业务信息、内部文档、实时数据它一概不知第二它天生有幻觉倾向不知道答案的时候不会老实说“不知道”而是会编一个听起来合理的答案。所以想让模型回答业务问题必须把相关资料主动送到它眼前。RAG检索增强生成就是干这件事的先从一个知识库里检索出跟用户问题最相关的内容再把检索结果拼进Prompt让模型基于这些内容回答。说白了就是把模型从一个什么都硬答的“半吊子”变成“开卷考试的考生”资料由你给它负责阅读和作答。我一开始做知识库问答时天真地以为“把文档全部塞进Prompt就行”结果几百页文档一塞要么超长被截断要么模型抓到一堆无关内容开始胡说。后来才意识到RAG的关键不只是“塞进去”而是“怎么塞得少而准”。4.2 RAG的完整流程与核心环节一个标准的RAG流程大致分成离线索引和在线检索两段。离线索引阶段把原始文档清洗干净去掉页眉页脚、无关广告、乱码然后按策略切分成小块每一块通过Embedding模型转成向量存入向量数据库。在线检索阶段把用户问题也转成向量在库里搜索最相似的若干文本块再把这些文本块作为上下文连同原始问题一起发给模型生成回答。整个链路里最影响效果的有三处切块策略、Embedding模型、检索后处理。Embedding负责把文本映射成一个高维向量语义相近的文本在向量空间里距离更近。选Embedding时重点看两点一是有没有针对中文优化过直接拿英文预训练的向量模型跑中文效果通常打折扣二是对比检索召回样例不要只看榜单分数实际业务里“相近语义”的定义很个性化跑一跑自己数据最有说服力。向量数据库的选择则看规模。实验阶段只有几千个块直接写个脚本加载到内存里用numpy算相似度就完全够用到了生产级别再用专门的向量库重点关注是否支持混合过滤、索引构建速度和查询延迟。4.3 切块策略怎么选切块是整个RAG里最容易被轻视、又最容易翻车的地方。切大了一块内容包含多个主题跟问题相关的部分被稀释切小了语义不完整单块信息量不足模型也答不利索。我用的经验值是这样的常规文档按固定长度切512个token左右相邻块重叠80到100个token防止上一块末尾的语义被腰斩。代码或日志这类结构性强的内容优先按函数、模块、报错段落切一个完整单元算是天然的好块。Markdown、HTML这类带标题结构的文档按标题层级切分效果往往比纯固定长度好很多。三者的对比我整理成了表格切块方式优点缺点适用场景固定长度重叠简单通用实现成本低可能切断语义通用文档、百科类内容结构切分语义完整贴合原文块大小不均代码、Markdown、HTML、合同条款语义切分按语义边界切质量高依赖额外模型成本高长报告、会议纪要、问答对切块这块没有绝对标准最好的办法是拿真实业务文档配合测试问题做对比实验。你会很快发现某些问题检索不到改改切块策略就好了根本不用换Embedding模型。4.4 检索优化从“能搜到”到“准确”向量检索不是银弹典型场景就经常翻车用户问“订单号ABC123有没有发货”向量检索会把“订单号ABC123”这种精确匹配需求当作语义相似度来处理容易匹配到完全不相关的句子。解决办法是混合检索把关键词匹配比如BM25和向量检索的结果合并再做融合排序。关键词擅长精确匹配向量擅长语义扩展两者结合后既能听懂“发货了没有”这种口语问法也不会放过“ABC123”这种精确ID。检索质量的重排环节同样关键。向量检索先召回一批候选比如50个之后可以用更重的重排模型对候选按相关性重新打分只取前5个作为最终上下文。这一步能把大量“搜到了但不相关”的结果过滤掉模型生成质量立竿见影。重排虽然增加了一点延迟和成本但相比整体效果提升这投入非常值。还有一个小经验检索结果数量不要盲求多。每一块可能几百个token5块就是一两千token如果大部分无关模型就容易被带偏。检索结果宁缺毋滥给模型喂3到5块高质量内容比喂10块乱七八糟的内容效果好得多。4.5 把检索结果拼进上下文检索到内容之后拼Prompt也有一些讲究。我习惯用清晰的标记把检索材料和其他指令分隔开比如“以下是从知识库检索到的参考资料用 编号 标注”。然后在回答要求里明确“只能依据参考资料回答如果资料没有提到直接回答‘根据现有资料无法确认’不要编造。”这里有个反直觉的细节参考资料里如果混入了明显与问题无关的内容模型被带偏的概率会急剧上升因为它倾向于“凑合所有输入”。所以拼接前要做一次质量过滤比如相关性得分低于阈值的直接丢弃。另外要盯紧上下文总量。不同模型对上下文长度有硬限制超出部分会被静默截断。一旦系统指令被截断模型行为立刻崩坏。我的习惯是做一个简单的token估算函数每次拼接后算总量超出限额就优先丢弃最老的对话历史、而不是压缩参考资料。参考资料是当前问题的“考卷”对话历史只是辅助保前者丢后者。5. Function Calling与Agent让模型从聊天变成干活5.1 工具调用解决的问题大模型本质上只能“生成文字”不能真去查你的订单表、不能真去改数据库、不能真去发邮件。但如果你定义一个函数列表告诉它“当用户需要查订单时请输出一个结构化的调用请求参数包括订单号”模型就能按要求输出这个请求。应用层收到请求后调用真实接口拿到真实结果再交回给模型模型据此生成对用户的回答。这就是Function Calling工具调用的基本机制。它把模型的“语言理解能力”和外部系统的“真实执行能力”连接起来是大模型从玩具到生产力的关键一步。我印象最深的一个场景是给内部运维做辅助机器人。用户问“线上服务是不是挂了”模型通过工具调用去查监控接口拿到实际状态之后才回答“目前有3个节点异常”。没有工具调用时模型只能凭感觉说“建议检查服务”那种回答本质上就是空洞的废话。5.2 函数定义与调用流程工具调用的核心是函数描述。你需要用一个结构化的schema告诉模型函数名叫什么、有哪些参数、参数类型是什么、这个函数是干什么的。以订单查询为例{ name: query_order, description: 根据订单号查询订单状态和物流信息, parameters: { type: object, properties: { order_id: {type: string, description: 用户提供的订单号} }, required: [order_id] } }调用流程是一个多轮交互而非一次请求就完事用户提问模型分析后认为需要调用query_order于是在回复中携带工具调用请求应用层解析这个请求并调用真实接口拿到结果后作为一条新消息回传给模型模型看到工具结果后生成对用户的最终回答。这个流程在整个Agent应用里会不断循环每一次循环都要维护好messages列表。工具调用的返回结果尽量结构化比如直接返回JSON字符串别让模型去解读一团乱麻的文本日志。日志类数据尤其要提炼只给模型看关键字段。5.3 Agent循环与防失控设计当一个模型可以连续多次调用不同工具时就进入了Agent的范畴。经典的Agent循环是“思考、调用工具、观察结果、再思考”直到模型认为任务完成给出最终答复。这个循环设计上有几个容易失控的点。第一是无限循环模型因为拿不到理想结果不断重试同一个调用token飞快消耗。第二是参数幻觉模型调用了一个不存在的函数名或者编造了一个参数应用层解析直接出错。第三是后果不可控工具带有修改功能的副作用模型在误导下执行了不该执行的操作。我的防失控设计有三板斧一是限制最大迭代次数一般设为3到5次超过就强制结束并转人工兜底二是做函数白名单校验模型要求调用的函数必须存在于注册表里参数也要做类型校验非法参数直接拒绝三是所有带写操作的工具默认加“二次确认”开关对风险操作要求用户确认后再执行。5.4 一个订单查询助手的实现思路用一个具体例子把上面串起来。假设要做一个订单查询助手配置两个工具query_order查订单send_coupon给用户发补偿券。代码骨架大概是这样messages [{role: system, content: 你是订单客服助手。查询订单后如实回答不要编造信息。}] messages.append({role: user, content: 我的订单A123还没发货帮我看看}) for _ in range(5): response call_model(messages, toolsTOOL_SCHEMAS) msg response[message] if msg.get(tool_calls): messages.append(msg) for call in msg[tool_calls]: result execute_tool(call[function][name], call[function][arguments]) messages.append({ role: tool, tool_call_id: call[id], content: json.dumps(result, ensure_asciiFalse) }) else: final_answer msg[content] break这里有个我特别想提醒的细节工具执行的返回结果要当作“事实依据”提交给模型而不是直接拼进用户消息里。协议上通常会区分“工具返回消息”和“普通对话消息”写代码时别图省事把工具结果塞进user消息那样在多轮工具调用场景下模型容易把历史工具结果和用户最新意图混在一起。另外参数解析要容错。模型偶尔会把参数写成JSON字符串、甚至带注释的伪JSON解析失败时先做一轮清理比如去掉注释、修正多余的逗号再重试。解析失败太多次就放弃本轮工具调用直接回复“我需要人工介入”比硬撑更体面。6. 微调什么时候做、怎么做、怎么评估6.1 别急着微调先判断有没有必要微调是在模型原有能力基础上用一批高质量数据做进一步训练让模型在某些任务上表现更好。很多同学一觉得效果不好就想着微调这是成本最高、风险最大的冲动。在动手微调之前建议先把这个判断矩阵过一遍。如果你要解决的是“模型不知道内部知识”的问题优先RAG微调不适合存储碎片化事实如果你要解决的是“输出格式不稳定”的问题优先Prompt约束加解析兜底微调不是唯一解如果你要解决的是“必须完全按照某种话术/风格输出”或者“模型在某个专业领域理解明显不足”且Prompt和RAG都试过仍不满意这时候才轮到微调。再补充一个常见动机数据不能出内网。这种情况下你没法用商业API只能本地部署开源模型再通过微调让模型适配业务。这类场景里微调不是优化手段而是方案前提。微调和RAG从来不是互斥的。实际项目里两者常常配合微调负责让模型适应专业术语和输出风格RAG负责提供最新的事实材料。我印象比较深的某个业务助手就是这么做的模型先经过微调学会规范话术再通过RAG获取实时库存和价格信息两边各司其职。6.2 微调数据怎么准备微调效果七分靠数据三分靠参数。数据质量不过关再怎么调参都是白搭。主流微调格式就是对话格式一条样本对应一轮完整的对话{ messages: [ {role: system, content: 你是订单客服助手回答简洁不得编造信息。}, {role: user, content: 订单A123什么时候发货}, {role: assistant, content: 您的订单A123已于今天上午10点发货预计明天送达。} ] }数据数量上几百到几千条高质量样本足够撬动明显变化不用一上来就追求几万条。数据质量上每条都要检查有没有错误答案、有没有不符合目标风格的表达一个错误样本会造成模型反复学习到不想要的行为。我踩过最深的坑是数据全部来自模型自己生成的答案没做人工质检就拿来训练。结果模型把“胡说”当成正常行为学得更到位了线上效果不升反降。从那以后我坚持每条训练数据的assistant回答至少有一个人工复核节点宁可少训练一千条也绝不带病上船。6.3 LoRA参数与训练实操对大多数团队来说全参微调既烧资源又难控制通常用LoRA就够了。LoRA的基本原理是冻结原始模型权重只在旁边训练一个低秩矩阵训练参数量骤降显存需求大幅减少。LoRA有四个参数需要特别关注lora rank秩、学习率、训练轮数、批次大小。我的经验范围是参数推荐区间我的用法LoRA rank8到32先试8数据量大或任务复杂再加学习率1e-5到3e-4大多任务用2e-4附近起步训练轮数1到5先3看验证loss再调batch size4到8依显存来小显存用梯度累积训练过程中要盯住loss曲线同时准备一组与训练数据不重叠的验证问题在每个epoch结束后跑一遍。训练不足任务表现提升不明显训练过头模型可能“遗忘”原有通用能力连日常对话都变得僵硬。这个平衡点没有固定答案只能靠验证集实测判断。顺带提醒一点LoRA训练完要测试“微调前和微调后的同一批问题”的效果差异因为正常情况下训练loss下降是必然的但业务指标是否提升需要单独验证。模型在训练集上表现好不代表在真实用户提问上表现好。6.4 微调完怎么评估微调后的评估坚决不能只看“感觉变强了”。我见过很多人微调完让同事随口聊两句就说“效果可以”结果一上线发现特定类型的问题崩了一大片。必须建一套简单的评估集再下结论。评估集建议分三块典型主场景样本占六成覆盖用户最常见的问题边界条件占两成比如空输入、超长输入、带敏感词输入历史坏case占两成把微调之前表现不好的真实案例放进去专门验证这些坑有没有被填上。跑完评估后逐条对比微调前和微调后的输出记录结构化指标比如格式通过率、关键字段正确率、拒绝回答的合理性。如果发现微调后把某些不该拒绝的问题也拒绝了说明数据里“不知道就拒绝”的样本太多需要减少拒绝样本比例或降低训练轮数。7. 评估与工程化上线前的最后一公里7.1 没有评估集所有优化都是盲人摸象做一段时间大模型应用后你会发现一个残酷事实每次改Prompt或者换模型都会有一批老case变好、另一批变差如果不记录你根本分不清整体是在进步还是倒退。所以我给所有项目定的规矩是上线前必须有评估集哪怕一开始只有几十条。评估集怎么建把真实用户问题按场景分类收集起来挑有代表性的再补充一些极端情况和历史出过错的问题。指标不要只盯着一个我常用这五类正确率答案对不对、格式通过率结构和字段是否合规、拒绝率该说不知道时有没有乱说、延迟、token成本。有了评估集每次修改都在同一批问题上跑一遍记录分数变化你就能清楚看到改动带来的真实影响。这个习惯能把“我觉得”“大概”“好像”全部替换成数字团队协作时尤其有用。7.2 日志、缓存与并发控制线上系统出了坏case如果没法重放定位问题就成了一场灾难。所以从第一天开始就要记录完整请求日志系统Prompt、用户输入、上下文拼装内容、模型响应、token用量、耗时、工具调用轨迹缺一不可。日志要特别注意上下文拼装内容的记录很多“线上答非所问”就是检索结果或者上下文被截断导致的不记录完整输入你就没法重演。记录时做好敏感信息脱敏手机号、身份证号、密钥这类字段先打码再落库。缓存是降本增效最直接的手段。大模型对完全相同的输入加上相同参数输出不一定完全相同但很多业务场景可以接受短时间内的相同回答。对高频且稳定的请求做缓存命中一次就省一次推理费用。简单场景用内存LRU字典就够了分布式场景可以再上外部缓存组件。并发控制别忽略。API有速率限制本地部署模型有GPU算力限制无限发请求的结果就是雪崩。应用层要加并发信号量超过阈值时排队或者直接返回降级提示同时配合前面说的重试退避。这里的原则是宁可让用户等一秒也不要让系统在高峰时全线崩溃。7.3 成本控制与模型路由策略大模型应用的成本大头在推理token而且输出token通常比输入token更贵。控制成本有几个立竿见影的手段。第一是压输出长度。很多任务其实不需要长篇大论在Prompt里直接限定字数能省不少token。第二是上下文瘦身。多轮对话里历史消息长期堆积会非常烧钱通常做法是滑动窗口只保留最近几轮历史内容做摘要压缩。第三是路由策略简单任务用便宜的小模型复杂任务才用能力更强的大模型。可以先让模型或规则做一次意图识别判断难度后分配模型。路由策略的写法不难核心就是加一个判断def route(user_message): if is_simple_intent(user_message): return cheap-small-model return strong-large-model难点在于“简单”的定义。我的做法是先用一段时间双模型并存把每个请求同时发给大小两个模型对比输出质量和成本积累数据后就能总结出路由规则。上线后再持续监控不同路由的坏case率动态优化阈值。7.4 幻觉缓解与内容安全幻觉是大模型天然的缺陷工程上要层层设防。最便宜的一层是Prompt规则强制要求“没有依据时直接说不知道”第二层是检索引用回答必须标注引用的知识库来源编号没有编号的回答判为不合规第三层是置信度策略对检索分数低于阈值的问题直接拒绝作答并引导用户补充信息。还有一种幻觉来自模型“过度配合”。用户问“我能不能不付款就下单”模型为了讨好用户可能给出建议。这类问题光靠Prompt压不住要在应用层做输入输出过滤输入侧识别高风险意图输出侧再做一次内容校验两边都拦住才安全。内容安全上还有两件事要提早规划敏感数据的脱敏处理和用户隐私保护。日志、向量库、缓存里都可能留存用户数据脱敏和证书管理要有明确方案。这块没有统一模板但晚做比早做代价大得多。8. 高频问题排查实录8.1 问题速查表下面这些是群里同学和项目里新人问得最多的问题我整理成了一张速查表基本覆盖了从开发到上线的大部分坑。现象可能原因解决建议输出偶尔带markdown代码块Prompt没强约束或temperature太高加“只输出JSON不要代码块”temperature调到0多轮对话后模型“失忆”messages太长被截断或历史未滑动窗口记录上下文占用优先保留system和最近轮次检索结果相关但回答不对参考资料混入无关内容或模型没被要求只依据资料提高检索阈值重排明确要求“资料未提及则说不知道”工具调用死循环没有最大迭代限制或参数解析失败导致重试加MaxIterations参数校验和容错解析微调后通用能力下降训练轮数过多或数据分布单一减少epoch微调后用通用能力抽查集验证每次问同一问题答案都变temperature太高降低temperature对稳定场景加缓存8.2 四个我实际遇到的排错案例第一个案例是JSON输出飘忽不定。线上客服分类功能偶尔返回带markdown标记的JSON解析直接失败。排查时发现我虽然写了“输出JSON”但没写“不要代码块”且temperature设成了0.7。修复方式是强约束0.2温度解析失败重试问题当天就消失了。第二个案例是用户多轮提问后答非所问。看日志发现用户发了5轮消息后messages长度已经超过模型上下文限制早期的系统指令被静默截断。修复方案是先压缩历史对话为摘要再拼上当前问题和检索资料确保系统指令永远不被挤出上下文窗口。第三个案例是RAG检索出大量无关内容模型被带偏。用户问“怎么退款”检索匹配出来的段落五花八门模型把售后政策和会员规则混在一起瞎答。修复方式是做了重排只保留真正高相关的3到5个段落同时给Prompt加上“如果资料不相关直接说无法确认”。第四个案例是Agent无限循环烧了几千个token。排查发现模型在某次工具调用时把参数解析成了非法JSON应用层重试了一次又失败模型陷入“继续尝试”的死循环。修复方式有三个最大迭代限制、工具参数严格校验、解析失败超过两次强制结束转人工。最后分享一句我自己的体会大模型应用开发真正拉开差距的往往不是“谁调模型调得更花哨”而是“谁能在问题发生时更快定位根因”。上面这些坑几乎每个都能靠日志和评估集提前发现。所以无论项目多急别把日志和评估砍掉。你省下的那两天迭代时间迟早会在线上故障里连本带利还回去。