# 임베딩 코사인 유사도로 시드 중심의 추정 연관 경로(트리형 네트워크)를 만드는 모듈 import numpy as np from clusterer import _model def build_search_path(seed: str, df, embeddings, top_n: int = 12) -> dict: # df와 embeddings는 같은 순서로 정렬돼 있어야 한다 (filter_by_relevance 출력) # 실제 순차 검색 로그가 아니라, 의미 유사도 기반 추정 연결망이다. if len(df) < 2: return {"nodes": [], "edges": []} df = df.reset_index(drop=True).copy() df["total_volume"] = df["search_volume_pc"].fillna(0) + df["search_volume_mobile"].fillna(0) emb = np.asarray(embeddings) # 검색량 상위 키워드를 노드 후보로 (시드 자신은 제외) order = df.sort_values("total_volume", ascending=False) picked = [i for i in order.index if df.loc[i, "keyword"] != seed][:top_n] seed_emb = _model().encode([seed], normalize_embeddings=True)[0] seed_vol = int(df.loc[df["keyword"] == seed, "total_volume"].max()) if (df["keyword"] == seed).any() else None nodes = [{"id": 0, "keyword": seed, "volume": seed_vol, "depth": 0}] node_vecs = [seed_emb] edges = [] # 각 키워드를 "이미 배치된 노드 중 가장 유사한 것"에 붙여 트리를 만든다 for rank, i in enumerate(picked, start=1): vec = emb[i] sims = [float(np.dot(vec, nv)) for nv in node_vecs] # 정규화 임베딩 → 내적=코사인 parent = int(np.argmax(sims)) nodes.append({ "id": rank, "keyword": df.loc[i, "keyword"], "volume": int(df.loc[i, "total_volume"]), "depth": nodes[parent]["depth"] + 1, }) node_vecs.append(vec) edges.append({"source": parent, "target": rank, "relation": round(sims[parent], 3)}) return {"nodes": nodes, "edges": edges}