首页
/ graphify 语义提取的手工分发模式:无子代理 API 主机(VSCode)上的 Step B2 手动粘贴工作流

graphify 语义提取的手工分发模式:无子代理 API 主机(VSCode)上的 Step B2 手动粘贴工作流

2026-09-07 10:43:48作者:卓艾滢Kingsley

graphify 将代码、文档、论文、图片乃至视频统一抽取为可查询的知识图谱,其中「语义抽取(semantic extraction)」阶段(Step 3 Part B)负责用 LLM 提炼 AST 结构性抽取无法覆盖的关系。本文聚焦该阶段在没有并行 Agent/Task API 的主机(典型如 VSCode 宿主)上的特殊工作流——manual-paste 手工分发模式:不依赖任何自动化工具,通过手动开启子会话、粘贴子代理回传的 JSON、并按约定落盘到分块文件来驱动整条抽取流水线。读完本文,你将掌握该模式的完整运作机制、精确的 Shell 落盘命令、分块文件命名与装配约定,以及它与其余六种自动分发片段(agent-tool / task-tool / codex / opencode 等)在设计上的取舍关系。

一、背景:语义抽取为什么需要「分发(Dispatch)」

graphify 的抽取管线分为两条腿(见 fragments/core/core.md 与渲染产物 graphify/skill-vscode.md):

  • Part A — 结构性抽取(AST):对代码文件做确定性、零成本的语法树解析,输出到 graphify-out/.graphify_ast.json,全程不消耗 LLM token。
  • Part B — 语义抽取(LLM):只处理 documentpaperimage 三类非代码文件(代码已由 AST 覆盖,见 graphify/skill-vscode.md),把每个文件内的命名实体、概念、引用与跨文件关系提炼成节点/边/超边。

Part B 的成本与并发度直接取决于如何把海量文件切块并分发给 LLM 子代理。graphify 的 skills 生成器(skillgen)将这一环节做成可插拔的 @@DISPATCH@@ 槽位,每种宿主(Claude Code、Codex、OpenCode、Aider、VSCode……)在 tools/skillgen/platforms.toml 中声明自己采用哪种「分发策略」:

分发片段 目标平台 子代理并发方式
agent-tool-disk.md claude、kilo、copilot、kiro、pi 同一消息内多次调用 Agent 工具实现并行,子代理写盘
agent-tool-disk-powershell.md windows 同上,但落盘命令翻译为 PowerShell 语法
task-tool-disk.md / -trae droid、amp、agents、trae 基于 Task 工具的磁盘收集式并行
codex-agenttask.md codex spawn_agent + wait_agent + close_agent,内存汇总
opencode-mention.md opencode @agent 并行 + .graphify_semantic_new.json 汇总
manual-paste.md(本文) vscode 无自动化 API,人工开子会话、粘贴回传 JSON

其中 platform.vscode 显式声明 dispatch = "manual-paste",即本片段的唯一消费方。它由 gen.py 在渲染时读取并替换进核心模板的 @@DISPATCH@@ 槽位,最终产出的完整文档是 graphify/skill-vscode.md 中 Step B2 那一整节。

二、为什么需要「手动粘贴」:无并行 Agent API 主机的降级方案

manual-paste 片段的定位在标题与引言中写得很直白(原文):

No automated subagent tool: this host has no parallel Agent/Task API, so extraction is driven by hand. Dispatch a subagent per chunk however the host allows (a fresh conversation, a parallel pane), then paste each response back.

也就是说,当宿主不具备「同一条消息内发起多个并行子代理」的能力时,skillgen 不会让抽取停下来,而是退化为由宿主自身作为编排者:按块(chunk)开启独立对话或并行面板充当子代理,再把每个子代理返回的 JSON 内容手工粘贴回主会话,并写入对应的分块文件。这里存在一个隐含的分工原则(见核心模板对宿主的说明):

  • 纯代码语料库直接走「快路径」(fast path):写空语义文件后跳到 Part C,从不读取抽取规范文件;
  • 只有包含文档/论文/图片的分块才需要在 Step B2 加载并分发子代理;
  • 即便是在无子代理能力的主机上,也不应向用户索要 API key 或阻塞等待——GEMINI_API_KEY/GOOGLE_API_KEY 存在则由 graphify/llm.pyextract_corpus_parallel 承担语义抽取;否则由宿主会话亲自作为 LLM 完成任务。

三、Step B2 分步执行:分块、分发、粘贴、落盘

3.1 上游状态:先过缓存、再切分块

Step B2 只在两种前置条件满足后才会执行,构成典型的四步流水线:

  1. Step B0(缓存检查):调用 graphify/cache.pycheck_semantic_cache 对比文件与抽取提示词指纹,命中结果写入 graphify-out/.graphify_cached.json,未命中的文件清单写入 graphify-out/.graphify_uncached.txt。只有清单中的文件才需要被分发(见 graphify/skill-vscode.md)。
  2. Step B1(切块):读取 .graphify_uncached.txt,按 每块 20–25 个文件 切分;每张图片独占一个块(视觉理解需要独立上下文);尽量把同目录文件聚到同一块,以便子代理更容易提取跨文件关系。
  3. Step B2(分发+粘贴,即本文核心):为每一块分发一个子代理,回收其 JSON 响应并写入该块文件。
  4. Step B3(收集合并):检查分块文件是否在磁盘上存在(成功信号),合并进 .graphify_semantic_new.json,经 save_semantic_cache 写缓存后与缓存结果合成 .graphify_semantic.json(见 graphify/skill-vscode.md)。

3.2 手工落盘命令(原文核心代码段)

每当一个子代理的 JSON 回传后,需要把它保存为该块的分块文件,供 Step B3 的 glob 收集。原文给出的命令是:

# After pasting a subagent's JSON for chunk N, save it (replace N and PASTED_JSON):
PROJECT_ROOT=$(pwd)  # cwd — where Part C globs graphify-out/ (NOT .graphify_root/scan dir, #1392)
cat > "${PROJECT_ROOT}/graphify-out/.graphify_chunk_0N.json" <<'CHUNK_JSON'
PASTED_JSON
CHUNK_JSON

这段命令有几个必须遵守的细节:

  • PROJECT_ROOT=$(pwd):以当前工作目录为准。其目的是让 Part C 后续的 glob 能在正确的 graphify-out/ 下取到文件,而不是 .graphify_root/scan 扫描目录(原文以 issue #1392 标注这一历史教训——扫描根目录与输出目录曾经被混淆,导致收集器找不到分块文件)。
  • heredoc 使用带引号的定界符 <<'CHUNK_JSON':单引号关闭了 Shell 对内容的一切变量展开与命令替换,保证子代理返回的 JSON 原样写入,避免其中包含的 $、反引号等字符被意外解释。
  • 定界符 CHUNK_JSON 本身需保持唯一且不与正文冲突;PASTED_JSON 只是占位符,实际替换为粘贴的完整 JSON。
  • 文件名采用零填充序号 0N:即 N=1.graphify_chunk_01.json,以此类推。这是因为 Step B3 的收集脚本使用 sorted(glob.glob('graphify-out/.graphify_chunk_*.json')) 按字典序排序(见 graphify/skill-vscode.md),若文件名不带零填充,chunk_10.json 会排在 chunk_2.json 之前导致序号错乱。

3.3 逐个块重复,直至全部落盘

「Repeat for every chunk」是硬性要求:每个块的 JSON 必须在 Step B3 运行之前落入各自独立的 graphify-out/.graphify_chunk_NN.json。Step B3 以「文件存在于磁盘」作为成功判定信号,因此遗漏任何一个块都会造成该块抽取结果被静默丢弃。

3.4 子代理提示词模板:只在此处、按需加载

分发子代理时,宿主并不是自由发挥,而是把精确的抽取提示词逐字传给每个子代理。提示词出处为 references/extraction-spec.md(VSCode 平台装配到 graphify/skills/vscode/references/extraction-spec.md,模板源头在 tools/skillgen/fragments/references/shared/extraction-spec.md)。提示词中需要被替换的占位符有四个:

  • FILE_LIST — 本块负责的文件清单;
  • CHUNK_NUM — 当前块序号;
  • TOTAL_CHUNKS — 总块数;
  • DEEP_MODE — 若原始调用携带 --mode deep,则必须向每个子代理传 DEEP_MODE=true,让其对 INFERRED 边更激进(见 tools/skillgen/fragments/core/core.md)。

规范文件的核心约束包括:只输出符合 schema 的 JSON(无解释、无围栏);边必须携带 EXTRACTED / INFERRED / AMBIGUOUS 证据标记与 confidence_scorecalls 边必须同语言内且源为调用方;file_type 只能是 code/document/paper/image/rationale/concept 六值之一;超边仅当 3 个以上节点共同参与同一概念时才使用且每块至多 3 条;图片用视觉理解「是什么」而非仅 OCR;语义相似但无结构链接的概念补 semantically_similar_to 边。

特别值得注意的是加载条件:仅当至少一个分块包含文档、论文或图片时才读取该规范文件;纯代码语料库在进入 Part B 之前就已跳过整个语义抽取,永远不会加载它。这既节省 token,也避免子代理在无事可做时收到无关指令。

四、与其他分发模式的差异:manual-paste 的独有取舍

manual-paste 片段与同目录其他分发片段对照,可以清晰看出它在设计上的三个独特点:

  1. 无并行原语、人工串并行agent-tool-disk 强调「同一消息内多次调用 Agent 工具,否则就退化为顺序执行并失去意义」;codex-agenttask 使用 spawn_agent/wait_agent/close_agent 并要求 multi_agent = true 配置。而 manual-paste 明确放弃任何 API 依赖,改用「新开会话 / 并行面板」的人工方式——这是对无 API 主机在功能完整性上的兜底。
  2. 磁盘是唯一事实源:Step B3 通过检查 .graphify_chunk_NN.json 是否存在来判断子代理是否成功,因此「粘贴 + 落盘」这一步绝不能省。Codex 变体则相反——在内存中累计结果后统一写 .graphify_semantic_new.json,磁盘成功检查不适用(见 codex-agenttask.md)。manual-paste 位于「磁盘收集」阵营,与 agent-tool-disk / task-tool-disk 共享同一套 B3 检查协议。
  3. 提示词注入方式相同、槽位不同:所有分块文件的 schema、节点 ID 规则、置信度量规、超边与视觉规则都由抽取规范统一约束,manual-paste 与自动模式的区别仅在「谁、以什么方式把提示词交给子代理并取回结果」。

五、装配机制:这一片段如何变成可用的 SKILL.md

了解片段如何进入最终产物,有助于排查分发故障。装配链如下:

  1. tools/skillgen/platforms.toml 定义 [platform.vscode]bucket = "split"core = "core"skill_dst = "graphify/skill-vscode.md"refs_dst = "graphify/skills/vscode/references"dispatch = "manual-paste"extraction = "verbose"shell = "posix"
  2. tools/skillgen/gen.py_render_core 按平台声明读取 fragments/dispatch/manual-paste.md,将其文本替换进核心模板的 @@DISPATCH@@ 槽位;若替换后仍残留未填充的 @@xxx@@ 槽位会直接报错,防止产出残缺技能。
  3. 若平台 shell 为 powershell(如 windows),则整段 body 还会经 PowerShell 翻译器处理——manual-paste 本身只面向 POSIX(VSCode 宿主),因此保留了 cat heredoc 原生写法。
  4. 最终渲染产物就是 graphify/skill-vscode.md 第 253–273 行的 Step B2 完整正文。仓库中 tools/skillgen/expected/graphify__skill-vscode.md 保存了用于防漂移的期望快照。

这意味着:分发的行为完全由片段决定,而片段又由 platform 声明选择。当你在 VSCode 宿主上运行 graphify 并观察到语义抽取以「人工分发」方式进行时,根因就在 platforms.tomldispatch = "manual-paste" 这一行。

六、实操清单与常见问题

把整条手动分发工作流压缩为可直接照做的清单:

  1. 确认语料含非代码文件:检测结果中 document/paper/image 数量为零时走快路径,直接写空语义文件进入 Part C,无需执行本文任何步骤。
  2. 先缓存后分发:运行 Step B0 得到 .graphify_uncached.txt,只对清单文件分发。
  3. 切块:每块 20–25 文件、图片独占一块、同目录优先聚拢。
  4. 逐块分发:为每块开启新会话/并行面板,逐字传入抽取提示词,替换 FILE_LIST / CHUNK_NUM / TOTAL_CHUNKS / DEEP_MODE
  5. 回贴落盘:将子代理返回的 JSON 用 cat > ... <<'CHUNK_JSON' heredoc 写入 graphify-out/.graphify_chunk_NN.json,序号需零填充,PROJECT_ROOT 取当前工作目录。
  6. 校验后合并:确认每个分块文件都真实存在且含合法 nodes/edges,再运行 Step B3 的合并脚本;失败或缺失的块应告警跳过而非整体中止,但超过一半块失败则应停止并提示用户。

容易踩的坑集中在三点:heredoc 定界符漏加引号导致 JSON 被变量展开污染;块文件名未按 0N 零填充造成 Step B3 字典序排序错乱;以及把 PROJECT_ROOT 误设为 .graphify_root/scan 而非输出根目录(#1392),使 Part C 的 glob 扑空。

结语

manual-paste 是 graphify skills 体系中对「宿主能力下限」的优雅处理:即便没有并行子代理 API,语义抽取仍能通过「人工开启子会话 + 粘贴回传 + 规范化落盘」完整运行,并与其他分发模式共享同一套缓存、合并与置信度协议。对技能使用者而言,理解这段 Step B2 的落盘约定与命名规则,能显著减少在 VSCode 等主机上遇到的「分块文件缺失」「合并结果为空」等疑难问题;对 skillgen 的二次开发者而言,它也是一份如何为一个无 Agent API 平台编写专属分发片段的参考样例。

登录后查看全文
热门项目推荐
相关项目推荐