DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

ufiredong /

ufiredong/dsh-interviewer

Verified

DSH 模拟面试面板 + 技能:真实模型当面试官,读简历层层追问、按证据打分并推导薪资区间。面试官是考官,不是助教。

★ 1 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@90b40c90

dsh-interviewer

License: MIT Node DSH

在 DSH 里跑一场真的模拟技术面试 —— 真实模型当面试官,读你的简历一层层往下追问, 按证据打分并推导薪资区间。面试官是考官,不是助教:不讲解、不纠正、不鼓励, 只负责在有限时间里测出你的真实能力边界 —— 这一点决定了它的所有行为。

它同时是技能 + 面板插件:右下角一个可拖拽、可收起的悬浮窗,有自己的对话框, 不占用主会话;同一份面试 SOP 还会注册成 interviewer 技能,主会话里 skill interviewer 一样能用。

它能做什么

  • 真实模型当面试官。 用你 DSH 里已经配好的模型,插件不额外要 API key。
  • 简历驱动,一层层往下钻。 从你简历上的事实问起,追到反事实和复盘,不走过场。
  • 四种面试官风格 × 四种轮次。 温和引导 / 务实工程 / 冷峻压测 / 学术严谨, 配技术一面 / 技术二面 / 主管面 / HR 面 —— 风格换的是问法和压力,不是标准。
  • 不会在一个话题上死磕。 内置面试状态机,同一话题最多追 2 次, 保证考察广度而不是把人逼死在一处(见下文「面试状态机」)。
  • 按证据打分。 五个维度加权,每一分都要能在你的原话里找到落点; 薪资区间由确定性脚本算出来,不靠模型心算 —— 同一份输入永远得到同一个结果。
  • 过程可见。 面板顶部一条状态条:已用时间、面试计划推进到哪、当前话题问了几次。
  • 语音作答。 用浏览器内置的语音识别边说边转文字,识别结果只填进草稿、不自动发送 (Chrome / Edge 有,Firefox / Safari 没有)。

和其他插件的区别

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. 点右下角的悬浮按钮 → 弹出设置弹窗
  2. 选目标方向(大模型/Agent、Java 后端、算法)、职级、面试轮次、面试官风格
  3. 确认后进入面试面板,把简历粘进对话框(PDF 读不出字,用 Markdown 或纯文本), 或者直接说「开始面试」
  4. 聊完说一声,面试官会根据全程对话给出评分与薪资结论

面板顶部有一条状态条,显示已用时间、面试计划推进到哪、当前话题问了几次 —— 到上限时会标红并显示接下来要换到哪个话题。这条是给用户看的进度, 也是「面试官有没有在一个话题上死磕」的可视化证据。


界面

面试设置 —— 方向 / 职级 / 轮次 / 风格。前四项决定整场面试的难度和问法:

面试设置弹窗:目标方向、目标职级、面试轮次、面试官风格四个下拉框,底部是取消与开始面试按钮

面试进行中 —— 顶部两条:上面是配置摘要,下面是状态条,实时显示已用时间、 当前话题问到第几问、面试计划推进到哪:

面试面板:顶部配置条与状态条,中间是面试官与候选人的对话,底部是输入框、语音按钮与发送按钮

刚打开时 —— 直接发消息就能开始,简历可以粘进来:

面试面板空态:提示「说说你的情况」,并建议第一句只写「开始面试」


面试官到底是什么样

定位

你的任务是在有限时间里测出候选人的真实能力边界。不是让他感觉良好, 不是把知识讲给他听,也不是帮他通过面试。

六条铁律

# 规则
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 挂掉 「好,我整理一下」

数据与隐私

  • 简历和全部对话内容会发送给你在 DSH 里配置的模型服务商。 这是它工作的前提, 不要往里粘你不想外发的东西。
  • 面试状态(计划 / 话题 / 观察)保存在浏览器内存里,每轮随请求回传给主机侧的 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,面板上出现一个断掉的话轮。

所以有一层保护:一轮如果没有产出任何内容,自动把额度翻倍重试一次, 并追加一句「别想了直接说」。已经写出半句话的情况不重试 —— 那会得到一个前后不连贯的答案。


已知限制

  • 只有右下角悬浮窗一个入口(原因见上)
  • 评分报告不在面板里。评分通过 SOP 在主会话里跑 scripts/score.mjs 得到, 面板目前只负责面试对话
  • 简历要手动粘贴,没有拖放区;PDF 读不出字
  • shell.overlay 是 root scope,浮层里拿不到 sessionId, 所以面试对话不进入 DSH 的会话历史
  • 状态条上的话题名依赖模型偶尔吐出的标记,可能有一轮偏差(乐观前进的代价)

装插件之后 DSH 起不来?

见 docs/dev-notes.md。那不是这个插件的问题,是 dsh plugin add 会跑一次 pnpm install,扰动 DSH 的自定义原生插件加载器。 文档里有失败签名和官方退路。

License

MIT

—/ 5

No ratings yet

Verified DSH bundle

Commit 90b40c90f13f

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout