diff --git a/analyst-knowledge-log.md b/analyst-knowledge-log.md index b7d42d0c..7756a285 100644 --- a/analyst-knowledge-log.md +++ b/analyst-knowledge-log.md @@ -290,4 +290,23 @@ cron prompt for 系统健康检查-开盘前 (job_id: 37c02f4d7df9) - 过期信号不再堆积 → 健康检查正常 - 消费者只处理时效内信号 → 最新信号优先 - 无信号时自动清理过期堆积 → 不积压 -- 区间说明格式改为: "→ 进入操作区间,但重评结果: {reason}" \ No newline at end of file +- 区间说明格式改为: "→ 进入操作区间,但重评结果: {reason}"### 2026-07-19 22:32 Dad资产更新 +- **来源**: Dad XMPP截图+文字确认 +- **现金**: 0→241,330 (Dad确认) +- **持仓**: 12只,与DB及截图一致(OCR确认) +- **总资产**: 783,321 CNY +- **仓位**: 69.19% +- **变更**: cash_log id=17, portfolio_summary已更新, portfolio.json已同步 +- **问题**: XMPP bot此前3次超时/丢图导致Dad截图未处理,已人工补偿 +### 2026-07-19 23:35 Dad重导holding.xls全量更新 +- **来源**: Dad从券商重导holding.xls(23:30更新) +- **持仓变化**: 12只→14只 + - 新增: 300308 中际旭创 (100股, MV 97,946) + - 新增: 300035 中科电气 (1400股, MV 16,912) + - 之前我OCR截图漏了这两只,原因是字幕混淆(中科电气与宁德时代代码相邻被合并,中际旭创在截图折叠区) +- **现金**: 241,330.80 (不变) +- **冻结**: 0.05 (不变) +- **总市值**: 655,231 (holding.xls当前价) +- **总资产**: 896,562 +- **仓位**: 73.08% +- **截图头部659,354与xls的655,231差异来源**: 截图是13:01价格,xls是23:30价格,中间价格波动导致 diff --git a/data/mofin.db-shm b/data/mofin.db-shm new file mode 100644 index 00000000..fe9ac284 Binary files /dev/null and b/data/mofin.db-shm differ diff --git a/data/mofin.db-wal b/data/mofin.db-wal new file mode 100644 index 00000000..e69de29b diff --git a/data/portfolio.json b/data/portfolio.json index 4f9cbf14..6428fd96 100644 --- a/data/portfolio.json +++ b/data/portfolio.json @@ -1,78 +1,18 @@ { "holdings": [ { - "code": "518880", - "name": "黄金ETF华安", - "shares": 2400, - "cost": 12.1915, - "position_pct": null, - "added_at": "2026-07-17", + "code": "300308", + "name": "中际旭创", + "shares": 100, + "cost": 1316.5333, + "position_pct": 14.95, + "added_at": "2026-07-19 23:36:27", "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, + "price": 979.46, + "market_value": 97946.0, + "change_pct": -12.0, "currency": "CNY" }, { @@ -80,74 +20,29 @@ "name": "华茂股份", "shares": 20400, "cost": 3.9408, - "position_pct": null, - "added_at": "2026-07-17", + "position_pct": 12.7, + "added_at": "2026-07-19 23:36:27", "is_active": 1, "closed_at": null, "close_pnl": null, - "price": 4.09, + "price": 4.08, "market_value": 83232.0, - "change_pct": 0.25, + "change_pct": 0.0, "currency": "CNY" }, { - "code": "300035", - "name": "中科电气", - "shares": 1400, - "cost": 22.2914, - "position_pct": null, - "added_at": "2026-07-17", + "code": "601899", + "name": "紫金矿业", + "shares": 2400, + "cost": 39.8885, + "position_pct": 10.39, + "added_at": "2026-07-19 23:36:27", "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, + "price": 28.36, + "market_value": 68064.0, + "change_pct": -3.04, "currency": "CNY" }, { @@ -155,14 +50,44 @@ "name": "丘钛科技", "shares": 11000, "cost": 13.531, - "position_pct": null, - "added_at": "2026-07-17", + "position_pct": 9.67, + "added_at": "2026-07-19 23:36:27", "is_active": 1, "closed_at": null, "close_pnl": null, - "price": 6.56, - "market_value": 62967.76, - "change_pct": -6.42, + "price": 6.67, + "market_value": 63347.66, + "change_pct": -4.85, + "currency": "HKD" + }, + { + "code": "01211", + "name": "比亚迪股份", + "shares": 600, + "cost": 105.3827, + "position_pct": 7.01, + "added_at": "2026-07-19 23:36:27", + "is_active": 1, + "closed_at": null, + "close_pnl": null, + "price": 88.7, + "market_value": 45950.15, + "change_pct": -2.47, + "currency": "HKD" + }, + { + "code": "688639", + "name": "华恒生物", + "shares": 2800, + "cost": 21.5085, + "position_pct": 6.9, + "added_at": "2026-07-19 23:36:27", + "is_active": 1, + "closed_at": null, + "close_pnl": null, + "price": 16.14, + "market_value": 45192.0, + "change_pct": -6.11, "currency": "CNY" }, { @@ -170,21 +95,130 @@ "name": "万科企业", "shares": 19700, "cost": 4.6906, - "position_pct": null, - "added_at": "2026-07-17", + "position_pct": 6.44, + "added_at": "2026-07-19 23:36:27", "is_active": 1, "closed_at": null, "close_pnl": null, - "price": 2.36, - "market_value": 40141.19, - "change_pct": -5.98, + "price": 2.48, + "market_value": 42182.27, + "change_pct": -1.2, + "currency": "HKD" + }, + { + "code": "688981", + "name": "中芯国际", + "shares": 300, + "cost": 126.0681, + "position_pct": 6.4, + "added_at": "2026-07-19 23:36:27", + "is_active": 1, + "closed_at": null, + "close_pnl": null, + "price": 139.88, + "market_value": 41964.0, + "change_pct": -7.85, + "currency": "CNY" + }, + { + "code": "00700", + "name": "腾讯控股", + "shares": 100, + "cost": 445.2906, + "position_pct": 6.08, + "added_at": "2026-07-19 23:36:27", + "is_active": 1, + "closed_at": null, + "close_pnl": null, + "price": 461.6, + "market_value": 39854.54, + "change_pct": -4.63, + "currency": "HKD" + }, + { + "code": "688411", + "name": "海博思创", + "shares": 200, + "cost": 266.9461, + "position_pct": 5.55, + "added_at": "2026-07-19 23:36:27", + "is_active": 1, + "closed_at": null, + "close_pnl": null, + "price": 181.68, + "market_value": 36336.0, + "change_pct": -6.3, + "currency": "CNY" + }, + { + "code": "300750", + "name": "宁德时代", + "shares": 100, + "cost": 401.7803, + "position_pct": 5.49, + "added_at": "2026-07-19 23:36:27", + "is_active": 1, + "closed_at": null, + "close_pnl": null, + "price": 360.0, + "market_value": 36000.0, + "change_pct": -1.69, + "currency": "CNY" + }, + { + "code": "518880", + "name": "黄金ETF华安", + "shares": 2400, + "cost": 12.1915, + "position_pct": 3.04, + "added_at": "2026-07-19 23:36:27", + "is_active": 1, + "closed_at": null, + "close_pnl": null, + "price": 8.31, + "market_value": 19946.4, + "change_pct": -0.62, + "currency": "CNY" + }, + { + "code": "01088", + "name": "中国神华", + "shares": 500, + "cost": 46.1178, + "position_pct": 2.79, + "added_at": "2026-07-19 23:36:27", + "is_active": 1, + "closed_at": null, + "close_pnl": null, + "price": 42.4, + "market_value": 18304.08, + "change_pct": 1.24, + "currency": "HKD" + }, + { + "code": "300035", + "name": "中科电气", + "shares": 1400, + "cost": 22.2914, + "position_pct": 2.58, + "added_at": "2026-07-19 23:36:27", + "is_active": 1, + "closed_at": null, + "close_pnl": null, + "price": 12.08, + "market_value": 16912.0, + "change_pct": -3.28, "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" + "id": 1, + "total_assets": 896561.95, + "stock_value": 655860.0, + "cash": 241330.8, + "position_pct": 73.08, + "total_pnl": 0.0, + "updated_at": "2026-07-20 08:03:11", + "total_mv": 655231.1, + "frozen_cash": 0.05, + "currency": "CNY" } \ No newline at end of file diff --git a/data/price_events.json b/data/price_events.json index ed78c0a3..0b1d63f5 100644 --- a/data/price_events.json +++ b/data/price_events.json @@ -10989,6 +10989,16 @@ "event_label": "止盈区间", "timestamp": "2026-07-17T16:59:07.705299", "date": "2026-07-17" + }, + { + "code": "000850", + "name": "华茂股份", + "event_type": "entry_zone", + "price": 4.08, + "trigger_value": "3.96~4.09", + "event_label": "加仓区间", + "timestamp": "2026-07-20T01:21:34.739614", + "date": "2026-07-20" } ] } \ No newline at end of file diff --git a/data/price_history.json b/data/price_history.json index 6036ba1c..108df184 100644 --- a/data/price_history.json +++ b/data/price_history.json @@ -23,6 +23,22 @@ "close": 14.21, "volume": 37396292.0, "amount": 55424.0 + }, + { + "date": "2026-07-19", + "high": 15.75, + "low": 14.1, + "close": 14.21, + "volume": 37396292.0, + "amount": 55424.0 + }, + { + "date": "2026-07-20", + "high": 15.75, + "low": 14.1, + "close": 14.21, + "volume": 37396292.0, + "amount": 55424.0 } ], "688775": [ @@ -49,6 +65,22 @@ "close": 138.34, "volume": 6157193.0, "amount": 86656.0 + }, + { + "date": "2026-07-19", + "high": 149.0, + "low": 135.76, + "close": 138.34, + "volume": 6157193.0, + "amount": 86656.0 + }, + { + "date": "2026-07-20", + "high": 149.0, + "low": 135.76, + "close": 138.34, + "volume": 6157193.0, + "amount": 86656.0 } ], "688475": [ @@ -75,6 +107,22 @@ "close": 26.64, "volume": 5999240.0, "amount": 16223.0 + }, + { + "date": "2026-07-19", + "high": 27.74, + "low": 26.4, + "close": 26.64, + "volume": 5999240.0, + "amount": 16223.0 + }, + { + "date": "2026-07-20", + "high": 27.74, + "low": 26.4, + "close": 26.64, + "volume": 5999240.0, + "amount": 16223.0 } ], "688778": [ @@ -101,6 +149,22 @@ "close": 44.14, "volume": 7684619.0, "amount": 34599.0 + }, + { + "date": "2026-07-19", + "high": 46.45, + "low": 44.09, + "close": 44.14, + "volume": 7684619.0, + "amount": 34599.0 + }, + { + "date": "2026-07-20", + "high": 46.45, + "low": 44.09, + "close": 44.14, + "volume": 7684619.0, + "amount": 34599.0 } ], "688501": [ @@ -127,6 +191,22 @@ "close": 16.79, "volume": 1904231.0, "amount": 3272.0 + }, + { + "date": "2026-07-19", + "high": 17.8, + "low": 16.64, + "close": 16.79, + "volume": 1904231.0, + "amount": 3272.0 + }, + { + "date": "2026-07-20", + "high": 17.8, + "low": 16.64, + "close": 16.79, + "volume": 1904231.0, + "amount": 3272.0 } ], "688510": [ @@ -153,6 +233,22 @@ "close": 21.0, "volume": 5596808.0, "amount": 12011.0 + }, + { + "date": "2026-07-19", + "high": 22.46, + "low": 20.63, + "close": 21.0, + "volume": 5596808.0, + "amount": 12011.0 + }, + { + "date": "2026-07-20", + "high": 22.46, + "low": 20.63, + "close": 21.0, + "volume": 5596808.0, + "amount": 12011.0 } ], "688513": [ @@ -179,6 +275,22 @@ "close": 54.63, "volume": 4293163.0, "amount": 24190.0 + }, + { + "date": "2026-07-19", + "high": 59.9, + "low": 54.5, + "close": 54.63, + "volume": 4293163.0, + "amount": 24190.0 + }, + { + "date": "2026-07-20", + "high": 59.9, + "low": 54.5, + "close": 54.63, + "volume": 4293163.0, + "amount": 24190.0 } ], "688518": [ @@ -205,6 +317,22 @@ "close": 18.46, "volume": 18674329.0, "amount": 35390.0 + }, + { + "date": "2026-07-19", + "high": 19.78, + "low": 18.31, + "close": 18.46, + "volume": 18674329.0, + "amount": 35390.0 + }, + { + "date": "2026-07-20", + "high": 19.78, + "low": 18.31, + "close": 18.46, + "volume": 18674329.0, + "amount": 35390.0 } ], "00700": [ @@ -231,6 +359,22 @@ "close": 461.6, "volume": 36237657.0, "amount": 16928705332.905 + }, + { + "date": "2026-07-19", + "high": 488.8, + "low": 458.0, + "close": 461.6, + "volume": 36237657.0, + "amount": 16928705332.905 + }, + { + "date": "2026-07-20", + "high": 488.8, + "low": 458.0, + "close": 461.6, + "volume": 36237657.0, + "amount": 16928705332.905 } ], "688533": [ @@ -257,6 +401,22 @@ "close": 20.59, "volume": 1359591.0, "amount": 2855.0 + }, + { + "date": "2026-07-19", + "high": 21.74, + "low": 20.4, + "close": 20.59, + "volume": 1359591.0, + "amount": 2855.0 + }, + { + "date": "2026-07-20", + "high": 21.74, + "low": 20.4, + "close": 20.59, + "volume": 1359591.0, + "amount": 2855.0 } ], "01088": [ @@ -283,6 +443,22 @@ "close": 42.4, "volume": 13003969.0, "amount": 547347699.16 + }, + { + "date": "2026-07-19", + "high": 42.5, + "low": 41.54, + "close": 42.4, + "volume": 13003969.0, + "amount": 547347699.16 + }, + { + "date": "2026-07-20", + "high": 42.5, + "low": 41.54, + "close": 42.4, + "volume": 13003969.0, + "amount": 547347699.16 } ], "688566": [ @@ -309,6 +485,22 @@ "close": 25.0, "volume": 3344706.0, "amount": 8599.0 + }, + { + "date": "2026-07-19", + "high": 27.11, + "low": 25.0, + "close": 25.0, + "volume": 3344706.0, + "amount": 8599.0 + }, + { + "date": "2026-07-20", + "high": 27.11, + "low": 25.0, + "close": 25.0, + "volume": 3344706.0, + "amount": 8599.0 } ], "01211": [ @@ -335,6 +527,22 @@ "close": 88.7, "volume": 26427225.0, "amount": 2340941950.5 + }, + { + "date": "2026-07-19", + "high": 91.45, + "low": 87.25, + "close": 88.7, + "volume": 26427225.0, + "amount": 2340941950.5 + }, + { + "date": "2026-07-20", + "high": 91.45, + "low": 87.25, + "close": 88.7, + "volume": 26427225.0, + "amount": 2340941950.5 } ], "688639": [ @@ -361,6 +569,22 @@ "close": 16.14, "volume": 10030710.0, "amount": 16760.0 + }, + { + "date": "2026-07-19", + "high": 17.56, + "low": 16.1, + "close": 16.14, + "volume": 10030710.0, + "amount": 16760.0 + }, + { + "date": "2026-07-20", + "high": 17.56, + "low": 16.1, + "close": 16.14, + "volume": 10030710.0, + "amount": 16760.0 } ], "688582": [ @@ -387,6 +611,22 @@ "close": 38.61, "volume": 10970620.0, "amount": 44852.0 + }, + { + "date": "2026-07-19", + "high": 43.8, + "low": 38.5, + "close": 38.61, + "volume": 10970620.0, + "amount": 44852.0 + }, + { + "date": "2026-07-20", + "high": 43.8, + "low": 38.5, + "close": 38.61, + "volume": 10970620.0, + "amount": 44852.0 } ], "01478": [ @@ -413,6 +653,22 @@ "close": 6.67, "volume": 7833000.0, "amount": 52372235.0 + }, + { + "date": "2026-07-19", + "high": 7.02, + "low": 6.52, + "close": 6.67, + "volume": 7833000.0, + "amount": 52372235.0 + }, + { + "date": "2026-07-20", + "high": 7.02, + "low": 6.52, + "close": 6.67, + "volume": 7833000.0, + "amount": 52372235.0 } ], "688755": [ @@ -439,6 +695,22 @@ "close": 24.01, "volume": 1862009.0, "amount": 4574.0 + }, + { + "date": "2026-07-19", + "high": 25.89, + "low": 23.8, + "close": 24.01, + "volume": 1862009.0, + "amount": 4574.0 + }, + { + "date": "2026-07-20", + "high": 25.89, + "low": 23.8, + "close": 24.01, + "volume": 1862009.0, + "amount": 4574.0 } ], "02202": [ @@ -465,6 +737,22 @@ "close": 2.48, "volume": 40325958.0, "amount": 98037795.66 + }, + { + "date": "2026-07-19", + "high": 2.55, + "low": 2.34, + "close": 2.48, + "volume": 40325958.0, + "amount": 98037795.66 + }, + { + "date": "2026-07-20", + "high": 2.55, + "low": 2.34, + "close": 2.48, + "volume": 40325958.0, + "amount": 98037795.66 } ], "688692": [ @@ -491,6 +779,22 @@ "close": 215.4, "volume": 1706538.0, "amount": 38102.0 + }, + { + "date": "2026-07-19", + "high": 231.68, + "low": 215.0, + "close": 215.4, + "volume": 1706538.0, + "amount": 38102.0 + }, + { + "date": "2026-07-20", + "high": 231.68, + "low": 215.0, + "close": 215.4, + "volume": 1706538.0, + "amount": 38102.0 } ], "300750": [ @@ -517,6 +821,22 @@ "close": 360.0, "volume": 512453.0, "amount": 1884671.0 + }, + { + "date": "2026-07-19", + "high": 375.24, + "low": 360.0, + "close": 360.0, + "volume": 512453.0, + "amount": 1884671.0 + }, + { + "date": "2026-07-20", + "high": 375.24, + "low": 360.0, + "close": 360.0, + "volume": 512453.0, + "amount": 1884671.0 } ], "688696": [ @@ -543,6 +863,22 @@ "close": 62.9, "volume": 1185875.0, "amount": 7647.0 + }, + { + "date": "2026-07-19", + "high": 67.39, + "low": 62.37, + "close": 62.9, + "volume": 1185875.0, + "amount": 7647.0 + }, + { + "date": "2026-07-20", + "high": 67.39, + "low": 62.37, + "close": 62.9, + "volume": 1185875.0, + "amount": 7647.0 } ], "518880": [ @@ -569,6 +905,22 @@ "close": 8.311, "volume": 2844769.0, "amount": 235805.0 + }, + { + "date": "2026-07-19", + "high": 8.326, + "low": 8.256, + "close": 8.311, + "volume": 2844769.0, + "amount": 235805.0 + }, + { + "date": "2026-07-20", + "high": 8.326, + "low": 8.256, + "close": 8.311, + "volume": 2844769.0, + "amount": 235805.0 } ], "688708": [ @@ -595,6 +947,22 @@ "close": 44.09, "volume": 1442625.0, "amount": 6448.0 + }, + { + "date": "2026-07-19", + "high": 46.18, + "low": 43.51, + "close": 44.09, + "volume": 1442625.0, + "amount": 6448.0 + }, + { + "date": "2026-07-20", + "high": 46.18, + "low": 43.51, + "close": 44.09, + "volume": 1442625.0, + "amount": 6448.0 } ], "688779": [ @@ -621,6 +989,22 @@ "close": 6.7, "volume": 25020310.0, "amount": 17115.0 + }, + { + "date": "2026-07-19", + "high": 7.03, + "low": 6.67, + "close": 6.69, + "volume": 26278826.0, + "amount": 17957.0 + }, + { + "date": "2026-07-20", + "high": 7.03, + "low": 6.67, + "close": 6.69, + "volume": 26278826.0, + "amount": 17957.0 } ], "300035": [ @@ -647,6 +1031,22 @@ "close": 12.08, "volume": 158047.0, "amount": 19469.0 + }, + { + "date": "2026-07-19", + "high": 12.68, + "low": 12.05, + "close": 12.08, + "volume": 158047.0, + "amount": 19469.0 + }, + { + "date": "2026-07-20", + "high": 12.68, + "low": 12.05, + "close": 12.08, + "volume": 158047.0, + "amount": 19469.0 } ], "688789": [ @@ -673,6 +1073,22 @@ "close": 46.23, "volume": 2173285.0, "amount": 10089.0 + }, + { + "date": "2026-07-19", + "high": 47.73, + "low": 45.19, + "close": 46.1, + "volume": 2179608.0, + "amount": 10118.0 + }, + { + "date": "2026-07-20", + "high": 47.73, + "low": 45.19, + "close": 46.1, + "volume": 2179608.0, + "amount": 10118.0 } ], "600563": [ @@ -717,6 +1133,22 @@ "close": 957.88, "volume": 419591.0, "amount": 4233073.0 + }, + { + "date": "2026-07-19", + "high": 1072.0, + "low": 950.55, + "close": 979.46, + "volume": 561861.0, + "amount": 5648563.0 + }, + { + "date": "2026-07-20", + "high": 1072.0, + "low": 950.55, + "close": 979.46, + "volume": 561861.0, + "amount": 5648563.0 } ], "601899": [ @@ -743,6 +1175,22 @@ "close": 28.36, "volume": 2904169.0, "amount": 824106.0 + }, + { + "date": "2026-07-19", + "high": 28.82, + "low": 27.92, + "close": 28.36, + "volume": 2904169.0, + "amount": 824106.0 + }, + { + "date": "2026-07-20", + "high": 28.82, + "low": 27.92, + "close": 28.36, + "volume": 2904169.0, + "amount": 824106.0 } ], "688981": [ @@ -769,6 +1217,22 @@ "close": 139.88, "volume": 102952043.0, "amount": 1517166.0 + }, + { + "date": "2026-07-19", + "high": 155.51, + "low": 139.02, + "close": 139.88, + "volume": 102952043.0, + "amount": 1517166.0 + }, + { + "date": "2026-07-20", + "high": 155.51, + "low": 139.02, + "close": 139.88, + "volume": 102952043.0, + "amount": 1517166.0 } ], "688372": [ @@ -795,6 +1259,22 @@ "close": 120.73, "volume": 11689864.0, "amount": 151702.0 + }, + { + "date": "2026-07-19", + "high": 139.2, + "low": 120.66, + "close": 120.73, + "volume": 11689864.0, + "amount": 151702.0 + }, + { + "date": "2026-07-20", + "high": 139.2, + "low": 120.66, + "close": 120.73, + "volume": 11689864.0, + "amount": 151702.0 } ], "688411": [ @@ -821,6 +1301,22 @@ "close": 181.68, "volume": 4238973.0, "amount": 78494.0 + }, + { + "date": "2026-07-19", + "high": 193.48, + "low": 179.03, + "close": 181.68, + "volume": 4238973.0, + "amount": 78494.0 + }, + { + "date": "2026-07-20", + "high": 193.48, + "low": 179.03, + "close": 181.68, + "volume": 4238973.0, + "amount": 78494.0 } ], "688271": [ @@ -847,6 +1343,22 @@ "close": 110.37, "volume": 8809320.0, "amount": 97913.0 + }, + { + "date": "2026-07-19", + "high": 113.43, + "low": 109.86, + "close": 110.37, + "volume": 8809320.0, + "amount": 97913.0 + }, + { + "date": "2026-07-20", + "high": 113.43, + "low": 109.86, + "close": 110.37, + "volume": 8809320.0, + "amount": 97913.0 } ], "688439": [ @@ -873,6 +1385,22 @@ "close": 39.58, "volume": 3189314.0, "amount": 13157.0 + }, + { + "date": "2026-07-19", + "high": 42.38, + "low": 39.58, + "close": 39.58, + "volume": 3189314.0, + "amount": 13157.0 + }, + { + "date": "2026-07-20", + "high": 42.38, + "low": 39.58, + "close": 39.58, + "volume": 3189314.0, + "amount": 13157.0 } ], "688588": [ @@ -899,6 +1427,22 @@ "close": 8.6, "volume": 4173682.0, "amount": 3684.0 + }, + { + "date": "2026-07-19", + "high": 9.19, + "low": 8.55, + "close": 8.6, + "volume": 4173682.0, + "amount": 3684.0 + }, + { + "date": "2026-07-20", + "high": 9.19, + "low": 8.55, + "close": 8.6, + "volume": 4173682.0, + "amount": 3684.0 } ], "688621": [ @@ -925,6 +1469,22 @@ "close": 57.58, "volume": 6154202.0, "amount": 36271.0 + }, + { + "date": "2026-07-19", + "high": 61.75, + "low": 56.73, + "close": 57.58, + "volume": 6154202.0, + "amount": 36271.0 + }, + { + "date": "2026-07-20", + "high": 61.75, + "low": 56.73, + "close": 57.58, + "volume": 6154202.0, + "amount": 36271.0 } ], "000850": [ @@ -943,6 +1503,22 @@ "close": 4.08, "volume": 159057.0, "amount": 6535.0 + }, + { + "date": "2026-07-19", + "high": 4.24, + "low": 3.98, + "close": 4.08, + "volume": 159057.0, + "amount": 6535.0 + }, + { + "date": "2026-07-20", + "high": 4.24, + "low": 3.98, + "close": 4.08, + "volume": 159057.0, + "amount": 6535.0 } ], "301308": [ @@ -1029,6 +1605,22 @@ "close": 10.3, "volume": 245805.0, "amount": 25674.0 + }, + { + "date": "2026-07-19", + "high": 10.58, + "low": 10.3, + "close": 10.3, + "volume": 245805.0, + "amount": 25674.0 + }, + { + "date": "2026-07-20", + "high": 10.58, + "low": 10.3, + "close": 10.3, + "volume": 245805.0, + "amount": 25674.0 } ], "688087": [ @@ -1039,6 +1631,22 @@ "close": 36.18, "volume": 2780918.0, "amount": 10256.0 + }, + { + "date": "2026-07-19", + "high": 38.23, + "low": 35.99, + "close": 36.18, + "volume": 2780918.0, + "amount": 10256.0 + }, + { + "date": "2026-07-20", + "high": 38.23, + "low": 35.99, + "close": 36.18, + "volume": 2780918.0, + "amount": 10256.0 } ], "688466": [ @@ -1049,6 +1657,22 @@ "close": 12.6, "volume": 1788822.0, "amount": 2286.0 + }, + { + "date": "2026-07-19", + "high": 13.19, + "low": 12.44, + "close": 12.6, + "volume": 1788822.0, + "amount": 2286.0 + }, + { + "date": "2026-07-20", + "high": 13.19, + "low": 12.44, + "close": 12.6, + "volume": 1788822.0, + "amount": 2286.0 } ], "600111": [ diff --git a/deploy/bot/xmpp_agent_core.py b/deploy/bot/xmpp_agent_core.py index 795b8a51..25078382 100644 --- a/deploy/bot/xmpp_agent_core.py +++ b/deploy/bot/xmpp_agent_core.py @@ -1,205 +1,158 @@ #!/usr/bin/env python3 -""" -Core XMPP Agent — shared logic for zhiwei / mohe / xxm bots. -Imports by xmpp_zhiwei_bot.py / xmpp_mohe_bot.py with --agent flag. -""" -import os, sys, json, time, logging, threading, traceback -from http.server import HTTPServer, BaseHTTPRequestHandler -from urllib.parse import urlparse, parse_qs -from hashlib import md5 +"""XMPP Bot - 统一版,支持 --agent mohe|zhiwei|xiao 参数""" +import asyncio, logging, ssl, json, urllib.request, os, time, sys, re +from slixmpp import ClientXMPP -import slixmpp -from slixmpp import JID -import asyncio - -# ── Per-agent configuration ── -PER_AGENT = { +# ── Agent 配置 ────────────────────────────────────────────── +AGENTS = { "mohe": { "jid": "mohe@yoin.fun", "password": "hermes123", "nick": "mohe", - "http_port": 5808, - "gateway_url": "http://localhost:8642/v1/chat/completions", - "gateway_api_key": "hermes123", - "session_id": "xmpp-mohe-v2", "name_cn": "莫荷", + "http_port": 5802, + "gateway": "http://localhost:8642/v1/chat/completions", + "session_id": "xmpp-mohe-v2", + "kanban_session_id": "xmpp-mohe-kanban", "mention": "@mohe/@莫荷", }, "zhiwei": { "jid": "zhiwei@yoin.fun", - "password": "2nw4psra", + "password": "hermes123", "nick": "zhiwei", - "http_port": 5805, - "gateway_url": "http://localhost:8643/v1/chat/completions", - "gateway_api_key": "hermes123", - "session_id": "xmpp-zhiwei-v3", "name_cn": "知微", - "mention": "@知微/zhiwei", + "http_port": 5805, + "gateway": "http://localhost:8643/v1/chat/completions", + "session_id": "xmpp-zhiwei-v2", + "kanban_session_id": "xmpp-zhiwei-kanban", + "mention": "@zhiwei/@知微", + }, + "xiaoguo": { + "jid": "xiaoguo@yoin.fun", + "password": "hermes123", + "nick": "xiaoguo", + "name_cn": "小果", + "http_port": 5806, + "gateway": "http://localhost:8645/v1/chat/completions", + "session_id": "xmpp-xiaoguo", + "kanban_session_id": "xmpp-xiaoguo-kanban", + "mention": "@xiaoguo/@小果", }, } -_DEFAULT_AGENT = "mohe" -# ── Module-level config (populated by _apply_config after agent detection) ── -AGENT_NAME = "" -XMPP_JID = "" -XMPP_PASSWORD = "" -MUC_ROOM = "coregroup@conference.yoin.fun" -MUC_NICK = "" -AGENT_MENTION = "" -HTTP_PORT = 5808 -AGENT_NICK = "" -ACK_DELAY = 15 -GATEWAY_URL = "" -GATEWAY_API_KEY = "" -GATEWAY_SESSION_ID = "" -GATEWAY_DEADLINE_SECONDS = 180 -CALL_HERMES_TIMEOUT = 180 -FALLBACK_REPLY = "请稍等,我在处理..." +agent = sys.argv[sys.argv.index("--agent") + 1] if "--agent" in sys.argv else "mohe" +cfg = AGENTS.get(agent, AGENTS["mohe"]) -# ── 全局队列 ── +logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s') +GATEWAY = cfg["gateway"] +API_KEY = "hermes123" +AGENT_NICK = cfg["nick"] +AGENT_NAME = cfg["name_cn"] +AGENT_JID = cfg["jid"] +AGENT_MENTION = cfg["mention"] +SESSION_ID = cfg["session_id"] +KANBAN_SESSION_ID = cfg.get("kanban_session_id", SESSION_ID) +HTTP_PORT = cfg["http_port"] +_opener = urllib.request.build_opener(urllib.request.ProxyHandler({})) + +# ── 持久化消息队列(入站+出站) ── +# 入站:Dad发的消息,先入队再处理,处理失败不丢失 +_inbound_queue = asyncio.Queue() +# 出站:外部脚本通过HTTP桥提交的待发送消息 _outbound_queue = [] -_outbound_lock = threading.Lock() -_inbound_queue = [] -_inbound_lock = threading.Lock() -RECENT_SENT_MAX = 50 +# ── HTTP 桥(接收本地脚本的主动发送请求) ── +from http.server import HTTPServer, BaseHTTPRequestHandler +import threading, json as json_mod +_xmpp_resource = "mohe" -def _rs(p): - """Parse agent arg from sys.argv, returns agent name string.""" - agent = _DEFAULT_AGENT - skip_next = False - for i, a in enumerate(sys.argv[1:]): - if skip_next: - skip_next = False - continue - if a.startswith('--agent='): - agent = a.split('=', 1)[1] - elif a == '--agent' and i + 1 < len(sys.argv[1:]): - agent = sys.argv[i + 2] - skip_next = True - return agent +class SendHandler(BaseHTTPRequestHandler): + def do_POST(self): + length = int(self.headers.get('Content-Length', 0)) + body = self.rfile.read(length) + try: + data = json_mod.loads(body) + target = data.get('to', 'hmo@yoin.fun') + text = data.get('body', '') + msg_type = data.get('type', 'chat') + if text: + _outbound_queue.append((target, text, msg_type)) + self.send_response(200) + self.end_headers() + self.wfile.write(b'{"ok":true}') + else: + self.send_response(400) + self.end_headers() + self.wfile.write(b'{"ok":false,"error":"empty body"}') + except Exception as e: + self.send_response(500) + self.end_headers() + self.wfile.write(f'{{"ok":false,"error":"{e}"}}'.encode()) +def _run_http(): + server = HTTPServer(('127.0.0.1', HTTP_PORT), SendHandler) + server.timeout = 1.0 + while True: + try: + server.handle_request() + except: + pass -agent, is_mohe = _rs(None), None +threading.Thread(target=_run_http, daemon=True).start() +logging.info(f"🚀 {AGENT_NAME} HTTP 桥启动于 :{HTTP_PORT}") -logging.basicConfig( - level=logging.INFO, - format='%(asctime)s %(levelname)s %(message)s', - stream=sys.stdout, -) -log = logging.getLogger('xmpp_agent') - - -def _apply_config(agent_name): - """Set module-level config variables from PER_AGENT dict + env overrides.""" - global AGENT_NAME, XMPP_JID, XMPP_PASSWORD, MUC_NICK, AGENT_MENTION - global HTTP_PORT, AGENT_NICK, GATEWAY_URL, GATEWAY_API_KEY, GATEWAY_SESSION_ID - cfg = PER_AGENT.get(agent_name, PER_AGENT.get(_DEFAULT_AGENT, {})) - AGENT_NAME = agent_name - XMPP_JID = os.environ.get('XMPP_JID', cfg.get('jid', '')) - XMPP_PASSWORD = os.environ.get('XMPP_PASSWORD', cfg.get('password', '')) - MUC_NICK = os.environ.get('MUC_NICK', cfg.get('nick', agent_name)) - AGENT_MENTION = os.environ.get('AGENT_MENTION', cfg.get('mention', '')) - HTTP_PORT = int(os.environ.get('HTTP_PORT', cfg.get('http_port', 5808))) - AGENT_NICK = os.environ.get('AGENT_NICK', cfg.get('nick', agent_name)) - GATEWAY_URL = os.environ.get('GATEWAY_URL', cfg.get('gateway_url', '')) - GATEWAY_API_KEY = os.environ.get('GATEWAY_API_KEY', cfg.get('gateway_api_key', '')) - GATEWAY_SESSION_ID = os.environ.get('GATEWAY_SESSION_ID', cfg.get('session_id', '')) - - -# ── Periodic ACK task ── -class AckManager: +# ── Agent Bot ─────────────────────────────────────────────── +class AgentBot(ClientXMPP): def __init__(self): - self._active = {} - self._lock = threading.Lock() - - def start(self, session_id, to_jid, msg_body): - """Record an active LLM analysis and schedule the ACK.""" - with self._lock: - self._active[session_id] = { - 'to_jid': to_jid, - 'body': msg_body[:80], - 'started': time.time(), - 'acked': False, - } - - def ack(self, session_id): - with self._lock: - self._active.pop(session_id, None) - - def tick(self, bot): - now = time.time() - to_send = [] - with self._lock: - for sid, info in list(self._active.items()): - if not info['acked'] and now - info['started'] >= ACK_DELAY: - info['acked'] = True - to_send.append((info['to_jid'], FALLBACK_REPLY)) - for jid, msg in to_send: - try: - bot.send_message(mto=jid, mbody=msg, mtype='chat') - except Exception: - pass - - -ack_mgr = AckManager() - - -# ── Slixmpp Bot ── -class XmppAgent(slixmpp.ClientXMPP): - def __init__(self, jid, password, room, nick): - super().__init__(jid, password) - self._room = room - self._nick = nick - self._muc_joined = False + super().__init__(AGENT_JID, cfg["password"]) + self.ready = asyncio.Event() + self._call_seq = 0 self._recent_sent = [] - self._reconnecting = False # 防递归重连 - self.add_event_handler('session_start', self.on_start) + self._muc_joined = False + global _xmpp_resource + + self.add_event_handler('session_bind', self.on_bind) + self.add_event_handler('session_start', self.on_session_start) self.add_event_handler('message', self.on_msg) self.add_event_handler('disconnected', self.on_disconnect) - self.register_plugin('xep_0030') - self.register_plugin('xep_0045') - self.register_plugin('xep_0199') + self.add_event_handler('connected', self.on_connected) - async def on_start(self, event): - self.send_presence() - await self.get_roster() - try: - await self.plugin['xep_0045'].join_muc(self._room, self._nick) - self._muc_joined = True - log.info(f"{AGENT_NAME} XMPP 就绪 (已加入 {self._room})") - except Exception as e: - log.error(f"{AGENT_NAME} MUC加入失败: {e}") + def on_connected(self, event): + self.ready.clear() + + def on_bind(self, event): + global _xmpp_resource + bound_jid = str(self.boundjid) + if "/" in bound_jid: + _xmpp_resource = bound_jid.split("/", 1)[1] + logging.info(f"XMPP resource captured: {_xmpp_resource}") + logging.info(f"JID set to: {bound_jid}") + + async def on_session_start(self, event): + self.send_presence() # 发送上线presence,否则收不到私聊消息 + self.plugin['xep_0045'].join_muc('coregroup@conference.yoin.fun', AGENT_NICK) + logging.info(f"✅ {AGENT_NAME} 加入群聊 coregroup") + self.ready.set() def on_disconnect(self, event): self._muc_joined = False - log.info(f"{AGENT_NAME} XMPP 断开") - 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'): body = str(msg['body']).strip() if not body: return - log.info(f"📩 收到: from={msg['from']} type={msg['type']} body={body[:60]}") - if ('[executor]' in body and 'gateway_zhiwei' in body): - log.info(f"过滤 executor 消息: {body[:80]}...") - return sender = str(msg['from']) msg_type = msg['type'] + + # 防回声 for s in self._recent_sent: if body[:50] in s or s in body[:50]: return + + # 群聊过滤 if msg_type == 'groupchat': nick = sender.split('/')[-1] if '/' in sender else '' if nick == AGENT_NICK: @@ -208,184 +161,218 @@ class XmppAgent(slixmpp.ClientXMPP): is_for_me = any(m in body for m in ['@' + m for m in mention_list] + mention_list) if not is_for_me: return - with _inbound_lock: - _inbound_queue.append((sender, body, msg_type)) + logging.info(f"💬 群聊(#{self._call_seq}): {body[:60]}") + else: + logging.info(f"📩 老爸(#{self._call_seq}): {body[:60]}") - def mark_sent(self, body: str): - self._recent_sent.append(body[:80]) - if len(self._recent_sent) > RECENT_SENT_MAX * 2: - self._recent_sent = self._recent_sent[-RECENT_SENT_MAX:] + self._call_seq += 1 + # 入队,不直接调gateway + _inbound_queue.put_nowait({ + "seq": self._call_seq, + "content": body, + "sender": sender, + "is_group": (msg_type == 'groupchat'), + "session_id": SESSION_ID, + "ts": time.time(), + "bot_ref": self, # 保留bot引用用于回复 + }) + + async def call_hermes(self, content, sender, is_group=False, seq=None, session_id=None): + """调gateway处理消息,返回回复文本""" + msg_type = 'groupchat' if is_group else 'chat' + sid = session_id or SESSION_ID + try: + payload = json.dumps({ + "model": "hermes-agent", + "messages": [{"role": "user", "content": content}] + }).encode() + req = urllib.request.Request(GATEWAY, data=payload, method="POST") + req.add_header("Content-Type", "application/json") + req.add_header("Authorization", f"Bearer {API_KEY}") + req.add_header("X-Hermes-Session-Id", sid) + + loop = asyncio.get_event_loop() + result = await loop.run_in_executor(None, lambda: _opener.open(req, timeout=600)) + + data = json.loads(result.read()) + reply = data.get("choices", [{}])[0].get("message", {}).get("content", "") + return reply.strip() + except Exception as e: + logging.error(f"❌ {AGENT_NAME} gateway调用失败: {e}") + return None + + def send_reply(self, sender, reply, msg_type): + """通过bot发送回复""" + if not reply: + return + if reply.startswith('__SILENT__') or '__SILENT__' in reply: + logging.info(f"⏭️ {AGENT_NAME} 沉默,不发送") + return + for phrase in ['我沉默', '我不说', '不说了', '不回复', '不插嘴', '我闭嘴']: + if phrase in reply: + logging.info(f"⏭️ {AGENT_NAME} 宣布沉默,拦截") + return + + if msg_type == 'groupchat': + self.send_message(mto=sender, mbody=reply, mtype='groupchat') + else: + # 私聊:发到具体resource(Dad发消息时绑定的客户端) + self.send_message(mto=sender, mbody=reply, mtype='chat') + + sent_norm = reply[:100] + self._recent_sent.append(sent_norm) + if len(self._recent_sent) > 10: + self._recent_sent.pop(0) + logging.info(f"✅ {AGENT_NAME} 回复: {reply[:80]}") -# ── Deliver loop ── -def _deliver_loop(bot): - global _outbound_queue +# ── 入站消息处理循环(独立任务,崩了自动重启) ── +async def process_inbound(): + """从队列消费入站消息,调gateway处理,发回响应。 + 崩了会自动重启,不会丢消息(队列中的消息会等待下一轮处理)。""" + pending_tasks = set() while True: try: - items = [] - with _outbound_lock: - items, _outbound_queue = _outbound_queue[:], [] - for target, text, msg_type in items: - try: - async def _send(to, body, mtype): - bot.send_message(mto=to, mbody=body, mtype=mtype) - asyncio.run(_send(target, text, msg_type)) - bot.mark_sent(text) - log.info(f" 已发送到 {target}: {text[:80]}") - except Exception as e: - log.error(f" 发送到 {target} 失败: {e}") - time.sleep(0.3) + item = await _inbound_queue.get() + bot = item["bot_ref"] + + # 创建处理任务,保留引用防止GC + task = asyncio.create_task(handle_one(item, bot)) + pending_tasks.add(task) + task.add_done_callback(pending_tasks.discard) + + except asyncio.CancelledError: + break except Exception as e: - log.error(f"_deliver_loop error: {e}") - time.sleep(1) + logging.error(f"❌ 入站处理循环异常(已恢复): {e}") + await asyncio.sleep(1) + continue -# ── Inbound processing loop ── -def _inbound_loop(bot): - global _inbound_queue - while True: - try: - time.sleep(0.2) - with _inbound_lock: - if not _inbound_queue: - continue - sender, body, msg_type = _inbound_queue.pop(0) - log.info(f"🔄 inbound处理: {body[:40]}") - ack_mgr.start(body[:40], sender, body) - reply = call_hermes(body) - ack_mgr.ack(body[:40]) - if reply: - with _outbound_lock: - _outbound_queue.append((sender, reply, 'chat')) - except Exception as e: - log.error(f"_inbound_loop error: {e}") - time.sleep(1) - - -# ── HTTP SendHandler ── -class SendHandler(BaseHTTPRequestHandler): - def do_POST(self): - content_len = int(self.headers.get('Content-Length', 0)) - post_body = self.rfile.read(content_len) - try: - data = json.loads(post_body) - target = data.get('to', '') - text = data.get('body', '') - if not target or not text: - self.send_response(400) - self.end_headers() - self.wfile.write(b'{"error":"missing to or body"}') - return - if '修复失败' in text and 'gateway_zhiwei' in text: - self.send_response(200) - self.end_headers() - self.wfile.write(b'{"ok":true,"filtered":true}') - return - msg_type = data.get('type', 'chat') - if text: - _outbound_queue.append((target, text, msg_type)) - self.send_response(200) - self.end_headers() - self.wfile.write(b'{"ok":true}') - log.info(f"SendHandler: enqueued -> {target}: {text[:80]}") - except Exception as e: - traceback.print_exc() - self.send_response(500) - self.end_headers() - self.wfile.write(str({'error': str(e)}).encode()) - - def log_message(self, format, *args): - pass - - -def _run_http_server(): - server = HTTPServer(('127.0.0.1', HTTP_PORT), SendHandler) - log.info(f"HTTP SendHandler listening on 127.0.0.1:{HTTP_PORT}") - server.serve_forever() - - -# ── call_hermes ── -def call_hermes(content: str, session_id=None) -> str: - now_str = time.strftime("[%Y-%m-%d %H:%M %A]", time.localtime()) - timed_content = f"{now_str}\n{content}" - payload = { - 'model': 'hermes-agent', - 'messages': [ - {'role': 'user', 'content': timed_content}, - ], - 'stream': False, - } - if session_id is None: - session_id = GATEWAY_SESSION_ID - headers = { - 'Content-Type': 'application/json', - 'X-Hermes-Session-Id': session_id, - } - if GATEWAY_API_KEY: - headers['Authorization'] = f'Bearer {GATEWAY_API_KEY}' - - import urllib.request - data_bytes = json.dumps(payload).encode('utf-8') - req = urllib.request.Request(GATEWAY_URL, data=data_bytes, headers=headers, method='POST') +async def handle_one(item, bot): + """处理单条入站消息""" try: - resp = urllib.request.urlopen(req, timeout=CALL_HERMES_TIMEOUT) - resp_data = json.loads(resp.read().decode('utf-8')) - reply = '' - if 'choices' in resp_data and len(resp_data['choices']) > 0: - choice = resp_data['choices'][0] - if 'message' in choice and 'content' in choice['message']: - reply = choice['message']['content'] - elif 'delta' in choice and 'content' in choice['delta']: - reply = choice['delta']['content'] - if not reply: - reply = resp_data.get('response', '') - if not reply: - reply = str(resp_data) - return reply.strip() - except urllib.request.HTTPError as e: - err_body = e.read().decode('utf-8', errors='replace') - log.error(f"call_hermes HTTP {e.code}: {err_body[:200]}") - return '' + reply = await bot.call_hermes( + item["content"], item["sender"], + is_group=item["is_group"], + seq=item["seq"], session_id=item["session_id"] + ) + if reply: + if seq := item.get("seq"): + if seq < bot._call_seq - 5: + return # 太旧的消息,跳过 + msg_type = 'groupchat' if item["is_group"] else 'chat' + bot.send_reply(item["sender"], reply, msg_type) + else: + logging.warning(f"⚠️ 消息#{item['seq']} gateway返回空,保留在队列") + # 放回队尾等重试(最多3次) + retry_count = item.get("retry", 0) + if retry_count < 3: + item["retry"] = retry_count + 1 + item["ts"] = time.time() + _inbound_queue.put_nowait(item) except Exception as e: - log.error(f"call_hermes error: {type(e).__name__}: {e}") - return '' + logging.error(f"❌ 处理消息#{item.get('seq','?')}异常: {e}") -# ── ACK manager tick ── -def _ack_tick(bot): +# ── 出站消息发送循环 ── +async def drain_outbound(bot): + """从_outbound_queue取消息发送。独立循环,崩了自动重启。""" while True: try: - ack_mgr.tick(bot) - time.sleep(5) - except Exception: - time.sleep(5) + await asyncio.sleep(0.5) + while _outbound_queue: + target, text, msg_type = _outbound_queue.pop(0) + try: + bot.send_message(mto=target, mbody=text, mtype=msg_type) + sent_norm = text.strip()[:100] + bot._recent_sent.append(sent_norm) + if len(bot._recent_sent) > 10: + bot._recent_sent.pop(0) + logging.info(f"📤 主动发送到 {target}: {text[:60]}") + except Exception as e: + logging.error(f"❌ 主动发送失败: {e}") + _outbound_queue.insert(0, (target, text, msg_type)) # 放回队首重试 + await asyncio.sleep(3) + break + except asyncio.CancelledError: + break + except Exception as e: + logging.error(f"❌ 出站循环异常(已恢复): {e}") + await asyncio.sleep(1) + continue -# ── Main ── -def main(): - global is_mohe - agent_name = _rs(None) - _apply_config(agent_name) - is_mohe = (agent_name == 'mohe') +# ── 主入口 ─────────────────────────────────────────────── +async def main(): + retry_delay = 1 + max_delay = 60 + while True: + bot = None + inbound_task = None + outbound_task = None + try: + bot = AgentBot() + bot.register_plugin('xep_0030') + bot.register_plugin('xep_0045') + bot.register_plugin('xep_0199') - log.info(f"Starting XMPP Agent: {agent_name} (mohe={is_mohe})") - log.info(f" JID={XMPP_JID} HTTP_PORT={HTTP_PORT}") - log.info(f" GATEWAY={GATEWAY_URL}") - log.info(f" SESSION_ID={GATEWAY_SESSION_ID}") + bot.connect(host='127.0.0.1', port=5222) + await asyncio.wait_for(bot.ready.wait(), timeout=30) + logging.info(f"{AGENT_NAME} XMPP 就绪") + retry_delay = 1 - bot = XmppAgent(XMPP_JID, XMPP_PASSWORD, MUC_ROOM, MUC_NICK) - bot.connect(host='127.0.0.1', port=5222) - t_deliver = threading.Thread(target=_deliver_loop, args=(bot,), daemon=True) - t_deliver.start() - t_inbound = threading.Thread(target=_inbound_loop, args=(bot,), daemon=True) - t_inbound.start() - t_ack = threading.Thread(target=_ack_tick, args=(bot,), daemon=True) - t_ack.start() - t_http = threading.Thread(target=_run_http_server, daemon=True) - t_http.start() + # 启动独立处理循环 + inbound_task = asyncio.create_task(process_inbound()) + outbound_task = asyncio.create_task(drain_outbound(bot)) - bot.loop.run_forever() + while True: + await asyncio.sleep(15) + if not bot.is_connected(): + logging.warning("检测到断线,准备重连...") + break + # XMPP心跳检测:如果run_filters任务挂了,is_connected可能仍为True + try: + ping_ok = await asyncio.wait_for( + bot.plugin['xep_0199'].send_ping(AGENT_JID, timeout=5), + timeout=8 + ) + if not ping_ok: + logging.warning("XMPP心跳超时(XEP-0199),准备重连...") + break + except asyncio.TimeoutError: + logging.warning("XMPP心跳超时,准备重连...") + break + except Exception: + # xep_0199可能因run_filters已死而抛出异常 + logging.warning("XMPP心跳异常(run_filters可能已死),准备重连...") + break + except asyncio.TimeoutError: + logging.warning("连接超时,准备重连...") + except Exception as e: + logging.error(f"❌ 主循环错误: {e}") + finally: + # 取消任务但不丢队列 + for t in [inbound_task, outbound_task]: + if t and not t.done(): + t.cancel() + + if bot: + try: + bot.disconnect() + except: + pass + + # 等待旧session完全释放,防止两个bot抢资源 + await asyncio.sleep(2) + + logging.info(f"⏳ 等待 {retry_delay} 秒后重连...") + await asyncio.sleep(retry_delay) + retry_delay = min(retry_delay * 2, max_delay) if __name__ == '__main__': - main() + try: + asyncio.run(main()) + except KeyboardInterrupt: + pass diff --git a/deploy/profile-scripts/batch_reassess.py b/deploy/profile-scripts/batch_reassess.py index 196dab1b..4a4f52c2 100644 --- a/deploy/profile-scripts/batch_reassess.py +++ b/deploy/profile-scripts/batch_reassess.py @@ -1,369 +1,424 @@ -#!/usr/bin/env python3 -"""batch_reassess.py — 批量补全九维分析(逐只处理,间隔防限流) - -用法: python3 batch_reassess.py [--all] [--code XXXXXX] - -流程:收集最新数据 → 调LLM(gateway)写九维分析+策略 → 保存到DB -""" -import sys, json, subprocess, sqlite3, re, time -from datetime import datetime - -DB = "/home/hmo/MoFin/data/mofin.db" -GATEWAY = "http://127.0.0.1:8643/v1/chat/completions" -COOLDOWN_HOURS = 1 - -def has_llm_analysis(code): - """检查是否为LLM生成的九维分析(>500字)""" - conn = sqlite3.connect(DB) - r = conn.execute("SELECT LENGTH(full_analysis) FROM holding_strategies WHERE code=? AND status='active'", (code,)).fetchone() - conn.close() - return r and r[0] and r[0] > 500 - -def in_cooldown(code): - """冷却期检查""" - conn = sqlite3.connect(DB) - r = conn.execute("SELECT reassessed_at FROM holding_strategies WHERE code=? AND status='active'", (code,)).fetchone() - conn.close() - if not r or not r[0]: - return False - try: - last = datetime.fromisoformat(r[0]) - diff = (datetime.now() - last).total_seconds() / 3600 - return diff < COOLDOWN_HOURS - except: - return False - -def collect_data(code): - """收集最新数据""" - data = {"code": code} - - # 从DB读策略 - conn = sqlite3.connect(DB) - r = conn.execute("SELECT name, entry_low, entry_high, stop_loss, take_profit, timing_signal, action, rr_ratio, tech_snapshot, sector_context, stock_category FROM holding_strategies WHERE code=? AND status='active'", (code,)).fetchone() - if r: - data["name"] = r[0] - data["entry_low"] = r[1] or 0 - data["entry_high"] = r[2] or 0 - data["stop_loss"] = r[3] or 0 - data["take_profit"] = r[4] or 0 - data["timing_signal"] = r[5] or "" - data["action"] = r[6] or "" - data["rr_ratio"] = r[7] or 0 - data["tech_snapshot"] = r[8] or "" - data["sector_context"] = r[9] or "" - data["stock_category"] = r[10] or "" - conn.close() - - # 从腾讯API拉最新价和基本面 - prefix = "sh" if str(code).startswith(("6","9")) else "sz" - try: - r = subprocess.run(["curl", "-s", f"http://qt.gtimg.cn/q={prefix}{code}"], capture_output=True, timeout=10) - parts = r.stdout.decode("gbk", errors="ignore").split("~") - data["price"] = float(parts[3]) if len(parts) > 3 and parts[3] else 0 - data["pe"] = parts[39] if len(parts) > 39 and parts[39] else "" - data["mcap"] = parts[44] if len(parts) > 44 and parts[44] else "" - data["change_pct"] = parts[32] if len(parts) > 32 and parts[32] else "0" - except: - data["price"] = 0 - - # 大盘 - try: - conn = sqlite3.connect(DB) - mr = conn.execute("SELECT structure FROM macro_context_log ORDER BY id DESC LIMIT 1").fetchone() - if mr and mr[0]: - s = json.loads(mr[0]) - data["macro"] = s.get("description", "大盘震荡") - conn.close() - except: - data["macro"] = "大盘震荡" - - return data - -def build_prompt(data): - """构建LLM prompt,要求输出完整策略""" - cash = 321271 # 可用现金(从DB读取) - total = 952879 # 总资产 - - # 拉取资金流数据 - _flow_note = "暂无资金流数据" - try: - import sqlite3 as _sq, json as _j - _db = _sq.connect("/home/hmo/MoFin/data/mofin.db") - _fr = _db.execute("SELECT cache_json FROM capital_flow_cache ORDER BY id DESC LIMIT 1").fetchone() - if _fr and _fr[0]: - _fc = _j.loads(_fr[0]) - _stocks = _fc.get("stocks", {}) - _s = _stocks.get(data['code'], {}) - if _s and _s.get("analysis"): - _a = _s["analysis"] - _net = _a.get("net_flow", 0) - _main = _a.get("main_force", 0) - _retail = _a.get("retail_flow", 0) - _trend = _a.get("trend", "中性") - _flow_note = f"净流入{_net:.0f}万 主力{_main:.0f}万 散户{_retail:.0f}万 趋势{_trend}" - _db.close() - except: - pass - - # 拉取近期消息面 - _news_note = "暂无近期消息" - try: - import sqlite3 as _sq - _db = _sq.connect("/home/hmo/MoFin/data/mofin.db") - _nr = _db.execute( - "SELECT summary, overall_sentiment, created_at FROM signal_news " - "WHERE (code=? OR sector LIKE ?) AND overall_sentiment IN ('利好','利空') " - "ORDER BY id DESC LIMIT 3", - (data['code'], f'%{data.get("name","")[:4]}%') - ).fetchall() - if _nr: - _news_note = " | ".join([f"{r[2][:10]} {r[1]} {r[0][:40]}" for r in _nr]) - _db.close() - except: - pass - - return f"""你是一个资深A股分析师。请对{data['code']} {data.get('name','')}做一个完整的九维矩阵分析,并输出策略参数。 - -⚠️ 重要:以下9个维度不是独立分析的,你必须交叉对比后给出综合结论。 -例如:如果消息面利好但资金流在流出,说明利好可能是出货;如果基本面强但技术面破位,说明估值可能还没到底。 - -当前数据(以下数据均来自实时API,每条标注时间窗口,禁止使用模型内部训练数据): -大盘:{data.get('macro','震荡')}(当日实时) -最新价:{data.get('price',0)} 涨跌:{data.get('change_pct','0')}%(当日实时) -PE={data.get('pe','?')}(最新财报) 市值={data.get('mcap','?')}亿 -行业:{data.get('sector_context','?')}(当日实时) -技术面:{data.get('tech_snapshot','')[:300]}(MA=5/10/20/60日 支撑阻力=近20日 量价=当日+近5日趋势) -资金流:{_flow_note}(近5日累计) -消息面:{_news_note}(最近3条,自动标注抓取时间) -当前信号:{data.get('timing_signal','?')} 分类:{data.get('stock_category','?')} -原策略:{(data.get('action','') or '')[:200]} - -我的总资产={total}元,可用现金={cash}元。 - -请严格按以下格式输出: - -【交叉分析】用2-3句话说明哪些维度出现矛盾/共振,最关键的信号是什么 -① 大盘×基本面 [一句话,说明矛盾关系] -② 大盘×消息面 [一句话] -③ 大盘×技术面 [一句话] -④ 大盘×资金面 [一句话] -⑤ 行业×基本面 [一句话] -⑥ 行业×消息面 [一句话] -⑦ 行业×技术面 [一句话] -⑧ 行业×资金面 [一句话] -⑨ 个股×基本面 [一句话] -⑩ 个股×消息面 [一句话] -⑪ 个股×技术面 [一句话] -⑫ 个股×资金面 [一句话] - -【综合结论】(买入/关注/观望/卖出) -【操作建议】具体操作建议 -【买入区间】最低价~最高价 -【建议止损】数字 -【建议止盈】数字 - -【建议仓位】只有综合结论为"买入"时才输出此项。仓位计算公式: -基础仓位按RR确定:RR<1.5→不推荐,RR1.5~3→8%,RR3~5→12%,RR5+→15% -大盘偏弱×0.8,大盘偏强×1.15 -蓝筹/白马×1.2,成长×0.85,题材/短线×0.6 -最终仓位范围:5%~20% -同时考虑:现金{cash}元足够买多少手。 -输出格式:"X%(理由:一句话说明为什么这个仓位)""" -def parse_response(text): - """从LLM回复中提取策略参数""" - result = {"signal": "", "entry_low": 0, "entry_high": 0, "stop_loss": 0, "take_profit": 0, "position": ""} - - # 信号 - sl = [l for l in text.split("\n") if "综合结论" in l] - if sl: - for kw in ["买入","关注","观望","卖出"]: - if kw in sl[0]: - result["signal"] = kw - break - - # 买入区间 - zl = [l for l in text.split("\n") if "买入区间" in l] - if zl: - nums = re.findall(r'[\d.]+', zl[0]) - if len(nums) >= 2: - result["entry_low"] = float(nums[0]) - result["entry_high"] = float(nums[1]) - - # 止损 - for l in text.split("\n"): - if "建议止损" in l: - nums = re.findall(r'[\d.]+', l) - if nums: result["stop_loss"] = float(nums[0]) - - # 止盈 - for l in text.split("\n"): - if "建议止盈" in l: - nums = re.findall(r'[\d.]+', l) - if nums: result["take_profit"] = float(nums[0]) - - # 仓位:只有买入信号才需要,提取百分比数字 - result["position"] = "" - if result["signal"] == "买入": - for l in text.split("\n"): - if "建议仓位" in l: - nums = re.findall(r'[\d.]+', l) - for n in nums: - f = float(n) - if 1 <= f <= 30: # 合理的仓位范围 - result["position"] = f"{f:.0f}%" - break - break - - return result - -def save_result(code, full_text, parsed): - """保存LLM结果到DB""" - conn = sqlite3.connect(DB) - now = datetime.now().isoformat() - - updates = ["full_analysis=?", "reassessed_at=?"] - params = [full_text, now] - - if parsed["signal"]: - updates.append("timing_signal=?") - params.append(parsed["signal"]) - if parsed["entry_low"] > 0: - updates.append("entry_low=?") - params.append(parsed["entry_low"]) - if parsed["entry_high"] > 0: - updates.append("entry_high=?") - params.append(parsed["entry_high"]) - if parsed["stop_loss"] > 0: - updates.append("stop_loss=?") - params.append(parsed["stop_loss"]) - if parsed["take_profit"] > 0: - updates.append("take_profit=?") - params.append(parsed["take_profit"]) - if parsed["position"]: - updates.append("position_advice=?") - params.append(parsed["position"]) - - params.append(code) - sql = f"UPDATE holding_strategies SET {', '.join(updates)} WHERE code=? AND status='active'" - conn.execute(sql, params) - conn.commit() - - # 买入信号→推XMPP通知(在conn close前执行) - if parsed.get("signal") == "买入": - try: - _nr = conn.execute("SELECT name, price FROM holding_strategies WHERE code=? AND status='active'", (code,)).fetchone() - _name = _nr[0] if _nr else code - _p = _nr[1] if _nr else 0 - _el = parsed.get("entry_low", 0) - _eh = parsed.get("entry_high", 0) - _sl = parsed.get("stop_loss", 0) - _tp = parsed.get("take_profit", 0) - _pos = parsed.get("position", "") - _msg = f"📈 {_name}({code}) 价{_p}→12维分析生成买入信号!区间{_el}~{_eh} 损{_sl} 盈{_tp} 仓位{_pos}" - import urllib.request, json as _jj - _req = urllib.request.Request("http://127.0.0.1:5805/", - data=_jj.dumps({"body": _msg, "to": "hmo@yoin.fun", "type": "chat"}).encode(), - headers={"Content-Type": "application/json"}) - urllib.request.urlopen(_req, timeout=5) - print(f" 📨 XMPP推送成功: {_msg[:60]}") - except Exception as _e: - print(f" ⚠️ XMPP推送失败: {_e}") - - conn.close() - -def process_stock(code): - """处理单只股票""" - print(f"\n{'='*50}") - print(f"处理: {code}") - print(f"{'='*50}") - - if has_llm_analysis(code): - print(f" ⏭ 已有LLM九维分析,跳过") - return False - - if in_cooldown(code): - print(f" ⏭ 冷却期内,跳过") - return False - - print(f" 收集数据...", flush=True) - data = collect_data(code) - if not data.get("price"): - print(f" ⚠️ 无价格数据,跳过") - return False - - print(f" 调LLM生成九维分析...", flush=True) - prompt = build_prompt(data) - - try: - r = subprocess.run(["curl", "-s", "--max-time", "300", - "-H", "Content-Type: application/json", - "-H", "Authorization: Bearer hermes123", - "-d", json.dumps({"model":"deepseek-v4-flash","messages":[{"role":"user","content":prompt}],"max_tokens":2048}), - GATEWAY], capture_output=True, timeout=310) - - if r.returncode != 0: - print(f" ❌ curl失败: {r.stderr.decode()[:100]}") - return False - - resp = json.loads(r.stdout) - if "choices" not in resp: - print(f" ❌ API异常: {str(resp)[:200]}") - return False - - full_text = resp["choices"][0]["message"]["content"] - print(f" ✅ LLM返回({len(full_text)}字)", flush=True) - - parsed = parse_response(full_text) - print(f" 信号={parsed['signal']} 区间={parsed['entry_low']}~{parsed['entry_high']} 损={parsed['stop_loss']} 盈={parsed['take_profit']} 仓位={parsed['position']}") - - save_result(code, full_text, parsed) - print(f" ✅ 已保存到DB") - return True - - except subprocess.TimeoutExpired: - print(f" ❌ 超时") - return False - except Exception as e: - print(f" ❌ 错误: {e}") - return False - -def main(): - codes = [] - if "--code" in sys.argv: - idx = sys.argv.index("--code") - codes = [sys.argv[idx+1]] - else: - # 所有自选策略 - conn = sqlite3.connect(DB) - rows = conn.execute("SELECT code FROM holding_strategies WHERE status='active' AND decision_type='自选策略' ORDER BY code").fetchall() - conn.close() - codes = [r[0] for r in rows] - - print(f"待处理: {len(codes)}只") - - ok = 0 - fail = 0 - skip = 0 - for i, code in enumerate(codes): - if has_llm_analysis(code): - print(f" [{i+1}/{len(codes)}] ⏭ {code} 已有LLM分析") - skip += 1 - continue - - print(f" [{i+1}/{len(codes)}] ", end="", flush=True) - if process_stock(code): - ok += 1 - else: - fail += 1 - - # 间隔15秒(防gateway过载) - if i < len(codes) - 1: - print(f" 等待15秒...", flush=True) - time.sleep(15) - - print(f"\n{'='*50}") - print(f"完成: {ok}成功, {fail}失败, {skip}跳过") - print(f"{'='*50}") - -if __name__ == "__main__": - main() +#!/usr/bin/env python3 +"""batch_reassess.py — 批量补全12维(九维矩阵)LLM分析(逐只处理,间隔防限流) + +用法: + python3 batch_reassess.py # 所有缺分析/过期的 active 策略 + python3 batch_reassess.py --type holding # 只处理持仓策略 + python3 batch_reassess.py --type watchlist # 只处理自选策略 + python3 batch_reassess.py --type holding --today # 持仓每日刷新(今早未评过的强制重评) + python3 batch_reassess.py --code XXXXXX # 单只 + +流程:收集最新数据 → 调LLM(gateway)写12维分析+策略 → 保存到DB +""" +import sys, json, subprocess, sqlite3, re, time +from datetime import datetime + +DB = "/home/hmo/MoFin/data/mofin.db" +GATEWAY = "http://127.0.0.1:8643/v1/chat/completions" +COOLDOWN_HOURS = 1 +STALE_HOURS = 20 # 分析超过20小时视为过期,需要重评 + +def has_llm_analysis(code): + """检查是否为LLM生成的12维分析(>500字)""" + conn = sqlite3.connect(DB) + r = conn.execute("SELECT LENGTH(full_analysis) FROM holding_strategies WHERE code=? AND status='active'", (code,)).fetchone() + conn.close() + return r and r[0] and r[0] > 500 + +def in_cooldown(code): + """冷却期检查""" + conn = sqlite3.connect(DB) + r = conn.execute("SELECT reassessed_at FROM holding_strategies WHERE code=? AND status='active'", (code,)).fetchone() + conn.close() + if not r or not r[0]: + return False + try: + last = datetime.fromisoformat(r[0]) + diff = (datetime.now() - last).total_seconds() / 3600 + return diff < COOLDOWN_HOURS + except: + return False + +def analysis_stale(code, force_today=False): + """分析是否过期(>STALE_HOURS 或 force_today 时今早4点前未重评)""" + conn = sqlite3.connect(DB) + r = conn.execute("SELECT reassessed_at FROM holding_strategies WHERE code=? AND status='active'", (code,)).fetchone() + conn.close() + if not r or not r[0]: + return True + try: + last = datetime.fromisoformat(r[0]) + if force_today: + today4am = datetime.now().replace(hour=4, minute=0, second=0, microsecond=0) + return last < today4am + return (datetime.now() - last).total_seconds() / 3600 > STALE_HOURS + except: + return True + +def get_portfolio(): + """从 portfolio_summary 读实时现金/总资产(不再硬编码)""" + try: + conn = sqlite3.connect(DB) + r = conn.execute("SELECT cash, total_assets FROM portfolio_summary WHERE id=1").fetchone() + conn.close() + if r and r[1]: + return int(r[0] or 0), int(r[1]) + except Exception: + pass + return 0, 0 + +def collect_data(code): + """收集最新数据""" + data = {"code": code} + + # 从DB读策略 + conn = sqlite3.connect(DB) + r = conn.execute("SELECT name, entry_low, entry_high, stop_loss, take_profit, timing_signal, action, rr_ratio, tech_snapshot, sector_context, stock_category FROM holding_strategies WHERE code=? AND status='active'", (code,)).fetchone() + if r: + data["name"] = r[0] + data["entry_low"] = r[1] or 0 + data["entry_high"] = r[2] or 0 + data["stop_loss"] = r[3] or 0 + data["take_profit"] = r[4] or 0 + data["timing_signal"] = r[5] or "" + data["action"] = r[6] or "" + data["rr_ratio"] = r[7] or 0 + data["tech_snapshot"] = r[8] or "" + data["sector_context"] = r[9] or "" + data["stock_category"] = r[10] or "" + conn.close() + + # 从腾讯API拉最新价和基本面 + # 代码前缀:5位=港股(hk),6/9开头=沪(sh),其他=深(sz) + _c = str(code) + if len(_c) == 5: + prefix = "hk" + elif _c.startswith(("6", "9")): + prefix = "sh" + else: + prefix = "sz" + try: + r = subprocess.run(["curl", "-s", f"http://qt.gtimg.cn/q={prefix}{code}"], capture_output=True, timeout=10) + parts = r.stdout.decode("gbk", errors="ignore").split("~") + data["price"] = float(parts[3]) if len(parts) > 3 and parts[3] else 0 + data["pe"] = parts[39] if len(parts) > 39 and parts[39] else "" + data["mcap"] = parts[44] if len(parts) > 44 and parts[44] else "" + data["change_pct"] = parts[32] if len(parts) > 32 and parts[32] else "0" + except: + data["price"] = 0 + + # 大盘 + try: + conn = sqlite3.connect(DB) + mr = conn.execute("SELECT structure FROM macro_context_log ORDER BY id DESC LIMIT 1").fetchone() + if mr and mr[0]: + s = json.loads(mr[0]) + data["macro"] = s.get("description", "大盘震荡") + conn.close() + except: + data["macro"] = "大盘震荡" + + return data + +def build_prompt(data): + """构建LLM prompt,要求输出完整策略""" + cash, total = get_portfolio() # 实时从 portfolio_summary 读 + if not total: + cash, total = 241330, 929727 # 兜底(DB读不到时) + + # 拉取资金流数据 + _flow_note = "暂无资金流数据" + try: + import sqlite3 as _sq, json as _j + _db = _sq.connect("/home/hmo/MoFin/data/mofin.db") + _fr = _db.execute("SELECT cache_json FROM capital_flow_cache ORDER BY id DESC LIMIT 1").fetchone() + if _fr and _fr[0]: + _fc = _j.loads(_fr[0]) + _stocks = _fc.get("stocks", {}) + _s = _stocks.get(data['code'], {}) + if _s and _s.get("analysis"): + _a = _s["analysis"] + _net = _a.get("net_flow", 0) + _main = _a.get("main_force", 0) + _retail = _a.get("retail_flow", 0) + _trend = _a.get("trend", "中性") + _flow_note = f"净流入{_net:.0f}万 主力{_main:.0f}万 散户{_retail:.0f}万 趋势{_trend}" + _db.close() + except: + pass + + # 拉取近期消息面 + _news_note = "暂无近期消息" + try: + import sqlite3 as _sq + _db = _sq.connect("/home/hmo/MoFin/data/mofin.db") + _nr = _db.execute( + "SELECT summary, overall_sentiment, created_at FROM signal_news " + "WHERE (code=? OR sector LIKE ?) AND overall_sentiment IN ('利好','利空') " + "ORDER BY id DESC LIMIT 3", + (data['code'], f'%{data.get("name","")[:4]}%') + ).fetchall() + if _nr: + _news_note = " | ".join([f"{r[2][:10]} {r[1]} {r[0][:40]}" for r in _nr]) + _db.close() + except: + pass + + return f"""你是一个资深A股分析师。请对{data['code']} {data.get('name','')}做一个完整的九维矩阵分析,并输出策略参数。 + +⚠️ 重要:以下9个维度不是独立分析的,你必须交叉对比后给出综合结论。 +例如:如果消息面利好但资金流在流出,说明利好可能是出货;如果基本面强但技术面破位,说明估值可能还没到底。 + +当前数据(以下数据均来自实时API,每条标注时间窗口,禁止使用模型内部训练数据): +大盘:{data.get('macro','震荡')}(当日实时) +最新价:{data.get('price',0)} 涨跌:{data.get('change_pct','0')}%(当日实时) +PE={data.get('pe','?')}(最新财报) 市值={data.get('mcap','?')}亿 +行业:{data.get('sector_context','?')}(当日实时) +技术面:{data.get('tech_snapshot','')[:300]}(MA=5/10/20/60日 支撑阻力=近20日 量价=当日+近5日趋势) +资金流:{_flow_note}(近5日累计) +消息面:{_news_note}(最近3条,自动标注抓取时间) +当前信号:{data.get('timing_signal','?')} 分类:{data.get('stock_category','?')} +原策略:{(data.get('action','') or '')[:200]} + +我的总资产={total}元,可用现金={cash}元。 + +请严格按以下格式输出: + +【交叉分析】用2-3句话说明哪些维度出现矛盾/共振,最关键的信号是什么 +① 大盘×基本面 [一句话,说明矛盾关系] +② 大盘×消息面 [一句话] +③ 大盘×技术面 [一句话] +④ 大盘×资金面 [一句话] +⑤ 行业×基本面 [一句话] +⑥ 行业×消息面 [一句话] +⑦ 行业×技术面 [一句话] +⑧ 行业×资金面 [一句话] +⑨ 个股×基本面 [一句话] +⑩ 个股×消息面 [一句话] +⑪ 个股×技术面 [一句话] +⑫ 个股×资金面 [一句话] + +【综合结论】(买入/关注/观望/卖出) +【操作建议】具体操作建议 +【买入区间】最低价~最高价 +【建议止损】数字 +【建议止盈】数字 + +【建议仓位】只有综合结论为"买入"时才输出此项。仓位计算公式: +基础仓位按RR确定:RR<1.5→不推荐,RR1.5~3→8%,RR3~5→12%,RR5+→15% +大盘偏弱×0.8,大盘偏强×1.15 +蓝筹/白马×1.2,成长×0.85,题材/短线×0.6 +最终仓位范围:5%~20% +同时考虑:现金{cash}元足够买多少手。 +输出格式:"X%(理由:一句话说明为什么这个仓位)""" +def parse_response(text): + """从LLM回复中提取策略参数""" + result = {"signal": "", "entry_low": 0, "entry_high": 0, "stop_loss": 0, "take_profit": 0, "position": ""} + + # 信号 + sl = [l for l in text.split("\n") if "综合结论" in l] + if sl: + for kw in ["买入","关注","观望","卖出"]: + if kw in sl[0]: + result["signal"] = kw + break + + # 买入区间 + zl = [l for l in text.split("\n") if "买入区间" in l] + if zl: + nums = re.findall(r'[\d.]+', zl[0]) + if len(nums) >= 2: + result["entry_low"] = float(nums[0]) + result["entry_high"] = float(nums[1]) + + # 止损 + for l in text.split("\n"): + if "建议止损" in l: + nums = re.findall(r'[\d.]+', l) + if nums: result["stop_loss"] = float(nums[0]) + + # 止盈 + for l in text.split("\n"): + if "建议止盈" in l: + nums = re.findall(r'[\d.]+', l) + if nums: result["take_profit"] = float(nums[0]) + + # 仓位:只有买入信号才需要,提取百分比数字 + result["position"] = "" + if result["signal"] == "买入": + for l in text.split("\n"): + if "建议仓位" in l: + nums = re.findall(r'[\d.]+', l) + for n in nums: + f = float(n) + if 1 <= f <= 30: # 合理的仓位范围 + result["position"] = f"{f:.0f}%" + break + break + + return result + +def save_result(code, full_text, parsed): + """保存LLM结果到DB""" + conn = sqlite3.connect(DB) + now = datetime.now().isoformat() + + updates = ["full_analysis=?", "reassessed_at=?"] + params = [full_text, now] + + if parsed["signal"]: + updates.append("timing_signal=?") + params.append(parsed["signal"]) + if parsed["entry_low"] > 0: + updates.append("entry_low=?") + params.append(parsed["entry_low"]) + if parsed["entry_high"] > 0: + updates.append("entry_high=?") + params.append(parsed["entry_high"]) + if parsed["stop_loss"] > 0: + updates.append("stop_loss=?") + params.append(parsed["stop_loss"]) + if parsed["take_profit"] > 0: + updates.append("take_profit=?") + params.append(parsed["take_profit"]) + if parsed["position"]: + updates.append("position_advice=?") + params.append(parsed["position"]) + + params.append(code) + sql = f"UPDATE holding_strategies SET {', '.join(updates)} WHERE code=? AND status='active'" + conn.execute(sql, params) + conn.commit() + + # 买入信号→推XMPP通知(在conn close前执行) + if parsed.get("signal") == "买入": + try: + _nr = conn.execute("SELECT name, price FROM holding_strategies WHERE code=? AND status='active'", (code,)).fetchone() + _name = _nr[0] if _nr else code + _p = _nr[1] if _nr else 0 + _el = parsed.get("entry_low", 0) + _eh = parsed.get("entry_high", 0) + _sl = parsed.get("stop_loss", 0) + _tp = parsed.get("take_profit", 0) + _pos = parsed.get("position", "") + _msg = f"📈 {_name}({code}) 价{_p}→12维分析生成买入信号!区间{_el}~{_eh} 损{_sl} 盈{_tp} 仓位{_pos}" + import urllib.request, json as _jj + _req = urllib.request.Request("http://127.0.0.1:5805/", + data=_jj.dumps({"body": _msg, "to": "hmo@yoin.fun", "type": "chat"}).encode(), + headers={"Content-Type": "application/json"}) + urllib.request.urlopen(_req, timeout=5) + print(f" 📨 XMPP推送成功: {_msg[:60]}") + except Exception as _e: + print(f" ⚠️ XMPP推送失败: {_e}") + + conn.close() + +def process_stock(code, force_today=False): + """处理单只股票""" + print(f"\n{'='*50}") + print(f"处理: {code}") + print(f"{'='*50}") + + if in_cooldown(code): + print(f" ⏭ 冷却期内,跳过") + return False + + # 有分析且未过期 → 跳过(除非 force_today 且今早未评) + if has_llm_analysis(code) and not analysis_stale(code, force_today): + print(f" ⏭ 已有12维分析且未过期,跳过") + return False + + print(f" 收集数据...", flush=True) + data = collect_data(code) + if not data.get("price"): + print(f" ⚠️ 无价格数据,跳过") + return False + + print(f" 调LLM生成九维分析...", flush=True) + prompt = build_prompt(data) + + try: + r = subprocess.run(["curl", "-s", "--max-time", "300", + "-H", "Content-Type: application/json", + "-H", "Authorization: Bearer hermes123", + "-d", json.dumps({"model":"deepseek-v4-flash","messages":[{"role":"user","content":prompt}],"max_tokens":2048}), + GATEWAY], capture_output=True, timeout=310) + + if r.returncode != 0: + print(f" ❌ curl失败: {r.stderr.decode()[:100]}") + return False + + resp = json.loads(r.stdout) + if "choices" not in resp: + print(f" ❌ API异常: {str(resp)[:200]}") + return False + + full_text = resp["choices"][0]["message"]["content"] + print(f" ✅ LLM返回({len(full_text)}字)", flush=True) + + parsed = parse_response(full_text) + print(f" 信号={parsed['signal']} 区间={parsed['entry_low']}~{parsed['entry_high']} 损={parsed['stop_loss']} 盈={parsed['take_profit']} 仓位={parsed['position']}") + + save_result(code, full_text, parsed) + print(f" ✅ 已保存到DB") + return True + + except subprocess.TimeoutExpired: + print(f" ❌ 超时") + return False + except Exception as e: + print(f" ❌ 错误: {e}") + return False + +def main(): + codes = [] + force_today = "--today" in sys.argv + dtype = None + if "--type" in sys.argv: + idx = sys.argv.index("--type") + dtype = sys.argv[idx + 1] # holding | watchlist | all + if "--code" in sys.argv: + idx = sys.argv.index("--code") + codes = [sys.argv[idx+1]] + else: + # 按类型筛选 active 策略 + type_map = {"holding": "持仓策略", "watchlist": "自选策略"} + conn = sqlite3.connect(DB) + if dtype in type_map: + rows = conn.execute( + "SELECT code FROM holding_strategies WHERE status='active' AND decision_type=? ORDER BY code", + (type_map[dtype],)).fetchall() + else: + rows = conn.execute( + "SELECT code FROM holding_strategies WHERE status='active' ORDER BY decision_type, code").fetchall() + conn.close() + codes = [r[0] for r in rows] + + print(f"待处理: {len(codes)}只 (type={dtype or 'all'}, force_today={force_today})") + + ok = 0 + fail = 0 + skip = 0 + for i, code in enumerate(codes): + if has_llm_analysis(code) and not analysis_stale(code, force_today): + print(f" [{i+1}/{len(codes)}] ⏭ {code} 已有12维分析且未过期") + skip += 1 + continue + + print(f" [{i+1}/{len(codes)}] ", end="", flush=True) + if process_stock(code, force_today): + ok += 1 + else: + fail += 1 + + # 间隔15秒(防gateway过载) + if i < len(codes) - 1: + print(f" 等待15秒...", flush=True) + time.sleep(15) + + print(f"\n{'='*50}") + print(f"完成: {ok}成功, {fail}失败, {skip}跳过") + print(f"{'='*50}") + +if __name__ == "__main__": + main() diff --git a/deploy/profile-scripts/premarket_full_review.py b/deploy/profile-scripts/premarket_full_review.py index 1cebcd1c..e777a3f9 100644 --- a/deploy/profile-scripts/premarket_full_review.py +++ b/deploy/profile-scripts/premarket_full_review.py @@ -1,40 +1,64 @@ -#!/usr/bin/env python3 -"""premarket_full_review.py — 盘前全量重评 - -执行顺序: -1. regenerate_all() 全量技术分析重评(持仓+自选) -2. watchlist_auto_exit() 自选退出检查 -3. 输出摘要 - -调度:交易日 08:10(A股09:30开盘) -""" -import sys, os, json -sys.path.insert(0, '/home/hmo/MoFin') - -# Step 1: 全量重评 -print("=" * 50) -print("📊 盘前全量重评开始") -print("=" * 50) -from strategy_lifecycle import regenerate_all -result = regenerate_all(stdout=True) -print(f"\n重评完成: {result.get('ok',0)}/{result.get('total',0)}成功") - -# Step 2: 自选退出 -print("\n" + "=" * 50) -print("🔍 自选退出检查") -print("=" * 50) -from scripts.watchlist_auto_exit import main as auto_exit -exited = auto_exit(dry_run=False) - -# Step 3: 写入摘要供开盘简报引用 -summary = { - "premarket_at": __import__('datetime').datetime.now().isoformat(), - "reassess": result, - "auto_exit": [{"code": c, "name": n, "reason": r} for c, n, s, r in exited], - "total_kept": result.get('total', 0) - len(exited), -} -os.makedirs("/tmp/mofin_premarket", exist_ok=True) -with open("/tmp/mofin_premarket/summary.json", "w") as f: - json.dump(summary, f, ensure_ascii=False, indent=2) - -print(f"\n✅ 盘前重评完毕") +#!/usr/bin/env python3 +"""premarket_full_review.py — 盘前全量重评 + +执行顺序: +1. regenerate_all() 全量技术参数重评(持仓+自选) +2. batch_reassess.py --type holding --today 持仓12维LLM分析(每日强制刷新) +3. watchlist_auto_exit() 自选退出检查 +4. 输出摘要 + +调度:交易日 08:10(A股09:30开盘) +""" +import sys, os, json +sys.path.insert(0, '/home/hmo/MoFin') + +# Step 1: 全量技术参数重评 +print("=" * 50) +print("📊 盘前全量重评开始") +print("=" * 50) +from strategy_lifecycle import regenerate_all +result = regenerate_all(stdout=True) +print(f"\n重评完成: {result.get('ok',0)}/{result.get('total',0)}成功") + +# Step 1.5: 持仓 12 维 LLM 深度分析(每日强制,14只约8-10分钟) +print("\n" + "=" * 50) +print("🧠 持仓12维LLM分析(每日强制刷新)") +print("=" * 50) +import subprocess as _sp +analysis_result = {"ok": 0, "fail": 0, "skip": 0} +try: + r = _sp.run( + ["python3", "/home/hmo/.hermes/profiles/position-analyst/scripts/batch_reassess.py", + "--type", "holding", "--today"], + capture_output=True, text=True, timeout=3600) + print(r.stdout[-2000:] if len(r.stdout) > 2000 else r.stdout) + if r.returncode != 0 and r.stderr: + print(f"⚠️ stderr: {r.stderr[:300]}") + # 从输出尾部解析统计 + import re as _re + m = _re.search(r"完成: (\d+)成功, (\d+)失败, (\d+)跳过", r.stdout) + if m: + analysis_result = {"ok": int(m.group(1)), "fail": int(m.group(2)), "skip": int(m.group(3))} +except Exception as e: + print(f"⚠️ 12维分析步骤异常: {e}") + +# Step 2: 自选退出 +print("\n" + "=" * 50) +print("🔍 自选退出检查") +print("=" * 50) +from scripts.watchlist_auto_exit import main as auto_exit +exited = auto_exit(dry_run=False) + +# Step 3: 写入摘要供开盘简报引用 +summary = { + "premarket_at": __import__('datetime').datetime.now().isoformat(), + "reassess": result, + "llm_analysis_12d": analysis_result, + "auto_exit": [{"code": c, "name": n, "reason": r} for c, n, s, r in exited], + "total_kept": result.get('total', 0) - len(exited), +} +os.makedirs("/tmp/mofin_premarket", exist_ok=True) +with open("/tmp/mofin_premarket/summary.json", "w") as f: + json.dump(summary, f, ensure_ascii=False, indent=2) + +print(f"\n✅ 盘前重评完毕") diff --git a/deploy/profile-scripts/watchlist_12d_backfill.py b/deploy/profile-scripts/watchlist_12d_backfill.py new file mode 100644 index 00000000..b95a9ad9 --- /dev/null +++ b/deploy/profile-scripts/watchlist_12d_backfill.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 +"""watchlist_12d_backfill.py — 自选12维LLM分析补全(每日午间) + +包装 batch_reassess.py --type watchlist,供 hermes cron 调度(不支持参数)。 +调度:交易日 12:30(午间低峰,自选补全约80分钟) +""" +import subprocess, sys + +r = subprocess.run( + [sys.executable, "/home/hmo/MoFin/deploy/profile-scripts/batch_reassess.py", + "--type", "watchlist"], + timeout=7200) +sys.exit(r.returncode) \ No newline at end of file diff --git a/gateway/logs/health_check.log b/gateway/logs/health_check.log index 5de28a2e..3adc0f78 100644 --- a/gateway/logs/health_check.log +++ b/gateway/logs/health_check.log @@ -88,3 +88,42 @@ [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 +[2026-07-19 22:30:01] AUTO-HEAL actions: + {"action": "restart_zhiwei_bot", "target": "xmpp-zhiwei", "success": true, "detail": "restarted"} +[2026-07-19 22:35:01] AUTO-HEAL actions: + {"action": "restart_zhiwei_bot", "target": "xmpp-zhiwei", "success": true, "detail": "restarted"} +[2026-07-19 22:40:01] AUTO-HEAL actions: + {"action": "restart_zhiwei_bot", "target": "xmpp-zhiwei", "success": true, "detail": "restarted"} +[2026-07-19 22:45:01] AUTO-HEAL actions: + {"action": "restart_zhiwei_bot", "target": "xmpp-zhiwei", "success": true, "detail": "restarted"} +[2026-07-19 22:50:01] AUTO-HEAL actions: + {"action": "restart_zhiwei_bot", "target": "xmpp-zhiwei", "success": true, "detail": "restarted"} +[2026-07-19 23:35:01] AUTO-HEAL actions: + {"action": "restart_zhiwei_bot", "target": "xmpp-zhiwei", "success": true, "detail": "restarted"} +[2026-07-19 23:40:01] AUTO-HEAL actions: + {"action": "bot_busy_not_stuck", "inbound_age_sec": 313, "reason": "inbound < 10min ago — LLM still processing, no restart"} +[2026-07-19 23:45:01] AUTO-HEAL actions: + {"action": "restart_zhiwei_bot", "target": "xmpp-zhiwei", "success": true, "detail": "restart triggered (async)"} +[2026-07-19 23:50:01] AUTO-HEAL actions: + {"action": "bot_busy_not_stuck", "inbound_age_sec": 130, "reason": "inbound < 10min ago — LLM still processing, no restart"} +[2026-07-19 23:55:01] AUTO-HEAL actions: + {"action": "bot_busy_not_stuck", "inbound_age_sec": 446, "reason": "inbound < 10min ago — LLM still processing, no restart"} +[2026-07-20 00:00:01] AUTO-HEAL actions: + {"action": "restart_zhiwei_bot", "target": "xmpp-zhiwei", "success": true, "detail": "restart triggered (async)"} +[2026-07-20 00:05:01] AUTO-HEAL actions: + {"action": "skip_bot_restart", "reason": "bot restart cooldown 331s < 600s"} +[2026-07-20 00:10:01] AUTO-HEAL actions: + {"action": "restart_zhiwei_bot", "target": "xmpp-zhiwei", "success": true, "detail": "restart triggered (async)"} +[2026-07-20 00:15:02] AUTO-HEAL actions: + {"action": "skip_bot_restart", "reason": "bot restart cooldown 284s < 600s"} +[2026-07-20 08:35:01] AUTO-HEAL actions: + {"action": "restart_zhiwei_bot", "target": "xmpp-zhiwei", "success": true, "detail": "restart triggered (async)"} +[2026-07-20 08:40:01] AUTO-HEAL actions: + {"action": "skip_bot_restart", "reason": "bot restart cooldown 299s < 600s"} +[2026-07-20 08:45:01] AUTO-HEAL actions: + {"action": "skip_bot_restart", "reason": "bot restart cooldown 599s < 600s"} +[2026-07-20 08:45:01] Gateway DOWN → systemctl restart triggered (async, cooldown set) +[2026-07-20 08:45:01] ISSUES: 1 failed + - 知微 Gateway: +[2026-07-20 08:50:01] AUTO-HEAL actions: + {"action": "restart_zhiwei_bot", "target": "xmpp-zhiwei", "success": true, "detail": "restart triggered (async)"} diff --git a/gateway/logs/health_check_cron.log b/gateway/logs/health_check_cron.log index 2953d755..44205f59 100644 --- a/gateway/logs/health_check_cron.log +++ b/gateway/logs/health_check_cron.log @@ -100,3 +100,42 @@ NameError: name '_collect_xmpp_health' is not defined [20:40] Gateway DOWN → systemctl restart triggered [20:40] Health check: 1/4 services failed FAIL: 知微 Gateway (zhiwei_gateway) — timed out +[22:30] Auto-heal: 1 action(s) — degraded + → restart_zhiwei_bot: success=True +[22:35] Auto-heal: 1 action(s) — degraded + → restart_zhiwei_bot: success=True +[22:40] Auto-heal: 1 action(s) — degraded + → restart_zhiwei_bot: success=True +[22:45] Auto-heal: 1 action(s) — degraded + → restart_zhiwei_bot: success=True +[22:50] Auto-heal: 1 action(s) — degraded + → restart_zhiwei_bot: success=True +[23:35] Auto-heal: 1 action(s) — degraded + → restart_zhiwei_bot: success=True +[23:40] Auto-heal: 1 action(s) — degraded + → bot_busy_not_stuck: success=? +[23:45] Auto-heal: 1 action(s) — degraded + → restart_zhiwei_bot: success=True +[23:50] Auto-heal: 1 action(s) — degraded + → bot_busy_not_stuck: success=? +[23:55] Auto-heal: 1 action(s) — degraded + → bot_busy_not_stuck: success=? +[00:00] Auto-heal: 1 action(s) — degraded + → restart_zhiwei_bot: success=True +[00:05] Auto-heal: 1 action(s) — critical + → skip_bot_restart: success=? +[00:10] Auto-heal: 1 action(s) — critical + → restart_zhiwei_bot: success=True +[00:15] Auto-heal: 1 action(s) — critical + → skip_bot_restart: success=? +[08:35] Auto-heal: 1 action(s) — critical + → restart_zhiwei_bot: success=True +[08:40] Auto-heal: 1 action(s) — critical + → skip_bot_restart: success=? +[08:45] Auto-heal: 1 action(s) — ok + → skip_bot_restart: success=? +[08:45] Gateway DOWN → systemctl restart triggered +[08:45] Health check: 1/4 services failed + FAIL: 知微 Gateway (zhiwei_gateway) — +[08:50] Auto-heal: 1 action(s) — ok + → restart_zhiwei_bot: success=True diff --git a/gateway/logs/last_bot_restart.txt b/gateway/logs/last_bot_restart.txt new file mode 100644 index 00000000..66aba8ff --- /dev/null +++ b/gateway/logs/last_bot_restart.txt @@ -0,0 +1 @@ +1784508601.5944698 \ No newline at end of file diff --git a/gateway/logs/last_restart.txt b/gateway/logs/last_restart.txt index 9ac47169..d046f029 100644 --- a/gateway/logs/last_restart.txt +++ b/gateway/logs/last_restart.txt @@ -1 +1 @@ -1784464840.6959908 \ No newline at end of file +1784508301.8403428 \ No newline at end of file diff --git a/gateway/logs/xmpp_health_log.jsonl b/gateway/logs/xmpp_health_log.jsonl index 0212c7f9..b234ab61 100644 --- a/gateway/logs/xmpp_health_log.jsonl +++ b/gateway/logs/xmpp_health_log.jsonl @@ -69,3 +69,145 @@ {"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}} +{"timestamp": "2026-07-19 21:05:01", "status": "critical", "last_message_age_sec": 16259, "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": 7806, "status": "ok", "usage_percent": 79}, "weekly": {"reset_in_sec": 39291, "status": "ok", "usage_percent": 45}, "monthly": {"reset_in_sec": 2468987, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 21:10:01", "status": "critical", "last_message_age_sec": 16559, "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": 7501, "status": "ok", "usage_percent": 79}, "weekly": {"reset_in_sec": 38986, "status": "ok", "usage_percent": 45}, "monthly": {"reset_in_sec": 2468682, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 21:15:01", "status": "critical", "last_message_age_sec": 16858, "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": 7501, "status": "ok", "usage_percent": 79}, "weekly": {"reset_in_sec": 38986, "status": "ok", "usage_percent": 45}, "monthly": {"reset_in_sec": 2468682, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 21:20:02", "status": "critical", "last_message_age_sec": 17159, "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": 7189, "status": "ok", "usage_percent": 79}, "weekly": {"reset_in_sec": 38674, "status": "ok", "usage_percent": 45}, "monthly": {"reset_in_sec": 2468370, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 21:25:01", "status": "critical", "last_message_age_sec": 17459, "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": 6878, "status": "ok", "usage_percent": 79}, "weekly": {"reset_in_sec": 38363, "status": "ok", "usage_percent": 45}, "monthly": {"reset_in_sec": 2468059, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 21:30:01", "status": "critical", "last_message_age_sec": 17758, "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": "key7", "label": "key7 (Kimi)", "masked": "sk-kimi-...Rt2T", "rolling": {"reset_in_sec": 39543, "status": "ok", "usage_percent": 16.36}, "weekly": {"reset_in_sec": 626343, "status": "ok", "usage_percent": 3.27}, "monthly": {"reset_in_sec": 2543643, "status": "ok", "usage_percent": 0.69}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 21:35:01", "status": "critical", "last_message_age_sec": 18059, "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": "key7", "label": "key7 (Kimi)", "masked": "sk-kimi-...Rt2T", "rolling": {"reset_in_sec": 39543, "status": "ok", "usage_percent": 16.36}, "weekly": {"reset_in_sec": 626343, "status": "ok", "usage_percent": 3.27}, "monthly": {"reset_in_sec": 2543643, "status": "ok", "usage_percent": 0.69}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 21:40:01", "status": "critical", "last_message_age_sec": 18359, "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": "key7", "label": "key7 (Kimi)", "masked": "sk-kimi-...Rt2T", "rolling": {"reset_in_sec": 39543, "status": "ok", "usage_percent": 16.36}, "weekly": {"reset_in_sec": 626343, "status": "ok", "usage_percent": 3.27}, "monthly": {"reset_in_sec": 2543643, "status": "ok", "usage_percent": 0.69}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 21:45:01", "status": "critical", "last_message_age_sec": 18658, "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": "key7", "label": "key7 (Kimi)", "masked": "sk-kimi-...Rt2T", "rolling": {"reset_in_sec": 39543, "status": "ok", "usage_percent": 16.36}, "weekly": {"reset_in_sec": 626343, "status": "ok", "usage_percent": 3.27}, "monthly": {"reset_in_sec": 2543643, "status": "ok", "usage_percent": 0.69}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 21:50:01", "status": "critical", "last_message_age_sec": 18958, "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": "key7", "label": "key7 (Kimi)", "masked": "sk-kimi-...Rt2T", "rolling": {"reset_in_sec": 39543, "status": "ok", "usage_percent": 16.36}, "weekly": {"reset_in_sec": 626343, "status": "ok", "usage_percent": 3.27}, "monthly": {"reset_in_sec": 2543643, "status": "ok", "usage_percent": 0.69}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 21:55:02", "status": "critical", "last_message_age_sec": 19259, "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": "key7", "label": "key7 (Kimi)", "masked": "sk-kimi-...Rt2T", "rolling": {"reset_in_sec": 39543, "status": "ok", "usage_percent": 16.36}, "weekly": {"reset_in_sec": 626343, "status": "ok", "usage_percent": 3.27}, "monthly": {"reset_in_sec": 2543643, "status": "ok", "usage_percent": 0.69}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 22:00:01", "status": "critical", "last_message_age_sec": 19558, "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": "key7", "label": "key7 (Kimi)", "masked": "sk-kimi-...Rt2T", "rolling": {"reset_in_sec": 39543, "status": "ok", "usage_percent": 16.36}, "weekly": {"reset_in_sec": 626343, "status": "ok", "usage_percent": 3.27}, "monthly": {"reset_in_sec": 2543643, "status": "ok", "usage_percent": 0.69}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 22:05:01", "status": "critical", "last_message_age_sec": 19859, "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": "key7", "label": "key7 (Kimi)", "masked": "sk-kimi-...Rt2T", "rolling": {"reset_in_sec": 39543, "status": "ok", "usage_percent": 16.36}, "weekly": {"reset_in_sec": 626343, "status": "ok", "usage_percent": 3.27}, "monthly": {"reset_in_sec": 2543643, "status": "ok", "usage_percent": 0.69}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 22:10:01", "status": "critical", "last_message_age_sec": 20159, "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": "key7", "label": "key7 (Kimi)", "masked": "sk-kimi-...Rt2T", "rolling": {"reset_in_sec": 39543, "status": "ok", "usage_percent": 16.36}, "weekly": {"reset_in_sec": 626343, "status": "ok", "usage_percent": 3.27}, "monthly": {"reset_in_sec": 2543643, "status": "ok", "usage_percent": 0.69}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 22:15:01", "status": "degraded", "last_message_age_sec": 20458, "error_rate_1h": 0, "bot_activity": {"inbound": 1, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": "2026-07-19 22:12:29,344 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": "ok", "latency": "fast"}, "best_key": {"key_id": "key7", "label": "key7 (Kimi)", "masked": "sk-kimi-...Rt2T", "rolling": {"reset_in_sec": 39543, "status": "ok", "usage_percent": 16.36}, "weekly": {"reset_in_sec": 626343, "status": "ok", "usage_percent": 3.27}, "monthly": {"reset_in_sec": 2543643, "status": "ok", "usage_percent": 0.69}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 22:20:01", "status": "degraded", "last_message_age_sec": 20758, "error_rate_1h": 0, "bot_activity": {"inbound": 1, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": "2026-07-19 22:12:29,344 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": "ok", "latency": "fast"}, "best_key": {"key_id": "key7", "label": "key7 (Kimi)", "masked": "sk-kimi-...Rt2T", "rolling": {"reset_in_sec": 36349, "status": "ok", "usage_percent": 29.38}, "weekly": {"reset_in_sec": 623149, "status": "ok", "usage_percent": 5.96}, "monthly": {"reset_in_sec": 2540449, "status": "ok", "usage_percent": 1.23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 22:25:01", "status": "degraded", "last_message_age_sec": 21059, "error_rate_1h": 0, "bot_activity": {"inbound": 3, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": "2026-07-19 22:24:46,921 INFO 📩 收到: from=hmo@yoin.fun/Conversations.gJgBWWNBNE type=chat body=上面截图是持仓。现金是241330cny", "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": "key7", "label": "key7 (Kimi)", "masked": "sk-kimi-...Rt2T", "rolling": {"reset_in_sec": 36349, "status": "ok", "usage_percent": 29.38}, "weekly": {"reset_in_sec": 623149, "status": "ok", "usage_percent": 5.96}, "monthly": {"reset_in_sec": 2540449, "status": "ok", "usage_percent": 1.23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 22:30:01", "status": "degraded", "last_message_age_sec": 21359, "error_rate_1h": 0, "bot_activity": {"inbound": 3, "outbound": 0, "errors": 2, "last_error": "2026-07-19 22:29:56,134 ERROR call_hermes error: TimeoutError: timed out", "last_inbound": "2026-07-19 22:24:46,921 INFO 📩 收到: from=hmo@yoin.fun/Conversations.gJgBWWNBNE type=chat body=上面截图是持仓。现金是241330cny", "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": "key7", "label": "key7 (Kimi)", "masked": "sk-kimi-...Rt2T", "rolling": {"reset_in_sec": 36349, "status": "ok", "usage_percent": 29.38}, "weekly": {"reset_in_sec": 623149, "status": "ok", "usage_percent": 5.96}, "monthly": {"reset_in_sec": 2540449, "status": "ok", "usage_percent": 1.23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 22:35:01", "status": "degraded", "last_message_age_sec": 21658, "error_rate_1h": 0, "bot_activity": {"inbound": 3, "outbound": 0, "errors": 2, "last_error": "2026-07-19 22:29:56,134 ERROR call_hermes error: TimeoutError: timed out", "last_inbound": "2026-07-19 22:24:46,921 INFO 📩 收到: from=hmo@yoin.fun/Conversations.gJgBWWNBNE type=chat body=上面截图是持仓。现金是241330cny", "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": "key7", "label": "key7 (Kimi)", "masked": "sk-kimi-...Rt2T", "rolling": {"reset_in_sec": 36349, "status": "ok", "usage_percent": 29.38}, "weekly": {"reset_in_sec": 623149, "status": "ok", "usage_percent": 5.96}, "monthly": {"reset_in_sec": 2540449, "status": "ok", "usage_percent": 1.23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 22:40:01", "status": "degraded", "last_message_age_sec": 21960, "error_rate_1h": 0, "bot_activity": {"inbound": 3, "outbound": 0, "errors": 2, "last_error": "2026-07-19 22:29:56,134 ERROR call_hermes error: TimeoutError: timed out", "last_inbound": "2026-07-19 22:24:46,921 INFO 📩 收到: from=hmo@yoin.fun/Conversations.gJgBWWNBNE type=chat body=上面截图是持仓。现金是241330cny", "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": "key7", "label": "key7 (Kimi)", "masked": "sk-kimi-...Rt2T", "rolling": {"reset_in_sec": 36349, "status": "ok", "usage_percent": 29.38}, "weekly": {"reset_in_sec": 623149, "status": "ok", "usage_percent": 5.96}, "monthly": {"reset_in_sec": 2540449, "status": "ok", "usage_percent": 1.23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 22:45:01", "status": "degraded", "last_message_age_sec": 22259, "error_rate_1h": 0, "bot_activity": {"inbound": 2, "outbound": 0, "errors": 2, "last_error": "2026-07-19 22:29:56,134 ERROR call_hermes error: TimeoutError: timed out", "last_inbound": "2026-07-19 22:24:46,921 INFO 📩 收到: from=hmo@yoin.fun/Conversations.gJgBWWNBNE type=chat body=上面截图是持仓。现金是241330cny", "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": "key7", "label": "key7 (Kimi)", "masked": "sk-kimi-...Rt2T", "rolling": {"reset_in_sec": 36349, "status": "ok", "usage_percent": 29.38}, "weekly": {"reset_in_sec": 623149, "status": "ok", "usage_percent": 5.96}, "monthly": {"reset_in_sec": 2540449, "status": "ok", "usage_percent": 1.23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 22:50:01", "status": "degraded", "last_message_age_sec": 22558, "error_rate_1h": 0, "bot_activity": {"inbound": 2, "outbound": 0, "errors": 2, "last_error": "2026-07-19 22:29:56,134 ERROR call_hermes error: TimeoutError: timed out", "last_inbound": "2026-07-19 22:24:46,921 INFO 📩 收到: from=hmo@yoin.fun/Conversations.gJgBWWNBNE type=chat body=上面截图是持仓。现金是241330cny", "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": "key7", "label": "key7 (Kimi)", "masked": "sk-kimi-...Rt2T", "rolling": {"reset_in_sec": 36349, "status": "ok", "usage_percent": 29.38}, "weekly": {"reset_in_sec": 623149, "status": "ok", "usage_percent": 5.96}, "monthly": {"reset_in_sec": 2540449, "status": "ok", "usage_percent": 1.23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 22:55:01", "status": "degraded", "last_message_age_sec": 22859, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 2, "last_error": "2026-07-19 22:29:56,134 ERROR call_hermes error: TimeoutError: timed out", "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": "key7", "label": "key7 (Kimi)", "masked": "sk-kimi-...Rt2T", "rolling": {"reset_in_sec": 36349, "status": "ok", "usage_percent": 29.38}, "weekly": {"reset_in_sec": 623149, "status": "ok", "usage_percent": 5.96}, "monthly": {"reset_in_sec": 2540449, "status": "ok", "usage_percent": 1.23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 23:00:01", "status": "critical", "last_message_age_sec": 23159, "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": "key7", "label": "key7 (Kimi)", "masked": "sk-kimi-...Rt2T", "rolling": {"reset_in_sec": 36349, "status": "ok", "usage_percent": 29.38}, "weekly": {"reset_in_sec": 623149, "status": "ok", "usage_percent": 5.96}, "monthly": {"reset_in_sec": 2540449, "status": "ok", "usage_percent": 1.23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 23:05:01", "status": "critical", "last_message_age_sec": 23459, "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": "key7", "label": "key7 (Kimi)", "masked": "sk-kimi-...Rt2T", "rolling": {"reset_in_sec": 36349, "status": "ok", "usage_percent": 29.38}, "weekly": {"reset_in_sec": 623149, "status": "ok", "usage_percent": 5.96}, "monthly": {"reset_in_sec": 2540449, "status": "ok", "usage_percent": 1.23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 23:10:01", "status": "critical", "last_message_age_sec": 23759, "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": "key7", "label": "key7 (Kimi)", "masked": "sk-kimi-...Rt2T", "rolling": {"reset_in_sec": 33400, "status": "ok", "usage_percent": 36.79}, "weekly": {"reset_in_sec": 620200, "status": "ok", "usage_percent": 7.36}, "monthly": {"reset_in_sec": 2537500, "status": "ok", "usage_percent": 1.54}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 23:15:01", "status": "critical", "last_message_age_sec": 24059, "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": "key7", "label": "key7 (Kimi)", "masked": "sk-kimi-...Rt2T", "rolling": {"reset_in_sec": 33280, "status": "ok", "usage_percent": 37.36}, "weekly": {"reset_in_sec": 620080, "status": "ok", "usage_percent": 7.49}, "monthly": {"reset_in_sec": 2537380, "status": "ok", "usage_percent": 1.57}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 23:20:01", "status": "critical", "last_message_age_sec": 24359, "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": 17997, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 31477, "status": "ok", "usage_percent": 45}, "monthly": {"reset_in_sec": 2461173, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 23:25:01", "status": "critical", "last_message_age_sec": 24658, "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": 17682, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 31162, "status": "ok", "usage_percent": 45}, "monthly": {"reset_in_sec": 2460858, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 23:30:02", "status": "degraded", "last_message_age_sec": 24959, "error_rate_1h": 0, "bot_activity": {"inbound": 5, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": "2026-07-19 23:26:24,985 INFO 📩 收到: from=hmo@yoin.fun/Conversations.gJgBWWNBNE 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": "ok", "latency": "fast"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 17367, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 30847, "status": "ok", "usage_percent": 45}, "monthly": {"reset_in_sec": 2460543, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 23:35:01", "status": "degraded", "last_message_age_sec": 25259, "error_rate_1h": 0, "bot_activity": {"inbound": 7, "outbound": 0, "errors": 1, "last_error": "2026-07-19 23:34:07,470 ERROR call_hermes error: TimeoutError: timed out", "last_inbound": "2026-07-19 23:34:59,820 INFO 📩 收到: from=hmo@yoin.fun/gajim.02ZMOMIM type=chat body=上面的地址是windows的,246上实际的文件路径你应该知道", "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": 17045, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 30525, "status": "ok", "usage_percent": 45}, "monthly": {"reset_in_sec": 2460221, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 23:40:01", "status": "degraded", "last_message_age_sec": 25559, "error_rate_1h": 0, "bot_activity": {"inbound": 7, "outbound": 0, "errors": 1, "last_error": "2026-07-19 23:34:07,470 ERROR call_hermes error: TimeoutError: timed out", "last_inbound": "2026-07-19 23:34:59,820 INFO 📩 收到: from=hmo@yoin.fun/gajim.02ZMOMIM type=chat body=上面的地址是windows的,246上实际的文件路径你应该知道", "last_outbound": null, "last_inbound_age_sec": 302, "last_outbound_age_sec": -1}, "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": 16736, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 30216, "status": "ok", "usage_percent": 45}, "monthly": {"reset_in_sec": 2459912, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 23:45:01", "status": "degraded", "last_message_age_sec": 25858, "error_rate_1h": 0, "bot_activity": {"inbound": 7, "outbound": 0, "errors": 1, "last_error": "2026-07-19 23:34:07,470 ERROR call_hermes error: TimeoutError: timed out", "last_inbound": "2026-07-19 23:34:59,820 INFO 📩 收到: from=hmo@yoin.fun/gajim.02ZMOMIM type=chat body=上面的地址是windows的,246上实际的文件路径你应该知道", "last_outbound": null, "last_inbound_age_sec": 602, "last_outbound_age_sec": -1}, "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": 16426, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 29906, "status": "ok", "usage_percent": 45}, "monthly": {"reset_in_sec": 2459602, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 23:50:01", "status": "degraded", "last_message_age_sec": 26159, "error_rate_1h": 0, "bot_activity": {"inbound": 9, "outbound": 0, "errors": 1, "last_error": "2026-07-19 23:34:07,470 ERROR call_hermes error: TimeoutError: timed out", "last_inbound": "2026-07-19 23:48:13,262 INFO 📩 收到: from=hmo@yoin.fun/gajim.02ZMOMIM type=chat body=没有变化?那如果你总市值是对的,加上我的现金,你怎么算出来的总资产才783k?", "last_outbound": null, "last_inbound_age_sec": 108, "last_outbound_age_sec": -1}, "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": 16116, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 29596, "status": "ok", "usage_percent": 45}, "monthly": {"reset_in_sec": 2459292, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-19 23:55:01", "status": "degraded", "last_message_age_sec": 26459, "error_rate_1h": 0, "bot_activity": {"inbound": 9, "outbound": 0, "errors": 1, "last_error": "2026-07-19 23:34:07,470 ERROR call_hermes error: TimeoutError: timed out", "last_inbound": "2026-07-19 23:48:13,262 INFO 📩 收到: from=hmo@yoin.fun/gajim.02ZMOMIM type=chat body=没有变化?那如果你总市值是对的,加上我的现金,你怎么算出来的总资产才783k?", "last_outbound": null, "last_inbound_age_sec": 409, "last_outbound_age_sec": -1}, "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": 15803, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 29283, "status": "ok", "usage_percent": 45}, "monthly": {"reset_in_sec": 2458979, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 00:00:01", "status": "degraded", "last_message_age_sec": 26759, "error_rate_1h": 0, "bot_activity": {"inbound": 4, "outbound": 0, "errors": 1, "last_error": "2026-07-19 23:34:07,470 ERROR call_hermes error: TimeoutError: timed out", "last_inbound": "2026-07-19 23:48:13,262 INFO 📩 收到: from=hmo@yoin.fun/gajim.02ZMOMIM type=chat body=没有变化?那如果你总市值是对的,加上我的现金,你怎么算出来的总资产才783k?", "last_outbound": null, "last_inbound_age_sec": 708, "last_outbound_age_sec": -1}, "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": 15490, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 28970, "status": "ok", "usage_percent": 45}, "monthly": {"reset_in_sec": 2458666, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 00:05:01", "status": "critical", "last_message_age_sec": 27059, "error_rate_1h": 0, "bot_activity": {"inbound": 2, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": "2026-07-19 23:48:13,262 INFO 📩 收到: from=hmo@yoin.fun/gajim.02ZMOMIM type=chat body=没有变化?那如果你总市值是对的,加上我的现金,你怎么算出来的总资产才783k?", "last_outbound": null, "last_inbound_age_sec": 1008, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "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": 15179, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 28659, "status": "ok", "usage_percent": 45}, "monthly": {"reset_in_sec": 2458355, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 00:10:01", "status": "critical", "last_message_age_sec": 27359, "error_rate_1h": 0, "bot_activity": {"inbound": 2, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": "2026-07-19 23:48:13,262 INFO 📩 收到: from=hmo@yoin.fun/gajim.02ZMOMIM type=chat body=没有变化?那如果你总市值是对的,加上我的现金,你怎么算出来的总资产才783k?", "last_outbound": null, "last_inbound_age_sec": 1308, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "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": 14867, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 28347, "status": "ok", "usage_percent": 45}, "monthly": {"reset_in_sec": 2458043, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 00:15:02", "status": "critical", "last_message_age_sec": 27662, "error_rate_1h": 0, "bot_activity": {"inbound": 2, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": "2026-07-19 23:48:13,262 INFO 📩 收到: from=hmo@yoin.fun/gajim.02ZMOMIM type=chat body=没有变化?那如果你总市值是对的,加上我的现金,你怎么算出来的总资产才783k?", "last_outbound": null, "last_inbound_age_sec": 1612, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "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": 14552, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 28032, "status": "ok", "usage_percent": 45}, "monthly": {"reset_in_sec": 2457728, "status": "ok", "usage_percent": 22}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 00:20:01", "status": "critical", "last_message_age_sec": 27959, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "4.3s", "age_sec": 1, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 14239, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 27719, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2457415, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 00:25:01", "status": "critical", "last_message_age_sec": 28258, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "11.0s", "age_sec": 114, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 13930, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 27410, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2457106, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 00:30:01", "status": "critical", "last_message_age_sec": 28559, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "11.0s", "age_sec": 415, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 13620, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 27100, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2456796, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 00:35:01", "status": "critical", "last_message_age_sec": 28858, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "11.0s", "age_sec": 714, "source": "agent.log"}, "best_key": {"key_id": "key7", "label": "key7 (Kimi)", "masked": "sk-kimi-...Rt2T", "rolling": {"reset_in_sec": 46491, "status": "ok", "usage_percent": 2.38}, "weekly": {"reset_in_sec": 615291, "status": "ok", "usage_percent": 12.75}, "monthly": {"reset_in_sec": 2532591, "status": "ok", "usage_percent": 2.67}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 00:40:01", "status": "critical", "last_message_age_sec": 29159, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "11.0s", "age_sec": 1015, "source": "agent.log"}, "best_key": {"key_id": "key7", "label": "key7 (Kimi)", "masked": "sk-kimi-...Rt2T", "rolling": {"reset_in_sec": 46491, "status": "ok", "usage_percent": 2.38}, "weekly": {"reset_in_sec": 615291, "status": "ok", "usage_percent": 12.75}, "monthly": {"reset_in_sec": 2532591, "status": "ok", "usage_percent": 2.67}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 00:45:01", "status": "critical", "last_message_age_sec": 29459, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "11.0s", "age_sec": 1315, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 12689, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 26169, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2455865, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 00:50:01", "status": "critical", "last_message_age_sec": 29759, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "11.0s", "age_sec": 1614, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 12381, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 25861, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2455557, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 00:55:01", "status": "critical", "last_message_age_sec": 30059, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "unknown", "error": "no LLM calls in log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 12068, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 25548, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2455244, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 01:00:01", "status": "critical", "last_message_age_sec": 30359, "error_rate_1h": 0, "bot_activity": {"error": "journalctl failed"}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": false}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "4.0s", "age_sec": 46, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 11753, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 25233, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2454929, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 01:05:01", "status": "critical", "last_message_age_sec": 30659, "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": "4.0s", "age_sec": 347, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 11443, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 24923, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2454619, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 01:10:01", "status": "critical", "last_message_age_sec": 30959, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "4.0s", "age_sec": 647, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 11132, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 24612, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2454308, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 01:15:01", "status": "critical", "last_message_age_sec": 31259, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "7.1s", "age_sec": 272, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "3.1s", "age_sec": 252, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 10822, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 24302, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2453998, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 01:20:01", "status": "critical", "last_message_age_sec": 31559, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "7.1s", "age_sec": 571, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "3.1s", "age_sec": 551, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 10822, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 24302, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2453998, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 01:25:01", "status": "critical", "last_message_age_sec": 31859, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.5s", "age_sec": 221, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.4s", "age_sec": 242, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 10512, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 23992, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2453688, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 01:30:01", "status": "critical", "last_message_age_sec": 32159, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.5s", "age_sec": 521, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.4s", "age_sec": 542, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 10202, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 23682, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2453378, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 01:35:01", "status": "critical", "last_message_age_sec": 32459, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "6.1s", "age_sec": 139, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "5.4s", "age_sec": 14, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 9894, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 23374, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2453070, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 01:40:01", "status": "critical", "last_message_age_sec": 32759, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "6.1s", "age_sec": 439, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "12.5s", "age_sec": 9, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 9584, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 23064, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2452760, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 01:45:01", "status": "critical", "last_message_age_sec": 33059, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.2s", "age_sec": 58, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.8s", "age_sec": 203, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 9276, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 22756, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2452452, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 01:50:01", "status": "critical", "last_message_age_sec": 33359, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.2s", "age_sec": 358, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.8s", "age_sec": 503, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 8967, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 22447, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2452143, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 01:55:01", "status": "critical", "last_message_age_sec": 33659, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.2s", "age_sec": 658, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "3.0s", "age_sec": 275, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 8659, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 22139, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2451835, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 02:00:01", "status": "critical", "last_message_age_sec": 33960, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.6s", "age_sec": 270, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "3.0s", "age_sec": 576, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 8351, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 21831, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2451527, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 02:05:01", "status": "ok", "last_message_age_sec": 58, "error_rate_1h": 0.0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.6s", "age_sec": 570, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "49.1s", "age_sec": 76, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 8042, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 21522, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2451218, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 02:10:01", "status": "ok", "last_message_age_sec": 358, "error_rate_1h": 0.0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.0s", "age_sec": 208, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "49.1s", "age_sec": 376, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 7735, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 21215, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2450911, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 02:15:01", "status": "critical", "last_message_age_sec": 657, "error_rate_1h": 0.0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.0s", "age_sec": 507, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "3.0s", "age_sec": 239, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 7426, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 20906, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2450602, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 02:20:01", "status": "critical", "last_message_age_sec": 957, "error_rate_1h": 0.0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "2.8s", "age_sec": 154, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "3.0s", "age_sec": 540, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 7116, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 20596, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2450292, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 02:25:01", "status": "critical", "last_message_age_sec": 1258, "error_rate_1h": 0.0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "2.8s", "age_sec": 454, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.7s", "age_sec": 244, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 6809, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 20289, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2449985, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 02:30:01", "status": "critical", "last_message_age_sec": 1557, "error_rate_1h": 0.0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "4.6s", "age_sec": 81, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.7s", "age_sec": 544, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 6502, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 19982, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2449678, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 02:35:01", "status": "critical", "last_message_age_sec": 1856, "error_rate_1h": 0.0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "4.6s", "age_sec": 380, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "50.5s", "age_sec": 129, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 6195, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 19675, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2449371, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 02:40:01", "status": "critical", "last_message_age_sec": 2157, "error_rate_1h": 0.0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "4.5s", "age_sec": 25, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "50.5s", "age_sec": 429, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 5889, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 19369, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2449065, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 02:45:01", "status": "critical", "last_message_age_sec": 2456, "error_rate_1h": 0.0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "4.5s", "age_sec": 325, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.8s", "age_sec": 239, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 5582, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 19062, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2448758, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 02:50:02", "status": "critical", "last_message_age_sec": 2757, "error_rate_1h": 0.0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "4.5s", "age_sec": 626, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.8s", "age_sec": 540, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 5275, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 18755, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2448451, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 02:55:01", "status": "critical", "last_message_age_sec": 3057, "error_rate_1h": 0.0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "5.2s", "age_sec": 254, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.7s", "age_sec": 210, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 4968, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 18448, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2448144, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 03:00:01", "status": "critical", "last_message_age_sec": 3356, "error_rate_1h": 0.0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "5.2s", "age_sec": 554, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.7s", "age_sec": 510, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 4661, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 18141, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2447837, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 03:05:01", "status": "critical", "last_message_age_sec": 3657, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.2s", "age_sec": 200, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "39.6s", "age_sec": 4, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 4354, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 17834, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2447530, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 03:10:02", "status": "critical", "last_message_age_sec": 3957, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.2s", "age_sec": 501, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "20.8s", "age_sec": 241, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 4047, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 17527, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2447223, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 03:15:01", "status": "critical", "last_message_age_sec": 4257, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "5.0s", "age_sec": 137, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.5s", "age_sec": 284, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 3740, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 17220, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2446916, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 03:20:01", "status": "critical", "last_message_age_sec": 4557, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "5.0s", "age_sec": 437, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.5s", "age_sec": 584, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 3433, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 16913, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2446609, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 03:25:01", "status": "critical", "last_message_age_sec": 4856, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.4s", "age_sec": 76, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.5s", "age_sec": 282, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 3127, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 16607, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2446303, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 03:30:01", "status": "critical", "last_message_age_sec": 5157, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.4s", "age_sec": 376, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.5s", "age_sec": 582, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 2820, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 16300, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2445996, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 03:35:01", "status": "critical", "last_message_age_sec": 5457, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.6s", "age_sec": 13, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "17.0s", "age_sec": 241, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 2514, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 15994, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2445690, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 03:40:01", "status": "critical", "last_message_age_sec": 5757, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.6s", "age_sec": 314, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "17.0s", "age_sec": 542, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 2207, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 15687, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2445383, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 03:45:02", "status": "critical", "last_message_age_sec": 6057, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.6s", "age_sec": 614, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "156.5s", "age_sec": 22, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 1900, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 15380, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2445076, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 03:50:01", "status": "critical", "last_message_age_sec": 6357, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "4.4s", "age_sec": 245, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "156.5s", "age_sec": 321, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 1594, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 15074, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2444770, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 03:55:01", "status": "critical", "last_message_age_sec": 6657, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "4.4s", "age_sec": 545, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "156.5s", "age_sec": 621, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 1287, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 14767, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2444463, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 04:00:02", "status": "critical", "last_message_age_sec": 6957, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "4.1s", "age_sec": 193, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "159.8s", "age_sec": 94, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 979, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 14459, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2444155, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 04:05:01", "status": "critical", "last_message_age_sec": 7257, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "4.1s", "age_sec": 492, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "159.8s", "age_sec": 393, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 672, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 14152, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2443848, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 04:10:01", "status": "critical", "last_message_age_sec": 7557, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.2s", "age_sec": 132, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "130.5s", "age_sec": 27, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 366, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 13846, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2443542, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 04:15:02", "status": "critical", "last_message_age_sec": 7857, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.2s", "age_sec": 433, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "29.3s", "age_sec": 145, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 59, "status": "ok", "usage_percent": 1}, "weekly": {"reset_in_sec": 13539, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2443235, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 04:20:01", "status": "critical", "last_message_age_sec": 8157, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.1s", "age_sec": 73, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "29.3s", "age_sec": 445, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 17955, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 13232, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2442928, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 04:25:01", "status": "critical", "last_message_age_sec": 8457, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.1s", "age_sec": 372, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.8s", "age_sec": 265, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 17649, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 12926, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2442622, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 04:30:01", "status": "critical", "last_message_age_sec": 8757, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.2s", "age_sec": 9, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.8s", "age_sec": 566, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 17342, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 12619, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2442315, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 04:35:01", "status": "critical", "last_message_age_sec": 9057, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.2s", "age_sec": 308, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "7.5s", "age_sec": 249, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 17035, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 12312, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2442008, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 04:40:02", "status": "critical", "last_message_age_sec": 9357, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.2s", "age_sec": 609, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "7.5s", "age_sec": 550, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 16728, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 12005, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2441701, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 04:45:01", "status": "critical", "last_message_age_sec": 9657, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.6s", "age_sec": 238, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.9s", "age_sec": 260, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 16728, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 12005, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2441701, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 04:50:01", "status": "critical", "last_message_age_sec": 9956, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.6s", "age_sec": 538, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.9s", "age_sec": 560, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 16422, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 11699, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2441395, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 04:55:01", "status": "critical", "last_message_age_sec": 10257, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.1s", "age_sec": 187, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.9s", "age_sec": 255, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 16115, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 11392, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2441088, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 05:00:01", "status": "critical", "last_message_age_sec": 10556, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.1s", "age_sec": 487, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.9s", "age_sec": 555, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 15808, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 11085, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2440781, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 05:05:01", "status": "critical", "last_message_age_sec": 10857, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "5.8s", "age_sec": 123, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "189.9s", "age_sec": 72, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 15502, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 10779, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2440475, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 05:10:01", "status": "critical", "last_message_age_sec": 11157, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "5.8s", "age_sec": 422, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "82.6s", "age_sec": 186, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 15195, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 10472, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2440168, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 05:15:01", "status": "critical", "last_message_age_sec": 11456, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.1s", "age_sec": 65, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.7s", "age_sec": 252, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 14888, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 10165, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2439861, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 05:20:01", "status": "critical", "last_message_age_sec": 11757, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.1s", "age_sec": 365, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.7s", "age_sec": 552, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 14581, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 9858, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2439554, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 05:25:01", "status": "critical", "last_message_age_sec": 12056, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.9s", "age_sec": 1, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.9s", "age_sec": 246, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 14274, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 9551, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2439247, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 05:30:01", "status": "critical", "last_message_age_sec": 12358, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.9s", "age_sec": 303, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.9s", "age_sec": 548, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 13967, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 9244, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2438940, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 05:35:01", "status": "critical", "last_message_age_sec": 12657, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.9s", "age_sec": 601, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "9.5s", "age_sec": 225, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 13660, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 8937, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2438633, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 05:40:01", "status": "critical", "last_message_age_sec": 12956, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.5s", "age_sec": 234, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "9.5s", "age_sec": 525, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 13354, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 8631, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2438327, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 05:45:02", "status": "critical", "last_message_age_sec": 13257, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.5s", "age_sec": 535, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.5s", "age_sec": 243, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 13047, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 8324, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2438020, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 05:50:01", "status": "critical", "last_message_age_sec": 13557, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.5s", "age_sec": 175, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.5s", "age_sec": 542, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 12740, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 8017, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2437713, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 05:55:01", "status": "critical", "last_message_age_sec": 13856, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.5s", "age_sec": 475, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.8s", "age_sec": 238, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 12434, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 7711, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2437407, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 06:00:01", "status": "critical", "last_message_age_sec": 14157, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.2s", "age_sec": 120, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.8s", "age_sec": 538, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 12127, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 7404, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2437100, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 06:05:01", "status": "critical", "last_message_age_sec": 14456, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.2s", "age_sec": 420, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "92.9s", "age_sec": 5, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 11820, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 7097, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2436793, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 06:10:01", "status": "critical", "last_message_age_sec": 14757, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "8.1s", "age_sec": 52, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "60.4s", "age_sec": 243, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 11513, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 6790, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2436486, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 06:15:01", "status": "critical", "last_message_age_sec": 15056, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "8.1s", "age_sec": 352, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "6.4s", "age_sec": 223, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 11206, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 6483, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2436179, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 06:20:02", "status": "critical", "last_message_age_sec": 15357, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.0s", "age_sec": 4, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "6.4s", "age_sec": 524, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 10899, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 6176, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2435872, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 06:25:01", "status": "critical", "last_message_age_sec": 15657, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.4s", "age_sec": 296, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.6s", "age_sec": 231, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 10592, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 5869, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2435565, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 06:30:01", "status": "critical", "last_message_age_sec": 15957, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.4s", "age_sec": 596, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.6s", "age_sec": 531, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 10286, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 5563, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2435259, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 06:35:01", "status": "critical", "last_message_age_sec": 16256, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "2.9s", "age_sec": 231, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "32.0s", "age_sec": 218, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 9979, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 5256, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2434952, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 06:40:01", "status": "critical", "last_message_age_sec": 16557, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "2.9s", "age_sec": 531, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "32.0s", "age_sec": 518, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 9672, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 4949, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2434645, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 06:45:01", "status": "critical", "last_message_age_sec": 16856, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.3s", "age_sec": 173, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.8s", "age_sec": 282, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 9366, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 4643, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2434339, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 06:50:01", "status": "critical", "last_message_age_sec": 17157, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.3s", "age_sec": 473, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.8s", "age_sec": 582, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 9059, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 4336, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2434032, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 06:55:01", "status": "critical", "last_message_age_sec": 17457, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "4.0s", "age_sec": 111, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.7s", "age_sec": 286, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 8752, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 4029, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2433725, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 07:00:01", "status": "critical", "last_message_age_sec": 17757, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "4.0s", "age_sec": 411, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.7s", "age_sec": 586, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 8446, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 3723, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2433419, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 07:05:02", "status": "critical", "last_message_age_sec": 18057, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "4.1s", "age_sec": 54, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "138.3s", "age_sec": 35, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 8139, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 3416, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2433112, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 07:10:01", "status": "critical", "last_message_age_sec": 18357, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "4.1s", "age_sec": 353, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "92.0s", "age_sec": 242, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 7833, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 3110, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2432806, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 07:15:01", "status": "critical", "last_message_age_sec": 18656, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "4.1s", "age_sec": 653, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "17.1s", "age_sec": 249, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 7526, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 2803, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2432499, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 07:20:01", "status": "critical", "last_message_age_sec": 18957, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.1s", "age_sec": 292, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "17.1s", "age_sec": 550, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 7219, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 2496, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2432192, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 07:25:01", "status": "critical", "last_message_age_sec": 19257, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.1s", "age_sec": 591, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.9s", "age_sec": 279, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 6913, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 2190, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2431886, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 07:30:02", "status": "critical", "last_message_age_sec": 19557, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.5s", "age_sec": 231, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.9s", "age_sec": 580, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 6606, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 1883, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2431579, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 07:35:01", "status": "critical", "last_message_age_sec": 19857, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.5s", "age_sec": 530, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "64.9s", "age_sec": 95, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 6300, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 1577, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2431273, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 07:40:01", "status": "critical", "last_message_age_sec": 20157, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.0s", "age_sec": 168, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "64.9s", "age_sec": 396, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 5993, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 1270, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2430966, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 07:45:01", "status": "critical", "last_message_age_sec": 20457, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.0s", "age_sec": 467, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.6s", "age_sec": 266, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 5686, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 963, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2430659, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 07:50:01", "status": "critical", "last_message_age_sec": 20757, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "2.9s", "age_sec": 105, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "2.6s", "age_sec": 566, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 5379, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 656, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2430352, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 07:55:01", "status": "critical", "last_message_age_sec": 21056, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "2.9s", "age_sec": 405, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "6.2s", "age_sec": 261, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 5073, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 350, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2430046, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 08:00:01", "status": "critical", "last_message_age_sec": 21357, "error_rate_1h": 0, "bot_activity": {"inbound": 0, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": null, "last_outbound": null, "last_inbound_age_sec": -1, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "3.0s", "age_sec": 44, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "6.2s", "age_sec": 562, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 4766, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 43, "status": "ok", "usage_percent": 46}, "monthly": {"reset_in_sec": 2429739, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 08:05:01", "status": "critical", "last_message_age_sec": 21657, "error_rate_1h": 0, "bot_activity": {"inbound": 1, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": "2026-07-20 08:01:40,718 INFO 📩 收到: from=hmo@yoin.fun/Conversations.gJgBWWNBNE type=chat body=一会就要开盘了。你检查一下系统是不是正常。", "last_outbound": null, "last_inbound_age_sec": 201, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "6.8s", "age_sec": 12, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "214.2s", "age_sec": 60, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 4460, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 604537, "status": "ok", "usage_percent": 0}, "monthly": {"reset_in_sec": 2429433, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 08:10:01", "status": "critical", "last_message_age_sec": 21957, "error_rate_1h": 0, "bot_activity": {"inbound": 3, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": "2026-07-20 08:07:38,478 INFO 📩 收到: from=hmo@yoin.fun/Conversations.gJgBWWNBNE type=chat body=我说的是持仓和自选的股,每个交易日开盘前都应重评一次", "last_outbound": null, "last_inbound_age_sec": 143, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "5.9s", "age_sec": 61, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "211.9s", "age_sec": 151, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 4153, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 604230, "status": "ok", "usage_percent": 0}, "monthly": {"reset_in_sec": 2429126, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 08:15:01", "status": "critical", "last_message_age_sec": 22257, "error_rate_1h": 0, "bot_activity": {"inbound": 5, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": "2026-07-20 08:12:32,867 INFO 📩 收到: from=hmo@yoin.fun/Conversations.gJgBWWNBNE type=chat body=> [08:10] [08:08] 盘前全量重评已完成,39/39 全部成功。**结果是:持仓14只全部持有状态,无买入", "last_outbound": null, "last_inbound_age_sec": 149, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "5.3s", "age_sec": 2, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "121.2s", "age_sec": 132, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 3846, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 603923, "status": "ok", "usage_percent": 0}, "monthly": {"reset_in_sec": 2428819, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 08:20:01", "status": "critical", "last_message_age_sec": 22557, "error_rate_1h": 0, "bot_activity": {"inbound": 5, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": "2026-07-20 08:12:32,867 INFO 📩 收到: from=hmo@yoin.fun/Conversations.gJgBWWNBNE type=chat body=> [08:10] [08:08] 盘前全量重评已完成,39/39 全部成功。**结果是:持仓14只全部持有状态,无买入", "last_outbound": null, "last_inbound_age_sec": 449, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "11.3s", "age_sec": 204, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "121.2s", "age_sec": 432, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 3540, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 603617, "status": "ok", "usage_percent": 0}, "monthly": {"reset_in_sec": 2428513, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 08:25:02", "status": "critical", "last_message_age_sec": 22857, "error_rate_1h": 0, "bot_activity": {"inbound": 6, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": "2026-07-20 08:22:14,972 INFO 📩 收到: from=hmo@yoin.fun/gajim.02ZMOMIM type=chat body=我要的不是你手动的缝缝补补,我要的是系统能全功能就位。这样,我让笑笑来做这个改动。你这里别动", "last_outbound": null, "last_inbound_age_sec": 168, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "5.7s", "age_sec": 161, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "34.0s", "age_sec": 189, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 3233, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 603310, "status": "ok", "usage_percent": 0}, "monthly": {"reset_in_sec": 2428206, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 08:30:02", "status": "critical", "last_message_age_sec": 23157, "error_rate_1h": 0, "bot_activity": {"inbound": 6, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": "2026-07-20 08:22:14,972 INFO 📩 收到: from=hmo@yoin.fun/gajim.02ZMOMIM type=chat body=我要的不是你手动的缝缝补补,我要的是系统能全功能就位。这样,我让笑笑来做这个改动。你这里别动", "last_outbound": null, "last_inbound_age_sec": 468, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "5.7s", "age_sec": 461, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "34.0s", "age_sec": 489, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 2926, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 603003, "status": "ok", "usage_percent": 0}, "monthly": {"reset_in_sec": 2427899, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 08:35:01", "status": "critical", "last_message_age_sec": 23457, "error_rate_1h": 0, "bot_activity": {"inbound": 5, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": "2026-07-20 08:22:14,972 INFO 📩 收到: from=hmo@yoin.fun/gajim.02ZMOMIM type=chat body=我要的不是你手动的缝缝补补,我要的是系统能全功能就位。这样,我让笑笑来做这个改动。你这里别动", "last_outbound": null, "last_inbound_age_sec": 768, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "109.6s", "age_sec": 58, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "92.5s", "age_sec": 65, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 2926, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 603003, "status": "ok", "usage_percent": 0}, "monthly": {"reset_in_sec": 2427899, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 08:40:01", "status": "critical", "last_message_age_sec": 23757, "error_rate_1h": 0, "bot_activity": {"inbound": 3, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": "2026-07-20 08:22:14,972 INFO 📩 收到: from=hmo@yoin.fun/gajim.02ZMOMIM type=chat body=我要的不是你手动的缝缝补补,我要的是系统能全功能就位。这样,我让笑笑来做这个改动。你这里别动", "last_outbound": null, "last_inbound_age_sec": 1067, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "40.0s", "age_sec": 6, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "49.8s", "age_sec": 13, "source": "agent.log"}, "best_key": {"key_id": "key6", "label": "key6 (ycdennismo@163.com)", "masked": "sk-oF1WG...hCS3", "rolling": {"reset_in_sec": 2620, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 602697, "status": "ok", "usage_percent": 0}, "monthly": {"reset_in_sec": 2427593, "status": "ok", "usage_percent": 23}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 08:45:01", "status": "ok", "last_message_age_sec": 58, "error_rate_1h": 0.0, "bot_activity": {"inbound": 1, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": "2026-07-20 08:22:14,972 INFO 📩 收到: from=hmo@yoin.fun/gajim.02ZMOMIM type=chat body=我要的不是你手动的缝缝补补,我要的是系统能全功能就位。这样,我让笑笑来做这个改动。你这里别动", "last_outbound": null, "last_inbound_age_sec": 1367, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": false}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "11.4s", "age_sec": 4, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "19.1s", "age_sec": 142, "source": "agent.log"}, "best_key": {"key_id": "key2", "label": "key2 (Google, hua65111@gmail.com)", "masked": "sk-5miR8...tGTB", "rolling": {"reset_in_sec": 18000, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 602394, "status": "ok", "usage_percent": 0}, "monthly": {"reset_in_sec": 1765184, "status": "ok", "usage_percent": 50}, "session_expired": false, "issues": [], "total_keys": 7}} +{"timestamp": "2026-07-20 08:50:01", "status": "ok", "last_message_age_sec": 357, "error_rate_1h": 0.0, "bot_activity": {"inbound": 1, "outbound": 0, "errors": 0, "last_error": null, "last_inbound": "2026-07-20 08:22:14,972 INFO 📩 收到: from=hmo@yoin.fun/gajim.02ZMOMIM type=chat body=我要的不是你手动的缝缝补补,我要的是系统能全功能就位。这样,我让笑笑来做这个改动。你这里别动", "last_outbound": null, "last_inbound_age_sec": 1667, "last_outbound_age_sec": -1, "last_error_age_sec": -1, "last_error_resolved": false}, "gateways": {"zhiwei": {"port": 8643, "alive": true}, "mohe": {"port": 8642, "alive": true}, "xiaoguo": {"port": 8645, "alive": true}}, "llm_provider": {"status": "ok", "latency": "20.8s", "age_sec": 13, "source": "agent.log"}, "llm_provider_default": {"status": "ok", "latency": "19.1s", "age_sec": 442, "source": "agent.log"}, "best_key": {"key_id": "key2", "label": "key2 (Google, hua65111@gmail.com)", "masked": "sk-5miR8...tGTB", "rolling": {"reset_in_sec": 18000, "status": "ok", "usage_percent": 0}, "weekly": {"reset_in_sec": 602088, "status": "ok", "usage_percent": 0}, "monthly": {"reset_in_sec": 1764878, "status": "ok", "usage_percent": 50}, "session_expired": false, "issues": [], "total_keys": 7}} diff --git a/gateway/logs/xmpp_messages.jsonl b/gateway/logs/xmpp_messages.jsonl index 3c2174c2..164144c8 100644 --- a/gateway/logs/xmpp_messages.jsonl +++ b/gateway/logs/xmpp_messages.jsonl @@ -1,3 +1,6 @@ {"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} +{"timestamp": "2026-07-20 02:04:04", "direction": "out", "from": "zhiwei@yoin.fun", "to": "hmo@yoin.fun", "body_preview": "没有 accepted 提案,正常工作。", "status": "ok", "error": null, "latency_ms": 2424, "epoch": 1784484244.5065012} +{"timestamp": "2026-07-20 08:44:02", "direction": "out", "from": "zhiwei@yoin.fun", "to": "hmo@yoin.fun", "body_preview": "Now I have everything. Here's the report: --- 【⚡ 重点推荐操作】 ① 中芯国际(688981) 现价139.9 |仓位6.4%|止损131.6 技术面:弱,阻力155.5/支撑139(周五低) 操作:开盘若跳空破135减半仓,科创50续跌-7%大概率触发止损 ② 腾讯控股(00700) 现价461.6 |仓位6.1%|entry区间450-4", "status": "ok", "error": null, "latency_ms": 1185, "epoch": 1784508242.9271429} +{"timestamp": "2026-07-20 08:44:03", "direction": "out", "from": "zhiwei@yoin.fun", "to": "hmo@yoin.fun", "body_preview": "【⚠️宏观高风险】2026-07-20 08:32 — 全面系统性大跌进行中 整体风险:HIGH(90%置信度)— 确认全面系统性大跌 核心判断: 周一A股延续上周五暴跌,所有8个指数全数下跌超过2%。催化剂是美伊冲突进入第9晚且升级至打击核电站+能源供给中断风险。但存在有序吸收信号(黄金反跌/美股期货上涨/国家队增持),这不是纯恐慌场景。 --- 催化剂: 美军连续第九晚袭击伊朗,已打击", "status": "ok", "error": null, "latency_ms": 409, "epoch": 1784508243.488295} diff --git a/gateway/temp/health_todos.jsonl b/gateway/temp/health_todos.jsonl index 8c13a428..46a3b78d 100644 --- a/gateway/temp/health_todos.jsonl +++ b/gateway/temp/health_todos.jsonl @@ -41,3 +41,4 @@ {"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"} +{"service": "zhiwei_gateway", "label": "知微 Gateway", "reason": "", "timestamp": "2026-07-20T08:45:01.667438"} diff --git a/gateway/temp/last_health_check.json b/gateway/temp/last_health_check.json index 0b78a8d8..6a924f29 100644 --- a/gateway/temp/last_health_check.json +++ b/gateway/temp/last_health_check.json @@ -45,5 +45,5 @@ "ok": 4, "total": 4 }, - "generated_at": "2026-07-19 21:00:01" + "generated_at": "2026-07-20 08:50:01" } \ No newline at end of file diff --git a/server.py b/server.py index baa15f4a..99b2648b 100644 --- a/server.py +++ b/server.py @@ -1340,6 +1340,72 @@ def api_xmpp_keys(): return jsonify({"error": "xmpp_logger not available"}), 500 +# ── 开发原则 Tab 端点(仿 AgentsMeeting: G规范/K测试/H需求)── + +DOCS_DIR = Path(__file__).resolve().parent / "docs" +HEALTH_REPORT = Path(__file__).resolve().parent / "gateway" / "temp" / "last_health_check.json" + + +@app.route("/api/spec") +def api_spec(): + """G 规范:docs/dev-spec.md 内容""" + f = DOCS_DIR / "dev-spec.md" + if not f.exists(): + return jsonify({"ok": False, "error": "dev-spec.md not found"}) + return jsonify({"ok": True, "content": f.read_text(encoding="utf-8")}) + + +@app.route("/api/spec/history") +def api_spec_history(): + """dev-spec.md 的 git 历史""" + import subprocess + try: + r = subprocess.run( + ["git", "log", "--oneline", "-20", "--", "docs/dev-spec.md"], + capture_output=True, timeout=10, text=True, + cwd=str(Path(__file__).resolve().parent)) + lines = [l for l in r.stdout.splitlines() if l.strip()] + return jsonify({"ok": True, "log": lines, "count": len(lines)}) + except Exception as e: + return jsonify({"ok": False, "error": str(e)[:100]}) + + +@app.route("/api/tests") +def api_tests(): + """K 测试:agents_health_check 服务检查结果,渲染为 pass/fail 测试报告""" + if not HEALTH_REPORT.exists(): + return jsonify({"ok": False, "error": "health report not found (cron not run yet)"}) + try: + rep = json.loads(HEALTH_REPORT.read_text(encoding="utf-8")) + except Exception as e: + return jsonify({"ok": False, "error": str(e)[:100]}) + tests = [] + for svc in rep.get("services", []): + ok = svc.get("health", {}).get("ok", False) + tests.append({ + "name": f"{svc.get('label', svc.get('name'))} ({svc.get('name')})", + "ok": ok, + "expected": False, + "detail": svc.get("detail", ""), + }) + passed = sum(1 for t in tests if t["ok"]) + return jsonify({ + "ok": True, + "tests": tests, + "summary": {"total": len(tests), "passed": passed, "failed": len(tests) - passed}, + "time": rep.get("generated_at", ""), + }) + + +@app.route("/api/prd") +def api_prd(): + """H 需求:docs/prd.md(不存在则返回未建立)""" + f = DOCS_DIR / "prd.md" + if not f.exists(): + return jsonify({"ok": False, "error": "prd.md 尚未建立"}) + return jsonify({"ok": True, "content": f.read_text(encoding="utf-8")}) + + # 注册提示词管理路由 register_routes(app) diff --git a/static/index.html b/static/index.html index 3b5131b4..bb9fb0f8 100644 --- a/static/index.html +++ b/static/index.html @@ -12,6 +12,8 @@ body { background: #0f0f13; color: #e2e8f0; } .tab-btn { transition: all 0.15s; } .tab-btn.active { background: #1e293b; color: #60a5fa; border-color: #60a5fa; } +.princ-btn { transition: all 0.15s; color: #8b949e; } +.princ-btn.active { background: #1e293b; color: #60a5fa; } .card { background: #1a1a24; border: 1px solid #2a2a3a; border-radius: 12px; } .badge-up { color: #22c55e; } .badge-down { color: #ef4444; } @@ -63,6 +65,7 @@ body { background: #0f0f13; color: #e2e8f0; } + 📸 上传 @@ -78,6 +81,18 @@ body { background: #0f0f13; color: #e2e8f0; } + '; + + // ── 最近 XMPP 对话日志 ── + html += '
'; + html += '
💬 最近对话(' + xmppMsgs.length + ' 条)
'; + if (xmppMsgs.length === 0) { + html += '
暂无记录 — bot 重启后对话开始采集
'; + } else { + html += '
'; + xmppMsgs.slice(0, 10).forEach(m => { + const isIn = m.direction === 'in'; + const arrow = isIn ? '📩' : '📤'; + const dirColor = isIn ? 'text-[#58a6ff]' : 'text-[#3fb950]'; + const statusMark = m.status === 'ok' ? '' : ' [' + m.status + ']'; + const who = isIn ? (m.from || '').split('@')[0] : '知微'; + const t = (m.timestamp || '').slice(11, 16); + const preview = (m.body_preview || '').replace(/' + arrow + ' ' + who + '' + preview + statusMark + '' + t + '
'; + }); + html += '
'; + } + html += ''; html += ''; } + + // ── 原有健康监控(功能树/Cron/数据实体/数据流)—— 恢复自 mofin_health.html ── + html += '
'; + html += '
📊 系统健康监控(功能树 · Cron · 数据实体 · 数据流)
'; + html += ''; + html += '
'; + el.innerHTML = html; el.dataset.rendered = '1'; // 标记已首渲 } catch(e) { @@ -1563,9 +1615,10 @@ async function refreshHealth() { const el = document.getElementById('tab-health'); if (!el || el.classList.contains('hidden') || !el.dataset.rendered) return; try { - const [services, xmpp] = await Promise.all([ + const [services, xmpp, msgData] = await Promise.all([ fetchJSON('/api/services'), - fetchJSON('/api/xmpp/health') + fetchJSON('/api/xmpp/health'), + fetchJSON('/api/xmpp/messages').catch(() => null) ]); const svcs = (services.services || []); const sum = services.summary || {}; @@ -1613,12 +1666,40 @@ async function refreshHealth() { if (bi) bi.textContent = ba.inbound || 0; if (bo) { bo.textContent = ba.outbound || 0; bo.className = 'font-mono ' + (ba.outbound ? 'text-[#3fb950]' : (ba.inbound ? 'text-[#f85149]' : 'text-slate-500')); } if (be) { be.textContent = ba.errors || 0; be.className = 'font-mono ' + (ba.errors ? 'text-[#f85149]' : 'text-slate-500'); } - if (bl) bl.textContent = ba.last_error || ''; + if (bl) { + const errAge = ba.last_error_age_sec >= 0 ? Math.round(ba.last_error_age_sec/60) + '分钟前' : ''; + if (ba.last_error && ba.last_error_resolved) { + bl.textContent = '⚠️ 历史错误(' + errAge + ',已恢复): ' + ba.last_error; + bl.className = 'text-xs text-slate-500 mt-2 p-2 bg-slate-800/30 rounded'; + } else if (ba.last_error) { + bl.textContent = '⚠️ ' + errAge + ': ' + ba.last_error; + bl.className = 'text-xs text-[#f85149] mt-2 p-2 bg-red-900/20 rounded'; + } else { + bl.textContent = ''; + } + } // LLM Provider const llm = xmpp.llm_provider || {}; const ls = document.getElementById('hLlmStatus'), le = document.getElementById('hLlmErr'); if (ls) { ls.textContent = llm.status || '?'; ls.className = 'font-mono ' + (llm.status === 'ok' ? 'text-[#3fb950]' : 'text-[#f85149]'); } if (le) le.textContent = llm.error || ''; + + // 最近对话列表 + const msgBox = document.getElementById('hXmppMsgs'); + if (msgBox && msgData && msgData.messages) { + let mh = ''; + msgData.messages.slice(0, 10).forEach(m => { + const isIn = m.direction === 'in'; + const arrow = isIn ? '📩' : '📤'; + const dirColor = isIn ? 'text-[#58a6ff]' : 'text-[#3fb950]'; + const statusMark = m.status === 'ok' ? '' : ' [' + m.status + ']'; + const who = isIn ? (m.from || '').split('@')[0] : '知微'; + const t = (m.timestamp || '').slice(11, 16); + const preview = (m.body_preview || '').replace(/' + arrow + ' ' + who + '' + preview + statusMark + '' + t + ''; + }); + msgBox.innerHTML = mh; + } } // 更新时间戳 @@ -1690,6 +1771,159 @@ async function renderWatch() { watchTimer = setInterval(renderWatch, 60000); } } + +// ── 开发原则 Tab(仿 AgentsMeeting: G规范/K测试/F健康/H需求)── +let _princLoaded = {}; +function renderPrinciples() { + // 绑定子tab按钮 + document.querySelectorAll('.princ-btn').forEach(btn => { + btn.onclick = () => { + document.querySelectorAll('.princ-btn').forEach(b => b.classList.remove('active')); + document.querySelectorAll('.princ-pane').forEach(p => p.classList.add('hidden')); + btn.classList.add('active'); + const id = btn.dataset.princ; + document.getElementById('princ-' + id).classList.remove('hidden'); + _loadPrinc(id); + }; + }); + // 默认加载当前激活的子tab + const active = document.querySelector('.princ-btn.active'); + _loadPrinc(active ? active.dataset.princ : 'spec'); +} + +function _loadPrinc(id) { + if (id === 'spec') return _renderPrincSpec(); + if (id === 'tests') return _renderPrincTests(); + if (id === 'health') return _renderPrincHealth(); + if (id === 'prd') return _renderPrincPrd(); +} + +async function _renderPrincSpec() { + const el = document.getElementById('princ-spec'); + el.innerHTML = '
加载中...
'; + try { + const d = await fetchJSON('/api/spec'); + if (!d.ok) { el.innerHTML = '
' + (d.error || '加载失败') + '
'; return; } + let h = '
' + mdRender(d.content) + '
'; + try { + const dh = await fetchJSON('/api/spec/history'); + if (dh.ok && dh.log && dh.log.length) { + h += '
历史版本 (' + dh.count + ' commits)
'; + } + } catch(e) {} + el.innerHTML = h; + } catch(e) { el.innerHTML = '
加载失败: ' + e.message + '
'; } +} + +async function _renderPrincTests() { + const el = document.getElementById('princ-tests'); + el.innerHTML = '
加载中...
'; + try { + const d = await fetchJSON('/api/tests'); + if (!d.ok) { el.innerHTML = '
' + (d.error || '加载失败') + '
'; return; } + const s = d.summary || {}; + let h = '
'; + h += '
' + (s.total||0) + '
Total
'; + h += '
' + (s.passed||0) + '
Passed
'; + h += '
' + (s.failed||0) + '
Failed
'; + h += '
'; + const fails = (d.tests||[]).filter(t => !t.ok); + if (fails.length) { + h += '
Failed (' + fails.length + ')
'; + fails.forEach(t => { + h += '
FAIL ' + t.name.replace(/
' + (t.detail||'').replace(/
'; + }); + } + const passes = (d.tests||[]).filter(t => t.ok); + h += '
Passed (' + passes.length + ')
'; + passes.forEach(t => { + h += '
PASS ' + t.name.replace(/ ' + (t.detail||'').replace(/
'; + }); + h += '
数据源: agents_health_check (cron */5min) · 最后运行: ' + (d.time||'?') + '
'; + el.innerHTML = h; + } catch(e) { el.innerHTML = '
加载失败: ' + e.message + '
'; } +} + +function _renderPrincHealth() { + const el = document.getElementById('princ-health'); + if (_princLoaded.health) return; + el.innerHTML = '
完整健康监控与 🏥 健康 Tab 同源。快捷入口:🏥 健康 Tab
' + + '
'; + _princLoaded.health = true; +} + +async function _renderPrincPrd() { + const el = document.getElementById('princ-prd'); + el.innerHTML = '
加载中...
'; + try { + const d = await fetchJSON('/api/prd'); + if (!d.ok) { el.innerHTML = '
' + (d.error || 'prd.md 尚未建立') + '
'; return; } + el.innerHTML = '
' + mdRender(d.content) + '
'; + } catch(e) { el.innerHTML = '
加载失败: ' + e.message + '
'; } +} + +// markdown 渲染(移植自 AgentsMeeting dashboard) +function mdRender(md) { + if (!md) return ''; + let h = ''; + const blocks = md.split('\n\n'); + for (const raw of blocks) { + const block = raw.trim(); + if (!block) continue; + if (block.startsWith('```')) { + const code = block.replace(/^```[\s\S]*?\n/, '').replace(/```$/, '').trim(); + h += '
' + code.replace(/';
+      continue;
+    }
+    if (block.startsWith('#### ')) { h += '

' + inMd(block.slice(5).replace(/'; continue; } + if (block.startsWith('### ')) { h += '

' + inMd(block.slice(4).replace(/'; continue; } + if (block.startsWith('## ')) { h += '

' + inMd(block.slice(3).replace(/'; continue; } + if (block.startsWith('# ')) { h += '

' + inMd(block.slice(2).replace(/'; continue; } + if (block.startsWith('> ')) { h += '
' + inMd(block.slice(2).replace(/'; continue; } + if (block.startsWith('---') || block.startsWith('***')) { h += '
'; continue; } + if (block.startsWith('- ') || block.startsWith('* ')) { + const items = block.split('\n'); + h += '
    '; + items.forEach(li => { const t = li.replace(/^[-*]\s*/, '').trim(); if (t) h += '
  • ' + inMd(t.replace(/'; }); + h += '
'; + continue; + } + if (/^\d+\.\s/.test(block)) { + const items = block.split('\n'); + h += '
    '; + items.forEach(li => { const t = li.replace(/^\d+\.\s*/, '').trim(); if (t) h += '
  1. ' + inMd(t.replace(/'; }); + h += '
'; + continue; + } + if (block.indexOf('|') >= 0 && block.indexOf('\n|') >= 0) { + const rows = block.split('\n'); + let inTable = false; + rows.forEach(row => { + if (row.indexOf('---') >= 0) return; + const cells = row.split('|').filter(c => c.trim()); + if (cells.length < 2) return; + if (!inTable) { + h += '' + cells.map(c => ''; + inTable = true; + } else { + h += '' + cells.map(c => ''; + } + }); + if (inTable) h += '
' + inMd(c.trim().replace(/').join('') + '
' + inMd(c.trim().replace(/').join('') + '
'; + continue; + } + h += '

' + inMd(block.replace(/'; + } + return h; +} +function inMd(t) { + return t.replace(/\*\*([^*]+)\*\*/g, '$1') + .replace(/\*([^*]+)\*/g, '$1') + .replace(/`([^`]+)`/g, '$1') + .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1'); +} \ No newline at end of file diff --git a/xmpp_logger.py b/xmpp_logger.py index fa4b76a6..5d329ca5 100644 --- a/xmpp_logger.py +++ b/xmpp_logger.py @@ -19,13 +19,32 @@ LOG_DIR = Path(__file__).resolve().parent / "gateway" / "logs" LOG_FILE = LOG_DIR / "xmpp_messages.jsonl" MAX_AGE_DAYS = 7 -# Position-analyst profile config path (where model.provider is set) -HERMES_CONFIG = Path("/home/hmo/.hermes/profiles/position-analyst/config.yaml") -GATEWAY_SERVICE = "hermes-gateway-zhiwei.service" +# 监控的 Hermes Gateway profiles +PROFILES = { + "zhiwei": { + "config": Path("/home/hmo/.hermes/profiles/position-analyst/config.yaml"), + "agent_log": Path("/home/hmo/.hermes/profiles/position-analyst/logs/agent.log"), + "service": "hermes-gateway-zhiwei.service", + "user_service": False, + "gateway_port": 8643, + }, + "default": { + "config": Path("/home/hmo/.hermes/config.yaml"), + "agent_log": Path("/home/hmo/.hermes/logs/agent.log"), + "service": "hermes-gateway.service", + "user_service": True, + "gateway_port": 8642, + }, +} +# 向后兼容(旧引用) +HERMES_CONFIG = PROFILES["zhiwei"]["config"] +GATEWAY_SERVICE = PROFILES["zhiwei"]["service"] RESTART_COOLDOWN_FILE = LOG_DIR / "last_restart.txt" RESTART_COOLDOWN_SEC = 180 # 3 min: don't trigger another restart within 3 min of last # Map AgentsMeeting key_id -> Hermes provider name +# NOTE: key7 (kimi) is intentionally NOT in this map — different model provider. +# zhiwei uses deepseek-v4-flash via OCG; auto_heal must never switch to kimi. KEY_TO_PROVIDER = { "key1": "ocg-new", "key2": "ocg-old", @@ -36,15 +55,16 @@ KEY_TO_PROVIDER = { } -def current_provider() -> str | None: +def current_provider(profile="zhiwei") -> str | None: """Read current model.provider from Hermes config. Walks the file tracking the model: block to find its nested `provider:` (avoiding other blocks like `agent.alerts[0].provider:`). """ + cfg_path = PROFILES.get(profile, PROFILES["zhiwei"])["config"] try: in_model = False - for line in HERMES_CONFIG.read_text().splitlines(): + for line in cfg_path.read_text().splitlines(): stripped = line.rstrip() # Detect top-level "model:" at column 0 if stripped == "model:" or stripped.startswith("model:") and not line.startswith(" "): @@ -63,50 +83,74 @@ def current_provider() -> str | None: return None -def switch_key(key_id: str) -> dict: +def _ensure_provider_block(target_cfg: Path, provider: str) -> bool: + """若目标 config 缺少 provider 凭据块,从 zhiwei config(凭据单一事实源)复制注入。""" + txt = target_cfg.read_text() + if _re.search(rf"^ {provider}:\s*$", txt, flags=_re.MULTILINE): + return True + src = PROFILES["zhiwei"]["config"].read_text() + m = _re.search(rf"(^ {provider}:\n(?: .+\n)+)", src, flags=_re.MULTILINE) + if not m: + return False + block = m.group(1) + # 插到 providers: 区块第一个 provider 之前 + m2 = _re.search(r"^providers:\s*$", txt, flags=_re.MULTILINE) + if not m2: + return False + txt = txt[:m2.end()] + "\n" + block + txt[m2.end():] + target_cfg.write_text(txt) + return True + + +def _restart_gateway(profile: str) -> dict: + """异步重启指定 profile 的 gateway。""" + p = PROFILES[profile] + try: + if p["user_service"]: + _sp.Popen(["systemctl", "--user", "restart", p["service"]], + stdout=_sp.DEVNULL, stderr=_sp.DEVNULL, start_new_session=True) + else: + _sp.Popen(["sudo", "-n", "systemctl", "restart", p["service"]], + stdout=_sp.DEVNULL, stderr=_sp.DEVNULL, start_new_session=True) + return {"success": True, "detail": "restart triggered (async)"} + except Exception as e: + return {"success": False, "detail": str(e)[:100]} + + +def switch_key(key_id: str, profile: str = "zhiwei") -> dict: """Switch Hermes config to use the given key_id's provider, then restart Gateway.""" provider = KEY_TO_PROVIDER.get(key_id) if not provider: return {"switched": False, "detail": f"unknown key_id {key_id}"} - old = current_provider() + cfg_path = PROFILES[profile]["config"] + old = current_provider(profile) if old == provider: return {"switched": False, "old": old, "new": provider, "detail": "already on this key"} try: - txt = HERMES_CONFIG.read_text() + if not _ensure_provider_block(cfg_path, provider): + return {"switched": False, "detail": f"provider {provider} 凭据块缺失且无法注入"} + txt = cfg_path.read_text() new_txt, n = _re.subn(r"^ provider:\s*\S+\s*$", f" provider: {provider}", txt, count=1, flags=_re.MULTILINE) if n == 0: return {"switched": False, "detail": "no provider: line found"} - HERMES_CONFIG.write_text(new_txt) + cfg_path.write_text(new_txt) except Exception as e: return {"switched": False, "detail": f"config edit failed: {e}"} - # systemctl restart blocks until gateway drain completes (60+s). - # Fire asynchronously via Popen to avoid blocking cron; verify later. - try: - _sp.Popen(["sudo", "-n", "systemctl", "restart", GATEWAY_SERVICE], - stdout=_sp.DEVNULL, stderr=_sp.DEVNULL, start_new_session=True) - RESTART_COOLDOWN_FILE.write_text(str(_time.time())) - ok = True - detail = "restart triggered (async)" - except Exception as e: - ok = False - detail = f"restart trigger failed: {e}" - - # Wait long enough for systemd to drain + restart the gateway (max 90s) - _time.sleep(45) - verify = _verify_llm() + r = _restart_gateway(profile) + RESTART_COOLDOWN_FILE.write_text(str(_time.time())) return { "switched": True, "old": old, "new": provider, "key_id": key_id, - "restart": ok, - "verify": verify, - "detail": detail, + "profile": profile, + "restart": r["success"], + "detail": r["detail"], } @@ -290,14 +334,36 @@ def health(): inbound = [l for l in lines if "📩 收到" in l] outbound = [l for l in lines if "📤 发送" in l or "📤" in l] errors = [l for l in lines if "ERROR" in l or "TimeoutError" in l or "timed out" in l] + + # 解析最后一条 inbound 的时间(行首 python logging 时间戳 "2026-07-19 23:31:07,342") + import re as _re2 + def _line_epoch(line): + m = _re2.search(r"(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})", line) + if not m: + return 0 + try: + return datetime.strptime(m.group(1), "%Y-%m-%d %H:%M:%S").timestamp() + except Exception: + return 0 + + last_inbound_ts = max((_line_epoch(l) for l in inbound), default=0) + last_outbound_ts = max((_line_epoch(l) for l in outbound), default=0) + last_error_ts = max((_line_epoch(l) for l in errors), default=0) result["bot_activity"] = { "inbound": len(inbound), "outbound": len(outbound), "errors": len(errors), "last_error": errors[-1][:250] if errors else None, "last_inbound": inbound[-1][:250] if inbound else None, "last_outbound": outbound[-1][:200] if outbound else None, + "last_inbound_age_sec": int(now_epoch - last_inbound_ts) if last_inbound_ts else -1, + "last_outbound_age_sec": int(now_epoch - last_outbound_ts) if last_outbound_ts else -1, + "last_error_age_sec": int(now_epoch - last_error_ts) if last_error_ts else -1, + # 错误之后已有成功出站 → 该错误已被覆盖,不算当前问题 + "last_error_resolved": bool(last_error_ts and last_outbound_ts > last_error_ts), } - if errors and not outbound: result["status"] = "degraded" - if inbound and not outbound: result["status"] = "degraded" + # 只有未覆盖的错误才降级状态 + unresolved_error = last_error_ts and not (last_outbound_ts > last_error_ts) + if errors and not outbound and unresolved_error: result["status"] = "degraded" + if inbound and not outbound and unresolved_error: result["status"] = "degraded" except Exception: result["bot_activity"] = {"error": "journalctl failed"} @@ -314,20 +380,11 @@ def health(): except Exception: pass result["gateways"][name] = {"port": port, "alive": ok} - # 测试知微的 LLM 调用是否可达 - if result["gateways"].get("zhiwei", {}).get("alive"): - try: - import urllib.request - payload = json.dumps({"model": "deepseek-v4-flash", "messages": [{"role": "user", "content": "ping"}], - "max_tokens": 5, "stream": False}).encode() - req = urllib.request.Request("http://127.0.0.1:8643/v1/chat/completions", - data=payload, headers={"Content-Type": "application/json", - "Authorization": "Bearer hermes123"}) - urllib.request.urlopen(req, timeout=90) - result["llm_provider"] = {"status": "ok", "latency": "fast"} - except Exception as e: - result["llm_provider"] = {"status": "timeout" if "timeout" in str(e).lower() else "error", - "error": str(e)[:150]} + # LLM 健康:扫 gateway agent.log 最近一次真实调用结果(零成本,不发 LLM 请求) + # agent.log 里每次真实调用都有记录:成功 "API call #N: ... latency=Xs" / 失败 "HTTP 429..." + result["llm_provider"] = _scan_agent_log(now_epoch, "zhiwei") + # default profile(莫荷 8642)同样监控——它的 429 曾导致整组 cron 失败 + result["llm_provider_default"] = _scan_agent_log(now_epoch, "default") except Exception as e: result["gateways"] = {"error": str(e)[:100]} @@ -352,6 +409,59 @@ def health(): return result +def _scan_agent_log(now_epoch, profile="zhiwei", tail_lines=300): + """扫 gateway agent.log 尾部,取最近一次真实 LLM 调用的结果。 + + 成功行: "2026-07-19 20:04:42,880 INFO ... API call #4: ... latency=16.0s ..." + 失败行: "2026-07-19 19:35:59 WARNING ... API call failed ... HTTP 429: Weekly usage limit reached ..." + 返回 {"status": "ok"|"error"|"unknown", "latency"/"error", "age_sec"} + """ + log_path = PROFILES.get(profile, PROFILES["zhiwei"])["agent_log"] + if not log_path.exists(): + return {"status": "unknown", "error": "agent.log not found"} + try: + # 只读尾部(大文件不全读) + size = log_path.stat().st_size + with open(log_path, "rb") as f: + f.seek(max(0, size - 64 * 1024)) + chunk = f.read().decode("utf-8", errors="replace") + lines = [l for l in chunk.splitlines() if l.strip()][-tail_lines:] + except Exception as e: + return {"status": "unknown", "error": str(e)[:80]} + + last_ok = None # (epoch, latency_str) + last_fail = None # (epoch, summary) + for line in lines: + m_ts = _re.search(r"(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})", line) + if not m_ts: + continue + try: + ts = datetime.strptime(m_ts.group(1), "%Y-%m-%d %H:%M:%S").timestamp() + except Exception: + continue + m_ok = _re.search(r"API call #\d+.*latency=([\d.]+)s", line) + if m_ok: + last_ok = (ts, m_ok.group(1)) + continue + if "API call failed" in line and ("summary=" in line or "after" in line): + m_err = _re.search(r"summary=(.+?)$", line) + summary = (m_err.group(1) if m_err else line)[:150] + last_fail = (ts, summary) + + if not last_ok and not last_fail: + return {"status": "unknown", "error": "no LLM calls in log"} + + if last_fail and (not last_ok or last_fail[0] > last_ok[0]): + return {"status": "error", + "error": last_fail[1], + "age_sec": int(now_epoch - last_fail[0]), + "source": "agent.log"} + return {"status": "ok", + "latency": f"{last_ok[1]}s", + "age_sec": int(now_epoch - last_ok[0]), + "source": "agent.log"} + + def _fetch_keys(): """从 AgentsMeeting Dashboard 获取可用 API Key 列表""" try: @@ -416,46 +526,62 @@ def auto_heal(): h = health() actions = [] - # 0. LLM Provider 异常 → 先查是否有更好的 Key 可切换 - llm_status = h.get("llm_provider", {}).get("status") - llm_error = h.get("llm_provider", {}).get("error", "") + # 0. 检查每个 profile 的 LLM 状态(zhiwei + default),429 → 切 key + bk_cache = None + for profile in ("zhiwei", "default"): + llm = h.get("llm_provider" if profile == "zhiwei" else "llm_provider_default", {}) + llm_status = llm.get("status") + llm_error = llm.get("error", "") or "" - # Detect rate-limit / timeout / error - is_rate_limited = (llm_status == "rate_limited" or - "429" in llm_error or "Weekly usage" in llm_error or - "RateLimit" in llm_error) + is_rate_limited = (llm_status == "rate_limited" or + "429" in llm_error or "Weekly usage" in llm_error or + "RateLimit" in llm_error) + if not is_rate_limited: + continue + + if bk_cache is None: + bk_cache = best_key() + bk = bk_cache + if not bk: + continue + actions.append({ + "action": "check_keys", + "profile": profile, + "best_key": bk["key_id"], + "current_provider": current_provider(profile), + "best_provider": KEY_TO_PROVIDER.get(bk["key_id"]), + "weekly_pct": bk["weekly"]["usage_percent"], + }) + + target_provider = KEY_TO_PROVIDER.get(bk["key_id"]) + current = current_provider(profile) + if target_provider is None: + actions.append({ + "action": "skip_switch", + "profile": profile, + "reason": f"best key {bk['key_id']} is non-OCG (kimi), stay on deepseek", + }) + elif target_provider != current: + if bk["weekly"]["status"] == "ok": + action = switch_key(bk["key_id"], profile=profile) + action["action_group"] = "switch_key" + action["profile"] = profile + actions.append(action) + else: + actions.append({ + "action": "skip_switch", + "profile": profile, + "reason": f"best key {bk['key_id']} weekly status {bk['weekly']['status']}, no improvement", + }) + + # zhiwei LLM timeout/error(非429)→ 重启 Gateway + llm_status = h.get("llm_provider", {}).get("status") + llm_error = h.get("llm_provider", {}).get("error", "") or "" + is_rate_limited_zw = ("429" in llm_error or "Weekly usage" in llm_error or "RateLimit" in llm_error) is_timeout_or_error = llm_status in ("timeout", "error") - if is_rate_limited or is_timeout_or_error: - bk = best_key() - if bk: - actions.append({ - "action": "check_keys", - "best_key": bk["key_id"], - "current_provider": current_provider(), - "best_provider": KEY_TO_PROVIDER.get(bk["key_id"]), - "rolling_pct": bk["rolling"]["usage_percent"], - "weekly_pct": bk["weekly"]["usage_percent"], - "issues": bk["issues"], - }) - - # 如果 best key 对应的 provider 跟当前不同 → 切换 - target_provider = KEY_TO_PROVIDER.get(bk["key_id"]) - current = current_provider() - if target_provider and target_provider != current: - # 只有当 best key 自身至少 weekly ok 才切,否则切过去也白搭 - if bk["weekly"]["status"] == "ok": - action = switch_key(bk["key_id"]) - action["action_group"] = "switch_key" - actions.append(action) - else: - actions.append({ - "action": "skip_switch", - "reason": f"best key {bk['key_id']} weekly status {bk['weekly']['status']}, no improvement", - }) - # 1. LLM Provider timeout/error(非429)→ 重启 Gateway(不是切 key) - if is_timeout_or_error and not is_rate_limited: + if is_timeout_or_error and not is_rate_limited_zw: gw = h.get("gateways", {}).get("zhiwei", {}) if gw.get("alive"): # Check cooldown to avoid restart loops @@ -485,16 +611,44 @@ def auto_heal(): "success": False, "detail": str(e)[:100]}) - # 2. Bot 无出站 → 重启知微 Bot + # 2. Bot 真卡死才重启:最后一条 inbound 超过 10 分钟仍无出站,才算死(不是 LLM 慢) + # LLM 正常冷启动/工具调用需要 1-10 分钟,inbound 后立即重启会杀掉进行中的调用。 ba = h.get("bot_activity", {}) - if ba.get("inbound", 0) > 0 and ba.get("outbound", 0) == 0 and ba.get("errors", 0) > 0: + inbound_age = ba.get("last_inbound_age_sec", -1) + outbound_age = ba.get("last_outbound_age_sec", -1) + bot_truly_stuck = ( + inbound_age > 600 # 最后一条用户消息已超过10分钟 + and (outbound_age < 0 or outbound_age > inbound_age - 600) # 且其间没有任何出站 + and ba.get("inbound", 0) > 0 + ) + if bot_truly_stuck: + # cooldown: bot 重启不频繁于10分钟一次 try: - r = _sp.run(["sudo", "-n", "systemctl", "restart", "xmpp-zhiwei"], capture_output=True, timeout=30, text=True) - actions.append({"action": "restart_zhiwei_bot", "target": "xmpp-zhiwei", - "success": r.returncode == 0, "detail": "restarted" if r.returncode == 0 else r.stderr[:100]}) - except Exception as e: - actions.append({"action": "restart_zhiwei_bot", "target": "xmpp-zhiwei", - "success": False, "detail": str(e)[:100]}) + last_bot_restart = 0 + bf = LOG_DIR / "last_bot_restart.txt" + if bf.exists(): + last_bot_restart = float(bf.read_text().strip() or 0) + except Exception: + last_bot_restart = 0 + bot_elapsed = _time.time() - last_bot_restart + if bot_elapsed < 600: + actions.append({"action": "skip_bot_restart", + "reason": f"bot restart cooldown {int(bot_elapsed)}s < 600s"}) + else: + try: + _sp.Popen(["sudo", "-n", "systemctl", "restart", "xmpp-zhiwei"], + stdout=_sp.DEVNULL, stderr=_sp.DEVNULL, start_new_session=True) + (LOG_DIR / "last_bot_restart.txt").write_text(str(_time.time())) + actions.append({"action": "restart_zhiwei_bot", "target": "xmpp-zhiwei", + "success": True, "detail": "restart triggered (async)"}) + except Exception as e: + actions.append({"action": "restart_zhiwei_bot", "target": "xmpp-zhiwei", + "success": False, "detail": str(e)[:100]}) + elif ba.get("inbound", 0) > 0 and ba.get("outbound", 0) == 0 and ba.get("errors", 0) > 0: + # inbound 后仍在处理窗口内(LLM 慢但正常),不动 bot + actions.append({"action": "bot_busy_not_stuck", + "inbound_age_sec": inbound_age, + "reason": "inbound < 10min ago — LLM still processing, no restart"}) # 3. ejabberd → 重启容器 if h.get("ejabberd", "") and "Up" not in str(h["ejabberd"]):