coordinator: use XMPP in-band signaling instead of shared DB (cross-machine compatible)

This commit is contained in:
2026-06-20 19:02:04 +08:00
parent 457ad27044
commit 90429116c7
+27 -50
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
"""XMPP Bot - 统一版,支持 --agent mohe|zhiwei|xiao 参数"""
import asyncio, logging, ssl, json, urllib.request, os, time, sys, re, sqlite3
import asyncio, logging, ssl, json, urllib.request, os, time, sys, re
from slixmpp import ClientXMPP
# ── Agent 配置 ──────────────────────────────────────────────
@@ -104,6 +104,8 @@ class AgentBot(ClientXMPP):
self._call_seq = 0
self._muc_joined = False
self._recent_sent = []
self._coordinator = 'mohe' # 默认协调者
self._granted = None
async def on_connected(self, event):
logging.info(f"🔗 {AGENT_NAME} TCP连接已建立")
@@ -139,50 +141,31 @@ class AgentBot(ClientXMPP):
if nickname == AGENT_NICK:
return
# Coordinator 模式:读取当前协调者和被授权发言者
_coordinator = None
_granted = None
try:
_cdb = sqlite3.connect('/home/hmo/.hermes/state.db')
_crow = _cdb.execute("SELECT value FROM state_meta WHERE key='coregroup_coordinator'").fetchone()
_grow = _cdb.execute("SELECT value FROM state_meta WHERE key='coregroup_granted_speaker'").fetchone()
_cdb.close()
if _crow: _coordinator = _crow[0]
if _grow: _granted = _grow[0]
except Exception:
pass
# Coordinator 模式 — 全走 XMPP 消息,不依赖共享 DB
# hmo 可以动态切换 coordinator
# 1. hmo 切换 coordinator → 所有 bot 检测,各自更新本地状态
if nickname == 'hmo' and 'coordinator=' in body.lower():
for _name in ['mohe', 'zhiwei', 'xxm']:
if f'coordinator={_name}' in body.lower():
try:
_wdb = sqlite3.connect('/home/hmo/.hermes/state.db')
_wdb.execute("INSERT OR REPLACE INTO state_meta (key, value) VALUES ('coregroup_coordinator', ?)", (_name,))
_wdb.execute("DELETE FROM state_meta WHERE key='coregroup_granted_speaker'")
_wdb.commit()
_wdb.close()
_coordinator = _name
_granted = None
self._coordinator = _name
self._granted = None
logging.info(f"👑 Coordinator 切换为 {_name}")
except Exception:
pass
break
# 判断当前 bot 能否处理这条消息
_can_speak = False
if _coordinator == AGENT_NICK:
_can_speak = True # 协调者始终能说
if _granted == AGENT_NICK:
_can_speak = True # 被授权者也能说
# 用完后自动清除授权(一次性)
try:
_wdb = sqlite3.connect('/home/hmo/.hermes/state.db')
_wdb.execute("DELETE FROM state_meta WHERE key='coregroup_granted_speaker'")
_wdb.commit()
_wdb.close()
except Exception:
pass
# 2. 检测其他 bot 发出的授权信号 [GRANT:xxx]
_grant_match = re.search(r'\[GRANT:(\w+)\]', body)
if _grant_match:
self._granted = _grant_match.group(1)
logging.info(f"🎤 收到授权:{self._granted} 获得发言权")
# 3. 判断当前 bot 能否处理这条消息
_coordinator = getattr(self, '_coordinator', AGENT_NICK)
_granted = getattr(self, '_granted', None)
_can_speak = (_coordinator == AGENT_NICK) or (_granted == AGENT_NICK)
if _can_speak and _granted == AGENT_NICK:
# 被授权者用完即收回
self._granted = None
if not _can_speak:
return # 代码层拦截,不走 LLM
@@ -208,9 +191,9 @@ class AgentBot(ClientXMPP):
"3. 别人说错了关键事实,不纠正会有后果\n"
"如果以上都不符合,你的回复必须只包含 __SILENT__ 这10个字符,"
"不要有任何其他内容(不要前缀、不要解释、不要标点、不要空格)。\n\n"
"注意:你是当前群聊的协调者(coordinator)。如果你认为个问题应该由其他 Agent 回答,"
"可以在回复中包含 [GRANT:agent名] 标记(例如 [GRANT:zhiwei]),bot 会自动授权该 Agent 发言一次。"
"授权后该 Agent 的回复会出现在群里,你不需要自己回答\n\n"
"注意:你是协调者(coordinator)。如果你认为个问题应该由其他 Agent 回答,"
"可以在回复中加入 [GRANT:agent名](例如 [GRANT:zhiwei]),"
"该 Agent 会在看到标记后获得发言权。标记会显示在消息中\n\n"
f"[核心群 {room}] {nickname} 说: {body}"
)
await self.call_hermes(ctx_body, room, is_group=True)
@@ -266,15 +249,9 @@ class AgentBot(ClientXMPP):
_grant_match = re.search(r'\[GRANT:(\w+)\]', reply)
if _grant_match:
_grant_name = _grant_match.group(1)
try:
_gdb = sqlite3.connect('/home/hmo/.hermes/state.db')
_gdb.execute("INSERT OR REPLACE INTO state_meta (key, value) VALUES ('coregroup_granted_speaker', ?)", (_grant_name,))
_gdb.commit()
_gdb.close()
logging.info(f"🎤 Coordinator 授权 {_grant_name} 发言")
except Exception:
pass
reply = reply.replace(_grant_match.group(0), '').strip()
self._granted = _grant_name
logging.info(f"🎤 授权 {_grant_name} 发言(通过 XMPP 发送)")
# 不剥离标记,让其他 bot 从 XMPP 消息中解析
if msg_type == 'groupchat':
self.send_message(mto=sender, mbody=reply, mtype='groupchat')