graphify 语义提取子代理规范(extraction-spec)深度解析:如何把文档、论文与图片确定性地结构化为知识图谱 JSON
【免费下载链接】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 在传统代码库之外,还要把 README、架构文档、论文、PDF 切片乃至截图一并纳入可查询的知识图谱。本文围绕仓库中随 Skill 分发的 语义提取子代理规范(同时在 tools/skillgen/expected/ 下 有 skillgen 生成快照),逐条拆解其中的提取规则、JSON Schema、节点 ID 生成约束与置信度标尺,并结合源码解释这些约束为何存在——读完你将掌握:语义提取通道的触发条件、如何写出不被校验器拒绝的提取 JSON、如何保证节点 ID 与 AST 抽取器完全一致从而避免"幽灵重复节点",以及每条边的来源字段为什么要逐字符复制 FILE_LIST 路径。
规范文件的定位:只在 Part B 且有语义内容时被加载
该 reference 文件的开头明确定义了它的使用边界:
Load this in Step 3 Part B when the corpus has at least one doc, paper, or image chunk. A pure-code corpus skips Part B and never reads this file.
也就是说,graphify 的构建流程分为两大通道:
- Part A(AST 通道):代码文件由本地确定性 AST 解析器处理,无需 LLM、无需 API Key;
- Part B(语义通道):只有文档(document)、论文(paper)、图片(image)被送入语义子代理做知识图谱抽取,本规范文件就是这套子代理的提示词正文。
纯代码语料(最常见的/graphify .)直接跳过 Part B,也因此从不加载本文件。主编排脚本 graphify/skill-kilo.md 中同样强调"只在至少一个 chunk 包含 doc/paper/image 时、且仅在此时读取它"。
实际调度流程中的调用点
参考主 Skill 文档 Part B 的完整编排 可以看到本规范被使用的具体方式:
- Step B0:先通过
graphify.cache.check_semantic_cache(all_files, root=..., prompt_file='SPEC_PATH')查询缓存。注意prompt_file参数传入的正是本规范文件的绝对路径——缓存条目按提示词归属,graphify 升级导致提示词变化后旧条目会被重新抽取而非原样回放(对应 issue #1939); - Step B1:把未命中缓存的文件按 20–25 个一组切 chunk,每张图片独占一个 chunk(视觉需要独立上下文),同目录文件尽量分到同一 chunk 以提高跨文件关系命中率;
- Step B2:在同一条消息中并行派发所有 Agent 子代理,每个子代理收到本规范文件的提示词(替换
FILE_LIST、CHUNK_NUM、TOTAL_CHUNKS、DEEP_MODE、CHUNK_PATH五个占位符),并把结果写到绝对路径graphify-out/.graphify_chunk_NN.json; - Step B3:等待全部子代理落盘,把真实 token 数回填后合并且写缓存。
因此,本文件实质上是语义抽取通道的"接口契约":子代理是执行者,规范是唯一权威 prompt。下面的规则全部来自规范正文,并与仓库源码相互印证。
证据分级三原色:EXTRACTED / INFERRED / AMBIGUOUS
规范在文件开头定义了三种证据等级,用于在 JSON 的confidence字段标注每条边"为什么可信":
| confidence | 含义 | 判据 |
|---|---|---|
EXTRACTED | 源文本中显式存在的关系 | import、call、引用、"see §3.2"这类显式指涉 |
INFERRED | 合理推断出的关系 | 共享数据结构、隐含依赖、功能对齐 |
AMBIGUOUS | 不确定的关系 | 标记出来供人工复核,绝不静默省略 |
这条分级的工程价值在于"宁可标记,不可吞没":对 LLM 而言,把可疑的边降级为AMBIGUOUS比把它扔掉更安全,因为图谱查询阶段的缺失不可恢复,而AMBIGUOUS边可以被诊断与人工审查过滤。源码侧 graphify/validate.py 将其固化为一组白名单常量VALID_CONFIDENCES = {"EXTRACTED", "INFERRED", "AMBIGUOUS"},任何其它取值都会在校验阶段被拒绝。
按文件类型分类的提取细则
代码文件:只补 AST 看不见的语义
规范明确要求:代码文件的语义抽取聚焦 AST 无法发现的边(调用关系、共享数据、架构模式),不要重复抽取 import——AST 已经覆盖了它们。这与"纯代码语料完全不需要 LLM"的整体设计自洽:代码结构交给确定性解析器,LLM 只负责"读代码之间的人味关系"。
文档与论文:把 rationale 存成属性而不是节点
这是规范里最容易犯错的规则之一,值得单独强调:
- 对文档/论文提取命名的概念、实体、引用;
- 对于决策理由(WHY)——为什么这么做、取舍、设计意图——把它作为
rationale属性挂在相关的概念节点上,绝不单独创建 "rationale 节点"或"片段节点"; - 只有"本身就是一个命名实体或概念"的对象才配创建节点;
- 概念类节点(想法、原则、机制、设计模式)使用
file_type: "rationale"。
file_type必须且只能是以下六个值之一:code、document、paper、image、rationale、concept。任何其它值都是非法取值并会被拒绝。源码 graphify/validate.py 用VALID_FILE_TYPES = {"code", "document", "paper", "image", "rationale", "concept"}一一对应地校验。
图片:用视觉理解"图是什么",而不是只做 OCR
规范为六类图片分别定义了提取目标,核心原则是"不要只 OCR,要看懂这张图":
- UI 截图→ 布局模式、设计决策、关键元素与用途;
- 图表(chart)→ 指标、趋势/洞见、数据来源;
- 推文/帖子→ 以"观点"为节点,附带作者与涉及的概念;
- 示意图(diagram)→ 组件及其连接关系;
- 研究图(research figure)→ 它演示了什么、方法、结论;
- 手写笔记/白板→ 想法与箭头连线,不确定的解读一律标
AMBIGUOUS。
跨语言边与方向约束
关于calls边,规范给出两条硬性规则:
- 方向不可逆:
source必须是调用方(发起调用的函数/类),target必须是被调用方; - 语言边界不可逾越:
calls边必须保持在同一种语言内——Python 函数不能callsJS/TS/Go/Rust/Java 符号,反之亦然。跨语言调用边被视为幻影伪影(phantom artifacts),严禁产出。
源码中 graphify/analyze.py 等在分析阶段对跨语言幽灵调用有对应的防护逻辑,可见这条约束贯穿从抽取到装配的全链路。implements/references/cites等边的语义在 graphify/llm.py 的内置抽取系统提示词中被同样明确:source 永远是动作发出方,target 是被作用方。
confidence_score:禁用 0.5 默认值的离散置信度标尺
规范用一整段强调:每条边都必须携带confidence_score,绝不能省略,也绝不能把 0.5 当默认值。其背后的理由非常工程化:
Models follow discrete rubrics better than continuous ranges; the bimodal distribution observed in production (>50% at 0.5, >40% at 0.85+) shows the range guidance is being collapsed to a binary.
即生产数据观察到明显的双峰分布(超过 50% 落在 0.5、超过 40% 落在 0.85+),说明给连续区间反而诱导模型退化成二值选择。因此对INFERRED边只能从下面这组离散值中恰好选一个:
| confidence_score | 语义 | 判据示例 |
|---|---|---|
0.95 | 直接结构证据 | 共享数据结构、具名的跨文件引用 |
0.85 | 强推断 | 明确的功能对齐,但没有直接符号连接 |
0.75 | 合理推断 | 共享问题域 + 形态相似,需要解读 |
0.65 | 弱推断 | 主题相关,无形态证据 |
0.55 | 猜测但合理 | 仅表面共现 |
配套规则:
EXTRACTED边的confidence_score恒为 1.0;AMBIGUOUS边落在0.1–0.3区间;- 若上述没有一个值适用,宁可把边标为
AMBIGUOUS,也不要选 0.4 或更低; - 语义相似边(见下文)的置信度区间为0.6–0.95。
Node ID:确定性格式与"幽灵重复节点"的根源
规范中篇幅最长、最容易踩坑的规则是节点 ID 的生成,因为ID 必须与 AST 抽取器生成的 ID 完全一致,否则会产生没有任何边能连接到的"孤儿幽灵节点(orphan ghost-duplicate nodes)"。
规则精要:
- 只允许小写
[a-z0-9_],不允许点号与斜杠; - 格式为
{stem}_{entity}:stem=完整仓库相对路径去掉扩展名,每个目录段都保留、转成小写并用_连接,非字母数字字符替换为_;entity= 符号名,同样归一化;- 必须使用每一级目录,不能只取文件名或仅用最近一级父目录——这样才能区分不同目录下的同名文件。
规范给出的官方示例:
| 源 | 符号 | 结果 ID |
|---|---|---|
src/auth/session.py | ValidateToken | src_auth_session_validatetoken |
lib/utils/helpers.py | parse_url | lib_utils_helpers_parse_url |
tests/test_foo.py | _helper | tests_test_foo_helper |
docs/v1/api/README.md | getUser | docs_v1_api_readme_getuser |
setup.py(顶层文件) | my_func | setup_my_func |
错误示范:只写session_validatetoken(丢目录)或auth_session_validatetoken(丢深层目录),都会与 AST 生成的真实 ID 失配。如果项目是旧的"仅最近父目录"格式构建的,用户应运行graphify extract --force干净重建。
最关键的一条铁律:
CRITICAL: never append chunk numbers, sequence numbers, or any suffix to an ID (no
_c1,_c2,_chunk2, etc.). IDs must be deterministic from the label alone — the same entity must always produce the same ID regardless of which chunk processes it.
ID 必须仅由实体本身决定——同一实体无论被哪个 chunk 处理都必须得到相同 ID。这也是并行分片抽取能够合并不冲突的前提。该格式在 graphify/llm.py 的引擎内置系统提示词中与规范一字不差地保持一致。
semantically_similar_to:只在"真正跨界的非显然相似"时才添加
规范允许在没有任何结构连接(无 import、无 call、无引用)的情况下,为"解决同一问题 / 表达同一思想"的两个概念添加semantically_similar_to边,标记为INFERRED,confidence_score按相似度取 0.6–0.95。官方示例包括:
- 两个都校验用户输入、但从不互相调用的函数;
- 代码里的一个类与论文中描述同一算法的概念;
- 两个以不同方式处理同一失败模式的错误类型。
同时给出限制:只有相似性真正非显然且跨边界时才加,琐碎相似不要加。这也与 graphify/analyze.py 的注释一致——分析时会将semantically_similar_to视为"真正的跨边界洞见"而做特殊处理;报告输出在 graphify/report.py 中也会对其标注[semantically similar]。
Hyperedges:3 个以上节点才值得用,每 chunk 上限 3 条
当 3 个及以上节点明确共同参与一个共享概念、流程或模式,而这种群体关系无法仅靠两两边表达时,写入顶层hyperedges数组。官方示例:
- 实现同一协议/接口的全部类;
- 认证流程中的所有函数(即使它们并不互相调用);
- 论文某一节中构成统一思想的全部概念。
relation只能取participate_in、implement、form三者之一,且每个 chunk 最多 3 条,务必克制。这条规则防止图谱被成堆的二元边淹没,同时让"群体语义"(协议实现集、流程参与者)有第一等的表达位置。
YAML frontmatter 元数据透传
对于带 YAML frontmatter(--- ... ---)的文件,规范要求把source_url、captured_at、author、contributor四个字段复制到该文件的每一个节点上。这让来自网页归档、博客、PDF 的文章在知识图谱中保留出处与溯源信息,便于回答"这段知识来自哪里、什么时候被抓取"。
JSON Schema 逐字段精读
规范要求子代理输出恰好匹配下面结构的纯 JSON——无解释、无 markdown 代码围栏、无开场白(源码侧 graphify/llm.py 的内置 prompt 也执行同样的纪律)。下面是整理后的逐字段注解(示例路径沿用了规范原文):
{ "nodes": [ { "id": "auth_session_validatetoken", // 确定性 ID,见上文规则 "label": "Human Readable Name", // 人类可读展示名 "file_type": "code|document|paper|image|rationale|concept", "source_file": "<FILE_LIST path verbatim>", "source_location": null, // 如 "L1" / "§3.1",无则 null "source_url": null, // frontmatter 透传,无则 null "captured_at": null, "author": null, "contributor": null } ], "edges": [ { "source": "node_id", "target": "node_id", "relation": "calls|implements|references|cites|conceptually_related_to|shares_data_with|semantically_similar_to|rationale_for", "confidence": "EXTRACTED|INFERRED|AMBIGUOUS", "confidence_score": 1.0, // 按离散标尺取值 "source_file": "<FILE_LIST path verbatim>", "source_location": null, "weight": 1.0 } ], "hyperedges": [ { "id": "snake_case_id", "label": "Human Readable Label", "nodes": ["node_id1", "node_id2", "node_id3"], "relation": "participate_in|implement|form", "confidence": "EXTRACTED|INFERRED", "confidence_score": 0.75, "source_file": "<FILE_LIST path verbatim>" } ], "input_tokens": 0, // 占位,Part B Step B3 会用真实 usage 回填 "output_tokens": 0 }注意两点细节差异:
- 规范正文的边
relation枚举比引擎内置 prompt 多了rationale_for,可用于表达概念节点对另一节点的"理由说明"关系;对应 graphify/callflow_html.py 等下游渲染中对rationale_for有专门的关系文案与配色处理; input_tokens/output_tokens由子代理写 0 占位,合并阶段由编排者从真实调用用量回填。
校验器视角:哪些字段是硬性的
graphify/validate.py 定义了装配图之前的 schema 校验:
- 节点必填:
id、label、file_type、source_file; - 边必填:
source、target、relation、confidence、source_file; file_type与confidence均为白名单枚举,越界即报错。
这解释了规范里"只输出合法 JSON"的强语气——任何偏离都会被validate_extraction拦下并返回错误列表。
source_file 规则:逐字符复制,别自作聪明
规范用单独一段强调source_file是最容易"好心办坏事"的字段:
set source_file to the path of the originating file EXACTLY as it appears in FILE_LIST — verbatim and absolute. Do NOT shorten to a basename, do NOT re-relativize, do NOT strip any directory prefix, and do NOT change separators.
即:逐字符复制 FILE_LIST 中的条目,不改 basename、不改相对化、不删目录前缀、不改分隔符。原因写得非常清楚:
- 下游引擎会负责分隔符规范化与相对化(canonicalize separators and relativize against the build root downstream);
- 全量构建与增量
--update必须站在同一基线上; - 只有路径逐字符一致,
build_merge的"重抽取即替换(replace-on-re-extract)"才能命中已存在的节点,而不是累积出重复节点。
从工程视角看,这是"LLM 输出的脏数据由引擎统一清洗"的典型设计:模型不需要理解引擎的路径规范,只需当一个忠实的复制机器。这点与 graphify/llm.py 中对rel使用as_posix()的注释形成闭环——文件内容被包装进带path=与sha256=的<untrusted_source>块(llm.py 的 _wrap_untrusted),供模型照抄,同时哈希可用于把可疑节点追溯到原始字节。
落盘位置:CHUNK_PATH 必须是绝对路径
规范最后一条指令是:用 Write 工具把 JSON 写到提示词末尾给出的精确绝对路径CHUNK_PATH。它特别警告:不要用相对路径——Write 会以未定义的 cwd 解析相对路径,文件会被静默丢失。结合主 Skill 的 Step B3,"chunk 文件是否存在于磁盘"本身就是子代理成功的判定信号,所以落盘位置错了等于整个 chunk 白跑。
DEEP_MODE 与 --mode deep:更深、更激进的推断
规范提示词中含有一段条件指令:当用户以--mode deep调用时,子代理要更激进地补充INFERRED边——间接依赖、共享假设、潜在耦合都要尝试挖掘,拿不准的标AMBIGUOUS而不是省略。
命令层面,graphify extract --mode deep是真实的 CLI 选项(参见 graphify/cli.py 的--mode解析与校验,以及 graphify/main.py 帮助文本 "--mode deepaggressive INFERRED-edge semantic extraction")。在 Skill 文档的快速上手里对应:/graphify <path> --mode deep # thorough extraction, richer INFERRED edges(见 graphify/skill-agents.md)。
编排层面,主 Skill 要求编排者在调用开始时记录是否传入--mode deep,若传入则把DEEP_MODE=true传给每个 Part B 子代理。缓存层面,graphify/cache.py 为 deep 模式维护独立的semantic-deep/缓存命名空间(普通模式用semantic/),graphify cache-check --mode deep也据此检查——避免深浅两种抽取结果互相污染。
规范背后的设计哲学小结
- 分层确定性:代码交给 AST(确定性、免费、无幻觉),LLM 只处理代码解析器覆盖不到的语义层(文档/论文/图片),两者通过同一套确定性 ID 规则缝合;
- 约束即接口:
file_type、relation、confidence、ID 字符集、source_file复制规则,全部在 validate.py 与装配逻辑中有对应的硬校验,规范文本只是把校验器翻译成了人话; - 防幻觉与防伪影:
AMBIGUOUS宁可多标不可省略、跨语言calls一律禁止、非显然才允许semantically_similar_to、每 chunk 超边上限 3 条——这些规则的共同目标都是控制图的质量边界,而不是追求"抽得越多越好"; - 为增量与缓存而生:确定性 ID 让 replace-on-re-extract 不会叠重复节点;逐字符
source_file让全量与增量构建对齐同一基线;提示词文件路径被作为缓存 key 的一部分,规范一变,旧缓存自动作废重抽。
因此,对任何想在 Claude Code、Cursor、Codex、Gemini CLI 等宿主上为 graphify 贡献语义抽取实现、或排查"图里出现幽灵节点/重复节点/跨语言假调用"的开发者来说,这份 reference 文件 + graphify/llm.py 的内置系统提示词 + graphify/validate.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),仅供参考