第 2 课
查图谱:god-nodes、explain、path、query、affected
用 god-nodes 找核心类,用 explain 看符号的位置和连接,用 path 追两个符号的最短路径;处理同名符号的歧义和退出码 3、4,再用 query 取预算内的子图、用 affected 估算改动影响。
课程目录第 2 / 3 课
学习位置仅保存在当前浏览器,有效期 180 天。
查图谱: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 看改动波及面。下一课把这些能力交给编程助手,并让图谱跟着代码自动更新。
