dsh-werewolf · 狼人杀 AI 圆桌
DeepSeek Harness Web GUI 的左侧边栏插件:八名 AI 角色围坐圆桌玩狼人杀。
每个座位可以单独配置接口地址 / API Key / 模型(默认全部 DeepSeek V4.1 Flash),
并用 token 虚拟货币给座位下注。
圆桌总览
✨ 功能
| 标签页 | 说明 | 状态 |
|---|
| 圆桌 | 顶部固定控制条(开始对局 / 单步推进 / 自动推进 / 重置);控制条下方是实时「思考过程 + 最近发言」字幕带(含逐句 🔊 重播);再往下是 8 个 AI 座位的环形圆桌与中间牌局状态;上帝视角可看身份。发言可以用语音朗读出来 | ✅ |
| 座位配置 | 每个座位独立设置角色名、头像、人设、模型 ID、baseURL、API Key;「测试连接」按钮会用该座位已保存的路由真实发一次最小请求,回报延迟与回复;一键全部切回 DeepSeek V4.1 Flash | ✅ |
| 下注 | 用虚拟 token(初始 10000)给座位下注,下注即扣款,结算按阵营输赢 2 倍赔付,获胜阵营座位 +200 积分;幂等结算、换局自动退款 | ✅ |
| 对局 | 发牌、夜晚行动、白天发言与投票,每个座位调用自己的模型 | ✅ |
说明:面板是旁观视角,座位身份默认可见(圆桌的「上帝视角」开关控制圆桌标签页)。
如果你希望"带着信息优势下注"这件事更公平,把下注也改成结束后才显示身份即可。
思考过程与语音
- 思考过程是真的:DeepSeek 推理模型会在回答前输出思维链(
reasoning_content),插件把它随该条记录一起存进 game.log[].reasoning 并显示在圆桌上。
每条最多保留 700 字,且只为最近 40 条保留(状态文档会在每次动作后重写并经 SSE 推送,不做限制会越来越重)。
发牌、票数统计、出局播报这类非模型动作没有思考过程,这是正常的。
- 语音朗读用浏览器自带的
speechSynthesis(不需要任何服务端 TTS、不额外花钱):默认开启,
控制条里有「语音播报」开关与音色下拉(默认按座位自动分配音色,八个 AI 音调不同);每条发言右侧的 🔊 可以单独重播。
若听不到声音:确认系统装了中文语音(Windows + Chrome/Edge 自带 Microsoft 中文语音),然后在下拉里手动选一个音色;
打开面板不会补读历史发言,只会读新产生的。
🚀 一键安装
前置条件:Node.js ≥ 22;装 DSH 且 dsh web 能正常打开 GUI(profile 已初始化)。
方式一:DSH 原生安装(推荐,不用克隆源码)
dsh plugin --profile web add github:HUSTforever/dsh-werewolf
也可以直接指向一份本地检出(绝对路径):
dsh plugin --profile web add link:/absolute/path/to/dsh-werewolf
这条命令会把本包装进 ~/.dsh/profiles/web 的依赖,并自动把 dsh-werewolf 追加进 profile 的 dsh.profile.bundles
——也就是让加载器应用本包自带的 cordis.patch.yml(插入 loader 行 ui-werewolf)。
方式二:克隆 + 双击(Windows 真·一键)
git clone https://github.com/HUSTforever/dsh-werewolf.git
cd dsh-werewolf
- Windows:双击
install.cmd(等价于 node scripts/install.mjs)
- macOS / Linux / WSL:
./install.sh 或 node scripts/install.mjs
安装脚本做两件事,都幂等、可逆、不需要 pnpm、不需要联网:
- 在
~/.dsh/profiles/web/node_modules/dsh-werewolf 建立指向本目录的目录联结(bare 包名因此可解析);
- 在
~/.dsh/profiles/web/cordis.patch.yml 写入一行受管 insert(loader 行 ui-werewolf)。
web profile 的用户补丁层是热重载的(patchReload: live),所以不需要重启宿主即可挂载。
其它可用参数:node scripts/install.mjs --status(只看状态)、--remove(卸载)、--official(改用上面的 DSH 原生方式)、--help。
⚠️ 两种方式只能选一种
两种方式会各自插入同一个 loader id ui-werewolf,同时存在会让第二次挂载失败。
scripts/install.mjs 检测到官方依赖(dsh plugin 安装)时会主动拒绝本地安装;切换方式前请先卸载。
🗑️ 卸载
# 方式一
dsh plugin --profile web remove dsh-werewolf
# 方式二
node scripts/install.mjs --remove # Windows 也可双击 uninstall.cmd
🔁 装好后请重启一次宿主(重要)
浏览器半(面板 UI)刷新页面即可生效;宿主半(游戏引擎 / 路由)需要重启一次 dsh web:
# 停掉当前的 dsh web,然后重新启动
dsh web
原因:宿主进程对已加载的 JS 模块有缓存,而「带缓存戳的 feature 加载器」本身也是被缓存的那一份,
所以只有重启一次才能让 lib/index.js / lib/host/features.js 的改动(以及被它加载的 feature 源码更新)生效。
重启之后,lib/host/features/*.js 的后续改动会在插件重新挂载时自动重载,不必再重启。
不重启的表现:牌局仍能跑完,但每次「决策」都会因为旧的 max_tokens 预算被推理模型吃光而退化成启发式动作
(日志里会出现「的模型没有返回内容(finish_reason: length…)」,且没有真正的 AI 投票/刀人)。
🔍 安装自检
node scripts/status.mjs # 需要宿主在运行
curl http://127.0.0.1:3080/api/werewolf/diagnostics
🎮 使用
- 刷新 GUI 页面(默认
http://127.0.0.1:3080)。
- 点左侧边栏「狼人杀」→ 面板在右侧打开,默认显示圆桌。
- 「座位配置」里改任意座位:模型填
deepseek-flash(DeepSeek V4.1 Flash)、
deepseek-v4-pro,或任何 OpenAI 兼容模型;接口地址填对应的 baseURL。
API Key 留空即使用 DSH 已保存的 DEEPSEEK_API_KEY。
- 座位上的 API Key 只存在本机
~/.dsh/werewolf/state.json(权限 600),
永远不会下发给浏览器。
- 「对局」标签页:
开始对局 发牌 → 单步推进 看每个 AI 一步步行动 → 自动推进 连续推进(可随时停止)。
每个动作都会调用该座位自己的模型。
实测速度与费用(DeepSeek V4.1 Flash,推理模型)
| 动作 | 实测 |
|---|
| 结构化决策(刀人 / 查验 / 用药 / 投票) | 低推理强度,约 550 推理 token,3~5 秒 |
| 发言 | 默认推理强度,5~10 秒(好的发言需要模型自己想) |
| 一个白天(8 发言 + 8 投票) | 约 1.5~2 分钟 |
| 一整局(2~3 天) | 约 3~6 分钟 |
- 决策走
reasoning_effort: 'low'(实测同样答案下比默认快约 2.5 倍);发言保留默认推理强度,
若某次发言把预算烧光则自动降级重试一次,不会退化成模板台词。
reasoning_effort 是 DeepSeek 扩展字段:其它 OpenAI 兼容服务若因此报 400,会自动去掉该字段重试一次。
- 想让每个座位更快/更省,可以在「座位配置」里给单个座位换成非推理模型(例如
deepseek-v4-pro 或第三方模型),
每个座位的 baseURL / apiKey / model 都是独立的。
🖼️ 预览
预览图可以在一个「仿真外壳」里独立渲染,用真实浏览器截图自检(无需 GUI 令牌):
node scripts/preview-server.mjs --port 4400 --scenario midgame # 终端 1(真实插件 + 假外壳 + 假模型)
node scripts/screenshot.mjs http://127.0.0.1:4400/ preview- # 终端 2(系统 Chrome)
--scenario 可选 idle / midgame / ended,--real-llm 会关掉假模型、改打真实接口。
截图落在 preview/,每个标签页一张;出现 page error 时脚本以非 0 退出。
预览服务会加载 lib/host/features/ 与 web/features/ 下的全部模块,新增功能会自动出现在截图里。
📁 项目结构
package.json dsh.bundle.patch + dsh.client(platform=web)
cordis.patch.yml DSH 原生安装时生效的 profile 层(插入 loader 行)
install.cmd Windows 双击一键安装
install.sh macOS / Linux / WSL 一键安装
uninstall.cmd Windows 双击一键卸载
lib/index.js 宿主入口:状态 / 路由 / SSE / 静态资源 / feature 发现
lib/client.js 浏览器入口:左侧边栏入口行 + 面板容器(模块加载器信封)
lib/host/*.js 状态、路由、SSE、脱敏、feature 发现
lib/host/features/ ★ 宿主功能模块(座位配置、诊断、下注、牌局…)
web/*.js 浏览器应用(api / shell / main / style)
web/features/ ★ 视图模块(圆桌、座位配置、下注、对局…)
scripts/ 安装脚本与测试
docs/CONTRACT.md 作者契约(接口、状态结构、验证流程)
🧪 测试
npm test # = node scripts/test-all.mjs,一键跑全部
node scripts/test-install.mjs # 安装器自检(沙箱 DSH_HOME,不动真环境)
node scripts/smoke.mjs # 宿主半(临时 DSH_HOME + 真实 HTTP)
node scripts/ui-test.mjs # 浏览器半(jsdom + 假宿主,真实渲染)
node scripts/check-client-binding.mjs # 客户端包解析与启动图绑定
运行时自检:
curl http://127.0.0.1:3080/api/werewolf/state
curl http://127.0.0.1:3080/api/werewolf/diagnostics
🔌 接口一览(全部挂在 /api/werewolf,仅本机回环)
| 路由 | 说明 |
|---|
GET /state | 脱敏后的完整状态文档 + feature/路由清单 |
GET /events | SSE,每次状态变更推一帧 |
GET /diagnostics | 自检:路由、feature、客户端包是否进入浏览器启动图 |
GET /ui/manifest.json | 浏览器视图模块清单 |
POST /seat · POST /seat/clear-key · POST /seats/defaults · POST /seats/reset | 座位配置读写 |
POST /seat/test | 用该座位已保存的 baseURL / Key / 模型做一次真实连通性测试 |
GET /wallet · POST /bet · POST /bet/settle | 虚拟货币下注与结算 |
POST /game/start · POST /game/step · POST /game/auto · POST /game/reset | 牌局引擎(一次 step = 一次 AI 发言/投票/阶段切换) |
🔒 端口与安全
- 所有接口挂在
/api/werewolf 前缀下,仅接受本机回环请求(Host 必须是 localhost/127.0.0.1/::1)。
- 响应一律
no-store,方便改完刷新即生效。
- 座位 API Key 只保存在本机
~/.dsh/werewolf/state.json,响应里只出现 apiKeySet / apiKeyHint 脱敏字段。
- 数据持久化在
~/.dsh/werewolf/state.json;删掉即可恢复初始状态。
⚠️ 已知边界
- 面板是旁观/上帝视角:圆桌与对局页会显示每个座位的身份(可用圆桌里的「上帝视角」开关关闭;下注页也会显示身份)。
- 「赢」按阵营判定:押中的座位若所属阵营获胜即 2 倍赔付,与它本人是否出局无关。
- 猎人被女巫毒杀不开枪;女巫不能毒自己、也不能毒当晚已被狼刀的人;每个白天平票会重投一次,再平票则本轮无人出局。
- 单次模型调用 60 秒超时;超时/报错的座位会记录原因并改用启发式动作,牌局不会卡死。
- 只在 DSH Web profile 下验证过(Electron 桌面版未验证)。
👩💻 开发者提示
- 宿主半的入口文件(
lib/index.js)改动需要重启 dsh web;
lib/host/features/ 下的新增文件会在宿主重新挂载时自动加载(见 docs/CONTRACT.md 的开发循环)。
web/ 下的改动刷新页面即可生效。
- 修改 install.mjs 后跑
node scripts/test-install.mjs 验证幂等与卸载。
📄 License
MIT © 2026 HUSTforever
🙏 致谢