146 lines
4.8 KiB
Bash
Executable File
146 lines
4.8 KiB
Bash
Executable File
#!/bin/bash
|
||
# ============================================================
|
||
# memory-system-check.sh — 三套记忆系统自检脚本
|
||
# 每小时由 cron 触发(no_agent 模式)
|
||
# 正常静默,异常飞书报警
|
||
# ============================================================
|
||
set -e
|
||
|
||
FEISHU_WEBHOOK="https://open.feishu.cn/open-apis/bot/v2/hook/446db983-e392-4d2c-bfb8-f9060e5df3ad"
|
||
STATE_DIR="$HOME/.hermes/watchdog"
|
||
STATE_FILE="$STATE_DIR/memory_state.json"
|
||
mkdir -p "$STATE_DIR"
|
||
|
||
ALERT=0
|
||
MSG=""
|
||
|
||
# ── 织忆 (ZhiYi) ──
|
||
check_zhiyi() {
|
||
local zhiyi_ok=true
|
||
local issues=()
|
||
|
||
# 1. zhiyid 进程
|
||
if ! pgrep -f "zhiyi" > /dev/null 2>&1; then
|
||
issues+=("zhiyid 进程不存在")
|
||
zhiyi_ok=false
|
||
fi
|
||
|
||
# 2. bge-embed 进程
|
||
if ! pgrep -f "bge_embed" > /dev/null 2>&1; then
|
||
issues+=("bge-embed 进程不存在")
|
||
zhiyi_ok=false
|
||
fi
|
||
|
||
# 3. API 健康
|
||
health=$(curl -s --max-time 3 -H "X-API-Key: zhiyi-dev-key-2026" http://127.0.0.1:7821/api/v1/health 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('status','unknown'))" 2>/dev/null || echo "unreachable")
|
||
if [ "$health" != "ok" ]; then
|
||
issues+=("API status=$health")
|
||
zhiyi_ok=false
|
||
fi
|
||
|
||
# 4. 图谱统计(节点/边数)
|
||
stats=$(curl -s --max-time 3 -H "X-API-Key: zhiyi-dev-key-2026" http://127.0.0.1:7821/api/v1/stats 2>/dev/null || echo "{}")
|
||
nodes=$(echo "$stats" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('total_episodes','?'))" 2>/dev/null)
|
||
mems=$(echo "$stats" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('total_memories','?'))" 2>/dev/null)
|
||
|
||
if [ "$zhiyi_ok" = true ]; then
|
||
echo "[OK] 织忆: episodes=$nodes memories=$mems"
|
||
else
|
||
ALERT=1
|
||
echo "[FAIL] 织忆: ${issues[*]}"
|
||
fi
|
||
}
|
||
|
||
# ── Soulful ──
|
||
check_soulful() {
|
||
local ok=true
|
||
local issues=()
|
||
|
||
# 1. 文件存在性
|
||
for f in "$HOME/.hermes/soulful/cares-queue.json" "$HOME/.hermes/soulful/heart-traces.jsonl" "$HOME/.hermes/soulful/user-profile.json"; do
|
||
if [ ! -f "$f" ]; then
|
||
issues+=("文件缺失: $(basename $f)")
|
||
ok=false
|
||
fi
|
||
done
|
||
|
||
# 2. cares 过期检查(超过7天未处理视为需要关注)
|
||
overdue=$(python3 -c "
|
||
import json, datetime
|
||
d = json.load(open('$HOME/.hermes/soulful/cares-queue.json'))
|
||
today = datetime.date.today()
|
||
for c in d.get('cares', []):
|
||
due = c.get('follow_up_date','')
|
||
if due:
|
||
try:
|
||
d = datetime.date.fromisoformat(due)
|
||
if (today - d).days > 7:
|
||
print('OLD:', c.get('content','')[:30], due)
|
||
except: pass
|
||
" 2>/dev/null)
|
||
|
||
if [ -n "$overdue" ]; then
|
||
issues+=("有超过7天的旧牵挂")
|
||
fi
|
||
|
||
# 3. 心迹条数(太少说明没积累)
|
||
heart_count=$(wc -l < "$HOME/.hermes/soulful/heart-traces.jsonl" 2>/dev/null || echo 0)
|
||
if [ "$heart_count" -lt 5 ]; then
|
||
issues+=("心迹仅${heart_count}条,积累不足")
|
||
fi
|
||
|
||
if [ "$ok" = true ]; then
|
||
cares_count=$(python3 -c "import json; print(len(json.load(open('$HOME/.hermes/soulful/cares-queue.json')).get('cares',[])))" 2>/dev/null)
|
||
echo "[OK] Soulful: cares=$cares_count 心迹=$heart_count"
|
||
else
|
||
ALERT=1
|
||
echo "[WARN] Soulful: ${issues[*]}"
|
||
fi
|
||
}
|
||
|
||
# ── TencentDB ──
|
||
check_tddb() {
|
||
local ok=true
|
||
local issues=()
|
||
|
||
# 1. Gateway 进程
|
||
if ! pgrep -f "tdai" > /dev/null 2>&1; then
|
||
issues+=("tdai-gateway 进程不存在")
|
||
ok=false
|
||
fi
|
||
|
||
# 2. API 健康
|
||
health=$(curl -s --max-time 3 http://127.0.0.1:8420/health 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('status','unknown'))" 2>/dev/null || echo "unreachable")
|
||
if [ "$health" != "ok" ]; then
|
||
issues+=("Gateway status=$health")
|
||
ok=false
|
||
fi
|
||
|
||
# 3. 数据量检查
|
||
data=$(curl -s --max-time 5 -H "Content-Type: application/json" -d '{"query":"牧尘","top_k":1}' http://127.0.0.1:8420/search/memories 2>/dev/null || echo "{}")
|
||
if echo "$data" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('L1','?'), d.get('L0','?'))" 2>/dev/null | grep -q "unreachable"; then
|
||
issues+=("API unreachable")
|
||
ok=false
|
||
fi
|
||
|
||
if [ "$ok" = true ]; then
|
||
echo "[OK] TencentDB: $health"
|
||
else
|
||
ALERT=1
|
||
echo "[FAIL] TencentDB: ${issues[*]}"
|
||
fi
|
||
}
|
||
|
||
# ── 主流程 ──
|
||
echo "=== $(date '+%Y-%m-%d %H:%M:%S') ==="
|
||
check_zhiyi
|
||
check_soulful
|
||
check_tddb
|
||
|
||
# ── 告警 ──
|
||
if [ "$ALERT" = 1 ]; then
|
||
curl -s -X POST "$FEISHU_WEBHOOK" \
|
||
-H "Content-Type: application/json" \
|
||
-d "{\"msg_type\":\"text\",\"text\":\"⚠️ 记忆系统异常,请检查\"}" > /dev/null 2>&1
|
||
fi
|