Files
tw/utils/intent_analyzer.py
ZuoWei a6c42d505a feat: 完整功能部署 v1.0
新增功能:
- 天网协作系统 (HTTP API 端口 6060)
- 三种工作流 (查找图片/处理图片/转人工派单)
- 图片任务数据库 (支持客户后续增加需求)
- 图绘派单系统集成 (API: 8005)
- 文字检测与加价 (60-80 元高价值订单)
- 风险评估与接单判断
- 作图失败自动转人工

新增文档:
- 项目功能汇总.md
- 三种工作流功能说明.md
- 文字加价功能说明.md
- 风险评估功能说明.md
- 图片任务数据库功能说明.md
- 图绘派单系统集成说明.md
- 作图失败转接人工说明.md
- DEPLOYMENT.md
- TIANWANG_INTEGRATION.md

核心修改:
- core/pydantic_ai_agent.py
- core/workflow.py
- core/websocket_client.py
- image/image_analyzer.py
- services/service_tuhui_dispatch.py
- db/image_tasks_db.py

版本:v1.0
日期:2026-02-28
2026-02-28 11:20:40 +08:00

121 lines
3.9 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""
语义匹配 - 用 embedding 做意图/情绪识别
配置 EMBEDDING_MODEL 后启用,否则回退到关键词
"""
import os
import logging
from typing import Optional, Tuple
logger = logging.getLogger(__name__)
# 意图模板(用于 embedding 相似度匹配)
INTENT_TEMPLATES = {
"询价": "我想问一下价格多少钱",
"发图": "我发图给你看看",
"砍价": "能不能便宜点太贵了",
"批量": "我要做很多张图批量",
"加急": "能不能快点很急",
"售后": "已经付款了什么时候好",
"修改": "不满意要改一下",
"转接": "我要退款投诉",
"打招呼": "你好在吗有人吗",
}
EMOTION_TEMPLATES = {
"平静": "好的谢谢",
"着急": "快点啊很急",
"不满": "怎么这么慢不满意",
"砍价": "太贵了便宜点",
}
_template_embeddings: dict = {}
def _get_embedding(text: str, cache_key: str = None) -> Optional[list]:
"""调用 embedding API失败返回 None。cache_key 用于缓存模板向量"""
model = os.getenv("EMBEDDING_MODEL", "")
if not model:
return None
if cache_key and cache_key in _template_embeddings:
return _template_embeddings[cache_key]
try:
from openai import OpenAI
client = OpenAI(
api_key=os.getenv("OPENAI_API_KEY"),
base_url=os.getenv("OPENAI_BASE_URL"),
)
resp = client.embeddings.create(model=model, input=text[:2000])
emb = resp.data[0].embedding
if cache_key:
_template_embeddings[cache_key] = emb
return emb
except Exception as e:
logger.debug(f"embedding 失败: {e}")
return None
def _cosine_sim(a: list, b: list) -> float:
if not a or not b or len(a) != len(b):
return 0.0
dot = sum(x * y for x, y in zip(a, b))
na = sum(x * x for x in a) ** 0.5
nb = sum(y * y for y in b) ** 0.5
if na == 0 or nb == 0:
return 0.0
return dot / (na * nb)
def detect_intent_embedding(msg: str) -> Optional[str]:
"""用 embedding 检测意图,未配置或失败返回 None"""
msg_emb = _get_embedding(msg)
if not msg_emb:
return None
best_intent, best_score = "", 0.0
for intent, template in INTENT_TEMPLATES.items():
tpl_emb = _get_embedding(template, cache_key=f"intent_{intent}")
if not tpl_emb:
continue
sim = _cosine_sim(msg_emb, tpl_emb)
if sim > best_score:
best_score = sim
best_intent = intent
return best_intent if best_score > 0.6 else None
def detect_emotion_embedding(msg: str) -> Optional[str]:
"""用 embedding 检测情绪"""
msg_emb = _get_embedding(msg)
if not msg_emb:
return None
best_emotion, best_score = "", 0.0
for emotion, template in EMOTION_TEMPLATES.items():
tpl_emb = _get_embedding(template, cache_key=f"emotion_{emotion}")
if not tpl_emb:
continue
sim = _cosine_sim(msg_emb, tpl_emb)
if sim > best_score:
best_score = sim
best_emotion = emotion
return best_emotion if best_score > 0.55 else None
def detect_intent_keywords(msg: str) -> str:
"""关键词回退:无 embedding 时使用"""
m = (msg or "").strip().lower()
if any(k in m for k in ["退款", "退货", "投诉"]):
return "转接"
if any(k in m for k in ["多张", "批量", "很多", "几十张"]):
return "批量"
if any(k in m for k in ["快点", "加急", "很急", "着急"]):
return "加急"
if any(k in m for k in ["便宜", "", "少点", "打折"]):
return "砍价"
if any(k in m for k in ["", "修改", "不满意"]):
return "修改"
if any(k in m for k in ["多少钱", "价格", "报价", "多钱"]):
return "询价"
if any(k in m for k in ["在吗", "你好", "有人"]):
return "打招呼"
return ""