langchain简介

核心理念:为各种llms实现通用的接口,将llms相关组件链接在一起,简化llms应用开发难度(相当于编程SDK(software development kit))

主要功能: all in langchain
①prompts(优化提示词)
②models(调用各类模型)
③history(管理会话历史记录/记忆)
④indexes(管理和分析各类文档)
⑤chains(构建功能执行链条)
⑥agent(构建智能体)

支持的三种类型的模型:
LLMs
chat models
embeddings models

langchain环境部署

pip install langchain langchain-community langchain-ollama dashscope chromadb -i https://pypi.tuna.tsinghua.edu.cn/simple

RAG介绍

通用基础大模型存在问题:
①知识不是实时的,不具备自动更新知识能力
②领域知识缺乏(主要知识来源于训练数据)
③幻觉问题(看似正确实则错误)
④数据安全性

RAG(retrieval augmented generation):检索增强生成,为大模型提供了从特定数据源检索到的信息,以此来修正和补充生成的答案
RAG = 检索技术 + LLM提示

标准流程:
(1)索引:处理文档提取文本,切分为标准长度的文本块(chunk),并进行嵌入向量化(embedding),向量存储在向量数据库(vector database)
(2)检索:用户输入的查询(query)被转换为向量表示,通过相似度匹配从向量数据库中检索出最相关的文本块
(3)生成:检索到的相关文本与原始查询共同构成提示词,输入大语言模型,生成精确且具备上下文关联的回答

离线流程:知识和信息——>向量嵌入——>存入向量库
在线流程:用户提问——>向量嵌入——>在向量库中匹配

向量的基础概念

将一段文字的语义信息,转换成一串固定长度的数字列表,让计算机能看懂文字的含义并做相似度计算
向量的计算(文本嵌入的过程),可借助文本嵌入模型实现
文本嵌入模型(如:text-embedding-v1)通过深度学习等技术,从文本提取语义特征并映射为固定长度的数字序列

向量的维度表示一段文本在多个抽象语义特征方面的强度:
维数代表模型用多少个抽象语义特征来描述文本
维度越多,做语义匹配越精准
但性能压力也会增强

余弦相似度算法

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

余弦相似度 = 两个向量的点积 / 两个向量模长的乘积
(即两个向量夹角)

import numpy as np

# 计算点积
def get_dot(vec_a,vec_b):
    if len(vec_a) != len(vec_b):
        raise ValueError

    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 * v
    return np.sqrt(sum_square)

# 计算余弦相似度(计算两个向量夹角)
def cosine_similarity(vec_a,vec_b):
    result = get_dot(vec_a,vec_b) / (get_norm(vec_a) * get_norm(vec_b))
    return result

if __name__ == '__main__':
    vec_a = [0.5, 0.5]
    vec_b = [0.7, 0.7]
    vec_c = [0.7, 0.5]
    vec_d = [-0.6, -0.5]

print("ab:", cosine_similarity(vec_a, vec_b))
print("ac:", cosine_similarity(vec_a, vec_c))
print("ad:", cosine_similarity(vec_a, vec_d))


langchain调用大语言模型


from langchain_community.llms.tongyi import Tongyi
# 实例化模型  注意:qwen-max是大语言模型  qwen3-max是聊天模型
llm = Tongyi(model='qwen-max')
# 模型推理
res = llm.invoke("帮我讲个笑话,要很冷,且简短")  # 调用invoke方法向模型提问(invoke:调用)
print(res)

'''
#ollama本地访问大语言模型
from langchain_ollama import OllamaLLM
model = OllamaLLM(model='qwen3-vl:8b')
res = model.invoke(input="你是谁呀?你能做什么?")
print(res)
'''

langchain模型的流式输出

'''
from langchain_community.llms.tongyi import Tongyi

model = Tongyi(model = "qwen-max")

res = model.stream(input = "给我讲一个简短的冷笑话")

for chunk in res:
    print(chunk,end=' ',flush=True)  # flush 是为了让结果立刻刷新出来
'''

from langchain_ollama import OllamaLLM

model = OllamaLLM(model="qwen3-vl:8b")

res = model.stream(input="你是谁呀?能做什么")

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

langchain调用聊天模型

AImessage:AI输出的消息,可以是针对问题的回答(=OpenAI库的assistant角色)
HumanMessage:人类消息(=OpenAI库的user角色)
SystemMessage:可以用于指定模型具体所处的环境和背景(=OpenAI库的system角色)

'''
from langchain_community.chat_models.tongyi import ChatTongyi
from langchain_core.messages import HumanMessage,SystemMessage,AIMessage
# 得到模型对象  qwen3-max就是聊天模型
model = ChatTongyi(model = "qwen3-max")

# 准备消息列表
messages = [
    SystemMessage(content = "你是一个边塞诗人"),
    HumanMessage(content = "给我写一首唐诗"),
    AIMessage(content = "锄禾日当午,汗滴禾下土。谁知盘中餐,粒粒皆辛苦。"),
    HumanMessage(content = "按照上面你写的格式,再写一首给我看看")
]

# 调用stream流式输出
res = model.stream(input=messages)

# for循环打印输出  要通过.content来获取到内容,不然输出乱七八糟
for chunk in res:
    print(chunk.content,end="",flush=True)
'''
from langchain_ollama import ChatOllama
from langchain_core.messages import HumanMessage,SystemMessage,AIMessage
# 得到模型对象  qwen3-max就是聊天模型
model = ChatOllama(model = "qwen3-vl:8b")

# 准备消息列表
messages = [
    SystemMessage(content = "你是一个边塞诗人"),
    HumanMessage(content = "给我写一首唐诗"),
    AIMessage(content = "锄禾日当午,汗滴禾下土。谁知盘中餐,粒粒皆辛苦。"),
    HumanMessage(content = "按照上面你写的格式,再写一首给我看看")
]

# 调用stream流式输出
res = model.stream(input=messages)

# for循环打印输出  要通过.content来获取到内容,不然输出乱七八糟
for chunk in res:
    print(chunk.content,end="",flush=True)

langchain消息简写形式

from langchain_community.chat_models.tongyi import ChatTongyi

model = ChatTongyi(model = "qwen3-max")

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

res = model.stream(input=messages)

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

原来使用类对象时是静态的,一步到位 直接得到了message类的类对象

简写形式是动态的(元组),需要在运行时,由langchain机制转换为message类对象

好处在于:简写形式避免导包,写起来更简单,更重要的是支持在内部填充{ 变量 }占位
可在运行时填充具体值(提示词模板)

langchain调用嵌入模型

# from langchain_community.embeddings import DashScopeEmbeddings
#
# # 创建模型对象   不传model默认为 text-embeddings-v1
# model = DashScopeEmbeddings()
#
# # 将字符串转化为向量(这样计算机才嫩读取)
# print(model.embed_query("我喜欢你"))  # 只读取一个字符串
# print(model.embed_documents(["我真的好喜欢你", "我稀饭你", "晚上吃什么"])) # 读取多个字符串

from langchain_ollama.embeddings import OllamaEmbeddings
model = OllamaEmbeddings(model="qwen3-embedding:4b")
print(model.embed_query("我喜欢你"))
print(model.embed_documents(["我喜欢你", "我爱你", "我超级爱你"]))

总结目前已掌握的LangChain API

(阿里云千问/ollama本地调用)
LLMs:from langchain_community.llms.tongyi import Tongyi or from langchain_ollama import OllamaLLM
聊天模型:from langchai_community.chat_models.tongyi import ChatTongyi or from langchain_ollama import ChatOllama
以上两种模型均支持invoke批量和stream流式 两种方法

文本嵌入模型:from langchain_community.embeddings import DashScopeEmbeddings or from langchain_ollama import OllamaEmbeddings
支持embed_query单次转换 还有 embed_documents 批量转换

langchain通用提示词模板

在这里插入图片描述

from langchain_core.prompts import PromptTemplate
from langchain_community.llms.tongyi import Tongyi
# zero_shot思想
prompt_template = PromptTemplate.from_template(   # 创建类对象,调用方法
    "我的邻居姓{lastname},刚生了一个{gender},你帮我起个名字,简单回答"
) # 相较于字符串   使用模板类更容易做标准化模板

model = Tongyi(model="qwen-max")
# 调用.format方法注入信息
# prompt_text = prompt_template.format(lastname="张",gender="男")
# res = model.invoke(input = prompt_text)
# print(res)

# 链式
chain = prompt_template | model
res = chain.invoke(input={"lastname":"张","gender":"男"})
print(res)

fewshot提示词模板

在这里插入图片描述

from langchain_core.prompts import PromptTemplate, FewShotPromptTemplate
from langchain_community.llms.tongyi import Tongyi

# 示例的模板
example_template = PromptTemplate.from_template("单词:{word},反义词:{antonym}")

# 示例动态数据的注入
examples_data = [
    {"word": "大", "antonym": "小"},
    {"word": "上", "antonym": "下"}
]

# 调用方法
few_shot_template = FewShotPromptTemplate(
    example_prompt=example_template,        # 示例数据的模板
    examples=examples_data,            # 示例的数据(用来注入动态数据的),list内套字典
    prefix="告知我单词的反义词,我提供一下示例",                 # 示例之前的提示词
    suffix="基于前面的示例,{input_word}的反义词是?",          # 示例之后的提示词
    input_variables=["input_word"]          # 声明在前缀或后缀中所需要的注入的变量名
)

prompt_text = few_shot_template.invoke(input={"input_word": "左"}).to_string()   # 调用to_string这个方法使得输出更易读

print(prompt_text)

model = Tongyi(model="qwen-max")

print(model.invoke(input=prompt_text))

模板类的format和invoke方法

在这里插入图片描述

from langchain_core.prompts import PromptTemplate

template = PromptTemplate.from_template("我的邻居是:{lastname},最喜欢{hobby}")

res = template.format(lastname="周杰伦",hobby="唱歌")
print(res)

res2 = template.invoke({"lastname":"林俊杰","hobby":"和周杰伦一起唱歌"}).to_string()
print(res2)

ChatPromptTemplate的使用

在这里插入图片描述
在这里插入图片描述

from langchain_core.prompts import ChatPromptTemplate,MessagesPlaceholder
from langchain_community.chat_models.tongyi import ChatTongyi

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

history_data = [
    ("human","你来写一首唐诗"),
    ("ai","床前明月光,疑是地上霜。举头望明月,低头思故乡"),
    ("human","好诗,再给我来一首"),
    ("ai","锄禾日当午,汗滴禾下土。谁之盘中餐,粒粒皆辛苦。")
]
# 返回类型是stringpromptvalue这个类  所以需要to_string()
prompt_text = chat_prompt_template.invoke({"history":history_data}).to_string()
model = ChatTongyi(model="qwen-max")
res = model.invoke(prompt_text)

print(res.content)

Chain的基础使用

在这里插入图片描述在这里插入图片描述

from langchain_core.prompts import ChatPromptTemplate,MessagesPlaceholder
from langchain_community.chat_models.tongyi import ChatTongyi

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

history_data = [
    ("human","你来写一首唐诗"),
    ("ai","床前明月光,疑是地上霜。举头望明月,低头思故乡"),
    ("human","好诗,再给我来一首"),
    ("ai","锄禾日当午,汗滴禾下土。谁之盘中餐,粒粒皆辛苦。")
]

model = ChatTongyi(model="qwen-max")

# 组成链,要求每一个组件都是runnable接口的子类
chain = chat_prompt_template | model

# # 通过链去调用invoke/stream
# res = chain.invoke({"history": history_data})
# print(res.content)

# 通过stream流式输出
for chunk in chain.stream({"history": history_data}):
    print(chunk.content,end="",flush=True)

或运算符的重载

在这里插入图片描述

# 定义第一个类 Test,继承自object(Python3中所有类默认继承,可省略)
class Test(object):
    # 构造方法:创建Test类的对象时,自动调用的方法
    # self 代表当前创建的对象本身,name 是传入的参数(给对象起的名字)
    def __init__(self,name):
        # 给当前对象绑定一个name属性,值等于传入的name参数
        self.name = name

    # 【核心重点】重写Python的 | 或运算符
    # 魔法方法__or__:当对象使用 | 运算符时,自动触发这个方法
    # self:| 左边的对象,other:| 右边的对象
    def __or__(self,other):
        # 把左右两个对象,打包传入MySequence类,返回一个MySequence对象
        return MySequence(self,other)

    # 重写打印对象时的显示内容(默认打印对象内存地址,重写后更直观)
    def __str__(self):
        # 打印Test对象时,直接返回它的name属性值
        return self.name

# 定义第二个类 MySequence,用来存储Test对象的序列(管理用|连接的所有对象)
class MySequence(object):
    # 构造方法:*args 表示接收【任意多个参数】(0个、1个、多个都可以)
    def __init__(self,*args):
        # 创建一个空列表,用来存储所有用|连接的Test对象
        self.sequence = []
        # 遍历传入的所有参数,逐个添加到sequence列表中
        for arg in args:
            self.sequence.append(arg)

    # 【核心重点】再次重写|运算符,实现【链式调用】(a|b|c|d...)
    # 因为a|b会返回MySequence对象,再|c时,就会调用这个方法
    def __or__(self,other):
        # 把|右边的新对象,追加到自身的sequence列表里
        self.sequence.append(other)
        # 关键:返回自身(self),这样才能继续用|连接下一个对象
        return self

    # 自定义方法:执行序列,打印所有存储的Test对象的名字
    def run(self):
        # 遍历存储所有Test对象的列表
        for i in self.sequence:
            # 打印每个Test对象(因为Test重写了__str__,所以直接打印name)
            print(i)

# 主程序入口:只有直接运行这个文件时,才会执行下面的代码
if __name__ == '__main__':
    # 创建3个Test类的对象,分别命名为a、b、c
    a = Test('a')
    b = Test('b')
    c = Test('c')

    # 【核心逻辑】链式使用|运算符
    # 第一步:a | b → 调用Test类的__or__ → 返回 MySequence(a, b)
    # 第二步:上一步的结果 | c → 调用MySequence类的__or__ → 把c追加到列表,返回自身
    d = a | b | c

    # 调用MySequence的run方法,遍历打印所有对象
    d.run()
    # 打印d的类型,验证d是MySequence类的对象(不是Test对象)
    print(type(d))

StrOutputParser

在这里插入图片描述
第二个model的接收参数不对
在这里插入图片描述
在这里插入图片描述

from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_community.chat_models.tongyi import ChatTongyi

parser = StrOutputParser()
model = ChatTongyi(model="qwen3-max")
prompt = PromptTemplate.from_template(
"我的邻居姓:{lastname},刚生了{gender},请起名,仅告知我名字无需其他内容"
)

# chain = prompt | model | parser | model | parser
#
# res = chain.invoke({"lastname":"张","gender":"女"})
# print(res)

# 或者也可以不用parser(第二个model的输出仍为AIMessage)就需要.content直接获取内容
chain = prompt | model | parser | model
res = chain.invoke({"lastname":"张","gender":"女"})
print(res.content)

# 第一个模型输出的内容只有一个名字,再输入给第二个模型的时候没有记忆历史消息的功能,所以模型就乱回答了

JsonOutputParser和多模型链

在这里插入图片描述
在这里插入图片描述

from langchain_core.prompts import PromptTemplate
from langchain_community.chat_models.tongyi import ChatTongyi
from langchain_core.output_parsers import StrOutputParser, JsonOutputParser

strparser = StrOutputParser()
jsonparser = JsonOutputParser()

model = ChatTongyi(model="qwen3-max")
first_prompt = PromptTemplate.from_template(
    "我的邻居姓{lastname},他们家刚生了一个小孩,性别{gender},请你帮他们给小孩起一个名字。回答名字给我即可。"
    "注意以JSON格式返回给我,并且key是name,value就是你起的名字。严格按照格式要求返回给我"
)

second_prompt = PromptTemplate.from_template(
    "姓名是{name},请你帮我解析含义"
)

chain = first_prompt | model | jsonparser | second_prompt| model | strparser

for chunk in chain.stream({"lastname":"黄","gender":"女"}):
    print(chunk,end="",flush=True)

自定义函数加入链

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser,JsonOutputParser
from langchain_community.chat_models.tongyi import ChatTongyi

str_outputparser = StrOutputParser()
model = ChatTongyi(model="qwen3-max")

first_prompt = PromptTemplate.from_template(
    "我的邻居姓{lastname},他们家生了一个小孩,性别为{gender}。请你帮他们起一个名字,回答名字即可"
)

second_prompt = PromptTemplate.from_template(
    "名字是{name},请你解析这个名字的含义"
)

chain = first_prompt | model | (lambda ai_msg:{"name":ai_msg.content}) | second_prompt | model | str_outputparser

for chunk in chain.stream({"lastname":"黄","gender":"女"}):
    print(chunk,end="",flush=True)

memory临时记忆功能

在这里插入图片描述
在这里插入图片描述

from langchain_community.chat_models.tongyi import ChatTongyi
from langchain_core.prompts import PromptTemplate,MessagesPlaceholder,ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_core.chat_history import InMemoryChatMessageHistory

# 选用模型  构建提示词模板和解析器  构建基础chain
model = ChatTongyi(model="qwen3-max")

# prompt = PromptTemplate.from_template(
#     "你需要根据会话历史来回答用户的问题。对话历史{chat_history},用户提问{input},请回答"
# )

# 比通用提示词模板更为规范
prompt = ChatPromptTemplate.from_messages(
    [
        ("system","你需要根据会话历史来回答用户的问题"),
        MessagesPlaceholder("chat_history"),
        ("human","请回答如下问题:{input}")
    ]
)
str_parser = StrOutputParser()

base_chain = prompt | model | str_parser
###

# 获取对话ID,增加聊天记录实例
store = {}


def get_history(session_id):
    if session_id not in store:
        store[session_id] = InMemoryChatMessageHistory()
    return store[session_id]


# 构建增强链
conversation_chain = RunnableWithMessageHistory(
    base_chain,         # 被增强的链
    get_history,        # 实现函数:通过对话id获取InMemoryChatMessageHistory()实例
    input_messages_key="input",             # 用户输入在模板中的占位符
    history_messages_key="chat_history"     # 用户输入在模板中的占位符
)

# 实现以上业务
if __name__ == '__main__':
    # 固定格式:添加langchain配置,为程序配置所需的session_id   字典套字典的模式
    session_config = {
        "configurable": {
            "session_id": "user001"
        }
    }

    res = conversation_chain.invoke({"input": "小明有2只猫"}, session_config)   # 同时自动创建session_id
    print("第一次执行:", res)
    res = conversation_chain.invoke({"input": "小贝有1只狗"}, session_config)
    print("第二次执行:", res)
    res = conversation_chain.invoke({"input": "请问有几只宠物?"}, session_config)
    print("第三次执行:", res)

在这里插入图片描述

长期会话记忆功能的实现

import os,json
from typing import Sequence

from langchain_community.chat_models import ChatTongyi
from langchain_core.messages import message_to_dict,messages_from_dict, BaseMessage
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import RunnableWithMessageHistory


# message_to_dict: 单个消息对象(BaseMessage类实例) -> 字典
# messages_from_dict: [字典、字典...]  -> [消息、消息...]
# aimessage humanmessage systemmessage都是BaseMessage的子类
class FileChatMessageHistory(BaseChatMessageHistory):    # 用于替换临时记忆,作为文件存储会话记忆
    def __init__(self,session_id,storage_path):
        self.session_id = session_id        # 会话id
        self.storage_path = storage_path    # 不同会话id的存储文件所在的文件夹路径
        # 完整的文件路径(将上面的内容都拼接在一起)
        self.file_path = os.path.join(self.session_id, self.storage_path)
        # 确保文件夹是存在的(如果存在就不创建了  不存在就创建)
        os.makedirs(os.path.dirname(self.file_path),exist_ok=True)

    def add_messages(self,messages: Sequence[BaseMessage]) -> None:
        # Sequence序列 类似与list tuple
        all_messages = list(self.messages)      # 已有的消息列表
        all_messages.extend(messages)           # 新的和已有的融合为1个list

        # 将数据同步写入到本地文件中
        # 类对象写入文件 -> 一堆二进制
        # 为了方便, 可以将BaseMessage消息转换为字典(借助json模块以json字符串写入文件)
        # 官方message_to_dict: 单个消息对象(BaseMessage类实例) -> 字典
        # new_messages = []
        # for message in all_messages:
        #     d = message_to_dict(message)
        #     new_messages.append(d)
        # 用推导式更为简洁
        new_messages = [message_to_dict(message) for message in all_messages]
        # 将数据写入文件
        with open(self.file_path,'w', encoding="utf-8") as f:
            json.dump(new_messages, f)          # 转换为json格式


    # 获取消息  同时返回值是list[BaseMessage]
    @property        # @property装饰器将messages方法变为成员属性用
    def messages(self) -> list[BaseMessage]:
            # 当前文件内: list[字典]
            try:
                with open(self.file_path,'r',encoding="utf-8") as f:
                    messages_data = json.load(f)        # 返回值就是list[字典]
                    return messages_from_dict(messages_data)
            except FileNotFoundError:   # 抓一下异常
                return []

    # 清除文件内容  往文件里面写一个空列表也相当于清除了
    def clear(self) -> None:
            with open(self.file_path,'w',encoding="utf-8") as f:
                json.dump([],f)


# 下面的核心代码都一样 直接copy
# 选用模型  构建提示词模板和解析器  构建基础chain
model = ChatTongyi(model="qwen3-max")

# prompt = PromptTemplate.from_template(
#     "你需要根据会话历史来回答用户的问题。对话历史{chat_history},用户提问{input},请回答"
# )

# 比通用提示词模板更为规范
prompt = ChatPromptTemplate.from_messages(
    [
        ("system","你需要根据会话历史来回答用户的问题"),
        MessagesPlaceholder("chat_history"),
        ("human","请回答如下问题:{input}")
    ]
)
str_parser = StrOutputParser()

base_chain = prompt | model | str_parser
###

# 获取对话ID,增加聊天记录实例
store = {}

# 将底层存储修改一下就能够继续使用了
def get_history(session_id):
    return FileChatMessageHistory(session_id,"./chat_history")   # ctrl + p 查看参数列表


# 构建增强链
conversation_chain = RunnableWithMessageHistory(
    base_chain,
    get_history,
    input_messages_key="input",
    history_messages_key="chat_history"
)

if __name__ == '__main__':
    session_config = {
        "configurable": {
            "session_id": "user001"
        }
    }

    res = conversation_chain.invoke({"input": "小明有2只猫"}, session_config)
    print("第一次执行:", res)
    res = conversation_chain.invoke({"input": "小贝有1只狗"}, session_config)
    print("第二次执行:", res)
    res = conversation_chain.invoke({"input": "请问有几只宠物?"}, session_config)
    print("第三次执行:", res)

CSVLoader

在这里插入图片描述
参数可能不同,但是因为父类都是一样的,所以能够统一读取
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

from langchain_community.document_loaders import CSVLoader

loader = CSVLoader(
    file_path="./user001/stu.csv",      # 指定读取的文件路径
    csv_args={
        "delimiter": ",",               # 指定分隔符
        "quotechar": '"',               # 指定带有分隔符的文本被引号包围是单引号还是双引号
        "fieldnames": ['a','b','c','d'] # 如果原本数据有表头,就不要下面的代码  否则会把原本数据的表头当作第一行也读取进来
    },
    encoding="utf-8"                   # 指定编码为UTF-8(报错:UnicodeDecodeError: 'gbk' codec can't decode byte 0xa2 in position 29: illegal multibyte sequence)
)

# 批量加载 .load()  -> [document,document...] 最终目的都是为了得到document对象 方便后续操作
# documents = loader.load()
# print(documents)

# 懒加载 .lazy_load()  -> [document,document...]
for document in loader.lazy_load():
    print(document)

JSONLoader

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

from langchain_community.document_loaders import JSONLoader

loader = JSONLoader(
    file_path="./数据/stu_json_lines.json",
    jq_schema=".name",
    text_content=False,                 # 告知jsonloader  我读取的不是字符串
    json_lines=True                     # 告知jsonloader  我读取的数据每一行都是标准的json格式
)

document = loader.load()
print(document)

TextLoader和文档分割器

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
loader = TextLoader("./数据/Python基础语法.txt","utf-8")
docs = loader.load()

# 进行文本分割   类的实例化
splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,              # 分段允许的最大字符数
    chunk_overlap=50,            # 分段之间允许重叠的最大字符数
    # 文本分割段落之间的依据符号
    separators=["\n\n", "\n", ",", ".", ",", "。", "?", "!", "!"],
    length_function=len         # 统计字符的依据函数(chunk_size & chunk_overlap)
)
# 方法的调用 
split_docs = splitter.split_documents(docs)
print(len(split_docs))
for doc in split_docs:
    print("="*20)
    print(doc)
    print("="*20)
# 这样是不合适的,单个document太大,不利于后续的操作
# document = loader.load()    # 得到[document]
# print(document)
# print(len(document))

pypdfLoader

在这里插入图片描述

from langchain_community.document_loaders import PyPDFLoader

loader = PyPDFLoader(
    file_path="./数据/pdf2.pdf",
    mode="single",           # 默认是page模式  即每一页形成一个document文档对象  single模式则不管有多少页,都只返回一个document对象
    password="itheima"
)

i = 0
for doc in loader.lazy_load():
    i += 1
    print(doc)
    print("="*20)

向量存储

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
外部长期存储

from langchain_chroma import Chroma
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_community.document_loaders import CSVLoader

vector_store = Chroma(
    collection_name="test",         # 给当前向量存储起个名字,类似数据库的表名称
    embedding_function=DashScopeEmbeddings(),   # 嵌入模型
    persist_directory="./chroma_db" # 指定数据存放的文件夹
)

loader = CSVLoader(
    file_path="./数据/info.csv",
    encoding="utf-8",
    source_column="source"          # 指明本条数据是来源于哪里的
)

documents = loader.load()
# id1 id2 id3 id4 id5
# 向量存储的  新增 删除 检索

# 新增
vector_store.add_documents(
    documents=documents,        # 被添加的文件(前面load进来的文件)  类型:list[document]
    ids=["id"+str(i) for i in range(1,len(documents)+1)],       # 给添加的文件提供id(字符串)   list[str]
)

# 删除
vector_store.delete(["id1","id2"])

# 检索  返回类型为list[document]
result = vector_store.similarity_search(
    "python是不是简单易学呀?",      # 询问内容
    3     ,                          # 检索的结果(按照相似度排行 由高到低)
    filter={"source" : "黑马程序员"}      # 相较于原本的临时存储  多了一个过滤
)

print(result)

临时存储

from langchain_core.vectorstores import InMemoryVectorStore
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_community.document_loaders import CSVLoader


vector_store = InMemoryVectorStore(
    embedding=DashScopeEmbeddings()
)

loader = CSVLoader(
    file_path="./数据/info.csv",
    encoding="utf-8",
    source_column="source"          # 指明本条数据是来源于哪里的
)

documents = loader.load()
# id1 id2 id3 id4 id5
# 向量存储的  新增 删除 检索

# 新增
vector_store.add_documents(
    documents=documents,        # 被添加的文件(前面load进来的文件)  类型:list[document]
    ids=["id"+str(i) for i in range(1,len(documents)+1)],       # 给添加的文件提供id(字符串)   list[str]
)

# 删除
vector_store.delete(["id1","id2"])

# 检索  返回类型为list[document]
result = vector_store.similarity_search(
    "python是不是简单易学呀?",      # 询问内容
    3                               # 检索的结果(按照相似度排行 由高到低)
)

print(result)

基于向量存储构建提示词

from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.vectorstores import InMemoryVectorStore
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_community.chat_models.tongyi import ChatTongyi

str_parser = StrOutputParser()
model = ChatTongyi(model="qwen3-max")
prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "以我提供的参考资料为主,简洁且专业地回答用户的问题。参考资料{context}"),
        ("user", "用户提问{input}")
    ]
)

vector_store = InMemoryVectorStore(embedding=DashScopeEmbeddings(model="text-embedding-v4"))

# 准备资料(向量库的数据)
# add_texts  传入一个 list[str]  此处是为了简便(不用再导入文件)
vector_store.add_texts(
    ["减肥就是要少吃多练", "在减脂期间吃东西很重要,清淡少油控制卡路里摄入并运动起来", "跑步是很好的运动哟"])

input_text = "怎么减肥呢?"

# 检索向量库   拿到参考资料
result = vector_store.similarity_search(input_text, 2)  # 返回类型: list[document]  但想要的是字符串
reference_text = "["
for doc in result:
    reference_text += doc.page_content  # 用.page_content 获取document中的字符串内容
reference_text += "]"


# 构建执行链条
def print_prompt(prompt):
    print(prompt.to_string())
    print("=" * 20)
    return prompt


# 构建完整的提示词链条
chain = prompt | print_prompt | model | str_parser
res = chain.invoke({"input": input_text,"context": reference_text})
print(res)

在这里插入图片描述

runnablepassthrough的使用

from langchain_core.documents import Document
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.vectorstores import InMemoryVectorStore
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_community.chat_models.tongyi import ChatTongyi

str_parser = StrOutputParser()
model = ChatTongyi(model="qwen3-max")
prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "以我提供的参考资料为主,简洁且专业地回答用户的问题。参考资料{context}"),
        ("user", "用户提问{input}")
    ]
)

vector_store = InMemoryVectorStore(embedding=DashScopeEmbeddings(model="text-embedding-v4"))

# 准备资料(向量库的数据)
# add_texts  传入一个 list[str]  此处是为了简便(不用再导入文件)
vector_store.add_texts(
    ["减肥就是要少吃多练", "在减脂期间吃东西很重要,清淡少油控制卡路里摄入并运动起来", "跑步是很好的运动哟"])

input_text = "怎么减肥呢?"
# langchain中的向量存储对象,有一个方法:as_retriever 可以返回1个runnable接口的子类对象(runnable接口的子类才能入链,还有callable 以及mapping(str的顶级父类))
retriever = vector_store.as_retriever(search_kwargs={"k": 2})  # 相当于similarity_search 返回相似个数为2


# 这个retriever就是检索结果

def format_func(docs: list[Document]):  # docs的类型
    if not docs:  # 因为向量的检索并不是百分百都有结果
        return "无相关参考资料"

    formatted_str = "["
    for doc in docs:
        formatted_str += doc.page_content
    formatted_str += "]"
    return formatted_str


def print_prompt(prompt):
    print(prompt.to_string())
    print("=" * 20)
    return prompt


# 构建chain
# chain = retriever | prompt | model | StrOutputParser()
chain = (
        {"input": RunnablePassthrough(),
         "context": retriever | format_func} | prompt | print_prompt | model | StrOutputParser()
)
res = chain.invoke(input_text)  # 因为retriever的输入接收类型是str 所以这里就不用字典了
# 同时:context的value最好也是str 所以要写一个format_func函数将list[document]转换为str
print(res)
"""
invoke取到用户的输入,同时传输给input和context中的retriever,这样就不会造成用户输入的丢失
"""
"""
retriever:
        - 输入:用户的提问           str
        - 输出:向量库的检索结果      list[Document]

prompt:
        - 输入:用户的提问 + 向量库的检索结果   dict
        - 输出:完整的提示词                 PromptValue
        
不仅输入输出类型对不上 还会丢失用户的提问数据
查看输入输出类型:找到对应方法,ctrl点击 然后structure找到在对应类下找到invoke
"""

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐