From 1fa5fc7d0365e0bbf6d8b530422708a2e69fc18c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=BF=E4=BA=91=E7=A7=8B=E6=9C=88?= <15273589815@163.com> Date: Thu, 10 Sep 2026 20:33:13 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E5=AE=A2=E6=9C=8D=E5=9B=9E?= =?UTF-8?q?=E7=AD=94=E6=AD=A3=E6=96=87=E5=8F=AA=E4=BF=9D=E7=95=99=E5=9B=BA?= =?UTF-8?q?=E5=AE=9A=E5=85=8D=E8=B4=A3=E5=A3=B0=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 业务方要求客户侧只看到一句固定话术(不构成投资建议),因此从回答正文移除: 1. 中置信的「(以上信息可能不完整,具体以产品说明书与公司制度为准)」提示; 2. 「(依据:…)」出处行——原先它显示的是知识块标题,FAQ 的标题就是问题本身, 展示为「(依据:公司什么时候成立的?)」并没有可读价值。 可追溯性不受影响:本次命中哪个知识块仍由审计(agent.tool_executed 的工具调用记录) 与消息表留痕,source_references(tool 类型)也照常返回,只是不再面向客户展示。 取舍已在代码注释中标明:中置信回答此后不再向客户标注不确定性。 若将来要把出处展示给客户,应当走 source_references 的 knowledge 类型 (前提是让 ToolExecutor 把工具返回的 doc_id 登记为本次可引用来源), 而不是继续往正文里拼字符串。 同时移除因此不再使用的 INCOMPLETE_NOTICE 常量、_source_note 方法, 以及提问工具里那句"正文没有依据行"的提示。 验证:ruff 通过、mypy 107 文件无错;customer_service_check 9/9 通过; ask_customer_service 实测回答正文为「答案 + 免责声明」两行。 --- .../agent/implementations/customer_service.py | 35 +++++++------------ tools/ask_customer_service.py | 2 -- 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/app/service/agent/implementations/customer_service.py b/app/service/agent/implementations/customer_service.py index 990fbea..dc5f1bb 100644 --- a/app/service/agent/implementations/customer_service.py +++ b/app/service/agent/implementations/customer_service.py @@ -68,7 +68,8 @@ FALLBACK_TEMPLATE = ( "抱歉,这个问题我暂时无法给出准确答复。为避免给您错误信息," f"建议您拨打客服热线 {HOTLINE}({SERVICE_HOURS})转人工客服咨询。" ) -INCOMPLETE_NOTICE = "(以上信息可能不完整,具体以产品说明书与公司制度为准)" +# 客户侧只展示这一句固定话术(业务方确定)。原中置信的"信息可能不完整"提示已按此移除, +# 即中置信回答不再对客户标注不确定性——这是调整时知情的取舍,不是遗漏。 DISCLAIMER = "(以上内容由智能客服依据公司公开资料整理,不构成投资建议)" CHITCHAT_PROMPT_CODE = "customer_service_chitchat" @@ -149,20 +150,17 @@ class CustomerServiceAgent(BaseAgent): if not content: return self._guide_to_human("命中内容为空") - answer = content[:MAX_ANSWER_CHARS] - if not confident: - answer = f"{answer}\n{INCOMPLETE_NOTICE}" - # 知识出处写进正文,而不是塞进 source_references: - # `governance.review_output` 只接受「本次召回的记忆」与「本次成功调用的工具」两类引用 - # (用于防止 Agent 伪造来源),知识块的 doc_id 不属于这两类,会被判为非法引用。 - # 把文件标题与内部文件编号写进正文,客户与人工同样能核对,且不必放开那道校验。 - source_note = self._source_note(best) - text = ( - f"{answer}\n{source_note}\n{DISCLAIMER}" - if source_note - else f"{answer}\n{DISCLAIMER}" + # 正文只保留「答案 + 固定免责声明」:业务方要求客户侧只看到这一句固定话术, + # 因此不确定性提示与出处行都不再出现在正文里。 + # + # 可追溯性不受影响:本次命中哪个知识块仍由审计(agent.tool_executed 的工具调用记录) + # 与消息表留痕,只是不面向客户展示。若将来要把出处给客户看,应当走 + # source_references 的 knowledge 类型(需先让 ToolExecutor 登记本次可引用的 doc_id), + # 而不是继续往正文里拼字符串。 + return CoreResult( + text=f"{content[:MAX_ANSWER_CHARS]}\n{DISCLAIMER}", + intent=self._classified_intent, ) - return CoreResult(text=text, intent=self._classified_intent) # ---- 出口二:闲聊(提示词走发布配置) ---- @@ -242,15 +240,6 @@ class CustomerServiceAgent(BaseAgent): return 0.0 return self._score(hits[1].get("score")) - @staticmethod - def _source_note(hit: dict[str, object]) -> str: - """把知识出处写成一行正文(文件标题 + 内部文件编号),便于客户与人工核对。""" - title = str(hit.get("title") or "").strip() - doc_no = str(hit.get("doc_no") or "").strip() - if not title: - return "" - return f"(依据:{title}{',' + doc_no if doc_no else ''})" - def _references(self, hits: list[object]) -> tuple[SourceReference, ...]: """保留待用:等基座支持 knowledge 类型引用后再启用。 diff --git a/tools/ask_customer_service.py b/tools/ask_customer_service.py index 04ba2ca..e690cd7 100644 --- a/tools/ask_customer_service.py +++ b/tools/ask_customer_service.py @@ -96,8 +96,6 @@ async def ask( references = result.get("source_references") or [] for reference in references: print(f"来源引用:{reference.get('source_type')} / {reference.get('source_id')}") - if not guided and "依据:" not in text: - print("提示:这条回答正文里没有「依据:…」,说明知识命中的是通用来源,请人工核对。") print()