feat: 重大功能更新 v1.4.0 - 飞书集成、AI语义相似度、前端优化
主要更新内容: - 🚀 飞书多维表格集成,支持工单数据同步 - 🤖 AI建议与人工描述语义相似度计算 - 🎨 前端UI全面优化,现代化设计 - 📊 智能知识库入库策略(AI准确率<90%使用人工描述) - 🔧 代码重构,模块化架构优化 - 📚 完整文档整合和更新 - 🐛 修复配置导入和数据库字段问题 技术特性: - 使用sentence-transformers进行语义相似度计算 - 快速模式结合TF-IDF和语义方法 - 响应式设计,支持移动端 - 加载状态和动画效果 - 配置化AI准确率阈值
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
- `knowledge.py`: 知识库管理相关API
|
||||
- `monitoring.py`: 监控相关API
|
||||
- `system.py`: 系统管理相关API
|
||||
- ~~`feishu_sync.py`~~: 已合并到主仪表板(删除)
|
||||
|
||||
## 蓝图模块说明
|
||||
|
||||
@@ -59,6 +60,12 @@
|
||||
- `/api/backup/*` - 数据备份
|
||||
- `/api/database/status` - 数据库状态
|
||||
|
||||
### 7. 飞书集成功能(已合并)
|
||||
- **原独立页面**: `http://localhost:5000/feishu-sync`
|
||||
- **现集成位置**: 主仪表板的"飞书同步"标签页
|
||||
- **功能**: 飞书多维表格数据同步和管理
|
||||
- **API端点**: 通过主应用路由提供
|
||||
|
||||
## 优势
|
||||
|
||||
1. **模块化**: 每个功能模块独立,便于维护
|
||||
@@ -96,7 +103,16 @@ src/web/
|
||||
│ ├── system.py # 系统管理
|
||||
│ └── README.md # 架构说明
|
||||
├── static/ # 静态文件
|
||||
│ ├── css/
|
||||
│ │ └── style.css # 样式文件(包含飞书集成样式)
|
||||
│ └── js/
|
||||
│ ├── dashboard.js # 仪表板逻辑(包含飞书同步功能)
|
||||
│ ├── chat.js # 对话功能
|
||||
│ └── app.js # 应用主逻辑
|
||||
└── templates/ # 模板文件
|
||||
├── dashboard.html # 主仪表板(包含飞书同步标签页)
|
||||
├── chat.html # 对话页面
|
||||
└── index.html # 首页
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
@@ -106,3 +122,17 @@ src/web/
|
||||
3. 懒加载模式避免启动时的重复初始化
|
||||
4. 错误处理统一在蓝图内部进行
|
||||
5. 保持与原有API接口的兼容性
|
||||
6. 飞书集成功能已从独立蓝图合并到主仪表板
|
||||
7. 前端JavaScript类管理不同功能模块(TSPDashboard、FeishuSyncManager等)
|
||||
|
||||
## 最新更新 (v1.4.0)
|
||||
|
||||
### 功能合并
|
||||
- **飞书同步页面合并**: 原独立的飞书同步页面已合并到主仪表板
|
||||
- **统一用户体验**: 所有功能现在都在一个统一的界面中
|
||||
- **代码优化**: 删除了冗余的蓝图和模板文件
|
||||
|
||||
### 架构改进
|
||||
- **前端模块化**: JavaScript代码按功能模块组织
|
||||
- **数据库扩展**: 工单表新增12个飞书相关字段
|
||||
- **字段映射**: 智能映射飞书字段到本地数据库结构
|
||||
|
||||
@@ -6,11 +6,36 @@
|
||||
|
||||
import os
|
||||
import pandas as pd
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from flask import Blueprint, request, jsonify, send_file
|
||||
from werkzeug.utils import secure_filename
|
||||
from sqlalchemy import text
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 简化的AI准确率配置类
|
||||
class SimpleAIAccuracyConfig:
|
||||
"""简化的AI准确率配置"""
|
||||
def __init__(self):
|
||||
self.auto_approve_threshold = 0.95
|
||||
self.use_human_resolution_threshold = 0.90
|
||||
self.manual_review_threshold = 0.80
|
||||
self.ai_suggestion_confidence = 0.95
|
||||
self.human_resolution_confidence = 0.90
|
||||
|
||||
def should_auto_approve(self, similarity: float) -> bool:
|
||||
return similarity >= self.auto_approve_threshold
|
||||
|
||||
def should_use_human_resolution(self, similarity: float) -> bool:
|
||||
return similarity < self.use_human_resolution_threshold
|
||||
|
||||
def get_confidence_score(self, similarity: float, use_human: bool = False) -> float:
|
||||
if use_human:
|
||||
return self.human_resolution_confidence
|
||||
else:
|
||||
return max(similarity, self.ai_suggestion_confidence)
|
||||
|
||||
from src.main import TSPAssistant
|
||||
from src.core.database import db_manager
|
||||
from src.core.models import WorkOrder, Conversation, WorkOrderSuggestion, KnowledgeEntry
|
||||
@@ -250,51 +275,101 @@ def save_workorder_human_resolution(workorder_id):
|
||||
rec = WorkOrderSuggestion(work_order_id=w.id)
|
||||
session.add(rec)
|
||||
rec.human_resolution = human_text
|
||||
# 计算相似度(使用简单cosine TF-IDF,避免外部服务依赖)
|
||||
# 计算语义相似度(使用sentence-transformers进行更准确的语义比较)
|
||||
try:
|
||||
from sklearn.feature_extraction.text import TfidfVectorizer
|
||||
from sklearn.metrics.pairwise import cosine_similarity
|
||||
texts = [rec.ai_suggestion or "", human_text]
|
||||
vec = TfidfVectorizer(max_features=1000)
|
||||
mat = vec.fit_transform(texts)
|
||||
sim = float(cosine_similarity(mat[0:1], mat[1:2])[0][0])
|
||||
except Exception:
|
||||
sim = 0.0
|
||||
from src.utils.semantic_similarity import calculate_semantic_similarity
|
||||
ai_text = rec.ai_suggestion or ""
|
||||
sim = calculate_semantic_similarity(ai_text, human_text)
|
||||
logger.info(f"AI建议与人工描述语义相似度: {sim:.4f}")
|
||||
except Exception as e:
|
||||
logger.error(f"计算语义相似度失败: {e}")
|
||||
# 回退到传统方法
|
||||
try:
|
||||
from sklearn.feature_extraction.text import TfidfVectorizer
|
||||
from sklearn.metrics.pairwise import cosine_similarity
|
||||
texts = [rec.ai_suggestion or "", human_text]
|
||||
vec = TfidfVectorizer(max_features=1000)
|
||||
mat = vec.fit_transform(texts)
|
||||
sim = float(cosine_similarity(mat[0:1], mat[1:2])[0][0])
|
||||
except Exception:
|
||||
sim = 0.0
|
||||
rec.ai_similarity = sim
|
||||
# 自动审批条件≥0.95
|
||||
approved = sim >= 0.95
|
||||
|
||||
# 使用简化的配置
|
||||
config = SimpleAIAccuracyConfig()
|
||||
|
||||
# 自动审批条件
|
||||
approved = config.should_auto_approve(sim)
|
||||
rec.approved = approved
|
||||
|
||||
# 记录使用人工描述入库的标记(当AI准确率低于阈值时)
|
||||
use_human_resolution = config.should_use_human_resolution(sim)
|
||||
rec.use_human_resolution = use_human_resolution
|
||||
|
||||
session.commit()
|
||||
return jsonify({"success": True, "similarity": sim, "approved": approved})
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"similarity": sim,
|
||||
"approved": approved,
|
||||
"use_human_resolution": use_human_resolution
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@workorders_bp.route('/<int:workorder_id>/approve-to-knowledge', methods=['POST'])
|
||||
def approve_workorder_to_knowledge(workorder_id):
|
||||
"""将已审批的AI建议入库为知识条目"""
|
||||
"""将已审批的AI建议或人工描述入库为知识条目"""
|
||||
try:
|
||||
with db_manager.get_session() as session:
|
||||
w = session.query(WorkOrder).filter(WorkOrder.id == workorder_id).first()
|
||||
if not w:
|
||||
return jsonify({"error": "工单不存在"}), 404
|
||||
|
||||
rec = session.query(WorkOrderSuggestion).filter(WorkOrderSuggestion.work_order_id == w.id).first()
|
||||
if not rec or not rec.approved or not rec.ai_suggestion:
|
||||
return jsonify({"error": "未找到可入库的已审批AI建议"}), 400
|
||||
# 入库为知识条目(问=工单标题;答=AI建议;类目用工单分类)
|
||||
if not rec:
|
||||
return jsonify({"error": "未找到工单建议记录"}), 400
|
||||
|
||||
# 使用简化的配置
|
||||
config = SimpleAIAccuracyConfig()
|
||||
|
||||
# 确定使用哪个内容入库
|
||||
if rec.use_human_resolution and rec.human_resolution:
|
||||
# AI准确率低于阈值,使用人工描述入库
|
||||
answer_content = rec.human_resolution
|
||||
confidence_score = config.get_confidence_score(rec.ai_similarity or 0, use_human=True)
|
||||
verified_by = 'human_resolution'
|
||||
logger.info(f"工单 {workorder_id} 使用人工描述入库,AI相似度: {rec.ai_similarity:.4f}")
|
||||
elif rec.approved and rec.ai_suggestion:
|
||||
# AI准确率≥阈值,使用AI建议入库
|
||||
answer_content = rec.ai_suggestion
|
||||
confidence_score = config.get_confidence_score(rec.ai_similarity or 0, use_human=False)
|
||||
verified_by = 'auto_approve'
|
||||
logger.info(f"工单 {workorder_id} 使用AI建议入库,相似度: {rec.ai_similarity:.4f}")
|
||||
else:
|
||||
return jsonify({"error": "未找到可入库的内容"}), 400
|
||||
|
||||
# 入库为知识条目
|
||||
entry = KnowledgeEntry(
|
||||
question=w.title or (w.description[:20] if w.description else '工单问题'),
|
||||
answer=rec.ai_suggestion,
|
||||
answer=answer_content,
|
||||
category=w.category or '其他',
|
||||
confidence_score=0.95,
|
||||
confidence_score=confidence_score,
|
||||
is_active=True,
|
||||
is_verified=True,
|
||||
verified_by='auto_approve',
|
||||
verified_by=verified_by,
|
||||
verified_at=datetime.now()
|
||||
)
|
||||
session.add(entry)
|
||||
session.commit()
|
||||
return jsonify({"success": True, "knowledge_id": entry.id})
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"knowledge_id": entry.id,
|
||||
"used_content": "human_resolution" if rec.use_human_resolution else "ai_suggestion",
|
||||
"confidence_score": confidence_score
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"入库知识库失败: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@workorders_bp.route('/import', methods=['POST'])
|
||||
|
||||
Reference in New Issue
Block a user