首页
/ graphify extraction-spec 规范解析:语义抽取子代理的知识图谱 JSON 抽取协议

graphify extraction-spec 规范解析:语义抽取子代理的知识图谱 JSON 抽取协议

2026-09-06 18:03:12作者:薛曦旖Francesca

graphify 通过两条路径构建知识图谱:确定性 AST 结构抽取,以及面向文档、论文、图片的语义抽取。本文聚焦后者——托管在 graphify/skills/trae/references/extraction-spec.md(以及多平台 SKILL 的相同副本)中的 extraction-spec 是一份会原样下发给每个语义抽取子代理的“提示词规范”,它同时定义了证据分级、置信度打分、节点/边/超边的 JSON Schema、节点 ID 生成规则等全套约束。读完本文,你将掌握该子代理协议的设计动机、每一条规则的落地校验方式(validate.pybuild.pysemantic_cleanup.py 等源码佐证),并能在自己的 AI Agent 工作流里正确接入这套抽取协议。

这份文档是什么:所有宿主 SKILL 共用的“抽取子代理提示词”

extraction-spec.md 不是普通说明文档,而是一份会被逐字加载并注入子代理任务描述的提示词正文。以 Trae 平台的宿主 SKILL 为例:

  • 它在 Step 3 Part B 被加载。触发条件是语料中至少存在一个 doc、paper 或 image 分块;纯代码语料直接跳过 Part B,永不读取本文件(代码由 Part A 的 AST 引擎负责)。
  • 文档中的 FILE_LISTCHUNK_NUMTOTAL_CHUNKSDEEP_MODECHUNK_PATH 都是占位符,宿主在派发前完成替换,随后把整段提示词作为子代理的任务描述,通过 Task 工具并行派发(每个分块一次调用,块大小 20~25 个文件,每张图片独占一块)。
  • 每个子代理把结果写入自己的 graphify-out/.graphify_chunk_NN.json,父代理在 Step B3 以“文件是否落盘”作为成功信号做收集与合并。

同样的 extraction-spec.md 在仓库中按平台各存一份(如 graphify/skills/{agents,amp,claude,claw,codex,copilot,droid,kilo,kiro,opencode,pi,trae,vscode,windows}/references/),并由 tools/skillgen 统一渲染。这正是 tests/test_extraction_spec_ids.py 要把所有“shipped specs”里的示例逐个解析出来做漂移守卫的原因——提示词正文与代码实现一旦不同步,就会产生幽灵重复节点。

文档要求子代理只输出符合下述 Schema 的合法 JSON:不解释、不加 markdown 代码围栏、不加前言。

顶层输出协议:只接受一种 JSON 形状

子代理产出的 JSON 必须严格匹配以下 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:每个实体对应一个节点,字段包括 idlabel(人类可读名)、file_type(六选一)、source_file(FILE_LIST 中原样路径)、可空的 source_location/source_url/captured_at/author/contributor
  • edges:有向关系,字段为 sourcetargetrelationconfidenceconfidence_scoresource_file、可空的 source_location、固定 1.0 的 weight
  • hyperedges:三个以上节点共同参与的群组关系,含 idlabelnodes 成员列表、relationconfidenceconfidence_scoresource_file
  • 顶层另有 input_tokensoutput_tokens 两个令牌计数占位。

任何键缺失、类型错误或字段枚举越界都会在入库环节被拦截:见 validate.py 中声明的 VALID_FILE_TYPESVALID_CONFIDENCESREQUIRED_NODE_FIELDS = {"id","label","file_type","source_file"}REQUIRED_EDGE_FIELDS = {"source","target","relation","confidence","source_file"}

证据分级:EXTRACTED / INFERRED / AMBIGUOUS

图谱的价值在于“诚实审计”,因此文档强制给每条边标注证据来源:

  • EXTRACTED:关系在源文中显式存在(import、调用、引用、"see §3.2" 等);
  • INFERRED:合理推断(共享数据结构、隐含依赖);
  • AMBIGUOUS:不确定——必须标记出来留给人工复核,禁止直接省略

这一“宁可标记也不丢弃”的取向贯穿整份规范,也与宿主的 Honesty Rules(“绝不臆造边,拿不准就用 AMBIGUOUS”)完全一致。

confidence_score 离散打分准则

规范要求每条边上都必须有 confidence_score,且永远不能省略,永远不能拿 0.5 当默认值

分级 confidence_score 语义
EXTRACTED 1.0(固定) 源文中有直接结构证据
INFERRED 0.95 直接结构证据(共享数据结构、跨文件具名引用)
INFERRED 0.85 强推断(清晰的功能对齐,但无直接符号链接)
INFERRED 0.75 合理推断(共享问题域 + 相似形状,需要解读)
INFERRED 0.65 弱推断(仅主题相关,无形状证据)
INFERRED 0.55 推测性但可能(仅表层共现)
AMBIGUOUS 0.10.3 不确定,留待复核

文档专门解释了为什么要给 INFERRED 一个离散档位而非连续区间:模型对离散打分表的表现好于连续区间,生产中观察到的双峰分布(超过 50% 集中在 0.5、超过 40% 集中在 0.85+)说明连续范围指引被模型坍缩成了“二元决策”。若没有任何一档适配,规则明确要求把边标为 AMBIGUOUS,而不是填 0.4 或更低。

该枚举约束同样落在代码层:validate.py 声明 VALID_CONFIDENCES = {"EXTRACTED","INFERRED","AMBIGUOUS"},非法值直接报错。

节点模型:六类 file_type 与 rationale 的属性化存储

文档规定 file_type 必须是且只能是以下六值之一,其余任何值都会被判非法并拒绝:

file_type 适用对象
code 代码符号
document 文档(.md/.txt 等)
paper 论文
image 图片(由视觉子代理产出)
rationale 概念型节点(想法、原则、机制、设计模式等)
concept 概念型节点

关于“设计理由”,文档给出一条容易被误解的规则:不要把 WHY(决策理由、权衡、设计意图)建成独立的 rationale 节点或碎片节点,而要把它作为 rationale 属性挂到相关概念节点上;只有本身是具名实体或概念的东西才值得建节点。源码侧 semantic_cleanup.pyrationale_for 边与“句子式标签”做了专门清洗:把挂在概念节点上的理由文本回填为属性、收拢过度拆分的碎片节点(semantic_cleanup.py)。

规范同时要求:如果文件带有 YAML frontmatter(--- ... ---),需把其中的 source_urlcaptured_atauthorcontributor 复制到来自该文件的每一个节点上。

一个值得一提的工程细节是下游容错:虽然规范只接受六值,但 build.py 内置了一张 LLM 高频误写同义词表(markdown→documenttext→documenttool→codelibrary→codepattern/principle/constraint→concept 等),在入库时尽量保留语义就近归一,实在无法映射的非法值才兜底为 concept。这体现了“提示词严格、管线宽容”的分层设计。

节点 ID 确定性规范:防止幽灵重复节点的核心

这是整份规范中最具工程含量的一部分。文档给出的 ID 格式是:

{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

两条“红线”必须同时遵守:

  1. 与 AST 抽取器完全一致。规范明确警告:如果只用文件名(如 session_validatetoken)或只用直接父目录(如 auth_session_validatetoken),就会造出与 AST 节点无法合并的孤儿幽灵重复节点。这并非空话——extract.py 中的 _file_stem + _make_id 就是 AST 侧的同款实现,而 tests/test_extraction_spec_ids.py 的职责正是解析每份 spec 文档里的 示例,断言真实生产函数能逐一复现,任何一侧漂移都会让测试失败。
  2. 绝不追加任何后缀。文档用 CRITICAL 强调:不得在 ID 后加 _c1_c2_chunk2 之类的分块序号。ID 必须由符号本身确定性地推导——无论由哪个分块处理,同一个实体永远产出同一个 ID,这是增量 --update 与合并去重的前提。

若旧格式项目需要迁移(例如历史上用 immediate-parent 格式构建过的工程),规范给出修复手段:重新执行 graphify extract --force 做一次干净重建。字符级归一化背后的算法保证可见于 ids.pynormalize_id 以 NFKC + casefold 迭代到不动点、幂等且大小写无关;make_id(*parts) 将各部分剥掉首尾点/下划线后以 _ 连接。

边:方向、关系词表与跨语言禁区

calls 边方向与跨语言禁令

文档对代码 calls 边给出两条绝对规则:

  • 方向不得反转source 必须是调用方(发起调用的函数/类),target 必须是被调用方
  • 不得跨语言:一个 Python 函数不能 calls JS/TS/Go/Rust/Java 符号,反之亦然——规范直言跨语言调用边是“phantom artifacts”(幻影产物),永远不要输出。跨语言/跨仓库的合法关系应通过 referencesshares_data_with 等边表达,其解析由 symbol_resolution.pycross_repo_types.py 等模块负责。

关系词表

边上的 relation 是枚举值:callsimplementsreferencescitesconceptually_related_toshares_data_withsemantically_similar_torationale_for。图谱前端对部分关系的展示语义可见 callflow_html.py(如 rationale_for → “说明/explains”、conceptually_related_to → “相关/relates”)。

代码文件的语义分工

对代码文件,子代理要提取的是 AST 找不到的语义边(调用关系、共享数据、架构模式),并明确不要重复抽取 imports——AST 已经覆盖了它们。这也解释了抽取管线为何按文件类型分工:代码走 Part A 的确定性 AST,语义子代理只在文档/论文/图片上消费 LLM 预算。

分文件类型的抽取指引

提示词对四类语料的关注点各不相同:

  • 代码文件:聚焦语义边(调用、共享数据、架构模式),不重抽 imports;
  • 文档/论文文件:抽取具名概念、实体、引用;
  • 图片文件:文档特别强调要用视觉理解图片“是什么”,而不是只做 OCR,并针对六类图片给出差异化的关注点。

图片按内容类型拆解如下:

图片类型 需要抽取的内容
UI 截图 布局模式、设计决策、关键元素、用途
图表(Chart) 指标、趋势/洞察、数据来源
推文/帖子 主张(claim)作为节点、作者、提及的概念
架构图(Diagram) 组件及其连接
科研插图(Research figure) 它演示了什么、方法、结果
手写/白板 想法与箭头连线;读不准的内容标记为 AMBIGUOUS

值得一提:管线对图片的处理不止于这份提示词。llm.py 定义了 file_type:"image" 节点的产出路径,限制单图 ≤5 MB、每块 ≤20 张图,并对无法读取或超限的图片降级为“仅文本引用节点”,保证图片至少成为一个图谱节点。

深度模式 DEEP_MODE 与语义相似边

当用户以 --mode deep 运行时,宿主会把 DEEP_MODE=true 传给每个子代理(见 skill-trae.md 中“Before starting”一段)。此时规范鼓励子代理更激进地输出 INFERRED 边——间接依赖、共享假设、潜在耦合——拿不准就标 AMBIGUOUS 而不是省略。注意库内 API 路径(llm.py)的 deep 后缀表述更克制:仅对“具体架构信号”补充 INFERRED 边,避免宽泛的概念相似边。

semantically_similar_to 边

规范允许一种特殊的跨结构关系:若同一分块中两个概念解决同一个问题、代表同一个想法,却没有结构性链接(无 import、无调用、无引用),就添加一条 semantically_similar_to 边,标记为 INFERRED,confidence_score 落在 0.6~0.95,反映相似度。给出的三条判例:

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

同时明确只在相似度真正“非显然、横切面”时才加,禁止为琐碎相似的东西滥加。该边在图谱分析中承担特殊角色:analyze.py 在做“surprising connections(意外连接)”统计时专门保留它并排除其他桥接边,因为它是跨越社区边界的真洞察来源。

超边:三节点以上的群组关系

当 3 个及以上节点明显共同参与一个“两两成对的边无法覆盖”的共享概念、流程或模式时,子代理应在顶层 hyperedges 数组里补一条超边。文档给出的范例:

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

relation 限定为 participate_inimplementform。规范两次强调“审慎使用”且每块最多 3 条——只有当群组关系比两两边的信息量更多时才值得建。入库管线对超边也做了容错归一:成员键统一为 nodes,兼容 members/node_ids 别名,并把“对象型成员”收敛成裸 ID(build.py)。

两条写入铁律:source_file 原样复制与 CHUNK_PATH 绝对路径

source_file RULE

每个 node、edge、hyperedge 的 source_file 都必须设置为来源文件在 FILE_LIST 中出现的原样路径——逐字符、绝对路径复制:不缩短成 basename、不重新相对化、不删目录前缀、不改路径分隔符。规范解释了动机:引擎在下游负责分隔符规范化并相对化到构建根目录。只有两边基准一致,“全量构建与增量 --update 落在同一基线上”,build_merge 的“重抽取即替换”逻辑才能匹配到既有节点,而不是堆积出重复节点。

CHUNK_PATH 绝对路径

子代理必须用 Write 工具把 JSON 写到确定的绝对路径 CHUNK_PATH。文档特别解释:不要用相对路径——Write 工具会相对一个未定义的 cwd 解析相对路径,文件会被静默丢失。

与语义缓存、校验、清理链路的关系

这份提示词还是语义缓存指纹的一部分:cache.pycheck_semantic_cache 接受 prompt/prompt_file 参数,缓存命中被限定为“由同一提示词版本产出的条目”——这正是把 SPEC_PATH 同时传给 Step B0 与 Step B3 的原因:当 graphify 升级导致抽取提示词变化时,旧提示词产出的缓存条目会被重新抽取而不是被原样回放;提示词未变则继续命中缓存,避免重复计费。

此外,库内 llm.py_EXTRACTION_SYSTEM 是这份规范在“非子代理环境”下的直接 API 等价物(对应 graphify extract . --backend gemini 等路径),Schema、证据分级、ID 规则、方向规则均保持一致;deep 模式后缀同样存在。这意味着无论走“宿主 Agent 派发子代理”还是“直连 API”哪条路径,产出的图结构都是同一协议、可合并的。

小结:一份“写给 LLM 看、却由代码背书”的协议

从结构上看,extraction-spec.md 的每一段约束都能在仓库源码里找到对应的校验或实现:

理解这份协议的价值在于:它把“图谱质量”问题前置成了可执行、可校验、可测试的提示词约束。无论你是在 Claude Code、Trae、Codex、Gemini CLI 等宿主中按 graphify/skill-trae.md 的 Step 3 Part B 派发子代理,还是在自有管线中直连 LLM,遵循这套 Schema、置信度准则与确定性 ID 规范,就能让语义抽取产物与确定性 AST 产物在同一张图上无缝合并,并支撑后续的 --update 增量重建、去重与合并,最终得到一份边边可溯源、节点条条确定的知识图谱。

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