支持私有化部署
AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


大模型:多种RAG组合优化(langchain实现)

发布日期:2025-04-23 07:23:30 浏览次数: 1524 作者:大数据机器学习笔记
推荐语

探索多种RAG优化策略及其在Langchain中的实现,有效解决幻觉问题。

核心内容:
1. RAG优化策略整合与Langchain实现
2. 名词嵌入对比训练及性能评估
3. 业务分析与RAG搜索能力增强

杨芳贤
53A创始人/腾讯云(TVP)最具价值专家

 

大模型RAG优化:Adaptive RAG

    

这篇文档整合了多种rag优化策略,并且使用langchain实现。可以有效的解决幻觉的问题。

概要

我们将把RAG论文中的想法整合到RAG代理中:

  • • Routing: Adaptive RAG (paper). 将问题路由到不同的检索方法
  • • Fallback: Corrective RAG (paper). 如果文档与查询不相关,则回退到网络搜索
  • • Self-correction: Self-RAG (paper). 修正有幻觉的答案或不回答问题

具体的逻辑图如下:

大致意思为:

1、首先根据问题描述判断是从rag获取信息还是网络搜索

2、如果rag获取的信息中出现幻觉,则重新回到网络进行搜索

3、根据问题和信息生成答案,然后查看答案和问题的资料判断是否有幻觉。

4、最终生成问题的答案。

5、在生成答案的过程中发现内容信息不相关,则会loop回去网络搜索重新生成答案。


Adaptive_rag1

embedding模型

名词嵌入对比训练
我们用nomic-bert-2048初始化nomic嵌入的训练。我们的对比数据集由约2.35亿个文本对组成。我们在收集Nomic Atlas的过程中广泛验证了其质量。你可以在nomic ai/constrastors代码库中找到数据集的详细信息,也可以在nomic Atlas中探索500万对子集。
在大规模文本嵌入基准测试(MTEB)中,nomic嵌入的性能优于Text-Embedding-ada-002和jina-embeddings-v2-base-en。

nomic和其他的embedding算法比较下来,效果更好。

Name
SeqLen
MTEB
LoCo
Jina Long Context
Open Weights
Open Training Code
Open Data
nomic-embed
8192
62.39 85.53
54.16
jina-embeddings-v2-base-en
8192
60.39
85.45
51.90
text-embedding-3-small
8191
62.26
82.40
58.20
text-embedding-ada-002
8191
60.99
52.7
55.25

不过我们要使用qwen-max对应的embedding


业务分析

我们根据上面的摘要,拆分成多个模块。一步步的实现。增加rag搜索的能力。总体的业务流程如下:

下载

1、在start后面隐藏了一个条件边(route结点)【决定将问题路由到不同的检索方法】

2、retrieve结点【返回知识库获取的数据信息】

3、grade_documents结点【确定检索到的文档是否与问题相关如果任何文档不相关,我们将设置一个标志来运行网络搜索】

4、web_search【网络搜索:根据问题在网上寻找答案】

5、generate【根据文档的内容生成答案】

6、增加条件边grade_generation_v_documents_and_question【确定生成是否基于文档并回答问题、是否产生幻觉】

1、route模块

可以将问题路由到不同的检索方法。我们通过Agent来决定路由到哪个方向。

# Prompt
router_prompt = """You are an expert at routing a user question to a vectorstore or web search.

The vectorstore contains documents related to agents, prompt engineering, and adversarial attacks.

Use the vectorstore for questions on these topics. For all else, and especially for current events, use web-search.

Return JSON with single key, datasource, that is 'websearch' or 'vectorstore' depending on the question.
Here is the user question: \n\n {question}. 
"""


router_prompt = ChatPromptTemplate.from_template(router_prompt)
#json格式输出
class router_out(BaseModel):
    datasource: str = Field(description="选择'websearch' or 'vectorstore'")
    res :str = Field(description="结果")

router_llm  = router_prompt | model.with_structured_output(router_out)

1、我们建立一个prompt。

您是将用户问题路由到向量库或网络搜索的专家。
向量库包含与agents, prompt engineering, and adversarial attacks相关的文档。【可以根据知识库内容修改,也可以再增加一个agent总结知识库的内容】
使用向量库回答有关这些主题的问题。对于所有其他内容,尤其是时事,请使用网络搜索。
根据问题的不同,返回带有单键数据源的JSON,即“websearch”或“vectorstore”。
这是用户的问题:\n\n{question}。

2、将结果格式化输出:

输出作为一个BaseModel的类,里面有两个对象datasource和res

class router_out(BaseModel):
    datasource: str = Field(description="选择'websearch' or 'vectorstore'")
    res :str = Field(description="结果")

2、retrieve

定义知识库返回的节点

def retrieve(state):
    """
    Retrieve documents from vectorstore
    Args:
        state (dict): The current graph state
    Returns:
        state (dict): New key added to state, documents, that contains retrieved documents
    """

    print("---RETRIEVE---")
    question = state["question"]

    # Write retrieved documents to documents key in state
    documents = retriever.invoke(question)
    return {"documents": documents}

3、generate(基于rag回答问题)

def generate(state):
    """
    Generate answer using RAG on retrieved documents
    Args:
        state (dict): The current graph state

    Returns:
        state (dict): New key added to state, generation, that contains LLM generation
    """
    print("---GENERATE---")
    question = state["question"]
    documents = state["documents"]
    loop_step = state.get("loop_step", 0)

    # RAG generation
    docs_txt = format_docs(documents)

    rag_prompt_formatted = rag_prompt.format(context=docs_txt, question=question)
    generation = model.invoke([HumanMessage(content=rag_prompt_formatted)])
    return {"generation": generation.content, "loop_step": loop_step + 1}

4、grade_documents

def grade_documents(state):
    """
    Determines whether the retrieved documents are relevant to the question
    If any document is not relevant, we will set a flag to run web search

    Args:
        state (dict): The current graph state

    Returns:
        state (dict): Filtered out irrelevant documents and updated web_search state
    """


    print("---CHECK DOCUMENT RELEVANCE TO QUESTION---")
    question = state["question"]
    documents = state["documents"]

    # Score each doc
    filtered_docs = []
    web_search = "No"

    for d in documents:
        result = grader_llm.invoke(
            {"document": d, "question": question}
        )
        grade = result.binary_score
        # Document relevant
        if grade.lower() == "yes":
            print("---GRADE: DOCUMENT RELEVANT---")
            filtered_docs.append(d)
        # Document not relevant
        else:
            print("---GRADE: DOCUMENT NOT RELEVANT---")
            # We do not include the document in filtered_docs
            # We set a flag to indicate that we want to run web search
            web_search = "Yes"
            continue
    return {"documents": filtered_docs, "web_search": web_search}

1、遍历所有知识库返回的文档,只要有一个文档内容和问题无关。我们就需要通过网络搜索进行补充。

5、grade_generation_v_documents_and_question

def grade_generation_v_documents_and_question(state):
    print("---CHECK HALLUCINATIONS---")
    question = state["question"]
    documents = state["documents"]
    generation = state["generation"]
    max_retries = state.get("max_retries"3)  # Default to 3 if not provided
    result = hallucination_llm.invoke(
        {"documents": format_docs(documents), "generation": generation}
    )
    grade =result.binary_score

    # Check hallucination
    if grade == "yes":
        print("---DECISION: GENERATION IS GROUNDED IN DOCUMENTS---")
        # Check question-answering
        print("---GRADE GENERATION vs QUESTION---")
        # Test using question and generation from above
        result = answer_llm.invoke({"question":question,"generation": generation})
        grade = result.binary_score
        if grade == "yes":
            print("---DECISION: GENERATION ADDRESSES QUESTION---")
            return "useful"
        elif state["loop_step"] <= max_retries:
            print("---DECISION: GENERATION DOES NOT ADDRESS QUESTION---")
            return "not useful"
        else:
            print("---DECISION: MAX RETRIES REACHED---")
            return "max retries"
    elif state["loop_step"] <= max_retries:
        print("---DECISION: GENERATION IS NOT GROUNDED IN DOCUMENTS, RE-TRY---")
        return "not supported"
    else:
        print("---DECISION: MAX RETRIES REACHED---")
        return "max retries"

1、确定生成是否基于文档并回答问题

如果不相关且循环次数<3,则输出不支持。重新返回generate结点

2、根据最终问题的回答判断是否相关

如果不相关且循环次数<3,则输出无用,则返回websearch结点。


监控与对比

![image-20250421181430100](/Users/mwx/Library/Application Support/typora-user-images/image-20250421181430100.png)

查看langsmith的监控信息会看到我们的任务走向和实际消耗的时间。

1、先调用retrieve后进入grade_documents

2、需要web搜索,进入web_search

3、grade_generation_v_documents_and_question 输出结果

loop_step =1


对比情况

问题:什么是cot?

答案:思维链 (CoT) 是一种通过模拟人类推理过程来解决复杂问题的技术,它将任务分解为一系列逻辑步骤,逐步引导至最终答案。这种方法提高了模型的透明度和多步推理能力,适用于多种任务如算术推理、常识推理等。然而,它也存在对高质量提示的需求和计算成本增加等局限性。

直接用rag:

自一致性抽样#
自洽采样(Wang等人,2022a)是对温度>0的多个输出进行采样,然后从这些候选者中选择最佳的一个。
选择最佳候选人的标准可能因任务而异。一般的解决方案是选择多数票。对于易于验证的任务,例如使用单元测试的编程问题,我们可以简单地运行解释器并使用单元测试验证其正确性。
思维链(CoT)#
思维链(CoT)提示(Wei等人,2022)生成一系列短句,逐步描述推理逻辑,称为推理链或理由,最终得出最终答案。CoT的优势在复杂的推理任务中更为明显,同时使用大型模型(例如,参数超过50B)。简单的任务只会从CoT提示中略微受益。
CoT提示的类型#
CoT提示的两种主要类型:
很少拍摄CoT。它是通过一些演示来提示模型,每个演示都包含手动编写(或模型生成)的高质量推理链。

代码重点

ChatPromptTemplate

用途

ChatPromptTemplate为聊天式交互场景设计,用于构建多轮对话形式的提示信息。它可以处理不同角色(如人类、AI)的消息,并按照一定的格式组合这些消息。这更符合聊天机器人、对话式 AI 等应用的需求。ChatPromptTemplate能够处理不同角色,是与PromptTemplate最大的不同。
模板结构

ChatPromptTemplate由多个消息模板组成,每个消息模板对应一个特定的角色(如 HumanMessagePromptTemplateAIMessagePromptTemplate 等)。这些消息模板可以包含各自的占位符,用于填充动态内容。例如:

from langchain.prompts.chat import (
    ChatPromptTemplate,
    SystemMessagePromptTemplate,
    HumanMessagePromptTemplate
)

# 定义系统消息模板
system_template = "你是一个乐于助人的助手,总是用简洁的语言回答问题。"
system_message_prompt = SystemMessagePromptTemplate.from_template(system_template)

# 定义用户消息模板
human_template = "请提供关于{topic}的简要信息。"
human_message_prompt = HumanMessagePromptTemplate.from_template(human_template)

# 组合成聊天提示模板
chat_prompt = ChatPromptTemplate.from_messages([system_message_prompt, human_message_prompt])

# 构建提示,填充占位符
prompt = chat_prompt.format_prompt(topic="LLM").to_messages()

print(prompt)

主要参数
messages:列表类型。定义聊天消息的结构。列表中的每个元素代表一条消息,消息通常由 BaseMessagePromptTemplate 类的实例组成,比如 SystemMessagePromptTemplate、HumanMessagePromptTemplate 等,分别对应系统消息、人类消息等不同角色的消息模板。通过 messages 参数可以构建出一个完整的聊天对话流程的提示模板。

例子如下:

[HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['topic'], input_types={}, partial_variables={}, template='请提供关于{topic}的简要信息。\n\n'), additional_kwargs={})]

input_variables:列表类型。模板字符串里所有占位符的名称。

LLM的角色
system“角色,通过分配特定行为给聊天助手来创建对话的上下文或范围。例如,如果您希望与ChatGPT在与体育相关的话题范围内进行对话,可以将”system"角色分配给聊天助手,并设置内容为"体育专家”。然后ChatGPT会表现得像体育专家一样回答您的问题。
"human"角色,代表实际的最终用户,他向ChatGPT发送提问。
"ai“角色,代表响应最终用户提示的实体。这个角色表示消息是助手(聊天模型)的响应。”ai"角色用于在当前请求中设置模型的先前响应,以保持对话的连贯性。


 


53AI,企业落地大模型首选服务商

产品:场景落地咨询+大模型应用平台+行业解决方案

承诺:免费场景POC验证,效果验证后签署服务协议。零风险落地应用大模型,已交付160+中大型企业

联系我们

售前咨询
186 6662 7370
预约演示
185 8882 0121

微信扫码

添加专属顾问

回到顶部

加载中...

扫码咨询