Files
admin e6207aa691
build / build-and-scan (push) Waiting to run
chore(仓库对齐): 文档库结构治理 + IM/插件线落地
文档库:目录改为编号制(01-规范/02-架构设计/03-数据库/04-调整方案/
05-交接单/06-ops/07-scripts/08-skills/09-archive),顶层散文件归入 01-规范/;
INDEX.md 与 docs-manifest.json 重刷(档案 146 篇);旧目录名引用全量对齐。

IM 线:src/im/**(SDK / hub / store / presence / ws / gateway-token)、
src/web/routes/im.ts、src/db/plugin-data/**、src/supervisor/plugin-assembly.ts
及对应 test/**。

插件线:poc/{im-agent-bridge,im-connection-gateway,im-conversation-tabs,
business-plugins-im,carbon-mcp-probe}、src/web/routes/{sessions,overlay-device}.ts、
src/net/relay/{device-grant,instance-credential}.ts。

仓库卫生:清出 40 个历史误入库 / 已改名文件(34 个交接单归档 + 6 个旧结构,
本地均有副本);dsh-server-docs/.gitignore 补 tmp/;交接单不入库(政策)。
2026-09-24 07:25:16 +08:00

8.0 KiB
Raw Permalink Blame History

技能重组 · 千行技能拆分(SKILL.md 主干 + references/ 详情档)

归属:技能 dsh-knowledge-upkeep 的详情档(按需读,不是每次都要读)。 本档覆盖:§10 的执行清单 · 通用拆分器用法 · 2026-09-22 首次实战的实测数据与决策记录 · 坑。 主文件 / 判据 = ../SKILL.md(§2 分层判据 · §8.6 注入预算 · §9 技能集体检 · §10 技能重组)。 来源:2026-09-22「技能重组线」首跑后沉淀(首次对象 = dsh-change-workflow / dsh-opensource-release)。


1. 为什么要有它(一句话)

技能长到千行级 ⇒ 每次加载都全量注入,而里面大半是长表 / 实测记录 / 历史细节。拆成「主干 + 详情档」不是审美,是把注入预算还给判据(同 §8.6:长文件后半段等于不存在)。

与相邻机制的分工:

机制 管什么
§9 技能集体检 技能挑不挑得中(description 区分度)
§10 技能重组 技能加载后吃多少上下文 / 判据找不找得到(文件内部结构)
agent-operating-rules §10.1 要不要把多个技能合并
dsh-architecture-lifecycle 架构过程文档 vs 定稿的分层

2. 通用拆分器 07-scripts/split_skill.py

<python> 07-scripts/split_skill.py --spec <spec.json>

规格格式(照抄改):

{
  "skills_dir": "E:/ProgramData/.workbuddy/skills",
  "backup_dir": "E:/ProgramData/AI技能/aliyun-dsh-server/tmp/_keep-20260922/split-bak",
  "skill": "dsh-change-workflow",
  "main_desc": "§1 六阶段流程 · §2 红线速查 · §3 Git Bash 坑",
  "moves": [
    { "ranges": [[14, 92]],
      "ref": "00-平台速查.md",
      "title": "平台速查(硬编码事实,勿猜)",
      "covers": ["平台速查(硬编码事实,勿猜)"],
      "pointer": "> 📂 **平台速查表已下沉** → `references/00-平台速查.md`" }
  ]
}

要点:

  • ranges = 1-based 闭区间,可多段([[481,504],[555,600]]),不得重叠;一段一个 ref。
  • pointer = 插在该组区间起点位置的指针行(可多行用 \n);要点名该档覆盖的章节,否则违反验收 ③。
  • covers = 原章节名,用于生成主干的「详情档 × 覆盖的原章节 × 原行段」表(跨档引用的定位表)。
  • --dry-run 只看校验,不落盘。

脚本内置自校验(任一 FAIL 就必须停下来看,别硬推):

  1. 行数守恒:主干内容行 + 详情行数 == 原行数;
  2. 逐行包含:Counter(原行) <= Counter(主干非指针行 + 全部详情行) ⇒ 丢失 0;
  3. 节可寻址:原文件每个 ^#{1,3} 标题文本都能在新结构(主干 + 任一详情档)里找到;
  4. frontmatter:version: 行唯一且值不变。

幂等:脚本永远以备份为拆前基准 ⇒ 反复跑不会二次切割(⚠️ 首次失误版本读的是活文件,第二次跑就 AssertionError —— 这个坑已修,别再写回活文件)。


3. 2026-09-22 首跑实测数据(两个对象)

技能 原 SKILL.md 主干 详情档 ≤350 目标
dsh-change-workflow 1052 行 319 行 9 档 / 867 行 ✅ 达标
dsh-opensource-release 1089 行 534 行 5 档 / 629 行 ❌ 多 184 行
  • 守恒实测:275 + 777 = 1052|510 + 579 = 1089(逐行比对丢失 0;新增的 34 / 19 行全是指针与索引行)。
  • 可寻址实测:原 51 / 68 个标题,未命中 0。
  • 三处对账:16 个文件(2 主干 + 14 详情档)三方 md5 全一致。

为什么 opensource-release 到不了 350(决策记录):它的「不看会违规/事故」常驻项合计约 453 行 —— 硬规则 R-O1–R-O17 239 + 事故清单 40 + 事实 34 + 硬规则总表 23 + 用户当场纠正口径 16 + 授权结构 35 + 验证八件套 37 + SOP 主干 29。压到 350 的唯一路径是把 R-O 硬规则降级成指针 ⇒ 违背「任何会话不得放宽」。当时的选择 = 判据常驻优先,如实报告数字,没有为凑行数动硬规则(对应用户「确保功能效果不受影响」的硬约束)。


4. 断头引用:怎么扫、怎么修

扫(正则挑跨节引用,逐行打印,人工过一遍):

见上文|见下文|见前文|见后文|见上表|见下表|见上面|见下面|上文|下文|见 §|见§|参见|详见|见阶段|见坑|见附

实测命中 5 处(目标全在详情档里):见下表:dsh-univer-office(→ 保留/移除档)· 见 §8 坑 9、见坑 36(→ 实测坑档)· 详情档之间的 见 §8 坑、见坑 31–36。

修法(不删字):主干末节加「详情档 × 覆盖的原章节 × 原行段」表 + 一句「编号引用按本表定位(下沉后原编号不再有独立章节标题)」。详情档头里也写一句同样的指引。 ⛔ 不要去改正文里的引用句(那会破坏「逐行未改」的可校验性)。


5. 坑(都踩过)

  1. 必须从备份重建:否则第二次跑读到的是已拆过的活文件,区间校验直接 AssertionError。
  2. 详情档编号要按文档顺序:首跑把「浏览器验证栈」编成 08、而「并行调度」是 07(因为它先被创建)⇒ 索引表出现 …06 / 08 / 07 的倒序。修法:生成索引表时对档名 sorted()。
  3. 行尾只信字节级:拆分后要确认新文件是纯 LF(CR=0);文档库 INDEX.md 是 CR/LF 混排(CR=5/LF=270)⇒ 改它只做字节级单行插入,⛔ 别整文件 Write。
  4. 登记跟改用「精确串替换器」:每个 old 必须命中恰好 1 次,任一不中 ⇒ 整体不落盘(比逐次 Edit 可靠、比 sed 安全,非 ASCII 不被改写)。
  5. 环境:本轮本机 bash 包装器整体起不来(ls/cat 也 command not found、export PATH 救不回)⇒ 全程改走 PowerShell + 托管 python:脚本先 Write 成 .py、输出落文件再 Read。⚠️ MSYS 路径(/e/…)⛔ 不许交给 Windows 原生程序(会落到 E:\e\… 影子目录)。
  6. 对账用 Python 算 md5,别用 md5sum:本机输出 hash *path、远端 hash path,多一个空格会让逐行比对假失败。
  7. 按技能名匹配表格行会误命中:用 NAME in line 找登记行时,别的技能行常在描述里提到这个技能名(实测:README 的 dsh-architecture-lifecycle 行写着「与 dsh-knowledge-upkeep 互补」⇒ 两行同时命中,登记被挂到了错行,且我按「行里含对方名」做回退时又同时命中两行、把两行都清了)。 判据 = 只认行首单元格:l.startswith("| 08-skills//"),且命中数必须恰好 1;改动前后各打印一次「命中行号 + 行首 34 字」自证。
  8. 判定远端登记文件是否「只是陈旧」——别用 SequenceMatcher 的 opcode 判据:我要求「opcode 全为 equal/delete」时,** 与 | 密集的长表格行会给出假 replace ⇒ 误报「远端含本机没有的内容」而停手(实测:README 3 行 / INDEX 4 行全被误报,实际覆盖率 1.000)。 正确判据 = 整行子序列覆盖率:sum(b.size for b in SequenceMatcher(None, 远端行, 本机行).get_matching_blocks()) / len(远端行) >= 0.98(远端只被删减,本机只做插入)⇒ 可安全推。更省事的 oracle = git:git show HEAD:<f> 与远端比 —— 相等即"远端 = 已提交基线,差异全是未提交改动"。
  9. /opt/dsh/docs/ 根也镜像文档仓根:README.md(技能登记表)与 INDEX.md(清单与状态)在服务器上各有一份 ⇒ 三处同步不能只算 08-skills/。实测 2026-09-22:skills 16 文件三方 md5 全一致,而根 README/INDEX 落后一整轮登记(差异恰为被改的 3 + 4 行)。推送后 chmod 600 + chown root:root,旧副本备份到 /opt/dsh/backups/docs-root/。