首页
/ graphify 语义抽取子代理规范(extraction-spec)全解:将文档、论文与图像可靠编码为知识图谱 JSON

graphify 语义抽取子代理规范(extraction-spec)全解:将文档、论文与图像可靠编码为知识图谱 JSON

2026-09-06 19:10:25作者:何举烈Damon

graphify 通过两阶段构建知识图谱:先由本地确定性 AST 抽取器覆盖代码结构,再由并行的语义子代理把文档、论文与图像中的命名概念、实体、引用与推理关系转成统一 JSON。本指南围绕驱动这些子代理的 extraction-spec 参考提示词,完整拆解其节点 ID 确定性规则、证据分级与置信度标尺、语义相似边与超边约束、六类 file_type、以及逐字复制 source_file 等硬性要求,帮助你理解(或自行驱动)语义抽取管道,并让产出与 AST 抽取结果在合并时不会产生"幽灵重复节点"。

关联文档本体为 skillgen 渲染各平台技能文件后生成的 golden 副本 tools/skillgen/expected/graphify__skills__agents__references__extraction-spec.md,它与仓库内实际随技能分发的同构文件 graphify/skills/agents/references/extraction-spec.md 内容一致;除 agents 外,graphify/skills/ 下每个平台(claude、kiro、opencode、vscode、codex 等)都有各自同名副本。

一、这份规范解决什么问题:AST 之外的语义抽取通道

graphify 的抽取管道分为互补的两路:

  • AST 路径(结构化):对代码文件做本地确定性解析,产出 importscalls、类型关系等结构性边——见 graphify/extract.pygraphify/extractors/
  • 语义路径(LLM 子代理):当语料中至少存在一个文档、论文或图像 chunk 时,启动多个语义抽取子代理,负责捕获"AST 无法发现"的语义边:跨文件共享数据结构、架构模式、文档/论文中的命名概念、引用关系、图像里的设计信息等。纯代码语料会直接跳过该阶段,永远不会读取本规范。

两种产出最终合并成同一张图。规范开头即说明该提示词的使用方式:每个语义子代理逐字接收下面这份 prompt,仅替换五个占位符——FILE_LIST(文件清单)、CHUNK_NUM(当前 chunk 序号)、TOTAL_CHUNKS(总 chunk 数)、DEEP_MODE(是否深度模式)、CHUNK_PATH(本 chunk 结果写入的绝对路径)。它同时规定了三个"铁律":输出只能是合法 JSON(无解释、无 markdown 围栏、无前言);节点 ID 必须与 AST 抽取器完全一致;每条边都必须带合规的 confidence_score

二、触发时机与编排上下文:技能文档中的 Step 3 Part B

extraction-spec 不是独立运行的工具,而是技能编排流程的一部分。以 graphify/skill-agents.md 的 Step 3 Part B 为例,整个语义抽取分四步:

步骤 动作 与 spec 的关系
B0 先查抽取缓存 check_semantic_cache 通过 prompt_file='SPEC_PATH' 把缓存条目归属于这份提示词文件(issue #1939)
B1 分块 每个 chunk 20–25 个文件,每张图像独占一个 chunk(视觉需要独立上下文),同一目录文件尽量同 chunk
B2 单条消息并行派发所有子代理 把 spec 提示词作为任务描述逐字传入,替换占位符
B3 收集、写缓存、合并 校验 graphify-out/.graphify_chunk_NN.json 是否落盘,汇总进 .graphify_semantic_new.json

关键机制:缓存键归属 SPEC_PATHSPEC_PATH 是随 SKILL.md 同目录分发的 references/extraction-spec.md 的绝对路径——"当 graphify 升级改动了这份 prompt,旧 prompt 产出的缓存条目会被重新抽取而不是被回放;未变化的 prompt 则保留缓存条目"。这也解释了为何本规范如此强调"子代理必须把 JSON 写入指定的绝对路径 CHUNK_PATH",以及各平台技能(如 codex/opencode 的"内联返回 JSON"变体)如何差异化适配。底层实现在 graphify/cache.pycheck_semantic_cache 同时接收 promptprompt_file 参数,提示词内容(或其文件)参与缓存键计算。

编排方还要求派发前先做估算(未缓存非代码文件数 /22 约等于 agent 数,约 45s/批);若某个 chunk 结果文件缺失,则提示子代理可能以只读型被派发,需改用 general-purpose 类型重跑;超过一半 chunk 失败则停下要求重跑。

三、证据分级与按文件类型的抽取规则

3.1 三种证据分级(confidence)

分级 语义 使用场景
EXTRACTED 源中显式存在的关系 import、call、引用、"see §3.2"式交叉引用
INFERRED 合理的推理关系 共享数据结构、隐含依赖
AMBIGUOUS 不确定的关系 标记出来供复查,不得省略

3.2 代码文件

语义子代理对代码只补语义边(调用关系、共享数据、架构模式),绝不重新抽取 import——那些 AST 已覆盖

calls 边有两条不可违反的方向与语言规则:

  • source 必须是调用方(发起调用的函数/类),target 必须是被调用方,严禁反向;
  • calls必须限定在同一门语言内:Python 函数不能 calls 一个 JS/TS/Go/Rust/Java 符号,反之亦然——跨语言调用边是幽灵伪影(phantom artifacts),永远不要产出。

3.3 文档 / 论文文件

抽取命名概念、实体与引用。对于设计缘由(为什么做此决策、权衡、设计意图),规范明确要求:

  • 作为 rationale 属性存放在相关概念节点上,不要单独创建 rationale 节点或 fragment 节点;
  • 只有当某事物本身就是命名实体或概念时才为它建节点;
  • 概念类节点(想法、原则、机制、设计模式)使用 file_type:"rationale"
  • file_type 必须是且只能是六个枚举值之一:codedocumentpaperimagerationaleconcept——任何其他值都非法,会被拒绝。

3.4 图像文件(视觉理解)

规范强调"用视觉理解图像是什么,不要只做 OCR":

图像类型 应抽取的内容
UI 截图 布局模式、设计决策、关键元素、用途
图表 指标、趋势/洞察、数据来源
推文/帖子 主张(claim)作为节点、作者、提及的概念
示意图 组件与连接关系
科研图 论证了什么、方法、结果
手写/白板 想法与箭头连线;不确定的解读标记为 AMBIGUOUS

3.5 YAML frontmatter 元数据继承

如果某文件带有 YAML frontmatter(--- ... ---),则把其中 source_urlcaptured_atauthorcontributor 复制到该文件产出的每个节点上,保证溯源元数据不丢失。

四、置信度标尺:为什么禁止 0.5,以及五档离散分值

规范给出的置信度体系是全文最容易出错的部分,值得单独一节:

  • confidence_score每条边上都必填——绝不省略、绝不默认取 0.5
  • EXTRACTED 边恒为 1.0
  • INFERRED 边只能从下面五个离散档位中精确选取一个
  • AMBIGUOUS 边取 0.1–0.3
分值 含义
0.95 直接结构证据(共享数据结构、具名跨文件引用)
0.85 强推理(清晰的功能对齐,但没有直接符号链接)
0.75 合理推理(共享问题域 + 相似形状,需要解读)
0.65 弱推理(主题相关,无形状证据)
0.55 推测但合理(仅表层共现)

规范同时解释了为什么用离散档位而非连续区间:"模型遵循离散评分表比连续区间更好;生产观测到的双峰分布(>50% 落在 0.5、>40% 落在 0.85+)表明连续区间指导被坍缩成了二值判断"。兜底规则是:如果没有任何一档适用,就把边标记为 AMBIGUOUS,而不要给 0.4 或更低的分(该区间不属于 INFERRED 的合法取值)。

这条规则并非只约束 LLM:仓库中 AST 抽取器产出的 INFERRED 边同样遵守同一标尺。默认分值定义在 graphify/export.py_CONFIDENCE_SCORE_DEFAULTS 中(EXTRACTED=1.0、AMBIGUOUS=0.2、INFERRED≠0.5),并由 tests/test_inferred_confidence_rubric.py 锁定:测试显式断言 INFERRED 默认值属于离散集合 {0.55, 0.65, 0.75, 0.85, 0.95} 且不等于被禁止的 0.5。该测试文档串中记录了一次真实违规:graphify 自身上 128 条 INFERRED 边中 54 条缺失分值(落入默认 0.5)、74 条硬编码了不在集合内的 0.8(issue #2813)——正是这类漂移促使 rubic 被写成守护测试。

五、两种"成组/跨域"机制:语义相似边与超边

5.1 semantically_similar_to 语义相似边

当本 chunk 内两个概念解决问题相同或表达同一思想、却没有任何结构链接(无 import、无 call、无引用)时,可以添加一条标记为 INFERREDsemantically_similar_to 边,confidence_score 反映相似程度(0.6–0.95)。规范给出的典型例子:

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

注意约束:只有当相似性真正非显然且跨领域(cross-cutting)时才加;琐碎相似严禁加边。这也解释了为何 graphify/analyze.py 在消费语义图时把 semantically_similar_to 单独看待——它是"真正的跨边界洞察"。

5.2 超边(hyperedge)

3 个或更多节点显然共同参与某一个共享概念、流程或模式,而仅靠两两成对边表达不出来时,把它们写进顶层 hyperedges 数组:

  • 实现同一协议/接口的所有类;
  • 认证流程中的所有函数(即使它们不互相调用);
  • 论文某节中构成一个连贯想法的所有概念。

约束:克制使用——只有当群组关系提供了超越成对边的信息时才加;每个 chunk 最多 3 条超边。超边 ID 为 snake_case,relation 只能是 participate_inimplementform 三者之一。

六、节点 ID:从仓库路径到确定性标识符

节点 ID 是语义抽取与 AST 抽取能否合并成同一张图的关键契约:

  • 格式为 {stem}_{entity}
  • stem = 去掉扩展名后的完整仓库相对路径,每一级路径段都保留,段与段之间用 _ 拼接(每段转小写、非字母数字字符替换为 _);
  • entity = 符号名,同样规范化;
  • 字符集限制:仅小写 [a-z0-9_],禁止点号与斜杠

规范给出五个可直接对照的示例:

路径 + 符号 结果
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。规范特别警告:如果只取文件名(如 session_validatetoken)或只取最近父目录(如 auth_session_validatetoken),就会制造孤立幽灵重复节点。CRITICAL 铁律:永远不要在 ID 上追加 chunk 号、序号或任何后缀(禁止 _c1_c2_chunk2 之类);ID 必须仅由标签(label)确定性地推导——同一个实体无论由哪个 chunk 处理,都必然得到同一个 ID。

这套规则在代码侧有精确对应。_file_stemgraphify/extractors/base.py 中实现:保留完整路径(去扩展名)作为路径段,make_id 随后把分隔符折叠为下划线;注释中的示例与规范完全同构(docs/v1/api/README.md -> docs_v1_api_readme),并注明"使用全部段而非仅最近父目录(#1504)",避免同名文件互相覆盖成 last-writer-wins 单节点;顶层文件保留裸 stem(setup.py -> setup)。规范文本与代码实现之间还有专门的漂移守护测试 tests/test_extraction_spec_ids.py:它用正则直接从各平台分发的 extraction-spec.md 中解析出全部 path + entity → id 示例,再调用真实的 _file_stem/_make_id 逐一断言相等——任何一侧被改动导致示例失效都会让测试失败。

若你正在重新抽取一个用旧版"最近父目录"格式构建过的项目,规范给出的迁移动作是让用户运行 graphify extract --force 全量重建,以获得干净的路径级 ID。

七、输出 JSON Schema 逐字段解析

子代理必须生成与下列 schema 完全一致的抽取 JSON(提示词原文):

{"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}

各对象字段含义如下:

node 字段

字段 说明
id 按第六节确定性规则生成的节点 ID
label 人类可读名称
file_type 六选一:code|document|paper|image|rationale|concept
source_file 来源文件路径,必须与 FILE_LIST 中逐字一致(见第八节)
source_location / source_url / captured_at / author / contributor 溯源与元数据;frontmatter 存在时从 YAML 继承

edge 字段

字段 说明
source / target 起止节点 ID;calls 边方向与同语言约束见 3.2
relation 枚举:calls|implements|references|cites|conceptually_related_to|shares_data_with|semantically_similar_to|rationale_for
confidence EXTRACTED|INFERRED|AMBIGUOUS
confidence_score 必填;按第四节标尺取值,EXTRACTED=1.0,INFERRED 取离散五档,AMBIGUOUS 取 0.1–0.3
source_file / source_location / weight 溯源与权重

hyperedge 字段id(snake_case)、labelnodes(≥3 个成员 ID)、relationparticipate_in\|implement\|form)、confidence(仅 EXTRACTED/INFERRED)、confidence_scoresource_file

顶层还包含 input_tokensoutput_tokens(占位 0;编排层会在每个 Agent 完成后用工具结果 usage 字段的真实计数回填)。

注意:schema 骨架中的 auth_session_validatetoken 只是便于阅读的占位写法,真实 ID 一律采用第六节的全路径规则。

八、source_file 逐字复制规则:与增量构建的对齐关键

source_file 规则适用于每一个 node、edge 与 hyperedge

  • 取该来源文件在 FILE_LIST原样出现的路径——逐字、绝对;
  • 不缩短为 basename、不重新相对化、不剥除任何目录前缀、不改动分隔符(引擎会在下游对分隔符做规范化并按构建根相对化);
  • 直接逐字符复制 FILE_LIST 条目。

这样做的根本目的写在规范末尾:让完整构建与增量 --update 站在同一基准上,build_mergereplace-on-re-extract 才能命中既有节点,而不是不断累积出重复节点。换言之,source_file 的一致性直接决定了"同一文件被重新抽取时是替换旧节点还是长出第二份"这一去重语义。这与仓库中增量更新/去重的整体设计同源,可进一步阅读 docs/superpowers/specs/2026-05-04-incremental-updates-dedup-design.md

九、CHUNK_PATH:必须写绝对路径,避免 JSON 静默丢失

每个语义子代理在生成 JSON 后,必须用 Write 工具把它写入一个精确的绝对路径 CHUNK_PATH。规范特别强调:不允许用相对路径——"Write 会把相对路径解析到某个未定义的 cwd,文件会被静默丢失"。编排侧(如 skill-agents.md 的 B2 步骤)会预先从 PROJECT_ROOT=$(pwd) 推导出形如 ${PROJECT_ROOT}/graphify-out/.graphify_chunk_0N.json 的绝对路径再派发;子代理结果文件是否存在于磁盘,正是编排层判定本次派发成功与否的信号(read-only 型子代理不会落盘,会触发"re-run with general-purpose agent"警告)。

十、下游消费与守护测试:规则不会悄悄漂移

语义子代理产出的 nodes/edges/hyperedges 最终被构建合并(build_merge)、缓存(cache)与下游分析消费。graphify/analyze.py 会依据同一套字段约定工作:confidence 缺省视为 EXTRACTED、对 semantically_similar_to 特殊对待、AMBIGUOUS 边以低置信度提示("Edge tagged AMBIGUOUS — confidence is low")方式呈现、建议新增边也携带 confidence 字段并按分级排序。

仓库把"提示词示例"与"真实实现"绑定成了守护测试网,防止手写的规范文本与代码静默漂移:

  • tests/test_extraction_spec_ids.py:解析 spec 中全部节点 ID 示例,断言 _file_stem + _make_id 的产出与之一致,同时锁定两种被警告的错误形态确实非法;
  • tests/test_inferred_confidence_rubric.py:断言 INFERRED 默认值属于五档离散集合、不等于禁止值 0.5,EXTRACTED/AMBIGUOUS 默认值保持 1.0/0.2;
  • tools/skillgen/gen.py 及 golden 输出(含本指南所依托的 tools/skillgen/expected/ 目录):负责把同一份 spec 渲染到 graphify/skills/<host>/references/ 的各平台副本,skillgen --check 对 expected 目录做一致性校验。

对读者而言,最实用的经验是:当需要为 graphify(或类似两阶段管道)设计自己的 LLM 抽取提示词时,把"节点 ID 确定性、置信度离散标尺、source_file 逐字一致、绝对路径落盘"这四条写成显式规则并配守护测试,才能让并行子代理的输出真正可合并、可缓存、可增量。

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