72 lines
2.7 KiB
Python
72 lines
2.7 KiB
Python
"""知识库对话 Tool(T21-5 · FLOW §2「milvus_tool:产品规则 RAG + source_refs」)。
|
||
|
||
与 core_tools / risk.chat_tools 同构的 Tool 定义层:由 tool_service.run_tool
|
||
经统一注册表分发。search_knowledge 是公开知识检索(产品手册/交易规则)——
|
||
**不做客户归属校验**(ToolSpec.skip_access_check=True):
|
||
|
||
- 知识内容为公开产品信息,无客户数据,归属校验无对象;
|
||
- 开放范围(customer + advisor,用户拍板 2026-09-07)由意图层控制:
|
||
_INTENT_KEYWORDS 仅 customer/advisor 组配置 kb 关键词,risk/analyst
|
||
不命中 → Tool 不会被触发;即使被直接调用,返回的也只是公开知识。
|
||
|
||
func 签名对齐 runner 约定 ``func(customer_id, core_ro, risk_repo, **params)``;
|
||
query 为唯一业务参数(tool_node 从 user_message 构造 tool_input 注入,
|
||
一期不来自 LLM 输出)。RAG 异常(EmbeddingError 等)原样上抛 → run_tool
|
||
统一转 TOOL_ERROR 留痕,不静默降级空结果(防 LLM 编造回答)。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from typing import Any, Callable
|
||
|
||
from app.service import rag_service
|
||
|
||
# 对话 Tool 检索条数(与 rag_service.DEFAULT_TOP_K 一致;独立常量便于对话口径单独调整)
|
||
KB_TOP_K = 3
|
||
|
||
|
||
def search_knowledge(
|
||
query: str = "",
|
||
customer_id: str = "",
|
||
core_ro=None,
|
||
risk_repo=None,
|
||
) -> dict[str, Any]:
|
||
"""产品知识检索(TopK chunks + 溯源清单)。
|
||
|
||
customer_id/core_ro/risk_repo 为 runner 恒传参数,本 Tool 不使用
|
||
(公开知识,无归属语义);保留形参以满足统一签名。
|
||
"""
|
||
out = rag_service.search_knowledge(query, top_k=KB_TOP_K)
|
||
return {
|
||
"hit_count": len(out["results"]),
|
||
"results": out["results"],
|
||
"source_refs": out["source_refs"],
|
||
}
|
||
|
||
|
||
class KBToolSpec(dict):
|
||
"""知识库 Tool 注册表条目(与 core_tools.ToolSpec / RiskToolSpec 同构;
|
||
新增 skip_access_check:True → run_tool 跳过客户归属校验)。"""
|
||
|
||
|
||
KB_TOOL_REGISTRY: dict[str, KBToolSpec] = {
|
||
"search_knowledge": KBToolSpec(
|
||
func=search_knowledge,
|
||
description="检索产品知识库(基金产品手册、费率、申赎规则、风险说明;返回结果附溯源)",
|
||
requires_customer=False,
|
||
param_whitelist=("query",),
|
||
int_bounds={},
|
||
skip_access_check=True,
|
||
),
|
||
}
|
||
|
||
|
||
def get_kb_tool(name: str) -> KBToolSpec | None:
|
||
"""白名单查找(未知 Tool 返回 None)。"""
|
||
return KB_TOOL_REGISTRY.get(name)
|
||
|
||
|
||
def kb_tool_func(name: str) -> Callable[..., dict[str, Any]] | None:
|
||
spec = KB_TOOL_REGISTRY.get(name)
|
||
return spec["func"] if spec else None
|