v0.2.1: merge-wait debounce, time-awareness, persistent heartbeat, red-state crash fix, pitfall #19

This commit is contained in:
kenyanghui
2026-09-29 11:41:31 +08:00
parent e9c54923ea
commit ebbdb82781
4 changed files with 29 additions and 5 deletions
+6
View File
@@ -16,3 +16,9 @@
## v0.1.x (历史) ## v0.1.x (历史)
macOS 视觉自动化技能初版:vision.py + 单文件 SKILL.md(坐标绝不落盘、每步验证、朋友圈发布全流程案例)。 macOS 视觉自动化技能初版:vision.py + 单文件 SKILL.md(坐标绝不落盘、每步验证、朋友圈发布全流程案例)。
## v0.2.1 (2026-09-29)
- agent.py: 消息合并去抖(merge_wait)、AI时间感知(当前时间注入prompt)、心跳时间持久化(重启不重发)、修复 Python3 推导式变量泄漏导致的红点状态复位崩溃
- pitfalls-linux.md: 第19条(发文件需先聚焦输入区,否则 Send 点击被吞)
- config-template: 新增 merge_wait
+8
View File
@@ -99,3 +99,11 @@ Qt 渲染管线崩掉——窗口 Map State 仍是 IsViewable、wmctrl 一切正
②自愈的透明壳重启前,先看进程存活时长——刚登录几分钟就"渲染死亡"的, ②自愈的透明壳重启前,先看进程存活时长——刚登录几分钟就"渲染死亡"的,
大概率是这条坑,宁可等待观察也不要立即杀; 大概率是这条坑,宁可等待观察也不要立即杀;
③同样的坑没人知道微信哪版会修,升级微信后应重测。 ③同样的坑没人知道微信哪版会修,升级微信后应重测。
## 19. 发文件时 Send 按钮点击无效(输入区必须先有焦点)
Qt 文件选择器的 File name 栏直接粘贴完整路径回车即可挂载文件,这步很稳;
但挂载后点 Send 按钮若输入区从未获得过焦点,点击会被无声吞掉(连按两次都没用)。
**对策**:先点一下输入区任意位置(如文本光标处)让输入区拿到焦点,再点 Send。
若仍未发出,补一个 Return(焦点在输入区时回车可发送文件卡片)。
+14 -5
View File
@@ -296,7 +296,8 @@ def compose_reply(info):
history = "\n".join(("对方: " if m["from"] == "them" else "我: ") + m["text"] history = "\n".join(("对方: " if m["from"] == "them" else "我: ") + m["text"]
for m in info["messages"]) for m in info["messages"])
prompt = (CFG["persona"] + prompt = (CFG["persona"] +
"\n\n当前聊天对象: " + info.get("chat_name", "未知") + "\n\n当前时间: " + datetime.now().strftime("%Y-%m-%d %H:%M %A") + # AI时间感知(学自 WXAUTO_SE)
"\n当前聊天对象: " + info.get("chat_name", "未知") +
"\n最近对话:\n" + history + "\n最近对话:\n" + history +
"\n\n请以「我」的身份回复对方最后一条消息。只输出回复正文,不要任何解释、引号或表情符号堆砌。") "\n\n请以「我」的身份回复对方最后一条消息。只输出回复正文,不要任何解释、引号或表情符号堆砌。")
reply = think(prompt).strip().strip('"') reply = think(prompt).strip().strip('"')
@@ -345,6 +346,8 @@ def handle_trigger(slots, img):
if not brain_ready(): if not brain_ready():
log.info("观察模式,无大脑,不操作: %s" % name) log.info("观察模式,无大脑,不操作: %s" % name)
continue continue
# 消息合并去抖(学自 WXAUTO_SE):等突发的一串消息到齐再读,避免逐条回复
time.sleep(CFG.get("merge_wait", 8))
y = c["slot_y0"] + s * c["slot_dy"] y = c["slot_y0"] + s * c["slot_dy"]
click(c["slot_click_x"], y, 1.5) # 点开该会话 click(c["slot_click_x"], y, 1.5) # 点开该会话
info = read_conversation() info = read_conversation()
@@ -431,6 +434,14 @@ def heartbeat():
hb = CFG.get("heartbeat_chat") hb = CFG.get("heartbeat_chat")
if not hb or not brain_ready(): if not hb or not brain_ready():
return # 没有大脑时无法核验目标,不发,防误发 return # 没有大脑时无法核验目标,不发,防误发
hb_file = os.path.join(BASE, "last_hb")
try:
since = time.time() - os.path.getmtime(hb_file)
except OSError:
since = CFG["heartbeat_interval_min"] * 60
if since < CFG["heartbeat_interval_min"] * 60 - 120:
return # 未到期。上次心跳时间持久化到文件,
# 服务重启不再重发(实测重启曾连发3条骚扰)
if not ensure_window(): if not ensure_window():
return return
img = scrot() img = scrot()
@@ -450,6 +461,7 @@ def heartbeat():
return return
free = sh("free -m | awk '/Mem:/{print $7}'").stdout.decode().strip() free = sh("free -m | awk '/Mem:/{print $7}'").stdout.decode().strip()
send_text("〔自动心跳〕Agent在线 ✓ 可用内存%sMB 时间%s" % (free, datetime.now().strftime("%m-%d %H:%M"))) send_text("〔自动心跳〕Agent在线 ✓ 可用内存%sMB 时间%s" % (free, datetime.now().strftime("%m-%d %H:%M")))
open(hb_file, "w").write(datetime.now().isoformat())
log.info("心跳已发") log.info("心跳已发")
# ---------------- 主循环 ---------------- # ---------------- 主循环 ----------------
@@ -457,7 +469,6 @@ def main():
ensure_window() ensure_window()
prev = None prev = None
red_state = {} red_state = {}
last_hb = time.time() - CFG["heartbeat_interval_min"] * 60 + 90 # 启动90秒后先发一次心跳
log.info("Agent启动 mode=%s brain=%s" % (CFG["mode"], "GLM" if brain_ready() else "无(观察)")) log.info("Agent启动 mode=%s brain=%s" % (CFG["mode"], "GLM" if brain_ready() else "无(观察)"))
while True: while True:
try: try:
@@ -486,9 +497,7 @@ def main():
prev = cur prev = cur
elif prev is None: elif prev is None:
prev = cur prev = cur
if time.time() - last_hb > CFG["heartbeat_interval_min"] * 60: heartbeat() # 内部按 last_hb 文件到期判定,重启不重发
heartbeat()
last_hb = time.time()
except Exception: except Exception:
log.exception("循环异常") log.exception("循环异常")
time.sleep(5) time.sleep(5)
+1
View File
@@ -1,6 +1,7 @@
{ {
"display": ":1", "display": ":1",
"poll_interval": 8, "poll_interval": 8,
"merge_wait": 8,
"mode": "draft", "mode": "draft",
"whitelist": [ "whitelist": [
"联系人A", "联系人A",