from __future__ import annotations from datetime import datetime from typing import TYPE_CHECKING, Optional if TYPE_CHECKING: from core.pydantic_ai_agent import AgentResponse, ConversationState, CustomerMessage, CustomerServiceAgent async def handle_find_image_batch_flow( agent: "CustomerServiceAgent", *, message: "CustomerMessage", state: "ConversationState", customer_text: str, shop_type: str, ) -> Optional["AgentResponse"]: """Handle find-image collecting/quote flow. Return response when handled.""" from core.pydantic_ai_agent import AgentResponse, TRANSFER_MESSAGE if not (shop_type == "find_image" and agent._is_batch_quote_enabled(message.from_id, message.acc_id)): return None incoming_urls = agent._extract_image_urls(customer_text) text_without_urls = agent._strip_urls_from_text(customer_text) short_intent = agent._classify_short_customer_text(text_without_urls) if incoming_urls: is_related_followup = bool(text_without_urls and agent._is_related_image_followup_intent(text_without_urls)) for u in incoming_urls: if u not in state.pending_image_urls: state.pending_image_urls.append(u) if text_without_urls: agent._append_requirement(state, text_without_urls) if is_related_followup: agent._append_requirement(state, "与上一张相关(截图/局部细节)") state.image_count = len(state.pending_image_urls) agent._refresh_quote_phase(state, "collecting") agent._sync_pending_quote_state(message.from_id, state) if agent._is_batch_finish_intent( text=customer_text, state=state, has_incoming_urls=bool(incoming_urls), ): should_defer = agent._should_defer_batch_quote(state, mark_ready=True) agent._sync_pending_quote_state(message.from_id, state) if should_defer: defer_fallback = "图片和需求我都收齐了,我先整理下,马上给你报总价。" defer_reply = await agent._render_collection_reply_with_ai( message=message, state=state, scene="quote_defer_notice", intent_hint="确认已收齐图片与需求,先承接,告知稍后马上报价。", fallback=defer_fallback, ) state.last_reply_at = datetime.now() print(f"{agent.C_REPLY}[REPLY->CUSTOMER]{agent.C_RESET} {defer_reply}") return AgentResponse(reply=defer_reply, should_reply=True, need_transfer=False) quote_res = await agent._quote_pending_images(state, message) reply_text = agent._colloquialize_reply(quote_res.get("reply", "")) reply_text = await agent._rewrite_reply_with_ai( message=message, state=state, reply=reply_text, scene="batch_quote_reply", ) need_transfer = bool(quote_res.get("need_transfer")) state.last_reply_at = datetime.now() print(f"{agent.C_REPLY}[REPLY->CUSTOMER]{agent.C_RESET} {reply_text}") return AgentResponse( reply=reply_text, should_reply=not need_transfer, need_transfer=need_transfer, transfer_msg=TRANSFER_MESSAGE if need_transfer else "", ) ack_fallback = "图片收到了,你有补充就继续发,我这边一起看。" ack_intent = ( "告知图片已收到;如果客户继续发图就继续收,发完可统一报价。" if not is_related_followup else "告知这是和上一张相关的截图/局部图,已按同一需求一起处理。" ) ack = await agent._render_collection_reply_with_ai( message=message, state=state, scene="collect_ack", intent_hint=ack_intent, fallback=ack_fallback, ) state.last_reply_at = datetime.now() print(f"{agent.C_REPLY}[REPLY->CUSTOMER]{agent.C_RESET} {ack}") return AgentResponse(reply=ack, should_reply=True, need_transfer=False) if not state.pending_image_urls: return None if text_without_urls: if short_intent == "finish_signal": agent._mark_quote_ready(state) elif short_intent == "progress_query": if state.quote_phase != "ready_to_quote": agent._refresh_quote_phase(state, "waiting_result") elif short_intent == "ack": if state.quote_phase != "ready_to_quote": agent._refresh_quote_phase(state, "collecting") else: agent._append_requirement(state, text_without_urls) agent._refresh_quote_phase(state, "collecting") agent._sync_pending_quote_state(message.from_id, state) if agent._is_find_image_not_edit_conflict(text_without_urls): clarify_fallback = "明白你是要找图,不是做图。你说下要找原图、同款还是高清版,我按这个给你找。" clarify = await agent._render_collection_reply_with_ai( message=message, state=state, scene="find_not_edit_clarify", intent_hint="确认客户要找图不是做图,并追问是找原图/同款/高清版。", fallback=clarify_fallback, ) state.last_reply_at = datetime.now() print(f"{agent.C_REPLY}[REPLY->CUSTOMER]{agent.C_RESET} {clarify}") return AgentResponse(reply=clarify, should_reply=True, need_transfer=False) if state.quote_phase == "ready_to_quote" and state.quote_ready_turns <= 0 and short_intent in {"progress_query", "ack", "finish_signal"}: quote_res = await agent._quote_pending_images(state, message) reply_text = agent._colloquialize_reply(quote_res.get("reply", "")) reply_text = await agent._rewrite_reply_with_ai( message=message, state=state, reply=reply_text, scene="batch_quote_reply", ) need_transfer = bool(quote_res.get("need_transfer")) state.last_reply_at = datetime.now() print(f"{agent.C_REPLY}[REPLY->CUSTOMER]{agent.C_RESET} {reply_text}") return AgentResponse( reply=reply_text, should_reply=not need_transfer, need_transfer=need_transfer, transfer_msg=TRANSFER_MESSAGE if need_transfer else "", ) if short_intent == "progress_query" or agent._is_result_followup_query(text_without_urls): progress_fallback = "我这边在跟进了,一有结果马上发你。" progress = await agent._render_collection_reply_with_ai( message=message, state=state, scene="collect_progress", intent_hint="承接客户的进度/结果追问,简短说明正在跟进,有结果会第一时间回复。", fallback=progress_fallback, ) state.last_reply_at = datetime.now() print(f"{agent.C_REPLY}[REPLY->CUSTOMER]{agent.C_RESET} {progress}") return AgentResponse(reply=progress, should_reply=True, need_transfer=False) if agent._needs_clarification_in_collecting(text_without_urls): ask_fallback = "你再补一句具体要什么效果,我马上按你的要求来。" ask = await agent._render_collection_reply_with_ai( message=message, state=state, scene="collect_clarify", intent_hint="客户表达不清,礼貌请对方补充一句关键需求,不要机械,不要生硬。", fallback=ask_fallback, ) state.last_reply_at = datetime.now() print(f"{agent.C_REPLY}[REPLY->CUSTOMER]{agent.C_RESET} {ask}") return AgentResponse(reply=ask, should_reply=True, need_transfer=False) if agent._is_batch_finish_intent( text=customer_text, state=state, has_incoming_urls=False, ): should_defer = agent._should_defer_batch_quote(state, mark_ready=True) agent._sync_pending_quote_state(message.from_id, state) if should_defer: defer_fallback = "收到,我先把这批图过一遍,马上给你总价。" defer_reply = await agent._render_collection_reply_with_ai( message=message, state=state, scene="quote_defer_notice", intent_hint="确认已收齐,先承接并告知稍后马上报价。", fallback=defer_fallback, ) state.last_reply_at = datetime.now() print(f"{agent.C_REPLY}[REPLY->CUSTOMER]{agent.C_RESET} {defer_reply}") return AgentResponse(reply=defer_reply, should_reply=True, need_transfer=False) quote_res = await agent._quote_pending_images(state, message) reply_text = agent._colloquialize_reply(quote_res.get("reply", "")) reply_text = await agent._rewrite_reply_with_ai( message=message, state=state, reply=reply_text, scene="batch_quote_reply", ) need_transfer = bool(quote_res.get("need_transfer")) state.last_reply_at = datetime.now() print(f"{agent.C_REPLY}[REPLY->CUSTOMER]{agent.C_RESET} {reply_text}") return AgentResponse( reply=reply_text, should_reply=not need_transfer, need_transfer=need_transfer, transfer_msg=TRANSFER_MESSAGE if need_transfer else "", ) remind_fallback = "需求我记上了,你继续发图,或者让我直接给你报价都行。" remind = await agent._render_collection_reply_with_ai( message=message, state=state, scene="collect_remind", intent_hint="确认需求已记录,引导客户继续补图或直接让你报价。", fallback=remind_fallback, ) state.last_reply_at = datetime.now() print(f"{agent.C_REPLY}[REPLY->CUSTOMER]{agent.C_RESET} {remind}") return AgentResponse(reply=remind, should_reply=True, need_transfer=False)