graphify 在 Codex 平台上的并行语义抽取:spawn_agent 单消息派发全指南
graphify 的 /graphify 技能在其 v8 代码库中把"文档/论文/图片的语义抽取"拆分成了可供多平台复用的片段体系。tools/skillgen/fragments/dispatch/codex-agenttask.md 正是其中专为 OpenAI Codex 定制的"Step B2 —— 单条消息内派发全部子代理"执行片段:Codex 不使用 Claude Code 式的 Agent 工具与磁盘 chunk 文件,而是以 spawn_agent + wait_agent + close_agent 三件套实现全并行语义抽取,并把结果在内存中汇总。读完本文,你将掌握:如何开启 Codex 的 multi_agent 特性、如何按 chunk 构造带任务委托框架的子代理提示词、为什么 Codex 路径没有逐 chunk 落盘与磁盘成功检查、以及在纯代码语料(corpus)下为何整个 Part B 与抽取规范(extraction-spec)都永远不会被读取。
一段代码片段在整个 skill 工程中的位置
graphify 的 Skill 文案不是手写的单一大文件,而是"片段(fragment)为唯一编辑源、产物(artifact)为生成结果"的构建式工程。核心模板 fragments/core/core.md 中有一个 @@DISPATCH@@ 占位符,它位于 Step 3 Part B 的"Step B1 - 分块"与"Step B3 - 汇总、缓存与合并"之间,专门承载"如何把语义子代理真正派发出去"这一步的平台差异。
生成器 gen.py 在渲染平台产物时按 platforms.toml 中每个 [platform.<key>] 声明的 dispatch 字段,把对应片段替换进该占位符。例如:
[platform.codex]→dispatch = "codex-agenttask",即读取 codex-agenttask.md;[platform.claude]→dispatch = "agent-tool-disk",使用 agent-tool-disk.md;[platform.trae]→dispatch = "task-tool-disk-trae",另有 manual-paste.md、opencode-mention.md 等。
把七个 dispatch 片段并排看(它们同目录于 fragments/dispatch/),就得到一份"各 AI 宿主如何并行抽取"的平台对照表:Claude 靠 Agent 工具写磁盘、Trae/Amp 靠 Task 工具、VS Code 靠手动粘贴、OpenCode 靠 @ 提及,而 Codex 靠本文的主角——原生子代理句柄 API。该片段的最终渲染产物可见 graphify/skill-codex.md 中 Step B2 整段,仓库还通过 tools/skillgen/expected/graphify__skill-codex.md 的快照对渲染结果做字节级防漂移校验。
Codex 与原版 Agent 工具的本质差异
阅读本片段前先建立对照。Claude 风格平台的派发协议(agent-tool-disk.md)依赖两件事:一是在同一响应里多次调用 Agent 工具,让宿主把它们并行调度;二是子代理把结果写入磁盘上每个 chunk 对应的 .graphify_chunk_0N.json 绝对路径,主代理之后以"文件是否存在"作为成功信号。
Codex 提供的是另一套更底层的句柄式 API。本片段第一段引用块就明确给出了三条铁律:
Codex platform: Uses
spawn_agent+wait_agent+close_agentinstead of the Agent tool. Requiresmulti_agent = trueunder[features]in~/.codex/config.toml. Ifspawn_agentis unavailable, tell the user to add that config and restart Codex.
要点可归纳为:
- 工具不同:用
spawn_agent(生成子代理)替代 Agent 工具; - 配置前置:必须在
~/.codex/config.toml的[features]段开启multi_agent = true; - 故障定位前置:若
spawn_agent不可用,不要自行降级,而是明确告知用户补充该配置并重启 Codex 后重试。
全并行派发:同一响应内 spawn 全部 chunk
片段正文给出的操作纪律与 Agent 工具版一脉相承,但落地方式不同——把一次 spawn_agent 视为一个 chunk 的"派出"动作,全部放在同一条响应里发出:
spawn_agent(agent_type="worker", message="Your task is to perform the following. Follow the instructions below exactly.\n\n<agent-instructions>\n[extraction prompt, with FILE_LIST, CHUNK_NUM, TOTAL_CHUNKS, DEEP_MODE substituted]\n</agent-instructions>\n\nExecute this now. Output ONLY the structured JSON response.")
逐项拆解这个模板:
| 要素 | 含义 |
|---|---|
agent_type="worker" |
声明这是执行型 worker,而非只读探查型代理 |
message 头尾框架 |
"Your task is to perform the following. Follow the instructions below exactly." + 收尾 "Execute this now. Output ONLY the structured JSON response.",是任务委托(task-delegation)包裹层 |
<agent-instructions> 内层 |
被逐字传入的抽取提示词本体,即把抽取规范中需要按批次实例化的四个变量替换后的版本 |
| 四个替换变量 | FILE_LIST(本 chunk 文件清单)、CHUNK_NUM(第几块)、TOTAL_CHUNKS(总块数)、DEEP_MODE(是否 --mode deep) |
| 输出约束 | 只输出结构化 JSON,杜绝解释性文字 |
与主技能"Step 3"入口的约定呼应:--mode deep 一旦在原始调用中出现,就必须转换成 DEEP_MODE=true 传递给每一个子代理,且整个流程不能丢失该标记(core.md Step 3 开头有专门提醒)。
为什么要强调"同一响应"?片段的潜在逻辑是:只有在同一响应中发起全部 spawn_agent,Codex 运行时才会把它们放进同一个并行批次;若发一个等一个,就退化成了串行,使分块并行失去意义——这与 agent-tool-disk.md 中"If you make one Agent call, wait, then make another, you are doing it sequentially and defeating the purpose"的表达完全同构,只是换成了 spawn 句法。
内存收集:wait_agent + close_agent 顺序回收
所有子代理被派出后,主代理进入收集阶段,同样是一行模式,对每个句柄重复执行:
result = wait_agent(handle); close_agent(handle) # repeat per handle
wait_agent(handle):阻塞等待对应子代理产出结果,取回result;close_agent(handle):用完即关,释放句柄资源;- 每个句柄顺序处理,随后把各结果中的
nodes/edges/hyperedges跨结果累加,汇总写入graphify-out/.graphify_semantic_new.json。
这里有一个本片段反复强调、且最容易踩坑的平台架构性差异:
Codex collects in memory, so there are no per-chunk files on disk; the disk-based success checks in Step B3 do not apply — a chunk that returns invalid JSON is the failure signal instead.
翻译成实操语言就是三条推论:
- 不要期待
.graphify_chunk_NN.json出现。那是 Agent 工具版(Claude/Windows 等)的产物;Codex 版子代理把 JSON 直接作为返回值带回,磁盘上自始至终没有中间 chunk 文件; - "成功信号"的定义变了。Step B3 原文中"检查文件在磁盘上存在"的成功判据在 Codex 路径不适用,Codex 以"能否把
wait_agent的返回解析为合法 JSON"作为成败分界; - 失败处理就落在 JSON 解析上——某个 chunk 返回非法 JSON 即为该块失败的信号,应参照 Step B3 的通用策略警告并跳过该块,而不是中止整个流程。
这一设计还牵动下游的缓存与清单逻辑:既然没有磁盘 chunk 文件,后续 .graphify_cached.json/.graphify_uncached.txt/缓存写入/清单盖章(stamping)都以 .graphify_semantic_new.json 为唯一数据来源,因此该文件的字段结构(含 input_tokens/output_tokens 占位零值)必须与 Part C 合并代码期望的 schema 完全一致。
抽取规范只在需要时才加载
片段的最后一段回答了"子代理的提示词正文从哪来、何时读":
See
references/extraction-spec.mdfor the compact subagent prompt (rules, node-ID format, confidence rubric, hyperedge and vision rules, JSON schema). Load it only here, only when at least one chunk holds a doc, paper, or image; a pure-code corpus has skipped Part B and never reads it. Pass each agent that prompt verbatim with FILE_LIST, CHUNK_NUM, TOTAL_CHUNKS, and DEEP_MODE substituted, and have it return the JSON inline.
约束分三层:
- 路径:提示词正文由与技能同目录的 graphify/skills/codex/references/extraction-spec.md 承载(其源片段在 tools/skillgen/fragments/references/shared/extraction-spec-compact.md,Codex 平台在 platforms.toml 中声明了
extraction = "compact",取精简版); - 时机:仅在至少一个 chunk 包含文档(doc)、论文(paper)或图片(image)时才在此处读取;纯代码语料已整段跳过 Part B,永远不需要读它,以省 token;
- 传递方式:把提示词**逐字(verbatim)**喂给每个子代理,仅替换四个批次变量,并要求子代理把 JSON 内联(inline)返回,不写文件。
精简版抽取规范里到底约束了什么
为了让读者理解传给 Codex worker 的这段"紧凑指令"分量几何,下面按 extraction-spec-compact.md 原文归纳其核心契约(Codex 渲染版与此字节一致):
置信度三段式(confidence rubric)
EXTRACTED:关系在源中显式存在(import、call、citation),confidence_score 恒为1.0;INFERRED:合理推断(共享结构、隐含依赖),score 必须且只能从0.95 / 0.85 / 0.75 / 0.65 / 0.55五档中选一,严禁用 0.5 作默认值;都不贴切就转AMBIGUOUS;AMBIGUOUS:不确定也要标记而非省略,score 取0.1–0.3。
file_type 六值枚举
code | document | paper | image | rationale | concept,其余任何值都会被拒绝;rationale 用于承载"为什么做此决策"的概念类节点,且决策理由优先存为节点属性而非独立节点。仓库在 gen.py 中把这六值枚举定义为唯一超集,并通过 schema-singleton 守卫校验所有平台渲染产物字节一致。
节点 ID 确定性规则
仅用小写 [a-z0-9_],格式 {stem}_{entity}:stem 是完整仓库相对路径去掉扩展名、各级目录以 _ 拼接并逐段小写化的结果,entity 是同样规范化的符号名;例如 src/auth/session.py + ValidateToken → src_auth_session_validatetoken。规则要求与 AST 抽取器产生的 ID 完全一致、不附加任何 chunk/序号后缀——这是后续 AST+语义两路结果能按 ID 去重合并(Part C)的前提。
其他抽取纪律
- 代码文件只补 AST 抓不到的语义边,不得重抽 import;
calls边 source 恒为调用方、target 恒为被调方且不跨语言; - 语义相似(无结构链接但同题同思路)时添加
semantically_similar_to,INFERRED、score0.6–0.95,只限非显然的跨文件链接; - 超边(hyperedge):3 个以上节点共享某概念/流/模式且两两边无法表达时才建,节制使用,每 chunk 最多 3 条;
- YAML frontmatter(
source_url、captured_at、author、contributor)需拷贝到该文件产出的每个节点上; - 图片走视觉理解,"明白这张图是什么"而非只做 OCR;
source_file必须逐字使用 FILE_LIST 中路径,不缩短 basename、不改写相对化,保证全量构建与--update共用一个基准,避免合并替换失配造成重复节点;- 输出只有 JSON,禁止解释文字、markdown 围栏或前言。
与"磁盘版"派发的对照:一份双轨运维清单
把本片段与其兄弟片段 agent-tool-disk.md 对照,即可得到一份可直接用于排障的双轨清单:
| 维度 | Codex(spawn_agent 版,本文) | Claude 等(Agent 工具版) |
|---|---|---|
| 派发原语 | spawn_agent(agent_type="worker", ...) |
Agent tool 调用(须 general-purpose) |
| 前置配置 | ~/.codex/config.toml 的 [features] 开启 multi_agent = true |
无(但禁用只读 Explore 型) |
| 并行手段 | 同一响应内发出全部 spawn | 同一响应内发出全部 Agent 调用 |
| 子代理落盘 | 不落盘,JSON 内联返回 | 写入绝对路径 .graphify_chunk_0N.json |
| 成功信号 | wait_agent 返回可解析为合法 JSON |
chunk 文件在磁盘存在且含合法 nodes/edges |
| 汇总方式 | 内存累加后写 .graphify_semantic_new.json |
磁盘 glob 合并后再写 .graphify_semantic_new.json |
| 失败语义 | 某 chunk 返回非法 JSON 即该块失败,跳过不中止 | 文件缺失提示"子代理可能是只读型";非法 JSON 跳过;过半失败则中止并提示改用 general-purpose |
可以看到:两份协议在"单消息全量派发、失败单块降级、最终数据归一进 .graphify_semantic_new.json"上完全一致,分歧只发生在传输层——这也是为什么 gen.py 允许两者共享同一份 core 模板、仅以 dispatch 槽位隔离差异。从该生成结构可以推断:若未来 Codex 改变子代理 API 形态,改动点应收敛在这一个片段内,而不必触碰 Step B0/B1/B3 的缓存与合并逻辑。
生产建议与常见误区
结合片段正文与仓库守卫机制,实际接入或二次开发时值得注意:
- 先验配置后验可用。
multi_agent = true缺失时spawn_agent直接不可用——把"配置 → 重启 Codex → 重试"作为标准修复路径,而不是临时改成串行 Agent 调用(那会违背该技能的并行纪律并大幅拖慢大型语料)。 - 不要给 Codex 路径套磁盘检查。若在某 Codex 实现中沿用"等待
.graphify_chunk_NN.json"的旧逻辑,会因文件从不出现而误报 chunk 失败;正确信号永远是对wait_agent返回值做 JSON 解析。 - 提示词逐字传递,宁可统一不要本地改写。四个占位变量的替换是唯一允许的差异;同时注意 core.md 与缓存设计约定:抽取提示文本本身是语义缓存条目的归属键(prompt 变更会令旧缓存条目失效重抽),逐字传递也是缓存正确性的保障。
- 纯代码语料走快路径。无 doc/paper/image 时整个 Part B 被跳过,自然也不会加载抽取规范与派发 worker——这正是"免费 AST + 按需 LLM"架构的体现。
- 回归防护由工程机制兜底。改完片段后需在仓库根目录运行
python -m tools.skillgen --check验证渲染产物与 expected/ 快照无漂移,生成入口与守卫实现见 gen.py。
综上所述,codex-agenttask.md 以不到 30 行定义了一条完整、可执行、可与其它平台"等价的"Codex 语义抽取派发协议:一处 [features] 配置、一轮同响应 spawn_agent、一轮 wait_agent/close_agent 内存回收,外加按需逐字加载的紧凑抽取规范——理解它,也就理解了 graphify 如何在"每种 AI 宿主 API 各不同"的现实约束下,让知识图谱的语义侧流水线保持行为一致与可审计。
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