1. 为什么“三层架构”是理解大模型应用的万能钥匙
很多人第一次接触大模型应用开发时,会被各种新名词砸晕:RAG、Agent、Function Calling、思维链、多模态、工作流编排……每隔几周就冒出一个新概念,感觉永远追不完。但如果你把这些花样全部拆开看,会发现一个让人安心的事实:几乎所有大模型应用,本质上都在做三件事——决定输入什么、决定模型怎么处理、决定输出怎么用。这就是我所说的“三层架构”。
这个框架不是什么官方标准,而是我在实际做项目时总结出来的一套思维模型。它的价值在于:当你面对一个从没见过的新框架或新产品时,可以迅速把它归类到这三层中的某一层或某几层,然后判断它到底解决了什么问题、值不值得投入时间学。
三层分别是什么?第一层是输入层,解决的是“给模型看什么”的问题,包括提示词设计、上下文组装、检索增强、多模态输入等。第二层是处理层,解决的是“模型怎么想”的问题,包括推理策略、工具调用、多步规划、自我反思等。第三层是输出层,解决的是“拿到结果之后怎么办”的问题,包括格式解析、结果校验、后处理管线、与外部系统的对接等。
为什么说这个框架好用?因为大模型本身是一个黑盒,你能控制的只有它的输入和输出。处理层虽然涉及模型内部的推理过程,但你能施加的影响也主要是通过输入来间接引导。所以从工程角度看,真正可操作的空间集中在输入层和输出层,处理层更多是选择用什么模型、用什么推理策略的问题。
我见过不少团队在做一个大模型应用时,把80%的精力花在“换更好的模型”上,却忽略了输入组装和输出处理才是真正决定产品质量的地方。同一个模型,输入层做得好不好,输出质量可以差出好几倍。同样,输出层有没有做校验和兜底,直接决定了产品能不能上线。
接下来的内容,我会逐层拆解每一层到底包含哪些具体技术点、常见的坑在哪里、实际项目中应该怎么选。每一层我都会给出可操作的方案和参数建议,而不是泛泛而谈。
2. 输入层:决定模型上限的关键战场
2.1 提示词工程只是输入层最表面的一层
一说到输入层,大多数人第一反应就是“写提示词”。确实,提示词是输入层最直观的部分,但它远不是全部。提示词工程解决的是“怎么把任务说清楚”的问题,而输入层要解决的是“怎么把正确的信息以正确的形式送到模型面前”。
举个例子,你要做一个客服问答系统。提示词可以写成“你是一个客服助手,请根据以下知识回答用户问题”。但真正决定回答质量的,是你有没有把相关的知识片段检索出来、有没有把历史对话整理成合适的格式、有没有把用户当前问题和上下文正确拼接。这些都属于输入层的范畴。
提示词工程的核心技巧其实就那么几条:角色设定、任务描述、输出格式约束、少样本示例。这些技巧在大多数场景下都够用了。真正难的是上下文组装——在有限的上下文窗口里,放哪些信息、按什么顺序放、怎么压缩。
我做过一个对比测试:同一个模型、同一个问题,一组直接把所有相关文档塞进去,另一组做了精心的上下文筛选和排序。结果后者的回答准确率高出近40%。这说明输入层的优化空间远比换模型大。
2.2 上下文组装的三个核心决策
上下文组装是输入层最核心的工作。每次调用模型之前,你都需要决定三件事:放什么、放多少、怎么排。
放什么取决于你的任务类型。如果是问答任务,你需要放相关的知识片段和对话历史。如果是代码生成任务,你需要放相关的代码文件、接口定义和错误信息。如果是数据分析任务,你需要放数据摘要和字段说明。关键原则是:只放和当前任务直接相关的信息,无关信息不仅浪费上下文窗口,还会干扰模型的注意力。
放多少是一个需要实测的问题。上下文窗口虽然越来越大,但不是塞得越多越好。我实测下来的经验是:对于问答类任务,有效信息占比低于30%时,模型的表现会明显下降。也就是说,如果你塞了10000个token的上下文,其中真正有用的不到3000个token,那不如精简到3000个token再送进去。
怎么排经常被忽略,但影响很大。一个实用的原则是:最重要的信息放在最前面和最后面。模型对上下文开头和结尾的关注度高于中间部分,这是注意力机制的特性决定的。所以如果你有一段最关键的知识,不要放在中间,要么放最前面,要么放最后面。
下面是一个上下文组装的示例结构,我在多个项目中都用过这个模板:
def build_context(system_prompt, knowledge_chunks, chat_history, user_query): # 第一段:系统提示词,定义角色和任务 context = f"[系统指令]\n{system_prompt}\n\n" # 第二段:知识片段,按相关度排序,最相关的放最前 context += "[参考资料]\n" for i, chunk in enumerate(knowledge_chunks[:5]): context += f"资料{i+1}:{chunk}\n" context += "\n" # 第三段:对话历史,只保留最近几轮 context += "[对话历史]\n" for turn in chat_history[-3:]: context += f"用户:{turn['user']}\n助手:{turn['assistant']}\n" context += "\n" # 第四段:当前问题,放在最后 context += f"[当前问题]\n{user_query}" return context这个结构看起来简单,但每一段的顺序和截断策略都是经过实测调整的。比如知识片段限制在5条以内,对话历史只保留最近3轮,这些都是为了避免上下文过长导致模型注意力分散。
2.3 检索增强:输入层最值得投入的方向
检索增强生成(RAG)是输入层最重要的技术之一。它的核心思路是:在模型回答之前,先从外部知识库中检索出相关信息,作为上下文的一部分送给模型。这样模型就能基于最新、最准确的信息来回答,而不是只依赖训练时记住的内容。
RAG的流程可以拆成四个步骤:文档切分、向量化、检索、重排序。每一步都有讲究。
文档切分不是简单地按固定长度切。我试过按500字切、按段落切、按语义切,效果差异很大。对于技术文档,按段落切通常最好,因为每个段落本身就是一个完整的语义单元。对于对话记录,按轮次切更合适。切分粒度太细会导致检索到的片段缺乏上下文,太粗则会引入无关信息。我的经验值是:每个片段控制在200到500字之间,并且保留一定的重叠区域(比如前后各重叠50字),避免关键信息被切断。
向量化就是选择合适的嵌入模型把文本转成向量。这里有一个常见的误区:很多人直接用通用的嵌入模型,不做任何微调。对于垂直领域,通用模型的效果可能不够好。如果条件允许,用领域数据对嵌入模型做微调,检索准确率能提升不少。如果没有微调条件,至少要在检索时加入关键词匹配作为补充,纯向量检索在专有名词上容易翻车。
检索阶段的核心参数是返回结果数量。返回太少可能漏掉关键信息,返回太多会引入噪声。我的经验是:先返回10到20个候选,然后用重排序模型筛选出最相关的3到5个。这个两阶段策略比直接返回固定数量效果好很多。
重排序是很多人忽略的一步。向量检索是基于语义相似度的,但语义相似不等于真正相关。重排序模型可以更精细地判断查询和文档之间的相关性。我用过的方案里,交叉编码器(Cross-Encoder)做重排序的效果明显好于纯向量相似度。
2.4 多模态输入的处理要点
当输入不只是文本时,输入层的复杂度会上升一个量级。图片、音频、视频、表格、PDF,每种格式都有自己的处理方式。
图片输入的关键是分辨率和对齐。模型对图片的理解能力受分辨率影响很大,太小的图片看不清细节,太大的图片会被压缩导致信息丢失。我的经验是:对于文档截图类图片,分辨率保持在1024x1024左右比较合适;对于自然场景图片,可以适当降低到512x512。
PDF处理是另一个大坑。很多PDF是扫描件,需要先做OCR。但OCR出来的文本往往格式混乱,段落边界不清。我的做法是:先用版面分析工具识别出标题、正文、表格、图片区域,然后分别处理。表格单独提取成结构化数据,图片单独做描述生成,正文按段落整理。这样比直接OCR整页效果好得多。
音频输入需要先做语音转文字,然后按文本处理。但要注意:语音转文字的结果往往没有标点,需要做后处理加上标点,否则模型理解起来会很吃力。
3. 处理层:模型选择与推理策略的取舍
3.1 不是所有任务都需要最强的模型
处理层最核心的决策是:用什么模型。很多人下意识觉得“用最强的就对了”,但实际项目中,模型选择是一个需要权衡成本、延迟和效果的工程问题。
我把常见任务按复杂度分成三档。第一档是简单任务,比如文本分类、信息抽取、格式转换。这类任务用小模型就够了,速度快、成本低,效果和大模型差距不大。第二档是中等任务,比如问答、摘要、翻译。这类任务需要一定的理解能力,中等规模的模型通常够用。第三档是复杂任务,比如多步推理、代码生成、创意写作。这类任务才真正需要大模型。
我做过一个实测:在信息抽取任务上,小模型和大模型的效果差距不到5%,但成本差了将近20倍。所以先从小模型试起,效果不够再往上换,这是最务实的策略。
另一个重要决策是:用通用模型还是领域模型。通用模型在大多数场景下表现不错,但在垂直领域(比如医疗、法律、金融)可能不够专业。如果有条件,用领域数据做微调,效果提升很明显。但微调需要标注数据和算力,不是所有团队都有条件。折中方案是用通用模型加领域知识库做检索增强,效果也能接受。
3.2 工具调用:让模型突破自身限制
工具调用(Function Calling)是处理层最重要的能力之一。它的本质是:模型不直接回答问题,而是输出一个“调用某个工具的请求”,由外部系统执行后再把结果返回给模型。
这个机制解决了一个根本问题:模型的知识是静态的,但很多任务需要动态信息。比如查天气、查库存、算数学题,这些都需要实时数据或精确计算。工具调用让模型可以“借用”外部能力。
工具调用的设计有几个关键点。工具描述要清晰,模型需要知道每个工具是干什么的、需要什么参数。描述模糊会导致模型调用错误的工具或传错参数。参数校验要严格,模型输出的参数不一定符合预期,需要在执行前做校验和修正。错误处理要完善,工具调用可能失败,需要有重试和降级机制。
我踩过的一个坑是:工具数量太多导致模型选择困难。当可用工具超过10个时,模型选错工具的概率明显上升。解决方案是对工具做分组,先让模型选择工具类别,再在类别内选择具体工具。这个两阶段策略把工具选择准确率从70%提升到了90%以上。
3.3 多步推理:把复杂任务拆成可执行的步骤
对于复杂任务,让模型一步到位给出答案往往效果不好。更好的做法是把任务拆成多个步骤,每一步的输出作为下一步的输入。这就是多步推理的核心思路。
常见的多步推理模式有几种。链式推理是把任务拆成线性步骤,一步步执行。树式推理是在每一步生成多个候选,然后选择最优路径。反思式推理是让模型先给出答案,然后自我检查并修正。
我在实际项目中最常用的是链式推理加反思。具体做法是:先让模型生成一个初步方案,然后让模型自己检查这个方案有没有问题,最后根据检查结果生成最终方案。这个“生成-检查-修正”的循环,对于代码生成和方案设计类任务效果提升很明显。
但多步推理也有代价:延迟增加、成本上升。每一步都需要调用一次模型,三步就是三倍的成本。所以只在必要时使用多步推理,简单任务一步到位就好。
3.4 推理策略的选择框架
面对一个具体任务,怎么决定用什么推理策略?我总结了一个简单的判断框架:
| 任务特征 | 推荐策略 | 理由 |
|---|---|---|
| 单步可完成,输入输出明确 | 直接推理 | 不需要额外开销 |
| 需要外部信息或计算 | 工具调用 | 模型自身能力不足 |
| 需要多步逻辑推导 | 链式推理 | 分步降低单步难度 |
| 答案不唯一,需要探索 | 树式推理 | 多路径增加覆盖 |
| 对准确性要求极高 | 反思式推理 | 自我检查减少错误 |
| 任务复杂且步骤多 | 组合策略 | 按需组合上述方法 |
这个框架不是绝对的,但可以作为一个起点。实际选择时还要考虑延迟要求和成本预算。如果延迟敏感,就尽量用简单策略;如果成本敏感,就控制推理步数。
4. 输出层:从模型输出到可用产品的最后一公里
4.1 结构化输出:让模型结果可编程
模型输出的原始文本是给人看的,但产品需要的是结构化数据。输出层的第一项工作就是把模型的自由文本转成程序能处理的格式。
最直接的方式是让模型直接输出JSON。但这里有一个坑:模型输出的JSON不一定是合法的。可能缺少引号、可能有多余的逗号、可能嵌套层级不对。所以永远不要直接解析模型输出的JSON,一定要做校验和修复。
我的做法是分三步。第一步,用提示词约束输出格式,明确告诉模型“只输出JSON,不要有其他内容”。第二步,用JSON解析器尝试解析,如果失败就用正则表达式提取JSON部分再解析。第三步,用schema校验工具检查字段是否完整、类型是否正确。如果校验失败,把错误信息返回给模型让它重新生成。
对于更复杂的结构化输出,比如表格、树形结构,可以考虑用模型输出Markdown表格或缩进文本,然后自己写解析器转换。这种方式比直接输出JSON更稳定,因为模型对Markdown格式的遵循度通常更高。
4.2 结果校验:不是所有输出都能直接用
模型会产生幻觉,这是众所周知的问题。输出层的校验就是要在结果送到用户之前,把明显错误的内容拦截下来。
校验分几个层次。格式校验是最基本的,检查输出是否符合预期的格式。范围校验检查数值是否在合理范围内,比如价格不能是负数、日期不能是过去的时间。一致性校验检查输出内部是否自相矛盾,比如前面说“有库存”后面说“已售罄”。事实校验是最难的一层,需要和外部知识库对比,确认模型陈述的事实是否正确。
实际项目中,格式校验和范围校验是必须做的,成本低、效果好。一致性校验可以用另一个模型来做,让模型检查自己的输出是否自洽。事实校验成本最高,通常只在关键场景下做。
我踩过的一个坑是:过度依赖模型自我校验。让模型检查自己的输出,它往往会说“没问题”,即使实际上有问题。后来我改成用另一个独立的模型来做校验,效果好了很多。校验模型和生成模型最好是不同的,避免相同的偏见。
4.3 后处理管线:把原始输出变成最终产品
模型输出经过校验之后,还需要经过一系列后处理才能变成最终产品。后处理的内容取决于具体应用场景。
对于文本生成类应用,后处理可能包括:去除重复内容、调整语气风格、添加格式化标记、拼接模板文本。对于代码生成类应用,后处理包括:语法检查、格式化、添加注释、生成测试用例。对于数据分析类应用,后处理包括:单位换算、精度调整、图表生成。
后处理管线的设计原则是:每一步只做一件事,步骤之间解耦。这样当某一步出问题时,容易定位和修复。比如格式化和语法检查分开做,不要混在一个步骤里。
另一个原则是:后处理要可配置。不同场景可能需要不同的后处理步骤,把每一步做成可插拔的模块,根据配置决定启用哪些步骤。这样一套管线可以服务多个场景,不用为每个场景单独写代码。
4.4 流式输出与用户体验的平衡
流式输出是提升用户体验的重要手段。模型生成一个token就返回一个token,用户不用等全部生成完就能看到内容。但流式输出给输出层带来了新的挑战。
第一个挑战是校验时机。非流式输出可以在全部生成完之后做校验,但流式输出需要在生成过程中做校验。我的做法是:先做流式输出保证体验,同时在后台做完整校验,如果校验发现问题再撤回或修正。这需要前端支持内容替换。
第二个挑战是结构化输出的流式化。JSON格式在流式输出时很难处理,因为JSON的合法性需要完整解析才能确认。解决方案是:流式输出时用简单的分隔符格式(比如每行一个字段),等全部生成完后再转成JSON。
第三个挑战是中断处理。用户可能在生成过程中取消请求,这时需要正确清理资源、保存已生成的内容。我见过不少项目在中断处理上出问题,导致资源泄漏或状态不一致。
5. 三层架构在实际项目中的组合应用
5.1 一个完整案例的逐层拆解
拿一个智能文档问答系统来举例。用户上传一份合同,然后问“这份合同的违约金比例是多少”。
输入层要做的事情:把合同PDF解析成文本,按条款切分,对每个条款做向量化存入向量库。用户提问时,把问题向量化,检索出最相关的条款片段,组装成上下文。上下文里包括系统提示词、检索到的条款、用户问题。
处理层要做的事情:选择模型。这个任务需要理解法律文本,中等规模的模型通常够用。如果合同里有复杂的计算(比如违约金按天数累计),可能需要工具调用来做计算。推理策略上,单步推理通常够用,但如果问题涉及多个条款的交叉引用,可能需要链式推理。
输出层要做的事情:模型输出的是自然语言答案,需要提取出具体的数字和单位。校验这个数字是否在合同文本中出现过,避免模型编造。后处理把答案格式化成“根据第X条,违约金比例为Y%”的形式。如果用户需要,还可以生成引用来源。
这个案例里,三层各司其职。输入层决定了模型能看到什么,处理层决定了模型怎么思考,输出层决定了最终呈现什么。任何一层出问题,都会影响最终效果。
5.2 不同场景下三层的权重差异
三层的重要性在不同场景下是不一样的。我根据经验总结了一个权重参考:
| 场景类型 | 输入层权重 | 处理层权重 | 输出层权重 |
|---|---|---|---|
| 知识问答 | 高 | 中 | 中 |
| 代码生成 | 中 | 高 | 高 |
| 内容创作 | 高 | 中 | 低 |
| 数据分析 | 中 | 高 | 高 |
| 客服对话 | 高 | 中 | 中 |
| 文档处理 | 高 | 低 | 高 |
知识问答类场景,输入层的检索质量直接决定回答质量,所以权重最高。代码生成类场景,处理层的推理策略和输出层的语法校验都很关键。内容创作类场景,输入层的提示词和上下文决定创作方向,输出层反而不用太复杂。
这个权重表可以帮助你在资源有限时决定优先优化哪一层。比如做知识问答,先把检索做好,比换模型效果更明显。
5.3 常见误区:只关注处理层
我见过太多团队把大部分精力花在处理层上,不断尝试新模型、新推理框架,却忽略了输入和输出。结果就是:模型换了一个又一个,效果提升不明显,因为瓶颈根本不在模型上。
一个典型的误区是:认为“模型越强效果越好”。实际上,当输入层的检索质量很差时,再强的模型也回答不好。因为模型只能基于你给它的信息来回答,你给的信息不对,它只能编造。
另一个误区是:忽略输出层的工程化。模型输出直接返回给用户,不做校验、不做后处理。结果就是用户经常看到格式错误、事实错误、不完整的回答。这些问题不是换模型能解决的,必须在输出层做工程处理。
我的建议是:先把输入层和输出层做扎实,再考虑优化处理层。输入层做好检索和上下文组装,输出层做好校验和后处理,这两层的投入产出比远高于换模型。
6. 从三层架构看技术选型与迭代节奏
6.1 新框架出现时怎么快速判断价值
每当一个新框架或新产品出现时,我会用三层架构来快速判断它值不值得学。具体方法是问三个问题:它主要解决哪一层的问题?这一层我现在的痛点是什么?它能带来多大提升?
比如一个检索框架,它解决的是输入层的检索问题。如果我现在的检索准确率很低,那它就值得关注。如果我现在的检索已经够用了,那它可能只是锦上添花。再比如一个推理框架,它解决的是处理层的推理策略问题。如果我的任务很简单,不需要多步推理,那它对我的价值就有限。
这个方法可以帮你过滤掉大量“看起来很美但用不上”的新技术,把时间花在真正能解决你问题的东西上。
6.2 迭代优先级:先做哪一层
项目刚开始时,三层的迭代顺序建议是:先做输入层,再做输出层,最后优化处理层。
输入层是基础。输入层没做好,后面两层再优化也是白搭。先把提示词写好、把上下文组装逻辑跑通、把检索做起来。这一步不需要太多资源,但效果立竿见影。
输出层是保障。输入层跑通后,模型能给出回答了,但回答可能格式不对、可能有错误。这时候加上输出校验和后处理,让结果变得可用。这一步也不需要太多资源,但能显著提升产品质量。
处理层是优化。当前两层都做扎实了,再考虑换更好的模型、加更复杂的推理策略。这时候你会发现,处理层的优化效果更容易体现出来,因为瓶颈已经不在输入和输出上了。
6.3 团队分工与三层架构的对应关系
如果是一个团队在做大模型应用,三层架构也可以对应到不同的角色分工。
输入层的工作适合数据工程师和搜索工程师来做。文档处理、向量化、检索优化,这些是他们的强项。输出层的工作适合后端工程师来做。格式校验、后处理管线、API设计,这些是后端工程的常规工作。处理层的工作适合算法工程师来做。模型选型、推理策略、效果调优,这些需要算法背景。
当然,小团队可能一个人全包。但即使是一个人,也可以按这三层来组织工作,每次专注一层,避免同时处理太多变量。
6.4 三层架构的边界与局限
三层架构是一个思维工具,不是万能公式。它也有局限。
首先,三层之间的边界不是绝对的。比如工具调用,它既涉及处理层的推理策略,也涉及输出层的格式解析。实际项目中,三层是相互影响的,不能完全割裂。
其次,这个框架主要适用于应用层开发,不适用于模型训练。如果你在做预训练或微调,关注点会完全不同。
最后,三层架构是一个静态框架,但大模型技术在快速演进。今天属于处理层的能力,明天可能被整合到模型内部。所以这个框架需要定期更新,不能一成不变。
我在实际使用中的体会是:三层架构最大的价值不是分类,而是帮你建立一种“可控性思维”。大模型很复杂,但你能控制的只有输入和输出。把可控的部分做到极致,不可控的部分自然会有更好的表现。这个思路在我做过的多个项目里都得到了验证。