新闻详情

新闻详情

首页 / 资讯中心 / 详情

graphify 查询参考实战:query、path、explain 三种图谱遍历与“工作记忆”反馈闭环

发布时间:2026/9/7 20:15:14来源:尧图网络
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 技能体系的查询参考文档Copilot 版 query.md的完整技术解读覆盖三大图谱查询流程对已有知识图谱提问的query遍历、两概念间最短路径的path查询、单节点邻域解释的explain以及贯穿其中的“约束式查询扩展 答案回写 经验教训沉淀”反馈闭环。读完本文你将掌握如何在已构建的graphify-out/graph.json之上执行确定性遍历、选择 BFS/DFS 模式、用图谱自身词表安全扩展查询词并通过save-result与reflect让 Agent 会话越用越聪明。参考文档的触发时机与整体设计该参考文档的加载条件非常明确当用户针对已有图谱提问或执行/graphify path、/graphify explain命令时加载。技能核心的 query stub 遇到完整遍历需求时会指向本文档。所有流程遵循同一优先级原则优先使用graphify queryCLI如果已安装CLI 不可用时回退为内联 NetworkX 遍历——直接加载graphify-out/graph.json用文档中自带的 Python 脚本完成同样的工作。两种遍历模式按问题类型选择模式标志适用场景BFS默认无“X 和什么相连”——宽泛上下文最近邻优先DFS--dfs“X 如何到达 Y”——追踪特定链条或依赖路径前置检查图谱必须存在一切查询以图谱文件存在为前提用输出目录里记录的 Python 解释器先做检查$(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构建图谱。这里$(cat graphify-out/.graphify_python)的写法是 graphify 的惯例构建时把所用 Python 解释器路径写入该文件保证内联脚本与构建环境一致。Step 0约束式查询扩展遍历前必做这一节是查询流程中最容易被忽视、却直接决定成败的环节。文档给出的动机是graphify 的queryCLI 通过大小写折叠的子串匹配 IDF 权重来命中节点——二进制内部没有词干提取、没有同义词、没有跨语言匹配内联回退脚本也是同样的匹配方式。于是出现了典型的失配场景用户说 “обработчик”俄语“处理器”图谱标签写的是handler用户说 “authentication”图谱写的是Guardian。字面匹配器会返回 0 命中答案随即退化为噪声。解法是在不发明任何词元的前提下先针对图谱的真实词表做查询扩展第 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]它把FooBarService拆成foo、bar、service三个词元长度窗口保留 3~30 字符的词避免超短噪声与超长标签碎片进入词表。词表落盘到graphify-out/.vocab.txt。第 2 步从词表中挑选扩展词元硬性约束读完graphify-out/.vocab.txt后针对用户问题最多选择 12 个语义匹配的词元。文档对此给出了四条不可违反的约束只能挑选词表文件中真实存在的词元禁止发明词元若某个查询概念在词表中找不到合理对应直接跳过该概念——不要用训练记忆里的近义替换词顶替若没有任何词表词元与查询匹配输出空列表并如实告知用户“语料中没有与这个问题相关的词汇”不得伪造搜索跨语言翻译俄语 “аутентификация” → 仅当词表中存在时才选用auth、credential、token、security形态学归一“handlers” 映射到handler、todos 映射到todo前提同样是词表中存在。第 3 步显式输出选择结果保证可审计Query expanded to (from graph vocab, N tokens): [token1, token2, ...]遍历前必须把扩展结果打印给用户若列表为空直说并停止——不要进入遍历。这一步让“LLM 到底用了哪些词去查图”完全可审计是防止幻觉搜索的关键设计。Step 1遍历——CLI 优先内联回退兜底将选中的词元用空格连接构成扩展查询串后续所有遍历使用这个串作为QUESTION原始问题只在结尾save-result时保留。CLI 路径graphify query QUESTION # 或graphify query QUESTION --dfs --budget 3000CLI 的实现在 cli.py 的 query 分支可以确认文档中提及的每个参数都有对应实现--dfs切换遍历模式缺省为 BFS--budget N指定 token 预算默认 2000非法整数会直接报错退出--context支持上下文过滤--graph可指向非默认位置的图谱每次查询还会调用 querylog 记录 kind、问题、语料、耗时并刷新查询时间戳。一个值得注意的实现细节源码注释明确指出query刻意保持无向图不同于强制有向的path/explain因为 BFS/DFS 必须同时探索种子节点的调用方与调用方两侧边方向则通过_src/_tgt标记在渲染层恢复保证遍历不被方向收窄。CLI 内部的核心遍历入口是 _query_graph_text其签名参数与文档语义一一对应def _query_graph_text( G: nx.Graph, question: str, *, mode: str bfs, depth: int 3, token_budget: int 2000, context_filters: list[str] | None None, graph_path: str | None None, ) - str:匹配管线比文档描述的“子串 IDF”更细从源码看可确认三层机制serve.py查询词切分_query_terms剔除英德法西葡意多语停用词表支持中文分词若整句全是停用词则回退为未过滤词元保证 “how does it work” 这类问题仍能落到某些词上加权评分_score_queryserve.py精确命中标签加分 1000、前缀命中加 100、子串命中加 1.0、源文件命中加 0.5每档权重再乘以该词元的IDF 值——error、exception这类高频词的权重被压低FooBarService这类稀有标识符权重被抬高大图上还有三字符trigram候选预筛只在可能命中的节点集合上评分种子选择_pick_seeds按分数差阈值gap ratio 0.2截断防止error、exception这类高频噪声词抢占种子槽位同时保证“每个有命中的查询词元至少占一个种子席位”避免某一词元偶然精确撞名就把其他相关词元的子串命中全部挤出源码中引用了 issue #1445 作为该设计的由来。输出侧_query_graph_text会先打印一行头部遍历模式、深度、种子节点、命中节点数以及当提供graph_path时的图谱来源与节点总数——防止在错误目录查询时“格式正确地答错图”见源码引用的 issue #2789随后按相关性排序、按token_budget × 4字符预算截断——这正是文档中“~4 chars/token”估算的出处。内联回退路径CLI 不可用时加载graphify-out/graph.json并在本地执行与 CLI 等价的遍历。完整脚本来自参考文档$(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 或 dfs terms [t.lower() for t in question.split() if len(t) 3] # 与词表阈值一致保留 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) 使用时替换三处占位符QUESTION扩展后的查询串不是原始问题、MODEbfs或dfs、BUDGETtoken 预算默认2000或由--budget N指定。脚本的遍历语义与文档规定完全一致从标签与词元匹配度最高的1~3 个节点出发BFS 逐层 3 层、DFS 深度上限 6读子图时同时取节点标签、边关系relation、置信度标签confidence与源码位置只用图里有的东西作答引用具体事实时给出source_location图内信息不足时如实说明绝不虚构边。答案回写save-result 闭环写完答案后必须把答案存回图谱让它改善未来查询。注意要在--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是答案中引用的节点标签列表。这闭合了反馈回路下一次--update会把这个 QA 抽成图中的节点。CLI 侧 save-result 的完整参数 与文档一致--question必填、--answer或--answer-file、--type默认query、--nodes任意多个节点标签、--outcome取值useful | dead_end | corrected、--correction、--memory-dir默认graphify-out/memory。工作记忆让每次会话都从上次会话中学习参考文档把--outcome称为“self-improving loop自我改进回路”。在save-result命令上追加--outcome未来会话就能从本次会话中学习useful——被引用的节点很好地回答了问题它们会成为优先来源dead_end——这个问题/路径走不通下次别重新推导corrected——保存的答案是错的--correction the right answer记录正确答案。会话开始时要刷新并阅读教训执行graphify reflect --if-stale廉价、确定性、无 LLM 参与--if-stale在LESSONS.md已比所有输入都新时是空操作——例如 git hook 刚刚刷新过然后阅读graphify-out/reflections/LESSONS.md。其中列出优先来源从这里开始、已知死路跳过它们和历史更正。从源码看这套机制的实现在 reflect.pyreflect读取save-result写入memory/的 QA 文档聚合useful / dead_end / corrected三类结果信号产出单一的教训工件graphify-out/reflections/LESSONS.md来源节点是打分而非计数每次引用贡献一个带符号、时间衰减的分数useful为正dead_end/corrected为负默认半衰期 30 天一个新鲜的死路可以压过几个月前的一次 useful、最小佐证数 2一次 save 不能凭空把一个节点变成可信教训完全确定性无 LLM、稳定排序、给定输入与now时字节级稳定输出有图谱时按社区标签分组无图谱则退化为单一平铺章节教训文件放在reflections/而非 wiki 目录是因为graphify export wiki每次运行会删除wiki/*.md下所有文件写在那里的教训会被下一次导出抹掉模块 docstring 原话。--if-stale的判定逻辑在 lessons_fresh比较LESSONS.md的 mtime 与所有输入memory 目录下的*.md、graph.json 及 sidecar的最新 mtime输出缺失或不可读一律视为“不新鲜必须重建”。这正是文档所说“装了 post-commit hook 时会话开始的 reflect 开销几乎为零”的底层依据。另外graphify explain的 CLI 输出还会叠加一层“经验提示”explain 命令实现 会从 graph.json 旁的.graphify_learning.jsonsidecar 读取节点状态打印形如Lesson: preferred source (start here) — 3 useful, score1.4142或Lesson: contested (useful 1 / dead-end 2)的行代码变化后还会追加[code changed since — re-verify]。也就是说工作记忆不仅影响 Agent 的宏观决策也会直接体现在单节点解释里。/graphify path两概念间的最短路径在图中查找两个命名概念之间的最短路径。已安装 CLI 时优先使用graphify path NODE_A NODE_BCLI 不可用时运行内联版本$(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替换为用户给出的实际概念名然后用通俗语言解释这条路径每一跳意味着什么、为什么重要。值得对照的是 CLI 实际比内联脚本更严谨cli.py path 分支默认有向--undirected可切换方向真值存在每个 graph.json 的边标记里默认尊重它无有向路径时会提示 “Re-run with --undirected to search ignoring edge direction.”歧义防护两端解析到同一节点时直接报错几乎不可能是调用者想要的源码引用 issue #828次选分数与前选差距不足 10% 时打印 ambiguous 警告确定性选路通过构建排序过的物化图保证同一 graph.json 上邻居顺序与选出的路径是规范化的不再因哈希种子不同而每次跑出不同等长路径issue #2074如实报告关系每一跳打印的是该节点对实际存储的关系同一对节点可携带多条平行关系全部展示绝不虚构calls。写完解释后同样回写$(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_B/graphify explain单节点的通俗解释对一个节点给出通俗解释——它是什么、连向哪里。CLI 优先graphify explain NODE_NAMECLI 不可用时的内联版本$(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})) 把NODE_NAME替换为用户询问的概念。然后写一段 3~5 句的解释这个节点是什么、它连向哪里、这些连接为何重要。用源码位置作为引用。完成后回写$(cat graphify-out/.graphify_python) -m graphify save-result --question Explain NODE_NAME --answer ANSWER --type explain --nodes NODE_NAMECLI 版的explaincli.py explain 分支在节点解析上走的是分层匹配而非简单词元计数优先级为源文件精确匹配 → 标签/ID 精确匹配 → 前缀匹配 → 子串匹配_find_node_tiersserve.py。当同一获胜层级横跨多个源文件时比如两个工作区各自定义了MetricsPort命令会列出全部竞争者并要求“用仓库相对路径或完整 node id 重试”而不是任取其一自信作答。连接列表超过 20 条时不会只给一个裸计数而是按“方向 源文件”分组统计让高连接度节点的“谁在调用它、影响面多大”仍然可见源码引用 issue #2009。小结三个命令、一条反馈回路把参考文档的三条流程合起来看graphify 的查询体系是一套完整的闭环查询先用图谱自身词表做约束式扩展最多 12 个词元、零发明再走 BFS/DFS 遍历只用图中事实作答并引用source_location回写每个答案经save-result存回graphify-out/memory/扩展轨迹、引用节点、结果类型useful/dead_end/corrected都成为下次推导的素材沉淀graphify reflect --if-stale以时间衰减打分聚合这些素材产出LESSONS.md优先来源、已知死路、历史更正并在explain的输出中以 Lesson 行直接提示——下一次会话从这里开始。三条流程的 CLI 入口在 cli.pyquery、cli.pypath、cli.pyexplain内联回退脚本则全部内嵌在参考文档 graphify/skills/copilot/references/query.md 中可在无 CLI 环境下原样复制运行对应的行为测试可参考 tests/test_query_cli.py 与 tests/test_explain_cli.py。【免费下载链接】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),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

信号量:互斥、同步、死锁(伪代码解释说明) 2026/9/7 20:51:23

信号量:互斥、同步、死锁(伪代码解释说明)

一、信号量基础机制信号量由 计数器、等待队列、原子P/V操作 组成。P/V 为原子操作&#xff0c;执行过程不可被线程调度打断。P、V 标准底层逻辑// P&#xff1a;申请资源&#xff0c;资源不足则阻塞 P(S){S--;if(S < 0)线程阻塞&#xff0c;进入等待队列; }// V&#xff1a…

阅读更多 →
从零手写简化版unique_ptr:彻底搞懂RAII与移动语义 2026/9/7 20:51:23

从零手写简化版unique_ptr:彻底搞懂RAII与移动语义

1. 项目概述&#xff1a;为什么要造一个“简化版unique_ptr”的轮子C 里最有排面的智能指针&#xff0c;非unique_ptr莫属。C11 引入它之后&#xff0c;无数资深开发者把它写进代码规范&#xff0c;甚至很多现代 C 项目里直接规定裸指针只能当“观察者”&#xff0c;真正拥有资…

阅读更多 →
燃气工控安全防护实战:SCADA操作员认证与身份统一管理落地 2026/9/7 20:51:23

燃气工控安全防护实战:SCADA操作员认证与身份统一管理落地

一次燃气事故后的追问,往往是从"查不到人"开始的:那座门站最后是谁操作的、谁下的远程指令、当时谁在值班——翻系统,只有一堆共用账号;查记录,只有"scada-admin";问值班室,谁也不承认。 2021 年湖北十堰"613"燃气爆炸重大事故之后,城市燃气的安…

阅读更多 →
Java跨平台原理:JVM如何实现一次编译到处运行? 2026/9/7 20:51:23

Java跨平台原理:JVM如何实现一次编译到处运行?

Java是怎么实现跨平台的&#xff1f;是JVM“骗”了操作系统我这些年面试别人Java岗位&#xff0c;十个里至少有八个会把“跨平台”挂在嘴边&#xff0c;但当我追问一句“那你给我讲讲JVM是怎么做到跨平台的&#xff0c;它和C语言的跨平台到底差在哪”的时候&#xff0c;能说清楚…

阅读更多 →
如何在CANoe中关闭VN8900的RT模式 2026/9/7 20:51:23

如何在CANoe中关闭VN8900的RT模式

&#x1f345; 我是蚂蚁小兵&#xff0c;专注于车载诊断领域&#xff0c;尤其擅长于对CANoe工具的使用&#x1f345; 寻找组织 &#xff0c;答疑解惑&#xff0c;摸鱼聊天&#xff0c;博客源码&#xff0c;点击加入&#x1f449;【相亲相爱一家人】&#x1f345; 玩转CANoe&…

阅读更多 →
纯CSS实现创意单选按钮:无JS也能做出高质感交互 2026/9/7 20:48:22

纯CSS实现创意单选按钮:无JS也能做出高质感交互

1. 创意雷达&#xff1a;先搞明白“为什么默认的单选按钮这么丑”做前端的人&#xff0c;尤其是做偏展示型页面的&#xff0c;一定都经历过这个痛点&#xff1a;<input type"radio">这个原生控件&#xff0c;在不同浏览器里长得完全不一样。Chrome 里是蓝色圆点…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞