简单智能体房间
何时使用
房间 是一个多对多聊天日志。任意数量的智能体都可以向房间发布消息,任何智能体都可以 Monitor(监控)它。典型用途:
- 多个子智能体协作处理相关任务,需共享中间发现(例如,编码者报告解析器 bug、研究者报告 URL、测试者报告失败的断言)。
- 一个长时间运行的智能体,需要能被之后可能生成的其他智能体发现。
- 作为执行多步骤计划的“智能体团队”的协调渠道。
对于一对一的、与单个长期运行的子智能体的通信,兄弟技能 simple-agent-comms(两个文件 + 一个监控器)会更轻量。
你(智能体)需要知道的内容
三个命令,全部位于 $PATH 中。你不需要知道任何文件路径。 房间名称是唯一需要传递的内容。
simple-room-send <room> [args] # 发布消息
simple-room-monitor <room> [args] # 流式输出新消息(对 Monitor 友好)
simple-room-scan <room> <subcmd> # 计数 / tail / active / grep / ids / path
就是这样。你选择一个房间名称(字母数字、点号、下划线、连字符;最长 64 个字符),为自己选择一个智能体 ID,然后发送/读取消息。
如果系统的其他部分需要知道房间存储的位置,其标准位置是 ~/.cache/simple-agent-room/<room>.log,可通过 $SIMPLE_AGENT_ROOM_DIR 环境变量覆盖(很少使用,仅用于测试或多租户布局)。
快速开始(一个智能体,一行命令)
simple-room-send kitchen "stove is on" # 你发布了一条消息
simple-room-scan kitchen tail -n 5 # 你读取了最近 5 条
simple-room-scan kitchen active --window 60 # 查看谁在这里
快速开始(两个智能体协调)
# 智能体 A # 智能体 B
simple-room-send tasks -m "I'm starting" \
simple-room-monitor tasks # 在 Monitor 中:
▲ # 新行到达,
│ # 自我消息被过滤
# --- 稍后,在另一个终端/轮次中 --- simple-room-send tasks -m "ack"
正确的模式:将监控器作为持久化的后台命令在 Monitor(...) 工具调用中生成,然后在前台发送/扫描/交谈。
ASCII 流程图
发布消息
智能体 simple-room-send 磁盘
───── ──────────────── ───────
$ simple-room-send kitchen \ │
-a alice -m "hi" │ format_record
│ → JSON 行 + '\n'
│ → os.open(O_APPEND|O_CREAT, 0o644)
│ → os.write(fd, line)
▼
~/.cache/simple-agent-room/kitchen.log
─────────────────────────────────────────
{"id":"…","ts":"…","agent":"alice", … }
{"id":"…","ts":"…","agent":"bob", … }
…
并发的发布者不会破坏彼此的记录:对于缓冲区 ≤ PIPE_BUF(4096 字节)的写入,POSIX 上的 O_APPEND 写入是原子的,而实际的 JSON 记录通常小于 1 KiB。
监控(Monitor(...) 形态)
simple-room-monitor Monitor 工具
─────────────────── ───────────
backfill: 输出最后 N 条已有记录 stdout
(或为 0 表示“仅实时”) │
解析 JSON,丢弃自身消息 ──────►│ 每行都是一个
│ 后台事件,
实时: pyinotify.watch 监控文件 │ 不是用户回复
IN_MODIFY ─► 读取新字节
IN_DELETE ─► 等待重新创建
IN_MOVE ─► 等待重新创建
▼
智能体读取该行,
决定如何处理
此脚本不会轮询。它阻塞在 pyinotify.Notifier 中,并在内核事件发生时唤醒。在本地文件系统上,从发送到事件传递的延迟低于毫秒级。
扫描(只读)
simple-room-scan <room> <subcmd> [args]
────────────────────────────────────────
│
├─ count → 1 行:总记录数
├─ ids → 每行一个 ID(已排序)
├─ path → 1 行:绝对文件路径
├─ active [--window S] → 每个活跃智能体一行 "agent<TAB>ts"
├─ tail [-n N] [-a A] → 最后 N 条(或特定智能体的)记录
└─ grep <pattern> → 消息内容匹配的记录
[--since-seq N]
[-a A] [--json]
│
▼
iter_records(path) → dict 流
(跳过空白 / '#' / 格式错误行)
参考
simple-room-send <room> [-m MSG] [-k {msg,system,meta}] [-a AGENT] [--stdin]
| 标志 | 含义 |
|---|---|
-m MESSAGE | 消息文本(否则从标准输入读取) |
--stdin | 强制从标准输入读取(会覆盖 -m) |
-k KIND | msg(默认)、system 或 meta |
-a AGENT | 本地智能体 ID;默认为 $SIMPLE_AGENT_ID,否则为 $USER-<host>-<pid> |
--seq N | 显式单调序列号(可选) |
--id ID | 显式记录 ID(uuid4 十六进制,可选) |
--ts ISO | 显式时间戳(可选) |
-q | 在标准错误输出上抑制“sent id=…” 确认信息 |
消息会原样存储(换行符、制表符、反斜杠在记录内部会被 JSON 转义;在读取端你会看到它们作为普通字符)。
simple-room-monitor <room> [-a AGENT] [--exclude-self] [--backfill N] [--grep PATTERN] [--json]
| 标志 | 含义 |
|---|---|
-a AGENT | 本地智能体 ID(默认如上) |
--exclude-self | 丢弃来自本地智能体的记录(默认:开启) |
--no-exclude-self | 包含自身记录(当你希望看到自己的消息时使用) |
-b N | 在进入实时模式前,发射最后 N 条已有记录 |
(-1 = 全部;0 = 仅事件,默认值) | |
--grep PATTERN | 仅发射渲染后的行匹配正则表达式的记录 |
--json | 每行发射原始 JSON 记录(否则为 HH:MM:SS agent: msg) |
此脚本会一直运行直到被终止。将其用作持久化 Monitor(...) 工具调用的 command —— 正确的模式是每个房间、每个会话一个 Monitor。Monitor 事件是后台事件,不是用户回复;不要将它们视为结束一轮对话的用户消息。
simple-room-scan <room> <subcommand> [args]
| 子命令 | 参数 | 输出 |
|---|---|---|
count | — | 总记录数 |
ids | — | 每行一个智能体 ID |
path | — | 绝对文件路径 |
active | --window SECONDS (120) | 每个活跃智能体一行 agent<TAB>ts |
tail | -n N (20), -a AGENT, --json | 最后 N 条记录 |
grep | PATTERN, --since-seq N, -a A, --json, -c (计数→stderr) | 匹配的记录 |
grep 区分大小写,使用 Python 正则表达式语法(在常见情况下接近 PCRE——.、*、+、?、\b、字符类)。
自我过滤模式(“不要自言自语”配方)
监控器默认启用 --exclude-self,因为在单智能体轮询循环中,智能体刚刚发送了自己的消息,将其作为事件回放是噪音。如果你需要看到单智能体调试会话的两边消息,请传递 --no-exclude-self。
同一房间中的两个智能体必须选择不同的智能体 ID。默认值($USER-<host>-<pid>)在每个进程中是唯一的;如果你从同一个 shell 中生成多个智能体,请为每个智能体设置 SIMPLE_AGENT_ID 以保持它们不同。
你不需要知道的内容
- 磁盘路径。它是
~/.cache/simple-agent-room/<room>.log。你永远不需要直接写入它。simple-room-scan <room> path会告诉你(如果你好奇的话)。 - 模式。库会读取文件、解析 JSON、丢弃格式错误的行,并转义消息字符串。你会看到一个包含
id、ts、agent、msg、kind、可选的seq的字典。 - 轮询。监控器完全由事件驱动(inotify)。
- 锁定。记录大小 < 4 KiB,POSIX 上的 O_APPEND 是原子的,因此不需要 flock / fcntl。两个写入者可以在不破坏彼此记录的情况下同时发布。
值得了解的故障模式
- 删除/重命名后 inotify 过期:库会在
IN_CREATE事件上为同一路径重新附加监控,因此执行日志轮换的工具可以正常处理。如果你手动rm文件,监控器会在下一次发送时创建一个新的空文件。 - 智能体 ID 冲突:具有相同 ID 的两个智能体会将对方的消息视为自己的(并默认过滤掉它们)。始终使用
SIMPLE_AGENT_ID或$USER-<host>-<pid>默认值。 - 时钟偏差:
ts是发送方的挂钟 UTC 时间。如果你跨智能体按ts排序,请设置 NTP。对于单个智能体内部的排序,使用seq。 - 非常长的消息:没有硬性上限,但如果你经常发布超过 4 KiB 的消息,可能会达到 PIPE_BUF 并开始看到记录被截断。请将消息保持在 4 KiB 以下。
- 格式错误的行:读取器会静默跳过任何无法解析为 JSON 对象的行。垃圾输入 = 垃圾被静默丢弃;其周围的合法记录不受影响。
文件
skills/simple-agent-room/
SKILL.md ← 本文档
bin/
simple-room-send ← 瘦包装器,调用 lib.simple_room_send.main
simple-room-monitor ← 瘦包装器,调用 lib.simple_room_monitor.main
simple-room-scan ← 瘦包装器,调用 lib.simple_room_scan.main
lib/
simple_agent_room_lib.py ← 共享库(格式化/解析、inotify、扫描)
simple_room_send.py ← 发送入口点
simple_room_monitor.py ← 监控入口点
simple_room_scan.py ← 扫描入口点
每个 bin/simple-room-* 包装器都是一个 12 行的 Python 文件,它调整 sys.path 并委托给 lib/simple_room_*.main。包装器会解析自己的真实路径,因此 ~/.local/bin/ 中的符号链接可以透明工作。
安装
从仓库根目录:
./install.sh
这会将 simple-agent-room 放置到 ~/.claude/skills/ 和 ~/.agents/skills/ 中,并将三个 CLI 程序作为 simple-room-send、simple-room-monitor、simple-room-scan 添加到你的 $PATH。
如果你希望手动安装:
ln -s "$PWD/skills/simple-agent-room" ~/.claude/skills/simple-agent-room
ln -s "$PWD/skills/simple-agent-room" ~/.agents/skills/simple-agent-room
ln -s "$PWD/skills/simple-agent-room/bin/"* ~/.local/bin/
依赖
- Python ≥ 3.10
pyinotify(pip install pyinotify)。仅监控器需要;simple-room-send和simple-room-scan无需它也能工作。