AI的学习之路_4_LangChain
什么是LangChain
LangChain 是一个专为构建 AI 应用(尤其是基于大语言模型 LLM)设计的 Python/JavaScript 框架,你可以把它理解成:连接大语言模型(如 GPT、Qwen、Llama)与外部资源(数据、工具、API)的 “胶水工具”。
部署LangChain
打开PyCharm的终端执行下面的指令
pip install langchain langchain-community langchain-ollama dashscope chromadb -i https://pypi.tuna.tsinghua.edu.cn/simple
其中的包对应作用如下表
| 包名 | 核心作用 |
|---|---|
langchain |
LangChain 核心框架:提供LLM应用开发的基础模块(链、提示词、记忆等) |
langchain-community |
LangChain 社区扩展:包含第三方集成(如Ollama、ChromaDB、各类文档加载器) |
langchain-ollama |
专门对接 Ollama(你本地部署 Qwen 模型的工具)的官方集成包,简化本地LLM调用 |
dashscope |
阿里云灵积模型服务的Python SDK:用于调用阿里云的通义千问等模型 |
chromadb |
轻量级向量数据库:用于存储文本向量,实现“文档检索+LLM问答”(RAG场景核心) |
-i 清华镜像源 |
指定国内镜像源,解决官方源下载慢/超时问题,大幅提升安装速度 |
下载成功

什么是RAG
可以理解为让 LLM 先去 “查资料”,再基于查到的资料生成回答,而不是只靠自己训练时学到的知识。
RAG 主要分 3 步:
- 数据准备(入库)
把你的文档、PDF、网页等拆分成小片段(Chunk)。
用 embedding 模型把这些片段转成向量(数值化表示语义)。
把向量和原文一起存到向量数据库(比如 Chroma、FAISS、Pinecone)。 - 检索(查资料)
用户提问后,把问题也转成向量。
在向量数据库里找出和问题语义最相似的文档片段(相似度搜索)。
把这些片段作为 “参考资料”。 - 生成(写答案)
把问题 + 检索到的参考资料一起喂给 LLM。
LLM 只基于这些资料生成回答,避免胡编乱造(幻觉)。
输出最终答案,并可以标注引用来源。
向量
在RAG流程中,向量数据库是一个重要的节点
在 RAG 场景中,向量(Vector) 是把文字 / 图片等非结构化信息转换成的一串数字,核心作用是用数值表示语义 / 内容特征,让计算机能 “理解” 文字的含义并做相似度对比。
有兴趣可以了解向量维度工程 余弦匹配算法等
余弦相似度算法
余弦相似度,就是用来衡量两段文本、两张图片、两个向量 “像不像” 的算法。
余弦相似度通过计算两个向量之间的夹角余弦值来衡量它们的相似性。余弦值越接近1,夹角越小,说明两个向量越相似;余弦值越接近-1,夹角越大,说明两个向量越不相似;余弦值为0时,两个向量正交,表示它们之间没有相关性。
公式

其中:
是向量A和B的点积(dot product)
分别是向量A和B的模(magnitude,即长度)。
余弦相似度的值域在-1到1之间:
- 如果值为1,表示两个向量完全相同。
- 如果值为-1,表示两个向量方向完全相反。
- 如果值为0,表示两个向量正交(即,它们不共享任何相同的维度)。
通常,在文本处理中,余弦相似度值越接近1,两个文本就越相似。
值得注意的是,余弦相似度只考虑向量的方向,而不考虑其大小(即,向量的模)。因此,它对于具有不同长度的文本向量(即,包含不同数量词汇的文本)也能给出合理的相似性度量。
举个例子
U1: [5, 3, 0, 1, 4]
U2: [4, 2, 1, 0, 5]
A ⋅ B = 5*4 + 3*2 + 0*1 + 1*0 + 4*5 = 20 + 6 + 0 + 0 + 20 = 46
||A|| = √(5^2 + 3^2 + 0^2 + 1^2 + 4^2) = √(25 + 9 + 0 + 1 + 16) = √51
||B|| = √(4^2 + 2^2 + 1^2 + 0^2 + 5^2) = √(16 + 4 + 1 + 0 + 25) = √46
Cosine Similarity = (A ⋅ B) / (||A|| * ||B||) = 46 / (√51 * √46) ≈ 0.976
import numpy as np
def cosine_similarity(vec1, vec2):
"""
计算两个向量的余弦相似度
:param vec1: 第一个向量(列表/数组)
:param vec2: 第二个向量(列表/数组)
:return: 相似度值(0~1之间,越接近1越相似)
"""
# 转成numpy数组(方便计算)
vec1 = np.array(vec1)
vec2 = np.array(vec2)
# 核心计算:点积 / (模长1 × 模长2)
dot_product = np.dot(vec1, vec2) # 点积
norm1 = np.linalg.norm(vec1) # 向量1的模长
norm2 = np.linalg.norm(vec2) # 向量2的模长
# 避免除以0(防止空向量报错)
if norm1 == 0 or norm2 == 0:
return 0.0
return dot_product / (norm1 * norm2)
# ---------------------- 测试案例 ----------------------
if __name__ == "__main__":
# 模拟:"苹果手机"和"iPhone"的向量(简化版,实际是几百维)
vec_apple = [0.8, 0.2, 0.1]
vec_iphone = [0.78, 0.21, 0.12]
# 模拟:"香蕉"的向量
vec_banana = [0.05, 0.1, 0.9]
# 计算相似度
sim1 = cosine_similarity(vec_apple, vec_iphone)
sim2 = cosine_similarity(vec_apple, vec_banana)
# 打印结果(保留4位小数,更直观)
print(f"苹果手机 ↔ iPhone 的相似度:{sim1:.4f}") # 接近1,超相似
print(f"苹果手机 ↔ 香蕉 的相似度:{sim2:.4f}") # 接近0,完全不相似
手写算法
import numpy as np
def get_dot(vec_a, vec_b):
# 计算两个向量的点积
if len(vec_a) != len(vec_b):
return ValueError("Vectors do not match")
dot_sum = 0
for a, b in zip(vec_a, vec_b):
dot_sum += a*b
return dot_sum
def get_norm(vec):
# 计算单个向量模长
sum_square = 0
for v in vec:
sum_square += v**2
return np.sqrt(sum_square)
def cosine_similarity(vec_a, vec_b):
# 余弦相似度
return get_dot(vec_a, vec_b) / (get_norm(vec_a) * get_norm(vec_b))
if __name__ == '__main__':
A = [0.5, 0.5]
B = [0.7, 0.7]
C = [0.7, 0.5]
D = [-0.6, -0.5]
print(cosine_similarity(A, B))
print(cosine_similarity(A, C))
print(cosine_similarity(A, D))
print(cosine_similarity(B, C))
print(cosine_similarity(B, D))
LangChain组件的使用
主要支持LLM(大语言模型)、Chat Models(聊天模型)、Embeddings Models(嵌入模型)
- LLM 大语言模型:会写文章、会思考、会回答的 “大脑”
- Chat Models 聊天模型:专门用来聊天对话的 LLM
- Embeddings Model 嵌入模型:把文字变成向量的 “翻译官”
调用LLM
尝试调用QWen模型
from langchain_community.llms.tongyi import Tongyi
model = Tongyi(
model="qwen-max"
)
# invoke进行提问
res = model.invoke(input="你是谁,能做什么")
print(res)
代码是非常简单的
但是,model="qwen-max"提供的必须是LLM模型
因为导包的时候导入的是llms包里面的tongyi
尝试调用Ollama模型
from langchain_ollama import OllamaLLM
model = OllamaLLM(
model="qwen2.5:3b-instruct-q4_0"
)
# invoke进行提问
res = model.invoke(input="你是谁,能做什么")
print(res)

流式输出
from langchain_ollama import OllamaLLM
model = OllamaLLM(
model="qwen2.5:3b-instruct-q4_0"
)
# 使用stream进行提问
res = model.stream(input="你是谁,能做什么, 说的多一点,不低于500字")
for a in res:
print(a, end="", flush=True)

调用聊天模型
尝试调用QWen模型
from langchain_community.chat_models.tongyi import ChatTongyi
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
# 初始化模型
chat = ChatTongyi(
model="qwen-max"
)
# 准备消息列表
messages = [
HumanMessage(content="写一首诗") # 人类输入
]
res = chat.stream(messages)
for m in res:
print(m.content, end="", flush=True)

注意,聊天模型输出时要加content
不然输出的就是这样了

from langchain_community.chat_models.tongyi import ChatTongyi
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
# 初始化模型
chat = ChatTongyi(
model="qwen-max"
)
# 准备消息列表
messages = [
SystemMessage(content="你是一个唐代诗人,喜欢以拼音为m开头的文字作为一首诗的最后一个字"), # 可以理解为系统设定
HumanMessage(content="写一首诗") # 人类输入
]
res = chat.stream(messages)
for m in res:
print(m.content, end="", flush=True)
结果就很有趣

虽然诗句不通顺,但是很好的验证了SystemMessage的效果
from langchain_community.chat_models.tongyi import ChatTongyi
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
# 初始化模型
chat = ChatTongyi(
model="qwen-max"
)
# 准备消息列表
messages = [
SystemMessage(content="你是一个唐代诗人,喜欢以拼音为m开头的文字作为一首诗的最后一个字"), # 可以理解为系统设定
HumanMessage(content="写一首诗"), # 人类输入
AIMessage(content="桥头流水细") # 手动为AI回复的第一句
]
res = chat.stream(messages)
for m in res:
print(m.content, end="", flush=True)

看样子AI也是有原则的
所以 messages可以模仿历史对话记录来使用,少量样本学习也可以利用这种形式
尝试调用Ollama模型
from langchain_ollama import ChatOllama
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
# 初始化模型
chat = ChatOllama(
model="qwen2.5:3b-instruct-q4_0"
)
# 准备消息列表
messages = [
SystemMessage(content="你是一个唐代诗人,喜欢以拼音为m开头的文字作为一首诗的最后一个字"), # 可以理解为系统设定
HumanMessage(content="写一首诗"), # 人类输入
AIMessage(content="好的我将输出10句") # 手动为AI回复的第一句
]
res = chat.stream(messages)
for m in res:
print(m.content, end="", flush=True)

消息的简写形式
from langchain_ollama import ChatOllama
# 初始化模型
chat = ChatOllama(
model="qwen2.5:3b-instruct-q4_0"
)
# 准备消息列表
messages = [
# SystemMessage(content="你是一个唐代诗人,喜欢以拼音为m开头的文字作为一首诗的最后一个字"), # 可以理解为系统设定
# HumanMessage(content="写一首诗"), # 人类输入
# AIMessage(content="好的我将输出10句") # 手动为AI回复的第一句
("system", "你是一个唐代诗人,喜欢以拼音为m开头的文字作为一首诗的最后一个字"),
("human", "写一首诗"),
("ai", "好的我将输出10句")
]
res = chat.stream(messages)
for m in res:
print(m.content, end="", flush=True)
使用二元元组来代替原来创建对象
此时就不用导包了
但是是否简便,仁者见仁 智者见智了

这些内容是在langchain内部进行转换成对象的
同时,这种写法支持变量的注入,这就方便许多呐
调用嵌入模型
尝试调用QWen模型
from langchain_community.embeddings import DashScopeEmbeddings
model = DashScopeEmbeddings(
)
print(model.embed_query("我喜欢你")) # 文字转向量
print(model.embed_documents(["我喜欢你", "你喜欢我", "晚上吃什么"])) # 列表进行转换

结果是非常非常长的向量
没有指定模型会使用默认的,查看源码就知道了

尝试调用Ollama模型
from langchain_ollama import OllamaEmbeddings
model = OllamaEmbeddings(
model="***" # 这里自行输入,本地没有安装 就不演示了
)
print(model.embed_query("我喜欢你")) # 文字转向量
print(model.embed_documents(["我喜欢你", "你喜欢我", "晚上吃什么"])) # 列表进行转换
余弦相似度实际使用一下
import numpy as np
from langchain_community.embeddings import DashScopeEmbeddings
def get_dot(vec_a, vec_b):
# 计算两个向量的点积
if len(vec_a) != len(vec_b):
return ValueError("Vectors do not match")
dot_sum = 0
for a, b in zip(vec_a, vec_b):
dot_sum += a*b
return dot_sum
def get_norm(vec):
# 计算单个向量模长
sum_square = 0
for v in vec:
sum_square += v**2
return np.sqrt(sum_square)
def cosine_similarity(vec_a, vec_b):
# 余弦相似度
return get_dot(vec_a, vec_b) / (get_norm(vec_a) * get_norm(vec_b))
if __name__ == '__main__':
model = DashScopeEmbeddings()
A = model.embed_query("我喜欢你")
B = model.embed_query("你喜欢我")
print(cosine_similarity(A, B))

嘻嘻,AI对爱情有自己的看法

浙公网安备 33010602011771号