fix(knowledge): 检索服务适配现库集合 schema、发布对齐的工具白名单、免责声明只由治理层注入
合并暴露的三个真机问题(单测全绿但线上必挂): 1. 检索服务字段名与现库集合不符 -> 静默零召回 架构师那套按 load_knowledge_milvus.py 的 schema 读 doc_id/content/chapter/section/ visibility,而现库三集合的真实字段是 knowledge_id/title/snippet/tags/version/intent。 Milvus 对不存在的字段直接报错 -> 三集合全失败 -> degraded -> 客服一律转人工。 改法:只改读取侧,用 _FIELD_ALIASES 映射;KnowledgeHit 对外形状不变(下游与测试不动)。 代价已注明:没有 visibility 字段 -> 检索层内部资料硬隔离失效(现库 356 行均为对外知识)。 2. 发布白名单与代码上限不匹配 -> AGENT_PERMISSION_DENIED active 版本白名单是 query_knowledge,而合并后代码上限是 search_knowledge/check_suitability/ query_customer_profile -> 交集为空 -> 所有知识问题 failed。 改法:publish_customer_service_config.py 补 PROFILE_TOOL 进 faq 白名单,并让同 key 的 继承项被本次定义覆盖(旧值原样继承会被子集校验 422 拒掉整次发布)。已激活版本 216。 3. 免责声明重复出现 Agent 自己拼一句 + 治理层追加权威话术 -> 客户看到两条。改为只由治理层注入 (话术属发布配置,改文案不该改代码)。风控等内部 Agent 不注入(结构化输出不被污染)。 测试:934 passed / 1 failed(test_fund_readonly_contract 既有空集缺陷,与本线无关) 真机:知识问答两问 succeeded 且只带一条声明;画像问答 succeeded;知识库三端点全绿
This commit is contained in:
@@ -13,14 +13,19 @@
|
||||
|
||||
from typing import Any
|
||||
|
||||
from app.service.agent.governance import FALLBACK_DISCLAIMER
|
||||
from app.service.agent.implementations.customer_service import (
|
||||
DISCLAIMER,
|
||||
FALLBACK_TEMPLATE,
|
||||
CustomerServiceAgent,
|
||||
)
|
||||
|
||||
PRODUCT = "南方季季盈90天"
|
||||
|
||||
#: 固定免责声明由**治理层**注入(`PlatformGovernance.review` → `review_output`),
|
||||
#: 业务 Agent 不再自己拼——否则客户会看到两条重复声明(合并时实测复现)。
|
||||
#: 这里取治理层的代码兜底文案,只用于还原"治理后"的正文形状。
|
||||
DISCLAIMER = FALLBACK_DISCLAIMER
|
||||
|
||||
|
||||
def _decision(**overrides: Any) -> dict[str, Any]:
|
||||
base: dict[str, Any] = {
|
||||
@@ -32,8 +37,8 @@ def _decision(**overrides: Any) -> dict[str, Any]:
|
||||
|
||||
|
||||
def _replied(body: str) -> str:
|
||||
"""出口交给客户的正文形状:`{正文}\\n{DISCLAIMER}`(四个出口都这样组装)。"""
|
||||
return f"{body}\n{DISCLAIMER}"
|
||||
"""出口交给客户的正文形状:`{正文}\\n\\n{免责声明}`(治理层统一追加)。"""
|
||||
return f"{body}\n\n{DISCLAIMER}"
|
||||
|
||||
|
||||
# ---- 出口一:知识直返(正文 = 知识块 content + DISCLAIMER) ----
|
||||
|
||||
@@ -29,13 +29,17 @@ async def _embed(text: str) -> list[float]:
|
||||
|
||||
|
||||
def _row(doc_id: str, title: str, score: float, content: str = "正文") -> dict[str, Any]:
|
||||
"""构造 pymilvus 的 `{distance, entity}` 行(`search()` 的返回形状)。"""
|
||||
"""构造 pymilvus 的 `{distance, entity}` 行(`search()` 的返回形状)。
|
||||
|
||||
字段名按**现库集合的真实 schema**(`knowledge_id` / `snippet`),不是灌库脚本里那套
|
||||
(`doc_id` / `content`)——后者在本环境的集合从未建起来过,按它写替身会让测试假绿:
|
||||
业务代码读取 `snippet` 拿到空串,命中被静默丢弃,而断言又恰好只查 `doc_id`。
|
||||
"""
|
||||
return {
|
||||
"distance": score,
|
||||
"entity": {
|
||||
"doc_id": doc_id, "title": title, "content": content,
|
||||
"source_file": "x.md", "visibility": "public", "doc_no": "",
|
||||
"version": "", "chapter": "",
|
||||
"knowledge_id": doc_id, "title": title, "snippet": content,
|
||||
"tags": "", "version": "", "intent": "",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -48,9 +52,8 @@ def _flat(doc_id: str, title: str, content: str = "正文") -> dict[str, Any]:
|
||||
测试红了一次——是测试写错,不是业务代码有问题(业务代码"没有正文就不作答"是对的)。
|
||||
"""
|
||||
return {
|
||||
"doc_id": doc_id, "title": title, "content": content,
|
||||
"source_file": "x.md", "visibility": "public", "doc_no": "",
|
||||
"version": "", "chapter": "",
|
||||
"knowledge_id": doc_id, "title": title, "snippet": content,
|
||||
"tags": "", "version": "", "intent": "",
|
||||
}
|
||||
|
||||
|
||||
@@ -73,8 +76,11 @@ class FakeClient:
|
||||
|
||||
def query(self, **kwargs: Any) -> Any:
|
||||
self.query_calls += 1
|
||||
if "content" not in kwargs.get("output_fields", []):
|
||||
return [{"doc_id": d, "title": t} for d, t in self._product_titles]
|
||||
# 业务代码请求的字段名取自 `_FIELD_ALIASES`(现库是 `knowledge_id`/`snippet`):
|
||||
# 这里按"有没有要正文"区分两轮查询,而不是写死某一套字段名。
|
||||
output_fields = kwargs.get("output_fields", [])
|
||||
if "snippet" not in output_fields and "content" not in output_fields:
|
||||
return [{"knowledge_id": d, "title": t} for d, t in self._product_titles]
|
||||
pattern = str(kwargs.get("filter") or "")
|
||||
return [row for doc_id, row in self._product_rows.items() if doc_id in pattern]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user