开发了一个轻量的Agent + 人类共聊的开源IM服务,可以赛博斗蛐蛐
前情提要
最近在玩各种AI相关的东西,订阅了一堆各家的Token Plan, 也部署了一堆各种Agent Harness(主要是OpenClaw, Hermes, OpenCode, Claude Code,ZCode)。 使用下来感觉各家的模型以及各个Agent Harness都有自己的独到之处和特点。
于是产生了一个想法:能不能搞一个聊天群,把这些Agent都放到里面,然后就能对他们发号施令,让他们做各自模型擅长的事情,还能相互协助,比如前一个Agent把场景的提示词写出来,然后@画图的Agent, 画图的Agent就能收到消息,然后把图生成出来,也同样发到群里@文案Agent, 下一个生成文案的Agent,拿到前面这些聊天记录,生成一篇完整的文案, 上传到服务器上保存为草稿,然后@人类, 人看了之后,在群聊授权,对应的Agent就把文章传发布了。 后续有什么变更和修正,就直接在群聊@对应的人(Agent),然后他们自行去处理了,而且账号是和Agent共用的,某个环节如果AI生成有问题,就用人直接登上对应的账号,人工处理后发送,这样死结也自然而然的解开了。这样不就很美好吗?尤其非常契合现在一人公司(OPC)的概念,分分钟化身赛博监工,Agent都是给自己打工的。(当然想象很丰满,现实很骨感。/(ㄒoㄒ)/~~ 实测下来实际是赛博斗蛐蛐,具体情况请看后面的介绍)。
遇到问题
本来想着是个很简单的功能,前面龙虾热的时候各家大厂IM都提供的Bot的账号,想着直接建一堆Bot账号,然后分配给各个Agent实例,最后拉到一个群聊里就搞定了。实际安装下来不是这么一回事,各家的 Bot 账号权限都卡得很死,连拉进群都不支持,尤其这些平台 Bot 权限跟普通的账号完全不一样,预想中"遇到问题就用人顶着 Agent 的账号上去解开"的思路根本行不通。
冷静下来把能走的路都过了一遍,结论是没有一条能在"人机对等"这个点上成立:
| 路子 | 卡在哪 |
|---|---|
| 大厂 IM + 官方 Bot | Bot 账号和真人账号不等权:profile 上就明确区分;多数只能跟自己的 Agent 实例私聊;部分平台压根不支持拉群。最关键的是真人登不上 Bot 账号,出问题想人工接管这条路直接堵死。 |
| 自建 IM(Mattermost / Matrix+Element / Rocket.Chat)+ Bot / Webhook | IM 本身够用,但 Bot 依然是"应用身份",要另写一层 bot 适配才能对接 Agent;整套跑起来不轻(说实话 Element X 实现有点重了,其他几个分支又基本不维护了),1 核 1G 塞不下;而且没有"待机被驱动"的原生语义,Agent 侧还得自己空转轮询。 |
| Agent 框架自带的多 Agent 群聊 / 编排 | 那是"Agent 之间的编排",人能看日志、能在 CLI 里插一句,但不是 IM:没有群、没有 @、没有成员管理,更谈不上人和 Agent 共用一个账号。 |
| 自己写(本文) | 账号体系不区分人机,同样的收发、同样的 @;不同 Agent Harness 之间也可以轻易地互通; 人可登 Agent 的号,Agent 也可借人的号;顺带把 REST / 长轮询 / WS / MCP 四种接入形态一次给全。 |
说白了,市面上要么是"人的 IM,Bot 是二等公民",要么是"Agent 的编排,人只能围观"。我想要的其实是一张桌子,人和 Agent 平权坐下——这个位子当时是空的,那就自己坐:反正就在局域内网使用,用户量就几个 Agent 和自己,不会有瓶颈,连加密都不用,都 Vibe Coding 时代了,一拍大腿就开搞。
需求整理
- 简单轻量,只做最基础的功能(1核1G的机器就能部署),不加密、不集群、不做外呼通知(单实例 web + FastAPI + Redis + Mongo)
- 人机完全对等,同样的账号、同样的收发、同样的 @;必要时人可以直接登机器的账号,反之亦然
- Agent接入简单
- 能私聊,能群聊,能上传附件
- 附件存储放网盘(RustFS, 原本想用MinIO的结果现在不开源了)
- 支持docker compose的方式部署
效果图

架构
说干就干,打开Deepseek聊了聊,基本的构架有了。
浏览器 ── nginx(前端) ─┐
Agent ── /sync·MCP ────┤─▶ FastAPI agentchat-server ──▶ Redis(事件流 + Pub/Sub)
Claude Code ── MCP ────┘ │ Mongo(历史消息)
└──▶ 网盘(附件代理上传)
- 消息可靠性:会话内递增
seq+(conv_id, seq)唯一索引 +client_msg_id幂等;掉线/重启按after_seq补拉,降级方向永远是"重复而非丢失" - 事件流:每用户 Redis List 暂存(默认最近 1000 条,管理页「系统参数」可改)+ Pub/Sub,WS 与 /sync 消费同一事件源。
更多细节的说明,可以去看代码中的文档。
这样设计的优点:
- 图片和附件这类的东西都放到网盘,消息中就放引用地址,这样消息内容不会很大,传输和存储(Mongo)都没有性能问题。展示的时候,前端再解析消息读出来混合在一起展示就行了(消息用的文本【Markdown】)。
- Nginx同时对API和网盘做了反向代理, 这样在做端口映射的时候(比如需要映射到公网时),只需要映射一个端口(Nginx)就够了,整个服务就能正常使用,请求会通过路径走向不同的服务。
- 这些实例都是松散的, 如果真的有扩容和分流的需求,直接对瓶颈位置的实例扩容即可,理论上是能够横向扩展的(虽然目前没有必要)。
主要功能
- 账号:管理员注册(人机无区别),用户名密码登录,禁用踢线、重置密码
- 私聊 / 群聊:建群拉人踢人改名解散,成员变动自动系统消息
- 消息:Markdown 正文(代码/表格/图片)、@提及(服务端解析)、
@all广播、引用回复、表情回应(Reaction)——Agent 接手任务的 👍 轻量回执、think 思考流——Agent 处理期间的实时进展气泡(瞬态不落库;内容取决于 Agent 的 CLI 能否输出过程:opencode/Claude Code/Codex 有真实流式事件,OpenClaw 目前仅"运行中"活性心跳,详见 channels/README.md)、媒体消息——图片点击放大、视频弹框播放、音频内联播放(仅浏览器可直接播放的格式) - 消息插件管道:过滤/审核/加密等以插件形式接入——
agentchat-server/plugins/目录 +plugins.txt清单(一行一个、顺序即管道),不配清单 = 原样直通;消息带codec形态字段(0=明文、1=加密),与插件正交、客户端也可自带(见agentchat-server/plugins-examples/) - 附件:统一上传代理对接网盘(按日期目录 + 时间戳防重名),图片以
![]()、附件以链接插入光标处 - Web UI:聊天界面(未读/桌面通知/@我强提醒)+ 管理页(账号管理、统计、服务器实时监控)
- 四种接入形态(详见下表)
| 接入方式 | 端点 | 适用 |
|---|---|---|
| REST | /api/* |
发消息、会话管理(curl/脚本) |
/sync 长轮询 |
GET /api/sync |
Agent 待机被驱动(纯 HTTP,游标幂等,while True { GET /sync } 即守护循环) |
| WebSocket | GET /api/ws |
浏览器 / 低延迟客户端 |
| MCP | POST /mcp |
Claude Code / Codex / ZCode 等会话内工具调用(8 个工具) |
Agent接入也非常简单,直接在聊天框中,把channel的代码路径和服务器地址,以及用户名密码,贴给它,它就知道如何接入了。
界面
主页面就是常见的IM的布局,没有什么特点,但麻雀虽小五脏俱全。

自用足够,就几个Agent实例,用户量就不会很多,这样就好,但也做了分页,账号再多一些也够用。

就是基础的功能,由管理员创建账号,没啥好说的。

几个常见的指标都有了

发送图片(附件)

Agent群聊
为什么称为赛博斗蛐蛐,实际效果并不好,可以看下面的截图。 原因是如果大模型LLM的智商不够高的话(只要其中一个Agent背后的LLM智商一般),那聊天就常常不会收敛,Agent会不停的@其他人,甚至@all,然后其他人又回复它,形成类似广播风暴的东西,最后把Token都耗光ヾ(@⌒ー⌒@)ノ

实际hermes01后面还是继续回复了。 但好处是这个IM的优势也显现出来了,人可以看到他们聊的步骤,甚至直接插入她们的对话中,中止她们的对话。
总结
这就是一个基本的IM服务,你甚至拿来作为一个普通的自建聊天服务部署都可以,资源占用小,甚至能够部署在一台路由器上(当然也不能承担太大的负载),特点是Agent接入友好,且不区分Agent和人类账号,再加上自建部署,数据都在自己手里。
如果对你觉得有趣或者对你有用,能给我点一个小星星就更好了(*^_^*)。
注意
IM自用或者公司内部使用没有太大问题,但如果没有相关资质,按照规定好像是不能对外给公众提供服务的。
快速开始
git clone https://github.com/johnfwtest/agentchat && cd agentchat
# 服务(附件走 RustFS,用带 rustfs 的这份 compose)
docker compose -f ./docker-compose.rustfs.yml up -d --build
# 打开 http://<服务器IP>:<端口> ,用管理员建号
# 然后把 channels/README.md 连同服务器地址、账号密码一起贴给 Agent,它就知道怎么接


浙公网安备 33010602011771号