LangChain实战课-启程篇:从 0 到 1 (二) | 豆包MarsCode AI刷题

221 阅读10分钟

课程介绍

  • 启程篇:从 0 到 1
    • 内容:LangChain的安装流程和快速入门操作
    • 实践:构建基于“易速鲜花”本地知识库的智能问答系统
    • 目标:直观感受LangChain的强大功能
  • 基础篇:深入 6 大组件
    • 模型(Models) :各大语言模型的接口、调用细节以及输出解析机制
    • 提示模板(Prompts) :提示工程流线化,进一步激发大语言模型的潜力
    • 数据检索(Indexes) :建立和操作文档,返回相关文档以搭建知识库
    • 记忆(Memory) :通过短时和长时记忆存储和检索对话数据
    • 链(Chains)封装功能的核心机制,灵活完成常见用例
    • 代理(Agents)另一个LangChain中的核心机制,使大模型自主调用工具,形成智能自主Agent
  • 应用篇:积累场景中的智慧
    • 内容:LangChain组件在实际场景中的应用
    • 实践:嵌入式存储、数据库连接、异步通信、智能代理的角色扮演等
    • 目标:通过实际案例展示组件了解如何共同完成复杂任务,提升实战能力
  • 实战篇:动手!
    • 项目:部署鲜花网络电商的人脉工具,开发易速鲜花聊天客服机器人
    • 实践:从模型调用到数据连接,再到记忆的存储与检索,掌握构建智能系统的每个环节
    • 目标:能够独立利用LangChain构建智能问答系统,适用于企业或个人需求

大语言模型概述

大语言模型(Large Language Model, LLM)是一种基于深度学习的人工智能模型,旨在理解和生成自然语言

工作原理

  • 大语言模型像一个巨大的预测机器,其训练过程主要基于“猜词”
  • 模型预测不仅依赖于词语的统计关系,还能理解上下文,甚至反映对世界常识的认知,能够继续生成或者说推理出相关的内容
  • 但是,大语言模型并不完全理解语言,它们没有人类的情感、意识或理解力。它们只是通过复杂的数学函数学习到的语言模式,一个概率模型来做预测,所以有时候它们会犯错误,或者生成不合理甚至偏离主题的内容

LangChain概述

LangChain 是一个全方位的、基于大语言模型这种预测能力的应用开发工具,它的灵活性和模块化特性使得处理语言模型变得极其简便

主要特点

  • 预构建链功能:LangChain 提供类似乐高积木的功能,适合不同水平的开发者快速构建项目
  • 模块化组件:LangChain 允许定制和创建功能链条,以满足特定的应用需求

通过 LangChain 调用 Text 和 Chat 模型

调用 Text 模型

import os
from langchain_openai import ChatOpenAI

# os.environ["OPENAI_API_KEY"] = '你的OpenAI API Key'
# os.environ["OPENAI_BASE_URL"] = 'OpenAI 的 API URL'

llm = ChatOpenAI(
    model=os.environ.get("LLM_MODELEND"),
    temperature=0.8,
    max_tokens=600,
)
response = llm.predict("请给我的花店起个名")

print(response)
  • 导入库
    • os: 用于处理环境变量
    • ChatOpenAI: 从 LangChain 中导入的类,用于与 OpenAI 的聊天模型交互
  • 创建 ChatOpenAI 实例 :
    • model: 从环境变量中获取要使用的模型名称
    • temperature: 控制生成文本的随机性,值越高,生成的文本越随机
    • max_tokens: 限制生成文本的最大长度
  • 调用模型进行预测 :
    • 使用 predict 方法向模型发送输入提示,并获取模型的响应

调用 Chat 模型

import os
from langchain_openai import ChatOpenAI
from langchain.schema import HumanMessage, SystemMessage

# os.environ["OPENAI_API_KEY"] = '你的OpenAI API Key'
# os.environ["OPENAI_BASE_URL"] = 'OpenAI 的 API URL'

chat = ChatOpenAI(model=os.environ.get("LLM_MODELEND"), temperature=0.8, max_tokens=600)

messages = [    SystemMessage(content="你是一个很棒的智能助手"),    HumanMessage(content="请给我的花店起个名"),]
response = chat(messages)
print(response)
  • 导入库
    • HumanMessage 和 SystemMessage: 从 langchain.schema 导入,用以构建消息结构
  • 构建消息列表
    • SystemMessage: 用来设定上下文或指示模型的角色。这里指定模型是一个“很棒的智能助手”
    • HumanMessage: 包含用户的请求,询问模型为花店起名
  • 调用聊天模型
    • 将消息列表传递给 chat 对象,使用了 ChatOpenAI 类的 __call__ 方法,进行消息处理和文本生成

Text模型与Chat模型的优缺点

Text模型的优点:

  1. 简单直接:适合处理单轮文本生成任务,只需提供一个提示即可
  2. 易于使用:调用和实现相对简单,适合快速生成文本
  3. 资源消耗较低:对于简单任务,可能比Chat模型消耗更少的计算资源

Text模型的缺点:

  1. 缺乏上下文:无法处理多轮对话,无法利用历史信息
  2. 灵活性不足:无法像Chat模型一样通过消息列表管理对话状态

Chat模型的优点:

  1. 对话历史管理:可以接受包含system、user和assistant消息的列表,提供更丰富的上下文
  2. 角色模拟:通过system角色设定背景信息,增强对话的引导性和控制力
  3. 多轮交互:更适合处理复杂的对话场景,能够生成更自然、连贯的回复

Chat模型的缺点:

  1. 复杂性:对于简单任务,调用和管理相对复杂
  2. 资源消耗较高:处理多轮对话可能需要更多的计算资源

当需要快速生成简单文本,且不需要上下文时,选择Text模型;当需要进行多轮对话,或希望模型能够考虑历史信息和上下文时,选择Chat模型。总的来说,Text模型适合简单的单轮文本生成,而Chat模型则更适合复杂的对话场景。

构建基于“易速鲜花”本地知识库的智能问答系统

“易速鲜花”作为一个大型在线鲜花销售平台,有自己的业务流程和规范,也拥有针对员工的SOP手册。新员工入职培训时,会分享相关的信息。但是,这些信息分散于内部网和HR部门目录各处,有时不便查询;有时因为文档过于冗长,员工无法第一时间找到想要的内容;有时公司政策已更新,但是员工手头的文档还是旧版内容。

为了构建基于“易速鲜花”本地知识库的智能问答系统,我们需要先让文档加载器将Documents加载为LangChain可读的格式;接着,文本分割器将这些Documents切分为指定大小的“文档块”;然后将这些“文档块”以“嵌入”形式存储到向量数据库中;接下来应用程序从存储中检索分割后的文档;最后,将问题与相似的嵌入片传递给语言模型生成答案

数据的准备和载入

# 1.Load 导入Document Loaders
import os
from langchain_community.document_loaders import PyPDFLoader
from langchain_community.document_loaders import Docx2txtLoader
from langchain_community.document_loaders import TextLoader
from typing import Dict, List, Any
from langchain.embeddings.base import Embeddings
from langchain.pydantic_v1 import BaseModel
from volcenginesdkarkruntime import Ark

# os.environ["OPENAI_API_KEY"] = '你的OpenAI API Key'
# os.environ["OPENAI_BASE_URL"] = 'OpenAI 的 API URL'

# 加载Documents
base_dir = "./OneFlower"  # 文档的存放目录
documents = []
for file in os.listdir(base_dir):
    # 构建完整的文件路径
    file_path = os.path.join(base_dir, file)
    if file.endswith(".pdf"):
        loader = PyPDFLoader(file_path)
        documents.extend(loader.load())
    elif file.endswith(".docx"):
        loader = Docx2txtLoader(file_path)
        documents.extend(loader.load())
    elif file.endswith(".txt"):
        loader = TextLoader(file_path)
        documents.extend(loader.load())
  • 导入模块:导入必要的库和类,用于加载不同格式的文档
  • 加载文档:遍历指定目录中的文件,根据文件类型选择合适的加载器(PDF、DOCX、TXT),并将加载的文档存储在documents列表中

文本的分割

# 2.Split 将Documents切分成块以便后续进行嵌入和向量存储
from langchain.text_splitter import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=10)
chunked_documents = text_splitter.split_documents(documents)
  • 文本分割器:使用RecursiveCharacterTextSplitter来处理文档
  • 切分文档:将加载的文档切分为多个文档块,便于后续的嵌入和存储

向量数据库存储

# 3.Store 将分割嵌入并存储在矢量数据库Qdrant中
from langchain_community.vectorstores import Qdrant

class DoubaoEmbeddings(BaseModel, Embeddings):
    client: Ark = None
    api_key: str = ""
    model: str

    def __init__(self, **data: Any):
        super().__init__(**data)
        if self.api_key == "":
            self.api_key = os.environ["OPENAI_API_KEY"]
        self.client = Ark(
            base_url=os.environ["OPENAI_BASE_URL"],
            api_key=self.api_key
        )

    def embed_query(self, text: str) -> List[float]:
        """
        生成输入文本的 embedding.
        Args:
            texts (str): 要生成 embedding 的文本.
        Return:
            embeddings (List[float]): 输入文本的 embedding,一个浮点数值列表.
        """
        embeddings = self.client.embeddings.create(model=self.model, input=text)
        return embeddings.data[0].embedding

    def embed_documents(self, texts: List[str]) -> List[List[float]]:
        return [self.embed_query(text) for text in texts]

    class Config:
        arbitrary_types_allowed = True

vectorstore = Qdrant.from_documents(
    documents=chunked_documents,  # 以分块的文档
    embedding=DoubaoEmbeddings(
        model=os.environ["EMBEDDING_MODELEND"],
    ),  # 用OpenAI的Embedding Model做嵌入
    location=":memory:",  # in-memory 存储
    collection_name="my_documents",
)  # 指定collection_name
  • Qdrant:存储嵌入向量的数据库
  • 存储嵌入:将切分后的文档存储为嵌入,指定集合名称和存储位置

相关信息的获取

# 4. Retrieval 准备模型和Retrieval链
import logging  # 导入Logging工具
from langchain_openai import ChatOpenAI  # ChatOpenAI模型
from langchain.retrievers.multi_query import (
    MultiQueryRetriever,
)  # MultiQueryRetriever工具
from langchain.chains import RetrievalQA  # RetrievalQA链

# 设置Logging
logging.basicConfig()
logging.getLogger("langchain.retrievers.multi_query").setLevel(logging.INFO)

# 实例化一个大模型工具 - OpenAI的GPT-3.5
llm = ChatOpenAI(model=os.environ["LLM_MODELEND"], temperature=0)

# 实例化一个MultiQueryRetriever
retriever_from_llm = MultiQueryRetriever.from_llm(
    retriever=vectorstore.as_retriever(), llm=llm
)

# 实例化一个RetrievalQA链
qa_chain = RetrievalQA.from_chain_type(llm, retriever=retriever_from_llm)
  • 导入模块:引入日志记录、OpenAI模型、检索器和问答链
  • 设置日志:配置日志记录以便调试
  • 实例化语言模型:创建一个OpenAI的Chat模型实例
  • 创建检索器:使用MultiQueryRetriever将向量存储转换为检索器
  • 创建问答链:将语言模型和检索器结合,形成一个RetrievalQA链,便于处理用户查询

生成回答并展示

# 5. Output 问答系统的UI实现
from flask import Flask, request, render_template

app = Flask(__name__)  # Flask APP

@app.route("/", methods=["GET", "POST"])
def home():
    if request.method == "POST":
        # 接收用户输入作为问题
        question = request.form.get("question")

        # RetrievalQA链 - 读入问题,生成答案
        result = qa_chain({"query": question})

        # 把大模型的回答结果返回网页进行渲染
        return render_template("index.html", result=result)

    return render_template("index.html")

if __name__ == "__main__":
    app.run(host="0.0.0.0", debug=True, port=5000)
  • 导入Flask:用于创建Web应用程序
  • 创建Flask应用:实例化Flask应用
  • 定义路由:设置根路由处理GET和POST请求
  • 启动应用:在主程序中运行Flask应用,监听指定端口

至此,基于“易速鲜花”本地知识库的智能问答系统搭建完成(省略部分内容)。我们先把本地知识切片后做Embedding,存储到向量数据库中,然后把用户的输入和从向量数据库中检索到的本地知识传递给大模型,最终生成所想要的回答。

写在最后

LangChain 是一个强大的框架,主要用于构建多模态应用程序,尤其在结合大语言模型(LLMs)与其他数据源时表现出色。这种强大的集成能力使得开发者能够灵活构建复杂的应用,充分发挥 AI 的潜力。

学习心得
  • API调用不同类型的模型
  • 通过 LangChain 调用 Text 和 Chat 模型
  • 构建基于“易速鲜花”本地知识库的智能问答系统

正如我们所看到的,像 LangChain 这样的工具已然浸入我们的生活,成为推动变革的重要推手。通过LangChain与大模型的结合,我们可以构建的应用场景几乎是无穷无尽的……技术的驱动使得实现这些想法成为可能。无论如何,有一点毋庸置疑。我们正站在一个崭新的历史节点上。