DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

shuiiiiimu /

shuiiiiimu/dsh-selfharness

Verified

DSH 上的 Harness 层自我改进插件

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@3422a4cc

dsh-selfharness — DSH 上的 Harness 层自我改进插件

把 Harness 物化成可版本化的 Harness Pack:从真实会话轨迹里挖反复出现的缺陷,据此改进, 在留出集上配对评测;只有增益超过噪声底的候选才会生效。基础模型全程冻结, 整个演化过程落盘成可 git diff、可 git revert 的产物。


1. 自进化的逻辑

Harness 也是模型能力的一部分。 同一个模型,换一份 system prompt、换几个 skill、 收紧几条工具边界,表现会不一样。所以「自我改进」在这里不是改权重,而是改这些外置的东西, 并且把它们从散落的配置收拢成一个对象(Pack),才能版本化、评测、回滚。

改进必须可测,因为增益小、噪声大。 每一次改动都在留出集上做 task 级配对比较, 只有超过噪声底、且不牺牲能力的候选才算数 —— 这一条把「听起来有道理」和「真的更好」分开。

判据是泛化,不是适应。 记住训练任务不算自我改进。所以留出集分三类: 提议者可见的 public 只看诊断,private / transfer / regression 只给门控看。

[1] 真实会话轨迹 ──▶ 经验库(SQLite)
                          │  同一个失败签名反复出现
                          ▼
[2] 缺陷簇 ──▶ 机会筛选 ──▶ 值不值得花这一轮预算?
                          │  估不出来就直接砍掉,不花预算
                          ▼
[3] 提议(改 Pack)──▶ 审查(静态 + 行为契约)
                          │
                          ▼
[4] 门控(配对统计 + 风险分级)──▶ 不通过:写进否决台账后结束
                          │  通过
                          ▼
[5] 激活(指针移动)──▶ 新 Pack 生效 ──▶ 挂上观察窗
                                             │  同一缺陷签名回升
                                             ▼
                                       自动回滚(只回滚、不推进)

五个面(候选能改的东西):

面 是什么 生效方式
doctrine system prompt 的一段文本 热切换:下一个会话立刻生效
skills skills/*.md 热切换
tool-policy 工具 deny 列表,沿版本链单调不减 热切换
context-policy 别的插件的 Config 只产出建议值,粘进 cordis.patch.yml 后重启
observation-policy 同上 同上(这两个面不参与自动门控)

三道防线,任何一道不通都不激活:

  1. 静态 —— schema 校验 + deny 单调 + 全 Pack 字面量扫描(把留出集特征写进 Harness 直接拒);
  2. 统计 —— 能力下限 + 两条并列通道之一: Δ_private ≥ δ_noise 且区间下界 > 0(能力增益),或至少一项效率指标显著改善且其余不倒退(约束效率)。 δ_noise 由历史 baseline 池的成对差异 95 分位标定,不是两次基线自比。 配对单位是 task 不是 rollout:k 次重复先聚合成任务通过率,再做 McNemar + bootstrap 区间。
  3. 风险 —— doctrine / skills / tool-policy 一律 defer 到人工;只有两个"建议值"面能自动通过。

激活之后仍然有安全阀:Pack 指针移动、新会话生效,同时按该候选针对的缺陷签名叫醒一个观察窗; 证据显示该签名回升就自动回滚(只回滚、不推进),并写台账。回滚是一等公民操作,不需要评测。

留出集不落盘:private / transfer / regression 只以数据库行存在,评测时才物化进那个 rollout 的 工作目录,跑完随目录一起删。提议阶段磁盘上不存在这些文件,这是泄漏防火墙的主防线。

    缺陷签名 ~ 机制假设(并行)
    ├── lineage L1 ──▶ 候选 A ──┐
    └── lineage L2 ──▶ 候选 B ──┴──▶ 各自独立评测 ──▶ 门控 ──▶ 至多一个被激活

2. 安装与上手

作为普通 bundle 装进 profile(<checkout> 是本仓库路径):

dsh plugin --profile web add <checkout>

装完必须重启一次 dsh:bundle 列表在启动时读取,profile 的 patchReload: live 只能热重载已挂载的 row,装不进来一个全新的 bundle。

开发时 Host 侧改动不必重启:

npm run build:js && npm run reload   # 禁用原 row + 插入 selfharness-dev 指向新的内容寻址副本

reload 不能直接把原 row 的 name 改指向新副本:非 insert 补丁里的 name 只是断言, 与 row 不符时整条补丁会被 Loader 跳过(patch: name mismatch … skipping),这正是它早期版本 静默失效的原因。所以它写两条补丁——按 id 禁用 bundle 自己的 row,再插入 selfharness-dev row(insert 的 name 才可解析,相对路径由 DSH 锚定到 patch 文件所在目录)。落盘前它用 DSH checkout 自己的 applyEntryPatches 复算一遍,patch 不生效就拒绝写入。

全部操作都在面板上(侧边栏「自进化」→ 收益 / 轮次 / 谱系 / 设置四个视图):

要做什么 在哪里
看这次进化的收益:已生效的改进各改了什么、门控实测到多少、覆盖之前哪些历史任务 「收益」页(默认首屏)。模型侧对应 selfharness_value 工具
看某一条候选的判决依据、逐行 diff 轮次 → 展开该轮 → 候选卡片
回填历史会话、挖缺陷、装内置示例套件 设置 → 「登记评测套件」下方的 bootstrap 入口(面板 bootstrap 动作)
试算一轮值不值得跑 / 真跑一轮 页头「运行一轮」;设置页的预算与熔断
采纳 / 拒绝 / 推后一个候选 轮次 → 展开该轮 → 候选卡片上的三个按钮(二次确认 + 填理由)
回滚到任一历史版本 谱系 → 每个非 active rev 上的「回滚到此版本」
熔断 / 恢复 设置 → 「熔断(pause)」/「恢复(resume)」
导出两个"建议值"面 设置 → 「导出 context / observation 建议值」
问它现在什么状态 设置 → 「循环状态」,或让模型调 selfharness_status 工具
在模型里看「尺子」够不够大(不花钱) selfharness_preview 工具:套件题数、MDE、δ_noise 来源
让模型/脚本做维护动作(bootstrap / harvest / 扩产 / 自检 / 复检 / 清理 / 清空历史) selfharness_maintain 工具(面板上都有对应按钮)
清空全部历史、从零重跑(调试/验收用) 设置 → 「清空历史(重新开始)」:要输入 reset-history 才可点;也可用 selfharness_maintain。默认保留已装套件(尺子不是历史;留出集正文只存在数据库里),要连套件一起删就传 includeSuites=true

所有写操作都要二次确认并填理由。

无界面(headless)跑一轮

面板不是唯一的入口:模型工具与脚本都能跑完整流程。DSH 自带 headless profile,把插件装进去即可:

# 1. 建一个 headless profile(只含 base + headless,没有任何 HTTP 端口)
dsh --profile headless --dump-config >/dev/null

# 2. 把插件挂进这个 profile(本地 link,不联网)
ln -s ../../../codespaces/dsh-selfharness ~/.dsh/profiles/headless/node_modules/dsh-selfharness
#    并在 ~/.dsh/profiles/headless/package.json 里加 "dsh-selfharness" 到 dsh.profile.bundles

# 3. 跑一轮
dsh --profile headless "用 selfharness_maintain 做 bootstrap,再用 selfharness_run 跑一轮并把报告原样打印出来"

headless profile 里没有交互式批准,所以要么给 selfharness 行配 config.toolApproval: never, 要么自己接一个 answerer —— 默认的 mutating 在无人应答时会失败关闭(ask 被拒), 这不是 bug,是设计。

注意:内置示例任务(一段真有 bug 的小 JS)只够跑通链路,不够支撑显著性结论 —— 在它们上面门控通常会正确地拒绝所有候选。要看到真正的增益,需要把你自己的历史失败 会话沉淀成套件。改进幅度本身也不大(同量级工作报告在 Terminal-Bench 上是 +4.9pt), 这是受约束的有限自我修改。


3. 配置

所有字段可选,写在 profile 的 cordis.patch.yml 里:

- id: selfharness
  config:
    enabled: true
    toolApproval: mutating          # always | mutating | never
    pinnedRev: 0003-a1b2c3          # 钉住一个已知可用的版本
    agentModel: { provider: deepseek-official, model: deepseek-flash, reasoningEffort: high }
    budgets:
      maxRoundsPerDay: 4
      maxRolloutsPerRound: 400
      autoPauseAfterConsecutiveNoImprovement: 3
      phaseShares: { screen: 0.1, confirm: 0.8, recheck: 0.1 }   # 轮内预算按阶段硬闸
      noiseCalibrationTarget: 4           # 同一 rev 的基线样本数,够了才允许能力通道
      noiseCalibrationRepeatsPerRound: 2  # 每轮最多花几次基线重复来标定 δ
    gates:
      screenK: 1
      confirmK: 3
      noiseFloor: auto                    # auto = 由 (rev,model,suite,split,k) 池标定
      capabilityFloorTolerance: 0.03      # τ_cap:非劣边界(≠ δ_noise)
      capabilityAlpha: 0.05               # 单侧显著性水平
      minDiscordantPairs: 5               # 达不到就只能说「证据不足」
      targetMde: 0.05                     # 面板上的 MDE 目标
    risk: { requireHumanApprovalFor: [tool-policy, doctrine, skills, permissions, self-modification, request-policy, proposer-policy] }
    crossModel: { enabled: false, arm: { provider: xiaomi, model: mimo-v2.6-pro },
                  critic: { provider: xiaomi, model: mimo-v2.6-pro }, required: false }
    taskFactory: { enabled: true, stabilityRuns: 3, mainTaskTarget: 30 }
    population: { archiveLimit: 200, routingEnabled: false }
    requestPolicy: { enabled: false, audit: true }   # 审计始终开;策略默认关
    selfModification: { enabled: false, minStableRounds: 5 }
字段 默认 说明
rootDir <cwd>/.dsh/selfharness Pack 仓库 + 台账;这个目录本身是一个 git 仓库
dataDir $DSH_HOME/storages/selfharness selfharness.db
enabled true 全局 kill switch:false 时读面照常、写面拒绝
toolApproval mutating 只读工具不申请批准;花钱/改 Harness 的三个(run / maintain / decide)强制申请。headless 部署把它设成 never(等于「这个部署自己有闸门」)
pinnedRev 无 钉住一个版本,忽略 ledger/active.json
budgets.maxRoundsPerDay 4 每日轮次上限;熔断只挡写操作
gates.confirmK 3 阶段 2 每任务重复次数
gates.minEvidenceCount 3 缺陷成案所需的独立证据条数
gates.noiseFloor auto auto = 由 (packRev, model, suite, split, k) 基线池标定;给数字可钉死(复现实验用)
gates.capabilityFloorTolerance 0.03 τ_cap:非劣边界(2–5pt)。它与 δ_noise 是两个旋钮:δ 回答「有没有变好」,τ_cap 回答「有没有明显变坏」
gates.capabilityAlpha / minDiscordantPairs 0.05 / 5 预注册的单侧能力检验;不一致对数不够时判决是 insufficient-evidence 而不是 reject
budgets.phaseShares 0.1/0.8/0.1 轮内 rollout 按阶段硬分配;超限抛 BudgetExhausted,候选记 insufficient-evidence
budgets.noiseCalibrationTarget 4 同一 rev 的基线样本少于它时,能力通道被禁用,本轮最多只能 defer
crossModel.* 关闭 F1:异族迁移臂 + 异族审查者。enabled 不等于泛化成立——无数据时门控报 undetermined
population.routingEnabled false F3:按任务族路由到不同 rev。默认关闭时行为与单 pack 完全一致
requestPolicy.enabled false 面 D:按任务族覆盖 reasoningEffort/maxTokens。关闭时行为逐字节不变
evalPreset / proposerPreset selfharness-eval / selfharness-proposer 打在这两类会话上,经验采集据此排除它们

预算、门控、路径与预设都是插件 Config,不是运行时状态:改它们要编辑 cordis.patch.yml 并重启 Harness,面板只做只读投影。


4. 数据落地

<rootDir>/                                  # 本身是一个 git 仓库
├── packs/<rev>/                            # pack.json + doctrine.md + skills/ + tool-policy.json
│                                           #   + context-policy.json + observation-policy.json
│                                           #   + request-policy.json + proposer-policy.json
├── eval/suites|fixtures/                   # 套件与任务工作目录
├── work/                                   # 评测工作区,每 rollout 一个,跑完即删(崩溃残留按保留期清理)
└── ledger/{active.json,round.lock,rounds.jsonl,rejections.jsonl,coverage.json}

$DSH_HOME/storages/selfharness/selfharness.db   # episodes / defects / candidates / evaluations /
                                                # decisions / baselines / feedback / archive

审计 = git log,回滚 = 指针移动。


5. 面板

面板 · 收益页

首屏是**「收益」页**,它回答使用者真正打开这个页面的那个问题——这次进化给了我什么:先是一排计数 (已生效 / 实测增益 / 覆盖历史经验 / 受影响任务族 / 等你决定 / 已回滚),然后是每条已采纳改进的卡片 (针对哪个缺陷签名、命中多少条真实经验、改了哪个面、门控实测的 Δ 与区间、成本的前后对比、观察窗状态), 最后按任务族归集「对哪一类历史任务有优化」。

三处诚实性规则把这个页面和一张宣传单分开:没测过的增益显示「未测量」而不是 +0.0pt(零是测量结果, 未知不是);不同版本的增益不求和(它们是在各自轮次的留出集上配对测的,相加会造出一个没有任何门控 产出过的效应量);影响范围来自真实回合(按经验里记下的缺陷根因匹配,而不是引用候选自己的说法), 扫描有上限时明说「只覆盖最近 N 条经验」。

首屏常驻一条 MDE 横幅:套件太小就显示「不足以支撑任何显著性结论」并给出还差多少题;如果基线上每道题 得分完全一样(方差为 0),它显示「无法估计 MDE:这个套件对当前难度没有分辨力」。两种情况下都不会 为不可判定的增益画曲线——这是刻意的负向验收。评测表直接读门控当时存下的那份报告,面板不复算任何统计量。 候选卡上还有一个「逐行 diff」按钮:按需读出父版本与候选,对每个面给出折叠过的行级差异——这正是 「整面替换文本」和「看得出改了什么」之间的区别。

页头只有一个「运行一轮」按钮:一轮要跑几分钟到几小时,所以它总在后台跑,面板显示阶段、进度、rollout done/total、已用时长与心跳,随时可取消。进度在三处可见,按「离你多远」排:侧边栏「自进化」上的小圆点 (不必先打开面板)、「轮次」页顶部的进度卡片(含当前正在测哪道题)、卡片下方的最近若干次运行记录。 取消的候选记为「证据不足」而不是「否决」;进程崩溃或重载留下的「运行中」记录会被判为中断(无心跳) 并允许清理,而不会一直挂着一个停不掉的「取消本轮」。台账仍然逐行留存,上面三处只是手边的状态视图。

页头左侧的指示灯只回答一个问题——插件能不能进化:已就绪(空闲)(不会自己开轮,等你点)/ 轮次进行中 / 已熔断(连续无改进或人工暂停,写操作被拒,可在设置页恢复)/ 已关闭(插件被配置关掉:读面可用、写面全拒)。轮次结束后横幅不立刻消失,而是显示上一轮的结局与判决摘要,点「知道了」才清掉。

面板文案里不出现仓库内部的计划编号(§x.y / M7 / F1 之类)——那是内部说法,不该让使用者猜。

6. 已知边界

  1. 工具策略只能收紧:tool-policy.json 表达三层——deny(工具名)、families(能力组:shell / net / fs-write / fs-read / process / package)、argumentRules(参数级规则),三层都按集合包含单调;allow / 白名单语义仍排在 v2。
  2. 参数级规则不是沙箱,也不是防火墙:tools/pre-execute 拿到的是已解析的参数对象,所以 bash.command 是可读字符串、可以按模式拒绝(curl|wget|nc|ssh 等),并留下审计证据。但 sh -c、解释器、变量拼接都能绕过它; 真正的围栏仍只有文件系统 sandbox,而网络没有围栏。真正的泄漏防线始终是「留出集在提议阶段不落盘」。
  3. 评测 Agent 没有 per-agent 断网 API:缓解只有 fixture 不依赖网络 + verifier 注入死 proxy + 受限环境跑验证。
  4. 只做人工触发:唯一自动的动作是激活后的观察窗,且只回滚/提示、不推进;后台轮由人点、可随时取消。
  5. 跨模型是可立项的实证课题,不是既成事实:第二个 provider 已装入且凭据已配置,但门控在没有异族数据时 只会报 undetermined——「配置了第二个 provider」不等于跨模型泛化(红线 9)。
  6. context-policy 仍不能热生效(属于别的插件 Config,只产出建议值 + 重启);但 observation-policy 一分为二: inlineTokenBudget / preserveOriginal / shape 由本插件自己的 tools/post-execute 缩减器热应用且 fail-open, 只有 maxInlineTokens 是对 spill-policy 的建议值。
  7. M7(改插件自身代码)默认关闭,且打开后没有激活路径:改动只在 git worktree 隔离副本里 apply + build + test, 验证跑在死 proxy + ignore-scripts 的受限环境里,产出可复现构建哈希交人 review;正在运行的 checkout 永远不被写。 patch 只许动 src/** 与 test/**;package.json / lockfile / 组合文件一律拒绝。
  8. 不可削弱条款(F4-b)不进任何可演化面:doctrine 里的 discipline canary、preserveOriginal=true、 受保护判据键(capabilityAlpha / minDiscordantPairs / capabilityFloorTolerance / costTolerance / …) 由 applyProposal 里的硬断言守住,违反者在 schema 阶段就被拒。
  9. F5 的外部任务源不可用:本版本只交付「本仓库自身历史」这条零成本冷启动路径; issue/PR 挖掘需要网络 + 磁盘 + 离线镜像,面板如实显示为不可用,而不是留一个看似接好的 stub。

7. 测试与预检

npm test             # 242 个用例,不联网、不花预算、不需要真模型(约 24s)
npm run typecheck
npm run check:compat # 对照 DSH checkout 断言 24 个 seam + 工具归一名单全覆盖
npm run check:config
npm run preview      # 渲染 tmp/preview/*.html 并截图到 tmp/shots/

test/calibration.test.mjs 是这个仓库最重要的一条测试:用确定性 Bernoulli 生成 Δ=0 的成对任务计数, 直接调 evaluateGates(),断言零效应接受率 ≤ 3%。它锁的不是数学(统计函数本来是诚实的),而是门控的构造—— v1 的 能力 ∨ 效率 在这里会给出约 40% 的放行率。同一文件还有正向对照(真实 25pt 效应接受率 84%) 与「纯成本改善不得被接受」,所以它同时钉住了特异度、敏感度与通道独立性。

test/round.harness.test.mjs 是零花费端到端:在 service 边界上注入确定性 fake,驱动生产编排 buildRoundEngine 跑完整轮。除了原有的泄漏拒绝 / 高风险 defer / 观察窗回滚 / dry-run 之外,现在还断言: freeze 行先于任何有判决意义的 rollout、δ 来自同 rev 的池、整段 regression 都被测到、预算耗尽记 insufficient-evidence 而不是 reject、并发轮次被轮锁拒绝、过期轮锁可回收。

test/value.test.mjs 是收益页的地基:它把「这次进化收益是什么」最容易说谎的四处钉住—— 没测过的增益必须报 measured: false(而不是 +0.0pt)、影响范围必须来自真实 episode 的 signals (而不是候选自己的说法)、被回滚的改进不得计入「已生效」、active 指针不可解析时必须报 「无法判定」而不是「已被取代」。另外两条钉住刻意的特例:效率类缺陷按任务族匹配、扫描上限必须自报。

改面板时的视觉验收:npm run preview 把三个视图(外加一个强制展开的轮次)渲染成 tmp/preview/*.html, 再用本机无头 Chrome 截图到 tmp/shots/,不必重启 dsh。

样式表有一条硬规则:只引用真实存在的 DSH 主题 token。引用不存在的 token 不会报错,只会静默走 fallback—— 于是浅色下「差不多对」、深色下整个错。test/styles.test.mjs 断言这条规则(token 白名单 + 无字面颜色 + 类名全覆盖 + focus / reduced-motion),npm run check:compat 再断言白名单本身没有脱离 checkout 的主题表。

其余文件覆盖:stats(Newcombe / McNemar / 单侧检验 / MDE / 成本规则)、suite、leak、value(收益投影:实测 / 未测量、回滚、取代、指针损坏、扫描上限)、 pack(schema / 三层单调 / 内容哈希 / discipline 六条)、gates(预注册单侧判据、非劣、活跃子集、 insufficient-evidence、跨模型三态、budget-stop)、policy(工具归一 / 能力组 / 参数规则 / 注入返回值 / 观测缩减 fail-open / 请求路由开关)、factory(白名单 / k 稳定性 / 难度带 / blueprint / MDE 环 / Pareto / 路由默认关 / 冷启动)、 selfmod(patch 范围 / 稳定增益门槛 / 三方隔离声明)、diff、panel(含 GET /value 与面板 payload 的同形断言)、client(jsdom 里真实加载 lib/client.js)、reload(dev override 的补丁形状过 DSH 自己的 applyEntryPatches,脚本对临时 profile 实跑)、styles。


8. 代码地图

src/
├── index.ts            apply():装配、注册、注入(含观测面 / 请求路由 / feedback / SSE)
├── value.ts            收益:把「这次进化给了我什么」join 成一份报告(改了什么 / 实测多少 / 覆盖哪类历史任务)
├── config.ts           Config schema、默认值、kill switch、F 层开关
├── service.ts          状态与写入的唯一入口(含 MDE / δ / 档案 / 轮锁投影)
├── pack/               pack schema、discipline(F4-b)、git 版本库、原子落盘、轮锁、激活
├── guard.ts            工具策略求值:NFKC + 别名归一、能力组、参数级规则
├── observation.ts      面 C:证据保全式观测缩减(fail-open)
├── request-routing.ts  面 D:审计 + 按族路由(默认关)
├── experience/         轨迹投影、SQLite(留出集以行存在,v2 迁移)、经验回填、feedback
├── miner/              根因签名聚簇成案(含效率案)、机会估计
├── proposer/           提示与行为契约、全量编辑历史、覆盖图、实现↔审查有界回路
├── evaluator/          套件解析、Pack 注入(agent-scoped pre-execute)、worktree 评测、统计、泄漏扫描
├── selector/gates.ts   预注册单侧判据 + 能力非劣 + 效率非劣 + 风险分级
├── gate-report.ts      从 decision 行里取出「门控当时用的那份报告」(轮次页与收益页共用)
├── round.ts            一轮循环的编排 + 预算硬闸 + freeze 行 + 面板 action 表 + 后台轮
├── observe.ts          观察窗(推导证据量)与自动回滚
├── rsi.ts              RSI 六维自评(按测量而非计数)
├── taskfactory.ts      F2:白名单 / 稳定性 / 难度带 / blueprint / MDE 环
├── population.ts       F3:Pareto 父代采样、固定容量合并、路由表
├── coldstart.ts        F5:把本仓库自己的已知坑变成 public 诊断任务
├── diff.ts             面板用的行级 diff(宿主与浏览器共用同一实现)
├── selfmod.ts          F4-c:默认关闭、范围受限、受限环境验证、可复现构建哈希
├── status.ts tools.ts http.ts     状态文本、模型工具、面板 JSON API
└── client/             浏览器半边(只用主题 token,不 import 任何 Harness Client 包)

9. 许可

MIT


10. DSH 版本

实测运行的 DSH 版本是 0.1.7-rc.2:Host 半边与浏览器半边都正常挂载(sidebar.panellist 的 selfharness 入口与 main 的 selfharness key 都 active),npm run check:compat 对该版本的 24 个 seam 全部命中,npm test 242/242。升级 DSH 之后的第一件事仍然是 node scripts/check-dsh-compat.mjs <新 checkout>——它把两个最容易在升级里炸的点 (客户端平台模块表、按名字结构调用的宿主 seam)变成会失败的断言,而不是运行时的 throw。

—/ 5

No ratings yet

Verified DSH bundle

Commit 3422a4cc1117

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout