深入 graphify extraction-spec 语义抽取子代理提示词:节点 ID 契约、置信度离散刻度与 JSON Schema
2026/9/7 4:48:50 网站建设 项目流程

深入 graphify extraction-spec 语义抽取子代理提示词:节点 ID 契约、置信度离散刻度与 JSON Schema

【免费下载链接】graphifyTurn any codebase, with its docs, SQL schemas, configs, and PDFs, into a queryable knowledge graph. A /graphify skill for Claude Code, Cursor, Codex, and Gemini CLI: local deterministic AST parsing, every edge explained, no vector store.项目地址: https://gitcode.com/GitHub_Trending/graph/graphify

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见下表
AMBIGUOUS0.10.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必须且只能是以下六个值之一,其他任何值无效并会被拒绝:

codedocumentpaperimagerationaleconcept

这与 validate.py 中VALID_FILE_TYPES = {"code", "document", "paper", "image", "rationale", "concept"}完全一致——规范文本与校验代码是同一契约的两面。

针对文档/论文文件,规范给出两条关键纪律:

  1. rationale(WHY:决策原因、权衡、设计意图)不作为独立节点存储,而是作为rationale属性挂在相关概念节点上。不得为 rationale 单独创建节点或 fragment 节点;只有本身构成命名实体或概念的东西才有资格成为节点。
  2. 概念性节点(思想、原则、机制、设计模式)使用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_urlcaptured_atauthorcontributor四个字段复制到该文件产出的每个节点上——这是/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。

该测试的行为:

  1. 扫描graphify/skills/tools/skillgen/fragments/每一份shipped 的extraction-spec.md(OpenCode 版只是其中一份;skillgen 从共享片段渲染出各宿主平台的副本);
  2. 用正则解析文中所有形如`path` + `entity` → `id`的示例;
  3. 对每个示例调用生产环境真实函数_make_id(_file_stem(path), entity),断言输出与示例完全一致;
  4. 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相对化到与--updaterunbook 相同的基准,保证全量构建与增量更新不会在重抽取时漂移)可以确认:这条提示词规则的落点就是增量更新的节点匹配键。

落盘规则同样具体:子代理必须用 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 }

要点归纳:

  • 顶层四个键nodesedgeshyperedges三个数组,加上input_tokens/output_tokens两个计量字段(子代理输出占位零值,宿主在 Step B3 从 Agent 工具的usage字段读回真实 token 数并写回,再合并各块求和);
  • 节点必填idlabelfile_typesource_file(与 validate.py 中REQUIRED_NODE_FIELDS一致),可选的来源追溯字段source_location/source_url/captured_at/author/contributor用于 frontmatter 透传与行级引用;
  • 边必填sourcetargetrelationconfidencesource_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 的防漂移测试共同构成了一条从提示词到磁盘产物的可验证链路。

【免费下载链接】graphifyTurn any codebase, with its docs, SQL schemas, configs, and PDFs, into a queryable knowledge graph. A /graphify skill for Claude Code, Cursor, Codex, and Gemini CLI: local deterministic AST parsing, every edge explained, no vector store.项目地址: https://gitcode.com/GitHub_Trending/graph/graphify

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询