D1正式合并: 后端双模支持(主Dify读intent_type,无字段降级旧路径)+ ai_service透传路由字段 + Dify Prompt v1.3(新增路由意图标注规则+场景4)

This commit is contained in:
Simon
2026-07-18 02:46:28 +08:00
parent 1bfc00559a
commit 6f545fe0b6
4 changed files with 414 additions and 25 deletions
+100 -12
View File
@@ -1033,11 +1033,82 @@ async def _step_byod_intercept(db, conversation, employee_id, content, msg_type)
return False
async def _step_routing_intercept(db, conversation, employee_id, content, msg_type) -> bool:
"""步骤3:非IT业务路由拦截。命中发送名片返回 True(终止管线)。"""
if msg_type != "text" or not routing_keyword_prefilter(content):
async def _step_routing_from_result(db, conversation, employee_id, content, result) -> bool:
"""步骤3(D1 合并版):从主 Dify 结果读取路由意图,命中发送名片(终止管线)。
双模支持:
- 主结果含 intent_type 字段(新 Prompt)→ 直接使用,零额外 Dify 调用
- 主结果无 intent_type 字段(旧 Prompt 过渡期)→ 降级调 detect_routing_intent(旧路径)
Returns:
bool: True 表示已发送路由名片(终止管线),False 继续正常 AI 流程
"""
from app.config import settings
intent_type = result.get("intent_type")
# 兼容模式:主 Dify 未输出路由字段(Prompt 未更新)→ 旧路径兜底
if intent_type is None:
logger.info("[D1] 主结果无 intent_type 字段,降级 detect_routing_intent 旧路径")
return await _handle_routing(db, conversation, employee_id, content)
# 合并模式:主 Dify 直接输出路由意图
business_category = result.get("business_category")
routing_confidence = float(result.get("routing_confidence") or 0.0)
logger.info(
f"[D1] 路由意图(主Dify): intent_type={intent_type}, "
f"business_category={business_category}, routing_confidence={routing_confidence}"
)
# 审批意图不拦截(让审批流程处理)
if intent_type == "approval":
return False
return await _handle_routing(db, conversation, employee_id, content)
threshold = settings.routing_confidence_threshold
if intent_type != "non_it_routing" or routing_confidence < threshold:
return False
if not business_category:
logger.warning("[D1] 路由意图为 non_it_routing 但 business_category 为空,跳过")
return False
# 查询联系人
contact = await get_contact_by_category(db, business_category)
if not contact:
logger.warning(f"未找到 {business_category} 类别的联系人,跳过路由推荐")
return False
# 构建路由说明文本
category_display = business_category.replace("行政-物业", "物业")
reason = (
f"您的问题属于{category_display}业务范畴,不在IT服务台服务范围内 😊\n\n"
f"为您推荐{category_display}服务相关联系人,您可以直接点击名片联系TA:"
)
# 发送名片三段式消息
await send_contact_card(
db=db,
conversation=conversation,
employee_id=employee_id,
contact=contact,
reason=reason,
business_category=business_category,
routing_confidence=routing_confidence,
)
# 记录路由事件
await record_routing_event(
db=db,
conversation_id=str(conversation.id),
employee_id=employee_id,
message_content=content,
business_category=business_category,
routing_confidence=routing_confidence,
contact=contact,
)
return True
async def _step_local_quick_reply(db, conversation, employee_id, content, msg_type, dify_conversation_id) -> bool:
@@ -1208,6 +1279,13 @@ async def _step_call_dify(db, conversation, employee_id, conversation_id, conten
except Exception as fallback_err:
logger.error(f"[Fallback] 降级推送失败: {fallback_err}")
# D1 合并:审批关键词未命中时,路由关键词兜底(超时场景路由不丢失)
if routing_keyword_prefilter(content):
logger.info("[D1] 超时降级:审批未命中,尝试路由名片兜底")
routed = await _handle_routing(db, conversation, employee_id, content)
if routed:
return None # 已发名片,终止管线
# 降级也失败 -> 建议转人工
await _step_notify_failure(conversation_id, employee_id, "AI 响应时间较长,建议转人工坐席处理。")
return None
@@ -1263,15 +1341,19 @@ async def process_h5_ai_reply(
v4.0 批次 3 管线化重构:原 12 步/11 对 try/except 编排为 9 个步骤函数,
主函数仅最外层 1 个 try/except,行为与 v3.2 外部表现一致。
v4.0 D1 合并:路由意图识别并入主 Dify 调用(同一请求返回 text+action+intent_type),
消除原 detect_routing_intent 前置串行调用(最坏 15+30=45s → 主调用一次)。
过渡期双模:主结果无 intent_type 字段时自动降级旧路径。
流程:
1. _step_load_conversation 加载会话(重试3次)
2. _step_byod_intercept BYOD 关键词拦截
3. _step_routing_intercept 非IT业务路由拦截(名片推荐
4. _step_local_quick_reply 本地快判断(打招呼/呼叫人工)
5. _step_enrich_image 图片消息 VisionService 增强
5b. _enrich_with_last_ai_context 简短回复上下文拼接(v2.3)
6. _step_graph_shortcut Neo4j 图谱短路
7. _step_call_dify Dify 主推理(含超时/无action关键词降级
3. _step_local_quick_reply 本地快判断(打招呼/呼叫人工
4. _step_enrich_image 图片消息 VisionService 增强
4b. _enrich_with_last_ai_context 简短回复上下文拼接(v2.3)
5. _step_graph_shortcut Neo4j 图谱短路
6. _step_call_dify Dify 主推理(含超时/无action关键词降级)
7. _step_routing_from_result D1 路由判断(主结果读 intent_type,命中发名片
8. _step_persist 持久化 + 双 WS 推送
9. _step_assets 资产推荐推送
@@ -1293,11 +1375,12 @@ async def process_h5_ai_reply(
# 前置拦截管线(任一命中即终止)
if await _step_byod_intercept(db, conversation, employee_id, content, msg_type):
return
if await _step_routing_intercept(db, conversation, employee_id, content, msg_type):
return
if await _step_local_quick_reply(db, conversation, employee_id, content, msg_type, dify_conversation_id):
return
# D1 合并:路由关键词预标记(不调用 Dify,仅用于主结果返回后判断是否走路由分支)
is_routing_candidate = (msg_type == "text" and routing_keyword_prefilter(content))
# 内容增强管线
enriched_content = await _step_enrich_image(
db, content, msg_type, media_url, conversation_id, employee_id,
@@ -1318,6 +1401,11 @@ async def process_h5_ai_reply(
if result is None:
return # 降级路径已全部处理(超时转人工 或 已推送降级卡片)
# D1 合并:路由候选消息 → 从主结果读路由意图,命中则发名片(终止管线)
if is_routing_candidate:
if await _step_routing_from_result(db, conversation, employee_id, content, result):
return
# 后置处理管线
await _step_persist(db, conversation, employee_id, result)
await _step_assets(db, employee_id, content, result)