第 2 课

查图谱:god-nodes、explain、path、query、affected

用 god-nodes 找核心类,用 explain 看符号的位置和连接,用 path 追两个符号的最短路径;处理同名符号的歧义和退出码 3、4,再用 query 取预算内的子图、用 affected 估算改动影响。

图文25 分钟Graphify 官方文档 ↗

课程目录第 2 / 3 课

学习位置仅保存在当前浏览器,有效期 180 天。

01 / 图文教材

查图谱:god-nodes、explain、path、query、affected

本课对照 Graphify 官方 CLI quickstart、Ask better graph questions 与 CLI reference 文档编写;命令与输出在 Debian 13 x86_64 测试机上实跑,版本 graphifyy 0.9.108(2026-11-15),示例仓库 click 8.2.1。

本课目标

  • 用 god-nodes 找出仓库的核心符号
  • 用 explain 看一个符号在哪、和谁相连,用 path 找两个符号之间的最短路径
  • 用 query 按关键词取一张控制在 token 预算内的子图,用 affected 估算改动影响
  • 看懂同名符号的歧义提示和退出码 3、4

前置条件

  • 做完第 1 课,在 click 目录里已经有 graphify-out/graph.json
  • 下面所有命令都在 click 目录里执行

第 1 步:先看核心节点

graphify god-nodes --top 5
God nodes (most connected):
  1. Context - 164 edges
  2. Command - 94 edges
  3. Parameter - 86 edges
  4. Option - 45 edges
  5. Group - 40 edges

连接最多的节点就是大部分改动都会碰到的地方。接手一个陌生仓库时,先从这几个类读起,效率最高。加 --json 可以输出 JSON,方便脚本处理。

第 2 步:用 explain 看一个符号

graphify explain "Command"

对照结果(只保留前 10 条连接):

Node: Command
  ID:        src_click_core_command
  Source:    src/click/core.py L843
  Type:      code
  Community: Community 6
  Degree:    94

Connections (94):
  <-- .invoke() [references] [EXTRACTED] src/click/testing.py:L423
  <-- click/__init__.py [imports] [EXTRACTED] src/click/__init__.py:L11
  <-- core.py [contains] [EXTRACTED] src/click/core.py:L843
  <-- Group [inherits] [EXTRACTED] src/click/core.py:L1460
  <-- shell_completion.py [imports] [EXTRACTED] src/click/shell_completion.py:L10
  <-- exceptions.py [imports] [EXTRACTED] src/click/exceptions.py:L14
  <-- decorators.py [imports] [EXTRACTED] src/click/decorators.py:L9
  <-- CliRunner [uses] [INFERRED 0.95] src/click/testing.py:L259
  <-- testing.py [imports] [EXTRACTED] src/click/testing.py:L23
  --> CompletionItem [uses] [INFERRED 0.95] src/click/core.py:L1228
  ... and 74 more (--all lists all 94)

<-- 表示别人指向它,--> 表示它指向别人。每条边都带文件和行号,可以直接跳过去核对。默认只列 20 条,加 --all 列出全部。

第 3 步:遇到同名符号怎么办

click 里有好几个叫 echo() 的函数。直接查会得到歧义提示:

graphify explain "echo()"
echo "exit=$?"
Ambiguous: 'echo()' matches 3 nodes in different files.
  tests/test_utils.py
    id: tests_test_utils_test_prompts_eof_echo
  src/click/utils.py
    id: src_click_utils_echo
  src/click/testing.py
    id: src_click_testing_echoingstdin_echo
Retry with path::symbol using one of the paths above (e.g. <path>::echo()) or the full node id.
exit=3

按提示用「文件路径::符号」的写法指定是哪一个:

graphify explain "src/click/utils.py::echo()" | head -7
Node: echo()
  ID:        src_click_utils_echo
  Source:    src/click/utils.py L222
  Type:      code
  Community: Community 29
  Degree:    35

函数名要带上括号,例如 "echo()",不带括号可能匹配不到。

第 4 步:用 path 找两个符号怎么连起来

graphify path "src/click/utils.py::echo()" "Context"
Shortest path (3 hops):
  echo() --calls [EXTRACTED]--> resolve_color_default() --calls [EXTRACTED]--> get_current_context() --uses [EXTRACTED/INFERRED 0.95]--> Context
Path confidence: INFERRED (weakest hop 3: uses, score 0.95) (weakest of 3 equal-length routes)

默认按边的方向找。加 --undirected 忽略方向,路径可能更短:

graphify path "src/click/utils.py::echo()" "Context" --undirected
Shortest path (2 hops):
  echo() <--calls [EXTRACTED]-- help_option() --uses [INFERRED 0.95]--> Context
Path confidence: INFERRED (weakest hop 2: uses, score 0.95) (weakest of 9 equal-length routes)

Path confidence 取整条路径里最弱的一跳。只要有一跳是 INFERRED,整条路径就标为 INFERRED,需要你回源码确认。

第 5 步:用 query 取一张子图

graphify query "prompt confirm" --budget 300

对照结果(节选):

Graph: graphify-out/graph.json (2080 nodes) | Traversal: BFS depth=2 | Start: ['confirm()', 'prompt()', ...] | 104 nodes found

[!] TRUNCATED: showing 10 of 104 nodes (~300-token budget). ...

NODE confirm() [src=src/click/termui.py loc=L194 community=Community 32]
NODE prompt() [src=src/click/termui.py loc=L83 community=Community 32]
NODE prompt_func() [src=src/click/termui.py loc=L136 community=Community 32]
NODE _build_prompt() [src=src/click/termui.py loc=L60 community=Community 32]
NODE .prompt_for_value() [src=src/click/core.py loc=L2928 community=Community 13]
...

query 从和关键词匹配的节点出发,按广度优先走两层,再按 --budget 截断,默认预算是 2000 个 token。这正是给编程助手喂上下文的方式:只给它相关的那一小块图,而不是整个仓库。截断时输出会告诉你需要多大的预算才能拿全。

关键词要用符号名或文件名。写成一整句自然语言,起点会变得很杂;完全匹配不到时会这样:

graphify query "kubernetes"
echo "exit=$?"
No matching nodes found.
exit=4

第 6 步:用 affected 估算改动影响

想改 make_str() 之前,先看哪些地方会受影响:

graphify affected "make_str()"
Affected nodes for make_str()
Relations: calls, indirect_call, references, imports, imports_from, dynamic_import, re_exports, inherits, extends, implements, uses, mixes_in, embeds, defines, requires
Depth: 2
- .resolve_command() [calls] src/click/core.py:L1867
- core.py [imports] src/click/core.py:L43
- .invoke() [calls] src/click/core.py:L1824
- decorators.py [imports_from] src/click/decorators.py:L13
- test_shell_completion.py [imports_from] tests/test_shell_completion.py:L9
- click/__init__.py [re_exports] src/click/__init__.py:L10
- testing.py [imports_from] src/click/testing.py:L23
- test_context.py [imports_from] tests/test_context.py:L10

affected 沿边反向走两层,列出调用、导入或引用了它的地方,连受影响的测试文件也一起列出来。

常见坑

  • 退出码 3:名字匹配到多个节点。用「路径::符号」或完整节点 ID 重试
  • 退出码 4:一个节点都没匹配到。换成符号名或文件名,函数记得带括号
  • path 提示 No directed path:加 --undirected 忽略边的方向
  • 把 INFERRED 当成事实:INFERRED 边是解析器推断的,带置信度。做重构决定前,按输出里的文件和行号回源码核对

小结

god-nodes 看全局,explain 看一个点,path 看两点之间,query 取一块上下文,affected 看改动波及面。下一课把这些能力交给编程助手,并让图谱跟着代码自动更新。