首页
/ graphify 增量维护实战:用 `/graphify add` 摄取 URL、用 `--watch` 自动守护知识图谱

graphify 增量维护实战:用 `/graphify add` 摄取 URL、用 `--watch` 自动守护知识图谱

2026-09-07 18:59:40作者:傅爽业Veleda

面向 Claude Code、Cursor、Codex、Gemini CLI 等 Agent 的 graphify 技能集中,add-watch 引用文档(本仓库 tools/skillgen/expected/graphify__skills__pi__references__add-watch.md 及各平台的同名副本)回答了两个高频问题:如何把任意 URL(网页、arXiv、推文、PDF、图片、视频)抓取为语料并并入图,以及如何在文件变动时让图自动保持新鲜。读完本文,你将掌握两条让 graphify 图谱“随取随用、随改随新”的实操链路,并能深入到 ingest.pywatch.py 的源码级行为,在 Agent 工作流中正确地指挥它们。

graphify 的默认构建并不包含“URL 摄取”和“目录监听”这两条增量路径——这正是本引用文档被单独拆分出来的原因:只有当用户运行了 /graphify add <url> 或传入 --watch 时,Agent 才加载它,按其中步骤代表用户执行。下面分别展开两条路径的完整用法与其背后的实现原理。

一、/graphify add:把 URL 变成图谱语料

当用户说“把这篇文章/这条推文/这个视频加进图里”,Agent 应加载本引用并执行一次 URL 摄取。其目标明确:抓取 URL 并落盘到语料目录(corpus),随后走一次更新管线把新文件并入既有图

1.1 底层命令与参数语义

引用文档给出的可执行模板(各平台技能共用)如下,它以 graphify 安装时记录的解释器执行一段内联 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 真实链接 ingest() 自动判定类型(见下文)
AUTHOR 作者名 用户提供则写入;可为空字符串
CONTRIBUTOR 贡献者名 团队协作图里用于溯源,缺省回退到 author

其中的 $(cat graphify-out/.graphify_python) 是技能管线的一个重要约定:首次构建图时,技能把可用的 Python 解释器绝对路径写入 graphify-out/.graphify_python(详见 skill-agents.mdhooks.py),此后所有 bash 片段都用 $(cat graphify-out/.graphify_python) 而非裸 python3 取回它,避免“命令行的 python 里 import 不到 graphify”这类经典错位。./raw 是默认语料目录,若用户之前把图建在别的子目录(如 ./src./docs),应把文件存到与既有语料一致的位置,保证后续更新能扫描到它。

1.2 ingest() 函数签名与落地细节

模板调用的 graphify/ingest.py ingest() 是整条链路的核心,签名与行为为:

def ingest(url: str, target_dir: Path, author: str | None = None,
           contributor: str | None = None) -> Path
  • mkdir(parents=True, exist_ok=True) 确保 target_dir 存在;
  • _detect_url_type()ingest.py)按 URL 特征分类;
  • 调用 graphify/security.pyvalidate_url() 做安全校验(阻断私网 IP、非法 scheme 等),失败抛 ValueError
  • 网络类失败(HTTP 错误、DNS/连接错误、写盘 OSError)统一包装为 RuntimeError,因此模板只捕获这两类异常并转成非零退出码——引用文档特别强调:命令出错时必须如实告诉用户原因,不能静默继续

落盘文件名由 _safe_filename()ingest.py)从 URL 的 netloc + path 清洗生成,最长 80 字符;同名冲突时会自动追加 _1_2 计数器(上限 999)避免覆盖既有语料。所有文本产物都带 YAML frontmatter(source_urltypecaptured_at 等),这正是后续 LLM 语义抽取阶段读取的节点元数据来源。

1.3 URL 类型自动识别表(含源码级产出物)

引用文档列出的类型与对应的 ingest.py 处理分支完全对应:

URL 特征 检测依据 处理方式与落盘产物
YouTube / 视频站 youtube.com / youtu.be transcribe.py download_audio() 用 yt-dlp 下载音频(文件名取自 URL 的 SHA-1 哈希前 12 位,如 yt_<hash>.m4a),下一次运行经转写生成 .txt;依赖 pip install 'graphifyy[video]'
Twitter / X twitter.com / x.com 走 oEmbed(publish.twitter.com/oembed,x.com 归一化为 twitter.com),保存为 .md:frontmatter 带 type: tweet、作者,正文含推文文本;oEmbed 失败则落一个带源 URL 的 stub
arXiv arxiv.org export.arxiv.org/abs/<id> 解析标题/作者/摘要,存为 arxiv_XXXX_XXXXX.md,frontmatter 含 arxiv_idtype: paper
PDF 路径以 .pdf 结尾 以二进制直接下载到语料目录(.pdf 原样保留)
图片 路径以 .png/.jpg/.jpeg/.webp/.gif 结尾 二进制下载原图,交由后续 LLM/视觉模型抽取(文档称“Claude vision extracts on next run”)
普通网页 其余情况 先剔除 <script>/<style> 防正文泄漏,再用 markdownify 转 Markdown(源码中实际是 markdownify,行为与引用文档所述 HTML→Markdown 转换一致),标题从 <title> 提取,正文截断约 12000 字符,落 .md

值得注意的工程细节:文本型产物统一使用 YAML 双引号转义 _yaml_str()ingest.py)写入 frontmatter,把 \t\0、U+2028/U+2029 等 YAML 行分隔符全部转义,防止抓取到的页面标题等“敌对字符串”逃逸出标量注入额外键(源码注释中的 F-009/F-019)。因此本文档命令生成的文件可直接安全进入语料,无需二次清理。

1.4 保存成功后:衔接 --update

引用文档要求:保存成功后自动在 ./raw 上运行一次更新管线,把新文件并入既有图。这里需区分两种“update”:

  • Agent 技能语境里的 /graphify --update:由 Agent 驱动的完整语义重抽取(LLM 参与,参见 update 引用),适用于网页/论文/推文/图片这类需要语义节点的新增语料;
  • CLI 的 graphify updatecli.py):纯 AST 增量重建,内部等价于调用 watch.py_rebuild_code(..., block_on_lock=True),打印“For doc/paper/image changes run /graphify --update”,不调用 LLM。

新增的 .md/.txt/.pdf/.png 等文件本身不携带 AST 语法结构,必须靠语义抽取(含视觉)才能产生高质量节点,所以/graphify add 抓回来的内容,正确衔接是 Agent 层的 /graphify --update。对应地,仓库里提供 CLI 直接可用:python -m graphify.ingest <url> <target_dir>,以及技能常用的一等子命令(cli.py):

graphify add <url> [--author Name] [--contributor Name] [--dir ./raw]

二、--watch:让图随文件变动自动更新

第二条路径解决“图会过时”的问题:在后台起一个守护进程监听目录,文件一变就按需重建。对代码与文档混合、且由多个 Agent 并行写文件的仓库,这正是让图保持可查询状态的关键基础设施。

2.1 启动命令与 debounce

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

INPUT_PATH 换成要监听的目录。参数语义对应 watch.py watch(watch_path, debounce=3.0)

  • debounce(默认 3 秒):只有文件活动停止达到该时长后才触发重建,因此“一波并行 Agent 写文件”不会变成每写一个文件就重建一次——事件在 0.5s 轮询周期内汇聚成一个 batch,静默期过后统一处理;
  • 事件过滤由 watchdog handler 完成(watch.py):忽略目录事件、只读事件(Linux inotify 下的 opened/closed_no_write,避免监听器自我触发死循环)、点号开头的隐藏路径以及 graphify-out/ 自身;.graphifyignore 规则启动时只加载一次;
  • Ctrl+C 停止,打印 [graphify watch] Stopped.

监听集合来自 detect.pyCODE_EXTENSIONS | DOC_EXTENSIONS | PAPER_EXTENSIONS | IMAGE_EXTENSIONSwatch.py)。平台差异在源码中有明确处理:macOS 用轮询型 PollingObserver(FSEvents 可能漏掉编辑器的快速保存),其余平台用原生 Observer

2.2 分诊逻辑:什么该即时重建,什么只打标记

引用文档给出的核心规则,在源码中得到精确验证(watch.py):

  • 仅代码文件(.py/.ts/.go 等)变动_batch_triggers_rebuild() 为真 → 立刻走 _rebuild_code(),即“AST 抽取 + 重建 + 聚类 + 报告”的无 LLM 管线graphify-out/graph.jsonGRAPH_REPORT.md 自动刷新;若拓扑未变,走快速路径打印 “No code-graph changes detected” 并保持产物不动;
  • 文档/论文/图片变动_batch_needs_llm_flag() 为真(文件仍存活于磁盘)→ _notify_only() 写入 graphify-out/needs_update 标志并打印提示,告诉用户运行 /graphify --updateLLM 语义重抽取——因为这类文件没有可增量解析的语法结构;
  • 删除事件(源码细节):无论删的是哪种受监文件都会触发重建,因为节点驱逐同样无需 LLM;纯删除 batch 不会留下过期的 needs_update 标志(tests/test_watch.pytest_batch_doc_only_deletion_triggers_rebuildtest_batch_doc_only_deletion_skips_llm_flag 分别覆盖这两种边界)。

needs_update 标志还有配套的轮询入口:CLI graphify check-update <path>cli.py)调用 watch.py check_update(),供 cron 定时检查“是否有未消费的非代码变更”,且被设计为 cron 安全(恒返回 True)。

2.3 _rebuild_code 的内部链路(增量重建为什么可靠)

把“监听”和“重建”解耦是 watch 模块的设计核心:事件处理器只负责攒 batch,真正的图更新全部落在 _rebuild_code()watch.py),其内部是一条相当严谨的增量流水线:

  1. 重新发现语料:以 detect() 扫描受监目录,但会先读取 graphify-out/.graphify_build.json 里持久化的 --exclude/gitignore 选项——避免 update/watch 重建时悄悄把最初 extract 刻意排除的路径又算进来(_read_build_excludes());
  2. 变更定位与解析上下文:命中变更列表时只重抽取变动的文件,其余文件的 AST 节点/边从既有 graph.json 保留;为保证跨文件调用解析不丢边,把未变更文件的 _callable 标记与 contains/method 边作为只读“解析上下文”喂给 extract()
  3. 与旧图对账_reconcile_existing_graph() 把新抽取结果与既有节点、边、超边合并,驱逐“源文件已从磁盘消失”的旧节点;对“活着但离开扫描语料”的文件采用 fail-closed 策略——只有命中实时 ignore 规则才判删,避免过滤器回归导致的大规模误删;
  4. 收缩护栏_check_shrink()watch.py)比较新旧节点数,当重建失败导致节点骤减时拒绝覆写并提示 --force
  5. 原子写graph.json 先写临时文件再 replace,覆写前 backup_if_protected() 备份,写后清理过期的 needs_update 并重写 .graphify_rootmanifest.json、社区标签侧车 .graphify_labels.json(.sig),需要时再补渲染 graph.html 与调用流 HTML。

此外还有一套进程级并发保护_rebuild_lock()fcntl.flock 做每仓库锁(进程被杀自动释放,无陈旧锁问题),拿不到锁的重建把自己的变更集先追加进 graphify-out/.pending_changes,等锁的进程重建前后都会 drain 该队列并合并,保证并发 post-commit hook 或并行监听者提交的变更不被吞掉。

2.4 与 Agent 工作流的配合(引用文档的落点)

引用文档最后给出的建议正是上述机制的意图所在:

在后台终端跑 --watch。Agent 一波波写代码产生的变更会在波与波之间被自动拾取;若 Agent 同时也在写文档/笔记,则需要在这些波结束后手动跑一次 /graphify --update

展开成可执行清单:起监听(代码改动零人工介入);收到 needs_update/通知提示后,由 Agent 在写作波次收尾时统一执行一次 /graphify --update(LLM 语义层),把文档、论文、图片和视频转写出的语义节点并入图。这样代码图层的“确定性 AST 实时性”与文档图层的“语义深度”各得其所,且互不拖累。

三、两条路径的取舍速查

场景 推荐命令 是否需 LLM 产出
想追一条推文/一篇论文/一个网页/一张图 /graphify add <url> + /graphify --update 语义抽取需要 raw/ 下带 frontmatter 的 .md/.pdf/.png/.txt
只想把 URL 原样收进语料备用 graphify add <url> --dir ./raw 否(仅抓取) 落盘文件 + “Run /graphify --update”提示
代码改动想自动上图 --watch(或 git post-commit hook,见 hooks 引用 graph.json / GRAPH_REPORT.md 自动刷新
Agent 批量写文档后补语义层 收到 needs_update 后手动 /graphify --update 文档语义节点并入图

需要强调的两点边界:其一,/graphify add--watch 都不是默认构建的一部分,因此必须通过本引用文档给出的显式命令驱动,而不是假定它们总在运行;其二,watch 只覆盖受监扩展名集合内的文件,.graphifyignore 中声明的路径不会进入监听/重建范围。以上全部行为均可在 graphify/ingest.pygraphify/watch.pygraphify/cli.pygraphify/transcribe.pytests/test_watch.py 中直接核对,仓库内其余平台(agents/amp/claude/claw/codex/copilot/kilo/kiro/opencode/trae/vscode/windows)的同名 add-watch 引用与 tools/skillgen/expected 下生成的预期文件内容一致,可交叉参照。

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

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 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
531
595
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
918
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.6 K
1.02 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
517
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
389