做教学类工作的同学需要留意一个现象:知识诅咒——知者不难,难者不会。从最近训练营的反馈来看,这个问题就很有代表性:学员们常常对一些关键概念感到混淆,特别是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) → 拼上下文 → 生成 → 校对/引用 → 评测与回流。
RAG概述
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:"酸度 或 酸味 或 明亮酸质 的 咖啡豆 品种"
向量化的意义
最后再展开说一下。大家可能已经注意到:RAG技术其实并不必须依赖向量库。只要能实现知识搜索,不一定非要经过向量化这一步。
RAG技术在两年前之所以被广泛接受,主要有两个原因:
- 第一,当时模型上下文太短,4k、8k、16k是主流(32k都一票难求)。在这个基础上,就算向量库好用,受限于提示词长度,实际体验也大打折扣。
- 第二,受限于AI项目认知,大家并不清楚如何组织私有化数据。RAG提供了一变钱成的范式,自然而然就用上了,至于好不好,那是后来才考虑的事。
基于这一点,可以说早期的RAG实际体验并不理想。模型上下文窗口装不下完整知识库,知识库不全,回答质量自然受影响。在当时的场景下,根本不可能把全量数据塞给模型。将知识切分成小片段、进行精炼压缩以减少长度,本质上是一种用精度换准度的妥协。
后来模型技术持续发展,上下文窗口扩展到足够大,RAG反而变得好用了。这也印证了前面提到的观点:RAG在当时是微调的一种替代方案。
不过,在上下文能力已经相当强的今天,另一个问题又浮现了:向量化的意义似乎没有想象中那么大了。比如在知识库规模不大时,全量导入反而可能是更优解。想象一下,直接把整本《咖啡百科全书》PDF的文本内容(经过必要的清洗和格式优化后)塞进提示词,好像也没什么问题,毕竟也就几万字。
所以,需要重新思考的是:向量库在模型早期阶段不好用,到了后期又用不太着,真正重要的或许一直是结构化的知识库。
进一步的思考
前面提到,向量库的核心目标只有一个:把用户提问中涉及本地知识的部分准确找出来。从这个角度看,向量检索所做的工作和LLM有些类似,甚至可以粗略地把它看作一个"预训练过的小模型"。
但向量化毕竟不是模型训练,无论分块策略怎么优化,向量检索始终面临一个瓶颈:用固定维度的向量去表示无限可能的语义。而且,它的优化目标(语义相似性)和下游任务目标(答案精准性)之间存在一条天然鸿沟,比如:
# 语义相似高 ≠ 答案支持性强
Query = "酸味明亮的咖啡豆推荐?"
Doc1 = "耶加雪菲:柑橘酸感突出(产地埃塞)" # 高相关!但向量相似度可能被弱化
Doc2 = "咖啡酸味的化学成因:绿原酸分解" # 高相似!但无法直接回答"推荐"
因为根本无法预判用户会提什么样的问题,所以一定会出现检索不到知识的场景——这可能是RAG目前最大的挑战。
需要说明的是,这并不是要否定向量化,而是在思考如何更巧妙地使用它。以实际项目中的一次尝试为例:我们设计了一套结构化的知识库,比如知识图谱。然后,将向量索引的使用范围压缩到关键Key的筛选上。比如,结构化的知识库里存的是完善的疾病信息,而向量库中只保留症状与疾病的映射关系。检索时,系统只需要判断用户的描述对应什么症状,然后从症状向量中找出可能关联的疾病,最后直接调用结构化的疾病库即可。
知识载体-语义路由
这里提到的"将向量索引仅用于关键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)
