feat(vault): Phase 3 tools + batch citation/relation enrichment
This commit is contained in:
@@ -5,14 +5,17 @@ graph-search.py — 图感知搜索
|
||||
|
||||
流程:
|
||||
1. qmd vsearch 获取语义匹配结果(Top 10)
|
||||
2. 对每个结果,读取 relations 字段 → 获取相邻节点
|
||||
2. 对每个结果,扩展相邻节点:
|
||||
- 默认:读取各页面 relations 字段(实时,慢但零状态)
|
||||
- --use-graph:读取预构建的 knowledge-graph.json(快,需先 build)
|
||||
3. 去重后返回(直接关联 + 关系扩展)
|
||||
|
||||
用法:
|
||||
python tools/scripts/graph-search.py "查询词" [--count 15] [--depth 1]
|
||||
python tools/scripts/graph-search.py "查询词" [--count 15] [--depth 1] [--use-graph]
|
||||
|
||||
依赖:
|
||||
- qmd 已安装(通过 node)
|
||||
- --use-graph 需先运行 knowledge-graph.py build
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
@@ -28,6 +31,7 @@ except ImportError:
|
||||
|
||||
QMD = r'node "C:\Users\hhhh2024\AppData\Roaming\npm\node_modules\@tobilu\qmd\dist\cli\qmd.js"'
|
||||
WIKI = Path(r"D:\Applications\app\kepano-obsidian-main\wiki")
|
||||
GRAPH_DB = Path(r"D:\Applications\app\kepano-obsidian-main\tools\data\knowledge-graph.json")
|
||||
|
||||
|
||||
def qmd_search(query: str, count: int = 10) -> list[str]:
|
||||
@@ -57,7 +61,7 @@ def get_relations(page_name: str) -> list[dict]:
|
||||
|
||||
|
||||
def expand_nodes(nodes: list[str], depth: int = 1) -> list[str]:
|
||||
"""从起始节点出发,沿 relations 扩展相邻节点"""
|
||||
"""从起始节点出发,沿 relations 扩展相邻节点(实时读取页面 frontmatter)"""
|
||||
expanded = list(nodes)
|
||||
frontier = list(nodes)
|
||||
for _ in range(depth):
|
||||
@@ -75,11 +79,38 @@ def expand_nodes(nodes: list[str], depth: int = 1) -> list[str]:
|
||||
return expanded
|
||||
|
||||
|
||||
def expand_nodes_via_graph(nodes: list[str], depth: int = 1, min_conf: int = 3) -> list[str]:
|
||||
"""通过预构建的 knowledge-graph.json 扩展(更快,无 IO)"""
|
||||
if not GRAPH_DB.exists():
|
||||
return list(nodes)
|
||||
graph = json.loads(GRAPH_DB.read_text(encoding="utf-8"))
|
||||
adj: dict[str, list] = {}
|
||||
for e in graph["edges"]:
|
||||
adj.setdefault(e["source"], []).append((e["target"], e.get("confidence", 3)))
|
||||
adj.setdefault(e["target"], []).append((e["source"], e.get("confidence", 3)))
|
||||
|
||||
expanded = list(nodes)
|
||||
frontier = list(nodes)
|
||||
for _ in range(depth):
|
||||
next_frontier = []
|
||||
for node in frontier:
|
||||
for target, conf in adj.get(node, []):
|
||||
if conf >= min_conf and target not in expanded:
|
||||
expanded.append(target)
|
||||
next_frontier.append(target)
|
||||
frontier = next_frontier
|
||||
if not frontier:
|
||||
break
|
||||
return expanded
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Graph-aware search for wiki pages")
|
||||
parser.add_argument("query", help="Search query")
|
||||
parser.add_argument("--count", type=int, default=15, help="Max results")
|
||||
parser.add_argument("--depth", type=int, default=1, help="Graph expansion depth")
|
||||
parser.add_argument("--use-graph", action="store_true",
|
||||
help="Use prebuilt knowledge-graph.json instead of live frontmatter")
|
||||
args = parser.parse_args()
|
||||
|
||||
print(f"=== Searching: {args.query} ===")
|
||||
@@ -92,10 +123,20 @@ def main():
|
||||
for r in results:
|
||||
print(f" [[{r}]]")
|
||||
|
||||
expanded = expand_nodes(results, depth=args.depth)
|
||||
if args.use_graph and not GRAPH_DB.exists():
|
||||
print(f"\n (note: {GRAPH_DB.name} not found, run `knowledge-graph.py build`; "
|
||||
f"falling back to live frontmatter)")
|
||||
|
||||
if args.use_graph and GRAPH_DB.exists():
|
||||
expanded = expand_nodes_via_graph(results, depth=args.depth)
|
||||
source = "knowledge-graph.json"
|
||||
else:
|
||||
expanded = expand_nodes(results, depth=args.depth)
|
||||
source = "live frontmatter"
|
||||
|
||||
new = [e for e in expanded if e not in results]
|
||||
if new:
|
||||
print(f"\nGraph-expanded (via relations, depth={args.depth}):")
|
||||
print(f"\nGraph-expanded (via {source}, depth={args.depth}):")
|
||||
for n in new[:args.count]:
|
||||
print(f" [[{n}]]")
|
||||
if len(new) > args.count:
|
||||
|
||||
Reference in New Issue
Block a user