dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
DSH 模拟面试面板 + 技能:真实模型当面试官,读简历层层追问、按证据打分并推导薪资区间。面试官是考官,不是助教。
在 DSH 里跑一场真的模拟技术面试 —— 真实模型当面试官,读你的简历一层层往下追问, 按证据打分并推导薪资区间。面试官是考官,不是助教:不讲解、不纠正、不鼓励, 只负责在有限时间里测出你的真实能力边界 —— 这一点决定了它的所有行为。
它同时是技能 + 面板插件:右下角一个可拖拽、可收起的悬浮窗,有自己的对话框,
不占用主会话;同一份面试 SOP 还会注册成 interviewer 技能,主会话里 skill interviewer 一样能用。
DSH 市场里做「求职」的插件目前分两类,都不是面试本身:
| 插件 | 视角 | 做什么 |
|---|---|---|
recruiting-copilot | 招聘方 | HR / 猎头的寻源、初筛、约面试 |
resume-reviser | 候选方 | 改简历 → 岗位推荐 → 末尾顺带一次模拟面试 |
dsh-resume-expert | 候选方 | 引导式生成简历,直出单页 A4 PDF |
dsh-scout(司察) | 候选方 | 面试前查公司、查岗位的尽调 |
dsh-interviewer | 候选方 | 只做面试本身 |
区别不在功能多少,在产出是什么:
所以它能做一件「顺带跑一次模拟面试」做不到的事:把同一个话题真正追到底, 然后再换下一个。这也是这个插件里最费劲的一块(见下文「面试状态机」)。
dsh plugin --profile web add github:ufiredong/dsh-interviewer
装完重启 dsh web(重启会断开当前会话,属正常)。重启后右下角出现「模拟面试」悬浮按钮。
不需要另外装技能。 面试 SOP 随插件一起分发,插件启动时把它注册成名为 interviewer
的运行时技能 —— 装了插件就等于装了技能。
卸载:
dsh plugin --profile web remove dsh-interviewer
用的是哪个模型? 插件不自己管密钥、不自己发 HTTP 请求。它注入 DSH 已经配好的
llm服务,从你settings.yaml的agent-default-model读当前选择。 所以效果取决于你当前用的模型。
面板顶部有一条状态条,显示已用时间、面试计划推进到哪、当前话题问了几次 —— 到上限时会标红并显示接下来要换到哪个话题。这条是给用户看的进度, 也是「面试官有没有在一个话题上死磕」的可视化证据。
面试设置 —— 方向 / 职级 / 轮次 / 风格。前四项决定整场面试的难度和问法:
面试进行中 —— 顶部两条:上面是配置摘要,下面是状态条,实时显示已用时间、 当前话题问到第几问、面试计划推进到哪:
刚打开时 —— 直接发消息就能开始,简历可以粘进来:
你的任务是在有限时间里测出候选人的真实能力边界。不是让他感觉良好, 不是把知识讲给他听,也不是帮他通过面试。
| # | 规则 |
|---|---|
| 1 | 一次只问一个问题。不列 1/2/3,不给选项菜单 |
| 2 | 不讲解、不纠正、不铺垫。答错时记录,继续往下问 |
| 3 | 含糊必追问。「大概」「差不多」「应该是」「我们」→ 立刻要具体数字或你的角色 |
| 4 | 不放过「不知道」。给一次换角度的机会,仍不会才记录并推进 |
| 5 | 不替候选人组织语言。不说「你是不是想说……」 |
| 6 | 不评价、不安慰、不鼓励 |
从简历上的事实出发,一层层往下钻:
L0 事实核查 「这个项目你负责哪部分?」
L1 技术选型 「为什么选它,不选另一个?」
L2 实现细节 「具体怎么做的,关键代码在哪?」
L3 反事实 「如果 X 挂了会怎样?」
L4 复盘 「现在回头看,哪一步做错了?」
L5 迁移 「换个场景,你还会这么做吗?」
同一个话题最多追 2 次。追问能力很强但不知道什么时候停,等于没有追问能力 —— 这条是实测吃出来的(见下面的「面试状态机」)。
风格改变怎么问和压力多大,不改变铁律 —— 温和风格一样要追到底。
| 风格 | 什么样 |
|---|---|
| 温和引导 | 语气平和,用「我们来看看」把问题递出去,压力低但一样追 |
| 务实工程 | 直接问工程细节和取舍,不在意包装,关心「你到底做了什么」 |
| 冷峻压测 | 专门压边界和失败路径,答得顺不代表过得了 |
| 学术严谨 | 要定义、要前提、要证据链,不接受「业界都这么做」 |
五个维度加权(1–5 分),每一分都要能在你的原话里找到落点:
| 维度 | 权重 | 在测什么 |
|---|---|---|
| 技术深度 | 30% | 是否理解原理,而非记住方案 |
| 系统设计 | 25% | 能否在约束下做取舍 |
| 工程严谨 | 20% | 是否考虑边界、失败、可验证性 |
| 表达与结构 | 15% | 能否把复杂的事讲清楚 |
| 边界认知 | 10% | 知不知道自己不知道什么 |
加权分定档(一线城市总包):
| 加权分 | 职级对标 | 定位 | 年薪总包 |
|---|---|---|---|
| ≥ 4.5 | P8 / 3-1 / T7 | 专家级 | 120 – 180 万 |
| 3.8 – 4.5 | P7 / 2-2 / T6 | 高级 | 70 – 120 万 |
| 3.0 – 3.8 | P6 / 2-1 / T5 | 中级 | 45 – 70 万 |
| 2.2 – 3.0 | P5 / 1-2 / T4 | 初级 | 30 – 45 万 |
| < 2.2 | — | 不建议推进 | — |
一线以外按城市系数折算(新一线 0.82、二线 0.68、其他 0.58)。
薪资数字由 skills/interviewer/scripts/score.mjs 算,不靠模型心算。
脚本是唯一口径来源,这样两次面试的结论才可比 —— 也让「同一份输入永远得到同一个分」
成为一条可验证的性质(见下面的自检)。
这是总包不是 Base,级差不是线性的,而且模拟面试 ≠ 真实定级。 真实结果还受城市、公司、团队缺口和议价能力影响。
第一版是无状态的:每轮把整份对话丢给模型问「下一句问什么」。
结果它在同一个话题上追了 5 轮(SOP 只允许 2 轮),代价是没余力接别的话题 —— 放掉了一段教科书级的含糊回答(候选人说「JVM 调过一些参数,后来就好了」, 面试官因为还卡在上一个话题,整段放过去了)。
根因不是提示词写得不好,是模型没有工作记忆:它每轮都要从零推导「该问什么」, 而最近上下文里最抢眼的就是「他刚才没回答我」,所以它一直拉那根线。
所以状态被显式化了。分工很明确:
让模型每轮吐一个状态标记,命中率只有 50% 上下。换措辞、把协议挪到提示词最末尾, 都救不回来(挪末尾反而从 4/5 掉到 1/5)。
所以状态机不依赖模型配合才能正确:
结果是即使模型完全不吐标记,广度推进依然成立。
interview-sim/state-run.mjs,11 轮真实模型调用,与无状态版同一套候选人台词:
| 无状态版 | 状态机版 | |
|---|---|---|
| 单话题最多追问 | 4 轮(「多出来的 1.5 秒」反复重问) | 2 轮 |
| 走过的不同话题 | 2 个 | 6 个 |
| 收尾表现 | max-tokens 挂掉 | 「好,我整理一下」 |
POST /interviewer/api/chat。主机侧是无状态的纯函数 —— 不写数据库、不落盘,
DSH 重启也不会留下面试记录(对话本身也随之丢失)。node dev-check.mjs # 插件结构与接线(含用桩真跑一遍浏览器侧模块)
node state-test.mjs # 面试状态机(纯函数,可离线断言)
dev-check.mjs 不是"看起来对",是"真的执行了":它用 node:vm + 桩
window.__ModuleLoader__ 把 lib/client.js 真的跑一遍,用桩 ctx 执行 apply(),
抓取它实际发出的每一条槽位注册,再逐条核对槽位名。还包含发布前必查的自包含断言 ——
包内 SOP 是否齐全、files/exports 有没有漏掉 skills。这类 bug 在本机永远复现不了,
只在别人机器上炸,所以只能靠断言钉住。
没有构建步骤。 lib/client.js 手写在 DSH 的浏览器模块格式里,改完直接生效:
| 改了什么 | 需要 |
|---|---|
lib/client.js | 刷新浏览器 |
lib/index.js、lib/skill.js、lib/state.js、lib/prompt.js、package.json | 重启 dsh web |
package.json 的依赖声明 | 重新 dsh plugin add |
只有真的动了依赖才需要重装。改代码不需要。
package.json 包清单:dsh.bundle.patch + dsh.client 两个声明
cordis.patch.yml 插件行:装插件时 CLI 读它,把包接进 dsh.profile.bundles
lib/index.js 主机侧:HTTP 路由 /interviewer/api/{health,chat}
lib/prompt.js 系统提示词装配(纯 Node,可离线拼出来做断言)
lib/state.js 面试状态机(纯函数)
lib/skill.js 把 SOP 注册成运行时技能 + 定位包内 SOP 目录
lib/client.js 浏览器侧:悬浮窗、设置弹窗、状态条、语音输入
skills/interviewer/ 随包分发的面试 SOP(SKILL.md + references/ + score.mjs)
dev-check.mjs 插件自检
state-test.mjs 状态机单元测试
DSH 的 Web 界面是一棵插槽(slot)树。插件不直接改界面,只能往别人已经声明的槽位里
加条目 —— register 到未声明的槽位会直接抛错。所以这不是「改 DSH」,是「插进 DSH
预留的座位」。
本插件只用一个座位:
| 槽位 | 类型 | 作用 |
|---|---|---|
shell.overlay | list | 全框架浮层,渲染一个可拖拽的悬浮窗口 |
ctx.slots.inject(槽位名, 回调) 是延迟注册,等槽位被声明之后再注入,所以不用担心加载顺序。
为什么只有这一个入口:
| 槽位 | 为什么不用 |
|---|---|
main | 全屏接管中间区域,用户进得去出不来。体验太霸道,而且挡住主会话 |
sidebar.panellist | 注册成功(回读确认「账面 1 条,生效 1 条」),但侧边栏就是不渲染它。原因未定位,疑似 dsh-better-sidebar 介入那块区域的渲染 |
| 端点 | 用途 |
|---|---|
GET /interviewer/api/health | 探测:路由挂上没有、模型解析到没有、SOP 从哪个目录读的 |
POST /interviewer/api/chat | 发对话,拿回复 |
浏览器 → 主机走一条普通 HTTP 路由,不走 DSH 的 Remote:Remote 要接 typert 协议 (host / client 各一份描述),而这里只需要一次「发过去、拿回来」。路由更直接, 出问题 curl 就能验。
推理(thinking)token 也计入 maxTokens。 所以这个数字不是「回复能有多长」,
而是「想 + 说总共能有多长」。
实测下来模型的思考长度波动极大 —— 同一段对话同一个额度,有时 3 秒答完,
有时思考 14 秒一个字都没吐就直接 max-tokens,面板上出现一个断掉的话轮。
所以有一层保护:一轮如果没有产出任何内容,自动把额度翻倍重试一次, 并追加一句「别想了直接说」。已经写出半句话的情况不重试 —— 那会得到一个前后不连贯的答案。
scripts/score.mjs 得到,
面板目前只负责面试对话shell.overlay 是 root scope,浮层里拿不到 sessionId,
所以面试对话不进入 DSH 的会话历史见 docs/dev-notes.md。那不是这个插件的问题,是
dsh plugin add 会跑一次 pnpm install,扰动 DSH 的自定义原生插件加载器。
文档里有失败签名和官方退路。