Files
ailine/backend/app/main_graph/utils/main_graph_builder.py
root 229cfa67a2
All checks were successful
构建并部署 AI Agent 服务 / deploy (push) Successful in 5m56s
修改架构:llm_call后增加观察环节注释
2026-05-01 15:00:17 +08:00

214 lines
6.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
React 模式主图构建器 - 完整循环推理版本
Main Graph Builder - Full React Mode with Loop Reasoning
"""
from app.main_graph.graph import StateGraph, START, END
from typing import Dict, Any
from app.main_graph.state import MainGraphState, CurrentAction
from app.main_graph.nodes.react_nodes import (
init_state_node,
react_reason_node,
web_search_node,
error_handling_node,
route_by_reasoning
)
from app.main_graph.nodes.llm_call import create_llm_call_node
from app.main_graph.nodes.rag_nodes import rag_retrieve_node
from app.subgraphs.contact import build_contact_subgraph
from app.subgraphs.dictionary import build_dictionary_subgraph
from app.subgraphs.news_analysis import build_news_analysis_subgraph
# ========== 子图包装器(处理子图错误传递) ==========
def wrap_subgraph_for_error_handling(subgraph, name: str):
"""
包装子图,使其错误能传递给主图
Args:
subgraph: 编译好的子图
name: 子图名称(用于错误标识)
Returns: 包装后的节点函数
"""
def wrapped_node(state: MainGraphState) -> MainGraphState:
try:
# 调用子图
result = subgraph.invoke(state)
# 更新主图状态
if name == "contact":
state.contact_result = result
elif name == "dictionary":
state.dictionary_result = result
elif name == "news_analysis":
state.news_result = result
# 标记成功
state.success = True
return state
except Exception as e:
# 捕获子图错误,传递给主图
from app.main_graph.state import ErrorRecord, ErrorSeverity
from datetime import datetime
error_record = ErrorRecord(
error_type=f"{name}SubgraphError",
error_message=str(e),
severity=ErrorSeverity.WARNING,
source=f"{name}_subgraph",
timestamp=datetime.now().isoformat(),
retry_count=0,
max_retries=1,
context={"user_query": state.user_query}
)
state.errors.append(error_record)
state.current_error = error_record
state.current_phase = "error_handling"
state.success = False
return state
return wrapped_node
# ========== 主图构建 ==========
def build_react_main_graph(llm=None, tools=None) -> StateGraph:
"""
构建完整的 React 模式主图
流程:
START
init_state (初始化)
react_reason (推理) ←──────────────┐
↓ │
条件路由 │
├─ rag_retrieve →───────────────┤
├─ contact_subgraph →───────────┤
├─ dictionary_subgraph →────────┤
├─ news_analysis_subgraph →─────┤
├─ handle_error → (重试或结束) ─┤
└─ llm_call (大模型调用) ←──────┘
🔍 观察 (检查 tool_calls)
[有工具调用?]
├─ 是 → 执行工具 → 回到 llm_call
└─ 否 → END
"""
# 创建图
graph = StateGraph(MainGraphState)
# 创建 llm_call 节点
llm_node = None
if llm is not None:
llm_node = create_llm_call_node(llm, tools or [])
# ========== 添加节点 ==========
# 1. 初始化节点
graph.add_node("init_state", init_state_node)
# 2. React 推理节点
graph.add_node("react_reason", react_reason_node)
# 3. RAG 检索节点
graph.add_node("rag_retrieve", rag_retrieve_node)
# 4. 联网搜索节点
graph.add_node("web_search", web_search_node)
# 5. 错误处理节点
graph.add_node("handle_error", error_handling_node)
# 6. LLM 调用节点(真正的大模型输出)
if llm_node is not None:
graph.add_node("llm_call", llm_node)
# ========== 添加子图节点 ==========
# 构建并包装子图(带错误处理)
contact_graph = build_contact_subgraph()
dictionary_graph = build_dictionary_subgraph()
news_analysis_graph = build_news_analysis_subgraph()
graph.add_node(
"contact_subgraph",
wrap_subgraph_for_error_handling(contact_graph.compile(), "contact")
)
graph.add_node(
"dictionary_subgraph",
wrap_subgraph_for_error_handling(dictionary_graph.compile(), "dictionary")
)
graph.add_node(
"news_analysis_subgraph",
wrap_subgraph_for_error_handling(news_analysis_graph.compile(), "news_analysis")
)
# ========== 添加边 ==========
# 1. START → init_state
graph.add_edge(START, "init_state")
# 2. init_state → react_reason
graph.add_edge("init_state", "react_reason")
# 3. 条件路由react_reason → 各分支
graph.add_conditional_edges(
"react_reason",
route_by_reasoning,
{
# 检索分支 → 检索后回到推理
"rag_retrieve": "rag_retrieve",
# 联网搜索分支
"web_search": "web_search",
# 子图分支 → 子图后回到推理
"contact_subgraph": "contact_subgraph",
"dictionary_subgraph": "dictionary_subgraph",
"news_analysis_subgraph": "news_analysis_subgraph",
# 错误处理分支
"handle_error": "handle_error",
# LLM 调用分支 → 直接输出给用户
"llm_call": "llm_call"
}
)
# 4. 循环边:检索/搜索/子图/错误处理后 → 回到推理
graph.add_edge("rag_retrieve", "react_reason")
graph.add_edge("web_search", "react_reason")
graph.add_edge("contact_subgraph", "react_reason")
graph.add_edge("dictionary_subgraph", "react_reason")
graph.add_edge("news_analysis_subgraph", "react_reason")
graph.add_edge("handle_error", "react_reason")
# 5. 条件路由llm_call 后检查是否有工具调用
# 注意:这里简化处理,先直接 END后续再完善工具调用循环
if llm_node is not None:
graph.add_edge("llm_call", END)
return graph
# ========== 兼容性:保留旧的函数名 ==========
def build_main_graph() -> StateGraph:
"""
兼容性函数:旧代码调用 build_main_graph() 时返回 React 版本
"""
return build_react_main_graph()
# ========== 导出 ==========
__all__ = [
"build_react_main_graph",
"build_main_graph",
"wrap_subgraph_for_error_handling"
]