1. 大模型三层架构到底在拆什么
1.1 从一个常见困惑说起
这两年做大模型应用开发的人,几乎都会经历一个相似的阶段:每隔几周就冒出一个新概念,今天是某个新的提示词框架,明天是某个新的智能体编排方案,后天又出来一个检索增强的新玩法。追着追着就发现,自己像在追一条永远追不上的尾巴,学了一堆名词,但真正落到项目里,还是不知道该怎么选、怎么搭。
我自己也踩过这个坑。最开始做对话类应用的时候,看到别人用什么多轮反思、什么思维链、什么工具调用,就一股脑往项目里塞,结果系统变得又慢又贵,效果还不稳定。后来复盘才发现,问题不在于这些技术本身不好,而在于我没有搞清楚它们各自处在整个系统的哪一层,解决的是哪一类问题。
把大模型应用拆开来看,不管外面包装了多少新名词,本质上都逃不出三层结构:输入层、模型层、输出层。而真正决定一个应用好不好用的,往往不是中间那个模型本身,而是输入什么和输出怎么处理这两头。模型层反而是相对标准化的一层,你换一个模型,接口大同小异,但输入怎么组织、输出怎么加工,才是各家产品拉开差距的地方。
这篇文章就是想把这三层彻底拆开讲清楚。我会说明每一层到底在做什么、为什么这么分层、每一层里那些花里胡哨的技术到底在解决什么问题,以及在实际项目里应该怎么选、怎么搭。适合正在做大模型应用、被各种新概念绕晕、想找一个稳定框架来理解这一切的开发者。看完之后,你再看任何一个新的AI玩法,都能快速判断它属于哪一层、值不值得用。
1.2 三层架构的划分逻辑
先说清楚这三层是怎么划的,以及为什么这么划。
输入层,指的是从用户发出请求,到真正把内容送进模型之前的所有处理。这里面包括提示词的组装、上下文的拼接、历史对话的管理、外部知识的检索、工具的描述注入等等。一句话概括:输入层决定了模型"看到"什么。
模型层,指的是模型本身完成推理计算的过程。这一层包括模型的选择、参数的配置、推理的调度、多模型的协同等等。它决定了模型"怎么想"。
输出层,指的是模型吐出结果之后,到最终呈现给用户之前的所有处理。这里面包括结果的解析、格式的校验、内容的过滤、多轮结果的聚合、失败重试等等。它决定了模型"说的话"怎么变成"能用的东西"。
为什么这么分?因为这三层的变化频率和可控程度完全不同。模型层是最不稳定的,模型版本迭代快、能力边界模糊、输出有随机性,你很难完全控制它。而输入层和输出层是你可以完全掌控的,你可以精确地决定给模型看什么、怎么让它看、拿到结果后怎么加工。所以一个成熟的系统,一定是把尽可能多的确定性放在输入层和输出层,把模型层当成一个"能力提供方"来用,而不是把全部逻辑都押在模型身上。
这个思路听起来简单,但真正落地的时候,很多人会不自觉地违反它。比如把复杂的业务规则写进提示词里让模型去判断,这就是把本该在输出层做的校验推给了模型层,结果就是不稳定。理解了分层逻辑,你就能避免这类错误。
2. 输入层:决定模型看到什么
2.1 提示词组装的核心思路
输入层最基础的工作就是组装提示词。很多人觉得提示词就是把话写清楚,其实远不止于此。提示词组装本质上是一个信息压缩和结构化的过程,你要在有限的上下文窗口里,塞进最能让模型做出正确判断的信息。
我一般把提示词分成几个固定模块来组织:角色设定、任务描述、约束条件、输入数据、输出格式要求。这几个模块的顺序和边界要清晰,因为模型对位置是敏感的,放在开头和放在结尾的信息,被重视的程度不一样。
角色设定放在最前面,是为了给模型一个稳定的行为基调。但这里有个常见的误区,就是角色写得越详细越好。实际上,角色描述太长反而会稀释任务本身的权重。我的经验是,角色设定控制在两三句话,说清楚"你是谁、你擅长什么、你的风格是什么"就够了。
任务描述是核心,要具体到可执行。比如"帮我分析这段文本"就是模糊的,"从情感倾向、关键实体、潜在风险三个维度分析这段文本,每个维度给出判断依据"就是可执行的。任务描述越具体,模型的输出越稳定。
约束条件容易被忽略,但它恰恰是提升稳定性的关键。比如"如果信息不足,明确说明缺少什么,不要编造",这一句就能大幅减少幻觉。约束条件要针对你实际遇到过的问题来写,而不是抄一堆通用的。
输入数据要明确标注边界。我习惯用清晰的分隔符把输入数据包起来,比如用三个反引号或者特定的标记,这样模型能准确区分"指令"和"数据",避免把数据里的内容当成指令来执行。
输出格式要求放在最后,因为模型对结尾的信息记忆比较深。如果你需要结构化输出,这里要写清楚字段名、类型、示例。
2.2 上下文管理与历史对话处理
多轮对话是输入层里最容易被低估的部分。很多人以为多轮对话就是把历史消息全部拼进去,其实这里面有很多讲究。
首先是上下文窗口的预算分配。模型的上下文窗口是有限的,历史对话、检索到的知识、当前问题、系统提示,都要抢这块空间。如果不做管理,很容易出现历史对话把知识挤没了,或者知识把当前问题挤没了的情况。我的做法是给每一类内容设定一个预算上限,比如系统提示占10%,历史对话占30%,检索知识占40%,当前问题占20%,超出就按优先级裁剪。
其次是历史对话的压缩。当对话轮次多了,全部保留既浪费空间又引入噪声。常见的做法有两种:一种是滑动窗口,只保留最近N轮;另一种是摘要压缩,把早期对话总结成一段摘要。滑动窗口简单但会丢失早期信息,摘要压缩保留信息但会引入摘要本身的误差。我一般结合使用:最近几轮保留原文,更早的做摘要。
还有一个细节是历史对话的清洗。用户的历史消息里可能包含无关内容、错误信息、甚至恶意注入。直接拼进去会污染模型的判断。我习惯在拼接前做一轮过滤,把明显无关的内容去掉,把可能是指令注入的内容做转义处理。
提示:上下文管理没有万能方案,要根据你的具体场景来调。对话类应用重历史,问答类应用重知识,任务类应用重当前指令。先想清楚你的场景里哪类信息最重要,再决定预算怎么分。
2.3 外部知识检索的接入方式
检索增强是输入层里最热门的技术,但很多人用不好,问题往往出在检索和组装的衔接上。
检索本身是另一套系统的事,这里只说检索结果怎么接入输入层。核心原则是:检索到的内容不能直接堆进去,要做筛选、排序、压缩。
筛选是指去掉不相关的结果。检索系统返回的Top-K结果里,往往有一部分是噪声。我一般会加一个相关性打分的环节,低于阈值的直接丢掉。宁可少给几条,也不要给一堆无关的干扰模型。
排序是指把最相关的结果放在最显眼的位置。模型对上下文的不同位置敏感度不同,把最重要的信息放在开头或结尾,效果通常更好。
压缩是指把长文档精简成关键片段。检索到的文档可能很长,全部塞进去会挤占其他信息的空间。我习惯把每篇文档压缩成几句话的核心摘要,或者只保留和当前问题最相关的段落。
还有一个容易被忽略的点是引用标注。在拼接检索内容时,给每条内容标上来源编号,然后在输出格式要求里让模型引用编号。这样一方面方便追溯,另一方面能约束模型不要编造,因为它知道自己的每句话都要对应到具体来源。
2.4 工具描述与函数调用的注入
当应用需要模型调用外部工具时,工具的描述也是输入层的一部分。这部分做得好不好,直接决定了模型能不能正确选择和使用工具。
工具描述的核心是让模型知道什么时候该用这个工具。很多人只写了工具的功能,没写使用场景,结果模型要么不用,要么乱用。我的做法是每个工具描述里都包含三部分:这个工具做什么、什么情况下用、输入参数是什么格式。
参数描述要具体。比如一个查询天气的工具,参数是城市名,那就要说明"城市名用中文,不要带'市'字后缀"这类细节。模型对参数的格式很敏感,描述不清楚就容易传错。
工具数量也要控制。工具太多会让模型选择困难,而且每个工具的描述都占上下文。我一般把工具控制在十个以内,超过就考虑分组或者用路由先做一层筛选。
注意:工具描述里的示例很重要。给一两个正确的调用示例,能大幅提升模型调用的准确率。示例要覆盖典型的参数组合,不要只给最简单的。
3. 模型层:能力提供方的正确用法
3.1 模型选型的考量维度
模型层虽然相对标准化,但选型仍然是个技术活。选型不是选"最强"的,而是选"最合适"的。我一般从几个维度来考量。
能力匹配度是第一位的。不同模型在不同任务上的表现差异很大,有的擅长推理,有的擅长创作,有的擅长代码。选型前要先明确你的核心任务是什么,然后找在这个任务上表现好的模型。不要迷信榜单,榜单上的综合分高不代表在你的具体任务上就好。
成本是绕不开的。模型调用是按量计费的,输入和输出的价格还不一样。如果你的应用调用量大,成本会迅速累积。我一般会算一笔账:单次调用的平均token消耗乘以日均调用量,再乘以单价,看看月度成本是否在预算内。如果超了,就要考虑用更便宜的模型,或者优化输入输出来降低token消耗。
延迟直接影响用户体验。有些模型能力强但响应慢,适合离线处理;有些模型响应快但能力一般,适合实时交互。要根据你的场景来权衡。我做过一个实时对话的应用,最开始用了能力最强的模型,结果用户等得不耐烦,后来换成响应更快的模型,体验反而更好。
稳定性是长期运行的关键。有些模型高峰期会限流,有些模型偶尔会返回异常。选型时要考虑有没有备用方案,能不能快速切换。
下面这张表是我常用的选型对照,供参考:
| 考量维度 | 关键问题 | 常见取舍 |
|---|---|---|
| 能力匹配 | 核心任务是什么 | 专用模型 vs 通用模型 |
| 成本 | 月度预算多少 | 强模型少调用 vs 弱模型多调用 |
| 延迟 | 能接受多长响应 | 实时交互 vs 离线批处理 |
| 稳定性 | 能否容忍异常 | 单模型 vs 多模型备份 |
3.2 参数配置的实际影响
模型层有一堆参数可以调,但真正影响大的就那么几个。我把常用的几个说一下实际影响。
温度控制输出的随机性。温度低,输出稳定但可能呆板;温度高,输出多样但可能跑偏。做事实性问答,温度要低;做创意生成,温度可以高。我一般从0.3开始试,根据效果上下调。
最大输出长度要设合理。设太短,输出被截断;设太长,浪费成本还可能让模型啰嗦。我一般根据任务预估一个合理值,比如摘要任务设500,创作任务设2000。
top_p和温度类似,控制采样的范围。一般调一个就行,不用两个都调。我习惯固定top_p,只调温度。
惩罚项包括频率惩罚和存在惩罚,用来抑制重复。如果发现模型老是重复同样的话,可以适当加一点。但加太多会让输出变得不自然。
这些参数没有标准答案,要在你的具体任务上试。我的建议是每次只调一个参数,观察效果变化,找到适合你任务的组合后固定下来。
3.3 多模型协同的常见模式
单一模型往往满足不了复杂需求,多模型协同就成了常见方案。我总结了几种模式。
级联模式是用小模型先处理,处理不了的再交给大模型。比如先用便宜的小模型做意图识别,识别出复杂意图再调用大模型。这样能大幅降低成本。
投票模式是让多个模型对同一问题给出答案,然后取多数或综合。适合对准确性要求高的场景,但成本翻倍。
分工模式是不同模型负责不同环节。比如一个模型负责理解,一个模型负责生成,一个模型负责校验。这种模式灵活但编排复杂。
兜底模式是主模型失败时切换到备用模型。适合对可用性要求高的场景。
选哪种模式,取决于你的核心诉求是成本、准确性还是可用性。我一般从级联模式开始,因为它性价比最高。
3.4 推理调度的稳定性设计
模型调用不是百分之百成功的,超时、限流、返回异常都可能发生。稳定性设计就是让系统在这些情况下还能正常工作。
重试是最基本的。调用失败后等一小段时间再试,通常能成功。但重试要有上限,不能无限重试。我一般设两到三次,间隔递增。
超时要设合理。设太短,正常的长响应会被误杀;设太长,用户等太久。我一般根据任务复杂度设,简单任务10秒,复杂任务30秒。
降级是重试都失败后的方案。可以返回一个预设的兜底回复,或者切换到备用模型。降级方案要提前准备好,不能等出问题了才想。
熔断是防止雪崩的。当某个模型连续失败多次,就暂时不再调用它,过一段时间再试。这样能避免把资源浪费在已经不可用的服务上。
提示:稳定性设计要在开发阶段就做,不要等上线了出问题再补。我见过太多项目,功能做得花哨,一上量就崩,就是因为没做这些基础设计。
4. 输出层:把模型的话变成能用的东西
4.1 结构化输出的解析与校验
模型输出的第一道处理就是解析。如果你要求了结构化输出,就要有对应的解析逻辑。
JSON解析是最常见的。但模型输出的JSON经常有问题,比如多了markdown标记、少了引号、多了注释。解析前要先做清洗,把代码块标记去掉,把明显的格式问题修掉。解析失败要有兜底,不能让整个流程崩掉。
字段校验是解析后的必要步骤。检查必填字段有没有、类型对不对、值在不在合理范围。校验不通过的要标记出来,走重试或人工处理。
容错解析是应对模型不听话的。有时候模型就是不给标准格式,这时候可以用正则提取关键信息,或者用另一个模型来解析。我一般先试标准解析,失败了再走容错。
4.2 内容过滤与安全校验
输出层是安全的最后一道关。模型可能生成不合适的内容,必须在呈现给用户前过滤掉。
敏感词过滤是最基础的。维护一个敏感词库,输出前扫一遍,命中的做替换或拦截。词库要定期更新,覆盖新出现的风险词。
内容分类是更高级的。用分类模型判断输出是否属于某个风险类别,比如暴力、歧视、误导。分类模型可以比敏感词更准确地识别变体。
一致性校验是防止自相矛盾的。有时候模型前后说的话不一致,可以通过逻辑检查发现。比如前面说A是B,后面说A不是B,就要标记出来。
过滤策略要平衡安全和体验。过滤太严,正常内容被误伤;过滤太松,风险内容漏过。我一般分级处理:高风险的直接拦截,中风险的加提示,低风险的放行但记录。
4.3 多轮结果的聚合与去重
当一次请求触发了多次模型调用,输出层要把这些结果聚合起来。
聚合是把多个结果合并成一个。比如让模型从不同角度分析同一问题,输出层要把这些分析整合成一份完整报告。聚合可以用规则,也可以用模型来做。
去重是去掉重复内容。多次调用可能产生相似的结果,直接拼接会很冗余。我一般用相似度计算,超过阈值的只保留一条。
排序是决定呈现顺序。重要的放前面,次要的放后面。排序可以按相关性、按时间、按重要性。
4.4 失败重试与降级策略
输出层的失败处理同样重要。解析失败、校验不通过、过滤拦截,都要有对应的处理。
重试是首选。解析失败可以重新调用模型,校验不通过可以把问题反馈给模型让它重做。重试要带上失败原因,让模型知道哪里错了。
降级是重试失败后的方案。可以返回部分结果,可以返回预设回复,可以转人工。降级方案要让用户知道发生了什么,不要静默失败。
记录是改进的依据。每次失败都要记录原因,定期分析,找出高频问题,从输入层或模型层去优化。
5. 三层协同的实战要点
5.1 一个完整请求的生命周期
把三层串起来看,一个请求的完整流程是这样的:用户输入进来,输入层做预处理,组装提示词,拼接上下文,检索知识,注入工具描述,然后调用模型层。模型层完成推理,返回结果。输出层解析结果,校验格式,过滤内容,聚合多轮结果,最后呈现给用户。
这个流程里,每一层都可能出问题,每一层都要有监控和日志。我习惯在每层的入口和出口都打点,记录耗时、成功率、异常类型。这样出问题的时候能快速定位是哪一层的锅。
5.2 各层的优化优先级
资源有限的时候,优化要有优先级。我的经验是:先优化输入层,再优化输出层,最后才考虑模型层。
为什么?因为输入层的优化成本最低、见效最快。把提示词写好、把上下文管好、把知识检索做好,往往能解决大部分效果问题。输出层的优化次之,主要是提升稳定性和安全性。模型层的优化成本最高,换模型、调参数、做协同,都要投入不少精力,而且效果不一定比前两层优化好。
我见过很多项目,一上来就纠结用哪个模型,其实他们的提示词写得一塌糊涂,换个再好的模型也救不回来。
5.3 常见误区与避坑清单
最后整理几个我踩过的坑,供参考。
误区一:把业务逻辑写进提示词。提示词适合做引导,不适合做精确判断。复杂的业务规则应该在代码里实现,不要指望模型去执行。
误区二:忽视上下文预算。上下文窗口是有限资源,不做管理就会互相挤占。要提前规划好各类内容的预算。
误区三:不做输出校验。模型输出不可信,必须校验。不校验的系统迟早出问题。
误区四:只调模型不调输入输出。效果不好就换模型,是治标不治本。先看看输入输出有没有优化空间。
误区五:不做监控和日志。出了问题不知道哪层的问题,只能瞎猜。监控和日志是排查的基础。
| 常见误区 | 后果 | 正确做法 |
|---|---|---|
| 业务逻辑写进提示词 | 不稳定、难维护 | 规则用代码实现 |
| 忽视上下文预算 | 信息互相挤占 | 提前规划预算 |
| 不做输出校验 | 异常输出直接呈现 | 加解析和校验 |
| 只调模型 | 治标不治本 | 先优化输入输出 |
| 不做监控 | 问题难定位 | 每层打点记录 |
这套三层架构的思路,我在多个项目里用过,最大的价值是提供了一个稳定的分析框架。不管外面出什么新概念,你都可以问自己:它属于哪一层?解决什么问题?值不值得引入?想清楚这几个问题,就不会被各种新花样带偏。