优化大型语言模型的规模:基于路由、检索与评估而非仅考虑模型本身的大小。
学习如何根据工作负载在小型与大型语言模型之间进行选择,通过衡量每项成功任务的成本,并优先运用路由、RAG、缓存及验证技术。
参数规模很容易成为新闻标题的素材,人们也容易将其视为产品质量的指标:700亿参数的模型肯定比70亿参数的更好,因此能负担得起的最大规模模型自然就是最佳选择。但在实际应用中,这种简化思路很快就会失效。更大规模的模型通常每次调用成本更高、响应更慢、会增加基础设施负担,而且往往解决的是应用程序根本不存在的问题。本指南将介绍如何根据工作负载而非模型规模来选择模型,如何综合考虑成本、延迟和故障问题,以及哪些架构手段(路由、检索、验证、缓存和普通代码)往往能带来比单纯升级更好的效果。
为何“规模越大越好”在实战中不再适用
这种直觉是可以理解的。软件工程师们数十年来一直见证着硬件升级带来的好处:更快的CPU、更多的内存、更大的硬盘以及更新的GPU几乎总能带来性能提升。当语言模型变得越来越大、功能越来越强时,人们很自然地会沿用这种思维模式。如果某个模型比另一个模型的推理能力更强,那为什么还要刻意选择较弱的那个呢?
一旦模型被应用于实际任务中,答案便会出现。人们提出的问题也会从“哪个模型最聪明?”转变为“哪个模型能为此类特定任务带来最佳结果?”。这是两个截然不同的问题。规模最大的候选模型或许能给出略好一些的答案,但速度会慢得多,成本也可能高出数倍。对于简单的分类任务而言,它可能毫无用处,生成的答案长度超出用户界面的显示范围,还会耗尽上下文预算并增加部署的复杂性。最重要的是,它解决的或许根本不是你实际面临的问题。
从工作负载出发,而非参数数量
选择模型的一种更可靠方法是从对任务本身的描述开始。在比较任何模型之前,先回答以下问题:
- 输入数据是什么样的?
- 期望得到什么输出?
最后一个问题才是真正的工程难题,本指南的其余内容都在探讨如何回答它:为何最大的模型并不一定就是最好的,如何在小型与大型模型之间做出选择,何时大型模型才真正物有所值,以及合理的生产环境设计应具备哪些特点。
同一款支持产品,两种截然不同的工作负载
以企业支持应用为例。当用户输入“重置我的密码”时,AI层应该做什么?它很可能会识别出用户的意图,并将其映射到类似以下的标签,这样应用就可以将任务交给固定的、确定性的工作流程来处理。
PASSWORD_RESET
无需使用复杂的推理模型就能得出该标签。通过复杂的模型来处理此请求是一种值得质疑的设计选择,因为简单的模型或甚至关键词与规则匹配就能很好地完成这项任务。
现在想象另一条消息:自昨天的部署以来,欧洲客户一直遇到间歇性的支付失败问题,用户希望系统能够将部署差异与支付服务日志进行对比,找出可能的故障模式,判断是否涉及新的重试机制,并提出回滚方案。这类请求需要处理的内容要多得多:
- 获取相关的部署变更记录和日志
- 大量的上下文信息
- 阅读并理解代码的能力
- 分析日志输出内容
- 能够进行多步骤推理
- 跨系统关联各种事件
- 给出清晰的技术解释
- 诚实地处理不确定性问题
在这种情况下,更强大的模型确实能够创造价值。错误并不在于使用大型模型,而在于将这两类任务视为相同的工作负载。
模型价值的实用定义
一种有助于做出选择的简单方法论是:
模型价值 = 能力 × 可靠性 × 实用性 ÷ 成本
这并非一个需要计算的公式,而是一种思维方式。一个能力提升10%,但成本增加5倍、速度降低3倍的模型,并不必然就是更好的生产选择。同样,一个价格极低却无法完成任务的模型也是糟糕的选择。你需要优化的并非最高的智能水平,而是单位成本、延迟和复杂度下最具实用价值的智能。
为何大型模型如此诱人,以及限制因素何在
偏好大型模型并非不合理,因为它们确实具有显著优势。它们往往能更好地处理复杂推理,在不同任务中表现更为稳定,更从容地理解模糊的指令,更高效地处理复杂的代码库,需要的任务特定提示也更少,并且在真正艰巨的任务上能展现出更强的能力。
那么为何不在所有场景都使用最强大的模型呢?因为实际应用中存在诸多限制,而这些限制往往不会体现在排行榜的数字上。想象一下某个接口每天要处理10万次请求。如果更大型的模型每次调用的成本明显更高,这种差异就不再是抽象概念,而是会体现在基础设施预算中。如果更大型的模型速度也更慢,用户就会察觉到。如果它倾向于生成冗长的回答,输出所需的令牌数量也会随之增加。而如果应用程序需要执行成千上万个简单任务,将每个任务都发送给高级推理模型无疑是一种浪费。
选择模型是一个优化问题,而非人气竞赛。
基准测试榜单中排名靠前的模型并不一定就是能打造出最佳应用程序的模型。
参数数量只是其中一个考量因素
模型之间的比较往往只关注参数规模:70亿、130亿、340亿、700亿,甚至数百亿。仅凭规模很难判断模型的适用性。实际的比较应同时考量多个维度,例如:
- 在特定任务上的表现能力
- 延迟,包括生成第一个token的时间以及总生成时间
- 每次请求及每完成一项任务的成本
- 在预期负载下的处理能力
- 任务实际所需的上下文大小
- 多次运行时的可靠性与一致性
- 部署选项,包括本地或私有主机托管
- 可控制性
可控制性值得特别重视,因为它很容易被忽视。一个模型可能能力很强,但却难以控制在合理范围内。在企业工作流程中,可预测的行为往往比创造力更重要。
结构化提取是不同的目标
以发票提取为例,该功能需要的是如下所示的固定格式,而非关于文档的详细分析文章。
{
"invoiceNumber": "...",
"invoiceDate": "...",
"vendor": "...",
"total": 0
}
这里重要的是能够被下游代码每次都准确解析的可靠、格式规范的输出。这与一般的推理能力是不同的优化目标,规模较小或约束更多的模型往往能很好地满足这一要求,尤其是与模式验证结合使用时。
延迟:首个生产环境中的陷阱
在演示中,六秒的等待时间似乎还可以接受。当得到令人印象深刻的答案并与团队分享时,大家都会感到满意。但在实际应用中,将同样的处理流程放在按钮后面后,体验就会发生变化:用户点击后出现加载动画,接着三秒、五秒甚至八秒的时间流逝。此时没人会再欣赏模型的智能之处,反而会在想为什么应用如此缓慢。
延迟是产品的一项特性,在交互式软件中其重要性尤为突出。
为何更大型的模型响应速度更慢
具体的关系取决于诸多因素:模型架构、硬件、服务堆栈、量化处理、批量处理方式、生成的标记数量、提示词长度以及模型设计。不过总体而言,计算需求更高的模型每生成一个标记需要更多资源,因此返回结果的速度也会更慢。这种情况在以下场景中最为明显:
- 聊天界面
- 编码助手与自动补全功能
- 语音助手
- 客户支持工具
- 交互式控制面板
- 存在多步骤等待累积问题的智能工作流
自动补全功能恰恰体现了这一点。需要五秒钟才能给出建议的自动补全已不再是真正的自动补全,而是一种干扰。相比那些让开发者等待的强大模型,能够几乎立即给出响应的较小模型往往更有用。
流式处理提升的是用户体验,而非计算能力
流式处理是让等待时间显得更短的标准方法。没有它,用户必须等到完整响应准备好才能看到任何内容:
[wait...]
Hello! Here is the answer...
通过流式处理,随着数据分块陆续到达,屏幕上的内容也会逐步显示:
Hello
Hello, here
Hello, here is
Hello, here is the
Hello, here is the answer...
要明确区分这两者。流式处理能加快首批内容的显示速度,从而降低感知延迟,但它并不会减少计算量或得到完整答案所需的总时间。这属于用户体验的提升,而非性能优化,对于管道中的分类器这类非交互式步骤也毫无帮助。
成本:按每个成功任务来衡量
许多原型正是从这里开始变成高成本系统的。在开发阶段,成本看似微不足道:一名工程师发送几条提示语,没人会考虑费用问题。但当真实流量到来时,每个请求所携带的内容远不止用户的消息:
- 大量同时在线的用户
- 每次会话中的多个请求
- 较长的提示语
- 检索到的文档
- 工具的运行结果
- 对话历史记录
- 生成的响应内容
代币交易量增长迅速,模型选择的重要性也随之大幅提升。
相比每次调用的价格,更准确的衡量标准是正确完成任务的成本。以下是两种假设模型的对比(数据仅为示例,并非实际测试结果):
- 模型A:每次请求费用为0.01美元,任务准确率为90%,每完成一次任务大约需要1.11次请求,因此每次成功任务的成本约为0.011美元。
- 模型B:每次请求费用为0.05美元,任务准确率为96%,每完成一次任务大约需要1.04次请求,因此每次成功任务的成本约为0.052美元。
预期尝试次数即为1除以成功率,因此每次成功的成本则为请求费用除以准确率。如果模型B的成本是模型A的五倍,但仅能带来微小的改进,那么企业很可能会选择模型A。而当错误答案会带来高昂代价时,情况就会发生变化——比如引发退款、合规问题或服务中断。正因如此,成本必须与失败后果一起考量,而不能单独考虑。
用于小问题的过大模型
一种常见的反模式是这样的:每条收到的消息都被视为投诉、问题或退款请求,然后全部交给最复杂的模型处理。这样做的原因是方便——一个API、一个提示词、一个模型,问题就解决了。但架构设计的重点并非让某个组件具备处理所有任务的能力,而是为每项任务匹配合适的组件。对于简单的分类任务,可选方案包括:
- 确定性规则
- 嵌入模型
- 小型语言模型
- 专用分类器
- 为低置信度案例准备的更大模型
最后一种方案能带来最有效的生产环境架构模式之一。
分级处理:将昂贵模型用作异常处理机制
简单的设计会直接将所有任务发送给大型模型:
Every request
↓
Large model
分级处理设计让成本较低的模型先进行尝试,并判断其置信度:
Every request
↓
Small/cheap model
↓
Confidence check
↓
┌───────────────┐
│ │
High confidence Low confidence
│ │
Fast answer Large model
置信度较高的答案会立即返回;只有不确定的情况才会被转交给更复杂的模型。原本作为默认处理路径的高成本模型此时则变为异常处理机制。该模式依赖于可靠的置信度信号,比如经过校准的分类器得分、不同方法之间的一致性,或是能够识别并拒绝错误输出的验证机制;因此在依赖“高置信度”分支之前,需先检测低质量答案通过该分支的频率。
在不同层级模型之间路由请求
一旦这样思考,模型就不再被视为竞争对手,而更像是专门的工作者。请求路由器可以位于多个层级之前,为每项请求选择合适的处理层,在任何内容到达用户之前先进行统一的验证:
User Request
|
v
Request Router
|
+-----------+-----------+
| | |
v v v
Simple Medium Complex
| | |
v v v
Small LLM Mid Model Large Model
| | |
+-----------+-----------+
|
v
Validation Layer
|
v
Response
路由器需要某种复杂度概念。最简单的版本就是一组简短的类别:
SIMPLE
MEDIUM
COMPLEX
随后,调度逻辑只需根据分类器的判断结果进行简单切换即可。下面的示例是用 C# 编写的,但语言只是形式上的;同样的结构在 TypeScript 服务中同样适用。
public async Task<string> ProcessAsync(Request request)
{
var complexity = await classifier.ClassifyAsync(request);
return complexity switch
{
Complexity.Simple =>
await smallModel.GenerateAsync(request),
Complexity.Medium =>
await mediumModel.GenerateAsync(request),
Complexity.Complex =>
await largeModel.GenerateAsync(request),
_ => throw new InvalidOperationException()
};
}
重要的是代码所体现的架构理念:并非每个请求都需要最高的智能处理。如果始终遵循这一原则,这一个决策就能显著改变人工智能系统的成本结构。需要注意的是,分类器本身会为每个请求增加一次调用开销和一定的延迟,因此它的成本应该远低于它所转发的那些模型;而对于未知类别,则应像这里的默认处理方式那样给出明确的错误提示。
差距在于知识而非智能
另一种常见的反应是:“模型不了解我们的内部文档,那就换一个更大的模型吧。”但模型规模并不能弥补知识缺失的问题。当信息属于专有内容、更新频繁或具有高度领域特殊性时,问题在于知识的获取而非推理能力。这正是检索增强生成(RAG)技术要解决的问题。其基本流程如下:
User Question
|
v
Embedding / Retrieval
|
v
Relevant Documents
|
v
Prompt + Retrieved Context
|
v
Language Model
|
v
Answer
如果用户询问贵公司针对企业客户的内部退款政策,无论多么强大的通用模型都无法给出答案。您必须在提示中提供相关内容。如需深入了解这一检索流程的运作方式,请参阅我们的指南:了解RAG系统如何按需检索最新知识。
在模型之前优化信息处理流程
这引出了一个值得作为准则的原则:
在升级模型之前,先改进输入模型的数据。
很多时候,团队试图通过更换更强大的模型来解决糟糕的回答问题,而实际原因却在其他方面:
- 检索能力不足
- 文档相关性低
- 缺少元数据
- 分块处理不当
- 上下文信息不足
在这些情况下,瓶颈并非模型本身,而是信息处理流程。
上下文质量优于数量
较大的上下文窗口固然令人印象深刻,但更多的上下文并不一定意味着更好的效果。如果只有两段文字就包含了答案,却仍给模型提供200页的文档,从技术上讲虽然提供了信息,但实际上却增加了任务的难度,因为模型现在必须在海量信息中筛选出有用内容。过大的上下文往往会带来:
- Token消耗增加
- 延迟上升
- 成本提高
- 注意力分散
- 信息冲突的风险增加
更好的目标是以最少的优质上下文让模型仍能给出正确答案。正因如此,成熟的RAG系统会在检索层上投入大量精力,采用诸如以下技术:
- 语义搜索与混合搜索
- 基于元数据的过滤
- 重写用户查询语句
- 重新排序候选段落
- 保持文档的新鲜度
- 生成结构良好的内容块
幻觉现象需要验证,而非更大的模型
一个令人不安的事实是:使用足够强大的模型并不能消除幻觉现象。更强的模型确实能在更多任务中更准确地获取事实并更好地进行推理,但语言模型终究只是文本生成工具,而非像数据库那样可以查询的真实信息来源。因此,解决方案应体现在设计层面:在处理流程中加入明确的验证步骤,例如:
User Request
↓
Retrieve Evidence
↓
Generate Answer
↓
Validate Claims
↓
Return Response
对于高价值的工作流程,还可以通过以下方式进一步加强验证:
- 提供指向相关证据的引用
- 根据结构化模式检查输出内容
- 依据业务规则进行验证
让模型负责推理,让软件执行规则
这在架构中划定了重要的界限。如果要求模型计算发票总额,当应用程序能够精确完成该运算时,就没有理由信任模型的计算结果。应当划分各自的职责:
Model:
Extract line items
Application:
Calculate subtotal
Application:
Calculate tax
Application:
Calculate total
Model:
Explain the result
模型负责提取各项明细并解释结果;应用程序则计算小计、税额和总计。每个部分都做自己最擅长的事,这样用户看到的数字从结构上就一定是正确的。
小型模型的优势与局限
小型语言模型常被认为“智能程度较低”,在许多场景下这一说法确实成立。但工程学并非只关乎智力因素,小型模型具有诸多实际优势:
- 推理成本更低
- 延迟更小
- 本地部署更为简单
- 对基础设施的要求更低
- 吞吐量可能更高
- 扩展更便捷
- 非常适合处理特定任务
- 在边缘场景中十分实用
- 在本地运行时隐私保护可能更好
它们在分类、提取、总结、路由、自动补全、简单转换以及特定领域的工作流程中尤其具有优势。如果您想更深入地了解这一趋势,我们关于小型专用模型如何超越大型LLM的综述会提供更详细的介绍。
不过,体积小并不代表就一定更好。有些任务超出了小型模型的可靠处理能力:需要综合多来源信息进行复杂推理、分析复杂代码、解决棘手的规划问题或进行细致的解读。在这些情况下,性能更强的模型才值得其高昂的成本。两种极端做法都是不可取的。“总是使用最大的模型”会浪费资金,而“总是使用最小的模型”则会导致质量低下。更好的原则是:
使用能够满足应用质量要求的最低规格模型即可。
需要使用大型模型的工作负载
以上内容并非反对使用大型模型。它们确实非常有用,而且某些工作负载显然能从更强的处理能力中获益。
复杂的多步骤推理
当某个功能需要依赖连续的推理过程时,更强大的模型能够带来显著更好的结果。
硬编码相关任务
在面对复杂需求、陌生的代码库、架构决策以及艰难的调试过程时,大型模型才能发挥其价值。
含义模糊的自然语言
有些请求并不符合预定义的类别,而更强大的模型通常更能理解其中的细微差别和真实意图。
多文档的综合处理
当答案需要整合多个来源的信息时,模型能力就显得更为重要。
智能体工作流
一个智能体通常需要:
- 理解目标
- 规划行动步骤
- 选择工具
- 检查结果
- 从失败中恢复
- 调整计划
- 完成任务
这比分类任务困难得多,因此使用更强大的模型是完全合理的。所有情况下的测试标准都是一样的:只有在大型模型的额外能力能带来可量化的价值时,才应使用它们。
精巧架构的运营成本
有一个基准测试从未体现过的成本:架构复杂性。最简单的设计就是一个应用、一个大型模型、一条响应:
Application
↓
Large Model
↓
Response
现在想象一下要同时优化所有这些要素的情况:
Application
↓
Router
↓
Classifier
↓
Small Model
↓
Confidence Evaluator
↓
RAG
↓
Reranker
↓
Large Model
↓
Validator
↓
Fallback Model
↓
Human Review
这种架构或许能打造出更出色的系统,但也会引入更多组件,而每个组件都会带来:
- 更多的监控与日志记录
- 新的故障模式
- 更庞大的测试范围
- 需要维护的额外基础设施
- 更复杂的部署流程
- 团队为运行该系统所需掌握的更多知识
因此,优化工作必须经过深思熟虑。仅仅为了每次请求节省几美分而构建七层架构往往并非明智之举;只有当相关数据表明其维护成本值得时,才应添加新层。
根据自身工作负载进行评估,而非参考排行榜
公开基准数据仅可作为起点,而非决策依据。您的应用拥有属于自己的基准标准,也只有这个标准才具有实际意义。对于人工智能代码审查助手而言,评估集可能包括:
- SQL注入漏洞
- 竞态条件
- 空指针错误
- 授权缺陷
- 异常处理不当
- 性能问题
- 架构违规
对于客户支持助理,需要评估的不同方面包括:
- 政策合规性
- 信息准确性
- 沟通语气
- 问题升级的准确度
- 拒绝处理的方式
- 结构化输出的有效性
评估数据集应尽可能贴近实际生产环境中的请求情况。
构建对比测试工具
基本流程很简单:收集具有预期结果的类似生产环境的测试用例,让每个候选模型进行处理,然后进行比较。
Production-like prompts
↓
Expected outcomes
↓
Run Model A
↓
Run Model B
↓
Compare
↓
Measure
每次运行时可记录的有用指标包括:
- 正确性及任务完成度
有了这些标准,选择模型就变成了基于工程的决策而非凭猜测行事,而且每当供应商推出新版本模型时,都可以重新运行相同的测试流程。
四步模型选择流程
对于新的人工智能功能,一个简单且可重复的流程效果很好。
步骤1:明确任务要求
不要从“我们应该使用哪个模型?”开始,而应从“模型究竟需要完成什么任务?”入手,并用具体表述把答案写下来:
Input:
Customer email
Output:
Intent + urgency + recommended workflow
像这样有明确输入和输出要求的规范,远比模糊的目标有用。
步骤2:定义“足够好”的标准
在测试任何内容之前,先明确设定验收标准。例如:
Intent accuracy >= target threshold
Structured output must always validate
Response should normally arrive within target latency
具体的阈值因应用而异;关键在于在比较模型之前这些标准就必须存在,这样之后才不会为结果找借口。
步骤3:先尝试最小的可行模型
这是团队最常跳过的步骤。从小规模开始,进行测试,如果通过则停止;只有失败时才继续提升:
Small Model
↓
Evaluation
↓
Pass? ── Yes → Ship
|
No
↓
Larger Model
↓
Evaluation
↓
Pass? ── Yes → Ship
|
No
↓
Stronger architecture/model
实际上你是在一步步提升能力,而每上升一级都需要通过失败的评估来获得。
步骤4:优化周边系统
在继续提升之前,先检查系统的其他部分:
- 检索结果是否正确?
- 提示语是否清晰明确?
- 所有上下文信息是否都真正相关?
- 输出内容在使用前是否经过验证?
通过这些改进,有时甚至可以完全不需要更大的模型。
升级前值得考虑的其他方法
将提示语视为契约
提示语设计并非魔法,但若任务描述不清,即便是性能出色的模型也可能会表现不佳。对比一下模糊的指令:
Analyze this customer message.
与那些明确了预期输出及处理不确定性的规则的指令:
Analyze the customer message.
Return JSON with:
- intent
- urgency
- sentiment
- recommended_action
Do not invent information that isn't present.
If the intent is unclear, return "unknown".
第二种版本为模型明确了规范:命名字段、明确禁止编造事实,以及设定默认值。加入一些示例通常能进一步提升效果。不过这也存在上限——再多的提示词也无法赋予模型其根本不具备的能力,而当某种能力缺失时,调整提示词的效果也会逐渐减弱。应将提示词运用视为优化流程中的一环,而非全部解决方案。
微调、RAG还是验证?
面对较差的结果,另一种常见的反应是“那就进行微调吧”。有时这确实是个好办法,但首先需要诊断问题:
- 如果模型缺乏最新或特定于公司的知识,比如当天的政策,那么检索通常比微调更合适,因为知识会不断变化,而重新训练速度很慢。
根据实际问题选择合适的解决方案既能节省时间又能降低成本。
缓存:简单却有效的优化方法
缓存虽然不那么引人注目,但效果非常好。如果用户不断询问“你们的退换货政策是什么?”,就没有理由每次都调用模型。当答案稳定时,就将其缓存起来。下面的 C# 示例会先检查缓存,只有在未找到结果时才调用模型,并将结果保存30分钟:
public async Task<string> GetAnswerAsync(string question)
{
var key = CreateCacheKey(question);
var cached = await cache.GetStringAsync(key);
if (cached is not null)
return cached;
var answer = await model.GenerateAsync(question);
await cache.SetStringAsync(
key,
answer,
TimeSpan.FromMinutes(30));
return answer;
}
真正的语义缓存比简单的字符串匹配更为复杂,因为表述不同的问题可能得到相同的答案,而且当底层事实发生变化时还需要有使答案失效的机制。其核心理念依然适用:
最快的AI请求其实是那些从未被发起的请求。
这一原则同样适用于去重、预计算、确定性响应、检索结果的重用、在提供方支持时的提示前缀缓存,以及普通响应的重用。在投入更多计算资源之前,先消除那些不必要的计算。
将令牌视为资源预算
一旦将人工智能系统视为分布式系统,令牌就变成了另一种需要管理的资源。传统服务需要管理CPU、内存、网络和存储,而人工智能应用则还需考虑输入令牌、输出令牌、上下文大小以及推理时间,这使得提示词设计也成了影响性能的重要因素。
想象一下,每次请求都要传输完整的对话历史。提示词会不断变长,随后检索到的文档、工具输出、系统指令以及之前的智能体操作也会随之增加。一个简单的问题最终可能承载着庞大的数据量。更精简的设计会在检索前仅选择相关历史记录,从而构建出简洁的上下文:
Conversation
↓
Relevant history selection
↓
Retrieval
↓
Compact context
↓
Model
而不是转发系统曾经处理过的所有内容:
Everything we've ever seen
↓
Model
更多的上下文从来都不是免费的:你需要为它付出金钱、延迟成本,往往还会影响答案的质量。
智能体让每一个决策都产生多重影响
智能体系统使得模型选择变得更加重要。一个用户请求可能会衍生出许多步骤:
User Request
↓
Planning
↓
Tool Selection
↓
Search
↓
Database Query
↓
Code Execution
↓
Analysis
↓
Final Response
如果每个步骤都使用最昂贵的模型,成本将会急剧上升。然而这些步骤往往并不需要相同的功能。混合分配可能如下所示:
Intent classification → Small model
Simple tool selection → Small model
Complex planning → Large model
Data extraction → Small model
Final explanation → Medium model
这是一种异构的AI架构,也是许多实际系统正在采用的方案:不是由一个巨型模型处理所有任务,而是多个模型、工具、确定性组件、检索流程和验证器协同工作。我们在探讨智能体AI成本为何会飙升一文中更详细地分析了其中的成本问题。
把模型视为团队成员
一个有助于理解的类比:想象有三名工程师。一名经验极为丰富,但成本高昂且工作负担过重;一名经验丰富且效率很高;还有一名资历较浅,但在重复性工作中速度极快。你不会把所有任务都交给经验最丰富的工程师,而是会根据任务的难度来分配工作:
Simple repetitive task
→ Engineer C
Normal feature
→ Engineer B
Complex architecture problem
→ Engineer A
模型也可以用同样的方式来处理。较小的模型并非“劣质”的,它可能只是更适合处理范围更窄的任务。关键技能在于将工作拆分成多个部分。与其寻找一个能处理所有事情的模型,不如将工作流程分解,让每个组件负责其最擅长的部分。这样的思维方式更具扩展性。
整合应用:参考架构
结合这些理念,企业级AI助手可以按如下结构构建:
User
|
v
API / Gateway
|
v
Request Router
|
+-------------+-------------+
| |
v v
Simple Request Complex Request
| |
v v
Small Model Planner
|
+------------+------------+
| | |
v v v
Search Database Tools
| | |
+------------+-------------+
|
v
Context
|
v
Strong Model
|
v
Validator
|
+------+------+
| |
Valid Invalid
| |
v v
Response Retry/Fallback
注意那些没有发生的情况:最强大的模型并没有被要求处理所有任务。简单的请求会交给小型模型处理,而复杂的请求则会先经过一个规划器,该规划器从搜索结果、数据库和工具中收集相关证据,之后才由强大的模型在经过筛选的上下文中进行处理。验证器会检查输出结果,如果发现无效结果,则会触发重试或备用方案。该设计依赖于:
- 用于选择处理路径的路由器
- 用于获取证据的相关功能与工具
- 能够实现精确处理的确定性系统
- 针对不同角色定制的模型
- 验证机制以及重试和备用策略
在这里,模型只是系统的一部分,而非整个系统。
屡见不鲜的十个错误
- 先选定模型,后再描述问题。正确的顺序应该是先明确工作负载,然后再决定使用何种模型。
选择模型的决策清单
在需要决定使用哪种模型时,请按顺序思考以下问题:
- 能否用确定性代码解决该问题?如果可以,就编写代码。不要仅仅因为人工智能工具可用就使用它。
- 任务是否简单且重复性高?可以尝试使用小型模型。
这份检查清单比单纯询问哪种模型最强大要有用得多。
值得向资深AI工程师提出的问题
在面试AI架构岗位时,一个能揭示深层思考的问题是:“为什么不能直接为所有任务选择市场上功能最强大的模型呢?”
一个薄弱的回答只会停留在“模型越小成本越低”这一层面。这虽然正确,但并不全面。一个有力的回答则会考虑到特定任务的处理能力、可靠性、延迟与吞吐量、成本、所需上下文量、模型间的路由方式、数据检索功能、代码应完成的任务、评估标准、故障带来的损失以及运营负担等因素。
接下来的自然问题是:“如何证明你选择的模型足够好?”答案应基于评估结果,而非个人观点、社交媒体帖子、排行榜截图或供应商的演示文稿。真正决定性的因素是你的工作负载、数据以及各项指标。
逐步优化现有应用
当某个人工智能功能运行速度过慢或成本过高时,不要急于立即更换模型。应采取系统化的方法进行调查:
- 进行测量。收集请求数量、输入与输出token数、延迟时间、错误率、任务成功率以及模型成本。
- 找出高资源消耗的任务类型。识别出哪些请求类型会占用最多资源。
- 消除不必要的调用。利用缓存、确定性逻辑、去重机制以及预计算技术。
- 缩小上下文范围。删除无关的历史记录和文档。
- 优化信息检索效果。提供更优质的证据而非单纯增加证据数量。
- 尝试使用更小的模型。检查其质量是否仍能满足要求。
- 引入路由机制。仅将复杂案例发送给性能更强的模型处理。
- 验证关键要素。在适当情况下运用架构规范、规则、工具以及人工审核。
如果这看起来很熟悉,那是因为它本质上与优化传统软件所采用的准则相同。
最佳的人工智能架构通常是混合型的
将人工智能应用视为前端调用大语言模型API并返回响应的这种观念正在逐渐过时:
Frontend
↓
LLM API
↓
Response
实际应用越来越类似于一个协调规则、数据检索、模型、工具及验证功能的网关:
Application
|
v
AI Gateway
|
+-----------+-----------+
| | |
v v v
Rules Retrieval Models
| | |
+-----------+-----------+
|
v
Tools
|
v
Validation
|
v
Application
这样的设计将传统软件工程与机器学习、语言模型及数据检索技术相结合,同时还融入了数据库、API、安全机制、可观测性以及以代码形式编写的业务逻辑。这对软件工程师来说是个好消息:人工智能工程并非要取代传统软件工程,而是为其增添了一个强大的新组件。
另一种有助于改变视角的方法是:不要再去问哪个模型最聪明,而应思考哪个系统最聪明。在设计糟糕的系统中,再出色的模型也可能产生糟糕的结果;而在设计精良的架构中,能力一般的模型就能打造出优秀的产品。优秀的系统能够通过检索与工具、路由与缓存、结构化输出与验证、专为特定任务编写的代码以及持续评估等方式,弥补模型的不足。从这个意义上说,人工智能的能力属于架构的属性,而不仅仅是模型本身的属性。
从小规模开始,并以证据为依据逐步扩展
对于任何新的人工智能功能,一个合理的默认策略可遵循以下流程:
Start
|
v
Define the workload
|
v
Can code solve the problem?
/ \
Yes No
| |
Use code v
Try small model
|
v
Evaluate
|
+----------+----------+
| |
Pass Fail
| |
Ship v
Improve architecture
|
v
Evaluate
|
v
Try stronger model
|
v
Evaluate
这能避免一个非常常见的错误:把钱花在本可通过更好的工程设计解决的问题上。
有时,最合适的模型反而是没有模型
关键并非小型模型就更好;这种观点与相反的观点一样错误。合适的模型应是能在满足任务需求的同时,兼顾性能与可靠性,并权衡延迟、成本及复杂度的那个模型。有时是小型模型,有时是大型模型,有时是两者的结合,有时则根本不是人工智能模型。如果某种请求类型可以通过像这样的简单分支来处理,那就没有理由调用大语言模型:
if (request.Type == "PasswordReset")
{
return StartPasswordResetWorkflow();
}
这并非反对人工智能,而是良好的工程实践。对于只需要两核的处理任务,你不会购买配备无限CPU的服务器;对于仅需4GB内存的进程,你也不会配置1TB的内存。同样的道理也适用于模型:不必要的功能意味着不必要的成本。
核心要点
- 将“我们能负担得起的最大模型是什么?”替换为“能够可靠解决此问题的最简单架构是什么?”,这样的问题会自然而然地引导你思考路由、检索、缓存、确定性代码、评估、延迟以及故障处理等问题。
- 应依据每个成功任务的成本,并结合错误答案可能带来的后果来评判模型,而非仅看每次调用的价格或参数数量。
- 将缺失的知识视为检索问题,将算术运算或规则视为软件问题;更大的模型并不能解决这些问题。
- 优先选择能够通过基于实际生产环境数据构建的评估标准的最小模型,只有在有充分证据时才考虑升级模型。
- 在成本允许的范围内保持架构的简洁性,并在模型上线后持续进行性能监测,因为模型、提示词以及用户都会发生变化。
首先围绕问题进行设计,然后再选择适合该设计的模型。有时这个模型会非常庞大,有时却出奇地小巧,而有时候最明智的决定根本就不调用模型。
相关阅读
- 检索增强生成详解:弥补大语言模型的知识缺陷 — 了解大语言模型为何会产生幻觉并过时,进而逐步学习RAG如何通过检索、分块、嵌入及增强提示词来解决问题。
- RAG与智能代理式RAG及图结构RAG:如何选择合适的检索架构 — 了解传统RAG在多跳查询和结构化数据处理上的不足,以及智能代理循环和基于图的检索方式如何分别解决这些缺陷。