基于 nomic-embed-text、chromadb 和 deepseek-r1:1.5b,实现本地私有库的搭建与问答

RAG简介

RAG (Retrieval Augmented Generation) 是一种将信息检索文本生成相结合的技术,它正在成为工作和学习中的强大助理。

工作原理:

  1. 检索 (Retrieval): 当你提出一个问题或请求时,RAG 首先会从大量的外部知识库(例如维基百科、公司文档、代码库等)中检索相关的文档或片段。
  2. 增强 (Augmentation): 检索到的信息会被用来增强语言模型的上下文,使其能够更好地理解你的需求。
  3. 生成 (Generation): 最后,语言模型利用检索到的信息和自身的知识,生成更准确、更全面的答案、总结、翻译、代码等。

优势:

  • 更准确的答案: RAG 可以访问外部知识库,从而提供比仅依赖自身参数的语言模型更准确、更可靠的答案。
  • 更全面的信息: RAG 可以整合多个来源的信息,提供更全面的视角和更深入的理解。
  • 更强的适应性: RAG 可以根据不同的任务和领域选择不同的知识库,从而更好地适应各种应用场景。
  • 减少幻觉: 通过 grounding 生成的文本到检索到的信息,RAG 可以减少语言模型产生幻觉(即编造事实)的可能性。

应用场景:

  • 问答系统: 提供更准确、更全面的答案,例如客服机器人、知识库搜索。
  • 文本摘要: 生成更准确、更简洁的摘要,例如新闻摘要、论文摘要。
  • 代码生成: 根据自然语言描述生成代码,例如代码补全、代码生成工具。
  • 翻译: 提供更准确、更流畅的翻译,例如机器翻译系统。
  • 教育: 提供个性化的学习资源和辅导,例如智能辅导系统、学习助手。

RAG 应用流程

完整的 RAG 应用流程主要包含两个阶段:

RAG 系统核心技术

嵌入模型 (Embedding Models)

嵌入模型是通过训练生成向量嵌入,这是一长串数字数组,代表文本序列的关联关系。

除了文本,类似图片之类的非结构化数据也能通过模型向量化,比如我们常见的人脸识别系统。

  • 作用: 将文本(例如文档、查询、代码等)转换为稠密的向量表示,称为嵌入向量。这些向量捕捉了文本的语义信息,使得语义相似的文本在向量空间中彼此靠近。
  • 工作原理: 嵌入模型通常基于深度学习技术,例如神经网络。它们通过学习大量的文本数据,将每个单词或短语映射到一个固定维度的向量空间中。在这个空间中,语义相似的单词或短语的向量距离会更近。
  • 在 RAG 中的应用:
    • 将文档和查询转换为嵌入向量,以便于后续的相似度计算和检索。

向量数据库 (Vector Databases)

  • 作用: 专门用于存储和检索高维向量,例如文本嵌入向量。它们能够高效地执行相似性搜索,即找到与给定查询向量最相似的向量。
  • 工作原理: 向量数据库使用 specialized 的索引结构和算法来存储和检索向量。这些索引结构能够快速地找到与查询向量距离最近的向量,从而实现高效的相似性搜索。
  • 在 RAG 中的应用:
    • 存储文档的嵌入向量,以便于快速检索。
    • 根据查询向量,快速检索与之最相似的文档向量,从而找到相关文档。

生成模型 (Generation Models)

  • 作用: 根据输入的文本或信息,生成新的文本内容,例如答案、摘要、翻译、代码等。
  • 工作原理: 生成模型通常基于深度学习技术,例如循环神经网络 (RNN) 或 Transformer。它们通过学习大量的文本数据,掌握语言的语法和语义规则,从而能够生成流畅、连贯的文本。
  • 在 RAG 中的应用:
    • 根据检索到的相关文档和查询,生成最终的答案或文本内容。

其他技术

文本预处理 (Text Preprocessing)

  • 作用: 对文本进行清洗、分词、去除停用词等操作,以便于后续的嵌入和检索。
  • 常见操作:
    • 分词 (Tokenization): 将文本分割成单词或子词单元。
    • 去除停用词 (Stop Word Removal): 去除一些常见的、对语义贡献较小的词语,例如 "the", "a", "is" 等。
    • 词干提取 (Stemming) / 词形还原 (Lemmatization): 将单词的不同形态转换为其基本形式,例如 "running" -> "run"。
    • 大小写转换 (Lowercasing): 将所有字母转换为小写。
  • 在 RAG 中的应用:
    • 对文档和查询进行预处理,提高嵌入和检索的效率和准确性。

相似度度量 (Similarity Metrics)

  • 作用: 用于计算查询向量和文档向量之间的相似度,从而判断文档与查询的相关程度。
  • 常见度量方法:
    • 余弦相似度 (Cosine Similarity): 计算两个向量之间夹角的余弦值,值越大表示相似度越高。
    • 点积 (Dot Product): 计算两个向量的点积,值越大表示相似度越高。
    • 欧氏距离 (Euclidean Distance): 计算两个向量之间的欧氏距离,值越小表示相似度越高。
  • 在 RAG 中的应用:
    • 用于检索与查询最相关的文档。

重排序 (Reranking)

  • 作用: 对检索到的文档进行重新排序,以进一步提高检索结果的准确性。
  • 常见方法:
    • 交叉编码器 (Cross-Encoder): 将查询和文档一起输入到一个编码器模型中,计算它们之间的相关性得分,用于排序。
  • 在 RAG 中的应用:
    • 对初始检索结果进行精细化排序,将最相关的文档排在前面。

技术选型

选择:

  • Embedding Models:nomic-embed-text
  • Vector Databases:chromadb
  • Generation Models: deepseek-r1:1.5b

 

一、系统概述

    我们构建的本地私有库问答系统主要分为以下几个部分:

    • 数据存储:使用 chromadb 存储用户数据的向量表示,以便进行高效的相似度查询。

    • 数据嵌入:借助 nomic-embed-text 提供的嵌入功能,将用户数据转换为向量形式,方便 chromadb 进行存储和检索。

    • 查询处理:当用户提出问题时,利用 nomic-embed-text 将问题嵌入为向量,并在 chromadb 中搜索相关的用户数据。

    • 结果生成:结合检索到的相关数据和用户问题,使用 deepseek-r1:1.5b 生成最终的回答。

    嵌入模型(Embedding Models)

    • nomic-embed-text: 这个模型用于生成文本的嵌入向量。嵌入向量是一种能够表示文本语义信息的数学表达形式,它允许我们在向量空间中计算文本之间的相似度。

    向量数据库(Vector Databases)

    • chromadb: ChromaDB 是一个高效的向量数据库,用于存储、索引以及查询嵌入向量。它允许快速检索与给定查询最相似的数据点,非常适合基于内容的搜索任务。

    生成模型(Generation Models)

    • deepseek-r1:1.5b: 这是一个语言生成模型,可以基于给定的提示(prompt)生成自然语言文本。这类模型通常用于文本生成、问答系统、摘要等需要理解和生成自然语言的任务。

     

    二、环境搭建

    在开始构建系统之前,需要确保安装了以下依赖库:

    bash复制

    pip install ollama chromadb

    同时,确保 Ollama 服务正在运行,可以通过以下命令启动:

    bash复制

    ollama run

    ollama拉取deepseek及nomic-embed-text就不在赘述,官网有相关命令;

    这是我本地模型:

    C:\Users\skyvis>ollama list
    NAME                       ID              SIZE      MODIFIED
    nomic-embed-text:latest    0a109f422b47    274 MB    2 hours ago
    deepseek-r1:1.5b           a42b25d8c10a    1.1 GB    6 days ago

    三、代码实现

    以下是实现用户数据查询系统的完整代码:

    1. 初始化 ChromaDB 客户端

    Python复制

    import ollama
    import chromadb
    from chromadb.utils.embedding_functions import OllamaEmbeddingFunction
    
    chroma_client = chromadb.PersistentClient(path="./chroma_db")  # 使用持久化存储

    2. 定义嵌入模型

    Python复制

    embedding_function = OllamaEmbeddingFunction(
        model_name="nomic-embed-text:latest",
        url="http://localhost:11434/api/embeddings"  # Ollama 的嵌入 API 地址
    )

    3. 创建或获取集合

    Python复制

    try:
        collection = chroma_client.create_collection(
            name="user_data",
            embedding_function=embedding_function
        )
        print("集合已创建")
    except chromadb.errors.UniqueConstraintError as e:
        print("集合已存在,直接获取")
        collection = chroma_client.get_collection(
            name="user_data",
            embedding_function=embedding_function
        )

    4. 添加用户数据

    假设我们有以下用户数据:

    Python复制

    data = """
    姓名	年龄	手机号	地址
    张三	25	13800138000	北京市朝阳区望京SOHO
    李四	30	13900139000	上海市浦东新区陆家嘴
    王五	28	13700137000	广州市天河区珠江新城
    """

    将数据添加到 ChromaDB 中:

    Python复制

    documents = [line for line in data.split('\n')[1:] if line.strip() != '']  # 去掉空行和表头
    
    for idx, doc in enumerate(documents):
        collection.add(
            documents=[doc],
            ids=[str(idx)]
        )

    5. 查询用户数据

    定义问题并进行查询:

    Python复制

    query = "查询李四的数据"
    
    query_embedding = embedding_function([query])  # 使用列表作为输入
    
    results = collection.query(
        query_embeddings=query_embedding,
        n_results=3
    )

    6. 生成回答

    Python复制

    related_documents = results['documents'][0]
    
    if not related_documents:
        answer = "没有找到相关数据"
    else:
        context = "\n".join(related_documents)
        prompt = f"根据以下上下文:\n{context}\n回答:{query}"
        response = ollama.generate(model="deepseek-r1:1.5b", prompt=prompt)
        answer = response['response']
    
    print(answer)

    四、运行结果

    假设运行上述代码,查询“李四的数据”时,输出结果如下:

    复制

    姓名:李四
    年龄:30 岁
    手机号:13900139000
    地址:上海市浦东新区陆家嘴

    五、完整代码:

    import ollama
    import chromadb
    from chromadb.utils.embedding_functions import OllamaEmbeddingFunction
    
    # 自定义文本内容
    data = """
    姓名	年龄	手机号	地址
    张三	25	13800138000	北京市朝阳区望京SOHO
    李四	30	13900139000	上海市浦东新区陆家嘴
    王五	28	13700137000	广州市天河区珠江新城
    """
    
    # 初始化 ChromaDB 客户端
    chroma_client = chromadb.PersistentClient(path="./chroma_db")  # 使用持久化存储
    
    # 定义嵌入模型
    embedding_function = OllamaEmbeddingFunction(
        model_name="nomic-embed-text:latest",
        url="http://localhost:11434/api/embeddings"
    )
    
    # 创建或获取集合
    try:
        collection = chroma_client.create_collection(
            name="user_data",
            embedding_function=embedding_function
        )
        print("集合已创建")
    except chromadb.errors.UniqueConstraintError as e:
        print("集合已存在,直接获取")
        collection = chroma_client.get_collection(
            name="user_data",
            embedding_function=embedding_function
        )
    
    # 将自定义文本存储到向量数据库
    documents = [line for line in data.split('\n')[1:] if line.strip() != '']  # 去掉空行和表头
    for idx, doc in enumerate(documents):
        collection.add(
            documents=[doc],
            ids=[str(idx)]
        )
    
    # 定义问题
    query = "查询李四的数据"
    
    # 将问题向量化并查询数据库
    query_embedding = embedding_function([query])  # 使用列表作为输入
    results = collection.query(
        query_embeddings=query_embedding,
        n_results=3
    )
    
    # 获取最相关的文档
    related_documents = results['documents'][0]
    
    # 如果没有找到相关文档,返回默认信息
    if not related_documents:
        answer = "没有找到相关数据"
    else:
        # 将相关文档作为上下文提供给模型
        context = "\n".join(related_documents)
        prompt = f"根据以下上下文:\n{context}\n回答:{query}"
        response = ollama.generate(model="deepseek-r1:1.5b", prompt=prompt)
        answer = response['response']
    
    # 输出结果
    print(answer)

    运行结果:

    <think>
    嗯,好的,我现在需要回答用户的问题。用户说:“根据以下上下文:李四 30 岁,13900139000,上海市浦东新区陆家嘴,王五 28 岁,13700137000,广州市天河区珠江新城。姓名 年龄 数号 地址”。然后用户说“回答:查询李四的数据”
    。
    
    首先,我需要理解用户的上下文。看起来用户已经提供了三个人的信息:李四和王五,还有他们的手机号、年龄和地址。用户的问题是要“回答”,所以可能是在请求提供李四的具体信息。
    
    接下来,我要确定每个信息的具体内容:
    - 李四的年龄是30岁。
    - 声调是13900139000。
    - 地址在上海市浦东新区陆家嘴。
    - 王五的数据与李四类似,但年龄28岁,地址在广州市天河区珠江新城。
    
    现在,用户的问题是“查询李四的数据”。这意味着他们希望得到李四的详细信息。根据上下文,数据包括年龄、号码和地址。因此,我需要按照这个顺序整理出数据。
    
    首先列出姓名:李四。
    然后是年龄:30岁。
    接着号码:13900139000。
    最后地址:上海市浦东新区陆家嘴。
    
    接下来,按照用户的指示“回答”,可能需要明确写出这些信息的结构。因为用户之前已经提到了李四和王五的数据,但现在的问题是关于查询李四的数据,所以不需要重复王五的信息。
    
    总结一下,我应该按照以下格式呈现数据:
    姓名:李四
    年龄:30岁
    号码:13900139000
    地址:上海市浦东新区陆家嘴
    
    这样用户就能清楚地看到李四的详细信息了。
    </think>
    
    根据提供的上下文,以下是查询李四的数据:
    
    姓名:李四
    年龄:30 岁
    号码:13900139000
    地址:上海市浦东新区陆家嘴


     

     

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值