30 行 Python,给你的 AI 接口加一个「降智检测器」
30 行 Python,给你的 AI 接口加一个「降智检测器」
很多人用 API 调模型的时候,从来不知道自己到底在和哪个版本说话。
OpenAI 官方早就承认过「模型漂移」这个现象——同一个模型标识符,不同时间返回的能力可能差异显著。最近 V2EX 上有人做了一个 AI 模型降智监测页面,引发了不少讨论。这件事本身挺有意思:你付了钱,调了接口,但你甚至不知道对面那个模型是不是在「摸鱼」。
这篇文章就教你从零搭一个自己的降智监测脚本,持续追踪模型的推理能力基线。
核心思路
降智检测本质上是「能力基准测试的持续化」。每隔一段时间,用同一套题目问模型,把结果存下来,跟历史对比。题目需要覆盖几个维度:
- 逻辑推理:需要多步推导的数学题
- 代码能力:让它写一段有明确边界条件的代码
- 指令遵循:给出严格格式要求,看它能不能准确执行
- 上下文理解:带有隐含前提的问题
每个维度都有一个「标准答案」或「评分规则」,跑完之后算出一个综合分,写进数据库,画出折线图。
完整实现
依赖极少,openai + sqlite3(标准库),直接跑。
import sqlite3
import json
import time
from datetime import datetime
from openai import OpenAI
# 改一行 base_url,国内直连,比官方便宜 60%+
client = OpenAI(
api_key="your_api_key_here",
base_url="https://api2everything.xyz/v1"
)
MODEL = "gpt-4o" # 换成你要监测的模型
# ── 评测题库 ──────────────────────────────────────────
BENCHMARKS = [
{
"id": "logic_math",
"prompt": "一个房间里有 3 盏灯,门外有 3 个开关,每个开关控制一盏灯。你只能进房间一次,如何判断哪个开关控制哪盏灯?请给出完整步骤。",
"keywords": ["开一盏", "摸", "热", "温度"], # 答案必须包含「先开一个等一会儿」的思路
"max_tokens": 300,
},
{
"id": "code_gen",
"prompt": "用 Python 写一个函数 flatten(lst),将任意深度嵌套的列表展平。要求:不使用 itertools,处理空列表,不修改原列表。只输出函数体,不要解释。",
"keywords": ["isinstance", "list", "yield", "recursive", "def flatten"],
"max_tokens": 200,
},
{
"id": "instruction_follow",
"prompt": '请严格按照以下格式回复,不要有任何多余内容:\nRESULT: <把 "hello world" 反转后大写>',
"keywords": ["RESULT:", "DLROW OLLEH"],
"max_tokens": 50,
},
{
"id": "context_implicit",
"prompt": "我昨天买了一本书,今天又买了同样数量的两倍,现在一共有 9 本。问:我昨天买了几本?只回答数字。",
"keywords": ["3"],
"max_tokens": 20,
},
]
# ── 数据库初始化 ──────────────────────────────────────
def init_db(db_path="benchmark.db"):
conn = sqlite3.connect(db_path)
conn.execute("""
CREATE TABLE IF NOT EXISTS results (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ts TEXT,
model TEXT,
bench_id TEXT,
score REAL,
response TEXT
)
""")
conn.commit()
return conn
# ── 单题评分 ──────────────────────────────────────────
def score_response(response: str, keywords: list[str]) -> float:
"""简单关键词命中率评分,生产环境可换成 LLM-as-judge"""
hits = sum(1 for kw in keywords if kw.lower() in response.lower())
return round(hits / len(keywords), 2)
# ── 执行一轮测试 ──────────────────────────────────────
def run_benchmark(conn: sqlite3.Connection):
ts = datetime.utcnow().isoformat()
scores = {}
for bench in BENCHMARKS:
try:
resp = client.chat.completions.create(
model=MODEL,
messages=[{"role": "user", "content": bench["prompt"]}],
max_tokens=bench["max_tokens"],
temperature=0, # 固定温度,结果才可比
)
text = resp.choices[0].message.content or ""
except Exception as e:
print(f"[ERROR] {bench['id']}: {e}")
text = ""
score = score_response(text, bench["keywords"])
scores[bench["id"]] = score
conn.execute(
"INSERT INTO results (ts, model, bench_id, score, response) VALUES (?,?,?,?,?)",
(ts, MODEL, bench["id"], score, text[:500]),
)
print(f" {bench['id']:25s} score={score:.2f} → {text[:60].strip()!r}")
time.sleep(1) # 避免触发限速
conn.commit()
avg = sum(scores.values()) / len(scores)
print(f"\n[{ts}] 综合得分: {avg:.2f} / 1.00\n")
return avg
# ── 趋势报告(最近 10 次)────────────────────────────
def print_trend(conn: sqlite3.Connection):
rows = conn.execute("""
SELECT ts, AVG(score) as avg_score
FROM results
WHERE model = ?
GROUP BY ts
ORDER BY ts DESC
LIMIT 10
""", (MODEL,)).fetchall()
print("── 最近 10 次综合得分趋势 ──")
for ts, avg in reversed(rows):
bar = "█" * int(avg * 20)
print(f" {ts[:16]} {bar:<20} {avg:.2f}")
# ── 主入口 ────────────────────────────────────────────
if __name__ == "__main__":
conn = init_db()
print(f"正在测试模型: {MODEL}\n")
run_benchmark(conn)
print_trend(conn)
conn.close()
跑起来
pip install openai
python benchmark.py
第一次跑完大概是这样:
logic_math score=0.75 → '第一步,打开第一个开关,等待5分钟...'
code_gen score=1.00 → 'def flatten(lst):\n result = []\n for...'
instruction_follow score=1.00 → 'RESULT: DLROW OLLEH'
context_implicit score=1.00 → '3'
[2025-07-16T08:32:11] 综合得分: 0.94 / 1.00
然后用 cron 每天跑一次,数据沉淀几周之后,折线图就有参考价值了。
进阶方向
把关键词评分换成 LLM-as-judge:让一个轻量模型(比如 gpt-4o-mini)来判断答案是否正确,准确率更高。题目里加一个 grader_prompt 字段,把模型回复传进去,返回 0/1。
多模型对比:把 MODEL 换成一个列表,同一轮题目全部跑一遍,横向比较 gpt-4o、claude-sonnet、deepseek-v3 的当前状态,发现哪个「今天状态好」。
接入告警:如果当日得分比近 7 天均值低 15% 以上,发一条钉钉/Telegram 通知。
关于 API 费用
这套脚本每天跑一次,一个模型消耗 token 大概在 1000 以内,费用极低。不过如果你要同时监测 5-10 个模型,或者加了更多测试题,成本就不容忽视了。
我自己在用无量 Api(api2everything.xyz),国内直连不需要梯子,支持 300+ 模型,定价比官方便宜 65% 左右。上面脚本里的 base_url 已经填好了,注册就送 ¥1 余额,拿来跑这个监测脚本完全够用。格式是标准 OpenAI 兼容的,所以代码零改动。
最后
「降智」这个话题本身就说明,用 AI API 做产品的话,把模型当黑盒是不够的,需要主动建立一套可观测性体系。今天这个脚本只是最简版,但架子搭起来之后,往上加东西很容易。
你在用 AI API 的过程中有没有遇到过明显的能力下降?欢迎评论区聊聊,或者直接把你的监测数据贴出来对比。
推广链接:无量 Api —— 国内直连 300+ 模型,比官方便宜 65%