#!/usr/bin/env python3 # -*- coding: utf-8 -*- """SSH 上服务器,快速检查: 1. .env 里的 AGNES_API_KEY 是否已配(不要再打印 key 值) 2. worker 进程是否在跑、enrichment_loop 任务是否在跑 3. worker 日志最近 200 行是否出现 enrich_article / classify / commentary / format 等关键字 4. 翻译/enrich 各自最后处理时间 5. enrichment_loop 配置(LLM enable 状态) """ from __future__ import annotations import sys import os import paramiko HOST = os.environ.get("REMOTE_HOST", "207.57.129.228") PORT = int(os.environ.get("REMOTE_PORT", "19717")) USER = os.environ.get("REMOTE_USER", "root") PASS = os.environ.get("REMOTE_PASS", "") if not PASS: print("ERROR: REMOTE_PASS not set", file=sys.stderr) sys.exit(1) def run(c, cmd, timeout=30, label=""): if label: print(f"\n=== {label} ===") print(f"$ {cmd}") si, so, se = c.exec_command(cmd, timeout=timeout, get_pty=True) out = so.read().decode(errors="replace") err = se.read().decode(errors="replace") rc = so.channel.recv_exit_status() if out.strip(): print(out.rstrip()) if err.strip(): print(f"[stderr] {err.rstrip()}") print(f"-> rc={rc}") return out print(f"连 {USER}@{HOST}:{PORT} ...") c = paramiko.SSHClient() c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) c.connect(HOST, port=PORT, username=USER, password=PASS, timeout=30, banner_timeout=30, auth_timeout=30, allow_agent=False, look_for_keys=False) print("✓ 连上\n") # 1) AGNES_API_KEY 状态(只看长度,不打值) run(c, "cd /srv/news && " "grep -E '^AGNES_(API_KEY|BASE_URL|CHAT_MODEL|IMAGE_MODEL)=' .env | " "awk -F= 'BEGIN{FS=\"=\"} { " " if ($1==\"AGNES_API_KEY\") { k=$2; gsub(/\"/,\"\",k); " " printf \" %s = (length=%d, prefix=%s***)\\n\", $1, length(k), substr(k,1,4) " " } else { print \" \" $0 }" "}'", label="1) .env 中 Agnes 相关配置") # 2) worker 进程 + enrichment_loop 状态 run(c, "cd /srv/news && " "echo '--- docker compose ps ---' && " "docker compose ps worker && " "echo '--- worker 容器内进程 ---' && " "docker compose exec -T worker sh -c 'ps -ef | grep -E \"python|app.workers\" | grep -v grep' && " "echo '--- enrichment 任务在 asyncio 队列 ---' && " "docker compose exec -T worker sh -c 'cat /proc/1/status 2>/dev/null | head -3' ", label="2) Worker 进程 + enrichment_loop 状态") # 3) LLM enable 状态(admin_llm_settings 表) run(c, "cd /srv/news && set -a && . ./.env && set +a && " "docker compose exec -T postgres psql -U \"$POSTGRES_USER\" -d \"$POSTGRES_DB\" -t -A -F $'\\t' -c " "\"SELECT key, value, updated_at FROM admin_llm_settings ORDER BY key;\" 2>&1 | head -10", label="3) admin_llm_settings(LLM enable 状态)") # 4) worker 日志最近 200 行 run(c, "cd /srv/news && " "docker compose logs --no-color --tail=200 worker 2>&1 | tail -80", label="4) worker 日志(最近 200 行)") # 5) 翻译/enrich 各自最后活跃时间 run(c, "cd /srv/news && set -a && . ./.env && set +a && " "docker compose exec -T postgres psql -U \"$POSTGRES_USER\" -d \"$POSTGRES_DB\" -t -A -F $'\\t' -c \"" "SELECT 'translated_last_5min=' || count(*) FROM articles WHERE translated_at > now() - interval '5 minute';" "SELECT 'classified_last_5min=' || count(*) FROM articles WHERE classify_status='ok' AND translated_at > now() - interval '5 minute';" "SELECT 'format_last_5min=' || count(*) FROM articles WHERE format_status='ok' AND translated_at > now() - interval '5 minute';" "SELECT 'commentary_last_5min=' || count(*) FROM articles WHERE commentary_status='ok' AND translated_at > now() - interval '5 minute';" "SELECT 'image_ai_last_5min=' || count(*) FROM articles WHERE image_ai_status='ok' AND translated_at > now() - interval '5 minute';" "SELECT 'pending_classify=' || count(*) FROM articles WHERE classify_status IN ('pending','n/a') AND translation_status='ok';" "SELECT 'pending_format=' || count(*) FROM articles WHERE format_status IN ('pending','n/a') AND translation_status='ok';" "\"", label="5) 最近 5 分钟 LLM 步骤活跃度") c.close() print("\n✓ 检查完成")