【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.py → build.py → cluster.py graph.json
查询链路 Cursor 启动后 AI 自动调用 serve.py(MCP 协议) 文本上下文

2. 三个核心概念

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

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

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