graphify 语义抽取子代理规范全解:把文档、论文与图片语料转成可合并的知识图谱 JSON
导读:graphify 在“代码 → 知识图谱”之外,还有一条面向文档、论文、图片等非代码语料的语义抽取通道,而这条通道的全部行为都收敛在 extraction-spec.md 这一份“子代理 Prompt 规范”里。本文以该规范为骨架,逐条解析三类置信度、六种 file_type、离散可信度刻度、确定性 Node ID 契约、超边(hyperedges)、图片视觉规则与 source_file 逐字规则,并结合 validate.py、semantic_cleanup.py、skill.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 边有两条硬性纪律:
- 方向不可反:
source必须是调用方(发起调用的函数/类),target必须是 callee; - 语言内封闭:一条 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_type 为 rationale/concept 且参与 rationale_for 边、标签又呈“句子状”的节点,把它们合并为父节点上的句子级 rationale 数据,并保证只有 rationale_for 边携带 rationale 文本、其他出边不传播句子内容。也就是说,Prompt 层“别建 rationale 节点”的纪律,在数据清洗层还有兜底验证。
4.3 file_type 六值枚举:其他值一律非法
file_type 被约束为恰好六个值之一,任何其他值都会被拒绝:code、document、paper、image、rationale、concept。
源码侧可精确印证这一约束: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_url、captured_at、author、contributor 复制到来自该文件的每一个节点上。这保证了来源元数据在图谱里逐节点可追溯。
八、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_url、captured_at、author、contributor 默认为 null,由 YAML frontmatter 规则填充。
edges 允许的 relation 共 8 种:calls、implements、references、cites、conceptually_related_to、shares_data_with、semantically_similar_to、rationale_for。注意 confidence 是 EXTRACTED/INFERRED/AMBIGUOUS 三值字符串,而 confidence_score 是数值,两者共同构成证据体系。
hyperedges 的 relation 枚举为 participate_in、implement、form,示例默认分值为 0.75。
顶层还有 input_tokens 与 output_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 判断子代理成功与否的唯一信号。
十三、易错点速查清单
- 输出格式:只输出裸 JSON,不要 markdown 围栏、解释、前言。
file_type:必须命中六值枚举,任何其他值都会被 validate.py 拒绝。- rationale:存为属性或概念节点的
file_type:"rationale",禁止建独立 rationale 节点;句子状 rationale 节点会在 semantic_cleanup.py 被吸收为父节点属性。 calls方向与语言:source 恒为调用方;跨语言calls是 phantom artifact,禁止生成。- ID 确定性:必须用“全路径段 + 符号”拼 ID,禁后缀、禁
_c1式编号,否则产生幽灵重复节点。 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)。- 超边节制:每 chunk 至多 3 条,仅当超过 pairwise 表达力才建。
source_file:逐字复制 FILE_LIST 条目,verbatim absolute,这是增量替换命中的前提。- 落盘:绝对路径写
CHUNK_PATH,用带写权限的 general-purpose 子代理。
十四、把规范放进 graphify 运行全景
理解这份规范的最佳方式是把它的生命周期与 graphify 的运行流水线对应起来。以 Claude 平台 skill 为例,规范文件与 SKILL.md 同目录部署,运行路径为:
- 检测阶段产出语料分类(code/document/paper/image),参考
graphify-out/.graphify_detect.json; - 若存在非代码语料,进入 Part B;
- Step B0 以本规范路径(SPEC_PATH)为缓存归因键调用 check_semantic_cache;
- Step B1~B2 分块并以
general-purpose子代理并行执行本规范; - 子代理按上述规则产出 chunk JSON 落到绝对路径;
- Part C 合并所有 chunk 为
.graphify_semantic.json; - validate.py 在构建校验阶段执行六值
file_type与必填字段的最终把关,semantic_cleanup.py 随后完成 rationale/概念节点的句子化清洗。
由此可以看到设计闭环:Prompt 层用离散刻度和确定性 ID 约束模型输出,数据层用枚举校验和清洗兜底,缓存与 source_file 规则保证增量可复现——三者共同把“LLM 自由文本抽取”这一天然发散的过程,牢牢锚定在 graphify 可合并、可增量、可验证的图数据结构上。若要在自己的 Agent 流程中复刻这一模式,本规范本身就是一份可直接照抄的、经过工程打磨的子代理契约模板。
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 StartedRust0627
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