
graphify 知识图谱查询实战query / path / explain 三种遍历流程、Token 预算与 Work-Memory 反馈闭环【免费下载链接】graphifyTurn any codebase, with its docs, SQL schemas, configs, and PDFs, into a queryable knowledge graph. A /graphify skill for Claude Code, Cursor, Codex, and Gemini CLI: local deterministic AST parsing, every edge explained, no vector store.项目地址: https://gitcode.com/GitHub_Trending/graph/graphifygraphify 把任意代码库含文档、SQL 配置、PDF构建成一个本地可查询的知识图谱后对图谱提问是最高频的使用场景。本文以 Codex 平台的技能参考文档 graphify/skills/codex/references/query.md 为主体完整讲清三条查询流程——/graphify queryBFS/DFS 子图遍历、/graphify path两节点最短路径、/graphify explain单节点解释——的完整操作步骤、约束式查询扩词规则、内联 NetworkX 回退脚本以及save-resultreflect构成的自改进工作记忆闭环。读完后你既能照着文档逐步复现全部命令也能理解这些命令在 CLI 源码 中的实际行为与取舍。一、文档定位与适用前提该参考文档是/graphify技能体系在 Codex 平台下的查询参考当用户在已有图谱上提问或执行/graphify path、/graphify explain时加载。技能主文件中的 query 存根stub指向它来获取完整的遍历流程同一份参考在仓库中为各平台复制了一份如 graphify/skills/claude/references/query.md、graphify/skills/codex/references/query.md 等内容保持一致。流程的统一约定是优先使用graphifyCLI若已安装不可用时回退为内联 NetworkX 遍历。开始前必须先确认图谱存在$(cat graphify-out/.graphify_python) -c from pathlib import Path if not Path(graphify-out/graph.json).exists(): print(ERROR: No graph found. Run /graphify path first to build the graph.) raise SystemExit(1) 失败即停止并提示用户先执行/graphify path构建图谱。这里graphify-out/.graphify_python是构建阶段写入的 Python 解释器标记文件所有后续内联脚本都用$(cat graphify-out/.graphify_python)取到与构建一致的解释器避免环境问题。二、两种遍历模式BFS 与 DFS 的选择原文档给出了一张选型表这是整篇参考文档的决策起点模式标志适用问题BFS默认无标志X 与什么相连——要广域上下文最近邻优先DFS--dfsX 如何到达 Y——追踪具体链条或依赖路径在 CLI 中对应graphify query QUESTION # 或graphify query QUESTION --dfs --budget 3000对照 CLI query 命令实现 可以看到实际支持的参数比文档示例更多完整用法为graphify query question [--dfs] [--context C] [--budget N] [--graph path]其中--budget默认2000、--context用于附加上下文过滤、--graph指定非默认的graph.json路径。三、Step 0 — 约束式查询扩词遍历前必做这是本文档最有工程价值的部分。文档明确指出graphify 的queryCLI 通过小写折叠的子串匹配 IDF来命中节点在源码中对应 graphify/serve.py 的_query_terms、_compute_idf、_score_nodes等函数由 CLI 入口 导入调用二进制内部没有词干还原、没有同义词、没有跨语言匹配内联回退脚本也用同样的匹配方式。后果是如果用户提问的词汇与图谱标签不一致用户说обработчик / 图谱叫handler用户说 authentication / 图谱叫Guardian字面匹配器会返回 0 命中回答退化成噪声。文档给出的修复方式是不凭空造词而是先用真实图谱词汇表扩词第 1 步从节点标签抽取 token 词汇表$(cat graphify-out/.graphify_python) -c import json, re from pathlib import Path data json.loads(Path(graphify-out/graph.json).read_text(encodingutf-8)) vocab set() for n in data[nodes]: for c in re.findall(r[^\W\d_], n.get(label,) or , re.UNICODE): parts re.findall(r[A-Z](?[A-Z][a-z])|[A-Z]?[a-z]|[A-Z], c) or [c] for p in parts: t p.lower() if 3 len(t) 30: vocab.add(t) Path(graphify-out/.vocab.txt).write_text(\n.join(sorted(vocab)), encodingutf-8) print(fvocab: {len(vocab)} tokens) 注意细节驼峰切分[A-Z](?[A-Z][a-z])|...保证AuthHandler会拆出auth、handler长度过滤3 len(t) 30既去掉噪声又保留api、jwt、ios这类短但有效的 token。第 2 步从词汇表里选词硬约束四条只能选graphify-out/.vocab.txt中实际存在的 token最多 12 个绝不凭空造词若某个查询概念在词汇表中找不到合理对应直接跳过不要用训练记忆里的近义同义词顶替若完全没有词汇表 token 匹配该问题输出空列表并明确告诉用户语料库中没有相关词汇不要编造一次搜索跨语言转换只在目标 token 确实存在时进行俄语 аутентификация → 仅在词汇表存在时选auth、credential、token、security形态转换同理handlers →handler仅在handler存在时成立。第 3 步把选择显式打印给用户让扩词过程可审计Query expanded to (from graph vocab, N tokens): [token1, token2, ...]列表为空就直说并停止不要进入遍历。四、Step 1 — 遍历执行把选出的 token 用空格连接构成扩展查询串作为下面的QUESTION原始问题只在最后save-result时保留。CLI 可用时直接graphify query QUESTION # 或: graphify query QUESTION --dfs --budget 3000CLI 不可用时加载graphify-out/graph.json内联遍历。流程五步找 1~3 个与扩展 token 最匹配的起始节点 → 从每个起始节点执行对应遍历 → 读子图节点标签、边关系、置信度标签、源码位置→只用图里有的内容回答引用具体事实时引用source_location→ 图里信息不足就明说不要幻觉边。完整内联脚本替换QUESTION、MODEbfs/dfs、BUDGET默认2000$(cat graphify-out/.graphify_python) -c import sys, json from networkx.readwrite import json_graph import networkx as nx from pathlib import Path data json.loads(Path(graphify-out/graph.json).read_text(encodingutf-8)) G json_graph.node_link_graph(data, edgeslinks) question QUESTION mode MODE # bfs or dfs terms [t.lower() for t in question.split() if len(t) 3] # 与 vocab 阈值一致保留 api/jwt/ios#1392 # 找最佳匹配的起始节点 scored [] for nid, ndata in G.nodes(dataTrue): label ndata.get(label, ).lower() score sum(1 for t in terms if t in label) if score 0: scored.append((score, nid)) scored.sort(reverseTrue) start_nodes [nid for _, nid in scored[:3]] if not start_nodes: print(No matching nodes found for query terms:, terms) sys.exit(0) subgraph_nodes set() subgraph_edges [] if mode dfs: # DFS尽可能沿一条路径深走再回溯深度限 6 避免遍历全图 visited set() stack [(n, 0) for n in reversed(start_nodes)] while stack: node, depth stack.pop() if node in visited or depth 6: continue visited.add(node) subgraph_nodes.add(node) for neighbor in G.neighbors(node): if neighbor not in visited: stack.append((neighbor, depth 1)) subgraph_edges.append((node, neighbor)) else: # BFS逐层扩展所有邻居深度 3 frontier set(start_nodes) subgraph_nodes set(start_nodes) for _ in range(3): next_frontier set() for n in frontier: for neighbor in G.neighbors(n): if neighbor not in subgraph_nodes: next_frontier.add(neighbor) subgraph_edges.append((n, neighbor)) subgraph_nodes.update(next_frontier) frontier next_frontier # Token 预算感知输出按相关性排序预算处截断约 4 字符/token token_budget BUDGET # 默认 2000 char_budget token_budget * 4 def relevance(nid): label G.nodes[nid].get(label, ).lower() return sum(1 for t in terms if t in label) ranked_nodes sorted(subgraph_nodes, keyrelevance, reverseTrue) lines [fTraversal: {mode.upper()} | Start: {[G.nodes[n].get(\label\,n) for n in start_nodes]} | {len(subgraph_nodes)} nodes] for nid in ranked_nodes: d G.nodes[nid] lines.append(f NODE {d.get(\label\, nid)} [src{d.get(\source_file\,\\)} loc{d.get(\source_location\,\\)}]) for u, v in subgraph_edges: if u in subgraph_nodes and v in subgraph_nodes: _raw G[u][v]; d next(iter(_raw.values()), {}) if isinstance(G, nx.MultiGraph) else _raw lines.append(f EDGE {G.nodes[u].get(\label\,u)} --{d.get(\relation\,\\)} [{d.get(\confidence\,\\)}]-- {G.nodes[v].get(\label\,v)}) output \n.join(lines) if len(output) char_budget: output output[:char_budget] f\n... (truncated at ~{token_budget} token budget - use --budget N for more) print(output) 几个值得注意的实现细节起始节点选取对每个节点统计扩展 token 命中标签的子串次数取分数最高的前 3 个scored[:3]BFS 深度 3、DFS 深度限 6两者都是刻意限制子图规模防止大图谱下输出爆炸Token 预算截断输出按相关性排序后按token_budget * 4字符截断超预算时追加提示用--budget N扩大MultiGraph 兼容isinstance(G, nx.MultiGraph)分支处理平行边取第一条数据保证脚本在旧/新两种图谱存储格式下都能跑。CLI 路径下的对应行为源码级佐证对照 graphify/cli.py 的 query 分支内联脚本的行为与 CLI 高度一致但有几处值得了解CLI 调用 _query_graph_text定义在 graphify/serve.py传入modebfs/dfs、depth2、token_budgetbudget、context_filters源码注释明确说明query刻意保持图无向而path/explain强制有向——因为 BFS/DFS 必须同时探索种子节点的调用方与 callee 才能构成有用上下文若强转 DiGraphG.neighbors()只会返回后继节点种子无出边时调用方一侧结果会被静默丢弃方向性则靠逐边_src/_tgt标记在渲染时恢复每次 query 会写 querylog 并_touch_query_stamp记录最近查询时间戳供 hook 守卫判断本会话是否查询过图谱。五、把答案存回图谱save-result 反馈闭环回答写完后必须回写图谱让未来查询受益。文档要求把扩展 token 痕迹写进--answer文本例如Expanded from original query via vocab: [tokens]. Then traversed...这样下次--update会把这条扩词历史抽成图谱节点$(cat graphify-out/.graphify_python) -m graphify save-result --question ORIGINAL_QUESTION --answer ANSWER --type query --nodes NODE1 NODE2其中ORIGINAL_QUESTION是用户原话、ANSWER是完整回答含扩词痕迹、NODE1 NODE2是回答中引用的节点标签列表。从 save-result 命令实现 可以看到完整参数面--question必填、--answer或--answer-file二选一必填、--type默认querypath 流程用path_queryexplain 流程用explain、--nodes变参列表、--memory-dir默认graphify-out/memory。最终落到 save_query_result 写入记忆文档。Work Memory三档结果标签追加--outcome参数让未来会话从本次会话中学习--correction the right answer用于记录更正useful— 引用的节点很好地回答了问题它们会成为优先来源dead_end— 该问题/路径走不通下次不要再重复推导corrected— 保存的答案是错的--correction记录正确答案。会话开始时的课程刷新开始图谱工作前刷新并阅读课程执行graphify reflect --if-stale廉价、确定性、无 LLM--if-stale在LESSONS.md已比所有输入都新时直接 no-op例如 git hook 刚刷新过的场景然后读graphify-out/reflections/LESSONS.md。它列出优先来源从这里开始、已知死路跳过、历史更正。reflect 命令实现 补充了文档未展开的参数--half-life-days信号权重每 N 天减半默认 30、--min-corroboration提升为 preferred 所需的不同 useful 结果数默认 2、--out默认graphify-out/reflections/LESSONS.md。底层聚合逻辑在 graphify/reflect.py实现半衰期衰减、来源佐证与 LESSONS.md 渲染自己跑一次reflect能保证课程在没装 git hook 时也是最新的而若 post-commit hook 已安装--if-stale使会话启动时的这次运行几乎零成本。六、/graphify path两概念间的最短路径找到图中两个命名概念之间的最短路径。CLI 优先graphify path NODE_A NODE_BCLI 不可用时内联执行替换NODE_A/NODE_B为用户给出的实际概念名$(cat graphify-out/.graphify_python) -c import json, sys import networkx as nx from networkx.readwrite import json_graph from pathlib import Path data json.loads(Path(graphify-out/graph.json).read_text(encodingutf-8)) G json_graph.node_link_graph(data, edgeslinks) a_term NODE_A b_term NODE_B def find_node(term): term term.lower() scored sorted( [(sum(1 for w in term.split() if w in G.nodes[n].get(label,).lower()), n) for n in G.nodes()], reverseTrue ) return scored[0][1] if scored and scored[0][0] 0 else None src find_node(a_term) tgt find_node(b_term) if not src or not tgt: print(fCould not find nodes matching: {a_term!r} or {b_term!r}) sys.exit(0) try: path nx.shortest_path(G, src, tgt) print(fShortest path ({len(path)-1} hops):) for i, nid in enumerate(path): label G.nodes[nid].get(label, nid) if i len(path) - 1: _raw G[nid][path[i1]]; edge next(iter(_raw.values()), {}) if isinstance(G, nx.MultiGraph) else _raw rel edge.get(relation, ) conf edge.get(confidence, ) print(f {label} --{rel}-- [{conf}]) else: print(f {label}) except nx.NetworkXNoPath: print(fNo path found between {a_term!r} and {b_term!r}) except nx.NodeNotFound as e: print(fNode not found: {e}) 拿到路径后用自然语言解释每一跳意味着什么、为什么重要。然后回写$(cat graphify-out/.graphify_python) -m graphify save-result --question Path from NODE_A to NODE_B --answer ANSWER --type path_query --nodes NODE_A NODE_BCLI 实现的三个关键行为对照 path 命令源码有向为默认源码标注 #2487graphify path完整用法是graphify path source target [--graph path] [--directed|--undirected]。方向真值存在于每份 graph.json新文件的弧线顺序、旧文件的_src/_tgt标记所以默认尊重方向无有向路径时会提示加--undirected重试。这与内联脚本用无向shortest_path的语义不同——CLI 更严格多图层加载#2074加载时强制directedTrue, multigraphTrue使同一对节点间的平行边如一条references和一条calls不会被最后写入者覆盖合并掉输出的是该点对实际存储的关系歧义防护#828两端解析到同一节点时会直接报错退出要求更具体的标签或精确节点 IDtop 分数与次名差距小于 10% 时打印 ambiguous 警告。七、/graphify explain单节点全景解释对单个节点给出通俗解释——它是什么、连向谁、为什么这些连接重要。CLI 优先graphify explain NODE_NAMECLI 不可用时内联执行替换NODE_NAME$(cat graphify-out/.graphify_python) -c import json, sys import networkx as nx from networkx.readwrite import json_graph from pathlib import Path data json.loads(Path(graphify-out/graph.json).read_text(encodingutf-8)) G json_graph.node_link_graph(data, edgeslinks) term NODE_NAME term_lower term.lower() # 找最佳匹配节点 scored sorted( [(sum(1 for w in term_lower.split() if w in G.nodes[n].get(label,).lower()), n) for n in G.nodes()], reverseTrue ) if not scored or scored[0][0] 0: print(fNo node matching {term!r}) sys.exit(0) nid scored[0][1] data_n G.nodes[nid] print(fNODE: {data_n.get(\label\, nid)}) print(f source: {data_n.get(\source_file\,\unknown\)}) print(f type: {data_n.get(\file_type\,\unknown\)}) print(f degree: {G.degree(nid)}) print() print(CONNECTIONS:) for neighbor in G.neighbors(nid): _raw G[nid][neighbor]; edge next(iter(_raw.values()), {}) if isinstance(G, nx.MultiGraph) else _raw nlabel G.nodes[neighbor].get(label, neighbor) rel edge.get(relation, ) conf edge.get(confidence, ) src_file G.nodes[neighbor].get(source_file, ) print(f --{rel}-- {nlabel} [{conf}] ({src_file})) 随后写 3~5 句话的解释节点是什么、连接了什么、为什么重要并用 source 位置作为引用出处。最后回写$(cat graphify-out/.graphify_python) -m graphify save-result --question Explain NODE_NAME --answer ANSWER --type explain --nodes NODE_NAMECLI 版本输出得更多从 explain 命令源码 看CLI 版除了标签、来源、类型、度数外还输出社区community_name与工作记忆叠加层读取 reflect 生成的.graphify_learning.json侧车文件显示Lesson: preferred source (start here)、contested、或tentative状态及得分代码变更过还会标注[code changed since — re-verify]歧义防护同名节点存在于不同文件时列出候选并要求用仓库相对路径或完整节点 ID 重试连接分类按边的真实方向_src标记#2309区分--出与--入每条连接附带关系、置信度和边位置调用/引用发生地而非定义行高连接度节点只展开前 20 条其余按方向 文件分组计数展示#2009避免裸计数把答案藏起来。八、流程速查步骤命令关键点前置检查读取graphify-out/graph.json是否存在不存在先跑/graphify path会话开始graphify reflect --if-stale 读graphify-out/reflections/LESSONS.md优先来源、已知死路、历史更正扩词生成graphify-out/.vocab.txt选 ≤12 个真实 token禁止造词空列表则停止查询graphify query 扩展串 [--dfs] [--budget N] [--context C]默认 BFS、budget 2000路径graphify path A B [--directed\|--undirected]有向为默认歧义时报错解释graphify explain NODE含社区、lesson 叠加、入/出边分类回写graphify save-result --question ... --answer ... --type ... --nodes ... [--outcome useful\|dead_end\|corrected]answer 中保留扩词痕迹九、总结graphify/skills/codex/references/query.md 定义的不只是三条命令而是一套可审计的图谱问答协议查询前用真实词汇表约束扩词防幻觉、遍历时用预算和深度控制输出规模防噪声、回答只引用图中存在的source_location防编造、结束后用save-result--outcome把经验写回自改进。配合 graphify/cli.py 中的 CLI 实现与 graphify/reflect.py 的课程聚合这套流程让同一个知识图谱随使用次数增加而越答越准——这正是 graphify no vector store、每条边都有解释 设计哲学在查询侧的落地。【免费下载链接】graphifyTurn any codebase, with its docs, SQL schemas, configs, and PDFs, into a queryable knowledge graph. A /graphify skill for Claude Code, Cursor, Codex, and Gemini CLI: local deterministic AST parsing, every edge explained, no vector store.项目地址: https://gitcode.com/GitHub_Trending/graph/graphify创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考