#!/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