graphify 的 Claw 技能文件:/graphify 九步流水线,把代码库变成可查询知识图谱的完整 Agent 运行手册
graphify/skill-claw.md 是 graphify 项目为 "claw" 类 Agent 宿主编写的 /graphify 技能(Skill)运行手册:它规定 Agent 被调用后必须按什么顺序执行哪些命令、何时派发子 Agent、如何合并 AST 与语义两路抽取结果、怎样保证图谱不被静默损坏。读懂这份文件,你就能掌握 graphify 完整管线的每一步——从 Python 解释器自举、文件探测、并行实体抽取,到社区聚类、图谱健康检查、manifest 增量追踪与查询流程。
一、这份技能文件是什么
技能文件以标准 YAML frontmatter 开头,声明技能名 graphify 及触发条件:"对代码库架构、文件关系或项目内容的任何提问——尤其是存在 graphify-out/ 时,提问应优先被视为 graphify 查询"。其核心承诺是:
- 把任意文件夹(代码、文档、论文、图片、视频)变成一个可导航的知识图谱,带社区检测;
- 诚实的审计轨迹:每条边都标注 EXTRACTED / INFERRED / AMBIGUOUS 三种来源置信度;
- 三种产物:交互式 HTML、可直接用于 GraphRAG 的 JSON、以及自然语言版的
GRAPH_REPORT.md。
在仓库中,该技能文件的参考文档(references)独立存放于 graphify/skills/claw/references/,共 8 个文件:
- extraction-spec.md —— 子 Agent 抽取提示词规范(JSON schema、节点 ID 规则、置信度准则、frontmatter、超边与视觉规则);
- query.md —— BFS/DFS 遍历、
--budget上限、NetworkX 回退、path/explain 流程; - update.md ——
--update与--cluster-only流程; - add-watch.md ——
/graphify add与--watch流程; - exports.md —— wiki/Neo4j/FalkorDB/SVG/GraphML/MCP/benchmark 各导出;
- github-and-merge.md —— clone、跨仓库合并、monorepo 流程;
- transcribe.md —— 音视频转写;
- hooks.md —— post-commit 钩子与 CLAUDE.md 集成。
二、完整命令与标志(Usage)
技能文件定义的调用面如下,全部继承自原文档:
/graphify # 当前目录完整流水线(HTML 可视化;加 --obsidian 生成 vault)
/graphify <path> # 指定路径完整流水线
/graphify https://github.com/<owner>/<repo> # clone 仓库后跑完整流水线
/graphify https://github.com/<owner>/<repo> --branch <branch> # clone 指定分支
/graphify <url1> <url2> ... # 多仓库 clone、各自构建、合并为跨仓库图
/graphify <path> --mode deep # 更彻底抽取,更丰富的 INFERRED 边
/graphify <path> --update # 增量:只重抽新增/变更文件
/graphify <path> --directed # 构建有向图(保留边方向 source→target)
/graphify <path> --whisper-model medium # 使用更大的 Whisper 模型提升转写精度
/graphify <path> --cluster-only # 对已有图重跑聚类
/graphify <path> --no-viz # 跳过可视化,只出报告 + JSON
/graphify <path> --html # (HTML 默认生成,此标志无操作)
/graphify <path> --svg # 额外导出 graph.svg(可嵌入 Notion、GitHub)
/graphify <path> --graphml # 导出 graph.graphml(Gephi、yEd)
/graphify <path> --neo4j # 生成 graphify-out/cypher.txt 供 Neo4j
/graphify <path> --neo4j-push bolt://localhost:7687 # 直推 Neo4j
/graphify <path> --falkordb # 生成 cypher.txt 供 FalkorDB
/graphify <path> --falkordb-push falkordb://localhost:6379 # 直推 FalkorDB
/graphify <path> --mcp # 启动 MCP stdio 服务器供 Agent 访问
/graphify <path> --watch # 监听目录,代码变更自动重建(无需 LLM)
/graphify <path> --wiki # 构建 Agent 可爬取的 wiki(index.md + 每社区一篇)
/graphify <path> --obsidian --obsidian-dir ~/vaults/my-project # vault 写到自定义路径
/graphify add <url> # 抓取 URL 存入 ./raw,更新图
/graphify add <url> --author "Name" # 标注作者
/graphify add <url> --contributor "Name" # 标注加入语料的人
/graphify query "<question>" # BFS 遍历——宽泛上下文
/graphify query "<question>" --dfs # DFS——追踪特定路径
/graphify query "<question>" --budget 1500 # 答案限定在 N token
/graphify path "AuthModule" "Database" # 两概念间最短路径
/graphify explain "SwinTransformer" # 用自然语言解释某个节点
三、调用时的强制规则与快速路径
技能文件用几段硬规则约束 Agent 行为,避免浪费与误操作:
- 帮助短路:若用户调用
/graphify --help(无其他参数),逐字打印 Usage 段后停止——不运行任何命令、不探测文件、不默认路径为.。 - 快速路径(已有图):先检查
graphify-out/graph.json是否存在(相对当前工作目录)。若存在,且用户请求是关于代码库的自然语言问题("X 怎么工作?""谁调用了 Y?""追踪 Z 的数据流")而非显式重建命令(--update、--cluster-only、或暗示全新抽取的路径/URL),则跳过步骤 1–5,直接进入 query 流程,立即运行graphify query "<question>"——不跑 detect、不查语料规模、不要求用户缩小范围。 - 路径默认值:未给路径就用
.,不追问用户。 - URL 识别:路径参数以
https://github.com/或http://github.com/开头时视为 GitHub URL,先执行 Step 0,再用解析出的本地路径继续。
四、流水线逐步拆解
Step 0 — GitHub 仓库与多路径合并(仅 URL 或多路径时)
仅当路径是一个或多个 GitHub URL、或需要合并多个本地子目录时执行;纯本地路径跳过此步。具体 clone、跨仓库合并与 monorepo 流程见 github-and-merge.md。
Step 1 — 确保 graphify 已安装(解释器自举)
这是技能文件中最长的一段 bash 逻辑,核心是三级探测出正确的 Python 解释器,因为不同宿主(uv tool、pipx、venv、系统安装)下 python3 未必能 import graphify:
- 优先
uv tool run --from graphifyy python -c ...取 uv 工具链的内置解释器(现代 Mac/Linux 上最可靠); - 否则读取
which graphify得到的可执行文件首行 shebang,验证其能import graphify后采用; - 兜底
python3。
若 import graphify 失败,则用 uv tool install --upgrade graphifyy 或 pip install graphifyy(含 --break-system-packages 兜底)补装。随后两行关键的状态持久化:
mkdir -p graphify-out
"$PYTHON" -c "import sys; open('graphify-out/.graphify_python', 'w', encoding='utf-8').write(sys.executable)"
# 保存扫描根,让无参的 graphify update 知道下次去哪找
echo "$(cd INPUT_PATH && pwd)" > graphify-out/.graphify_root
技能文件强调:后续所有 bash 块中,一律用 $(cat graphify-out/.graphify_python) 替换 python3。后文还专门设有"子命令解释器守卫"——运行 --update、--cluster-only、query、path、explain、add 前先检查 .graphify_python 存在,缺失(例如用户删了 graphify-out/)则重新走一遍 shebang 探测并写回。
Step 2 — 探测文件(detect)
$(cat graphify-out/.graphify_python) -c "
import json
from graphify.detect import detect
from pathlib import Path
result = detect(Path('INPUT_PATH'))
# 用 Python 写 sidecar 而非 shell 重定向,避免 PowerShell 宿主的控制台编码漂移(#2528)
Path('graphify-out/.graphify_detect.json').write_text(json.dumps(result, ensure_ascii=False), encoding='utf-8')
print(f'Detected {result[\"total_files\"]} files')
"
结果不许 cat 出来,只呈现干净摘要:
Corpus: X files · ~Y words
code: N files (.py .ts .go ...)
docs: N files (.md .txt ...)
papers: N files (.pdf ...)
images: N files
video: N files (.mp4 .mp3 ...)
零文件的类别整行省略。对应实现是 graphify/detect.py#L1665 的 detect(),其文件分类枚举 FileType 与技能文件里的类别一一对应:code / document / paper(.pdf)/ image / video。技能文件随后要求 Agent 按探测结果行动:
total_files为 0:以 "No supported files found in [path]." 终止;skipped_sensitive非空:报告数量并列出被跳过的文件名,让误标敏感的源码/文档可见、可改名或移动(#2106);total_words> 2,000,000 或total_files> 500:给出警告,然后按文件数算出第一级子目录 Top 5 供用户选择缩小范围——规则细节包括:从 detect JSON 读scan_root(始终为 INPUT_PATH 的绝对路径)、合并五类文件列表、剔除scan_root + "/graphify-out/"前缀的转换 sidecar、无子目录的根直属文件记作(root);若全部在(root)则不追问,改建议--no-cluster跳过昂贵聚类后继续;- 否则直接进入 Step 2.5(有视频时)或 Step 3。
文件数上限 500 与源码常量一致:graphify/detect.py#L53 定义 FILE_COUNT_UPPER = 500,同文件还有词数阈值常量 CORPUS_UPPER_THRESHOLD = 500_000(超出即提示 token 成本)——技能文件里的 200 万词预警是 Agent 层的更激进阈值,两者共同构成"语料健康检查"。
Step 2.5 — 视频与音频(仅有视频文件时)
detect 返回零 video 文件则整步跳过。有音视频时,按 transcribe.md 先转写成文本,转写稿在 Step 3 中当作文档处理。--whisper-model 标志在这里起作用。
Step 3 — 实体与关系抽取(双轨并行)
技能文件把抽取拆成两路并明确要求同一消息内并行派发:"Run Part A (AST) and Part B (semantic) in parallel. Dispatch all semantic subagents AND start AST extraction in the same message."——文档注明并行可在大语料上省 5–15 秒。
API key 硬性规则:graphify 不需要任何 API key,Agent 绝不向用户索要、绝不因此阻塞。代码走 AST 结构抽取,纯代码语料(最常见的 /graphify .)完全跳过语义抽取。语义抽取(仅文档/论文/图片)只在 GEMINI_API_KEY/GOOGLE_API_KEY 已设置时用 Gemini;否则宿主 Agent 本身就是 LLM。graphify 不读 ANTHROPIC_API_KEY、OPENAI_API_KEY 或其他任何 provider key——"若你发现自己要提示、等待或停止于缺 key,那是对本技能的误读"。未设置 key 时只打印一次提示(pip install 'graphifyy[gemini]',默认模型 gemini-3-flash-preview,可用 GRAPHIFY_GEMINI_MODEL 或 --model 覆盖)后继续,不等待。若 key 已设置,则改用 graphify.llm.extract_corpus_parallel(files, backend="gemini") 而非派发子 Agent。
Part A — 代码文件的 AST 结构抽取
$(cat graphify-out/.graphify_python) -c "
import sys, json
from graphify.extract import collect_files, extract
from pathlib import Path
code_files = []
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding='utf-8'))
for f in detect.get('files', {}).get('code', []):
code_files.extend(collect_files(Path(f)) if Path(f).is_dir() else [Path(f)])
if code_files:
result = extract(code_files, cache_root=Path('INPUT_PATH'))
Path('graphify-out/.graphify_ast.json').write_text(json.dumps(result, indent=2, ensure_ascii=False), encoding='utf-8')
print(f'AST: {len(result[\"nodes\"])} nodes, {len(result[\"edges\"])} edges')
else:
Path('graphify-out/.graphify_ast.json').write_text(json.dumps({'nodes':[],'edges':[],'input_tokens':0,'output_tokens':0}, ensure_ascii=False), encoding='utf-8')
print('No code files - skipping AST extraction')
"
底层实现是 graphify/extract.py#L5859 的 extract():两遍处理——先逐文件结构抽取(类、函数、import),再做跨文件 import 解析,把文件级 import 转成类级 INFERRED 边(如 DigestAuth --uses--> Response)。它支持 parallel=True 时按未缓存文件数决定是否启用 ProcessPoolExecutor 多核抽取,max_workers 默认为 cpu_count(或 GRAPHIFY_MAX_WORKERS)。文件收集器 collect_files 在 graphify/extract.py#L7289。代码文件支持面可以从 graphify/detect.py#L44 的 CODE_EXTENSIONS 看出——覆盖 Python、TS/JS 全家族、C/C++/CUDA、Go、Rust、Java、Kotlin、C#、Swift、PHP、Lua、Zig、PowerShell、Elixir、Objective-C、OCaml、Julia、Vue/Svelte/Astro、Dart、Verilog、SQL、Fortran、Pascal/Delphi、Terraform、.sln/.csproj、XAML/Razor 等上百种扩展名。
Part B — 语义抽取(并行子 Agent)
快速路径:若探测发现文档、论文、图片均为零(纯代码语料),整体跳过 Part B 直达 Part C;但必须先写空语义文件,否则 Part C 无条件读 .graphify_semantic.json 会抛 FileNotFoundError:
$(cat graphify-out/.graphify_python) -c "
import json
from pathlib import Path
Path('graphify-out/.graphify_semantic.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8')
"
派发纪律(原文用粗体强调):必须使用 Agent 工具;逐个自己读文件被明令禁止(慢 5–10 倍);派发前先打印时间估算:读 detect JSON 的 total_words,估算 Agent 数为 ceil(uncached_non_code_files / 22)(块大小 20–25),每批约 45 秒,总时长约 45s × ceil(agents/并行上限)。
Step B0 — 先查抽取缓存。关键细节是 SPEC_PATH:它必须是本技能文件旁边那份 references/extraction-spec.md 的绝对路径,同一个路径要同时传给 B0 的读缓存和 B3 的写缓存——缓存条目"归属"于产生它的抽取提示词,graphify 升级若改了提示词,旧条目会被重新抽取而非重放,未变的提示词则继续命中(#1939)。
$(cat graphify-out/.graphify_python) -c "
import json
from graphify.cache import check_semantic_cache
from pathlib import Path
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding='utf-8'))
# 只把内容文件送去语义抽取。代码已由 Part A 的 AST 遍覆盖;
# 在这里展平所有类别会让子 Agent 重读每个源文件(#1392)。
# 视频已在 Step 2.5 先转写成文档。
all_files = [f for cat in ('document', 'paper', 'image') for f in detect['files'].get(cat, [])]
cached_nodes, cached_edges, cached_hyperedges, uncached = check_semantic_cache(all_files, root='INPUT_PATH', prompt_file='SPEC_PATH')
# 总是(重)写缓存文件:有命中就写;否则删除上次残留,
# 确保 Part C 不会合并过期的 .graphify_cached.json(#1392)。
if cached_nodes or cached_edges or cached_hyperedges:
Path('graphify-out/.graphify_cached.json').write_text(json.dumps({'nodes': cached_nodes, 'edges': cached_edges, 'hyperedges': cached_hyperedges}, ensure_ascii=False), encoding='utf-8')
else:
Path('graphify-out/.graphify_cached.json').unlink(missing_ok=True)
Path('graphify-out/.graphify_uncached.txt').write_text('\n'.join(uncached), encoding='utf-8')
print(f'Cache: {len(all_files)-len(uncached)} files hit, {len(uncached)} files need extraction')
"
只有 .graphify_uncached.txt 里列出的文件才派发子 Agent;全部命中缓存则直接进 Part C。这套缓存机制在源码中即 graphify/cache.py#L1233 的 check_semantic_cache()——docstring 明确说明:mode 参数选择缓存命名空间(deep 模式读写 cache/semantic-deep/,不会与标准模式互相遮蔽,#1894);prompt_file 参数把命中限定在"由同一提示词产生"的条目上,这正是技能文件 B0/B3 要求传同一 SPEC_PATH 的原因。
Step B1 — 分块:每块 20–25 个文件;每张图片单独一块(视觉需要独立上下文);同目录文件分在同一块,提高跨文件关系抽取概率。
Step B2 — 单条消息内派发所有子 Agent:每个块一次 Agent 调用,全部放在同一条响应里才会并行;串行调用等于白派。且必须 subagent_type="general-purpose"——禁用 Explore,因为它是只读的,无法把块结果写到磁盘,会静默丢弃抽取结果。每块的结果写入 CHUNK_PATH(必须为绝对路径,由 $(pwd)/graphify-out/.graphify_chunk_0N.json 推导——注意是工作目录而非 .graphify_root 的扫描目录,#1392)。子 Agent 的提示词模板取自 extraction-spec.md,仅在至少一块含有文档/论文/图片时才加载;纯代码语料永远读不到它。
Step B3 — 收集、缓存、合并:
- 成功信号是
graphify-out/.graphify_chunk_NN.json落盘且含有效 JSON 的nodes/edges; - 文件缺失通常意味着子 Agent 被派成只读类型——必须打印警告"chunk N missing from disk — subagent may have read-only. Re-run with general-purpose agent.",不许静默跳过;
- 超过一半的块失败/缺失则停止并让用户重跑;
- 每个 Agent 调用完成后,从 Agent 工具结果的
usage字段读出真实 token 数写回块 JSON(块 JSON 自带的是占位零); - 合并所有块为
.graphify_semantic_new.json; - 写缓存时再次传同一个 SPEC_PATH——"用与读取不同的提示词写,条目就会落在下次运行不会看的地方"(#1939)。
save_semantic_cache实现见 graphify/cache.py#L1379; - 最后把缓存命中与新结果合并进
.graphify_semantic.json,节点按id去重,然后清理临时文件(.graphify_cached.json、.graphify_uncached.txt、.graphify_semantic_new.json)。
Part C — AST + 语义合并为最终抽取
# 合并:AST 节点在前,语义节点按 id 去重
seen = {n['id'] for n in ast['nodes']}
merged_nodes = list(ast['nodes'])
for n in sem['nodes']:
if n['id'] not in seen:
merged_nodes.append(n)
seen.add(n['id'])
merged_edges = ast['edges'] + sem['edges']
merged_hyperedges = sem.get('hyperedges', [])
# → 写入 graphify-out/.graphify_extract.json
注意超边(hyperedges)只来自语义路:它表示 3 个及以上节点共享同一概念,是 LLM 才能归纳出的关系。
Step 4 — 建图、聚类、分析、产出
技能文件提醒:代码块中的 directed=IS_DIRECTED 要按是否给了 --directed 替换为 True/False——True 构建保留边方向(source→target)的 DiGraph,False 是默认的无向 Graph。对应实现 graphify/build.py#L798 的 build_from_json(extraction, *, directed=False, root=None) 与之一致:root 参数把语义子 Agent 产出的绝对 source_file 路径相对化,让所有节点共享一致的路径键(#932),这正是技能文件在 Step 4/5 反复传 root='INPUT_PATH' 的原因(与 --update 运行手册对齐,#1361)。
核心步骤块(节选):
G = build_from_json(extraction, root='INPUT_PATH', directed=IS_DIRECTED)
# 在任何写操作之前设守卫:空抽取不许覆盖好的 graph.json / GRAPH_REPORT.md(#1392)
if G.number_of_nodes() == 0:
print('ERROR: Graph is empty - extraction produced no nodes.')
raise SystemExit(1)
communities = cluster(G)
cohesion = score_all(G, communities)
gods = god_nodes(G)
surprises = surprising_connections(G, communities)
labels = {cid: 'Community ' + str(cid) for cid in communities}
questions = suggest_questions(G, communities, labels)
# 先导出,并遵守 #479 收缩守卫:新图比现有 graph.json 小时 to_json 返回 False(不写任何东西)
wrote = to_json(G, communities, 'graphify-out/graph.json')
if not wrote:
print('ERROR: refused to shrink graphify-out/graph.json (existing graph has more nodes; #479).')
print('If this shrink is intentional (you deleted files), re-run a full build with --force.')
raise SystemExit(1)
report = generate(G, communities, cohesion, labels, gods, surprises, detection, tokens, 'INPUT_PATH', suggested_questions=questions)
Path('graphify-out/GRAPH_REPORT.md').write_text(report, encoding='utf-8')
# 另写 .graphify_analysis.json:communities / cohesion / gods / surprises / questions
两处守卫值得注意,都能在源码里找到对应:空图守卫防止全量重建覆盖已有好图;收缩守卫(#479)在 graphify/export.py#L266 的 to_json() 中返回布尔值——新图节点数少于现有 graph.json 时什么都不写,只有图真的写出去了才允许生成 GRAPH_REPORT.md 和分析 sidecar,确保报告永远不描述一个 graph.json 里不存在的图(#1392)。若此步打印 ERROR: Graph is empty,停止并告知用户,不得继续标注或可视化。分析函数分别位于 graphify/analyze.py#L109 的 god_nodes()(Top-10 高度中心性节点)、graphify/analyze.py#L133 的 surprising_connections() 与 graphify/analyze.py#L428 的 suggest_questions();聚类与内聚评分在 graphify/cluster.py#L223 的 cluster() 和 graphify/cluster.py#L357 的 score_all()。
Step 4.5 — 图谱健康检查(只读完整性闸门)
这是标注前的非破坏性诊断,专门暴露增量更新与 AST/LLM ID 不匹配的三种"静默损坏"模式:同端点边塌缩、悬空/缺失端点、自环。只读、永不中止:
from graphify.diagnostics import diagnose_extraction, format_diagnostic_report
summary = diagnose_extraction(extraction, directed=IS_DIRECTED, root='INPUT_PATH')
print(format_diagnostic_report(summary))
flags = [f'{summary[k]} {label}' for k, label in (
('dangling_endpoint_edges', 'dangling-endpoint edges'),
('missing_endpoint_edges', 'missing-endpoint edges'),
('self_loop_edges', 'self-loop edges'),
('directed_same_endpoint_collapsed_edges', 'collapsed (directed) edges'),
('undirected_same_endpoint_collapsed_edges', 'collapsed (undirected) edges'),
) if summary.get(k, 0)]
print('GRAPH HEALTH WARNING: ...' if flags else 'Graph health: OK (no dangling/missing/collapsed edges).')
实现见 graphify/diagnostics.py#L156:它逐条规范化边,区分 missing_endpoint_edges(source/target 为空)、dangling_endpoint_edges(端点不在节点 ID 集合中)、self_loop_edges,并用 directed_pairs / undirected_pairs 两个 Counter 统计同端点重复——正是技能文件要求打印的五个指标来源。若出现 GRAPH HEALTH WARNING,按"Honesty Rules"必须在最终总结中呈现(图中仍有损坏痕迹,但图可用,不中止)。
Step 5 — 社区标注
读 graphify-out/.graphify_analysis.json,为每个社区根据节点标签写 2–5 词的自然语言名(如 "Attention Mechanism"、"Training Pipeline"、"Data Loading")。然后用真实标签重新生成报告(标签会影响建议问题的措辞)、把 labels 存为 .graphify_labels.json 供可视化器使用,并重新导出一次 graph.json 使节点携带 community_name(#2490)——由于与 Step 4 使用同一份抽取数据,#479 收缩守卫在节点数上必然通过;若仍被拒,呈现守卫信息而不是强推。labels 需替换代码块中的 LABELS_DICT 占位符,如 {0: "Attention Mechanism", 1: "Training Pipeline"}。
Step 6 — Obsidian vault(可选)+ HTML
HTML 恒生成(除非 --no-viz);Obsidian vault 仅在显式给出 --obsidian 时生成——它每个节点产一个文件,默认关闭:
graphify export obsidian # 或 graphify export obsidian --dir ~/vaults/my-project
graphify export html # 图 > 5000 节点时自动聚合为社区视图
# 或:graphify export html --no-viz
Step 6b–8:wiki、Neo4j、FalkorDB、SVG、GraphML、MCP、benchmark 导出全部只在对应标志出现时运行(--wiki、--neo4j/--neo4j-push、--falkordb/--falkordb-push、--svg、--graphml、--mcp),或当 total_words 超过 5,000 时跑 token 缩减 benchmark。无导出标志的默认运行跳过全部。各项细节见 exports.md。一个顺序约束:--wiki 导出要在 Step 9 清理之前运行,因为 wiki 还要读 .graphify_labels.json。
Step 9 — 保存 manifest、更新成本追踪器、清理、汇报
这是增量能力(--update)的地基。技能文件给出的 manifest 保存逻辑有三个源码级要点,恰好与 graphify/cli.py#L88 的 _stamped_manifest_files() 文档字符串逐条对应:
- 只盖戳真正产出结果的语义文件:某文档的块失败或遗漏,其 manifest 条目必须保持未盖戳,下次
--update会重新排队;否则它被标记"完成"、内容永远丢失(#2015)。代码文件(AST 是确定性的)恒盖戳; - root 相对化:manifest 键相对扫描根存储,使落盘 manifest 在 clone/机器间可移植,后续
--update匹配缓存文件而不是全量 miss(#1417); - 清除未盖戳文件的旧 semantic_hash:本次派发但未盖戳的文件仍带着上次运行的 stale hash,
clear_semantic清掉它,detect_incremental才能重新排队(#1948)。
技能文件实际执行的是:
from graphify.cli import _stamped_manifest_files
_corpus = detect.get('all_files') or detect['files'] # --update 时 all_files 是完整语料,files 是变更子集
_manifest_files = _stamped_manifest_files(_corpus, extract, Path('INPUT_PATH'))
_sem_types = ('document', 'paper', 'image')
_dispatched = {f for t, fl in detect['files'].items() if t in _sem_types for f in fl}
_stamped = {f for fl in _manifest_files.values() for f in fl}
_cleared = _dispatched - _stamped
_scan = {f for fl in _corpus.values() for f in fl} # 原始完整语料,排除新排除文件而非伪装成删除(#1908)
save_manifest(_manifest_files, root='INPUT_PATH', scan_corpus=_scan, clear_semantic=_cleared or None)
save_manifest 实现在 graphify/detect.py#L2112。随后更新 graphify-out/cost.json 累计成本追踪器(每次运行追加日期/输入输出 token/文件数,并累加总计),最后清理临时 sidecar:
rm -f graphify-out/.graphify_detect.json graphify-out/.graphify_extract.json \
graphify-out/.graphify_ast.json graphify-out/.graphify_semantic.json \
graphify-out/.graphify_analysis.json
find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
rm -f graphify-out/.needs_update 2>/dev/null || true
汇报格式固定:
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
graph.html - interactive graph, open in browser
GRAPH_REPORT.md - audit report
graph.json - raw graph data
obsidian/ - Obsidian vault (only if --obsidian was given)
然后只粘贴 GRAPH_REPORT.md 的 God Nodes、Surprising Connections、Suggested Questions 三段进对话(不贴全文),再立即主动探索:挑报告中最有趣、跨社区边界最多的建议问题,问 "The most interesting question this graph can answer: [question]. Want me to trace it?"。技能文件的收尾宣言点出了它的设计哲学:"The graph is the map. Your job after the pipeline is to be the guide."——每个回答以自然的追问结尾,让会话像导航而非一次性报告。
五、查询流程(query 快速路径的落地)
当 graphify-out/graph.json 已存在且用户提问关于语料的内容时,从图回答而不是重建:
graphify query "<question>"
关键规则:遍历前先用图自身的词汇表扩展问题,防止措辞不匹配把答案坍缩成噪声;若 graphify query CLI 不可用,回退为对 graph.json 的内联 NetworkX 遍历;回答只用图输出中有的内容,引用具体事实时引用 source_location。BFS/DFS 两种遍历模式、--budget token 上限、NetworkX 回退细节、save-result 反馈,以及 /graphify path 与 /graphify explain 流程,全部在 query.md。
/graphify add <url>(抓 URL 入语料)与 --watch(文件变更自动重建、无需 LLM)不属于默认构建,流程见 add-watch.md;--update(只重抽新增/变更文件)与 --cluster-only(对现有图重跑聚类)见 update.md。
六、Honesty Rules(诚实规则)
技能文件末尾用四条硬约束收束整个 Agent 行为,与 Step 4 的空图守卫、Step 4.5 的健康警告、#479 收缩守卫一脉相承:
- 永不虚构边:不确定就用 AMBIGUOUS;
- 永不跳过语料检查警告(大语料必须提示用户);
- 报告中始终显示 token 成本(对应 Step 9 的
cost.json与输出); - 内聚分数不以符号遮掩——展示原始数字;
- 图超过 5,000 节点时未经用户确认不得跑 HTML 可视化(对应 Step 6 中 HTML 导出"超过 5000 节点自动聚合为社区视图"的行为)。
七、小结:技能文件与源码的对应关系
| 技能文件环节 | 源码落点 |
|---|---|
| Step 2 文件探测 | graphify/detect.py#L1665 detect()、L44-L49 扩展名分类 |
| Step 3 Part A AST | graphify/extract.py#L5859 extract()、L7289 collect_files() |
| Step 3 B0/B3 语义缓存 | graphify/cache.py#L1233 check_semantic_cache()、L1379 save_semantic_cache() |
| Step 4 建图 | graphify/build.py#L798 build_from_json() |
| Step 4 分析/报告 | graphify/analyze.py#L109 god_nodes()、graphify/report.py#L96 generate()、graphify/export.py#L266 to_json() |
| Step 4.5 健康检查 | graphify/diagnostics.py#L156 diagnose_extraction() |
| Step 9 manifest 盖戳 | graphify/cli.py#L88 _stamped_manifest_files()、graphify/detect.py#L2112 save_manifest() |
graphify 的技能文件本质上是一份"写给 Agent 的确定性施工图":它把 LLM 的不确定性约束在语义抽取这一环,其余环节——解释器自举、缓存指纹、空图/收缩双守卫、诊断闸门、manifest 盖戳——全部是可从源码复核的确定性逻辑。这正是 graphify 项目 "本地确定性 AST 解析、每条边可解释、无向量库" 设计主张在 Agent 集成层的完整落地。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00