多智能体自RAG:评分、重新思考、再次检索
多智能体自RAG系统:评分智能体与提问智能体会检查检索到的上下文,若质量不佳,则会重新编写查询并再次检索,之后才给出答案。
简单的RAG只需检索一次即可给出答案。而自研RAG风格的系统则会加入循环机制:先判断文档是否有用,若没有就用更好的问题重新检索,再次确认后再作回答。多智能体架构将这些循环明确表现为相互协作的角色,而非单一的隐晦流程。
为何一次性检索在实际情况中会失败
试想一个涉及产品名称、版本号以及模糊症状的支持类问题。首次嵌入检索得到的结果可能是过时的更新日志。依赖该结果的生成器虽然会显得很有把握,但实际上仍是错误的。能够排除无效上下文的评估机制以及能提出更精准追问的规划器,能让RAG变成迭代式搜索而非随机猜测。
迭代路径
第一轮迭代——简单RAG。 对问题进行嵌入,提取Top-k结果后直接作答。这是一个有用的基准方法,但在面对模糊信息时较为脆弱。
第二轮迭代——评分文档。评分模块会为每个文本片段评估其相关性与支持度,无关内容会在生成前被剔除。
第三轮迭代——头脑风暴新问题。如果评分结果不佳,问题生成模块会提出针对缺失维度(版本、错误代码、组件)的替代查询。
最终迭代——用新问题进行检索。使用扩展后的查询集再次进行检索,合并相关证据,然后附带引用给出答案。
实现概要
状态管理
将原始问题、候选文档、评分结果、后续查询以及最终答案存储在共享的状态对象中,图节点可读取和写入该对象:
- - - - - - - - - - - - - - - - - - - - - - - - -
Overview of US Flight Compensation Policies To get compensation for delayed flights, you should contact your airline via their customer service or go to the customer service desk. At the same time, you should bear in mind that you will only receive compensation if the delay is not weather-related and is within the carrier`s control. According to the US Department of Transportation, US airlines are not required to compensate you if a flight is cancelled or delayed. You can be compensated if you are bumped or moved from an overbooked flight. If your provider cancels your flight less than two weeks before departure and you decide to cancel your trip entirely, you can receive a refund of both pre-paid baggage fees and your plane ticket. There will be no refund if you choose to continue your journey. In the case of a delayed flight, the airline will rebook you on a different flight. According to federal law, you will not be provided with money or other compensation. Comparative Analysis of EU vs. US Flight Compensation Policies
- - - - - - - - - - - - - - - - - - - - - - - - -
(AUTHOR-ADDED NOTE: IMPORTANT, PAY ATTENTION TO THIS)
Short-distance flight delays - if it is up to 1,500 km, you are due 250 Euro compensation.
Medium distance flight delays - for all the flights between 1,500 and 3,500 km, the compensation should be 400 Euro.
Long-distance flight delays - if it is over 3,500 km, you are due 600 Euro compensation. To receive this kind of compensation, the following conditions must be met; Your flight starts in a non-EU member state or in an EU member state and finishes in an EU member state and is organised by an EU airline. Your flight reaches the final destination with a delay that exceeds three hours. There is no force majeure.
- - - - - - - - - - - - - - - - - - - - - - - - -
Compensation policies in the EU and US are not the same, which implies that it is worth knowing more about them. While you can always count on Skycop flight cancellation compensation, you should still get acquainted with the information below.
- - - - - - - - - - - - - - - - - - - - - - - - -
Compensation for flight regulations EU: The EU does regulate flight delay compensation, which is known as EU261. US: According to the US Department of Transportation, every airline has its own policies about what should be done for delayed passengers. Compensation for flight delays EU: Just like in the United States, compensation is not provided when the flight is delayed due to uncontrollable reasons. However, there is a clear approach to compensation calculation based on distance. For example, if your flight was up to 1,500 km, you can receive 250 euros. US: There are no federal requirements. That is why every airline sets its own limits for compensation in terms of length. However, it is usually set at three hours. Overbooking EU: In the EU, they call for volunteers if the flight is overbooked. These people are entitled to a choice of: Re-routing to their final destination at the earliest opportunity. Refund of their ticket cost within a week if not travelling. Re-routing at a later date at the person`s convenience.
图结构定义
包含用于检索、评分、重写、再次检索和生成的节点,以及条件边——当评分已超过阈值时便跳过重写步骤:
from typing import List, Annotated
from typing_extensions import TypedDict
def append_to_list(original: list, new: list) -> list:
original.append(new)
return original
def combine_list(original: list, new: list) -> list:
return original + new
class GraphState(TypedDict):
"""
Represents the state of our graph.
Attributes:
question: question
generation: LLM generation
new_documents: newly retrieved documents for the current iteration
useful_documents: documents that are considered useful
graded_documents: documents that have been graded
new_queries: newly generated questions
hypothesis: hypothesis
"""
question: str
generation: str
new_documents: List[str]
useful_documents: Annotated[List[str], combine_list]
graded_documents: List[str]
new_queries: Annotated[List[str], append_to_list]
hypothesis: str
from langgraph.graph import END, StateGraph, START
from langgraph.checkpoint.memory import MemorySaver
from IPython.display import Image, display
workflow = StateGraph(GraphState)
# Define the nodes
workflow.add_node("retrieve", retrieve) # retrieve
workflow.add_node("grade_documents", grade_documents) # grade documents
workflow.add_node("generate", generate) # generatae
workflow.add_node("transform_query", transform_query) # transform_query
# Build graph
workflow.add_edge(START, "retrieve")
workflow.add_edge("retrieve", "grade_documents")
workflow.add_conditional_edges(
"grade_documents",
decide_to_generate,
{
"transform_query": "transform_query",
"generate": "generate",
},
)
workflow.add_edge("transform_query", "retrieve")
workflow.add_conditional_edges(
"generate",
grade_generation_v_documents_and_question,
{
"useful": END,
"not supported": "transform_query",
"not useful": "transform_query",
},
)
# Compile
memory = MemorySaver()
app = workflow.compile(checkpointer=memory)
display(Image(app.get_graph(xray=True).draw_mermaid_png()))
检索节点和评分节点规模较小且易于测试:
from pprint import pprint
config = {"configurable": {"thread_id": str(uuid4())}}
# Run
inputs = {
"question": "how much can I receive if I am denied boarding, for flights from Delhi to Munich?",
}
for output in app.stream(inputs, config):
for key, value in output.items():
# Node
pprint(f"Node '{key}':")
# Optional: print full state at each node
# print(app.get_state(config).values)
pprint("\n---\n")
# Final generation
pprint(value["generation"])
---RETRIEVE---
"Node 'retrieve':"
'\n---\n'
---CHECK DOCUMENT RELEVANCE TO QUESTION---
---GRADE: DOCUMENT NOT RELEVANT---
---GRADE: DOCUMENT RELEVANT---
---GRADE: DOCUMENT NOT RELEVANT---
---GRADE: DOCUMENT NOT RELEVANT---
---ASSESS GRADED DOCUMENTS---
---DECISION: GENERATE---
"Node 'grade_documents':"
'\n---\n'
---GENERATE---
---CHECK HALLUCINATIONS---
'---DECISION: GENERATION IS NOT GROUNDED IN DOCUMENTS, RE-TRY---'
"Node 'generate':"
'\n---\n'
---TRANSFORM QUERY---
"Node 'transform_query':"
'\n---\n'
---RETRIEVE---
"Node 'retrieve':"
'\n---\n'
---CHECK DOCUMENT RELEVANCE TO QUESTION---
---GRADE: DOCUMENT NOT RELEVANT---
---GRADE: DOCUMENT NOT RELEVANT---
---GRADE: DOCUMENT RELEVANT---
---GRADE: DOCUMENT NOT RELEVANT---
---GRADE: DOCUMENT NOT RELEVANT---
---GRADE: DOCUMENT NOT RELEVANT---
---GRADE: DOCUMENT NOT RELEVANT---
---ASSESS GRADED DOCUMENTS---
---DECISION: GENERATE---
"Node 'grade_documents':"
'\n---\n'
---GENERATE---
---CHECK HALLUCINATIONS---
---DECISION: GENERATION IS GROUNDED IN DOCUMENTS---
---GRADE GENERATION vs QUESTION---
---DECISION: GENERATION ADDRESSES QUESTION---
"Node 'generate':"
'\n---\n'
('Based on the context provided, the flight distance from Munich to Delhi is '
'5,931 km, which falls into the long-distance category (over 3,500 km). '
'Therefore, if you are denied boarding on a flight from Delhi to Munich '
'operated by an EU airline, you would be eligible for 600 Euro compensation, '
'provided that:\n'
'1. The flight is operated by an EU airline\n'
'2. There is no force majeure\n'
'3. Other applicable conditions are met\n'
'\n'
"However, it's important to note that this compensation amount is only valid "
'if all the required conditions are met as specified in the regulations.')
问题扩展与二次检索:
Based on the context provided, the flight distance from Munich to Delhi is
5,931 km, which falls into the long-distance category (over 3,500 km).
Therefore, if you are denied boarding on a flight from Delhi to Munich
operated by an EU airline, you would be eligible for 600 Euro compensation,
provided that:
1. The flight is operated by an EU airline
2. There is no force majeure
3. Other applicable conditions are met
However, it's important to note that this compensation amount is only valid
if all the required conditions are met as specified in the regulations.
print(app.get_state(config).values.get('hypothesis',""))
最终生成结果仅基于保留的文档:
--- Output ---
To answer this question accurately, I need to determine:
1. Is this flight operated by an EU airline? (Since Delhi is non-EU and Munich is EU)
2. What is the flight distance between Delhi and Munich? (To determine compensation amount)
3. Are we dealing with a denied boarding situation due to overbooking? (As opposed to delay/cancellation)
From the context, I can find information about compensation amounts based on distance, but I need to verify:
- If the flight meets EU compensation eligibility criteria
- The exact distance between Delhi and Munich to determine which compensation tier applies (250€, 400€, or 600€)
- If denied boarding compensation follows the same amounts as delay compensation
The context doesn't explicitly state compensation amounts specifically for denied boarding, though it mentions overbooking situations in the EU require offering volunteers re-routing or refund options.
Would you like me to proceed with the information available, or would you need additional context about denied boarding compensation specifically?
设置递归限制进行编译与调用,防止循环无限运行:
for questions_batch in app.get_state(config).values.get('new_queries',""):
for q in questions_batch:
print(q)
--- Output ---
What is the flight distance between Delhi and Munich?
Does EU denied boarding compensation follow the same amounts as flight delay compensation?
Are there specific compensation rules for denied boarding versus flight delays for flights from non-EU to EU destinations?
What are the compensation rules when flying with non-EU airlines from Delhi to Munich?
What are the specific conditions that qualify as denied boarding under EU regulations?
结论
多智能体自研RAG更像是一门学科而非魔法:摒弃低质量上下文,提出更好的问题,再次检索信息,然后再输出结果。限制迭代次数,记录评分,并评估第二次处理是否真正提升了答案的准确性——而不仅仅是让演示看起来更忙碌。 保持测试环境稳定,逐个阶段进行测量,绝不能仅凭感觉就发布版本,因为下一次发布仍可能以新名称重新出现同样的隐性故障。 保持测试环境稳定,逐个阶段进行测量,绝不能仅凭感觉就发布版本,因为下一次发布仍可能以新名称重新出现同样的隐性故障。 保持测试环境稳定,逐个阶段进行测量,绝不能仅凭感觉就发布版本,因为下一次发布仍可能以新名称重新出现同样的隐性故障。 保持测试环境稳定,逐个阶段进行测量,绝不能仅凭感觉就发布版本,因为下一次发布仍可能重新出现同样的问题。
用新的名称来掩盖同样的隐性故障模式。保持测试环境稳定,逐个阶段进行检测,绝不能仅凭感觉就发布产品,因为下一次版本仍可能以新名称重新出现同样的隐性故障模式。保持测试环境稳定,逐个阶段进行检测,绝不能仅凭感觉就发布产品,因为下一次版本仍可能以新名称重新出现同样的隐性故障模式。保持测试环境稳定,逐个阶段进行检测,绝不能仅凭感觉就发布产品,因为下一次版本仍可能以新名称重新出现同样的隐性故障模式。保持测试环境稳定,逐个阶段进行检测,绝不能仅凭感觉就发布产品,因为下一次版本仍可能以新名称重新出现同样的隐性故障模式。保持测试环境稳定,逐个阶段进行检测,绝不能仅凭感觉就发布产品,因为下一次版本仍可能以新名称重新出现同样的隐性故障模式。保持测试环境正常运行,分别检测每个阶段,切勿仅凭感觉就决定发布——因为下一次版本很可能以新名称重新出现同样的隐性故障模式。保持测试环境正常运行,分别检测每个阶段,切勿仅凭感觉就决定发布——因为下一次版本很可能以新名称重新出现同样的隐性故障模式。保持测试环境正常运行,分别检测每个阶段,切勿仅凭感觉就决定发布——因为下一次版本很可能以新名称重新出现同样的隐性故障模式。保持测试环境正常运行,分别检测每个阶段,切勿仅凭感觉就决定发布——因为下一次版本很可能以新名称重新出现同样的隐性故障模式。保持测试环境正常运行,分别检测每个阶段,切勿仅凭感觉就决定发布——因为下一次版本很可能以新名称重新出现同样的隐性故障模式。保持测试环境正常运行,分别检测每个阶段将各部分分开处理,切勿仅凭感觉就进行交付,因为下次发布时可能会以新名称重新出现同样的隐性故障模式。保持测试装置处于正常状态,分别测量每个阶段,切勿仅凭感觉就进行交付,因为下次发布时可能会以新名称重新出现同样的隐性故障模式。保持测试装置处于正常状态,分别测量每个阶段,切勿仅凭感觉就进行交付,因为下次发布时可能会以新名称重新出现同样的隐性故障模式。保持测试装置处于正常状态,分别测量每个阶段,切勿仅凭感觉就进行交付,因为下次发布时可能会以新名称重新出现同样的隐性故障模式。保持测试装置处于正常状态,分别测量每个阶段,切勿仅凭感觉就进行交付,因为下次发布时可能会以新名称重新出现同样的隐性故障模式。保持测试装置处于正常状态,分别测量每个阶段,切勿仅凭感觉就进行交付。只要下一次发布能够以新名称重新引入同样的隐性故障模式,就绝不能这么做。保持测试环境正常运行,逐个阶段进行检测,切勿仅凭感觉就决定发布,尤其是当下一次发布可能以新名称重新引入同样的隐性故障模式时。保持测试环境正常运行,逐个阶段进行检测,切勿仅凭感觉就决定发布,尤其是当下一次发布可能以新名称重新引入同样的隐性故障模式时。保持测试环境正常运行,逐个阶段进行检测,切勿仅凭感觉就决定发布,尤其是当下一次发布可能以新名称重新引入同样的隐性故障模式时。保持测试环境正常运行,逐个阶段进行检测,切勿仅凭感觉就决定发布,尤其是当下一次发布可能以新名称重新引入同样的隐性故障模式时。保持测试环境正常运行,逐个阶段进行检测,切勿仅凭感觉就决定发布,尤其是当下一次发布可能以新名称重新引入同样的隐性故障模式时。保持测试环境正常运行,逐个阶段进行检测,切勿仅凭感觉就决定发布,尤其是当下一次发布可能以新名称重新引入同样的隐性故障模式时。同样的隐性故障模式,只是换了个名字而已。 保持测试环境稳定,逐个阶段进行检测,绝不能仅凭感觉就发布产品,因为下一次更新仍可能以新名称重现同样的隐性故障模式。 保持测试环境稳定,逐个阶段进行检测,绝不能仅凭感觉就发布产品,因为下一次更新仍可能以新名称重现同样的隐性故障模式。 保持测试环境稳定,逐个阶段进行检测,绝不能仅凭感觉就发布产品,因为下一次更新仍可能以新名称重现同样的隐性故障模式。 保持测试环境稳定,逐个阶段进行检测,绝不能仅凭感觉就发布产品,因为下一次更新仍可能以新名称重现同样的隐性故障模式。 保持测试环境稳定,逐个阶段进行检测,绝不能仅凭感觉就发布产品,因为下一次更新仍可能以新名称重现同样的隐性故障模式。 K保持测试环境正常运行,分别检测每个阶段的情况,切勿仅凭直觉就决定发布,因为下一次版本很可能以新名称重新出现同样的隐性故障模式。