scope_link.py 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. """作用域定位(scope-link):候选值 → 本地树 embedding 余弦最近邻 → top-K。
  2. 对得上(高分)→ 复用现有节点原名,ingest 时按名挂靠;对不上 → 保留为新建值(丰富树)。
  3. 本地内存 + numpy 暴力最近邻,无需向量数据库。
  4. 编程接口:
  5. from scripts.scope_link import ScopeLinker
  6. ScopeLinker().link("撕裂共识", source_type="作用", top_k=5)
  7. 自测:python scripts/scope_link.py
  8. """
  9. from __future__ import annotations
  10. import json
  11. from pathlib import Path
  12. import numpy as np
  13. from core.embedding import ArkEmbedConfig, embed_text
  14. OUT = Path("scope_trees")
  15. class ScopeLinker:
  16. def __init__(self, env_file: str = ".env") -> None:
  17. self.index = json.loads((OUT / "trees_index.json").read_text(encoding="utf-8"))
  18. emb = np.load(OUT / "trees_embeddings.npy")
  19. self.norm = emb / (np.linalg.norm(emb, axis=1, keepdims=True) + 1e-9)
  20. self.src = np.array([it["source_type"] for it in self.index])
  21. self.cfg = ArkEmbedConfig.from_env(env_file)
  22. def link(self, candidate: str, source_type: str | None = None, top_k: int = 5) -> list[dict]:
  23. q = np.asarray(embed_text(candidate, self.cfg), dtype=np.float32)
  24. q = q / (np.linalg.norm(q) + 1e-9)
  25. sims = self.norm @ q
  26. idxs = (np.where(self.src == source_type)[0] if source_type
  27. else np.arange(len(sims)))
  28. order = idxs[np.argsort(-sims[idxs])][:top_k]
  29. return [{
  30. "name": self.index[i]["name"], "path": self.index[i]["path"],
  31. "source_type": self.index[i]["source_type"],
  32. "score": round(float(sims[i]), 4),
  33. } for i in order]
  34. def _selftest() -> None:
  35. sl = ScopeLinker()
  36. cases = [
  37. ("撕裂共识", "作用"), ("撕裂共识", "意图"), ("三幕结构", "形式"),
  38. ("角色代入共鸣", "感受"), ("反转", "形式"), ("引发讨论", "作用"),
  39. ]
  40. for cand, st in cases:
  41. print(f"\n[{cand}] @ {st}:")
  42. for hit in sl.link(cand, st, 3):
  43. print(f" {hit['score']:.3f} {hit['name']} ({hit['path']})")
  44. if __name__ == "__main__":
  45. _selftest()