实现知识库问答机器人 langchain + LLM + Embeddings (比Langchain-Chatchat更智能)

【LangChain系列文章】4. 向量数据库Vector Stores 向量数据库Vector Stores 和 embeddings介绍 及简单的使用 阅读详情

一、前言

模型既然会胡说八道那为啥不让模型在判断一下自己输出的是否正确呢?🤔
于是我产生了一个想法:提供一些条件让模型自己判断自己的推理是否正确✨

本文以实现一个知识库为例,但是和其他的知识库有区别。 目前流行知识库如 Langchain-Chatchat 只能回答检索到的内容,但是对内容的整理和总结的能力就很弱了。还有对一些上下文不是很连贯的内容推理也比较弱,比如招标文件等这种模板文件,多个文件格式都一样。只有标题和一些字段不同。

总结就是要实现能检索知识库和聊天并行。

下文代码将使用 python3 进行编码。并且在 google 实验室中进行。

二、实现思路

分为知识库整理和推理两个步骤:

合理不合理合理不合理合理不合理文件内容加载文件txt文件图片文件doc…langchainc拆分文本其他处理Embeddings文本转向量存储到向量库用户输入LLM对语句补全补全是否合理LLM获取Embeddings语义语义是否合理向量库检索检索内容+用户输入LLM推理结果是否合理输出

三、依赖库下载:

  1. langchain 用来调用 llm 和 embeddings
  2. chromadb 向量库
  3. gradio 超级方便的写 Ai 用的 UI 库
  4. sentence_transformers HF提供的(调用抱脸的方法来使用模型)
下载脚本体验AI代码助手代码解读复制代码!pip install --upgrade --quiet  langchain langchain-community langchainhub chromadb bs4
!pip install sentence_transformers
!pip install gradio

四、模型下载

  1. text2vec-large-chinese (huggingface.co/GanymedeNil…)
  2. openchat (huggingface.co/openchat/op…)

根据测试,上面两个模型在中文环境下比较好用,而且部署 openchat (7B) 只需要使用一张 3090 即可。 text2vec-large-chinese 用 gpu 的情况下 3G 左右的显存就够了。

因为使用的免费版的实验室,所以提供的 GPU 跑不起来这个模型。所以模型使用官方的 api,只有 embeddings 在实验室环境中跑。

openchat 在 Hugging Face 也提供了接口,但是测试发现返回的内容过长会被截断,也就是不完整,所以只能去官网案例中找一个接口用(下文会提供)。

下载脚本体验AI代码助手代码解读复制代码!git clone https://huggingface.co/GanymedeNil/text2vec-large-chinese

五、依赖引入

先直接在顶部把要用到的依赖全部引入。

javascript体验AI代码助手代码解读复制代码import os
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain import hub

from langchain.vectorstores import Chroma
from langchain.text_splitter import CharacterTextSplitter, RecursiveCharacterTextSplitter
from langchain.chains import RetrievalQA
from langchain_community.document_loaders import TextLoader  

from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.llms import HuggingFaceHub
import gradio as gr

import re
import bs4
import requests
import random

六、openchat 模型接口调用

上面说了实验室环境中没法直接搭建 openchat 环境,所以只能写法方法调用线上接口了。 这个代码比较简单,直接使用 requests 来调用接口,传入对话内容和温度参数。

arduino体验AI代码助手代码解读复制代码# @title openchat 模型接口调用函数 
def openchatGen(messages, prompt = " ", temperature = 0.5): 
    API_URL = "https://openchat.team/api/chat"

    def query(payload):
        response = requests.post(
            API_URL,
            #  headers={},
            json=payload
        )
        # print(response)
        return response.text


    output = query({
        "key": "",
        "prompt": prompt, # 接口未处理这个参数
        "temperature": temperature,
        "model": {
            "id": "openchat_v3.2_mistral",
            # "id": "openchat_3",
            "name": "OpenChat Aura",
            "maxLength": 24576,
            "tokenLimit": 8192
        },
        "messages": messages or [
            # {  "role": "user",  "content": "xxx",  }
            # {  "role": "assistant",  "content": "xxx",  }
            # {  "role": "system",  "content": "xxx",  }
        ]
    })
    return output

# 普通调用
openchatGen_test1 = openchatGen([
    {"role": "user",  "content": "你好"}
])
print(openchatGen_test1)

# prompt 设置 | 多轮对话
openchatGen_test2 = openchatGen([
        {"role": "user",  "content": "你的名字叫大白,你喜欢吃饭。记住了吗?"},
        {"role": "assistant",  "content": "好的,我记住了"},
        {"role": "user",  "content": "你叫什么名字?"}
])
print(openchatGen_test2)

效果如下 image.png

七、加载文档

测试用文学作品形式的文档体验会很好,但是工作中那种文本会比较少。所以这里直接使用上下文都不怎么连贯的招标数据。

本文中文档使用 20 条招标数据。数据如下格式。

注:这些数据都是公开的,不是隐私数据。 image.png

直接调用 langchain 提供的加载文档和拆分文档的方法,非常的简单。只是注意参数需要调好即可。代码如下:

ini体验AI代码助手代码解读复制代码# @title 加载文档 
loader = TextLoader("./test.txt")
document = loader.load()
# 文档拆分
text_splitter = CharacterTextSplitter(  
    separator="",
    chunk_size=124,
    chunk_overlap=0, 
    length_function=len,
    is_separator_regex=False,
)

org_texts = text_splitter.split_documents(document) 
 
# 竞标数据 
texts = []
for item in org_texts:
    # 去除空格
    item.page_content = item.page_content.replace(" ","").replace("   ","")
    texts.append(item)
  

八、embeddings 模型环境搭建

老手的话可以看出来这里直接调用 HF 接口可能更简单。但是文档拆太细的话调用接口次数多就会噶!

模型路径是基于 google 实验室写的,自行根据情况更改即可。

ini体验AI代码助手代码解读复制代码# @title embeddings 模型使用, 
# 模型有些大必须用 GPU 跑,不然会卡住 
model_path = "/content/text2vec-large-chinese"  # 根据条件改
model_kwargs = {'device': 'cuda:0'} # 用一个gpu跑,根据条件改
encode_kwargs = {'normalize_embeddings': False}
embeddings = HuggingFaceEmbeddings(
    model_name=model_path,
    model_kwargs=model_kwargs,
    encode_kwargs=encode_kwargs
)

text = "你好."
query_result = embeddings.embed_query(text)
print("emb len:", len(query_result))

测试效果
image.png

九、将文本转换为向量

还是使用 langchain 集成的向量库 Chroma 将文本转为向量数据。
检索方法下面代码中写了三种,实际上第一种默认的就够用了,当第一种效果不佳时可以尝试下另外两种。

ini体验AI代码助手代码解读复制代码# @title 使用 embedding engion 将 text 转换为向量
db = Chroma.from_documents(
    texts,
    embeddings,
    # 可以存起来,下次就不用做这个操作了
    # persist_directory="/content/chroma_db"
)
 
# 默认检索
retriever = db.as_retriever()

# 只搜索最匹配的一条
# retriever = db.as_retriever(search_kwargs={'k': 1})

# 稍微严格些
# retriever = db.as_retriever(
#     search_type="mmr",
#     # search_kwargs={'k': 5, 'fetch_k': 80}
#     search_kwargs={'fetch_k': 90}
# )
 

十、将用户输入转为更有利于 embeddings 检索的语句

用户和机器人对话时候往往不是以关键词的方式进行,而向量数据库是需要用到关键词的。所以需要将用户的语句中的关键词转一下,不然检索效果往往太依赖于用户的提问技巧。所以这个步骤非常有必要。

下面用 prompt 来让 llm 帮我们做这个事情。其实只要 prompt 写得准确,llm 给我们的答案还是不错的。

ini体验AI代码助手代码解读复制代码# @title 将用户输入转为更有利于 embeddings 检索的语句
def getAIQuestion(userQuestion): 
    # 改为使用接口方式请求
    template = """ 原始语句:{context} \n帮我转换为更适合Embedding模型检索的语句 """
    prompt = PromptTemplate(template=template, input_variables=["context"])
    prompt_text = prompt.format(context=userQuestion)
    # prompt 设置 | 多轮对话
    res = openchatGen([
            {"role": "user",  "content": "你只负责把语义转换为更适合Embedding模型检索的语句,注意不是翻译句子,并不能和用户对话。当用户语义没有问题时不做处理"},
            {"role": "assistant",  "content": "好的。"},
            {"role": "user",  "content": "原始语句:小桂子胎记在哪? \n帮我转换为更适合Embedding模型检索的语句"},
            {"role": "assistant",  "content": "小桂子胎记位置"},
            {"role": "user",  "content": "原始语句:谁是疯子? \n帮我转换为更适合Embedding模型检索的语句"},
            {"role": "assistant",  "content": "疯子的名字"},
            {"role": "user",  "content": prompt_text }
    ], temperature=0.4)
    return res

# 测试
test_arr_1 = [
    "帮我搜索一下2024年有哪些比选信息",
    "微订饮水机招标信息的联系人告诉我",
    "你好"
]

for item in test_arr_1:
    res = getAIQuestion(item)
    print(f"=== {item} -> {res}")

image.png

十一、测试向量库的检索

有了上面的 getAIQuestion 方法,现在我们来测试一下检索效果。

swift体验AI代码助手代码解读复制代码# @title 向量库检索测试 
questionTest = "生产性耗材的招标有吗?" 

questionTest_ai = getAIQuestion(questionTest)
print("原始:", questionTest)
print("AI:", questionTest_ai)
 
retrieved_docs_ai = retriever.invoke(questionTest_ai)

print("测试结果:", len(retrieved_docs_ai))
print("\n") 
print("\n---------------- AI ------------\n")

for item in retrieved_docs_ai:
    print(item) 

结果不是那么丰富,因为是从库里匹配出来的知识片段,还需要给 LLM 整理才行。 image.png

十二、获取一轮对话的相关性得分 返回 0 - 1 的浮点数

怎么保证 LLM 不会答非所问,这就得写另一个方法来控制了。下面这个方法对比一轮对话是否是有关联,给关联性打个分。因为直接判断是或者不是的情况并不会很准确,但是打分的话咋们自己控制一下阈值会得到很好的效果。

ini体验AI代码助手代码解读复制代码# @title 获取一轮对话的相关性得分 返回 0 - 1 的浮点数
# 一般大于 0.3 就有一定的相关性了
def getAIQAIS(question, answer):
    template = """
    <question>{question}</question><answer>{answer}</answer>
    """

    prompt = PromptTemplate(template=template, input_variables=["question", "answer"])
    prompt_text = prompt.format(question=question, answer=answer)
    # prompt 设置 | 多轮对话
    res = openchatGen([
            {"role": "user",  "content": "我会把一轮对话分别放到<question></question>和<answer></answer>标签中,你需要分析一下两个句子的相关性得分,第二句是不知道的意思也需要给高分,因为也算是对第一句的回答,只要两句话有关联得分就需要大于 0.5, 得分范围 0 - 1。仅仅告诉用户得分即可,不用发送其他语句。"},
            {"role": "assistant",  "content": "好的。"},
            {"role": "user",  "content": "<question>兰姨娘非常的美吗?</question><answer>是的兰姨娘很美。</answer>"},
            {"role": "assistant",  "content": "0.99"},
            {"role": "user",  "content": "<question>德先生为什么很奇怪?</question><answer>因为他是一个妖怪</answer>"},
            {"role": "assistant",  "content": "0.6"},
            {"role": "user",  "content": "<question>小米为啥打不过大米?</question><answer>我刚刚吃完饭</answer>"},
            {"role": "assistant",  "content": "0.01"},
            {"role": "user",  "content": prompt_text},
    ], temperature=0.4)
    return res

# 测试
test_arr = [
    {"question": "小明几岁了?","answer":"小明15岁了。"},
    {"question": "在做什么呢?","answer":"在吃饭。"},
    {"question": "奥特曼会打未成年的怪兽吗?","answer":"这个问题我也不知道。"},
    {"question": "小红年年第一名?","answer":"那她可真厉害!"},
    {"question": "查一下2024年有哪些名人?","answer":"今天的饭难吃极了!"},
    {"question": "明天会更好吗?","answer":"小红打了小明!"},
    {"question": "告诉我蜘蛛侠和哈利波特什么关系","answer":"我吃了一大个披萨"},
    {"question": "白素贞为什么被压在雷峰塔下面?","answer":"因为白素贞是一个妖怪!"}
]

for item in test_arr:
    res2 = getAIQAIS(item["question"], item["answer"].replace("\n", ""))
    res2t = res2.replace("\n", "").replace(" ", "")
    print(f"=== {item} -> {res2t}")

image.png

十三、获取两句话的相关性得分 返回 0 - 1 的浮点数

在知识库对话中,如果想实现对轮对话得咋们需要做两件事,

首先要明确用户提问的句子中的目标是什么?比如用户问 联系人电话号码是什么? 那需要先推理出用户问的是公司还是个人或者是哪个公司哪个人的电话号码。

完成上一步后得到的答案难道一定正确吗?不!LLM 根据一堆对话内容推理出来的结果一点也不可信。所以这时候需要保证 LLM 没有瞎说。所以需要再写一个打分函数来让第一步变的可靠。

ini体验AI代码助手代码解读复制代码# @title 获取两句话的相关性得分 返回 0 - 1 的浮点数
# 一般大于 0.3 就有一定的相关性了
def getAIQsIS(question1, question2):
    template = """
    <question1>{question1}</question1><question2>{question2}</question2>
    """

    prompt = PromptTemplate(template=template, input_variables=["question1", "question2"])
    prompt_text = prompt.format(question1=question1, question2=question2)
    # prompt 设置 | 多轮对话
    res = openchatGen([
            {"role": "user",  "content": "我会把两个问句分别放到<question1></question1>和<question2></question2>标签中,你需要分析一下两个句子的相关性得分,只要两句话都是问的相同的问题得分就需要大于 0.5, 得分范围 0 - 1。仅仅告诉用户得分即可,不用发送其他语句。"},
            {"role": "assistant",  "content": "好的。"},
            {"role": "user",  "content": "<question1>小红是这个项目的负责人吗?</question1><question2>京昆石太高速2024年供电(外电)线路及变压器日常维护采购项目比选的小红是这个项目的负责人吗?</question2>"},
            {"role": "assistant",  "content": "0.5"},
            {"role": "user",  "content": "<question1>比选什么时候开始?</question1><question2>比选什么时候开始?</question2>"},
            {"role": "assistant",  "content": "0.99"},
            {"role": "user",  "content": "<question1>明年棒球赛的冠军你猜会是谁?</question1><question2>小明联系地址在哪?</question2>"},
            {"role": "assistant",  "content": "0"},
            {"role": "user",  "content": prompt_text},
    ], temperature=0.5)
    return res.replace("\n", "").replace(" ", "")

# 测试
test_arr_getAIQsIS = [
    {"question1": "对公司有什么要求吗?","question2":"京昆石太高速2024年供电(外电)线路及变压器日常维护采购项目的联系地址在哪?"},
    {"question1": "对公司有什么要求吗?","question2":"京昆石太高速2024年供电(外电)线路及变压器日常维护采购项目的比选对公司有什么要求?"},
    {"question1": "小明叫什么名字?","question2":"微订科技的员工小明叫什么名字?"},
    {"question1": "他说昨天是周一吗?","question2":"海伦问昨天是周一吗?"},
    {"question1": "英子住在哪里?","question2":"海伦住在哪里?"},
]

for item in test_arr_getAIQsIS:
    res2 = getAIQsIS(item["question1"], item["question2"].replace("\n", ""))
    print(f"=== {item} -> {res2}")

image.png

十四、根据对话让模型推理出用户问题相对的目标(一般是名词)

这个判断会比较复杂,因为对话中可能涉及多个目标名词,需要明确让 LLM 提取最近的目标。所以会写比较多的 prompt 才行。而且拿到的结果不一定会准确,所以需要用到上面写的打分函数getAIQsIS来打一分,分数过低就重新推理一遍。

swift体验AI代码助手代码解读复制代码
def random_between_03_and_08():
    """
    生成介于 0.3 和 0.8 之间的随机浮点数,保留一位小数精度
    需要调整,不要随机,按循序走一遍就行...
    """
    random_number = random.uniform(0.3, 0.8)
    rounded_number = round(random_number, 1)
    return rounded_number
 

# @title 根据对话让模型推理出用户问题相对的目标(一般是主语/名词)
def getAISubject(question, context, max_repeat = 10): 
    _max_repeat = max_repeat
    # 具体推理逻辑
    def doFn(temperature = 0.6):
        template = """
        记住这段对话,我会问你一些问题。 {context}
        """

        prompt = PromptTemplate(template=template, input_variables=["context"])
        prompt_text = prompt.format(context=context)
        print("主语推理 prompt_text:", prompt_text)
        # prompt 设置 | 多轮对话
        res = openchatGen([
            {"role": "user",  "content": "根据对话内容,如果用户最后的句子缺少目标名称,则需要添加它。目标可以是一个公司,一个人,等等。如果你不知道目标是什么,直接回复\"用户\"问题的内容就行,不要回复任何与用户的句子不相关的东西,不能更改用户问题的原本意思。"},
            {"role": "assistant",  "content": "好的。"},

            {"role": "user",  "content": "记住这段对话,我会问你一些问题。\n 用户: 变压器日常维护采购项目相关的竞标有吗? \n助理: 是的,有关于变压器日常维护采购项目的竞标信息。您之前提供的信息中提到了一项名为“京昆石太高速2024年供电(外电)线路及变压器日常维护采购项目”的竞标项目。 \n用户: 联系人是谁? \n "},
            {"role": "assistant",  "content": "好的,我记住了。"},
            {"role": "user",  "content": "将内容中的目标名称补充到 \"用户\" 最后提问的句子中给我。"},
            {"role": "assistant",  "content": "“京昆石太高速2024年供电(外电)线路及变压器日常维护采购项目”的竞标项目联系人是谁?"},

            {"role": "user",  "content": "记住这段对话,我会问你一些问题。\n 用户: 变压探伤仪配件采购相关的比选信息有吗? \n助理: 1. 项目名称:石家庄市轨道交通集团有限责任公司运营分公司2023年度探伤仪配件采购项目比选公告。 \n1.1 项目编号:SJZXJT-HWBX-202001 \n1.2 服务内容:探伤仪配件采购项目, \n用户: 联系人是谁? \n "},
            {"role": "assistant",  "content": "好的,我记住了。"},
            {"role": "user",  "content": "将内容中的目标名称补充到 \"用户\" 最后提问的句子中给我。"},
            {"role": "assistant",  "content": "石家庄市轨道交通集团有限责任公司运营分公司2023年度探伤仪配件采购项目比选”的竞标项目联系人是谁?"},

            {"role": "user",  "content": "记住这段对话,我会问你一些问题。\n 用户: 今年小红怎么样?\n助理: 小红非常的好。\n 用户: 今年小红打电话给小米了吗?\n助理: 是的已经打了。 \n用户: 小明吃饭了吗?\n "},
            {"role": "assistant",  "content": "好的,我记住了。"},
            {"role": "user",  "content": "将内容中的目标名称补充到 \"用户\" 最后提问的句子中给我。"},
            {"role": "assistant",  "content": "小明吃饭了吗?"},

            {"role": "user",  "content": "记住这段对话,我会问你一些问题。\n 用户: 联系电话是18216811014的比选项目名称叫什么?\n助理: 没找到呢。\n用户: 那这个联系电话是谁的?\n "},
            {"role": "assistant",  "content": "好的,我记住了。"},
            {"role": "user",  "content": "将内容中的目标名称补充到 \"用户\" 最后提问的句子中给我。"},
            {"role": "assistant",  "content": "18216811014这个联系电话是谁的?"},

            {"role": "user",  "content": "记住这段对话,我会问你一些问题。\n 用户: 京昆石太高速2024年供电(外电)线路及变压器日常维护采购项目比选的联系人是谁? \n助理: hi \n用户: 联系人是谁 \n助理: hi \n用户: 内容概括下 \n "},
            {"role": "assistant",  "content": "好的,我记住了。"},
            {"role": "user",  "content": "将内容中的目标名称补充到 \"用户\" 最后提问的句子中给我。"},
            {"role": "assistant",  "content": "京昆石太高速2024年供电(外电)线路及变压器日常维护采购项目比选的内容概括下"},


            {"role": "user",  "content": "记住这段对话,我会问你一些问题。\n 用户: 2024年安全设备规则库升级采购项目比选有吗?\n助理: “石家庄市京昆石太高速公路管理服务有限公司2024年安全设备规则库升级采购项目比选\" 为你找到了这个信息。\n用户: 属于什么行业的?\n助理: 铁路运输业。 \n用户: 比选什么时候开始? \n "},
            {"role": "assistant",  "content": "好的,我记住了。"},
            {"role": "user",  "content": "将内容中的目标名称补充到 \"用户\" 最后提问的句子中给我。"},
            {"role": "assistant",  "content": "“石家庄市京昆石太高速公路管理服务有限公司2024年安全设备规则库升级采购项目比选\"什么时候开始开始?"},

            {"role": "user",  "content": "记住这段对话,我会问你一些问题。\n 用户: 京昆石太高速2024年供电(外电)线路及变压器日常维护采购项目比选的联系人是谁? \n助理: 根据提供的文本内容,比选人名称为:石家庄市京昆石太高速公路管理服务有限公司。联系人:杨工。电话:0311-85869915。 \n用户: 代理机构的联系方式呢\n "},
            {"role": "assistant",  "content": "好的,我记住了。"},
            {"role": "user",  "content": "将内容中的目标名称补充到 \"用户\" 最后提问的句子中给我。"},
            {"role": "assistant",  "content": "京昆石太高速2024年供电(外电)线路及变压器日常维护采购项目的代理机构的联系方式呢"},

            {"role": "user",  "content": prompt_text},
            {"role": "assistant",  "content": "好的,我记住了。"},
            {"role": "user",  "content": "将内容中的目标名称补充到 \"用户\" 最后提问的句子中给我。"},
        ], temperature = temperature)
        return res

    res = doFn()
    # 正确性判断
    res2 = getAIQsIS(question, res)
    while float(res2) < 0.3 and _max_repeat > 0:
        temperature = random_between_03_and_08()
        print("\n\n",question, " -> ", res)
        print("得分过低,需要重新推理。当前得分:", res2)
        print("下一次推理温度:", temperature)
        res = doFn(temperature)
        res2 = getAIQsIS(question, res)
        _max_repeat -= 1

    return res
    
# 测试
test_arr_getAISubject = [ 
    {
        "question": "这个项目的比选范围说一下。",
        "context":  """
            用户: 京昆石太高速2024年供电(外电)线路及变压器日常维护采购项目比选的联系人是谁?
            助理: 根据您提供的文本内容,比选人名称为:石家庄市京昆石太高速公路管理服务有限公司。联系人:杨工。电话:0311-85869915。比选代理机构名称:河北省成套招标有限公司。地址:河北省石家庄市工农路486号。联系人:孟凡真、尉思源。 电话:0311-83086950
            用户: 比选内容概括下
        """
    },
]

test_index = 1
for item in test_arr_getAISubject:
    question = item["question"]
    context = item["context"]
    res = getAISubject(question, context)
    print(f"{test_index}. {question} -> {res}")
    test_index+=1

image.png

十五、从上下文中推理出答案的方法封装

写完了上面的那些方法后就进入了正题了,把检索到的知识碎片和用户的问题全部给到 LLM。让其推理。为了后面的使用,这里先封装一个输入输出函数。

这里封装很有必要,因为一次对话如果打分过低可能会被调用多次推理。

ini体验AI代码助手代码解读复制代码
def getAnswerByContext(context, question, query_len, history=[], temperature = 0.6):
    # template = """根据用户的问题,从知识库中检索出来了 {query_len} 条相关信息,下面是具体内容:\n{context} \n 请根据内容回答用户问题,务必告诉用户从知识库中检索出来了多少条相关信息。"""
    # template = """记住这段内容,我有些问题要问你,请根据这段内容对我的问题进行回答,下面是具体内容:\n\n\n检索到相关信息共 {query_len} 条。\n \n{context}"""
    template = """<text>从知识库中检索出来了 {query_len} 条相关信息,下面是具体内容:\n{context}</text>"""

    prompt = PromptTemplate(template=template, input_variables=["context", "query_len"])
    prompt_text = prompt.format(context=context, question = question, query_len = query_len)
 
    messages = [
            {"role": "user", "content": "我会把一段文本放到<text></text>标签中,你需要根据标签中的内容对用户提出的问题进行回答,所有的回答必须参照标签中的内容。"},
            {"role": "assistant",  "content": "好的。"},
            {"role": "user", "content": prompt_text},
            {"role": "assistant",  "content": "请提出您的问题吧,我会根据内容对您的问题进行回复。"},
        ] + history + [
            {"role": "user",  "content": question}
        ]
 
    res = openchatGen(messages, temperature=temperature) 
    return res

# 测试
test_arr4 = [
    {"question": "小明认识小红吗?","context":"小红是一名三年级的学生,每次考试都第一名,成绩非常的好。"},
    {"question": "小红几年级?","context":"小红是一名三年级的学生,每次考试都第一名,成绩非常的好。"}
]

for item in test_arr4:
    res2 = getAnswerByContext(context=item["context"], question=item["question"], query_len = 1)
    print(f"=== {item} -> {res2}")

image.png

十六、闭环推理

这是整个程序的最后一步了,只需要 推理->打分->输出 即可,

python体验AI代码助手代码解读复制代码# @title 闭环(结合向量数据库 + AI 微调检索句 + + AI 完善主语) 
query_doc = ""

# 对话历史
chat_historys = ""
chat_historys_arr = []
chat_historys_ai_use = "\n"

# gr 自带的history暂时不用...
# is_repat 是否是重新回答
def main_bh(question, is_repat = False):
    global query_doc, chat_historys, chat_historys_ai_use, chat_historys_arr

    # max_repeat 最多尝试多少次推理
    _max_repeat = 5
    # print(question)

      # 特殊指令
    if question == "/clear":
        chat_historys = ""
        chat_historys_ai_use = ""
        chat_historys_arr = []
        print("清理会话完毕!")
        return "清理会话完毕!"

    print("\n\n\n\n**************************************") 
 
    _question = question
 
    def format_docs(docs):
        # 文学作品需要去除换行等符号会更准确
        # pt = "\n\n".join(doc.page_content for doc in docs).replace("\n", "").replace(" ", "")

        # 比选数据 
        pt = "\n\n".join(doc.page_content for doc in docs) 
        return pt

    real_question = ""
    # print("is_repat", is_repat)
    if is_repat != True:
        chat_historys_ai_use += f"用户: {_question} \n"

    # 让模型完整用户语义
    # if len(chat_historys_arr) > 0 and is_repat == False:
    if len(chat_historys_arr) > 0:
        real_question = getAISubject(_question, chat_historys_ai_use) or _question
        # print('目标推理内容:', chat_historys_ai_use)
        print('目标推理结果:', real_question)
    else:
        real_question = _question

    # 检索内容需要去重 
    # 用模型处理一遍语义再进行向量数据库搜索
    retrieved_docs = retriever.invoke(getAIQuestion(real_question))  
    contexts = retrieved_docs;
    contexts_unique_list = []
    for item in contexts:
        if item not in contexts_unique_list:
            contexts_unique_list.append(item)

    # 最多只要一条数据,但是总数可以告诉llm  ing... 
    cur_query_context = format_docs(contexts_unique_list)
    query_doc = cur_query_context

    print("\n=================知识库=======================")
    print("检索到数量:", len(contexts_unique_list))
    for item in contexts_unique_list:
        print(item)
    print("========================================");

    temperature =  random_between_03_and_08() if is_repat == True else 0.5
    q_res = getAnswerByContext(
        context=query_doc,
        question=real_question,
        query_len = len(contexts_unique_list),
        history = chat_historys_arr,
        # is_repat 时需要随机取值
        temperature =temperature
    )

    # 删除换行 
    q_res_f = q_res.replace('\n', '。 ').replace('\r', '。 ')
    # 用模型推理下回答是否合理,不合理就重新执行本函数
    q_res_score = getAIQAIS(real_question, q_res_f) or "1.0"
    float_number = float(q_res_score)
    print("回答得分:", q_res_score)

    if float_number < 0.2:
        # 回答不合理,需要重新推理一次
        print(f"回答内容:{q_res_f}")
        print("回答不合理,需要重新推理一次")
        return main_bh(question, True) 
        
    chat_historys_arr.append({ "role":"user", "content":real_question })
    chat_historys_arr.append({ "role":"assistant", "content":q_res_f })  
    chat_historys_ai_use += f"助理: {q_res_f} \n"

    print("\n================结果========================")

    print("温度:", temperature)
    print(f"用户输入:{question}") 
    print(f"主语增加:{real_question}")
    print("回答->:", q_res) 
    print("========================================");

    return q_res 

def serverChat(msg, history):
    return main_bh(msg)
gr.ChatInterface(
    serverChat,
    retry_btn=None,
    undo_btn=None,
    clear_btn=None,
    description="胡言乱语时使用 /clear 指令清除会话"
).launch(share=True, debug = True) 

十七、最后效果

企业微信截图_17061728791984.png

企业微信截图_1706172896179.png

企业微信截图_17059888844411.png

企业微信截图_17059889028502.png

END

如果微调打分模型效果一定会更好。

如果每个步骤都使用不同的模型数一数要用多少个模型?

如何零基础入门 / 学习AI大模型?

大模型时代,火爆出圈的LLM大模型让程序员们开始重新评估自己的本领。 “AI会取代那些行业?”“谁的饭碗又将不保了?”等问题热议不断。

不如成为「掌握AI工具的技术人」,毕竟AI时代,谁先尝试,谁就能占得先机!

想正式转到一些新兴的 AI 行业,不仅需要系统的学习AI大模型。同时也要跟已有的技能结合,辅助编程提效,或上手实操应用,增加自己的职场竞争力。

但是LLM相关的内容很多,现在网上的老课程老教材关于LLM又太少。所以现在小白入门就只能靠自学,学习成本和门槛很高

那么我作为一名热心肠的互联网老兵,我意识到有很多经验和知识值得分享给大家,希望可以帮助到更多学习大模型的人!至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

这份完整版的大模型 AI 学习资料已经上传优快云,朋友们如果需要可以微信扫描下方优快云官方认证二维码免费领取【保证100%免费】

👉 福利来袭优快云大礼包:《2025最全AI大模型学习资源包》免费分享,安全可点 👈

全套AGI大模型学习大纲+路线

AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!

read-normal-img

640套AI大模型报告合集

这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

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

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

👉学会后的收获:👈
• 基于大模型全栈工程实现(前端、后端、产品经理、设计、数据分析等),通过这门课可获得不同能力;

• 能够利用大模型解决相关实际项目需求: 大数据时代,越来越多的企业和机构需要处理海量数据,利用大模型技术可以更好地处理这些数据,提高数据分析和决策的准确性。因此,掌握大模型应用开发技能,可以让程序员更好地应对实际项目需求;

• 基于大模型和企业数据AI应用开发,实现大模型理论、掌握GPU算力、硬件、LangChain开发框架和项目实战技能, 学会Fine-tuning垂直训练大模型(数据准备、数据蒸馏、大模型部署)一站式掌握;

• 能够完成时下热门大模型垂直领域模型训练能力,提高程序员的编码能力: 大模型应用开发需要掌握机器学习算法、深度学习框架等技术,这些技术的掌握可以提高程序员的编码能力和分析能力,让程序员更加熟练地编写高质量的代码。

👉 福利来袭优快云大礼包:《2025最全AI大模型学习资源包》免费分享,安全可点 👈

img

这份完整版的大模型 AI 学习资料已经上传优快云,朋友们如果需要可以微信扫描下方优快云官方认证二维码免费领取【保证100%免费】

作为普通人,入局大模型时代需要持续学习和实践,不断提高自己的技能和认知水平,同时也需要有责任感和伦理意识,为人工智能的健康发展贡献力量。

LangChain(二)——构建多轮对话 因此,不同线程间的对话是隔开的,同一线程的对话是能被记录的,因为上面设置状态流图的内存检查点是记忆函数。是一种与对话流和任务流设计相关的框架或工具,它被设计为一种能够管理和处理复杂任务流(Task Flow)和状态流(State Flow)的工具,通常与语言模型(如 GPT 系列)结合使用,用于构建动态多轮对话系统或复杂的流程自动化任务。来构建多轮对话时,每个对话轮次是一个节点(Node),代表需要完成的任务(例如调用大模型或调用工具链),节点可以是同步或异步的函数,任务的完成可能依赖于上一个节点的输出。 阅读详情

相关推荐

大模型应用开发实战:从RAG到Agent,7天掌握AI应用构建全流程

大语言模型(LLM)作为当前人工智能的核心技术,正从通用对话向解决特定领域问题演进。其核心原理在于通过检索增强生成(RAG)和智能体(Agent)两大范式,为模型赋予实时知识获取与自主行动能力。RAG通过“先检索,后生成”的机制,将外部知识库与模型生成相结合,有效解决了知识滞后与幻觉问题,是构建企业级知识问答系统的关键技术。Agent则通过“思考-行动-观察”的循环,使模型能够调用外部工具,完成复杂任务序列,实现了从“思考者”到“行动者”的跨越。这些技术的工程价值在于,它们能将前沿的AI能力低成本、高效率地

weixin_30819085的博客 357

Langchain使用介绍之 - 基于向量存储进行检索

通过实际代码例子介绍如何将文本的向量进行存储,然后存储的向量检索答案

qiaotl的博客 8233

langchain 模型加载HuggingFaceEmbeddings、文本切割RecursiveCharacterTextSplitter与向量数据库使用FAISS

参考: https://github.com/TommyTang930/LangChain_LLM_ChatBot https://python.langchain.com/docs/integrations/vectorstores/faiss这里对着类进行了改写,对中文切分更友好 用于后续输入的就是chunks,是一个列表对于HuggingFace上的模型可以加载用作文本向量模型;这里加载的下载好的离线模型text2vec-base-chinese 3、向量数据库使用FAISS embedding就是上

weixin_42357472的博客 4407

【可能是全网最丝滑的LangChain教程】十五、LangChain进阶之Embedding Models

Embeddings 类为各种文本嵌入模型提供商提供了一个统一的接口,简化了模型的使用。 文本嵌入模型将文本转换为向量形式,便于在向量空间中进行操作,如语义搜索和相似度计算。

Jeffray1991的博客 4489

中小公司无线组网实战:用一台H3C AC和普通三层交换,实现办公网AP统一管理

本文详细介绍了中小型企业如何利用H3C AC控制器与普通三层交换机实现无线AP统一管理的实战方案。通过VLAN规划、DHCP配置和AC控制器设置,构建经济高效的办公网络,解决信号覆盖和连接稳定性问题,特别适合50-200人规模的企业部署。

weixin_42531516的博客 382

别再花钱买插件了!手把手教你实现UE5运行时动态导入FBX模型(附完整源码解析)

本文详细解析了UE5运行时动态导入FBX模型的全过程,从FBX SDK集成配置到核心解析流程,再到引擎资源转换和性能优化。通过多线程加载架构和内存优化技巧,开发者可以高效实现动态模型导入,避免购买昂贵插件。文章还提供了完整源码解析和高级功能扩展,帮助开发者深入理解UE5的底层资源管理机制。

weixin_27033895的博客 377

提示词工程:从基础概念到RAG实战的工程化方法

你是不是也遇到过这种情况:花了几小时精心设计了一个问题,扔给 ChatGPT 或 Claude,结果得到的回答要么是“正确的废话”,要么干脆跑偏,完全不是你想要的。你开始怀疑,到底是模型不行,还是自己不会提问? 这背后的问题,恰恰是当前 AI 大模型应用中最核心、也最容易被忽视的一环: 提示词工程(Prompt Engineering) 。很多人以为它只是“把话说清楚”,但实际上,它是一门融合了心理学、语言学、软件工程和特定领域知识的系统性技术。一个优秀的提示词,能让一个普通模型发挥出专家级水平;而一个糟糕

cigang4063的博客 476

微信小程序测试号开发后上传按钮灰色?三步快速切换正式AppID

本文详细解析了微信小程序测试号开发后上传按钮灰色的原因,并提供了三步快速切换正式AppID的解决方案。通过注册正式小程序账号、修改项目配置和重新编译上传,开发者可以轻松解决上传按钮不可用的问题,确保小程序顺利上线。

weixin_29291185的博客 1576

【AI大模型】开源RAG框架汇总,收藏这一篇就够了!!

检索增强生成(Retrieval Augmented Generation),简称 RAG,已经成为当前最火热的LLM应用方案。经历今年年初那一波大模型潮,想必大家对大模型的能力有了一定的了解,但是当我们将大模型应用于实际业务场景时会发现,通用的基础大模型基本无法满足我们的实际业务需求,主要有以下几方面原因:

Android23333的博客 1511

FPGA实战 —— 基于UART的多设备串口通信系统设计

本文深入探讨了基于FPGA的多设备UART串口通信系统设计。针对工业控制与物联网应用中一对多通信的需求,文章对比了共享总线式、多路独立式等硬件连接方案,并详细阐述了可配置UART IP核、波特率自适应算法以及多路数据仲裁与分发逻辑等核心设计要点。最后,通过一个四传感器采集节点的实战案例,展示了从硬件连接到Verilog代码实现的完整系统构建过程。

weixin_29172963的博客 234

Mplus实战:如何用潜在类别分析(LCA)快速搞定心理学数据分类?

本文详细介绍了如何运用Mplus软件进行潜在类别分析(LCA),以快速处理心理学数据并识别内部异质性群体。文章从数据准备、Mplus语法编写、模型拟合与选择,到结果解读与高级应用(如纳入协变量),提供了完整的实战指南,帮助研究者从变量视角转向个体视角,有效挖掘数据中的潜在子类别。

weixin_28318011的博客 410

LangChain上使用huggingface的embedding模型(如jina-embeddings-v3)

embedding将文本映射到稠密的向量空间中,方便存储和查询。这里结合自己的应用场景,选择1b以下的embedding模型,综合考量下选择arkohut/jina-embeddings-v3下载使用模型卡片提供的命令,用cmd下载(强烈建议使用SSH命令下载!!!!用http容易报错)需要使用魔法上网才能下载。如果超时的话,也可以选择手动下载。注意下载后的文件名应该和网站上提供的一样。

weixin_44737922的博客 2247

顶会新宠!Gate+Attention创新思路发A会

性能提升:在Places2自然场景和CelebA-HQ人脸数据集上,与以往方法相比,该系统在自由形式掩码修复任务中取得了更高的质量和更低的误差(例如,平均ℓ1误差和ℓ2误差显著降低)。硬件效率提升:Griffin和Hawk在TPU-v3上的训练效率与Transformer相当,且在推理时具有更低的延迟和显著更高的吞吐量。门控卷积:提出了一种新的门控卷积机制,通过学习每个通道在每个空间位置的动态特征选择机制,解决了传统卷积将所有输入像素视为有效像素的问题。

2501_92785035的博客 974

LangChain系列使用指南:组件介绍_数据处理_文本嵌入Embeddings

LangChain 中的基本 Embeddings 类提供了两种方法:一个用于嵌入文档,另一个用于嵌入查询。之所以将它们作为两个单独的方法,是因为一些嵌入提供商对文档(用于搜索)和查询(搜索查询本身)有不同的嵌入方法。这很有用,因为这意味着我们可以在向量空间中思考文本,并做一些类似语义搜索的操作,比如在向量空间中寻找最相似的文本片段。可以实现嵌入的缓存。缓存支持的嵌入器是一个包装器,用于在键值存储中缓存嵌入。首先,让我们看一个示例,该示例使用本地文件系统存储嵌入,并使用 FAISS 向量存储进行检索。

数智笔记 2880

【麒麟系统】Rsync文件复制与同步工具

本文摘要: Rsync是一款Linux/Unix系统标配的高效文件同步工具,支持增量传输、SSH加密、断点续传等核心功能,相比SCP具有显著优势。文章详细解析了Rsync的五种标准语法格式和常用参数组合,重点介绍了本地同步、远程SSH同步(推送/拉取)的实操方法,并提供了密钥免密登录配置方案。针对企业运维场景,还讲解了文件过滤、镜像同步(--delete)、迁移同步(--remove-source-files)、空运行预校验(--dry-run)等高级功能,以及带宽限速等优化技巧,帮助用户安全高效地完成各类

信创运维工程师 · 银河麒麟 & 容器实战 | 金融生产环境迁移·等保加固 | 142篇原创·17万+阅读 999

【unity实战】Unity手搓脚本工具实现合并网格功能

本文介绍了在Unity中优化网格性能的方法,通过合并重复网格减少绘制调用。主要使用Mesh.CombineMeshes方法实现网格合并,支持统一材质和多材质处理。核心内容包括:1)基础合并脚本实现,将子网格合并到父对象并禁用原网格;2)统一材质设置,支持自定义或默认材质;3)多材质合并方案,通过设置mergeSubMeshes=false保留子网格材质索引。文章还提供了完整代码示例和操作演示,适用于实际项目中的网格优化需求。

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 560

社区供稿 | 中文 LangChain 项目的实现开源工作

Chinese-LangChain 是 yanqiangmiffy 同学的开源项目https://hf.co/spaces/ChallengeHub/Chinese-LangChainhttps://github.com/yanqiangmiffy/Chinese-LangChain中文 langchain 项目,基于 ChatGLM-6b + langchain 实现本地化知识库检索与智能答案生...

Hugging Face 5191

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

LangChain入门(四)-构建本地知识库问答机器人

因为是临时存入,所以当我们上面的代码执行完成后,上面的向量化后的数据将会丢失。如果想下次使用,那么就还需要再计算一次embeddings,这肯定不是我们想要的。其中hnswlib安装不了,去一下网站下载源码解压后,将hnswlib文件夹放在了项目的\Lib\site-packages文件夹中。上面的案例中我们只是将embeddings临时存进了Chroma,只需多加一个路径参数就可以实现持久化。在项目中新建一个data文件夹,里面放一个doc.txt,内容我摘抄一段朱炫大师兄的年少荒唐中的文章。

Good Luck 2万+

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

LangChain 27 AI Agents角色扮演多轮对话解决问题CAMEL

对话和基于聊天的语言模型的快速发展已经取得了在复杂任务解决方面的显著进展。然而,它们的成功在很大程度上依赖于人类输入来引导对话,这可能具有挑战性且耗时。本文探讨了构建可扩展技术以促进沟通代理之间的自主合作,并洞察其“认知”过程的潜力。为了解决实现自主合作的挑战,我们提出了一个名为角色扮演的新型沟通代理框架。我们的方法涉及使用启发式提示来引导聊天代理完成任务,同时保持与人类意图的一致性。我们展示了角色扮演如何用于生成对话数据,以研究聊天代理的行为和能力,为研究对话语言模型提供了宝贵的资源。

AI架构师易筋 4137

基于LangChain+LLM的本地知识库问答:从企业单文档问答到批量文档问答

本文则侧重讲解 1 什么是LangChain及langchain的整体组成架构 2 解读langchain-ChatGLM项目的关键源码,不只是把它当做一个工具使用,因为对工具的原理更了解,则对工具的使用更顺畅 3 langchain-ChatGLM项目的升级版langchain-chatchat

结构之法 算法之道 13万+
上一篇: 大模型RAG实战:全面讲解RAG技术原理、实战应用(附PDF免费下载书籍)
下一篇: Spring Boot3+Vue2极速整合: 10分钟搭建DeepSeek AI对话系统
爱喝白开水a
博客等级 码龄3年 6793粉丝 763原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值