From ebbdb8278186201d7be0d0d6171a412eef055fc4 Mon Sep 17 00:00:00 2001 From: kenyanghui Date: Tue, 29 Sep 2026 11:41:31 +0800 Subject: [PATCH] v0.2.1: merge-wait debounce, time-awareness, persistent heartbeat, red-state crash fix, pitfall #19 --- CHANGELOG.md | 6 ++++++ references/pitfalls-linux.md | 8 ++++++++ scripts/agent.py | 19 ++++++++++++++----- scripts/config-template.json | 1 + 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e4d967..d311139 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,3 +16,9 @@ ## v0.1.x (历史) 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 diff --git a/references/pitfalls-linux.md b/references/pitfalls-linux.md index a3b744b..88654db 100644 --- a/references/pitfalls-linux.md +++ b/references/pitfalls-linux.md @@ -99,3 +99,11 @@ Qt 渲染管线崩掉——窗口 Map State 仍是 IsViewable、wmctrl 一切正 ②自愈的透明壳重启前,先看进程存活时长——刚登录几分钟就"渲染死亡"的, 大概率是这条坑,宁可等待观察也不要立即杀; ③同样的坑没人知道微信哪版会修,升级微信后应重测。 + +## 19. 发文件时 Send 按钮点击无效(输入区必须先有焦点) + +Qt 文件选择器的 File name 栏直接粘贴完整路径回车即可挂载文件,这步很稳; +但挂载后点 Send 按钮若输入区从未获得过焦点,点击会被无声吞掉(连按两次都没用)。 + +**对策**:先点一下输入区任意位置(如文本光标处)让输入区拿到焦点,再点 Send。 +若仍未发出,补一个 Return(焦点在输入区时回车可发送文件卡片)。 diff --git a/scripts/agent.py b/scripts/agent.py index a900e61..6f67132 100644 --- a/scripts/agent.py +++ b/scripts/agent.py @@ -296,7 +296,8 @@ def compose_reply(info): history = "\n".join(("对方: " if m["from"] == "them" else "我: ") + m["text"] for m in info["messages"]) 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请以「我」的身份回复对方最后一条消息。只输出回复正文,不要任何解释、引号或表情符号堆砌。") reply = think(prompt).strip().strip('"') @@ -345,6 +346,8 @@ def handle_trigger(slots, img): if not brain_ready(): log.info("观察模式,无大脑,不操作: %s" % name) continue + # 消息合并去抖(学自 WXAUTO_SE):等突发的一串消息到齐再读,避免逐条回复 + time.sleep(CFG.get("merge_wait", 8)) y = c["slot_y0"] + s * c["slot_dy"] click(c["slot_click_x"], y, 1.5) # 点开该会话 info = read_conversation() @@ -431,6 +434,14 @@ def heartbeat(): hb = CFG.get("heartbeat_chat") if not hb or not brain_ready(): 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(): return img = scrot() @@ -450,6 +461,7 @@ def heartbeat(): return free = sh("free -m | awk '/Mem:/{print $7}'").stdout.decode().strip() send_text("〔自动心跳〕Agent在线 ✓ 可用内存%sMB 时间%s" % (free, datetime.now().strftime("%m-%d %H:%M"))) + open(hb_file, "w").write(datetime.now().isoformat()) log.info("心跳已发") # ---------------- 主循环 ---------------- @@ -457,7 +469,6 @@ def main(): ensure_window() prev = None 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 "无(观察)")) while True: try: @@ -486,9 +497,7 @@ def main(): prev = cur elif prev is None: prev = cur - if time.time() - last_hb > CFG["heartbeat_interval_min"] * 60: - heartbeat() - last_hb = time.time() + heartbeat() # 内部按 last_hb 文件到期判定,重启不重发 except Exception: log.exception("循环异常") time.sleep(5) diff --git a/scripts/config-template.json b/scripts/config-template.json index fdbf1e5..b183794 100644 --- a/scripts/config-template.json +++ b/scripts/config-template.json @@ -1,6 +1,7 @@ { "display": ":1", "poll_interval": 8, + "merge_wait": 8, "mode": "draft", "whitelist": [ "联系人A",