构建一个自主深度思考的RAG管道以解决复杂查询--深度思考RAG机器的布线(7)

34 阅读7分钟

我们所有的单个组件都已准备就绪:

  1. 我们的节点(工作节点

  2. 我们的条件边(管理者)。

现在是时候将它们全部连接成一个统一的系统了。

我们将使用LangGraph的状态图来定义我们的智能体的完整认知架构。这是我们规划智能体思维过程蓝图的地方,精确地定义信息如何从一个步骤流向另一个步骤。

我们要做的第一件事是创建一个StateGraph实例。我们将告诉它,它要传递的**"状态"** 是我们的RAGState字典。

from langgraph.graph import StateGraph, END # Import the main graph components

# Instantiate the graph, telling it to use our RAGState TypedDict as its state schema.
graph = StateGraph(RAGState)

现在我们有了一个空图。下一步是添加我们之前定义的所有节点。.add_node()方法接受两个参数:节点的唯一字符串名称,以及节点将执行的Python函数。

# Add all of our Python functions as nodes in the graph
graph.add_node("plan", plan_node)                     # The node that creates the initial plan
graph.add_node("retrieve_10k", retrieval_node)        # The node for internal document retrieval
graph.add_node("retrieve_web", web_search_node)       # The node for external web search
graph.add_node("rerank", rerank_node)                 # The node that performs precision reranking
graph.add_node("compress", compression_node)          # The node that distills the context
graph.add_node("reflect", reflection_node)            # The node that summarizes findings and updates history
graph.add_node("generate_final_answer", final_answer_node) # The node that synthesizes the final answer

现在,我们所有的专家都已就位。最后也是最关键的一步是定义连接它们的 “线路”。这就是我们使用.add_edge()和.add_conditional_edges()方法来定义控制流的地方。

# The entry point of our graph is the "plan" node. Every run starts here.
graph.set_entry_point("plan")

# After the "plan" node, we use our first conditional edge to decide which tool to use.
graph.add_conditional_edges(
    "plan",           # The source node
    route_by_tool,    # The function that makes the decision
    {                 # A dictionary mapping the function's output string to the destination node
        "search_10k": "retrieve_10k",
        "search_web": "retrieve_web",
    },
)

# After retrieving from either the 10-K or the web, the flow is linear for a bit.
graph.add_edge("retrieve_10k", "rerank") # After internal retrieval, always go to rerank.
graph.add_edge("retrieve_web", "rerank") # After web retrieval, also always go to rerank.
graph.add_edge("rerank", "compress")      # After reranking, always go to compress.
graph.add_edge("compress", "reflect")     # After compressing, always go to reflect.

# After the "reflect" node, we hit our main conditional edge, which controls the reasoning loop.
graph.add_conditional_edges(
    "reflect",        # The source node
    should_continue_node, # The function that calls our Policy Agent
    {                 # A dictionary mapping the decision to the next step
        "continue": "plan", # If the decision is "continue", we loop back to the "plan" node to route the next step.
        "finish": "generate_final_answer", # If the decision is "finish", we proceed to generate the final answer.
    },
)

# The "generate_final_answer" node is the last step before the end.
graph.add_edge("generate_final_answer", END) # After generating the answer, the graph concludes.
print("StateGraph constructed successfully.")

这是我们智能体大脑的蓝图。让我们追踪一下流程:

  1. 一切总是始于计划。

  2. 随后,route_by_tool条件边就像一个开关,将流程导向retrieve_10k或retrieve_web。

  3. 无论运行哪种检索器,输出始终会通过重排序 -> 压缩 -> 反思管道。

  4. 这就把我们带到了最重要的部分:should_continue_node条件边。这是我们循环推理的核心。

  • 如果策略代理说CONTINUE_PLAN,边缘将工作流一路发送回计划节点。我们回到计划(而不是直接到下一个检索器),以便route_by_tool能够正确地路由计划中的

    下一个

    步骤。

  • 如果策略代理说FINISH,边缘节点将跳出循环,并将工作流发送到generate_final_answer节点。

  • 最后,答案生成后,图在END处终止。

我们已经成功地定义了深度思考智能体的完整、复杂且循环的架构。现在唯一要做的就是将这个蓝图编译成可运行的应用程序,并将其可视化,看看我们构建了什么。

编译和可视化迭代工作流程

在我们的图完全连接好之后,汇编过程的最后一步是编译它。.compile()方法会获取我们对节点和边的抽象定义,并将其转化为一个具体的、可执行的应用程序。

然后,我们可以使用内置的LangGraph实用工具来生成我们图形的图表。可视化工作流程对于理解和调试复杂的智能体系统非常有帮助。它将我们的代码转换为直观的流程图,清晰地展示了智能体可能的推理路径。

所以,基本上,我们正在把蓝图变成一台真正的机器。

# The .compile() method takes our graph definition and creates a runnable object.
deep_thinking_rag_graph = graph.compile()
print("Graph compiled successfully.")


# Now, let's visualize the architecture we've built.
try:
    from IPython.display import Image, display
    # We can get a PNG image of the graph's structure.
    png_image = deep_thinking_rag_graph.get_graph().draw_png()
    # Display the image in our notebook.
    display(Image(png_image))
except Exception as e:
    # This can fail if pygraphviz and its system dependencies are not installed.
    print(f"Graph visualization failed: {e}. Please ensure pygraphviz is installed.")

deep_thinking_rag_graph 对象现在是我们功能完备的代理。可视化代码随后调用 .get_graph().draw_png() 来生成我们构建的状态机的可视化表示。

深度思考简化管道流程(作者: 法里德·汗 )

我们可以清楚地看到:

  • 初始分支逻辑,其中route_by_tool在retrieve_10k和retrieve_web之间进行选择。

  • 每个研究步骤的线性处理流程(重排序 -> 压缩 -> 反思)。

  • 关键的反馈回路,其中should_continue边将工作流返回到计划节点,以开始下一个研究周期。

  • 一旦研究完成,通向generate_final_answer的最终“出口匝道”。

这是一个具备思考能力的系统架构。现在,让我们来对它进行测试。

运行深度思考管道

我们已经设计出了一个推理引擎。现在是时候看看它能否在我们的基线系统惨败的地方取得成功了。

我们将使用完全相同的多跳、多源挑战查询来调用我们编译的deep_thinking_rag_graph。我们将使用.stream()方法来获取代理执行的实时、逐步跟踪,观察其在解决问题时的“思维过程”。

本节的计划如下:

  • **调用图表:**我们将运行代理并观察它执行计划的过程,在工具之间切换并构建其研究历史。

  • **分析最终输出:**我们将检查最终的综合答案,看它是否成功整合了10-K报告和网络上的信息。

  • **比较结果:**我们将进行最终的并排比较,以明确凸显我们深度思考智能体的架构优势。

我们将设置初始输入,它只是一个包含original_question的字典,然后调用.stream()方法。stream方法对于调试和观察非常有用,因为它会在每个节点完成工作后输出图的状态。

# This will hold the final state of the graph after the run is complete.
final_state = None
# The initial input for our graph, containing the original user query.
graph_input = {"original_question": complex_query_adv}

print("--- Invoking Deep Thinking RAG Graph ---")
# We use .stream() to watch the agent's process in real-time.
# "values" mode means we get the full RAGState object after each step.
for chunk in deep_thinking_rag_graph.stream(graph_input, stream_mode="values"):
    # The final chunk in the stream will be the terminal state of the graph.
    final_state = chunk
print("\n--- Graph Stream Finished ---")

这个循环是我们的智能体活跃起来的地方。在每次迭代中,LangGraph 都会执行工作流中的下一个节点,更新RAGState,并将新状态返回给我们。我们嵌入在节点内部的rich库console.print语句将为我们提供智能体行动和决策的实时评论。

#### OUTPUT ####

--- Invoking Deep Thinking RAG Graph ---

--- 🧠: Generating Plan ---
plan:
  steps:
  - sub_question: What are the key risks related to competition as stated in NVIDIA's 2023 10-K filing?
    tool: search_10k
    ...
  - sub_question: What are the recent news and developments in AMD's AI chip strategy in 2024?
    tool: search_web
    ...

--- 🔍: Retrieving from 10-K (Step 1: ...) ---
  Rewritten Query: key competitive risks for NVIDIA in the semiconductor industry...
  Supervisor Decision: Use `hybrid_search`. ...

--- 🎯: Reranking Documents ---
  Reranked to top 3 documents.

--- ✂️: Distilling Context ---
  Distilled Context Snippet: NVIDIA operates in the intensely competitive semiconductor industry...

--- 🤔: Reflecting on Findings ---
  Summary: According to its 2023 10-K, NVIDIA operates in an intensely competitive semiconductor industry...

--- 🚦: Evaluating Policy ---
  -> Decision: CONTINUE_PLAN | Justification: The first step...has been completed. The next step...is still pending...

--- 🌐: Searching Web (Step 2: ...) ---
  Rewritten Query: AMD AI chip strategy news and developments 2024...

--- 🎯: Reranking Documents ---
  Reranked to top 3 documents.

--- ✂️: Distilling Context ---
  Distilled Context Snippet: AMD has ramped up its challenge to Nvidia in the AI accelerator market with its Instinct MI300 series...

--- 🤔: Reflecting on Findings ---
  Summary: In 2024, AMD is aggressively competing with NVIDIA in the AI chip market through its Instinct MI300X accelerator...

--- 🚦: Evaluating Policy ---
  -> Decision: FINISH | Justification: The research history now contains comprehensive summaries of both NVIDIA's stated risks and AMD's recent strategy...

--- ✅: Generating Final Answer with Citations ---

--- Graph Stream Finished ---

您可以看到我们设计的执行情况。代理:

  1. **计划:**它制定了正确的两步多工具计划。

  2. **执行步骤1:**使用了search_10k,将其通过完整的检索漏斗运行,并对结果进行了反思。

  3. **自我评估:**策略代理发现计划尚未完成,决定继续执行计划。

  4. **执行步骤2:**它正确切换到search_web工具,通过相同的漏斗运行,并再次反映。

  5. **再次自我审视:**这次,策略代理发现所有必要信息都已收集齐全,并正确决定结束。

  6. **合成:**工作流随后进入generate_final_answer节点。

智能体已成功处理复杂查询。现在,让我们来查看它给出的最终答案。