diff --git a/core/pydantic_ai_agent.py b/core/pydantic_ai_agent.py index 95dd74f..1f76146 100755 --- a/core/pydantic_ai_agent.py +++ b/core/pydantic_ai_agent.py @@ -1342,12 +1342,19 @@ class CustomerServiceAgent: try: from utils.content_filter import should_block_customer if should_block_customer(message.msg) or self._is_political_inquiry(message.msg): + # 命中敏感询问时清空待报价队列,避免旧图残留污染后续会话 + state.pending_image_urls.clear() + state.pending_requirements.clear() + self._sync_pending_quote_state(message.from_id, state) reply = "这类不做哈,政治相关图片和人物都不接。" state.last_reply_at = datetime.now() print(f"{self.C_REPLY}[REPLY->CUSTOMER]{self.C_RESET} {reply}") return AgentResponse(reply=reply, should_reply=True, need_transfer=False) except Exception: if self._is_political_inquiry(message.msg): + state.pending_image_urls.clear() + state.pending_requirements.clear() + self._sync_pending_quote_state(message.from_id, state) reply = "这类不做哈,政治相关图片和人物都不接。" state.last_reply_at = datetime.now() print(f"{self.C_REPLY}[REPLY->CUSTOMER]{self.C_RESET} {reply}") diff --git a/core/websocket_client.py b/core/websocket_client.py index bf2a9a6..2a067ed 100755 --- a/core/websocket_client.py +++ b/core/websocket_client.py @@ -98,6 +98,8 @@ class QingjianAPIClient: self._agent_semaphore = asyncio.Semaphore(8) self._pending_images: dict = {} self._pending_image_tasks: dict = {} + # 旧版“看图即报价”快速链路(默认关闭,避免与 Agent 批量收集逻辑并发打架) + self._legacy_fast_quote_enabled = os.getenv("LEGACY_FAST_IMAGE_QUOTE", "false").lower() in ("1", "true", "yes") self._system_inquiry_rules = self._load_system_inquiry_rules() self._last_reply_sent_at: dict = {} # customer_key -> monotonic ts self._inbound_log_seen: dict = {} # signature -> monotonic ts(防重复写入) @@ -600,6 +602,7 @@ class QingjianAPIClient: _cid = data.get('from_id', '') _name = self.to_chinese(data.get('from_name', '') or data.get('cy_name', '')) _plat = data.get('acc_type', '') + _shop_type = _get_shop_type(data.get('acc_id', ''), self.to_chinese(data.get('goods_name', '') or '')) # 超大尺寸(米制)直接拒单,避免进入报价/处理流程 oversize_reply = self._oversize_reply_if_needed(msg_text) @@ -607,92 +610,94 @@ class QingjianAPIClient: await self.send_reply(data, oversize_reply) return - # 消息含图片URL:累积到待处理列表,先询问要求 - if self._msg_has_image_url(msg_text): - urls = self._extract_image_urls(msg_text) - key = self._customer_key(data) - self._add_pending_images(key, urls) - await self.send_reply(data, "收到,我看看哈") - old = self._pending_image_tasks.get(key) - if old and not old.done(): - old.cancel() - async def _delay_flush(capture_key, capture_data): - await asyncio.sleep(self._DEBOUNCE_SECONDS + 4) - # 与同客户 agent_reply 串行,避免“延迟报价”和“当前追问”并发打架 - async with self._get_customer_lock(capture_key): - await self._flush_pending_images(capture_key, capture_data) - task = asyncio.create_task(_delay_flush(key, data)) - self._pending_image_tasks[key] = task - return - elif self._msg_refers_images(msg_text): - urls = self._collect_recent_image_urls(_cid, data.get('acc_id', ''), max_count=6) - if urls: - key = self._customer_key(data) - self._add_pending_images(key, urls) - await self.send_reply(data, "稍等,我找找刚才那几张") - await self._flush_pending_images(key, data) - return - else: - status = self._detect_order_status(msg_text) - if status == "paid": - ack = "收到付款,我马上安排处理,有需要第一时间联系您" - await self.send_reply(data, ack) - return - elif status in ("waiting", "order"): - ack = "订单我看到了哈,方便的话请完成付款,我好安排处理" - await self.send_reply(data, ack) - return - else: - urls = self._extract_image_urls(msg_text) - if len(urls) == 1: + # 找图/修图店铺:统一走 Agent 的“收集需求后统一报价”流程,避免按单图快速报价 + if self._legacy_fast_quote_enabled and _shop_type != "find_image": + # 消息含图片URL:累积到待处理列表,先询问要求 + if self._msg_has_image_url(msg_text): + urls = self._extract_image_urls(msg_text) key = self._customer_key(data) self._add_pending_images(key, urls) await self.send_reply(data, "收到,我看看哈") + old = self._pending_image_tasks.get(key) + if old and not old.done(): + old.cancel() + async def _delay_flush(capture_key, capture_data): + await asyncio.sleep(self._DEBOUNCE_SECONDS + 4) + # 与同客户 agent_reply 串行,避免“延迟报价”和“当前追问”并发打架 + async with self._get_customer_lock(capture_key): + await self._flush_pending_images(capture_key, capture_data) + task = asyncio.create_task(_delay_flush(key, data)) + self._pending_image_tasks[key] = task return - else: - if self._msg_requests_external_contact(msg_text): - reply = "这里沟通就可以哦,其他联系方式不方便" - await self.send_reply(data, reply) - try: - from utils.wechat_chat_log import push_chat_to_wechat - asyncio.create_task(push_chat_to_wechat( - customer_name=_name, - customer_id=_cid, - acc_id=data.get('acc_id', ''), - customer_msg=msg_text, - reply_msg=reply, - goods_name=self.to_chinese(data.get('goods_name', '') or ''), - )) - except Exception: - pass - return - if self._msg_is_requirement(msg_text) or self._msg_is_price_inquiry(msg_text): + elif self._msg_refers_images(msg_text): + urls = self._collect_recent_image_urls(_cid, data.get('acc_id', ''), max_count=6) + if urls: key = self._customer_key(data) - if self._pending_images.get(key): - old = self._pending_image_tasks.get(key) - if old and not old.done(): - old.cancel() - await self.send_reply(data, "稍等,我把刚才那几张一起看下") - await self._flush_pending_images(key, data) - return - if self._msg_is_price_inquiry(msg_text): - recent_urls = self._collect_recent_image_urls(_cid, data.get('acc_id', ''), max_count=6) - if recent_urls: - await self.send_reply(data, "稍等,我刚才那几张一起看下") - if len(recent_urls) == 1: - asyncio.create_task(self._analyze_single_and_reply(data, recent_urls[0])) - else: - asyncio.create_task(self._analyze_multi_and_reply(data, recent_urls)) - return - status = self._detect_order_status(msg_text) - if status == "paid": - ack = "收到付款,我马上安排处理,有需要第一时间联系您" - await self.send_reply(data, ack) + self._add_pending_images(key, urls) + await self.send_reply(data, "稍等,我找找刚才那几张") + await self._flush_pending_images(key, data) return - elif status in ("waiting", "order"): - ack = "订单我看到了哈,方便的话请完成付款,我好安排处理" - await self.send_reply(data, ack) + else: + status = self._detect_order_status(msg_text) + if status == "paid": + ack = "收到付款,我马上安排处理,有需要第一时间联系您" + await self.send_reply(data, ack) + return + elif status in ("waiting", "order"): + ack = "订单我看到了哈,方便的话请完成付款,我好安排处理" + await self.send_reply(data, ack) + return + else: + urls = self._extract_image_urls(msg_text) + if len(urls) == 1: + key = self._customer_key(data) + self._add_pending_images(key, urls) + await self.send_reply(data, "收到,我看看哈") return + else: + if self._msg_requests_external_contact(msg_text): + reply = "这里沟通就可以哦,其他联系方式不方便" + await self.send_reply(data, reply) + try: + from utils.wechat_chat_log import push_chat_to_wechat + asyncio.create_task(push_chat_to_wechat( + customer_name=_name, + customer_id=_cid, + acc_id=data.get('acc_id', ''), + customer_msg=msg_text, + reply_msg=reply, + goods_name=self.to_chinese(data.get('goods_name', '') or ''), + )) + except Exception: + pass + return + if self._msg_is_requirement(msg_text) or self._msg_is_price_inquiry(msg_text): + key = self._customer_key(data) + if self._pending_images.get(key): + old = self._pending_image_tasks.get(key) + if old and not old.done(): + old.cancel() + await self.send_reply(data, "稍等,我把刚才那几张一起看下") + await self._flush_pending_images(key, data) + return + if self._msg_is_price_inquiry(msg_text): + recent_urls = self._collect_recent_image_urls(_cid, data.get('acc_id', ''), max_count=6) + if recent_urls: + await self.send_reply(data, "稍等,我刚才那几张一起看下") + if len(recent_urls) == 1: + asyncio.create_task(self._analyze_single_and_reply(data, recent_urls[0])) + else: + asyncio.create_task(self._analyze_multi_and_reply(data, recent_urls)) + return + status = self._detect_order_status(msg_text) + if status == "paid": + ack = "收到付款,我马上安排处理,有需要第一时间联系您" + await self.send_reply(data, ack) + return + elif status in ("waiting", "order"): + ack = "订单我看到了哈,方便的话请完成付款,我好安排处理" + await self.send_reply(data, ack) + return # 构建 CustomerMessage customer_msg = CustomerMessage( diff --git a/image/image_analyzer.py b/image/image_analyzer.py index 9a4296f..155d8b3 100755 --- a/image/image_analyzer.py +++ b/image/image_analyzer.py @@ -579,7 +579,7 @@ class ImageAnalyzer: ) dense_text_hint = any( kw in scene_text - for kw in ("密集文字", "大量文字", "小字", "多板块", "细字") + for kw in ("密集文字", "大量文字", "多板块") ) # 校验比例合法性