深入 graphify extraction-spec 语义抽取子代理提示词:节点 ID 契约、置信度离散刻度与 JSON Schema
graphify 的 /graphify 流水线把"代码用确定性 AST 抽取、文档用 LLM 语义抽取"分成两条轨道,而 extraction-spec.md 正是语义轨道的核心契约:它是分发给每一个语义抽取子代理(subagent)的逐字提示词模板,定义了子代理"读哪些文件、抽什么关系、如何给节点命名、按什么刻度标注置信度、最终以什么 JSON Schema 落盘"。本文以该文档为骨架完整拆解其规则体系,并结合仓库中真正执行契约的校验器、ID 生成函数与防漂移测试,说明这些看似"提示词约定"的规则如何成为保证图完整性(不产生孤儿重复节点、不丢失增量更新对齐)的工程约束。
一、extraction-spec.md 在 /graphify 流水线中的位置
该文档开头即声明了加载时机:只有当语料中至少存在一个 doc、paper 或 image 分块时,才在 Step 3 Part B 加载它;纯代码语料跳过 Part B,永远不会读取此文件。每个语义子代理收到的提示词都是该模板的逐字替换版本,需要替换的占位符有五个:
| 占位符 | 含义 |
|---|---|
FILE_LIST |
该子代理负责的本次分块文件清单(逐字、绝对路径) |
CHUNK_NUM |
当前分块序号 |
TOTAL_CHUNKS |
分块总数 |
DEEP_MODE |
是否以 --mode deep 运行 |
CHUNK_PATH |
子代理必须把结果 JSON 写到的精确绝对路径 |
在宿主技能文件 skill-opencode.md 中可以看到它的完整消费链路:Step B0 先做抽取缓存检查,Step B1 把未命中缓存的文件切成每块 20–25 个文件的分块(每张图片单独成块,因为视觉理解需要独立上下文;同目录文件尽量聚在同一块以便抽取跨文件关系),Step B2 在单条消息里派发全部子代理(OpenCode 平台使用 @mention 派发,同一消息中的所有 mention 并行执行),Step B3 收集、写缓存并合并。技能文件明确写道:
关于确切的子代理提示词(JSON schema、节点 ID 规则、置信度刻度、超边与视觉规则),见
references/extraction-spec.md。仅在此处加载,且仅当至少一个分块包含 doc、paper 或 image 时加载。
也就是说,extraction-spec.md 不是给人阅读的说明文档,而是机器间协议的规范文本——它同时约束 LLM 子代理的输出行为和下游合并器(build_merge)的匹配行为。
二、三层置信度体系:EXTRACTED / INFERRED / AMBIGUOUS
规范为每条边定义了三个置信度等级,这是 graphify"诚实审计(honest audit trail)"设计在抽取层的直接体现:
- EXTRACTED:关系在源码中是显式的(import、call、引用、"see §3.2" 这类文本指针);
- INFERRED:合理推断(共享数据结构、隐含依赖);
- AMBIGUOUS:不确定——必须标记出来供人工审查,不允许直接省略。
这三档在仓库的 schema 校验器 validate.py 中被硬编码为常量 VALID_CONFIDENCES = {"EXTRACTED", "INFERRED", "AMBIGUOUS"},validate_extraction() 会对每条边做合法性检查,非法值会进入错误列表并最终由 assert_valid() 抛出异常。因此子代理输出的不是"自由文本",而是必须通过机器校验的结构。
2.1 confidence_score 的离散刻度
规范对 confidence_score 的要求非常严格:每条边必须携带,禁止省略,禁止把 0.5 当默认值,且各等级取值如下:
| 置信等级 | 允许的取值 | 语义 |
|---|---|---|
| EXTRACTED | 恒为 1.0 |
源码中显式存在的关系 |
| INFERRED | 五值之一:0.95 / 0.85 / 0.75 / 0.65 / 0.55,永不取 0.5 |
见下表 |
| AMBIGUOUS | 0.1–0.3 |
不确定,标记待审 |
INFERRED 五档的判定基准(原样继承自规范文本):
0.95:直接结构性证据(共享数据结构、跨文件命名引用);0.85:强推断(功能对齐清晰,但没有直接的符号级链接);0.75:合理推断(共享问题域 + 相似形状,需要解释);0.65:弱推断(主题相关,但没有形状证据);0.55:投机但可信(仅有表层共现)。
规范还给出了一条来自生产经验的元规则:模型对离散刻度的遵循度高于连续区间——生产环境观察到双峰分布(>50% 的边坍缩到 0.5,>40% 到 0.85+),说明区间式引导会被模型坍缩成二分法;因此这里强制五档离散值。若上述档位都不贴合,应把边标记为 AMBIGUOUS,而不是选 0.4 或更低的值。仓库中 test_inferred_confidence_rubric.py 等测试正是围绕这一刻度契约建立的回归保护(tests/test_inferred_confidence_rubric.py)。
三、节点与 file_type:六值封闭枚举
规范约束 file_type 必须且只能是以下六个值之一,其他任何值无效并会被拒绝:
code、document、paper、image、rationale、concept
这与 validate.py 中 VALID_FILE_TYPES = {"code", "document", "paper", "image", "rationale", "concept"} 完全一致——规范文本与校验代码是同一契约的两面。
针对文档/论文文件,规范给出两条关键纪律:
- rationale(WHY:决策原因、权衡、设计意图)不作为独立节点存储,而是作为
rationale属性挂在相关概念节点上。不得为 rationale 单独创建节点或 fragment 节点;只有本身构成命名实体或概念的东西才有资格成为节点。 - 概念性节点(思想、原则、机制、设计模式)使用
file_type:"rationale"或concept。
四、按文件类型分轨的抽取规则
规范对不同文件类型给出差异化的关注点,核心思想是语义抽取只补 AST 到不了的边:
4.1 代码文件
- 聚焦 AST 找不到的语义边(调用关系、共享数据、架构模式);
- 不要重复抽取 import——AST 已经拿走了这些边,重复抽取会造成节点/边污染。
calls 边有两条硬性方向约束:
- source 必须是调用方(发起调用的函数/类),target 必须是被调方,方向永不反转;
calls边必须停留在同一种语言内部:Python 函数不能callsJS/TS/Go/Rust/Java 符号,反之亦然。跨语言调用边是"幽灵产物(phantom artifacts)",永远不要输出。
4.2 图片文件:用视觉理解"图是什么",而非 OCR
规范要求对图片使用视觉能力理解图片本身是什么:
- UI 截图:布局模式、设计决策、关键元素、用途;
- 图表:指标、趋势/洞察、数据来源;
- 推文/帖子:主张作为节点,加上作者、被提及的概念;
- 示意图(diagram):组件与连接;
- 科研图(research figure):证明了什么、方法、结果;
- 手写/白板:想法与箭头,读不确定的内容标记 AMBIGUOUS。
4.3 DEEP_MODE 与语义相似边
当构建时给出了 --mode deep(模板中占位为 DEEP_MODE),子代理应激进地产出 INFERRED 边——间接依赖、共享假设、潜在耦合;拿不准的标记 AMBIGUOUS 而不是省略。
此外,规范定义了一类特殊边 semantically_similar_to:当同一分块内两个概念没有任何结构链接(无 import、无 call、无引用)却解决同一问题或表达同一思想时,添加该边并标为 INFERRED,confidence_score 落在 0.6–0.95 区间反映相似度。规范给出的三类示例:
- 两个都校验用户输入但互不调用的函数;
- 代码中的一个类与论文中一个概念描述同一算法;
- 两个处理同一失败模式但方式不同的错误类型。
约束是:只在相似性"真正非显然且跨切面"时添加,平凡相似不加分。
4.4 超边(Hyperedges)
当 3 个或更多节点共同参与一个仅靠成对边无法表达的共享概念、流程或模式时,向顶层 hyperedges 数组添加超边。规范给出的示例:
- 实现同一协议/接口的所有类;
- 认证流程中的全部函数(即使它们并非互相都调用);
- 论文某节中共同构成一个连贯思想的全部概念。
使用纪律:节制使用——只有当群组关系提供了成对边之外的信息时才加;每个分块最多 3 条超边。
4.5 YAML frontmatter 透传
如果文件带有 YAML frontmatter(--- ... ---),要把 source_url、captured_at、author、contributor 四个字段复制到该文件产出的每个节点上——这是 /graphify add 抓取的 URL 语料保留来源元数据的基础。
五、节点 ID 格式:与 AST 抽取器对齐的硬契约
这是整份规范中工程后果最重的部分。ID 规则原文要点:
- 小写,仅允许
[a-z0-9_],无点号、无斜杠; - 格式为
{stem}_{entity},其中 stem 是去掉扩展名的完整仓库相对路径,保留所有路径层级、用_连接(每段小写,非字母数字字符替换为_),entity 是同样方式归一化的符号名; - 使用每一级目录,而不只是直接父目录——这让不同目录下同名文件互不冲突;
- 顶层文件(如
setup.py,无父目录)直接用文件名字干:setup_my_func; - 禁止在 ID 后追加分块号、序号或任何后缀(不允许
_c1、_c2、_chunk2之类);ID 必须仅由标签确定性推出——同一实体无论落在哪个分块处理,都必须生成同一 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
为什么必须"用全路径"?规范警告:只用文件名(如 session_validatetoken)或只用直接父目录(如 auth_session_validatetoken)会制造孤儿幽灵重复节点。这与 AST 侧的实现一一对应:extractors/base.py 中的 _file_stem() 明确注释"使用所有段——而不只是直接父目录(#1504)——意味着不同目录下的同名文件获得不同 ID,而不是坍缩成一个 last-writer-wins 节点",并给出 docs/v1/api/README.md -> docs_v1_api_readme 的同一组例子;_make_id() 再把分隔符折叠成下划线。也就是说,LLM 子代理(语义轨)和确定性 AST 抽取器(结构轨)共同遵守同一 ID 算法,两条轨道产出的节点才能在 Part C 合并时按 ID 精确去重(AST 节点优先,语义节点按 ID 去重追加)。
规范还提示:如果项目是按旧的"直接父目录"格式构建的,应运行 graphify extract --force 干净重建。
六、规范文本与代码的双向防漂移
一个值得注意的仓库设计:规范文本是人工维护的提示词,而它给出的 ID 示例是 LLM 的"地面真值"——一旦示例与代码漂移,同一符号会被两条轨道生成不同 ID,图就会分裂。仓库用一个专门的测试把这份规范本身锁进了测试套件:tests/test_extraction_spec_ids.py。
该测试的行为:
- 扫描
graphify/skills/与tools/skillgen/fragments/下每一份 shipped 的extraction-spec.md(OpenCode 版只是其中一份;skillgen 从共享片段渲染出各宿主平台的副本); - 用正则解析文中所有形如
`path` + `entity` → `id`的示例; - 对每个示例调用生产环境真实函数
_make_id(_file_stem(path), entity),断言输出与示例完全一致; test_cautionary_wrong_forms_are_actually_wrong进一步把"反面示例"也锁死:断言文件名-only 与直接父目录-only 两种 ID 形态确实不等于正确形态。
它的失败条件是双向的:规范示例被改成错误值,或 ID 函数被改动导致文档示例不再成立,测试都会挂掉。换句话说,本文引用的全部示例不是"文档装饰",而是 CI 中可执行的契约。
七、source_file 逐字规则与 CHUNK_PATH 落盘规则
规范对 source_file 字段(出现在每个 node、edge、hyperedge 上)给出了一条"逐字符复制"规则:
source_file必须设置为来源文件在FILE_LIST中原样出现的路径——逐字、绝对路径;- 不得缩短为 basename、不得重新相对化、不得剥离任何目录前缀、不得更换分隔符;
- 引擎在下游统一做分隔符归一化、相对构建根(build root)的相对化。
原文给出的动机:保持"完整构建"与"增量 --update" 在同一基准上,这样 build_merge 的 replace-on-re-extract 才能命中已有节点并替换,而不是累积一个重复节点。配合宿主技能中 Step 4 的注释(root= 使 source_file 相对化到与 --update runbook 相同的基准,保证全量构建与增量更新不会在重抽取时漂移)可以确认:这条提示词规则的落点就是增量更新的节点匹配键。
落盘规则同样具体:子代理必须用 Write 工具把 JSON 写到 CHUNK_PATH 指定的精确绝对路径——因为 Write 工具对相对路径按未定义的 cwd 解析,文件会被静默丢失。宿主技能 Step B3 的验收信号也与之呼应:检查 .graphify_chunk_NN.json 是否存在于磁盘上,存在且含有效 nodes/edges 才计入;缺失则提示"子代理可能以只读方式派发,请改用 general-purpose agent 重跑",不静默跳过;超过一半分块失败则停止并要求用户重跑。
八、输出 JSON Schema:逐字段解读
规范第 63–64 行给出了必须严格匹配的输出 Schema(原文示例,缩进整理):
{
"nodes": [{
"id": "auth_session_validatetoken",
"label": "Human Readable Name",
"file_type": "code|document|paper|image|rationale|concept",
"source_file": "<FILE_LIST 中的路径,逐字>",
"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 中的路径,逐字>",
"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 中的路径,逐字>"
}],
"input_tokens": 0,
"output_tokens": 0
}
要点归纳:
- 顶层四个键:
nodes、edges、hyperedges三个数组,加上input_tokens/output_tokens两个计量字段(子代理输出占位零值,宿主在 Step B3 从 Agent 工具的usage字段读回真实 token 数并写回,再合并各块求和); - 节点必填:
id、label、file_type、source_file(与 validate.py 中REQUIRED_NODE_FIELDS一致),可选的来源追溯字段source_location/source_url/captured_at/author/contributor用于 frontmatter 透传与行级引用; - 边必填:
source、target、relation、confidence、source_file(对应REQUIRED_EDGE_FIELDS),relation是一个 8 值封闭枚举,边两端必须命中已声明的 node id(校验器会检查悬空端点); - 超边只允许
EXTRACTED/INFERRED两档置信度(不允许 AMBIGUOUS),relation 为 3 值枚举; - 输出纪律:只输出合法 JSON,无解释、无 markdown 围栏、无开场白。
九、规范如何与缓存机制联动(SPEC_PATH)
宿主技能 skill-opencode.md 的 Step B0 中有一处容易被忽略的细节:语义缓存的读写都要传入同一个 SPEC_PATH 参数——即这份 references/extraction-spec.md 的绝对路径:
cached_nodes, cached_edges, cached_hyperedges, uncached = check_semantic_cache(
all_files, root='INPUT_PATH', prompt_file='SPEC_PATH')
其语义是:缓存条目归属于产生它的那份提示词。当 graphify 升级修改了 extraction-spec 提示词,旧提示词产出的缓存条目会因 prompt_file 指纹不匹配而被判为失效、重新抽取;提示词未变时则直接回放缓存。同理 Step B3 的 save_semantic_cache(..., prompt_file='SPEC_PATH') 写入时必须用同一 SPEC_PATH——"读用一个提示词、写用另一个提示词"的条目会落进下一次运行查不到的命名空间。这让 graphify/cache.py 中的 check_semantic_cache/save_semantic_cache 与规范文本之间形成了一个可追溯的闭环:规范即缓存版本号。
十、小结:一份提示词为何要写成工程契约
回看整份 extraction-spec.md,它的每条规则都对应一个下游工程问题:
| 规则 | 防住的故障模式 |
|---|---|
| 三层置信度 + 离散刻度 | 模型把不确定边伪装成事实边;连续区间被坍缩成二分 |
calls 方向与单语言约束 |
反向边、跨语言幽灵边污染调用图 |
| rationale 作为节点属性 | 图被解释性碎片淹没、社区检测失真 |
| 全路径节点 ID + 禁后缀 | 两条轨道 ID 不一致 → 孤儿重复节点;同实体跨分块 ID 漂移 |
| source_file 逐字复制 | 全量构建与 --update 增量基准漂移 → replace 失配、重复累积 |
| 精确绝对 CHUNK_PATH | 相对路径 Write 落盘到错误 cwd,结果静默丢失 |
| 六值 file_type / 8 值 relation 封闭枚举 | 非法输出在 validate.py 处被机器拒绝而非静默混入 |
| prompt_file 缓存指纹 | 提示词升级后旧缓存被误回放 |
对使用者而言,这份文件的实用价值在于:如果你在自研"LLM 抽取 + 确定性解析"混合的知识图谱流水线,extraction-spec.md 提供了一个可复制的完整样例——用封闭枚举约束词汇表、用离散刻度约束数值、用与解析器共享的 ID 算法约束身份、用"规范文本进测试"的方式约束文档漂移。在 graphify 仓库内部,它与 skill-opencode.md 的 Step B 流程、extractors/base.py 的 ID 函数、validate.py 的 schema 校验、tests/test_extraction_spec_ids.py 的防漂移测试共同构成了一条从提示词到磁盘产物的可验证链路。
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