【Graphify知识图谱执行设计实践】

知识图谱执行流程

面向新人的 Graphify 知识图谱系统完整链路说明

精确到每个文件和关键函数,方便定位和调试


一、系统总览

#mermaid-svg-KOe6LZKly0jy66zR{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-KOe6LZKly0jy66zR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KOe6LZKly0jy66zR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KOe6LZKly0jy66zR .error-icon{fill:#552222;}#mermaid-svg-KOe6LZKly0jy66zR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KOe6LZKly0jy66zR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KOe6LZKly0jy66zR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KOe6LZKly0jy66zR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KOe6LZKly0jy66zR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KOe6LZKly0jy66zR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KOe6LZKly0jy66zR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KOe6LZKly0jy66zR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KOe6LZKly0jy66zR .marker.cross{stroke:#333333;}#mermaid-svg-KOe6LZKly0jy66zR svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KOe6LZKly0jy66zR p{margin:0;}#mermaid-svg-KOe6LZKly0jy66zR .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-KOe6LZKly0jy66zR .cluster-label text{fill:#333;}#mermaid-svg-KOe6LZKly0jy66zR .cluster-label span{color:#333;}#mermaid-svg-KOe6LZKly0jy66zR .cluster-label span p{background-color:transparent;}#mermaid-svg-KOe6LZKly0jy66zR .label text,#mermaid-svg-KOe6LZKly0jy66zR span{fill:#333;color:#333;}#mermaid-svg-KOe6LZKly0jy66zR .node rect,#mermaid-svg-KOe6LZKly0jy66zR .node circle,#mermaid-svg-KOe6LZKly0jy66zR .node ellipse,#mermaid-svg-KOe6LZKly0jy66zR .node polygon,#mermaid-svg-KOe6LZKly0jy66zR .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KOe6LZKly0jy66zR .rough-node .label text,#mermaid-svg-KOe6LZKly0jy66zR .node .label text,#mermaid-svg-KOe6LZKly0jy66zR .image-shape .label,#mermaid-svg-KOe6LZKly0jy66zR .icon-shape .label{text-anchor:middle;}#mermaid-svg-KOe6LZKly0jy66zR .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-KOe6LZKly0jy66zR .rough-node .label,#mermaid-svg-KOe6LZKly0jy66zR .node .label,#mermaid-svg-KOe6LZKly0jy66zR .image-shape .label,#mermaid-svg-KOe6LZKly0jy66zR .icon-shape .label{text-align:center;}#mermaid-svg-KOe6LZKly0jy66zR .node.clickable{cursor:pointer;}#mermaid-svg-KOe6LZKly0jy66zR .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-KOe6LZKly0jy66zR .arrowheadPath{fill:#333333;}#mermaid-svg-KOe6LZKly0jy66zR .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-KOe6LZKly0jy66zR .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-KOe6LZKly0jy66zR .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KOe6LZKly0jy66zR .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-KOe6LZKly0jy66zR .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KOe6LZKly0jy66zR .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-KOe6LZKly0jy66zR .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-KOe6LZKly0jy66zR .cluster text{fill:#333;}#mermaid-svg-KOe6LZKly0jy66zR .cluster span{color:#333;}#mermaid-svg-KOe6LZKly0jy66zR div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-KOe6LZKly0jy66zR .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-KOe6LZKly0jy66zR rect.text{fill:none;stroke-width:0;}#mermaid-svg-KOe6LZKly0jy66zR .icon-shape,#mermaid-svg-KOe6LZKly0jy66zR .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KOe6LZKly0jy66zR .icon-shape p,#mermaid-svg-KOe6LZKly0jy66zR .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-KOe6LZKly0jy66zR .icon-shape .label rect,#mermaid-svg-KOe6LZKly0jy66zR .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KOe6LZKly0jy66zR .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-KOe6LZKly0jy66zR .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-KOe6LZKly0jy66zR :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 运行时(AI 查询)
开发时(构建图谱)
源代码变更
graphify update .
AST 解析
构建图
社群聚类
导出 graph.json
Cursor 启动
MCP Server
加载 graph.json
等待查询
AI 发起查询
返回相关节点

核心文件位置.venv\Lib\site-packages\graphify\

文件 行数 职责
__main__.py 4583 CLI 主入口,命令分发
serve.py 1312 MCP stdio 服务器 + 查询引擎
extract.py 11658 AST 提取(tree-sitter,30+ 语言)
build.py 490 NetworkX 图构建
cluster.py 273 Leiden/Louvain 社群聚类
report.py --- 生成 GRAPH_REPORT.md
export.py --- 导出 JSON/HTML
watch.py --- 增量更新 + 文件监听
cache.py --- AST 缓存管理
manifest.py --- 文件变更清单追踪

二、链路一:Cursor 启动 → MCP 服务就绪 → AI 查询

这是运行时的核心链路:AI 如何查询知识图谱。
Claude/AI graph.json serve.py Python 进程 Cursor IDE Claude/AI graph.json serve.py Python 进程 Cursor IDE #mermaid-svg-mM0Q8uY3M9JdI5qn{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mM0Q8uY3M9JdI5qn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mM0Q8uY3M9JdI5qn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mM0Q8uY3M9JdI5qn .error-icon{fill:#552222;}#mermaid-svg-mM0Q8uY3M9JdI5qn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mM0Q8uY3M9JdI5qn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mM0Q8uY3M9JdI5qn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mM0Q8uY3M9JdI5qn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mM0Q8uY3M9JdI5qn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mM0Q8uY3M9JdI5qn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mM0Q8uY3M9JdI5qn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mM0Q8uY3M9JdI5qn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mM0Q8uY3M9JdI5qn .marker.cross{stroke:#333333;}#mermaid-svg-mM0Q8uY3M9JdI5qn svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mM0Q8uY3M9JdI5qn p{margin:0;}#mermaid-svg-mM0Q8uY3M9JdI5qn .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-mM0Q8uY3M9JdI5qn text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-mM0Q8uY3M9JdI5qn .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-mM0Q8uY3M9JdI5qn .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-mM0Q8uY3M9JdI5qn .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-mM0Q8uY3M9JdI5qn .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-mM0Q8uY3M9JdI5qn #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-mM0Q8uY3M9JdI5qn .sequenceNumber{fill:white;}#mermaid-svg-mM0Q8uY3M9JdI5qn #sequencenumber{fill:#333;}#mermaid-svg-mM0Q8uY3M9JdI5qn #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-mM0Q8uY3M9JdI5qn .messageText{fill:#333;stroke:none;}#mermaid-svg-mM0Q8uY3M9JdI5qn .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-mM0Q8uY3M9JdI5qn .labelText,#mermaid-svg-mM0Q8uY3M9JdI5qn .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-mM0Q8uY3M9JdI5qn .loopText,#mermaid-svg-mM0Q8uY3M9JdI5qn .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-mM0Q8uY3M9JdI5qn .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-mM0Q8uY3M9JdI5qn .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-mM0Q8uY3M9JdI5qn .noteText,#mermaid-svg-mM0Q8uY3M9JdI5qn .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-mM0Q8uY3M9JdI5qn .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-mM0Q8uY3M9JdI5qn .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-mM0Q8uY3M9JdI5qn .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-mM0Q8uY3M9JdI5qn .actorPopupMenu{position:absolute;}#mermaid-svg-mM0Q8uY3M9JdI5qn .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-mM0Q8uY3M9JdI5qn .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-mM0Q8uY3M9JdI5qn .actor-man circle,#mermaid-svg-mM0Q8uY3M9JdI5qn line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-mM0Q8uY3M9JdI5qn :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 启动时读取 .cursor/mcp.json 🟢 服务就绪,等待请求 启动 python.exe -m graphify.serve graphify-out/graph.json__main__.py → serve.py:serve()_build_server(graph_path)_load_graph() → 读取 JSONNetworkX Graph 对象(2494节点)asyncio.run() → stdio_server 就绪call_tool("query_graph", {question: "保存接口"})_maybe_reload() → 检查文件是否变化_query_terms("保存接口") → 分词_score_nodes(G, terms) → IDF 评分_pick_seeds(scored) → 选种子节点_bfs_context(seeds, depth=3) → BFS 遍历_format_result() → 按社群分组 + Token 裁剪返回相关节点和关系文本

关键文件调用栈

复制代码
.cursor/mcp.json                          ← 配置入口
  └─ command: ".venv/Scripts/python.exe"
     └─ args: ["-m", "graphify.serve", "graphify-out/graph.json"]

graphify/__main__.py :: main()            ← Python 包入口
  └─ 检测 sys.argv → cmd = "serve"
     └─ serve.py :: serve(graph_path)     ← MCP 服务入口
        ├─ _load_graph(path)              ← 加载图谱
        │   └─ json.load() → nx.node_link_graph()
        ├─ _build_server(graph_path)      ← 注册 MCP 工具
        │   ├─ list_tools()               ← 声明可用工具
        │   └─ call_tool(name, args)      ← 工具调度路由
        └─ asyncio.run(main())            ← 启动 stdio 事件循环

查询引擎内部逻辑

复制代码
serve.py :: _query_graph_text(G, question, mode, depth, token_budget)

  Step 1: 分词
  ├─ _query_terms(question)
  │   ├─ 中文 → jieba.cut() 分词
  │   └─ 英文 → re.findall(r"\w+")

  Step 2: 节点评分
  ├─ _score_nodes(G, terms)
  │   ├─ 精确匹配 × 1000
  │   ├─ 前缀匹配 × 100
  │   ├─ 子串匹配 × 1
  │   └─ 源文件路径匹配 × 0.5

  Step 3: 选择种子节点
  ├─ _pick_seeds(scored, max_k=3, gap_ratio=0.2)
  │   └─ 按分数排序,取 Top-K(置信度间隙过滤)

  Step 4: 图遍历
  ├─ _bfs_context(seeds, depth=3)  或  _dfs_context()
  │   ├─ 层 0: 种子节点
  │   ├─ 层 1: 直接邻居
  │   ├─ 层 2: 二度关系
  │   └─ 层 3: 三度关系

  Step 5: 格式化返回
  └─ _format_result(nodes, edges, token_budget)
      ├─ 按 community 分组
      ├─ 去重
      └─ 截断至 token 限制

MCP 暴露的 5 个工具

工具名 功能 关键参数
query_graph 自然语言查询图谱 question, mode(bfs/dfs), depth, token_budget
get_node 获取节点详情 label(节点标签或 ID)
get_neighbors 获取邻居节点 label, relation_filter
list_communities 列出所有社群 ---
get_community 获取社群内节点 community_id

三、链路二:代码变更 → 图谱更新

这是构建时的核心链路:代码变更后如何更新知识图谱。
graphify-out/ report.py cluster.py build.py extract.py manifest.json watch.py graphify CLI 开发者 graphify-out/ report.py cluster.py build.py extract.py manifest.json watch.py graphify CLI 开发者 #mermaid-svg-7pNHIag0p7h6lUY5{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7pNHIag0p7h6lUY5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7pNHIag0p7h6lUY5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7pNHIag0p7h6lUY5 .error-icon{fill:#552222;}#mermaid-svg-7pNHIag0p7h6lUY5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7pNHIag0p7h6lUY5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7pNHIag0p7h6lUY5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7pNHIag0p7h6lUY5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7pNHIag0p7h6lUY5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7pNHIag0p7h6lUY5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7pNHIag0p7h6lUY5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7pNHIag0p7h6lUY5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7pNHIag0p7h6lUY5 .marker.cross{stroke:#333333;}#mermaid-svg-7pNHIag0p7h6lUY5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7pNHIag0p7h6lUY5 p{margin:0;}#mermaid-svg-7pNHIag0p7h6lUY5 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-7pNHIag0p7h6lUY5 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-7pNHIag0p7h6lUY5 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-7pNHIag0p7h6lUY5 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-7pNHIag0p7h6lUY5 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-7pNHIag0p7h6lUY5 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-7pNHIag0p7h6lUY5 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-7pNHIag0p7h6lUY5 .sequenceNumber{fill:white;}#mermaid-svg-7pNHIag0p7h6lUY5 #sequencenumber{fill:#333;}#mermaid-svg-7pNHIag0p7h6lUY5 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-7pNHIag0p7h6lUY5 .messageText{fill:#333;stroke:none;}#mermaid-svg-7pNHIag0p7h6lUY5 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-7pNHIag0p7h6lUY5 .labelText,#mermaid-svg-7pNHIag0p7h6lUY5 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-7pNHIag0p7h6lUY5 .loopText,#mermaid-svg-7pNHIag0p7h6lUY5 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-7pNHIag0p7h6lUY5 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-7pNHIag0p7h6lUY5 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-7pNHIag0p7h6lUY5 .noteText,#mermaid-svg-7pNHIag0p7h6lUY5 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-7pNHIag0p7h6lUY5 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-7pNHIag0p7h6lUY5 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-7pNHIag0p7h6lUY5 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-7pNHIag0p7h6lUY5 .actorPopupMenu{position:absolute;}#mermaid-svg-7pNHIag0p7h6lUY5 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-7pNHIag0p7h6lUY5 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-7pNHIag0p7h6lUY5 .actor-man circle,#mermaid-svg-7pNHIag0p7h6lUY5 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-7pNHIag0p7h6lUY5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} tree-sitter AST 解析 图谱更新完成 ✅ graphify update . --forcemain.py → _rebuild_code()读取 manifest.json(历史 hash)扫描文件系统 → 对比 mtime + hash标记变动文件列表extract(changed_files)collect_files() + .graphifyignore 过滤并行: extract_csharp() / extract_js() / ...{nodes: ..., edges: ...}build_from_json(extraction)三层去重 + NetworkX 构建nx.Graph 对象cluster(G, resolution=1.0)Leiden 算法 → 社群分割 → ID 稳定化{community_id: nodes}generate(G, communities)GRAPH_REPORT.mdgraph.json + manifest.json + .graphify_labels.json

关键文件调用栈

复制代码
命令行: .\.venv\Scripts\graphify update . [--force]

graphify/__main__.py :: main()                ← CLI 入口
  └─ cmd = "update"
     └─ watch.py :: _rebuild_code(path, force, no_cluster)
        │
        ├─ 1. 增量检测
        │   ├─ manifest.py :: load_manifest()     ← 读取历史清单
        │   ├─ 扫描文件系统 mtime
        │   └─ 对比 ast_hash → 识别变动文件
        │
        ├─ 2. AST 提取
        │   ├─ extract.py :: extract(target)      ← 主提取函数
        │   │   ├─ collect_files()                ← 收集文件(.graphifyignore 过滤)
        │   │   ├─ extract_csharp(path)           ← C# 提取器
        │   │   ├─ extract_js(path)               ← JS/Vue 提取器
        │   │   ├─ extract_python(path)           ← Python 提取器
        │   │   └─ ... (30+ 语言)
        │   └─ cache.py :: load_cached() / save_cached()
        │       └─ 缓存位置: graphify-out/cache/ast/<hash>.json
        │
        ├─ 3. 图构建
        │   └─ build.py :: build_from_json(extraction)
        │       ├─ _normalize_id()                ← 节点 ID 标准化
        │       ├─ _deduplicate_by_label()        ← 三层去重
        │       └─ nx.Graph.add_node() / .add_edge()
        │
        ├─ 4. 社群聚类
        │   └─ cluster.py :: cluster(G, resolution=1.0)
        │       ├─ _partition(G)                  ← Leiden 优先,Louvain 备选
        │       ├─ 社群分割(>25% 图 → 递归)
        │       └─ remap_communities_to_previous() ← ID 稳定化
        │
        ├─ 5. 报告生成
        │   └─ report.py :: generate(G, communities)
        │       ├─ analyze.py :: god_nodes()
        │       └─ analyze.py :: surprising_connections()
        │
        └─ 6. 导出保存
            ├─ export.py :: to_json(G) → graphify-out/graph.json
            ├─ export.py :: to_html(G) → graphify-out/graph.html
            └─ manifest.py :: save_manifest() → graphify-out/manifest.json

增量 vs 全量

模式 命令 行为
增量(默认) graphify update . 只提取变动文件,保留旧节点
全量(强制) graphify update . --force 重新扫描全部文件

增量更新的判定逻辑:

复制代码
manifest.json 中的 ast_hash ≠ 当前文件内容 hash → 需要重新提取
manifest.json 中的 ast_hash = 当前文件内容 hash → 跳过,复用缓存

四、链路三:入图规则(何时触发更新)

#mermaid-svg-CX0HWL2F7OPfkMPp{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CX0HWL2F7OPfkMPp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CX0HWL2F7OPfkMPp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CX0HWL2F7OPfkMPp .error-icon{fill:#552222;}#mermaid-svg-CX0HWL2F7OPfkMPp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CX0HWL2F7OPfkMPp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CX0HWL2F7OPfkMPp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CX0HWL2F7OPfkMPp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CX0HWL2F7OPfkMPp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CX0HWL2F7OPfkMPp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CX0HWL2F7OPfkMPp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CX0HWL2F7OPfkMPp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CX0HWL2F7OPfkMPp .marker.cross{stroke:#333333;}#mermaid-svg-CX0HWL2F7OPfkMPp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CX0HWL2F7OPfkMPp p{margin:0;}#mermaid-svg-CX0HWL2F7OPfkMPp .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-CX0HWL2F7OPfkMPp .cluster-label text{fill:#333;}#mermaid-svg-CX0HWL2F7OPfkMPp .cluster-label span{color:#333;}#mermaid-svg-CX0HWL2F7OPfkMPp .cluster-label span p{background-color:transparent;}#mermaid-svg-CX0HWL2F7OPfkMPp .label text,#mermaid-svg-CX0HWL2F7OPfkMPp span{fill:#333;color:#333;}#mermaid-svg-CX0HWL2F7OPfkMPp .node rect,#mermaid-svg-CX0HWL2F7OPfkMPp .node circle,#mermaid-svg-CX0HWL2F7OPfkMPp .node ellipse,#mermaid-svg-CX0HWL2F7OPfkMPp .node polygon,#mermaid-svg-CX0HWL2F7OPfkMPp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CX0HWL2F7OPfkMPp .rough-node .label text,#mermaid-svg-CX0HWL2F7OPfkMPp .node .label text,#mermaid-svg-CX0HWL2F7OPfkMPp .image-shape .label,#mermaid-svg-CX0HWL2F7OPfkMPp .icon-shape .label{text-anchor:middle;}#mermaid-svg-CX0HWL2F7OPfkMPp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CX0HWL2F7OPfkMPp .rough-node .label,#mermaid-svg-CX0HWL2F7OPfkMPp .node .label,#mermaid-svg-CX0HWL2F7OPfkMPp .image-shape .label,#mermaid-svg-CX0HWL2F7OPfkMPp .icon-shape .label{text-align:center;}#mermaid-svg-CX0HWL2F7OPfkMPp .node.clickable{cursor:pointer;}#mermaid-svg-CX0HWL2F7OPfkMPp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CX0HWL2F7OPfkMPp .arrowheadPath{fill:#333333;}#mermaid-svg-CX0HWL2F7OPfkMPp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CX0HWL2F7OPfkMPp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CX0HWL2F7OPfkMPp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CX0HWL2F7OPfkMPp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CX0HWL2F7OPfkMPp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CX0HWL2F7OPfkMPp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CX0HWL2F7OPfkMPp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CX0HWL2F7OPfkMPp .cluster text{fill:#333;}#mermaid-svg-CX0HWL2F7OPfkMPp .cluster span{color:#333;}#mermaid-svg-CX0HWL2F7OPfkMPp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-CX0HWL2F7OPfkMPp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CX0HWL2F7OPfkMPp rect.text{fill:none;stroke-width:0;}#mermaid-svg-CX0HWL2F7OPfkMPp .icon-shape,#mermaid-svg-CX0HWL2F7OPfkMPp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CX0HWL2F7OPfkMPp .icon-shape p,#mermaid-svg-CX0HWL2F7OPfkMPp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CX0HWL2F7OPfkMPp .icon-shape .label rect,#mermaid-svg-CX0HWL2F7OPfkMPp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CX0HWL2F7OPfkMPp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CX0HWL2F7OPfkMPp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CX0HWL2F7OPfkMPp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 命中
未命中
L0
L1
L2
L3
AI 会话开始
路由器查询图谱
命中?
返回相关节点,继续工作
触发入图流程
分级处理
补充 reference 文件

→ graphify update .
已有文件未索引

→ graphify update .
需新增 Spec/CHG

→ graphify update . --force
路径被 .graphifyignore 过滤

→ 编辑过滤 + --force
图谱更新完成
下次查询命中 ✅

配置文件对应

规则/配置 文件路径 作用
入图规则 .cursor/rules/05-graphify-entry.mdc 定义何时必须查图、未命中怎么处理
运维操作 .cursor/skills/20-capabilities/10-graphify-ops/SKILL.md L0~L3 分级操作手册
过滤清单 .graphifyignore 哪些路径不纳入图谱
MCP 配置 .cursor/mcp.json 服务启动命令

五、数据流全景图

#mermaid-svg-aSaN2i5FF35tRSuf{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-aSaN2i5FF35tRSuf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-aSaN2i5FF35tRSuf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-aSaN2i5FF35tRSuf .error-icon{fill:#552222;}#mermaid-svg-aSaN2i5FF35tRSuf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-aSaN2i5FF35tRSuf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-aSaN2i5FF35tRSuf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-aSaN2i5FF35tRSuf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-aSaN2i5FF35tRSuf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-aSaN2i5FF35tRSuf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-aSaN2i5FF35tRSuf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-aSaN2i5FF35tRSuf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-aSaN2i5FF35tRSuf .marker.cross{stroke:#333333;}#mermaid-svg-aSaN2i5FF35tRSuf svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-aSaN2i5FF35tRSuf p{margin:0;}#mermaid-svg-aSaN2i5FF35tRSuf .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-aSaN2i5FF35tRSuf .cluster-label text{fill:#333;}#mermaid-svg-aSaN2i5FF35tRSuf .cluster-label span{color:#333;}#mermaid-svg-aSaN2i5FF35tRSuf .cluster-label span p{background-color:transparent;}#mermaid-svg-aSaN2i5FF35tRSuf .label text,#mermaid-svg-aSaN2i5FF35tRSuf span{fill:#333;color:#333;}#mermaid-svg-aSaN2i5FF35tRSuf .node rect,#mermaid-svg-aSaN2i5FF35tRSuf .node circle,#mermaid-svg-aSaN2i5FF35tRSuf .node ellipse,#mermaid-svg-aSaN2i5FF35tRSuf .node polygon,#mermaid-svg-aSaN2i5FF35tRSuf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-aSaN2i5FF35tRSuf .rough-node .label text,#mermaid-svg-aSaN2i5FF35tRSuf .node .label text,#mermaid-svg-aSaN2i5FF35tRSuf .image-shape .label,#mermaid-svg-aSaN2i5FF35tRSuf .icon-shape .label{text-anchor:middle;}#mermaid-svg-aSaN2i5FF35tRSuf .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-aSaN2i5FF35tRSuf .rough-node .label,#mermaid-svg-aSaN2i5FF35tRSuf .node .label,#mermaid-svg-aSaN2i5FF35tRSuf .image-shape .label,#mermaid-svg-aSaN2i5FF35tRSuf .icon-shape .label{text-align:center;}#mermaid-svg-aSaN2i5FF35tRSuf .node.clickable{cursor:pointer;}#mermaid-svg-aSaN2i5FF35tRSuf .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-aSaN2i5FF35tRSuf .arrowheadPath{fill:#333333;}#mermaid-svg-aSaN2i5FF35tRSuf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-aSaN2i5FF35tRSuf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-aSaN2i5FF35tRSuf .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-aSaN2i5FF35tRSuf .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-aSaN2i5FF35tRSuf .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-aSaN2i5FF35tRSuf .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-aSaN2i5FF35tRSuf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-aSaN2i5FF35tRSuf .cluster text{fill:#333;}#mermaid-svg-aSaN2i5FF35tRSuf .cluster span{color:#333;}#mermaid-svg-aSaN2i5FF35tRSuf div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-aSaN2i5FF35tRSuf .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-aSaN2i5FF35tRSuf rect.text{fill:none;stroke-width:0;}#mermaid-svg-aSaN2i5FF35tRSuf .icon-shape,#mermaid-svg-aSaN2i5FF35tRSuf .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-aSaN2i5FF35tRSuf .icon-shape p,#mermaid-svg-aSaN2i5FF35tRSuf .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-aSaN2i5FF35tRSuf .icon-shape .label rect,#mermaid-svg-aSaN2i5FF35tRSuf .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-aSaN2i5FF35tRSuf .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-aSaN2i5FF35tRSuf .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-aSaN2i5FF35tRSuf :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 消费层
服务层(serve.py
产物层(graphify-out/)
构建层
提取层(extract.py
源码层(输入)
.cs 文件

NRD.WebAPI.New/
.vue/.js 文件

nrd-eln/src/
.md 文件

docs/ + .cursor/skills/
.ps1 脚本

.cursor/hooks/
tree-sitter AST 解析
缓存

cache/ast/<hash>.json
NetworkX Graph

build.py
Leiden 聚类

cluster.py
报告生成

report.py
graph.json

2494 节点 · 3038 边
GRAPH_REPORT.md

统计 + 社群导航
graph.html

可视化界面
manifest.json

文件变更追踪
.graphify_labels.json

社群命名
MCP stdio Server
查询引擎

分词→评分→BFS→格式化
Claude / AI 助手
路由器

00-router-skill


六、热重载机制

复制代码
AI 发起查询
     ↓
serve.py :: _maybe_reload()
     ├─ 检查 graph.json 的 mtime
     ├─ 如果文件变化 → 重新加载
     │   └─ _load_graph() → 新的 NetworkX Graph
     └─ 如果未变化 → 使用内存缓存
     ↓
继续执行查询

这意味着:你执行 graphify update 后,无需重启 Cursor,下次 AI 查询会自动使用最新图谱。


七、常用命令速查

powershell 复制代码
# 增量更新图谱(日常使用)
.\.venv\Scripts\graphify update .

# 全量重建图谱(大改后使用)
.\.venv\Scripts\graphify update . --force

# 仅重新聚类(不重新提取)
.\.venv\Scripts\graphify cluster-only graphify-out/graph.json

# 给社群命名(需要 LLM API Key)
.\.venv\Scripts\graphify label graphify-out/

# 手动启动 MCP 服务(调试用)
.\.venv\Scripts\python.exe -m graphify.serve graphify-out/graph.json

# 查看当前图谱统计
打开 graphify-out/GRAPH_REPORT.md

# 可视化浏览图谱
打开 graphify-out/graph.html

八、新人快速理解要点

1. 两条独立链路

链路 触发时机 关键文件 产物
构建链路 手动执行 graphify update extract.pybuild.pycluster.py graph.json
查询链路 Cursor 启动后 AI 自动调用 serve.py(MCP 协议) 文本上下文

2. 三个核心概念

概念 解释 类比
节点(Node) 代码里的类、方法、配置项、业务术语 百科词条
边(Edge) 节点之间的关系(调用、继承、引用) 词条之间的超链接
社群(Community) 紧密关联的一组节点 百科的分类目录

3. 为什么需要这个系统?

复制代码
没有图谱:AI 要全局搜索 337 个文件 → 慢、不准、带入无关代码
有了图谱:AI 查一次图 → 精准定位 3-5 个相关节点 → 快、准、上下文紧凑