diff --git a/backend/app/main_graph/nodes/react_nodes.py b/backend/app/main_graph/nodes/react_nodes.py index b0338c0..2843aa1 100644 --- a/backend/app/main_graph/nodes/react_nodes.py +++ b/backend/app/main_graph/nodes/react_nodes.py @@ -88,7 +88,61 @@ def react_reason_node(state: MainGraphState) -> MainGraphState: return state -# ========== 2. 错误处理节点 ========== +# ========== 2. 联网搜索节点 ========== + +def web_search_node(state: MainGraphState) -> MainGraphState: + """ + 联网搜索节点:执行搜索并将结果保存到状态 + """ + state.current_phase = "web_searching" + + # 获取搜索查询 + reasoning_result = state.debug_info.get("reasoning_result") + search_query = reasoning_result.metadata.get("search_query", state.user_query) if reasoning_result else state.user_query + + try: + from app.core import web_search + + print(f"[WebSearch] 搜索: {search_query}") + search_result = web_search(search_query, max_results=5) + + # 保存搜索结果到状态 + if not hasattr(state, "web_search_results"): + state.web_search_results = [] + state.web_search_results.append(search_result) + + # 将搜索结果添加到 rag_context,供 LLM 使用 + if state.rag_context: + state.rag_context = f"{state.rag_context}\n\n---\n\n## 🌐 联网搜索结果:\n{search_result}" + else: + state.rag_context = f"## 🌐 联网搜索结果:\n{search_result}" + + state.success = True + print(f"[WebSearch] 搜索完成") + + except Exception as e: + from app.main_graph.state import ErrorRecord, ErrorSeverity + from datetime import datetime + + error_record = ErrorRecord( + error_type="WebSearchError", + error_message=str(e), + severity=ErrorSeverity.WARNING, + source="web_search_node", + timestamp=datetime.now().isoformat(), + retry_count=0, + max_retries=2, + context={"search_query": search_query} + ) + state.errors.append(error_record) + state.current_error = error_record + state.current_phase = "error_handling" + state.success = False + + return state + + +# ========== 3. 错误处理节点 ========== def error_handling_node(state: MainGraphState) -> MainGraphState: """ @@ -276,6 +330,7 @@ def route_by_reasoning(state: MainGraphState) -> str: "direct_response": "final_response", "retrieve_rag": "rag_retrieve", "re_retrieve_rag": "rag_retrieve", + "web_search": "web_search", # ⭐ 新增:联网搜索 "clarify": "final_response", "call_tool": "final_response", # 暂时映射到 final_response,后续可以扩展 "contact": "contact_subgraph", @@ -291,6 +346,7 @@ def route_by_reasoning(state: MainGraphState) -> str: __all__ = [ "init_state_node", "react_reason_node", + "web_search_node", # ⭐ 新增 "error_handling_node", "final_response_node", "route_by_reasoning" diff --git a/backend/app/main_graph/state.py b/backend/app/main_graph/state.py index eee414e..ed08255 100644 --- a/backend/app/main_graph/state.py +++ b/backend/app/main_graph/state.py @@ -88,6 +88,9 @@ class MainGraphState: rag_retrieved: bool = False # 是否已经检索过 rag_docs: List[Dict[str, Any]] = field(default_factory=list) # 检索到的文档 + # ========== 联网搜索相关字段 ⭐ 新增 ========== + web_search_results: List[str] = field(default_factory=list) # 联网搜索结果 + # ========== 错误处理字段 ========== errors: List[ErrorRecord] = field(default_factory=list) # 错误列表 current_error: Optional[ErrorRecord] = None # 当前错误 diff --git a/backend/app/main_graph/utils/subgraph_builder.py b/backend/app/main_graph/utils/subgraph_builder.py index 9d3868f..9df544f 100644 --- a/backend/app/main_graph/utils/subgraph_builder.py +++ b/backend/app/main_graph/utils/subgraph_builder.py @@ -10,6 +10,7 @@ from app.main_graph.state import MainGraphState, CurrentAction from .react_nodes import ( init_state_node, react_reason_node, + web_search_node, # ⭐ 新增 error_handling_node, final_response_node, route_by_reasoning @@ -109,10 +110,13 @@ def build_react_main_graph() -> StateGraph: # 3. RAG 检索节点 graph.add_node("rag_retrieve", rag_retrieve_node) - # 4. 错误处理节点 + # 4. 联网搜索节点 ⭐ 新增 + graph.add_node("web_search", web_search_node) + + # 5. 错误处理节点 graph.add_node("handle_error", error_handling_node) - # 5. 最终回答节点 + # 6. 最终回答节点 graph.add_node("final_response", final_response_node) # ========== 添加子图节点 ========== @@ -151,6 +155,9 @@ def build_react_main_graph() -> StateGraph: # 检索分支 → 检索后回到推理 "rag_retrieve": "rag_retrieve", + # 联网搜索分支 ⭐ 新增 + "web_search": "web_search", + # 子图分支 → 子图后回到推理 "contact_subgraph": "contact_subgraph", "dictionary_subgraph": "dictionary_subgraph", @@ -164,8 +171,9 @@ def build_react_main_graph() -> StateGraph: } ) - # 4. 循环边:检索/子图/错误处理 后 → 回到推理 + # 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") diff --git a/requirement.txt b/requirement.txt index ae0a3a5..a9d522a 100644 --- a/requirement.txt +++ b/requirement.txt @@ -43,4 +43,5 @@ pypdf==6.10.0 beautifulsoup4==4.14.3 lxml==6.1.0 pandas==3.0.2 # 若需Excel保留,否则移除 -spacy==3.8.14 # unstructured 可能依赖 \ No newline at end of file +spacy==3.8.14 # unstructured 可能依赖 +duckduckgo-search>=6.0.0 # 联网搜索 \ No newline at end of file