2024 年总结:LLM 应用工程化检查清单
2024 年初,很多团队还在讨论“怎样把模型接进产品”;到年底,问题已经变成“接进去之后如何让它稳定工作”。这一年的变化很像从搭积木进入修桥阶段:模型能力越来越强,接口越来越丰富,但真正让人睡不着觉的,已经不是 Demo 能不能跑,而是线上会不会答错、会不会泄露、会不会超时、会不会贵到无法续费。
我这一年看过不少大模型项目。最初的版本通常都很有冲劲:一个聊天框,一套 Prompt,加一个 RAG 或函数调用,就能演示出令人惊艳的效果。等真实用户进来,问题开始成串出现:文档更新后检索不到,长上下文把关键信息淹没,模型输出 JSON 偶尔多一个逗号,用户一句话就能诱导工具越权,开发团队甚至不知道一次错误回答究竟发生在哪一层。
所以这篇总结不再罗列热点名词,而是把 2024 年真正值得留下的工程经验整理成一份清单。它不是“全部做到才能上线”的教条,而是一张地图:你可以知道系统缺哪块,知道哪块风险最高,也知道下一次迭代应该先修什么。
一、先确认:你解决的是问题,不是展示模型
上线前先写清楚三个句子:用户是谁,任务是什么,成功意味着什么。没有这三句,团队很容易把“模型回答得很像人”误认为产品价值。
用户场景 → 目标任务 → 可验证结果 → 允许的失败方式
例如“客服助手”不是一个完整目标。更具体的目标应该是“帮助客服在两分钟内找到退款政策,并给出带原文引用的回复草稿”。这样才能决定是否需要 RAG、什么算正确、无答案时应该怎么处理,以及哪些动作必须由人工确认。
还要提前定义不能做什么。知识库没有依据时是否拒答?涉及个人信息时是否脱敏?模型是否允许修改订单?范围越清楚,后面的 Prompt、工具和评测越不会互相打架。
二、模型选型:不要只看榜单第一名
模型选型至少要同时考虑质量、延迟、成本、上下文长度、结构化输出、工具调用和数据边界。一个模型在公开榜单上得分很高,不代表它最适合你的中文客服、代码审查或内部知识库。
建议先建立任务样本,再做对照实验:
type ModelReport = {
model: string
task: string
successRate: number
p95LatencyMs: number
inputCost: number
outputCost: number
schemaPassRate: number
refusalQuality: number
}
不要用一两个“特别惊艳”的问题做决定。评测集应该包含正常、边界、无答案、长输入、恶意输入和工具失败样本。平均质量之外,要看长尾和最坏情况。对于高风险任务,稳定地拒绝错误请求,可能比偶尔多答对几个普通问题更重要。
模型也应该通过统一适配层接入。应用业务不应到处散落某个厂商的消息格式和错误类型,否则换模型会变成全项目重构。适配层负责请求转换、流式事件、重试和计量,业务层只依赖自己的抽象。
三、Prompt:从“会说话”走向可维护
2024 年最大的 Prompt 教训,是一段写得很长的系统提示词并不等于一段可靠的规则。Prompt 应该有版本号、变更记录、适用场景和回归样本;重要约束还要在代码和工具层重复执行。
一个可维护的 Prompt 通常会明确:角色、任务、输入边界、输出格式、证据要求、失败行为和工具使用条件。把这些混成一段充满形容词的文字,模型和人都很难判断优先级。
目标:根据公开政策回答退款问题
证据:只能使用检索结果中的内容
无答案:明确说明没有找到依据
格式:返回 answer、citations、needsReview
边界:不要修改订单,不要请求用户密码
Prompt 变更后必须重放固定测试集。只看几个手工问题很容易被偶然性欺骗。还要记录模型版本、温度、工具版本和检索配置,否则即使结果变好,也无法知道究竟是哪次改动产生了影响。
四、结构化输出:让字符串接受类型检查
只要下游程序要消费模型结果,就不要把自然语言当接口。JSON Schema、类型校验和业务规则应该分层存在:
type ActionPlan = {
intent: 'search' | 'answer' | 'ask_user'
answer?: string
citations: string[]
needsReview: boolean
}
function validatePlan(value: unknown): ActionPlan {
const parsed = actionPlanSchema.safeParse(value)
if (!parsed.success) throw new Error('模型输出不符合结构约束')
if (parsed.data.needsReview && parsed.data.intent === 'search') {
throw new Error('审核状态与动作意图冲突')
}
return parsed.data
}
格式正确只是第一关。日期可能是合法字符串但不在营业时间,金额可能是数字但超过额度,引用可能是 URL 但不来自实际检索结果。Schema 管形状,业务代码管语义,权限系统管是否允许执行,不能让一个正则表达式承担全部责任。
五、RAG:真正难的是数据和闭环
RAG 在 2024 年从概念变成了常规能力,但很多项目仍然只关注向量数据库。生产 RAG 至少要检查:文档解析是否保留标题和表格关系,切分是否保持语义完整,Embedding 是否适合语言和领域,召回是否结合关键词,重排是否稳定,答案是否引用真实证据。
源文档 → 解析 → 清洗 → 切分 → Embedding
↓
用户问题 → 改写 → 混合检索 → 重排 → 上下文压缩 → 生成
↓
引用与反馈
离线要有黄金数据集,线上要有 Trace 和用户反馈。评测必须分开看召回、排序和生成,不要把所有错误都归因给模型。知识库修改时也不能每次清空全库重建,要有稳定文档 ID、内容哈希、增量任务、删除处理和索引版本。
RAG 还有一个重要边界:检索到内容不等于内容正确。来源、更新时间、权限和租户范围都要进入系统。模型可以根据证据组织回答,却不能替数据来源承担可信度。
六、多模态:输入变多,验证不能变少
文本、图片、音频和视频进入同一应用后,系统获得了更多信息,也获得了更多不确定性。图片中的小字可能被识别错,表格的行列关系可能丢失,音频转写可能把关键数字听错。
多模态管道应该保留中间产物和置信信息:原始文件、转写文本、OCR 坐标、表格结构、模型描述和最终结论之间要能关联。对发票金额、合同日期、药品剂量等高风险字段,应该使用规则、专用解析器或人工抽检进行二次校验。
原始媒体 → OCR / ASR → 结构化抽取 → 业务校验 → 生成回答
└────────────── 保留来源和坐标 ──────────────┘
不要因为模型“看懂了图片”就跳过验证。多模态模型的错误通常很自然,用户反而更难察觉。越像人说话,越需要系统给出可追踪的证据。
七、长上下文:能放下不等于能用好
上下文窗口变长是好事,但它没有消除信息选择问题。把整本手册塞进 Prompt,成本会上升,关键段落可能被噪声淹没,模型还可能忽略中间位置的内容。
工程上要区分三种场景:短输入直接处理,长文档先分层摘要或检索,跨多轮任务使用显式状态和持久化记忆。不要把“窗口够大”当成数据架构。
评测长上下文时,要设计针藏问题、跨段关联、冲突信息和无答案样本。测试的不只是能否找到某句话,还要看它是否把正确段落和问题关联起来,是否会被靠后的错误信息覆盖。
成本也要按输入和输出分别核算。长上下文每轮重复发送,会让一个看似便宜的聊天功能迅速变贵。缓存、摘要、检索和上下文压缩,往往比单纯更换模型更有效。
八、微调:先确认 Prompt 和数据真的不够
2024 年 LoRA、QLoRA 和 SFT 让微调门槛降低了,但“能训练”不等于“应该训练”。如果问题是知识更新,优先考虑 RAG;如果问题是输出格式,先做结构化约束;如果问题是工具权限,应该改执行层。微调适合稳定地改变风格、格式、领域表达和特定任务行为,不适合替代实时数据和安全控制。
微调前必须准备高质量数据:明确输入输出、去重、脱敏、去除互相矛盾的答案,并保留独立验证集。数据数量不是第一指标,错误样本和边界样本更有价值。
训练集:学习行为
验证集:选择超参数和发现过拟合
测试集:只在最后证明泛化
训练后要检查通用能力退化、拒答行为、长度变化、事实准确率和工具调用格式。模型可能在目标任务上变好,却把原来会做的事情忘掉;也可能学会训练集的表面模板,而没有真正理解规则。
九、安全:Prompt Injection 不是模型一句话能解决的
任何外部内容都可能包含恶意指令:网页、文档、邮件、代码仓库和用户上传图片都不能自动成为系统命令。要把指令与数据分层,限制工具白名单,在服务端重新校验权限,并对高风险动作设置审批。
模型提议动作
↓
参数校验 → 身份校验 → 资源权限 → 风险策略
↓
低风险自动 / 高风险审批
不要相信模型参数里的 isAdmin、approved 或 safe 字段。它们可以作为候选信息,却不能成为授权事实。真正的身份来自可信会话,真正的权限来自服务端策略,真正的审批来自用户或工作流系统。
日志也要做隐私设计。问题、答案、文件、令牌和内部文档不能无限期原文存储。使用哈希、脱敏、短期调试存储和访问审计,确保排障不会制造第二个数据泄露渠道。
十、可观测性:没有 Trace 就没有复盘
LLM 应用至少要能沿着一个 traceId 找到:输入摘要、模型版本、Prompt 版本、检索结果、工具参数、每段延迟、Token、错误、最终答案和用户反馈。
type LlmTrace = {
traceId: string
model: string
promptVersion: string
steps: Array<{
name: string
latencyMs: number
status: 'ok' | 'error'
}>
inputTokens: number
outputTokens: number
userFeedback?: 'helpful' | 'unhelpful'
}
指标至少分三层:基础设施层的成功率、延迟和限流;模型层的 Token、结构化通过率和拒答率;业务层的任务成功率、人工接管率和用户负反馈。一个接口 200 不代表业务成功,一个答案流畅也不代表事实正确。
抽样日志要有保留策略,告警要能关联到样本。只在大屏上显示“错误率 3%”,却不能打开具体失败轨迹,等于知道着火但不知道哪栋楼在烧。
十一、成本:把一次回答当成完整账单
成本不只来自模型价格。输入输出 Token、Embedding、向量数据库、Reranker、OCR、ASR、缓存、网络、GPU、日志存储和人工复核都要算进去。
单任务成本
= 模型输入输出
+ 检索与重排
+ 多模态预处理
+ 重试与降级
+ 存储和观测
+ 人工处理
不要只看平均成本,要看 P95、最坏重试次数和高峰并发。为不同任务设置模型路由:简单分类用小模型,复杂推理用强模型;能缓存的结果缓存;能提前结束就不要继续生成;无法承受成本时要有明确的降级回答。
成本优化不能牺牲安全和证据。把日志关掉、跳过校验、无限压缩上下文,也许能让账单好看,却会把风险转移到更昂贵的故障和人工处理中。
十二、发布门禁:不要把生产当实验室
一次可执行的发布门禁可以包括:
- 固定评测集的质量没有超过回归阈值。
- 结构化输出和工具参数通过率达到目标。
- P95 延迟、Token 和单位任务成本没有突破预算。
- 无答案、越权、Prompt Injection 和敏感数据样本通过安全检查。
- 关键 Trace、指标、告警和回滚配置已经存在。
- 新索引、Prompt、模型和代码都有版本号,可以定位和恢复。
代码检查 → 单元测试 → 评测集 → 安全测试 → 小流量 → 监控 → 放量
小流量发布不是形式。它能发现真实用户表达、长尾文档、峰值延迟和评测集没有覆盖的工具错误。放量前要明确停止条件:哪些指标恶化就暂停,谁负责决定回滚,旧版本能否立即恢复。
十三、我在 2024 年留下的工程排序
如果团队资源有限,我会按这个顺序做:先定义任务和失败边界,再建立最小评测集;然后做结构化输出和服务端权限;接着补 Trace、成本和延迟监控;之后优化 RAG、长上下文和模型路由;最后才是微调、复杂 Agent 和更炫的交互。
这个顺序可能没有演示视频那么令人兴奋,却能让系统一步步变得可信。没有评测的优化不知道是否有效,没有权限的工具调用不应该上线,没有 Trace 的错误无法复盘,没有预算的复杂推理最终会变成财务问题。
十四、我的总结:工程化就是把惊喜变成可重复
2024 年大模型最重要的变化,不只是模型会看图、能读长文、能调用工具,而是我们开始认真面对“能力如何进入系统”这个问题。Demo 依靠一次成功证明可能性,产品则需要面对每天成千上万次不完美的输入。
我现在判断一个 LLM 应用是否成熟,不先问它用了哪个热门模型,而是看它能不能回答这些问题:结果怎么评测?错误怎么定位?权限谁来判断?知识怎么更新?成本谁来承担?出了问题如何停止和回滚?
如果这些问题还没有答案,继续堆 Prompt、模型和工具只会让系统更复杂。先把边界写清,把数据、状态、版本、指标和失败路径接起来,能力才会从一次偶然的惊艳变成稳定的服务。
这也是我对 2024 年最朴素的总结:大模型应用真正的门槛,不在于让模型说出一句漂亮的话,而在于让它在证据、权限、成本和时间的约束下,持续做出值得信任的事。下一年我们会讨论推理模型和 Agent,但无论模型多强,这些工程底座都不会过时。