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:
@@ -3,10 +3,13 @@
|
||||
|
||||
处理安保人员在企微卡片上的操作(前往处理/已处理/误报忽略)。
|
||||
提供告警详情接口供 H5 页面使用。
|
||||
接收企微用户消息并路由到交互Agent。
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from fastapi import APIRouter, Depends, Query, Request
|
||||
from fastapi.responses import PlainTextResponse
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
|
||||
@@ -117,3 +120,85 @@ async def alarm_action_callback(
|
||||
)
|
||||
|
||||
return YudaoResponse.success(True)
|
||||
|
||||
|
||||
# ==================== 交互Agent消息回调 ====================
|
||||
|
||||
@router.get("/agent/callback")
|
||||
async def wechat_agent_verify(
|
||||
msg_signature: str = Query(...),
|
||||
timestamp: str = Query(...),
|
||||
nonce: str = Query(...),
|
||||
echostr: str = Query(...),
|
||||
):
|
||||
"""企微回调URL验证(首次配置时调用)"""
|
||||
from app.services.wechat_crypto import WeChatCrypto
|
||||
try:
|
||||
crypto = WeChatCrypto()
|
||||
echo = crypto.verify_url(msg_signature, timestamp, nonce, echostr)
|
||||
return PlainTextResponse(content=echo)
|
||||
except Exception as e:
|
||||
logger.error(f"企微URL验证失败: {e}")
|
||||
return PlainTextResponse(content="", status_code=403)
|
||||
|
||||
|
||||
@router.post("/agent/callback")
|
||||
async def wechat_agent_message(
|
||||
request: Request,
|
||||
msg_signature: str = Query(...),
|
||||
timestamp: str = Query(...),
|
||||
nonce: str = Query(...),
|
||||
):
|
||||
"""接收企微用户消息,路由到交互Agent"""
|
||||
body = await request.body()
|
||||
|
||||
from app.services.wechat_crypto import WeChatCrypto
|
||||
try:
|
||||
crypto = WeChatCrypto()
|
||||
msg = crypto.decrypt_message(body, msg_signature, timestamp, nonce)
|
||||
except Exception as e:
|
||||
logger.error(f"企微消息解密失败: {e}")
|
||||
return PlainTextResponse(content="success")
|
||||
|
||||
# 只处理文本消息
|
||||
if msg.get("MsgType") != "text":
|
||||
return PlainTextResponse(content="success")
|
||||
|
||||
user_id = msg.get("FromUserName", "")
|
||||
content = msg.get("Content", "")
|
||||
|
||||
logger.info(f"收到企微消息: user={user_id}, content={content[:50]}")
|
||||
|
||||
# 异步处理(企微要求5秒内响应),通过主动消息回复
|
||||
asyncio.create_task(_process_agent_message(user_id, content))
|
||||
return PlainTextResponse(content="success")
|
||||
|
||||
|
||||
async def _process_agent_message(user_id: str, content: str):
|
||||
"""异步处理消息并主动回复"""
|
||||
try:
|
||||
from app.services.agent_dispatcher import get_agent_dispatcher
|
||||
from app.services.wechat_service import get_wechat_service
|
||||
|
||||
dispatcher = get_agent_dispatcher()
|
||||
reply = await dispatcher.handle_message(user_id, content)
|
||||
|
||||
wechat = get_wechat_service()
|
||||
await wechat.send_text_message(user_id, reply)
|
||||
except Exception as e:
|
||||
logger.error(f"Agent消息处理失败: user={user_id}, error={e}", exc_info=True)
|
||||
|
||||
|
||||
# ==================== Agent测试接口(开发用) ====================
|
||||
|
||||
@router.post("/agent/test")
|
||||
async def test_agent_message(
|
||||
user_id: str = Query(default="test_user"),
|
||||
content: str = Query(..., description="测试消息内容"),
|
||||
):
|
||||
"""测试Agent对话(开发用,无加密,直接返回回复)"""
|
||||
from app.services.agent_dispatcher import get_agent_dispatcher
|
||||
dispatcher = get_agent_dispatcher()
|
||||
reply = await dispatcher.handle_message(user_id, content)
|
||||
return YudaoResponse.success({"user_id": user_id, "content": content, "reply": reply})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user