客服 Agent:知识检索、会话记忆、图投影与适当性裁决 #3

Merged
lzf_0626 merged 30 commits from qyqy_develop_1 into qyqy_develop 2026-09-11 10:43:39 +08:00
2 changed files with 12 additions and 25 deletions
Showing only changes of commit 1fa5fc7d03 - Show all commits
@@ -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 类型引用后再启用。
-2
View File
@@ -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()