| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- // Copyright (c) 未来飞马
- //
- // This Source Code Form is subject to the terms of the Mozilla Public
- // License, v. 2.0. If a copy of the MPL was not distributed with this
- // file, You can obtain one at https://mozilla.org/MPL/2.0/.
- //
- // Trademark Notice:
- // The MPL-2.0 license grants copyright permissions for source code only.
- // It does NOT grant any rights to use trademarks including "未来飞马",
- // "Harness Loop", "RSI", and associated slogan "让AI进化提前发生,让AI落地快人一步".
- // Any use of these trademarks requires separate written permission.
- export const VERSION = '2.1.0';
- export const SKILL_NAME = 'skill-heterarchy';
- // ---------------------------------------------------------------------------
- // v2.1 dispatch model — 判断0 硬门槛 + 四判断决策流(对齐 SKILL.md §一 / §二)
- //
- // v2.0 失效根因(2026-10-08 实测):用户给出 git 项目路径并明说"项目级任务",
- // 主控仍自己读写改文件。原因是「四判断流」全是软描述,且判断 2 的 ≤2 调用预算
- // 被解读成"每一步",主控边走边看逐步自我授权;git 项目的映射规则又藏在判断 2
- // 之后,判断 2 一旦误判"自做",映射表根本不会被查到。
- //
- // v2.1 修正:
- // ① 新增 hardTrigger()——判断 0,在判断 2 之前执行,命中即收回"自做"裁量权。
- // ② 判断 2 预算改为「整条请求累计」而非单步(见 cumulativeToolCalls 选项)。
- // ③ git 仓库 / 技能仓任务 → claude -p(需项目根 + CLAUDE.md + rules)。
- // ④ 导出 signatures 保持向后兼容:isConversation / canSelfExecute /
- // selectProfile / selectMechanism / planDispatch 全部保留。
- // ---------------------------------------------------------------------------
- /** 判断 1:沟通(问候/确认/简单回执/已知信息问答)还是干活。 */
- export function isConversation(text = '') {
- // 去掉尾部标点/空白;注意 CJK 后不能用 \b(JS \b 仅识别 ASCII 词边界)
- const t = String(text).trim().replace(/[!!。.,,、~~\s]+$/g, '');
- if (!t) return true;
- if (t.length > 20) return false;
- return /^(你好|您好|hi|hello|在吗|在么|收到|好的|好|嗯+|谢谢|thanks|thank you|ok|okk?|早|晚安)$/i.test(t);
- }
- // ---------------------------------------------------------------------------
- // 判断 0:硬门槛(★ v2.1 新增,最先执行,命中即派)
- // ---------------------------------------------------------------------------
- /** 硬触发规则表(SKILL.md §一)。命中任一 → 必须派发,不允许主控自做。 */
- export const HARD_TRIGGERS = [
- {
- id: 'H1',
- label: 'git 仓库路径',
- // 绝对/相对路径里出现 git-repos / repos/ / git- 前缀 / .git
- re: /(^|[\s"'`(])(\S*\/git-repos\/|\S*\/repos\/|\S*git-[a-z0-9_-]+|\.git\b)/i,
- profile: 'fullstack',
- mechanism: 'claude-p',
- },
- {
- id: 'H2',
- label: 'git 操作',
- re: /(git\s+clone|git\s+commit|git\s+push|git\s+checkout\s+-b|建仓|打\s*tag|发版|发布\s*release)/i,
- profile: 'fullstack',
- mechanism: 'claude-p',
- },
- {
- id: 'H4',
- label: '技能开发/安装',
- re: /(开发\s*skill|skill\s*开发|改\s*SKILL\.md|修改\s*SKILL\.md|\/opt\/data\/skills\/|安装技能|npx\s+.*skill)/i,
- profile: 'fullstack',
- mechanism: 'claude-p',
- },
- // H5 排在 H3 之前:用户显式声明比泛化的"项目级"词更具体(否则 "项目级任务" 会误报为 H3)
- {
- id: 'H5',
- label: '用户显式声明',
- re: /(项目级任务|项目任务|派\s*(CC|FmodeCode\s*Code)|交给\s*FmodeCode\s*Code|用\s*CC\s*做)/i,
- profile: null, // 由文本再判
- mechanism: 'delegate-task',
- },
- {
- id: 'H3',
- label: '项目级动词',
- re: /(build|构建|拆分|重构|部署|发布|上线|\bCI\b|多文件|全栈|实现功能|项目级)/i,
- profile: 'fullstack',
- mechanism: 'delegate-task',
- },
- {
- id: 'H6',
- label: '批量对象',
- re: /(全舰队|所有容器|逐台|批量|\d+\s*(台|个|套)\s*(容器|服务器|机器|节点)?)/i,
- profile: 'devops',
- mechanism: 'delegate-task',
- },
- ];
- /**
- * 判断 0:硬门槛。命中即返回命中的规则,未命中返回 null。
- * @returns {{id:string,label:string,profile:string|null,mechanism:string}|null}
- */
- export function hardTrigger(text = '') {
- const t = String(text);
- for (const rule of HARD_TRIGGERS) {
- if (rule.re.test(t)) return { ...rule };
- }
- return null;
- }
- // ---------------------------------------------------------------------------
- // 判断 2:是否主控直接执行(整条请求累计 ≤2 个简单工具调用)
- // ---------------------------------------------------------------------------
- /**
- * 判断 2:主控自做判定。
- *
- * v2.0 的坑:预算被读成"每一步"。v2.1 明确按「整条用户请求累计」计算——
- * 调用方应把当前已累计的调用数传进 `cumulativeToolCalls`,而非"这一步要几个"。
- *
- * @param {object} plan
- * @param {boolean} [plan.known] 已知信息、无需工具即可回答
- * @param {number} [plan.toolCalls] 本步预计调用数(兼容旧签名)
- * @param {number} [plan.cumulativeToolCalls] 整条请求累计调用数(v2.1 推荐)
- */
- export function canSelfExecute(plan) {
- if (!plan || typeof plan !== 'object') return false; // 无计划 → 不臆断为自做
- if (plan.known === true) return true;
- const budget =
- typeof plan.cumulativeToolCalls === 'number' ? plan.cumulativeToolCalls : plan.toolCalls;
- if (typeof budget === 'number') return budget <= 2;
- return false;
- }
- // 判断 3:任务类型 → Profile 映射表(SKILL.md §三 的内置速查)
- const PROFILE_MAP = [
- { profile: 'writer', re: /(小说|章节|剧本|文案|报告|拉片|文章|内容|html|ppt|讲述)/i },
- { profile: 'researcher', re: /(调研|检索|分析|研究|选型|竞品|资料|查证)/i },
- { profile: 'fullstack', re: /(开发|实现|功能|项目|全栈|前端|后端|重构|代码|skill|技能)/i },
- { profile: 'devops', re: /(运维|部署|发布|容器|docker|k8s|批量|上线|舰队|服务器)/i },
- ];
- /** 判断 3:根据任务描述自动选 Profile;不要求用户显式指定。 */
- export function selectProfile(task = '') {
- const t = String(task);
- for (const { profile, re } of PROFILE_MAP) {
- if (re.test(t)) return profile;
- }
- return 'fullstack'; // 兜底:通用实现
- }
- /** 判断 4:选派发机制。项目上下文(git 项目/需读 CLAUDE.md)优先 claude -p。 */
- export function selectMechanism(opts = {}) {
- if (opts.needsProjectContext === true) return 'claude-p';
- return 'delegate-task';
- }
- /**
- * 完整决策:硬门槛(判断0)→ 沟通(判断1)→ 自做(判断2)→ Profile(判断3)→ 机制(判断4)。
- *
- * @returns {{action:'reply'|'self'|'dispatch', hit?:string, label?:string,
- * profile?:string, mechanism?:string, parallel?:boolean}}
- */
- export function planDispatch(text = '', opts = {}) {
- // ── 判断 0:硬门槛——命中即派,跳过判断 2 的自做裁量 ──────────────────
- const hit = hardTrigger(text);
- if (hit) {
- const profile = hit.profile || selectProfile(text);
- // git 项目 / 技能仓 → claude -p;其余按规则表给的机制
- const mechanism =
- hit.mechanism === 'claude-p' || opts.needsProjectContext === true
- ? 'claude-p'
- : selectMechanism(opts);
- return { action: 'dispatch', hit: hit.id, label: hit.label, profile, mechanism, parallel: opts.parallel === true };
- }
- // ── 判断 1:沟通 / 干活 ─────────────────────────────────────────────
- if (isConversation(text)) return { action: 'reply' };
- // ── 判断 2:自做 / 派发(整条请求累计 ≤2 才允许自做)──────────────────
- if (canSelfExecute(opts.plan)) return { action: 'self' };
- // ── 判断 3 / 4:Profile + 机制 ──────────────────────────────────────
- const profile = selectProfile(text);
- const mechanism = selectMechanism(opts);
- return { action: 'dispatch', profile, mechanism, parallel: opts.parallel === true };
- }
- /**
- * 教训库条目构造器(SKILL.md §十一)。发现"该派未派"时调用,
- * 返回一行 Markdown 表格记录,追加到 LESSONS.md。
- *
- * @param {{date?:string, hit:string, action:string, should:string, cause:string}} e
- */
- export function lessonEntry(e = {}) {
- const date = e.date || new Date().toISOString().slice(0, 10);
- const esc = (s) => String(s ?? '').replace(/\|/g, '\\|').replace(/\n/g, ' ');
- return `| ${esc(date)} | ${esc(e.hit)} | ${esc(e.action)} | ${esc(e.should)} | ${esc(e.cause)} |`;
- }
|