feat: add richer clarification replies for ambiguous customer intent

This commit is contained in:
2026-02-28 23:40:17 +08:00
parent 5b8ca6fb02
commit 3c77c618e7
2 changed files with 90 additions and 1 deletions

View File

@@ -1534,6 +1534,19 @@ class CustomerServiceAgent:
if text_without_urls: if text_without_urls:
self._append_requirement(state, text_without_urls) self._append_requirement(state, text_without_urls)
self._sync_pending_quote_state(message.from_id, state) self._sync_pending_quote_state(message.from_id, state)
# 客户明确“找图,不是做图”时,先澄清意图,不继续报价链路
if self._is_find_image_not_edit_conflict(text_without_urls):
clarify = self._build_find_image_clarify_reply(state)
state.last_reply_at = datetime.now()
print(f"{self.C_REPLY}[REPLY->CUSTOMER]{self.C_RESET} {clarify}")
return AgentResponse(reply=clarify, should_reply=True, need_transfer=False)
# 信息不足时先追问,避免误判为“直接报价”
if self._needs_clarification_in_collecting(text_without_urls):
ask = self._build_not_understood_reply()
state.last_reply_at = datetime.now()
print(f"{self.C_REPLY}[REPLY->CUSTOMER]{self.C_RESET} {ask}")
return AgentResponse(reply=ask, should_reply=True, need_transfer=False)
if self._is_batch_finish_intent( if self._is_batch_finish_intent(
text=customer_text, text=customer_text,
state=state, state=state,
@@ -2202,7 +2215,7 @@ class CustomerServiceAgent:
one_templates = [ one_templates = [
"这个要求我记住了。你还有图就继续发,不补图我就按这张给你报价。", "这个要求我记住了。你还有图就继续发,不补图我就按这张给你报价。",
"明白,这个需求我加上了。你继续发图也行,想直接报价也可以。", "明白,这个需求我加上了。你继续发图也行,想直接报价也可以。",
"懂你意思,这种能做。要不先按这张来,我现在就给你报个实在价", "我先记下这张。你如果是要我找图,不是做图,直接说一声,我按找图思路给你走",
"这个需求我收到了。你要是就做这张,我现在就给你报。", "这个需求我收到了。你要是就做这张,我现在就给你报。",
"你这要求我记下了,后面还有图就发,没有的话我现在直接算价。", "你这要求我记下了,后面还有图就发,没有的话我现在直接算价。",
"行,我按你这个要求来。继续补图也行,不补我就先报这张。", "行,我按你这个要求来。继续补图也行,不补我就先报这张。",
@@ -2222,6 +2235,55 @@ class CustomerServiceAgent:
] ]
return random.choice(templates).format(n=count) return random.choice(templates).format(n=count)
@staticmethod
def _is_find_image_not_edit_conflict(text: str) -> bool:
"""识别客户明确声明“要找图,不是做图”的冲突语义。"""
s = (text or "").strip()
if not s:
return False
find_kw = ("找图", "找原图", "找素材", "找同款")
deny_edit_kw = ("不是让你做图", "不是做图", "不用做图", "不需要做图", "不是修图", "不用修图")
return any(k in s for k in find_kw) and any(k in s for k in deny_edit_kw)
@staticmethod
def _needs_clarification_in_collecting(text: str) -> bool:
"""
信息不足时先追问,不急着报价。
例:这个也是大图 / 一共几个图 / 啥意思 / 没明白
"""
s = (text or "").strip()
if not s:
return False
if len(s) <= 4:
return True
vague_kw = (
"这个也是", "一共几个图", "几个图", "啥意思", "没明白", "什么意思",
"这个呢", "这个可以吗", "然后呢", "咋办", "怎么搞",
)
return any(k in s for k in vague_kw)
def _build_find_image_clarify_reply(self, state: ConversationState) -> str:
count = len(state.pending_image_urls or [])
return (
f"明白,你是要我帮你找图,不是做图。现在我这边先记了{count}张,"
"你告诉我具体要找哪种:原图/同款/高清版,我按这个方向给你找。"
)
@staticmethod
def _build_not_understood_reply() -> str:
"""信息不足时的澄清话术(随机)。"""
templates = [
"不好意思,不太懂你的意思,你再具体说下哈。",
"抱歉我这边没完全理解,你可以换个说法再说一次吗?",
"我有点没听明白,你是要找图还是要做图呀?",
"不好意思我没抓到重点,你再补一句我就能接着处理。",
"这句我理解得不太准,你再说具体一点我马上给你办。",
"抱歉,这里我没太看懂。你是想让我找原图,还是按图处理?",
"我这边还没完全明白你的意思,麻烦你再具体描述一下。",
"不好意思,这条我没读懂,你再详细说一点我马上跟上。",
]
return random.choice(templates)
def _append_requirement(self, state: ConversationState, text: str): def _append_requirement(self, state: ConversationState, text: str):
"""追加需求并做去重/截断,减少上下文噪音。""" """追加需求并做去重/截断,减少上下文噪音。"""
t = (text or "").strip() t = (text or "").strip()

View File

@@ -139,6 +139,33 @@ class RegressionPipelineTest(unittest.IsolatedAsyncioTestCase):
self.assertIn("50", resp.reply) self.assertIn("50", resp.reply)
agent._quote_pending_images.assert_awaited() agent._quote_pending_images.assert_awaited()
async def test_find_image_not_edit_conflict_triggers_clarification(self):
agent = CustomerServiceAgent()
st = agent._get_conversation_state(self.customer_id)
st.pending_image_urls = ["https://img.alicdn.com/a.jpg"]
st.pending_requirements = []
agent._sync_pending_quote_state(self.customer_id, st)
agent._quote_pending_images = AsyncMock(return_value={"reply": "不该触发", "need_transfer": False})
msg = CustomerMessage(
msg_id="m6",
acc_id="test_shop",
msg="我要你帮我找图,不是让你做图",
from_id=self.customer_id,
from_name="t",
cy_id=self.customer_id,
acc_type="AliWorkbench",
msg_type=0,
cy_name="t",
goods_name="专业找图",
goods_order="",
)
resp = await agent.process_message(msg)
self.assertTrue(resp.should_reply)
self.assertIn("找图", resp.reply)
self.assertIn("不是做图", resp.reply)
agent._quote_pending_images.assert_not_awaited()
async def test_pending_state_restore(self): async def test_pending_state_restore(self):
db.update_pending_quote_state(self.customer_id, ["u1", "u2"], ["r1"]) db.update_pending_quote_state(self.customer_id, ["u1", "u2"], ["r1"])
agent = CustomerServiceAgent() agent = CustomerServiceAgent()