单框输入自动路由到会话,按注意力排队待办
29.9/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/14 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add anhaoXie/dsh-client-ui-zero-friction该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
图例:✓ 通过! 需注意— 未判定 / 无数据· 「未判定」= 本站尚无数据,不代表插件有问题
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
README
由 DeepSeek 最新模型翻译生成dsh-client-ui-zero-friction
零摩擦单框交互 —— 一个真正的 DSH 客户端插件,跑在现有的 DeepSeek Harness Web GUI 里。
侧栏出现 零摩擦 入口,主区域是一个工作区:一个输入框,打完字回车,插件判断这句话该发给哪个真实会话;左侧是按「多需要你看」排序的注意力队列;右侧是学到的偏好与跨会话检索;顶部是摩擦计数器。
关于截图:开发过程中留下了 17 张真实 GUI 的验证截图,但它们拍到了真实会话的标题与结论,属于私有数据,因此没有随仓库发布。本文里的验证记录保留了每条证据的原始描述,去掉了图片路径。
它解决什么问题
对话越来越多是一个趋势,而每一次「点开某个对话框 → 想清楚该发给谁 → 切过去 → 打字」都是一次分叉。分叉是摩擦的主要来源,等待是另一个。
这个插件试着取消分叉,而不是优化它:
- 不问「你要发给谁」——判断不了就发给最可能的那个,把「不是它?改投给…」摆在手边。误投的代价被压到一次点击。
- 队列不按时间排,按「多需要你看」排。只有只有人类能推进的事才进最上面那一组。
- 摘要不用额外调模型——主机已经把 projectionValues.turnOutline 算好挂在列表快照上了。
能力与数据来源
这一版没有 mock 数据。
展开完整文档(README 剩余 1 节)
dsh-client-ui-zero-friction
零摩擦单框交互 —— 一个真正的 DSH 客户端插件,跑在现有的 DeepSeek Harness Web GUI 里。
侧栏出现 零摩擦 入口,主区域是一个工作区:一个输入框,打完字回车,插件判断这句话该发给哪个真实会话;左侧是按「多需要你看」排序的注意力队列;右侧是学到的偏好与跨会话检索;顶部是摩擦计数器。
关于截图:开发过程中留下了 17 张真实 GUI 的验证截图,但它们拍到了真实会话的标题与结论,属于私有数据,因此没有随仓库发布。本文里的验证记录保留了每条证据的原始描述,去掉了图片路径。
它解决什么问题
对话越来越多是一个趋势,而每一次「点开某个对话框 → 想清楚该发给谁 → 切过去 → 打字」都是一次分叉。分叉是摩擦的主要来源,等待是另一个。
这个插件试着取消分叉,而不是优化它:
- 不问「你要发给谁」——判断不了就发给最可能的那个,把「不是它?改投给…」摆在手边。误投的代价被压到一次点击。
- 队列不按时间排,按「多需要你看」排。只有只有人类能推进的事才进最上面那一组。
- 摘要不用额外调模型——主机已经把 projectionValues.turnOutline 算好挂在列表快照上了。
能力与数据来源
这一版没有 mock 数据。所有东西都接到了运行中的应用上:
| 能力 | 接的是什么 | 状态 |
| 会话列表 | ctx.sessions.list(Session 控制器的反应式快照) | ✅ 真实 |
| 投递消息 | ctx.sessions.using(id, …).prompt([{type:'text'}], 'queue') | ✅ 真实 |
| 打开会话 | ctx.uiWorkspace.openSession(id)(走 ctx.get 反射,不占激活依赖) | ✅ 真实 |
| 跨会话检索 | 本地搜 projectionValues.turnOutline(主机全文索引在 web/desktop profile 是关的,见坑 #4) | ✅ 真实 |
| 路由偏好 | localStorage,按天持久化,可停用 | ✅ 真实 |
| 摩擦计数 | 真实记录每一次手动切换 / 自动投递 / 纠正 | ✅ 真实 |
| 自动摘要 | projectionValues.turnOutline:主机维护的逐轮大纲,每轮带 prompt + response | ✅ 真实 |
| 自动命名 | 主机自己给的 title(LLM 生成),队列直接显示 | ✅ 由 DSH 提供 |
| 「真的在等你」判定 | userQuestions.active / plan.pending —— 真信号,不靠猜 | ✅ 真实 |
| 进度与健康 | sessionStats.turns、contextPressure、goal.objective/phase/roundsStarted | ✅ 真实 |
| 自动归入(新话题开新会话) | ctx.sessions.create({cwd}):接不住的句子不硬塞进旧会话 | ✅ 真实 |
| 路由候选集 | 只含主会话;子智能体(parentId / origin === "subagent")显式排除,标题上带 ↳ 标记 | ✅ 真实 |
| 摩擦账本 | 按天累计(localStorage),带一个可证伪的指标 | ✅ 真实 |
| 低把握发送前护栏 | 把握 < 65% 时,发送之前就在预览里警示并列出改投目标(一键选定,不拦回车) | ✅ 真实 |
| 没证据就问你 | 标题匹配不上时不再替你猜(旧版会静默"沿用上一次用过的"):按回车弹出选择面板,默认「开个新会话发出去」 | ✅ 真实 |
| 「开新会话」永远在 | 输入框边上常驻一个按钮,不理任何阈值与启发式 | ✅ 真实 |
| 语义路由(可选) | 词面对不上时把候选会话交给一个专用助手会话判断,它只回编号或 NEW;带缓存与 45 秒超时兜底 | ⚠️ 管道已真机验证,模型答案质量我验不了(需要凭据) |
| 就地汇报视图 | 选中会话即在中间栏展开它的逐轮「你问 / 汇报」;不跳页面,子智能体作为"下属"列出来 | ✅ 真实 |
关键发现:这些全都不需要读消息正文。DSH 已经把一切都算进 projectionValues 里挂在列表快照上了——零额外 RPC、零模型调用、零解析。我原本打算去啃事件窗口(reference.binding.eventSource),探针一跑才发现根本不用。
队列因此从"近似"变成了真判断:
- 🔴 真的在等你 —— userQuestions.active 或 plan.pending(即使它没在跑)
- 🟡 正在跑 —— running === true,或队列里还有活等它处理
- ⚪ 已停下 —— 其余,按最后互动时间排
- ↳ 子智能体
摘要卡片给的是:目标(🎯 objective · phase · 第 N 轮)、你问、结论、上一轮、轮次、上下文占用,外加「打开」和「让它自己总结」(后者会向那个会话投一句话,是唯一走模型的路子)。
安装
环境:Windows + DeepSeek Harness 桌面版。脚本会在 pwsh 不可用时回退到 Windows PowerShell 5.1,两者都测过。
一键方式:双击 install.cmd。它是幂等的,并且会先备份 profile、写完自校验、失败自动回滚。
install.cmd # 装进默认的 desktop profile install.ps1 -ProfileDir <路径> # 装进别的 profile install.ps1 -DshHome <路径> -ProfileDir <路径> # 完全自定义
手动方式,等价于脚本做的三件事:
1. profile 的 package.json 里 dependencies 增加一项、dsh.profile.bundles 增加一项:
{
"dependencies": {
"dsh-client-ui-zero-friction": "link:<本仓库的绝对路径>" },json
"dsh": { "profile": { "bundles": ["…", "dsh-client-ui-zero-friction"] } }
}
用 link: 而不是 file: 是刻意的:file: 会被 pnpm 硬链接复制,源码改了 profile 里还是旧的;link: 建立 Junction 指向源码,改动实时生效。
2. 用 DSH 自带的 pnpm 安装:
powershell
$pnpm = "…\DeepSeek Harness\resources\runtime\pnpm\bin\pnpm.mjs"
cd ~\.dsh\profiles\desktop
node $pnpm install
3. 这个包自己是 bundle:dsh.bundle.patch 指向 cordis.patch.yml,里面插入一行
- id: dsh-client-ui-zero-friction name: dsh-client-ui-zero-friction。
行 id、包名、浏览器模块 id 三者刻意完全一致 —— 客户端半区是挂在「说明符恰为包名」的那一 Loader 行上的,三者一致可以消掉一整类挂载失败。
dsh plugin --profile desktop add … 用不了:命令行明确拒绝,profile "desktop" is managed exclusively by the Electron application。所以走的是改 profile 文件 + pnpm 这条路。
install.cmd 里 .cmd 包装器是必需的:.ps1 默认未签名会被执行策略拦下,而且包装器保证用对编码(见坑 #5)。
启用
先刷新页面(Ctrl+R)。如果侧栏没出现「零摩擦」,再重启 DeepSeek Harness。
为什么可能刷新就够:dsh-client-modules 的宿主半区会监听 Loader 图变化并重新组合浏览器模块名单,而 dsh-hmr 的 profile 配置默认是热重载的。所以安装之后应用有可能已经重组过、刷新即可生效。
为什么也可能需要重启:模块名单是宿主半区在启动时组合进 window.__DSH_BOOT__ 的,如果那次热重载没被触发,就得重启才重编。
注意:不要用不带 token 的 http://127.0.0.1:<port> 打开界面——那只会得到 dsh web authentication required。正确的地址是 dsh web 启动时打印的那一条(带 ?token=…)。
卸载 powershell uninstall.cmd # 移除依赖与 bundle 项 uninstall.ps1 -RemoveNodeModules # 连 node_modules / lockfile 一起清
卸载后同样需要刷新或重启。
它改动了什么
只改 profile 的 package.json(加一项依赖 + 加一项 bundle),并用 DSH 自带的 pnpm 在 profile 里建一个指向源码目录的 Junction。原始文件备份在 profile 的 .install-backup\ 下。
实测出来的 API 事实
DSH 的客户端插件 API 没有公开文档,下面每一条都是在真实运行的应用里探出来的(window.__ZF_DEBUG__.dump()),不是从压缩代码里猜的。写别的 DSH 插件时能省很多时间。
| 事实 | 说明 |
| ctx.get(name) 能拿到没声明的服务 | 我用它取 uiWorkspace。ctx.get('uiWorkspace') → 有值,openSession 是函数 |
| ctx.name 取没声明的服务会抛错 | cannot get property "uiWorkspace" without inject —— 不是返回 undefined。所以漏写 inject 的后果是异常,被 try/catch 吃掉后就变成"功能静默失效" |
| sessions.list.getSnapshot() → { ids, byId, phase } | byId 每项:id / displayTitle / running / blank / updatedAt / cwd / retainedBy / parentId / origin / projectionValues |
| sessions.using(id, { source }, cb) | cb 收到 reference;reference.binding = { sessionId, session, eventSource, ctx };投递用 binding.session.prompt([{type:'text', text}], 'queue') → {ok:true} |
| sessions.create(opts) | 直接返回新会话 id 字符串,失败抛 SessionCreateError。opts 是 { workspaceId } 或 { cwd } |
| sessions.search(query) | 本 profile 下不可用:{ok:false, error:{code:"gateway/internal"}}(全文索引 openAt: never) |
| projectionValues.turnOutline | 逐轮 { turn, seq, prompt, response } —— 现成的摘要来源 |
| projectionValues 其它有用字段 | title / goal(objective,phase,roundsStarted) / sessionStats.turns / contextPressure / userQuestions.active / inbox / plan.pending / sessionListMetadata.lastPromptAt |
| inbox 的真实语义 | 装的是排队等智能体处理的消息(含你自己刚投的那条),非空代表"智能体还有活要干",不是"在等你"(见坑 #8) |
| eventSource.getSnapshot() | { entries, hasMore, revision, change },entry 是原始事件 {type:"event", event:{type, seq, time, data}}(不是渲染好的消息,别指望直接读文本) |
| 客户端 bundle 形态 | window.__ModuleLoader__.load({ id: 包名, factory: (require) => {...} }),可 require("react") / require("react/jsx-runtime"),无需构建工具链 |
| 模块 URL | plugins/??<id>/client.js&rev=<hash> —— ?? 既是路径也是查询分隔符,少 rev 必 404 |
| bundle patch | 包根放 cordis.patch.yml,package.json 里 dsh.bundle.patch 指向它;包名加进 profile 的 dsh.profile.bundles |
| ctx.get("remote") 是主机调用通道 | ClientRemoteService,方法有 invoke / invokeMethod / invokeSelected / invokeStream / openRemoteStream / createNamespace …;remote.namespaces 是已注册的主机命名空间清单 |
| 主机命名空间清单(实测) | llm session subagents agentPresets credentials account settings goals commands schedule skills job workspace workspaceFiles terminal fileUploads userQuestions pluginManager … 每个外面包一层 { service, dispose } |
| llm 命名空间只有列模型/列供应商 | 直接方法只有 discoverModels / listProviders / listConfigurableProviders,没有补全方法。所以客户端唯一的模型入口是 session.prompt —— 语义路由必须借一个会话当分类器 |
| session 命名空间的方法 | create / prompt / list / projections / search / rename / fork / follow / control / cancel / modelCatalog / selectModel / updateQueue … |
踩过的坑
这十条都是真踩过的,其中几条差点让功能「看起来能用其实永远是坏的」。
1. Cordis 只暴露声明过的服务 —— 差点让整个面板变成空的
第一版我写的是 const inject = ["slots"],但代码里用了 ctx.sessions。Cordis 只把 inject 里声明过的服务挂到插件上下文上,没声明的就是 undefined。
后果极具欺骗性:ctx.sessions 是 undefined → 代码走防御分支 → 面板正常渲染,只是永远显示「会话 0」、永远发不出消息。和一个「确实没有会话」的空实例表现完全一样。
修法:const inject = ["slots", "sessions"]。参照系是 DSH 自己的插件: ui-conversation 声明 ["slots","sessions","fileUpload","uiSession","uiWorkspace","locale","configForms"], ui-workspace 声明 ["slots","sessions","workspaces","locale","remote",…]。
uiWorkspace 刻意不声明——它只在点「打开会话」时用到,晚到无所谓,走 ctx.get("uiWorkspace") 反射读取,少一个激活依赖。
tools/verify.cjs 里有一条静态检查专守这个:扫出 bundle 里所有 app.X / ctx.X 访问,凡是不在 inject 里的就报错。
2. 我一度得出「必须重启」的错误结论
因为 GET /plugins/<包名>/client.js 返回 404。那个 URL 是错的。 真实 URL 是 combo 形式:
plugins/??dsh-client-ui-zero-friction/client.js&rev=a740b732a93a
?? 本身既是路径也是查询分隔符,且必须带精确 rev。少了 rev,任何模块都 404,与插件装没装无关。这条假证据一度被当成了结论。
3. 测试台曾经「印证」了一个错误的返回值假设
真机实测发现:会话确实建出来了,但界面报「投递失败:新建会话没有返回 id」。
原因是猜 sessions.create() 返回 { ok: true, value: { sessionId } },而真机是直接返回 id 字符串、失败时抛错(ClientSessions.create 的最后一行就是 return result.value.sessionId)。
真正的问题不在猜错,而在假 sessions 服务也用了我猜的那个结构 —— 于是 49 项回归全绿,却完全测不出这个错误。测试台在用我的假设验证我的假设。
修法:把假服务改成照抄真机契约(返回字符串、失败抛错),并在注释里写明「必须照抄,否则测试台会印证错的假设」。
教训:mock 的返回值必须来自真机观测,不能来自「我觉得应该是这样」。
4. 「跨会话检索」曾经是一个永远返回空的功能
第一版直接调 ctx.sessions.search(query),README 里还写着「接主机的内容索引」。真机一探: json { "ok": false, "error": { "code": "gateway/internal", "isDSHRemoteError": true } } web/desktop 配置档的主机全文索引是关闭的 —— dsh-web-app 的补丁把 session-query-sqlite 设成了 openAt: never("Full-text session search is opt-in")。所以那个功能一次都没成功过,而且在界面上表现得只是「没命中」——静默失效,比报错更糟。
修法:改成本地检索。主机已经把每一轮的 prompt/response 算进 projectionValues.turnOutline 了,直接在内存里搜即可 —— 零 RPC、零延迟、不依赖任何主机开关。顺带把「边打边搜」做了(不用按回车),并加了「探测主机索引」按钮:它把主机的真实回答原样报出来,而不是让功能继续静默失效。
教训:一个依赖外部开关的功能,必须有一条路径能让它把「我这里没开」说出来。
5. 安装脚本会把应用弄到起不来(写 BOM)
第一版 install.ps1 用了: powershell $json | ConvertTo-Json -Depth 12 | Set-Content $pkgFile -Encoding UTF8
在 Windows PowerShell 5.1 下,Set-Content -Encoding UTF8 会写进 UTF-8 BOM。而带 BOM 的 JSON,DSH 的 Loader 解析不了:
❌ Unexpected token '', "{ "n"... is not valid JSON
.cmd 包装器在找不到 pwsh 时会回退到 powershell(5.1),所以这条路真的会被走到。如果当初直接双击 install.cmd,重启后应用就起不来了——而症状会指向 DSH 本身,不是这个脚本。
三个修法叠在一起:
1. 写文件改用 [System.IO.File]::WriteAllText(... UTF8Encoding($false)) —— 明确不要 BOM
2. 写后自校验:读回来检查首字节不是 BOM、并且 ConvertFrom-Json 能解析;失败就立刻用备份回滚并中止
3. .ps1 本身加 UTF-8 BOM —— 因为 5.1 读 BOM-less 文件按 ANSI 解,中文注释会让它报「括号不匹配」
顺带发现 .cmd 里不能写中文注释:cmd.exe 按 OEM 代码页读 .cmd,中文字节会被当成命令。
教训:一个会修改「应用启动所依赖的配置」的脚本,必须自己校验自己写出来的东西,并且能回滚。
6. 测试台渲染了「上一个组件」的状态,让规模测试说谎
写规模用例时构造了 200 个会话 × 40 轮,结果它 2ms 跑完、摘要卡片 0 张——因为实际渲染的是上一次那个 4 会话的组件状态。
原因:mini-React 把 hooks 按调用顺序存在一个数组里,渲染第二个组件时没有重置。两个组件的 hook 序列不同,于是 useSyncExternalStore 读到了上一个组件留下的快照。
修法是加 RUNTIME.reset(),并且在渲染另一个组件前必须调用。加完之后规模用例才真的开始测 200 个会话——立刻又抓出两个真问题:摘要面板没封顶(200 张卡片)、队列的「正在跑」分组没封顶(53 张卡片)。
教训:测试台自己出错时,比没有测试台更危险——它会给你绿灯。
7. 「连点两次回车」的竞态,以及测试台一度测不出它
零确认界面里没有对话框兜底,所以同一条消息投递两次是很伤的。闸门原来用的是 React state: js
if (text === "" || busy) return; // busy 是 state
state 是异步的:两次事件落在同一次渲染周期里,会都读到 busy === false,于是投递两次。改成同步的 busyRef,并且把整个函数体放进 try/finally(否则路由里任何一处意外抛错,busyRef 就永远停在 true,面板从此再也发不出消息)。
有意思的是测试这条的过程中,测试台骗了我两次:
1. 第一版测试捕获同一个按钮元素点两次 —— 抓到了 bug。但后来「改进」成每次重新取元素,测试就再也抓不到了:mini-React 在 setState 时同步重渲染,第二次取到的是 busy=true 的新闭包。真实 React 是批处理的,不会这样。
2. 更要命的是,我一度以为这条测试有效——直到按纪律把 busyRef 删掉重跑,它照样全绿。
所以那条测试现在是刻意「捕获同一个闭包」的写法,注释里写明了为什么必须这样。
由此得出的纪律:每一条守卫 bug 的测试,都必须先证明它在未修复的代码上会变红。 做不到这一点,那条测试就只是一句安慰。
8. 我把 inbox 的语义搞反了 —— 而且离线测试一直在替我背书
「真的在等你」这个分组里,原来把三个字段都算作「需要你」: js
if (activeQuestions.length) needs.push(...); // agent 提问 → 只有你能答 ✓
if (inboxCount) needs.push(inboxCount + " 条排队中"); // ✗ 语义反了
if (planPending) needs.push(...); // 计划待批 → 只有你能批 ✓
真机实验(让会话进入 running,再投一条,看投影怎么变)给出了答案。inbox 里装的是: json
"next-turn": [ { "content": [ { "type": "text",
"text": "……第二条:这条应该被排队" } ] } ]
那是刚投进去、排队等 agent 处理的消息。 inbox 非空意味着「agent 还有活要干」,不是「你在等」。
后果很实在:那个会话本来在正确的「🟡 正在跑」,被代码抬到「🔴 真的在等你」并标上「需要你:1 条排队中」——而它什么都不需要你做。注意力队列最不能有的就是这种噪音。
更正后:needs 只留 userQuestions.active 与 plan.pending(两者都意味着 agent 停下来等你);有排队工作的会话归入「正在跑」,并以中性提示「队列里还有 N 条等它处理」呈现。
这件事的方法论意义比 bug 本身大:那条离线用例之所以一直是绿的,是因为它断言的就是我那个错的信念——假数据由我构造,于是它忠实地印证了我的误解。只有拿真机投影做实验才能推翻它。
顺带一提,这个实验不需要凭据:让会话进入 running、再投一条,排队就会发生——「没有模型」并不妨碍验证数据模型。
9. 学习回路在第一次真机使用时,就学出了一条会「越用越错」的规则
「改投一次 → 记住这个特征」是这个插件的核心承诺。它从没在真机上点过,所以去点了。
机械上是通的(重投成功、纠正计数 +1、账本记下),但持久化里躺着的规则是: json { "match": "工具", "sessionId": "…视频摘要与口语练习工具" }
消息是「每日领域前沿信息自动报告工具 再加一个数据源」,改投目标是「视频摘要与口语练习工具」。两者唯一共享的 2 字片段就是「工具」。于是以后任何带「工具」的句子都会被吸到那个会话——包括「帮我做个工具…」。
这条规则会持续地把东西投错地方,而且它会以「我学聪明了」的面目出现。
改法(两层):
1. 关键词只能来自「消息与目标标题真正共享的片段」,删掉「从消息里任意取一段中文」的回退路径 —— 那条路会学出与目标毫无关系的词。
2. 排他性检查:这个片段如果在别的会话标题里也出现(或属于明显泛化的词表),就不写规则。
学不到的时候如实说:「这句话和「X」没有共同特征,所以只改了这一次。一条不准确的偏好会长期把东西投错地方,比这次不学更糟。」
取舍说明:这样一来能学到的规则变少了。这是刻意的——一条错的规则会静默地长期投错,而少一条规则只是让你再纠正一次。
10. 一个看起来像插件崩溃、其实是测试点错了的假警报
第一次跑改投实验时,点完候选之后面板整个消失。看起来像渲染崩溃。按纪律去抓浏览器异常——一条都没有。
没有异常才是线索:面板消失不是崩了,而是应用导航走了。点击助手搜的是整个文档,而 DSH 自己的左侧会话列表里有同名会话、且它在 DOM 里更靠前 —— 点中的是原生侧栏。
把点击范围收进 .zf-reroute 之后一切正常。
教训:在宿主页面里做测试,选择器必须限定在自己的容器内;宿主的 UI 里到处是同名的东西。
11. 「让匹配读懂意思」 —— 我试了内容匹配,真机两轮把它否掉了
有人指出路由「匹配度不太对,希望读懂意思,而不是文字表面匹配」。当时的算法是: js
for (const gram of grams(row.displayTitle)) // 只把会话标题切成 2/3/4 字片段
if (text.includes(gram)) score += gram.length;
三个缺陷:只比标题不比内容;没有证据时静默替你选(「沿用上一次用过的」);纯字面。
第一步我加了内容匹配:把会话最近几轮的内容也切成词元,消息在标题里找不到证据时再去内容里找。听上去对,真机跑了两轮:
第一轮(词元取 2/3 字):太松。「量子计算机的散热方案该怎么设计」被一个无关会话以「内容匹配 · 62%」吸走——因为「怎么」「设计」「方案」在任何长会话里都有。一个 62% 的假阳性比一句「没把握」更糟,因为它会跳过询问。
第二轮(词元改成 3/4 字):该匹配的匹配不上了。「归档是怎么回事」不再命中任何会话——而「归档」恰恰是那个会话真正的关键词,只是它只有 2 个字。
结论:字面匹配在长中文文本上无法区分「有意义的短词」和「到处都有的虚词」。 所以最终做法是把它降级——内容相似度只排序,不决定:
- 路由的决定只看标题匹配与学到的偏好;两者都没有,就是「没把握」,交给人
- 内容相似度只用来给询问面板排序(回答「如果是这几个,哪个最像」)
代价是「读懂意思」这件事没有真正做成,这里不含糊:真正的语义路由要过模型。插件侧没有可用的语义服务,唯一能过模型的路是「往某个会话投一句话」,那要花掉一轮、还有延迟。所以现在的答案是:宁可问你一次,也不假装懂你。
顺带一个方法论的教训:这两轮失败都不是靠推理发现的,是在真实会话上跑出来的。离线夹具里没有长中文内容,第一轮的假阳性根本不会出现——type() 一句话,真机就把它打回来了。
12. 语义路由:先探清「能不能不污染会话地调模型」,答案是不能,于是造了个专用会话
有人明确要求「读懂意思」。我先把客户端能拿到什么都探了一遍(探针就是 __ZF_DEBUG__.probe()):
- ctx 是个 Proxy,只暴露 fiber / baseUrl
- ctx.get("remote") 是主机调用通道,remote.namespaces 列出全部主机命名空间: llm session subagents credentials account goals skills terminal …
- llm 命名空间只有 discoverModels / listProviders / listConfigurableProviders —— 没有补全方法
所以客户端唯一的模型入口是 session.prompt。想用模型判断路由,就必须有一个会话。
于是做法是造一个专用助手会话(不是拿你的会话当分类器——那正是最开始要避免的污染):
候选会话:
1. 零摩擦会话插件|要做,开始吧,因为这样匹配度根本达不到我的要求…
2. 前沿信息获取|现在给你说清楚,这俩完全不是一回事…
3. DSH询问|结论:DSH 根本没有"删除会话"功能,只有"归档"…
用户这句话:「量子计算机的散热方案该怎么设计」应该发给几号?都不像就回 NEW。只回一个编号或 NEW。
配套的四件事,每一件都是被真机或测试台打回来才补上的:
1. 防抖 800ms。不然打字过程中每个字符都会触发一次分类。
2. 先同步占位再 await。ensureRouterSession 里有异步创建,不先占位的话并发调用会各建一个助手会话——测试台抓到过 12 个。
3. 防抖 effect 不能依赖 rows。第一版放进去了,而 store 每发布一次就重排一次定时器——真机上就是「每来一次通知就多问一次模型」,那是烧 token。测试台直接炸出 20 条分类请求。
4. 助手会话本身要从投递候选里剔除。真机第一次验证就冒出 发给「你是「零摩擦」插件的语义路」——拿插件的工具当对话框是错的。
我验不了的部分:模型答案的质量。隔离实例没有凭据(我也不该拿你的凭据去跑),所以「它答得准不准」只能由你在真机上判断。我验到的是管道:请求确实发出去了、候选列表与目标句确实在里面、不回答时 45 秒如实退回、按回车绝不静默投错。
变异审计:证明每条守卫测试都会红
第 7 条那个纪律光立不够,所以这里把它做成了可重复运行的审计: powershell tools\mutation-audit.cmd # 逐个把修复退回去,跑套件,看是否红在预期的测试上
十四个变异,十四个都红在它该红的那条上:
| 变异(把修复退回) | 红在哪条测试 |
| 1 去掉 sessions 声明 | 声明了 sessions 服务 |
| 2 create 返回值解析退化 | 新话题真的开了一个新会话 |
| 3 路由不再排除子智能体 | 命中子智能体标题时也不会投给它 |
| 4 本地检索返回空 | 边打边搜:不需要回车就已经出结果 |
| 5 摘要面板不封顶 | 摘要面板默认封顶在 12 张(实际 200) |
| 6 子智能体不带 ↳ 标记 | 子智能体在标题上带 ↳ 标记 |
| 8 检索结果不截断 | 规模检索命中并被截断渲染(实际渲染 8200 行) |
| 9 HUD 去掉「本该打断你」 | HUD 显示「本该打断你 N 次 · 实际 0 次」 |
| 10 默认页签改成偏好 | 摘要面板默认展示 |
| 11 账本表去掉「本该打断」列 | 账本表头是 日期/投递/新建/纠正/本该打断 |
| 12 封顶提示去掉数字 | 封顶处给出「还有 N 个」提示 |
| 13 把排队当成需要你 | 有排队工作的会话不算「需要你」 |
| 14 关键词不再要求与标题有共同特征 | 有共同特征时学到规则,且指向被改投的那个会话 |
| 15 去掉排他性闸门 | 特征在别的会话标题里也有时,不写规则 |
| 16 去掉发送前的低把握警示 | 把握低于 65% 时,发送前就出现警示 |
| 17 选中不再钉住发送目标 | 钉住后真的投给被钉住的会话 |
| 18 看汇报改回跳页面 | 点「看汇报」不会打开原生界面 |
| 19 汇报视图不显示下属 | 汇报视图里点名了它手下的子智能体 |
| 20 询问面板不按内容排序(退回按时间) | 面板把内容最接近的排在第一位 |
| 21 没证据时不问、直接发 | 没证据时按回车不发送,而是把选择面板摆出来 |
| 22 没证据时退回"选上一次用过的" | 没证据时如实标注「没有把握」 |
| 24 语义路由从不发问 | 没证据时会去问路由助手 |
| 25 不复用助手会话(每问一次建一个) | 第二次问仍然复用同一个助手会话 |
| 26 不采纳助手的回答 | 助手答了编号 → 预览采纳为「语义路由」 |
| 27 分类请求不再要求只回编号或 NEW | 并且结尾就是那句指令 |
第 07 条已移除(编号保留以示来历):它守的是"剥尾部虚词"的分支,而第 9 条那个改动删掉了"任意取一段中文"的回退路径之后,这个分支成了防御性代码,现有夹具再也触发不到。按纪律,审计里不留不会红的条目。
第 28 条同样已移除:它守的是「路由助手会话不该成为候选」,但夹具里会话多、助手会话排不进前 8,变异在夹具上不会红(试过)。该守卫改在真机上验证——会话少时它确实会冒出来(第一次真机验证就抓到了)。
第 23 条同样已移除:它守的是"内容匹配的最小片段长度",而第 11 条那个改动把内容相似度降级成"只排序、不决定"之后,这个长度不再影响任何决定,夹具触发不到它。
审计本身抓到两件事
一是弱断言。 原来「边打边搜」断言的是整页文本里含会话标题——可队列里本来就有那个标题,于是检索返回空它照样通过。同理「摩擦计数器显示『二次确认 0』」:那个词早就从 HUD 里换掉了,它能通过是因为输入框下方的提示文案里有「不做二次确认」——它测的是一句文案。这两个都属于同一类:断言的字符串在别处也出现。
二是"失败时把整轮跑挂"。 变异 10 让套件直接崩溃而不是干净地变红。根因是一行详情表达式: js "第一张: " + JSON.stringify(...).slice(0, 150)
JSON.stringify(undefined) 返回的是 undefined 值而不是字符串,.slice 就抛了。这条检查恰恰在它失败的时候把整轮跑挂——比弱断言更糟,因为它连"哪里坏了"都不告诉你。修完,同一个变异从"崩"变成干净的 9 条失败。
审计工具自己也踩了一次:它最初没有 finally 恢复,变异 10 一崩就把源码留在了变异状态。现在恢复写在 finally 里,而且子进程写 stderr 不再中止审计。
验证记录
不是"我觉得能跑",是真的在运行的 DSH 里跑过。下面每一条都是实际执行过的。
| # | 验证项 | 证据 |
| 1 | 官方 CLI 接受这个包并安装 | 在隔离 profile 里 dsh plugin --profile zfcheck add <路径> → + dsh-client-ui-zero-friction,exit 0,自动用 link: |
| 2 | 组合进 profile 树 | dsh --profile zfcheck --dump-config → exit 0,行 - id: dsh-client-ui-zero-friction |
| 3 | 进入真实浏览器模块名单 | 从运行中服务器的 __DSH_BOOT__ 里读出 {"id":"dsh-client-ui-zero-friction","url":"plugins/??dsh-client-ui-zero-friction/client.js&rev=a740b732a93a","rev":"a740b732a93a","inject":["@deepseek-ai/dsh-api-session-controller"]} |
| 4 | 模块真的能取到 | 用上面的 combo URL 请求 → HTTP 200,27022 字节,内容就是 window.__ModuleLoader__.load({ id: "dsh-client-ui-zero-friction" … |
| 5 | 浏览器里真的激活 | 无头 Chrome 打开真实 GUI → 侧栏出现 零摩擦 |
| 6 | 主面板真的渲染 | 点开后完整出现工作区标题、摩擦计数器、三列布局、输入框与发送按钮 |
| 7 | 降级路径在真实 GUI 里成立 | 零会话时回车 → 界面原样输出「还没有任何会话,先在正常界面里开一个对话。」 |
| 8 | 逻辑回归 | node tools/verify.cjs |
| 9 | 修正 inject 后插件仍能激活 | 隔离实例里侧栏「零摩擦」照常出现——这本身就是声明正确的证明:Cordis 等不到 sessions 就根本不会挂载这个插件 |
| 10 | 真实会话进了队列 | 新建一个会话后,面板显示 会话 1,队列出现「已停下,等你看 / default-workspace」 |
| 11 | 真实路由 | 输入后路由预览实时显示「→ default-workspace · 延续当前会话 · 50%」 |
| 12 | 真实投递成功 | 回车后回执显示「已投递给「default-workspace」」,并且 DSH 自己的左侧会话列表同时出现了这条消息,还据此自动给会话起了标题 |
| 13 | 摩擦计数器 | HUD 显示 自动投递 1,与第 12 项那次真实投递一致 |
| 14 | 真实会话全部读到 | 把真实 sessions + storages 复制进隔离 DSH_HOME 后,队列显示 8 个真实会话 |
| 15 | 真实自动摘要 | 摘要面板对每个真实会话给出:目标(active · 第 3 轮 / complete · 第 1 轮 / blocked · 第 40 轮)、你问、结论、上一轮、轮次、上下文占用 |
| 16 | 摘要内容正确 | 抽出的结论与真实会话内容一致,例如「LB idle timeout 已调平」「88 项操作全部成功,0 失败」「五个功能全部做完并端到端验证通过」 |
| 17 | 「真的在等你」由真信号驱动 | 离线用例:带 userQuestions.active 的会话即使 running === false 也进入该分组,卡片显示「1 个问题等你回答」 |
| 18 | 真实自动开新会话 | 真机输入新话题 → 会话数 2→3、回执显示「新话题 · 自动开了新会话」、HUD 新建会话 1 |
| 19 | 真实摩擦账本 | 真机账本页显示 10-12 投递 1 新建 1 纠正 0 本该打断 1,合计行一致 |
| 20 | 「本该打断 vs 实际打断」可证伪 | 同一次投递里把握度 35%(旧设计会问),HUD 显示「本该打断你 1 次 · 实际 0 次」——这个数字会随真实使用增长,所以「0 次打断」不是空话 |
| 21 | 修掉 create 契约错误后重验 | 见坑 #3:这个 bug 是真机测出来的,测试台当时是全绿的 |
| 22 | 探出检索是死功能 | 真机 search('502') → {ok:false, error:{code:"gateway/internal"}}(见坑 #4) |
| 23 | 本地检索在真实大纲里命中 | 真机搜「零摩擦」→ 命中「… · 你问 · 第 2 轮」,高亮 1 处;零 RPC |
| 24 | 主机索引如实报告 | 点「探测主机索引」→ 日志原文「主机全文索引在这个 profile 里是关闭的(gateway/internal)」 |
| 25 | 在真实 desktop profile 的副本上完整工作 | 把 ~/.dsh/profiles/desktop 整体复制到隔离 DSH_HOME 并从 CLI 启动(不是 web 模板)→ 侧栏入口、8 个真实会话、自动摘要、goal 状态(active · 第 6 轮)全部正常 |
| 26 | install / uninstall 端到端可用 | 在一个全新 profile 上跑完整循环:安装 → dump-config exit 0 且行入树 → 卸载 → 行移除且 JSON 始终可解析、无 BOM |
| 27 | 安装器的自校验真的会回滚 | 见坑 #5:BOM 那个 bug 就是被这条检查暴露的 |
| 28 | 「打开会话」真机可用 | 点摘要卡片的「打开」→ 主视图切走了(面板还在: false),并显示出那个真实会话的完整对话记录。路由 → 投递 → 打开,三件事全部真机验证完毕 |
| 29 | 打开失败不再静默 | openSession 抛错时会在日志里显示「打不开这个会话:…」;离线用例覆盖(含一条把 openSession 换成会抛错的桩的用例) |
| 30 | 规模:200 个会话 × 40 轮 | 渲染 2ms、不抛错;「真的在等你」19 个全部渲染(刻意不封顶),其余分组封顶(共 35 张卡片,不是 200 张);摘要面板封顶 12 张并给出「显示全部 200 个」,点开后确实渲染 200 张 |
| 31 | 规模下真机仍正常 | 复制真实会话启动隔离实例,队列与摘要照常渲染 |
| 32 | 子智能体不会被当成投递目标 | 一条通篇在说子智能体标题的消息,不会被投进那个子智能体;改投候选里也没有它。离线用例覆盖(真机测不了:需要一个真的子智能体,那要有模型) |
| 33 | 过滤改动没误伤主会话(真机) | 复制真实会话启动实例:无匹配的消息 → 判定「新话题」并真的开了新会话;匹配标题的消息 → 标题匹配 90% → 成功投递。两条分支都在真机上验过 |
| 34 | 连点两次只投递一次 | 见坑 #7:这条测试在未修复的代码上确实是红的(实际投递 2 次),修复后为 1 次 |
| 35 | 失败之后闸门一定被释放 | 让 sessions.create 抛错走真实失败路径:如实报错、不误投到别的会话、草稿被还回来,且下一次投递仍然成功 |
| 36 | 规模检索的成本 | 200 会话 × 40 轮(8000 条大纲)下一次按键 13–18ms,命中被截断到 60 行渲染 |
| 37 | 守卫测试本身是真的 | 变异审计:全部红在预期的测试上。tools\mutation-audit.cmd 可重复运行 |
| 38 | 审计抓出弱测试并修掉 | 见「审计本身抓到两件事」 |
| 39 | 审计扩到 12 个变异,全中 | 新增的 4 个针对加强过的断言(HUD 指标 / 默认页签 / 账本表头 / 封顶提示) |
| 40 | 修掉"失败时把整轮跑挂" | 变异 10 曾让套件崩溃而非干净变红。修完变成干净的 9 条失败 |
| 41 | 审计工具自身的健壮性 | 恢复源码写进 finally;子进程 stderr 不再中止审计;套件崩溃单独归类 |
| 42 | 投递真的启动了一个 agent 回合(真机) | 经插件投一条消息进真实会话后:running 从 false 变 true,turnOutline 从 18 条涨到 19 条,lastAgentError/promptError 仍是 null。不只是 prompt() 返回 ok |
| 43 | 「🟡 正在跑」分组真机生效 | 同一个会话在投递后实时从「已停下」移到「正在跑」,卡片显示「在跑 · 刚刚 · 16 轮 · 上下文 62%」 |
| 44 | 摘要随回合实时增长 | 新回合一进 turnOutline,摘要面板第一张卡片立刻出现新的「你问」。摘要不是快照,是跟着主机投影走的 |
| 45 | 真机实验推翻了 inbox 的语义 | 让会话 running 后投第二条 → inbox.next-turn 里是刚投进去的那句话(见坑 #8) |
| 46 | 修正后真机复验 | 有排队的会话留在「🟡 正在跑」,显示中性提示「队列里还有 2 条等它处理」;不再出现在「真的在等你」 |
| 47 | 变异审计扩到 13 个 | 新增变异(把排队重新算成"需要你")→ 红在对应测试 |
| 48 | 改投交互真机跑通 | 点「不是它?改投给…」→ 候选列表正确(排除被改投的那个、排除子智能体、含「取消」)→ 重投成功、纠正 1、账本记下 |
| 49 | 真机发现学习回路会学出泛词 | 第一次真机改投学到的关键词是「工具」,会劫持以后所有带「工具」的句子(见坑 #9) |
| 50 | 修正后真机复验 | 同一场景:rules: []、learned: 0、纠正 1——不再写出那条泛词规则;下一句正常路由,没有被坏规则劫持 |
| 51 | 变异审计扩到 14 个 | 新增两个变异均红在对应测试上;同时删掉已过时的第 07 条并注明原因 |
| 52 | 最终验收:15/15(真实 desktop profile 的副本) | 面板加载 → 真实队列 → 真实摘要 → 本地检索命中真实大纲 → 投递回执 → 改投候选 → 纠正记账 → 不学泛词 → 摩擦账本 → 打开会话切换主视图 → 全程零未捕获异常 |
| 53 | 低把握发送前护栏(真机) | 输入「改版的事情」→ 预览显示「延续当前会话 · 50%」→ 出现警示「⚠ 把握只有 50%,可能投错」+ 三个一键改投按钮(列出真实会话标题);此时没有发送 |
| 54 | 发送前改投(真机) | 点「改投「零摩擦会话插件」」→ 预览变成「→ 零摩擦会话插件 · 你指定的 · 100%」,警示消失,仍然没有发送 |
| 55 | 就地汇报视图(真机) | 点队列卡片 → 中间栏展开该会话的逐轮「你问 / 汇报」(真实内容),头部显示「已停下 · 16 轮 · 上下文 13%」+「改回自动」「原生界面打开」;面板没有被导航走(document.querySelector('.zf-root') 仍存在) |
| 56 | 改回自动(真机) | 点「改回自动」→ 汇报收起、预览恢复「延续当前会话 · 50%」,回到自动路由 |
| 57 | 没证据就问你(真机) | 输入「量子计算机的散热方案该怎么设计」→ 预览「没有把握 · 没有任何会话和这句话有共同点 · 20%」→ 回车不发送,弹出面板:[开个新会话发出去] [发给「DSH询问」] [发给「零摩擦会话插件」] [发给「default-workspace」] [取消];日志确认没有回执 |
| 58 | 选了就真的开新会话(真机) | 点「开个新会话发出去」→ 回执「已投递给「新会话」· 没有像的,你选择开新会话」,会话数 8→9,HUD 新建会话 1 |
| 59 | 账本新增「问你」列(真机) | 账本显示 10-13 投递 1 新建 1 纠正 0 问你 1 本该打断 0,与第 57 步一致 |
| 60 | 内容匹配的假阳性是被真机否掉的 | 见坑 #11:第一轮「量子计算机的散热方案该怎么设计」被无关会话以 62% 吸走;第二轮「归档是怎么回事」又匹配不上了。最终把它降级成"只排序、不决定" |
| 61 | 「1+1=2」不再被路由到当前会话(真机报的 bug) | 旧判据只看"短 + 没有领域词",一个全新的短问题同样满足。改成必须有真正的续说标记(再/继续/它/这个…) |
| 62 | 「开新会话」永远可用 | 输入框边上常驻按钮;即使这句话路由有把握,点它也照样新建并投递(离线用例覆盖) |
| 63 | 语义路由的管道(真机) | 词面对不上时确实建了助手会话并发出分类请求(候选列表 + 目标句都在里面);模型不回答时 45 秒后如实退回,按回车弹询问面板、一条都没发出去 |
| 64 | 模型答案的质量:未验证 | 隔离实例没有凭据,我也不该拿使用者的凭据去跑。这一条只能由使用者在真机上判断 |
最终验收的做法
验证分两类环境:
1. 隔离的 web profile 实例(复制真实 sessions 与 storages,独立 DSH_HOME)——用于功能逐项验证。
2. 真实 desktop profile 的副本(同样的 cordis.yml / package.json / node_modules / 会话数据)——用于最终验收。
第二类是最接近真实使用环境的:同样的 bundle 列表、同样的依赖链接、同样的会话数据。
还没在真机上验过的
🔴 「真的在等你」里 userQuestions.active 与 plan.pending 两个字段的真机触发。 这两个字段只在 agent 真的提问或提交计划时才非空——那需要一个能跑通的模型,而开发环境刻意没有配置凭据(也不应该拿别人的凭据去跑)。这一条由离线用例覆盖,用的字段结构来自真机 dump;分组代码路径与「正在跑」完全相同,而后者已经真机验证过。
开发 powershell
node tools/verify.cjs # 离线回归(86 项)
tools\mutation-audit.cmd # 变异审计(14 个变异)
tools/verify.cjs 把真实的 lib/client.js 装进一个假客户端环境里跑:模拟 window.__ModuleLoader__、一个最小 React 运行时(hooks + 渲染 + 事件)、以及一个假 ctx(slots + sessions)。它验证的是:
- 模块契约:是否调了 __ModuleLoader__.load、模块 id 是否等于包名
- 静态检查:有没有「用到却没声明」的服务(坑 #1)
- slot 注册:main 的 key 是否等于侧栏 sidebar.panellist 的 id
- 渲染:首次渲染不抛错、真实会话标题出现、分组正确、规模下不膨胀
- 交互:路由预览、回车投递真的打到正确的那个会话、走 queue 模式、改投真的重投
- 学习:纠正后写入偏好、下一次路由真的命中;不可靠的特征不写偏好
- 检索:本地搜大纲命中,且零 RPC
- 降级:零会话、以及 sessions 服务整个缺失时,渲染都不能抛错
改完 lib/client.js 直接重跑这个脚本,比重启应用快得多。
设计要点(为什么这么做)
1. 取消选择,而不是优化选择。 路由不问「要找谁」;判断不了就发给最可能的那个,把「不是它?改投给…」摆在手边。
2. 路由是纯函数。 routeMessage(text, rows, prefs) 不碰网络、不碰 React,所以能被 verify.cjs 直接测。这是它能被反复调参的前提。
3. 学到的偏好必须可见、可停用。 一个偷偷学习的黑盒和没有学习是一样的——用户不会信它,也就不会用「不是它」这个动作,学习回路就断了。
4. 任何服务缺失都不能让界面炸掉。 sessions 拿不到就少一块能力、显示一行说明,而不是白屏。
5. 该短就短。 唯一保留摩擦的地方是不可逆动作;除此之外不该有「请确认」。
6. 宁可少学一条,也不要学错一条。 错误的偏好会静默地长期投错地方,而缺失的偏好只是让你再纠正一次。
7. 护栏放在「发送之前」,不是「发送之后」。 投错一条消息会污染那个会话的上下文,还要白烧一轮算力 —— 那是不可逆的。所以把握 < 65% 时,警示和改投目标出现在你按回车之前;但它不拦你:按回车照样发给最可能的那个。零摩擦的意思是「不制造分叉」,不是「看着你投错也不吭声」。
8. 层级式汇报:只跟「上级」说话。 选中一个会话就等于指定它是你的对接人 —— 输入框永远是同一个,它的逐轮汇报就地展开在中间栏,子智能体是它的下属。主按钮是「看汇报」(不跳页面),原生界面 降级成次要入口。真要跳走的时候你才跳。
9. 打平局时选最近用过的那个。 两个会话标题匹配分数相同时,旧代码保留数组里靠前的那个,而那个顺序来自主机、语义未知。改成按 updatedAt 打平局:更像答案,而且可预测。
10. 没证据就不猜。 标题匹配和学到的偏好都给不出证据时,旧版会静默替你挑一个(先是「延续当前会话」,再是「接到最近会话」)。现在如实标成「没把握」,把选择摆出来,默认答案是开新会话。唯一例外是看起来在追问的短句(很短、且没有新话题特征),那接着当前会话说是合理的——但仍然会被问一次。
12. 语义路由借一个专用会话当分类器,而不是借你的会话。 客户端唯一的模型入口是 session.prompt(llm 命名空间没有补全方法),所以想用模型判断就必须有会话。用一个专用助手会话,污染就只落在一个可以随时归档的工具会话上,不落在你的任何对话里。它带缓存、带防抖、带超时;任何一步没成,退回的永远是「让你选」,不是「替你猜」。
13. 相似度只能排序,不能决定。 内容相似度用来回答「如果是这几个,哪个最像」,由人拍板。它一旦参与决定,就会在长中文文本上产生 62% 这种看起来很确定的假阳性,而那比「没把握」糟得多——因为它跳过了询问。
已知限制
- 摘要不是重写过的综述。 现在直接展示主机大纲里那一轮的原文(已截断),够用来判断「要不要点进去」。想要真正的一句话综述,得让某个会话来生成——所以「让它自己总结」那个按钮就是这条路,代价是花掉那个会话一轮。自动(免模型)和综述(花模型)选了前者做默认,因为它零延迟、零成本,而且原文常常比综述更可信。
- 语义路由的答案质量未经验证。 它依赖一个专用助手会话的模型回答;我没有凭据去跑,所以只知道管道通(请求发出、超时退回、不静默投错),不知道它答得准不准。默认开着,觉得不划算可以在「偏好」里关掉,或点「换一个」换掉那个助手会话。
- 路由的字面部分仍然是字面的。 标题片段匹配 + 学到的偏好都不理解同义词与指代;那部分靠「没证据就问你」和语义路由补。 它比对标题片段与学到的偏好,不理解同义词、指代和言外之意。这是刻意的取舍:真正的语义路由要过模型(一轮 + 延迟)。缺的那部分用「没证据就问你」补上(见坑 #11)。
- 「真的在等你」的两个字段未在真机触发过(见上)。
- 只在 Windows 上测过。 install/uninstall 脚本依赖 PowerShell;客户端插件本身是跨平台的。
- 开发探针留着没删:window.__ZF_DEBUG__.dump(i) 会把第 i 个会话的完整 projectionValues 和事件窗口结构原样吐出来。会话事件窗口没有文档、磁盘上的会话文件又是二进制,这个探针是唯一可靠的信息来源。只读,不改变任何状态。
许可
MIT预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
anhaoXie/dsh-client-ui-zero-friction
单框输入自动路由到会话,按注意力排队待办
相关插件 · UI / 主题
破甲增强版JeffKeeping/dsh-purge-plus
dsh-purge 的非官方分支fork:适配 DeepSeek Harness 0.2.1-rc.2、破甲内核…
★ 387已汉化DeepSeek 客户端
71.9
无代码业务系统平台nocobase/nocobase
NocoBase 是什么
★ 24,804已汉化DSH
71.8
1Panel 服务器面板1Panel-dev/1Panel
全球 250 万+ 自托管用户共同喜爱。
★ 37,362已汉化DSH
71.4
终端编码代理esengine/DeepSeek-Reasonix
· 指南 · ACP · 扩展开发 · 规格 · 官方网站 · Discord · QQ
★ 36,076已汉化DSH
71.2
工作状态行动态ccch1mneyyy/working-activity
作者:chimney@ccch1mneyyy。社区项目,非 DeepSeek 官方项目。
★ 664已汉化DSH
71.2
多模型配置管家farion1231/cc-switch
跨平台桌面一体化助手,支持 Claude Code、Codex、OpenCode、DeepSeek Harness 等工具,唯一官网 ccswitch.io
★ 151,814已汉化DSH
71.1
这个插件是你维护的吗?
作者本人可认领并核对本条目;认领通过后,详情页会显示「作者已认领」标记。