Back to home

linkage18

donecheck

A plugin for DSH that checks whether your work has actually been completed. It automatically loops over the model’s responses to check for errors or shortcomings.

Stars
1
Language
JavaScript
Created
Aug 15, 2026
Updated
Aug 15, 2026

Introduction

DoneCheck(完工核查)

让 LLM 代理不再"自以为做完"——自动复查-修复循环插件,面向 DeepSeek Harness (DSH) 动态插件运行时。

LLM 代理最常见的隐性失败不是"答错",而是提前自我终止:它认为自己已经完成任务、改完了代码,实际上没有。DoneCheck 在每次回答结束后自动执行一轮带实际操作结果的复查,发现问题就带着工具权限迭代修复,直到满足要求或达到轮次上限——并全程把"做了什么、为什么停"讲清楚。

项目对外名称:DoneCheck;代码内部标识符沿用 doublecheck

适用模型:特别适合 deepseek-flash 这类轻量高速模型——速度快但更容易"自以为做完",复查-修复闭环正好把完成度兜住;对更强模型同样有效,收益随模型可靠性递减。

核心特性

  • 复查看实据:复查员能看到回答全文 + 实际操作记录(改了什么文件、命令输出、失败信息),判定的是"实际完成度",不是"两遍回答像不像";
  • 修复是真步骤:修复轮是正式会话消息,模型保留工具权限——能真改代码、真跑命令,而不是嘴上说改;
  • 多轮迭代闭环:复查-修复循环最多 4 轮,满足要求或达到上限即停止,并明确说明结束原因;
  • 来源可追溯:每轮修复新增/修改的内容按轮次标记(【复查A2补充】…),一眼看出哪段是哪一轮补的;
  • 判定可靠:复查输出 JSON 契约,三级解析回退,无法解析时保守处理——宁可不动,不乱动
  • 零侵入:不修改会话日志、不污染模型上下文、不干扰正常回合;任何失败都静默兜底。

背景:它解决什么问题

代理模型在工具型任务(改代码、跑命令、写文档)上经常犯一个隐蔽错误:任务只完成了一部分,模型却宣布完成。典型表现:

  • 改了 A 文件,B 文件的要求被遗漏;
  • 声称"测试通过",实际没跑;
  • 输出里写着"已实现",代码里根本没有。

为什么"让它再想一遍"不够?因为独立重答看不到实际操作结果——闭卷重考只能发现"问题没覆盖",发现不了"代码没改到位、命令没跑通"。DoneCheck 的核心转变:复查员带着实际结果(工具调用/输出的累积记录)去核对完成度。

工作原理

总体流程

回合结束前(agent/turn-stopping 钩子,串行等待)
  ↓
取最新回答 A_k + 原始问题 + 实际操作记录(工具调用/结果,跨轮自动累积)
  ↓
复查调用(独立模型调用,JSON 契约判定)
  ├─ completed=true 且 problems=[] → 结束(原因:满足要求)
  ├─ problems 非空 且 轮次 < 4   → 触发修复轮
  │     输入 = 原始问题 + 历轮回答全文(较老轮次截断)+ 问题清单
  │     执行 = agent.steer 注入真实步骤(模型保留工具权限)
  │     输出 = A_{k+1},新增/修改内容标【复查Ak补充】
  │     → 回合再次进入 turn-stopping → 复查 A_{k+1} → 循环
  └─ 用完 4 轮                   → 结束(原因:达到上限,最后一轮提示"务必一次解决")
  ↓
报告卡:判定链(A1 未完成 → A2 完成)、修复轮数、结束原因、问题清单、复查原文

复查协议(Review Protocol)

要素说明
输入【原始问题】+【待复查的回答】+【执行工具的实际记录】(工具调用名/参数/结果,按执行顺序,含失败标记)
判定契约严格 JSON:{"completed": true, "problems": []}{"completed": false, "problems": ["问题1(引用原文证据)", ...]}
检查要点① 每条要求是否真正完成(代码改到位?命令跑通?结果符合要求?)② 是否存在"自认为完成实际没做完"③ 有无遗漏要求
长消息锚点原始问题超长(转储型粘贴)时拆"开头/结尾"两段,优先以结尾最后提出的请求为核对目标,开头若含完整需求清单也一并核对
解析回退JSON → 旧行格式 → 保守(不判定、不误触发修复,原文展示给用户)

修复协议(Fix Protocol)

要素说明
触发条件复查发现具体问题(problems 非空)
执行方式agent.steer 注入一条真实用户消息 → 模型在真实会话步骤中修复(完整历史 + 工具权限)
输入问题清单 + 历轮回答全文(最新轮完整、较老轮次截断,总量 12000 字预算省 token)+ 原始问题
输出要求① 解决全部问题并重新核对所有要求 ② 本轮新增/修改内容标 【复查A{k}补充】 ③ 原本正确内容保持原样
终止条件复查判定"完成且无问题"(原因:满足要求);或达到 MAX_FIX_ROUNDS 上限(原因:达到上限)

多轮状态机(跨 turn-stopping)

DoneCheck 在回合关闭边界上驱动循环:每次 agent.turn-stopping 触发时,插件检查"当前最新回答"→ 复查 → 需要修复则 steer 一条修复消息并记住进度(历轮回答、历轮问题清单、修复轮数);回合继续运行,模型产出 A_{k+1} 后再次进入 turn-stopping → 复查新回答 → 继续或收尾。每回合最多修复 4 轮,steer 后立即标记该回合,绝不重复触发、绝不死循环

设计决策(为什么这么做)

设计决策理由
复查是独立模型调用(不进会话日志)不污染模型上下文,后续轮次不受复查报告影响;报告只经 RPC 展示在卡片
复查看实际操作记录,而非只重答"自以为做完"发生在操作层——代码改没改到位只有看实际结果才知道
修复走真实会话步骤(steer)正式消息、正确落日志、可被后续对话引用;模型保留工具权限,能真动手
每回合最多 4 轮成本上限明确;最后一轮提示"务必一次解决",把预算用在刀刃上
JSON 判定契约 + 保守解析判定确定可机读;解析失败宁可不修,绝不凭猜测触发修复
报告经 turnTail 卡片展示复查结论留在对话流中,用户随时可展开看原文;报告为内存态,刷新丢失(回答本身不受影响)
触发门槛(≥60 字或含代码)短回复/闲聊不复查,成本可控
判官路由可配置默认同模型(零成本);配置更强模型可突破"同模型盲区共享"限制

架构总览

┌─ Host 半段(host.js)────────────────────────────┐
│ agent/turn-stopping 监听(回合关闭前)           │
│   → 取回答/问题/操作记录                          │
│   → llm.stream 复查(JSON 契约)                 │
│   → agent.steer 修复轮(真实步骤,最多 4 轮)    │
│   → 报告存入内存 Map(上限 200 条)               │
│ harness.handle('doublecheck/report') 提供 RPC     │
└──────────────────────────────────────────────────┘
                    │ host.call(package-private RPC)
┌─ Client 半段(client.js)────────────────────────┐
│ conversation.chat.turnTail chain 条目(priority -1)│
│   → select 同步判断回合有收尾文本回答             │
│   → 拉取报告 → 渲染复查卡片(判定/轮数/原因/清单/原文)│
│   → 回合产生文件时顺带渲染文件行(不丢功能)      │
└──────────────────────────────────────────────────┘

用到的 DSH 扩展点:agent/turn-stopping(serial 事件)、llm.stream(模型调用)、agent.steer(真实步骤注入)、harness.handle/host.call(Client-Host RPC)、conversation.chat.turnTail(对话尾部 Slot)、会话事件日志(读取工具调用/结果记录)。

兜底路径(安全网)

场景兜底行为
复查调用失败(API 错误/网络)捕获 → 不触发修复,回合正常关闭,卡片"无法判定"
复查输出无法解析三级回退(JSON → 行格式 → 保守),不误触发修复,原文展示
修复轮被取消/异常状态机保留,回合安全关闭
用户中断回合(signal abort)立即中止复查/修复,干净退出
短回复/无文本回答跳过(≥60 字或含代码才触发)
判官路由配置无效回退同模型 + 警告日志
llm 服务/请求头缺失直接跳过
会话销毁清理全部内存状态(reports/turnState)
报告超过 200 条淘汰最旧
长消息问题锚点拆"开头/结尾"两段,优先以结尾最后提出的请求为核对目标

原则:宁可不动,不乱动——任何失败都不抛错、不干扰回合、不误触发修复。

效果示意(复查卡片)

自动复查  复查温度 0.2 · 修复 1/4 轮  复查发现:未完成
A1 未完成 → A2 完成
结束原因:满足要求
已生成修订后的最终回答(见上方)
标记说明:第 k 轮修复新增/修改的内容标【复查Ak补充】
最近一轮复查发现的问题:
▶ 查看详情

配置(host.js 顶部常量)

常量默认说明
REVIEW_TEMPERATURE0.2复查温度(要严格,不要创造性)
MAX_FIX_ROUNDS4最大修复轮数(A2~A5)
TRIGGER_MIN_CHARS60触发门槛:回答长度下限(含代码块时不受限)
JUDGE_PROVIDER / JUDGE_MODEL独立判官路由;留空 = 用回答模型自己当判官
MAX_ANSWER_CHARS8000复查输入中回答全文上限
MAX_TOOL_LOG_CHARS8000复查输入中操作记录上限

安装与使用

DoneCheck 是 DSH(DeepSeek Harness)的动态 Cordis 插件

  1. 在 DSH 会话中调用 cordis_defineplugin.kind = "new"idPrefix 任意 3-6 位小写字母;
  2. code.host = host.js 文件内容,code.client = client.js 文件内容;
  3. cordis_run 激活(Client 半段需授权);
  4. 之后每次回答自动复查,无需任何手动操作。

注意:这是动态插件代码(cordis_define 的函数体),不是独立可运行的 npm 包。若要以包形式分发,需按 DSH 插件包规范(packages/ 下的 cordis 插件)改装——纯运行时逻辑可直接复用。

开发规范

本仓库采用 Conventional Commits + 轻量 Git Flow(详见 CONTRIBUTING.md):

  • 提交信息格式 <type>(<scope>): <subject>,由 .githooks/commit-msg 强制校验;
  • 克隆后启用钩子:git config core.hooksPath .githooks(commit-msg 格式校验 + pre-commit 语法检查);
  • 分支:main 常绿 + feature/*bugfix/*hotfix/v<版本>-*
  • 版本:语义化版本,当前发布 v1.0.0