diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fd5ffae..a4a44626 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,12 @@ -# MoFin 架构改革 — 变更日志 +## 2026-07-19 — XMPP bot 递归重连修复 + +### 根因 +XMPP bot 断线后 `on_disconnect` 调用 `self.reconnect()`,但 slixmpp 在重连失败时再次触发 disconnect 事件,导致 `on_disconnect → reconnect → disconnect` 无限递归,最终 `maximum recursion depth exceeded`。 + +### 修改 +- **文件:** `MoFin/deploy/bot/xmpp_agent_core.py` +- **变更:** 在 `XmppAgent.__init__` 中增加 `_reconnecting` 标志位,重连开始前设为 True、结束后(finally)恢复 False。重入时直接 return。 +- **效果预期:** 断线重连失败后最多尝试一次,不会爆栈;bot 保持在 service 管理下等待 systemd 自动重启。 > 日期:2026-06-29 ~ 2026-07-03 > 执行:Sisyphus (小小莫) + Zhiwei (知微) diff --git a/analyst-knowledge-log.md b/analyst-knowledge-log.md index 07ac6e9d..b7d42d0c 100644 --- a/analyst-knowledge-log.md +++ b/analyst-knowledge-log.md @@ -1,4 +1,14 @@ -记录知微对MoFin系统的缺陷修复和知识萃取项。 +## 2026-07-19 20:58 XMPP bot on_disconnect 递归重连修复 + +**发现了什么:** zhiwei XMPP bot 从 20:53:57 开始反复输出 "XMPP 断开" × 27次,最后 "重连失败: maximum recursion depth exceeded"。slixmpp 的 `reconnect()` 在连接失败时会再次触发 disconnect 事件,而 `on_disconnect` 无条件调用 `reconnect()`,形成无限递归。 + +**修改了什么:** +- 文件: `MoFin/deploy/bot/xmpp_agent_core.py` +- 在 `XmppAgent.__init__` 添加 `self._reconnecting = False` +- `on_disconnect` 判断 `_reconnecting` 为 True 时直接 return +- 重连前后用 try/finally 管理标志位 + +**效果预期:** 断线后最多一次 reconnect 尝试,不会爆栈。重连失败后保持静默,靠 systemd 的 Restart=always 自动恢复。 ## 2026-07-02 11:30 macro_context_collector.py Pattern 9 百分比阈值修复 + 跨句涨幅匹配修复 diff --git a/dashboard.py b/dashboard.py new file mode 100644 index 00000000..af5a95c2 --- /dev/null +++ b/dashboard.py @@ -0,0 +1,304 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +dashboard.py - MoFin management dashboard backend +================================================== +Minimal Flask app on :5804. Monitors MoFin services and serves +module specs (human_help + ai_spec) via ?§ button system. + +Adapted from AgentsMeeting dashboard.py. Does NOT modify server.py. +""" +import os, sys, json, socket, logging, time +from pathlib import Path +from datetime import datetime +from flask import Flask, jsonify, request, send_from_directory + +# ---- Paths (auto-detect from script location) ---- +_SCRIPT_DIR = Path(__file__).resolve().parent # MoFin/ +_TEMPLATES_DIR = _SCRIPT_DIR / "templates" +_SPECS_DIR = _SCRIPT_DIR / "specs" +_GATEWAY_DIR = _SCRIPT_DIR / "gateway" +_LOGS_DIR = _GATEWAY_DIR / "logs" +_TEMP_DIR = _GATEWAY_DIR / "temp" + +# Allow override via env +_PROJECT_ROOT = os.environ.get("MOFIN_ROOT") +if _PROJECT_ROOT: + _SCRIPT_DIR = Path(_PROJECT_ROOT) + _TEMPLATES_DIR = _SCRIPT_DIR / "templates" + _SPECS_DIR = _SCRIPT_DIR / "specs" + _GATEWAY_DIR = _SCRIPT_DIR / "gateway" + _LOGS_DIR = _GATEWAY_DIR / "logs" + _TEMP_DIR = _GATEWAY_DIR / "temp" + +app = Flask(__name__, template_folder=str(_TEMPLATES_DIR)) + +# ---- Logging ---- +_LOG_FILE = _LOGS_DIR / "dashboard.log" +_LOG_FILE.parent.mkdir(parents=True, exist_ok=True) +logging.basicConfig( + filename=str(_LOG_FILE), + level=logging.INFO, + format="%(asctime)s [dashboard] %(message)s", +) +log = logging.getLogger("dashboard") + +# ---- Constants ---- +PORT = int(os.environ.get("MOFIN_DASHBOARD_PORT", 5807)) +START_TIME = time.time() + +# ---- Monitored Services ---- +SERVICES = [ + { + "name": "mofin_api", + "label": "MoFin API", + "port": 8899, + "host": "127.0.0.1", + "type": "http", + "check": "/api/portfolio", + "layer": "核心服务", + "critical": True, + }, + { + "name": "mofin_dashboard", + "label": "Dashboard", + "port": 5807, + "host": "127.0.0.1", + "type": "http", + "check": "/api/health", + "layer": "核心服务", + "critical": True, + }, + { + "name": "zhiwei_gateway", + "label": "知微 Gateway", + "port": 8643, + "host": "127.0.0.1", + "type": "http", + "check": "/v1/health", + "layer": "AI 网关", + "critical": True, + }, + { + "name": "ejabberd", + "label": "ejabberd XMPP", + "port": 5222, + "host": "127.0.0.1", + "type": "tcp", + "check": None, + "layer": "通信层", + "critical": True, + }, + { + "name": "mofin_db", + "label": "MoFin 数据库", + "port": 0, + "host": "127.0.0.1", + "type": "db", + "check": "/home/hmo/web-dashboard/data/mofin.db", + "layer": "数据层", + "critical": True, + }, +] + + +# ---- Service Check Helpers ---- + +def _check_tcp(host, port, timeout=3): + """Check if TCP port is open.""" + try: + sock = socket.create_connection((host, port), timeout=timeout) + sock.close() + return True + except Exception: + return False + + +def _check_http(host, port, path, timeout=3): + """Check HTTP endpoint returns 2xx.""" + import urllib.request + try: + url = f"http://{host}:{port}{path}" if host else f"http://127.0.0.1:{port}{path}" + req = urllib.request.Request(url) + resp = urllib.request.urlopen(req, timeout=timeout) + return 200 <= resp.status < 300 + except Exception: + return False + + +def _check_db(db_path): + """Check SQLite database is accessible.""" + import sqlite3 + try: + conn = sqlite3.connect(db_path) + conn.execute("SELECT 1") + conn.close() + return True + except Exception: + return False + + +def _check_service(svc): + """Check a single service, return (ok, detail).""" + if svc["type"] == "tcp": + ok = _check_tcp(svc["host"], svc["port"]) + return ok, "port open" if ok else "port closed" + elif svc["type"] == "http": + ok = _check_http(svc["host"], svc["port"], svc["check"]) + return ok, "HTTP 2xx" if ok else "HTTP fail" + elif svc["type"] == "db": + ok = _check_db(svc["check"]) + return ok, "DB accessible" if ok else "DB fail" + return False, "unknown type" + + +# ---- API Endpoints ---- + +@app.route("/") +def index(): + return send_from_directory(str(_TEMPLATES_DIR), "dashboard.html") + + +@app.route("/api/health") +def api_health(): + return jsonify({ + "status": "ok", + "uptime": int(time.time() - START_TIME), + "version": "1.0", + }) + + +@app.route("/api/services") +def api_services(): + """Return status of all monitored services.""" + result = [] + for svc in SERVICES: + ok, detail = _check_service(svc) + result.append({ + "name": svc["name"], + "label": svc["label"], + "port": svc["port"], + "type": svc["type"], + "layer": svc["layer"], + "critical": svc["critical"], + "health": {"ok": ok}, + "detail": detail, + }) + ok_count = sum(1 for s in result if s["health"]["ok"]) + return jsonify({ + "services": result, + "summary": {"ok": ok_count, "total": len(result)}, + }) + + +@app.route("/api/expected") +def api_expected(): + """Return expectation matrix.""" + expected = [] + for svc in SERVICES: + expected.append({ + "name": svc["name"], + "label": svc["label"], + "port": svc["port"], + "expected": "running", + "critical": svc["critical"], + "layer": svc["layer"], + "check": f"{svc['type']}:{svc['port']}" if svc["port"] else svc["type"], + }) + + # Actual status + actual = {} + for svc in SERVICES: + ok, _ = _check_service(svc) + actual[svc["name"]] = "running" if ok else "stopped" + + return jsonify({ + "expected": expected, + "actual": actual, + }) + + +@app.route("/api/monitor") +def api_monitor(): + """Aggregate health check data from Tier1/Tier2 reports.""" + tasks = [] + tier1 = {"summary": {"ok": 0, "total": 0}, "services": []} + tier2 = {"summary": {"ok": 0, "total": 0}, "services": []} + + # Try to read Tier1 report + t1_path = _TEMP_DIR / "last_health_check.json" + if t1_path.exists(): + try: + with open(t1_path, encoding="utf-8") as f: + tier1 = json.load(f) + tasks.append({"name": "agents-health-check", "status": "cron_ok"}) + except Exception: + tasks.append({"name": "agents-health-check", "status": "error"}) + else: + tasks.append({"name": "agents-health-check", "status": "not_deployed"}) + + # Try to read Tier2 report + t2_path = _TEMP_DIR / "last_daily_health.json" + if t2_path.exists(): + try: + with open(t2_path, encoding="utf-8") as f: + tier2 = json.load(f) + tasks.append({"name": "agents-daily-health", "status": "cron_ok"}) + except Exception: + tasks.append({"name": "agents-daily-health", "status": "error"}) + else: + tasks.append({"name": "agents-daily-health", "status": "not_deployed"}) + + # Self-check: are we running? + svc_result = api_services().get_json() + tasks.append({ + "name": "dashboard", + "status": "running", + "detail": f"services: {svc_result.get('summary', {}).get('ok', 0)}/{svc_result.get('summary', {}).get('total', 0)}", + }) + + return jsonify({ + "tasks": tasks, + "tier1": tier1, + "tier2": tier2, + "generated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + }) + + +@app.route("/api/module-spec/") +def api_module_spec(module): + """Serve spec JSON for a module.""" + # Safety: prevent path traversal + module = module.replace("..", "").replace("/", "").replace("\\", "") + spec_path = _SPECS_DIR / f"{module}.json" + if spec_path.exists(): + try: + with open(spec_path, encoding="utf-8") as f: + return jsonify(json.load(f)) + except Exception as e: + return jsonify({"error": f"Failed to read spec: {e}"}), 500 + return jsonify({"error": f"Module '{module}' not found"}), 404 + + +# ---- Main ---- + +if __name__ == "__main__": + # Ensure directories exist + _LOGS_DIR.mkdir(parents=True, exist_ok=True) + _TEMP_DIR.mkdir(parents=True, exist_ok=True) + + log.info(f"MoFin Dashboard starting on port {PORT}") + log.info(f"Specs dir: {_SPECS_DIR}") + log.info(f"Templates dir: {_TEMPLATES_DIR}") + + # Optional: PID guard + try: + sys.path.insert(0, str(_SCRIPT_DIR)) + from proc_guard import guard + if not guard("mofin_dashboard"): + log.error("Another dashboard instance is already running") + sys.exit(1) + except ImportError: + log.warning("proc_guard not available, skipping PID lock") + + app.run(host="0.0.0.0", port=PORT, debug=False) diff --git a/data/portfolio.json b/data/portfolio.json new file mode 100644 index 00000000..4f9cbf14 --- /dev/null +++ b/data/portfolio.json @@ -0,0 +1,190 @@ +{ + "holdings": [ + { + "code": "518880", + "name": "黄金ETF华安", + "shares": 2400, + "cost": 12.1915, + "position_pct": null, + "added_at": "2026-07-17", + "is_active": 1, + "closed_at": null, + "close_pnl": null, + "price": 8.29, + "market_value": 19862.4, + "change_pct": -0.85, + "currency": "CNY" + }, + { + "code": "601899", + "name": "紫金矿业", + "shares": 2400, + "cost": 39.8885, + "position_pct": null, + "added_at": "2026-07-17", + "is_active": 1, + "closed_at": null, + "close_pnl": null, + "price": 28.38, + "market_value": 68184.0, + "change_pct": -2.97, + "currency": "CNY" + }, + { + "code": "688411", + "name": "海博思创", + "shares": 200, + "cost": 266.9461, + "position_pct": null, + "added_at": "2026-07-17", + "is_active": 1, + "closed_at": null, + "close_pnl": null, + "price": 184.26, + "market_value": 37048.0, + "change_pct": -4.97, + "currency": "CNY" + }, + { + "code": "688639", + "name": "华恒生物", + "shares": 2800, + "cost": 21.5085, + "position_pct": null, + "added_at": "2026-07-17", + "is_active": 1, + "closed_at": null, + "close_pnl": null, + "price": 16.47, + "market_value": 46480.0, + "change_pct": -4.19, + "currency": "CNY" + }, + { + "code": "688981", + "name": "中芯国际", + "shares": 300, + "cost": 126.0681, + "position_pct": null, + "added_at": "2026-07-17", + "is_active": 1, + "closed_at": null, + "close_pnl": null, + "price": 144.84, + "market_value": 44436.0, + "change_pct": -4.58, + "currency": "CNY" + }, + { + "code": "000850", + "name": "华茂股份", + "shares": 20400, + "cost": 3.9408, + "position_pct": null, + "added_at": "2026-07-17", + "is_active": 1, + "closed_at": null, + "close_pnl": null, + "price": 4.09, + "market_value": 83232.0, + "change_pct": 0.25, + "currency": "CNY" + }, + { + "code": "300035", + "name": "中科电气", + "shares": 1400, + "cost": 22.2914, + "position_pct": null, + "added_at": "2026-07-17", + "is_active": 1, + "closed_at": null, + "close_pnl": null, + "price": 12.31, + "market_value": 18116.0, + "change_pct": -1.44, + "currency": "CNY" + }, + { + "code": "00700", + "name": "腾讯控股", + "shares": 100, + "cost": 445.2906, + "position_pct": null, + "added_at": "2026-07-17", + "is_active": 1, + "closed_at": null, + "close_pnl": null, + "price": 461.0, + "market_value": 39837.28, + "change_pct": -4.75, + "currency": "CNY" + }, + { + "code": "01088", + "name": "中国神华", + "shares": 500, + "cost": 46.1178, + "position_pct": null, + "added_at": "2026-07-17", + "is_active": 1, + "closed_at": null, + "close_pnl": null, + "price": 41.62, + "market_value": 18114.13, + "change_pct": -0.62, + "currency": "CNY" + }, + { + "code": "01211", + "name": "比亚迪股份", + "shares": 600, + "cost": 105.3827, + "position_pct": null, + "added_at": "2026-07-17", + "is_active": 1, + "closed_at": null, + "close_pnl": null, + "price": 87.6, + "market_value": 45328.5, + "change_pct": -3.68, + "currency": "CNY" + }, + { + "code": "01478", + "name": "丘钛科技", + "shares": 11000, + "cost": 13.531, + "position_pct": null, + "added_at": "2026-07-17", + "is_active": 1, + "closed_at": null, + "close_pnl": null, + "price": 6.56, + "market_value": 62967.76, + "change_pct": -6.42, + "currency": "CNY" + }, + { + "code": "02202", + "name": "万科企业", + "shares": 19700, + "cost": 4.6906, + "position_pct": null, + "added_at": "2026-07-17", + "is_active": 1, + "closed_at": null, + "close_pnl": null, + "price": 2.36, + "market_value": 40141.19, + "change_pct": -5.98, + "currency": "CNY" + } + ], + "cash": 321271.0, + "frozen_cash": 0.0, + "total_market_value": 521073.53, + "total_assets": 842344.53, + "position_pct": 61.86, + "updated_at": "2026-07-17 14:08:07" +} \ No newline at end of file diff --git a/deploy/bot/xmpp_agent_core.py b/deploy/bot/xmpp_agent_core.py index 8709d782..0f4ae0f5 100644 --- a/deploy/bot/xmpp_agent_core.py +++ b/deploy/bot/xmpp_agent_core.py @@ -245,6 +245,7 @@ class XmppAgent(slixmpp.ClientXMPP): self._nick = nick self._muc_joined = False self._recent_sent = [] + self._reconnecting = False # 防递归重连 self.add_event_handler('session_start', self.on_start) self.add_event_handler('message', self.on_msg) self.add_event_handler('disconnected', self.on_disconnect) @@ -266,11 +267,16 @@ class XmppAgent(slixmpp.ClientXMPP): def on_disconnect(self, event): self._muc_joined = False log.info(f"{AGENT_NAME} XMPP 断开") - # 自动重连:slixmpp 1.15.0 没有 auto_reconnect 属性,需手动 + if self._reconnecting: + log.warning(f"{AGENT_NAME} 已在重连中,跳过递归重连") + return + self._reconnecting = True try: self.reconnect(wait=5.0, reason="断线自动重连") except Exception as e: log.warning(f"{AGENT_NAME} 重连失败: {e}") + finally: + self._reconnecting = False def on_msg(self, msg): if msg['type'] in ('chat', 'groupchat'): diff --git a/deploy/profile-scripts/market_scanner.py b/deploy/profile-scripts/market_scanner.py index 8f29454e..4d06b7ee 100644 --- a/deploy/profile-scripts/market_scanner.py +++ b/deploy/profile-scripts/market_scanner.py @@ -1,194 +1,205 @@ -#!/usr/bin/env python3 -"""market_scanner.py — 全市场异动扫描(替代小果扫描线) - -每15分钟扫描: - 1. 板块轮动检测(从已采集的 sector_snapshots 读) - 2. 热门板块领涨股扫描(从腾讯API批量拉) - 3. 资金流向异常检测(从已有 capital_flow_cache 读) - 4. 输出候选股到 candidates 表 - -不依赖小果LLM,纯数据驱动。 -""" -import sys, json, sqlite3, urllib.request, re, time -from pathlib import Path -from datetime import datetime - -DB_PATH = Path("/home/hmo/MoFin/data/mofin.db") -CANDIDATES_FILE = Path("/home/hmo/web-dashboard/data/candidate_pool.json") - -UA = "Mozilla/5.0" - -def get_conn(): - return sqlite3.connect(str(DB_PATH)) - -def fetch_qq_batch(symbols): - """腾讯批量行情""" - if not symbols: - return {} - url = f"http://qt.gtimg.cn/q={','.join(symbols)}" - try: - req = urllib.request.Request(url, headers={"User-Agent": UA}) - proxy = urllib.request.ProxyHandler({}) - opener = urllib.request.build_opener(proxy) - with opener.open(req, timeout=15) as r: - text = r.read().decode("gbk") - results = {} - for line in text.strip().split("\n"): - if "~" not in line: - continue - parts = line.split("~") - if len(parts) < 40: - continue - m = re.search(r'_(\w+)=', parts[0]) - market = m.group(1) if m else "" - code = parts[2] - name = parts[1] - price = float(parts[3]) if parts[3] else 0 - chg_pct = float(parts[32]) if parts[32] else 0 - high = float(parts[33]) if parts[33] else 0 - low = float(parts[34]) if parts[34] else 0 - volume = int(parts[6]) if parts[6] else 0 - amount = float(parts[37]) if len(parts) > 37 and parts[37] else 0 - if price > 0: - results[code] = {"code": code, "name": name, "price": price, - "change_pct": chg_pct, "high": high, "low": low, - "volume": volume, "amount": amount, - "market": "SH" if market == "sh" else "SZ" if market == "sz" else "HK"} - return results - except Exception as e: - print(f"[SCANNER] 腾讯API错误: {e}", file=sys.stderr) - return {} - -def scan_hot_sectors(): - """从DB读热门板块,返回板块名+领涨股""" - conn = get_conn() - latest = conn.execute("SELECT MAX(id) FROM market_snapshots").fetchone()[0] - if not latest: - conn.close() - return [] - sectors = conn.execute(""" - SELECT name, change_pct, lead_stock, lead_stock_code, up_count, down_count - FROM sector_snapshots WHERE snapshot_id=? - ORDER BY change_pct DESC LIMIT 15 - """, (latest,)).fetchall() - conn.close() - return [{"name": s[0], "change": s[1], "lead_stock": s[2], - "lead_code": s[3], "up": s[4], "down": s[5]} for s in sectors if s[1] > 2.0] - -def scan_candidates(): - """主扫描流程""" - print(f"[SCANNER] {datetime.now().strftime('%H:%M')} 开始扫描", flush=True) - - # 1. 热门板块领涨股 - hot = scan_hot_sectors() - print(f" 热门板块(涨幅>2%): {len(hot)}个", flush=True) - - candidates = {} - - # 从热门板块拉领涨股 - lead_codes = [] - for s in hot: - if s.get("lead_code"): - lc = str(s["lead_code"]).strip() - if lc and lc not in candidates: - lead_codes.append(lc) - candidates[lc] = {"source": f"板块:{s['name']}(+{s['change']:.1f}%)", "sector": s["name"]} - - print(f" 领涨股待查: {len(lead_codes)}只", flush=True) - - # 2. 腾讯API批量查行情 - symbols = [] - for c in lead_codes: - if len(c) == 6: - if c.startswith(("5", "6", "9")): - symbols.append(f"sh{c}") - else: - symbols.append(f"sz{c}") - else: - symbols.append(f"hk{c}") - - prices = fetch_qq_batch(symbols) - print(f" 行情返回: {len(prices)}只", flush=True) - - # 3. 评估候选 - new_candidates = [] - for code, info in prices.items(): - if code not in candidates: - continue - src = candidates[code] - price = info["price"] - chg = info["change_pct"] - name = info["name"] - vol = info["volume"] - amt = info["amount"] - - # 条件:涨幅>3%,有量 - if chg < 3.0: - continue - if vol <= 0: - continue - - score = min(10, round(3 + chg * 0.5 + (amt / 1e8 if amt > 0 else 0) * 0.1, 1)) - - entry_low = round(price * 0.95, 2) - entry_high = round(price, 2) - stop_loss = round(price * 0.92, 2) - take_profit = round(price * 1.15, 2) - - candidate = { - "code": code, - "name": name, - "price": price, - "change_pct": chg, - "score": score, - "entry_low": entry_low, - "entry_high": entry_high, - "stop_loss": stop_loss, - "take_profit": take_profit, - "source": src["source"], - "sector": src.get("sector", ""), - "reason": f"热门板块{src['sector']}领涨+{chg:.1f}%" - } - new_candidates.append(candidate) - print(f" ✅ {code} {name} 价{price} (+{chg:.1f}%) 评分{score}", flush=True) - - # 4. 写入candidates表 - if new_candidates: - conn = get_conn() - for c in new_candidates: - conn.execute( - "INSERT OR REPLACE INTO candidates (code, name, price, change_pct, score, " - "entry_low, entry_high, stop_loss, take_profit, source, sector, reason, created_at) " - "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,datetime('now','localtime'))", - (c["code"], c["name"], c["price"], c["change_pct"], c["score"], - c["entry_low"], c["entry_high"], c["stop_loss"], c["take_profit"], - c["source"], c["sector"], c["reason"]) - ) - conn.commit() - conn.close() - print(f" ✅ 写入{len(new_candidates)}只候选", flush=True) - else: - print(f" ⚪ 无新候选", flush=True) - - # 5. 推送到Dad(只推高分) - high_score = [c for c in new_candidates if c["score"] >= 6] - if high_score: - lines = ["🔍 市场扫描发现潜在机会:"] - for c in high_score[:3]: - lines.append( - f" {c['name']}({c['code']}) 价{c['price']:.2f}(+{c['change_pct']:.1f}%) " - f"评分{c['score']}/10 | {c['reason']}" - ) - msg = "\n".join(lines) - # 推XMPP - try: - payload = json.dumps({"to": "hmo@yoin.fun", "body": msg, "type": "chat"}).encode() - req = urllib.request.Request("http://127.0.0.1:5805/", data=payload, - headers={"Content-Type": "application/json"}) - urllib.request.urlopen(req, timeout=5) - print(f" 📨 已推送{len(high_score)}只高评分候选", flush=True) - except Exception as e: - print(f" ⚠️ 推送失败: {e}", file=sys.stderr) - -if __name__ == "__main__": - scan_candidates() +#!/usr/bin/env python3 +"""market_scanner.py — 全市场异动扫描(替代小果扫描线) + +每15分钟扫描: + 1. 板块轮动检测(从已采集的 sector_snapshots 读) + 2. 热门板块领涨股扫描(从腾讯API批量拉) + 3. 资金流向异常检测(从已有 capital_flow_cache 读) + 4. 输出候选股到 candidates 表 + +不依赖小果LLM,纯数据驱动。 +""" +import sys, json, sqlite3, urllib.request, re, time +from pathlib import Path +from datetime import datetime + +# XMPP 日志 hook +try: + sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) + from xmpp_logger import log_xmpp +except ImportError: + def log_xmpp(*a, **kw): pass + +DB_PATH = Path("/home/hmo/MoFin/data/mofin.db") +CANDIDATES_FILE = Path("/home/hmo/web-dashboard/data/candidate_pool.json") + +UA = "Mozilla/5.0" + +def get_conn(): + return sqlite3.connect(str(DB_PATH)) + +def fetch_qq_batch(symbols): + """腾讯批量行情""" + if not symbols: + return {} + url = f"http://qt.gtimg.cn/q={','.join(symbols)}" + try: + req = urllib.request.Request(url, headers={"User-Agent": UA}) + proxy = urllib.request.ProxyHandler({}) + opener = urllib.request.build_opener(proxy) + with opener.open(req, timeout=15) as r: + text = r.read().decode("gbk") + results = {} + for line in text.strip().split("\n"): + if "~" not in line: + continue + parts = line.split("~") + if len(parts) < 40: + continue + m = re.search(r'_(\w+)=', parts[0]) + market = m.group(1) if m else "" + code = parts[2] + name = parts[1] + price = float(parts[3]) if parts[3] else 0 + chg_pct = float(parts[32]) if parts[32] else 0 + high = float(parts[33]) if parts[33] else 0 + low = float(parts[34]) if parts[34] else 0 + volume = int(parts[6]) if parts[6] else 0 + amount = float(parts[37]) if len(parts) > 37 and parts[37] else 0 + if price > 0: + results[code] = {"code": code, "name": name, "price": price, + "change_pct": chg_pct, "high": high, "low": low, + "volume": volume, "amount": amount, + "market": "SH" if market == "sh" else "SZ" if market == "sz" else "HK"} + return results + except Exception as e: + print(f"[SCANNER] 腾讯API错误: {e}", file=sys.stderr) + return {} + +def scan_hot_sectors(): + """从DB读热门板块,返回板块名+领涨股""" + conn = get_conn() + latest = conn.execute("SELECT MAX(id) FROM market_snapshots").fetchone()[0] + if not latest: + conn.close() + return [] + sectors = conn.execute(""" + SELECT name, change_pct, lead_stock, lead_stock_code, up_count, down_count + FROM sector_snapshots WHERE snapshot_id=? + ORDER BY change_pct DESC LIMIT 15 + """, (latest,)).fetchall() + conn.close() + return [{"name": s[0], "change": s[1], "lead_stock": s[2], + "lead_code": s[3], "up": s[4], "down": s[5]} for s in sectors if s[1] > 2.0] + +def scan_candidates(): + """主扫描流程""" + print(f"[SCANNER] {datetime.now().strftime('%H:%M')} 开始扫描", flush=True) + + # 1. 热门板块领涨股 + hot = scan_hot_sectors() + print(f" 热门板块(涨幅>2%): {len(hot)}个", flush=True) + + candidates = {} + + # 从热门板块拉领涨股 + lead_codes = [] + for s in hot: + if s.get("lead_code"): + lc = str(s["lead_code"]).strip() + if lc and lc not in candidates: + lead_codes.append(lc) + candidates[lc] = {"source": f"板块:{s['name']}(+{s['change']:.1f}%)", "sector": s["name"]} + + print(f" 领涨股待查: {len(lead_codes)}只", flush=True) + + # 2. 腾讯API批量查行情 + symbols = [] + for c in lead_codes: + if len(c) == 6: + if c.startswith(("5", "6", "9")): + symbols.append(f"sh{c}") + else: + symbols.append(f"sz{c}") + else: + symbols.append(f"hk{c}") + + prices = fetch_qq_batch(symbols) + print(f" 行情返回: {len(prices)}只", flush=True) + + # 3. 评估候选 + new_candidates = [] + for code, info in prices.items(): + if code not in candidates: + continue + src = candidates[code] + price = info["price"] + chg = info["change_pct"] + name = info["name"] + vol = info["volume"] + amt = info["amount"] + + # 条件:涨幅>3%,有量 + if chg < 3.0: + continue + if vol <= 0: + continue + + score = min(10, round(3 + chg * 0.5 + (amt / 1e8 if amt > 0 else 0) * 0.1, 1)) + + entry_low = round(price * 0.95, 2) + entry_high = round(price, 2) + stop_loss = round(price * 0.92, 2) + take_profit = round(price * 1.15, 2) + + candidate = { + "code": code, + "name": name, + "price": price, + "change_pct": chg, + "score": score, + "entry_low": entry_low, + "entry_high": entry_high, + "stop_loss": stop_loss, + "take_profit": take_profit, + "source": src["source"], + "sector": src.get("sector", ""), + "reason": f"热门板块{src['sector']}领涨+{chg:.1f}%" + } + new_candidates.append(candidate) + print(f" ✅ {code} {name} 价{price} (+{chg:.1f}%) 评分{score}", flush=True) + + # 4. 写入candidates表 + if new_candidates: + conn = get_conn() + for c in new_candidates: + conn.execute( + "INSERT OR REPLACE INTO candidates (code, name, price, change_pct, score, " + "entry_low, entry_high, stop_loss, take_profit, source, sector, reason, created_at) " + "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,datetime('now','localtime'))", + (c["code"], c["name"], c["price"], c["change_pct"], c["score"], + c["entry_low"], c["entry_high"], c["stop_loss"], c["take_profit"], + c["source"], c["sector"], c["reason"]) + ) + conn.commit() + conn.close() + print(f" ✅ 写入{len(new_candidates)}只候选", flush=True) + else: + print(f" ⚪ 无新候选", flush=True) + + # 5. 推送到Dad(只推高分) + high_score = [c for c in new_candidates if c["score"] >= 6] + if high_score: + lines = ["🔍 市场扫描发现潜在机会:"] + for c in high_score[:3]: + lines.append( + f" {c['name']}({c['code']}) 价{c['price']:.2f}(+{c['change_pct']:.1f}%) " + f"评分{c['score']}/10 | {c['reason']}" + ) + msg = "\n".join(lines) + # 推XMPP(通过知微 Bot HTTP 桥 :5805) + import time as _time + t0 = _time.time() + try: + payload = json.dumps({"to": "hmo@yoin.fun", "body": msg, "type": "chat"}).encode() + req = urllib.request.Request("http://127.0.0.1:5805/", data=payload, + headers={"Content-Type": "application/json"}) + urllib.request.urlopen(req, timeout=5) + print(f" 📨 已推送{len(high_score)}只高评分候选", flush=True) + log_xmpp("out", "zhiwei@yoin.fun", "hmo@yoin.fun", msg, "ok", None, int((_time.time()-t0)*1000)) + except Exception as e: + print(f" ⚠️ 推送失败: {e}", file=sys.stderr) + log_xmpp("out", "zhiwei@yoin.fun", "hmo@yoin.fun", msg, "error", str(e)[:200], int((_time.time()-t0)*1000)) + +if __name__ == "__main__": + scan_candidates() diff --git a/dev-spec.md b/dev-spec.md new file mode 100644 index 00000000..31b33aaa --- /dev/null +++ b/dev-spec.md @@ -0,0 +1,217 @@ +# MoFin 开发规范 + +> 版本: v1.0 | 更新: 2026-07-19 | 基于 AgentsMeeting 样板重构 +> +> 📋 样板参考: [AgentsMeeting TEMPLATE-GUIDE.md](../AgentsMeeting/docs/TEMPLATE-GUIDE.md) + +--- + +## 五条红线 + +1. **先读/写 Spec,再写代码** — 新增功能先写 spec 再实现;修改已有功能先读对应 spec 了解架构和约束再动手。没有 spec 的模块在 Dashboard 不可见,视为未完成 +2. **部署必验** — 部署后不打开 Dashboard F Tab 验证 = 部署未完成 +3. **不可见即不存在** — 组件不在 Dashboard 中显示 = 等于没部署。离线不告警 = 监控缺陷 +4. **实现后同步 Spec** — 每轮开发完毕后,必须将 `specs/{module}.json` 更新为与实际实现一致的状态。文档过期 = 等于没写 +5. **部署目标即验收标准** — 所有代码必须以部署目标环境(Linux 246)为基准编写和测试。禁止使用 Windows 专属 API(`tasklist`、`netstat`、`schtasks`、`wmic`)在 246 部署的代码中 + +--- + +## 一、双轨同源规范体系 + +每新增/修改一个独立功能模块,必须先写 `specs/{module}.json`。 +一个来源同时产出两套文档: + +``` +specs/{module}.json +├── human_help → ? 按钮(人类看说明/排错) +└── ai_spec → § 按钮(AI 看接口/约束/依赖) +``` + +### 什么算一个模块 + +满足以下任一条件即视为独立模块,必须写 spec: + +- 暴露独立的 HTTP API 端点 +- 在 Dashboard 上有独立 UI 面板(`?` + `§` 按钮) +- 有独立的配置文件 / 数据文件 +- 可独立部署(如定时任务、数据采集脚本) + +### Spec 字段标准 + +```json +{ + "module": "模块名(与 Dashboard 引用名一致)", + "version": "1.0", + "purpose": "一句话说明这个模块干什么", + + "human_help": { + "title": "面向人类的标题", + "description": ["说明段落数组"], + "usage": ["使用步骤数组"], + "troubleshooting": ["常见问题数组"] + }, + + "ai_spec": { + "apis": [ + {"method": "GET", "path": "/api/xxx", "returns": "返回值说明"} + ], + "dependencies": ["依赖的服务或文件"], + "constraints": ["AI 必须遵守的约束"], + "must_not": ["AI 绝对不能做的事"], + "tests": [{"id": "T1", "name": "测试用例名"}], + "related_files": ["实现文件路径"] + } +} +``` + +### 当前模块清单 + +| 模块 | spec 路径 | 说明 | 状态 | +|------|----------|------|------| +| portfolio | `specs/portfolio.json` | 持仓数据 + 总览 | ✅ | +| watchlist | `specs/watchlist.json` | 自选股管理 | ✅ | +| decisions | `specs/decisions.json` | 策略决策库 | ✅ | +| market | `specs/market.json` | 市场观察数据 | ✅ | +| signals | `specs/signals.json` | 信号 + 全市场扫描 | ✅ | +| scanner | `specs/scanner.json` | 全市场选股机制 | ✅ | +| evaluation | `specs/evaluation.json` | 策略评估 | ✅ | +| prompts | `specs/prompts.json` | 提示词版本管理 | ✅ | +| reports | `specs/reports.json` | 分析报告管理 | ✅ | +| dashboard | `specs/dashboard.json` | Dashboard 自身 | ✅ | +| health | `specs/health.json` | 健康监控管线 | ✅ | +| xmpp_monitor | `specs/xmpp_monitor.json` | XMPP 通信可观测性 | ✅ | +| price_monitor | `specs/price_monitor.json` | 价格监控 cron | 📋 | +| strategy_lifecycle | `specs/strategy_lifecycle.json` | 策略生命周期 | 📋 | + +> 状态: ✅ = spec 已完成 | 📋 = 待编写 + +--- + +## 二、验证闭环 + +``` +┌────────────┐ ┌──────────┐ ┌──────────┐ +│ G: 规范体系 │────→│ K: 测试 │────→│ F: 健康 │ +│ 定义期望 │ │ 验证实现 │ │ 持续监控 │ +└────────────┘ └──────────┘ └──────────┘ + ↑ ↑ │ + └────────────────┼────────────────┘ + │ + ┌────────┴────────┐ + │ F 异常 → 触发 K │ + │ K 失败 → 更新 G │ + └─────────────────┘ +``` + +### 核心反馈链路 + +| 方向 | 触发条件 | 动作 | +|------|---------|------| +| G → K | 新增/修改 spec | 对应测试 ID 必须新增/更新 | +| K → F | 测试全部通过 | F Tab 组件标记为已验证 | +| **F → K** | **F Tab 发现异常** | 触发对应测试重跑,确认是服务故障还是测试过期 | +| **F → G** | **F Tab 持续异常但测试通过** | 期望矩阵或 spec 过时,应更新 G 和对应 spec | + +### F — 系统健康度(Dashboard F Tab) + +- **期望矩阵**:应该运行的服务 vs 实际状态 +- **监控数据**:Tier1(5min)/ Tier2(日报)作为实时状态输入 +- **服务拓扑**:所有服务的健康、端口状态 +- **?§ 覆盖**:F Tab 中的每条服务必须有对应的 ?(human_help)和 §(ai_spec)按钮 + +--- + +## 三、开发流程 + +### 新增功能流程 + +``` +确定模块边界 + │ + ├─ 1. 创建 specs/{module}.json + │ human_help + ai_spec + │ + ├─ 2. 实现功能代码 + │ 包含 /health 端点 + PID 锁(proc_guard) + │ + ├─ 3. 注册到系统 + │ - 端口注册 + │ - 添加到期望矩阵(F Tab 自动检测) + │ + ├─ 4. 编写测试 + │ - ai_spec.tests 添加对应测试标识 + │ + ├─ 5. 同步更新 Spec + │ - 将 specs/{module}.json 更新为与实际实现一致 + │ + └─ 6. 提交 → 部署 → 验证 +``` + +### 修改已有功能流程 + +``` +识别要修改的模块(查看模块清单确定 module 名) + │ + ├─ 1. 读 specs/{module}.json + │ 重点读 ai_spec:apis / constraints / dependencies / must_not + │ + ├─ 2. 确认理解 + │ - 如果 spec 描述与代码实际行为不一致,优先怀疑 spec 过期 + │ + ├─ 3. 修改功能代码 + │ 只改动需求直接涉及的部分,不顺手优化无关代码 + │ + ├─ 4. 同步更新 Spec + │ + └─ 5. 提交 → 部署 → 验证 +``` + +### Git 操作规范 + +| # | 规则 | 说明 | +|---|------|------| +| 1 | 开工前必 pull | `git pull --rebase` | +| 2 | 改完即 commit | 一个逻辑单元一次提交。禁止含密钥 | +| 3 | 推前必拉 + 配代理 | push 前 `git pull --rebase`。远程操作前配 `:15000` 代理 | +| 4 | trunk-based | 日常在 main。仅长周期大改开分支 | + +### 已有编码规范 + +MoFin 已有的编码规范见 `docs/DEVELOPMENT_STANDARDS.md`,包含: +- 代码结构(mo_models → mo_data → mofin_db 三层) +- 数据规范(币种、汇率、数据源) +- DB 规范(表设计、迁移) +- LLM Prompt 规范 +- Cron 规范(独立运行、幂等性) +- 测试要求(`run_all_tests.py`) + +以上规范与本文件互补,不冲突。本文件侧重"先 spec 后代码"和"通过 Dashboard 保证可见性"。 + +--- + +## 四、部署环境 + +| 项目 | 值 | +|------|-----| +| **生产环境** | Linux 192.168.1.246 | +| **代码目录** | `/home/hmo/MoFin/` | +| **数据库** | `/home/hmo/web-dashboard/data/mofin.db`(SQLite) | +| **Flask API** | `server.py` → `:8899`(含 Dashboard) | +| **Python** | 系统 Python 3 | + +--- + +## 五、文档索引 + +| 文档 | 用途 | +|------|------| +| `docs/dev-spec.md` | 本文件 — 开发规范(含五条红线) | +| `docs/DEVELOPMENT_STANDARDS.md` | 编码规范(已有) | +| `SYSTEM_ARCHITECTURE.md` | 系统架构(已有) | +| `docs/cron-catalog.md` | Cron 任务清单(已有) | +| `docs/DEPLOY.md` | 部署指南 | +| `docs/QUICKSTART.md` | 快速操作 | +| `docs/DASHBOARD.md` | Dashboard API 参考 | +| `docs/HEALTH-PIPELINE.md` | 健康管线文档 | +| `docs/learned.md` | 经验教训记录 | +| `docs/decisions/` | 架构决策日志 | diff --git a/gateway/logs/dashboard.log b/gateway/logs/dashboard.log new file mode 100644 index 00000000..363042bc --- /dev/null +++ b/gateway/logs/dashboard.log @@ -0,0 +1,52 @@ +2026-07-19 10:53:45,896 [dashboard] MoFin Dashboard starting on port 5805 +2026-07-19 10:53:45,896 [dashboard] Specs dir: /home/hmo/MoFin/specs +2026-07-19 10:53:45,896 [dashboard] Templates dir: /home/hmo/MoFin/templates +2026-07-19 10:53:45,896 [dashboard] proc_guard not available, skipping PID lock +2026-07-19 10:53:56,269 [dashboard] MoFin Dashboard starting on port 5805 +2026-07-19 10:53:56,269 [dashboard] Specs dir: /home/hmo/MoFin/specs +2026-07-19 10:53:56,269 [dashboard] Templates dir: /home/hmo/MoFin/templates +2026-07-19 10:53:56,269 [dashboard] proc_guard not available, skipping PID lock +2026-07-19 10:54:06,498 [dashboard] MoFin Dashboard starting on port 5805 +2026-07-19 10:54:06,498 [dashboard] Specs dir: /home/hmo/MoFin/specs +2026-07-19 10:54:06,498 [dashboard] Templates dir: /home/hmo/MoFin/templates +2026-07-19 10:54:06,498 [dashboard] proc_guard not available, skipping PID lock +2026-07-19 10:54:16,745 [dashboard] MoFin Dashboard starting on port 5805 +2026-07-19 10:54:16,745 [dashboard] Specs dir: /home/hmo/MoFin/specs +2026-07-19 10:54:16,745 [dashboard] Templates dir: /home/hmo/MoFin/templates +2026-07-19 10:54:16,745 [dashboard] proc_guard not available, skipping PID lock +2026-07-19 10:54:26,989 [dashboard] MoFin Dashboard starting on port 5805 +2026-07-19 10:54:26,989 [dashboard] Specs dir: /home/hmo/MoFin/specs +2026-07-19 10:54:26,989 [dashboard] Templates dir: /home/hmo/MoFin/templates +2026-07-19 10:54:26,989 [dashboard] proc_guard not available, skipping PID lock +2026-07-19 10:54:37,246 [dashboard] MoFin Dashboard starting on port 5805 +2026-07-19 10:54:37,246 [dashboard] Specs dir: /home/hmo/MoFin/specs +2026-07-19 10:54:37,246 [dashboard] Templates dir: /home/hmo/MoFin/templates +2026-07-19 10:54:37,246 [dashboard] proc_guard not available, skipping PID lock +2026-07-19 10:54:47,458 [dashboard] MoFin Dashboard starting on port 5807 +2026-07-19 10:54:47,458 [dashboard] Specs dir: /home/hmo/MoFin/specs +2026-07-19 10:54:47,458 [dashboard] Templates dir: /home/hmo/MoFin/templates +2026-07-19 10:54:47,459 [dashboard] proc_guard not available, skipping PID lock +2026-07-19 10:54:47,464 [dashboard] WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5807 + * Running on http://192.168.1.246:5807 +2026-07-19 10:54:47,464 [dashboard] Press CTRL+C to quit +2026-07-19 10:54:55,309 [dashboard] MoFin Dashboard starting on port 5807 +2026-07-19 10:54:55,309 [dashboard] Specs dir: /home/hmo/MoFin/specs +2026-07-19 10:54:55,309 [dashboard] Templates dir: /home/hmo/MoFin/templates +2026-07-19 10:54:55,309 [dashboard] proc_guard not available, skipping PID lock +2026-07-19 10:54:55,314 [dashboard] WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5807 + * Running on http://192.168.1.246:5807 +2026-07-19 10:54:55,314 [dashboard] Press CTRL+C to quit +2026-07-19 10:55:13,917 [dashboard] 127.0.0.1 - - [19/Jul/2026 10:55:13] "GET /api/health HTTP/1.1" 200 - +2026-07-19 10:55:13,981 [dashboard] 127.0.0.1 - - [19/Jul/2026 10:55:13] "GET /api/health HTTP/1.1" 200 - +2026-07-19 10:55:13,987 [dashboard] 127.0.0.1 - - [19/Jul/2026 10:55:13] "GET /api/services HTTP/1.1" 200 - +2026-07-19 10:55:31,857 [dashboard] 127.0.0.1 - - [19/Jul/2026 10:55:31] "GET /api/health HTTP/1.1" 200 - +2026-07-19 10:55:40,596 [dashboard] 127.0.0.1 - - [19/Jul/2026 10:55:40] "GET /api/health HTTP/1.1" 200 - +2026-07-19 10:55:40,602 [dashboard] 127.0.0.1 - - [19/Jul/2026 10:55:40] "GET /api/monitor HTTP/1.1" 200 - +2026-07-19 10:55:40,639 [dashboard] 127.0.0.1 - - [19/Jul/2026 10:55:40] "GET /api/module-spec/health HTTP/1.1" 200 - +2026-07-19 10:56:17,871 [dashboard] 127.0.0.1 - - [19/Jul/2026 10:56:17] "GET / HTTP/1.1" 200 - +2026-07-19 10:56:17,898 [dashboard] 127.0.0.1 - - [19/Jul/2026 10:56:17] "GET / HTTP/1.1" 200 - +2026-07-19 11:00:01,611 [dashboard] 127.0.0.1 - - [19/Jul/2026 11:00:01] "GET /api/health HTTP/1.1" 200 - diff --git a/gateway/logs/health_check.log b/gateway/logs/health_check.log new file mode 100644 index 00000000..5de28a2e --- /dev/null +++ b/gateway/logs/health_check.log @@ -0,0 +1,90 @@ +[2026-07-19 14:30:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 14:45:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 14:50:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 15:10:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 15:16:56] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 15:20:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 15:35:01] ISSUES: 1 failed + - 知微 Gateway: timed out +[2026-07-19 15:40:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 15:45:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 15:50:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 16:00:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 16:05:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 16:20:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 16:30:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 16:35:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 16:40:02] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 16:55:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 17:00:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 17:05:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 17:10:02] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 17:15:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 17:20:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 17:25:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 17:45:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 17:55:02] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 18:00:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 18:05:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 18:15:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 18:15:32] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 18:20:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 18:22:09] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 18:25:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 18:30:02] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 18:35:02] ISSUES: 1 failed + - 知微 Gateway: timed out +[2026-07-19 18:40:01] ISSUES: 1 failed + - 知微 Gateway: timed out +[2026-07-19 18:45:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 18:50:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 18:55:02] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 19:00:01] ISSUES: 1 failed + - 知微 Gateway: timed out +[2026-07-19 19:05:02] ISSUES: 1 failed + - 知微 Gateway: timed out +[2026-07-19 19:10:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 19:30:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-19 19:50:01] AUTO-HEAL actions: + {"action": "check_keys", "best_key": "key6", "current_provider": "ocg-key6", "best_provider": "ocg-key6", "rolling_pct": 38, "weekly_pct": 28, "issues": []} + {"action": "restart_hermes_gateway", "target": "position-analyst", "success": true, "detail": "restart triggered (async)"} +[2026-07-19 20:40:01] Gateway DOWN → systemctl restart triggered (async, cooldown set) +[2026-07-19 20:40:01] ISSUES: 1 failed + - 知微 Gateway: timed out diff --git a/gateway/logs/health_check_cron.log b/gateway/logs/health_check_cron.log new file mode 100644 index 00000000..2953d755 --- /dev/null +++ b/gateway/logs/health_check_cron.log @@ -0,0 +1,102 @@ +[14:30] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[14:45] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[14:50] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[15:10] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +Traceback (most recent call last): + File "/home/hmo/MoFin/agents_health_check.py", line 143, in + run() + File "/home/hmo/MoFin/agents_health_check.py", line 110, in run + _collect_xmpp_health(now) + ^^^^^^^^^^^^^^^^^^^^ +NameError: name '_collect_xmpp_health' is not defined +[15:20] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[15:35] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — timed out +[15:40] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[15:45] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[15:50] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[16:00] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[16:05] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[16:20] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[16:30] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[16:35] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[16:40] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[16:55] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[17:00] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[17:05] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[17:10] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[17:15] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[17:20] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[17:25] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[17:45] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[17:55] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[18:00] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[18:05] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[18:15] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[18:20] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[18:25] Auto-heal: Gateway DOWN → restarted +[18:25] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[18:30] Auto-heal: Gateway DOWN → restarted +[18:30] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[18:35] Auto-heal: Gateway DOWN → restarted +[18:35] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — timed out +[18:40] Auto-heal: Gateway DOWN → restarted +[18:40] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — timed out +[18:45] Auto-heal: Gateway DOWN → restarted +[18:45] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[18:50] Auto-heal: Gateway DOWN → restarted +[18:50] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[18:55] Auto-heal: Gateway DOWN → restarted +[18:55] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[19:00] Auto-heal: Gateway DOWN → restarted +[19:00] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — timed out +[19:05] Auto-heal: Gateway DOWN → restarted +[19:05] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — timed out +[19:10] Auto-heal: Gateway DOWN → restarted +[19:10] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[19:30] Auto-heal: Gateway DOWN → restarted +[19:30] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[19:50] Auto-heal: 2 action(s) — degraded + → check_keys: success=? + → restart_hermes_gateway: success=True +[20:40] Gateway DOWN → systemctl restart triggered +[20:40] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — timed out diff --git a/gateway/logs/last_restart.txt b/gateway/logs/last_restart.txt new file mode 100644 index 00000000..9ac47169 --- /dev/null +++ b/gateway/logs/last_restart.txt @@ -0,0 +1 @@ +1784464840.6959908 \ No newline at end of file diff --git a/gateway/logs/xmpp_health_log.jsonl b/gateway/logs/xmpp_health_log.jsonl new file mode 100644 index 00000000..0212c7f9 --- /dev/null +++ b/gateway/logs/xmpp_health_log.jsonl @@ -0,0 +1,71 @@ +{"timestamp": "2026-07-19 15:16:56", "status": "no_data", "last_message_age_sec": -1, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 10509, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 60188, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2489884, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 15:20:01", "status": "no_data", "last_message_age_sec": -1, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 10509, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 60188, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2489884, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 15:25:01", "status": "degraded", "last_message_age_sec": -1, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "error", "error": "timed out"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 10200, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 59879, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2489575, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 15:30:02", "status": "degraded", "last_message_age_sec": -1, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "error", "error": "timed out"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 9887, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 59566, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2489262, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 15:35:01", "status": "degraded", "last_message_age_sec": -1, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "error", "error": "timed out"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 9576, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 59255, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2488951, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 15:40:01", "status": "no_data", "last_message_age_sec": -1, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 9262, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 58941, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2488637, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 15:45:01", "status": "no_data", "last_message_age_sec": -1, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 8952, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 58631, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2488327, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 15:50:01", "status": "no_data", "last_message_age_sec": -1, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 8643, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 58322, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2488018, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 15:55:01", "status": "degraded", "last_message_age_sec": -1, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "error", "error": "timed out"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 8333, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 58012, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2487708, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 16:00:01", "status": "no_data", "last_message_age_sec": -1, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 8022, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 57701, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2487397, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 16:05:01", "status": "no_data", "last_message_age_sec": -1, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 7710, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 57389, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2487085, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 16:10:01", "status": "degraded", "last_message_age_sec": -1, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "error", "error": "timed out"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 7397, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 57076, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2486772, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 16:15:01", "status": "degraded", "last_message_age_sec": -1, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "error", "error": "timed out"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 7085, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 56764, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2486460, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 16:20:01", "status": "no_data", "last_message_age_sec": -1, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 6768, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 56447, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2486143, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 16:25:01", "status": "degraded", "last_message_age_sec": -1, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "error", "error": "timed out"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 6452, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 56131, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2485827, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 16:30:01", "status": "no_data", "last_message_age_sec": -1, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 6142, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 55821, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2485517, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 16:35:01", "status": "ok", "last_message_age_sec": 59, "error_rate_1h": 0.0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 5832, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 55511, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2485207, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 16:40:02", "status": "ok", "last_message_age_sec": 360, "error_rate_1h": 0.0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 5832, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 55511, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2485207, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 16:45:01", "status": "degraded", "last_message_age_sec": 658, "error_rate_1h": 0.0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "error", "error": "timed out"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 5516, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 55195, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2484891, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 16:50:01", "status": "degraded", "last_message_age_sec": 959, "error_rate_1h": 0.0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "error", "error": "timed out"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 5206, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 54885, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2484581, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 16:55:01", "status": "critical", "last_message_age_sec": 1259, "error_rate_1h": 0.0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 4897, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 54576, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2484272, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 17:00:01", "status": "critical", "last_message_age_sec": 1559, "error_rate_1h": 0.0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 4589, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 54268, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2483964, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 17:05:01", "status": "critical", "last_message_age_sec": 1859, "error_rate_1h": 0.0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 4280, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 53959, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2483655, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 17:10:02", "status": "critical", "last_message_age_sec": 2159, "error_rate_1h": 0.0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 3970, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 53649, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2483345, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 17:15:01", "status": "critical", "last_message_age_sec": 2459, "error_rate_1h": 0.0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 3659, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 53338, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2483034, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 17:20:01", "status": "critical", "last_message_age_sec": 2760, "error_rate_1h": 0.0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 3350, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 53029, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2482725, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 17:25:01", "status": "degraded", "last_message_age_sec": 3059, "error_rate_1h": 0.0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "error", "error": "timed out"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 3040, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 52719, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2482415, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 17:30:01", "status": "critical", "last_message_age_sec": 3360, "error_rate_1h": 0.0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 2731, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 52410, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2482106, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 17:35:01", "status": "degraded", "last_message_age_sec": 3659, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "error", "error": "timed out"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 2422, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 52101, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2481797, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 17:40:01", "status": "degraded", "last_message_age_sec": 3959, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "error", "error": "timed out"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 2104, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 51783, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2481479, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 17:45:01", "status": "critical", "last_message_age_sec": 4259, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 1795, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 51474, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2481170, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 17:50:01", "status": "degraded", "last_message_age_sec": 4561, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "error", "error": "timed out"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 1484, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 51163, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2480859, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 17:55:02", "status": "critical", "last_message_age_sec": 4860, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 1175, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 50854, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2480550, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 18:00:01", "status": "critical", "last_message_age_sec": 5159, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 866, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 50545, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2480241, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 18:05:01", "status": "critical", "last_message_age_sec": 5460, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 558, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 50237, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2479933, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 18:10:01", "status": "degraded", "last_message_age_sec": 5759, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "error", "error": "timed out"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 251, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 49930, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2479626, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 18:15:01", "status": "degraded", "last_message_age_sec": 6058, "error_rate_1h": 0, "bot_activity": {"inbound": 1, "outbound": 0, "errors": 1, "last_error": "2026-07-19 18:12:21,700 ERROR call_hermes error: RemoteDisconnected: Remote end closed connection without response", "last_inbound": "2026-07-19 18:12:03,842 INFO 📩 收到: from=hmo@yoin.fun/gajim.02ZMOMIM type=chat body=> 其实我的现金和持仓都发生了变化。但是之前给你发截图,你好像并没有回应", "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 18000, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 49621, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2479317, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 18:15:32", "status": "degraded", "last_message_age_sec": 6089, "error_rate_1h": 0, "bot_activity": {"inbound": 1, "outbound": 0, "errors": 1, "last_error": "2026-07-19 18:12:21,700 ERROR call_hermes error: RemoteDisconnected: Remote end closed connection without response", "last_inbound": "2026-07-19 18:12:03,842 INFO 📩 收到: from=hmo@yoin.fun/gajim.02ZMOMIM type=chat body=> 其实我的现金和持仓都发生了变化。但是之前给你发截图,你好像并没有回应", "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 18000, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 49621, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2479317, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 18:20:01", "status": "degraded", "last_message_age_sec": 6359, "error_rate_1h": 0, "bot_activity": {"inbound": 1, "outbound": 0, "errors": 1, "last_error": "2026-07-19 18:12:21,700 ERROR call_hermes error: RemoteDisconnected: Remote end closed connection without response", "last_inbound": "2026-07-19 18:12:03,842 INFO 📩 收到: from=hmo@yoin.fun/gajim.02ZMOMIM type=chat body=> 其实我的现金和持仓都发生了变化。但是之前给你发截图,你好像并没有回应", "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 17827, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 49312, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2479008, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 18:22:09", "status": "degraded", "last_message_age_sec": 6487, "error_rate_1h": 0, "bot_activity": {"inbound": 1, "outbound": 0, "errors": 1, "last_error": "2026-07-19 18:12:21,700 ERROR call_hermes error: RemoteDisconnected: Remote end closed connection without response", "last_inbound": "2026-07-19 18:12:03,842 INFO 📩 收到: from=hmo@yoin.fun/gajim.02ZMOMIM type=chat body=> 其实我的现金和持仓都发生了变化。但是之前给你发截图,你好像并没有回应", "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 17827, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 49312, "status": "ok", "usage_percent": 13}, "monthly": {"reset_in_sec": 2479008, "status": "ok", "usage_percent": 6}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 18:25:01", "status": "degraded", "last_message_age_sec": 6659, "error_rate_1h": 0, "bot_activity": {"inbound": 1, "outbound": 0, "errors": 1, "last_error": "2026-07-19 18:12:21,700 ERROR call_hermes error: RemoteDisconnected: Remote end closed connection without response", "last_inbound": "2026-07-19 18:12:03,842 INFO 📩 收到: from=hmo@yoin.fun/gajim.02ZMOMIM type=chat body=> 其实我的现金和持仓都发生了变化。但是之前给你发截图,你好像并没有回应", "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 17520, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 49005, "status": "ok", "usage_percent": 14}, "monthly": {"reset_in_sec": 2478701, "status": "ok", "usage_percent": 7}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 18:30:02", "status": "degraded", "last_message_age_sec": 6959, "error_rate_1h": 0, "bot_activity": {"inbound": 1, "outbound": 0, "errors": 1, "last_error": "2026-07-19 18:12:21,700 ERROR call_hermes error: RemoteDisconnected: Remote end closed connection without response", "last_inbound": "2026-07-19 18:12:03,842 INFO 📩 收到: from=hmo@yoin.fun/gajim.02ZMOMIM type=chat body=> 其实我的现金和持仓都发生了变化。但是之前给你发截图,你好像并没有回应", "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 17212, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 48697, "status": "ok", "usage_percent": 14}, "monthly": {"reset_in_sec": 2478393, "status": "ok", "usage_percent": 7}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 18:35:02", "status": "degraded", "last_message_age_sec": 7263, "error_rate_1h": 0, "bot_activity": {"inbound": 1, "outbound": 0, "errors": 1, "last_error": "2026-07-19 18:12:21,700 ERROR call_hermes error: RemoteDisconnected: Remote end closed connection without response", "last_inbound": "2026-07-19 18:12:03,842 INFO 📩 收到: from=hmo@yoin.fun/gajim.02ZMOMIM type=chat body=> 其实我的现金和持仓都发生了变化。但是之前给你发截图,你好像并没有回应", "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "error", "error": "timed out"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 16902, "status": "ok", "usage_percent": 3}, "weekly": {"reset_in_sec": 48387, "status": "ok", "usage_percent": 14}, "monthly": {"reset_in_sec": 2478083, "status": "ok", "usage_percent": 7}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 18:40:01", "status": "degraded", "last_message_age_sec": 7562, "error_rate_1h": 0, "bot_activity": {"inbound": 1, "outbound": 0, "errors": 1, "last_error": "2026-07-19 18:12:21,700 ERROR call_hermes error: RemoteDisconnected: Remote end closed connection without response", "last_inbound": "2026-07-19 18:12:03,842 INFO 📩 收到: from=hmo@yoin.fun/gajim.02ZMOMIM type=chat body=> 其实我的现金和持仓都发生了变化。但是之前给你发截图,你好像并没有回应", "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 16594, "status": "ok", "usage_percent": 3}, "weekly": {"reset_in_sec": 48079, "status": "ok", "usage_percent": 14}, "monthly": {"reset_in_sec": 2477775, "status": "ok", "usage_percent": 7}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 18:45:01", "status": "critical", "last_message_age_sec": 7859, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 16286, "status": "ok", "usage_percent": 3}, "weekly": {"reset_in_sec": 47771, "status": "ok", "usage_percent": 14}, "monthly": {"reset_in_sec": 2477467, "status": "ok", "usage_percent": 7}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 18:50:01", "status": "degraded", "last_message_age_sec": 8159, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "error", "error": "timed out"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 15977, "status": "ok", "usage_percent": 4}, "weekly": {"reset_in_sec": 47462, "status": "ok", "usage_percent": 15}, "monthly": {"reset_in_sec": 2477158, "status": "ok", "usage_percent": 7}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 18:55:02", "status": "critical", "last_message_age_sec": 8460, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 15665, "status": "ok", "usage_percent": 5}, "weekly": {"reset_in_sec": 47150, "status": "ok", "usage_percent": 15}, "monthly": {"reset_in_sec": 2476846, "status": "ok", "usage_percent": 7}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 19:00:01", "status": "degraded", "last_message_age_sec": 8762, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": false}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "error", "error": "[Errno 104] Connection reset by peer"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 15357, "status": "ok", "usage_percent": 6}, "weekly": {"reset_in_sec": 46842, "status": "ok", "usage_percent": 16}, "monthly": {"reset_in_sec": 2476538, "status": "ok", "usage_percent": 8}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 19:05:02", "status": "degraded", "last_message_age_sec": 9062, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "error", "error": "Remote end closed connection without response"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 15040, "status": "ok", "usage_percent": 7}, "weekly": {"reset_in_sec": 46525, "status": "ok", "usage_percent": 16}, "monthly": {"reset_in_sec": 2476221, "status": "ok", "usage_percent": 8}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 19:10:01", "status": "critical", "last_message_age_sec": 9359, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 14730, "status": "ok", "usage_percent": 8}, "weekly": {"reset_in_sec": 46215, "status": "ok", "usage_percent": 17}, "monthly": {"reset_in_sec": 2475911, "status": "ok", "usage_percent": 8}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 19:15:01", "status": "degraded", "last_message_age_sec": 9659, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "error", "error": "timed out"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 14419, "status": "ok", "usage_percent": 10}, "weekly": {"reset_in_sec": 45904, "status": "ok", "usage_percent": 17}, "monthly": {"reset_in_sec": 2475600, "status": "ok", "usage_percent": 8}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 19:20:02", "status": "degraded", "last_message_age_sec": 9960, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "error", "error": "timed out"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 14103, "status": "ok", "usage_percent": 10}, "weekly": {"reset_in_sec": 45588, "status": "ok", "usage_percent": 17}, "monthly": {"reset_in_sec": 2475284, "status": "ok", "usage_percent": 8}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 19:25:01", "status": "degraded", "last_message_age_sec": 10259, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "error", "error": "timed out"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 14103, "status": "ok", "usage_percent": 10}, "weekly": {"reset_in_sec": 45588, "status": "ok", "usage_percent": 17}, "monthly": {"reset_in_sec": 2475284, "status": "ok", "usage_percent": 8}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 19:30:01", "status": "critical", "last_message_age_sec": 10560, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 13794, "status": "ok", "usage_percent": 18}, "weekly": {"reset_in_sec": 45279, "status": "ok", "usage_percent": 20}, "monthly": {"reset_in_sec": 2474975, "status": "ok", "usage_percent": 10}, "session_expired": false, "issues": [], "total_keys": 6}} +{"timestamp": "2026-07-19 19:35:01", "status": "degraded", "last_message_age_sec": 10859, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "error", "error": "timed out"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 13487, "status": "ok", "usage_percent": 22}, "weekly": {"reset_in_sec": 44972, "status": "ok", "usage_percent": 22}, "monthly": {"reset_in_sec": 2474668, "status": "ok", "usage_percent": 11}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 19:40:01", "status": "degraded", "last_message_age_sec": 11159, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "error", "error": "timed out"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 13178, "status": "ok", "usage_percent": 26}, "weekly": {"reset_in_sec": 44663, "status": "ok", "usage_percent": 24}, "monthly": {"reset_in_sec": 2474359, "status": "ok", "usage_percent": 12}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 19:45:02", "status": "degraded", "last_message_age_sec": 11459, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "error", "error": "timed out"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 12872, "status": "ok", "usage_percent": 32}, "weekly": {"reset_in_sec": 44357, "status": "ok", "usage_percent": 26}, "monthly": {"reset_in_sec": 2474053, "status": "ok", "usage_percent": 13}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 19:50:01", "status": "critical", "last_message_age_sec": 11759, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "fast"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 12401, "status": "ok", "usage_percent": 38}, "weekly": {"reset_in_sec": 43886, "status": "ok", "usage_percent": 28}, "monthly": {"reset_in_sec": 2473582, "status": "ok", "usage_percent": 14}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 20:00:01", "status": "critical", "last_message_age_sec": 12359, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "fast"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 11775, "status": "ok", "usage_percent": 49}, "weekly": {"reset_in_sec": 43260, "status": "ok", "usage_percent": 33}, "monthly": {"reset_in_sec": 2472956, "status": "ok", "usage_percent": 16}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 20:05:01", "status": "critical", "last_message_age_sec": 12659, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "fast"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 11464, "status": "ok", "usage_percent": 60}, "weekly": {"reset_in_sec": 42949, "status": "ok", "usage_percent": 37}, "monthly": {"reset_in_sec": 2472645, "status": "ok", "usage_percent": 18}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 20:10:02", "status": "critical", "last_message_age_sec": 12960, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "fast"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 11153, "status": "ok", "usage_percent": 70}, "weekly": {"reset_in_sec": 42638, "status": "ok", "usage_percent": 41}, "monthly": {"reset_in_sec": 2472334, "status": "ok", "usage_percent": 20}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 20:15:01", "status": "critical", "last_message_age_sec": 13259, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "fast"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 10842, "status": "ok", "usage_percent": 77}, "weekly": {"reset_in_sec": 42327, "status": "ok", "usage_percent": 44}, "monthly": {"reset_in_sec": 2472023, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 20:20:02", "status": "critical", "last_message_age_sec": 13559, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "fast"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 10532, "status": "ok", "usage_percent": 77}, "weekly": {"reset_in_sec": 42017, "status": "ok", "usage_percent": 44}, "monthly": {"reset_in_sec": 2471713, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 20:25:01", "status": "critical", "last_message_age_sec": 13859, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "fast"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 10301, "status": "ok", "usage_percent": 78}, "weekly": {"reset_in_sec": 41786, "status": "ok", "usage_percent": 44}, "monthly": {"reset_in_sec": 2471482, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 20:30:01", "status": "critical", "last_message_age_sec": 14159, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "fast"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 9991, "status": "ok", "usage_percent": 78}, "weekly": {"reset_in_sec": 41476, "status": "ok", "usage_percent": 44}, "monthly": {"reset_in_sec": 2471172, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 20:35:01", "status": "critical", "last_message_age_sec": 14459, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "fast"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 9681, "status": "ok", "usage_percent": 78}, "weekly": {"reset_in_sec": 41166, "status": "ok", "usage_percent": 45}, "monthly": {"reset_in_sec": 2470862, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 20:40:01", "status": "critical", "last_message_age_sec": 14762, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "fast"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 9362, "status": "ok", "usage_percent": 79}, "weekly": {"reset_in_sec": 40847, "status": "ok", "usage_percent": 45}, "monthly": {"reset_in_sec": 2470543, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 20:45:02", "status": "critical", "last_message_age_sec": 15059, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "fast"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 9047, "status": "ok", "usage_percent": 79}, "weekly": {"reset_in_sec": 40532, "status": "ok", "usage_percent": 45}, "monthly": {"reset_in_sec": 2470228, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 20:50:01", "status": "critical", "last_message_age_sec": 15363, "error_rate_1h": 0, "bot_activity": {"error": "journalctl failed"}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "fast"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 8732, "status": "ok", "usage_percent": 79}, "weekly": {"reset_in_sec": 40217, "status": "ok", "usage_percent": 45}, "monthly": {"reset_in_sec": 2469913, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 20:55:01", "status": "critical", "last_message_age_sec": 15661, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "fast"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 8423, "status": "ok", "usage_percent": 79}, "weekly": {"reset_in_sec": 39908, "status": "ok", "usage_percent": 45}, "monthly": {"reset_in_sec": 2469604, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 21:00:01", "status": "critical", "last_message_age_sec": 15958, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "fast"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 8114, "status": "ok", "usage_percent": 79}, "weekly": {"reset_in_sec": 39599, "status": "ok", "usage_percent": 45}, "monthly": {"reset_in_sec": 2469295, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} diff --git a/gateway/logs/xmpp_messages.jsonl b/gateway/logs/xmpp_messages.jsonl new file mode 100644 index 00000000..3c2174c2 --- /dev/null +++ b/gateway/logs/xmpp_messages.jsonl @@ -0,0 +1,3 @@ +{"timestamp": "2026-07-19 16:30:03", "direction": "out", "from": "zhiwei@yoin.fun", "to": "hmo@yoin.fun", "body_preview": "【每日汇总】今日以下cron报告未送达(已拦截): • 脚本输出 99次 无操作信号的报告正常静默,有操作信号的都已送达。", "status": "ok", "error": null, "latency_ms": 1436, "epoch": 1784449803.6249352} +{"timestamp": "2026-07-19 16:32:02", "direction": "out", "from": "zhiwei@yoin.fun", "to": "hmo@yoin.fun", "body_preview": "【每日汇总】今日以下cron报告未送达(已拦截): • 脚本输出 99次 无操作信号的报告正常静默,有操作信号的都已送达。", "status": "ok", "error": null, "latency_ms": 401, "epoch": 1784449922.3636103} +{"timestamp": "2026-07-19 16:34:02", "direction": "out", "from": "zhiwei@yoin.fun", "to": "hmo@yoin.fun", "body_preview": "【每日汇总】今日以下cron报告未送达(已拦截): • 脚本输出 99次 无操作信号的报告正常静默,有操作信号的都已送达。", "status": "ok", "error": null, "latency_ms": 430, "epoch": 1784450042.5418942} diff --git a/gateway/temp/health_todos.jsonl b/gateway/temp/health_todos.jsonl new file mode 100644 index 00000000..8c13a428 --- /dev/null +++ b/gateway/temp/health_todos.jsonl @@ -0,0 +1,43 @@ +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T14:30:01.660636"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T14:45:01.509824"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T14:50:01.299686"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T15:10:01.664172"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T15:16:56.095701"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T15:20:01.499188"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "timed out", "timestamp": "2026-07-19T15:35:01.728475"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T15:40:01.641145"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T15:45:01.566000"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T15:50:01.481496"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T16:00:01.977074"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T16:05:01.820740"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T16:20:01.556094"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T16:30:01.714587"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T16:35:01.890435"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T16:40:02.012734"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T16:55:01.801842"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T17:00:01.449735"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T17:05:01.397273"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T17:10:02.184096"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T17:15:01.441236"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T17:20:01.917698"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T17:25:01.507624"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T17:45:01.480343"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T17:55:02.087320"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T18:00:01.432964"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T18:05:01.877289"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T18:15:01.180290"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T18:15:32.047339"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T18:20:01.729488"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T18:22:09.444652"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T18:25:01.641678"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T18:30:02.180070"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "timed out", "timestamp": "2026-07-19T18:35:02.017521"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "timed out", "timestamp": "2026-07-19T18:40:01.770567"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T18:45:01.936046"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T18:50:01.336498"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T18:55:02.111939"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "timed out", "timestamp": "2026-07-19T19:00:01.584617"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "timed out", "timestamp": "2026-07-19T19:05:02.034721"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T19:10:01.565114"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-19T19:30:01.210250"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "timed out", "timestamp": "2026-07-19T20:40:01.598513"} diff --git a/gateway/temp/last_health_check.json b/gateway/temp/last_health_check.json new file mode 100644 index 00000000..0b78a8d8 --- /dev/null +++ b/gateway/temp/last_health_check.json @@ -0,0 +1,49 @@ +{ + "services": [ + { + "name": "mofin_api", + "label": "MoFin API", + "type": "http", + "port": 8899, + "health": { + "ok": true + }, + "detail": "HTTP 200" + }, + { + "name": "zhiwei_gateway", + "label": "知微 Gateway", + "type": "http", + "port": 8643, + "health": { + "ok": true + }, + "detail": "HTTP 200" + }, + { + "name": "ejabberd", + "label": "ejabberd XMPP", + "type": "tcp", + "port": 5222, + "health": { + "ok": true + }, + "detail": "ok" + }, + { + "name": "mofin_db", + "label": "MoFin 数据库", + "type": "db", + "port": 0, + "health": { + "ok": true + }, + "detail": "ok" + } + ], + "summary": { + "ok": 4, + "total": 4 + }, + "generated_at": "2026-07-19 21:00:01" +} \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 00000000..3b5131b4 --- /dev/null +++ b/index.html @@ -0,0 +1,1695 @@ + + + + + +MoFin · 莫荷情报 + + + + + + +
+ +
+
+ 📊 +

MoFin

+ 知微 +
+
+ + +
+
+ + +
+ + + + + + + + + + + + 📸 上传 +
+ + +
+ + + + + + + + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/templates/closing_brief.txt b/templates/closing_brief.txt index 00e0dfc6..aa08fadf 100644 --- a/templates/closing_brief.txt +++ b/templates/closing_brief.txt @@ -1,19 +1,19 @@ -═══════════════════════════════════════ -知微 收盘简报 | {GENERATED_AT} -═══════════════════════════════════════ - -【数据面板 — 代码采集,LLM不得修改】 - -现金 {CASH_AMOUNT} CNY | 冻结 {portfolio.frozen_cash} CNY -持仓市值 {STOCK_VALUE} CNY | 总资产 {TOTAL_ASSETS} CNY -仓位 {POSITION_PCT}% | 持仓 {HOLDINGS_COUNT} 只 - -现金来源: {CASH_SOURCE} - -持仓明细: -{HOLDINGS_TABLE} - -⚠️ 浮亏>20%: -{HOLDINGS_RISK} - -═══════════════════════════════════════ +═══════════════════════════════════════ +知微 收盘简报 | {GENERATED_AT} +═══════════════════════════════════════ + +【数据面板 — 代码采集,LLM不得修改】 + +现金 {CASH_AMOUNT} CNY | 冻结 {portfolio.frozen_cash} CNY +持仓市值 {STOCK_VALUE} CNY | 总资产 {TOTAL_ASSETS} CNY +仓位 {POSITION_PCT}% | 持仓 {HOLDINGS_COUNT} 只 + +现金来源: {CASH_SOURCE} + +持仓明细: +{HOLDINGS_TABLE} + +⚠️ 浮亏>20%: +{HOLDINGS_RISK} + +═══════════════════════════════════════ diff --git a/templates/intraday_monitor.txt b/templates/intraday_monitor.txt index da0e4914..ea550a75 100644 --- a/templates/intraday_monitor.txt +++ b/templates/intraday_monitor.txt @@ -1,22 +1,22 @@ -═══════════════════════════════════════ -MoFin 盘中监控 | {GENERATED_AT} -═══════════════════════════════════════ - -【数据面板 — 代码采集,LLM不得修改】 - -大盘: 上证 {market.sh_index} ({market.sh_change:+.2f}%) | 深证 {market.sz_index} ({market.sz_change:+.2f}%) -涨跌比 {market.advance_decline_ratio} | 情绪 {market.mood} - -现金 {CASH_AMOUNT} CNY | 冻结 {portfolio.frozen_cash} CNY -持仓市值 {STOCK_VALUE} CNY | 总资产 {TOTAL_ASSETS} CNY -仓位 {POSITION_PCT}% | 持仓 {HOLDINGS_COUNT} 只 | 港币汇率 {HK_RATE} - -现金来源: {CASH_SOURCE} - -持仓明细: -{HOLDINGS_TABLE} - -⚠️ 浮亏>20%: -{HOLDINGS_RISK} - -═══════════════════════════════════════ +═══════════════════════════════════════ +MoFin 盘中监控 | {GENERATED_AT} +═══════════════════════════════════════ + +【数据面板 — 代码采集,LLM不得修改】 + +大盘: 上证 {market.sh_index} ({market.sh_change:+.2f}%) | 深证 {market.sz_index} ({market.sz_change:+.2f}%) +涨跌比 {market.advance_decline_ratio} | 情绪 {market.mood} + +现金 {CASH_AMOUNT} CNY | 冻结 {portfolio.frozen_cash} CNY +持仓市值 {STOCK_VALUE} CNY | 总资产 {TOTAL_ASSETS} CNY +仓位 {POSITION_PCT}% | 持仓 {HOLDINGS_COUNT} 只 | 港币汇率 {HK_RATE} + +现金来源: {CASH_SOURCE} + +持仓明细: +{HOLDINGS_TABLE} + +⚠️ 浮亏>20%: +{HOLDINGS_RISK} + +═══════════════════════════════════════ diff --git a/templates/opening_brief.txt b/templates/opening_brief.txt index 723b73d6..af6505e9 100644 --- a/templates/opening_brief.txt +++ b/templates/opening_brief.txt @@ -1,19 +1,19 @@ -═══════════════════════════════════════ -知微 开盘简报 | {GENERATED_AT} -═══════════════════════════════════════ - -【数据面板 — 代码采集,LLM不得修改】 - -现金 {CASH_AMOUNT} CNY | 冻结 {portfolio.frozen_cash} CNY -持仓市值 {STOCK_VALUE} CNY | 总资产 {TOTAL_ASSETS} CNY -仓位 {POSITION_PCT}% | 持仓 {HOLDINGS_COUNT} 只 - -现金来源: {CASH_SOURCE} - -持仓明细: -{HOLDINGS_TABLE} - -⚠️ 浮亏>20%: -{HOLDINGS_RISK} - -═══════════════════════════════════════ +═══════════════════════════════════════ +知微 开盘简报 | {GENERATED_AT} +═══════════════════════════════════════ + +【数据面板 — 代码采集,LLM不得修改】 + +现金 {CASH_AMOUNT} CNY | 冻结 {portfolio.frozen_cash} CNY +持仓市值 {STOCK_VALUE} CNY | 总资产 {TOTAL_ASSETS} CNY +仓位 {POSITION_PCT}% | 持仓 {HOLDINGS_COUNT} 只 + +现金来源: {CASH_SOURCE} + +持仓明细: +{HOLDINGS_TABLE} + +⚠️ 浮亏>20%: +{HOLDINGS_RISK} + +═══════════════════════════════════════ diff --git a/templates/self_buy_reminder.txt b/templates/self_buy_reminder.txt index e2fe9e1f..abd6804e 100644 --- a/templates/self_buy_reminder.txt +++ b/templates/self_buy_reminder.txt @@ -1,15 +1,15 @@ -═══════════════════════════════════════ -自选买入提醒 | {GENERATED_AT} -═══════════════════════════════════════ - -【数据面板 — 代码采集,LLM不得修改】 - -总资产 {TOTAL_ASSETS} CNY | 现金 {CASH_AMOUNT} CNY | 冻结 {portfolio.frozen_cash} CNY -仓位 {POSITION_PCT}% | 港币汇率 {HK_RATE} - -现金来源: {CASH_SOURCE} - -持仓明细(已有持仓,不计入现金占用): -{HOLDINGS_TABLE} - -═══════════════════════════════════════ +═══════════════════════════════════════ +自选买入提醒 | {GENERATED_AT} +═══════════════════════════════════════ + +【数据面板 — 代码采集,LLM不得修改】 + +总资产 {TOTAL_ASSETS} CNY | 现金 {CASH_AMOUNT} CNY | 冻结 {portfolio.frozen_cash} CNY +仓位 {POSITION_PCT}% | 港币汇率 {HK_RATE} + +现金来源: {CASH_SOURCE} + +持仓明细(已有持仓,不计入现金占用): +{HOLDINGS_TABLE} + +═══════════════════════════════════════ diff --git a/templates/strategy_eval.txt b/templates/strategy_eval.txt index bd05013c..35193c8a 100644 --- a/templates/strategy_eval.txt +++ b/templates/strategy_eval.txt @@ -1,19 +1,19 @@ -═══════════════════════════════════════ -MoFin 策略评估 | {GENERATED_AT} -═══════════════════════════════════════ - -【数据面板 — 代码采集,LLM不得修改】 - -组合概况: -总资产 {TOTAL_ASSETS} CNY | 现金 {CASH_AMOUNT} CNY | 仓位 {POSITION_PCT}% -持仓 {HOLDINGS_COUNT} 只 | 港币汇率 {HK_RATE} - -持仓明细: -{HOLDINGS_TABLE} - -⚠️ 浮亏>20%: -{HOLDINGS_RISK} - -现金来源: {CASH_SOURCE} - -═══════════════════════════════════════ +═══════════════════════════════════════ +MoFin 策略评估 | {GENERATED_AT} +═══════════════════════════════════════ + +【数据面板 — 代码采集,LLM不得修改】 + +组合概况: +总资产 {TOTAL_ASSETS} CNY | 现金 {CASH_AMOUNT} CNY | 仓位 {POSITION_PCT}% +持仓 {HOLDINGS_COUNT} 只 | 港币汇率 {HK_RATE} + +持仓明细: +{HOLDINGS_TABLE} + +⚠️ 浮亏>20%: +{HOLDINGS_RISK} + +现金来源: {CASH_SOURCE} + +═══════════════════════════════════════