# DSH Evolution Console 面向 DeepSeek Harness Creator 模式的自进化闭环插件:把一次运行时修改固化为不可变候选,在隔离的 Headless DSH 中做基线/候选对照评测,通过代码门禁后自动晋升,并保留可回滚的版本谱系。 > Creator 负责提出和实现新的 Cordis Package;Evolution Console 负责版本化、评测、判定、部署和记忆。它不是一张“自进化”仪表盘,而是把修改前后真正接起来的执行闭环。 ![DSH Evolution Console](docs/images/evolution-console.png) ## 为什么需要它 DSH 的 Creator 模式已经可以在会话中创建、运行和热更新 Cordis Package,也就是修改当前 Agent 的 Runtime。但原始流程还缺少四件关键事情: 1. 修改前的能力没有被冻结成可重放基线。 2. 修改后通常靠一次人工体验判断好坏。 3. 失败版本、成功版本和评测证据没有形成长期谱系。 4. “新代码运行成功”与“新代码确实更好”没有被分开。 Evolution Console 在 Creator 与 Runtime 之间加入一个确定性的发布门禁: ```mermaid flowchart LR C["Creator 生成 Cordis Package"] --> S["固化不可变候选"] S --> B["冻结 Benchmark Suite"] B --> E1["新 Headless DSH: Champion"] B --> E2["新 Headless DSH: Candidate"] E1 --> G["代码门禁"] E2 --> G G -->|"严格提升且无回归"| P["热晋升为 Champion"] G -->|"平分、回归或错误"| R["拒绝并保留证据"] P --> M["持久化谱系与历史"] M --> C P --> X["一键回滚上一 Champion"] ``` ## 闭环包含什么 一次完整循环包括: - **观察**:直接读取当前会话的 `dynamicCordisRunner`,列出 Creator 已定义的 Package。 - **固化**:保存 Package 的 Host/Client 源码、父版本和 SHA-256 内容身份;同样内容得到同样候选 ID。 - **对照**:Champion 与 Candidate 使用同一份冻结评测集、同一 Profile、相同运行次数。 - **隔离**:每次尝试启动一个新的 Headless DSH 子进程、临时工作区和未压缩 JSONL Session Log。 - **测量**:从真实轨迹中读取最终输出、工具调用、工具错误、步数、Token 和耗时。 - **判定**:由 TypeScript 门禁判定,不让模型给自己打分。 - **行动**:通过后自动热晋升,未开启自动晋升时标记为 `eligible`,失败则拒绝。 - **记忆**:候选、评测报告、事件和 Champion 历史保存在项目自己的 `.dsh/evolution-console/`。 - **恢复**:上一代 Champion 可重新挂载,当前版本转为 `rolled-back`。 它仍然保留一个有意的边界:**目标与候选可以由 Creator/Agent 产生,但验收规则必须由人预先写进 Benchmark**。否则 Agent 同时改实现和改考卷,并不构成可信进化。 ## 晋升门禁 Candidate 只有同时满足以下条件才会通过: | 条件 | 行为 | | --- | --- | | 总分严格大于 `baseline + minImprovement` | 平分也拒绝 | | 每个任务的通过率不低于基线 | 任一任务退化即拒绝 | | Candidate 尝试没有 Runtime/基础设施错误 | 超时、未加载、Session 缺失等都拒绝 | | Candidate 不包含未评测的 Client half | v0.1 只允许 Host-only 自动晋升 | 通过门禁不等于必须立即部署:关闭“自动晋升”后,Candidate 会停在 `eligible`,可从界面或 `evolution_promote` 手动晋升。 ## 安装 插件必须同时安装到 `web` 和 `headless` Profile。Web Profile 提供控制器、RPC 和可视化界面;评测子进程需要 Headless Profile 能解析 Evaluator 导出。 ### 从 GitHub 安装 ```bash dsh plugin --profile web add github:yu-xin-c/dsh-evolution-console dsh plugin --profile headless add github:yu-xin-c/dsh-evolution-console ``` 重启 DSH Web: ```bash dsh web ``` 打开一个已有工作区的会话,顶部会出现 **Evolution / 进化** 标签。 ### 从本地源码安装 ```bash git clone https://github.com/yu-xin-c/dsh-evolution-console.git cd dsh-evolution-console pnpm install pnpm check dsh plugin --profile web add "$PWD" dsh plugin --profile headless add "$PWD" ``` 本地 checkout 通过 `link:` 安装;重新执行 `pnpm build` 后重启 DSH 即可加载新 Host bundle,Web 开发环境也可通过 DSH Client HMR 刷新。 ## 配置 Bundle 默认配置如下: ```yaml - id: dsh-evolution-console name: '@dsh-external/dsh-evolution-console' config: directory: .dsh/evolution-console automaticPromotion: true evaluationProfile: headless dshBin: dsh dshPrefixArgs: [] evaluationTimeoutMs: 600000 ``` 如果 `dsh` 没有安装到全局 PATH,可以在 Web Profile 的用户层 `~/.dsh/profiles/web/cordis.patch.yml` 覆写完整配置。下面的源码开发配置会保留每次 Attempt 的临时工作目录;把所有绝对路径替换成你的 Harness checkout: ```yaml - id: dsh-evolution-console config: directory: .dsh/evolution-console automaticPromotion: true evaluationProfile: headless dshBin: env dshPrefixArgs: - TSX_TSCONFIG_PATH=/absolute/path/to/deepseek-harness/tsconfig.json - node - --import - /absolute/path/to/deepseek-harness/node_modules/tsx/dist/esm/index.mjs - /absolute/path/to/deepseek-harness/apps/cli/src/bin.ts evaluationTimeoutMs: 600000 ``` DSH 的同 ID Patch 会替换整段 `config`,因此覆写时要保留所有需要的字段。 ## 使用流程 ### 1. 在 Creator 模式生成候选 让 Creator 使用 `cordis_define` 创建 Package,并用 `cordis_run` 验证它能挂载。最小 Host half 示例见 [examples/evolution-probe.host.js](examples/evolution-probe.host.js)。它会贡献默认 Smoke Suite 期待的 `evolution_probe` 工具。 ### 2. 固化版本 进入会话的 **进化** 标签: 1. 在“运行时包”中选择目标 Package。 2. 点击“固化”。 3. 如果它是从一个已运行 Package 更新而来,插件会先把当前 Package 固化为初始 Champion,再把新 Package 记录为其子版本。 候选源码一旦固化不会被覆盖。后续 Creator 对同一 Dynamic Plugin 再次 `cordis_define`,会产生新的 Package 和新的候选节点。 ### 3. 运行前后对照 选择 Candidate 与 Benchmark,点击“开始评测”。每个任务会按以下顺序执行: ```text Champion / 原始 Runtime -> 新 Headless 进程 -> Session JSONL Candidate -> 新 Headless 进程 -> Session JSONL -> 断言 -> 加权分数 -> 门禁 ``` Candidate Evaluator 在第一次 `system-prompt/assemble` 时装载 Package,然后重新组装 Prompt。这样新工具会进入本次模型请求,而不是等到下一轮才出现。 ### 4. 查看证据或回滚 界面会展示: - 版本父子关系、Champion、拒绝和回滚状态; - 每次评测的基线分、候选分和差值; - 每个任务的通过率、Token 和回归项; - 门禁的代码判定原因; - 捕获、评测、晋升、拒绝和回滚事件。 回滚会重新激活 Champion 历史中的上一版本,不会删除任何候选或报告。 ## Benchmark 格式 评测集是工作区内的 JSON 文件: ```text /.dsh/evolution-console/benchmarks/*.json ``` 首次打开会自动生成 `creator-smoke.json`。完整示例见 [examples/creator-smoke.json](examples/creator-smoke.json): ```json { "version": 1, "id": "project-regression", "name": "Project regression", "runsPerTask": 3, "minImprovement": 5, "tasks": [ { "id": "search-citation", "name": "Search with citation", "prompt": "Search the project knowledge and cite the source path.", "weight": 2, "timeoutMs": 180000, "workspaceFixture": "tests/fixtures/search-case", "assert": { "outputContains": ["docs/"], "outputNotContains": ["I cannot"], "outputMatches": ["docs/.+\\.md"], "toolsCalled": ["project_search"], "toolsNotCalled": ["bash"], "maxSteps": 8, "maxTokens": 12000, "noToolErrors": true } } ] } ``` 断言含义: | 字段 | 含义 | | --- | --- | | `outputContains` / `outputNotContains` | 最终文本必须包含 / 不得包含指定字符串 | | `outputMatches` | 最终文本必须匹配全部正则表达式 | | `toolsCalled` | 指定工具名必须按给定顺序出现,允许中间夹有其他调用 | | `toolsNotCalled` | 不得调用指定工具 | | `maxSteps` / `maxTokens` | 限制轨迹步数和总 Token | | `noToolErrors` | 任一 Tool Result 报错即失败 | | `workspaceFixture` | 将工作区内的文件/目录复制到本次临时工作区;越界路径会拒绝 | | `weight` | 任务进入 Suite 总分时的权重 | `runsPerTask` 最大为 5。远程模型存在随机性时,应使用多次运行并把关键行为写成轨迹断言,而不是只检查一句自然语言。 ## Agent 工具 插件也会向每个根 Agent 注册五个工具,因此 Creator 可以在同一会话里执行闭环: | 工具 | 作用 | | --- | --- | | `evolution_status` | 读取候选、Champion、Suite、最近门禁和 Creator Package | | `evolution_capture` | 固化一个准确的 Dynamic Plugin / Package | | `evolution_evaluate` | 启动基线/候选隔离评测,可自动晋升 | | `evolution_promote` | 只允许晋升最新门禁为 `eligible` 的候选 | | `evolution_rollback` | 恢复上一 Champion | 四个写操作会经过 DSH Approval Policy。`evolution_evaluate` 的确认提示会明确说明它可能产生模型费用并自动切换 Runtime。 ## 本地数据 ```text .dsh/evolution-console/ state.json # Champion、摘要、运行索引、事件 candidates/ cand-.json # 不可变 Package 源码与父版本 benchmarks/ *.json # 用户维护的冻结评测集 runs/ run-*/ report.json # 聚合分数与门禁判定 baseline-*/ # Overlay、Session Log 等原始证据 candidate-*/ ``` 数据默认跟随项目而不是某个 DSH 会话,换会话后仍能看到同一工作区的进化历史。 ## 安全边界 - 状态目录和 `workspaceFixture` 都必须位于工作区内。 - Candidate 与 Champion 使用不同的临时工作区、子进程和 Session 根目录。 - 自动晋升只接受 Host-only Candidate;Client half 需要浏览器评测,v0.1 会明确拒绝。 - Headless 子进程装载失败、超时、无根 Session Log 或 Tool Error 都计为错误,不会当成低分后继续晋升。 - 自动恢复 Champion 发生在 Prompt 工具清单组装前,避免“代码已挂载但本轮看不到工具”。 - 动态 Cordis Host half 仍是受信任代码。子进程隔离用于生命周期与评测可重复性,**不是操作系统级恶意代码沙箱**。 - “离线评测”指 Benchmark、状态和证据保存在本地。模型是否联网取决于 `evaluationProfile` 的 Provider;使用远程 Provider 仍会联网并产生费用。 ## 当前限制 - v0.1 不评测或自动部署 Client half。 - 每次 Attempt 都会启动一个 DSH 进程,可靠但不追求极限吞吐。 - 门禁目前是确定性规则,不包含显著性检验、成本 Pareto 前沿或人工盲评。 - 插件不自行发明优化目标;Creator/Agent 产生候选,用户维护 Benchmark。两者可由 Agent 工具串成自动循环,但高影响动作仍服从 DSH Approval Policy。 - Runtime 评测不是源码静态安全审计。准备公开运行第三方 Candidate 前,仍应审查代码。 ## 架构 ```text src/index.ts Host 控制器、工具注册、Champion 恢复 src/service.ts 工作区作用域、捕获、评测、晋升、回滚 src/store.ts 原子 JSON 状态、候选、Suite 与报告 src/runner.ts Headless 子进程与基线/候选对跑 src/evaluator.ts 子进程内 Candidate 准入与 Prompt 重组装 src/trace.ts Session JSONL 解析与断言 src/gate.ts 不可被模型修改的晋升门禁 src/rpc-host.ts Loopback-only Web RPC src/client/ 原生 Conversation View、谱系和任务矩阵 ``` Controller 在所有 Profile 中挂载;`DSH_EVOLUTION_CONSOLE_EVALUATOR=1` 会让评测子进程中的 Controller 保持休眠,只留下专用 Evaluator,防止评测进程再次生成评测进程。 ## 开发与验证 ```bash pnpm install pnpm test pnpm typecheck pnpm build # 或一次执行 pnpm check ``` 给空工作区生成只用于视觉开发的演示谱系: ```bash pnpm exec tsx scripts/seed-demo.ts /absolute/path/to/workspace ``` 脚本在目标状态非空时会拒绝执行,不会覆盖真实进化历史。 ## License [MIT](LICENSE)