首页
/ graphify 语义抽取子代理规范全解:把文档、论文与图片语料转成可合并的知识图谱 JSON

graphify 语义抽取子代理规范全解:把文档、论文与图片语料转成可合并的知识图谱 JSON

2026-09-07 19:56:56作者:裴锟轩Denise

导读:graphify 在“代码 → 知识图谱”之外,还有一条面向文档、论文、图片等非代码语料的语义抽取通道,而这条通道的全部行为都收敛在 extraction-spec.md 这一份“子代理 Prompt 规范”里。本文以该规范为骨架,逐条解析三类置信度、六种 file_type、离散可信度刻度、确定性 Node ID 契约、超边(hyperedges)、图片视觉规则与 source_file 逐字规则,并结合 validate.pysemantic_cleanup.pyskill.md 等源码说明这些规则在下游如何被校验与消费。读完你将能完整理解并亲自实现一个符合 graphify 契约的语义抽取子代理。

一、规范定位:双通道抽取流水线中的“语义半边”

graphify 的知识图谱构建是双通道的:

  • AST 通道(Part A):由 extract.py 驱动,对代码文件做确定性语法解析,产出 import、调用、类型引用等结构边;
  • 语义通道(Part B):当语料中包含至少一个文档(doc)、论文(paper)或图片(image)分块时,由并行的 LLM 子代理负责,把代码的 AST 抓不到的东西——调用关系背后的设计意图、共享数据结构、架构模式、论文概念、图片内容——抽取为图片段。

本文的主角 extraction-spec.md(仓库内实际文件见 graphify/skills/claude/references/extraction-spec.md,其生成副本位于 tools/skillgen/expected/graphify__skills__claude__references__extraction-spec.md)正是 Part B 子代理收到的 Prompt 原文。它同时定义了:子代理的身份、输出格式、每类文件的抽取策略、Node ID 命名契约、置信度刻度、超边规则与落盘方式。只要语料纯代码、不含 doc/paper/image,Part B 直接跳过,该文件也永远不会被读取。

skill.md 的第 196~287 行可以还原它的完整加载上下文:Step B0 先用 check_semantic_cache 检查哪些文件已有缓存抽取结果;Step B1 把未缓存文件按 20~25 个为一组分块(每张图片独占一个 chunk);Step B2 在同一轮消息里并行派发多个 general-purpose 子代理,并把本规范逐字下发;Step B3 收集 chunk JSON、回填真实 token 计数后合并进 .graphify_semantic.json

二、Prompt 骨架:五个占位符与“只输出 JSON”的铁律

每个语义子代理收到的是下面这段 Prompt 的逐字副本,其中五个位置需要被调用方替换:

占位符 含义
FILE_LIST 本 chunk 负责的文件清单
CHUNK_NUM / TOTAL_CHUNKS 当前第几块 / 共几块
DEEP_MODE 是否启用了 --mode deep
CHUNK_PATH 抽取结果 JSON 的绝对落盘路径

Prompt 的第一性约束是:只输出符合 schema 的有效 JSON——不做解释、不包 markdown 围栏、不加前言。子代理是被程序化消费的,任何多余输出都会破坏后续 merge。

三、证据强度三级:EXTRACTED / INFERRED / AMBIGUOUS

整份规范的置信度体系建立在三个枚举值之上,它们描述的是“这条边的证据从哪来、有多硬”:

等级 定义 典型来源
EXTRACTED 关系在源文件中显式存在 import、调用、引用、论文中的 “see §3.2”
INFERRED 合理推断 共享数据结构、隐含依赖
AMBIGUOUS 不确定——标记待审,而不是漏掉 视觉模型对白板笔迹的不确定解读等

一个值得强调的哲学:AMBIGUOUS 边不许省略。宁可打上“待复核”标签进入图谱,也不能让不确定信息静默消失——这与确定性代码解析的“非此即彼”形成刻意互补。

四、按文件类型分类的抽取策略

4.1 代码文件:只补语义,不重复造轮子

代码 chunk 中,子代理的任务被明确限定在 AST 无法发现的语义边上:调用关系、共享数据、架构模式。而 import 之类的结构边“AST 已经有了”,禁止重复抽取——这是避免下游去重与幽灵节点问题的第一道防线。

calls 边有两条硬性纪律:

  1. 方向不可反source 必须是调用方(发起调用的函数/类),target 必须是 callee;
  2. 语言内封闭:一条 Python 函数与 JS/TS/Go/Rust/Java 符号之间的跨语言 calls 边被判定为 phantom artifact(幻影伪影),一律不得生成。跨语言关系应走其他语义关系而非 calls

4.2 文档与论文:抽取具名概念,WHY 存成属性而非节点

对 doc/paper,子代理要抽取具名概念、实体与引用(citation)。设计理由(rationale,即“为什么做这个决定、有哪些权衡、设计意图”)的处理方式非常关键:

  • rationale 以 rationale 属性挂在相关概念节点上,绝不单独创建 rationale 节点或“片段节点”;
  • 只有本身是具名实体或概念的东西才配拥有节点;
  • 对概念类节点(思想、原则、机制、设计模式),使用 file_type:"rationale"

这与 semantic_cleanup.py 的实现严丝合缝:构建后期会清理 file_typerationale/concept 且参与 rationale_for 边、标签又呈“句子状”的节点,把它们合并为父节点上的句子级 rationale 数据,并保证只有 rationale_for携带 rationale 文本、其他出边不传播句子内容。也就是说,Prompt 层“别建 rationale 节点”的纪律,在数据清洗层还有兜底验证。

4.3 file_type 六值枚举:其他值一律非法

file_type 被约束为恰好六个值之一,任何其他值都会被拒绝:codedocumentpaperimagerationaleconcept

源码侧可精确印证这一约束:validate.py 定义了 VALID_FILE_TYPES = {"code", "document", "paper", "image", "rationale", "concept"},并在第 48~51 行对每个节点的 file_type 做集合校验,非法值直接报错;第 6 行还规定节点必填字段为 {"id", "label", "file_type", "source_file"}

4.4 图片文件:用视觉理解“图是什么”,而非只做 OCR

图片分块要求子代理调用视觉能力理解图像语义,并按图类型差异化抽取:

图类型 抽取重点
UI 截图 布局模式、设计决策、关键元素、用途
图表 指标、趋势/洞见、数据来源
推文/帖子 主张作为节点、作者、涉及概念
示意图 组件与连接关系
科研图 它证明了什么、方法、结果
手写/白板 想法与箭头指向;不确定处标记为 AMBIGUOUS

4.5 DEEP_MODE:激进推断开关

当用户以 --mode deep 调用时,DEEP_MODE 占位符生效,规范指示子代理对 INFERRED 边变得激进:间接依赖、共享假设、潜在耦合都要挖出来;拿不准就标 AMBIGUOUS 而不是省略——宁可多一条待复核边,也不要在深度模式下漏掉潜在关联。

五、语义相似边 semantically_similar_to:跨结构的概念关联

当 chunk 中两个概念“解决同一个问题、代表同一个想法”,但没有任何结构链接(无 import、无调用、无引用)时,子代理应补一条 semantically_similar_to 边,标记为 INFERRED,并按相似程度给出 0.6~0.95 的 confidence_score。规范的示例场景包括:

  • 两个都校验用户输入、却从不互相调用的函数;
  • 代码里的一个类与论文里描述同一算法的概念;
  • 两个以不同方式处理同一失败模式的错误类型。

使用纪律是宁缺毋滥:只加真正非显而易见、跨切面的相似关系,严禁为琐碎相似添边。

六、超边 Hyperedges:三人成众才成群组

如果 3 个及以上节点明显共同参与一个共享概念、流程或模式,而两两之间的边表达不了这层整体关系,就应向顶层 hyperedges 数组添加超边。规范给出三类典型:

  • 实现同一协议/接口的全部类;
  • 认证流程中的所有函数(哪怕它们并不两两互调);
  • 一篇论文里构成一个连贯想法的全部概念。

约束是:每个 chunk 最多 3 条超边,且仅在群组关系能提供超过 pairwise 边的额外信息时使用。

七、YAML frontmatter 元数据继承

若某文件带 --- ... --- 形式的 YAML frontmatter,子代理必须把其中的 source_urlcaptured_atauthorcontributor 复制到来自该文件的每一个节点上。这保证了来源元数据在图谱里逐节点可追溯。

八、confidence_score 离散刻度:消灭“0.5 惰性默认”

confidence_score每一条边上都必填:不可省略,也不允许用 0.5 充当默认值。规范的刻度表如下:

证据等级 分值 语义
EXTRACTED 1.0 恒为 1.0
INFERRED 0.95 直接结构证据(共享数据结构、具名跨文件引用)
INFERRED 0.85 强推断(明确的功能对齐,无直接符号链接)
INFERRED 0.75 合理推断(共享问题域 + 相似形态,需解读)
INFERRED 0.65 弱推断(主题相关,无形态证据)
INFERRED 0.55 推测但合理(仅表层共现)
AMBIGUOUS 0.1~0.3 不确定区间

这个离散刻度的设计动机在规范中写得很直白:模型遵循离散规则比连续区间好。生产环境观测到的双峰分布(>50% 落在 0.5、>40% 落在 0.85+)说明连续区间指导正被模型坍缩为二元选择。因此规范强制:找不到合适档位就把边标为 AMBIGUOUS,而不是选 0.4 或更低。

九、Node ID 确定性契约:路径全段拼进 ID,杜绝幽灵重复

Node ID 是本规范最容易踩坑、也最体现工程严谨性的部分。格式规则:

  • 只允许小写 [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

规则背后的原因值得展开:ID 必须与 AST 抽取器生成的 ID 完全一致。如果只取文件名(如 session_validatetoken)或只取直接父目录(如 auth_session_validatetoken),就会制造孤儿的幽灵重复节点。特别地,若某个项目是用旧的 immediate-parent 格式建的,应当执行 graphify extract --force 全量重建才能干净切换。

另一条硬约束是:绝不追加 chunk 号、序号或任何后缀(不能出现 _c1_c2_chunk2 之类)。ID 必须能仅由 label 确定性推导——同一实体无论由哪个 chunk 处理,都必须产出同一个 ID。这是后续“按 ID 覆盖合并而非累积重复”的基石。

十、输出 JSON Schema:逐字段精解

子代理必须严格按如下 schema 输出(这是 Prompt 内嵌的原样 schema):

{"nodes":[{"id":"auth_session_validatetoken","label":"Human Readable Name","file_type":"code|document|paper|image|rationale|concept","source_file":"<FILE_LIST path verbatim>","source_location":null,"source_url":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,"output_tokens":0}

要点分解:

nodes 的字段与 validate.py 的必填约束一一对应(id/label/file_type/source_file),其余 source_urlcaptured_atauthorcontributor 默认为 null,由 YAML frontmatter 规则填充。

edges 允许的 relation 共 8 种:callsimplementsreferencescitesconceptually_related_toshares_data_withsemantically_similar_torationale_for。注意 confidence 是 EXTRACTED/INFERRED/AMBIGUOUS 三值字符串,而 confidence_score 是数值,两者共同构成证据体系。

hyperedges 的 relation 枚举为 participate_inimplementform,示例默认分值为 0.75。

顶层还有 input_tokensoutput_tokens,实际运行中会由调用方在 Part C merge 前用 Agent 工具结果 usage 字段的真实计数回填(见 skill.md 附近说明),因为 chunk JSON 里默认是占位 0。

十一、source_file 逐字规则:增量的命脉

这是规范中少有的、直接影响增量构建正确性的规则。每个 node、edge、hyperedge 的 source_file 都必须设置为FILE_LIST 中出现的逐字符原样路径——verbatim 且 absolute:

  • 不要缩短为 basename;
  • 不要重新相对化;
  • 不要剥掉任何目录前缀;
  • 不要改分隔符(引擎在下游统一做分隔符规范化并相对化到 build root)。

这样做的目的是让全量构建与增量 --update 站在同一个基准上:build_merge 的“按源文件重抽取即替换”语义才能命中既有节点,而不是累积出一条重复节点。source_file 字段同时也正是语义缓存的键的一部分——缓存按文件 + prompt 归因,一旦规范文本变化,旧条目会被判定过期并重抽取(graphify/skill.md 对 SPEC_PATH 与缓存归因的关系有明确说明)。

十二、落盘纪律:绝对路径写 CHUNK_PATH

Prompt 的收尾指令是:用 Write 工具把 JSON 写到 CHUNK_PATH 指定的精确绝对路径,并明确警告“相对路径会基于未定义的 cwd 解析,文件会被静默丢失”。在 skill.md 中可看到配套约束:子代理必须是 subagent_type="general-purpose"(具备 Write 与 Bash 权限),Explore 型只读子代理无法落盘 chunk 文件,会导致抽取结果静默丢弃;chunk 文件是否存在于磁盘上是 Part C 判断子代理成功与否的唯一信号。

十三、易错点速查清单

  1. 输出格式:只输出裸 JSON,不要 markdown 围栏、解释、前言。
  2. file_type:必须命中六值枚举,任何其他值都会被 validate.py 拒绝。
  3. rationale:存为属性或概念节点的 file_type:"rationale",禁止建独立 rationale 节点;句子状 rationale 节点会在 semantic_cleanup.py 被吸收为父节点属性。
  4. calls 方向与语言:source 恒为调用方;跨语言 calls 是 phantom artifact,禁止生成。
  5. ID 确定性:必须用“全路径段 + 符号”拼 ID,禁后缀、禁 _c1 式编号,否则产生幽灵重复节点。
  6. confidence_score:全边必填;EXTRACTED 恒 1.0;INFERRED 只能从 {0.95, 0.85, 0.75, 0.65, 0.55} 中选一,禁用 0.5,0.4 以下宁可为 AMBIGUOUS(0.1~0.3)。
  7. 超边节制:每 chunk 至多 3 条,仅当超过 pairwise 表达力才建。
  8. source_file:逐字复制 FILE_LIST 条目,verbatim absolute,这是增量替换命中的前提。
  9. 落盘:绝对路径写 CHUNK_PATH,用带写权限的 general-purpose 子代理。

十四、把规范放进 graphify 运行全景

理解这份规范的最佳方式是把它的生命周期与 graphify 的运行流水线对应起来。以 Claude 平台 skill 为例,规范文件与 SKILL.md 同目录部署,运行路径为:

  1. 检测阶段产出语料分类(code/document/paper/image),参考 graphify-out/.graphify_detect.json
  2. 若存在非代码语料,进入 Part B;
  3. Step B0 以本规范路径(SPEC_PATH)为缓存归因键调用 check_semantic_cache
  4. Step B1~B2 分块并以 general-purpose 子代理并行执行本规范;
  5. 子代理按上述规则产出 chunk JSON 落到绝对路径;
  6. Part C 合并所有 chunk 为 .graphify_semantic.json
  7. validate.py 在构建校验阶段执行六值 file_type 与必填字段的最终把关,semantic_cleanup.py 随后完成 rationale/概念节点的句子化清洗。

由此可以看到设计闭环:Prompt 层用离散刻度和确定性 ID 约束模型输出,数据层用枚举校验和清洗兜底,缓存与 source_file 规则保证增量可复现——三者共同把“LLM 自由文本抽取”这一天然发散的过程,牢牢锚定在 graphify 可合并、可增量、可验证的图数据结构上。若要在自己的 Agent 流程中复刻这一模式,本规范本身就是一份可直接照抄的、经过工程打磨的子代理契约模板。

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