Files
tw/core/find_image_flow.py

216 lines
10 KiB
Python

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)