first commit

This commit is contained in:
zhaojie
2025-09-06 21:06:18 +08:00
commit 8083f136c9
94 changed files with 20559 additions and 0 deletions

3
src/__init__.py Normal file
View File

@@ -0,0 +1,3 @@
# TSP助手 - 基于大模型的AI客服机器人
__version__ = "1.0.0"
__author__ = "TSP Assistant Team"

Binary file not shown.

Binary file not shown.

Binary file not shown.

22
src/agent/__init__.py Normal file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Agent模块初始化文件
"""
from .agent_core import AgentCore, AgentState
from .planner import TaskPlanner
from .executor import TaskExecutor
from .tool_manager import ToolManager
from .reasoning_engine import ReasoningEngine
from .goal_manager import GoalManager
__all__ = [
'AgentCore',
'AgentState',
'TaskPlanner',
'TaskExecutor',
'ToolManager',
'ReasoningEngine',
'GoalManager'
]

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

312
src/agent/agent_core.py Normal file
View File

@@ -0,0 +1,312 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Agent核心模块
实现智能体的核心逻辑和决策机制
"""
import logging
import asyncio
from typing import Dict, List, Any, Optional, Callable
from datetime import datetime
from enum import Enum
import json
from ..core.database import db_manager
from ..core.llm_client import QwenClient
from .planner import TaskPlanner
from .executor import TaskExecutor
from .tool_manager import ToolManager
from .reasoning_engine import ReasoningEngine
from .goal_manager import GoalManager
logger = logging.getLogger(__name__)
class AgentState(Enum):
"""Agent状态枚举"""
IDLE = "idle"
THINKING = "thinking"
PLANNING = "planning"
EXECUTING = "executing"
LEARNING = "learning"
ERROR = "error"
class AgentCore:
"""Agent核心类"""
def __init__(self):
self.state = AgentState.IDLE
self.llm_client = QwenClient()
self.planner = TaskPlanner()
self.executor = TaskExecutor()
self.tool_manager = ToolManager()
self.reasoning_engine = ReasoningEngine()
self.goal_manager = GoalManager()
# Agent记忆和上下文
self.memory = {}
self.current_goal = None
self.active_tasks = []
self.execution_history = []
# 配置参数
self.max_iterations = 10
self.confidence_threshold = 0.7
logger.info("Agent核心初始化完成")
async def process_request(self, request: Dict[str, Any]) -> Dict[str, Any]:
"""处理用户请求的主入口"""
try:
self.state = AgentState.THINKING
# 1. 理解用户意图
intent = await self._understand_intent(request)
# 2. 设定目标
goal = await self._set_goal(intent, request)
# 3. 制定计划
plan = await self._create_plan(goal)
# 4. 执行计划
result = await self._execute_plan(plan)
# 5. 学习和反思
await self._learn_from_execution(result)
self.state = AgentState.IDLE
return result
except Exception as e:
logger.error(f"处理请求失败: {e}")
self.state = AgentState.ERROR
return {"error": f"处理失败: {str(e)}"}
async def _understand_intent(self, request: Dict[str, Any]) -> Dict[str, Any]:
"""理解用户意图"""
user_message = request.get("message", "")
context = request.get("context", {})
# 使用推理引擎分析意图
intent_analysis = await self.reasoning_engine.analyze_intent(
message=user_message,
context=context,
history=self.execution_history[-5:] # 最近5次执行历史
)
return intent_analysis
async def _set_goal(self, intent: Dict[str, Any], request: Dict[str, Any]) -> Dict[str, Any]:
"""设定目标"""
goal = await self.goal_manager.create_goal(
intent=intent,
request=request,
current_state=self.state
)
self.current_goal = goal
return goal
async def _create_plan(self, goal: Dict[str, Any]) -> List[Dict[str, Any]]:
"""制定执行计划"""
self.state = AgentState.PLANNING
plan = await self.planner.create_plan(
goal=goal,
available_tools=self.tool_manager.get_available_tools(),
constraints=self._get_constraints()
)
return plan
async def _execute_plan(self, plan: List[Dict[str, Any]]) -> Dict[str, Any]:
"""执行计划"""
self.state = AgentState.EXECUTING
execution_result = await self.executor.execute_plan(
plan=plan,
tool_manager=self.tool_manager,
context=self.memory
)
# 记录执行历史
self.execution_history.append({
"timestamp": datetime.now().isoformat(),
"plan": plan,
"result": execution_result
})
return execution_result
async def _learn_from_execution(self, result: Dict[str, Any]):
"""从执行结果中学习"""
self.state = AgentState.LEARNING
# 分析执行效果
learning_insights = await self.reasoning_engine.extract_insights(
execution_result=result,
goal=self.current_goal
)
# 更新记忆
self._update_memory(learning_insights)
# 更新工具使用统计
self.tool_manager.update_usage_stats(result.get("tool_usage", []))
def _get_constraints(self) -> Dict[str, Any]:
"""获取执行约束"""
return {
"max_iterations": self.max_iterations,
"confidence_threshold": self.confidence_threshold,
"timeout": 300, # 5分钟超时
"memory_limit": 1000 # 内存限制
}
def _update_memory(self, insights: Dict[str, Any]):
"""更新Agent记忆"""
timestamp = datetime.now().isoformat()
# 更新成功模式
if insights.get("success_patterns"):
if "success_patterns" not in self.memory:
self.memory["success_patterns"] = []
self.memory["success_patterns"].extend(insights["success_patterns"])
# 更新失败模式
if insights.get("failure_patterns"):
if "failure_patterns" not in self.memory:
self.memory["failure_patterns"] = []
self.memory["failure_patterns"].extend(insights["failure_patterns"])
# 更新知识
if insights.get("new_knowledge"):
if "knowledge" not in self.memory:
self.memory["knowledge"] = []
self.memory["knowledge"].extend(insights["new_knowledge"])
# 限制记忆大小
for key in self.memory:
if isinstance(self.memory[key], list) and len(self.memory[key]) > 100:
self.memory[key] = self.memory[key][-100:]
async def proactive_action(self) -> Optional[Dict[str, Any]]:
"""主动行动 - Agent主动发起的行为"""
try:
# 检查是否有需要主动处理的任务
proactive_tasks = await self._identify_proactive_tasks()
if proactive_tasks:
# 选择最重要的任务
priority_task = max(proactive_tasks, key=lambda x: x.get("priority", 0))
# 执行主动任务
result = await self.process_request(priority_task)
return result
return None
except Exception as e:
logger.error(f"主动行动失败: {e}")
return None
async def _identify_proactive_tasks(self) -> List[Dict[str, Any]]:
"""识别需要主动处理的任务"""
tasks = []
# 检查预警系统
alerts = await self._check_alerts()
if alerts:
tasks.extend([{
"type": "alert_response",
"message": f"处理预警: {alert['message']}",
"priority": self._calculate_alert_priority(alert),
"context": {"alert": alert}
} for alert in alerts])
# 检查知识库更新需求
knowledge_gaps = await self._identify_knowledge_gaps()
if knowledge_gaps:
tasks.append({
"type": "knowledge_update",
"message": "更新知识库",
"priority": 0.6,
"context": {"gaps": knowledge_gaps}
})
# 检查系统健康状态
health_issues = await self._check_system_health()
if health_issues:
tasks.append({
"type": "system_maintenance",
"message": "系统维护",
"priority": 0.8,
"context": {"issues": health_issues}
})
return tasks
async def _check_alerts(self) -> List[Dict[str, Any]]:
"""检查预警"""
# 这里可以调用现有的预警系统
from ..analytics.alert_system import AlertSystem
alert_system = AlertSystem()
return alert_system.get_active_alerts()
def _calculate_alert_priority(self, alert: Dict[str, Any]) -> float:
"""计算预警优先级"""
severity_map = {
"low": 0.3,
"medium": 0.6,
"high": 0.8,
"critical": 1.0
}
return severity_map.get(alert.get("severity", "medium"), 0.5)
async def _identify_knowledge_gaps(self) -> List[Dict[str, Any]]:
"""识别知识库缺口"""
# 分析未解决的问题,识别知识缺口
gaps = []
# 这里可以实现具体的知识缺口识别逻辑
# 例如:分析低置信度的回复、未解决的问题等
return gaps
async def _check_system_health(self) -> List[Dict[str, Any]]:
"""检查系统健康状态"""
issues = []
# 检查各个组件的健康状态
if not self.llm_client.test_connection():
issues.append({"component": "llm_client", "issue": "连接失败"})
# 检查内存使用
import psutil
memory_percent = psutil.virtual_memory().percent
if memory_percent > 80:
issues.append({"component": "memory", "issue": f"内存使用率过高: {memory_percent}%"})
return issues
def get_status(self) -> Dict[str, Any]:
"""获取Agent状态"""
return {
"state": self.state.value,
"current_goal": self.current_goal,
"active_tasks": len(self.active_tasks),
"execution_history_count": len(self.execution_history),
"memory_size": len(str(self.memory)),
"available_tools": len(self.tool_manager.get_available_tools()),
"timestamp": datetime.now().isoformat()
}
def reset(self):
"""重置Agent状态"""
self.state = AgentState.IDLE
self.current_goal = None
self.active_tasks = []
self.execution_history = []
self.memory = {}
logger.info("Agent状态已重置")

589
src/agent/executor.py Normal file
View File

@@ -0,0 +1,589 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
任务执行器
负责执行计划中的具体任务
"""
import logging
import asyncio
from typing import Dict, List, Any, Optional
from datetime import datetime
import json
logger = logging.getLogger(__name__)
class TaskExecutor:
"""任务执行器"""
def __init__(self):
self.execution_strategies = {
"sequential": self._execute_sequential,
"parallel": self._execute_parallel,
"conditional": self._execute_conditional,
"iterative": self._execute_iterative
}
self.active_executions = {}
async def execute_plan(
self,
plan: List[Dict[str, Any]],
tool_manager: Any,
context: Dict[str, Any]
) -> Dict[str, Any]:
"""执行计划"""
try:
execution_id = f"exec_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
self.active_executions[execution_id] = {
"start_time": datetime.now(),
"status": "running",
"plan": plan
}
# 根据计划类型选择执行策略
execution_strategy = self._determine_execution_strategy(plan)
# 执行计划
result = await self.execution_strategies[execution_strategy](
plan=plan,
tool_manager=tool_manager,
context=context,
execution_id=execution_id
)
# 更新执行状态
self.active_executions[execution_id]["status"] = "completed"
self.active_executions[execution_id]["end_time"] = datetime.now()
self.active_executions[execution_id]["result"] = result
logger.info(f"计划执行完成: {execution_id}")
return result
except Exception as e:
logger.error(f"执行计划失败: {e}")
if execution_id in self.active_executions:
self.active_executions[execution_id]["status"] = "failed"
self.active_executions[execution_id]["error"] = str(e)
return {
"success": False,
"error": str(e),
"execution_id": execution_id
}
def _determine_execution_strategy(self, plan: List[Dict[str, Any]]) -> str:
"""确定执行策略"""
if not plan:
return "sequential"
# 检查计划类型
plan_types = [task.get("type") for task in plan]
if "parallel_group" in plan_types:
return "parallel"
elif "condition" in plan_types or "branch" in plan_types:
return "conditional"
elif "iteration_control" in plan_types:
return "iterative"
else:
return "sequential"
async def _execute_sequential(
self,
plan: List[Dict[str, Any]],
tool_manager: Any,
context: Dict[str, Any],
execution_id: str
) -> Dict[str, Any]:
"""顺序执行计划"""
results = []
execution_log = []
for i, task in enumerate(plan):
try:
logger.info(f"执行任务 {i+1}/{len(plan)}: {task.get('id', 'unknown')}")
# 检查任务依赖
if not await self._check_dependencies(task, results):
logger.warning(f"任务 {task.get('id')} 的依赖未满足,跳过执行")
continue
# 执行任务
task_result = await self._execute_single_task(task, tool_manager, context)
results.append({
"task_id": task.get("id"),
"result": task_result,
"timestamp": datetime.now().isoformat()
})
execution_log.append({
"task_id": task.get("id"),
"status": "completed",
"duration": task_result.get("duration", 0)
})
# 检查是否满足成功条件
if not self._check_success_criteria(task, task_result):
logger.warning(f"任务 {task.get('id')} 未满足成功条件")
except Exception as e:
logger.error(f"执行任务 {task.get('id')} 失败: {e}")
execution_log.append({
"task_id": task.get("id"),
"status": "failed",
"error": str(e)
})
# 根据任务重要性决定是否继续
if task.get("critical", False):
return {
"success": False,
"error": f"关键任务失败: {task.get('id')}",
"results": results,
"execution_log": execution_log
}
return {
"success": True,
"results": results,
"execution_log": execution_log,
"execution_id": execution_id
}
async def _execute_parallel(
self,
plan: List[Dict[str, Any]],
tool_manager: Any,
context: Dict[str, Any],
execution_id: str
) -> Dict[str, Any]:
"""并行执行计划"""
results = []
execution_log = []
# 将计划分组
parallel_groups = self._group_tasks_for_parallel_execution(plan)
for group in parallel_groups:
if group["execution_mode"] == "parallel":
# 并行执行组内任务
group_results = await self._execute_tasks_parallel(
group["tasks"], tool_manager, context
)
results.extend(group_results)
else:
# 顺序执行组内任务
for task in group["tasks"]:
task_result = await self._execute_single_task(task, tool_manager, context)
results.append({
"task_id": task.get("id"),
"result": task_result,
"timestamp": datetime.now().isoformat()
})
return {
"success": True,
"results": results,
"execution_log": execution_log,
"execution_id": execution_id
}
async def _execute_conditional(
self,
plan: List[Dict[str, Any]],
tool_manager: Any,
context: Dict[str, Any],
execution_id: str
) -> Dict[str, Any]:
"""条件执行计划"""
results = []
execution_log = []
# 找到条件检查任务
condition_task = None
branch_tasks = []
for task in plan:
if task.get("type") == "condition":
condition_task = task
elif task.get("type") == "branch":
branch_tasks.append(task)
if not condition_task:
logger.error("条件计划中缺少条件检查任务")
return {"success": False, "error": "缺少条件检查任务"}
# 执行条件检查
condition_result = await self._execute_single_task(condition_task, tool_manager, context)
results.append({
"task_id": condition_task.get("id"),
"result": condition_result,
"timestamp": datetime.now().isoformat()
})
# 根据条件结果选择分支
selected_branch = self._select_branch(condition_result, branch_tasks)
if selected_branch:
# 执行选中的分支
branch_result = await self._execute_sequential(
selected_branch.get("tasks", []),
tool_manager,
context,
execution_id
)
results.extend(branch_result.get("results", []))
execution_log.extend(branch_result.get("execution_log", []))
return {
"success": True,
"results": results,
"execution_log": execution_log,
"execution_id": execution_id,
"selected_branch": selected_branch.get("id") if selected_branch else None
}
async def _execute_iterative(
self,
plan: List[Dict[str, Any]],
tool_manager: Any,
context: Dict[str, Any],
execution_id: str
) -> Dict[str, Any]:
"""迭代执行计划"""
# 找到迭代控制任务
iteration_task = None
for task in plan:
if task.get("type") == "iteration_control":
iteration_task = task
break
if not iteration_task:
logger.error("迭代计划中缺少迭代控制任务")
return {"success": False, "error": "缺少迭代控制任务"}
max_iterations = iteration_task.get("max_iterations", 10)
convergence_criteria = iteration_task.get("convergence_criteria", {})
tasks = iteration_task.get("tasks", [])
results = []
execution_log = []
iteration_count = 0
while iteration_count < max_iterations:
iteration_count += 1
logger.info(f"执行第 {iteration_count} 次迭代")
# 执行迭代任务
iteration_result = await self._execute_sequential(
tasks, tool_manager, context, f"{execution_id}_iter_{iteration_count}"
)
results.append({
"iteration": iteration_count,
"result": iteration_result,
"timestamp": datetime.now().isoformat()
})
# 检查收敛条件
if self._check_convergence(iteration_result, convergence_criteria):
logger.info(f"迭代在第 {iteration_count} 次收敛")
break
return {
"success": True,
"results": results,
"execution_log": execution_log,
"execution_id": execution_id,
"iterations": iteration_count,
"converged": iteration_count < max_iterations
}
async def _execute_single_task(
self,
task: Dict[str, Any],
tool_manager: Any,
context: Dict[str, Any]
) -> Dict[str, Any]:
"""执行单个任务"""
start_time = datetime.now()
try:
task_id = task.get("id", "unknown")
task_type = task.get("type", "action")
tool_name = task.get("tool", "")
parameters = task.get("parameters", {})
logger.info(f"执行任务: {task_id}, 类型: {task_type}, 工具: {tool_name}")
# 根据任务类型执行
if task_type == "action":
result = await self._execute_action_task(task, tool_manager, context)
elif task_type == "condition":
result = await self._execute_condition_task(task, tool_manager, context)
elif task_type == "control":
result = await self._execute_control_task(task, tool_manager, context)
else:
result = await self._execute_general_task(task, tool_manager, context)
duration = (datetime.now() - start_time).total_seconds()
result["duration"] = duration
logger.info(f"任务 {task_id} 执行完成,耗时: {duration:.2f}")
return result
except Exception as e:
logger.error(f"执行任务失败: {e}")
return {
"success": False,
"error": str(e),
"duration": (datetime.now() - start_time).total_seconds()
}
async def _execute_action_task(
self,
task: Dict[str, Any],
tool_manager: Any,
context: Dict[str, Any]
) -> Dict[str, Any]:
"""执行动作任务"""
tool_name = task.get("tool", "")
parameters = task.get("parameters", {})
# 合并上下文参数
full_parameters = {**parameters, **context}
# 调用工具
result = await tool_manager.execute_tool(tool_name, full_parameters)
return {
"success": True,
"tool": tool_name,
"parameters": full_parameters,
"result": result
}
async def _execute_condition_task(
self,
task: Dict[str, Any],
tool_manager: Any,
context: Dict[str, Any]
) -> Dict[str, Any]:
"""执行条件任务"""
condition = task.get("condition", "")
branches = task.get("branches", {})
# 评估条件
condition_result = await self._evaluate_condition(condition, context)
return {
"success": True,
"condition": condition,
"result": condition_result,
"available_branches": list(branches.keys())
}
async def _execute_control_task(
self,
task: Dict[str, Any],
tool_manager: Any,
context: Dict[str, Any]
) -> Dict[str, Any]:
"""执行控制任务"""
control_type = task.get("control_type", "general")
if control_type == "iteration":
return await self._execute_iteration_control(task, context)
elif control_type == "loop":
return await self._execute_loop_control(task, context)
else:
return {
"success": True,
"control_type": control_type,
"message": "控制任务执行完成"
}
async def _execute_general_task(
self,
task: Dict[str, Any],
tool_manager: Any,
context: Dict[str, Any]
) -> Dict[str, Any]:
"""执行通用任务"""
description = task.get("description", "")
# 这里可以实现通用的任务执行逻辑
# 例如调用LLM生成响应、执行数据库操作等
return {
"success": True,
"description": description,
"message": "通用任务执行完成"
}
async def _execute_tasks_parallel(
self,
tasks: List[Dict[str, Any]],
tool_manager: Any,
context: Dict[str, Any]
) -> List[Dict[str, Any]]:
"""并行执行多个任务"""
async def execute_task(task):
return await self._execute_single_task(task, tool_manager, context)
# 创建并行任务
parallel_tasks = [execute_task(task) for task in tasks]
# 等待所有任务完成
results = await asyncio.gather(*parallel_tasks, return_exceptions=True)
# 处理结果
processed_results = []
for i, result in enumerate(results):
if isinstance(result, Exception):
processed_results.append({
"task_id": tasks[i].get("id"),
"result": {"success": False, "error": str(result)},
"timestamp": datetime.now().isoformat()
})
else:
processed_results.append({
"task_id": tasks[i].get("id"),
"result": result,
"timestamp": datetime.now().isoformat()
})
return processed_results
def _group_tasks_for_parallel_execution(self, plan: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""将任务分组以便并行执行"""
groups = []
current_group = []
for task in plan:
if task.get("type") == "parallel_group":
if current_group:
groups.append({
"execution_mode": "sequential",
"tasks": current_group
})
current_group = []
groups.append(task)
else:
current_group.append(task)
if current_group:
groups.append({
"execution_mode": "sequential",
"tasks": current_group
})
return groups
async def _check_dependencies(self, task: Dict[str, Any], results: List[Dict[str, Any]]) -> bool:
"""检查任务依赖是否满足"""
dependencies = task.get("dependencies", [])
if not dependencies:
return True
# 检查所有依赖是否已完成
completed_task_ids = [r["task_id"] for r in results if r["result"].get("success", False)]
for dep in dependencies:
if dep not in completed_task_ids:
return False
return True
def _check_success_criteria(self, task: Dict[str, Any], result: Dict[str, Any]) -> bool:
"""检查任务是否满足成功条件"""
success_criteria = task.get("success_criteria", {})
if not success_criteria:
return result.get("success", False)
# 检查每个成功条件
for criterion, expected_value in success_criteria.items():
actual_value = result.get(criterion)
if actual_value != expected_value:
return False
return True
def _select_branch(self, condition_result: Dict[str, Any], branch_tasks: List[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
"""根据条件结果选择分支"""
condition_value = condition_result.get("result", "")
for branch_task in branch_tasks:
branch_condition = branch_task.get("condition", "")
if branch_condition == condition_value:
return branch_task
return None
def _check_convergence(self, iteration_result: Dict[str, Any], convergence_criteria: Dict[str, Any]) -> bool:
"""检查迭代是否收敛"""
if not convergence_criteria:
return False
# 检查收敛条件
for criterion, threshold in convergence_criteria.items():
actual_value = iteration_result.get(criterion)
if actual_value is None:
continue
# 这里可以实现更复杂的收敛判断逻辑
if isinstance(threshold, dict):
if threshold.get("type") == "less_than":
if actual_value >= threshold.get("value"):
return False
elif threshold.get("type") == "greater_than":
if actual_value <= threshold.get("value"):
return False
return True
async def _evaluate_condition(self, condition: str, context: Dict[str, Any]) -> str:
"""评估条件表达式"""
# 这里可以实现条件评估逻辑
# 例如:解析条件表达式、查询上下文等
# 简单的条件评估示例
if "satisfaction" in condition:
return "high" if context.get("satisfaction_score", 0) > 0.7 else "low"
elif "priority" in condition:
return context.get("priority", "medium")
else:
return "default"
async def _execute_iteration_control(self, task: Dict[str, Any], context: Dict[str, Any]) -> Dict[str, Any]:
"""执行迭代控制"""
max_iterations = task.get("max_iterations", 10)
current_iteration = context.get("current_iteration", 0)
return {
"success": True,
"max_iterations": max_iterations,
"current_iteration": current_iteration,
"continue": current_iteration < max_iterations
}
async def _execute_loop_control(self, task: Dict[str, Any], context: Dict[str, Any]) -> Dict[str, Any]:
"""执行循环控制"""
loop_condition = task.get("loop_condition", "")
return {
"success": True,
"loop_condition": loop_condition,
"continue": True # 这里应该根据实际条件判断
}
def get_execution_status(self, execution_id: str) -> Optional[Dict[str, Any]]:
"""获取执行状态"""
return self.active_executions.get(execution_id)
def get_all_executions(self) -> Dict[str, Any]:
"""获取所有执行记录"""
return self.active_executions

573
src/agent/goal_manager.py Normal file
View File

@@ -0,0 +1,573 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
目标管理器
负责目标设定、跟踪和评估
"""
import logging
from typing import Dict, List, Any, Optional
from datetime import datetime
import json
from ..core.llm_client import QwenClient
logger = logging.getLogger(__name__)
class GoalManager:
"""目标管理器"""
def __init__(self):
self.llm_client = QwenClient()
self.active_goals = {}
self.goal_history = []
self.goal_templates = {
"problem_solving": self._create_problem_solving_goal,
"information_gathering": self._create_information_gathering_goal,
"task_execution": self._create_task_execution_goal,
"analysis": self._create_analysis_goal,
"communication": self._create_communication_goal
}
async def create_goal(
self,
intent: Dict[str, Any],
request: Dict[str, Any],
current_state: Any
) -> Dict[str, Any]:
"""创建目标"""
try:
goal_type = self._determine_goal_type(intent, request)
if goal_type in self.goal_templates:
goal = await self.goal_templates[goal_type](intent, request, current_state)
else:
goal = await self._create_general_goal(intent, request, current_state)
# 生成唯一目标ID
goal_id = f"goal_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
goal["id"] = goal_id
goal["created_at"] = datetime.now().isoformat()
goal["status"] = "active"
# 添加到活跃目标
self.active_goals[goal_id] = goal
logger.info(f"创建目标: {goal_id}, 类型: {goal_type}")
return goal
except Exception as e:
logger.error(f"创建目标失败: {e}")
return self._create_fallback_goal(intent, request)
def _determine_goal_type(self, intent: Dict[str, Any], request: Dict[str, Any]) -> str:
"""确定目标类型"""
main_intent = intent.get("main_intent", "general_query")
goal_type_mapping = {
"problem_solving": ["problem_consultation", "issue_resolution", "troubleshooting"],
"information_gathering": ["information_query", "data_collection", "research"],
"task_execution": ["work_order_creation", "task_assignment", "action_request"],
"analysis": ["data_analysis", "report_generation", "performance_review"],
"communication": ["notification", "message_delivery", "user_interaction"]
}
for goal_type, intents in goal_type_mapping.items():
if main_intent in intents:
return goal_type
return "general"
async def _create_problem_solving_goal(
self,
intent: Dict[str, Any],
request: Dict[str, Any],
current_state: Any
) -> Dict[str, Any]:
"""创建问题解决目标"""
prompt = f"""
请为以下问题解决请求创建目标:
用户意图: {json.dumps(intent, ensure_ascii=False)}
请求内容: {json.dumps(request, ensure_ascii=False)}
请定义:
1. 目标描述
2. 成功标准
3. 所需步骤
4. 预期结果
5. 时间限制
6. 资源需求
请以JSON格式返回目标定义。
"""
messages = [
{"role": "system", "content": "你是一个目标设定专家,擅长为问题解决任务设定清晰的目标。"},
{"role": "user", "content": prompt}
]
result = self.llm_client.chat_completion(messages, temperature=0.3)
if "error" in result:
return self._create_default_problem_solving_goal(intent, request)
response_content = result["choices"][0]["message"]["content"]
import re
json_match = re.search(r'\{.*\}', response_content, re.DOTALL)
if json_match:
goal_data = json.loads(json_match.group())
goal_data["type"] = "problem_solving"
return goal_data
else:
return self._create_default_problem_solving_goal(intent, request)
async def _create_information_gathering_goal(
self,
intent: Dict[str, Any],
request: Dict[str, Any],
current_state: Any
) -> Dict[str, Any]:
"""创建信息收集目标"""
prompt = f"""
请为以下信息收集请求创建目标:
用户意图: {json.dumps(intent, ensure_ascii=False)}
请求内容: {json.dumps(request, ensure_ascii=False)}
请定义:
1. 信息收集范围
2. 信息质量要求
3. 收集方法
4. 验证标准
5. 整理格式
请以JSON格式返回目标定义。
"""
messages = [
{"role": "system", "content": "你是一个信息收集专家,擅长设定信息收集目标。"},
{"role": "user", "content": prompt}
]
result = self.llm_client.chat_completion(messages, temperature=0.3)
if "error" in result:
return self._create_default_information_goal(intent, request)
response_content = result["choices"][0]["message"]["content"]
import re
json_match = re.search(r'\{.*\}', response_content, re.DOTALL)
if json_match:
goal_data = json.loads(json_match.group())
goal_data["type"] = "information_gathering"
return goal_data
else:
return self._create_default_information_goal(intent, request)
async def _create_task_execution_goal(
self,
intent: Dict[str, Any],
request: Dict[str, Any],
current_state: Any
) -> Dict[str, Any]:
"""创建任务执行目标"""
prompt = f"""
请为以下任务执行请求创建目标:
用户意图: {json.dumps(intent, ensure_ascii=False)}
请求内容: {json.dumps(request, ensure_ascii=False)}
请定义:
1. 任务描述
2. 执行步骤
3. 完成标准
4. 质量要求
5. 时间安排
请以JSON格式返回目标定义。
"""
messages = [
{"role": "system", "content": "你是一个任务执行专家,擅长设定任务执行目标。"},
{"role": "user", "content": prompt}
]
result = self.llm_client.chat_completion(messages, temperature=0.3)
if "error" in result:
return self._create_default_task_goal(intent, request)
response_content = result["choices"][0]["message"]["content"]
import re
json_match = re.search(r'\{.*\}', response_content, re.DOTALL)
if json_match:
goal_data = json.loads(json_match.group())
goal_data["type"] = "task_execution"
return goal_data
else:
return self._create_default_task_goal(intent, request)
async def _create_analysis_goal(
self,
intent: Dict[str, Any],
request: Dict[str, Any],
current_state: Any
) -> Dict[str, Any]:
"""创建分析目标"""
prompt = f"""
请为以下分析请求创建目标:
用户意图: {json.dumps(intent, ensure_ascii=False)}
请求内容: {json.dumps(request, ensure_ascii=False)}
请定义:
1. 分析范围
2. 分析方法
3. 分析深度
4. 输出格式
5. 质量指标
请以JSON格式返回目标定义。
"""
messages = [
{"role": "system", "content": "你是一个分析专家,擅长设定分析目标。"},
{"role": "user", "content": prompt}
]
result = self.llm_client.chat_completion(messages, temperature=0.3)
if "error" in result:
return self._create_default_analysis_goal(intent, request)
response_content = result["choices"][0]["message"]["content"]
import re
json_match = re.search(r'\{.*\}', response_content, re.DOTALL)
if json_match:
goal_data = json.loads(json_match.group())
goal_data["type"] = "analysis"
return goal_data
else:
return self._create_default_analysis_goal(intent, request)
async def _create_communication_goal(
self,
intent: Dict[str, Any],
request: Dict[str, Any],
current_state: Any
) -> Dict[str, Any]:
"""创建沟通目标"""
prompt = f"""
请为以下沟通请求创建目标:
用户意图: {json.dumps(intent, ensure_ascii=False)}
请求内容: {json.dumps(request, ensure_ascii=False)}
请定义:
1. 沟通对象
2. 沟通内容
3. 沟通方式
4. 预期效果
5. 反馈机制
请以JSON格式返回目标定义。
"""
messages = [
{"role": "system", "content": "你是一个沟通专家,擅长设定沟通目标。"},
{"role": "user", "content": prompt}
]
result = self.llm_client.chat_completion(messages, temperature=0.3)
if "error" in result:
return self._create_default_communication_goal(intent, request)
response_content = result["choices"][0]["message"]["content"]
import re
json_match = re.search(r'\{.*\}', response_content, re.DOTALL)
if json_match:
goal_data = json.loads(json_match.group())
goal_data["type"] = "communication"
return goal_data
else:
return self._create_default_communication_goal(intent, request)
async def _create_general_goal(
self,
intent: Dict[str, Any],
request: Dict[str, Any],
current_state: Any
) -> Dict[str, Any]:
"""创建通用目标"""
return {
"type": "general",
"description": intent.get("main_intent", "处理用户请求"),
"success_criteria": {
"completion": True,
"user_satisfaction": 0.7
},
"steps": ["理解请求", "执行任务", "返回结果"],
"expected_result": "用户需求得到满足",
"time_limit": 300, # 5分钟
"resource_requirements": ["llm_client", "knowledge_base"]
}
def _create_default_problem_solving_goal(self, intent: Dict[str, Any], request: Dict[str, Any]) -> Dict[str, Any]:
"""创建默认问题解决目标"""
return {
"type": "problem_solving",
"description": "解决用户问题",
"success_criteria": {
"problem_identified": True,
"solution_provided": True,
"user_satisfaction": 0.7
},
"steps": ["分析问题", "寻找解决方案", "提供建议", "验证效果"],
"expected_result": "问题得到解决或提供有效建议",
"time_limit": 300,
"resource_requirements": ["knowledge_base", "llm_client"]
}
def _create_default_information_goal(self, intent: Dict[str, Any], request: Dict[str, Any]) -> Dict[str, Any]:
"""创建默认信息收集目标"""
return {
"type": "information_gathering",
"description": "收集相关信息",
"success_criteria": {
"information_complete": True,
"information_accurate": True,
"information_relevant": True
},
"steps": ["确定信息需求", "搜索信息源", "收集信息", "整理信息"],
"expected_result": "提供准确、完整、相关的信息",
"time_limit": 180,
"resource_requirements": ["knowledge_base", "search_tools"]
}
def _create_default_task_goal(self, intent: Dict[str, Any], request: Dict[str, Any]) -> Dict[str, Any]:
"""创建默认任务执行目标"""
return {
"type": "task_execution",
"description": "执行指定任务",
"success_criteria": {
"task_completed": True,
"quality_met": True,
"time_met": True
},
"steps": ["理解任务", "制定计划", "执行任务", "验证结果"],
"expected_result": "任务成功完成",
"time_limit": 600,
"resource_requirements": ["task_tools", "monitoring"]
}
def _create_default_analysis_goal(self, intent: Dict[str, Any], request: Dict[str, Any]) -> Dict[str, Any]:
"""创建默认分析目标"""
return {
"type": "analysis",
"description": "执行数据分析",
"success_criteria": {
"analysis_complete": True,
"insights_meaningful": True,
"report_clear": True
},
"steps": ["收集数据", "分析数据", "提取洞察", "生成报告"],
"expected_result": "提供有价值的分析报告",
"time_limit": 900,
"resource_requirements": ["analytics_tools", "data_sources"]
}
def _create_default_communication_goal(self, intent: Dict[str, Any], request: Dict[str, Any]) -> Dict[str, Any]:
"""创建默认沟通目标"""
return {
"type": "communication",
"description": "与用户沟通",
"success_criteria": {
"message_delivered": True,
"response_received": True,
"understanding_achieved": True
},
"steps": ["准备消息", "发送消息", "等待响应", "确认理解"],
"expected_result": "成功沟通并达成理解",
"time_limit": 120,
"resource_requirements": ["communication_tools"]
}
def _create_fallback_goal(self, intent: Dict[str, Any], request: Dict[str, Any]) -> Dict[str, Any]:
"""创建备用目标"""
return {
"type": "fallback",
"description": "处理用户请求",
"success_criteria": {"completion": True},
"steps": ["处理请求"],
"expected_result": "返回响应",
"time_limit": 60,
"resource_requirements": ["basic_tools"]
}
async def update_goal_progress(self, goal_id: str, progress_data: Dict[str, Any]) -> bool:
"""更新目标进度"""
try:
if goal_id not in self.active_goals:
return False
goal = self.active_goals[goal_id]
goal["progress"] = progress_data
goal["updated_at"] = datetime.now().isoformat()
# 检查是否完成
if self._check_goal_completion(goal):
goal["status"] = "completed"
goal["completed_at"] = datetime.now().isoformat()
# 移动到历史记录
self.goal_history.append(goal)
del self.active_goals[goal_id]
logger.info(f"目标 {goal_id} 已完成")
return True
except Exception as e:
logger.error(f"更新目标进度失败: {e}")
return False
def _check_goal_completion(self, goal: Dict[str, Any]) -> bool:
"""检查目标是否完成"""
success_criteria = goal.get("success_criteria", {})
if not success_criteria:
return True
progress = goal.get("progress", {})
# 检查每个成功标准
for criterion, required_value in success_criteria.items():
actual_value = progress.get(criterion)
if actual_value != required_value:
return False
return True
async def evaluate_goal_performance(self, goal_id: str) -> Dict[str, Any]:
"""评估目标性能"""
try:
if goal_id in self.active_goals:
goal = self.active_goals[goal_id]
elif goal_id in [g["id"] for g in self.goal_history]:
goal = next(g for g in self.goal_history if g["id"] == goal_id)
else:
return {"error": "目标不存在"}
evaluation = {
"goal_id": goal_id,
"type": goal.get("type"),
"status": goal.get("status"),
"created_at": goal.get("created_at"),
"completed_at": goal.get("completed_at"),
"duration": self._calculate_goal_duration(goal),
"success_rate": self._calculate_success_rate(goal),
"efficiency": self._calculate_efficiency(goal),
"quality_score": self._calculate_quality_score(goal)
}
return evaluation
except Exception as e:
logger.error(f"评估目标性能失败: {e}")
return {"error": str(e)}
def _calculate_goal_duration(self, goal: Dict[str, Any]) -> float:
"""计算目标持续时间"""
created_at = datetime.fromisoformat(goal.get("created_at", datetime.now().isoformat()))
if goal.get("completed_at"):
completed_at = datetime.fromisoformat(goal["completed_at"])
return (completed_at - created_at).total_seconds()
else:
return (datetime.now() - created_at).total_seconds()
def _calculate_success_rate(self, goal: Dict[str, Any]) -> float:
"""计算成功率"""
if goal.get("status") == "completed":
return 1.0
elif goal.get("status") == "failed":
return 0.0
else:
# 根据进度计算部分成功率
progress = goal.get("progress", {})
success_criteria = goal.get("success_criteria", {})
if not success_criteria:
return 0.5
completed_criteria = 0
for criterion in success_criteria:
if progress.get(criterion) == success_criteria[criterion]:
completed_criteria += 1
return completed_criteria / len(success_criteria)
def _calculate_efficiency(self, goal: Dict[str, Any]) -> float:
"""计算效率"""
duration = self._calculate_goal_duration(goal)
time_limit = goal.get("time_limit", 300)
if duration <= time_limit:
return 1.0
else:
# 超时惩罚
return max(0.0, 1.0 - (duration - time_limit) / time_limit)
def _calculate_quality_score(self, goal: Dict[str, Any]) -> float:
"""计算质量分数"""
# 这里可以根据具体的目标类型和质量指标计算
# 暂时返回一个基于成功率的简单计算
success_rate = self._calculate_success_rate(goal)
efficiency = self._calculate_efficiency(goal)
return (success_rate + efficiency) / 2
def get_active_goals(self) -> List[Dict[str, Any]]:
"""获取活跃目标"""
return list(self.active_goals.values())
def get_goal_history(self, limit: int = 10) -> List[Dict[str, Any]]:
"""获取目标历史"""
return self.goal_history[-limit:] if self.goal_history else []
def get_goal_statistics(self) -> Dict[str, Any]:
"""获取目标统计"""
total_goals = len(self.active_goals) + len(self.goal_history)
completed_goals = len([g for g in self.goal_history if g.get("status") == "completed"])
active_goals = len(self.active_goals)
return {
"total_goals": total_goals,
"active_goals": active_goals,
"completed_goals": completed_goals,
"completion_rate": completed_goals / total_goals if total_goals > 0 else 0,
"goal_types": self._get_goal_type_distribution()
}
def _get_goal_type_distribution(self) -> Dict[str, int]:
"""获取目标类型分布"""
distribution = {}
# 统计活跃目标
for goal in self.active_goals.values():
goal_type = goal.get("type", "unknown")
distribution[goal_type] = distribution.get(goal_type, 0) + 1
# 统计历史目标
for goal in self.goal_history:
goal_type = goal.get("type", "unknown")
distribution[goal_type] = distribution.get(goal_type, 0) + 1
return distribution

409
src/agent/planner.py Normal file
View File

@@ -0,0 +1,409 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
任务规划器
负责制定执行计划和任务分解
"""
import logging
from typing import Dict, List, Any, Optional
from datetime import datetime
import json
from ..core.llm_client import QwenClient
logger = logging.getLogger(__name__)
class TaskPlanner:
"""任务规划器"""
def __init__(self):
self.llm_client = QwenClient()
self.planning_strategies = {
"sequential": self._create_sequential_plan,
"parallel": self._create_parallel_plan,
"conditional": self._create_conditional_plan,
"iterative": self._create_iterative_plan
}
async def create_plan(
self,
goal: Dict[str, Any],
available_tools: List[Dict[str, Any]],
constraints: Dict[str, Any]
) -> List[Dict[str, Any]]:
"""创建执行计划"""
try:
# 1. 分析目标复杂度
complexity = await self._analyze_goal_complexity(goal)
# 2. 选择规划策略
strategy = self._select_planning_strategy(complexity, goal)
# 3. 生成计划
plan = await self.planning_strategies[strategy](goal, available_tools, constraints)
# 4. 优化计划
optimized_plan = await self._optimize_plan(plan, constraints)
logger.info(f"创建计划成功,包含 {len(optimized_plan)} 个任务")
return optimized_plan
except Exception as e:
logger.error(f"创建计划失败: {e}")
return []
async def _analyze_goal_complexity(self, goal: Dict[str, Any]) -> Dict[str, Any]:
"""分析目标复杂度"""
prompt = f"""
请分析以下目标的复杂度:
目标: {goal.get('description', '')}
类型: {goal.get('type', '')}
上下文: {goal.get('context', {})}
请从以下维度评估复杂度1-10分
1. 任务数量
2. 依赖关系复杂度
3. 所需工具数量
4. 时间要求
5. 资源需求
请以JSON格式返回分析结果。
"""
messages = [
{"role": "system", "content": "你是一个任务规划专家,擅长分析任务复杂度。"},
{"role": "user", "content": prompt}
]
result = self.llm_client.chat_completion(messages, temperature=0.3)
if "error" in result:
return {"complexity_score": 5, "strategy": "sequential"}
try:
response_content = result["choices"][0]["message"]["content"]
import re
json_match = re.search(r'\{.*\}', response_content, re.DOTALL)
if json_match:
analysis = json.loads(json_match.group())
return analysis
else:
return {"complexity_score": 5, "strategy": "sequential"}
except Exception as e:
logger.error(f"解析复杂度分析失败: {e}")
return {"complexity_score": 5, "strategy": "sequential"}
def _select_planning_strategy(self, complexity: Dict[str, Any], goal: Dict[str, Any]) -> str:
"""选择规划策略"""
complexity_score = complexity.get("complexity_score", 5)
goal_type = goal.get("type", "general")
if complexity_score <= 3:
return "sequential"
elif complexity_score <= 6:
if goal_type in ["analysis", "monitoring"]:
return "parallel"
else:
return "conditional"
else:
return "iterative"
async def _create_sequential_plan(
self,
goal: Dict[str, Any],
available_tools: List[Dict[str, Any]],
constraints: Dict[str, Any]
) -> List[Dict[str, Any]]:
"""创建顺序执行计划"""
prompt = f"""
请为以下目标创建一个顺序执行计划:
目标: {goal.get('description', '')}
可用工具: {[tool.get('name', '') for tool in available_tools]}
请将目标分解为具体的执行步骤,每个步骤包含:
1. 任务描述
2. 所需工具
3. 输入参数
4. 预期输出
5. 成功条件
请以JSON数组格式返回计划。
"""
messages = [
{"role": "system", "content": "你是一个任务规划专家,擅长创建顺序执行计划。"},
{"role": "user", "content": prompt}
]
result = self.llm_client.chat_completion(messages, temperature=0.3)
if "error" in result:
return self._create_fallback_plan(goal)
try:
response_content = result["choices"][0]["message"]["content"]
import re
json_match = re.search(r'\[.*\]', response_content, re.DOTALL)
if json_match:
plan = json.loads(json_match.group())
return self._format_plan_tasks(plan)
else:
return self._create_fallback_plan(goal)
except Exception as e:
logger.error(f"解析顺序计划失败: {e}")
return self._create_fallback_plan(goal)
async def _create_parallel_plan(
self,
goal: Dict[str, Any],
available_tools: List[Dict[str, Any]],
constraints: Dict[str, Any]
) -> List[Dict[str, Any]]:
"""创建并行执行计划"""
# 先创建基础任务
base_tasks = await self._create_sequential_plan(goal, available_tools, constraints)
# 分析任务间的依赖关系
parallel_groups = self._group_parallel_tasks(base_tasks)
return parallel_groups
async def _create_conditional_plan(
self,
goal: Dict[str, Any],
available_tools: List[Dict[str, Any]],
constraints: Dict[str, Any]
) -> List[Dict[str, Any]]:
"""创建条件执行计划"""
prompt = f"""
请为以下目标创建一个条件执行计划:
目标: {goal.get('description', '')}
上下文: {goal.get('context', {})}
计划应该包含:
1. 初始条件检查
2. 分支逻辑
3. 每个分支的具体任务
4. 合并条件
请以JSON格式返回计划。
"""
messages = [
{"role": "system", "content": "你是一个任务规划专家,擅长创建条件执行计划。"},
{"role": "user", "content": prompt}
]
result = self.llm_client.chat_completion(messages, temperature=0.3)
if "error" in result:
return await self._create_sequential_plan(goal, available_tools, constraints)
try:
response_content = result["choices"][0]["message"]["content"]
import re
json_match = re.search(r'\{.*\}', response_content, re.DOTALL)
if json_match:
plan = json.loads(json_match.group())
return self._format_conditional_plan(plan)
else:
return await self._create_sequential_plan(goal, available_tools, constraints)
except Exception as e:
logger.error(f"解析条件计划失败: {e}")
return await self._create_sequential_plan(goal, available_tools, constraints)
async def _create_iterative_plan(
self,
goal: Dict[str, Any],
available_tools: List[Dict[str, Any]],
constraints: Dict[str, Any]
) -> List[Dict[str, Any]]:
"""创建迭代执行计划"""
# 创建基础计划
base_plan = await self._create_sequential_plan(goal, available_tools, constraints)
# 添加迭代控制任务
iteration_control = {
"id": "iteration_control",
"type": "control",
"description": "迭代控制",
"max_iterations": constraints.get("max_iterations", 10),
"convergence_criteria": goal.get("success_criteria", {}),
"tasks": base_plan
}
return [iteration_control]
def _group_parallel_tasks(self, tasks: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""将任务分组为可并行执行的任务组"""
groups = []
current_group = []
for task in tasks:
# 简单的分组逻辑:相同类型的任务可以并行
if not current_group or current_group[0].get("type") == task.get("type"):
current_group.append(task)
else:
if current_group:
groups.append({
"type": "parallel_group",
"tasks": current_group,
"execution_mode": "parallel"
})
current_group = [task]
if current_group:
groups.append({
"type": "parallel_group",
"tasks": current_group,
"execution_mode": "parallel"
})
return groups
def _format_plan_tasks(self, raw_plan: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""格式化计划任务"""
formatted_tasks = []
for i, task in enumerate(raw_plan):
formatted_task = {
"id": f"task_{i+1}",
"type": task.get("type", "action"),
"description": task.get("description", ""),
"tool": task.get("tool", ""),
"parameters": task.get("parameters", {}),
"expected_output": task.get("expected_output", ""),
"success_criteria": task.get("success_criteria", {}),
"dependencies": task.get("dependencies", []),
"priority": task.get("priority", 0.5),
"timeout": task.get("timeout", 60)
}
formatted_tasks.append(formatted_task)
return formatted_tasks
def _format_conditional_plan(self, raw_plan: Dict[str, Any]) -> List[Dict[str, Any]]:
"""格式化条件计划"""
formatted_tasks = []
# 添加条件检查任务
condition_task = {
"id": "condition_check",
"type": "condition",
"description": "条件检查",
"condition": raw_plan.get("condition", ""),
"branches": raw_plan.get("branches", {})
}
formatted_tasks.append(condition_task)
# 添加分支任务
for branch_name, branch_tasks in raw_plan.get("branches", {}).items():
branch_task = {
"id": f"branch_{branch_name}",
"type": "branch",
"description": f"执行分支: {branch_name}",
"condition": branch_name,
"tasks": self._format_plan_tasks(branch_tasks)
}
formatted_tasks.append(branch_task)
return formatted_tasks
async def _optimize_plan(self, plan: List[Dict[str, Any]], constraints: Dict[str, Any]) -> List[Dict[str, Any]]:
"""优化计划"""
optimized_plan = []
for task in plan:
# 检查时间约束
if task.get("timeout", 60) > constraints.get("timeout", 300):
task["timeout"] = constraints.get("timeout", 300)
# 检查资源约束
if task.get("resource_usage", 0) > constraints.get("memory_limit", 1000):
# 分解大任务
subtasks = await self._decompose_task(task)
optimized_plan.extend(subtasks)
else:
optimized_plan.append(task)
return optimized_plan
async def _decompose_task(self, task: Dict[str, Any]) -> List[Dict[str, Any]]:
"""分解大任务为小任务"""
prompt = f"""
请将以下大任务分解为更小的子任务:
任务: {task.get('description', '')}
类型: {task.get('type', '')}
参数: {task.get('parameters', {})}
请返回分解后的子任务列表,每个子任务应该是独立的、可执行的。
"""
messages = [
{"role": "system", "content": "你是一个任务分解专家,擅长将复杂任务分解为简单任务。"},
{"role": "user", "content": prompt}
]
result = self.llm_client.chat_completion(messages, temperature=0.3)
if "error" in result:
return [task] # 如果分解失败,返回原任务
try:
response_content = result["choices"][0]["message"]["content"]
import re
json_match = re.search(r'\[.*\]', response_content, re.DOTALL)
if json_match:
subtasks = json.loads(json_match.group())
return self._format_plan_tasks(subtasks)
else:
return [task]
except Exception as e:
logger.error(f"任务分解失败: {e}")
return [task]
def _create_fallback_plan(self, goal: Dict[str, Any]) -> List[Dict[str, Any]]:
"""创建备用计划"""
return [{
"id": "fallback_task",
"type": "action",
"description": goal.get("description", "执行目标"),
"tool": "general_response",
"parameters": {"goal": goal},
"expected_output": "目标完成",
"success_criteria": {"completion": True},
"priority": 0.5,
"timeout": 60
}]
def validate_plan(self, plan: List[Dict[str, Any]]) -> Dict[str, Any]:
"""验证计划的有效性"""
validation_result = {
"valid": True,
"issues": [],
"warnings": []
}
for task in plan:
# 检查必要字段
if not task.get("id"):
validation_result["issues"].append("任务缺少ID")
validation_result["valid"] = False
if not task.get("description"):
validation_result["warnings"].append(f"任务 {task.get('id', 'unknown')} 缺少描述")
# 检查依赖关系
dependencies = task.get("dependencies", [])
task_ids = [t.get("id") for t in plan]
for dep in dependencies:
if dep not in task_ids:
validation_result["issues"].append(f"任务 {task.get('id')} 的依赖 {dep} 不存在")
validation_result["valid"] = False
return validation_result

View File

@@ -0,0 +1,479 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
推理引擎
负责逻辑推理和决策制定
"""
import logging
from typing import Dict, List, Any, Optional
from datetime import datetime
import json
from ..core.llm_client import QwenClient
logger = logging.getLogger(__name__)
class ReasoningEngine:
"""推理引擎"""
def __init__(self):
self.llm_client = QwenClient()
self.reasoning_patterns = {
"causal": self._causal_reasoning,
"deductive": self._deductive_reasoning,
"inductive": self._inductive_reasoning,
"abductive": self._abductive_reasoning,
"analogical": self._analogical_reasoning
}
self.reasoning_history = []
async def analyze_intent(
self,
message: str,
context: Dict[str, Any],
history: List[Dict[str, Any]]
) -> Dict[str, Any]:
"""分析用户意图"""
try:
prompt = f"""
请分析以下用户消息的意图:
用户消息: {message}
上下文: {json.dumps(context, ensure_ascii=False)}
历史记录: {json.dumps(history, ensure_ascii=False)}
请从以下维度分析:
1. 主要意图(问题咨询、工单创建、系统查询等)
2. 情感倾向(积极、消极、中性)
3. 紧急程度(高、中、低)
4. 所需工具类型
5. 预期响应类型
6. 关键信息提取
请以JSON格式返回分析结果。
"""
messages = [
{"role": "system", "content": "你是一个意图分析专家,擅长理解用户需求和意图。"},
{"role": "user", "content": prompt}
]
result = self.llm_client.chat_completion(messages, temperature=0.3)
if "error" in result:
return self._create_fallback_intent(message)
response_content = result["choices"][0]["message"]["content"]
import re
json_match = re.search(r'\{.*\}', response_content, re.DOTALL)
if json_match:
intent_analysis = json.loads(json_match.group())
intent_analysis["timestamp"] = datetime.now().isoformat()
return intent_analysis
else:
return self._create_fallback_intent(message)
except Exception as e:
logger.error(f"意图分析失败: {e}")
return self._create_fallback_intent(message)
async def make_decision(
self,
situation: Dict[str, Any],
options: List[Dict[str, Any]],
criteria: Dict[str, Any]
) -> Dict[str, Any]:
"""制定决策"""
try:
prompt = f"""
请根据以下情况制定决策:
当前情况: {json.dumps(situation, ensure_ascii=False)}
可选方案: {json.dumps(options, ensure_ascii=False)}
决策标准: {json.dumps(criteria, ensure_ascii=False)}
请分析每个方案的优缺点,并选择最佳方案。
返回格式:
{{
"selected_option": "方案ID",
"reasoning": "选择理由",
"confidence": 0.8,
"risks": ["风险1", "风险2"],
"mitigation": "风险缓解措施"
}}
"""
messages = [
{"role": "system", "content": "你是一个决策制定专家,擅长分析情况并做出最优决策。"},
{"role": "user", "content": prompt}
]
result = self.llm_client.chat_completion(messages, temperature=0.3)
if "error" in result:
return self._create_fallback_decision(options)
response_content = result["choices"][0]["message"]["content"]
import re
json_match = re.search(r'\{.*\}', response_content, re.DOTALL)
if json_match:
decision = json.loads(json_match.group())
decision["timestamp"] = datetime.now().isoformat()
return decision
else:
return self._create_fallback_decision(options)
except Exception as e:
logger.error(f"决策制定失败: {e}")
return self._create_fallback_decision(options)
async def reason_about_problem(
self,
problem: str,
available_information: Dict[str, Any],
reasoning_type: str = "causal"
) -> Dict[str, Any]:
"""对问题进行推理"""
try:
if reasoning_type not in self.reasoning_patterns:
reasoning_type = "causal"
reasoning_func = self.reasoning_patterns[reasoning_type]
result = await reasoning_func(problem, available_information)
# 记录推理历史
self.reasoning_history.append({
"timestamp": datetime.now().isoformat(),
"problem": problem,
"reasoning_type": reasoning_type,
"result": result
})
return result
except Exception as e:
logger.error(f"问题推理失败: {e}")
return {"error": str(e)}
async def _causal_reasoning(self, problem: str, information: Dict[str, Any]) -> Dict[str, Any]:
"""因果推理"""
prompt = f"""
请使用因果推理分析以下问题:
问题: {problem}
可用信息: {json.dumps(information, ensure_ascii=False)}
请分析:
1. 问题的根本原因
2. 可能的因果关系链
3. 影响因素分析
4. 解决方案的预期效果
请以JSON格式返回分析结果。
"""
messages = [
{"role": "system", "content": "你是一个因果推理专家,擅长分析问题的因果关系。"},
{"role": "user", "content": prompt}
]
result = self.llm_client.chat_completion(messages, temperature=0.3)
if "error" in result:
return {"reasoning_type": "causal", "error": "推理失败"}
response_content = result["choices"][0]["message"]["content"]
import re
json_match = re.search(r'\{.*\}', response_content, re.DOTALL)
if json_match:
return json.loads(json_match.group())
else:
return {"reasoning_type": "causal", "analysis": response_content}
async def _deductive_reasoning(self, problem: str, information: Dict[str, Any]) -> Dict[str, Any]:
"""演绎推理"""
prompt = f"""
请使用演绎推理分析以下问题:
问题: {problem}
可用信息: {json.dumps(information, ensure_ascii=False)}
请分析:
1. 一般性规则或原理
2. 具体事实或条件
3. 逻辑推导过程
4. 必然结论
请以JSON格式返回分析结果。
"""
messages = [
{"role": "system", "content": "你是一个演绎推理专家,擅长从一般原理推导具体结论。"},
{"role": "user", "content": prompt}
]
result = self.llm_client.chat_completion(messages, temperature=0.3)
if "error" in result:
return {"reasoning_type": "deductive", "error": "推理失败"}
response_content = result["choices"][0]["message"]["content"]
import re
json_match = re.search(r'\{.*\}', response_content, re.DOTALL)
if json_match:
return json.loads(json_match.group())
else:
return {"reasoning_type": "deductive", "analysis": response_content}
async def _inductive_reasoning(self, problem: str, information: Dict[str, Any]) -> Dict[str, Any]:
"""归纳推理"""
prompt = f"""
请使用归纳推理分析以下问题:
问题: {problem}
可用信息: {json.dumps(information, ensure_ascii=False)}
请分析:
1. 观察到的具体现象
2. 寻找共同模式
3. 形成一般性假设
4. 验证假设的合理性
请以JSON格式返回分析结果。
"""
messages = [
{"role": "system", "content": "你是一个归纳推理专家,擅长从具体现象归纳一般规律。"},
{"role": "user", "content": prompt}
]
result = self.llm_client.chat_completion(messages, temperature=0.3)
if "error" in result:
return {"reasoning_type": "inductive", "error": "推理失败"}
response_content = result["choices"][0]["message"]["content"]
import re
json_match = re.search(r'\{.*\}', response_content, re.DOTALL)
if json_match:
return json.loads(json_match.group())
else:
return {"reasoning_type": "inductive", "analysis": response_content}
async def _abductive_reasoning(self, problem: str, information: Dict[str, Any]) -> Dict[str, Any]:
"""溯因推理"""
prompt = f"""
请使用溯因推理分析以下问题:
问题: {problem}
可用信息: {json.dumps(information, ensure_ascii=False)}
请分析:
1. 观察到的现象
2. 可能的最佳解释
3. 解释的合理性评估
4. 需要进一步验证的假设
请以JSON格式返回分析结果。
"""
messages = [
{"role": "system", "content": "你是一个溯因推理专家,擅长寻找现象的最佳解释。"},
{"role": "user", "content": prompt}
]
result = self.llm_client.chat_completion(messages, temperature=0.3)
if "error" in result:
return {"reasoning_type": "abductive", "error": "推理失败"}
response_content = result["choices"][0]["message"]["content"]
import re
json_match = re.search(r'\{.*\}', response_content, re.DOTALL)
if json_match:
return json.loads(json_match.group())
else:
return {"reasoning_type": "abductive", "analysis": response_content}
async def _analogical_reasoning(self, problem: str, information: Dict[str, Any]) -> Dict[str, Any]:
"""类比推理"""
prompt = f"""
请使用类比推理分析以下问题:
问题: {problem}
可用信息: {json.dumps(information, ensure_ascii=False)}
请分析:
1. 寻找相似的问题或情况
2. 识别相似性和差异性
3. 应用类比关系
4. 调整解决方案以适应当前情况
请以JSON格式返回分析结果。
"""
messages = [
{"role": "system", "content": "你是一个类比推理专家,擅长通过类比解决问题。"},
{"role": "user", "content": prompt}
]
result = self.llm_client.chat_completion(messages, temperature=0.3)
if "error" in result:
return {"reasoning_type": "analogical", "error": "推理失败"}
response_content = result["choices"][0]["message"]["content"]
import re
json_match = re.search(r'\{.*\}', response_content, re.DOTALL)
if json_match:
return json.loads(json_match.group())
else:
return {"reasoning_type": "analogical", "analysis": response_content}
async def extract_insights(
self,
execution_result: Dict[str, Any],
goal: Dict[str, Any]
) -> Dict[str, Any]:
"""从执行结果中提取洞察"""
try:
prompt = f"""
请从以下执行结果中提取洞察:
执行结果: {json.dumps(execution_result, ensure_ascii=False)}
目标: {json.dumps(goal, ensure_ascii=False)}
请分析:
1. 成功模式(什么导致了成功)
2. 失败模式(什么导致了失败)
3. 性能指标(效率、准确性等)
4. 改进建议
5. 新发现的知识
请以JSON格式返回分析结果。
"""
messages = [
{"role": "system", "content": "你是一个洞察提取专家,擅长从执行结果中提取有价值的洞察。"},
{"role": "user", "content": prompt}
]
result = self.llm_client.chat_completion(messages, temperature=0.3)
if "error" in result:
return {"error": "洞察提取失败"}
response_content = result["choices"][0]["message"]["content"]
import re
json_match = re.search(r'\{.*\}', response_content, re.DOTALL)
if json_match:
insights = json.loads(json_match.group())
insights["timestamp"] = datetime.now().isoformat()
return insights
else:
return {"analysis": response_content, "timestamp": datetime.now().isoformat()}
except Exception as e:
logger.error(f"洞察提取失败: {e}")
return {"error": str(e)}
async def evaluate_solution(
self,
problem: str,
solution: Dict[str, Any],
criteria: Dict[str, Any]
) -> Dict[str, Any]:
"""评估解决方案"""
try:
prompt = f"""
请评估以下解决方案:
问题: {problem}
解决方案: {json.dumps(solution, ensure_ascii=False)}
评估标准: {json.dumps(criteria, ensure_ascii=False)}
请从以下维度评估:
1. 有效性(是否能解决问题)
2. 效率(资源消耗和时间成本)
3. 可行性(实施难度)
4. 风险(潜在问题)
5. 创新性(新颖程度)
请以JSON格式返回评估结果。
"""
messages = [
{"role": "system", "content": "你是一个解决方案评估专家,擅长全面评估解决方案的质量。"},
{"role": "user", "content": prompt}
]
result = self.llm_client.chat_completion(messages, temperature=0.3)
if "error" in result:
return {"error": "解决方案评估失败"}
response_content = result["choices"][0]["message"]["content"]
import re
json_match = re.search(r'\{.*\}', response_content, re.DOTALL)
if json_match:
evaluation = json.loads(json_match.group())
evaluation["timestamp"] = datetime.now().isoformat()
return evaluation
else:
return {"evaluation": response_content, "timestamp": datetime.now().isoformat()}
except Exception as e:
logger.error(f"解决方案评估失败: {e}")
return {"error": str(e)}
def _create_fallback_intent(self, message: str) -> Dict[str, Any]:
"""创建备用意图分析"""
return {
"main_intent": "general_query",
"emotion": "neutral",
"urgency": "medium",
"required_tools": ["generate_response"],
"expected_response": "text",
"key_information": {"message": message},
"confidence": 0.5,
"timestamp": datetime.now().isoformat()
}
def _create_fallback_decision(self, options: List[Dict[str, Any]]) -> Dict[str, Any]:
"""创建备用决策"""
if not options:
return {
"selected_option": None,
"reasoning": "无可用选项",
"confidence": 0.0,
"timestamp": datetime.now().isoformat()
}
# 选择第一个选项作为默认选择
return {
"selected_option": options[0].get("id", "option_1"),
"reasoning": "默认选择",
"confidence": 0.3,
"risks": ["决策质量未知"],
"mitigation": "需要进一步验证",
"timestamp": datetime.now().isoformat()
}
def get_reasoning_history(self, limit: int = 10) -> List[Dict[str, Any]]:
"""获取推理历史"""
return self.reasoning_history[-limit:] if self.reasoning_history else []
def clear_reasoning_history(self):
"""清空推理历史"""
self.reasoning_history = []
logger.info("推理历史已清空")

435
src/agent/tool_manager.py Normal file
View File

@@ -0,0 +1,435 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
工具管理器
负责管理和执行各种工具
"""
import logging
import asyncio
from typing import Dict, List, Any, Optional, Callable
from datetime import datetime
import json
logger = logging.getLogger(__name__)
class ToolManager:
"""工具管理器"""
def __init__(self):
self.tools = {}
self.tool_usage_stats = {}
self.tool_performance = {}
self._register_default_tools()
def _register_default_tools(self):
"""注册默认工具"""
# 注册基础工具
self.register_tool("search_knowledge", self._search_knowledge_tool)
self.register_tool("create_work_order", self._create_work_order_tool)
self.register_tool("update_work_order", self._update_work_order_tool)
self.register_tool("generate_response", self._generate_response_tool)
self.register_tool("analyze_data", self._analyze_data_tool)
self.register_tool("send_notification", self._send_notification_tool)
self.register_tool("schedule_task", self._schedule_task_tool)
self.register_tool("web_search", self._web_search_tool)
self.register_tool("file_operation", self._file_operation_tool)
self.register_tool("database_query", self._database_query_tool)
logger.info(f"已注册 {len(self.tools)} 个默认工具")
def register_tool(self, name: str, func: Callable, metadata: Optional[Dict[str, Any]] = None):
"""注册工具"""
self.tools[name] = {
"function": func,
"metadata": metadata or {},
"usage_count": 0,
"last_used": None,
"success_rate": 0.0
}
logger.info(f"注册工具: {name}")
def unregister_tool(self, name: str) -> bool:
"""注销工具"""
if name in self.tools:
del self.tools[name]
logger.info(f"注销工具: {name}")
return True
return False
async def execute_tool(self, tool_name: str, parameters: Dict[str, Any]) -> Dict[str, Any]:
"""执行工具"""
if tool_name not in self.tools:
return {
"success": False,
"error": f"工具 '{tool_name}' 不存在"
}
tool = self.tools[tool_name]
start_time = datetime.now()
try:
# 更新使用统计
tool["usage_count"] += 1
tool["last_used"] = start_time
# 执行工具
if asyncio.iscoroutinefunction(tool["function"]):
result = await tool["function"](**parameters)
else:
result = tool["function"](**parameters)
# 更新性能统计
execution_time = (datetime.now() - start_time).total_seconds()
self._update_tool_performance(tool_name, True, execution_time)
logger.info(f"工具 '{tool_name}' 执行成功,耗时: {execution_time:.2f}")
return {
"success": True,
"result": result,
"execution_time": execution_time,
"tool": tool_name
}
except Exception as e:
logger.error(f"工具 '{tool_name}' 执行失败: {e}")
# 更新性能统计
execution_time = (datetime.now() - start_time).total_seconds()
self._update_tool_performance(tool_name, False, execution_time)
return {
"success": False,
"error": str(e),
"execution_time": execution_time,
"tool": tool_name
}
def _update_tool_performance(self, tool_name: str, success: bool, execution_time: float):
"""更新工具性能统计"""
if tool_name not in self.tool_performance:
self.tool_performance[tool_name] = {
"total_executions": 0,
"successful_executions": 0,
"total_time": 0.0,
"avg_execution_time": 0.0,
"success_rate": 0.0
}
perf = self.tool_performance[tool_name]
perf["total_executions"] += 1
perf["total_time"] += execution_time
perf["avg_execution_time"] = perf["total_time"] / perf["total_executions"]
if success:
perf["successful_executions"] += 1
perf["success_rate"] = perf["successful_executions"] / perf["total_executions"]
# 更新工具的成功率
self.tools[tool_name]["success_rate"] = perf["success_rate"]
def get_available_tools(self) -> List[Dict[str, Any]]:
"""获取可用工具列表"""
tools_info = []
for name, tool in self.tools.items():
tool_info = {
"name": name,
"metadata": tool["metadata"],
"usage_count": tool["usage_count"],
"last_used": tool["last_used"].isoformat() if tool["last_used"] else None,
"success_rate": tool["success_rate"]
}
# 添加性能信息
if name in self.tool_performance:
perf = self.tool_performance[name]
tool_info.update({
"avg_execution_time": perf["avg_execution_time"],
"total_executions": perf["total_executions"]
})
tools_info.append(tool_info)
return tools_info
def get_tool_info(self, tool_name: str) -> Optional[Dict[str, Any]]:
"""获取工具信息"""
if tool_name not in self.tools:
return None
tool = self.tools[tool_name]
info = {
"name": tool_name,
"metadata": tool["metadata"],
"usage_count": tool["usage_count"],
"last_used": tool["last_used"].isoformat() if tool["last_used"] else None,
"success_rate": tool["success_rate"]
}
if tool_name in self.tool_performance:
info.update(self.tool_performance[tool_name])
return info
def update_usage_stats(self, tool_usage: List[Dict[str, Any]]):
"""更新工具使用统计"""
for usage in tool_usage:
tool_name = usage.get("tool")
if tool_name in self.tools:
self.tools[tool_name]["usage_count"] += usage.get("count", 1)
# 默认工具实现
async def _search_knowledge_tool(self, query: str, top_k: int = 3, **kwargs) -> Dict[str, Any]:
"""搜索知识库工具"""
try:
from ..knowledge_base.knowledge_manager import KnowledgeManager
knowledge_manager = KnowledgeManager()
results = knowledge_manager.search_knowledge(query, top_k)
return {
"query": query,
"results": results,
"count": len(results)
}
except Exception as e:
logger.error(f"搜索知识库失败: {e}")
return {"error": str(e)}
async def _create_work_order_tool(self, title: str, description: str, category: str, priority: str = "medium", **kwargs) -> Dict[str, Any]:
"""创建工单工具"""
try:
from ..dialogue.dialogue_manager import DialogueManager
dialogue_manager = DialogueManager()
result = dialogue_manager.create_work_order(title, description, category, priority)
return result
except Exception as e:
logger.error(f"创建工单失败: {e}")
return {"error": str(e)}
async def _update_work_order_tool(self, work_order_id: int, **kwargs) -> Dict[str, Any]:
"""更新工单工具"""
try:
from ..dialogue.dialogue_manager import DialogueManager
dialogue_manager = DialogueManager()
success = dialogue_manager.update_work_order(work_order_id, **kwargs)
return {
"success": success,
"work_order_id": work_order_id,
"updated_fields": list(kwargs.keys())
}
except Exception as e:
logger.error(f"更新工单失败: {e}")
return {"error": str(e)}
async def _generate_response_tool(self, message: str, context: str = "", **kwargs) -> Dict[str, Any]:
"""生成回复工具"""
try:
from ..core.llm_client import QwenClient
llm_client = QwenClient()
result = llm_client.generate_response(message, context)
return result
except Exception as e:
logger.error(f"生成回复失败: {e}")
return {"error": str(e)}
async def _analyze_data_tool(self, data_type: str, date_range: str = "last_7_days", **kwargs) -> Dict[str, Any]:
"""数据分析工具"""
try:
from ..analytics.analytics_manager import AnalyticsManager
analytics_manager = AnalyticsManager()
if data_type == "daily_analytics":
result = analytics_manager.generate_daily_analytics()
elif data_type == "summary":
result = analytics_manager.get_analytics_summary()
elif data_type == "category_performance":
result = analytics_manager.get_category_performance()
else:
result = {"error": f"不支持的数据类型: {data_type}"}
return result
except Exception as e:
logger.error(f"数据分析失败: {e}")
return {"error": str(e)}
async def _send_notification_tool(self, message: str, recipients: List[str], notification_type: str = "info", **kwargs) -> Dict[str, Any]:
"""发送通知工具"""
try:
# 这里可以实现具体的通知逻辑
# 例如:发送邮件、短信、推送通知等
notification_data = {
"message": message,
"recipients": recipients,
"type": notification_type,
"timestamp": datetime.now().isoformat()
}
# 模拟发送通知
logger.info(f"发送通知: {message}{recipients}")
return {
"success": True,
"notification_id": f"notif_{datetime.now().strftime('%Y%m%d_%H%M%S')}",
"data": notification_data
}
except Exception as e:
logger.error(f"发送通知失败: {e}")
return {"error": str(e)}
async def _schedule_task_tool(self, task_name: str, schedule_time: str, task_data: Dict[str, Any], **kwargs) -> Dict[str, Any]:
"""调度任务工具"""
try:
# 这里可以实现任务调度逻辑
# 例如使用APScheduler、Celery等
schedule_data = {
"task_name": task_name,
"schedule_time": schedule_time,
"task_data": task_data,
"created_at": datetime.now().isoformat()
}
logger.info(f"调度任务: {task_name}{schedule_time}")
return {
"success": True,
"schedule_id": f"schedule_{datetime.now().strftime('%Y%m%d_%H%M%S')}",
"data": schedule_data
}
except Exception as e:
logger.error(f"调度任务失败: {e}")
return {"error": str(e)}
async def _web_search_tool(self, query: str, max_results: int = 5, **kwargs) -> Dict[str, Any]:
"""网络搜索工具"""
try:
# 这里可以实现网络搜索逻辑
# 例如使用Google Search API、Bing Search API等
search_results = [
{
"title": f"搜索结果 {i+1}",
"url": f"https://example.com/result{i+1}",
"snippet": f"这是关于 '{query}' 的搜索结果摘要 {i+1}"
}
for i in range(min(max_results, 3))
]
logger.info(f"网络搜索: {query}")
return {
"query": query,
"results": search_results,
"count": len(search_results)
}
except Exception as e:
logger.error(f"网络搜索失败: {e}")
return {"error": str(e)}
async def _file_operation_tool(self, operation: str, file_path: str, content: str = "", **kwargs) -> Dict[str, Any]:
"""文件操作工具"""
try:
import os
if operation == "read":
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
return {"success": True, "content": content, "operation": "read"}
elif operation == "write":
with open(file_path, 'w', encoding='utf-8') as f:
f.write(content)
return {"success": True, "operation": "write", "file_path": file_path}
elif operation == "exists":
exists = os.path.exists(file_path)
return {"success": True, "exists": exists, "file_path": file_path}
else:
return {"error": f"不支持的文件操作: {operation}"}
except Exception as e:
logger.error(f"文件操作失败: {e}")
return {"error": str(e)}
async def _database_query_tool(self, query: str, query_type: str = "select", **kwargs) -> Dict[str, Any]:
"""数据库查询工具"""
try:
from ..core.database import db_manager
with db_manager.get_session() as session:
if query_type == "select":
result = session.execute(query).fetchall()
return {
"success": True,
"result": [dict(row) for row in result],
"count": len(result)
}
else:
session.execute(query)
session.commit()
return {"success": True, "operation": query_type}
except Exception as e:
logger.error(f"数据库查询失败: {e}")
return {"error": str(e)}
def get_tool_performance_report(self) -> Dict[str, Any]:
"""获取工具性能报告"""
report = {
"total_tools": len(self.tools),
"tool_performance": {},
"summary": {
"most_used": None,
"most_reliable": None,
"fastest": None,
"slowest": None
}
}
if not self.tool_performance:
return report
# 分析性能数据
most_used_count = 0
most_reliable_rate = 0
fastest_time = float('inf')
slowest_time = 0
for tool_name, perf in self.tool_performance.items():
report["tool_performance"][tool_name] = perf
# 找出最常用的工具
if perf["total_executions"] > most_used_count:
most_used_count = perf["total_executions"]
report["summary"]["most_used"] = tool_name
# 找出最可靠的工具
if perf["success_rate"] > most_reliable_rate:
most_reliable_rate = perf["success_rate"]
report["summary"]["most_reliable"] = tool_name
# 找出最快的工具
if perf["avg_execution_time"] < fastest_time:
fastest_time = perf["avg_execution_time"]
report["summary"]["fastest"] = tool_name
# 找出最慢的工具
if perf["avg_execution_time"] > slowest_time:
slowest_time = perf["avg_execution_time"]
report["summary"]["slowest"] = tool_name
return report

816
src/agent_assistant.py Normal file
View File

@@ -0,0 +1,816 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
增强版TSP助手 - 集成Agent功能
这是一个真正的智能Agent实现
"""
import logging
import asyncio
from typing import Dict, Any, List, Optional
from datetime import datetime
import json
from src.main import TSPAssistant
from src.agent import AgentCore, AgentState
logger = logging.getLogger(__name__)
class TSPAgentAssistant(TSPAssistant):
"""TSP Agent助手 - 增强版TSP助手具备完整Agent功能"""
def __init__(self):
# 初始化基础TSP助手
super().__init__()
# 初始化Agent核心
self.agent_core = AgentCore()
# Agent特有功能
self.is_agent_mode = True
self.proactive_tasks = []
self.agent_memory = {}
logger.info("TSP Agent助手初始化完成")
async def process_message_agent(
self,
message: str,
user_id: str = None,
work_order_id: int = None,
enable_proactive: bool = True
) -> Dict[str, Any]:
"""Agent模式处理用户消息"""
try:
# 构建请求
request = {
"message": message,
"user_id": user_id,
"work_order_id": work_order_id,
"context": {
"session_id": f"session_{user_id}_{datetime.now().strftime('%Y%m%d_%H%M%S')}",
"timestamp": datetime.now().isoformat()
}
}
# 使用Agent核心处理请求
agent_result = await self.agent_core.process_request(request)
# 如果启用主动模式,检查是否需要主动行动
if enable_proactive:
proactive_result = await self.agent_core.proactive_action()
if proactive_result:
agent_result["proactive_action"] = proactive_result
# 记录Agent执行信息
agent_result["agent_mode"] = True
agent_result["agent_status"] = self.agent_core.get_status()
return agent_result
except Exception as e:
logger.error(f"Agent模式处理消息失败: {e}")
# 回退到传统模式
return await self._fallback_to_traditional_mode(message, user_id, work_order_id)
async def _fallback_to_traditional_mode(
self,
message: str,
user_id: str = None,
work_order_id: int = None
) -> Dict[str, Any]:
"""回退到传统模式"""
logger.info("回退到传统TSP助手模式")
# 使用原有的处理方式
result = self.process_message(message, user_id, work_order_id)
# 添加Agent标识
result["agent_mode"] = False
result["fallback_reason"] = "Agent处理失败使用传统模式"
return result
async def create_intelligent_work_order(
self,
user_message: str,
user_id: str = None,
auto_categorize: bool = True,
auto_priority: bool = True
) -> Dict[str, Any]:
"""智能创建工单 - 使用Agent能力"""
try:
# 使用Agent分析用户消息
request = {
"message": user_message,
"user_id": user_id,
"context": {"action": "create_work_order"}
}
agent_result = await self.agent_core.process_request(request)
if "error" in agent_result:
return agent_result
# 从Agent结果中提取工单信息
work_order_info = self._extract_work_order_info(agent_result, user_message)
# 创建工单
work_order = self.create_work_order(
title=work_order_info["title"],
description=work_order_info["description"],
category=work_order_info["category"],
priority=work_order_info["priority"]
)
# 添加Agent分析结果
work_order["agent_analysis"] = agent_result
work_order["intelligent_features"] = {
"auto_categorized": auto_categorize,
"auto_prioritized": auto_priority,
"confidence_score": work_order_info.get("confidence", 0.8)
}
return work_order
except Exception as e:
logger.error(f"智能创建工单失败: {e}")
return {"error": f"智能创建失败: {str(e)}"}
def _extract_work_order_info(self, agent_result: Dict[str, Any], user_message: str) -> Dict[str, Any]:
"""从Agent结果中提取工单信息"""
# 这里可以根据Agent的分析结果智能提取工单信息
# 暂时使用简单的提取逻辑
# 使用LLM提取关键信息
from src.core.llm_client import QwenClient
llm_client = QwenClient()
prompt = f"""
请从以下用户消息中提取工单信息:
用户消息: {user_message}
请提取:
1. 工单标题(简洁明了)
2. 问题描述(详细描述)
3. 问题类别(技术问题、账户问题、服务问题等)
4. 优先级high、medium、low
5. 置信度0-1
请以JSON格式返回。
"""
messages = [
{"role": "system", "content": "你是一个工单信息提取专家,擅长从用户消息中提取关键信息。"},
{"role": "user", "content": prompt}
]
result = llm_client.chat_completion(messages, temperature=0.3)
if "error" in result:
# 使用默认值
return {
"title": "用户问题",
"description": user_message,
"category": "技术问题",
"priority": "medium",
"confidence": 0.5
}
try:
response_content = result["choices"][0]["message"]["content"]
import re
json_match = re.search(r'\{.*\}', response_content, re.DOTALL)
if json_match:
extracted_info = json.loads(json_match.group())
return {
"title": extracted_info.get("title", "用户问题"),
"description": extracted_info.get("description", user_message),
"category": extracted_info.get("category", "技术问题"),
"priority": extracted_info.get("priority", "medium"),
"confidence": extracted_info.get("confidence", 0.7)
}
else:
return {
"title": "用户问题",
"description": user_message,
"category": "技术问题",
"priority": "medium",
"confidence": 0.5
}
except Exception as e:
logger.error(f"提取工单信息失败: {e}")
return {
"title": "用户问题",
"description": user_message,
"category": "技术问题",
"priority": "medium",
"confidence": 0.5
}
async def intelligent_knowledge_search(
self,
query: str,
context: Dict[str, Any] = None,
use_reasoning: bool = True
) -> Dict[str, Any]:
"""智能知识库搜索 - 使用推理能力"""
try:
# 基础搜索
basic_results = self.search_knowledge(query)
if not use_reasoning:
return basic_results
# 使用推理引擎增强搜索
reasoning_result = await self.agent_core.reasoning_engine.reason_about_problem(
problem=f"搜索知识: {query}",
available_information={
"search_results": basic_results.get("results", []),
"context": context or {}
},
reasoning_type="inductive"
)
# 结合搜索结果和推理结果
enhanced_results = {
"basic_search": basic_results,
"reasoning_analysis": reasoning_result,
"enhanced_results": self._enhance_search_results(
basic_results.get("results", []),
reasoning_result
),
"search_strategy": "intelligent_with_reasoning"
}
return enhanced_results
except Exception as e:
logger.error(f"智能知识搜索失败: {e}")
return self.search_knowledge(query)
def _enhance_search_results(
self,
basic_results: List[Dict[str, Any]],
reasoning_result: Dict[str, Any]
) -> List[Dict[str, Any]]:
"""增强搜索结果"""
enhanced_results = []
for result in basic_results:
enhanced_result = result.copy()
# 添加推理增强信息
if "analysis" in reasoning_result:
enhanced_result["reasoning_insights"] = reasoning_result["analysis"]
# 计算增强置信度
original_confidence = result.get("confidence_score", 0.5)
reasoning_confidence = reasoning_result.get("confidence", 0.5)
enhanced_result["enhanced_confidence"] = (original_confidence + reasoning_confidence) / 2
enhanced_results.append(enhanced_result)
return enhanced_results
async def proactive_monitoring(self) -> Dict[str, Any]:
"""主动监控 - Agent主动检查系统状态"""
try:
proactive_actions = []
# 检查预警
alerts = self.get_alerts()
if alerts.get("count", 0) > 0:
proactive_actions.append({
"type": "alert_response",
"description": f"发现 {alerts['count']} 个活跃预警",
"priority": "high",
"action": "需要立即处理预警"
})
# 检查系统健康
system_status = self.get_system_status()
if system_status.get("health_score", 1.0) < 0.8:
proactive_actions.append({
"type": "system_maintenance",
"description": "系统健康状态不佳",
"priority": "medium",
"action": "建议进行系统维护"
})
# 检查知识库质量
knowledge_stats = self.knowledge_manager.get_knowledge_stats()
if knowledge_stats.get("average_confidence", 0.8) < 0.6:
proactive_actions.append({
"type": "knowledge_improvement",
"description": "知识库质量需要提升",
"priority": "low",
"action": "建议更新知识库"
})
return {
"proactive_actions": proactive_actions,
"timestamp": datetime.now().isoformat(),
"agent_status": self.agent_core.get_status()
}
except Exception as e:
logger.error(f"主动监控失败: {e}")
return {"error": str(e)}
async def intelligent_analytics(
self,
analysis_type: str = "comprehensive",
date_range: str = "last_7_days"
) -> Dict[str, Any]:
"""智能分析 - 使用Agent推理能力"""
try:
# 基础分析
basic_analytics = self.generate_analytics(date_range)
if analysis_type == "basic":
return basic_analytics
# 使用Agent进行深度分析
analysis_request = {
"message": f"分析{date_range}的数据",
"context": {
"analysis_type": analysis_type,
"basic_data": basic_analytics
}
}
agent_analysis = await self.agent_core.process_request(analysis_request)
# 结合基础分析和Agent分析
intelligent_analytics = {
"basic_analytics": basic_analytics,
"agent_insights": agent_analysis,
"intelligent_recommendations": self._generate_recommendations(
basic_analytics,
agent_analysis
),
"analysis_confidence": self._calculate_analysis_confidence(agent_analysis)
}
return intelligent_analytics
except Exception as e:
logger.error(f"智能分析失败: {e}")
return self.generate_analytics(date_range)
def _generate_recommendations(
self,
basic_analytics: Dict[str, Any],
agent_analysis: Dict[str, Any]
) -> List[Dict[str, Any]]:
"""生成智能推荐"""
recommendations = []
# 基于基础分析生成推荐
if basic_analytics.get("summary", {}).get("avg_satisfaction", 0) < 0.7:
recommendations.append({
"type": "improvement",
"title": "提升客户满意度",
"description": "客户满意度较低,建议优化服务质量",
"priority": "high",
"action_items": [
"分析低满意度工单",
"改进响应时间",
"提升解决方案质量"
]
})
if basic_analytics.get("summary", {}).get("avg_resolution_time_hours", 0) > 24:
recommendations.append({
"type": "efficiency",
"title": "缩短解决时间",
"description": "平均解决时间过长,建议提升处理效率",
"priority": "medium",
"action_items": [
"优化工作流程",
"增加自动化处理",
"提升知识库质量"
]
})
return recommendations
def _calculate_analysis_confidence(self, agent_analysis: Dict[str, Any]) -> float:
"""计算分析置信度"""
# 基于Agent分析结果计算置信度
if "error" in agent_analysis:
return 0.3
# 这里可以实现更复杂的置信度计算逻辑
return 0.8
def get_agent_status(self) -> Dict[str, Any]:
"""获取Agent状态"""
return {
"success": True,
"status": "active" if self.is_agent_mode else "inactive",
"active_goals": len(self.agent_core.goal_manager.get_active_goals()),
"available_tools": len(self.agent_core.tool_manager.get_available_tools()),
"tools": [
{
"name": tool.name,
"usage_count": getattr(tool, 'usage_count', 0),
"success_rate": getattr(tool, 'success_rate', 0.8)
}
for tool in self.agent_core.tool_manager.get_available_tools()
],
"execution_history": []
}
def toggle_agent_mode(self, enabled: bool) -> bool:
"""切换Agent模式"""
try:
if enabled:
# 同步方式切换
self.is_agent_mode = True
logger.info("已切换到Agent模式")
return True
else:
return self.switch_to_traditional_mode()
except Exception as e:
logger.error(f"切换Agent模式失败: {e}")
return False
def start_proactive_monitoring(self) -> bool:
"""启动主动监控"""
try:
return self.start_agent_monitoring()
except Exception as e:
logger.error(f"启动主动监控失败: {e}")
return False
def stop_proactive_monitoring(self) -> bool:
"""停止主动监控"""
try:
return self.stop_agent_monitoring()
except Exception as e:
logger.error(f"停止主动监控失败: {e}")
return False
def run_proactive_monitoring(self) -> Dict[str, Any]:
"""运行主动监控"""
try:
# 模拟主动监控结果
proactive_actions = [
{"type": "alert_response", "description": "发现系统性能预警"},
{"type": "knowledge_update", "description": "建议更新知识库"},
{"type": "user_assistance", "description": "检测到用户可能需要帮助"}
]
return {
"success": True,
"proactive_actions": proactive_actions
}
except Exception as e:
logger.error(f"运行主动监控失败: {e}")
return {"success": False, "error": str(e)}
def run_intelligent_analysis(self) -> Dict[str, Any]:
"""运行智能分析"""
try:
# 模拟智能分析结果
analysis = {
"trends": {
"dates": ["2024-01-01", "2024-01-02", "2024-01-03"],
"satisfaction": [0.8, 0.85, 0.82],
"resolution_time": [2.5, 2.3, 2.1]
},
"recommendations": [
{"type": "improvement", "title": "提升客户满意度", "description": "建议优化响应时间"},
{"type": "optimization", "title": "知识库优化", "description": "建议增加更多技术问题解答"}
]
}
return analysis
except Exception as e:
logger.error(f"运行智能分析失败: {e}")
return {"error": str(e)}
def process_file_to_knowledge(self, file_path: str, filename: str) -> Dict[str, Any]:
"""处理文件并生成知识库"""
try:
import os
import mimetypes
# 检查文件类型
mime_type, _ = mimetypes.guess_type(file_path)
file_ext = os.path.splitext(filename)[1].lower()
# 读取文件内容
content = self._read_file_content(file_path, file_ext)
if not content:
return {"success": False, "error": "无法读取文件内容"}
# 使用LLM处理内容
knowledge_entries = self._extract_knowledge_from_content(content, filename)
# 保存到知识库
saved_count = 0
for i, entry in enumerate(knowledge_entries):
try:
logger.info(f"保存知识条目 {i+1}: {entry.get('question', '')[:50]}...")
success = self.knowledge_manager.add_knowledge_entry(
question=entry["question"],
answer=entry["answer"],
category=entry.get("category", "其他"),
confidence_score=entry.get("confidence_score", 0.7),
is_verified=False # 新添加的知识库条目默认为未验证
)
if success:
saved_count += 1
logger.info(f"知识条目 {i+1} 保存成功")
else:
logger.error(f"知识条目 {i+1} 保存失败")
except Exception as save_error:
logger.error(f"保存知识条目 {i+1} 时出错: {save_error}")
logger.error(f"条目内容: {entry}")
return {
"success": True,
"knowledge_count": saved_count,
"total_extracted": len(knowledge_entries),
"filename": filename
}
except Exception as e:
logger.error(f"处理文件失败: {e}")
return {"success": False, "error": str(e)}
def _read_file_content(self, file_path: str, file_ext: str) -> str:
"""读取文件内容"""
try:
if file_ext in ['.txt', '.md']:
with open(file_path, 'r', encoding='utf-8') as f:
return f.read()
elif file_ext == '.pdf':
# 需要安装 PyPDF2 或 pdfplumber
try:
import PyPDF2
with open(file_path, 'rb') as f:
reader = PyPDF2.PdfReader(f)
text = ""
for page in reader.pages:
text += page.extract_text() + "\n"
return text
except ImportError:
return "PDF文件需要安装PyPDF2库"
elif file_ext in ['.doc', '.docx']:
# 需要安装 python-docx
try:
from docx import Document
doc = Document(file_path)
text = ""
for paragraph in doc.paragraphs:
text += paragraph.text + "\n"
return text
except ImportError:
return "Word文件需要安装python-docx库"
else:
return "不支持的文件格式"
except Exception as e:
logger.error(f"读取文件失败: {e}")
return ""
def _extract_knowledge_from_content(self, content: str, filename: str) -> List[Dict[str, Any]]:
"""从内容中提取知识"""
try:
# 构建提示词
prompt = f"""
请从以下文档内容中提取问答对,用于构建知识库:
文档名称:{filename}
文档内容:
{content[:2000]}...
请按照以下格式提取问答对:
1. 问题:具体的问题描述
2. 答案:详细的答案内容
3. 分类问题所属类别技术问题、APP功能、远程控制、车辆绑定、其他
4. 置信度0-1之间的数值
请提取3-5个最有价值的问答对每个问答对都要完整且实用。
返回格式为JSON数组例如
[
{{
"question": "如何远程启动车辆?",
"answer": "远程启动车辆需要满足以下条件1. 车辆处于P档 2. 手刹拉起 3. 车门已锁 4. 电池电量充足",
"category": "远程控制",
"confidence_score": 0.9
}}
]
"""
# 调用LLM
response = self.llm_client.chat_completion(
messages=[{"role": "user", "content": prompt}],
temperature=0.3,
max_tokens=2000
)
if response and 'choices' in response:
content_text = response['choices'][0]['message']['content']
logger.info(f"LLM响应内容: {content_text[:500]}...")
# 尝试解析JSON
try:
import json
# 提取JSON部分
start_idx = content_text.find('[')
end_idx = content_text.rfind(']') + 1
if start_idx != -1 and end_idx != 0:
json_str = content_text[start_idx:end_idx]
knowledge_entries = json.loads(json_str)
logger.info(f"成功解析JSON提取到 {len(knowledge_entries)} 条知识")
# 验证每个条目的字段
for i, entry in enumerate(knowledge_entries):
if not isinstance(entry, dict):
logger.error(f"条目 {i} 不是字典格式: {entry}")
continue
if 'question' not in entry:
logger.error(f"条目 {i} 缺少question字段: {entry}")
continue
if 'answer' not in entry:
logger.error(f"条目 {i} 缺少answer字段: {entry}")
continue
logger.info(f"条目 {i} 验证通过: {entry.get('question', '')[:50]}...")
return knowledge_entries
except Exception as json_error:
logger.warning(f"JSON解析失败: {json_error}")
logger.warning(f"原始内容: {content_text}")
# 如果JSON解析失败尝试手动解析
manual_entries = self._parse_knowledge_manually(content_text)
logger.info(f"手动解析提取到 {len(manual_entries)} 条知识")
return manual_entries
else:
logger.error("LLM响应格式错误")
logger.error(f"响应内容: {response}")
return []
except Exception as e:
logger.error(f"提取知识失败: {e}")
return []
def _parse_knowledge_manually(self, content: str) -> List[Dict[str, Any]]:
"""手动解析知识内容"""
try:
entries = []
lines = content.split('\n')
current_entry = {}
for line in lines:
line = line.strip()
if not line:
continue
# 检查问题
if '问题' in line and ('' in line or ':' in line):
if current_entry and 'question' in current_entry:
entries.append(current_entry)
current_entry = {}
# 提取问题内容
if '' in line:
question = line.split('', 1)[1].strip()
else:
question = line.split(':', 1)[1].strip()
current_entry["question"] = question
# 检查答案
elif '答案' in line and ('' in line or ':' in line):
if '' in line:
answer = line.split('', 1)[1].strip()
else:
answer = line.split(':', 1)[1].strip()
current_entry["answer"] = answer
# 检查分类
elif '分类' in line and ('' in line or ':' in line):
if '' in line:
category = line.split('', 1)[1].strip()
else:
category = line.split(':', 1)[1].strip()
current_entry["category"] = category
# 检查置信度
elif '置信度' in line and ('' in line or ':' in line):
try:
if '' in line:
confidence_str = line.split('', 1)[1].strip()
else:
confidence_str = line.split(':', 1)[1].strip()
current_entry["confidence_score"] = float(confidence_str)
except:
current_entry["confidence_score"] = 0.7
# 添加最后一个条目
if current_entry and 'question' in current_entry and 'answer' in current_entry:
entries.append(current_entry)
# 确保每个条目都有必要的字段
for entry in entries:
if 'category' not in entry:
entry['category'] = '其他'
if 'confidence_score' not in entry:
entry['confidence_score'] = 0.7
logger.info(f"手动解析完成,提取到 {len(entries)} 条知识")
return entries
except Exception as e:
logger.error(f"手动解析知识失败: {e}")
return []
async def switch_to_agent_mode(self) -> bool:
"""切换到Agent模式"""
try:
self.is_agent_mode = True
logger.info("已切换到Agent模式")
return True
except Exception as e:
logger.error(f"切换到Agent模式失败: {e}")
return False
def switch_to_traditional_mode(self) -> bool:
"""切换到传统模式"""
try:
self.is_agent_mode = False
logger.info("已切换到传统模式")
return True
except Exception as e:
logger.error(f"切换到传统模式失败: {e}")
return False
async def start_agent_monitoring(self) -> bool:
"""启动Agent监控"""
try:
# 启动基础监控
self.start_monitoring()
# 启动Agent主动监控
asyncio.create_task(self._agent_monitoring_loop())
logger.info("Agent监控已启动")
return True
except Exception as e:
logger.error(f"启动Agent监控失败: {e}")
return False
async def _agent_monitoring_loop(self):
"""Agent监控循环"""
while True:
try:
# 每5分钟执行一次主动监控
await asyncio.sleep(300)
proactive_result = await self.proactive_monitoring()
if proactive_result.get("proactive_actions"):
logger.info(f"发现 {len(proactive_result['proactive_actions'])} 个主动行动机会")
# 这里可以实现自动处理逻辑
# 例如:自动发送通知、自动创建工单等
except Exception as e:
logger.error(f"Agent监控循环错误: {e}")
await asyncio.sleep(60) # 出错后等待1分钟再继续
# 使用示例
async def main():
"""主函数示例"""
# 创建Agent助手
agent_assistant = TSPAgentAssistant()
# 测试Agent功能
print("=== TSP Agent助手测试 ===")
# 测试Agent模式处理消息
response = await agent_assistant.process_message_agent(
message="我的账户无法登录,请帮助我解决这个问题",
user_id="user123"
)
print("Agent模式响应:", response)
# 测试智能工单创建
work_order = await agent_assistant.create_intelligent_work_order(
user_message="系统经常出现错误,影响正常使用",
user_id="user456"
)
print("智能工单创建:", work_order)
# 测试主动监控
monitoring_result = await agent_assistant.proactive_monitoring()
print("主动监控结果:", monitoring_result)
# 获取Agent状态
agent_status = agent_assistant.get_agent_status()
print("Agent状态:", agent_status)
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -0,0 +1 @@
# 分析模块

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,432 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
TSP助手智能预警系统
支持多种预警规则、实时监控和智能分析
"""
import logging
from typing import Dict, List, Any, Optional, Callable
from datetime import datetime, timedelta
from dataclasses import dataclass
from enum import Enum
import json
from ..core.database import db_manager
from ..core.models import WorkOrder, Conversation, Analytics, Alert
logger = logging.getLogger(__name__)
class AlertLevel(Enum):
"""预警级别"""
INFO = "info" # 信息
WARNING = "warning" # 警告
ERROR = "error" # 错误
CRITICAL = "critical" # 严重
class AlertType(Enum):
"""预警类型"""
PERFORMANCE = "performance" # 性能预警
QUALITY = "quality" # 质量预警
VOLUME = "volume" # 量级预警
SYSTEM = "system" # 系统预警
BUSINESS = "business" # 业务预警
@dataclass
class AlertRule:
"""预警规则"""
name: str
description: str
alert_type: AlertType
level: AlertLevel
threshold: float
condition: str # 条件表达式
enabled: bool = True
check_interval: int = 300 # 检查间隔(秒)
last_check: Optional[datetime] = None
cooldown: int = 3600 # 冷却时间(秒)
class AlertSystem:
"""智能预警系统"""
def __init__(self):
self.rules = self._initialize_rules()
self.alert_history = []
self.active_alerts = {}
def _initialize_rules(self) -> Dict[str, AlertRule]:
"""初始化预警规则"""
rules = {}
# 性能预警规则
rules["low_satisfaction"] = AlertRule(
name="满意度预警",
description="用户满意度低于阈值",
alert_type=AlertType.QUALITY,
level=AlertLevel.WARNING,
threshold=0.6,
condition="satisfaction_avg < threshold",
check_interval=1800 # 30分钟
)
rules["high_resolution_time"] = AlertRule(
name="解决时间预警",
description="平均解决时间过长",
alert_type=AlertType.PERFORMANCE,
level=AlertLevel.WARNING,
threshold=24, # 24小时
condition="avg_resolution_time > threshold",
check_interval=3600 # 1小时
)
rules["low_knowledge_hit_rate"] = AlertRule(
name="知识库命中率预警",
description="知识库命中率过低",
alert_type=AlertType.QUALITY,
level=AlertLevel.WARNING,
threshold=0.5,
condition="knowledge_hit_rate < threshold",
check_interval=1800 # 30分钟
)
rules["high_error_rate"] = AlertRule(
name="错误率预警",
description="系统错误率过高",
alert_type=AlertType.SYSTEM,
level=AlertLevel.ERROR,
threshold=0.1,
condition="error_rate > threshold",
check_interval=300 # 5分钟
)
rules["high_volume"] = AlertRule(
name="工单量预警",
description="工单量异常增长",
alert_type=AlertType.VOLUME,
level=AlertLevel.INFO,
threshold=50, # 每小时50个工单
condition="hourly_orders > threshold",
check_interval=600 # 10分钟
)
rules["low_response_time"] = AlertRule(
name="响应时间预警",
description="系统响应时间过长",
alert_type=AlertType.PERFORMANCE,
level=AlertLevel.WARNING,
threshold=5.0, # 5秒
condition="avg_response_time > threshold",
check_interval=300 # 5分钟
)
rules["memory_usage"] = AlertRule(
name="内存使用预警",
description="系统内存使用率过高",
alert_type=AlertType.SYSTEM,
level=AlertLevel.WARNING,
threshold=80.0, # 80%
condition="memory_usage > threshold",
check_interval=300 # 5分钟
)
rules["conversation_drop"] = AlertRule(
name="对话中断预警",
description="用户对话中断率过高",
alert_type=AlertType.QUALITY,
level=AlertLevel.WARNING,
threshold=0.3, # 30%
condition="conversation_drop_rate > threshold",
check_interval=1800 # 30分钟
)
return rules
def check_all_rules(self) -> List[Dict[str, Any]]:
"""检查所有预警规则"""
triggered_alerts = []
for rule_name, rule in self.rules.items():
if not rule.enabled:
continue
# 检查冷却时间
if rule.last_check and (datetime.now() - rule.last_check).seconds < rule.cooldown:
continue
try:
# 获取相关数据
data = self._get_rule_data(rule_name)
# 评估规则条件
if self._evaluate_rule(rule, data):
alert = self._create_alert(rule, data)
triggered_alerts.append(alert)
# 更新规则状态
rule.last_check = datetime.now()
except Exception as e:
logger.error(f"检查规则 {rule_name} 失败: {e}")
return triggered_alerts
def _get_rule_data(self, rule_name: str) -> Dict[str, Any]:
"""获取规则相关数据"""
data = {}
try:
with db_manager.get_session() as session:
# 获取最近24小时的数据
end_time = datetime.now()
start_time = end_time - timedelta(hours=24)
# 工单数据
work_orders = session.query(WorkOrder).filter(
WorkOrder.created_at >= start_time,
WorkOrder.created_at <= end_time
).all()
# 对话数据
conversations = session.query(Conversation).filter(
Conversation.timestamp >= start_time,
Conversation.timestamp <= end_time
).all()
# 计算基础指标
total_orders = len(work_orders)
resolved_orders = len([wo for wo in work_orders if wo.status == "resolved"])
# 满意度
satisfaction_scores = [wo.satisfaction_score for wo in work_orders if wo.satisfaction_score]
data["satisfaction_avg"] = sum(satisfaction_scores) / len(satisfaction_scores) if satisfaction_scores else 0
# 解决时间
resolution_times = []
for wo in work_orders:
if wo.status == "resolved" and wo.updated_at:
resolution_time = (wo.updated_at - wo.created_at).total_seconds() / 3600
resolution_times.append(resolution_time)
data["avg_resolution_time"] = sum(resolution_times) / len(resolution_times) if resolution_times else 0
# 知识库命中率
knowledge_hits = len([c for c in conversations if c.knowledge_used])
data["knowledge_hit_rate"] = knowledge_hits / len(conversations) if conversations else 0
# 错误率
error_conversations = len([c for c in conversations if "error" in c.assistant_response.lower()])
data["error_rate"] = error_conversations / len(conversations) if conversations else 0
# 工单量
data["hourly_orders"] = total_orders / 24
# 响应时间
response_times = []
for c in conversations:
if c.response_time:
response_times.append(c.response_time)
data["avg_response_time"] = sum(response_times) / len(response_times) if response_times else 0
# 内存使用
from ..utils.helpers import get_memory_usage
memory_info = get_memory_usage()
data["memory_usage"] = memory_info.get("percent", 0) * 100
# 对话中断率
total_conversations = len(conversations)
dropped_conversations = len([c for c in conversations if c.user_message and not c.assistant_response])
data["conversation_drop_rate"] = dropped_conversations / total_conversations if total_conversations else 0
except Exception as e:
logger.error(f"获取规则数据失败: {e}")
return data
def _evaluate_rule(self, rule: AlertRule, data: Dict[str, Any]) -> bool:
"""评估规则条件"""
try:
# 简单的条件评估
if rule.condition == "satisfaction_avg < threshold":
return data.get("satisfaction_avg", 0) < rule.threshold
elif rule.condition == "avg_resolution_time > threshold":
return data.get("avg_resolution_time", 0) > rule.threshold
elif rule.condition == "knowledge_hit_rate < threshold":
return data.get("knowledge_hit_rate", 0) < rule.threshold
elif rule.condition == "error_rate > threshold":
return data.get("error_rate", 0) > rule.threshold
elif rule.condition == "hourly_orders > threshold":
return data.get("hourly_orders", 0) > rule.threshold
elif rule.condition == "avg_response_time > threshold":
return data.get("avg_response_time", 0) > rule.threshold
elif rule.condition == "memory_usage > threshold":
return data.get("memory_usage", 0) > rule.threshold
elif rule.condition == "conversation_drop_rate > threshold":
return data.get("conversation_drop_rate", 0) > rule.threshold
return False
except Exception as e:
logger.error(f"评估规则条件失败: {e}")
return False
def _create_alert(self, rule: AlertRule, data: Dict[str, Any]) -> Dict[str, Any]:
"""创建预警"""
alert = {
"rule_name": rule.name,
"alert_type": rule.alert_type.value,
"level": rule.level.value,
"message": self._generate_alert_message(rule, data),
"data": data,
"timestamp": datetime.now().isoformat(),
"rule_id": rule.name
}
# 保存到数据库
self._save_alert(alert)
# 添加到活跃预警
self.active_alerts[rule.name] = alert
return alert
def _generate_alert_message(self, rule: AlertRule, data: Dict[str, Any]) -> str:
"""生成预警消息"""
if rule.name == "满意度预警":
return f"用户满意度较低: {data.get('satisfaction_avg', 0):.2f} (阈值: {rule.threshold})"
elif rule.name == "解决时间预警":
return f"平均解决时间过长: {data.get('avg_resolution_time', 0):.1f}小时 (阈值: {rule.threshold}小时)"
elif rule.name == "知识库命中率预警":
return f"知识库命中率较低: {data.get('knowledge_hit_rate', 0):.2f} (阈值: {rule.threshold})"
elif rule.name == "错误率预警":
return f"系统错误率过高: {data.get('error_rate', 0):.2f} (阈值: {rule.threshold})"
elif rule.name == "工单量预警":
return f"工单量异常增长: {data.get('hourly_orders', 0):.1f}个/小时 (阈值: {rule.threshold}个/小时)"
elif rule.name == "响应时间预警":
return f"系统响应时间过长: {data.get('avg_response_time', 0):.2f}秒 (阈值: {rule.threshold}秒)"
elif rule.name == "内存使用预警":
return f"系统内存使用率过高: {data.get('memory_usage', 0):.1f}% (阈值: {rule.threshold}%)"
elif rule.name == "对话中断预警":
return f"用户对话中断率过高: {data.get('conversation_drop_rate', 0):.2f} (阈值: {rule.threshold})"
else:
return f"触发预警: {rule.name}"
def _save_alert(self, alert: Dict[str, Any]) -> None:
"""保存预警到数据库"""
try:
with db_manager.get_session() as session:
db_alert = Alert(
rule_name=alert["rule_name"],
alert_type=alert["alert_type"],
level=alert["level"],
message=alert["message"],
data=json.dumps(alert["data"], ensure_ascii=False),
is_active=True,
created_at=datetime.now()
)
session.add(db_alert)
session.commit()
except Exception as e:
logger.error(f"保存预警失败: {e}")
def get_active_alerts(self) -> List[Dict[str, Any]]:
"""获取活跃预警"""
try:
with db_manager.get_session() as session:
alerts = session.query(Alert).filter(
Alert.is_active == True
).order_by(Alert.created_at.desc()).all()
return [{
"id": alert.id,
"rule_name": alert.rule_name,
"alert_type": alert.alert_type,
"level": alert.level,
"message": alert.message,
"created_at": alert.created_at.isoformat(),
"data": json.loads(alert.data) if alert.data else {}
} for alert in alerts]
except Exception as e:
logger.error(f"获取活跃预警失败: {e}")
return []
def resolve_alert(self, alert_id: int) -> bool:
"""解决预警"""
try:
with db_manager.get_session() as session:
alert = session.query(Alert).filter(Alert.id == alert_id).first()
if alert:
alert.is_active = False
alert.resolved_at = datetime.now()
session.commit()
return True
return False
except Exception as e:
logger.error(f"解决预警失败: {e}")
return False
def get_alert_statistics(self) -> Dict[str, Any]:
"""获取预警统计"""
try:
with db_manager.get_session() as session:
total_alerts = session.query(Alert).count()
active_alerts = session.query(Alert).filter(Alert.is_active == True).count()
# 按级别统计
level_stats = {}
for level in AlertLevel:
count = session.query(Alert).filter(Alert.level == level.value).count()
level_stats[level.value] = count
# 按类型统计
type_stats = {}
for alert_type in AlertType:
count = session.query(Alert).filter(Alert.alert_type == alert_type.value).count()
type_stats[alert_type.value] = count
return {
"total_alerts": total_alerts,
"active_alerts": active_alerts,
"level_distribution": level_stats,
"type_distribution": type_stats
}
except Exception as e:
logger.error(f"获取预警统计失败: {e}")
return {}
def add_custom_rule(self, rule: AlertRule) -> bool:
"""添加自定义规则"""
try:
self.rules[rule.name] = rule
logger.info(f"添加自定义规则: {rule.name}")
return True
except Exception as e:
logger.error(f"添加自定义规则失败: {e}")
return False
def update_rule(self, rule_name: str, **kwargs) -> bool:
"""更新规则"""
try:
if rule_name in self.rules:
rule = self.rules[rule_name]
for key, value in kwargs.items():
if hasattr(rule, key):
setattr(rule, key, value)
logger.info(f"更新规则: {rule_name}")
return True
return False
except Exception as e:
logger.error(f"更新规则失败: {e}")
return False
def delete_rule(self, rule_name: str) -> bool:
"""删除规则"""
try:
if rule_name in self.rules:
del self.rules[rule_name]
logger.info(f"删除规则: {rule_name}")
return True
return False
except Exception as e:
logger.error(f"删除规则失败: {e}")
return False

View File

@@ -0,0 +1,300 @@
import logging
from typing import Dict, List, Any, Optional
from datetime import datetime, timedelta
import json
from collections import defaultdict
from ..core.database import db_manager
from ..core.models import WorkOrder, Conversation, Analytics, Alert, KnowledgeEntry
logger = logging.getLogger(__name__)
class AnalyticsManager:
"""分析统计管理器"""
def __init__(self):
self.alert_thresholds = {
"low_satisfaction": 0.6,
"high_resolution_time": 24, # 小时
"knowledge_hit_rate": 0.5,
"error_rate": 0.1
}
def generate_daily_analytics(self, date: Optional[datetime] = None) -> Dict[str, Any]:
"""生成每日分析报告"""
if date is None:
date = datetime.now().date()
try:
with db_manager.get_session() as session:
# 获取指定日期的工单数据
start_time = datetime.combine(date, datetime.min.time())
end_time = datetime.combine(date, datetime.max.time())
work_orders = session.query(WorkOrder).filter(
WorkOrder.created_at >= start_time,
WorkOrder.created_at <= end_time
).all()
if not work_orders:
return {"message": f"{date} 没有工单数据"}
# 计算基础统计
total_orders = len(work_orders)
resolved_orders = len([wo for wo in work_orders if wo.status == "resolved"])
# 平均解决时间
resolution_times = []
for wo in work_orders:
if wo.status == "resolved" and wo.updated_at:
resolution_time = (wo.updated_at - wo.created_at).total_seconds() / 3600
resolution_times.append(resolution_time)
avg_resolution_time = sum(resolution_times) / len(resolution_times) if resolution_times else 0
# 平均满意度
satisfaction_scores = [wo.satisfaction_score for wo in work_orders if wo.satisfaction_score]
satisfaction_avg = sum(satisfaction_scores) / len(satisfaction_scores) if satisfaction_scores else 0
# 知识库命中率
conversations = session.query(Conversation).filter(
Conversation.timestamp >= start_time,
Conversation.timestamp <= end_time
).all()
knowledge_hit_rate = self._calculate_knowledge_hit_rate(conversations)
# 类别分布
category_distribution = defaultdict(int)
for wo in work_orders:
category_distribution[wo.category] += 1
# 保存分析结果
analytics = Analytics(
date=start_time,
total_orders=total_orders,
resolved_orders=resolved_orders,
avg_resolution_time=avg_resolution_time,
satisfaction_avg=satisfaction_avg,
knowledge_hit_rate=knowledge_hit_rate,
category_distribution=json.dumps(dict(category_distribution))
)
session.add(analytics)
session.commit()
# 检查预警条件
self._check_alerts(
session,
satisfaction_avg,
avg_resolution_time,
knowledge_hit_rate,
total_orders
)
return {
"date": date.isoformat(),
"total_orders": total_orders,
"resolved_orders": resolved_orders,
"resolution_rate": resolved_orders / total_orders if total_orders > 0 else 0,
"avg_resolution_time_hours": round(avg_resolution_time, 2),
"satisfaction_avg": round(satisfaction_avg, 2),
"knowledge_hit_rate": round(knowledge_hit_rate, 2),
"category_distribution": dict(category_distribution)
}
except Exception as e:
logger.error(f"生成每日分析报告失败: {e}")
return {"error": f"生成失败: {str(e)}"}
def _calculate_knowledge_hit_rate(self, conversations: List[Conversation]) -> float:
"""计算知识库命中率"""
if not conversations:
return 0.0
hit_count = 0
for conv in conversations:
if conv.knowledge_used and conv.knowledge_used != "[]":
hit_count += 1
return hit_count / len(conversations)
def _check_alerts(
self,
session,
satisfaction_avg: float,
avg_resolution_time: float,
knowledge_hit_rate: float,
total_orders: int
):
"""检查预警条件"""
alerts = []
# 满意度预警
if satisfaction_avg < self.alert_thresholds["low_satisfaction"]:
alerts.append({
"type": "low_satisfaction",
"message": f"客户满意度较低: {satisfaction_avg:.2f}",
"severity": "high"
})
# 解决时间预警
if avg_resolution_time > self.alert_thresholds["high_resolution_time"]:
alerts.append({
"type": "high_resolution_time",
"message": f"平均解决时间过长: {avg_resolution_time:.2f}小时",
"severity": "medium"
})
# 知识库命中率预警
if knowledge_hit_rate < self.alert_thresholds["knowledge_hit_rate"]:
alerts.append({
"type": "low_knowledge_hit_rate",
"message": f"知识库命中率较低: {knowledge_hit_rate:.2f}",
"severity": "medium"
})
# 创建预警记录
for alert_data in alerts:
alert = Alert(
alert_type=alert_data["type"],
message=alert_data["message"],
severity=alert_data["severity"],
is_active=True,
created_at=datetime.now()
)
session.add(alert)
session.commit()
def get_analytics_summary(self, days: int = 7) -> Dict[str, Any]:
"""获取分析摘要"""
try:
with db_manager.get_session() as session:
end_date = datetime.now()
start_date = end_date - timedelta(days=days)
analytics = session.query(Analytics).filter(
Analytics.date >= start_date,
Analytics.date <= end_date
).order_by(Analytics.date).all()
if not analytics:
return {"message": f"最近{days}天没有分析数据"}
# 计算汇总统计
total_orders = sum(a.total_orders for a in analytics)
total_resolved = sum(a.resolved_orders for a in analytics)
avg_resolution_time = sum(a.avg_resolution_time for a in analytics) / len(analytics)
avg_satisfaction = sum(a.satisfaction_avg for a in analytics) / len(analytics)
avg_knowledge_hit_rate = sum(a.knowledge_hit_rate for a in analytics) / len(analytics)
# 趋势分析
trends = {
"orders_trend": [a.total_orders for a in analytics],
"satisfaction_trend": [a.satisfaction_avg for a in analytics],
"resolution_time_trend": [a.avg_resolution_time for a in analytics]
}
return {
"period": f"{days}",
"total_orders": total_orders,
"total_resolved": total_resolved,
"resolution_rate": total_resolved / total_orders if total_orders > 0 else 0,
"avg_resolution_time_hours": round(avg_resolution_time, 2),
"avg_satisfaction": round(avg_satisfaction, 2),
"avg_knowledge_hit_rate": round(avg_knowledge_hit_rate, 2),
"trends": trends
}
except Exception as e:
logger.error(f"获取分析摘要失败: {e}")
return {"error": f"获取失败: {str(e)}"}
def get_active_alerts(self) -> List[Dict[str, Any]]:
"""获取活跃预警"""
try:
with db_manager.get_session() as session:
alerts = session.query(Alert).filter(
Alert.is_active == True
).order_by(Alert.created_at.desc()).all()
return [
{
"id": alert.id,
"type": alert.alert_type,
"message": alert.message,
"severity": alert.severity,
"created_at": alert.created_at.isoformat()
}
for alert in alerts
]
except Exception as e:
logger.error(f"获取活跃预警失败: {e}")
return []
def resolve_alert(self, alert_id: int) -> bool:
"""解决预警"""
try:
with db_manager.get_session() as session:
alert = session.query(Alert).filter(Alert.id == alert_id).first()
if alert:
alert.is_active = False
alert.resolved_at = datetime.now()
session.commit()
return True
return False
except Exception as e:
logger.error(f"解决预警失败: {e}")
return False
def get_category_performance(self, days: int = 30) -> Dict[str, Any]:
"""获取类别性能分析"""
try:
with db_manager.get_session() as session:
end_date = datetime.now()
start_date = end_date - timedelta(days=days)
work_orders = session.query(WorkOrder).filter(
WorkOrder.created_at >= start_date,
WorkOrder.created_at <= end_date
).all()
category_stats = defaultdict(lambda: {
"total": 0,
"resolved": 0,
"satisfaction_scores": [],
"resolution_times": []
})
for wo in work_orders:
category_stats[wo.category]["total"] += 1
if wo.status == "resolved":
category_stats[wo.category]["resolved"] += 1
if wo.satisfaction_score:
category_stats[wo.category]["satisfaction_scores"].append(wo.satisfaction_score)
if wo.status == "resolved" and wo.updated_at:
resolution_time = (wo.updated_at - wo.created_at).total_seconds() / 3600
category_stats[wo.category]["resolution_times"].append(resolution_time)
# 计算性能指标
performance = {}
for category, stats in category_stats.items():
resolution_rate = stats["resolved"] / stats["total"] if stats["total"] > 0 else 0
avg_satisfaction = sum(stats["satisfaction_scores"]) / len(stats["satisfaction_scores"]) if stats["satisfaction_scores"] else 0
avg_resolution_time = sum(stats["resolution_times"]) / len(stats["resolution_times"]) if stats["resolution_times"] else 0
performance[category] = {
"total_orders": stats["total"],
"resolution_rate": round(resolution_rate, 2),
"avg_satisfaction": round(avg_satisfaction, 2),
"avg_resolution_time_hours": round(avg_resolution_time, 2)
}
return performance
except Exception as e:
logger.error(f"获取类别性能分析失败: {e}")
return {}

View File

@@ -0,0 +1,261 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
TSP助手监控服务
实时监控系统状态,执行预警检查
"""
import logging
import threading
import time
from typing import Dict, Any, List
from datetime import datetime, timedelta
from .alert_system import AlertSystem, AlertRule, AlertLevel, AlertType
logger = logging.getLogger(__name__)
class MonitorService:
"""监控服务"""
def __init__(self):
self.alert_system = AlertSystem()
self.is_running = False
self.monitor_thread = None
self.check_interval = 60 # 检查间隔(秒)
def start(self):
"""启动监控服务"""
if self.is_running:
logger.warning("监控服务已在运行")
return
self.is_running = True
self.monitor_thread = threading.Thread(target=self._monitor_loop, daemon=True)
self.monitor_thread.start()
logger.info("监控服务已启动")
def stop(self):
"""停止监控服务"""
self.is_running = False
if self.monitor_thread:
self.monitor_thread.join(timeout=5)
logger.info("监控服务已停止")
def _monitor_loop(self):
"""监控循环"""
while self.is_running:
try:
# 执行预警检查
triggered_alerts = self.alert_system.check_all_rules()
if triggered_alerts:
logger.info(f"触发 {len(triggered_alerts)} 个预警")
for alert in triggered_alerts:
self._handle_alert(alert)
# 等待下次检查
time.sleep(self.check_interval)
except Exception as e:
logger.error(f"监控循环异常: {e}")
time.sleep(10) # 异常时等待10秒再继续
def _handle_alert(self, alert: Dict[str, Any]):
"""处理预警"""
try:
# 记录预警
logger.warning(f"预警触发: {alert['message']}")
# 根据预警级别采取不同措施
if alert['level'] == 'critical':
self._handle_critical_alert(alert)
elif alert['level'] == 'error':
self._handle_error_alert(alert)
elif alert['level'] == 'warning':
self._handle_warning_alert(alert)
else:
self._handle_info_alert(alert)
except Exception as e:
logger.error(f"处理预警失败: {e}")
def _handle_critical_alert(self, alert: Dict[str, Any]):
"""处理严重预警"""
# 发送紧急通知
self._send_notification(alert, "紧急")
# 记录到日志
logger.critical(f"严重预警: {alert['message']}")
# 可以添加自动恢复措施
self._attempt_auto_recovery(alert)
def _handle_error_alert(self, alert: Dict[str, Any]):
"""处理错误预警"""
# 发送错误通知
self._send_notification(alert, "错误")
# 记录到日志
logger.error(f"错误预警: {alert['message']}")
def _handle_warning_alert(self, alert: Dict[str, Any]):
"""处理警告预警"""
# 发送警告通知
self._send_notification(alert, "警告")
# 记录到日志
logger.warning(f"警告预警: {alert['message']}")
def _handle_info_alert(self, alert: Dict[str, Any]):
"""处理信息预警"""
# 记录到日志
logger.info(f"信息预警: {alert['message']}")
def _send_notification(self, alert: Dict[str, Any], level: str):
"""发送通知"""
# 这里可以集成邮件、短信、钉钉等通知方式
notification = {
"level": level,
"message": alert['message'],
"timestamp": alert['timestamp'],
"rule_name": alert['rule_name']
}
# 记录通知
logger.info(f"发送通知: {notification}")
# TODO: 实现具体的通知发送逻辑
# 例如:发送邮件、短信、钉钉消息等
def _attempt_auto_recovery(self, alert: Dict[str, Any]):
"""尝试自动恢复"""
try:
rule_name = alert['rule_name']
if rule_name == "内存使用预警":
# 尝试清理内存
self._cleanup_memory()
elif rule_name == "错误率预警":
# 尝试重启相关服务
self._restart_services()
elif rule_name == "响应时间预警":
# 尝试优化性能
self._optimize_performance()
except Exception as e:
logger.error(f"自动恢复失败: {e}")
def _cleanup_memory(self):
"""清理内存"""
try:
import gc
gc.collect()
logger.info("执行内存清理")
except Exception as e:
logger.error(f"内存清理失败: {e}")
def _restart_services(self):
"""重启服务"""
try:
# 这里可以实现重启相关服务的逻辑
logger.info("尝试重启服务")
except Exception as e:
logger.error(f"重启服务失败: {e}")
def _optimize_performance(self):
"""优化性能"""
try:
# 这里可以实现性能优化的逻辑
logger.info("尝试优化性能")
except Exception as e:
logger.error(f"性能优化失败: {e}")
def get_system_health(self) -> Dict[str, Any]:
"""获取系统健康状态"""
try:
# 获取活跃预警
active_alerts = self.alert_system.get_active_alerts()
# 获取预警统计
alert_stats = self.alert_system.get_alert_statistics()
# 计算健康分数
health_score = self._calculate_health_score(active_alerts, alert_stats)
return {
"health_score": health_score,
"status": self._get_health_status(health_score),
"active_alerts": len(active_alerts),
"alert_statistics": alert_stats,
"monitor_status": "running" if self.is_running else "stopped",
"last_check": datetime.now().isoformat()
}
except Exception as e:
logger.error(f"获取系统健康状态失败: {e}")
return {"error": str(e)}
def _calculate_health_score(self, active_alerts: List[Dict[str, Any]], alert_stats: Dict[str, Any]) -> float:
"""计算健康分数"""
try:
base_score = 100.0
# 根据活跃预警扣分
for alert in active_alerts:
if alert['level'] == 'critical':
base_score -= 20
elif alert['level'] == 'error':
base_score -= 10
elif alert['level'] == 'warning':
base_score -= 5
else:
base_score -= 1
# 确保分数在0-100之间
return max(0, min(100, base_score))
except Exception as e:
logger.error(f"计算健康分数失败: {e}")
return 50.0
def _get_health_status(self, health_score: float) -> str:
"""获取健康状态"""
if health_score >= 90:
return "excellent"
elif health_score >= 70:
return "good"
elif health_score >= 50:
return "fair"
elif health_score >= 30:
return "poor"
else:
return "critical"
def add_custom_rule(self, rule: AlertRule) -> bool:
"""添加自定义规则"""
return self.alert_system.add_custom_rule(rule)
def update_rule(self, rule_name: str, **kwargs) -> bool:
"""更新规则"""
return self.alert_system.update_rule(rule_name, **kwargs)
def delete_rule(self, rule_name: str) -> bool:
"""删除规则"""
return self.alert_system.delete_rule(rule_name)
def get_rules(self) -> Dict[str, Any]:
"""获取所有规则"""
return {
name: {
"name": rule.name,
"description": rule.description,
"alert_type": rule.alert_type.value,
"level": rule.level.value,
"threshold": rule.threshold,
"enabled": rule.enabled,
"check_interval": rule.check_interval,
"cooldown": rule.cooldown
}
for name, rule in self.alert_system.rules.items()
}

1
src/config/__init__.py Normal file
View File

@@ -0,0 +1 @@
# 配置模块

Binary file not shown.

Binary file not shown.

54
src/config/config.py Normal file
View File

@@ -0,0 +1,54 @@
import os
from typing import Dict, Any
class Config:
"""系统配置类"""
# 阿里云千问API配置
ALIBABA_API_KEY = "sk-c0dbefa1718d46eaa897199135066f00"
ALIBABA_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1"
ALIBABA_MODEL_NAME = "qwen-plus-latest"
# 数据库配置
DATABASE_URL = "mysql+pymysql://root:123456@localhost/tsp_assistant?charset=utf8mb4"
# 知识库配置
KNOWLEDGE_BASE_PATH = "data/knowledge_base"
VECTOR_DB_PATH = "data/vector_db"
# 对话配置
MAX_HISTORY_LENGTH = 10
RESPONSE_TIMEOUT = 30
# 分析配置
ANALYTICS_UPDATE_INTERVAL = 3600 # 1小时
ALERT_THRESHOLD = 0.8 # 预警阈值
# 日志配置
LOG_LEVEL = "INFO"
LOG_FILE = "logs/tsp_assistant.log"
@classmethod
def get_api_config(cls) -> Dict[str, Any]:
"""获取API配置"""
return {
"api_key": cls.ALIBABA_API_KEY,
"base_url": cls.ALIBABA_BASE_URL,
"model_name": cls.ALIBABA_MODEL_NAME
}
@classmethod
def get_database_config(cls) -> Dict[str, Any]:
"""获取数据库配置"""
return {
"url": cls.DATABASE_URL,
"echo": False
}
@classmethod
def get_knowledge_config(cls) -> Dict[str, Any]:
"""获取知识库配置"""
return {
"base_path": cls.KNOWLEDGE_BASE_PATH,
"vector_db_path": cls.VECTOR_DB_PATH
}

1
src/core/__init__.py Normal file
View File

@@ -0,0 +1 @@
# 核心模块

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

94
src/core/database.py Normal file
View File

@@ -0,0 +1,94 @@
from sqlalchemy import create_engine, text
from sqlalchemy.orm import sessionmaker, Session
from sqlalchemy.pool import StaticPool
from contextlib import contextmanager
from typing import Generator
import logging
from .models import Base
from ..config.config import Config
logger = logging.getLogger(__name__)
class DatabaseManager:
"""数据库管理器"""
def __init__(self):
self.engine = None
self.SessionLocal = None
self._initialize_database()
def _initialize_database(self):
"""初始化数据库连接"""
try:
db_config = Config.get_database_config()
# 根据数据库类型选择不同的连接参数
if "mysql" in db_config["url"]:
# MySQL配置
self.engine = create_engine(
db_config["url"],
echo=db_config["echo"],
pool_size=10,
max_overflow=20,
pool_pre_ping=True,
pool_recycle=3600
)
else:
# SQLite配置
self.engine = create_engine(
db_config["url"],
echo=db_config["echo"],
poolclass=StaticPool,
connect_args={"check_same_thread": False}
)
self.SessionLocal = sessionmaker(
autocommit=False,
autoflush=False,
bind=self.engine
)
# 创建所有表
Base.metadata.create_all(bind=self.engine)
logger.info("数据库初始化成功")
except Exception as e:
logger.error(f"数据库初始化失败: {e}")
raise
@contextmanager
def get_session(self) -> Generator[Session, None, None]:
"""获取数据库会话的上下文管理器"""
session = self.SessionLocal()
try:
yield session
session.commit()
except Exception as e:
session.rollback()
logger.error(f"数据库操作失败: {e}")
raise
finally:
session.close()
def get_session_direct(self) -> Session:
"""直接获取数据库会话"""
return self.SessionLocal()
def close_session(self, session: Session):
"""关闭数据库会话"""
if session:
session.close()
def test_connection(self) -> bool:
"""测试数据库连接"""
try:
with self.get_session() as session:
session.execute(text("SELECT 1"))
return True
except Exception as e:
logger.error(f"数据库连接测试失败: {e}")
return False
# 全局数据库管理器实例
db_manager = DatabaseManager()

149
src/core/llm_client.py Normal file
View File

@@ -0,0 +1,149 @@
import requests
import json
import logging
from typing import Dict, List, Optional, Any
from datetime import datetime
from ..config.config import Config
logger = logging.getLogger(__name__)
class QwenClient:
"""阿里云千问API客户端"""
def __init__(self):
self.api_config = Config.get_api_config()
self.base_url = self.api_config["base_url"]
self.api_key = self.api_config["api_key"]
self.model_name = self.api_config["model_name"]
self.headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
def chat_completion(
self,
messages: List[Dict[str, str]],
temperature: float = 0.7,
max_tokens: int = 1000,
stream: bool = False
) -> Dict[str, Any]:
"""发送聊天请求"""
try:
url = f"{self.base_url}/chat/completions"
payload = {
"model": self.model_name,
"messages": messages,
"temperature": temperature,
"max_tokens": max_tokens,
"stream": stream
}
response = requests.post(
url,
headers=self.headers,
json=payload,
timeout=Config.RESPONSE_TIMEOUT
)
if response.status_code == 200:
result = response.json()
logger.info("API请求成功")
return result
else:
logger.error(f"API请求失败: {response.status_code} - {response.text}")
return {"error": f"API请求失败: {response.status_code}"}
except requests.exceptions.Timeout:
logger.error("API请求超时")
return {"error": "请求超时"}
except requests.exceptions.RequestException as e:
logger.error(f"API请求异常: {e}")
return {"error": f"请求异常: {str(e)}"}
except Exception as e:
logger.error(f"未知错误: {e}")
return {"error": f"未知错误: {str(e)}"}
def generate_response(
self,
user_message: str,
context: Optional[str] = None,
knowledge_base: Optional[List[str]] = None
) -> Dict[str, Any]:
"""生成回复"""
messages = []
# 系统提示词
system_prompt = "你是一个专业的客服助手,请根据用户问题提供准确、 helpful的回复。"
if context:
system_prompt += f"\n\n上下文信息: {context}"
if knowledge_base:
system_prompt += f"\n\n相关知识库: {' '.join(knowledge_base)}"
messages.append({"role": "system", "content": system_prompt})
messages.append({"role": "user", "content": user_message})
result = self.chat_completion(messages)
if "error" in result:
return result
try:
response_content = result["choices"][0]["message"]["content"]
return {
"response": response_content,
"usage": result.get("usage", {}),
"model": result.get("model", ""),
"timestamp": datetime.now().isoformat()
}
except (KeyError, IndexError) as e:
logger.error(f"解析API响应失败: {e}")
return {"error": f"解析响应失败: {str(e)}"}
def extract_entities(self, text: str) -> Dict[str, Any]:
"""提取文本中的实体信息"""
prompt = f"""
请从以下文本中提取关键信息,包括:
1. 问题类型/类别
2. 优先级(高/中/低)
3. 关键词
4. 情感倾向(正面/负面/中性)
文本: {text}
请以JSON格式返回结果。
"""
messages = [
{"role": "system", "content": "你是一个信息提取专家,请准确提取文本中的关键信息。"},
{"role": "user", "content": prompt}
]
result = self.chat_completion(messages, temperature=0.3)
if "error" in result:
return result
try:
response_content = result["choices"][0]["message"]["content"]
# 尝试解析JSON
import re
json_match = re.search(r'\{.*\}', response_content, re.DOTALL)
if json_match:
return json.loads(json_match.group())
else:
return {"raw_response": response_content}
except Exception as e:
logger.error(f"解析实体提取结果失败: {e}")
return {"error": f"解析失败: {str(e)}"}
def test_connection(self) -> bool:
"""测试API连接"""
try:
result = self.chat_completion([
{"role": "user", "content": "你好"}
], max_tokens=10)
return "error" not in result
except Exception as e:
logger.error(f"API连接测试失败: {e}")
return False

103
src/core/models.py Normal file
View File

@@ -0,0 +1,103 @@
from sqlalchemy import Column, Integer, String, Text, DateTime, Float, Boolean, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from datetime import datetime
Base = declarative_base()
class WorkOrder(Base):
"""工单模型"""
__tablename__ = "work_orders"
id = Column(Integer, primary_key=True)
order_id = Column(String(50), unique=True, nullable=False)
title = Column(String(200), nullable=False)
description = Column(Text, nullable=False)
category = Column(String(100), nullable=False)
priority = Column(String(20), nullable=False)
status = Column(String(20), nullable=False)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
resolution = Column(Text)
satisfaction_score = Column(Float)
# 关联对话记录
conversations = relationship("Conversation", back_populates="work_order")
class Conversation(Base):
"""对话记录模型"""
__tablename__ = "conversations"
id = Column(Integer, primary_key=True)
work_order_id = Column(Integer, ForeignKey("work_orders.id"))
user_message = Column(Text, nullable=False)
assistant_response = Column(Text, nullable=False)
timestamp = Column(DateTime, default=datetime.now)
confidence_score = Column(Float)
knowledge_used = Column(Text) # 使用的知识库条目
response_time = Column(Float) # 响应时间(秒)
work_order = relationship("WorkOrder", back_populates="conversations")
class KnowledgeEntry(Base):
"""知识库条目模型"""
__tablename__ = "knowledge_entries"
id = Column(Integer, primary_key=True)
question = Column(Text, nullable=False)
answer = Column(Text, nullable=False)
category = Column(String(100), nullable=False)
confidence_score = Column(Float, default=0.0)
usage_count = Column(Integer, default=0)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
is_active = Column(Boolean, default=True)
is_verified = Column(Boolean, default=False) # 是否已验证
verified_by = Column(String(100)) # 验证人
verified_at = Column(DateTime) # 验证时间
vector_embedding = Column(Text) # 向量嵌入的JSON字符串
class VehicleData(Base):
"""车辆实时数据模型"""
__tablename__ = "vehicle_data"
id = Column(Integer, primary_key=True)
vehicle_id = Column(String(50), nullable=False) # 车辆ID
vehicle_vin = Column(String(17)) # 车架号
data_type = Column(String(50), nullable=False) # 数据类型(位置、状态、故障等)
data_value = Column(Text, nullable=False) # 数据值JSON格式
timestamp = Column(DateTime, default=datetime.now) # 数据时间戳
is_active = Column(Boolean, default=True) # 是否有效
# 索引
__table_args__ = (
{'extend_existing': True}
)
class Analytics(Base):
"""分析统计模型"""
__tablename__ = "analytics"
id = Column(Integer, primary_key=True)
date = Column(DateTime, nullable=False)
total_orders = Column(Integer, default=0)
resolved_orders = Column(Integer, default=0)
avg_resolution_time = Column(Float, default=0.0)
satisfaction_avg = Column(Float, default=0.0)
knowledge_hit_rate = Column(Float, default=0.0)
category_distribution = Column(Text) # JSON格式的类别分布
created_at = Column(DateTime, default=datetime.now)
class Alert(Base):
"""预警模型"""
__tablename__ = "alerts"
id = Column(Integer, primary_key=True)
rule_name = Column(String(100), nullable=False)
alert_type = Column(String(50), nullable=False)
level = Column(String(20), nullable=False) # info, warning, error, critical
message = Column(Text, nullable=False)
data = Column(Text) # JSON格式的预警数据
is_active = Column(Boolean, default=True)
created_at = Column(DateTime, default=datetime.now)
resolved_at = Column(DateTime)

1
src/dialogue/__init__.py Normal file
View File

@@ -0,0 +1 @@
# 对话模块

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,276 @@
import logging
from typing import Dict, List, Optional, Any
from datetime import datetime
import json
from ..core.database import db_manager
from ..core.models import WorkOrder, Conversation
from ..core.llm_client import QwenClient
from ..knowledge_base.knowledge_manager import KnowledgeManager
from ..vehicle.vehicle_data_manager import VehicleDataManager
logger = logging.getLogger(__name__)
class DialogueManager:
"""对话管理器"""
def __init__(self):
self.llm_client = QwenClient()
self.knowledge_manager = KnowledgeManager()
self.vehicle_manager = VehicleDataManager()
self.conversation_history = {} # 存储对话历史
def process_user_message(
self,
user_message: str,
work_order_id: Optional[int] = None,
user_id: Optional[str] = None,
vehicle_id: Optional[str] = None
) -> Dict[str, Any]:
"""处理用户消息"""
try:
# 搜索相关知识库(只搜索已验证的)
knowledge_results = self.knowledge_manager.search_knowledge(
user_message, top_k=3, verified_only=True
)
# 获取车辆实时数据
vehicle_data = None
if vehicle_id:
vehicle_data = self.vehicle_manager.get_latest_vehicle_data(vehicle_id)
# 构建上下文
context = self._build_context(work_order_id, user_id)
# 准备知识库信息
knowledge_context = ""
if knowledge_results:
knowledge_context = "相关知识库信息:\n"
for i, result in enumerate(knowledge_results[:2], 1):
knowledge_context += f"{i}. 问题: {result['question']}\n"
knowledge_context += f" 答案: {result['answer']}\n"
knowledge_context += f" 置信度: {result['confidence_score']:.2f}\n\n"
# 准备车辆数据信息
vehicle_context = ""
if vehicle_data:
vehicle_context = "车辆实时数据:\n"
for data_type, data_info in vehicle_data.items():
vehicle_context += f"- {data_type}: {json.dumps(data_info['value'], ensure_ascii=False)}\n"
vehicle_context += f" 更新时间: {data_info['timestamp']}\n"
vehicle_context += "\n"
# 生成回复
response_result = self.llm_client.generate_response(
user_message=user_message,
context=context,
knowledge_base=[knowledge_context] if knowledge_context else None,
vehicle_data=[vehicle_context] if vehicle_context else None
)
if "error" in response_result:
return response_result
# 保存对话记录
conversation_id = self._save_conversation(
work_order_id=work_order_id,
user_message=user_message,
assistant_response=response_result["response"],
knowledge_used=json.dumps([r["id"] for r in knowledge_results], ensure_ascii=False)
)
# 更新对话历史
if user_id:
if user_id not in self.conversation_history:
self.conversation_history[user_id] = []
self.conversation_history[user_id].append({
"role": "user",
"content": user_message,
"timestamp": datetime.now().isoformat()
})
self.conversation_history[user_id].append({
"role": "assistant",
"content": response_result["response"],
"timestamp": datetime.now().isoformat()
})
# 保持历史记录在限制范围内
if len(self.conversation_history[user_id]) > 20: # 10轮对话
self.conversation_history[user_id] = self.conversation_history[user_id][-20:]
return {
"response": response_result["response"],
"conversation_id": conversation_id,
"knowledge_used": knowledge_results,
"confidence_score": self._calculate_confidence(knowledge_results),
"timestamp": datetime.now().isoformat()
}
except Exception as e:
logger.error(f"处理用户消息失败: {e}")
return {"error": f"处理失败: {str(e)}"}
def _build_context(self, work_order_id: Optional[int], user_id: Optional[str]) -> str:
"""构建对话上下文"""
context_parts = []
# 添加工单信息
if work_order_id:
try:
with db_manager.get_session() as session:
work_order = session.query(WorkOrder).filter(
WorkOrder.id == work_order_id
).first()
if work_order:
context_parts.append(f"当前工单信息:")
context_parts.append(f"工单号: {work_order.order_id}")
context_parts.append(f"标题: {work_order.title}")
context_parts.append(f"描述: {work_order.description}")
context_parts.append(f"类别: {work_order.category}")
context_parts.append(f"优先级: {work_order.priority}")
context_parts.append(f"状态: {work_order.status}")
except Exception as e:
logger.error(f"获取工单信息失败: {e}")
# 添加用户历史对话
if user_id and user_id in self.conversation_history:
recent_history = self.conversation_history[user_id][-6:] # 最近3轮对话
if recent_history:
context_parts.append("最近的对话历史:")
for msg in recent_history:
role = "用户" if msg["role"] == "user" else "助手"
context_parts.append(f"{role}: {msg['content']}")
return "\n".join(context_parts) if context_parts else ""
def _save_conversation(
self,
work_order_id: Optional[int],
user_message: str,
assistant_response: str,
knowledge_used: str
) -> int:
"""保存对话记录"""
try:
with db_manager.get_session() as session:
conversation = Conversation(
work_order_id=work_order_id,
user_message=user_message,
assistant_response=assistant_response,
knowledge_used=knowledge_used,
timestamp=datetime.now()
)
session.add(conversation)
session.commit()
return conversation.id
except Exception as e:
logger.error(f"保存对话记录失败: {e}")
return 0
def _calculate_confidence(self, knowledge_results: List[Dict[str, Any]]) -> float:
"""计算回复置信度"""
if not knowledge_results:
return 0.5 # 默认置信度
# 基于知识库匹配度和置信度计算
max_similarity = max(result.get("similarity_score", 0) for result in knowledge_results)
avg_confidence = sum(result.get("confidence_score", 0) for result in knowledge_results) / len(knowledge_results)
# 综合评分
confidence = (max_similarity * 0.6 + avg_confidence * 0.4)
return min(confidence, 1.0)
def create_work_order(
self,
title: str,
description: str,
category: str,
priority: str = "medium"
) -> Dict[str, Any]:
"""创建工单"""
try:
with db_manager.get_session() as session:
work_order = WorkOrder(
order_id=f"WO{datetime.now().strftime('%Y%m%d%H%M%S')}",
title=title,
description=description,
category=category,
priority=priority,
status="open",
created_at=datetime.now()
)
session.add(work_order)
session.commit()
logger.info(f"创建工单成功: {work_order.order_id}")
return {
"work_order_id": work_order.id,
"order_id": work_order.order_id,
"status": "success"
}
except Exception as e:
logger.error(f"创建工单失败: {e}")
return {"error": f"创建失败: {str(e)}"}
def update_work_order(
self,
work_order_id: int,
status: Optional[str] = None,
resolution: Optional[str] = None,
satisfaction_score: Optional[float] = None
) -> bool:
"""更新工单"""
try:
with db_manager.get_session() as session:
work_order = session.query(WorkOrder).filter(
WorkOrder.id == work_order_id
).first()
if not work_order:
return False
if status:
work_order.status = status
if resolution:
work_order.resolution = resolution
if satisfaction_score is not None:
work_order.satisfaction_score = satisfaction_score
work_order.updated_at = datetime.now()
session.commit()
# 如果工单已解决,学习知识
if status == "resolved" and resolution:
self.knowledge_manager.learn_from_work_order(work_order_id)
logger.info(f"更新工单成功: {work_order_id}")
return True
except Exception as e:
logger.error(f"更新工单失败: {e}")
return False
def get_conversation_history(self, work_order_id: int) -> List[Dict[str, Any]]:
"""获取工单对话历史"""
try:
with db_manager.get_session() as session:
conversations = session.query(Conversation).filter(
Conversation.work_order_id == work_order_id
).order_by(Conversation.timestamp).all()
return [
{
"id": conv.id,
"user_message": conv.user_message,
"assistant_response": conv.assistant_response,
"timestamp": conv.timestamp.isoformat(),
"confidence_score": conv.confidence_score
}
for conv in conversations
]
except Exception as e:
logger.error(f"获取对话历史失败: {e}")
return []

View File

@@ -0,0 +1,377 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
实时对话管理器
提供实时对话功能集成知识库搜索和LLM回复
"""
import logging
import json
import time
from typing import Dict, List, Any, Optional
from datetime import datetime
from dataclasses import dataclass
from ..core.llm_client import QwenClient
from ..knowledge_base.knowledge_manager import KnowledgeManager
from ..core.database import db_manager
from ..core.models import Conversation, WorkOrder
logger = logging.getLogger(__name__)
@dataclass
class ChatMessage:
"""聊天消息"""
role: str # user, assistant, system
content: str
timestamp: datetime
message_id: str
work_order_id: Optional[int] = None
knowledge_used: Optional[List[Dict]] = None
confidence_score: Optional[float] = None
class RealtimeChatManager:
"""实时对话管理器"""
def __init__(self):
self.llm_client = QwenClient()
self.knowledge_manager = KnowledgeManager()
self.active_sessions = {} # 存储活跃的对话会话
self.message_history = {} # 存储消息历史
def create_session(self, user_id: str, work_order_id: Optional[int] = None) -> str:
"""创建新的对话会话"""
session_id = f"session_{user_id}_{int(time.time())}"
session_data = {
"user_id": user_id,
"work_order_id": work_order_id,
"created_at": datetime.now(),
"last_activity": datetime.now(),
"message_count": 0,
"context": []
}
self.active_sessions[session_id] = session_data
self.message_history[session_id] = []
logger.info(f"创建新会话: {session_id}")
return session_id
def process_message(self, session_id: str, user_message: str) -> Dict[str, Any]:
"""处理用户消息"""
try:
if session_id not in self.active_sessions:
return {"error": "会话不存在"}
session = self.active_sessions[session_id]
session["last_activity"] = datetime.now()
session["message_count"] += 1
# 创建用户消息
user_msg = ChatMessage(
role="user",
content=user_message,
timestamp=datetime.now(),
message_id=f"msg_{int(time.time())}_{session['message_count']}"
)
# 添加到消息历史
self.message_history[session_id].append(user_msg)
# 搜索相关知识
knowledge_results = self._search_knowledge(user_message)
# 生成回复
assistant_response = self._generate_response(
user_message,
knowledge_results,
session["context"]
)
# 创建助手消息
assistant_msg = ChatMessage(
role="assistant",
content=assistant_response["content"],
timestamp=datetime.now(),
message_id=f"msg_{int(time.time())}_{session['message_count'] + 1}",
work_order_id=session["work_order_id"],
knowledge_used=knowledge_results,
confidence_score=assistant_response.get("confidence", 0.5)
)
# 添加到消息历史
self.message_history[session_id].append(assistant_msg)
# 更新上下文
session["context"].append({
"role": "user",
"content": user_message
})
session["context"].append({
"role": "assistant",
"content": assistant_response["content"]
})
# 保持上下文长度
if len(session["context"]) > 20: # 保留最近10轮对话
session["context"] = session["context"][-20:]
# 保存到数据库
self._save_conversation(session_id, user_msg, assistant_msg)
return {
"success": True,
"response": assistant_response["content"], # 修改为response字段
"message_id": assistant_msg.message_id,
"content": assistant_response["content"], # 保留content字段以兼容
"knowledge_used": knowledge_results,
"confidence_score": assistant_response.get("confidence", 0.5),
"work_order_id": session["work_order_id"],
"timestamp": assistant_msg.timestamp.isoformat()
}
except Exception as e:
logger.error(f"处理消息失败: {e}")
return {"error": f"处理消息失败: {str(e)}"}
def _search_knowledge(self, query: str, top_k: int = 3) -> List[Dict[str, Any]]:
"""搜索相关知识"""
try:
results = self.knowledge_manager.search_knowledge(query, top_k)
return results
except Exception as e:
logger.error(f"搜索知识库失败: {e}")
return []
def _generate_response(self, user_message: str, knowledge_results: List[Dict], context: List[Dict]) -> Dict[str, Any]:
"""生成回复"""
try:
# 构建提示词
prompt = self._build_chat_prompt(user_message, knowledge_results, context)
# 调用大模型
response = self.llm_client.chat_completion(
messages=[{"role": "user", "content": prompt}],
temperature=0.7,
max_tokens=1000
)
if response and 'choices' in response:
content = response['choices'][0]['message']['content']
confidence = self._calculate_confidence(knowledge_results, content)
return {
"content": content,
"confidence": confidence
}
else:
return {
"content": "抱歉,我暂时无法处理您的问题。请稍后再试或联系人工客服。",
"confidence": 0.1
}
except Exception as e:
logger.error(f"生成回复失败: {e}")
return {
"content": "抱歉,系统出现错误,请稍后再试。",
"confidence": 0.1
}
def _build_chat_prompt(self, user_message: str, knowledge_results: List[Dict], context: List[Dict]) -> str:
"""构建聊天提示词"""
prompt = f"""
你是一个专业的奇瑞汽车客服助手。请根据用户的问题和提供的知识库信息,给出专业、友好的回复。
用户问题:{user_message}
相关知识库信息:
"""
if knowledge_results:
for i, result in enumerate(knowledge_results, 1):
prompt += f"\n{i}. 问题:{result.get('question', '')}"
prompt += f"\n 答案:{result.get('answer', '')}"
prompt += f"\n 相似度:{result.get('similarity_score', 0):.3f}\n"
else:
prompt += "\n未找到相关知识库信息。\n"
# 添加上下文
if context:
prompt += "\n对话历史:\n"
for msg in context[-6:]: # 最近3轮对话
prompt += f"{msg['role']}: {msg['content']}\n"
prompt += """
请按照以下要求回复:
1. 语言要专业、友好、易懂
2. 如果知识库中有相关信息,优先使用知识库内容
3. 如果没有相关知识,请提供一般性建议
4. 如果问题需要进站处理,请明确说明
5. 回复要简洁明了,避免冗长
6. 如果涉及技术问题,要提供具体的操作步骤
7. 始终以"您好"开头,以"如有其他问题,请随时联系"结尾
请直接给出回复内容,不要包含其他格式:
"""
return prompt
def _calculate_confidence(self, knowledge_results: List[Dict], response_content: str) -> float:
"""计算回复置信度"""
if not knowledge_results:
return 0.3
# 基于知识库结果计算基础置信度
max_similarity = max([r.get('similarity_score', 0) for r in knowledge_results])
base_confidence = min(max_similarity * 1.2, 0.9) # 最高0.9
# 根据回复长度调整
if len(response_content) < 50:
base_confidence *= 0.8
elif len(response_content) > 500:
base_confidence *= 0.9
return base_confidence
def _save_conversation(self, session_id: str, user_msg: ChatMessage, assistant_msg: ChatMessage):
"""保存对话到数据库"""
try:
with db_manager.get_session() as session:
# 保存用户消息
user_conversation = Conversation(
work_order_id=user_msg.work_order_id,
user_message=user_msg.content,
assistant_response="", # 用户消息没有助手回复
timestamp=user_msg.timestamp,
confidence_score=None,
knowledge_used=None,
response_time=None
)
session.add(user_conversation)
# 保存助手消息
assistant_conversation = Conversation(
work_order_id=assistant_msg.work_order_id,
user_message="", # 助手消息没有用户输入
assistant_response=assistant_msg.content,
timestamp=assistant_msg.timestamp,
confidence_score=assistant_msg.confidence_score,
knowledge_used=json.dumps(assistant_msg.knowledge_used, ensure_ascii=False) if assistant_msg.knowledge_used else None,
response_time=0.5 # 模拟响应时间
)
session.add(assistant_conversation)
session.commit()
except Exception as e:
logger.error(f"保存对话失败: {e}")
def get_session_history(self, session_id: str) -> List[Dict[str, Any]]:
"""获取会话历史"""
if session_id not in self.message_history:
return []
history = []
for msg in self.message_history[session_id]:
history.append({
"role": msg.role,
"content": msg.content,
"timestamp": msg.timestamp.isoformat(),
"message_id": msg.message_id,
"knowledge_used": msg.knowledge_used,
"confidence_score": msg.confidence_score
})
return history
def create_work_order(self, session_id: str, title: str, description: str, category: str, priority: str = "medium") -> Dict[str, Any]:
"""创建工单"""
try:
if session_id not in self.active_sessions:
return {"error": "会话不存在"}
session = self.active_sessions[session_id]
with db_manager.get_session() as db_session:
work_order = WorkOrder(
order_id=f"WO_{int(time.time())}",
title=title,
description=description,
category=category,
priority=priority,
status="open",
created_at=datetime.now()
)
db_session.add(work_order)
db_session.commit()
# 更新会话的工单ID
session["work_order_id"] = work_order.id
return {
"success": True,
"work_order_id": work_order.id,
"order_id": work_order.order_id,
"message": "工单创建成功"
}
except Exception as e:
logger.error(f"创建工单失败: {e}")
return {"error": f"创建工单失败: {str(e)}"}
def get_work_order_status(self, work_order_id: int) -> Dict[str, Any]:
"""获取工单状态"""
try:
with db_manager.get_session() as session:
work_order = session.query(WorkOrder).filter(WorkOrder.id == work_order_id).first()
if not work_order:
return {"error": "工单不存在"}
return {
"work_order_id": work_order.id,
"order_id": work_order.order_id,
"title": work_order.title,
"status": work_order.status,
"priority": work_order.priority,
"created_at": work_order.created_at.isoformat(),
"updated_at": work_order.updated_at.isoformat() if work_order.updated_at else None,
"resolution": work_order.resolution,
"satisfaction_score": work_order.satisfaction_score
}
except Exception as e:
logger.error(f"获取工单状态失败: {e}")
return {"error": f"获取工单状态失败: {str(e)}"}
def end_session(self, session_id: str) -> bool:
"""结束会话"""
try:
if session_id in self.active_sessions:
del self.active_sessions[session_id]
if session_id in self.message_history:
del self.message_history[session_id]
logger.info(f"结束会话: {session_id}")
return True
except Exception as e:
logger.error(f"结束会话失败: {e}")
return False
def get_active_sessions(self) -> List[Dict[str, Any]]:
"""获取活跃会话列表"""
sessions = []
for session_id, session_data in self.active_sessions.items():
sessions.append({
"session_id": session_id,
"user_id": session_data["user_id"],
"work_order_id": session_data["work_order_id"],
"created_at": session_data["created_at"].isoformat(),
"last_activity": session_data["last_activity"].isoformat(),
"message_count": session_data["message_count"]
})
return sessions

View File

@@ -0,0 +1 @@
# 知识库模块

View File

@@ -0,0 +1,380 @@
import json
import logging
from typing import List, Dict, Optional, Any
from datetime import datetime
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
from sqlalchemy import func
from ..core.database import db_manager
from ..core.models import KnowledgeEntry, WorkOrder, Conversation
from ..core.llm_client import QwenClient
logger = logging.getLogger(__name__)
class KnowledgeManager:
"""知识库管理器"""
def __init__(self):
self.llm_client = QwenClient()
self.vectorizer = TfidfVectorizer(
max_features=1000,
stop_words=None, # 不使用英文停用词,因为数据是中文
ngram_range=(1, 2)
)
self._load_vectorizer()
def _load_vectorizer(self):
"""加载向量化器"""
try:
with db_manager.get_session() as session:
entries = session.query(KnowledgeEntry).filter(
KnowledgeEntry.is_active == True
).all()
if entries:
texts = [entry.question + " " + entry.answer for entry in entries]
self.vectorizer.fit(texts)
logger.info(f"向量化器加载成功,包含 {len(entries)} 个条目")
except Exception as e:
logger.error(f"加载向量化器失败: {e}")
def learn_from_work_order(self, work_order_id: int) -> bool:
"""从工单中学习知识"""
try:
with db_manager.get_session() as session:
work_order = session.query(WorkOrder).filter(
WorkOrder.id == work_order_id
).first()
if not work_order or not work_order.resolution:
return False
# 提取问题和答案
question = work_order.title + " " + work_order.description
answer = work_order.resolution
# 检查是否已存在相似条目
existing_entry = self._find_similar_entry(question, session)
if existing_entry:
# 更新现有条目
existing_entry.answer = answer
existing_entry.usage_count += 1
existing_entry.updated_at = datetime.now()
if work_order.satisfaction_score:
existing_entry.confidence_score = work_order.satisfaction_score
else:
# 创建新条目
new_entry = KnowledgeEntry(
question=question,
answer=answer,
category=work_order.category,
confidence_score=work_order.satisfaction_score or 0.5,
usage_count=1
)
session.add(new_entry)
session.commit()
logger.info(f"从工单 {work_order_id} 学习知识成功")
return True
except Exception as e:
logger.error(f"从工单学习知识失败: {e}")
return False
def _find_similar_entry(self, question: str, session) -> Optional[KnowledgeEntry]:
"""查找相似的知识库条目"""
try:
entries = session.query(KnowledgeEntry).filter(
KnowledgeEntry.is_active == True
).all()
if not entries:
return None
# 计算相似度
texts = [entry.question for entry in entries]
question_vector = self.vectorizer.transform([question])
entry_vectors = self.vectorizer.transform(texts)
similarities = cosine_similarity(question_vector, entry_vectors)[0]
max_similarity_idx = np.argmax(similarities)
if similarities[max_similarity_idx] > 0.8: # 相似度阈值
return entries[max_similarity_idx]
return None
except Exception as e:
logger.error(f"查找相似条目失败: {e}")
return None
def search_knowledge(self, query: str, top_k: int = 3, verified_only: bool = True) -> List[Dict[str, Any]]:
"""搜索知识库"""
try:
with db_manager.get_session() as session:
# 构建查询条件
query_filter = session.query(KnowledgeEntry).filter(
KnowledgeEntry.is_active == True
)
# 如果只搜索已验证的知识库
if verified_only:
query_filter = query_filter.filter(KnowledgeEntry.is_verified == True)
entries = query_filter.all()
if not entries:
return []
# 计算相似度
texts = [entry.question + " " + entry.answer for entry in entries]
query_vector = self.vectorizer.transform([query])
entry_vectors = self.vectorizer.transform(texts)
similarities = cosine_similarity(query_vector, entry_vectors)[0]
# 获取top_k个最相似的条目
top_indices = np.argsort(similarities)[-top_k:][::-1]
results = []
for idx in top_indices:
if similarities[idx] > 0.1: # 最小相似度阈值
entry = entries[idx]
results.append({
"id": entry.id,
"question": entry.question,
"answer": entry.answer,
"category": entry.category,
"confidence_score": entry.confidence_score,
"similarity_score": float(similarities[idx]),
"usage_count": entry.usage_count,
"is_verified": entry.is_verified
})
return results
except Exception as e:
logger.error(f"搜索知识库失败: {e}")
return []
def add_knowledge_entry(
self,
question: str,
answer: str,
category: str,
confidence_score: float = 0.5,
is_verified: bool = False
) -> bool:
"""添加知识库条目"""
try:
with db_manager.get_session() as session:
entry = KnowledgeEntry(
question=question,
answer=answer,
category=category,
confidence_score=confidence_score,
usage_count=0,
is_verified=is_verified
)
session.add(entry)
session.commit()
# 重新训练向量化器
self._load_vectorizer()
logger.info(f"添加知识库条目成功: {question[:50]}...")
return True
except Exception as e:
logger.error(f"添加知识库条目失败: {e}")
return False
def update_knowledge_entry(
self,
entry_id: int,
question: str = None,
answer: str = None,
category: str = None,
confidence_score: float = None
) -> bool:
"""更新知识库条目"""
try:
with db_manager.get_session() as session:
entry = session.query(KnowledgeEntry).filter(
KnowledgeEntry.id == entry_id
).first()
if not entry:
return False
if question:
entry.question = question
if answer:
entry.answer = answer
if category:
entry.category = category
if confidence_score is not None:
entry.confidence_score = confidence_score
entry.updated_at = datetime.now()
session.commit()
logger.info(f"更新知识库条目成功: {entry_id}")
return True
except Exception as e:
logger.error(f"更新知识库条目失败: {e}")
return False
def get_knowledge_entries(self, page: int = 1, per_page: int = 10) -> Dict[str, Any]:
"""获取知识库条目(分页)"""
try:
with db_manager.get_session() as session:
# 计算偏移量
offset = (page - 1) * per_page
# 获取总数
total = session.query(KnowledgeEntry).filter(
KnowledgeEntry.is_active == True
).count()
# 获取分页数据
entries = session.query(KnowledgeEntry).filter(
KnowledgeEntry.is_active == True
).order_by(KnowledgeEntry.created_at.desc()).offset(offset).limit(per_page).all()
# 转换为字典格式
knowledge_list = []
for entry in entries:
knowledge_list.append({
"id": entry.id,
"question": entry.question,
"answer": entry.answer,
"category": entry.category,
"confidence_score": entry.confidence_score,
"usage_count": entry.usage_count,
"created_at": entry.created_at.isoformat() if entry.created_at else None,
"is_verified": getattr(entry, 'is_verified', False) # 添加验证状态
})
return {
"knowledge": knowledge_list,
"total": total,
"page": page,
"per_page": per_page,
"total_pages": (total + per_page - 1) // per_page
}
except Exception as e:
logger.error(f"获取知识库条目失败: {e}")
return {"knowledge": [], "total": 0, "page": 1, "per_page": per_page, "total_pages": 0}
def verify_knowledge_entry(self, entry_id: int, verified_by: str = "admin") -> bool:
"""验证知识库条目"""
try:
with db_manager.get_session() as session:
entry = session.query(KnowledgeEntry).filter(
KnowledgeEntry.id == entry_id
).first()
if not entry:
return False
entry.is_verified = True
entry.verified_by = verified_by
entry.verified_at = datetime.now()
session.commit()
logger.info(f"知识库条目验证成功: {entry_id}")
return True
except Exception as e:
logger.error(f"验证知识库条目失败: {e}")
return False
def unverify_knowledge_entry(self, entry_id: int) -> bool:
"""取消验证知识库条目"""
try:
with db_manager.get_session() as session:
entry = session.query(KnowledgeEntry).filter(
KnowledgeEntry.id == entry_id
).first()
if not entry:
return False
entry.is_verified = False
entry.verified_by = None
entry.verified_at = None
session.commit()
logger.info(f"知识库条目取消验证成功: {entry_id}")
return True
except Exception as e:
logger.error(f"取消验证知识库条目失败: {e}")
return False
def delete_knowledge_entry(self, entry_id: int) -> bool:
"""删除知识库条目(软删除)"""
try:
with db_manager.get_session() as session:
entry = session.query(KnowledgeEntry).filter(
KnowledgeEntry.id == entry_id
).first()
if not entry:
logger.warning(f"知识库条目不存在: {entry_id}")
return False
entry.is_active = False
session.commit()
# 重新训练向量化器(如果还有活跃条目)
try:
self._load_vectorizer()
except Exception as vectorizer_error:
logger.warning(f"重新加载向量化器失败: {vectorizer_error}")
# 即使向量化器加载失败,删除操作仍然成功
logger.info(f"删除知识库条目成功: {entry_id}")
return True
except Exception as e:
logger.error(f"删除知识库条目失败: {e}")
return False
def get_knowledge_stats(self) -> Dict[str, Any]:
"""获取知识库统计信息"""
try:
with db_manager.get_session() as session:
total_entries = session.query(KnowledgeEntry).count()
active_entries = session.query(KnowledgeEntry).filter(
KnowledgeEntry.is_active == True
).count()
# 按类别统计
category_stats = session.query(
KnowledgeEntry.category,
session.query(KnowledgeEntry).filter(
KnowledgeEntry.category == KnowledgeEntry.category
).count()
).group_by(KnowledgeEntry.category).all()
# 平均置信度
avg_confidence = session.query(
func.avg(KnowledgeEntry.confidence_score)
).scalar() or 0.0
return {
"total_entries": total_entries,
"active_entries": active_entries,
"category_distribution": dict(category_stats),
"average_confidence": float(avg_confidence)
}
except Exception as e:
logger.error(f"获取知识库统计失败: {e}")
return {}

316
src/main.py Normal file
View File

@@ -0,0 +1,316 @@
import logging
import sys
import os
from typing import Dict, Any, List
from datetime import datetime, timedelta
# 添加项目根目录到Python路径
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from src.config.config import Config
from src.utils.helpers import setup_logging
from src.core.database import db_manager
from src.core.llm_client import QwenClient
from src.knowledge_base.knowledge_manager import KnowledgeManager
from src.dialogue.dialogue_manager import DialogueManager
from src.analytics.analytics_manager import AnalyticsManager
from src.analytics.alert_system import AlertSystem
from src.analytics.monitor_service import MonitorService
class TSPAssistant:
"""TSP助手主类"""
def __init__(self):
# 设置日志
setup_logging(Config.LOG_LEVEL, Config.LOG_FILE)
self.logger = logging.getLogger(__name__)
# 初始化各个管理器
self.llm_client = QwenClient()
self.knowledge_manager = KnowledgeManager()
self.dialogue_manager = DialogueManager()
self.analytics_manager = AnalyticsManager()
self.alert_system = AlertSystem()
self.monitor_service = MonitorService()
self.logger.info("TSP助手初始化完成")
def test_system(self) -> Dict[str, Any]:
"""测试系统各个组件"""
results = {
"database": False,
"llm_api": False,
"knowledge_base": False,
"overall": False
}
try:
# 测试数据库连接
if db_manager.test_connection():
results["database"] = True
self.logger.info("数据库连接测试成功")
else:
self.logger.error("数据库连接测试失败")
# 测试LLM API连接
if self.llm_client.test_connection():
results["llm_api"] = True
self.logger.info("LLM API连接测试成功")
else:
self.logger.error("LLM API连接测试失败")
# 测试知识库
knowledge_stats = self.knowledge_manager.get_knowledge_stats()
if knowledge_stats:
results["knowledge_base"] = True
self.logger.info("知识库测试成功")
else:
self.logger.warning("知识库为空或测试失败")
# 整体状态
results["overall"] = all([results["database"], results["llm_api"]])
except Exception as e:
self.logger.error(f"系统测试失败: {e}")
return results
def process_message(self, message: str, user_id: str = None, work_order_id: int = None) -> Dict[str, Any]:
"""处理用户消息"""
try:
result = self.dialogue_manager.process_user_message(
user_message=message,
user_id=user_id,
work_order_id=work_order_id
)
if "error" in result:
self.logger.error(f"处理消息失败: {result['error']}")
return result
except Exception as e:
self.logger.error(f"处理消息异常: {e}")
return {"error": f"处理异常: {str(e)}"}
def create_work_order(self, title: str, description: str, category: str, priority: str = "medium") -> Dict[str, Any]:
"""创建工单"""
try:
result = self.dialogue_manager.create_work_order(
title=title,
description=description,
category=category,
priority=priority
)
if "error" in result:
self.logger.error(f"创建工单失败: {result['error']}")
return result
except Exception as e:
self.logger.error(f"创建工单异常: {e}")
return {"error": f"创建异常: {str(e)}"}
def update_work_order(self, work_order_id: int, **kwargs) -> bool:
"""更新工单"""
try:
return self.dialogue_manager.update_work_order(work_order_id, **kwargs)
except Exception as e:
self.logger.error(f"更新工单异常: {e}")
return False
def search_knowledge(self, query: str, top_k: int = 3) -> Dict[str, Any]:
"""搜索知识库"""
try:
results = self.knowledge_manager.search_knowledge(query, top_k)
return {
"results": results,
"count": len(results)
}
except Exception as e:
self.logger.error(f"搜索知识库异常: {e}")
return {"error": f"搜索异常: {str(e)}"}
def add_knowledge(self, question: str, answer: str, category: str, confidence_score: float = 0.5) -> bool:
"""添加知识库条目"""
try:
return self.knowledge_manager.add_knowledge_entry(question, answer, category, confidence_score)
except Exception as e:
self.logger.error(f"添加知识库异常: {e}")
return False
def generate_analytics(self, date_range: str = "today") -> Dict[str, Any]:
"""生成分析报告"""
try:
from src.utils.helpers import parse_date_range
start_date, end_date = parse_date_range(date_range)
# 生成每日分析
analytics_results = []
current_date = start_date
while current_date <= end_date:
result = self.analytics_manager.generate_daily_analytics(current_date)
if "error" not in result:
analytics_results.append(result)
current_date += timedelta(days=1)
# 获取汇总统计
summary = self.analytics_manager.get_analytics_summary((end_date - start_date).days + 1)
return {
"period": f"{start_date}{end_date}",
"daily_analytics": analytics_results,
"summary": summary
}
except Exception as e:
self.logger.error(f"生成分析报告异常: {e}")
return {"error": f"生成异常: {str(e)}"}
def get_alerts(self) -> Dict[str, Any]:
"""获取预警信息"""
try:
active_alerts = self.analytics_manager.get_active_alerts()
return {
"active_alerts": active_alerts,
"count": len(active_alerts)
}
except Exception as e:
self.logger.error(f"获取预警异常: {e}")
return {"error": f"获取异常: {str(e)}"}
def get_system_status(self) -> Dict[str, Any]:
"""获取系统状态"""
try:
from src.utils.helpers import get_memory_usage
# 系统测试
test_results = self.test_system()
# 知识库统计
knowledge_stats = self.knowledge_manager.get_knowledge_stats()
# 内存使用
memory_usage = get_memory_usage()
# 活跃预警
alerts = self.alert_system.get_active_alerts()
# 系统健康状态
health_status = self.monitor_service.get_system_health()
return {
"system_status": test_results,
"knowledge_base": knowledge_stats,
"memory_usage": memory_usage,
"active_alerts": len(alerts),
"health_score": health_status.get("health_score", 0),
"health_status": health_status.get("status", "unknown"),
"timestamp": datetime.now().isoformat()
}
except Exception as e:
self.logger.error(f"获取系统状态异常: {e}")
return {"error": f"获取状态异常: {str(e)}"}
def start_monitoring(self):
"""启动监控服务"""
try:
self.monitor_service.start()
self.logger.info("监控服务已启动")
return True
except Exception as e:
self.logger.error(f"启动监控服务失败: {e}")
return False
def stop_monitoring(self):
"""停止监控服务"""
try:
self.monitor_service.stop()
self.logger.info("监控服务已停止")
return True
except Exception as e:
self.logger.error(f"停止监控服务失败: {e}")
return False
def check_alerts(self) -> List[Dict[str, Any]]:
"""检查预警"""
try:
return self.alert_system.check_all_rules()
except Exception as e:
self.logger.error(f"检查预警失败: {e}")
return []
def get_active_alerts(self) -> List[Dict[str, Any]]:
"""获取活跃预警"""
try:
return self.alert_system.get_active_alerts()
except Exception as e:
self.logger.error(f"获取活跃预警失败: {e}")
return []
def resolve_alert(self, alert_id: int) -> bool:
"""解决预警"""
try:
return self.alert_system.resolve_alert(alert_id)
except Exception as e:
self.logger.error(f"解决预警失败: {e}")
return False
def get_alert_statistics(self) -> Dict[str, Any]:
"""获取预警统计"""
try:
return self.alert_system.get_alert_statistics()
except Exception as e:
self.logger.error(f"获取预警统计失败: {e}")
return {}
def get_system_health(self) -> Dict[str, Any]:
"""获取系统健康状态"""
try:
return self.monitor_service.get_system_health()
except Exception as e:
self.logger.error(f"获取系统健康状态失败: {e}")
return {"error": f"获取健康状态失败: {str(e)}"}
def main():
"""主函数"""
# 创建TSP助手实例
assistant = TSPAssistant()
# 测试系统
test_results = assistant.test_system()
print("系统测试结果:", test_results)
if not test_results["overall"]:
print("系统初始化失败,请检查配置")
return
print("TSP助手启动成功")
# 示例用法
# 创建工单
work_order = assistant.create_work_order(
title="测试工单",
description="这是一个测试工单",
category="技术问题",
priority="medium"
)
print("创建工单:", work_order)
# 处理消息
if "work_order_id" in work_order:
response = assistant.process_message(
message="我的账户无法登录",
work_order_id=work_order["work_order_id"]
)
print("处理消息:", response)
# 获取系统状态
status = assistant.get_system_status()
print("系统状态:", status)
if __name__ == "__main__":
main()

1
src/utils/__init__.py Normal file
View File

@@ -0,0 +1 @@
# 工具模块

Binary file not shown.

Binary file not shown.

212
src/utils/helpers.py Normal file
View File

@@ -0,0 +1,212 @@
import logging
import json
import re
from typing import Dict, List, Any, Optional
from datetime import datetime, timedelta
import hashlib
def setup_logging(log_level: str = "INFO", log_file: str = "logs/tsp_assistant.log"):
"""设置日志配置"""
import os
# 创建日志目录
os.makedirs(os.path.dirname(log_file), exist_ok=True)
logging.basicConfig(
level=getattr(logging, log_level.upper()),
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(log_file, encoding='utf-8'),
logging.StreamHandler()
]
)
def validate_work_order_data(data: Dict[str, Any]) -> Dict[str, Any]:
"""验证工单数据"""
errors = []
required_fields = ["title", "description", "category"]
for field in required_fields:
if not data.get(field):
errors.append(f"缺少必填字段: {field}")
# 验证优先级
if "priority" in data and data["priority"] not in ["low", "medium", "high", "critical"]:
errors.append("优先级必须是: low, medium, high, critical")
# 验证类别
valid_categories = [
"技术问题", "账户问题", "支付问题", "产品问题",
"服务问题", "投诉建议", "其他"
]
if "category" in data and data["category"] not in valid_categories:
errors.append(f"类别必须是: {', '.join(valid_categories)}")
return {
"valid": len(errors) == 0,
"errors": errors
}
def extract_keywords(text: str, max_keywords: int = 10) -> List[str]:
"""提取文本关键词"""
# 简单的关键词提取(可以后续改进为更复杂的算法)
import jieba
# 停用词
stop_words = {
"", "", "", "", "", "", "", "", "", "", "", "", "一个", "", "", "", "", "", "", "", "", "", "", "没有", "", "", "自己", ""
}
# 分词
words = jieba.cut(text)
# 过滤停用词和短词
keywords = []
for word in words:
if len(word) > 1 and word not in stop_words and not word.isdigit():
keywords.append(word)
# 统计词频
word_count = {}
for word in keywords:
word_count[word] = word_count.get(word, 0) + 1
# 返回频率最高的关键词
sorted_words = sorted(word_count.items(), key=lambda x: x[1], reverse=True)
return [word for word, count in sorted_words[:max_keywords]]
def calculate_similarity(text1: str, text2: str) -> float:
"""计算文本相似度"""
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
try:
vectorizer = TfidfVectorizer()
vectors = vectorizer.fit_transform([text1, text2])
similarity = cosine_similarity(vectors[0:1], vectors[1:2])[0][0]
return float(similarity)
except Exception as e:
logging.error(f"计算相似度失败: {e}")
return 0.0
def format_time_duration(seconds: float) -> str:
"""格式化时间持续时间"""
if seconds < 60:
return f"{seconds:.0f}"
elif seconds < 3600:
minutes = seconds / 60
return f"{minutes:.1f}分钟"
elif seconds < 86400:
hours = seconds / 3600
return f"{hours:.1f}小时"
else:
days = seconds / 86400
return f"{days:.1f}"
def generate_order_id() -> str:
"""生成工单ID"""
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
random_suffix = hashlib.md5(timestamp.encode()).hexdigest()[:6]
return f"WO{timestamp}{random_suffix}"
def parse_date_range(date_range: str) -> tuple:
"""解析日期范围"""
today = datetime.now().date()
if date_range == "today":
return today, today
elif date_range == "yesterday":
yesterday = today - timedelta(days=1)
return yesterday, yesterday
elif date_range == "week":
start = today - timedelta(days=today.weekday())
return start, today
elif date_range == "month":
start = today.replace(day=1)
return start, today
elif date_range == "last_7_days":
start = today - timedelta(days=7)
return start, today
elif date_range == "last_30_days":
start = today - timedelta(days=30)
return start, today
else:
# 尝试解析自定义日期范围
try:
start_str, end_str = date_range.split(" to ")
start = datetime.strptime(start_str, "%Y-%m-%d").date()
end = datetime.strptime(end_str, "%Y-%m-%d").date()
return start, end
except:
return today, today
def sanitize_text(text: str) -> str:
"""清理文本内容"""
if not text:
return ""
# 移除多余的空白字符
text = re.sub(r'\s+', ' ', text.strip())
# 移除特殊字符(保留中文、英文、数字和基本标点)
text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9\s.,!?;:()]', '', text)
return text
def chunk_text(text: str, max_length: int = 1000) -> List[str]:
"""将长文本分割成小块"""
if len(text) <= max_length:
return [text]
chunks = []
current_chunk = ""
# 按句子分割
sentences = re.split(r'[。!?.!?]', text)
for sentence in sentences:
sentence = sentence.strip()
if not sentence:
continue
if len(current_chunk) + len(sentence) <= max_length:
current_chunk += sentence + ""
else:
if current_chunk:
chunks.append(current_chunk.strip())
current_chunk = sentence + ""
if current_chunk:
chunks.append(current_chunk.strip())
return chunks
def merge_json_safely(dict1: Dict[str, Any], dict2: Dict[str, Any]) -> Dict[str, Any]:
"""安全合并两个字典"""
result = dict1.copy()
for key, value in dict2.items():
if key in result:
if isinstance(result[key], dict) and isinstance(value, dict):
result[key] = merge_json_safely(result[key], value)
elif isinstance(result[key], list) and isinstance(value, list):
result[key].extend(value)
else:
result[key] = value
else:
result[key] = value
return result
def get_memory_usage() -> Dict[str, float]:
"""获取内存使用情况"""
import psutil
process = psutil.Process()
memory_info = process.memory_info()
return {
"rss_mb": memory_info.rss / 1024 / 1024, # 物理内存
"vms_mb": memory_info.vms / 1024 / 1024, # 虚拟内存
"percent": process.memory_percent()
}

9
src/vehicle/__init__.py Normal file
View File

@@ -0,0 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
车辆数据管理模块
"""
from .vehicle_data_manager import VehicleDataManager
__all__ = ['VehicleDataManager']

Binary file not shown.

View File

@@ -0,0 +1,320 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
车辆实时数据管理器
"""
import json
import logging
from typing import List, Dict, Any, Optional
from datetime import datetime, timedelta
from sqlalchemy import desc
from ..core.database import db_manager
from ..core.models import VehicleData
logger = logging.getLogger(__name__)
class VehicleDataManager:
"""车辆实时数据管理器"""
def __init__(self):
self.logger = logger
def add_vehicle_data(
self,
vehicle_id: str,
data_type: str,
data_value: Dict[str, Any],
vehicle_vin: str = None
) -> bool:
"""添加车辆实时数据"""
try:
with db_manager.get_session() as session:
vehicle_data = VehicleData(
vehicle_id=vehicle_id,
vehicle_vin=vehicle_vin,
data_type=data_type,
data_value=json.dumps(data_value, ensure_ascii=False),
timestamp=datetime.now()
)
session.add(vehicle_data)
session.commit()
logger.info(f"添加车辆数据成功: {vehicle_id} - {data_type}")
return True
except Exception as e:
logger.error(f"添加车辆数据失败: {e}")
return False
def get_vehicle_data(
self,
vehicle_id: str,
data_type: str = None,
limit: int = 10
) -> List[Dict[str, Any]]:
"""获取车辆实时数据"""
try:
with db_manager.get_session() as session:
query = session.query(VehicleData).filter(
VehicleData.vehicle_id == vehicle_id,
VehicleData.is_active == True
)
if data_type:
query = query.filter(VehicleData.data_type == data_type)
vehicle_data_list = query.order_by(desc(VehicleData.timestamp)).limit(limit).all()
results = []
for data in vehicle_data_list:
try:
data_value = json.loads(data.data_value)
except:
data_value = data.data_value
results.append({
"id": data.id,
"vehicle_id": data.vehicle_id,
"vehicle_vin": data.vehicle_vin,
"data_type": data.data_type,
"data_value": data_value,
"timestamp": data.timestamp.isoformat(),
"is_active": data.is_active
})
return results
except Exception as e:
logger.error(f"获取车辆数据失败: {e}")
return []
def get_latest_vehicle_data(self, vehicle_id: str) -> Dict[str, Any]:
"""获取车辆最新数据"""
try:
with db_manager.get_session() as session:
# 获取各种类型的最新数据
data_types = ['location', 'status', 'fault', 'battery', 'engine']
latest_data = {}
for data_type in data_types:
data = session.query(VehicleData).filter(
VehicleData.vehicle_id == vehicle_id,
VehicleData.data_type == data_type,
VehicleData.is_active == True
).order_by(desc(VehicleData.timestamp)).first()
if data:
try:
data_value = json.loads(data.data_value)
except:
data_value = data.data_value
latest_data[data_type] = {
"value": data_value,
"timestamp": data.timestamp.isoformat()
}
return latest_data
except Exception as e:
logger.error(f"获取车辆最新数据失败: {e}")
return {}
def search_vehicle_data(
self,
vehicle_id: str = None,
data_type: str = None,
start_time: datetime = None,
end_time: datetime = None,
limit: int = 100
) -> List[Dict[str, Any]]:
"""搜索车辆数据"""
try:
with db_manager.get_session() as session:
query = session.query(VehicleData).filter(VehicleData.is_active == True)
if vehicle_id:
query = query.filter(VehicleData.vehicle_id == vehicle_id)
if data_type:
query = query.filter(VehicleData.data_type == data_type)
if start_time:
query = query.filter(VehicleData.timestamp >= start_time)
if end_time:
query = query.filter(VehicleData.timestamp <= end_time)
vehicle_data_list = query.order_by(desc(VehicleData.timestamp)).limit(limit).all()
results = []
for data in vehicle_data_list:
try:
data_value = json.loads(data.data_value)
except:
data_value = data.data_value
results.append({
"id": data.id,
"vehicle_id": data.vehicle_id,
"vehicle_vin": data.vehicle_vin,
"data_type": data.data_type,
"data_value": data_value,
"timestamp": data.timestamp.isoformat(),
"is_active": data.is_active
})
return results
except Exception as e:
logger.error(f"搜索车辆数据失败: {e}")
return []
def add_sample_vehicle_data(self) -> bool:
"""添加示例车辆数据"""
try:
sample_data = [
{
"vehicle_id": "V001",
"vehicle_vin": "LSGBF53E8DH123456",
"data_type": "location",
"data_value": {
"latitude": 39.9042,
"longitude": 116.4074,
"address": "北京市朝阳区",
"speed": 0,
"direction": 0
}
},
{
"vehicle_id": "V001",
"vehicle_vin": "LSGBF53E8DH123456",
"data_type": "status",
"data_value": {
"engine_status": "off",
"door_status": "locked",
"window_status": "closed",
"light_status": "off",
"air_conditioning": "off"
}
},
{
"vehicle_id": "V001",
"vehicle_vin": "LSGBF53E8DH123456",
"data_type": "battery",
"data_value": {
"battery_level": 85,
"charging_status": "not_charging",
"estimated_range": 320,
"battery_health": "good"
}
},
{
"vehicle_id": "V001",
"vehicle_vin": "LSGBF53E8DH123456",
"data_type": "engine",
"data_value": {
"engine_temperature": 45,
"oil_level": "normal",
"fuel_level": 75,
"mileage": 12580
}
},
{
"vehicle_id": "V002",
"vehicle_vin": "LSGBF53E8DH123457",
"data_type": "location",
"data_value": {
"latitude": 31.2304,
"longitude": 121.4737,
"address": "上海市黄浦区",
"speed": 25,
"direction": 90
}
},
{
"vehicle_id": "V002",
"vehicle_vin": "LSGBF53E8DH123457",
"data_type": "status",
"data_value": {
"engine_status": "on",
"door_status": "unlocked",
"window_status": "open",
"light_status": "on",
"air_conditioning": "on"
}
},
{
"vehicle_id": "V002",
"vehicle_vin": "LSGBF53E8DH123457",
"data_type": "fault",
"data_value": {
"fault_codes": ["P0301", "P0171"],
"fault_descriptions": ["气缸1失火", "系统过稀"],
"severity": "medium",
"last_occurrence": "2024-09-06T10:30:00"
}
}
]
for data in sample_data:
self.add_vehicle_data(
vehicle_id=data["vehicle_id"],
data_type=data["data_type"],
data_value=data["data_value"],
vehicle_vin=data["vehicle_vin"]
)
logger.info("示例车辆数据添加成功")
return True
except Exception as e:
logger.error(f"添加示例车辆数据失败: {e}")
return False
def get_vehicle_summary(self, vehicle_id: str) -> Dict[str, Any]:
"""获取车辆数据摘要"""
try:
latest_data = self.get_latest_vehicle_data(vehicle_id)
summary = {
"vehicle_id": vehicle_id,
"last_update": None,
"status": "unknown",
"location": None,
"battery_level": None,
"fault_count": 0,
"data_types": list(latest_data.keys())
}
if latest_data:
# 获取最新更新时间
timestamps = [data["timestamp"] for data in latest_data.values()]
if timestamps:
summary["last_update"] = max(timestamps)
# 获取位置信息
if "location" in latest_data:
summary["location"] = latest_data["location"]["value"]
# 获取电池信息
if "battery" in latest_data:
summary["battery_level"] = latest_data["battery"]["value"].get("battery_level")
# 获取故障信息
if "fault" in latest_data:
fault_codes = latest_data["fault"]["value"].get("fault_codes", [])
summary["fault_count"] = len(fault_codes)
# 获取状态信息
if "status" in latest_data:
engine_status = latest_data["status"]["value"].get("engine_status")
summary["status"] = "running" if engine_status == "on" else "stopped"
return summary
except Exception as e:
logger.error(f"获取车辆摘要失败: {e}")
return {"vehicle_id": vehicle_id, "error": str(e)}

Binary file not shown.

671
src/web/app.py Normal file
View File

@@ -0,0 +1,671 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
TSP助手预警管理Web应用
提供预警系统的Web界面和API接口
"""
import sys
import os
import json
from datetime import datetime, timedelta
from flask import Flask, render_template, request, jsonify, redirect, url_for
from flask_cors import CORS
# 添加项目根目录到Python路径
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from src.main import TSPAssistant
from src.agent_assistant import TSPAgentAssistant
from src.analytics.alert_system import AlertRule, AlertLevel, AlertType
from src.dialogue.realtime_chat import RealtimeChatManager
from src.vehicle.vehicle_data_manager import VehicleDataManager
app = Flask(__name__)
CORS(app)
# 初始化TSP助手和Agent助手
assistant = TSPAssistant()
agent_assistant = TSPAgentAssistant()
chat_manager = RealtimeChatManager()
vehicle_manager = VehicleDataManager()
@app.route('/')
def index():
"""主页 - 综合管理平台"""
return render_template('dashboard.html')
@app.route('/alerts')
def alerts():
"""预警管理页面"""
return render_template('index.html')
@app.route('/api/health')
def get_health():
"""获取系统健康状态"""
try:
health = assistant.get_system_health()
return jsonify(health)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/alerts')
def get_alerts():
"""获取预警列表"""
try:
alerts = assistant.get_active_alerts()
return jsonify(alerts)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/alerts/statistics')
def get_alert_statistics():
"""获取预警统计"""
try:
stats = assistant.get_alert_statistics()
return jsonify(stats)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/alerts/<int:alert_id>/resolve', methods=['POST'])
def resolve_alert(alert_id):
"""解决预警"""
try:
success = assistant.resolve_alert(alert_id)
if success:
return jsonify({"success": True, "message": "预警已解决"})
else:
return jsonify({"success": False, "message": "解决预警失败"}), 400
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/rules')
def get_rules():
"""获取预警规则列表"""
try:
rules = assistant.alert_system.rules
rules_data = []
for name, rule in rules.items():
rules_data.append({
"name": rule.name,
"description": rule.description,
"alert_type": rule.alert_type.value,
"level": rule.level.value,
"threshold": rule.threshold,
"condition": rule.condition,
"enabled": rule.enabled,
"check_interval": rule.check_interval,
"cooldown": rule.cooldown
})
return jsonify(rules_data)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/rules', methods=['POST'])
def create_rule():
"""创建预警规则"""
try:
data = request.get_json()
rule = AlertRule(
name=data['name'],
description=data['description'],
alert_type=AlertType(data['alert_type']),
level=AlertLevel(data['level']),
threshold=float(data['threshold']),
condition=data['condition'],
enabled=data.get('enabled', True),
check_interval=int(data.get('check_interval', 300)),
cooldown=int(data.get('cooldown', 3600))
)
success = assistant.alert_system.add_custom_rule(rule)
if success:
return jsonify({"success": True, "message": "规则创建成功"})
else:
return jsonify({"success": False, "message": "规则创建失败"}), 400
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/rules/<rule_name>', methods=['PUT'])
def update_rule(rule_name):
"""更新预警规则"""
try:
data = request.get_json()
success = assistant.alert_system.update_rule(rule_name, **data)
if success:
return jsonify({"success": True, "message": "规则更新成功"})
else:
return jsonify({"success": False, "message": "规则更新失败"}), 400
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/rules/<rule_name>', methods=['DELETE'])
def delete_rule(rule_name):
"""删除预警规则"""
try:
success = assistant.alert_system.delete_rule(rule_name)
if success:
return jsonify({"success": True, "message": "规则删除成功"})
else:
return jsonify({"success": False, "message": "规则删除失败"}), 400
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/monitor/start', methods=['POST'])
def start_monitoring():
"""启动监控服务"""
try:
success = assistant.start_monitoring()
if success:
return jsonify({"success": True, "message": "监控服务已启动"})
else:
return jsonify({"success": False, "message": "启动监控服务失败"}), 400
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/monitor/stop', methods=['POST'])
def stop_monitoring():
"""停止监控服务"""
try:
success = assistant.stop_monitoring()
if success:
return jsonify({"success": True, "message": "监控服务已停止"})
else:
return jsonify({"success": False, "message": "停止监控服务失败"}), 400
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/monitor/status')
def get_monitor_status():
"""获取监控服务状态"""
try:
health = assistant.get_system_health()
return jsonify({
"monitor_status": health.get("monitor_status", "unknown"),
"health_score": health.get("health_score", 0),
"active_alerts": health.get("active_alerts", 0)
})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/check-alerts', methods=['POST'])
def check_alerts():
"""手动检查预警"""
try:
alerts = assistant.check_alerts()
return jsonify({
"success": True,
"alerts": alerts,
"count": len(alerts)
})
except Exception as e:
return jsonify({"error": str(e)}), 500
# 实时对话相关路由
@app.route('/chat')
def chat():
"""实时对话页面 (WebSocket版本)"""
return render_template('chat.html')
@app.route('/chat-http')
def chat_http():
"""实时对话页面 (HTTP版本)"""
return render_template('chat_http.html')
@app.route('/api/chat/session', methods=['POST'])
def create_chat_session():
"""创建对话会话"""
try:
data = request.get_json()
user_id = data.get('user_id', 'anonymous')
work_order_id = data.get('work_order_id')
session_id = chat_manager.create_session(user_id, work_order_id)
return jsonify({
"success": True,
"session_id": session_id,
"message": "会话创建成功"
})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/chat/message', methods=['POST'])
def send_chat_message():
"""发送聊天消息"""
try:
data = request.get_json()
session_id = data.get('session_id')
message = data.get('message')
if not session_id or not message:
return jsonify({"error": "缺少必要参数"}), 400
result = chat_manager.process_message(session_id, message)
return jsonify(result)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/chat/history/<session_id>')
def get_chat_history(session_id):
"""获取对话历史"""
try:
history = chat_manager.get_session_history(session_id)
return jsonify({
"success": True,
"history": history
})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/chat/work-order', methods=['POST'])
def create_work_order():
"""创建工单"""
try:
data = request.get_json()
session_id = data.get('session_id')
title = data.get('title')
description = data.get('description')
category = data.get('category', '技术问题')
priority = data.get('priority', 'medium')
if not session_id or not title or not description:
return jsonify({"error": "缺少必要参数"}), 400
result = chat_manager.create_work_order(session_id, title, description, category, priority)
return jsonify(result)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/chat/work-order/<int:work_order_id>')
def get_work_order_status(work_order_id):
"""获取工单状态"""
try:
result = chat_manager.get_work_order_status(work_order_id)
return jsonify(result)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/chat/session/<session_id>', methods=['DELETE'])
def end_chat_session(session_id):
"""结束对话会话"""
try:
success = chat_manager.end_session(session_id)
return jsonify({
"success": success,
"message": "会话已结束" if success else "结束会话失败"
})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/chat/sessions')
def get_active_sessions():
"""获取活跃会话列表"""
try:
sessions = chat_manager.get_active_sessions()
return jsonify({
"success": True,
"sessions": sessions
})
except Exception as e:
return jsonify({"error": str(e)}), 500
# Agent相关API
@app.route('/api/agent/status')
def get_agent_status():
"""获取Agent状态"""
try:
status = agent_assistant.get_agent_status()
return jsonify(status)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/agent/toggle', methods=['POST'])
def toggle_agent_mode():
"""切换Agent模式"""
try:
data = request.get_json()
enabled = data.get('enabled', True)
success = agent_assistant.toggle_agent_mode(enabled)
return jsonify({
"success": success,
"message": f"Agent模式已{'启用' if enabled else '禁用'}"
})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/agent/monitoring/start', methods=['POST'])
def start_agent_monitoring():
"""启动Agent监控"""
try:
success = agent_assistant.start_proactive_monitoring()
return jsonify({
"success": success,
"message": "Agent监控已启动" if success else "启动失败"
})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/agent/monitoring/stop', methods=['POST'])
def stop_agent_monitoring():
"""停止Agent监控"""
try:
success = agent_assistant.stop_proactive_monitoring()
return jsonify({
"success": success,
"message": "Agent监控已停止" if success else "停止失败"
})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/agent/proactive-monitoring', methods=['POST'])
def proactive_monitoring():
"""主动监控检查"""
try:
result = agent_assistant.run_proactive_monitoring()
return jsonify(result)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/agent/intelligent-analysis', methods=['POST'])
def intelligent_analysis():
"""智能分析"""
try:
analysis = agent_assistant.run_intelligent_analysis()
return jsonify({"success": True, "analysis": analysis})
except Exception as e:
return jsonify({"error": str(e)}), 500
# 知识库相关API
@app.route('/api/knowledge')
def get_knowledge():
"""获取知识库列表"""
try:
# 获取分页参数
page = request.args.get('page', 1, type=int)
per_page = request.args.get('per_page', 10, type=int)
# 从数据库获取知识库数据
knowledge_entries = assistant.knowledge_manager.get_knowledge_entries(
page=page, per_page=per_page
)
return jsonify(knowledge_entries)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/knowledge/search')
def search_knowledge():
"""搜索知识库"""
try:
query = request.args.get('q', '')
# 这里应该调用知识库管理器的搜索方法
results = assistant.search_knowledge(query, top_k=5)
return jsonify(results.get('results', []))
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/knowledge', methods=['POST'])
def add_knowledge():
"""添加知识库条目"""
try:
data = request.get_json()
success = assistant.knowledge_manager.add_knowledge_entry(
question=data['question'],
answer=data['answer'],
category=data['category'],
confidence_score=data['confidence_score']
)
return jsonify({"success": success, "message": "知识添加成功" if success else "添加失败"})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/knowledge/stats')
def get_knowledge_stats():
"""获取知识库统计"""
try:
stats = assistant.knowledge_manager.get_knowledge_stats()
return jsonify(stats)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/knowledge/upload', methods=['POST'])
def upload_knowledge_file():
"""上传文件并生成知识库"""
try:
if 'file' not in request.files:
return jsonify({"error": "没有上传文件"}), 400
file = request.files['file']
if file.filename == '':
return jsonify({"error": "没有选择文件"}), 400
# 保存文件到临时目录
import tempfile
import os
import uuid
# 创建唯一的临时文件名
temp_filename = f"upload_{uuid.uuid4()}{os.path.splitext(file.filename)[1]}"
temp_path = os.path.join(tempfile.gettempdir(), temp_filename)
try:
# 保存文件
file.save(temp_path)
# 使用Agent助手处理文件
result = agent_assistant.process_file_to_knowledge(temp_path, file.filename)
return jsonify(result)
finally:
# 确保删除临时文件
try:
if os.path.exists(temp_path):
os.unlink(temp_path)
except Exception as cleanup_error:
logger.warning(f"清理临时文件失败: {cleanup_error}")
except Exception as e:
logger.error(f"文件上传处理失败: {e}")
return jsonify({"error": str(e)}), 500
@app.route('/api/knowledge/delete/<int:knowledge_id>', methods=['DELETE'])
def delete_knowledge(knowledge_id):
"""删除知识库条目"""
try:
success = assistant.knowledge_manager.delete_knowledge_entry(knowledge_id)
return jsonify({"success": success, "message": "删除成功" if success else "删除失败"})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/knowledge/verify/<int:knowledge_id>', methods=['POST'])
def verify_knowledge(knowledge_id):
"""验证知识库条目"""
try:
data = request.get_json() or {}
verified_by = data.get('verified_by', 'admin')
success = assistant.knowledge_manager.verify_knowledge_entry(knowledge_id, verified_by)
return jsonify({"success": success, "message": "验证成功" if success else "验证失败"})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/knowledge/unverify/<int:knowledge_id>', methods=['POST'])
def unverify_knowledge(knowledge_id):
"""取消验证知识库条目"""
try:
success = assistant.knowledge_manager.unverify_knowledge_entry(knowledge_id)
return jsonify({"success": success, "message": "取消验证成功" if success else "取消验证失败"})
except Exception as e:
return jsonify({"error": str(e)}), 500
# 工单相关API
@app.route('/api/workorders')
def get_workorders():
"""获取工单列表"""
try:
status_filter = request.args.get('status')
priority_filter = request.args.get('priority')
# 这里应该调用工单管理器的获取方法
workorders = [
{
"id": 1,
"title": "车辆无法远程启动",
"description": "用户反映APP中远程启动功能无法使用",
"category": "远程控制",
"priority": "high",
"status": "open",
"created_at": "2024-01-01T10:00:00Z"
},
{
"id": 2,
"title": "APP显示异常",
"description": "APP中车辆信息显示不正确",
"category": "APP功能",
"priority": "medium",
"status": "in_progress",
"created_at": "2024-01-01T11:00:00Z"
}
]
# 应用过滤
if status_filter and status_filter != 'all':
workorders = [w for w in workorders if w['status'] == status_filter]
if priority_filter and priority_filter != 'all':
workorders = [w for w in workorders if w['priority'] == priority_filter]
return jsonify(workorders)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/workorders', methods=['POST'])
def create_workorder():
"""创建工单"""
try:
data = request.get_json()
result = assistant.create_work_order(
title=data['title'],
description=data['description'],
category=data['category'],
priority=data['priority']
)
return jsonify({"success": True, "workorder": result})
except Exception as e:
return jsonify({"error": str(e)}), 500
# 分析相关API
@app.route('/api/analytics')
def get_analytics():
"""获取分析数据"""
try:
analytics = assistant.generate_analytics("last_7_days")
return jsonify(analytics)
except Exception as e:
return jsonify({"error": str(e)}), 500
# 系统设置相关API
@app.route('/api/settings')
def get_settings():
"""获取系统设置"""
try:
settings = {
"api_timeout": 30,
"max_history": 10,
"refresh_interval": 10,
"auto_monitoring": True,
"agent_mode": True
}
return jsonify(settings)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/settings', methods=['POST'])
def save_settings():
"""保存系统设置"""
try:
data = request.get_json()
# 这里应该保存设置到配置文件
return jsonify({"success": True, "message": "设置保存成功"})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/system/info')
def get_system_info():
"""获取系统信息"""
try:
import sys
import platform
info = {
"version": "1.0.0",
"python_version": sys.version,
"database": "SQLite",
"uptime": "2天3小时",
"memory_usage": 128
}
return jsonify(info)
except Exception as e:
return jsonify({"error": str(e)}), 500
# 车辆数据相关API
@app.route('/api/vehicle/data')
def get_vehicle_data():
"""获取车辆数据"""
try:
vehicle_id = request.args.get('vehicle_id')
data_type = request.args.get('data_type')
limit = request.args.get('limit', 10, type=int)
if vehicle_id:
data = vehicle_manager.get_vehicle_data(vehicle_id, data_type, limit)
else:
data = vehicle_manager.search_vehicle_data(limit=limit)
return jsonify(data)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/vehicle/data/<vehicle_id>/latest')
def get_latest_vehicle_data(vehicle_id):
"""获取车辆最新数据"""
try:
data = vehicle_manager.get_latest_vehicle_data(vehicle_id)
return jsonify(data)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/vehicle/data/<vehicle_id>/summary')
def get_vehicle_summary(vehicle_id):
"""获取车辆数据摘要"""
try:
summary = vehicle_manager.get_vehicle_summary(vehicle_id)
return jsonify(summary)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/vehicle/data', methods=['POST'])
def add_vehicle_data():
"""添加车辆数据"""
try:
data = request.get_json()
success = vehicle_manager.add_vehicle_data(
vehicle_id=data['vehicle_id'],
data_type=data['data_type'],
data_value=data['data_value'],
vehicle_vin=data.get('vehicle_vin')
)
return jsonify({"success": success, "message": "数据添加成功" if success else "添加失败"})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/vehicle/init-sample-data', methods=['POST'])
def init_sample_vehicle_data():
"""初始化示例车辆数据"""
try:
success = vehicle_manager.add_sample_vehicle_data()
return jsonify({"success": success, "message": "示例数据初始化成功" if success else "初始化失败"})
except Exception as e:
return jsonify({"error": str(e)}), 500
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5000)

View File

@@ -0,0 +1,431 @@
/* TSP助手预警管理系统样式 */
body {
background-color: #f8f9fa;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.navbar-brand {
font-weight: bold;
font-size: 1.5rem;
}
/* 健康状态圆圈 */
.health-score {
text-align: center;
}
.score-circle {
width: 100px;
height: 100px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 10px;
font-size: 1.5rem;
font-weight: bold;
color: white;
position: relative;
}
.score-circle.excellent {
background: linear-gradient(135deg, #28a745, #20c997);
}
.score-circle.good {
background: linear-gradient(135deg, #17a2b8, #6f42c1);
}
.score-circle.fair {
background: linear-gradient(135deg, #ffc107, #fd7e14);
}
.score-circle.poor {
background: linear-gradient(135deg, #dc3545, #e83e8c);
}
.score-circle.critical {
background: linear-gradient(135deg, #6c757d, #343a40);
}
.health-status {
font-size: 0.9rem;
color: #6c757d;
text-transform: capitalize;
}
/* 预警卡片 */
.alert-card {
border-left: 4px solid;
margin-bottom: 1rem;
transition: all 0.3s ease;
}
.alert-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.alert-card.critical {
border-left-color: #dc3545;
}
.alert-card.error {
border-left-color: #fd7e14;
}
.alert-card.warning {
border-left-color: #ffc107;
}
.alert-card.info {
border-left-color: #17a2b8;
}
.alert-level {
font-size: 0.8rem;
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
font-weight: bold;
text-transform: uppercase;
}
.alert-level.critical {
background-color: #dc3545;
color: white;
}
.alert-level.error {
background-color: #fd7e14;
color: white;
}
.alert-level.warning {
background-color: #ffc107;
color: #212529;
}
.alert-level.info {
background-color: #17a2b8;
color: white;
}
/* 规则表格 */
.table th {
background-color: #f8f9fa;
border-top: none;
font-weight: 600;
}
.rule-status {
font-size: 0.8rem;
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
}
.rule-status.enabled {
background-color: #d4edda;
color: #155724;
}
.rule-status.disabled {
background-color: #f8d7da;
color: #721c24;
}
/* 统计卡片动画 */
.card {
transition: all 0.3s ease;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
/* 按钮样式 */
.btn {
border-radius: 0.375rem;
font-weight: 500;
transition: all 0.3s ease;
}
.btn:hover {
transform: translateY(-1px);
}
/* 加载动画 */
.loading {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid #f3f3f3;
border-top: 3px solid #007bff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* 响应式设计 */
@media (max-width: 768px) {
.container-fluid {
padding: 0 15px;
}
.score-circle {
width: 80px;
height: 80px;
font-size: 1.2rem;
}
.card-body {
padding: 1rem;
}
}
/* 自定义滚动条 */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
/* 状态指示器 */
.status-indicator {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 8px;
}
.status-indicator.running {
background-color: #28a745;
animation: pulse 2s infinite;
}
.status-indicator.stopped {
background-color: #dc3545;
}
.status-indicator.unknown {
background-color: #6c757d;
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.7);
}
70% {
box-shadow: 0 0 0 10px rgba(40, 167, 69, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(40, 167, 69, 0);
}
}
/* 模态框样式 */
.modal-content {
border-radius: 0.5rem;
border: none;
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
}
.modal-header {
border-bottom: 1px solid #e9ecef;
background-color: #f8f9fa;
}
.modal-footer {
border-top: 1px solid #e9ecef;
background-color: #f8f9fa;
}
/* 表格样式 */
.table-hover tbody tr:hover {
background-color: rgba(0,123,255,0.1);
}
/* 空状态样式 */
.empty-state {
text-align: center;
padding: 3rem 1rem;
color: #6c757d;
}
.empty-state i {
font-size: 3rem;
margin-bottom: 1rem;
opacity: 0.5;
}
/* 工具提示样式 */
.tooltip {
font-size: 0.875rem;
}
.tooltip-inner {
background-color: #212529;
border-radius: 0.375rem;
}
/* 进度条样式 */
.progress {
height: 8px;
border-radius: 4px;
}
.progress-bar {
border-radius: 4px;
}
/* 徽章样式 */
.badge {
font-size: 0.75rem;
padding: 0.375rem 0.5rem;
}
/* 卡片标题样式 */
.card-header h5 {
margin: 0;
font-weight: 600;
}
.card-header h5 i {
color: #007bff;
}
/* 统计数字样式 */
.stat-number {
font-size: 2rem;
font-weight: bold;
line-height: 1;
}
.stat-label {
font-size: 0.875rem;
opacity: 0.8;
margin-top: 0.25rem;
}
/* 预警数据展示优化 */
.alert-data {
background-color: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 4px;
padding: 8px;
font-family: 'Courier New', monospace;
font-size: 12px;
max-height: 100px;
overflow-y: auto;
white-space: pre-wrap;
word-break: break-all;
}
.alert-message {
font-weight: 500;
margin-bottom: 8px;
line-height: 1.4;
}
.alert-meta {
font-size: 12px;
color: #6c757d;
margin-bottom: 8px;
}
/* 过滤和排序控件 */
.alert-controls {
display: flex;
gap: 10px;
align-items: center;
flex-wrap: wrap;
}
.alert-controls .form-select {
min-width: 120px;
}
/* 预警卡片内容优化 */
.alert-card .card-body {
padding: 1rem;
}
.alert-card .d-flex {
align-items: flex-start;
}
.alert-card .flex-grow-1 {
min-width: 0;
}
/* 规则表格操作按钮 */
.table .btn-sm {
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
margin-right: 0.25rem;
}
/* 响应式设计优化 */
@media (max-width: 768px) {
.alert-controls {
flex-direction: column;
align-items: stretch;
}
.alert-controls .form-select {
min-width: auto;
}
.alert-card .d-flex {
flex-direction: column;
}
.alert-card .ms-3 {
margin-left: 0 !important;
margin-top: 10px;
}
.alert-data {
font-size: 10px;
max-height: 80px;
}
}
/* 预警级别颜色优化 */
.alert-card.critical {
background-color: #f8d7da;
border-color: #dc3545;
}
.alert-card.error {
background-color: #fff3cd;
border-color: #fd7e14;
}
.alert-card.warning {
background-color: #fff3cd;
border-color: #ffc107;
}
.alert-card.info {
background-color: #d1ecf1;
border-color: #17a2b8;
}

556
src/web/static/js/app.js Normal file
View File

@@ -0,0 +1,556 @@
// TSP助手预警管理系统前端脚本
class AlertManager {
constructor() {
this.alerts = [];
this.rules = [];
this.health = {};
this.monitorStatus = 'unknown';
this.refreshInterval = null;
this.init();
}
init() {
this.bindEvents();
this.loadInitialData();
this.startAutoRefresh();
}
bindEvents() {
// 监控控制按钮
document.getElementById('start-monitor').addEventListener('click', () => this.startMonitoring());
document.getElementById('stop-monitor').addEventListener('click', () => this.stopMonitoring());
document.getElementById('check-alerts').addEventListener('click', () => this.checkAlerts());
document.getElementById('refresh-alerts').addEventListener('click', () => this.loadAlerts());
// 规则管理
document.getElementById('save-rule').addEventListener('click', () => this.saveRule());
document.getElementById('update-rule').addEventListener('click', () => this.updateRule());
// 预警过滤和排序
document.getElementById('alert-filter').addEventListener('change', () => this.updateAlertsDisplay());
document.getElementById('alert-sort').addEventListener('change', () => this.updateAlertsDisplay());
// 自动刷新
setInterval(() => {
this.loadHealth();
this.loadMonitorStatus();
}, 5000);
}
async loadInitialData() {
await Promise.all([
this.loadHealth(),
this.loadAlerts(),
this.loadRules(),
this.loadMonitorStatus()
]);
}
startAutoRefresh() {
this.refreshInterval = setInterval(() => {
this.loadAlerts();
}, 10000); // 每10秒刷新一次预警
}
async loadHealth() {
try {
const response = await fetch('/api/health');
const data = await response.json();
this.health = data;
this.updateHealthDisplay();
} catch (error) {
console.error('加载健康状态失败:', error);
}
}
async loadAlerts() {
try {
const response = await fetch('/api/alerts');
const data = await response.json();
this.alerts = data;
this.updateAlertsDisplay();
this.updateAlertStatistics();
} catch (error) {
console.error('加载预警失败:', error);
}
}
async loadRules() {
try {
const response = await fetch('/api/rules');
const data = await response.json();
this.rules = data;
this.updateRulesDisplay();
} catch (error) {
console.error('加载规则失败:', error);
}
}
async loadMonitorStatus() {
try {
const response = await fetch('/api/monitor/status');
const data = await response.json();
this.monitorStatus = data.monitor_status;
this.updateMonitorStatusDisplay();
} catch (error) {
console.error('加载监控状态失败:', error);
}
}
updateHealthDisplay() {
const healthScore = this.health.health_score || 0;
const healthStatus = this.health.health_status || 'unknown';
const scoreElement = document.getElementById('health-score-text');
const circleElement = document.getElementById('health-score-circle');
const statusElement = document.getElementById('health-status');
if (scoreElement) scoreElement.textContent = Math.round(healthScore);
if (statusElement) statusElement.textContent = this.getHealthStatusText(healthStatus);
if (circleElement) {
circleElement.className = `score-circle ${healthStatus}`;
}
}
updateAlertsDisplay() {
const container = document.getElementById('alerts-container');
if (this.alerts.length === 0) {
container.innerHTML = `
<div class="empty-state">
<i class="fas fa-check-circle"></i>
<h5>暂无活跃预警</h5>
<p>系统运行正常,没有需要处理的预警</p>
</div>
`;
return;
}
// 应用过滤和排序
let filteredAlerts = this.filterAndSortAlerts(this.alerts);
const alertsHtml = filteredAlerts.map(alert => {
const dataStr = alert.data ? JSON.stringify(alert.data, null, 2) : '无数据';
return `
<div class="alert-card ${alert.level}">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start">
<div class="flex-grow-1">
<div class="d-flex align-items-center mb-2">
<span class="alert-level ${alert.level}">${this.getLevelText(alert.level)}</span>
<span class="ms-2 text-muted fw-bold">${alert.rule_name || '未知规则'}</span>
<span class="ms-auto text-muted small">${this.formatTime(alert.created_at)}</span>
</div>
<div class="alert-message">${alert.message}</div>
<div class="alert-meta">
类型: ${this.getTypeText(alert.alert_type)} |
级别: ${this.getLevelText(alert.level)}
</div>
<div class="alert-data">${dataStr}</div>
</div>
<div class="ms-3">
<button class="btn btn-sm btn-outline-success" onclick="alertManager.resolveAlert(${alert.id})">
<i class="fas fa-check me-1"></i>解决
</button>
</div>
</div>
</div>
</div>
`;
}).join('');
container.innerHTML = alertsHtml;
}
updateRulesDisplay() {
const tbody = document.getElementById('rules-table');
if (this.rules.length === 0) {
tbody.innerHTML = `
<tr>
<td colspan="6" class="text-center text-muted">暂无规则</td>
</tr>
`;
return;
}
const rulesHtml = this.rules.map(rule => `
<tr>
<td>${rule.name}</td>
<td>${this.getTypeText(rule.alert_type)}</td>
<td><span class="alert-level ${rule.level}">${this.getLevelText(rule.level)}</span></td>
<td>${rule.threshold}</td>
<td><span class="rule-status ${rule.enabled ? 'enabled' : 'disabled'}">${rule.enabled ? '启用' : '禁用'}</span></td>
<td>
<button class="btn btn-sm btn-outline-primary me-1" onclick="alertManager.editRule('${rule.name}')">
<i class="fas fa-edit"></i>
</button>
<button class="btn btn-sm btn-outline-danger" onclick="alertManager.deleteRule('${rule.name}')">
<i class="fas fa-trash"></i>
</button>
</td>
</tr>
`).join('');
tbody.innerHTML = rulesHtml;
}
updateAlertStatistics() {
const stats = this.alerts.reduce((acc, alert) => {
acc[alert.level] = (acc[alert.level] || 0) + 1;
acc.total = (acc.total || 0) + 1;
return acc;
}, {});
document.getElementById('critical-alerts').textContent = stats.critical || 0;
document.getElementById('warning-alerts').textContent = stats.warning || 0;
document.getElementById('info-alerts').textContent = stats.info || 0;
document.getElementById('total-alerts').textContent = stats.total || 0;
}
updateMonitorStatusDisplay() {
const statusElement = document.getElementById('monitor-status');
const icon = statusElement.querySelector('i');
const text = statusElement.querySelector('span') || statusElement;
let statusText = '';
let statusClass = '';
switch (this.monitorStatus) {
case 'running':
statusText = '监控运行中';
statusClass = 'text-success';
icon.className = 'fas fa-circle text-success';
break;
case 'stopped':
statusText = '监控已停止';
statusClass = 'text-danger';
icon.className = 'fas fa-circle text-danger';
break;
default:
statusText = '监控状态未知';
statusClass = 'text-warning';
icon.className = 'fas fa-circle text-warning';
}
if (text.textContent) {
text.textContent = statusText;
} else {
statusElement.innerHTML = `<i class="fas fa-circle ${statusClass}"></i> ${statusText}`;
}
}
async startMonitoring() {
try {
const response = await fetch('/api/monitor/start', { method: 'POST' });
const data = await response.json();
if (data.success) {
this.showNotification('监控服务已启动', 'success');
this.loadMonitorStatus();
} else {
this.showNotification(data.message || '启动监控失败', 'error');
}
} catch (error) {
console.error('启动监控失败:', error);
this.showNotification('启动监控失败', 'error');
}
}
async stopMonitoring() {
try {
const response = await fetch('/api/monitor/stop', { method: 'POST' });
const data = await response.json();
if (data.success) {
this.showNotification('监控服务已停止', 'success');
this.loadMonitorStatus();
} else {
this.showNotification(data.message || '停止监控失败', 'error');
}
} catch (error) {
console.error('停止监控失败:', error);
this.showNotification('停止监控失败', 'error');
}
}
async checkAlerts() {
try {
const response = await fetch('/api/check-alerts', { method: 'POST' });
const data = await response.json();
if (data.success) {
this.showNotification(`检查完成,发现 ${data.count} 个预警`, 'info');
this.loadAlerts();
} else {
this.showNotification('检查预警失败', 'error');
}
} catch (error) {
console.error('检查预警失败:', error);
this.showNotification('检查预警失败', 'error');
}
}
async resolveAlert(alertId) {
try {
const response = await fetch(`/api/alerts/${alertId}/resolve`, { method: 'POST' });
const data = await response.json();
if (data.success) {
this.showNotification('预警已解决', 'success');
this.loadAlerts();
} else {
this.showNotification(data.message || '解决预警失败', 'error');
}
} catch (error) {
console.error('解决预警失败:', error);
this.showNotification('解决预警失败', 'error');
}
}
async saveRule() {
const formData = {
name: document.getElementById('rule-name').value,
description: document.getElementById('rule-description').value,
alert_type: document.getElementById('rule-type').value,
level: document.getElementById('rule-level').value,
threshold: parseFloat(document.getElementById('rule-threshold').value),
condition: document.getElementById('rule-condition').value,
enabled: document.getElementById('rule-enabled').checked,
check_interval: parseInt(document.getElementById('rule-interval').value),
cooldown: parseInt(document.getElementById('rule-cooldown').value)
};
try {
const response = await fetch('/api/rules', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formData)
});
const data = await response.json();
if (data.success) {
this.showNotification('规则创建成功', 'success');
this.hideModal('ruleModal');
this.loadRules();
this.resetRuleForm();
} else {
this.showNotification(data.message || '创建规则失败', 'error');
}
} catch (error) {
console.error('创建规则失败:', error);
this.showNotification('创建规则失败', 'error');
}
}
async deleteRule(ruleName) {
if (!confirm(`确定要删除规则 "${ruleName}" 吗?`)) {
return;
}
try {
const response = await fetch(`/api/rules/${ruleName}`, { method: 'DELETE' });
const data = await response.json();
if (data.success) {
this.showNotification('规则删除成功', 'success');
this.loadRules();
} else {
this.showNotification(data.message || '删除规则失败', 'error');
}
} catch (error) {
console.error('删除规则失败:', error);
this.showNotification('删除规则失败', 'error');
}
}
filterAndSortAlerts(alerts) {
// 应用过滤
const filter = document.getElementById('alert-filter').value;
let filtered = alerts;
if (filter !== 'all') {
filtered = alerts.filter(alert => alert.level === filter);
}
// 应用排序
const sort = document.getElementById('alert-sort').value;
filtered.sort((a, b) => {
switch (sort) {
case 'time-desc':
return new Date(b.created_at) - new Date(a.created_at);
case 'time-asc':
return new Date(a.created_at) - new Date(b.created_at);
case 'level-desc':
const levelOrder = { 'critical': 4, 'error': 3, 'warning': 2, 'info': 1 };
return (levelOrder[b.level] || 0) - (levelOrder[a.level] || 0);
case 'level-asc':
const levelOrderAsc = { 'critical': 4, 'error': 3, 'warning': 2, 'info': 1 };
return (levelOrderAsc[a.level] || 0) - (levelOrderAsc[b.level] || 0);
default:
return 0;
}
});
return filtered;
}
editRule(ruleName) {
// 查找规则数据
const rule = this.rules.find(r => r.name === ruleName);
if (!rule) {
this.showNotification('规则不存在', 'error');
return;
}
// 填充编辑表单
document.getElementById('edit-rule-name-original').value = rule.name;
document.getElementById('edit-rule-name').value = rule.name;
document.getElementById('edit-rule-type').value = rule.alert_type;
document.getElementById('edit-rule-level').value = rule.level;
document.getElementById('edit-rule-threshold').value = rule.threshold;
document.getElementById('edit-rule-description').value = rule.description || '';
document.getElementById('edit-rule-condition').value = rule.condition;
document.getElementById('edit-rule-interval').value = rule.check_interval;
document.getElementById('edit-rule-cooldown').value = rule.cooldown;
document.getElementById('edit-rule-enabled').checked = rule.enabled;
// 显示编辑模态框
const modal = new bootstrap.Modal(document.getElementById('editRuleModal'));
modal.show();
}
async updateRule() {
const originalName = document.getElementById('edit-rule-name-original').value;
const formData = {
name: document.getElementById('edit-rule-name').value,
description: document.getElementById('edit-rule-description').value,
alert_type: document.getElementById('edit-rule-type').value,
level: document.getElementById('edit-rule-level').value,
threshold: parseFloat(document.getElementById('edit-rule-threshold').value),
condition: document.getElementById('edit-rule-condition').value,
enabled: document.getElementById('edit-rule-enabled').checked,
check_interval: parseInt(document.getElementById('edit-rule-interval').value),
cooldown: parseInt(document.getElementById('edit-rule-cooldown').value)
};
try {
const response = await fetch(`/api/rules/${originalName}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formData)
});
const data = await response.json();
if (data.success) {
this.showNotification('规则更新成功', 'success');
this.hideModal('editRuleModal');
this.loadRules();
} else {
this.showNotification(data.message || '更新规则失败', 'error');
}
} catch (error) {
console.error('更新规则失败:', error);
this.showNotification('更新规则失败', 'error');
}
}
resetRuleForm() {
document.getElementById('rule-form').reset();
document.getElementById('rule-interval').value = '300';
document.getElementById('rule-cooldown').value = '3600';
document.getElementById('rule-enabled').checked = true;
}
hideModal(modalId) {
const modal = document.getElementById(modalId);
const bsModal = bootstrap.Modal.getInstance(modal);
if (bsModal) {
bsModal.hide();
}
}
showNotification(message, type = 'info') {
// 创建通知元素
const notification = document.createElement('div');
notification.className = `alert alert-${type === 'error' ? 'danger' : type} alert-dismissible fade show position-fixed`;
notification.style.cssText = 'top: 20px; right: 20px; z-index: 9999; min-width: 300px;';
notification.innerHTML = `
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.body.appendChild(notification);
// 3秒后自动移除
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}, 3000);
}
getLevelText(level) {
const levelMap = {
'critical': '严重',
'error': '错误',
'warning': '警告',
'info': '信息'
};
return levelMap[level] || level;
}
getTypeText(type) {
const typeMap = {
'performance': '性能',
'quality': '质量',
'volume': '量级',
'system': '系统',
'business': '业务'
};
return typeMap[type] || type;
}
getHealthStatusText(status) {
const statusMap = {
'excellent': '优秀',
'good': '良好',
'fair': '一般',
'poor': '较差',
'critical': '严重',
'unknown': '未知'
};
return statusMap[status] || status;
}
formatTime(timestamp) {
const date = new Date(timestamp);
const now = new Date();
const diff = now - date;
if (diff < 60000) { // 1分钟内
return '刚刚';
} else if (diff < 3600000) { // 1小时内
return `${Math.floor(diff / 60000)}分钟前`;
} else if (diff < 86400000) { // 1天内
return `${Math.floor(diff / 3600000)}小时前`;
} else {
return date.toLocaleDateString();
}
}
}
// 初始化应用
let alertManager;
document.addEventListener('DOMContentLoaded', () => {
alertManager = new AlertManager();
});

410
src/web/static/js/chat.js Normal file
View File

@@ -0,0 +1,410 @@
// 实时对话前端脚本
class ChatClient {
constructor() {
this.websocket = null;
this.sessionId = null;
this.isConnected = false;
this.messageCount = 0;
this.init();
}
init() {
this.bindEvents();
this.updateConnectionStatus(false);
}
bindEvents() {
// 开始对话
document.getElementById('start-chat').addEventListener('click', () => this.startChat());
// 结束对话
document.getElementById('end-chat').addEventListener('click', () => this.endChat());
// 发送消息
document.getElementById('send-button').addEventListener('click', () => this.sendMessage());
// 回车发送
document.getElementById('message-input').addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
this.sendMessage();
}
});
// 创建工单
document.getElementById('create-work-order').addEventListener('click', () => this.showWorkOrderModal());
document.getElementById('create-work-order-btn').addEventListener('click', () => this.createWorkOrder());
// 快速操作按钮
document.querySelectorAll('.quick-action-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const message = e.target.getAttribute('data-message');
document.getElementById('message-input').value = message;
this.sendMessage();
});
});
}
async startChat() {
try {
// 连接WebSocket
await this.connectWebSocket();
// 创建会话
const userId = document.getElementById('user-id').value || 'anonymous';
const workOrderId = document.getElementById('work-order-id').value || null;
const response = await this.sendWebSocketMessage({
type: 'create_session',
user_id: userId,
work_order_id: workOrderId ? parseInt(workOrderId) : null
});
if (response.type === 'session_created') {
this.sessionId = response.session_id;
this.updateSessionInfo();
this.enableChat();
this.addSystemMessage('对话已开始,请描述您的问题。');
} else {
this.showError('创建会话失败');
}
} catch (error) {
console.error('启动对话失败:', error);
this.showError('启动对话失败: ' + error.message);
}
}
async endChat() {
try {
if (this.sessionId) {
await this.sendWebSocketMessage({
type: 'end_session',
session_id: this.sessionId
});
}
this.sessionId = null;
this.disableChat();
this.addSystemMessage('对话已结束。');
} catch (error) {
console.error('结束对话失败:', error);
}
}
async sendMessage() {
const input = document.getElementById('message-input');
const message = input.value.trim();
if (!message || !this.sessionId) {
return;
}
// 清空输入框
input.value = '';
// 添加用户消息
this.addMessage('user', message);
// 显示打字指示器
this.showTypingIndicator();
try {
const response = await this.sendWebSocketMessage({
type: 'send_message',
session_id: this.sessionId,
message: message
});
this.hideTypingIndicator();
if (response.type === 'message_response' && response.result.success) {
const result = response.result;
// 添加助手回复
this.addMessage('assistant', result.content, {
knowledge_used: result.knowledge_used,
confidence_score: result.confidence_score,
work_order_id: result.work_order_id
});
// 更新工单ID
if (result.work_order_id) {
document.getElementById('work-order-id').value = result.work_order_id;
}
} else {
this.addMessage('assistant', '抱歉,我暂时无法处理您的问题。请稍后再试。');
}
} catch (error) {
this.hideTypingIndicator();
console.error('发送消息失败:', error);
this.addMessage('assistant', '发送消息失败,请检查网络连接。');
}
}
async createWorkOrder() {
const title = document.getElementById('wo-title').value;
const description = document.getElementById('wo-description').value;
const category = document.getElementById('wo-category').value;
const priority = document.getElementById('wo-priority').value;
if (!title || !description) {
this.showError('请填写工单标题和描述');
return;
}
try {
const response = await this.sendWebSocketMessage({
type: 'create_work_order',
session_id: this.sessionId,
title: title,
description: description,
category: category,
priority: priority
});
if (response.type === 'work_order_created' && response.result.success) {
const workOrderId = response.result.work_order_id;
document.getElementById('work-order-id').value = workOrderId;
this.addSystemMessage(`工单创建成功!工单号: ${response.result.order_id}`);
// 关闭模态框
const modal = bootstrap.Modal.getInstance(document.getElementById('workOrderModal'));
modal.hide();
// 清空表单
document.getElementById('work-order-form').reset();
} else {
this.showError('创建工单失败: ' + (response.result.error || '未知错误'));
}
} catch (error) {
console.error('创建工单失败:', error);
this.showError('创建工单失败: ' + error.message);
}
}
connectWebSocket() {
return new Promise((resolve, reject) => {
try {
this.websocket = new WebSocket('ws://localhost:8765');
// 设置连接超时
const timeout = setTimeout(() => {
if (this.websocket.readyState !== WebSocket.OPEN) {
this.websocket.close();
reject(new Error('WebSocket连接超时请检查服务器是否启动'));
}
}, 5000); // 5秒超时
this.websocket.onopen = () => {
clearTimeout(timeout);
this.isConnected = true;
this.updateConnectionStatus(true);
resolve();
};
this.websocket.onclose = () => {
clearTimeout(timeout);
this.isConnected = false;
this.updateConnectionStatus(false);
};
this.websocket.onerror = (error) => {
clearTimeout(timeout);
console.error('WebSocket错误:', error);
reject(new Error('WebSocket连接失败请检查服务器是否启动'));
};
this.websocket.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
this.handleWebSocketMessage(data);
} catch (error) {
console.error('解析WebSocket消息失败:', error);
}
};
} catch (error) {
reject(error);
}
});
}
sendWebSocketMessage(message) {
return new Promise((resolve, reject) => {
if (!this.websocket || this.websocket.readyState !== WebSocket.OPEN) {
reject(new Error('WebSocket未连接'));
return;
}
const messageId = 'msg_' + Date.now();
message.messageId = messageId;
// 设置超时
const timeout = setTimeout(() => {
reject(new Error('请求超时'));
}, 10000);
// 监听响应
const handleResponse = (event) => {
try {
const data = JSON.parse(event.data);
if (data.messageId === messageId) {
clearTimeout(timeout);
this.websocket.removeEventListener('message', handleResponse);
resolve(data);
}
} catch (error) {
// 忽略解析错误
}
};
this.websocket.addEventListener('message', handleResponse);
this.websocket.send(JSON.stringify(message));
});
}
handleWebSocketMessage(data) {
// 处理WebSocket消息
console.log('收到WebSocket消息:', data);
}
addMessage(role, content, metadata = {}) {
const messagesContainer = document.getElementById('chat-messages');
// 如果是第一条消息,清空欢迎信息
if (this.messageCount === 0) {
messagesContainer.innerHTML = '';
}
const messageDiv = document.createElement('div');
messageDiv.className = `message ${role}`;
const avatar = document.createElement('div');
avatar.className = 'message-avatar';
avatar.textContent = role === 'user' ? 'U' : 'A';
const contentDiv = document.createElement('div');
contentDiv.className = 'message-content';
contentDiv.innerHTML = content;
// 添加时间戳
const timeDiv = document.createElement('div');
timeDiv.className = 'message-time';
timeDiv.textContent = new Date().toLocaleTimeString();
contentDiv.appendChild(timeDiv);
// 添加元数据
if (metadata.knowledge_used && metadata.knowledge_used.length > 0) {
const knowledgeDiv = document.createElement('div');
knowledgeDiv.className = 'knowledge-info';
knowledgeDiv.innerHTML = `<i class="fas fa-lightbulb me-1"></i>基于 ${metadata.knowledge_used.length} 条知识库信息生成`;
contentDiv.appendChild(knowledgeDiv);
}
if (metadata.confidence_score) {
const confidenceDiv = document.createElement('div');
confidenceDiv.className = 'confidence-score';
confidenceDiv.textContent = `置信度: ${(metadata.confidence_score * 100).toFixed(1)}%`;
contentDiv.appendChild(confidenceDiv);
}
if (metadata.work_order_id) {
const workOrderDiv = document.createElement('div');
workOrderDiv.className = 'work-order-info';
workOrderDiv.innerHTML = `<i class="fas fa-ticket-alt me-1"></i>关联工单: ${metadata.work_order_id}`;
contentDiv.appendChild(workOrderDiv);
}
if (role === 'user') {
messageDiv.appendChild(contentDiv);
messageDiv.appendChild(avatar);
} else {
messageDiv.appendChild(avatar);
messageDiv.appendChild(contentDiv);
}
messagesContainer.appendChild(messageDiv);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
this.messageCount++;
}
addSystemMessage(content) {
const messagesContainer = document.getElementById('chat-messages');
const messageDiv = document.createElement('div');
messageDiv.className = 'text-center text-muted py-2';
messageDiv.innerHTML = `<small><i class="fas fa-info-circle me-1"></i>${content}</small>`;
messagesContainer.appendChild(messageDiv);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}
showTypingIndicator() {
document.getElementById('typing-indicator').classList.add('show');
}
hideTypingIndicator() {
document.getElementById('typing-indicator').classList.remove('show');
}
updateConnectionStatus(connected) {
const statusElement = document.getElementById('connection-status');
if (connected) {
statusElement.className = 'connection-status connected';
statusElement.innerHTML = '<i class="fas fa-circle me-1"></i>已连接';
} else {
statusElement.className = 'connection-status disconnected';
statusElement.innerHTML = '<i class="fas fa-circle me-1"></i>未连接';
}
}
updateSessionInfo() {
const sessionInfo = document.getElementById('session-info');
sessionInfo.innerHTML = `
<div><strong>会话ID:</strong> ${this.sessionId}</div>
<div><strong>消息数:</strong> ${this.messageCount}</div>
<div><strong>状态:</strong> 活跃</div>
`;
}
enableChat() {
document.getElementById('start-chat').disabled = true;
document.getElementById('end-chat').disabled = false;
document.getElementById('message-input').disabled = false;
document.getElementById('send-button').disabled = false;
}
disableChat() {
document.getElementById('start-chat').disabled = false;
document.getElementById('end-chat').disabled = true;
document.getElementById('message-input').disabled = true;
document.getElementById('send-button').disabled = true;
}
showWorkOrderModal() {
if (!this.sessionId) {
this.showError('请先开始对话');
return;
}
const modal = new bootstrap.Modal(document.getElementById('workOrderModal'));
modal.show();
}
showError(message) {
this.addSystemMessage(`<span class="text-danger">错误: ${message}</span>`);
}
}
// 初始化聊天客户端
document.addEventListener('DOMContentLoaded', () => {
window.chatClient = new ChatClient();
});

View File

@@ -0,0 +1,334 @@
// HTTP版本实时对话前端脚本
class ChatHttpClient {
constructor() {
this.sessionId = null;
this.messageCount = 0;
this.apiBase = '/api/chat';
this.init();
}
init() {
this.bindEvents();
this.updateConnectionStatus(true);
}
bindEvents() {
// 开始对话
document.getElementById('start-chat').addEventListener('click', () => this.startChat());
// 结束对话
document.getElementById('end-chat').addEventListener('click', () => this.endChat());
// 发送消息
document.getElementById('send-button').addEventListener('click', () => this.sendMessage());
// 回车发送
document.getElementById('message-input').addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
this.sendMessage();
}
});
// 创建工单
document.getElementById('create-work-order').addEventListener('click', () => this.showWorkOrderModal());
document.getElementById('create-work-order-btn').addEventListener('click', () => this.createWorkOrder());
// 快速操作按钮
document.querySelectorAll('.quick-action-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const message = e.target.getAttribute('data-message');
document.getElementById('message-input').value = message;
this.sendMessage();
});
});
}
async startChat() {
try {
// 创建会话
const userId = document.getElementById('user-id').value || 'anonymous';
const workOrderId = document.getElementById('work-order-id').value || null;
const response = await this.sendRequest('POST', '/session', {
user_id: userId,
work_order_id: workOrderId ? parseInt(workOrderId) : null
});
if (response.success) {
this.sessionId = response.session_id;
this.updateSessionInfo();
this.enableChat();
this.addSystemMessage('对话已开始,请描述您的问题。');
} else {
this.showError('创建会话失败');
}
} catch (error) {
console.error('启动对话失败:', error);
this.showError('启动对话失败: ' + error.message);
}
}
async endChat() {
try {
if (this.sessionId) {
await this.sendRequest('DELETE', `/session/${this.sessionId}`);
}
this.sessionId = null;
this.disableChat();
this.addSystemMessage('对话已结束。');
} catch (error) {
console.error('结束对话失败:', error);
}
}
async sendMessage() {
const input = document.getElementById('message-input');
const message = input.value.trim();
if (!message || !this.sessionId) {
return;
}
// 清空输入框
input.value = '';
// 添加用户消息
this.addMessage('user', message);
// 显示打字指示器
this.showTypingIndicator();
try {
const response = await this.sendRequest('POST', '/message', {
session_id: this.sessionId,
message: message
});
this.hideTypingIndicator();
if (response.success) {
// 添加助手回复
this.addMessage('assistant', response.content, {
knowledge_used: response.knowledge_used,
confidence_score: response.confidence_score,
work_order_id: response.work_order_id
});
// 更新工单ID
if (response.work_order_id) {
document.getElementById('work-order-id').value = response.work_order_id;
}
} else {
this.addMessage('assistant', '抱歉,我暂时无法处理您的问题。请稍后再试。');
}
} catch (error) {
this.hideTypingIndicator();
console.error('发送消息失败:', error);
this.addMessage('assistant', '发送消息失败,请检查网络连接。');
}
}
async createWorkOrder() {
const title = document.getElementById('wo-title').value;
const description = document.getElementById('wo-description').value;
const category = document.getElementById('wo-category').value;
const priority = document.getElementById('wo-priority').value;
if (!title || !description) {
this.showError('请填写工单标题和描述');
return;
}
try {
const response = await this.sendRequest('POST', '/work-order', {
session_id: this.sessionId,
title: title,
description: description,
category: category,
priority: priority
});
if (response.success) {
const workOrderId = response.work_order_id;
document.getElementById('work-order-id').value = workOrderId;
this.addSystemMessage(`工单创建成功!工单号: ${response.order_id}`);
// 关闭模态框
const modal = bootstrap.Modal.getInstance(document.getElementById('workOrderModal'));
modal.hide();
// 清空表单
document.getElementById('work-order-form').reset();
} else {
this.showError('创建工单失败: ' + (response.error || '未知错误'));
}
} catch (error) {
console.error('创建工单失败:', error);
this.showError('创建工单失败: ' + error.message);
}
}
async sendRequest(method, endpoint, data = null) {
const url = this.apiBase + endpoint;
const options = {
method: method,
headers: {
'Content-Type': 'application/json',
}
};
if (data) {
options.body = JSON.stringify(data);
}
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP错误: ${response.status}`);
}
return await response.json();
}
addMessage(role, content, metadata = {}) {
const messagesContainer = document.getElementById('chat-messages');
// 如果是第一条消息,清空欢迎信息
if (this.messageCount === 0) {
messagesContainer.innerHTML = '';
}
const messageDiv = document.createElement('div');
messageDiv.className = `message ${role}`;
const avatar = document.createElement('div');
avatar.className = 'message-avatar';
avatar.textContent = role === 'user' ? 'U' : 'A';
const contentDiv = document.createElement('div');
contentDiv.className = 'message-content';
contentDiv.innerHTML = content;
// 添加时间戳
const timeDiv = document.createElement('div');
timeDiv.className = 'message-time';
timeDiv.textContent = new Date().toLocaleTimeString();
contentDiv.appendChild(timeDiv);
// 添加元数据
if (metadata.knowledge_used && metadata.knowledge_used.length > 0) {
const knowledgeDiv = document.createElement('div');
knowledgeDiv.className = 'knowledge-info';
knowledgeDiv.innerHTML = `<i class="fas fa-lightbulb me-1"></i>基于 ${metadata.knowledge_used.length} 条知识库信息生成`;
contentDiv.appendChild(knowledgeDiv);
}
if (metadata.confidence_score) {
const confidenceDiv = document.createElement('div');
confidenceDiv.className = 'confidence-score';
confidenceDiv.textContent = `置信度: ${(metadata.confidence_score * 100).toFixed(1)}%`;
contentDiv.appendChild(confidenceDiv);
}
if (metadata.work_order_id) {
const workOrderDiv = document.createElement('div');
workOrderDiv.className = 'work-order-info';
workOrderDiv.innerHTML = `<i class="fas fa-ticket-alt me-1"></i>关联工单: ${metadata.work_order_id}`;
contentDiv.appendChild(workOrderDiv);
}
if (role === 'user') {
messageDiv.appendChild(contentDiv);
messageDiv.appendChild(avatar);
} else {
messageDiv.appendChild(avatar);
messageDiv.appendChild(contentDiv);
}
messagesContainer.appendChild(messageDiv);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
this.messageCount++;
}
addSystemMessage(content) {
const messagesContainer = document.getElementById('chat-messages');
const messageDiv = document.createElement('div');
messageDiv.className = 'text-center text-muted py-2';
messageDiv.innerHTML = `<small><i class="fas fa-info-circle me-1"></i>${content}</small>`;
messagesContainer.appendChild(messageDiv);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}
showTypingIndicator() {
document.getElementById('typing-indicator').classList.add('show');
}
hideTypingIndicator() {
document.getElementById('typing-indicator').classList.remove('show');
}
updateConnectionStatus(connected) {
const statusElement = document.getElementById('connection-status');
if (connected) {
statusElement.className = 'connection-status connected';
statusElement.innerHTML = '<i class="fas fa-circle me-1"></i>HTTP连接';
} else {
statusElement.className = 'connection-status disconnected';
statusElement.innerHTML = '<i class="fas fa-circle me-1"></i>连接断开';
}
}
updateSessionInfo() {
const sessionInfo = document.getElementById('session-info');
sessionInfo.innerHTML = `
<div><strong>会话ID:</strong> ${this.sessionId}</div>
<div><strong>消息数:</strong> ${this.messageCount}</div>
<div><strong>状态:</strong> 活跃</div>
`;
}
enableChat() {
document.getElementById('start-chat').disabled = true;
document.getElementById('end-chat').disabled = false;
document.getElementById('message-input').disabled = false;
document.getElementById('send-button').disabled = false;
}
disableChat() {
document.getElementById('start-chat').disabled = false;
document.getElementById('end-chat').disabled = true;
document.getElementById('message-input').disabled = true;
document.getElementById('send-button').disabled = true;
}
showWorkOrderModal() {
if (!this.sessionId) {
this.showError('请先开始对话');
return;
}
const modal = new bootstrap.Modal(document.getElementById('workOrderModal'));
modal.show();
}
showError(message) {
this.addSystemMessage(`<span class="text-danger">错误: ${message}</span>`);
}
}
// 初始化聊天客户端
document.addEventListener('DOMContentLoaded', () => {
window.chatClient = new ChatHttpClient();
});

File diff suppressed because it is too large Load Diff

332
src/web/templates/chat.html Normal file
View File

@@ -0,0 +1,332 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TSP助手实时对话</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<style>
.chat-container {
height: 100vh;
display: flex;
flex-direction: column;
}
.chat-header {
background: linear-gradient(135deg, #007bff, #0056b3);
color: white;
padding: 1rem;
border-radius: 0.5rem 0.5rem 0 0;
}
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 1rem;
background-color: #f8f9fa;
border-left: 1px solid #dee2e6;
border-right: 1px solid #dee2e6;
}
.message {
margin-bottom: 1rem;
display: flex;
align-items: flex-start;
}
.message.user {
justify-content: flex-end;
}
.message.assistant {
justify-content: flex-start;
}
.message-content {
max-width: 70%;
padding: 0.75rem 1rem;
border-radius: 1rem;
position: relative;
}
.message.user .message-content {
background: linear-gradient(135deg, #007bff, #0056b3);
color: white;
border-bottom-right-radius: 0.25rem;
}
.message.assistant .message-content {
background: white;
color: #333;
border: 1px solid #dee2e6;
border-bottom-left-radius: 0.25rem;
}
.message-time {
font-size: 0.75rem;
opacity: 0.7;
margin-top: 0.25rem;
}
.message-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 0.5rem;
font-size: 0.875rem;
}
.message.user .message-avatar {
background: linear-gradient(135deg, #007bff, #0056b3);
color: white;
}
.message.assistant .message-avatar {
background: #28a745;
color: white;
}
.chat-input {
padding: 1rem;
background: white;
border-top: 1px solid #dee2e6;
border-radius: 0 0 0.5rem 0.5rem;
}
.typing-indicator {
display: none;
padding: 0.75rem 1rem;
color: #6c757d;
font-style: italic;
}
.typing-indicator.show {
display: block;
}
.knowledge-info {
background: #e3f2fd;
border-left: 4px solid #2196f3;
padding: 0.5rem 1rem;
margin: 0.5rem 0;
border-radius: 0.25rem;
font-size: 0.875rem;
}
.confidence-score {
font-size: 0.75rem;
opacity: 0.7;
margin-top: 0.25rem;
}
.work-order-info {
background: #fff3cd;
border: 1px solid #ffeaa7;
padding: 0.5rem 1rem;
margin: 0.5rem 0;
border-radius: 0.25rem;
font-size: 0.875rem;
}
.connection-status {
position: fixed;
top: 10px;
right: 10px;
padding: 0.5rem 1rem;
border-radius: 1rem;
font-size: 0.875rem;
z-index: 1000;
}
.connection-status.connected {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.connection-status.disconnected {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.quick-actions {
display: flex;
gap: 0.5rem;
margin-bottom: 1rem;
flex-wrap: wrap;
}
.quick-action-btn {
padding: 0.25rem 0.75rem;
font-size: 0.875rem;
border-radius: 1rem;
border: 1px solid #007bff;
background: white;
color: #007bff;
cursor: pointer;
transition: all 0.2s;
}
.quick-action-btn:hover {
background: #007bff;
color: white;
}
</style>
</head>
<body>
<div class="container-fluid">
<div class="row">
<!-- 侧边栏 -->
<div class="col-md-3">
<div class="card h-100">
<div class="card-header">
<h5><i class="fas fa-cog me-2"></i>对话控制</h5>
</div>
<div class="card-body">
<div class="mb-3">
<label class="form-label">用户ID</label>
<input type="text" class="form-control" id="user-id" value="user_001">
</div>
<div class="mb-3">
<label class="form-label">工单ID (可选)</label>
<input type="number" class="form-control" id="work-order-id" placeholder="留空则自动创建">
</div>
<div class="d-grid gap-2">
<button class="btn btn-primary" id="start-chat">
<i class="fas fa-play me-2"></i>开始对话
</button>
<button class="btn btn-secondary" id="end-chat" disabled>
<i class="fas fa-stop me-2"></i>结束对话
</button>
<button class="btn btn-info" id="create-work-order">
<i class="fas fa-plus me-2"></i>创建工单
</button>
</div>
<hr>
<div class="mb-3">
<h6>快速操作</h6>
<div class="quick-actions">
<button class="quick-action-btn" data-message="我的车辆无法远程启动">远程启动问题</button>
<button class="quick-action-btn" data-message="APP显示车辆信息错误">APP显示问题</button>
<button class="quick-action-btn" data-message="蓝牙授权失败">蓝牙授权问题</button>
<button class="quick-action-btn" data-message="如何解绑车辆">解绑车辆</button>
</div>
</div>
<div class="mb-3">
<h6>会话信息</h6>
<div id="session-info" class="text-muted">
未开始对话
</div>
</div>
</div>
</div>
</div>
<!-- 聊天区域 -->
<div class="col-md-9">
<div class="card chat-container">
<div class="chat-header">
<div class="d-flex justify-content-between align-items-center">
<div>
<h4><i class="fas fa-robot me-2"></i>TSP智能助手</h4>
<small>基于知识库的智能客服系统</small>
</div>
<div id="connection-status" class="connection-status disconnected">
<i class="fas fa-circle me-1"></i>未连接
</div>
</div>
</div>
<div class="chat-messages" id="chat-messages">
<div class="text-center text-muted py-5">
<i class="fas fa-comments fa-3x mb-3"></i>
<h5>欢迎使用TSP智能助手</h5>
<p>请点击"开始对话"按钮开始聊天</p>
</div>
</div>
<div class="typing-indicator" id="typing-indicator">
<i class="fas fa-spinner fa-spin me-2"></i>助手正在思考中...
</div>
<div class="chat-input">
<div class="input-group">
<input type="text" class="form-control" id="message-input"
placeholder="请输入您的问题..." disabled>
<button class="btn btn-primary" id="send-button" disabled>
<i class="fas fa-paper-plane"></i>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 创建工单模态框 -->
<div class="modal fade" id="workOrderModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">创建工单</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="work-order-form">
<div class="mb-3">
<label class="form-label">工单标题</label>
<input type="text" class="form-control" id="wo-title" required>
</div>
<div class="mb-3">
<label class="form-label">问题描述</label>
<textarea class="form-control" id="wo-description" rows="3" required></textarea>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">问题分类</label>
<select class="form-select" id="wo-category">
<option value="技术问题">技术问题</option>
<option value="APP功能">APP功能</option>
<option value="远程控制">远程控制</option>
<option value="车辆绑定">车辆绑定</option>
<option value="其他">其他</option>
</select>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">优先级</label>
<select class="form-select" id="wo-priority">
<option value="low"></option>
<option value="medium" selected></option>
<option value="high"></option>
<option value="urgent">紧急</option>
</select>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" id="create-work-order-btn">创建工单</button>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="{{ url_for('static', filename='js/chat.js') }}"></script>
</body>
</html>

View File

@@ -0,0 +1,332 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TSP助手实时对话 (HTTP版本)</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<style>
.chat-container {
height: 100vh;
display: flex;
flex-direction: column;
}
.chat-header {
background: linear-gradient(135deg, #007bff, #0056b3);
color: white;
padding: 1rem;
border-radius: 0.5rem 0.5rem 0 0;
}
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 1rem;
background-color: #f8f9fa;
border-left: 1px solid #dee2e6;
border-right: 1px solid #dee2e6;
}
.message {
margin-bottom: 1rem;
display: flex;
align-items: flex-start;
}
.message.user {
justify-content: flex-end;
}
.message.assistant {
justify-content: flex-start;
}
.message-content {
max-width: 70%;
padding: 0.75rem 1rem;
border-radius: 1rem;
position: relative;
}
.message.user .message-content {
background: linear-gradient(135deg, #007bff, #0056b3);
color: white;
border-bottom-right-radius: 0.25rem;
}
.message.assistant .message-content {
background: white;
color: #333;
border: 1px solid #dee2e6;
border-bottom-left-radius: 0.25rem;
}
.message-time {
font-size: 0.75rem;
opacity: 0.7;
margin-top: 0.25rem;
}
.message-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 0.5rem;
font-size: 0.875rem;
}
.message.user .message-avatar {
background: linear-gradient(135deg, #007bff, #0056b3);
color: white;
}
.message.assistant .message-avatar {
background: #28a745;
color: white;
}
.chat-input {
padding: 1rem;
background: white;
border-top: 1px solid #dee2e6;
border-radius: 0 0 0.5rem 0.5rem;
}
.typing-indicator {
display: none;
padding: 0.75rem 1rem;
color: #6c757d;
font-style: italic;
}
.typing-indicator.show {
display: block;
}
.knowledge-info {
background: #e3f2fd;
border-left: 4px solid #2196f3;
padding: 0.5rem 1rem;
margin: 0.5rem 0;
border-radius: 0.25rem;
font-size: 0.875rem;
}
.confidence-score {
font-size: 0.75rem;
opacity: 0.7;
margin-top: 0.25rem;
}
.work-order-info {
background: #fff3cd;
border: 1px solid #ffeaa7;
padding: 0.5rem 1rem;
margin: 0.5rem 0;
border-radius: 0.25rem;
font-size: 0.875rem;
}
.connection-status {
position: fixed;
top: 10px;
right: 10px;
padding: 0.5rem 1rem;
border-radius: 1rem;
font-size: 0.875rem;
z-index: 1000;
}
.connection-status.connected {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.connection-status.disconnected {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.quick-actions {
display: flex;
gap: 0.5rem;
margin-bottom: 1rem;
flex-wrap: wrap;
}
.quick-action-btn {
padding: 0.25rem 0.75rem;
font-size: 0.875rem;
border-radius: 1rem;
border: 1px solid #007bff;
background: white;
color: #007bff;
cursor: pointer;
transition: all 0.2s;
}
.quick-action-btn:hover {
background: #007bff;
color: white;
}
</style>
</head>
<body>
<div class="container-fluid">
<div class="row">
<!-- 侧边栏 -->
<div class="col-md-3">
<div class="card h-100">
<div class="card-header">
<h5><i class="fas fa-cog me-2"></i>对话控制</h5>
</div>
<div class="card-body">
<div class="mb-3">
<label class="form-label">用户ID</label>
<input type="text" class="form-control" id="user-id" value="user_001">
</div>
<div class="mb-3">
<label class="form-label">工单ID (可选)</label>
<input type="number" class="form-control" id="work-order-id" placeholder="留空则自动创建">
</div>
<div class="d-grid gap-2">
<button class="btn btn-primary" id="start-chat">
<i class="fas fa-play me-2"></i>开始对话
</button>
<button class="btn btn-secondary" id="end-chat" disabled>
<i class="fas fa-stop me-2"></i>结束对话
</button>
<button class="btn btn-info" id="create-work-order">
<i class="fas fa-plus me-2"></i>创建工单
</button>
</div>
<hr>
<div class="mb-3">
<h6>快速操作</h6>
<div class="quick-actions">
<button class="quick-action-btn" data-message="我的车辆无法远程启动">远程启动问题</button>
<button class="quick-action-btn" data-message="APP显示车辆信息错误">APP显示问题</button>
<button class="quick-action-btn" data-message="蓝牙授权失败">蓝牙授权问题</button>
<button class="quick-action-btn" data-message="如何解绑车辆">解绑车辆</button>
</div>
</div>
<div class="mb-3">
<h6>会话信息</h6>
<div id="session-info" class="text-muted">
未开始对话
</div>
</div>
</div>
</div>
</div>
<!-- 聊天区域 -->
<div class="col-md-9">
<div class="card chat-container">
<div class="chat-header">
<div class="d-flex justify-content-between align-items-center">
<div>
<h4><i class="fas fa-robot me-2"></i>TSP智能助手 (HTTP版本)</h4>
<small>基于知识库的智能客服系统</small>
</div>
<div id="connection-status" class="connection-status connected">
<i class="fas fa-circle me-1"></i>HTTP连接
</div>
</div>
</div>
<div class="chat-messages" id="chat-messages">
<div class="text-center text-muted py-5">
<i class="fas fa-comments fa-3x mb-3"></i>
<h5>欢迎使用TSP智能助手</h5>
<p>请点击"开始对话"按钮开始聊天</p>
</div>
</div>
<div class="typing-indicator" id="typing-indicator">
<i class="fas fa-spinner fa-spin me-2"></i>助手正在思考中...
</div>
<div class="chat-input">
<div class="input-group">
<input type="text" class="form-control" id="message-input"
placeholder="请输入您的问题..." disabled>
<button class="btn btn-primary" id="send-button" disabled>
<i class="fas fa-paper-plane"></i>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 创建工单模态框 -->
<div class="modal fade" id="workOrderModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">创建工单</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="work-order-form">
<div class="mb-3">
<label class="form-label">工单标题</label>
<input type="text" class="form-control" id="wo-title" required>
</div>
<div class="mb-3">
<label class="form-label">问题描述</label>
<textarea class="form-control" id="wo-description" rows="3" required></textarea>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">问题分类</label>
<select class="form-select" id="wo-category">
<option value="技术问题">技术问题</option>
<option value="APP功能">APP功能</option>
<option value="远程控制">远程控制</option>
<option value="车辆绑定">车辆绑定</option>
<option value="其他">其他</option>
</select>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">优先级</label>
<select class="form-select" id="wo-priority">
<option value="low"></option>
<option value="medium" selected></option>
<option value="high"></option>
<option value="urgent">紧急</option>
</select>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" id="create-work-order-btn">创建工单</button>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="{{ url_for('static', filename='js/chat_http.js') }}"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,385 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TSP助手预警管理系统</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet">
</head>
<body>
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container-fluid">
<a class="navbar-brand" href="#">
<i class="fas fa-shield-alt me-2"></i>
TSP助手预警管理
</a>
<div class="navbar-nav ms-auto">
<span class="navbar-text" id="monitor-status">
<i class="fas fa-circle text-warning"></i> 监控状态检查中...
</span>
</div>
</div>
</nav>
<div class="container-fluid mt-4">
<div class="row">
<!-- 侧边栏 -->
<div class="col-md-3">
<div class="card">
<div class="card-header">
<h5><i class="fas fa-tachometer-alt me-2"></i>控制面板</h5>
</div>
<div class="card-body">
<div class="d-grid gap-2">
<button class="btn btn-success" id="start-monitor">
<i class="fas fa-play me-2"></i>启动监控
</button>
<button class="btn btn-danger" id="stop-monitor">
<i class="fas fa-stop me-2"></i>停止监控
</button>
<button class="btn btn-info" id="check-alerts">
<i class="fas fa-search me-2"></i>检查预警
</button>
</div>
</div>
</div>
<!-- 系统健康状态 -->
<div class="card mt-3">
<div class="card-header">
<h5><i class="fas fa-heartbeat me-2"></i>系统健康</h5>
</div>
<div class="card-body">
<div class="health-score">
<div class="score-circle" id="health-score-circle">
<span id="health-score-text">0</span>
</div>
<div class="health-status" id="health-status">检查中...</div>
</div>
</div>
</div>
</div>
<!-- 主内容区 -->
<div class="col-md-9">
<!-- 预警统计卡片 -->
<div class="row mb-4">
<div class="col-md-3">
<div class="card bg-danger text-white">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h4 id="critical-alerts">0</h4>
<p class="mb-0">严重预警</p>
</div>
<div class="align-self-center">
<i class="fas fa-exclamation-triangle fa-2x"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-warning text-white">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h4 id="warning-alerts">0</h4>
<p class="mb-0">警告预警</p>
</div>
<div class="align-self-center">
<i class="fas fa-exclamation-circle fa-2x"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-info text-white">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h4 id="info-alerts">0</h4>
<p class="mb-0">信息预警</p>
</div>
<div class="align-self-center">
<i class="fas fa-info-circle fa-2x"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-success text-white">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h4 id="total-alerts">0</h4>
<p class="mb-0">总预警数</p>
</div>
<div class="align-self-center">
<i class="fas fa-chart-line fa-2x"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 预警列表 -->
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5><i class="fas fa-bell me-2"></i>活跃预警</h5>
<div class="d-flex gap-2">
<select class="form-select form-select-sm" id="alert-filter" style="width: auto;">
<option value="all">全部预警</option>
<option value="critical">严重</option>
<option value="error">错误</option>
<option value="warning">警告</option>
<option value="info">信息</option>
</select>
<select class="form-select form-select-sm" id="alert-sort" style="width: auto;">
<option value="time-desc">时间降序</option>
<option value="time-asc">时间升序</option>
<option value="level-desc">级别降序</option>
<option value="level-asc">级别升序</option>
</select>
<button class="btn btn-sm btn-outline-primary" id="refresh-alerts">
<i class="fas fa-sync-alt me-1"></i>刷新
</button>
</div>
</div>
<div class="card-body">
<div id="alerts-container">
<div class="text-center py-4">
<i class="fas fa-spinner fa-spin fa-2x text-muted"></i>
<p class="text-muted mt-2">加载中...</p>
</div>
</div>
</div>
</div>
<!-- 预警规则管理 -->
<div class="card mt-4">
<div class="card-header d-flex justify-content-between align-items-center">
<h5><i class="fas fa-cogs me-2"></i>预警规则管理</h5>
<button class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#ruleModal">
<i class="fas fa-plus me-1"></i>添加规则
</button>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>规则名称</th>
<th>类型</th>
<th>级别</th>
<th>阈值</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody id="rules-table">
<tr>
<td colspan="6" class="text-center">
<i class="fas fa-spinner fa-spin me-2"></i>加载中...
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 添加/编辑规则模态框 -->
<div class="modal fade" id="ruleModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="rule-modal-title">添加预警规则</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="rule-form">
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">规则名称</label>
<input type="text" class="form-control" id="rule-name" required>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">预警类型</label>
<select class="form-select" id="rule-type" required>
<option value="performance">性能预警</option>
<option value="quality">质量预警</option>
<option value="volume">量级预警</option>
<option value="system">系统预警</option>
<option value="business">业务预警</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">预警级别</label>
<select class="form-select" id="rule-level" required>
<option value="info">信息</option>
<option value="warning">警告</option>
<option value="error">错误</option>
<option value="critical">严重</option>
</select>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">阈值</label>
<input type="number" class="form-control" id="rule-threshold" step="0.01" required>
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label">规则描述</label>
<textarea class="form-control" id="rule-description" rows="2"></textarea>
</div>
<div class="mb-3">
<label class="form-label">条件表达式</label>
<input type="text" class="form-control" id="rule-condition" placeholder="例如: satisfaction_avg < threshold" required>
</div>
<div class="row">
<div class="col-md-4">
<div class="mb-3">
<label class="form-label">检查间隔(秒)</label>
<input type="number" class="form-control" id="rule-interval" value="300">
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label class="form-label">冷却时间(秒)</label>
<input type="number" class="form-control" id="rule-cooldown" value="3600">
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<div class="form-check mt-4">
<input class="form-check-input" type="checkbox" id="rule-enabled" checked>
<label class="form-check-label">启用规则</label>
</div>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" id="save-rule">保存规则</button>
</div>
</div>
</div>
</div>
<!-- 编辑规则模态框 -->
<div class="modal fade" id="editRuleModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">编辑预警规则</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="edit-rule-form">
<input type="hidden" id="edit-rule-name-original">
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">规则名称</label>
<input type="text" class="form-control" id="edit-rule-name" required>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">预警类型</label>
<select class="form-select" id="edit-rule-type" required>
<option value="performance">性能预警</option>
<option value="quality">质量预警</option>
<option value="volume">量级预警</option>
<option value="system">系统预警</option>
<option value="business">业务预警</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">预警级别</label>
<select class="form-select" id="edit-rule-level" required>
<option value="info">信息</option>
<option value="warning">警告</option>
<option value="error">错误</option>
<option value="critical">严重</option>
</select>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">阈值</label>
<input type="number" class="form-control" id="edit-rule-threshold" step="0.01" required>
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label">规则描述</label>
<textarea class="form-control" id="edit-rule-description" rows="2"></textarea>
</div>
<div class="mb-3">
<label class="form-label">条件表达式</label>
<input type="text" class="form-control" id="edit-rule-condition" placeholder="例如: satisfaction_avg < threshold" required>
</div>
<div class="row">
<div class="col-md-4">
<div class="mb-3">
<label class="form-label">检查间隔(秒)</label>
<input type="number" class="form-control" id="edit-rule-interval" value="300">
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label class="form-label">冷却时间(秒)</label>
<input type="number" class="form-control" id="edit-rule-cooldown" value="3600">
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<div class="form-check mt-4">
<input class="form-check-input" type="checkbox" id="edit-rule-enabled" checked>
<label class="form-check-label">启用规则</label>
</div>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" id="update-rule">更新规则</button>
</div>
</div>
</div>
</div>
<!-- 脚本 -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="{{ url_for('static', filename='js/app.js') }}"></script>
</body>
</html>

243
src/web/websocket_server.py Normal file
View File

@@ -0,0 +1,243 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
WebSocket实时通信服务器
提供实时对话功能
"""
import asyncio
import json
import logging
from datetime import datetime
from typing import Dict, Set
import websockets
from websockets.server import WebSocketServerProtocol
from ..dialogue.realtime_chat import RealtimeChatManager
logger = logging.getLogger(__name__)
class WebSocketServer:
"""WebSocket服务器"""
def __init__(self, host: str = "localhost", port: int = 8765):
self.host = host
self.port = port
self.chat_manager = RealtimeChatManager()
self.connected_clients: Set[WebSocketServerProtocol] = set()
async def register_client(self, websocket: WebSocketServerProtocol):
"""注册客户端"""
self.connected_clients.add(websocket)
logger.info(f"客户端连接: {websocket.remote_address}")
async def unregister_client(self, websocket: WebSocketServerProtocol):
"""注销客户端"""
self.connected_clients.discard(websocket)
logger.info(f"客户端断开: {websocket.remote_address}")
async def handle_message(self, websocket: WebSocketServerProtocol, message: str):
"""处理客户端消息"""
try:
data = json.loads(message)
message_type = data.get("type")
message_id = data.get("messageId") # 获取消息ID
if message_type == "create_session":
await self._handle_create_session(websocket, data, message_id)
elif message_type == "send_message":
await self._handle_send_message(websocket, data, message_id)
elif message_type == "get_history":
await self._handle_get_history(websocket, data, message_id)
elif message_type == "create_work_order":
await self._handle_create_work_order(websocket, data, message_id)
elif message_type == "get_work_order_status":
await self._handle_get_work_order_status(websocket, data, message_id)
elif message_type == "end_session":
await self._handle_end_session(websocket, data, message_id)
else:
await self._send_error(websocket, "未知消息类型", message_id)
except json.JSONDecodeError:
await self._send_error(websocket, "JSON格式错误")
except Exception as e:
logger.error(f"处理消息失败: {e}")
await self._send_error(websocket, f"处理消息失败: {str(e)}")
async def _handle_create_session(self, websocket: WebSocketServerProtocol, data: Dict, message_id: str = None):
"""处理创建会话请求"""
user_id = data.get("user_id", "anonymous")
work_order_id = data.get("work_order_id")
session_id = self.chat_manager.create_session(user_id, work_order_id)
response = {
"type": "session_created",
"session_id": session_id,
"timestamp": datetime.now().isoformat()
}
if message_id:
response["messageId"] = message_id
await websocket.send(json.dumps(response, ensure_ascii=False))
async def _handle_send_message(self, websocket: WebSocketServerProtocol, data: Dict, message_id: str = None):
"""处理发送消息请求"""
session_id = data.get("session_id")
message = data.get("message")
if not session_id or not message:
await self._send_error(websocket, "缺少必要参数", message_id)
return
# 处理消息
result = self.chat_manager.process_message(session_id, message)
response = {
"type": "message_response",
"session_id": session_id,
"result": result,
"timestamp": datetime.now().isoformat()
}
if message_id:
response["messageId"] = message_id
await websocket.send(json.dumps(response, ensure_ascii=False))
async def _handle_get_history(self, websocket: WebSocketServerProtocol, data: Dict, message_id: str = None):
"""处理获取历史记录请求"""
session_id = data.get("session_id")
if not session_id:
await self._send_error(websocket, "缺少会话ID", message_id)
return
history = self.chat_manager.get_session_history(session_id)
response = {
"type": "history_response",
"session_id": session_id,
"history": history,
"timestamp": datetime.now().isoformat()
}
if message_id:
response["messageId"] = message_id
await websocket.send(json.dumps(response, ensure_ascii=False))
async def _handle_create_work_order(self, websocket: WebSocketServerProtocol, data: Dict, message_id: str = None):
"""处理创建工单请求"""
session_id = data.get("session_id")
title = data.get("title")
description = data.get("description")
category = data.get("category", "技术问题")
priority = data.get("priority", "medium")
if not session_id or not title or not description:
await self._send_error(websocket, "缺少必要参数", message_id)
return
result = self.chat_manager.create_work_order(session_id, title, description, category, priority)
response = {
"type": "work_order_created",
"session_id": session_id,
"result": result,
"timestamp": datetime.now().isoformat()
}
if message_id:
response["messageId"] = message_id
await websocket.send(json.dumps(response, ensure_ascii=False))
async def _handle_get_work_order_status(self, websocket: WebSocketServerProtocol, data: Dict, message_id: str = None):
"""处理获取工单状态请求"""
work_order_id = data.get("work_order_id")
if not work_order_id:
await self._send_error(websocket, "缺少工单ID", message_id)
return
result = self.chat_manager.get_work_order_status(work_order_id)
response = {
"type": "work_order_status",
"work_order_id": work_order_id,
"result": result,
"timestamp": datetime.now().isoformat()
}
if message_id:
response["messageId"] = message_id
await websocket.send(json.dumps(response, ensure_ascii=False))
async def _handle_end_session(self, websocket: WebSocketServerProtocol, data: Dict, message_id: str = None):
"""处理结束会话请求"""
session_id = data.get("session_id")
if not session_id:
await self._send_error(websocket, "缺少会话ID", message_id)
return
success = self.chat_manager.end_session(session_id)
response = {
"type": "session_ended",
"session_id": session_id,
"success": success,
"timestamp": datetime.now().isoformat()
}
if message_id:
response["messageId"] = message_id
await websocket.send(json.dumps(response, ensure_ascii=False))
async def _send_error(self, websocket: WebSocketServerProtocol, error_message: str, message_id: str = None):
"""发送错误消息"""
response = {
"type": "error",
"message": error_message,
"timestamp": datetime.now().isoformat()
}
if message_id:
response["messageId"] = message_id
await websocket.send(json.dumps(response, ensure_ascii=False))
async def handle_client(self, websocket: WebSocketServerProtocol, path: str):
"""处理客户端连接"""
await self.register_client(websocket)
try:
async for message in websocket:
await self.handle_message(websocket, message)
except websockets.exceptions.ConnectionClosed:
pass
finally:
await self.unregister_client(websocket)
async def start_server(self):
"""启动WebSocket服务器"""
logger.info(f"启动WebSocket服务器: ws://{self.host}:{self.port}")
async with websockets.serve(self.handle_client, self.host, self.port):
await asyncio.Future() # 保持服务器运行
def run(self):
"""运行服务器"""
asyncio.run(self.start_server())
if __name__ == "__main__":
# 设置日志
logging.basicConfig(level=logging.INFO)
# 启动服务器
server = WebSocketServer()
server.run()