Files
assist/src/web/app.py
赵杰 Jie Zhao (雄狮汽车科技) da4736c323 feat: 重大功能更新 v1.4.0 - 飞书集成、AI语义相似度、前端优化
主要更新内容:
- 🚀 飞书多维表格集成,支持工单数据同步
- 🤖 AI建议与人工描述语义相似度计算
- 🎨 前端UI全面优化,现代化设计
- 📊 智能知识库入库策略(AI准确率<90%使用人工描述)
- 🔧 代码重构,模块化架构优化
- 📚 完整文档整合和更新
- 🐛 修复配置导入和数据库字段问题

技术特性:
- 使用sentence-transformers进行语义相似度计算
- 快速模式结合TF-IDF和语义方法
- 响应式设计,支持移动端
- 加载状态和动画效果
- 配置化AI准确率阈值
2025-09-19 19:32:42 +01:00

766 lines
26 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""
TSP助手预警管理Web应用
提供预警系统的Web界面和API接口
重构版本 - 使用蓝图架构
"""
import sys
import os
import logging
from datetime import datetime, timedelta
from flask import Flask, render_template, request, jsonify, send_from_directory
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.dialogue.realtime_chat import RealtimeChatManager
# from src.vehicle.vehicle_data_manager import VehicleDataManager
from src.core.database import db_manager
from src.core.models import Conversation, Alert, WorkOrder
# from src.core.query_optimizer import query_optimizer
# 导入蓝图
from src.web.blueprints.alerts import alerts_bp
from src.web.blueprints.workorders import workorders_bp
from src.web.blueprints.conversations import conversations_bp
from src.web.blueprints.knowledge import knowledge_bp
from src.web.blueprints.monitoring import monitoring_bp
from src.web.blueprints.system import system_bp
from src.web.blueprints.feishu_sync import feishu_sync_bp
# 配置日志
logger = logging.getLogger(__name__)
# 抑制 /api/health 的访问日志
werkzeug_logger = logging.getLogger('werkzeug')
class HealthLogFilter(logging.Filter):
def filter(self, record):
try:
msg = record.getMessage()
return '/api/health' not in msg
except Exception:
return True
werkzeug_logger.addFilter(HealthLogFilter())
# 创建Flask应用
app = Flask(__name__)
CORS(app)
# 配置上传文件夹
UPLOAD_FOLDER = 'uploads'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max file size
# 延迟初始化TSP助手和Agent助手避免启动时重复初始化
assistant = None
agent_assistant = None
chat_manager = None
vehicle_manager = None
def get_assistant():
"""获取TSP助手实例懒加载"""
global assistant
if assistant is None:
from src.main import TSPAssistant
assistant = TSPAssistant()
return assistant
def get_agent_assistant():
"""获取Agent助手实例懒加载"""
global agent_assistant
if agent_assistant is None:
from src.agent_assistant import TSPAgentAssistant
agent_assistant = TSPAgentAssistant()
return agent_assistant
def get_chat_manager():
"""获取聊天管理器实例(懒加载)"""
global chat_manager
if chat_manager is None:
from src.dialogue.realtime_chat import RealtimeChatManager
chat_manager = RealtimeChatManager()
return chat_manager
def get_vehicle_manager():
"""获取车辆数据管理器实例(懒加载)"""
global vehicle_manager
if vehicle_manager is None:
from src.vehicle.vehicle_data_manager import VehicleDataManager
vehicle_manager = VehicleDataManager()
return vehicle_manager
# 注册蓝图
app.register_blueprint(alerts_bp)
app.register_blueprint(workorders_bp)
app.register_blueprint(conversations_bp)
app.register_blueprint(knowledge_bp)
app.register_blueprint(monitoring_bp)
app.register_blueprint(system_bp)
app.register_blueprint(feishu_sync_bp)
# 页面路由
@app.route('/')
def index():
"""主页 - 综合管理平台"""
return render_template('dashboard.html')
@app.route('/alerts')
def alerts():
"""预警管理页面"""
return render_template('index.html')
@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('/uploads/<filename>')
def uploaded_file(filename):
"""提供上传文件的下载服务"""
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
# 核心API路由
@app.route('/api/health')
def get_health():
"""获取系统健康状态附加1小时业务指标"""
try:
base = get_assistant().get_system_health() or {}
# 追加数据库近1小时指标
with db_manager.get_session() as session:
since = datetime.now() - timedelta(hours=1)
conv_count = session.query(Conversation).filter(Conversation.timestamp >= since).count()
resp_times = [c.response_time for c in session.query(Conversation).filter(Conversation.timestamp >= since).all() if c.response_time]
avg_resp = round(sum(resp_times)/len(resp_times), 2) if resp_times else 0
open_wos = session.query(WorkOrder).filter(WorkOrder.status == 'open').count()
levels = session.query(Alert.level).filter(Alert.is_active == True).all()
level_map = {}
for (lvl,) in levels:
level_map[lvl] = level_map.get(lvl, 0) + 1
base.update({
"throughput_1h": conv_count,
"avg_response_time_1h": avg_resp,
"open_workorders": open_wos,
"active_alerts_by_level": level_map
})
return jsonify(base)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/rules')
def get_rules():
"""获取预警规则列表"""
try:
rules = get_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:
from src.analytics.alert_system import AlertRule, AlertLevel, AlertType
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 = get_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 = get_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 = get_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 = get_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 = get_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 = get_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 = get_assistant().check_alerts()
return jsonify({
"success": True,
"alerts": alerts,
"count": len(alerts)
})
except Exception as e:
return jsonify({"error": str(e)}), 500
# 实时对话相关路由
@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 = get_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 = get_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 = get_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 = get_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 = get_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 = get_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 = get_agent_assistant().get_agent_status()
return jsonify(status)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/agent/action-history')
def get_agent_action_history():
"""获取Agent动作执行历史"""
try:
limit = request.args.get('limit', 50, type=int)
history = get_agent_assistant().get_action_history(limit)
return jsonify({
"success": True,
"history": history,
"count": len(history)
})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/agent/trigger-sample', methods=['POST'])
def trigger_sample_action():
"""触发示例动作"""
try:
import asyncio
result = asyncio.run(get_agent_assistant().trigger_sample_actions())
return jsonify(result)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/agent/clear-history', methods=['POST'])
def clear_agent_history():
"""清空Agent执行历史"""
try:
result = get_agent_assistant().clear_execution_history()
return jsonify(result)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/agent/llm-stats')
def get_llm_stats():
"""获取LLM使用统计"""
try:
stats = get_agent_assistant().get_llm_usage_stats()
return jsonify({
"success": True,
"stats": stats
})
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 = get_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 = get_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 = get_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 = get_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 = get_agent_assistant().run_intelligent_analysis()
return jsonify({"success": True, "analysis": analysis})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/agent/chat', methods=['POST'])
def agent_chat():
"""Agent对话接口"""
try:
data = request.get_json()
message = data.get('message', '')
context = data.get('context', {})
if not message:
return jsonify({"error": "消息不能为空"}), 400
# 使用Agent助手处理消息
agent_assistant = get_agent_assistant()
# 模拟Agent处理实际应该调用真正的Agent处理逻辑
import asyncio
result = asyncio.run(agent_assistant.process_message_agent(
message=message,
user_id=context.get('user_id', 'admin'),
work_order_id=None,
enable_proactive=True
))
return jsonify({
"success": True,
"response": result.get('response', 'Agent已处理您的请求'),
"actions": result.get('actions', []),
"status": result.get('status', 'completed')
})
except Exception as e:
return jsonify({"error": str(e)}), 500
# Agent 工具统计与自定义工具
@app.route('/api/agent/tools/stats')
def get_agent_tools_stats():
try:
tools = get_agent_assistant().agent_core.tool_manager.get_available_tools()
performance = get_agent_assistant().agent_core.tool_manager.get_tool_performance_report()
return jsonify({
"success": True,
"tools": tools,
"performance": performance
})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/agent/tools/execute', methods=['POST'])
def execute_agent_tool():
"""执行指定的Agent工具"""
try:
data = request.get_json() or {}
tool_name = data.get('tool') or data.get('name')
parameters = data.get('parameters') or {}
if not tool_name:
return jsonify({"error": "缺少工具名称tool"}), 400
import asyncio
result = asyncio.run(get_agent_assistant().agent_core.tool_manager.execute_tool(tool_name, parameters))
return jsonify(result)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/agent/tools/register', methods=['POST'])
def register_custom_tool():
"""注册自定义工具(仅登记元数据,函数为占位符)"""
try:
data = request.get_json() or {}
name = data.get('name')
description = data.get('description', '')
if not name:
return jsonify({"error": "缺少工具名称"}), 400
def _placeholder_tool(**kwargs):
return {"message": f"自定义工具 {name} 已登记(占位),当前不可执行", "params": kwargs}
get_agent_assistant().agent_core.tool_manager.register_tool(
name,
_placeholder_tool,
metadata={"description": description, "custom": True}
)
return jsonify({"success": True, "message": "工具已注册"})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/agent/tools/unregister/<name>', methods=['DELETE'])
def unregister_custom_tool(name):
try:
success = get_agent_assistant().agent_core.tool_manager.unregister_tool(name)
return jsonify({"success": success})
except Exception as e:
return jsonify({"error": str(e)}), 500
# 分析相关API
@app.route('/api/analytics')
def get_analytics():
"""获取分析数据"""
try:
# 支持多种参数
time_range = request.args.get('timeRange', request.args.get('days', '30'))
dimension = request.args.get('dimension', 'workorders')
analytics = generate_db_analytics(int(time_range), dimension)
return jsonify(analytics)
except Exception as e:
return jsonify({"error": str(e)}), 500
def generate_db_analytics(days: int, dimension: str) -> dict:
"""基于数据库生成真实分析数据(优化版)"""
# 使用优化后的查询
return query_optimizer.get_analytics_optimized(days)
@app.route('/api/analytics/export')
def export_analytics():
"""导出分析报告"""
try:
# 生成Excel报告使用数据库真实数据
analytics = generate_db_analytics(30, 'workorders')
# 创建工作簿
from openpyxl import Workbook
from openpyxl.styles import Font
wb = Workbook()
ws = wb.active
ws.title = "分析报告"
# 添加标题
ws['A1'] = 'TSP智能助手分析报告'
ws['A1'].font = Font(size=16, bold=True)
# 添加工单统计
ws['A3'] = '工单统计'
ws['A3'].font = Font(bold=True)
ws['A4'] = '总工单数'
ws['B4'] = analytics['workorders']['total']
ws['A5'] = '待处理'
ws['B5'] = analytics['workorders']['open']
ws['A6'] = '已解决'
ws['B6'] = analytics['workorders']['resolved']
# 保存文件
report_path = 'uploads/analytics_report.xlsx'
os.makedirs('uploads', exist_ok=True)
wb.save(report_path)
from flask import send_file
return send_file(report_path, as_attachment=True, download_name='analytics_report.xlsx')
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')
vehicle_vin = request.args.get('vehicle_vin')
data_type = request.args.get('data_type')
limit = request.args.get('limit', 10, type=int)
if vehicle_vin:
data = vehicle_manager.get_vehicle_data_by_vin(vehicle_vin, data_type, limit)
elif 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/vin/<vehicle_vin>/latest')
def get_latest_vehicle_data_by_vin(vehicle_vin):
"""按VIN获取车辆最新数据"""
try:
data = vehicle_manager.get_latest_vehicle_data_by_vin(vehicle_vin)
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
# API测试相关接口
@app.route('/api/test/connection', methods=['POST'])
def test_api_connection():
"""测试API连接"""
try:
data = request.get_json()
api_provider = data.get('api_provider', 'openai')
api_base_url = data.get('api_base_url', '')
api_key = data.get('api_key', '')
model_name = data.get('model_name', 'qwen-turbo')
# 这里可以调用LLM客户端进行连接测试
# 暂时返回模拟结果
return jsonify({
"success": True,
"message": f"API连接测试成功 - {api_provider}",
"response_time": "150ms",
"model_status": "可用"
})
except Exception as e:
return jsonify({"success": False, "error": str(e)}), 500
@app.route('/api/test/model', methods=['POST'])
def test_model_response():
"""测试模型回答"""
try:
data = request.get_json()
test_message = data.get('test_message', '你好,请简单介绍一下你自己')
# 这里可以调用LLM客户端进行回答测试
# 暂时返回模拟结果
return jsonify({
"success": True,
"test_message": test_message,
"response": "你好我是TSP智能助手基于大语言模型构建的智能客服系统。我可以帮助您解决车辆相关问题提供技术支持和服务。",
"response_time": "1.2s",
"tokens_used": 45
})
except Exception as e:
return jsonify({"success": False, "error": str(e)}), 500
# 飞书同步功能已合并到主页面,不再需要单独的路由
if __name__ == '__main__':
import time
app.config['START_TIME'] = time.time()
app.config['SERVER_PORT'] = 5000
app.config['WEBSOCKET_PORT'] = 8765
app.run(debug=True, host='0.0.0.0', port=5000)