redesign_summary.md 6.8 KB

Custom React Agent 重构概要设计

本文档总结了将原有基于 create_react_agent 的 Demo 重构为使用 StateGraph 的、具备强大上下文处理能力和流程控制能力的新版 Agent 的概要设计。

1. 重构核心目标

  • 解决上下文遗忘问题:确保 Agent 在多轮对话中,尤其是在连续调用 SQL 相关工具时,能够理解并利用之前的对话历史(如上文提到的实体“南城服务区”)。
  • 增强流程控制能力:对 generate_sql -> valid_sql -> run_sql 这一固定流程进行强力引导,防止 LLM“忘记”执行下一步或执行错误,提高 Agent 的可靠性和可预测性。
  • 提升代码健壮性与可维护性:通过模块化和清晰的职责划分,使代码更易于理解、调试和扩展。

2. 最终 StateGraph 架构

新架构的核心是一个包含 5 个节点的 StateGraph,它取代了原有的 create_react_agent 黑盒。

StateGraph Flow

graph TD
    A[START] --> B(agent_node);
    B --> C{有工具调用?};
    C -- 是 --> D(prepare_tool_input_node);
    C -- 否 --> G(format_final_response_node);
    D --> E(tool_node);
    E --> F(update_state_after_tool_node);
    F --> B;
    G --> H[END];

2.1. 节点职责

  • agent_node (决策者)

    • 输入: 完整的 state,包含 messages 历史和 suggested_next_step
    • 职责:
    • 读取完整的对话历史。
    • 读取 state.suggested_next_step 作为强烈的行动建议 (例如: valid_sql, run_sql, analyze_error)。
    • 通过提示工程,将建议和历史结合,让 LLM 做出决策。
    • 输出: 一个“草稿版”的 tool_calls,或决定直接回答的 AIMessage
  • prepare_tool_input_node (信息组装者) - (新增节点)

    • 位置: agent_node 之后, tool_node 之前。
    • 职责:
    • 检查 agent_node 输出的 tool_calls
    • 如果发现需要上下文的工具(如 generate_sql),则从 state.messages 中提取完整的对话历史。
    • 将提取的历史作为 history_messages 参数,注入tool_callsargs 中。
    • 输出: 一个“精装版”的、包含了完整上下文信息的 tool_calls
  • tool_node (执行者)

    • 职责: 接收“精装版”的 tool_calls,并忠实地调用 sql_tools.py 中的工具函数。
  • update_state_after_tool_node (流程建议与错误处理器) - (新增节点)

    • 位置: tool_node 之后。
    • 职责:
    • 检查刚刚执行的工具名称及其返回结果(成功/失败)。
    • 根据预设的逻辑,智能地更新 state.suggested_next_step 字段,以精确引导下一步:
      • generate_sql 成功: suggested_next_step -> "valid_sql"
      • generate_sql 失败: suggested_next_step -> "answer_with_common_sense" (引导LLM基于常识回答或向用户解释)
      • valid_sql 成功: suggested_next_step -> "run_sql"
      • valid_sql 失败: suggested_next_step -> "analyze_validation_error" (引导LLM分析错误原因)
      • run_sql 执行后: suggested_next_step -> "summarize_final_answer" (引导LLM基于数据总结)
    • 输出: 更新后的 state
  • format_final_response_node (最终输出格式化器) - (新增节点)

    • 位置: 在 agent_node 决定直接回答后,图结束前。
    • 职责 (v1 - 占位):
    • 当前阶段: 仅作为流程占位符,证明流程已正确进入此节点。
    • 在日志中打印一条明确的信息,如 "[Node] format_final_response - 准备格式化最终输出..."
    • 职责 (未来):
    • state 中提取 LLM 的最终文字总结和最近一次 run_sql 的数据(如果存在)。
    • 将数据格式化为 Markdown 表格。
    • 将文字总结和数据表格合并成一个对用户友好的、结构化的最终答案。
    • 输出: 更新 state 中最后一条 AIMessage 的内容。

3. AgentState 状态设计

state.py 文件将定义 StateGraph 中流转的数据结构。

from typing import TypedDict, Annotated, Optional, List
from langchain_core.messages import BaseMessage

class AgentState(TypedDict):
    messages: Annotated[List[BaseMessage], add_messages]
    user_id: str
    thread_id: str
    # 新增字段,用于引导 LLM 的下一步行动
    suggested_next_step: Optional[str]
  • messages: 核心字段,存储完整的、包含 HumanMessage, AIMessage, ToolMessage 的对话历史。
  • suggested_next_step: 流程控制的关键。它由 update_state_after_tool_node 写入,由 agent_node 读取,为 LLM 提供强力的流程引导。

4. 工具签名与实现

  • sql_tools.py:
    • generate_sql(question: str, history_messages: List[BaseMessage]) -> str:
    • 修改其函数签名,明确要求传入 history_messages
    • 在其内部,将 questionhistory_messages 组合成更丰富的提示,再交给 Vanna 的 LLM 进行处理,从而解决上下文理解问题。
    • valid_sqlrun_sql 保持简单的输入输出。

5. 日志与持久化

  • 日志: 使用 Python 内置的 logging 模块,由 config.py 控制级别。在每个节点的入口和出口、关键的逻辑判断处打印详细日志,以便清晰地追踪 Agent 的思考和执行链路。
  • 持久化: 完全复用并保留原有的 AsyncRedisSaver 机制。CustomReactAgent 在初始化时创建 checkpointer,并在编译 StateGraph 时传入,以实现自动的状态持久化。

6. 优势总结

  1. 双重上下文保障:
    • 数据上下文: 通过 prepare_tool_input_node 确保 generate_sql 能获取完整的对话历史。
    • 流程上下文: 通过 update_state_after_tool_nodesuggested_next_step 确保 Agent 遵循预设的执行流程。
  2. 职责分离: 每个节点职责单一(决策、准备数据、执行、更新状态),代码清晰,易于维护。
  3. 高度可控与可预测: 在给予 LLM 思考空间的同时,通过代码逻辑保证了核心流程的稳定性和可靠性。
  4. 易于调试: 详细的日志输出将使追踪和定位问题变得非常简单。