--- name: skill-heterarchy description: "预计超 3 步或 2 分钟的任务,先拆并行维度派 FmodeCode/Claude Code。主控写任务书+验收,不阻塞。" version: 1.2.0 author: Yuyang001 (FmodeAgent) license: MPL-2.0 copyright: "Copyright (c) 2026 未来飞马 Fmode" tags: [未来飞马, 智能体技能, 超级技能, 系统级, 平台基础设施, FmodeAgent, Hermes Agent, FmodeCode, Claude Code, heterarchy, dispatch, cognitive-collaboration, parallel, parallel-batch-orchestration, batch-orchestration, topological-sort, async-receipt, timeout-fallback, cdn-verification, obs-deploy, progress-board] --- # skill-heterarchy — 对内分思实操技能 > Delegate 是对外派活,Heterarchy 是对内分思。 > 本技能用 FmodeAgent / Hermes Agent 的底层工具在单一 Agent 内部分化多心智并行思考。 > 加载方式:`skill_view(name='skill-heterarchy')` --- ## 〇、FmodeAgent / Hermes Agent 工具并行调用机制(并行调用铁律) 每次 FmodeAgent / Hermes Agent 处理用户消息时,**可以在同一轮对话内同时发起多个独立的工具调用**。这是 Heterarchy 能真正"并行"的底层前提——如果连工具调用都串行发,后面的并行派发就是空谈。 ### 0.1 同轮并发的能力组合 | 场景 | 同一轮并发发出 | |------|----------------| | 多路信息采集 | 多个 `terminal()`(不同命令) | | 边读边搜 | `read_file()` + `search_files()` | | 边调研边干活 | `web_search()` + 一个后台 FmodeCode / Claude Code 启动 | | 多进程编排 | 多个 `tool_call()` 管理不同进程 | ### 0.2 铁律:独立无依赖 → 必须并发 **原则**:两个工具调用之间若**没有数据依赖**(后者不需要前者的输出),就必须放在**同一轮**发出,而不是分成多轮对话串行。 **反例(串行 3 轮对话,浪费 3 个往返):** ``` 第 1 轮:read_file(A) → 等结果 第 2 轮:read_file(B) → 等结果 第 3 轮:web_search(...) → 等结果 ``` **正例(一轮搞定,延迟≈最慢的那一个):** ``` 第 1 轮:read_file(A) + read_file(B) + web_search(...) ← 同轮并发 ``` ### 0.3 判断方法 在发工具调用前,对每个待发调用问一句: > "我这次调用需要用到另一个调用的输出吗?" - **不需要** → 并入本轮,并发发出。 - **需要** → 才拆到下一轮,等前置返回。 ### 0.4 与 Heterarchy 的衔接 本机制是后续所有章节(§X 批量分析、§Y 进度表、§Z 异步回执、§V 拓扑分批)的**地基**: - 认知拆解(§一)判定出的"无依赖维度",靠本章的并发机制真正并行落地; - FmodeCode / Claude Code 派发(§三)多个 `terminal(background=True, notify=True)` 必须在同一轮发出,才能同时启动多个心智; - 一旦发现有依赖,才退回 §V 的拓扑分批。 **一句话**:先把能并的都并出去,再把剩下的按依赖排序。 --- ## 零、何时触发本技能(★ 硬门槛,先算账再开工) > **本节 1.2.0 起改为硬规则**。原因:旧版只写了「当你收到一个复杂任务, > 明显可以拆成多个独立维度并行处理时」——判断太软,忙碌的 Agent 会直接跳过, > 于是长任务串行硬啃、用户干等。**触发条件必须是可自检的是非题,不是感觉。** ### 0.1 开工前 10 秒自检(任一为「是」→ 必须先加载本技能再动手) | # | 自检问题 | 是 | |---|---------|---| | 1 | 预计需要 **> 8 个工具调用**? | → 拆 | | 2 | 预计有**任何一次等待 > 30 秒**(安装/构建/克隆/遍历)? | → 拆 | | 3 | 目标是 **N 个同类对象**(N ≥ 3)?如「所有容器」「几十个生命」「批量安装」 | → 拆 | | 4 | 任务含 **≥ 2 个可独立完成的维度**(如「改代码」+「改文档」+「写测试」)? | → 拆 | | 5 | 用户说了 **批量 / 全部 / 所有 / 几十个 / 并发 / 尽快**? | → 拆 | **判定纪律:不确定就按「拆」处理。** 拆错的代价只是多写一份任务书; 不拆的代价是用户干等几十分钟、主控上下文被中间数据淹没。 ### 0.2 反例(真实事故,2026-09-23,运维001) ``` 任务:给 34 台数字生命容器批量铺 3 个技能 + 改云函数 + 建验收机制 实际:整轮串行自己啃 —— 47 轮工具调用,单条命令 60/90/120 秒地等, 期间用户在聊天里反复问「怎么还没反应」,主控上下文涨到 129k。 本应:拆成 4 个认知子单元并行派 Claude Code(技能改造 / 容器分发 / 云函数部署 / 验收器开发),主控只写任务书 + 验收。 教训:任务明明命中自检 1/2/3/5 四条,却因为「触发条件是软描述」而没被拦住。 ``` ### 0.3 触发词(出现即算命中) 「多个主题需要完成」「同时做这几件事」「这几个部分需要…」「尽量并行」 「批量」「全舰队」「所有容器」「几十个生命」「铺开」「逐台」 ### 0.4 何时不用(明确排除,避免滥用) - 单一步骤命令 → 直接 `terminal()` 或 `execute_code()` - 纯对外输出(发一条消息、写一个简单文件)→ 直接做 - 需要跨 Profile 协作 → 用 `kanban_create` 委派,不是 Heterarchy - 任务本身**不可分割**(如「把这一行配置改掉」)→ 直接做 --- ## 一、任务拆解(认知分化 — FmodeAgent / Hermes Agent 自己做) 这是唯一不能交给 FmodeCode / Claude Code 的步骤。FmodeAgent / Hermes Agent 必须亲自: 1. 理解用户真实需求 2. 识别可并行的独立维度(每个维度 = 一个独立认知子单元) 3. 判断子单元之间是否有依赖关系 4. 确定项目工作目录(每个 FmodeCode / Claude Code 进程必须在正确的 git 仓库根目录启动) **实操模板(FmodeAgent / Hermes Agent 的思考过程):** ``` 收到:"四个主题的 HTML PPT 需要图文并茂增强" 拆解: ├→ 主题一:数字分身(独立 → 无前置依赖) ├→ 主题二:Harness(独立 → 无前置依赖) ├→ 主题三:Loop(独立 → 无前置依赖) ├→ 主题四:RSI(独立 → 无前置依赖) └→ 依赖:无(四主题可完全并行) 项目根目录:/opt/data/git-repos/skill-present FmodeCode / Claude Code 必须在项目根目录启动,否则读不到 CLAUDE.md 的项目规则。 ``` --- ## 二、任务书编写(心智分化) 每个认知子单元需要一份独立的任务书,包含: ``` 1. 背景上下文(用户原始需求) 2. 本次要做什么(明确范围,不要多做) 3. 要保留什么(已有的内容别动) 4. inline 素材(路径、关键描述) 5. 纪律约束: - 框架不可改 - 自绘 SVG 保留 - 案例 CDN 图优先用已有 - skill-image 只用于核心架构图/场景图 6. 验收标准(git commit、Content-Type 验证、URL 200) 7. 禁止事项(不要重写、不要改标题、不要改结构) ``` 任务书写入临时文件 `/tmp/cc-xxx-task.md`,用 `cat` 管道喂入 FmodeCode / Claude Code,不用 `$(cat)` 避免路径问题: **正确姿势:** ```bash cat /tmp/cc-xxx-task.md | /opt/data/npm-global/bin/claude -p --dangerously-skip-permissions > /tmp/cc-xxx.log 2>&1 & ``` **错误姿势(踩过坑):** ```bash # 不要用 $(cat file) —— 文件路径在后台进程可能不同 # 不要省略绝对路径 —— exit 127 找不到 claude ``` --- ## 三、FmodeAgent / Hermes Agent 协同工具调用表(核心实操) ### 3.1 启动心智 — terminal + background + notify ```python # FmodeAgent / Hermes Agent 代码中: terminal( command=f""" export PATH="/opt/data/npm-global/bin:$PATH" cd {project_root} cat {task_file_path} | /opt/data/npm-global/bin/claude \\ -p --dangerously-skip-permissions \\ > {log_path} 2>&1 """, background=True, notify=True, timeout=1800, workdir=project_root ) # → 返回 session_id 用于后续监控 ``` ### 3.2 阻塞监控 — process_manage + sleep ```python # 启动后 60s 首次健康检查 terminal("sleep 60 && pgrep -f 'claude -p' | wc -l", timeout=90) # 或主动轮询 tool_call(calls=[{"name": "process_manage", "arguments": {"action": "list"}}]) # → 检查 uptime_seconds 是否在增长 # → 检查 output_preview 是否为空 ``` ### 3.3 心智收敛 — git log + curl Content-Type ```python # 检查是否 commit terminal("cd {repo} && git log --oneline -3", timeout=10) # 验证图片 Content-Type(重要!只查 HTTP 200 会被 404 首页骗) terminal(f""" for url in $(grep -roh 'https://fmode.cn[^"'"'"' )]*\\.png' {repo}/*.html 2>/dev/null | sort -u); do CT=$(curl -s -m 10 -o /dev/null -w "%{{content_type}}" "$url") [[ "$CT" == image/* ]] || echo "❌ 图裂: $CT $url" done""", timeout=30) ``` ### 3.4 横向协商(可选)— 前一子单元的产物成为后一子单元的输入 ```python # 如果主题一的 commit 包含架构图,主题二需要引用 terminal("cd {repo} && git diff HEAD~1 --name-only", timeout=10) # 读文件内容作为 inline 素材塞进主题二的任务书 read_file(path="{repo}/path/to/architecture.md") ``` ### 3.5 模型配置检查 ```python # 检查 settings.json 确保模型名正确 # 用户钦点:FmodeAgent / Hermes Agent=deepseek/deepseek-v4.1-flash, FmodeCode / Claude Code=deepseek/deepseek-v4.1-flash[1m] terminal("cat {project_root}/.claude/settings.json", timeout=5) # 如发现模型名不对,patch 修正 ``` --- ## 四、派发策略决策树 ``` 收到复杂任务 │ ├→ 可以拆成 2-5 个独立维度? │ ├→ 否 → 单一 FmodeCode / Claude Code 进程处理 或 本 Agent 直接完成 │ └→ 是 → 继续 │ ├→ 子单元之间有依赖关系? │ ├→ 是 → 拓扑排序:先做无依赖的,串行分批 │ │ ├→ 所有独立 → 全并行(最多 5 个同时) │ │ └→ 部分依赖 → 分批并行:依赖关系跨批次传递 │ └→ 否 → 全并行启动 │ ├→ 每个子单元的工作量? │ ├→ 简单(< 50 行代码/单页)→ 不启 FmodeCode / Claude Code,本 Agent 直接写 │ ├→ 中等(需要完整思考/调研/创作)→ 独立 FmodeCode / Claude Code 进程 │ └→ 复杂(需要跨文件多模块/项目级)→ 优先用 kanban_create 委派给 fullstack profile │ ├→ 项目目录正确吗? │ ├→ 每个 FmodeCode / Claude Code 必须在项目根目录启动 │ └→ 错目录 → FmodeCode / Claude Code 读不到 CLAUDE.md → 规则丢失 → 出次品 │ └→ 启动后: ├→ 60s 后检查存活 ├→ 存活 → 等待 notify → 收敛验收 └→ 全死 → 执行恢复流程 ``` --- ## 五、恢复流程(阻塞自愈 — 实操版) 一旦 `process_manage(action="list")` 显示所有进程已退出,按以下顺序处理: ### 5.1 查日志尾巴 ```python terminal(f"tail -30 {log_path}", timeout=10) ``` ### 5.2 根据错误分类处理 | 日志症状 | 诊断 | 处置 | |----------|------|------| | `command not found: claude` 或 exit 127 | claude 不在 PATH | 用 `/opt/data/npm-global/bin/claude` 绝对路径 | | `503 No available channel` 或 exit 1 | 模型渠道不可用 | 检查 `~/.claude/settings.json` 模型名(少 `deepseek/` 前缀会 503) | | `401` 或 `Not logged in` | Token 失效 | 检查 ANTHROPIC_AUTH_TOKEN=$FMODE_API_KEY | | 日志为空 + exit 0 | 假成功 | 从 `$(cat)` 改 `cat pipe`方式重派 | | 日志有内容但 exit 1 | 任务执行中出错 | 读日志末尾判断:模型退火 / 文件读写错 / 权限问题 | | 通知没来、长时间无响应 | 进程阻塞 | `pgrep -f claude` 如果还在跑则等待;否则杀进程重派 | ### 5.3 重派策略 ```python # 1. 如果是"续做"(之前的部分产物可用) # 在新的任务书开头写明: # "已完成:xxx(state) 继续做:yyy(未完成部分)" # 2. 如果是"从零重做"(完全失败) # 缩小范围:大任务拆小 # 禁用 FmodeCode / Claude Code 的某些能力:机器产出内容需要再确认 # 3. 连败 3 次同域 → 停止 FmodeCode / Claude Code 重试 # 本 Agent 直接处理 或 拆成更细的原子指令 ``` --- ## 六、完整实操流程(以 V6 四主题图文增强为例) ``` Step 1: 认知拆解 FmodeAgent / Hermes Agent 消化用户需求:"四个主题需要图文并茂增强,框架不动,加图" → 四主题可完全并行,项目根目录=skill-present Step 2: 心智分化 FmodeAgent / Hermes Agent 为每个主题编写独立任务书(/tmp/cc-v6-0x-resume.md) 每份包含:当前 commit hash + 结构框架 + 纪律 + 验收标准 Step 3: 并行启动 terminal(background=True, notify=True, workdir=skill-present, command=f"cat /tmp/cc-v6-01-resume.md | /opt/data/npm-global/bin/claude -p --dangerously-skip-permissions > /tmp/cc-v6-01-r2.log 2>&1") terminal(background=True, notify=True, workdir=skill-present, command=f"cat /tmp/cc-v6-02-resume.md | ...") # 主题二 terminal(background=True, notify=True, workdir=skill-present, command=f"cat /tmp/cc-v6-03-resume.md | ...") # 主题三 terminal(background=True, notify=True, workdir=skill-present, command=f"cat /tmp/cc-v6-04-resume.md | ...") # 主题四 Step 4: 阻塞自愈(首次 60s 后) process_manage(action="list") → 检查所有子单元存活 Step 5: 心智收敛(逐个子单元 notify) 每个 notify 到达: └→ git log --oneline 检查 commit → └→ curl Content-Type 验证图片 → └→ 记录产出摘要 Step 6: 统一交付 push 所有 commit → OBS sync → CDN 刷新 → URL 验证 → 发用户 ``` --- ## 七、常见陷阱(踩坑记录) ### 7.1 任务书喂入方式 - ❌ `claude -p "$(cat task.md)"` → 路径丢失,后台进程找不到文件 - ✅ `cat task.md | claude -p` → 管道直送,无文件路径依赖 ### 7.2 项目根目录 - ❌ 在父目录启动 FmodeCode / Claude Code → 读不到 `.claude/rules/` - ✅ 必须在 git 项目根目录启动(读得到 CLAUDE.md + rules + settings.json) ### 7.3 图片验证只看 200 - ❌ `curl -o /dev/null -w "%{http_code}"` → 200 可能是 404 首页 - ✅ `curl -o /dev/null -w "%{content_type}"` → 必须是 `image/*` ### 7.4 版本号 - ❌ 跳 1.0.0、5.0.0 大版本 - ✅ 0.0.1 → 0.0.12 → 0.0.99 → 0.1.1 小步迭代 ### 7.5 配置 files 字段 - ❌ 不设 `files: ["lib/", "bin/", ...]` → npm publish 把无关文件全打包 - ✅ 精确声明要发布的文件 --- ## 八、技能与本 Agent 内置工具的关系 本技能**不替代** FmodeAgent / Hermes Agent 内置工具,而是**编排**它们: ``` FmodeAgent / Hermes Agent 收到任务 │ ├→ skill-heterarchy(本技能)→ 决策是否启用异层认知 │ └→ 认知分化 → 写任务书 │ ├→ terminal(FmodeAgent / Hermes Agent 工具)→ 后台启动 FmodeCode / Claude Code 进程 ├→ process_manage → 监控存活 ├→ read_file / patch / git → 收敛验证 ├→ web_search → 前置调研 └→ delegate_task → 轻量子任务(与 Heterarchy 可组合使用) ``` **何时用 delegate_task 而不是 Heterarchy?** - 子任务是"帮我查点东西"(轻量检索)→ `delegate_task` - 子任务是"完整思考 + 产出可交付物" → Heterarchy + FmodeCode / Claude Code **何时用 kanban_create 而不是 Heterarchy?** - 任务需要跨 Profile 的独立 MCP 环境 → `kanban_create` - 任务在本 Agent 的能力范围内,只是需要并行 → Heterarchy --- ## X、execute_code 批量分析模式 当要对**多个文件/多条数据**做 grep、统计、聚合、转换时,不要用一串 `terminal()` 逐个敲。改用 FmodeAgent / Hermes Agent 内置的 `execute_code`(Python 内核),一次进程内跑完。 ### X.1 适用场景 | 场景 | 例子 | |------|------| | 跨目录 grep + 聚合 | 统计所有技能里各模型名的出现次数 | | 多 HTTP 请求并行测试 | 一次性 curl 全部产物 URL 并汇总 Content-Type | | 批量文件读写 + 统计 | 扫描所有 `.html`,统计图片引用数 | | 数据格式转换 | JSON ↔ CSV ↔ YAML 互转 | | git diff 分析 | 汇总一次 commit 涉及的文件类型/行数 | ### X.2 原则 > **超过 2 个文件需要 grep / 统计 / 聚合 → 用 `execute_code`,而不是逐个 `terminal`。** 逐个 `terminal` 的代价:每个文件一轮往返 + 一次进程启动开销;`execute_code` 把这些压成**一次调用、一个摘要**。 ### X.3 模板 ```python import os, json, subprocess, glob # 1. 收集目标 targets = glob.glob("/opt/data/git-repos/*/SKILL.md") # 2. 批量处理 summary = [] for t in targets: try: with open(t, encoding="utf-8") as f: text = f.read() summary.append({ "path": t, "lines": text.count("\n") + 1, "has_version": "version:" in text, }) except Exception as e: summary.append({"path": t, "error": str(e)}) # 3. 只打印结果摘要(不要 print 原始大文本,会污染上下文) print(f"扫描 {len(summary)} 个文件") print(json.dumps([s for s in summary if s.get("error")], ensure_ascii=False, indent=2)) print("缺失 version 的文件数:", sum(1 for s in summary if not s.get("has_version"))) ``` ### X.4 纪律 - **只 print 摘要**,不要把整个文件内容倒进上下文——那等于没省。 - 需要逐条证据时,print 结构化 JSON(`json.dumps(..., indent=2)`)而不是裸文本。 - 网络类批量任务用 `concurrent.futures.ThreadPoolExecutor` 并发,不要 for 循环串行 curl。 - `execute_code` 适合**确定性脚本活**;需要"整体思考/创作"的部分仍走 §三 的 FmodeCode / Claude Code 派发。 --- ## Y、多任务进度表(状态看板) FmodeAgent / Hermes Agent 在同时推进多个 FmodeCode / Claude Code 后台任务时,**每次回复用户前**先构造一张状态表,让用户一眼看清全局。不要只说"还在跑"。 ### Y.1 状态看板模板 ```python # 每次回复用户前构造(在 execute_code 或直接拼字符串) tasks = [ ("主题一 · 数字分身", "running", "3m12s"), ("主题二 · Harness", "done", "4m05s"), ("主题三 · Loop", "failed", "1m47s"), ] ICON = {"running": "🟡 运行中", "done": "✅ 已完成", "failed": "❌ 失败(重派)"} rows = "\n".join( f"| {name} | {ICON[status]} | {dur} |" for name, status, dur in tasks ) print(f""" | 任务 | 状态 | 时长 | |------|------|------| {rows} """) ``` 输出示例: ``` | 任务 | 状态 | 时长 | |------|------|------| | 主题一 · 数字分身 | 🟡 运行中 | 3m12s | | 主题二 · Harness | ✅ 已完成 | 4m05s | | 主题三 · Loop | ❌ 失败(重派) | 1m47s | ``` ### Y.2 纪律 - **状态三态**:🟡 运行中 / ✅ 已完成 / ❌ 失败(重派)——不要造模糊词。 - **时长要真实**:从 `process_manage(action="list")` 的 `uptime_seconds` 或启动时间戳推算,不要拍脑袋。 - 失败项必须注明**下一步动作**(重派/续做/放弃),否则看板只是情绪价值。 - 状态表是给用户的**唯一真相源**:有它就不必再让用户追问"到哪了"。 --- ## Z、主 Session 不阻塞(异步回执模式) Heterarchy 的核心承诺:**主 Session 永远不被某个 FmodeCode / Claude Code 任务卡住**。收到请求后立即回执,派完就撒手,靠 notify 回调驱动后续。 ### Z.1 标准流程 ``` 收到用户请求 ├→ ① 立即发一条回执:「已收到,开始执行,分配 X 个任务同时进行」 ├→ ② 全 FmodeCode / Claude Code Dispatch:terminal(background=True, notify=True) 全部同轮发出(见 §〇) ├→ ③ 不等完成,先回复用户:「任务已派发,等完成通知」 └→ ④ 当 FmodeCode / Claude Code notify 到达(作为新消息进来),再验证并交付 ``` ### Z.2 关键点 | 步骤 | 要点 | |------|------| | ① 回执 | 明确告知**拆成几个任务**、**预计并行**,让用户安心 | | ② 派发 | 所有 `terminal(background=True, notify=True)` **同一轮**发出,不要等第一个再发第二个 | | ③ 撒手 | 派发完**立刻**回复用户,**绝不**用前台 `sleep` 干等 | | ④ 回调 | notify 到达 = 一次新的唤醒,此时才 `git log` / 验证 / 交付 | ### Z.3 反模式 ``` ❌ 派发 → 前台 sleep 600 → 查状态 → 再 sleep → ...(主 Session 被阻塞,用户干等) ✅ 派发 → 立即回复 → 挂起 → notify 唤醒 → 验证交付 ``` **为什么重要**:前台阻塞会占满对话轮次、无法响应用户中途插话、也无法同时推进其它任务。异步回执把"等待"交给事件系统,主 Session 只负责**编排**。 --- ## W、超时兜底机制 每个 FmodeCode / Claude Code 任务必须设 `timeout=1800`(30 分钟)。terminal 前台最大 600s,超过会自动转后台 + notify。 ### W.1 派发时统一带上超时 ```python terminal( command=f"cat {task_file} | /opt/data/npm-global/bin/claude -p --dangerously-skip-permissions > {log} 2>&1", background=True, notify=True, timeout=1800, # ← 30 分钟硬上限 workdir=project_root, ) ``` ### W.2 超时处理流程 ``` notify: timeout (exit 124) ├→ tail -30 log → 看中途产物 ├→ 如果已有部分产出: │ └→ 写接力任务书(标明已完成部分 + 待做部分)→ 重派 ├→ 如果零产出(空日志): │ └→ 改小任务范围,重新派发 └→ 连续 2 次超时 → 拆分子任务更小粒度 ``` ### W.3 诊断命令 ```python # exit 124 = timeout;先看日志尾部和是否已 commit terminal(f"tail -30 {log_path}", timeout=10) terminal(f"cd {repo} && git log --oneline -3", timeout=10) # 有无中途 commit? ``` ### W.4 处置矩阵 | 症状 | 判断 | 处置 | |------|------|------| | exit 124 + 日志有产物 | 大任务未跑完 | 写**接力任务书**:已完成 X / 待做 Y | | exit 124 + 日志空 | 卡在启动/首轮推理 | 缩小范围重派,或换更小模型 | | 连续 2 次 exit 124 | 任务粒度过大 | 拆成更小原子子任务 | | 日志刷屏无进展 | 死循环/反复重试 | 杀进程,改任务书约束(禁某操作) | **铁律**:超时**不等于失败**——先看中途产物。有产物就走接力(省时省 token),零产物才重做。 --- ## V、拓扑排序(有依赖的任务分批并行) 当子单元之间**存在依赖**时,不能无脑全并行。先建模依赖图,再分批:无依赖的批次并行,有依赖的批次等前置完成。 ### V.1 依赖建模 ``` task_a → task_b → task_c (串行链) task_a → task_b (task_b 依赖 task_a) task_c (task_c 独立,与 a/b 无依赖) ``` ### V.2 分批并行启动 ``` 第 1 批(无依赖):task_a + task_c 同时启动 → 等 task_a 完成 → 取产出 第 2 批(依赖 task_a):task_b 启动 → 等 task_b 完成 交付 ``` ### V.3 执行要点 | 要点 | 说明 | |------|------| | 批内并行 | 同批任务**同一轮**发出(§〇),互不等待 | | 批间串行 | 下一批要等本批**关键路径**任务完成 | | 跨批传料 | 前置任务的 commit / 文件作为后置任务书的 inline 素材(§3.4) | | 关键路径优先 | 先启动最长依赖链的头,让串行链尽早开跑 | ### V.4 与 §四决策树的衔接 §四的决策树已判定"有依赖 → 拓扑排序,分批并行"。本章是它的**落地细则**: - 依赖图为空 → 退化为全并行(第 1 批装下所有任务); - 依赖图为链 → 每批只放 1 个,退化为串行,但**每批内部仍尽量并发**无关任务。 **一句话**:能并的并、该等的等——用依赖图说话,不靠感觉。 --- ## U、结果聚合汇报 当多个 FmodeCode / Claude Code 任务全部完成(或部分失败)后,**统一聚合成一张交付汇总表**,而不是把 N 个任务的日志碎片甩给用户。 ### U.1 聚合模板 ```python results = [] for notify in notifies: log = read_log(notify.log_path) if "commit" in log or success_marker(log): results.append(("✅", notify.task_name, notify.url)) else: results.append(("❌", notify.task_name, error_of(log))) rows = "\n".join(f"| {name} | {mark} | {link} |" for mark, name, link in results) print(f""" ## 并行任务交付汇总 | 任务 | 结果 | 链接 | |------|------|------| {rows} """) ``` ### U.2 纪律 - **先验证再汇总**:每条结果都要经过 `git log` + URL Content-Type 验证(§T / §P),不能拿"日志看着像成功"充数。 - **失败项必须带错误**:`❌` 行要写清原因(超时/503/空日志),便于用户决策。 - **汇总表 = 交付凭证**:表里每个 ✅ 都是可点击验证的链接。 - 部分失败**不阻塞交付**:先交付成功的,失败的进入 §W/§五 的重派队列,并在表下注明"重派中"。 --- ## T、OBS + CDN 部署验证链 FmodeCode / Claude Code 产出 HTML / 图片后,部署链路必须**逐环节验证**,不能 sync 完就当完事。 ### T.1 部署链 ``` 1. OBS sync → obs://www-fmode.cn/dev/xxx/ 2. CDN 刷新(华为云 CDN 缓存清除) 3. 验证所有 URL ``` ### T.2 逐 URL 验证 ```bash for url in $ARTIFACTS; do code=$(curl -m 10 -o /dev/null -w "%{http_code}" "$url") ct=$(curl -m 10 -o /dev/null -w "%{content_type}" "$url") if [[ "$code" != "200" ]]; then echo "❌ HTTP $code $url"; continue; fi case "$ct" in text/html*|image/*|application/*) echo "✅ $code $ct $url" ;; *) echo "❌ 类型异常: $ct $url" ;; esac done ``` ### T.3 Python 版(批量) ```python import subprocess def check(url): code = subprocess.run(["curl","-m","10","-o","/dev/null","-w","%{http_code}",url], capture_output=True, text=True).stdout ct = subprocess.run(["curl","-m","10","-o","/dev/null","-w","%{content_type}",url], capture_output=True, text=True).stdout ok = code == "200" and ct.startswith(("text/html","image/","application/")) return ok, code, ct bad = [(u, c, t) for u in artifacts for ok, c, t in [check(u)] if not ok] print("全部通过" if not bad else f"❌ {len(bad)} 个异常: {bad}") ``` ### T.4 纪律 - **CDN 刷新后必须重验**:不刷新可能拿到旧缓存,刷新后可能拿到 404——两头都要 curl。 - **断言而非观察**:`assert resp == 200` / `assert ct.startswith(...)`,不满足就报错,不要"看着还行"。 - 部署链是**串行依赖**(sync → 刷新 → 验证),天然适合 §V 的拓扑链:每步是上一步的依赖。 --- ## S、FmodeCode / Claude Code 环境自举 每次启动 FmodeCode / Claude Code 前必须确保环境变量齐备,否则轻则 401/503,重则 exit 127。 ### S.1 必备环境 ```bash export PATH="/opt/data/npm-global/bin:$PATH" export ANTHROPIC_AUTH_TOKEN=$FMODE_API_KEY export ANTHROPIC_BASE_URL=https://api.fmode.cn ``` 或**内联**写进 terminal 命令(推荐,避免 shell 状态不持久): ```python terminal( command=f""" export PATH="/opt/data/npm-global/bin:$PATH" export ANTHROPIC_AUTH_TOKEN=$FMODE_API_KEY export ANTHROPIC_BASE_URL=https://api.fmode.cn cd {project_root} cat {task_file} | /opt/data/npm-global/bin/claude -p --dangerously-skip-permissions > {log} 2>&1 """, background=True, notify=True, timeout=1800, workdir=project_root, ) ``` ### S.2 settings.json 模型名 ```python # 必须写入正确的模型名,少前缀会 503 terminal(f"cat {project_root}/.claude/settings.json", timeout=5) # FmodeCode / Claude Code 模型:deepseek/deepseek-v4.1-flash[1m] ``` ### S.3 自举检查清单 | 检查项 | 失败症状 | 修法 | |--------|----------|------| | `PATH` 含 npm-global | exit 127 `command not found` | 绝对路径 `/opt/data/npm-global/bin/claude` | | `ANTHROPIC_AUTH_TOKEN` | 401 / Not logged in | `=$FMODE_API_KEY` | | `ANTHROPIC_BASE_URL` | 连不上网关 | `https://api.fmode.cn` | | settings.json 模型名 | 503 No available channel | 补全 `deepseek/` 前缀 | **纪律**:环境设置**内联**在启动命令里,不要依赖"上一次 export 还在"——后台进程与交互 shell 状态不共享。 --- ## R、工作目录纪律 FmodeCode / Claude Code 进程的工作目录**决定它能否读到项目规则**。目录错了,规则静默丢失,产出必是次品。 ### R.1 铁律 - 每个 FmodeCode / Claude Code 进程的 `workdir` **必须**设为 **git 项目根目录**。 - 错目录 → FmodeCode / Claude Code 读不到 `.claude/rules/` / `CLAUDE.md` / `settings.json` → **规则丢失**。 - 在 `terminal()` 内用 `cd {workdir} && ...` **双重保证**(`workdir` 参数 + 命令内 `cd`)。 ### R.2 正误对照 ```python # ✅ 双重保证 terminal( command=f"cd {project_root} && cat {task_file} | /opt/data/npm-global/bin/claude -p ...", workdir=project_root, background=True, notify=True, ) # ❌ 在父目录启动 → 读不到 .claude/rules/ → 规则丢失 terminal(command="cat task.md | claude -p ...", workdir="/opt/data/git-repos") ``` ### R.3 为什么双重 - `workdir` 参数是**声明**,但某些封装/重定向可能不生效; - 命令内 `cd` 是**执行保证**,两者叠加才能确保万无一失。 - 派发前先确认根目录:`git rev-parse --show-toplevel` 的结果应与 `project_root` 一致。 --- ## Q、版本规范与发布检查 ### Q.1 版本号小步迭代 ``` 0.0.1 → 0.0.12 → 0.0.99 → 0.1.1 ``` - **只增不减**:新版本号必须**大于**当前已发布/已提交的版本,绝不回退。 - **小步快跑**:补丁 +0.0.1,新增能力 +0.1.0,破坏性变更 +1.0.0。 - **三处对齐**:`SKILL.md` frontmatter、`package.json`、`lib/index.mjs`(`VERSION`)必须一致。 ### Q.2 发布前检查清单 引用 **skill-core-guide §3.5** 的 13 项发布前检查(逐项勾选,缺一不可): 1. frontmatter 必填字段齐全(name/description/version/author/license/tags) 2. `description` 写清"何时用"而非"是什么" 3. 版本号三处对齐且单调递增 4. `package.json` `files` 字段精确声明发布内容 5. `main`/`exports` 指向 `lib/index.mjs` 6. `bin` 可执行且有 shebang 7. 无硬编码密钥/凭据 8. README 含简介 / 快速开始 / 许可 9. LICENSE 与 frontmatter 的 `license` 一致 10. 商标声明完整(未来飞马 / Harness Loop / RSI) 11. 六项自动质检全绿(`skip` 不算通过) 12. 四渠道版本号一致 13. commit message 不含内部工具名 ### Q.3 四渠道发布 ``` Gogs → GitHub → npm → skillhub ``` - 顺序固定:先内网 Gogs,再 GitHub,再 npm,最后 skillhub 上架。 - 每渠道发布后**验证**:npm 用 `npm view version`,git 用 `git ls-remote`。 - 任一渠道失败 → 停止后续渠道,先修复,避免版本分叉。 --- ## P、CDN 验证铁律 ### P.1 200 ≠ 真 200 > 对象存储/CDN 在资源不存在时,**可能返回 404 首页且 HTTP 状态是 200**。 只看 `%{http_code}` 会得到**假绿**。必须叠加 **Content-Type** 判断。 ### P.2 分类型验证 | 资源 | 必须满足 | |------|----------| | 图片 | `Content-Type: image/*` | | HTML | `Content-Type: text/html` | | 其它(JS/CSS/JSON) | `Content-Type: application/*` | ### P.3 逐 URL 验证,不可跳过 ```bash # ✅ 同时取 code 和 content_type curl -m 10 -o /dev/null -w "%{http_code} %{content_type}\n" "$url" ``` ```python # ✅ 断言式:不满足即报错 assert code == "200", f"HTTP {code}: {url}" assert ct.startswith(("text/html", "image/", "application/")), f"类型异常 {ct}: {url}" ``` ### P.4 铁律汇总 1. **200 不是通行证**——必须看 Content-Type。 2. **逐 URL 验证**,不接受"抽查几个"。 3. **断言而非目测**——写成 `assert`,失败即中断。 4. **CDN 刷新前后各验一次**(§T)——防旧缓存、防刷新后 404。 5. **图裂是最隐蔽的坑**——404 首页的 200 会让 HTML 看着正常,实际图全裂。 **一句话**:每个 URL 都要 `curl + code + content_type` 三重确认,一个都不能跳。