在 DSH Desktop 输入框工具行显示一个常驻胶囊:已运行时间 · 预计剩余区间。点开是浮层:当前阶段、区间把握概率、上次有效进展、各阶段实际耗时、待办权重进度、工具/模型耗时、异常与本地校准数据。
目标不是“精确到分钟”,而是让等待有依据:区间随实际进展修正,概率由真实历史回测得出,任务变难时上调,不制造假的百分比。
- 不额外调用 LLM,不往模型上下文注入内容,不注册工具。
- 不往聊天记录里追加进度消息。
- 不终止、不干预任何工具或子 Agent。
- 不把“工具调用次数”当成完成度。
- 任务开始时自动出现胶囊(空闲时 1.5 秒轮询,最迟 1.5 秒内出现首预测)。
- 点胶囊展开详情;
Esc或点面板外关闭。 - 任务结束后保留 10 分钟,显示实际用时,然后消失。
- 没有任务或宿主接口不可用时,输入框保持原样。
面板关键字段:
| 字段 | 含义 |
|---|---|
| 剩余 | 预测区间(低–高),由任务先验与经验分位共同决定 |
| 区间把握 | 这段区间在历史回测中覆盖真实剩余时间的比例,如“约 69%(n=149)” |
| 初始预测 | 任务开始时的区间,用来判断预测有没有跑偏 |
| 当前阶段 | 检查与准备 / 修改与实现 / 运行与验证 / 收尾与交付(按最近工具判断) |
| 计划 | 当前待办列表与状态(与内置待办面板同源) |
| 上次有效进展 | 时间点、距现在多久、做了什么 |
| 阶段耗时 | 每个阶段累计的墙钟时间 |
| 25/50/75% 覆盖 | 历史任务在 25%、50%、75% 进度时的区间覆盖率 |
胶囊上还会显示待办完成数(如 · 3/5)。展开面板里的“计划”与内置待办面板显示的是同一份数据;内置面板属于 ui-conversation,不是独立插件,无法单独禁用(禁用它会连聊天界面一起移除)。
“区间把握”不是模型自评,是经验频率:取历史上同样进度阶段的“实际剩余 / 预测中位数”比值,统计落在当前区间内的比例(拉普拉斯平滑)。样本少于 8 个预测点时显示“样本不足,暂不给出概率”。
从 GitHub 安装:
git clone https://github.com/iReic-coder/dsh-live-eta.git然后在 DSH 里用 plugin_manager install_bundle 指向克隆下来的目录(绝对路径)。本机已按这种方式安装为 dsh-live-eta(Profile: desktop,本地目录链接):
plugin_manager install_bundle target=<本目录绝对路径>
plugin_manager set_plugin target=include:live-eta enabled=false
plugin_manager set_plugin target=include:live-eta enabled=true
plugin_manager remove_bundle target=dsh-live-eta
- id: live-eta
name: dsh-live-eta
config:
stallMs: 240000 # 无进展多久判定“疑似停滞”
doneTtlMs: 600000 # 结束后胶囊保留时长
historyLimit: 1000 # 本地历史条数上限
storeTaskText: true # 历史里是否保存 80 字以内任务标签
dataDir: null # 默认 <DSH_HOME>/live-eta
backtestSessions: 120 # 单次回放会话数上限
backtestBudgetMs: 20000 # 单次回放时间预算一句话:类别先验 + 生存模型给出基线,待办权重与阶段份额修正工作量,最后用真实历史做分桶偏差校正和经验区间。
- 任务切分:一个 agent turn 一个任务(
turn/start→turn/end);中途用户消息算追加需求。 - 分类与复杂度:中英文关键词;长文本只看标题区(前 120 字)判断意图,避免正文里的“失败/异常”把任务误判成修 bug。
- 先验按“工作区 → 模型 → 类别”逐级收窄:同一
cwd下完成 ≥3 个任务就用该工作区的分布,否则退回类别中位数;不同项目的量级差异不再互相污染。 - 时间衰减:每条历史按 30 天半衰期加权,近期任务权重高,模型能跟上工作方式的变化。
- 先验:类别基准 × 复杂度;有历史后按
n/(n+4)与同类中位数混合;同类不足则退回静态基准。 - 无待办时:对数正态生存模型(给定已运行时间后的剩余中位数)、“先验剩余”和阶段计划三者加权;任务超过先验后保留
15% × 已用时间(上限 6 分钟)的尾部。 - 有待办时:待办按措辞赋权(测试/验证 1.5、修复 1.2、文档 0.8、其他 1.0),计划第一次出现时就用历史“每单位计划耗时”初始化速度,立即给出与计划规模匹配的估计;之后用实际完成速度做 EMA 更新。
- 阶段份额:用历史任务的阶段耗时中位数决定各阶段占比,未开始的阶段按份额计。
- 冷启动按短轮次尺度:本机历史呈双峰——多数 turn 半分钟内结束,少数跑几十分钟。任务未超过 45 秒前按“短轮次中位数”估,45→135 秒线性过渡到类别先验;一旦出现待办列表立即切到类别先验。避免对一句 10 秒的回复写“6–21 分钟”。
- 在线修正:运行中工具补足、近 3 分钟失败 (+60s/次,≤2.5 分钟)、近 6 分钟重复操作 (+60s/次,≤2 分钟)、追加需求(按先验 15%,20 分钟内线性衰减)。
- 分桶偏差校正:把历史预测按进度分三桶,计算每桶“实际剩余/预测中位数”的中位数作为乘性校正;区间由同桶 15%/85% 分位与先验半宽各半混合,再按进度阶段收窄(早/中/晚最大跨度 2.4×/1.95×/1.65×)。只放宽不收敛的区间没有信息量,所以宁可窄、让“区间把握”如实反映覆盖率。
- 概率:用同一桶的加权经验分布统计落入当前区间的比例;样本不足则不给概率。
- 防归零:超时任务保留可见尾部,不会出现“预计 0 秒但仍在跑”。
curl -s -X POST http://127.0.0.1:<端口>/api/dsh-live-eta \
-H 'content-type: application/json' \
-d '{"action":"backtest","limit":120}'- 只读:通过
sessionQuery.readSession()读取已保存会话,重放每个完成的 turn。 - 同时跑两遍:cold(只用静态先验)与 learned(用之前重放的 turn 积累先验和分桶模型),用于对比“学习后的提升”。
- 结果写入本地历史(按
会话#turn去重),因此回测一次后概率立刻可用。 - 实测:46 个会话、39 个完成 turn、16 个 ≥60 秒的可评估任务、336 个预测点,耗时约 2.3 秒。
| 指标 | 实测 |
|---|---|
| 事件处理(同步) | 3.2–7.6 µs/事件 |
| 状态接口服务端 | 0.43 ms/次(稳态),响应约 2.4 KB |
| 插件重载后首次恢复 | 约 30 ms(优先读内存中的 snapshotEvents()) |
| 若改用持久化读取(旧路径) | 约 1.5 s/次 |
| 客户端轮询 | 空闲 1.5 s、运行中 5 s、结束后 15 s |
| 回测 | 46 会话约 2.3 s,按需触发,不常驻 |
| 额外 LLM 调用 / 上下文注入 | 0 / 0 |
- 路由只接受 loopback 来源(127.0.0.1 / ::1),拒绝其它远端地址,避免移动端或局域网访问暴露任务标签。
- 只返回任务短标签、时间、阶段与计数;不返回消息正文、工具参数或文件内容。
- 历史文件只存统计字段与短标签;
storeTaskText:false可完全关闭标签。 - 插件内部全部 try/catch,异常不影响主任务;停用后客户端不渲染任何内容。
- 读取事件优先
Session.snapshotEvents()(当前版本的稳定内存读取),其次ownEvents(),冷会话用sessionQuery.readSession()。即使ownEvents()被移除,另两条路径仍可用。 - 只在
session/event上监听,不拦截tools/pre-execute、不注册系统提示,不影响 PTC、MCP 与子 Agent。 - 子 Agent 单独成表,主任务只显示数量,不混算 ETA。
- 分类是关键词规则,跨领域长任务会落在“通用/研究”。
- 工作区先验需要该目录下至少 3 个已完成任务;旧历史没有
cwd,所以工作区分桶从新任务开始积累。 - 超过历史最长任务(本机目前约 21 分钟)的任务属于分布外,区间会窄、概率会偏低。
- 没有待办列表时,生存模型只能依赖先验,任务本身偏离先验时误差仍大。
- 阶段耗时按工具类型归因,模型思考时间计入当前阶段。
- 子 Agent 不合并进主任务 ETA。
- 人工审批等待若发生在工具调用内部,会计入工具耗时。
node --test "test/*.test.mjs" # 27 个测试:引擎、校准、历史去重lib/eta-core.js:纯预估引擎(无 I/O、无框架依赖)。lib/history.js:JSONL 历史、去重、原子压缩。lib/index.js:Cordis 宿主插件、事件订阅、HTTP、回测。lib/client.js:胶囊 + portal 浮层。