feat: 交互Agent + VLM优化 + 企微演示模式

- 新增交互Agent调度器(意图识别 + 工单/查询/报表/闲聊4个Handler)
- 新增工单服务、Excel报表生成器、企微消息加解密模块
- VLM提示词优化(角色设定、≤25字描述、布尔值优先输出)
- VLM降级策略(入侵默认放行、离岗默认拦截)
- 企微演示模式(WECHAT_TEST_UIDS兜底 + SERVICE_BASE_URL修复)
- 新增Agent回调路由和测试接口

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-09 10:42:32 +08:00
parent 1d84456c0f
commit 7cc4f604d0
13 changed files with 827 additions and 54 deletions

View File

@@ -0,0 +1,144 @@
"""
工单服务
提供工单的创建、查询、更新功能。
由交互Agent的工单Handler调用。
"""
import uuid
from datetime import datetime, timezone
from typing import Optional, Dict, List, Tuple
from app.models import WorkOrder, WorkOrderStatus, WorkOrderPriority, get_session
from app.utils.logger import logger
def generate_order_no() -> str:
"""生成工单编号: WO + YYYYMMDDHHmmss + 6位uuid"""
ts = datetime.now(timezone.utc).strftime("%Y%m%d%H%M%S")
return f"WO{ts}{uuid.uuid4().hex[:6].upper()}"
class WorkOrderService:
"""工单服务"""
def create_work_order(
self,
title: str,
description: str = "",
priority: str = "medium",
assignee_uid: str = "",
assignee_name: str = "",
alarm_id: str = "",
) -> Optional[WorkOrder]:
"""创建工单"""
db = get_session()
try:
valid_priorities = [e.value for e in WorkOrderPriority]
order = WorkOrder(
order_no=generate_order_no(),
title=title,
description=description,
priority=WorkOrderPriority(priority) if priority in valid_priorities else WorkOrderPriority.MEDIUM,
assignee_id=assignee_uid,
assignee_name=assignee_name,
status=WorkOrderStatus.CREATED,
)
if alarm_id:
order.alert_no = alarm_id
db.add(order)
db.commit()
db.refresh(order)
logger.info(f"工单已创建: {order.order_no}, title={title}")
return order
except Exception as e:
db.rollback()
logger.error(f"创建工单失败: {e}")
return None
finally:
db.close()
def get_work_order(self, order_no: str) -> Optional[WorkOrder]:
"""查询工单"""
db = get_session()
try:
return db.query(WorkOrder).filter(WorkOrder.order_no == order_no).first()
finally:
db.close()
def get_work_orders(
self,
status: Optional[str] = None,
assignee_id: Optional[str] = None,
page: int = 1,
page_size: int = 20,
) -> Tuple[List[WorkOrder], int]:
"""分页查询工单"""
db = get_session()
try:
query = db.query(WorkOrder)
if status:
query = query.filter(WorkOrder.status == status)
if assignee_id:
query = query.filter(WorkOrder.assignee_id == assignee_id)
total = query.count()
orders = (
query.order_by(WorkOrder.created_at.desc())
.offset((page - 1) * page_size)
.limit(page_size)
.all()
)
return orders, total
finally:
db.close()
def update_status(
self,
order_no: str,
status: str,
result: str = "",
) -> Optional[WorkOrder]:
"""更新工单状态"""
db = get_session()
try:
order = db.query(WorkOrder).filter(WorkOrder.order_no == order_no).first()
if not order:
return None
valid_statuses = [e.value for e in WorkOrderStatus]
if status in valid_statuses:
order.status = WorkOrderStatus(status)
if result:
order.result = result
now = datetime.now(timezone.utc)
if status == "processing" and not order.started_at:
order.started_at = now
elif status == "completed":
order.completed_at = now
order.updated_at = now
db.commit()
db.refresh(order)
logger.info(f"工单状态更新: {order_no} -> {status}")
return order
except Exception as e:
db.rollback()
logger.error(f"更新工单失败: {e}")
return None
finally:
db.close()
# 全局单例
_work_order_service: Optional[WorkOrderService] = None
def get_work_order_service() -> WorkOrderService:
global _work_order_service
if _work_order_service is None:
_work_order_service = WorkOrderService()
return _work_order_service