P1-2: 统一Dify调用点 - 删除detect-intent死链路(approval/byod) + routing_service共享client修复连接泄漏 + 前端死代码清理

This commit is contained in:
Simon
2026-07-18 01:05:20 +08:00
parent af392f2bf0
commit aeb4e0cf39
5 changed files with 54 additions and 424 deletions
+3 -155
View File
@@ -1,17 +1,15 @@
# =============================================================================
# IT智能服务台 — 自备电脑补贴(BYOD)资格查询 API
# =============================================================================
# 说明:提供自备电脑补贴资格的意图检测和资格检查功能
# - 意图检测:关键词预过滤 → Dify → 关键词兜底(复用审批系统三级模式)
# 说明:提供自备电脑补贴资格检查功能
# - 资格检查:通过企微通讯录API获取员工岗位 → 与资格清单匹配 → 返回结果
# - 岗位匹配支持精确匹配和模糊匹配(如"高级前端开发岗"匹配"前端开发岗"
# - BYOD 意图检测已并入主 Dify 对话链路(v4.0 P1-2 删除独立 detect-intent 端点)
# =============================================================================
import json
import logging
from typing import Optional
import httpx
from fastapi import APIRouter, Depends
from pydantic import BaseModel
import redis.asyncio as aioredis
@@ -144,17 +142,6 @@ BYOD_REGISTER_NOTES: list[str] = [
# Schema 定义
# =============================================================================
class ByodEligibilityRequest(BaseModel):
"""BYOD 意图检测请求
Attributes:
text: 用户消息文本
employee_id: 员工ID(可选,传给 Dify 作为用户标识)
"""
text: str
employee_id: Optional[str] = None
class ByodCheckEligibilityRequest(BaseModel):
"""BYOD 资格检查请求
@@ -234,74 +221,7 @@ def _match_position(position: str) -> tuple[bool, str, str]:
# =============================================================================
# Dify 意图识别(复用审批意图识别 Dify 应用)
# =============================================================================
# 说明:BYOD 意图识别复用审批系统的 Dify 应用(同一个 approval_dify_base_url /
# approval_dify_api_key),Dify 后台的 System Prompt 已更新为同时支持
# 审批意图和 BYOD 意图识别。Dify 返回的 JSON 中包含 is_byod_intent 字段。
async def _call_dify_byod_intent(text: str, employee_id: str = "") -> dict:
"""调用 Dify 意图识别应用,检测 BYOD 意图。
复用审批意图识别的 Dify 应用(approval_dify_base_url / approval_dify_api_key),
Dify 的 System Prompt 已更新为同时返回 is_byod_intent 字段。
Dify 原生 API 调用方式与 approval.py 中的 _call_dify_approval_intent 一致:
POST {base_url}/v1/chat-messagesblocking 模式。
Args:
text: 用户消息文本
employee_id: 员工 ID(可选,传给 Dify 的 user 字段)
Returns:
dict: {
"is_byod_intent": bool,
"confidence": float,
}
Raises:
Exception: Dify 调用失败或响应解析失败
"""
base_url = settings.approval_dify_base_url
api_key = settings.approval_dify_api_key
timeout = settings.approval_dify_timeout
if not base_url or not api_key:
raise ValueError(
"Dify 意图识别应用未配置(APPROVAL_DIFY_BASE_URL / APPROVAL_DIFY_API_KEY"
)
# 构建请求 URLbase_url + /v1/chat-messagesDify 原生 API
url = f"{base_url.rstrip('/')}/v1/chat-messages"
body = {
"inputs": {},
"query": text,
"response_mode": "blocking",
"user": employee_id or "byod_detection",
}
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
}
async with httpx.AsyncClient(timeout=httpx.Timeout(timeout)) as client:
response = await client.post(url, json=body, headers=headers)
response.raise_for_status()
data = response.json()
# 解析 Dify 原生响应:answer 字段包含 AI 返回的文本(JSON 字符串)
answer = data.get("answer", "")
parsed = json.loads(answer)
return {
"is_byod_intent": bool(parsed.get("is_byod_intent", False)),
"confidence": float(parsed.get("confidence", 0.0)),
}
# =============================================================================
# 关键词预过滤 & 降级兜底
# 关键词预过滤
# =============================================================================
def _byod_keyword_prefilter(text: str) -> bool:
@@ -322,82 +242,10 @@ def _byod_keyword_prefilter(text: str) -> bool:
return any(kw.lower() in lower_text for kw in BYOD_PREFILTER_KEYWORDS)
def _byod_fallback_detect(text: str) -> tuple[bool, float]:
"""BYOD 关键词兜底:Dify 不可用时通过关键词匹配判断 BYOD 意图。
遍历 BYOD_PREFILTER_KEYWORDS,命中任意一个即认为有 BYOD 意图。
置信度取 0.6(预过滤已通过说明有 BYOD 相关关键词)。
Args:
text: 用户消息文本
Returns:
tuple: (is_byod_intent, confidence)
"""
lower_text = (text or "").lower()
for kw in BYOD_PREFILTER_KEYWORDS:
if kw.lower() in lower_text:
return True, 0.6
return False, 0.0
# =============================================================================
# API 端点
# =============================================================================
@router.post("/byod/detect-intent")
async def detect_byod_intent(request: ByodEligibilityRequest):
"""BYOD 意图检测端点。
流程(复用审批系统三级模式):
1. 关键词预过滤 — 未命中 BYOD 关键词直接返回 false(避免每条消息都调 Dify)
2. 命中关键词 → 调用 Dify 意图识别应用(复用审批 Dify 应用)
3. Dify 调用失败 → 降级为关键词匹配(兜底)
Args:
request: 包含 text(用户消息)和可选的 employee_id
Returns:
ByodEligibilityResponse: 检测结果(仅 is_byod_intent 和 source 字段有效)
"""
text = request.text or ""
# 1. 关键词预过滤
if not _byod_keyword_prefilter(text):
return success_response(data=ByodEligibilityResponse(
is_byod_intent=False,
eligible=False,
source="keyword_prefilter",
))
# 2. 调用 Dify 意图识别
try:
result = await _call_dify_byod_intent(text, request.employee_id or "")
threshold = settings.approval_confidence_threshold
is_byod = result["is_byod_intent"] and result["confidence"] >= threshold
logger.info(
f"BYOD意图检测(Dify): is_byod={is_byod}, "
f"confidence={result['confidence']}"
)
return success_response(data=ByodEligibilityResponse(
is_byod_intent=is_byod,
eligible=False,
source="dify",
))
except Exception as e:
logger.warning(f"Dify BYOD 意图识别失败,降级为关键词匹配: {e}")
# 3. 降级为关键词匹配
is_byod, confidence = _byod_fallback_detect(text)
logger.info(
f"BYOD意图检测(兜底): is_byod={is_byod}, confidence={confidence}"
)
return success_response(data=ByodEligibilityResponse(
is_byod_intent=is_byod,
eligible=False,
source="fallback",
))
@router.post("/byod/check-eligibility")
async def check_byod_eligibility(
request: ByodCheckEligibilityRequest,