大模型学习(二)-RAG、LangChain

文章目录

视频链接: 黑马程序员大模型RAG与Agent智能体项目实战教程,基于主流的LangChain技术从大模型提示词到实战项目

RAG

RAG介绍

通⽤的基础⼤模型存在一些问题:

  • LLM的知识不是实时的,模型训练好后不具备自动更新知识的能力,会导致部分信息滞后
  • LLM领域知识是缺乏的,大模型的知识来源于训练数据,这些数据主要来自公开的互联网和开源数据集,无法覆盖特定领域或高度专业化的内部知识
  • 幻觉问题,LLM有时会在回答中⽣成看似合理但实际上是错误的信息
  • 数据安全性

RAG(Retrieval-Augmented Generation)即检索增强生成,为大模型提供了从特定数据源检索到的信息,以此来修正和补充生成的答案。可以总结为一个公式:RAG = 检索技术 + LLM 提示

RAG工作原理

工作流程图解

RAG标准流程

简单来说,RAG工作分为两条线:

离线准备线 / 在线服务线

RAG标准流程

RAG 标准流程由索引(Indexing)、检索(Retriever)和生成(Generation)三个核心阶段组成。

  • 索引阶段,通过处理多种来源多种格式的文档提取其中文本,将其切分为标准长度的文本块(chunk),并进行嵌入向量化(embedding),向量存储在向量数据库(vector database)中。
    • 加载文件
    • 内容提取
    • 文本分割 ,形成chunk
    • 文本向量化
    • 存向量数据库

检索阶段,用户输入的查询(query)被转化为向量表示,通过相似度匹配从向量数据库中检索出最相关的文本块。

  • query向量化
  • 在文本向量中匹配出与问句向量相似的top_k个

生成阶段,检索到的相关文本与原始查询共同构成提示词(Prompt),输入大语言模型(LLM),生成精确且具备上下文关联的回答。

  • 匹配出的文本作为上下文和问题一起添加到prompt中
  • 提交给LLM生成答案:

总结

模型本质上就是用户输入,模型给出输出,用户能做的就是在输入上做功夫。

RAG就是在向模型提问之前基于已有的知识库或文档内容做检索,确保向模型提问的内容更精准以及包含足够的信息量用以提供给模型。

RAG的核心工作是2个流程:

RAG的核心价值:

  • 解决知识实效性问题:大模型的训练数据有截止时间,RAG 可以接入最新文档(如公司财报、政策文件),让模型输出 "与时俱进"。
  • 降低模型幻觉:模型的回答基于检索到的事实性资料,而非纯靠自身记忆,大幅减少编造信息的概率。
  • 无需重新训练模型:相比微调(Fine-tuning),RAG 只需更新知识库,成本更低、效率更高。

向量

向量的基础概念

RAG流程中,向量库是一个重要的节点。

  • 离线流程:知识和信息 -> 向量嵌入(向量化) -> 存入向量库
  • 在线流程:用户的提问 -> 向量嵌入(向量化) -> 在向量库中匹配


向量 (Vector)就是文本的 "数学身份证":它把一段文字的语义信息,转换成一串固定长度的数字列表,让计算机能 "看懂" 文字的含义并做相似度计算。

简单来说,就是让计算机更方便的理解不同的文本内容,是否表述的是一个意思。

文本嵌入模型(如text-embedding-v1)通过深度学习等技术,从文本提取语义特征并映射为固定长度的数字序列。

向量嵌入的过程,我们一般选用合适的文本嵌入模型来完成。

在向量匹配的过程中,如何识别2段文本是否表述相似的含义,主要可以通过如余弦相似度等算法来完成。

比如(下列案例中向量为示例,仅描述概念,非真实向量):

  • A: "如何快速学打篮球" -> [0.2, 0.5, 0.8]
  • B: "打篮球怎么学得快" -> [0.18, 0.52, 0.79]
  • C: "运动后吃什么好呢" -> [0.9, 0.1, 0.2]

通过余弦相似度算法可以计算得到:A和B相似度0.999789,A和C相似度0.361446

由此可通过精确的数学计算,去匹配2段文本是否描述同一个意思,提高语义匹配的效率和精度。

如何更为精准的完成语义匹配,生成向量的维度是一个很重要的指标。

如text-embedding-v1模型,可以生成1536维的向量(一段文本固定得到1536个数字序列),比较实用。

  • 1536个数字表示,这段文本在1536个主题(抽象的语义特征)方向上的得分(强度)
  • 生成向量的维度越多,就更好的记录文本的语义特征,做语义匹配会更加精准。
  • 更多的向量会在计算、存储和匹配过程中,带来更大的压力。

选择合适的向量维度需要在精确和性能之间做平衡。

一般1536维算是比较好的选择。

总结

向量(Vector)就是文本的 "数学身份证"

它把一段文字的语义信息,转换成一串固定长度的数字列表,让计算机能 "看懂" 文字的含义并做相似度计算。

  • 向量的计算(文本嵌入过程),可借助文本嵌入模型实现,如text-embedding-v1
  • 向量的匹配通过算法实现,如余弦相似度
  • 向量的维度表示一段文本在多个抽象语义特征方面的强度
    • 维度数代表模型用多少个抽象语义特征来描述文本
    • 维度越多,做语义匹配越精准
    • 但性能压力也会增大

余弦相似度

向量的数字序列,共同决定了向量在高维空间中的方向和长度.而余弦相似度主要就是撇除长度的影响,得到方向的夹角。夹角越小越相似,即方向相同。

如何体现向量的方向和长度呢?以一维向量为例:


余弦相似度主要匹配的就是:同向(无所谓长度)

我们能直接发现

  • 0.5, 0.5\]和\[0.7, 0.7\]是同向不同长

余弦相似度计算规则

在文本向量语义匹配中,余弦相似度是衡量两个向量方向相似程度的核心算法,即判断两段文本语义是否相近。

余弦相似度 = 两个向量的点积 ÷ 两个向量模长的乘积

以:A[0.5, 0.5]、B[0.7, 0.7]、C[0.7, 0.5]、D[-0.6, -0.5]为例

  • 点积:两个向量在同维度的乘积之和。
    • 向量AB点积:vec_a[0]×vec_b[0] + vec_a[1]×vec_b[1] + ... + vec_a[n]×vec_b[n]
    • 如AB的点积是:0.5 * 0.7 + 0.5 * 0.7 = 0.74
  • 模长:单个向量不同维度的平方之和开根号,如A的模长是:√(0.5 * 0.5 + 0.5 * 0.5)
    • 向量模长: ||vec|| = √(vec[0]² + vec[1]² + ... + vec[n]²)
    • 如向量A的模长: √(0.5 * 0.5 + 0.5 * 0.5) √ 是开根号

如AB之间的余弦相似度为: AB点积 ÷ (A模长 * B模长)

  • AB余弦相似度:(0.5 * 0.7 + 0.5 * 0.7) ÷ ( √(0.5 * 0.5 + 0.5 * 0.5) * √(0.7 * 0.7 + 0.7 * 0.7) ) = 1.0
  • AC 余弦相似度:(0.50.7 + 0.50.5) ÷ ( √(0.50.5 + 0.50.5) * √(0.70.7 + 0.50.5) ) ≈ 0.986
  • AD 余弦相似度:(0.5 * (-0.6) + 0.5 * (-0.5)) ÷ ( √(0.50.5 + 0.50.5) * √((-0.6) * (-0.6) + (-0.5) * (-0.5)) ) ≈ -0.996

LangChain

LangChain简介

LangChain 由 Harrison Chase 创建于2022年10月,它是围绕LLMs(大语言模型)建立的一个框架。

LangChain自身并不开发LLMs,它的核心理念是为各种LLMs实现通用的接口,把LLMs相关的组件"链接"在一起,简化LLMs应用的开发难度,方便开发者快速地开发复杂的LLMs应用。

LangChain的主要功能
总结

LangChain是一个开发LLM相关业务功能的集大成者,是一个Python的第三方库,提供了各种功能的API。

提供:

  • 提示词优化的相关功能API
  • 调用各类模型的功能API
  • 会话记忆的相关功能API
  • 各类文档管理分析的功能API
  • 构建Agent智能体的相关功能API
  • 各类功能链式执行的能力

LangChain是后续学习RAG开发的主力框架

Full LLM power---you only need LangChain

LangChain部署

LangChain安装

命令:

python 复制代码
pip install langchain langchain-community langchain-ollama langchain-chroma dashscope chromadb bs4 jq
  • langchain:核心包
  • langchain-community:社区支持包,提供了更多的第三方模型调用(我们用的阿里云千问模型就需要这个包)
  • langchain-ollama:Ollama支持包,支持调用Ollama托管部署的本地模型
  • langchain-chroma:ChromaDB支持包,支持调用ChromaDB
  • dashscope:阿里云通义千问的Python SDK
  • chromadb:轻量向量数据库(后续使用)
  • bs4:BeautifulSqop4库,协助解析HTML文档(后续学习文档加载器使用)

LangChain组件:Models大语言模型

Models

现在市面上的模型多如牛毛,各种各样的模型不断出现,LangChain模型组件提供了与各种模型的集成,并为所有模型提供一个精简的统一接口。

LangChain目前支持三种类型的模型:LLMs(大语言模型)、Chat Models(聊天模型)、Embeddings Models(嵌入模型).

  • LLMs:是技术范畴的统称,指基于大参数量、海量文本训练的 Transformer 架构模型,核心能力是理解和生成自然语言,主要服务于文本生成场景
  • 聊天模型:是应用范畴的细分,是专为对话场景优化的 LLMs,核心能力是模拟人类对话的轮次交互,主要服务于聊天场景
  • 文本嵌入模型: 文本嵌入模型接收文本作为输入, 得到文本的向量.

LangChain支持的三类模型,它们的使用场景不同,输入和输出不同,开发者需要根据项目需要选择相应。

我们所用的阿里云通义千问系列主要来自于:langchain_community包

LLMs

LLMs使用场景最多,常用大模型的下载库:

https://huggingface.co/models

https://modelscope.cn/models

同时LangChain支持对许多模型的调用,以通义千问为例:

python 复制代码
import os

from langchain_community.llms.tongyi import Tongyi


model = Tongyi(model='qwen-max', api_key=os.getenv('DASHSCOPE_API_KEY'))

#调用invok
# res = model.invoke(input = "你是谁呀,可以做什么")
res = model.stream(input = "你是谁呀,可以做什么")

for chunk in res:
    print(chunk, end='', flush=True)

本地访问:

如果要访问本地Ollama的模型,简单更改一下代码。

通过langchain_ollama包导入OllamaLLM类即可(请确保Ollama已经启动并提前下载好要使用的模型)。

python 复制代码
from langchain_ollama import OllamaLLM
model = OllamaLLM(model="qwen3:4b") # 通过invoke方法去调用模型
res = model.invoke(input="你是谁呀能做什么?")
print(res)
总结

通过:

from langchain_community.llm.tongyi import Tongyi 导入通义千问系列的支持

from langchain_ollama import OllamaLLM 导入Ollama系列的支持

创建好模型对象后,通过invoke对模型发起提问

并可以直接打印输出结果

LangChain组件:Models的流式输出

如果需要流式输出结果,需要将模型的invoke方法改为stream方法即可。

  • invoke方法:一次型返回完整结果
  • stream方法:逐段返回结果,流式输出
python 复制代码
import os

from langchain_community.llms.tongyi import Tongyi


model = Tongyi(model='qwen-max', api_key=os.getenv('DASHSCOPE_API_KEY'))

#调用invok
# res = model.invoke(input = "你是谁呀,可以做什么")
res = model.stream(input = "你是谁呀,可以做什么")

for chunk in res:
    print(chunk, end='', flush=True)
总结

模型对象有2个方法去调用模型:

  • invoke,调用模型,一次型返回完整结果
  • stream,调用模型,逐段流式输出

这两个方法是新版LangChain(1.0版本后)中基于Runnable接口的通用核心方法。

绝大多数组件(如提示词模板、链、向量检索、工具调用等,后续学习)都支持这两个方法,这也是 LangChain 设计的核心统一范式。

LangChain组件:models聊天模型的使用

聊天消息包含下面几种类型,使用时需要按照约定传入合适的值:

  • AIMessage: 就是 AI 输出的消息,可以是针对问题的回答. (OpenAI库中的assistant角色)
  • HumanMessage: 人类消息就是用户信息,由人给出的信息发送给LLMs的提示信息,比如"实现一个快速排序方法". (OpenAI库中的user角色)
  • SystemMessage: 可以用于指定模型具体所处的环境和背景,如角色扮演等。你可以在这里给出具体的指示,比如"作为一个代码专家",或者"返回json格式". (OpenAI库中的system角色)
python 复制代码
import os

from langchain_community.chat_models.tongyi import ChatTongyi
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage

#得到模型对象
model = ChatTongyi(model='qwen3-max', api_key=os.getenv('DASHSCOPE_API_KEY'))

messages = [
    SystemMessage(content='你是一个边塞诗人'),
    HumanMessage(content='写一首唐诗'),
    AIMessage(content='锄禾日当午,汗滴禾下土。谁之盘中餐,粒粒皆辛苦。'),
    HumanMessage(content='按照你回答的上一个格式,再写一首唐诗')
]

#流式执行
res = model.stream(input=messages)

for chunk in res:
    print(chunk.content, end='', flush=True)

LangChain组件:models消息的简写形式

python 复制代码
import os

from langchain_community.chat_models.tongyi import ChatTongyi

#得到模型对象
model = ChatTongyi(model='qwen3-max', api_key=os.getenv('DASHSCOPE_API_KEY'))

messages = [
    ('system','你是一个边塞诗人'),
    ('human','写一首唐诗'),
    ('ai','锄禾日当午,汗滴禾下土。谁之盘中餐,粒粒皆辛苦。'),
    ('human','按照你回答的上一个格式,再写一首唐诗')
]

#流式执行
res = model.stream(input=messages)

for chunk in res:
    print(chunk.content, end='', flush=True)

通过2元元组封装信息;

  • 第一个元素为角色

    • 字符串:system/human/ai
  • 第二个元素为内容

简写的优势

区别和优势在于,使用类对象的方式,如下:

python 复制代码
messages = [
	SystemMessage(content="内容..."),
	HumanMessage(content "内容..."),
	AIMessage(content= "内容..."),]

是静态的,一步到位直接就得到了Message类的类对象

简写形式如下:

python 复制代码
messages = [
	("system", "内容..."),
	("human", "内容..."),
	("ai", "内容..."),]

是动态的,需要在运行时由LangChain内部机制转换为Message类对象

好处就在于,简写形式避免导包、写起来更简单,更重要的是支持:

python 复制代码
messages = [
 ("system", "今天的天气是{weather}"),
 ("human", "我的名字是:{name}"),
 ("ai", "欢迎{lastname}先生"),]

由于是动态,需要转换步骤

所以简写形式支持内部填充{变量}占位,可在运行时填充具体值

LangChain组件:Models:嵌入模型的使用

Embeddings Models(文本嵌入模型)

Embeddings Models嵌入模型的特点:将字符串作为输入,返回一个浮点数的列表(向量)。

在NLP中,Embedding的作用就是将数据进行文本向量化。

阿里云千问模型访问方式:

python 复制代码
from langchain_community.embeddings import DashScopeEmbeddings 
# 初始化嵌入模型对象,其默认使用模型是:text-embedding-v1
embed = DashScopeEmbeddings()
# 测试print(embed.embed_query("我喜欢你"))
print(embed.embed_documents(['我喜欢你', '我稀饭你', '晚上吃啥'])) 
模型使用总结

目前所掌握的LangChain API如下:

总结

LangChain在模型的支持上主要基于LangChain_community包提供。

主要支持三类模型:

  • LLMs:大语言模型,主用于文本生成
  • Chat Model:聊天模型,主用于多轮次对话的聊天场景
  • Embeddings Model:文本嵌入模型,主用于生成文本向量

LangChain框架和OpenAI库一样,提供三种角色:

  • HumanMessage类,即User角色
  • AIMessage类,即Assistant角色
  • SystemMessage类,即System角色

LangChain组件:通用Prompts

通用prompt(zero-shot)

提示词优化在模型应用中非常重要,LangChain提供了PromptTemplate类,用来协助优化提示词。

PromptTemplate表示提示词模板,可以构建一个自定义的基础提示词模板,支持变量的注入,最终生成所需的提示词。

python 复制代码
import os

from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI


model = ChatOpenAI(
    api_key='sk-xxx',
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    model="qwen3.5-plus",
    temperature=0.7,
    max_tokens=1000,
    timeout=30
)

prompt_template = PromptTemplate.from_template(
    "我的邻居姓:{lastname},刚生了{gender},你帮我起个名字,简单回答"
)


# prompt_text = prompt_template.format(lastname = '张', gender = '女儿')
# print(prompt_text)



# res = model.invoke(input=prompt_text)
#
# print(res.content)

#构建执行链条
chain = prompt_template | model

res = chain.invoke(input={'lastname':'张', 'gender':"女儿"})
print(res.content)
总结

基于PromptTemplate类可以得到提示词模板,支持基于模板注入变量得到最终提示词。

  • zero-shot思想下,可以基于PromptTemplate直接完成。
  • few-shot思想下,需要更换为FewShotPromptTemplate(后续学习)

PS:使用PromptTemplate还不如自己手动拼接字符串?

  • 使用Template模板构建提示词,在大型工程中更容易做标准化模板
  • Template模板类,支持LangChian框架的链式调用(Runnable接口,后续学习)
    • PromptTemplate
    • FewShotPromptTemplate(后续学习)
    • ChatPromptTemplate(后续学习)

LangChain组件:FewShotPromptTemplate的使用

python 复制代码
from langchain_core.prompts import FewShotPromptTemplate
FewShotPromptTemplate(
	examples=None,
	example_prompt=None,
	prefix=None,
	suffix=None,
	input_variables=None
)

参数:

  • examples:示例数据,list,内套字典
  • example_prompt:示例数据的提示词模板
  • prefix:组装提示词,示例数据前内容
  • suffix:组装提示词,示例数据后内容
  • input_variables:列表,注入的变量列表
组装FewShotPromptTemplate对象并获得最终提示词,并调用模型
python 复制代码
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate

from langchain_openai import ChatOpenAI

model = ChatOpenAI(
    api_key='sk-xxx',
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    model="qwen3.5-plus"
)

example_template = PromptTemplate.from_template("单词:{word},反义词:{antonym}")

example_data = [
    {"word":"大", "antonym":"小"},
    {"word":"上", "antonym":"下"}
]

few_short_prompt = FewShotPromptTemplate(
    example_prompt=example_template,                    #示例数据模板
    examples=example_data,                              #示例数据(用来注入动态数据),list内 存字典
    prefix="给出给定词的反义词,有如下示例:",             #示例之前的提示词
    suffix="基于示例告诉我,{input_word}的反义词是?",     #示例之后的提示词
    input_variables=["input_word"]                      #生命再前缀或者后缀中需要注入的变量名
)

prompt_text= few_short_prompt.invoke(input={"input_word":"左"}).to_string()
print(prompt_text)

chain = few_short_prompt | model

res = chain.invoke(input={"input_word":"左"})
print(res.content)

LangChain组件:模板类的format和invoke方法

模板类的format和invoke方法

在PromptTemplate(通用提示词模板)和FewShotPromptTemplate(FewShot提示词模板)的使用中,我们使用了如下:

PromptTemplate、FewShotPromptTemplate、ChatPromptTemplate(后续学习)都拥有format和invoke这2类方法。

format和invoke的区别

LangChain组件:ChatPromptTemplate的使用

PromptTemplate:通用提示词模板,支持动态注入信息。

FewShotPromptTemplate:支持基于模板注入任意数量的示例信息。

ChatPromptTemplate:支持注入任意数量的历史会话信息。

通过from_messages方法,从列表中获取多轮次会话作为聊天的基础模板

PS: 前面PromptTemplate类用的from_template仅能接入一条消息,而from_messages可以接入一个list的消息

ChatPromptTemplate

历史会话信息并不是静态的(固定的),而是随着对话的进行不停地积攒,即动态的。

所以,历史会话信息需要支持动态注入。

python 复制代码
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_openai import ChatOpenAI


chat_prompt = ChatPromptTemplate.from_messages(
    [
        ("system","你是一个边塞诗人"),
        MessagesPlaceholder("history"),
        ("human","请再来一首唐诗")
    ]
)

history_data = [
        ("human","你来写一首唐诗"),
        ("ai","锄禾日当午,汗滴禾下土。谁之盘中餐,粒粒皆辛苦。"),
        ("system","你是一个边塞诗人"),
        ("ai","你是一个边塞诗人")
]

prompt_text = chat_prompt.invoke({"history":history_data}).to_string()

# print(prompt_text)

model = ChatOpenAI(
    api_key='sk-xxx',
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    model="qwen3.5-plus"
)

res = model.invoke(prompt_text)

print(res.content)
  • MessagePlaceholder作为占位
    • 提供history作为占位的key
  • 基于invoke动态注入历史会话记录

必须是invoke,format无法注入

LangChain组件:chains 链的基础使用

chain链

「将组件串联,上一个组件的输出作为下一个组件的输入」是 LangChain 链(尤其是 | 管道链)的核心工作原理,这也是链式调用的核心价值:实现数据的自动化流转与组件的协同工作,如下。

python 复制代码
chain = prompt_template | model

核心前提:即Runnable子类对象才能入链(以及Callable、Mapping接口子类对象也可加入(后续了解用的不多))。

我们目前所学习到的组件,均是Runnable接口的子类,如下类的继承关系:

python 复制代码
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import RunnableSerializable
from langchain_openai import ChatOpenAI


chat_prompt = ChatPromptTemplate.from_messages(
    [
        ("system","你是一个边塞诗人"),
        MessagesPlaceholder("history"),
        ("human","请再来一首唐诗")
    ]
)

history_data = [
        ("human","你来写一首唐诗"),
        ("ai","锄禾日当午,汗滴禾下土。谁之盘中餐,粒粒皆辛苦。"),
        ("system","你是一个边塞诗人"),
        ("ai","你是一个边塞诗人")
]

#prompt_text = chat_prompt.invoke({"history":history_data}).to_string()

# print(prompt_text)

model = ChatOpenAI(
    api_key='sk-xxx',
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    model="qwen3.5-flash"
)

chain: RunnableSerializable = chat_prompt | model

# res = chain.stream({"history":history_data})
#
# for chunk in res:
#     print(chunk.content, end="", flush=True)

res = chain.invoke({"history":history_data})
print(res.content)
  • 通过|链接提示词模板对象和模型对象
  • 返回值chain对象是RunnableSerializable对象
    • 是Runnable接口的直接子类
    • 也是绝大多数组件的父类
    • 通过invoke或stream进行阻塞执行或流式执行

组成的链在执行上有:上一个组件的输出作为下一个组件的输入的特性。

所以有如下执行流程:

相关推荐
诗酒当趁年华2 小时前
langchain核心组件1-智能体
数据库·langchain
2401_865721332 小时前
WEB 学习框架搭建
网络·学习·web
lifewange2 小时前
删除学习“叶平”老师课的sc表记录
学习
健康人猿3 小时前
SuperGrok Lite 是啥?值不值得升级?与旗舰版的差距有多大?
人工智能·学习·ai
路小雨~3 小时前
Milvus 向量数据库的官方文档笔记
数据库·学习·milvus
biuyyyxxx3 小时前
Power Query功能区 - 主页
笔记·学习·excel
今儿敲了吗3 小时前
DS-3 循环队列判断队满
数据结构·笔记·学习
m0_651562523 小时前
2026.3.23搭建AI算法可视化部署演示学习笔记
笔记·学习
renhongxia13 小时前
学习如何聚合零样本大型语言模型代理以进行企业披露分类
学习·语言模型·分类
im_AMBER3 小时前
Leetcode 147 零钱兑换 | 单词拆分
javascript·学习·算法·leetcode·动态规划