coordinator: default all can speak, REVOKE=read-only 5min, GRANT overrides

This commit is contained in:
2026-06-20 19:54:37 +08:00
parent aabec04d8a
commit ad3e0f004b
+36 -23
View File
@@ -141,9 +141,9 @@ class AgentBot(ClientXMPP):
if nickname == AGENT_NICK: if nickname == AGENT_NICK:
return return
# Coordinator 模式 — 全走 XMPP 消息,不依赖共享 DB # Coordinator 模式 — 全走 XMPP 消息
# 1. hmo 切换 coordinator → 所有 bot 检测,各自更新本地状态 # 1. hmo 切换 coordinator
if nickname == 'hmo' and 'coordinator=' in body.lower(): if nickname == 'hmo' and 'coordinator=' in body.lower():
for _name in ['mohe', 'zhiwei', 'xxm']: for _name in ['mohe', 'zhiwei', 'xxm']:
if f'coordinator={_name}' in body.lower(): if f'coordinator={_name}' in body.lower():
@@ -152,31 +152,43 @@ class AgentBot(ClientXMPP):
logging.info(f"👑 Coordinator 切换为 {_name}") logging.info(f"👑 Coordinator 切换为 {_name}")
break break
# 2. 检测其他 bot 发出的授权信号 [GRANT:xxx] # 2. 检测授权信号(优先于收回,GRANT 可以覆盖 REVOKE
_grant_match = re.search(r'\[GRANT:(\w+)\]', body) _grant_match = re.search(r'\[GRANT:(\w+)\]', body)
if _grant_match: if _grant_match:
self._granted = _grant_match.group(1) self._granted = _grant_match.group(1)
logging.info(f"🎤 收到授权:{self._granted} 获得发言权") self._revoked_until = 0 # 被授权时解除收回
logging.info(f"🎤 收到授权:{self._granted}")
# 3. 判断当前 bot 能否处理这条消息 # 3. 检测收回信号
_coordinator = getattr(self, '_coordinator', AGENT_NICK) _revoke_match = re.search(r'\[REVOKE:(\w+)\]', body)
_granted = getattr(self, '_granted', None) _revoked_until = getattr(self, '_revoked_until', 0)
_can_speak = (_coordinator == AGENT_NICK) or (_granted == AGENT_NICK) if _revoke_match and _revoke_match.group(1) == AGENT_NICK:
self._revoked_until = time.time() + 300 # 5分钟自动解除
if _can_speak and _granted == AGENT_NICK: logging.info(f"🔇 {AGENT_NAME} 发言权被收回(5分钟后自动恢复)")
# 被授权者用完即收回 if time.time() < _revoked_until:
self._granted = None # 被收回者:只读模式,能看到但不能回复
_rr = sender.split('/')[0]
if not _can_speak: _readonly_body = f"【只读消息】你目前被暂时收回发言权。只需了解内容。输出 __SILENT__。\n\n[核心群 {_rr}] {nickname} 说: {body}"
# 无权发言 → 仍发给 LLM 用于上下文积累,但标记为只读
_room = sender.split('/')[0]
_readonly_body = (
"【只读消息】你不需要回复,只需了解内容以便后续上下文连贯。输出 __SILENT__。\n\n"
f"[核心群 {_room}] {nickname} 说: {body}"
)
await self.call_hermes(_readonly_body, sender, is_group=True) await self.call_hermes(_readonly_body, sender, is_group=True)
return return
# 3. 判断角色
_coordinator = getattr(self, '_coordinator', 'mohe')
_granted = getattr(self, '_granted', None)
_is_coordinator = (_coordinator == AGENT_NICK)
_is_granted = (_granted == AGENT_NICK)
if _is_granted:
self._granted = None # 用完即收回
room = sender.split('/')[0]
# 4. 被 REVOKE 的人:只读模式
if time.time() < getattr(self, '_revoked_until', 0):
_readonly = f"【只读消息】你目前被收回发言权。只需了解内容。输出 __SILENT__。\n\n[核心群 {room}] {nickname} 说: {body}"
await self.call_hermes(_readonly, sender, is_group=True)
return
# 硬闭嘴闸门:hmo 说闭嘴类的话 → 静默 5 分钟 # 硬闭嘴闸门:hmo 说闭嘴类的话 → 静默 5 分钟
_silent_until = getattr(self, '_silent_until', 0) _silent_until = getattr(self, '_silent_until', 0)
if time.time() < _silent_until: if time.time() < _silent_until:
@@ -198,9 +210,10 @@ class AgentBot(ClientXMPP):
"3. 别人说错了关键事实,不纠正会有后果\n" "3. 别人说错了关键事实,不纠正会有后果\n"
"如果以上都不符合,你的回复必须只包含 __SILENT__ 这10个字符," "如果以上都不符合,你的回复必须只包含 __SILENT__ 这10个字符,"
"不要有任何其他内容(不要前缀、不要解释、不要标点、不要空格)。\n\n" "不要有任何其他内容(不要前缀、不要解释、不要标点、不要空格)。\n\n"
"注意:你是协调者(coordinator)。如果你认为某个问题应该由其他 Agent 回答," "注意:你是协调者(coordinator)。你可以控制讨论节奏。"
"可以在回复中加入 [GRANT:agent名](例如 [GRANT:zhiwei])," " [GRANT:agent名] 授权某人发言(例如 [GRANT:zhiwei]),"
"该 Agent 会在看到标记后获得发言权。标记会显示在消息中。\n\n" "用 [REVOKE:agent名] 收回某人发言权(例如 [REVOKE:zhiwei])。"
"其他 Agent 默认可以说话,但你可以在他们跑题时收回权限。标记会显示在消息中。\n\n"
f"[核心群 {room}] {nickname} 说: {body}" f"[核心群 {room}] {nickname} 说: {body}"
) )
await self.call_hermes(ctx_body, room, is_group=True) await self.call_hermes(ctx_body, room, is_group=True)