做教学类工作的同学一定要警惕知识诅咒 ,因为知者不难、难者不会 ,比如最近在训练营中我就遇到了类似的问题:
学员们真的会对一些概念搞不清楚,初学者对于:Langchain、向量化、RAG 他们是很难分清楚的,我们在做课程设计的时候一定要更加细致一些。
所以,我们今天做一篇科普文章,对几个概念进行下简单说明,首先Langchain需要被单拎出来,因为他是一套Agent开发框架,非要去对比也应该是Coze、dify、n8n等 。
Dify与Langchain都可以被归属到Agent平台,可以帮助用户快速生成各种Agent,只不过两者的定位与使用对象是不同的:
Dify的定位是低/零代码 平台,使用对象甚至可以是HR和财务; Langchain的定位是高代码 平台,使用对象就是程序员; 与Dify类似的有Coze、FastGPT,其中Coze体验是最好的,最近还开源了,会对Dify造成一定影响;
与Langchain类似的有n8n(会稍有差异,但这么理解也问题不大),这些框架需要开发者具备一定的编程能力,对开发者的技术水平要求较高,相对来说其灵活性也变高了。
就个人使用习惯来说,做POC验证我一定会选Coze或Dify,做复杂的业务系统我们会做详细的框架设计,自己上手写代码,暂时不会有Langchain或者n8n出手的空间。
原因也很简单:我们有自己的开发习惯,不喜欢按他们那种方式做归类。
从这里大家也可以看出来了,粉丝疑惑的Langchain其实与RAG没撒必然的联系,Agent平台/框架 确实会涉及到知识库(会实现该模块),他们也会用到RAG技术,并且还会涉及到向量化,仅此而已。
为了大家更清晰的理解,我们直接来一套相对完整的RAG链路算了:
采集/清洗 → 切分(Chunk) → 向量化 → 建索引 → 召回(Top-K) → 重排(Rerank) → 拼上下文 → 生成 → 校对/引用 → 评测与回流 。
unsetunsetRAG概述unsetunset RAG技术在两年多前开始被AI应用熟知,通过在生成前检索外部知识库,使模型能够及时利用最新或私有数据。
怎么说呢?虽然有点不恰当,但个人觉得在当时,RAG更多是微调的一种替代技术,因为微调成本确实太高了,结果用着用着大家还觉得挺香的...
在RAG技术框架中,数据工程 便已经开始崭露头角,懂行的同学会意识到:AI项目最重要的工作就是让数据与模型好好配合 。
RAG的实现来说有两个模块,一个是本地索引 模块,他涉及了原始文档的清洗和切片,并将每个段落转为向量存储到向量库;
第二个模块就是实时检索 ,他需要在用户提问的时候,先到向量库中查找相似的片段,再将检索到的上下文拼接到提示词中,最后调用大模型。
举一个案例:
简单案例 案例来源于最近接到的一个商家工作流,需求很简单:构建一个能回答关于“咖啡豆种类、冲泡方法、拿铁配方”等问题的智能助手 。
原始数据包含一份咖啡知识的PDF文档,里面包含文本、表格、少量格式混乱字符和网页URL。
工作流的目标是:用户问 “如何制作一杯标准的拿铁咖啡?需要多少克咖啡粉和牛奶?”时,系统能精准从知识库中找到配方步骤和分量 ,并生成清晰、无误的答案。
直接上手 实际实施工作流的是下面一个小朋友,他图省事未对文档进行清洗 ,按固定长度512字符切分;
当遭遇用户提问:"标准拿铁咖啡的配方是什么?需要多少毫升牛奶?"时,模型果然开始胡说八道了:向量检索模块可能返回了包含“拿铁”字样但内容并不精确相关的片段。
例如,一个片段来自文档中关于拿铁咖啡历史的段落,另一个片段则包含了对拿铁咖啡配料的不完整描述,甚至还可能检索到包含无关引用(如“咖啡师手册”)的片段。
这些检索结果在没有筛选的情况下被一并提供给模型,导致模型的回答驴唇不对马嘴,出现了明显错误,这背后可能的提示词是:
根据以下知识片段回答问题: [知识片段1] **© 咖啡知识大全 2025** https://coffee-wiki.com/retail 拿铁咖啡的历史与起源 17世纪,维也纳柯奇斯基将军发现土耳其人留下的咖啡豆... [知识片段2] 牛奶咖啡在意大利语中称为"Caffè latte",传统使用高温灭菌奶... [知识片段3] 取冷藏全脂牛奶200ml 用蒸汽棒加热至60℃并打出细奶泡 将牛奶缓慢倒入咖啡杯 问题:标准拿铁咖啡的配方是什么?需要多少毫升牛奶?这里如果引入CoT也是灾难:
1. 用户问配方和牛奶用量 2. 上下文提供: - 片段1:拿铁历史(无配方) - 片段2:名称起源(无用量) - 片段3:提到200ml牛奶但无完整步骤 3. 关键缺失: - 没有完整的步骤说明 - 没有明确说200ml就是标准用量 - 缺少开头(咖啡萃取)和结尾(拉花) 4. 基于训练数据推测: - 历史知识常见(维也纳起源正确) - 200ml是常见牛奶量(保留但加"左右"模糊化) - 缺少步骤→用通用话术填补("参考咖啡师手册")这里问题就很简单,没有对文档进行清洗,所以在文档向量化之前一定要对知识进行处理
文档处理 文档的全局处理属于框架型工作 是一门技术活,但具体到每个文档的处理,又变成体力活了,他的动作很简单:
去掉页眉/页脚/URL/无关引用;把表格转成完整句子;只保留与“拿铁配方”强相关的干净文本。
以下是一段清洗后的片段,供大家参考:
[Chunk_A_clean] 【标准拿铁配方(单杯)】 咖啡粉:18g(萃取一份双倍浓缩 Espresso) 牛奶:180ml(蒸汽打发,温度约55–60℃) 步骤:1) 研磨并萃取浓缩;2) 将180ml热奶缓慢倒入;3) 轻摇融合,可拉花。 [Chunk_B_clean] 比例说明:常见咖啡:牛奶体积约 1:4(以18g粉对应约30–40ml浓缩+180ml热奶为例)。这里形成的提示词就很清晰了:
角色:你是咖啡知识助手。 规则: - 仅基于“资料片段”作答;资料未覆盖的内容不要编造。 - 回答必须给出“咖啡粉(克)”与“牛奶(毫升)”的具体数字与单位。 - 若资料无答案,请输出:`未在资料中找到`。 - 在句末用[编号]标注引用来源(如来自片段[1]与[2])。 用户问题: “如何制作一杯标准的拿铁咖啡?需要多少克咖啡粉和牛奶?” 资料片段: [1] {Chunk_A_clean} [2] {Chunk_B_clean} 输出格式: - 先给出配方用量(粉、奶、温度) - 再给3步以内的简要步骤 - 最后标注引用,如:[1][2]这里其实不难,我们这里再插一句向量化 。
向量化 切片的目的是为了存入向量库方便后期检索,这里需要进行的一步就是文本向量化 :
向量化是将文字转换为高维空间中的坐标点(如512维向量 [0.24, -0.57, ..., 0.83]),让机器能计算语义相似度(距离近=语义相关)。
这里还是举个例子,让大家有更具象化的认知:
# 测试文本 query ="酸味明亮的咖啡豆" doc1 ="埃塞俄比亚耶加雪菲:柑橘酸感突出" doc2 ="巴西咖啡:坚果巧克力风味,低酸度" # 向量模型1:通用模型 text-embedding-ada-002 vec_query_ada = [0.12, -0.45, 0.23, ...] # 维度示例 vec_doc1_ada = [0.08, -0.41, 0.19, ...] vec_doc2_ada = [-0.33, 0.72, -0.15, ...] # 计算余弦相似度 sim_ada_doc1 = 0.68 # query与耶加雪菲 sim_ada_doc2 = 0.62 # query与巴西 # 向量模型2:领域模型 BGE-large-zh vec_query_bge = [0.87, -0.12, 0.64, ...] vec_doc1_bge = [0.82, -0.08, 0.61, ...] vec_doc2_bge = [-0.24, 0.33, -0.47, ...] sim_bge_doc1 = 0.92 # 显著提升! sim_bge_doc2 = 0.31 # 无关项被压制这个简单的案例,大家可以清晰看出query与doc1更为贴合 。
这里再举一个反面案例,当坏向量遇上检索 会怎么样?
# 步骤1:向量化(使用text-embedding-ada-002) - Query向量:`酸味明显的咖啡豆` → [0.21, -0.33, 0.47, ...] - 相关文档向量: - 正例《耶加雪菲》:"柑橘酸感明亮"→ [0.18, -0.29, 0.42, ...] # 相似度0.75 - 干扰文档向量: - 反例《巴西咖啡》:"低酸度"→ [0.24, -0.35, 0.39, ...] # 相似度0.82! (错误更高) # 步骤2:向量检索(Top 2召回) | 排名 | 文本 | 相似度 | 实际内容 | |------|---------------------------|--------|------------------------| | 1 | 巴西咖啡:坚果巧克力风味 | 0.82 | **低酸度**(干扰项!) | | 2 | 咖啡因含量对照表 | 0.78 | 无关表格 | | 3 | 耶加雪菲:柑橘酸感明亮 | 0.75 | 正确答案被挤出Top2 | # 步骤3:生成答案 输入Prompt: "巴西咖啡:坚果巧克力风味,低酸度" "罗布斯塔豆咖啡因含量:2.7%,阿拉比卡豆:1.5%" 问题:酸味明显的咖啡豆推荐?如果知识问答是这样的话,就会出问题:推荐巴西咖啡 ,它具有坚果风味且酸度较低。
正确的索引带来了错误的回答,这种情况在RAG技术中也不是个例,遇到这种问题,多半就要引入数据工程与飞轮系统 了,并且可能会涉及部分微调。
最后,在真实使用过程中会对问题进行重写,比如:
扩展前Query:"酸味明显的咖啡豆" 扩展后Query:"酸度 或 酸味 或 明亮酸质 的 咖啡豆 品种"unsetunset向量化的意义unsetunset 最后发散一下,大家其实也发现了:RAG技术其实并不非要依赖向量库 ,也就是只要能将知识搜索出来,事实上并不一定需要向量化。
而RAG技术在2年多之前普遍被大家接受,核心原因有两个:
第一是,当时模型上下文太短,4k、8k、16k是主流(32k都一票难求),在这个基础上,就算向量库特别好用,但受限于提示词长度,其实也不好用 ;第二是,受限于AI项目认知,并不知道如何组织私有化数据,RAG提供了一个范式,自然而然就用了,至于好不好又再说 ;站在这个基础上,大家事实上可以认为:所谓RAG在初期,事实上也并不好用,因为模型上下文装不了完整的知识库,知识库不全无论如何都回答不好 。
因为就是在之前的场景,也不存在将全量数据给模型的可能,将知识变成小片段、将知识进行精炼压缩以减少长度,这是一种以精度换准度的妥协 。
然后随着模型技术发展,模型上下文扩展到足够大了,RAG反而变得好用了,这也是为什么我前面会说,RAG属于后训练(微调)的一个替代方案的原因。
只不过,在上下文如此健壮的今天,另一个问题也就产生了:似乎,向量化意义不大,比如在知识库不多的时候,全量导入反而是最优解 。
想象一下,直接把整本《咖啡百科全书》PDF的文本内容(当然,经过必要的清洗和格式优化)塞进提示词,好像也没什么不好,毕竟也就几万字...
所以,也许我们需要的思考的是向量库在模型阶段初期不好用,模型阶段后期用不着,重要的可能一直是结构化的知识库 ...
unsetunset进一步的思考unsetunset 如前所述,向量库的目的只有一点:将用户的提问涉及到本地知识的部分搜索出来 ,仅从这个角度出发,向量检索完成的功能与模型LLM是类似的,甚至可以粗暴的将向量库当成“预训练过的小模型”。
但是,向量化毕竟不是模型训练,无论分块策略如何优化,向量检索始终面临“用固定维度向量表示无限语义” 的瓶颈,且其优化目标(语义相似性)与下游任务目标(答案精准性) 存在天然鸿沟,比如:
# 语义相似高 ≠ 答案支持性强 Query ="酸味明亮的咖啡豆推荐?" Doc1 ="耶加雪菲:柑橘酸感突出(产地埃塞)"# 高相关!但向量相似度可能被弱化 Doc2 ="咖啡酸味的化学成因:绿原酸分解" # 高相似!但无法直接回答“推荐”因为根本无法确定用户会输出什么莫名其妙的问题 ,多以一定会存在怎么都索引不到知识的场景 ,这也许是RAG最大的问题。
当然,我这里并不是要否定向量化,只不过我在思考其更好的用法,以我们的某次实践为例:也许我们可以设计一套结构化的知识库,比如知识图谱 。
然后我们对向量索引的使用仅仅压缩到,对关键Key的筛选,比如我们结构化的知识库中存的是完善的疾病信息,而向量库中存的是症状与疾病的映射信息。
在检索时,我们只需要关注用户的描述应该是什么症状,再从相关的症状向量中将可能关联的疾病检索出来,而后我们直接使用结构化的疾病库即可。
unsetunset知识载体-语义路由unsetunset 这里所谓将向量索引仅用于关键Key筛选 ,其本质是将向量库从知识载体降级为语义路由器 ,其实也是一种数据工程的混合架构了...
举个例子:
患者描述“心口疼”被向量匹配到《心肌梗死护理指南》(语义相似度高) 实际病因却是胃食管反流(需关联“饭后平躺加重”等非直接相似特征) 如果这里只将向量库作为语义路由的话,情况会有所变化:
A[患者描述:“饭后心口灼烧样疼”] --> B(向量症状路由器) B --> C[“灼烧感”聚类:胃酸反流症状组] C --> D{知识图谱路由} D --> E1[疾病库:胃食管反流病] D --> E2[疾病库:心绞痛] E1 --> F[关联“体位诱发”特征:阳性] E2 --> F[关联“运动诱发”特征:阴性] F --> G[确诊:胃食管反流病]在这个案例里面,向量层仅完成症状语义聚类(将“心口疼”映射到“胸痛症状组”) ,其余工作交给知识图谱(或者结构化的知识库)。
该框架的实现框架为二:语义向量库以及结构化的知识库 :
向量库 (语义路由器) :专注于理解用户意图的自然语言表达,将其映射到预先定义好的、结构化的关键概念、类别或索引键上。它的核心能力是“理解用户问的是什么(领域/主题/意图)”。结构化知识库 (知识载体) :存储经过精心组织、清洗、关联的领域知识。形式可以是知识图谱、关系数据库、文档数据库(包含强元数据)、甚至规则库。它的核心能力是“精准、高效、结构化地回答基于关键键的查询”。他核心目标其实是要解决语义相似 ≠ 答案相关 的问题,向量路由只负责理解意图并将其路由到最相关的“知识抽屉”(如“胸痛症状组”、“拿铁配方库”),而非直接返回可能包含干扰信息的原始文本片段。
后续由结构化的知识库基于精确的键进行查询,确保返回的信息与查询目标高度一致。
这是一种更贴近人类认知的做法,人类在解答问题时,也是先理解问题意图(路由),然后在结构化的知识体系(记忆、手册、数据库)中查找相关信息,最后进行推理和表达(生成)。
只不过,该架构的挑战也很明显:其结构化的知识库十分难设计 ,并且要考虑其如何与路由向量库交互,整体工程难度是很高的。最后给一个流程图:
用户Query └─► 语义路由层(多信号融合) ├─ 向量近邻召回(症状/意图/主题簇) ├─ 关键词/BM25/正则/实体识别 └─ 轻量规则(黑白名单、领域优先级) │ ▼ 路由决策(Top-N 目标:{实体、关系、表、规则集、API}) │ ▼ 知识载体层(结构化) ├─ 知识图谱(实体-关系-约束) ├─ 事实表/维度表(指标、版本、地域) ├─ 规则引擎(阈值、if-then、时效) └─ 特征/检验库(医疗、法务、品控) │ ▼ 生成层(可选) ├─ 模板化口径(严谨场景:医疗、法务) └─ LLM 语言润色(附引证与可追溯ID)