feat: add knowledge search explain endpoint
This commit is contained in:
@@ -8,6 +8,8 @@ from yuqei_sdk.ai_platform import (
|
||||
KnowledgeImportBatchCreate,
|
||||
KnowledgeReindexRequest,
|
||||
KnowledgeReindexResult,
|
||||
KnowledgeSearchExplain,
|
||||
KnowledgeSearchExplainItem,
|
||||
KnowledgeSearchResult,
|
||||
LegalCitation,
|
||||
LegalQaRequest,
|
||||
@@ -30,6 +32,8 @@ __all__ = [
|
||||
"KnowledgeImportBatchCreate",
|
||||
"KnowledgeReindexRequest",
|
||||
"KnowledgeReindexResult",
|
||||
"KnowledgeSearchExplain",
|
||||
"KnowledgeSearchExplainItem",
|
||||
"KnowledgeSearchResult",
|
||||
"LegalCitation",
|
||||
"LegalQaRequest",
|
||||
|
||||
@@ -123,6 +123,39 @@ class KnowledgeSearchResult(BaseModel):
|
||||
matched_terms: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class KnowledgeSearchExplainItem(BaseModel):
|
||||
chunk: KnowledgeChunk
|
||||
score: float = 0.0
|
||||
keyword_score: float = 0.0
|
||||
vector_score: float = 0.0
|
||||
hybrid_score: float = 0.0
|
||||
retrieval_strategy: str = "hybrid"
|
||||
matched_terms: list[str] = Field(default_factory=list)
|
||||
included: bool = False
|
||||
rank: int | None = None
|
||||
filter_reason: str | None = None
|
||||
|
||||
|
||||
class KnowledgeSearchExplain(BaseModel):
|
||||
keyword: str
|
||||
normalized_terms: list[str] = Field(default_factory=list)
|
||||
retrieval_strategy: str = "hybrid"
|
||||
knowledge_base_ids: list[str] = Field(default_factory=list)
|
||||
limit: int = 5
|
||||
candidate_limit: int = 20
|
||||
candidate_count: int = 0
|
||||
included_count: int = 0
|
||||
retrieval_backend: str = "json-explain"
|
||||
embedding_provider: str
|
||||
embedding_model: str
|
||||
query_embedding_dimension: int = 0
|
||||
keyword_threshold: float = 0.0
|
||||
vector_threshold: float = 0.35
|
||||
hybrid_keyword_weight: float = 0.55
|
||||
hybrid_vector_weight: float = 0.45
|
||||
results: list[KnowledgeSearchExplainItem] = Field(default_factory=list)
|
||||
|
||||
|
||||
class KnowledgeReindexRequest(BaseModel):
|
||||
document_id: str | None = None
|
||||
knowledge_base_id: str | None = None
|
||||
@@ -368,6 +401,32 @@ class AiPlatformClient:
|
||||
response.raise_for_status()
|
||||
return [KnowledgeSearchResult.model_validate(item) for item in response.json()]
|
||||
|
||||
def explain_knowledge_search(
|
||||
self,
|
||||
keyword: str = "",
|
||||
*,
|
||||
knowledge_base_id: str | None = None,
|
||||
limit: int = 5,
|
||||
candidate_limit: int = 20,
|
||||
strategy: str = "hybrid",
|
||||
context: RequestContext | None = None,
|
||||
) -> KnowledgeSearchExplain:
|
||||
params: dict[str, Any] = {
|
||||
"keyword": keyword,
|
||||
"limit": limit,
|
||||
"candidate_limit": candidate_limit,
|
||||
"strategy": strategy,
|
||||
}
|
||||
if knowledge_base_id:
|
||||
params["knowledge_base_id"] = knowledge_base_id
|
||||
response = self._client.get(
|
||||
f"{self._api_prefix}/knowledge-chunks/search/explain",
|
||||
params=params,
|
||||
headers=(context or RequestContext()).to_headers(),
|
||||
)
|
||||
response.raise_for_status()
|
||||
return KnowledgeSearchExplain.model_validate(response.json())
|
||||
|
||||
def list_ai_run_audits(
|
||||
self,
|
||||
*,
|
||||
|
||||
@@ -284,6 +284,62 @@ def test_ai_platform_client_covers_admin_data_endpoints() -> None:
|
||||
}
|
||||
],
|
||||
)
|
||||
if request.url.path == "/api/v1/knowledge-chunks/search/explain":
|
||||
query = parse_qs(request.url.query.decode())
|
||||
assert query["keyword"] == ["lease"]
|
||||
assert query["limit"] == ["3"]
|
||||
assert query["candidate_limit"] == ["9"]
|
||||
assert query["strategy"] == ["hybrid"]
|
||||
return httpx.Response(
|
||||
200,
|
||||
json={
|
||||
"keyword": "lease",
|
||||
"normalized_terms": ["lease"],
|
||||
"retrieval_strategy": "hybrid",
|
||||
"knowledge_base_ids": [],
|
||||
"limit": 3,
|
||||
"candidate_limit": 9,
|
||||
"candidate_count": 1,
|
||||
"included_count": 1,
|
||||
"retrieval_backend": "json-explain",
|
||||
"embedding_provider": "local-hash",
|
||||
"embedding_model": "local-hash-v1",
|
||||
"query_embedding_dimension": 64,
|
||||
"keyword_threshold": 0.0,
|
||||
"vector_threshold": 0.35,
|
||||
"hybrid_keyword_weight": 0.55,
|
||||
"hybrid_vector_weight": 0.45,
|
||||
"results": [
|
||||
{
|
||||
"chunk": {
|
||||
"id": "chunk-1",
|
||||
"document_id": "doc-1",
|
||||
"import_batch_id": "kb-batch-1",
|
||||
"knowledge_base_id": "laws-cn",
|
||||
"title": "Civil Code",
|
||||
"source_type": "law",
|
||||
"reference": "Article 703",
|
||||
"content": "A lease contract defines rent and use of leased property.",
|
||||
"chunk_index": 1,
|
||||
"locator": "chunk-1",
|
||||
"embedding_provider": "local-hash",
|
||||
"embedding_model": "local-hash-v1",
|
||||
"embedding_dimension": 64,
|
||||
"created_at": "2026-06-22T00:00:00Z",
|
||||
},
|
||||
"score": 0.82,
|
||||
"keyword_score": 0.76,
|
||||
"vector_score": 0.89,
|
||||
"hybrid_score": 0.82,
|
||||
"retrieval_strategy": "hybrid",
|
||||
"matched_terms": ["lease"],
|
||||
"included": True,
|
||||
"rank": 1,
|
||||
"filter_reason": None,
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
if request.url.path == "/api/v1/audit/ai-runs":
|
||||
return httpx.Response(
|
||||
200,
|
||||
@@ -369,6 +425,10 @@ def test_ai_platform_client_covers_admin_data_endpoints() -> None:
|
||||
chunk_result = client.search_knowledge_chunks("lease", limit=3)
|
||||
assert chunk_result[0].score == 0.82
|
||||
assert chunk_result[0].hybrid_score == 0.82
|
||||
explain = client.explain_knowledge_search("lease", limit=3, candidate_limit=9)
|
||||
assert explain.embedding_provider == "local-hash"
|
||||
assert explain.results[0].included is True
|
||||
assert explain.results[0].rank == 1
|
||||
audit = client.list_ai_run_audits(limit=5)[0]
|
||||
assert audit.operation == "legal_qa"
|
||||
assert audit.retrieved_chunk_ids == ["chunk-1"]
|
||||
|
||||
Reference in New Issue
Block a user