1小时搭建“汪汪宠物狗”公司的N种模态文档的RAG智能对话机器人

项目名称:1小时搭建“汪汪宠物狗”公司的N种模态文档的RAG智能对话机器人

报告日期:2024818

项目负责人:“汪汪宠物狗”公司创始人“汪汪”

项目概述:

大型语言模型(‌LLM在实际应用中,尽管功能强大且多领域适用,但常出现幻觉现象,即生成的内容包含编造成分或错误。为解决这一问题,引入了LLM-RAG框架

LLM-RAG通过结合检索和生成技术,提高了信息检索的准确性和效率,并能生成更贴近用户需求的文本。其检索库可定期更新,确保生成的文本内容基于最新信息,同时增强了回复的可解释性和定制能力‌1

研究表明,与无RAGLLM相比,‌RAG系统在回答问题方面的可靠性显著提高。在没有上下文的情况下,‌LLM平均只能正确回答34.7%的问题,而有了RAG准确率提高到了94%‌2这表明LLM-RAG框架在提升LLM性能方面具有显著优势。

基于NVIDIA AI 的智能库NIM,能够在1小时内快速地完成搭建基于文本,网页等等N种模态文档资料的RAG增强问答系统。当前版本代码,只支持了网页,txt格式2种模态文档数据的检索增强生成。后续版本会支持语音,图像,图表,PDF等等更多N种模态文档数据的检索增强生成。希望粉丝们,宝子们多多支持哦。

技术方案与实施步骤

模型选择:

RAG检索增强生成 

RAG检索增强生成(Retrieval Augmented Generation, RAG)是一种结合信息检索和语言生成的技术,旨在提高对用户查询的响应准确性和丰富性。在这一流程中,用户提出问题后,系统通过检索器从多种信息源(如维基百科和私有文档)中获取相关数据。检索到的信息被整合成增强提示,随后通过大型语言模型生成最终答案。这种方法不仅能够提供准确的答案,还能确保信息的时效性和多样性,从而提升用户体验。RAG技术在处理复杂查询和提供背景信息方面展现出了显著的优势,成为现代人工智能系统中重要的组成部分。

RAG框架如下图所示。这张图展示了检索增强生成(RAG)的工作流程,具体步骤如下:

用户提问:用户提出问题,例如“英伟达(NVIDIA)是什么时候成立的?”。

检索器:系统通过检索器访问公开可用的信息(如维基百科)和内部/私有文档(如PDF、DOC等),以获取相关数据。

向量数据库:检索到的信息被存储在向量数据库中,便于快速检索。

增强提示:系统将用户的问题与从数据库中检索到的信息结合,生成增强提示。这一提示包含了相关的上下文信息,例如“英伟达成立于1993年4月5日,由2024年首席执行官黄仁勋(Jensen Huang)创办”。

生成器(LLM):增强提示被发送到大型语言模型(LLM),模型根据提供的上下文生成回答。

AI生成的答案:最终,系统返回生成的答案,例如“英伟达成立于1993年”。

这一流程有效结合了信息检索与生成技术,能够快速、准确地回答用户的问题,提升了用户体验。

LLM-RAG

LLM-RAG(大型语言模型与检索增强生成)是一种结合了大型语言模型(LLM)和信息检索技术的先进框架,旨在提高信息获取和生成的效率与准确性。LLM-RAG通过将检索技术与生成模型结合,能够在面对复杂问题时,提供更准确和上下文相关的回答。其核心思想是利用外部知识库来增强模型的生成能力。

LLM-RAG框架如下图所示。这张图展示了检索增强生成(RAG)的工作流程,具体步骤如下:

文档检索与处理:

数据源:系统从企业知识库中检索各种文档,通常包括PDF文件和其他格式的文档。这些文档包含了组织内的知识和信息。

预处理:检索到的文档经过预处理,以提取有用的信息和结构化数据。预处理的目的是清理数据,使其适合后续的嵌入生成。

文档嵌入:经过预处理的文档被输入到嵌入模型中,生成文档嵌入(document embeddings)。这些嵌入是高维向量,能够捕捉文档的语义特征,使得相似文档能够在向量空间中靠近。

用户查询与响应生成:

用户交互:用户通过聊天机器人网页应用提交查询。用户的问题可以是具体的请求或信息查询。

查询处理:系统接收用户查询,并将其转换为嵌入形式,形成嵌入查询(embedded query)。此步骤确保查询能够与文档嵌入进行有效匹配。

向量数据库检索:嵌入查询被发送到向量数据库,系统根据相似度检索相关的文档嵌入。向量数据库能够快速找到与用户查询相匹配的文档。

生成响应:检索到的相关文档嵌入与用户查询一起被送入大型语言模型(LLM)。LLM利用这些信息生成响应,可能会进行提示调优(prompt tuning),以提高生成文本的质量和相关性。

实时响应:最终,系统生成的文本响应被返回给用户,提供实时、准确的信息。

这一流程的优势在于,它结合了信息检索的高效性和语言生成的灵活性,能够处理复杂的查询并提供上下文丰富的答案。通过这种方式,LLM-RAG不仅提升了用户体验,还能在各种应用场景中发挥重要作用,如客户支持、知识管理和信息检索等领域。

 AI-Agent 

AI-Agent是一种智能系统,能够自主感知环境、做出决策并执行任务。它通过模块化结构实现功能,包括记忆模块(存储信息)、Agent核心(处理用户请求)、规划模块(制定行动计划)和工具执行(调用外部资源),从而在无需人工干预的情况下高效地完成复杂任务。

AI-Agent是一种先进的智能系统,具备感知环境、进行决策和执行任务的能力。其核心功能包括:

自主执行任务:AI-Agent能够独立完成指定任务,无需人工干预,提升了工作效率。

环境感知和决策能力:通过分析环境信息和用户请求,AI-Agent可以做出合理的决策,以适应动态变化的环境。

模块化结构:

记忆模块:负责信息的存储和检索,帮助Agent在执行任务时维持上下文和历史记录。

Agent核心:作为系统的控制中心,处理用户请求并协调各个模块之间的交互。

规划模块:根据目标制定详细的行动计划,确保任务的有效执行和资源的合理利用。

工具执行:能够调用外部工具和资源,以完成具体的操作和任务。

通过以上模块的协同工作,AI-Agent不仅能够灵活应对复杂任务,还能在不断变化的环境中优化决策过程,从而提升整体工作效率和智能化水平。

AI-Agent的框架如下图所示。

数据的构建:

网页格式数据处理:

使用 BeautifulSoup 工具从指定网页(比如,宠物狗的百度百科这个网址 https://baike.baidu.com/item/%E5%AE%A0%E7%89%A9%E7%8B%97/6677317?fr=ge_ala)提取信息。通过解析HTML文档,提取所需的文本内容、标题和其他结构化数据,以便后续处理和分析。在提取信息后,进行数据清洗,包括:去除HTML标签:清除多余的标签,保留纯文本内容。去除冗余信息:删除无关的文本,如广告、导航链接等。统一格式:标准化文本格式,确保一致性(如大小写、空格处理)。

文本格式数据处理:

对于TXT文本数据,直接读取文件内容,确保数据的完整性和原始性。在此过程中,进行以下预处理步骤:去除空行和多余空格:清理文本中的空行和多余的空格,确保数据整洁。字符编码处理:确保文本的字符编码一致,避免乱码问题。分词与标记化:将文本分割为单词或句子或段落,以便后续分析和处理。通过上述数据清洗和预处理步骤,确保数据的质量,为后续的机器学习和深度学习任务提供高质量的基础数据。

向量化处理方法:

在数据处理的过程中,向量化是将文本数据转换为高维向量的重要步骤,这一过程能够使得文本数据适用于机器学习和深度学习任务。具体实现中,我们使用 NVIDIAEmbeddings 模型进行向量化,示例代码如下:

embeddings = NVIDIAEmbeddings(model="ai-embed-qa-4")

此模型将文本数据转换为高维向量,使其能够在机器学习和深度学习任务中使用。

向量化处理将文本转换为数值形式,显著提高了计算效率,便于进行高效的数学运算和模型训练。它能够捕捉文本中的语义关系,从而提升自然语言处理任务的准确性。此外,向量化支持相似度计算,适用于信息检索和推荐系统,且有助于降维和特征提取,减少计算复杂性。最终,向量化增强了模型的泛化能力,使其更好地适应不同的输入数据,从而为后续的机器学习和深度学习任务奠定了高质量的数据基础。

功能整合  

主要思路:

本方案主要在AI-Agent中进行了RAG检索增强生成,而且RAG的数据源支持了txt格式文件和网页格式的2种模态的增强文档资料,这就可以实现一些特定信息应用需求。比如,

有一个名字叫“汪汪宠物狗”的公司:存在一个名称是“汪汪宠物狗”的公司,“汪汪宠物狗”公司创始人是“汪汪”,这个公司成立于宇宙时间100001000100日。公司的简单介绍如下:1.‌公司愿景致力于打造一个宠物狗与主人共享幸福生活的乐园,让每一只宠物狗都能得到最好的关爱和照顾。2.‌公司使命通过提供专业的宠物狗服务,传播宠物文化,增进人与宠物之间的情感联系,提升宠物狗的生活品质。3.‌核心价值观:爱:以爱为出发点,对待每一只宠物狗如同家人一般。专业:不断学习和创新,供最高水平的宠物狗服务和产品。责任:承担起对宠物狗、客户和社会的责任,积极参与公益活动。4.‌公司历史与创始人故事公司由一位热爱宠物狗的资深宠物爱好者创立,/她曾经救助过许多流浪狗,并深感宠物狗给人类带来的快乐和陪伴是无价的。因此,决定创立这家公司,将这份爱传递给更多的宠物狗和它们的主人。5.‌公司特色项目“宠物狗才艺秀”:定期举办宠物狗才艺比赛,展示宠物狗的智慧和才能。“宠物狗公益行”:组织志愿者团队,参与宠物狗的救助和领养活动,推广宠物保护意识。“宠物狗主题度假村”:打造一个宠物狗和主人可以共同享受的度假村,提供豪华住宿和宠物设施。6.‌公司成就与荣誉荣获“宇宙时间10000年度的最佳宠物服务品牌”称号,由宠物行业协会颁发。成为多家宠物慈善机构的合作伙伴,共同推动宠物福利事业。累计帮助数千只流浪狗找到温暖的家,受到社会各界的广泛赞誉。7.‌公司未来规划扩大业务范围,开设更多的宠物狗专卖店和服务中心。推出自主研发的宠物狗食品和保健品系列,注重天然和健康。建立宠物狗训练学校,提供专业的宠物狗训练课程和教育服务。8.‌公司文化与氛围公司内部充满活力和创意,鼓励员工提出新的想法和建议。定期举办员工培训和团队建设活动,提升员工的专业技能和服务意识。营造一个温馨、和谐的工作环境,让员工和宠物狗都能感受到家的温暖。

假如我想检索公司名称是“汪汪宠物狗”公司,使用LLM肯定是没有结果,但是如果加入了这个txt文档内容的限制条件,就可以返回我想要的结果了。

同样,假如我想基于某个网页资料的内容,进行相关问题的检索,也可以得到我想要的检索结果信息。

本文中的案例,可以尝试输入一下文本,就可以得到自己想要的检索结果了。

宠物狗的介绍?

宠物狗的介绍?请将结果翻译成中文。

宠物狗的百度百科的介绍?

宠物狗的百度百科的介绍?请将结果翻译成中文。

汪汪宠物狗的公司的介绍?

汪汪宠物狗的公司的介绍,请将结果翻译成中文。

多模态文档数据:

代码中已经实现了txt格式文档和网页2种模态的文档数据的RAG检索增强生成,具体修改的位置如下所示。

def create_embeddings(embedding_path: str = "./embed"):

    embedding_path = "./embed"
    print(f"Storing embeddings to {embedding_path}")

    # 包含 宠物狗 技术文档的网页列表
    urls = [
        "https://baike.baidu.com/item/%E5%AE%A0%E7%89%A9%E7%8B%97/6677317?fr=ge_ala"
    ]
    
def create_embeddings_text(text_path: str = "./zh_data/", embedding_path: str = "./embed"):
    embedder = NVIDIAEmbeddings(model="ai-embed-qa-4")
    
    # Here we read in the text data and prepare them into vectorstore
ps = os.listdir(text_path)

绿色的可以进行网址的修改,以及txt格式文件路径修改

这样就可以在AI-Agent中进行RAG检索增强生成了,而且RAG的数据源支持了txt格式文件和网页格式的2种模态的增强文档资料,这就可以实现一些特定信息应用需求。

比如,有一个名字叫“汪汪宠物狗”的公司:存在一个名称是“汪汪宠物狗”的公司。而且有相关的txt格式文档或者网页资料数据。

多模态文档数据合并:

AI-Agent中进行RAG检索增强生成了,而且RAG的数据源支持了txt格式文件和网页格式的2种模态的增强文档资料,这就可以实现一些特定信息应用需求。

比如,有一个名字叫“汪汪宠物狗”的公司:存在一个名称是“汪汪宠物狗”的公司。而且有相关的txt文档或者网页资料数据。

代码中实现了2个函数,因为使用了同一个Embedding Model 的嵌入向量模型,比如

embeddings = NVIDIAEmbeddings(model="ai-embed-qa-4") 

所以,可以直接对数据库进行更新写入即可

create_embeddings()  ## 网页数据
create_embeddings_text() ## txt格式文档数据
### 使用定义好的相关函数和工具执行文档嵌入Embeddings的生成

import os
import shutil
def ensure_empty_directory(dir_path):
    if os.path.exists(dir_path):
        # 如果文件夹存在,删除它
        shutil.rmtree(dir_path)
ensure_empty_directory("./embed")
## 
create_embeddings()  ## 网页数据
create_embeddings_text() ## txt格式文档数据
embedding_model = NVIDIAEmbeddings(model="ai-embed-qa-4")
    # Step 6a - 将文档处理到 faiss vectorstore 并将其保存到磁盘
    # 只需要执行一次,后面可以重读已经保存的向量存储
    text_splitter = CharacterTextSplitter(chunk_size=400, separator="。")
    docs = []
    metadatas = []
    
    dest_embed_dir = embedding_path
    
    for i, d in enumerate(documents):
        splits = text_splitter.split_text(d)
        # print('splits: ', len(splits), len(splits[0]), splits)
        #print(len(splits))
        docs.extend(splits)
        metadatas.extend([{"source": sources[i]}] * len(splits))

        # print('docs: ', docs)
        # print('metadatas: ', metadatas)
        docs = remove_control_chars(docs)
        # print('docs: ', docs)
        # print('metadatas: ', metadatas)

    
        # 创建embeddings嵌入并通过FAISS进行向量存储
        if os.path.exists(dest_embed_dir):
            update = FAISS.load_local(folder_path=dest_embed_dir, embeddings=embedder, allow_dangerous_deserialization=True)
            update.add_texts(docs, metadatas=metadatas)
            update.save_local(folder_path=dest_embed_dir)
        else:
            os.makedirs(dest_embed_dir)
            docsearch = FAISS.from_texts(docs, embedder , metadatas=metadatas)
            docsearch.save_local(folder_path=dest_embed_dir)

多模态AI-Agent智能聊天机器人的封装和部署:

使用将多模态智能体封装进Gradio,需要要对函数再进行一次封装,需要满足gr.Interface的函数调用格式。

对于服务api的端口只能使用一次,需要再次更改端口号码。

### 将多模态智能体封装进Gradio 

def chat_gr(query):
    qa = ConversationalRetrievalChain(
    retriever=docsearch.as_retriever(),
    combine_docs_chain=doc_chain,
    memory=memory,
    question_generator=question_generator,
)
    result = qa({"question": query})
    rag_result = result.get("answer")
    return  rag_result

import gradio as gr
multi_modal_chat_agent = gr.Interface(fn=chat_gr,
                    inputs=['text'],
                    outputs=['text'],
                    title="Multi Modal chat agent",
                    description="Multi Modal chat agent",
                    allow_flagging="never")

multi_modal_chat_agent.launch(debug=True, share=False, show_api=False, server_port=5003, server_name="0.0.0.0")

本地服务器可以使用一下链接,在浏览器中直接进行访问了。

http://localhost:5003/

实施步骤:

环境搭建:

## 配置环境:
##  Win11系统,Anaconda安装完之后,打开Anaconda Powershell Prompt:
######
conda create --name ai_endpoint python=3.8
conda activate ai_endpoint
pip install -i https://mirrors.aliyun.com/pypi/simple  langchain-nvidia-ai-endpoints
pip install -i https://mirrors.aliyun.com/pypi/simple  jupyterlab
pip install -i https://mirrors.aliyun.com/pypi/simple  langchain_core
pip install -i https://mirrors.aliyun.com/pypi/simple  langchain
pip install -i https://mirrors.aliyun.com/pypi/simple  matplotlib
pip install -i https://mirrors.aliyun.com/pypi/simple  numpy
pip install -i https://mirrors.aliyun.com/pypi/simple  faiss-cpu==1.7.2
pip install -i https://mirrors.aliyun.com/pypi/simple  openai
pip install -i https://mirrors.aliyun.com/pypi/simple gradio
pip install -i https://mirrors.aliyun.com/pypi/simple -U langchain-community
######
## 启动 jupyter-lab
jupyter-lab

代码实现:

### 导入相关包

from langchain_nvidia_ai_endpoints import ChatNVIDIA
from langchain_nvidia_ai_endpoints import NVIDIAEmbeddings
from langchain.chains import ConversationalRetrievalChain, LLMChain
from langchain.chains.conversational_retrieval.prompts import CONDENSE_QUESTION_PROMPT, QA_PROMPT
from langchain.chains.question_answering import load_qa_chain
from langchain.memory import ConversationBufferMemory
from langchain.vectorstores import FAISS
from langchain.text_splitter import RecursiveCharacterTextSplitter

### 获取 NVIDIA API key

import getpass
import os
if not os.environ.get("NVIDIA_API_KEY", "").startswith("nvapi-"):
    nvapi_key = getpass.getpass("Enter your NVIDIA API key: ")
    assert nvapi_key.startswith("nvapi-"), f"{nvapi_key[:5]}... is not a valid key"
    os.environ["NVIDIA_API_KEY"] = nvapi_key

### 定义html_document_loader网络文档数据加载器:进行文档解析返回文档内容

import re
from typing import List, Union

import requests
from bs4 import BeautifulSoup

def html_document_loader(url: Union[str, bytes]) -> str:
    """
    Loads the HTML content of a document from a given URL and return it's content.

    Args:
        url: The URL of the document.

    Returns:
        The content of the document.

    Raises:
        Exception: If there is an error while making the HTTP request.

    """
    try:
        response = requests.get(url)
        html_content = response.text
    except Exception as e:
        print(f"Failed to load {url} due to exception {e}")
        return ""

    try:
        # 创建Beautiful Soup对象用来解析html
        soup = BeautifulSoup(html_content, "html.parser")

        # 删除脚本和样式标签
        for script in soup(["script", "style"]):
            script.extract()

        # 从 HTML 文档中获取纯文本
        text = soup.get_text()

        # 去除空格换行符
        text = re.sub("\s+", " ", text).strip()

        return text
    except Exception as e:
        print(f"Exception {e} while loading document")
        return ""



### 定义数据向量化工具

def create_embeddings(embedding_path: str = "./embed"):

    embedding_path = "./embed"
    print(f"Storing embeddings to {embedding_path}")

    # 包含 宠物狗 技术文档的网页列表
    urls = [
        "https://baike.baidu.com/item/%E5%AE%A0%E7%89%A9%E7%8B%97/6677317?fr=ge_ala"
    ]
    

    # 使用html_document_loader对 宠物狗 文档数据进行加载
    documents = []
    for url in urls:
        document = html_document_loader(url)
        documents.append(document)

    #进行chunk分词分块处理
    text_splitter = RecursiveCharacterTextSplitter(
        chunk_size=1000,
        chunk_overlap=0,
        length_function=len,
    )
    texts = text_splitter.create_documents(documents)    
    index_docs(url, text_splitter, texts, embedding_path)
    print("Generated embedding successfully")

import re
def remove_control_chars(text_list):
    for index, text in enumerate(text_list):
        if not isinstance(text, str):
            raise ValueError("Input must be a string")
        # text_list[index] = re.sub(r'[\u0000-\u001F\u007F-\u009F\u200C\u200D\n\r\t]', '', text)
        text_list[index] = text
    return text_list
    
### 定义index_docs函数作为构建向量储存和文档检索工具
def index_docs(url: Union[str, bytes], splitter, documents: List[str], dest_embed_dir) -> None:
    """
    Split the document into chunks and create embeddings for the document

    Args:
        url: Source url for the document.
        splitter: Splitter used to split the document
        documents: list of documents whose embeddings needs to be created
        dest_embed_dir: destination directory for embeddings

    Returns:
        None
    """
    # 通过NVIDIAEmbeddings工具类调用NIM中的"ai-embed-qa-4"向量化模型
    embeddings = NVIDIAEmbeddings(model="ai-embed-qa-4")
    # Step 6a - 将文档处理到 faiss vectorstore 并将其保存到磁盘
    text_splitter = CharacterTextSplitter(chunk_size=400, separator="。")
    docs = []
    metadatas = []
    # 只需要执行一次,后面可以重读已经保存的向量存储
    for document in documents:        
        texts_raw = splitter.split_text(document.page_content)  
        # print('texts_raw****: ', len(texts_raw), len(texts_raw[0]) ,texts_raw)
        splits = text_splitter.split_text(texts_raw[0])
        # print('splits****: ', len(splits), len(splits[0]) ,splits)
        #print(len(splits))
        docs.extend(splits)
        metadatas.extend([document.metadata]*len(splits))

        # print('docs: ', docs)
        # print('metadatas: ', metadatas)
        docs = remove_control_chars(docs)
        # print('docs: ', docs)
        # print('metadatas: ', metadatas)
    
        # 创建embeddings嵌入并通过FAISS进行向量存储
        if os.path.exists(dest_embed_dir):
            update = FAISS.load_local(folder_path=dest_embed_dir, embeddings=embeddings, allow_dangerous_deserialization=True)
            update.add_texts(docs, metadatas=metadatas)
            update.save_local(folder_path=dest_embed_dir)
        else:
            os.makedirs(dest_embed_dir)
            docsearch = FAISS.from_texts(docs, embeddings , metadatas=metadatas)
            docsearch.save_local(folder_path=dest_embed_dir)

### 获取文本数据集并,追加保存到向量数据库
import os
from tqdm import tqdm
from pathlib import Path
from langchain.text_splitter import CharacterTextSplitter
from langchain_nvidia_ai_endpoints import NVIDIAEmbeddings

def create_embeddings_text(text_path: str = "./zh_data/", embedding_path: str = "./embed"):
    embedder = NVIDIAEmbeddings(model="ai-embed-qa-4")
    
    # Here we read in the text data and prepare them into vectorstore
    ps = os.listdir(text_path)
    data = []
    sources = []
    for p in ps:
        if p.endswith('.txt'):
            path2file=text_path+p
            with open(path2file,encoding="utf-8") as f:
                lines=f.readlines()
                for line in lines:
                    if len(line)>=1:
                        data.append(line)
                        sources.append(path2file)
    
    # Step 5 - 进行一些基本的清理并删除空行
    documents=[d for d in data if d != '\n']
    len(data), len(documents), data[0]
    
    # Step 6a - 将文档处理到 faiss vectorstore 并将其保存到磁盘
    # 只需要执行一次,后面可以重读已经保存的向量存储
    text_splitter = CharacterTextSplitter(chunk_size=400, separator="。")
    docs = []
    metadatas = []
    
    dest_embed_dir = embedding_path
    
    for i, d in enumerate(documents):
        splits = text_splitter.split_text(d)
        # print('splits: ', len(splits), len(splits[0]), splits)
        #print(len(splits))
        docs.extend(splits)
        metadatas.extend([{"source": sources[i]}] * len(splits))

        # print('docs: ', docs)
        # print('metadatas: ', metadatas)
        docs = remove_control_chars(docs)
        # print('docs: ', docs)
        # print('metadatas: ', metadatas)

    
        # 创建embeddings嵌入并通过FAISS进行向量存储
        if os.path.exists(dest_embed_dir):
            update = FAISS.load_local(folder_path=dest_embed_dir, embeddings=embedder, allow_dangerous_deserialization=True)
            update.add_texts(docs, metadatas=metadatas)
            update.save_local(folder_path=dest_embed_dir)
        else:
            os.makedirs(dest_embed_dir)
            docsearch = FAISS.from_texts(docs, embedder , metadatas=metadatas)
            docsearch.save_local(folder_path=dest_embed_dir)


### 使用定义好的相关函数和工具执行文档嵌入Embeddings的生成

import os
import shutil
def ensure_empty_directory(dir_path):
    if os.path.exists(dir_path):
        # 如果文件夹存在,删除它
        shutil.rmtree(dir_path)
ensure_empty_directory("./embed")
## 
create_embeddings()
create_embeddings_text()
embedding_model = NVIDIAEmbeddings(model="ai-embed-qa-4")

# Embed documents
embedding_path = "embed/"
docsearch = FAISS.load_local(folder_path=embedding_path, embeddings=embedding_model, allow_dangerous_deserialization=True)



### langchain结合NIM实现LLM-RAG检索:并对比未使用RAG的llm输出与使用LLM-RAG的输出效果

llm = ChatNVIDIA(model="ai-mixtral-8x7b-instruct")
result = llm.invoke("Tell me something about nemo")
print(result.content)

llm = ChatNVIDIA(model="ai-mixtral-8x7b-instruct")

memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)

question_generator = LLMChain(llm=llm, prompt=CONDENSE_QUESTION_PROMPT)

chat = ChatNVIDIA(model="ai-mixtral-8x7b-instruct", temperature=0.1, max_tokens=1000, top_p=1.0)

doc_chain = load_qa_chain(chat , chain_type="stuff", prompt=QA_PROMPT)

qa = ConversationalRetrievalChain(
    retriever=docsearch.as_retriever(),
    combine_docs_chain=doc_chain,
    memory=memory,
    question_generator=question_generator,
)

query = "宠物狗的介绍?"
print(query)
query = "宠物狗的介绍?请将结果翻译成中文。"
print(query)
query = "宠物狗的百度百科的介绍?"
print(query)
query = "宠物狗的百度百科的介绍?请将结果翻译成中文。"
print(query)
query = "汪汪宠物狗的公司的介绍?"
print(query)
query = "汪汪宠物狗的公司的介绍,请将结果翻译成中文。"
print(query)




### 将多模态智能体封装进Gradio 

def chat_gr(query):
    qa = ConversationalRetrievalChain(
    retriever=docsearch.as_retriever(),
    combine_docs_chain=doc_chain,
    memory=memory,
    question_generator=question_generator,
)
    result = qa({"question": query})
    rag_result = result.get("answer")
    return  rag_result

import gradio as gr
multi_modal_chat_agent = gr.Interface(fn=chat_gr,
                    inputs=['text'],
                    outputs=['text'],
                    title="Multi Modal chat agent",
                    description="Multi Modal chat agent",
                    allow_flagging="never")

multi_modal_chat_agent.launch(debug=True, share=False, show_api=False, server_port=5003, server_name="0.0.0.0")

测试与调优:

代码中已经实现了txt格式文档和网页2种模态的文档数据的RAG检索增强生成,具体修改的位置如下所示。

def create_embeddings(embedding_path: str = "./embed"):

    embedding_path = "./embed"
    print(f"Storing embeddings to {embedding_path}")

    # 包含 宠物狗 技术文档的网页列表
    urls = [
        "https://baike.baidu.com/item/%E5%AE%A0%E7%89%A9%E7%8B%97/6677317?fr=ge_ala"
    ]
    
def create_embeddings_text(text_path: str = "./zh_data/", embedding_path: str = "./embed"):
    embedder = NVIDIAEmbeddings(model="ai-embed-qa-4")
    
    # Here we read in the text data and prepare them into vectorstore
ps = os.listdir(text_path)

绿色的可以进行网址的修改,以及txt格式文件路径修改

这样就可以在AI-Agent中进行RAG检索增强生成了,而且RAG的数据源支持了txt格式文件和网页格式的2种模态的增强文档资料,这就可以实现一些特定信息应用需求。

比如,有一个名字叫“汪汪宠物狗”的公司:存在一个名称是“汪汪宠物狗”的公司。而且有相关的txt格式文档或者网页资料数据。

假如我想检索公司名称是“汪汪宠物狗”公司,使用LLM肯定是没有结果,但是如果加入这个txt格式文档内容的限制条件,就可以返回我想要的结果了。

本文中的案例,可以尝试输入一下文本,就可以得到自己想要的检索结果了。

宠物狗的介绍?

宠物狗的介绍?请将结果翻译成中文。

宠物狗的百度百科的介绍?

宠物狗的百度百科的介绍?请将结果翻译成中文。

汪汪宠物狗的公司的介绍?

汪汪宠物狗的公司的介绍,请将结果翻译成中文。

集成与部署:

AI-Agent中进行RAG检索增强生成了,而且RAG的数据源支持了txt格式文件和网页格式的2种模态的增强文档资料,这就可以实现一些特定信息应用需求。

比如,有一个名字叫“汪汪宠物狗”的公司:存在一个名称是“汪汪宠物狗”的公司。而且有相关的txt或者网页资料数据。

 代码中实现了2个函数,因为使用了同一个Embedding Model 的嵌入向量模型,比如

embeddings = NVIDIAEmbeddings(model="ai-embed-qa-4") 

所以,可以直接对数据库进行更新写入即可

create_embeddings()  ## 网页数据
create_embeddings_text() ## txt格式文档数据
### 使用定义好的相关函数和工具执行文档嵌入Embeddings的生成

import os
import shutil
def ensure_empty_directory(dir_path):
    if os.path.exists(dir_path):
        # 如果文件夹存在,删除它
        shutil.rmtree(dir_path)
ensure_empty_directory("./embed")
## 
create_embeddings()  ## 网页数据
create_embeddings_text() ## txt格式文档数据
embedding_model = NVIDIAEmbeddings(model="ai-embed-qa-4")

 使用将多模态智能体封装进Gradio,需要要对函数再进行一次封装,需要满足gr.Interface的函数调用格式。

对于服务api的端口只能使用一次,需要再次更改端口号码。

### 将多模态智能体封装进Gradio 

def chat_gr(query):
    qa = ConversationalRetrievalChain(
    retriever=docsearch.as_retriever(),
    combine_docs_chain=doc_chain,
    memory=memory,
    question_generator=question_generator,
)
    result = qa({"question": query})
    rag_result = result.get("answer")
    return  rag_result

import gradio as gr
multi_modal_chat_agent = gr.Interface(fn=chat_gr,
                    inputs=['text'],
                    outputs=['text'],
                    title="Multi Modal chat agent",
                    description="Multi Modal chat agent",
                    allow_flagging="never")

multi_modal_chat_agent.launch(debug=True, share=False, show_api=False, server_port=5003, server_name="0.0.0.0")

本地服务器可以使用一下链接,在浏览器中直接进行访问了。

http://localhost:5003/

说明各模块集成方法及最终部署到实际运行环境的步骤。

使用将多模态智能体封装进Gradio,需要要对函数再进行一次封装,需要满足gr.Interface的函数调用格式。

对于服务api的端口只能使用一次,需要再次更改端口号码。

### 将多模态智能体封装进Gradio 

def chat_gr(query):
    qa = ConversationalRetrievalChain(
    retriever=docsearch.as_retriever(),
    combine_docs_chain=doc_chain,
    memory=memory,
    question_generator=question_generator,
)
    result = qa({"question": query})
    rag_result = result.get("answer")
    return  rag_result

import gradio as gr
multi_modal_chat_agent = gr.Interface(fn=chat_gr,
                    inputs=['text'],
                    outputs=['text'],
                    title="Multi Modal chat agent",
                    description="Multi Modal chat agent",
                    allow_flagging="never")

multi_modal_chat_agent.launch(debug=True, share=False, show_api=False, server_port=5003, server_name="0.0.0.0")

本地服务器可以使用一下链接,在浏览器中直接进行访问了。

http://localhost:5003/

项目成果与展示:

应用场景展示:

1)在AI-Agent中进行了RAG检索增强生成,而且RAG的数据源支持了txt格式文件和网页格式的2种模态的增强文档资料,这就可以实现一些特定信息应用需求。比如,

有一个名字叫“汪汪宠物狗”的公司:存在一个名称是“汪汪宠物狗”的公司,“汪汪宠物狗”公司创始人是“汪汪”,这个公司成立于宇宙时间100001000100日。公司的简单介绍如下:1.‌公司愿景致力于打造一个宠物狗与主人共享幸福生活的乐园,让每一只宠物狗都能得到最好的关爱和照顾。2.‌公司使命通过提供专业的宠物狗服务,传播宠物文化,增进人与宠物之间的情感联系,提升宠物狗的生活品质。3.‌核心价值观:爱:以爱为出发点,对待每一只宠物狗如同家人一般。专业:不断学习和创新,供最高水平的宠物狗服务和产品。责任:承担起对宠物狗、客户和社会的责任,积极参与公益活动。4.‌公司历史与创始人故事公司由一位热爱宠物狗的资深宠物爱好者创立,/她曾经救助过许多流浪狗,并深感宠物狗给人类带来的快乐和陪伴是无价的。因此,决定创立这家公司,将这份爱传递给更多的宠物狗和它们的主人。5.‌公司特色项目“宠物狗才艺秀”:定期举办宠物狗才艺比赛,展示宠物狗的智慧和才能。“宠物狗公益行”:组织志愿者团队,参与宠物狗的救助和领养活动,推广宠物保护意识。“宠物狗主题度假村”:打造一个宠物狗和主人可以共同享受的度假村,提供豪华住宿和宠物设施。6.‌公司成就与荣誉荣获“宇宙时间10000年度的最佳宠物服务品牌”称号,由宠物行业协会颁发。成为多家宠物慈善机构的合作伙伴,共同推动宠物福利事业。累计帮助数千只流浪狗找到温暖的家,受到社会各界的广泛赞誉。7.‌公司未来规划扩大业务范围,开设更多的宠物狗专卖店和服务中心。推出自主研发的宠物狗食品和保健品系列,注重天然和健康。建立宠物狗训练学校,提供专业的宠物狗训练课程和教育服务。8.‌公司文化与氛围公司内部充满活力和创意,鼓励员工提出新的想法和建议。定期举办员工培训和团队建设活动,提升员工的专业技能和服务意识。营造一个温馨、和谐的工作环境,让员工和宠物狗都能感受到家的温暖。 假如我想检索公司名称是“汪汪宠物狗”公司,使用LLM肯定是没有结果,但是如果假如了这个txt的限制条件,就可以返回我想要的结果了。

同样,假如我想基于某个网页资料的内容,进行相关问题的检索,也可以得到我想要的检索结果信息。

功能演示:

本地服务器可以使用一下链接,在浏览器中直接进行访问了。

http://localhost:5003/

本文中的案例,可以尝试输入一下文本,就可以得到自己想要的检索结果了。

宠物狗的介绍?

宠物狗的介绍?请将结果翻译成中文。

宠物狗的百度百科的介绍?

宠物狗的百度百科的介绍?请将结果翻译成中文。

汪汪宠物狗的公司的介绍?

汪汪宠物狗的公司的介绍,请将结果翻译成中文。

问题与解决方案:

  1. 问题分析: 详细描述在项目实施过程中遇到的主要问题。
  2. 解决措施: 阐述针对每个问题采取的具体解决措施及心路历程,体现问题解决能力。

问题1:创新方案的构思和构建问题。

1)从RAG本身的多模态信息来源的思路,支持文本,网页,语音,图像,图表,PDF,WORD,PPT等等多种模态的增强文档资料。

2)从输入查询信息的角度,可以输入支持文本,网页,语音,图像,图表,PDF,WORD,PPT等等多种模态的增强文档资料+query文本查询信息。

选择了(1),而且选择了便于实现的就是txt格式和网页格式的2种模态的增强文档资料。

问题2:就是txt格式和网页2种模态的增强文档资料的向量数据库的合并问题。

解决措施:因为使用了同一个Embedding Model 的嵌入向量模型,比如

embeddings = NVIDIAEmbeddings(model="ai-embed-qa-4") 

所以,可以直接对数据库进行更新写入即可。

关键代码如下:

    # Step 6a - 将文档处理到 faiss vectorstore 并将其保存到磁盘
    # 只需要执行一次,后面可以重读已经保存的向量存储
    text_splitter = CharacterTextSplitter(chunk_size=400, separator="。")
    docs = []
    metadatas = []
    
    dest_embed_dir = embedding_path
    
    for i, d in enumerate(documents):
        splits = text_splitter.split_text(d)
        # print('splits: ', len(splits), len(splits[0]), splits)
        #print(len(splits))
        docs.extend(splits)
        metadatas.extend([{"source": sources[i]}] * len(splits))

        # print('docs: ', docs)
        # print('metadatas: ', metadatas)
        docs = remove_control_chars(docs)
        # print('docs: ', docs)
        # print('metadatas: ', metadatas)

    
        # 创建embeddings嵌入并通过FAISS进行向量存储
        if os.path.exists(dest_embed_dir):
            update = FAISS.load_local(folder_path=dest_embed_dir, embeddings=embedder, allow_dangerous_deserialization=True)
            update.add_texts(docs, metadatas=metadatas)
            update.save_local(folder_path=dest_embed_dir)
        else:
            os.makedirs(dest_embed_dir)
            docsearch = FAISS.from_texts(docs, embedder , metadatas=metadatas)
            docsearch.save_local(folder_path=dest_embed_dir)

问题3:遇到了使用其他网页,其他txt资料进行向量化的错误问题。

解决措施:刚开始以为是txt文档中的控制字符的问题,去掉控制字符依旧没有解决这个问题。只能一行一行的删减txt文档中的文字,发现了只要字符窜的长度太长了就会有问题。只要修改代码中字符串的长度了就可以解决问题了。也就是使用“。”进行分割就正常了。如果是英文资料需要用“.”。也可以自己编写一个分割函数,兼容2种模式。

    text_splitter = CharacterTextSplitter(chunk_size=400, separator="。")

从此推断,网页也是这个问题。修改代码如下:   

    # 通过NVIDIAEmbeddings工具类调用NIM中的"ai-embed-qa-4"向量化模型
    embeddings = NVIDIAEmbeddings(model="ai-embed-qa-4")
    # Step 6a - 将文档处理到 faiss vectorstore 并将其保存到磁盘
    text_splitter = CharacterTextSplitter(chunk_size=400, separator="。")
    docs = []
    metadatas = []
    # 只需要执行一次,后面可以重读已经保存的向量存储
    for document in documents:        
        texts_raw = splitter.split_text(document.page_content)  
        # print('texts_raw****: ', len(texts_raw), len(texts_raw[0]) ,texts_raw)
        splits = text_splitter.split_text(texts_raw[0])
        # print('splits****: ', len(splits), len(splits[0]) ,splits)
        #print(len(splits))
        docs.extend(splits)
        metadatas.extend([document.metadata]*len(splits))

        # print('docs: ', docs)
        # print('metadatas: ', metadatas)
        docs = remove_control_chars(docs)
        # print('docs: ', docs)
        # print('metadatas: ', metadatas)
    
        # 创建embeddings嵌入并通过FAISS进行向量存储
        if os.path.exists(dest_embed_dir):
            update = FAISS.load_local(folder_path=dest_embed_dir, embeddings=embeddings, allow_dangerous_deserialization=True)
            update.add_texts(docs, metadatas=metadatas)
            update.save_local(folder_path=dest_embed_dir)
        else:
            os.makedirs(dest_embed_dir)
            docsearch = FAISS.from_texts(docs, embeddings , metadatas=metadatas)
            docsearch.save_local(folder_path=dest_embed_dir)

问题4:将多模态智能体封装进Gradio错误。

解决措施:需要对函数再进行一次封装,需要满足gr.Interface的函数调用格式。对于服务api的端口只能使用一次,需要再次更改端口号码。

### 将多模态智能体封装进Gradio 

def chat_gr(query):
    qa = ConversationalRetrievalChain(
    retriever=docsearch.as_retriever(),
    combine_docs_chain=doc_chain,
    memory=memory,
    question_generator=question_generator,
)
    result = qa({"question": query})
    rag_result = result.get("answer")
    return  rag_result

import gradio as gr
multi_modal_chat_agent = gr.Interface(fn=chat_gr,
                    inputs=['text'],
                    outputs=['text'],
                    title="Multi Modal chat agent",
                    description="Multi Modal chat agent",
                    allow_flagging="never")

multi_modal_chat_agent.launch(debug=True, share=False, show_api=False, server_port=5003, server_name="0.0.0.0")

项目总结与展望:

项目评估: 对项目的整体表现进行客观评估,总结成功点和存在的不足。

目前已经实现了txt格式和网页2种模态的增强文档资料进行RAG了。但是,还有很多可以做,比如,语音,图像,PDF,WORD,PPT等等多种模态的增强文档资料都很容易集成进去的。

未来方向: 基于项目经验,提出未来可能的改进方向和发展规划。

未来会在考虑

1)在RAG阶段加入更多的模态:文本,网页,语音,图像,图表,PDF,WORD,PPT等等多种模态的增强文档资料。

(2)在输入查询信息的直接就支持:文本,网页,语音,图像,图表,PDF,WORD,PPT等等多种模态的增强文档资料输入,以及query查询信息也支持文本,网页,语音,图像,图表,PDF,WORD,PPT等等多种模态的查询信息输入了。

附件与参考资料

  • 9
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值