diff --git a/config/shop_prompts.json b/config/shop_prompts.json index 1406c29..b3a1400 100755 --- a/config/shop_prompts.json +++ b/config/shop_prompts.json @@ -2,11 +2,28 @@ "shops": { "tb2801080146": { "type": "gemini_api", - "hint": "【店铺类型】Gemini API 账号。客户问账号/pro/续费/没pro时,按API客服回复:续费/充值/套餐说明。" + "hint": "【店铺类型】Gemini API 账号。客户问账号/pro/续费/没pro时,按API客服回复:续费/充值/套餐说明。", + "persona": "技术型客服,表达清晰,回答直接,少废话,优先给可执行步骤和结论。" }, "小威哥1216": { "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": { @@ -23,5 +40,10 @@ "gemini_api": "【店铺类型】Gemini API 账号。客户问账号/pro/续费/没pro时,按API客服回复:续费/充值/套餐说明,自然回复。", "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。" } diff --git a/core/prompt_builder.py b/core/prompt_builder.py index 3f45620..34b75b7 100644 --- a/core/prompt_builder.py +++ b/core/prompt_builder.py @@ -25,6 +25,7 @@ def build_prompt( state: Any, extract_image_url: Callable[[str], str], shop_type_resolver: Callable[[str, str], str], + shop_persona_resolver: Callable[[str, str], str], parse_order_info: Callable[[str], dict[str, str]], build_order_instruction: Callable[[str, str], str], ) -> str: @@ -58,6 +59,7 @@ def build_prompt( stage_info += f"\n【客户压价次数】{state.discount_count}" 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 = "" try: from config.config import CONFIG_DIR @@ -84,6 +86,8 @@ def build_prompt( prompt += f"商品名称: {message.goods_name}\n" if shop_hint: prompt += f"\n{shop_hint}\n" + if shop_persona: + prompt += f"\n【店铺人设】{shop_persona}\n" order_paid = False order_unpaid = False diff --git a/core/pydantic_ai_agent.py b/core/pydantic_ai_agent.py index 2ec899a..8074b22 100755 --- a/core/pydantic_ai_agent.py +++ b/core/pydantic_ai_agent.py @@ -234,6 +234,37 @@ def _get_shop_type(acc_id: str = "", goods_name: str = "") -> str: 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]: """按技能目录名加载 SKILL.md,返回 {skill_name: content}。""" skill_map: Dict[str, str] = {} @@ -504,9 +535,11 @@ class CustomerServiceAgent: ) history = self.message_histories.get(message.from_id, []) pending_req = ";".join((state.pending_requirements or [])[-4:]) or "无" + persona = _get_shop_persona(message.acc_id or "", message.goods_name or "") user_prompt = ( "请按下面意图生成给客户的自然回复。\n" f"场景: {scene}\n" + f"店铺人设: {persona}\n" f"回复意图: {intent_hint}\n" f"客户原话: {message.msg}\n" f"当前已收图片数: {len(state.pending_image_urls)}\n" @@ -551,9 +584,11 @@ class CustomerServiceAgent: ) history = self.message_histories.get(message.from_id, []) pending_req = ";".join((state.pending_requirements or [])[-4:]) or "无" + persona = _get_shop_persona(message.acc_id or "", message.goods_name or "") prompt = ( "请把下面这句客服回复润色成更自然的微信聊天口吻,语义必须保持一致。\n" f"场景: {scene}\n" + f"店铺人设: {persona}\n" f"客户原话: {message.msg}\n" f"当前已收图: {len(state.pending_image_urls)}张\n" f"当前需求摘要: {pending_req}\n" @@ -650,6 +685,7 @@ class CustomerServiceAgent: _is_political_inquiry = staticmethod(is_political_inquiry) _is_map_inquiry = staticmethod(is_map_inquiry) _get_shop_type = staticmethod(_get_shop_type) + _get_shop_persona = staticmethod(_get_shop_persona) _notify_wechat = staticmethod(_notify_wechat) _notify_wechat_overdue = staticmethod(_notify_wechat_overdue) @@ -989,6 +1025,7 @@ class CustomerServiceAgent: state=state, extract_image_url=self._extract_image_url, shop_type_resolver=_get_shop_type, + shop_persona_resolver=_get_shop_persona, parse_order_info=parse_order_info, build_order_instruction=build_order_instruction, )