ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

graphify 查询参考深度解析:query / path / explain 的受控查询扩展、遍历流程与自改进反馈闭环

graphify 查询参考深度解析:query / path / explain 的受控查询扩展、遍历流程与自改进反馈闭环 graphify 查询参考深度解析query / path / explain 的受控查询扩展、遍历流程与自改进反馈闭环【免费下载链接】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本文基于 graphify 仓库中 Droid Agent 的技能参考文档 query.md完整讲解对已构建知识图谱提问的三个核心流程query邻域遍历、path最短路径、explain单节点解释以及受控查询扩展constrained query expansion这一防止零命中退化的前置步骤、save-result反馈回路与reflect工作记忆机制。读完后你可以掌握如何在不发明词元的前提下把自然语言问题映射到图词汇表、如何用 CLI 或内联 NetworkX 脚本执行 BFS/DFS 遍历、如何把答案写回图谱形成自改进闭环。1. 文档定位与触发场景query.md是 graphify 面向 Droid Agent 的/graphify技能参考之一同目录还有 update.md、exports.md、hooks.md 等其他 Agent 平台如 Claude、Codex、Copilot 拥有各自独立副本。文档开篇明确了加载条件与双轨执行策略加载时机用户对已有图谱提问或运行/graphify path、/graphify explain时核心 query stub 指向这里获取完整遍历流程。双轨执行优先使用graphify queryCLICLI 不可用时回退到内联 NetworkX 遍历脚本。两条路径的匹配语义保持一致大小写折叠的子串 IDF 式打分保证结果可预期。关键前提是图谱必须已存在。文档给出的检查方式注意Python 解释器路径来自构建时写入的标记文件graphify-out/.graphify_python这是 graphify 保证环境一致性的做法$(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构建图谱而不是凭空作答。2. 两种遍历模式的选择文档用一个决策表区分两种遍历模式模式标志适用问题BFS默认(无)X 都连接着什么 —— 宽泛上下文近邻优先DFS--dfsX 如何到达 Y —— 追踪特定链条或依赖路径这个选择在源码中同样成立graphify query的 CLI 入口解析--dfs标志后以modedfs或modebfs调用统一遍历入口见 cli.py 中cmd query分支。3. Step 0 —— 受控查询扩展遍历前必做这是整份参考中最具方法论价值的部分。文档指出 graphify 的queryCLI 通过大小写折叠子串 IDF匹配节点二进制内部没有词干提取、没有同义词、没有跨语言匹配内联回退脚本也是同样的匹配方式。因此当用户的提问与图谱标签在语言或领域词汇上不一致时用户说 обработчик图里是 handler用户说 authentication图里是 Guardian字面匹配器会返回 0 命中答案退化为噪声。修复思路是不发明任何词元先把查询扩展映射到图的真实词汇表上。3.1 从节点标签抽取词汇表$(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])|[A-Z]?[a-z]|[A-Z]同时处理了驼峰命名parseConfig→parse、config和全大写缩写API长度阈值3 len(t) 30过滤掉噪声碎片。3.2 词汇表内的硬约束选择读取graphify-out/.vocab.txt后针对用户问题从这份精确列表中挑选至多 12 个与查询意图语义匹配的词元文档给出了四条硬约束只能选词汇表中存在的词元绝不发明词元若某个查询概念在词汇表中没有合理对应跳过它不要用训练记忆里的近义替换词若没有任何词汇表词元匹配查询输出空列表并明确告知用户语料中没有相关词汇不得伪造搜索跨语言翻译只在词元存在时生效俄语 аутентификация → 仅当词汇表存在时选auth、credential、token、security形态变化同理handlers → 仅当存在时映射handler。3.3 显式打印扩展结果以便审计Query expanded to (from graph vocab, N tokens): [token1, token2, ...]列表为空时要直说并停止不进入遍历。这一设计让 LLM 的改写变得可审计——扩展完全受限于语料自身词汇与文档不得虚构事实的总基调一致。4. Step 1 —— 执行遍历把选中的词元用空格连接组成扩展查询串以它作为下面的QUESTION原始问题仅保留给最后save-result使用。4.1 优先走 CLIgraphify query QUESTION # or: graphify query QUESTION --dfs --budget 3000从源码看graphify query完整支持--dfs、--budget N默认 2000、--context C上下文过滤、--graph path指定图谱文件四个选项cli.py。几个值得注意的实现细节query 刻意保持无向图path/explain会强制directedTrue而query不强制——因为 BFS/DFS 需要同时探索种子节点的调用方和被调用方若转成 DiGraphG.neighbors()只会返回后继种子若无出边则静默丢失所有调用方侧结果方向性改由每条边上的_src/_tgt标记在渲染时保留cli.py 有专门注释。统一遍历入口CLI 最终调用 serve.py 中的_query_graph_text。该函数用单次打分遍历同时产出综合排名与逐词元的种子候选注释说明此前 T1 次全图遍历在 10 万节点基准上浪费约 71% 打分时间关系意图动词calls、uses 等会从逐词元种子保证中剔除防止动词偶然命中抢占 BFS 根[#2507]输出头部会显式打印图谱路径与节点数避免在父项目目录里误查 vendored 子项目图谱导致格式正确、内容错误的静默错答[#2789]。留痕与钩子协同每次 query 会经querylog.log_query记录到查询日志并调用_touch_query_stamp更新graphify-out/cache/last_query_stamp时间戳——hook guard 依赖该时间戳判断本会话最近是否查过图谱从而决定放行还是拦截直接 grep 源码cli.py、graphify/security 相关守卫逻辑。4.2 CLI 不可用时的内联 NetworkX 回退文档要求依次完成找标签与扩展词元最匹配的 1–3 个节点 → 从各起点执行对应遍历 → 阅读子图节点标签、边关系、置信度标记、源码位置→仅用图内信息作答引用具体事实时引用source_location→ 图内信息不足就明说不得幻觉边。完整内联脚本如下将QUESTION替换为扩展查询串MODE替换为bfs/dfsBUDGET为 token 预算默认 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] # match the vocab threshold; keeps api/jwt/ios (#1392) # Find best-matching start nodes 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: follow one path as deep as possible before backtracking. # Depth-limited to 6 to avoid traversing the whole graph. 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: explore all neighbors layer by layer up to depth 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-budget aware output: rank by relevance, cut at budget (~4 chars/token) token_budget BUDGET # default 2000 char_budget token_budget * 4 # Score each node by term overlap for ranked output 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) 脚本要点值得逐条理解词元长度阈值 3与 Step 0 词汇表构建保持同一阈值注释注明这样保留了api/jwt/ios这类 3 字符缩写[#1392]种子选择按词元重叠计数排序取前 3 个节点0 命中时打印明确信息后退出遍历边界DFS 深度上限 6、BFS 展开 3 层防止大图上全图遍历Token 预算输出按~4 chars/token换算字符预算按相关度排序后截断并提示可用--budget N放宽MultiGraph 兼容读取边属性时用isinstance(G, nx.MultiGraph)分支对应 graphify 的 multigraph 兼容层graphify/multigraph_compat.py。5. 反馈闭环save-result 与工作记忆5.1 把答案写回图谱写完答案后文档要求将其保存回图谱以改进后续查询并在--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是所引用的节点标签。对照源码graphify save-result的完整参数集比文档示例更丰富cli.py参数说明--question必填原始问题--answer/--answer-file答案文本或从文件读取--type查询类型默认query本文档还会用到path_query、explain--nodes引用的节点标签列表--outcomeuseful/dead_end/corrected三选一--correction纠正文本配合corrected--memory-dir记忆目录默认graphify-out/memory底层实现在 graphify/ingest.py 的save_query_result。5.2 工作记忆三值结果与 reflect文档要求给save-result追加--outcome让未来会话从本次学习useful—— 被引用节点很好地回答了问题它们成为首选来源dead_end—— 该问题/路径走不通下次不必重新推导corrected—— 已存答案有误--correction the right answer记录正确答案。在每次图谱工作开始时刷新并阅读经验运行graphify reflect --if-stale廉价、确定性、无 LLM当LESSONS.md已新于所有输入文件时--if-stale使其成为空操作例如 git hook 刚刚刷新过然后读取graphify-out/reflections/LESSONS.md。该文件列出首选来源从那里开始、已知死路跳过与历史纠正。自己运行reflect可在未安装 git hook 的情况下保持经验最新若 post-commit hook 已安装--if-stale使会话启动时的这次运行几乎零成本。从源码看graphify reflect还支持若干可调参数cli.py--half-life-days信号权重半衰期默认 30 天、--min-corroboration将一个节点提升为 preferred 所需的独立 useful 结果数默认 2、--out默认graphify-out/reflections/LESSONS.md、--if-stale。核心逻辑在 graphify/reflect.py测试见 tests/test_reflect.py。这条链路save-result --outcome→ 记忆目录 →reflect→ LESSONS.md → 下次会话读取构成了 graphify 所称的自改进循环。6. /graphify path两概念间的最短路径在图谱中查找两个命名概念间的最短路径。CLI 可用时graphify path NODE_A NODE_B源码中该命令还支持--graph指定图谱、--directed/--undirected二选一互斥报错且默认按有向图计算——因为graph.json中保存了方向真值cli.py。CLI 不可用时的内联脚本$(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}) 将NODE_A/NODE_B替换为用户给出的实际概念名。找到路径后文档要求用自然语言解释这条路径——每一跳意味着什么、为何重要。解释完成后同样写回$(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_B7. /graphify explain单节点全景解释对单个节点给出自然语言解释——以及与之相连的一切。CLI 可用时graphify explain NODE_NAMECLI 实现比内联脚本多一层歧义防护当同名节点分布在多个文件时explain会打印所有竞争节点source_file 节点 id并以退出码 1 要求改用仓库相对路径或完整节点 id 重试cli.py。它还会输出community社区归属并在节点存在.graphify_learning.json伴随文件中的经验条目时叠加一行由reflect派生的经验提示display-only不改变图数据。CLI 不可用时的内联脚本$(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() # Find best matching node 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})) 将NODE_NAME替换为用户询问的概念。随后写 3–5 句解释这个节点是什么、连接了谁、这些连接为何重要并以源码位置作为引用。最后同样写回$(cat graphify-out/.graphify_python) -m graphify save-result --question Explain NODE_NAME --answer ANSWER --type explain --nodes NODE_NAME8. 测试与工程佐证三条流程在仓库中均有专门的测试覆盖可作为行为契约参考tests/test_query_cli.py —— query 的 CLI 行为tests/test_path_cli.py —— path 的最短路径与有向/无向选项tests/test_explain_cli.py —— explain 的节点解析与歧义处理tests/test_query_induced_edges.py、tests/test_query_names_its_graph.py —— 遍历输出对回答来自哪个图谱的标注行为tests/test_querylog.py —— 查询日志落盘。9. 小结这套流程的设计哲学把 query.md 的三个流程放在一起看graphify 对LLM 提问知识图谱这一场景做了三层防御输入侧受控查询扩展把自然语言到图谱词汇的映射限制在语料自身词表内零命中时明说而非伪造扩展结果显式打印可审计执行侧CLI 优先、内联 NetworkX 回退双轨执行BFS/DFS 明确分工且都有深度/预算上限输出按相关度排序并带 token 预算截断输出侧答案只能来自图内容、引用必须带source_location且每次作答都经save-result含三值 outcome写回再经reflect蒸馏为 LESSONS.md 供下次会话读取——查询行为本身成为图谱的一等数据。对于要在自己的项目中复用这套流程的开发者建议按先读 extract 理解节点/边结构 → 按本文流程执行查询 → 结合 update.md 维护增量的顺序阅读 Droid 技能参考目录即可获得 graphify 查询能力的完整操作面。【免费下载链接】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),仅供参考
返回列表