# ============================================================ # 混合嵌入降级黄金测试 # ============================================================ from __future__ import annotations from server.knowledge.embedding import EmbeddingProvider, EmbeddingStore, _cosine from server.knowledge.retrieval import hybrid_search def test_embedding_provider_falls_back_to_none(monkeypatch): monkeypatch.setenv("EMBEDDING_PROVIDER", "off") monkeypatch.delenv("EMBEDDING_API_KEY", raising=False) monkeypatch.delenv("LLM_API_KEY", raising=False) p = EmbeddingProvider() assert p.backend == "none" assert p.enabled is False assert p.embed(["hello"]) is None def test_cosine_and_store_search(tmp_path): store = EmbeddingStore(path=str(tmp_path / "emb.json")) store.upsert_many({ "a:1": [1.0, 0.0, 0.0], "b:1": [0.0, 1.0, 0.0], "c:1": [0.9, 0.1, 0.0], }) hits = store.search([1.0, 0.0, 0.0], top_k=2) assert hits[0][0] == "a:1" assert hits[0][1] == 1.0 assert _cosine([1, 0], [0, 1]) == 0.0 def test_hybrid_search_works_without_vectors(): units = [{ "assetId": "x1", "title": "换线标准SOP", "kind": "sop", "version": "v1", "tags": ["换线"], "content": "产线换型必须遵守夜班不换线规定。", "chunkId": "c1", "heading": None, "page": None, }] hits = hybrid_search(units, "换线夜班", top_k=3) assert hits assert hits[0]["assetId"] == "x1" assert hits[0]["version"] == "v1" miss = hybrid_search(units, "quantum blockchain xyzzy", top_k=3) assert miss == []