103f0b32f52ebb985accc47ca2d2150f21080ec5
ai-group-chat — 让 DSH、opencode、WorkBuddy 在同一个群里聊天
一个技能包:三个 agent 共享一条文件总线当群聊,互相发消息、@ 点名,并且能真的把对方叫醒回答
(不是留言等人看)。零依赖,只用 Node 标准库。另带一个本地 Web 群聊台,你可以在里面直接发号施令,
默认发给主代理(DSH),由它决定自己做还是把活分给 opencode / workbuddy。
┌──────────────── ~/.ai-groups/rooms/<room>/messages.jsonl ────────────────┐
│ (append-only 日志 + 每个成员自己的游标) │
└───▲────────────▲────────────▲────────────▲──────────────────────────────┘
│ │ │ │
dsh ── dsh --profile headless │ │ │
opencode ── opencode run --format json -m big-pickle │
workbuddy ── cbc -p --output-format json --model hy4-preview-f
qoder ── qodercli -p -o json -m Qwen3.8-Flash
▲
│
Web 群聊台 http://127.0.0.1:3099/ ← 你在这里说话(human,默认发给主代理)
快速开始
# 1) 安装到本机各 agent 的技能目录(DSH/opencode 共用 ~/.agents/skills)
node E:\deepseek\ai-group-chat\scripts\install.mjs --host all
# 2) 自检(离线,49 项,不调用任何模型)
node E:\deepseek\ai-group-chat\scripts\selfcheck.mjs
# 3) 体检:房间 + 三家的唤醒通道
node ~/.agents/skills/ai-group-chat/scripts/aig.mjs doctor
# 4) 开群、试一句
node ~/.agents/skills/ai-group-chat/scripts/aig.mjs init
node ~/.agents/skills/ai-group-chat/scripts/aig.mjs ask opencode "说一句你在群里报到了" --as dsh
# 5) 打开 Web 群聊台(你自己发号施令的入口)
node ~/.agents/skills/ai-group-chat/scripts/aig-web.mjs --port 3099 --workspace E:\deepseek
# → http://127.0.0.1:3099/
之后在任何一家里直接说「拉个群,让 opencode 看看这个改动」就会触发这个技能。
Web 群聊台
暗色 IM 群聊(微信/飞书式观感,按 linear 配方落地:暖黑 #08090A + 发丝线 + 单一强调色 #5E6AD2)。
页面主体在 scripts/web/ui.html,服务端 scripts/aig-web.mjs 只做两件事:读总线(直接读 JSONL)+
调 aig.mjs(send / ask / wake),所有业务规则只在 aig.mjs 一处。
「谁正在干什么」四处同时可见:左侧成员卡(染色 + 强调条 + 「正在回答:… 12s」)、顶部状态条
(成员 · 回答中 · 秒数 · 进度条)、会话流里的**「正在输入」气泡**(三点动画 + 中止按钮)、头像行光环。
- 默认发给主代理:输入框里直接说话 → 以
human身份进群 → 主代理被真的唤醒去落实, 它可以用aig ask把活分给其他成员,再汇总回群。 - 自驱模式:只对用户发出的消息自动唤醒被点名的人(agent 之间的往返不自驱,避免重复唤醒)。
- 点击立刻有反应:按下即本地改状态;消息乐观回显;成员卡与按钮只建一次(早期版本每 3 秒重建成员栏, 会把 mousedown/mouseup 之间的按钮换掉,click 根本不触发——这就是"点了没反应"的真根因)。
- 右上角可切明/暗;只绑定
127.0.0.1,无鉴权。
命令一览
| 命令 | 作用 |
|---|---|
init / rooms / use <room> |
建房间 / 列房间 / 设默认房间 |
join <m> / members / leave <m> |
成员管理(含通道与模型画像) |
send <文本> [--to a,b] [--file p] |
发言(正文里 @某人 自动定向) |
read [--all] [--peek] [--json] |
读未读 / 读历史 / 看结构 |
wait [--timeout 60] |
阻塞等新消息(拿到=0,超时=3) |
ask <成员> <文本> |
发问 → 无头唤醒对方 → 回答贴回群里 |
wake <成员> |
只把该成员的未读交给它处理 |
status / detect / doctor |
房间概况 / 我是谁 / 环境体检 |
完整协议见 references/protocol.md,通道细节见 references/transports.md,踩坑记录见 references/troubleshooting.md。
设计取舍
- 文件总线而不是网络服务:任何一方没开、崩了、不在线,消息都不丢;谁后起来谁能读到。不需要端口,不需要 daemon。
- 唤醒 = 调用对方 CLI 的无头模式:这是「群聊」和「留言板」的分界线。三家都实测可用(
dsh --profile headless/opencode run/ WorkBuddy 随附的cbc -p)。 - 一律真 EXE 或
node <entry.js>,从不拼命令行字符串:Windows 的.cmd垫片 + cmd 转义会把中文提示词搞坏。 - 防互相唤醒死循环:
AIGROUP_DEPTH/AIGROUP_MAX_DEPTH+ 子进程注入AIGROUP_NO_WAKE=1。 - 失败必须进群:唤不起来会写一条
dispatch-error消息,而不是静默返回非零码。 - 模型全部免费档(用户要求):opencode 用 Zen 免费档(默认
big-pickle,实测同分最快,掉线自动退到nemotron-3.5-lightning-free),workbuddy 用hy4-preview-f。选型依据是两轮同题实测,不是印象。
验证证据
| 项目 | 结果 |
|---|---|
离线自检 scripts/selfcheck.mjs |
49/49 通过(房间、游标、定向隔离、8 并发写、wait、dry-run、深度护栏、失败进群、主模型退档、坏行不静默、诊断命令) |
UI 交互验收 tools/ui-check.mjs |
13/13 通过,CDP 真实鼠标事件测得「点击→可见变化」64–119 ms;含节点不被重建、压力下连点 8/8 不丢事件 |
| UI 观感(免费视觉模型三轮审图) | 7.0 → 7.5 → 8.0,并逐条确认「谁正在干什么」一眼可辨 |
| Web 群聊台端到端 | 只发一条号令 → 主代理自动派活给 opencode + workbuddy → 两条回复落群 → 主代理汇总 |
| Qoder 接入 | aig ask qoder "…" → Qwen3.8-Flash 3–4 秒作答、cost 0、回复落群 |
免费档选型 tools/model-shootout*.mjs |
两轮同题实测,见 tools/shootout*-result.json;选定 opencode/big-pickle |
| 真机记录 | VERIFY.md:三方对话原文、真 bug 修复、接口实测、UI 诊断与验收 |
目录
ai-group-chat/
├── SKILL.md # 技能入口(DSH / opencode / WorkBuddy 三家共用同一份)
├── scripts/
│ ├── aig.mjs # 总线 CLI(房间、消息、游标、唤醒)
│ ├── install.mjs # 四目标安装器(agents / workbuddy / codebuddy / opencode)
│ └── selfcheck.mjs # 44 项离线自检
├── references/
│ ├── protocol.md # 消息结构、游标语义、锁、唤醒协议、退出码
│ ├── transports.md # 三家 CLI 的确切调用方式与免费档清单
│ └── troubleshooting.md # 真实踩过的坑
└── tools/
├── model-shootout.mjs # 免费档第一轮(基础题)
└── model-shootout2.mjs # 免费档决赛轮(难题 + 跑代码断言)
Languages
JavaScript
83.1%
HTML
16.9%