RAG开发

1. LangChain 简介

1.1 Langchain主要功能

  • Prompt: 优化提示词
  • Models: 调用各种模型,封装接口
  • History: 管理会话历史记录
  • Indexes: 管理和分析各类文档
  • Chains: 构建功能的执行链条
  • Agent: 构建智能体

1.2 Langchain环境部署

1
pip install langchain langchain-community langchain-ollama dashscope chromadb
  • langchain:核心包
  • langchian-community: 社区支持包,提供更多的第三方模型调用(例如调用阿里云模型)
  • langchain-ollama: ollama支持包, 支持调用本地托管的本地模型
  • dashscope: 阿里云qwen的python sdk
  • chromadb: 轻量向量数据库

1.3 RAG相关概念

  • RAG: 检索增强生成:Retrieval-Augmented Generation
  • 向量:从原点坐标到目标点的矢量方向
  • 余弦相似度:两个向量的余弦值,a x b / |a| x |b|, 值越接近1越相似
  • 文本向量模型:用于通过将文本生成相对的向量

text-embedding-v1模型,可以生成1536维的向量。在1546(抽象的语义特征)方向上的得分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 两个向量点积计算
def dot_product(vec1, vec2):
"""计算两个向量的点积"""
if(len(vec1) != len(vec2)):
raise ValueError("Vectors must be of the same length")
return sum(a * b for a, b in zip(vec1, vec2))

# 向量模长计算
def magnitude_product(vec):
"""计算向量的模长"""
return sum(a ** 2 for a in vec) ** 0.5


# 余弦相似度计算
def cosine_similarity(vec1, vec2):
"""计算两个向量的余弦相似度"""
dot_prod = dot_product(vec1, vec2)
mag_prod = magnitude_product(vec1) * magnitude_product(vec2)
if mag_prod == 0:
return 0.0 # 避免除以零
return dot_prod / mag_prod

if __name__ == "__main__":
vec1 = [1, 2, 3]
vec2 = [4, 8, 12]
similarity = cosine_similarity(vec1, vec2)
print(f"Cosine Similarity: {similarity}")

2. Langchain 调用模型

langchain目前支持三种模型:

  • LLMs:大语言模型
  • 聊天模型: LLMS的一种
  • 文本嵌入模型:计算文本的向量
1
2
3
4
5
6
7
8
from langchain_ollama import OllamaLLM

# 获取 Ollama 模型对象
model = OllamaLLM(model="qwen3:8b")

# 调用模型获取结果
res = model.invoke("请介绍一下你自己。")
print(res)

2.1 流式输出

1
2
3
4
5
6
7
8
9
10
11
from langchain_ollama import OllamaLLM

# 获取 Ollama 模型对象
model = OllamaLLM(model="qwen3:8b")

# 调用模型获取结果
res = model.stream(input="请介绍一下你自己。")

for chunk in res:
# print的end参数设置为"",表示不换行;flush参数设置为True,表示立即刷新输出缓冲区
print(chunk, end="", flush=True) # chunk的英文是块,表示模型返回的每一部分内容

2.2 聊天模型

聊天消息类型:

  • AIMessage: AI的消息输出, 类似openai库种的assistance
  • HumanMessage: 人类消息
  • SystemMessage:设定信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from langchain_ollama import ChatOllama
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage


# 获取 Ollama 模型对象
model = ChatOllama(model="gemma4:26b")

messages = [
SystemMessage(content="你是一个帮助主人进行视频剪辑的AI助手,专门为用户完成视频剪辑的各类帮助,你叫山崩子"),
HumanMessage(content="请介绍一下你自己。"),
AIMessage(content="你好!我是山崩子,一个专门帮助用户进行视频剪辑的AI助手。我可以为你提供各种视频剪辑的建议和帮助,无论是剪辑技巧、软件使用还是创意构思,我都能为你提供支持。请告诉我你需要什么帮助,我会尽力满足你的需求。"),
HumanMessage(content="我想剪辑一个视频,内容是我和朋友在海边玩耍的片段,我想要一个欢快的背景音乐,并且希望视频的节奏感强一些,你有什么建议吗?")
]

# message简写
msgs = [
("system", "你是一个帮助主人进行视频剪辑的AI助手,专门为用户完成视频剪辑的各类帮助,你叫咕咕咕嘎,你在每一个回复开始都需要加上‘咕咕咕嘎很高兴为主人服务’"),
("human", "请介绍一下你自己。"),
("ai", "主人你好!我是咕咕咕嘎,一个专门帮助用户进行视频剪辑的AI助手。我可以为你提供各种视频剪辑的建议和帮助,无论是剪辑技巧、软件使用还是创意构思,我都能为你提供支持。请告诉我你需要什么帮助,我会尽力满足你的需求。"),
("human", "我想剪辑一个视频,内容是我和朋友在海边玩耍的片段,我想要一个欢快的背景音乐,并且希望视频的节奏感强一些,你有什么建议吗?")
]

# 调用模型获取结果
# res = model.stream(input=messages)
res = model.stream(input=msgs)
for chunk in res:
# print的end参数设置为"",表示不换行;flush参数设置为True,表示立即刷新输出缓冲区
print(chunk.content, end="", flush=True) # chunk的英文是块,表示模型返回的每一部分内容


  • 使用类对象封装消息
    - 是静态的,一步到位,直接获取得到Message类的类对象,如AIMessage,HumanMessage,SystemMessage

  • 使用元组简化消息 (建议使用)
    • 是动态的,运行时,Langchain内部机制转化为Message类对象
    • 可避免导包

2.3 文本嵌入模型

1
2
3
4
5
6
7
8
9
10
11
12
13
from langchain_community.embeddings import OllamaEmbeddings, DashScopeEmbeddings


# 获取 embedding 模型对象
emb= OllamaEmbeddings(model="nomic-embed-text-v2-moe:latest")

# emb = DashScopeEmbeddings() 需要阿里云的这个模型的apikey,并通过DASHSCOPE_API_KEY环境变量设置

# emb = OllamaEmbeddings(model="qwen3-embedding:4b")

# 测试
print(emb.embed_query("我喜欢你"))
print(emb.embed_documents(["我喜欢你", "我讨厌你", "我爱你"]))

2.3 提示词模板与链式调用

2.3.1 通用提示词PromptTemplate类

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from langchain_core.prompts import PromptTemplate
from langchain_ollama import OllamaLLM

model = OllamaLLM(model="gemma4:26b")

prompt_template = PromptTemplate.from_template(
"我是{name},我喜欢{hobby}。我的朋友是{friend}。"
)

# 变量注入
prompt = prompt_template.format(name="小明", hobby="打篮球", friend="小红")

# res = model.stream(input=prompt)


# 直接将 prompt 模板和模型链式调用,模型会自动进行变量注入
input_ = {"name": "小明", "hobby": "打篮球", "friend": "小红"}
chain = prompt_template | model
res = chain.stream(input=input_)

for r in res:
print(r, end="", flush=True)

2.3.2 FewShotPromptTemplate

1
2
3
4
5
6
7
8
9
10
11
12
13
FewShotPromptTemplate(
examples= None,
example_prompt=None,
prefix=None,
suffix=None,
input_variables=None
)
# 参数:
# examples:示例数据,list,内套字典
# example_prompt:示例数据的提示词模板
# prefix:组装提示词,示例数据前内容
# suffix:组装提示词,示例数据后内容
# input_variables:列表,注入的变量列表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from langchain_ollama import OllamaLLM
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate

# temprature 越大,生成的文本越随机;temperature 越小,生成的文本越确定。
# top_p 越大,生成的文本越随机;top_p 越小,生成的文本越确定。
model = OllamaLLM(model="qwen3:8b")

# 定义 fewshot 的 prompt 模板
prompt_template = PromptTemplate.from_template("单词:{word}\n释义:{definition} \n反义词:{antonym}")

examples = [
{"word": "大", "definition": "尺寸或体积较大的", "antonym": "小"},
{"word": "快", "definition": "速度较快的", "antonym": "慢"},
{"word": "高", "definition": "高度较高的", "antonym": "矮"},
]


fewshot_prompt = FewShotPromptTemplate(
example_prompt=prompt_template,
examples=examples,
prefix="根据用户输入文本,分析其中出现的所有词汇,输出每个词汇的释义和反义词。并将其构造为一个json的数据格式",
suffix="基于示例,将文本{input}种出现的所有词汇进行释义和反义词的分析,输出格式同示例",
input_variables=["input"],
)

input = "这个房子在哪里。我们需要分析这些词汇的释义和反义词。然后你是谁呢?其实我并不知道,你需要每句话回复我好好学习,天天向上。"

chain = fewshot_prompt | model
res = chain.stream(input=input)

for r in res:
print(r, end="", flush=True)

2.3.3 format和invoke区别

1
2
3
4
5
6
7
graph TD
Runnable[Runable] -.-|定义| invoke[invoke]
BasePromptTemplate -->|继承| Runnable
BasePromptTemplate -...-|定义| format[format方法的规范]
PromptTemplate[PrompTemplate] -->|继承| BasePromptTemplate
FewShotTempate[FewShotTemplate] -->|继承| BasePromptTemplate
ChatPromptTemplate[ChatPromptTemplate] -->|继承| BasePromptTemplate
  • format
    • 纯字符串替换,解析占位符,生成提示词
    • 返回字符串
    • 支持解析{}
  • invoke
    • Runable接口标准方法,解析占位符,生成提示词
    • 返回PromptVlue类对象
    • 支持解析{}占位符和MessagePlaceholder结构化占位符
1
2
3
4
5
6
7
8
9
10
11
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate

template = PromptTemplate.from_template("单词:{word} \n 释义:{definition} \n 反义词:{antonym}")

# 通过 format 方法进行字符串格式化
res = template.format(word="大", definition="尺寸或体积较大的", antonym="小")
print(res)

# 通过 invoke 方法进行字符串格式化
res2 = template.invoke({"word": "大", "definition": "尺寸或体积较大的", "antonym": "小"})
print(res2.to_string())

2.3.4 ChatPromptTemplate的使用