1. 这个插件到底解决了什么问题先说结论我做的是一个面向 DeepSeek Harness下称 DSH的插件名字叫interview-dsh-plugin核心功能是把“面试”这件事从人工问答变成一套可编排、可复用、可本地运行的智能体工作流。它不是一个简单的问答脚本而是一个真正跑在 DSH 插件体系里的完整模块支持多智能体编排、本地知识库读取、结构化输出以及通过 DSH 的 profile 机制按需加载。很多人第一次听到“百万级别插件”这个说法会以为是下载量或者用户量到了百万。其实不是。这里的“百万级别”指的是它在设计上要处理的上下文规模和任务复杂度——单次面试模拟可能涉及几十页简历、上百道题库、多轮追问记录、评分维度表以及跨会话的状态保持。把这些东西塞进一个普通脚本里跑两轮就崩了。所以我从一开始就把它当成一个“百万 token 级别上下文管理”的工程问题来做而不是写个 prompt 就完事。这个插件适合谁用三类人一是正在准备技术面试的开发者想用本地模型做模拟面试但不想把简历传到云端二是做招聘工具的产品或工程团队想快速验证“AI 初筛追问”的流程三是已经在用 DSH 做智能体编排的人想找一个真实可跑的插件案例来改。它不依赖任何外部 API 密钥所有推理都在本地 DSH 环境里完成数据不出机器。我开源它的原因也很直接DSH 的插件生态还在早期官方文档给的是骨架真正跑起来会遇到的坑——插件树加载失败、profile 配置不生效、PDF 读取乱码、多智能体状态串扰——这些没人写。我把自己的踩坑记录和最终可跑的版本一起放出来比只给一个 README 有用得多。2. 为什么选择 DSH 插件体系而不是独立脚本2.1 DSH 的插件模型到底强在哪DSH 本身是一个本地优先的智能体运行框架它的插件机制不是简单的“加载一个 Python 文件然后调用函数”。它有一套插件树的概念每个插件声明自己依赖哪些能力、暴露哪些工具、在哪个 profile 下激活。这套设计的好处是当你同时跑多个智能体时不会出现“所有工具都塞给所有 agent”的混乱局面。我试过用纯 LangChain 或者自己写一个 FastAPI 服务来做同样的事。问题是面试模拟需要角色隔离出题 agent 不应该看到评分 agent 的评分标准追问 agent 不应该直接访问原始简历全文否则它会“作弊”——提前知道答案然后假装在追问。用独立脚本做你得手动管理每个 agent 的上下文窗口很容易串。DSH 的插件树天然支持按 profile 加载不同工具集dsh plugin --profile web add这种命令就是干这个的。另一个关键点是本地部署。DSH 支持本地模型接入我的插件里所有推理调用都走本地端点。这意味着简历、面试记录、评分表全部留在本机。对于招聘场景这是硬需求——没有公司愿意把候选人简历发给第三方 API。2.2 插件树加载失败的教训我第一次跑的时候遇到error: dsh: plugin tree failed to load: dsh: plugin(s) failed to load: deep...折腾了大半天。原因有三个按出现频率排第一插件目录结构不对。DSH 要求插件根目录下必须有dsh-plugin.json或者pyproject.toml里声明 entry point而且目录名必须和插件名一致。我一开始把interview-dsh-plugin放在了一个叫plugins/的子目录里DSH 扫描不到。第二依赖版本冲突。DSH 0.1.5 和 0.1.5-rc.2 对插件 API 的签名有细微差别。如果你在 rc 版本上开发然后退回到稳定版plugin tree failed to load就会出现。我的建议是锁定一个版本在dsh-plugin.json里写清楚min_dsh_version。第三profile 配置没生效。dsh plugin --profile web add之后如果你没有在dsh.config.yaml里把webprofile 设为默认插件加载了但不会激活。表现就是“没报错但工具列表是空的”。提示遇到插件树加载失败先跑dsh plugin list --verbose它会打印每个插件的加载状态和失败原因。比看日志快得多。2.3 为什么不用现成的面试插件市面上确实有一些 AI 面试工具但要么是 SaaS数据上云要么是单轮问答没有追问和评分闭环要么是写死的题库不能读你自己的简历。我要的是一个可编排的本地工作流读简历 → 生成岗位相关题目 → 多轮追问 → 按维度评分 → 输出结构化报告。这五步里每一步都可以替换成不同的 agent 或模型而 DSH 的插件体系正好支持这种“乐高式”组装。3. 核心模块拆解与实操要点3.1 插件目录结构与关键文件我的插件最终稳定下来的目录结构是这样的interview-dsh-plugin/ ├── dsh-plugin.json ├── pyproject.toml ├── src/ │ └── interview_dsh_plugin/ │ ├── __init__.py │ ├── agents/ │ │ ├── questioner.py │ │ ├── prober.py │ │ └── scorer.py │ ├── tools/ │ │ ├── resume_reader.py │ │ ├── jd_parser.py │ │ └── report_writer.py │ └── profiles/ │ └── web.yaml └── tests/ └── test_flow.pydsh-plugin.json是入口声明内容大概长这样{ name: interview-dsh-plugin, version: 0.1.0, min_dsh_version: 0.1.5, entry: interview_dsh_plugin:register, profiles: [web, desktop], tools: [ resume_reader, jd_parser, question_generator, answer_prober, dimension_scorer, report_writer ] }这里有个坑entry指向的函数必须返回一个PluginManifest对象而不是直接返回工具列表。我一开始返回了 listDSH 直接报plugin tree failed to load而且错误信息里没说是类型问题只说是加载失败。后来翻了源码才发现。3.2 简历读取PDF 和 DOCX 的坑resume_reader这个工具看起来简单实际上是最容易出问题的地方。DSH 本身不内置 PDF 解析你得自己选库。我试过三个方案方案优点缺点最终选择PyPDF2轻量无系统依赖中文乱码严重表格丢失否pdfplumber中文支持好能读表格速度慢大文件内存高是调用外部 pdftotext快稳定需要系统装 poppler否最终用 pdfplumber因为简历里的表格工作经历、技能列表必须保留结构。但 pdfplumber 有个问题如果 PDF 是扫描件它读出来是空的。我的处理逻辑是先尝试读文本如果字符数少于 50就返回一个明确的错误提示“疑似扫描件请提供文本版”而不是让 agent 拿着空字符串去生成题目。DOCX 用python-docx相对简单。但要注意很多简历用文本框排版python-docx默认读不到文本框里的内容。我的做法是同时遍历document.paragraphs和document.tables并且对每个 section 的 header/footer 也做一次扫描。注意简历里经常有“自我评价”这种大段文字直接塞给模型会浪费上下文。我在resume_reader里加了一个简单的分块逻辑按标题切分每个块不超过 800 字超出就截断并标记[truncated]。3.3 多智能体编排出题、追问、评分怎么隔离这是整个插件最核心的部分。DSH 支持在一个 profile 下注册多个 agent每个 agent 可以绑定不同的工具集。我的设计是questioner只能访问resume_reader和jd_parser不能访问评分标准。它的任务是生成 5-8 道岗位相关题目每题附带考察维度标签。prober只能访问当前对话历史和题目列表不能访问简历全文。它的任务是根据候选人的回答生成 1-2 个追问。scorer只能访问对话记录和评分维度表不能访问原始简历。它的任务是对每个维度打分并给出理由。这种隔离是通过 DSH 的tool_scope实现的。在profiles/web.yaml里这样写agents: questioner: tools: [resume_reader, jd_parser] prober: tools: [conversation_history] scorer: tools: [conversation_history, dimension_scorer]我试过不隔离结果 scorer 直接看到了简历里的“精通 Python”然后给 Python 维度打了满分但候选人在对话里其实没答上来。隔离之后scorer 只能根据对话表现打分准确多了。3.4 状态保持跨会话的面试记录DSH 的 agent 默认是无状态的每次调用都是新会话。但面试模拟需要记住“上一轮问了什么”“候选人答了什么”。我的做法是在插件里维护一个轻量的SessionStore用 SQLite 存路径放在~/.dsh/interview_sessions/。每个 session 存三张表sessions会话元信息、turns每轮问答、scores评分结果。这样即使 DSH 重启面试也能继续。而且report_writer可以直接从 SQLite 读数据生成报告不需要把整个对话历史塞进 prompt。提示SQLite 的并发写入在 DSH 多 agent 同时跑的时候会锁。我的处理是每个 agent 用独立的连接并且设置timeout5避免死锁。4. 完整实操流程从安装到跑通一次面试4.1 环境准备与安装假设你已经装好了 DSH 0.1.5。如果没有先按官方文档装。然后# 克隆插件 git clone https://github.com/yourname/interview-dsh-plugin.git cd interview-dsh-plugin # 安装依赖 pip install -e . # 注册到 DSH 的 web profile dsh plugin --profile web add ./interview-dsh-plugin # 确认加载成功 dsh plugin list --verbose如果dsh plugin list里能看到interview-dsh-plugin且状态是active就说明加载成功了。如果状态是loaded但不是active检查dsh.config.yaml里的default_profile是不是web。4.2 配置本地模型端点我的插件默认走 DSH 的本地模型配置。你需要在dsh.config.yaml里指定模型端点model: provider: local endpoint: http://127.0.0.1:11434/v1 model_name: deepseek-r1:14b max_tokens: 4096 temperature: 0.3温度设 0.3 是因为面试评分需要稳定性太高了每次打分波动大。max_tokens设 4096 是因为追问和评分不需要太长输出设太大反而慢。4.3 跑一次完整面试启动 DSH web 界面dsh web它会自动打开浏览器。如果你在服务器上跑用--no-open然后手动访问打印出来的 URL。在界面里选择interview工作流然后上传简历PDF 或 DOCX粘贴岗位 JD纯文本点击“开始面试”插件会依次执行解析简历 → 解析 JD → 生成题目 → 进入对话循环候选人回答 → prober 追问 → 下一题→ 结束后 scorer 打分 → 生成报告。我实测下来一份 2 页简历 500 字 JD生成 6 道题大约需要 15 秒本地 14B 模型完整面试 20 分钟报告生成 5 秒。4.4 报告长什么样report_writer输出的是 Markdown 格式包含总体评分加权平均每个维度的得分和评语每道题的问答记录追问次数统计建议基于低分维度报告存在~/.dsh/interview_sessions/session_id/report.md同时会在 web 界面里渲染出来。5. 常见问题与排查技巧实录5.1 插件加载类问题现象可能原因解决方法plugin tree failed to loadentry 函数返回类型不对确保返回PluginManifest插件列表为空profile 没设默认改dsh.config.yaml的default_profiledsh plugin add报权限错误目录权限不对chmod -R 755插件目录加载后工具不可见tool_scope没配检查profiles/web.yaml5.2 模型调用类问题最常见的是dsh web authentication required; reopen the url printed by dsh web.。这个不是插件的问题是 DSH web 的 token 过期了。关掉 DSH重新dsh web用新打印的 URL 访问就行。另一个是模型返回空。如果本地模型端点没启动DSH 不会报错只会返回空字符串。我的插件里加了一个检查如果模型返回空就重试一次还空就抛异常并提示“检查模型端点”。5.3 简历解析类问题扫描件 PDF 读出来是空的前面说过。还有一个坑有些简历用特殊字体pdfplumber 读出来是乱码。我的处理是检测非 ASCII 字符比例如果超过 30%就提示“疑似编码问题请提供文本版”。DOCX 的文本框问题也提过。补充一点如果简历里有图片格式的技能雷达图python-docx读不到我的插件会忽略图片只读文本。5.4 多智能体串扰类问题如果你发现 scorer 的评分明显偏高大概率是它看到了不该看的信息。检查profiles/web.yaml里的tool_scope确保 scorer 只有conversation_history和dimension_scorer。还有一个隐蔽的问题DSH 的 agent 之间共享context对象。如果你在 questioner 里往 context 写了一个 keyprober 也能读到。我的做法是每个 agent 用独立的 namespace比如context[questioner][current_question]。提示DSH 0.1.5-rc.2 里 agent 隔离有 bug会串 context。如果你用 rc 版本建议升级到稳定版或者手动加 namespace。6. 后续可以怎么扩展这个插件目前只做了技术面试的流程但架构是通用的。你可以把questioner换成行为面试的出题 agent把dimension_scorer换成领导力评估维度就能变成行为面试模拟。也可以把resume_reader换成code_repo_reader读 GitHub 仓库然后生成代码审查题目。我个人的体会是DSH 插件体系最大的价值不是“能跑”而是“能拆”。每个 agent 和 tool 都是独立的你可以只替换其中一个不影响其他部分。这种可组合性在快速迭代场景下非常省时间。我后面打算加一个interviewer_persona配置让用户选择“严厉型”或“引导型”面试官本质上就是换一个 prompt 模板但走的是同一套插件流程。最后分享一个小技巧如果你在调试插件时频繁重启 DSH可以用dsh plugin reload interview-dsh-plugin热重载不用整个重启。这个命令官方文档里没写但实测有效。
