xiaowei-system/scripts/model-health.sh

213 lines
5.5 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# ============================================================
# model-health.sh - NewAPI 模型健康巡检脚本
# 每 6 小时由 cron 触发no_agent 模式)
#
# 测试模型列表中的每个模型的:
# - 响应延迟TTFT + 总时间)
# - HTTP 状态码
# - 响应内容是否完整
# - 3 次连续测试的稳定性
#
# 输出:~/.hermes/model-health.json
# ============================================================
API="http://127.0.0.1:3000/v1"
KEY="0ExNiLblJvIWBDpkS50fwOBw4MmqLyKdHJK5iQtlw9dOMWBP"
OUTPUT="$HOME/.hermes/model-health.json"
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
# 测试列表 —— 按场景分组
declare -a MODELS=(
# 快速响应(日常)
"minimaxai/minimax-m3"
"minimaxai/minimax-m2.7"
"stepfun-ai/step-3.5-flash"
"deepseek-ai/deepseek-v3.2"
# 标准推理(主力)
"qwen/qwen3.5-122b-a10b"
"mistralai/mistral-large-3-675b-instruct-2512"
"mistralai/mistral-medium-3.5-128b"
"meta/llama-3.3-70b-instruct"
# 编程专用
"qwen/qwen3-coder-480b-a35b-instruct"
"qwen/qwen2.5-coder-32b-instruct"
# 中文优化
"z-ai/glm4.7"
"z-ai/glm5"
"moonshotai/kimi-k2-instruct"
"bytedance/seed-oss-36b-instruct"
# 轻量/特殊
"microsoft/phi-4-mini-instruct"
"meta/llama-4-maverick-17b-128e-instruct"
)
test_model() {
local model=$1
local trial=$2
local prompt="回复一句话:今天天气不错。"
local start=$(date +%s%N)
local http_body=$(mktemp)
local http_code
http_code=$(curl -s -w "%{http_code}" -o "$http_body" \
--max-time 30 \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d "{\"model\":\"$model\",\"messages\":[{\"role\":\"user\",\"content\":\"$prompt\"}],\"max_tokens\":50}" \
"$API/chat/completions" 2>&1)
local end=$(date +%s%N)
local total_ms=$(( ($end - $start) / 1000000 ))
if [ "$http_code" = "200" ]; then
# 提取关键指标
local content=$(python3 -c "
import json, sys
try:
d = json.load(open('$http_body'))
choice = d['choices'][0]
msg = choice.get('message', {})
finish = choice.get('finish_reason', '')
content = msg.get('content', '')
reasoning = msg.get('reasoning_content', '')
usage = d.get('usage', {})
has_content = 1 if content.strip() else 0
has_reasoning = 1 if reasoning and reasoning.strip() else 0
pt = usage.get('prompt_tokens', 0)
ct = usage.get('completion_tokens', 0)
ttft = d.get('nvext', {}).get('timing', {}).get('ttft_ms', -1)
print(f'{has_content}|{has_reasoning}|{pt}|{ct}|{ttft}|{finish}')
except Exception as e:
print(f'PARSE_ERROR|0|0|0|-1|{str(e)}')
" 2>&1)
rm -f "$http_body"
IFS='|' read -r has_content has_reasoning pt ct ttft finish <<< "$content"
echo "OK|${total_ms}|${ttft}|${has_content}|${has_reasoning}|${pt}|${ct}|${finish}"
else
rm -f "$http_body"
echo "FAIL|${total_ms}|-1|0|0|0|0|HTTP_${http_code}"
fi
}
# 开始测试
echo "[" > "$OUTPUT.tmp"
first=true
for model in "${MODELS[@]}"; do
# 每模型测 3 次,取均值
ok_count=0
fail_count=0
total_latency=0
total_ttft=0
last_status=""
last_finish=""
for trial in 1 2 3; do
result=$(test_model "$model" "$trial")
IFS='|' read -r status latency ttft has_content has_reasoning pt ct finish <<< "$result"
if [ "$status" = "OK" ]; then
ok_count=$((ok_count + 1))
total_latency=$((total_latency + latency))
total_ttft=$((total_ttft + ttft))
last_status="ok"
last_finish="$finish"
else
fail_count=$((fail_count + 1))
last_status="fail"
last_finish="$finish"
fi
done
# 计算统计
stability=""
if [ "$ok_count" -eq 3 ]; then
stability="stable"
elif [ "$ok_count" -ge 1 ]; then
stability="unstable"
else
stability="dead"
fi
avg_latency=0
avg_ttft=0
if [ "$ok_count" -gt 0 ]; then
avg_latency=$((total_latency / ok_count))
avg_ttft=$((total_ttft / ok_count))
fi
# 输出 JSON 行
if [ "$first" = true ]; then
first=false
else
echo "," >> "$OUTPUT.tmp"
fi
cat >> "$OUTPUT.tmp" << JSONBLOCK
{
"model": "$model",
"timestamp": "$TIMESTAMP",
"tests": 3,
"success": $ok_count,
"failure": $fail_count,
"avg_latency_ms": $avg_latency,
"avg_ttft_ms": $avg_ttft,
"stability": "$stability",
"last_status": "$last_status",
"last_finish": "$last_finish"
}
JSONBLOCK
done
echo "]" >> "$OUTPUT.tmp"
# 添加汇总统计
python3 -c "
import json
with open('$OUTPUT.tmp') as f:
data = json.load(f)
total = len(data)
healthy = sum(1 for m in data if m['stability'] == 'stable')
flaky = sum(1 for m in data if m['stability'] == 'unstable')
dead = sum(1 for m in data if m['stability'] == 'dead')
# 按稳定性分组
stable_models = [m for m in data if m['stability'] == 'stable']
fastest = sorted(stable_models, key=lambda x: x['avg_latency_ms'])[:3] if stable_models else []
summary = {
'timestamp': '$TIMESTAMP',
'total_models': total,
'stable': healthy,
'unstable': flaky,
'dead': dead,
'fastest_stable': [m['model'] for m in fastest],
'recommendations': {
'fast': [m['model'] for m in fastest],
'default': [m['model'] for m in sorted(stable_models, key=lambda x: x.get('avg_latency_ms', 9999))[:5]],
},
'models': data
}
with open('$OUTPUT', 'w') as f:
json.dump(summary, f, indent=2, ensure_ascii=False)
print(f'巡检完成: {healthy}个稳定 / {flaky}个不稳定 / {dead}个死')
print(f'最快稳定: {\", \".join(summary[\"recommendations\"][\"fast\"])}')
"
rm -f "$OUTPUT.tmp"