graphify extraction-spec 规范解析:语义抽取子代理的知识图谱 JSON 抽取协议
graphify 通过两条路径构建知识图谱:确定性 AST 结构抽取,以及面向文档、论文、图片的语义抽取。本文聚焦后者——托管在 graphify/skills/trae/references/extraction-spec.md(以及多平台 SKILL 的相同副本)中的 extraction-spec 是一份会原样下发给每个语义抽取子代理的“提示词规范”,它同时定义了证据分级、置信度打分、节点/边/超边的 JSON Schema、节点 ID 生成规则等全套约束。读完本文,你将掌握该子代理协议的设计动机、每一条规则的落地校验方式(validate.py、build.py、semantic_cleanup.py 等源码佐证),并能在自己的 AI Agent 工作流里正确接入这套抽取协议。
这份文档是什么:所有宿主 SKILL 共用的“抽取子代理提示词”
extraction-spec.md 不是普通说明文档,而是一份会被逐字加载并注入子代理任务描述的提示词正文。以 Trae 平台的宿主 SKILL 为例:
- 它在 Step 3 Part B 被加载。触发条件是语料中至少存在一个 doc、paper 或 image 分块;纯代码语料直接跳过 Part B,永不读取本文件(代码由 Part A 的 AST 引擎负责)。
- 文档中的
FILE_LIST、CHUNK_NUM、TOTAL_CHUNKS、DEEP_MODE、CHUNK_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:每个实体对应一个节点,字段包括
id、label(人类可读名)、file_type(六选一)、source_file(FILE_LIST 中原样路径)、可空的source_location/source_url/captured_at/author/contributor; - edges:有向关系,字段为
source、target、relation、confidence、confidence_score、source_file、可空的source_location、固定 1.0 的weight; - hyperedges:三个以上节点共同参与的群组关系,含
id、label、nodes成员列表、relation、confidence、confidence_score、source_file; - 顶层另有
input_tokens、output_tokens两个令牌计数占位。
任何键缺失、类型错误或字段枚举越界都会在入库环节被拦截:见 validate.py 中声明的 VALID_FILE_TYPES、VALID_CONFIDENCES、REQUIRED_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.1 – 0.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.py 对 rationale_for 边与“句子式标签”做了专门清洗:把挂在概念节点上的理由文本回填为属性、收拢过度拆分的碎片节点(semantic_cleanup.py)。
规范同时要求:如果文件带有 YAML frontmatter(--- ... ---),需把其中的 source_url、captured_at、author、contributor 复制到来自该文件的每一个节点上。
一个值得一提的工程细节是下游容错:虽然规范只接受六值,但 build.py 内置了一张 LLM 高频误写同义词表(markdown→document、text→document、tool→code、library→code、pattern/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 |
两条“红线”必须同时遵守:
- 与 AST 抽取器完全一致。规范明确警告:如果只用文件名(如
session_validatetoken)或只用直接父目录(如auth_session_validatetoken),就会造出与 AST 节点无法合并的孤儿幽灵重复节点。这并非空话——extract.py 中的_file_stem+_make_id就是 AST 侧的同款实现,而 tests/test_extraction_spec_ids.py 的职责正是解析每份 spec 文档里的→示例,断言真实生产函数能逐一复现,任何一侧漂移都会让测试失败。 - 绝不追加任何后缀。文档用 CRITICAL 强调:不得在 ID 后加
_c1、_c2、_chunk2之类的分块序号。ID 必须由符号本身确定性地推导——无论由哪个分块处理,同一个实体永远产出同一个 ID,这是增量--update与合并去重的前提。
若旧格式项目需要迁移(例如历史上用 immediate-parent 格式构建过的工程),规范给出修复手段:重新执行 graphify extract --force 做一次干净重建。字符级归一化背后的算法保证可见于 ids.py:normalize_id 以 NFKC + casefold 迭代到不动点、幂等且大小写无关;make_id(*parts) 将各部分剥掉首尾点/下划线后以 _ 连接。
边:方向、关系词表与跨语言禁区
calls 边方向与跨语言禁令
文档对代码 calls 边给出两条绝对规则:
- 方向不得反转:
source必须是调用方(发起调用的函数/类),target必须是被调用方; - 不得跨语言:一个 Python 函数不能
callsJS/TS/Go/Rust/Java 符号,反之亦然——规范直言跨语言调用边是“phantom artifacts”(幻影产物),永远不要输出。跨语言/跨仓库的合法关系应通过references、shares_data_with等边表达,其解析由 symbol_resolution.py、cross_repo_types.py 等模块负责。
关系词表
边上的 relation 是枚举值:calls、implements、references、cites、conceptually_related_to、shares_data_with、semantically_similar_to、rationale_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_in、implement、form。规范两次强调“审慎使用”且每块最多 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.py 的 check_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 的每一段约束都能在仓库源码里找到对应的校验或实现:
- 六类
file_type↔ validate.py 的VALID_FILE_TYPES与 build.py 的同义词归一; - 三类
confidence↔VALID_CONFIDENCES与离散打分准则; - 确定性节点 ID ↔ extract.py 的
_file_stem/_make_id、ids.py 的normalize_id/make_id,以及 test_extraction_spec_ids.py 的漂移守卫; rationale属性化 ↔ semantic_cleanup.py 的清洗与回填;- 提示词指纹 ↔ cache.py 的缓存键设计。
理解这份协议的价值在于:它把“图谱质量”问题前置成了可执行、可校验、可测试的提示词约束。无论你是在 Claude Code、Trae、Codex、Gemini CLI 等宿主中按 graphify/skill-trae.md 的 Step 3 Part B 派发子代理,还是在自有管线中直连 LLM,遵循这套 Schema、置信度准则与确定性 ID 规范,就能让语义抽取产物与确定性 AST 产物在同一张图上无缝合并,并支撑后续的 --update 增量重建、去重与合并,最终得到一份边边可溯源、节点条条确定的知识图谱。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00