feat: add per-shop persona routing for ai replies
This commit is contained in:
@@ -2,11 +2,28 @@
|
|||||||
"shops": {
|
"shops": {
|
||||||
"tb2801080146": {
|
"tb2801080146": {
|
||||||
"type": "gemini_api",
|
"type": "gemini_api",
|
||||||
"hint": "【店铺类型】Gemini API 账号。客户问账号/pro/续费/没pro时,按API客服回复:续费/充值/套餐说明。"
|
"hint": "【店铺类型】Gemini API 账号。客户问账号/pro/续费/没pro时,按API客服回复:续费/充值/套餐说明。",
|
||||||
|
"persona": "技术型客服,表达清晰,回答直接,少废话,优先给可执行步骤和结论。"
|
||||||
},
|
},
|
||||||
"小威哥1216": {
|
"小威哥1216": {
|
||||||
"type": "find_image",
|
"type": "find_image",
|
||||||
"hint": "【店铺类型】找原图/修图。"
|
"hint": "【店铺类型】找原图/修图。",
|
||||||
|
"persona": "修图老店主语气,接地气,像微信聊天,先承接再推进成交,不端着。"
|
||||||
|
},
|
||||||
|
"小威哥1216:媚媚": {
|
||||||
|
"type": "find_image",
|
||||||
|
"hint": "【店铺类型】找原图/修图。",
|
||||||
|
"persona": "女性店主口吻,亲切利落,话短不硬,先确认需求再自然推进下单。"
|
||||||
|
},
|
||||||
|
"tb7518056865:小林": {
|
||||||
|
"type": "find_image",
|
||||||
|
"hint": "【店铺类型】找原图/修图。",
|
||||||
|
"persona": "效率型客服,回复简短干脆,先给结论再补一句说明,避免重复。"
|
||||||
|
},
|
||||||
|
"tb637530900564:小威威": {
|
||||||
|
"type": "find_image",
|
||||||
|
"hint": "【店铺类型】找原图/修图。",
|
||||||
|
"persona": "熟客店主口吻,轻松自然,像真人在店里接单聊天,避免模板句。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"goods_keywords": {
|
"goods_keywords": {
|
||||||
@@ -23,5 +40,10 @@
|
|||||||
"gemini_api": "【店铺类型】Gemini API 账号。客户问账号/pro/续费/没pro时,按API客服回复:续费/充值/套餐说明,自然回复。",
|
"gemini_api": "【店铺类型】Gemini API 账号。客户问账号/pro/续费/没pro时,按API客服回复:续费/充值/套餐说明,自然回复。",
|
||||||
"find_image": "【店铺类型】找原图/修图。"
|
"find_image": "【店铺类型】找原图/修图。"
|
||||||
},
|
},
|
||||||
"_comment": "新增店铺:在 shops 加 acc_id。新增商品类型:在 goods_keywords 加关键词→类型。"
|
"type_personas": {
|
||||||
|
"gemini_api": "技术支持型客服,回答准确直给,先结论后解释,避免口水话。",
|
||||||
|
"find_image": "淘宝修图店主语气,口语自然,有温度但不啰嗦,先承接再推进成交。"
|
||||||
|
},
|
||||||
|
"default_persona": "淘宝老店主,说话自然,像真人微信聊天,不官腔、不背模板。",
|
||||||
|
"_comment": "新增店铺:在 shops 加 acc_id。可选 persona 设置店铺人设;未设置则走 type_personas/default_persona。"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ def build_prompt(
|
|||||||
state: Any,
|
state: Any,
|
||||||
extract_image_url: Callable[[str], str],
|
extract_image_url: Callable[[str], str],
|
||||||
shop_type_resolver: Callable[[str, str], str],
|
shop_type_resolver: Callable[[str, str], str],
|
||||||
|
shop_persona_resolver: Callable[[str, str], str],
|
||||||
parse_order_info: Callable[[str], dict[str, str]],
|
parse_order_info: Callable[[str], dict[str, str]],
|
||||||
build_order_instruction: Callable[[str, str], str],
|
build_order_instruction: Callable[[str, str], str],
|
||||||
) -> str:
|
) -> str:
|
||||||
@@ -58,6 +59,7 @@ def build_prompt(
|
|||||||
stage_info += f"\n【客户压价次数】{state.discount_count}"
|
stage_info += f"\n【客户压价次数】{state.discount_count}"
|
||||||
|
|
||||||
shop_type = shop_type_resolver(message.acc_id or "", message.goods_name or "")
|
shop_type = shop_type_resolver(message.acc_id or "", message.goods_name or "")
|
||||||
|
shop_persona = shop_persona_resolver(message.acc_id or "", message.goods_name or "")
|
||||||
shop_hint = ""
|
shop_hint = ""
|
||||||
try:
|
try:
|
||||||
from config.config import CONFIG_DIR
|
from config.config import CONFIG_DIR
|
||||||
@@ -84,6 +86,8 @@ def build_prompt(
|
|||||||
prompt += f"商品名称: {message.goods_name}\n"
|
prompt += f"商品名称: {message.goods_name}\n"
|
||||||
if shop_hint:
|
if shop_hint:
|
||||||
prompt += f"\n{shop_hint}\n"
|
prompt += f"\n{shop_hint}\n"
|
||||||
|
if shop_persona:
|
||||||
|
prompt += f"\n【店铺人设】{shop_persona}\n"
|
||||||
|
|
||||||
order_paid = False
|
order_paid = False
|
||||||
order_unpaid = False
|
order_unpaid = False
|
||||||
|
|||||||
@@ -234,6 +234,37 @@ def _get_shop_type(acc_id: str = "", goods_name: str = "") -> str:
|
|||||||
return "find_image"
|
return "find_image"
|
||||||
|
|
||||||
|
|
||||||
|
def _get_shop_persona(acc_id: str = "", goods_name: str = "") -> str:
|
||||||
|
"""按店铺返回人设描述,优先级:shops.persona > type_personas > default_persona。"""
|
||||||
|
default_persona = "淘宝老店主,说话自然,像真人微信聊天,不官腔、不背模板。"
|
||||||
|
try:
|
||||||
|
from config.config import CONFIG_DIR
|
||||||
|
import json
|
||||||
|
|
||||||
|
cfg_path = CONFIG_DIR / "shop_prompts.json"
|
||||||
|
if not cfg_path.exists():
|
||||||
|
return default_persona
|
||||||
|
with open(cfg_path, "r", encoding="utf-8") as f:
|
||||||
|
cfg = json.load(f)
|
||||||
|
|
||||||
|
shops = cfg.get("shops", {})
|
||||||
|
if acc_id and acc_id in shops:
|
||||||
|
persona = str(shops[acc_id].get("persona", "")).strip()
|
||||||
|
if persona:
|
||||||
|
return persona
|
||||||
|
|
||||||
|
shop_type = _get_shop_type(acc_id, goods_name)
|
||||||
|
type_personas = cfg.get("type_personas", {})
|
||||||
|
persona = str(type_personas.get(shop_type, "")).strip()
|
||||||
|
if persona:
|
||||||
|
return persona
|
||||||
|
|
||||||
|
cfg_default = str(cfg.get("default_persona", "")).strip()
|
||||||
|
return cfg_default or default_persona
|
||||||
|
except Exception:
|
||||||
|
return default_persona
|
||||||
|
|
||||||
|
|
||||||
def load_skill_map(skills_dir: str = "skills") -> Dict[str, str]:
|
def load_skill_map(skills_dir: str = "skills") -> Dict[str, str]:
|
||||||
"""按技能目录名加载 SKILL.md,返回 {skill_name: content}。"""
|
"""按技能目录名加载 SKILL.md,返回 {skill_name: content}。"""
|
||||||
skill_map: Dict[str, str] = {}
|
skill_map: Dict[str, str] = {}
|
||||||
@@ -504,9 +535,11 @@ class CustomerServiceAgent:
|
|||||||
)
|
)
|
||||||
history = self.message_histories.get(message.from_id, [])
|
history = self.message_histories.get(message.from_id, [])
|
||||||
pending_req = ";".join((state.pending_requirements or [])[-4:]) or "无"
|
pending_req = ";".join((state.pending_requirements or [])[-4:]) or "无"
|
||||||
|
persona = _get_shop_persona(message.acc_id or "", message.goods_name or "")
|
||||||
user_prompt = (
|
user_prompt = (
|
||||||
"请按下面意图生成给客户的自然回复。\n"
|
"请按下面意图生成给客户的自然回复。\n"
|
||||||
f"场景: {scene}\n"
|
f"场景: {scene}\n"
|
||||||
|
f"店铺人设: {persona}\n"
|
||||||
f"回复意图: {intent_hint}\n"
|
f"回复意图: {intent_hint}\n"
|
||||||
f"客户原话: {message.msg}\n"
|
f"客户原话: {message.msg}\n"
|
||||||
f"当前已收图片数: {len(state.pending_image_urls)}\n"
|
f"当前已收图片数: {len(state.pending_image_urls)}\n"
|
||||||
@@ -551,9 +584,11 @@ class CustomerServiceAgent:
|
|||||||
)
|
)
|
||||||
history = self.message_histories.get(message.from_id, [])
|
history = self.message_histories.get(message.from_id, [])
|
||||||
pending_req = ";".join((state.pending_requirements or [])[-4:]) or "无"
|
pending_req = ";".join((state.pending_requirements or [])[-4:]) or "无"
|
||||||
|
persona = _get_shop_persona(message.acc_id or "", message.goods_name or "")
|
||||||
prompt = (
|
prompt = (
|
||||||
"请把下面这句客服回复润色成更自然的微信聊天口吻,语义必须保持一致。\n"
|
"请把下面这句客服回复润色成更自然的微信聊天口吻,语义必须保持一致。\n"
|
||||||
f"场景: {scene}\n"
|
f"场景: {scene}\n"
|
||||||
|
f"店铺人设: {persona}\n"
|
||||||
f"客户原话: {message.msg}\n"
|
f"客户原话: {message.msg}\n"
|
||||||
f"当前已收图: {len(state.pending_image_urls)}张\n"
|
f"当前已收图: {len(state.pending_image_urls)}张\n"
|
||||||
f"当前需求摘要: {pending_req}\n"
|
f"当前需求摘要: {pending_req}\n"
|
||||||
@@ -650,6 +685,7 @@ class CustomerServiceAgent:
|
|||||||
_is_political_inquiry = staticmethod(is_political_inquiry)
|
_is_political_inquiry = staticmethod(is_political_inquiry)
|
||||||
_is_map_inquiry = staticmethod(is_map_inquiry)
|
_is_map_inquiry = staticmethod(is_map_inquiry)
|
||||||
_get_shop_type = staticmethod(_get_shop_type)
|
_get_shop_type = staticmethod(_get_shop_type)
|
||||||
|
_get_shop_persona = staticmethod(_get_shop_persona)
|
||||||
_notify_wechat = staticmethod(_notify_wechat)
|
_notify_wechat = staticmethod(_notify_wechat)
|
||||||
_notify_wechat_overdue = staticmethod(_notify_wechat_overdue)
|
_notify_wechat_overdue = staticmethod(_notify_wechat_overdue)
|
||||||
|
|
||||||
@@ -989,6 +1025,7 @@ class CustomerServiceAgent:
|
|||||||
state=state,
|
state=state,
|
||||||
extract_image_url=self._extract_image_url,
|
extract_image_url=self._extract_image_url,
|
||||||
shop_type_resolver=_get_shop_type,
|
shop_type_resolver=_get_shop_type,
|
||||||
|
shop_persona_resolver=_get_shop_persona,
|
||||||
parse_order_info=parse_order_info,
|
parse_order_info=parse_order_info,
|
||||||
build_order_instruction=build_order_instruction,
|
build_order_instruction=build_order_instruction,
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user