LLM代码实现-Qwen(挂载知识库)

为什么要挂载知识库?

LLM 在回答用户的问题时可能会产生幻觉,或者由于训练数据中不包含用户想要的内容而无法回答,通常情况下我们可以选择微调模型或者外挂知识库来缓解这类问题。微调模型的对数据和算力都有一定的要求,而知识库的门槛会更低一些,所以通常情况下会选择外挂知识库高效地来解决这类问题。

挂载知识库其实相当于引入外部知识,为了扩展语言模型以减少歧义,从大型文本数据库中检索相关文档。通常将输入序列分割成块并检索与用户输入的 query 相似的文档,然后将所选文档放在输入文本之前作为前置知识以改进模型的预测。使得模型可以更容易、更准确地访问专业知识。

挂载知识库的流程

文档 -> 文档向量化 -> 文档检索 -> 对话交互

我们可以借助 langchain 来实现这个流程:

1. 文档

我们使用中医药书籍来作为知识库,下载链接(700 本中医药古籍文本),下载完成后运行以下代码以合并数据。

python 复制代码
import os


dir_path = "./TCM-Ancient-Books-master"
for index, filename in enumerate(os.listdir(dir_path)):
    if not filename.endswith(".txt"):
        continue

    file_path = os.path.join(dir_path, filename)
    with open(file_path, "r", encoding='gb18030', errors='ignore') as f:
        text = f.read().replace("\n", "")

    mode = "a" if index else "w"
    with open("./knowledge.txt", mode, encoding="utf-8") as f:
        f.write(text + "\n")

2. 文档向量化

在文档检索的过程中如果利用字符串直接匹配文本相似度效率是很低的,尤其是知识库体量非常大的时候,因此一般会先对文档进行切割和向量化以提升检索的速度。将每个文档转换为数值向量,以便计算文档之间的相似度或进行聚类分析。

python 复制代码
from langchain.document_loaders import UnstructuredFileLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings.huggingface import HuggingFaceEmbeddings
from langchain.vectorstores import FAISS

def load_knowledge():
    filepath = "./knowledge.txt"
    loader = UnstructuredFileLoader(filepath)
    docs = loader.load()

    # chunk-size是文本最大的字符数。chunk-overlap是前后两个chunk的重叠部分最大字数
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=40)
    docs = text_splitter.split_documents(docs)

    # 这里需要下载一个中文的文本向量化模型
    embeddings = HuggingFaceEmbeddings(model_name="./text2vec-large-chinese",
                                       model_kwargs={'device': 'cuda'})
    
    # 指定向量化文档加载/保存路径
    save_path = "./med_faiss_store.faiss"
    if not os.path.exists(save_path):
        vector_store = FAISS.from_documents(docs, embeddings)
        vector_store.save_local(save_path)

    else:
        vector_store = FAISS.load_local(save_path, embeddings=embeddings)

    return vector_store

3. 文档检索

利用以下代码根据用户的输入按照相关性对向量库中的文本文本进行排序并取出排名靠前的 5 条知识,并将知识库中的知识库和用户的输入拼接在一起作为新的 prompt。

python 复制代码
docs = vector_store.similarity_search(patient_history)     # 计算相似度,并把相似度高的chunk放在前面
knowledge = [doc.page_content for doc in docs[:5]]  # 提取chunk的文本内容
prompt = f"知识库:{knowledge}\n问题如下:\n{patient_input}"

4. 对话交互

最后是把 prompt 送到模型中得到输出,与模型的交互在上一篇文章中有详细的介绍,这里就不赘述了,直接给出完整的代码(运行时要注意模型路径、知识库路径是否正确)。

ini 复制代码
from langchain.document_loaders import UnstructuredFileLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings.huggingface import HuggingFaceEmbeddings
from langchain.vectorstores import FAISS
from transformers import AutoModelForCausalLM, AutoTokenizer
from transformers.generation import GenerationConfig
import torch
import os


def load_model(model_path):
    tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
    model = AutoModelForCausalLM.from_pretrained(model_path, 
                                                 device_map="auto", 
                                                 trust_remote_code=True
                                                 ).eval()

    return tokenizer, model


def load_knowledge():
    filepath = "./knowledge.txt"
    loader = UnstructuredFileLoader(filepath)
    docs = loader.load()

    # chunk-size是文本最大的字符数。chunk-overlap是前后两个chunk的重叠部分最大字数
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=40)
    docs = text_splitter.split_documents(docs)

    embeddings = HuggingFaceEmbeddings(model_name="./text2vec-large-chinese",
                                       model_kwargs={'device': 'cuda'}.

    # 指定向量化文档加载/保存路径
    save_path = "./med_faiss_store.faiss"
    if not os.path.exists(save_path):
        vector_store = FAISS.from_documents(docs, embeddings)
        vector_store.save_local(save_path)

    else:
        vector_store = FAISS.load_local(save_path, embeddings=embeddings)

    return vector_store


def clear_screen():
    os.system('clear')
    return [], ""


def chat_qwen(model, tokenizer, SYSTEM_PROMPT, with_knowledge=False):
    vector_store = load_knowledge()
    history, patient_history = clear_screen()

    while True:
        patient_input = input("user:")
        patient_history += patient_input
        if patient_input.lower() == "clc":
            history, patient_history = clear_screen()
            continue

        if with_knowledge:
            docs = vector_store.similarity_search(patient_history)     # 计算相似度,并把相似度高的chunk放在前面
            knowledge = [doc.page_content for doc in docs[:5]]  # 提取chunk的文本内容
            prompt = f"知识库:{knowledge}\n问题如下:\n{inputs}"

        else:
            prompt = inputs

        response, _ = model.chat(tokenizer, prompt, history=history, system=SYSTEM_PROMPT)
        history.append((patient_input, response))    # history 不包括系统提示和知识库信息
        print("assistant:", response, end="\n\n")


if __name__ == '__main__':
    model_path = "/root/autodl-tmp/LLM_MODEL/Qwen-1_8B-Chat"

    SYSTEM_PROMPT = ""

    tokenizer, model = load_model(model_path)
    chat_qwen(model, tokenizer, SYSTEM_PROMPT, with_knowledge=True)
相关推荐
wshzd13 分钟前
LLM之Agent(六十八)|PI(七)构建测试与开发流程
人工智能
H03111698515 分钟前
App竞品数据平台功能梳理:月狐数据、七麦数据、点点数据
人工智能
YOLO数据集集合20 分钟前
高铁轨道紧固件损坏检测数据集 | 高铁巡检 紧固件缺陷 轨道安全9093期
人工智能·目标检测·计算机视觉·目标跟踪·轨道·铁轨紧固件·铁轨
冬奇Lab22 分钟前
一天一个开源项目(第223篇):TeamAI-CLI —— 腾讯开源的团队级 AI Agent 中间层,让每个人的 AI 能力变成团队共享能力
人工智能·开源·资讯
ShineWinsu29 分钟前
对于Coze—AI:SDK的解析
人工智能·python·ai·sdk·项目·coze·字节跳动
代码方舟36 分钟前
零信任架构实战:基于天远名下企业A构建自动化B2B供应链准入网关
人工智能·ai·工具分享
IvorySQL1 小时前
IvorySQL 5.6 发布:PG 18.6 内核升级,沙盒即开即用
数据库·人工智能·postgresql
右耳朵猫AI1 小时前
Python周刊2026W38 | 标准流编码修复、PEP 845/846 草案、解析器提速 10%、集合字典二次复杂度
python·ai·数据科学
俗人六哥AI企业获客盈利系统1 小时前
知识库才是AI落地的分水岭
人工智能·线性代数·矩阵·自动化
微软技术分享1 小时前
大模型网络结构:模型接口测试用例参考
python