1. 为什么用餐厅后厨来讲AI技术概念
第一次听到“LLM、RAG、ReAct、Memory、Tool Use”这一串词,很多人的反应是:每个字母都认识,连起来完全不知道在说什么。我刚开始接触大模型应用开发的时候也一样,看了一堆文档,概念背下来了,但真到动手搭一个能用的系统,还是不知道这些模块之间到底怎么配合。
后来我发现一个特别好用的方法:把AI系统想象成一家餐厅的后厨。这个类比不是随便找的,而是因为两者在结构上高度相似——都有信息输入、都有加工处理、都有记忆存储、都有工具调用、都有质量把控。你只要理解了后厨怎么运转,就能理解AI系统怎么运转。
这篇文章就是把这个类比从头到尾讲透。不管你是刚入门的产品经理、想转行做AI应用的开发者,还是纯粹对大模型好奇的技术爱好者,看完之后你至少能做到两件事:第一,别人再聊这些概念,你能听懂它们在说什么;第二,你自己设计一个AI应用的时候,知道每个模块该放在哪里、为什么放那里。
我下面会按照后厨的真实运转流程来拆解,从客人点单到菜品上桌,每一步对应一个AI技术概念。你不需要任何前置知识,跟着走就行。
2. 后厨全景:AI系统的整体架构拆解
2.1 一家餐厅后厨的基本分工
先想象一家正常运转的餐厅后厨。客人进门坐下,服务员递上菜单,客人点菜,服务员把订单传到后厨。后厨里有几个关键角色:
- 主厨:负责核心的烹饪工作,决定这道菜怎么做、放什么调料、火候多大。
- 配菜员:负责提前准备好各种食材,切好、腌好、分装好,主厨要用的时候直接拿。
- 传菜员:负责把做好的菜从后厨送到客人桌上。
- 仓库管理员:负责管理食材库存,知道什么东西放在哪里、还剩多少、什么时候该补货。
- 厨房设备:烤箱、微波炉、搅拌机、温度计,主厨需要用什么就去用什么。
这五个角色各司其职,缺一个后厨就转不动。AI系统也是一样的道理,只不过角色名字换了。
2.2 AI系统的对应角色
把上面的角色翻译成AI术语:
| 后厨角色 | AI对应概念 | 核心职责 |
|---|---|---|
| 主厨 | LLM(大语言模型) | 核心推理和生成 |
| 配菜员 | RAG(检索增强生成) | 提前准备和检索相关信息 |
| 传菜员 | Tool Use(工具调用) | 连接模型和外部能力 |
| 仓库管理员 | Memory(记忆系统) | 存储和调取历史信息 |
| 厨房设备 | 外部工具/API | 完成模型本身做不到的事 |
| 后厨工作流程 | ReAct(推理+行动) | 决定先做什么后做什么 |
这个对应关系不是硬凑的,而是它们在系统中的作用确实一一对应。下面我逐个展开讲。
2.3 为什么这个类比能帮你理解
很多人学AI技术概念的时候,习惯从定义出发:LLM是什么、RAG是什么、ReAct是什么。这种学法的问题是,你记住了定义,但不知道它们之间的关系。
从后厨的角度切入,你天然就会问:主厨做菜的时候,配菜员什么时候把食材递过来?传菜员什么时候把菜送出去?仓库管理员怎么知道食材不够了?这些问题对应的就是:LLM生成的时候,RAG什么时候触发检索?Tool Use什么时候被调用?Memory什么时候被读写?
一旦你开始问这些问题,你就不是在背概念了,而是在理解一个系统。这是完全不同的层次。
3. 主厨登场:LLM到底在做什么
3.1 LLM就是那个厨艺高超但有点“轴”的主厨
LLM(大语言模型)是整个AI系统的核心,就像主厨是整个后厨的核心。主厨的厨艺决定了这家餐厅的水平上限,LLM的能力决定了这个AI系统的能力上限。
但主厨有几个特点你需要知道:
第一,主厨的厨艺是训练出来的,不是天生的。一个主厨可能花了十年学做菜,去过很多餐厅,跟过很多师傅。LLM也是一样,它是在海量文本数据上训练出来的。训练数据就是它的“学厨经历”。
第二,主厨做菜有自己的风格,有时候不太听话。你让他少放盐,他可能觉得不放盐不好吃,还是放了一点。LLM也有这个问题,你让它严格按照某个格式输出,它有时候就是会加一些自己的“发挥”。这在AI圈叫“幻觉”,在后厨叫“主厨的倔强”。
第三,主厨不知道今天的食材新不新鲜。主厨的厨艺再好,如果配菜员给他的食材是坏的,做出来的菜也不行。LLM也是一样,它的知识有截止日期,训练数据之后发生的事情它不知道。这就是为什么需要RAG。
3.2 主厨的能力边界在哪里
理解LLM的能力边界,比理解它能做什么更重要。我总结下来是三条线:
- 知识线:LLM知道的是训练数据里有的东西。训练数据截止之后的事情,它不知道。你问它今天的天气,它答不出来。
- 计算线:LLM不擅长精确计算。你让它算一个复杂的数学题,它可能会给你一个看起来很像但实际错误的答案。这不是它笨,而是它的工作方式决定的——它是在“预测下一个词”,不是在“计算”。
- 行动线:LLM只能输出文字,不能直接操作外部系统。你让它帮你发一封邮件,它只能告诉你邮件内容应该怎么写,不能真的帮你发出去。要发邮件,需要Tool Use。
这三条线划定了主厨的能力范围。超出这个范围的,就需要其他角色来补位。
3.3 选主厨的标准:怎么挑合适的LLM
现在市面上的LLM很多,怎么选?我的经验是看三个维度:
第一,看菜系。不同LLM擅长的领域不一样。有的擅长写代码,有的擅长写文案,有的擅长做数学推理。你要根据你的应用场景来选。就像川菜馆不会请一个只会做粤菜的主厨。
第二,看厨房大小。大模型能力强但资源消耗大,小模型能力弱但跑得快、成本低。如果你的应用场景比较简单,比如只是做一个客服自动回复,没必要上最大的模型。就像一个小面馆不需要请米其林三星主厨。
第三,看预算。调用API是要花钱的,模型越大越贵。你需要算一笔账:你的应用每天大概有多少次请求,每次请求大概消耗多少token,然后乘以单价。这个成本在你选模型的时候就要算清楚。
实操心得:我一般会先用一个大模型做原型验证,确认效果达标之后,再尝试用小模型替换,看效果下降多少。如果下降在可接受范围内,就换小模型省钱。这个思路叫“先跑通再优化”。
4. 配菜员的智慧:RAG如何让AI“有据可查”
4.1 为什么主厨需要配菜员
回到后厨的场景。主厨虽然厨艺高超,但他不可能记住仓库里所有的食材。他需要配菜员在他做菜之前,把这道菜需要的食材准备好,放在他手边。
RAG(检索增强生成)就是这个配菜员。它的工作流程是:当用户提出一个问题时,RAG先去知识库里检索相关内容,然后把检索到的内容和用户的问题一起交给LLM,让LLM基于这些内容来生成回答。
为什么要这样做?因为LLM的知识是“死”的,训练完之后就固定了。但现实世界的信息是不断更新的。你不可能每次信息更新都重新训练一遍模型,那成本太高了。RAG的思路是:模型不用重新训练,我每次把最新信息检索出来喂给它就行。
4.2 RAG的三个核心步骤
RAG的工作可以拆成三步:
第一步:切片。把知识库里的文档切成小块。为什么要切?因为LLM的上下文窗口是有限的,你不可能把一整本书都塞进去。切成小块之后,每次只检索最相关的几块。
切片的粒度很关键。切得太碎,信息不完整;切得太粗,检索精度下降。我的经验是,中文文档一般切成300到500字一块比较合适,英文文档可以稍微大一点。块与块之间可以留一些重叠,避免关键信息被切断。
第二步:向量化。把切好的文本块转换成向量(一串数字)。这个过程叫embedding。转换之后,语义相近的文本块在向量空间里的距离就比较近。这样你检索的时候,就可以通过计算向量距离来找到最相关的文本块。
第三步:检索和生成。用户提问时,把问题也转成向量,然后在向量数据库里找最相似的几个文本块,把它们和问题一起交给LLM生成回答。
4.3 RAG的常见瓶颈和解决思路
RAG听起来简单,但实际做起来坑很多。我踩过的几个典型问题:
问题一:检索不准。用户问“怎么退款”,检索出来的却是“退款政策”的文档,而不是“退款操作步骤”。这是因为向量检索只能匹配语义相似度,不能理解用户的真实意图。
解决思路:在向量检索之前加一层关键词过滤,或者用混合检索(向量检索+关键词检索),把两种结果融合排序。另外,可以在检索之后加一个重排序(rerank)步骤,用一个专门的模型对检索结果重新打分。
问题二:知识库更新不及时。你昨天刚更新的文档,今天用户提问还是检索不到。这是因为向量数据库的索引没有及时更新。
解决思路:建立增量更新机制,文档变更时自动触发重新切片和向量化。如果更新频率很高,可以考虑用实时索引方案。
问题三:检索到的内容太长。检索出来五个文本块,每个500字,加起来2500字,再加上用户问题和系统提示词,可能就超过模型的上下文窗口了。
解决思路:控制检索返回的文本块数量,一般3到5个就够了。如果内容确实多,可以先做一轮摘要压缩,再交给LLM。
注意事项:RAG不是万能的。如果你的知识库本身质量很差,检索出来的内容也是垃圾,LLM基于垃圾生成的结果只会更垃圾。所以做RAG之前,先把知识库整理好。
5. 传菜员的艺术:Tool Use让AI“能动手”
5.1 主厨不能亲自端菜
主厨做完菜之后,他不能自己端着盘子跑到客人桌上。他需要传菜员把菜送过去。Tool Use(工具调用)就是这个传菜员。
LLM本身只能生成文字,不能直接操作外部系统。但很多场景下,你需要AI不只是“说”,还要“做”。比如:
- 用户说“帮我查一下明天北京的天气”,AI需要调用天气API。
- 用户说“帮我订一张明天去上海的机票”,AI需要调用订票系统。
- 用户说“帮我算一下这个月的支出”,AI需要调用计算器或者数据库查询。
这些都需要Tool Use来实现。
5.2 Tool Use的工作机制
Tool Use的工作流程大致是这样的:
- 你提前定义好一组工具,每个工具包括名称、功能描述、参数格式。
- 用户提问时,LLM判断是否需要调用工具。如果需要,它输出一个工具调用请求,包括工具名称和参数。
- 系统执行这个工具调用,拿到结果。
- 把结果返回给LLM,LLM基于结果生成最终回答。
这个流程看起来简单,但有几个关键点:
工具描述要清晰。LLM是根据你的工具描述来判断该不该调用的。如果你的描述含糊不清,LLM可能该调的时候不调,不该调的时候乱调。比如你定义一个“查询天气”的工具,描述写“查询信息”,LLM就不知道这个工具到底是查天气还是查别的。
参数格式要严格。LLM输出的参数格式必须和你的工具定义一致。如果工具要求传入日期格式是“YYYY-MM-DD”,LLM输出了“明天”,就会报错。所以参数描述里要写清楚格式要求,最好给一个示例。
错误处理要完善。工具调用可能会失败,比如API超时、参数错误、权限不足。你需要把这些错误信息返回给LLM,让LLM决定是重试、换一个工具、还是告诉用户出错了。
5.3 工具选型的几个原则
不是所有功能都适合做成工具。我总结了几条原则:
- LLM做不到的事情才做成工具。比如精确计算、实时查询、操作外部系统。LLM能直接生成的内容,不需要做成工具。
- 工具粒度要适中。太细了,LLM要调用很多次才能完成一个任务;太粗了,LLM不知道怎么用。一般一个工具对应一个明确的操作。
- 工具数量不要太多。工具太多,LLM选择的时候容易出错。我一般控制在10个以内,超过的话就分组,或者用路由机制先选组再选工具。
实操心得:定义工具的时候,我会先写一个“工具使用说明”,用自然语言描述这个工具是干什么的、什么时候用、参数怎么传。然后把这个说明直接作为工具描述。这样LLM理解起来最准确。
6. 仓库管理员的记忆术:Memory让AI“记得住”
6.1 主厨的记忆力是有限的
主厨在做菜的时候,需要记住很多事情:这道菜做到哪一步了、刚才放了多少盐、客人有没有说不要放辣。但主厨的记忆力是有限的,他不可能记住所有事情。
LLM也是一样。它的上下文窗口是有限的,你不可能把所有的对话历史都塞进去。而且,即使上下文窗口够大,把所有历史都塞进去也会导致两个问题:一是成本高,二是信息太多反而会干扰LLM的判断。
Memory(记忆系统)就是解决这个问题的。它的作用是:把重要的信息存下来,需要的时候再调出来。
6.2 Memory的三种类型
Memory可以分成三种类型:
短期记忆。就是当前对话的上下文。用户刚才说了什么、AI刚才回了什么。这部分信息直接放在上下文窗口里,LLM可以直接看到。
长期记忆。跨对话保存的信息。比如用户上次说他对花生过敏,这次点菜的时候AI应该记得提醒他。长期记忆需要存储到外部数据库,需要的时候检索出来。
工作记忆。当前任务执行过程中的中间状态。比如AI正在帮用户订机票,已经选了航班、选了座位,正在选餐食。这些中间状态需要记下来,不然下一步就不知道进行到哪里了。
6.3 Memory的实现方式
短期记忆的实现最简单,直接把对话历史拼接到prompt里就行。但要注意控制长度,太长了要截断或者摘要。
长期记忆的实现方式有几种:
- 键值存储:把用户信息存成键值对,比如
{user_id: 123, allergy: "花生"}。简单直接,但只能存结构化信息。 - 向量存储:把信息转成向量存起来,需要的时候检索。适合存非结构化的信息,比如用户的偏好描述。
- 图存储:把信息存成图结构,节点是实体,边是关系。适合存复杂的关系信息,比如“张三的朋友李四喜欢川菜”。
工作记忆一般用一个状态对象来维护,每一步操作更新这个对象,下一步操作读取这个对象。
注意事项:Memory系统最大的坑是“记了不该记的”。比如用户随口说了一句“今天好热”,你把它当成长期偏好存下来了,下次用户来的时候你推荐冷饮,用户可能觉得莫名其妙。所以存什么、不存什么,需要有明确的策略。
7. 后厨的指挥系统:ReAct如何让AI“边想边做”
7.1 主厨做菜不是一步到位的
一个主厨做一道复杂的菜,不是一步到位的。他会先想一下这道菜大概怎么做,然后开始动手,做到一半尝一下味道,发现淡了再加点盐,继续做,再尝一下,差不多了就出锅。
这个过程就是“推理+行动”的循环。ReAct(Reasoning + Acting)就是让AI也这样工作。
传统的LLM工作方式是:你问一个问题,它直接给你一个答案。但很多任务不是一步能完成的,需要多步推理和多次行动。ReAct的思路是:让LLM先推理下一步该做什么,然后执行这个动作,观察结果,再推理下一步,再执行,直到任务完成。
7.2 ReAct的循环流程
ReAct的循环可以概括为三个步骤:
- Thought(思考):LLM分析当前状态,决定下一步该做什么。
- Action(行动):执行一个具体的动作,比如调用一个工具、检索一次知识库。
- Observation(观察):获取行动的结果,作为下一步思考的输入。
这个循环一直持续,直到LLM认为任务完成,输出最终答案。
举个例子。用户问:“帮我查一下北京明天天气,如果下雨就帮我订一张电影票。”
ReAct的流程可能是:
- Thought:我需要先查天气。
- Action:调用天气API,查询北京明天天气。
- Observation:明天北京有雨。
- Thought:下雨了,需要订电影票。但我不知道用户想看什么电影、什么时间。
- Action:调用电影票API,查询当前上映的电影。
- Observation:返回电影列表。
- Thought:我需要问用户想看哪部电影、什么时间。
- Action:输出问题给用户。
这个流程比直接让LLM回答要复杂,但能处理更复杂的任务。
7.3 ReAct的常见问题和优化
ReAct最大的问题是“绕圈子”。LLM可能在某个步骤反复循环,一直不输出最终答案。我遇到过几种典型情况:
情况一:工具调用一直失败。LLM调用一个工具,失败了,它再调一次,又失败了,再调一次……陷入死循环。
解决思路:设置最大循环次数,超过就强制输出。另外,在工具调用失败时,把错误信息明确返回给LLM,让它知道失败原因,而不是简单地重试。
情况二:LLM忘记目标。循环了几步之后,LLM忘了最初的任务是什么。
解决思路:在每一步的prompt里都带上原始任务描述,让LLM始终能看到目标。
情况三:LLM过度思考。简单任务也走很多步,浪费时间和成本。
解决思路:在prompt里加一个判断,如果LLM认为可以直接回答,就跳过行动步骤,直接输出。
实操心得:ReAct的prompt设计很关键。我会在prompt里明确写出“如果你已经知道答案,直接输出,不需要调用工具”和“如果连续两次调用同一个工具都失败,换一种方式或者告诉用户”。这两句话能避免大部分死循环。
8. 后厨的质检环节:怎么保证AI不出错
8.1 主厨做菜需要尝味道
主厨做完菜之后,会尝一下味道,确认没问题才让传菜员端出去。AI系统也需要这样的质检环节。
AI出错的方式有很多种:LLM幻觉、检索内容不相关、工具调用参数错误、记忆信息过期。如果不做质检,这些错误就会直接暴露给用户。
8.2 质检的几种方式
方式一:规则校验。对LLM的输出做格式检查。比如你要求输出JSON格式,就用JSON解析器验证一下。如果解析失败,就让LLM重新生成。
方式二:模型校验。用一个单独的LLM来评估主LLM的输出质量。这叫“LLM as Judge”。比如你可以让一个模型判断“这个回答是否基于检索到的内容,有没有编造信息”。
方式三:人工抽检。定期人工检查AI的输出,发现系统性问题。这种方式成本高,但能发现规则和模型校验发现不了的问题。
方式四:用户反馈。让用户对AI的回答打分或者反馈。这是最直接的信号,但需要设计好反馈机制,不然用户不会主动反馈。
8.3 质检的时机
质检可以在三个时机进行:
- 生成前:检查输入是否合法、检索内容是否相关。
- 生成中:检查每一步的中间结果是否合理。
- 生成后:检查最终输出是否符合要求。
我的经验是,生成后的质检最重要,因为这是用户直接看到的。生成前的质检能提前拦截问题,减少无效计算。生成中的质检主要用于复杂任务,简单任务不需要。
注意事项:质检本身也会消耗资源。如果每个请求都做多重质检,成本会很高。我的做法是分级质检:简单请求只做规则校验,复杂请求才做模型校验。
9. 从后厨到AI:一套可落地的系统设计思路
9.1 先画流程图再写代码
我见过很多人做AI应用,上来就开始写代码,写到一半发现架构不对,推倒重来。我的建议是:先画流程图。
流程图不用很复杂,把用户输入到最终输出的每一步画出来就行。每一步标注清楚:这一步是LLM在做、还是RAG在做、还是Tool Use在做、还是Memory在做。画完之后,你就能看到整个系统的全貌,知道哪里可能出问题。
9.2 从最小可用系统开始
不要一上来就搭一个完整的系统。先搭一个最小可用的版本:一个LLM加一个简单的prompt,能跑通就行。然后逐步加RAG、加Tool Use、加Memory、加ReAct。
每加一个模块,都测试一下效果有没有提升。如果没有提升,或者提升不明显,就考虑是不是不需要这个模块。我见过很多系统,加了一堆模块,效果反而下降了,就是因为模块之间互相干扰。
9.3 监控和迭代
系统上线之后,监控是必须的。你需要知道:每天有多少请求、平均响应时间是多少、错误率是多少、用户满意度是多少。
这些数据能帮你发现系统的问题。比如错误率突然上升,可能是某个工具挂了;响应时间变长,可能是检索的内容太多了;用户满意度下降,可能是LLM的回答质量下降了。
根据监控数据做迭代,比凭感觉做迭代要靠谱得多。
10. 常见问题速查
| 问题 | 可能原因 | 排查思路 | 解决方法 |
|---|---|---|---|
| LLM回答不准确 | 训练数据不包含该知识 | 检查问题是否超出模型知识范围 | 引入RAG,补充相关知识 |
| RAG检索不到相关内容 | 切片粒度不合适 | 检查切片大小和重叠设置 | 调整切片参数,尝试不同粒度 |
| Tool Use调用失败 | 参数格式不匹配 | 检查工具定义和LLM输出 | 完善工具描述,增加参数示例 |
| Memory信息过期 | 没有更新机制 | 检查记忆存储的更新策略 | 增加过期时间,定期清理 |
| ReAct陷入死循环 | 工具调用一直失败 | 检查工具是否可用 | 设置最大循环次数,增加错误处理 |
| 响应时间过长 | 检索内容太多 | 检查检索返回的文本块数量 | 减少检索数量,增加摘要压缩 |
| 成本过高 | 模型太大或调用太频繁 | 统计token消耗和调用次数 | 换小模型,优化prompt长度 |
11. 我在实际项目中的几点体会
做AI应用这几年,最大的体会是:技术概念本身不难,难的是知道什么时候用什么。LLM、RAG、Tool Use、Memory、ReAct,这些概念单独拿出来都能讲清楚,但放到一个系统里,它们之间的配合才是真正的挑战。
我踩过的一个大坑是:一开始觉得RAG是万能的,什么问题都往RAG上套。结果发现,有些问题根本不需要检索,LLM直接回答就行,加了RAG反而引入了噪音。后来我学乖了,先判断问题类型:如果是通用知识,直接让LLM回答;如果是特定领域知识,才走RAG。
另一个坑是:Tool Use定义太多工具,LLM选择困难。后来我把工具按场景分组,每组不超过5个,LLM的选择准确率明显提升。
还有一个坑是:Memory存了太多没用的信息,导致检索的时候噪音很大。后来我加了一个过滤机制,只存对后续对话有影响的信息,比如用户的偏好、重要的事实,不存闲聊内容。
这些经验不是从文档里看来的,都是实际做项目踩出来的。希望对你有所帮助。如果你也在做AI应用,欢迎交流。