首页
/ graphify 语料持续进化指南:用 `/graphify add` 摄取外部 URL、用 `--watch` 监控文件夹自动重建知识图谱

graphify 语料持续进化指南:用 `/graphify add` 摄取外部 URL、用 `--watch` 监控文件夹自动重建知识图谱

2026-09-06 19:27:35作者:袁立春Spencer

本指南以 graphify 的 add-watch 技能参考文档为核心,围绕两个「默认构建之外」的进阶能力展开:/graphify add <url> 把网页、论文、PDF、图片、推文甚至视频一键摄入语料并并入已有图谱,以及 --watch 让 graphify 在后台实时监听文件夹、代码一有改动就自动做无 LLM 的 AST 重建。读完本文,你将掌握这两条能力的确切命令、支持的全部 URL 类型与各自产出格式、watch 的触发策略与防抖逻辑,并沿源码链路理解它们在 graphify/ingest.pygraphify/watch.py 中的真实实现。


一、先看懂执行前提:这两条命令在哪个环节工作

/graphify add--watch 都不是默认构建(default build)的一部分。在 graphify/skill.md 的主流程说明里,它们与 --update--cluster-only 一样属于「按需加载」的子命令:默认的 /graphify 是一次性全量构建并出图,而 add 与 watch 服务于已有语料和已有图谱的持续增量演进

运行任何子命令前,skill 要求先确认 graphify-out/.graphify_python 存在——这个文件记录了真正安装了 graphify 的那个 Python 解释器路径,后续所有 bash 命令都通过 $(cat graphify-out/.graphify_python) 调用它,避免误用 python3 而走到未安装 graphify 的虚拟环境(详见 graphify/skill.md 的解释器守卫逻辑)。输出目录默认是 graphify-out,且可用环境变量 GRAPHIFY_OUT 覆盖以支持 worktree 或多输出场景(见 graphify/paths.py 的常量定义)。


二、/graphify add <url>:把外部资料变成图谱节点

/graphify add 的职责是:抓取一个 URL,把它作为「graphify-ready」文件保存进语料目录,随后立即跑 --update 增量管线,把新文件并入已存在的图谱。参考文档给出的核心 Python 调用如下:

$(cat graphify-out/.graphify_python) -c "
import sys
from graphify.ingest import ingest
from pathlib import Path

try:
    out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR')
    print(f'Saved to {out}')
except ValueError as e:
    print(f'error: {e}', file=sys.stderr)
    sys.exit(1)
except RuntimeError as e:
    print(f'error: {e}', file=sys.stderr)
    sys.exit(1)
"

使用时把 URL 换成真实地址,AUTHOR 换成用户提供的姓名,CONTRIBUTOR 同理。要点有两处:

  1. 错误不能静默吞掉ValueError(通常是 URL 校验失败)与 RuntimeError(通常是抓取失败)都要把原因打到 stderr 并向用户说明,不能假装成功。
  2. 成功后必须续接增量管线:保存文件只是第一步,还应自动在 ./raw 上运行 --update,让新文件被重新抽取、合并进既有图结构。这条「抓取 → 落盘 → 增量合并」的链条,与参考文档对 --update 的描述(graphify/skill.md:「re-extract only new/changed files」)完全对应。

2.1 支持什么 URL,源码里如何自动识别

「哪些 URL 该走哪条抓取路径」完全由 graphify/ingest.py 中的 _detect_url_type() 判定,规则按优先级命中即返回:

触发特征 判定类型 说明
twitter.com / x.com tweet 经 oEmbed 抓取推文
arxiv.org arxiv 抓摘要页并存为论文元数据
github.com github 归类预留
youtube.com / youtu.be youtube yt-dlp 下载音频
路径以 .pdf 结尾 pdf 二进制下载
路径以 .png/.jpg/.jpeg/.webp/.gif 结尾 image 二进制下载
其余全部 webpage 转 Markdown

graphify/ingest.pyingest() 入口,先做目录创建与 validate_url() 安全校验(拦截私网 IP、非法 scheme,见 graphify/security.py),再按类型分派。

2.2 各类 URL 的产出与落盘格式

YouTube / 任意视频 URL:走 graphify/transcribe.pydownload_audio(),用 yt-dlp 下载纯音频流。依赖不在默认安装里,需额外安装:pyproject.tomlvideo extra 声明为 faster-whisper + yt-dlp(见 pyproject.toml),即参考文档所指 pip install 'graphify[video]' 的能力。下载得到的是音频文件本身;把它转写成 .txt 发生在下一次重建/语义抽取流程(transcribe 能力在 graphify/transcribe.py 等段落体现:若输入是 URL,则先经 yt-dlp 下载音频再处理)。

Twitter/X:源码 graphify/ingest.py _fetch_tweet()x.com 归一化为 twitter.com,请求 publish.twitter.com/oembed,将推文 HTML 剥离成纯文本,连同作者一起保存为 .md;抓取失败时仍保存一个「无法抓取」的占位文件而非静默放弃。文件带 YAML frontmatter,例如:

---
source_url: "https://x.com/..."
type: tweet
author: "..."
captured_at: 2026-...T...Z
contributor: "..."
---

arXiv:源码 graphify/ingest.py _fetch_arxiv() 先用正则 \d{4}\.\d{4,5} 从 URL 提取论文编号,再请求 export.arxiv.org/abs/<id> 解析标题、作者、摘要,保存为 arxiv_<id下划线版>.md,frontmatter 携带 arxiv_idpaper_authorstype: paper 等元数据,正文含 AuthorsAbstract 小节,供后续抽取直接作为论文语义节点。若 URL 中解析不出 arXiv 编号,则回退为普通网页处理。

PDF:由 _download_binary() 直接写字节流存为 .pdfgraphify/ingest.py),后续构建对 PDF 走 pypdf 抽取(pdf extra 同时带来 markdownify,见 pyproject.toml)。

图片(.png/.jpg/.webp 等):同样二进制下载到 ./raw。图片内容本身 graphify 不自己读——下一次运行时由带视觉能力的模型(Claude vision)做语义抽取,这也是为什么图片改动在 watch 场景里会被归入「需要 LLM 重抽取」的一类(后文详述)。

任意普通网页:抓取 HTML,先预剥离 <script>/<style>,再转成 Markdown 正文。参考文档将其表述为「转换为 markdown」(html2text);从源码 graphify/ingest.py_html_to_markdown() 看,实际优先使用 markdownifypdf extra 附带),未安装时降级为标签剥离并截断到 8000 字符;网页正文最终写入上限 12000 字符(graphify/ingest.py)。落盘格式同样是「YAML frontmatter(source_urltype: webpagetitlecaptured_atcontributor)+ 正文 + 原文链接」。

2.3 两个不易察觉的安全与命名细节

  • YAML 注入防护:抓取到的页面标题、推文作者都来自不可信外部内容,若直接拼进 YAML 可能用换行/制表符/Unicode 行分隔符逃逸出双引号标量、注入兄弟键(对应问题编号 F-009/F-019)。源码专门实现了 _yaml_str()graphify/ingest.py),手工对 \n\r\t\0、U+2028、U+2029 及全部控制字符做转义,而不依赖 PyYAML。
  • 文件名校验 + 防覆盖:URL 经 _safe_filename() 清洗成最长 80 字符的合法文件名;同名文件存在时用 _1.md_2.md 递增后缀,绝不覆盖旧语料(graphify/ingest.py)。

三、--watch:后台监听文件夹并自动重建图谱

--watch 解决的是「语料总在变、图谱不能总是手动重建」的问题。参考文档给出的启动命令:

$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3

INPUT_PATH 换成要监听的目录即可。从源码看,graphify/watch.py 的入口还允许省略路径(默认 .),--debounce 是浮点秒数、默认 3.0。watch 依赖第三方库 watchdog(对应 pyproject.tomlwatch extra,见 pyproject.toml),未安装时入口会明确抛出 ImportError 提示 pip install watchdog

3.1 触发后做什么,取决于「什么变了」

watch() 主循环(graphify/watch.py)把变化分成两类处理,这是整个 watch 设计的分水岭:

  • 只有代码文件变了(.py、.ts、.go 以及 graphify/detect.pyCODE_EXTENSIONS 列出的全部扩展名):立即重跑 AST 抽取 + 重建 + 聚类,全程不需要 LLM。产物 graph.jsonGRAPH_REPORT.md 自动更新——源码中重建成功后还会顺带按需刷新 HTML 可视化与用户先前生成过的 *-callflow.htmlgraphify/watch.py)。
  • 文档、论文或图片变了:watch 不自行做语义抽取,因为这类内容需要 LLM 理解。它只在 graphify-out/needs_update 写一个标志位,并打印提示,要求运行 /graphify --update 做 LLM 语义重抽取(见 graphify/watch.py_notify_only()graphify/watch.pycheck_update(),后者对 cron 场景安全返回 True)。扩展名集合在 graphify/detect.pyDOC_EXTENSIONS(.md/.txt/.rst/.html/.yaml 等)、PAPER_EXTENSIONS(.pdf)、IMAGE_EXTENSIONS(png/jpg/gif/webp/svg)。

值得强调的是,被监听范围是三者的并集 _WATCHED_EXTENSIONSgraphify/watch.py);但一个批次里只要包含现存非代码文件,就仍需写 needs_update 标志;而只要包含代码变更或任何删除事件就立即重建_batch_triggers_rebuild()graphify/watch.py:删除任何受监听文件都触发重建,因为「剔除失效节点」同样不需要 LLM,纯删除批次不该一直卡在标志位后面)。

3.2 防抖(debounce)为什么默认 3 秒

多个 agent 并行写文件时,文件系统事件会像波浪一样涌来。若每个事件都触发一次重建,既浪费又可能在文件尚未写完时抢跑。因此 watch 的 Handler(graphify/watch.py)只负责把变更路径累积进集合并刷新 last_trigger 时间戳;主循环每 0.5 秒检查一次,直到最后一次事件过去满 debounce 秒才真正开工graphify/watch.py)。这样一“波”并行写入被聚合成一次重建,默认 3 秒通常足以让一批 agent 提交落定。

3.3 事件层面的防抖与防自噬细节

从实现还能看到几个容易被忽略的健壮性设计(均集中在 graphify/watch.py):

  • 过滤只读事件:Linux 的 inotify(watchdog ≥ 2.3)会对每次文件 open/close 上报 openedclosed_no_write——包括 watcher 自己重建时读文件触发的那些。若不过滤,watcher 会陷入「发现变化 → 重建 → 重建读文件又触发变化」的无限循环。_is_read_only_event() 只把创建、修改、移动、删除与 write-after-close 视为真变化。
  • 一次加载 ignore 规则.graphifyignore 与持久化的 --exclude 模式在启动时读入一次(graphify/watch.py),事件回调里不重复解析文件,避免高负载卷上烧满 CPU。
  • 排除自身输出graphify-out 目录自身、隐藏文件(dotfile)都不参与触发。

3.4 重建背后的「增量合并」保障

代码变更触发的 _rebuild_code 不是无脑全量重建。它会与既有 graph.json 做 reconcile(_reconcile_existing_graph()graphify/watch.py):只对变更/删除的源文件做新抽取,保留未变文件的既有节点、边与超边;删除判定采用「fail-closed」策略——文件仍存在但因过滤条件离开语料的,默认保留并给出提示,只有磁盘确实缺失或命中实时 ignore 规则才剔除(graphify/watch.py)。写入侧则是先落临时文件、做 shrink 防护检查、再原子替换,并在写 graph.json 前置过期标记,保证中断后可重试(graphify/watch.py)。

若最终图与报告与上次完全一致,重建会打印 No code-graph changes detected 并保持产物不动(graphify/watch.py)——这正是增量语义下「改了没影响图」时的省钱行为。

3.5 停止与 agentic 工作流建议

Ctrl+C 即可停掉监听(源码在 KeyboardInterrupt 分支打印 Stopped. 并优雅关闭 observer,graphify/watch.py)。

参考文档对 agentic 工作流给出明确建议,这也是 watch 最有价值的用法:

  1. 后台终端--watch
  2. agent 一波波提交代码改动,wave 与 wave 之间由 watcher 自动吸收重建,图谱始终新鲜、且不消耗 LLM;
  3. 如果 agent 同时还在写文档或笔记,由于它们需要 LLM 语义重抽取,wave 结束后需手动执行一次 /graphify --update 收尾。

四、运行前提与依赖速查

把全文涉及的环境约束汇总如下(均以当前仓库为准):

能力 前置条件 依据
任意子命令 graphify 已安装,graphify-out/.graphify_python 已生成 graphify/skill.md
URL 视频下载 pip install 'graphify[video]'(yt-dlp、faster-whisper) pyproject.toml
PDF/网页转 Markdown pypdf、markdownify(随 pdf extra) pyproject.toml
--watch watchdog(随 watch extra) pyproject.toml
图片/文档/论文语义层 LLM 能力,经 /graphify --update 触发 graphify/watch.py
自定义输出目录 设置 GRAPHIFY_OUT 环境变量 graphify/paths.py

仓库测试目录中另有 tests/test_watch.pytests/test_incremental.py 等针对 watcher 行为与增量重建机制的用例,可作为深入理解这两条能力行为边界的补充阅读。


五、结语:两条命令拼出「图谱随代码呼吸」的闭环

把 add 与 watch 放在一起看,它们共同完成了语料生命周期的两翼:/graphify add 向外生长——网页、论文、推文、PDF、图片、视频都可以变成语料里带结构化 frontmatter 的文件,随后并入图谱;--watch 向内保鲜——代码改动被无 LLM 地即时吸收,文档类改动则通过 needs_update 标志精准提示何时该花一次 LLM 预算做语义重抽取。理解 graphify/ingest.py 的类型分派与安全落盘、graphify/watch.py 的防抖与增量 reconcile,你就能在 agent 化开发中把「喂语料」和「保图谱」都变成后台自动完成的工程能力。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388