242 lines
7.3 KiB
Python
242 lines
7.3 KiB
Python
|
|
# backend/app/agent/hybrid_router.py
|
|||
|
|
|
|||
|
|
from enum import Enum
|
|||
|
|
from typing import Optional, List, Dict, Any
|
|||
|
|
from dataclasses import dataclass
|
|||
|
|
import sys
|
|||
|
|
import os
|
|||
|
|
|
|||
|
|
# 添加项目路径
|
|||
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
|
|||
|
|
|
|||
|
|
from app.agent.intent_classifier import IntentClassifier, IntentType, get_intent_classifier
|
|||
|
|
|
|||
|
|
|
|||
|
|
class RouterAction(Enum):
|
|||
|
|
"""路由动作"""
|
|||
|
|
FAST_RAG = "fast_rag" # 快速 RAG 路径
|
|||
|
|
FAST_TOOL = "fast_tool" # 快速工具路径
|
|||
|
|
REACT_LOOP = "react_loop" # React 循环路径
|
|||
|
|
DIRECT_ANSWER = "direct_answer" # 直接回答
|
|||
|
|
CLARIFY = "clarify" # 澄清反问
|
|||
|
|
|
|||
|
|
|
|||
|
|
@dataclass
|
|||
|
|
class RouterDecision:
|
|||
|
|
"""路由决策结果"""
|
|||
|
|
action: RouterAction
|
|||
|
|
intent: IntentType
|
|||
|
|
confidence: float
|
|||
|
|
reasoning: str
|
|||
|
|
metadata: Dict[str, Any] = None
|
|||
|
|
|
|||
|
|
|
|||
|
|
class HybridRouter:
|
|||
|
|
"""混合路由决策器"""
|
|||
|
|
|
|||
|
|
def __init__(
|
|||
|
|
self,
|
|||
|
|
intent_classifier: IntentClassifier,
|
|||
|
|
rag_pipeline = None,
|
|||
|
|
tool_registry: Dict[str, Any] = None,
|
|||
|
|
react_graph = None
|
|||
|
|
):
|
|||
|
|
self.classifier = intent_classifier
|
|||
|
|
self.rag = rag_pipeline
|
|||
|
|
self.tools = tool_registry or {}
|
|||
|
|
self.react_graph = react_graph
|
|||
|
|
|
|||
|
|
async def route(self, user_input: str, context: Optional[str] = None) -> RouterDecision:
|
|||
|
|
"""
|
|||
|
|
路由决策
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
user_input: 用户输入
|
|||
|
|
context: 对话上下文
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
RouterDecision
|
|||
|
|
"""
|
|||
|
|
# 1. 意图分类
|
|||
|
|
intent_result = await self.classifier.classify(user_input, context)
|
|||
|
|
|
|||
|
|
# 2. 根据意图路由
|
|||
|
|
decision = self._make_decision(intent_result, user_input)
|
|||
|
|
|
|||
|
|
return decision
|
|||
|
|
|
|||
|
|
def _make_decision(self, intent_result: IntentResult, user_input: str) -> RouterDecision:
|
|||
|
|
"""根据意图做出路由决策"""
|
|||
|
|
intent = intent_result.intent_type
|
|||
|
|
confidence = intent_result.confidence
|
|||
|
|
|
|||
|
|
# 低置信度 → 走 React 循环(更安全)
|
|||
|
|
if confidence < 0.6:
|
|||
|
|
return RouterDecision(
|
|||
|
|
action=RouterAction.REACT_LOOP,
|
|||
|
|
intent=intent,
|
|||
|
|
confidence=confidence,
|
|||
|
|
reasoning=f"置信度 {confidence:.2f} 较低,走 React 循环"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# 根据意图路由
|
|||
|
|
routing_map = {
|
|||
|
|
IntentType.KNOWLEDGE: RouterAction.FAST_RAG,
|
|||
|
|
IntentType.REALTIME: RouterAction.FAST_TOOL,
|
|||
|
|
IntentType.ACTION: RouterAction.FAST_TOOL,
|
|||
|
|
IntentType.CHITCHAT: RouterAction.DIRECT_ANSWER,
|
|||
|
|
IntentType.CLARIFY: RouterAction.CLARIFY,
|
|||
|
|
IntentType.MIXED: RouterAction.REACT_LOOP,
|
|||
|
|
IntentType.UNKNOWN: RouterAction.REACT_LOOP,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
action = routing_map.get(intent, RouterAction.REACT_LOOP)
|
|||
|
|
|
|||
|
|
return RouterDecision(
|
|||
|
|
action=action,
|
|||
|
|
intent=intent,
|
|||
|
|
confidence=confidence,
|
|||
|
|
reasoning=intent_result.reasoning
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
async def execute(self, decision: RouterDecision, user_input: str, thread_id: str) -> str:
|
|||
|
|
"""
|
|||
|
|
根据决策执行对应路径
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
decision: 路由决策
|
|||
|
|
user_input: 用户输入
|
|||
|
|
thread_id: 线程 ID
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
最终答案
|
|||
|
|
"""
|
|||
|
|
if decision.action == RouterAction.FAST_RAG:
|
|||
|
|
return await self._execute_fast_rag(user_input)
|
|||
|
|
elif decision.action == RouterAction.FAST_TOOL:
|
|||
|
|
return await self._execute_fast_tool(user_input)
|
|||
|
|
elif decision.action == RouterAction.DIRECT_ANSWER:
|
|||
|
|
return await self._execute_direct_answer(user_input)
|
|||
|
|
elif decision.action == RouterAction.CLARIFY:
|
|||
|
|
return await self._execute_clarify(user_input)
|
|||
|
|
elif decision.action == RouterAction.REACT_LOOP:
|
|||
|
|
return await self._execute_react_loop(user_input, thread_id)
|
|||
|
|
else:
|
|||
|
|
return await self._execute_react_loop(user_input, thread_id)
|
|||
|
|
|
|||
|
|
async def _execute_fast_rag(self, user_input: str) -> str:
|
|||
|
|
"""快速 RAG 路径"""
|
|||
|
|
print("🚀 执行快速 RAG 路径")
|
|||
|
|
|
|||
|
|
# 1. 检索文档(如果 RAG 可用)
|
|||
|
|
docs = []
|
|||
|
|
if self.rag and hasattr(self.rag, 'aretrieve'):
|
|||
|
|
docs = await self.rag.aretrieve(user_input)
|
|||
|
|
|
|||
|
|
# 2. 格式化上下文
|
|||
|
|
context = ""
|
|||
|
|
if self.rag and hasattr(self.rag, 'format_context'):
|
|||
|
|
context = self.rag.format_context(docs)
|
|||
|
|
|
|||
|
|
# 3. 生成回答
|
|||
|
|
prompt = f"""
|
|||
|
|
请根据以下文档回答用户问题。
|
|||
|
|
|
|||
|
|
参考文档:
|
|||
|
|
{context or "(无文档)"}
|
|||
|
|
|
|||
|
|
用户问题: {user_input}
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
response = await self.classifier.llm.ainvoke(prompt)
|
|||
|
|
return response.content
|
|||
|
|
|
|||
|
|
async def _execute_fast_tool(self, user_input: str) -> str:
|
|||
|
|
"""快速工具路径"""
|
|||
|
|
print("🚀 执行快速工具路径")
|
|||
|
|
|
|||
|
|
# 这里简化处理,实际项目中:
|
|||
|
|
# 1. 解析需要调用的工具
|
|||
|
|
# 2. 生成工具参数
|
|||
|
|
# 3. 执行工具
|
|||
|
|
# 4. 生成回答
|
|||
|
|
|
|||
|
|
return "快速工具路径:功能开发中..."
|
|||
|
|
|
|||
|
|
async def _execute_direct_answer(self, user_input: str) -> str:
|
|||
|
|
"""直接回答路径"""
|
|||
|
|
print("💬 执行直接回答路径")
|
|||
|
|
|
|||
|
|
prompt = f"""
|
|||
|
|
用户说: {user_input}
|
|||
|
|
|
|||
|
|
请友好回应。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
response = await self.classifier.llm.ainvoke(prompt)
|
|||
|
|
return response.content
|
|||
|
|
|
|||
|
|
async def _execute_clarify(self, user_input: str) -> str:
|
|||
|
|
"""澄清反问路径"""
|
|||
|
|
print("❓ 执行澄清反问路径")
|
|||
|
|
|
|||
|
|
prompt = f"""
|
|||
|
|
用户说: {user_input}
|
|||
|
|
|
|||
|
|
用户的问题不太明确,请礼貌地询问更多细节。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
response = await self.classifier.llm.ainvoke(prompt)
|
|||
|
|
return response.content
|
|||
|
|
|
|||
|
|
async def _execute_react_loop(self, user_input: str, thread_id: str) -> str:
|
|||
|
|
"""React 循环路径"""
|
|||
|
|
print("🔄 执行 React 循环路径")
|
|||
|
|
|
|||
|
|
# 这里调用现有的完整 LangGraph 流程
|
|||
|
|
# 具体实现根据您的项目结构
|
|||
|
|
return "React 循环路径:调用现有 LangGraph..."
|
|||
|
|
|
|||
|
|
|
|||
|
|
# 便捷函数
|
|||
|
|
async def hybrid_agent_route(
|
|||
|
|
user_input: str,
|
|||
|
|
thread_id: str,
|
|||
|
|
context: Optional[str] = None
|
|||
|
|
) -> str:
|
|||
|
|
"""
|
|||
|
|
混合 Agent 路由入口函数
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
user_input: 用户输入
|
|||
|
|
thread_id: 线程 ID
|
|||
|
|
context: 对话上下文
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
最终答案
|
|||
|
|
"""
|
|||
|
|
# 获取依赖(实际项目应该用依赖注入)
|
|||
|
|
classifier = get_intent_classifier()
|
|||
|
|
# rag = get_rag_pipeline()
|
|||
|
|
# tools = get_tool_registry()
|
|||
|
|
# graph = get_react_graph()
|
|||
|
|
|
|||
|
|
# 创建路由器
|
|||
|
|
router = HybridRouter(
|
|||
|
|
intent_classifier=classifier,
|
|||
|
|
rag_pipeline=None, # 实际项目中传入
|
|||
|
|
tool_registry={}, # 实际项目中传入
|
|||
|
|
react_graph=None # 实际项目中传入
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# 路由决策
|
|||
|
|
decision = await router.route(user_input, context)
|
|||
|
|
print(f"🧭 路由决策: {decision.action} (意图: {decision.intent}, 置信度: {decision.confidence:.2f})")
|
|||
|
|
print(f"📝 推理: {decision.reasoning}")
|
|||
|
|
|
|||
|
|
# 执行
|
|||
|
|
# result = await router.execute(decision, user_input, thread_id)
|
|||
|
|
# return result
|
|||
|
|
|
|||
|
|
# 临时返回
|
|||
|
|
return f"路由决策: {decision.action}"
|