Files
group_fqcd_jr/app/core/knowledge_contracts.py
T
qyqy 57c4add7d8 merge: 客服Agent+RAG+画像 与 架构师最新 qyqy_develop 合并
- customer_service.py 以架构师实现为骨架(三档置信/适当性/会话记忆/话题矩阵),嫁接本人画像出口
- 知识检索契约合并两条链路:架构师 search_knowledge(KnowledgeSearchInput) + 本线
  query_knowledge 链路所需常量(ALLOWED_CONSTANTS/VECTOR_DIM/intent_for_qa_id)
- bootstrap 保留架构师 6 工具/3 Agent,补回 query_customer_profile 与 get_milvus_knowledge_writer
- model_gateway 能力映射修正 intent_classification→chat,保留空集回退兜底
- governance 免责声明限定面向客户 Agent(agent_type 由定义透传),风控结构化输出不再被追加
- 修 JWT 密钥路径(config/jwt/dev)、文档 21 号撞号→25
- 测试基线 934 passed / 1 failed(既有空集缺陷)
2026-09-11 15:06:44 +08:00

106 lines
4.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""知识检索公共契约。字段稳定,不暴露 Milvus/pymilvus 概念。
本模块同时承载**两条知识链路**的契约,它们共用同一批 Milvus 集合:
- 客服 Agent 的检索出口(`search_knowledge` 工具 → `knowledge_search_service`)用
`KnowledgeSearchInput`:调用方可按名字**收窄到单个集合**。
- 入库 / 向量同步 / 管理面(`knowledge_ingest_service`、`knowledge_vector_worker`、
`knowledge_management_service`、`milvus_adapter`)用下面那组常量和 `KnowledgeQuery`:
集合由**意图**映射,调用方不得直接指定集合名。
两者不是重复实现:前者面向"已发布的问答素材",后者面向"知识生命周期管理"。合并时曾
误删下面那组常量,导致 23 个测试模块收集失败——**删任何一半前先看两份引用点**。
"""
from pydantic import BaseModel, ConfigDict, Field, field_validator
#: 只有这三个集合允许被检索;调用方不得指定任意集合名。
ALLOWED_COLLECTIONS = frozenset({
"fin_faq_collection",
"fin_product_collection",
"fin_policy_collection",
})
#: text-embedding-v3 输出维度。维度不符必须失败关闭。
VECTOR_DIM = 1024
#: QA 编号前缀 → 业务意图。源文件共 105 条、11 种前缀,按语义归类;
#: 只有 fin_faq_collection 一个集合时集合名无法区分 faq 与 chitchat,故需前缀映射。
#: 放在契约层是为了让 Service、Worker 与 tools 脚本共同复用(tools 不应被应用层反向依赖)。
INTENT_BY_QA_PREFIX: dict[str, str] = {
"RAG-PER": "chitchat",
"RAG-CHAT": "chitchat",
"RAG-HUM": "transfer_human",
}
def intent_for_qa_id(qa_id: str) -> str | None:
"""按 QA 编号前缀推断业务意图;未列入前缀表时返回 None,由调用方按集合名推断。"""
prefix = "-".join(qa_id.split("-")[:2])
return INTENT_BY_QA_PREFIX.get(prefix)
class KnowledgeSearchInput(BaseModel):
"""知识库检索入参(客服 Agent 的 `search_knowledge` 工具)。
`collection` 留空表示三个集合全查(客服默认行为);指定单个集合用于意图明确时收窄范围。
放在 `app/core` 而不是 service 里:工具的 `input_model` 会被 ToolExecutor 用于参数校验,
属于跨层契约;放在 service 模块会让 API 层与工具注册处都反向依赖 service 实现。
"""
model_config = ConfigDict(extra="forbid")
query: str = Field(min_length=1, max_length=500)
collection: str = Field(default="", max_length=64)
top_k: int = Field(default=5, ge=1, le=10)
class KnowledgeQuery(BaseModel):
"""工具入参。集合由意图映射,调用方不得直接指定集合名。"""
model_config = ConfigDict(extra="forbid", frozen=True)
query: str = Field(min_length=1, max_length=2000)
intents: tuple[str, ...] = Field(min_length=1, max_length=4)
top_k: int = Field(default=5, ge=1, le=20)
@field_validator("query")
@classmethod
def query_must_not_be_blank(cls, value: str) -> str:
if not value.strip():
raise ValueError("query must not be blank")
return value
class KnowledgeHit(BaseModel):
model_config = ConfigDict(extra="forbid", frozen=True)
knowledge_id: str
collection: str
title: str | None = None
snippet: str
score: float | None = Field(default=None, ge=0, le=1)
tags: tuple[str, ...] = ()
version: str | None = None
#: 该条知识的业务意图标签(导入时按 QA 编号前缀写入)。
#: 仅 fin_faq_collection 一个集合同时装多种意图,靠集合名无法区分
#: "faq" 与 "chitchat",因此需要这一层显式标签。
intent: str | None = None
@field_validator("collection")
@classmethod
def collection_must_be_allowlisted(cls, value: str) -> str:
if value not in ALLOWED_COLLECTIONS:
raise ValueError(f"知识集合不在白名单内:{value}")
return value
class KnowledgeSearchResult(BaseModel):
model_config = ConfigDict(extra="forbid", frozen=True)
hits: tuple[KnowledgeHit, ...]
degraded: bool = False
degradation_reason: str | None = None
searched_collections: tuple[str, ...] = ()