AI模型体验指数:把“模型变笨”的用户吐槽变成可量化趋势指标

发布时间:2026/9/4 19:12:04
AI模型体验指数:把“模型变笨”的用户吐槽变成可量化趋势指标 最近AI 社区又掀起了一轮熟悉的大讨论模型是不是变笨了你今天问它一个昨天还能答对的问题它突然开始一本正经地胡说八道你让它修一个正则它分析了半天却给出一个更离谱的版本有些时候甚至连 API 都连不上屏幕上只留下 model is unavailable、selected model is at capacity 这类令人沮丧的提示。从 2023 年大模型开始大规模落地到现在“模型悄悄变笨”的质疑每隔一段时间就会出现一次模型厂商也不止一次公开回应过类似论调。但问题一直悬而未决我们到底该信个人体感还是信官方 benchmark能不能把“大家觉得变笨了”这件事变成可量化、可追溯、可比较的指标Hacker News 上最近出现了一个 Show HN 项目标题非常直接Is AI Dumber Today? An index of AI model experience from users opinion。它想做一件很多评测榜单没做过的事不从跑分出发而从用户的真实使用反馈出发建立一个“AI 模型体验指数”。我的判断是这类项目表面像是“AI 吐槽收集器”本质却是一个值得拆解的大模型应用工程样例。它背后包括数据采集、文本解析、LLM 观点抽取、模型名归一化、时间窗口聚合、Web 可视化这一整套链路。无论你是做模型评测、开发者工具、AI Agent 还是模型服务质量监控这篇文章都会给你一个完整的工程视角。我会从项目要解决的问题讲起然后给出一个最小可运行的实现你可以照着自己搭一个“AI 体感指数”。1. 为什么“AI 变笨了”会被反复讨论却一直说不清楚用户抱怨模型变弱厂商展示的 benchmark 分数却普遍上涨这种割裂在过去两年里反复出现。要理解体验指数的价值先得理解这种割裂是怎么产生的。1.1 个人体感的问题样本太小噪声太大单个用户的“变笨”判断本质上是一个小样本事件。你今天觉得模型写代码变差了可能只是因为这个问题恰好超出了当前模型擅长的分布可能因为你把 prompt 写得更长更复杂也可能因为后端起的是小模型而不是主模型。在大模型的实际工程链路里“变笨”的体感往往来自以下几类问题服务端为控制成本做容量调度高峰期部分请求被路由到更小、更快的模型上下文窗口被塞得太满模型在超长上下文里丢失了早前指令模型供应商悄悄切换了版本、量化精度或推理参数同一个模型名背后已经换了内核客户端配置错误比如 model 配置项缺失、模型名不被当前客户端识别单纯的服务不稳定API 返回 429、超时、capacity 错误。第一种是模型真实能力波动最后几种其实是工程故障或配置故障。普通用户分不清这些层次只会简单归类成“AI 变笨了”。所以个人体感是一条很重要的信号但它不能直接作为结论使用。1.2 官方 Benchmark 的问题离真实使用场景太远官方 benchmark 的问题正好相反它足够标准化但离用户真实任务太远。一个能在 MMLU、HumanEval 上拿到高分的模型在实际使用中仍可能因为指令跟随不稳定、输出过长、拒绝回答、工具调用格式错误等原因让用户崩溃。Benchmark 的数据集是静态的模型厂商理论上可以针对性地优化而用户每天提交的真实任务分布是动态的、长尾的甚至包含大量 benchmark 里完全不会出现的噪音输入。这两种数据其实互补。Benchmark 回答“模型在最优化条件下的潜力有多大”用户观点回答“模型在真实场景下让我满意吗”。过去大家过度关注前者反而忽视了后者。一个把用户观点工程化的体验指数就是想补上这块缺失。1.3 指数要做的是把“情绪”变成“时间序列”做体验指数的人真正要处理的不是“AI 到底笨没笨”而是“用户的负面情绪在什么时间、针对哪个模型、以什么强度出现”。如果能把吐槽文本变成带有模型名、时间、情绪分数的结构化记录再去按天聚合就能得到一条情绪时间序列。这条序列不能直接证明模型能力下降却能告诉我们用户体感是否在恶化、恶化集中在哪个模型、恶化从哪个版本周期开始。这对于模型运营方、Agent 开发者和工具链维护者来说都是高价值信号。2. “AI 模型体验指数”是什么概念、口径与边界2.1 从“指数”这个概念说起指数是把多个样本压缩成一个可比较数值的统计工具。常见的有 NPS净推荐值、消费者信心指数技术圈里也有 TIOBE、Google Trends 等。AI 模型体验指数就是把用户在社区中关于某个模型的反馈文本压缩成一个能反映“近期使用体验好坏”的数值。从项目标题看它是一个由用户观点驱动的模型体验索引。这意味着它的数据来源不是内部日志而是公开的、用户主动发布的内容。这类数据的优点在于真实、多样缺点在于稀疏、不均衡、噪声大。Alpha 版本通常只做一件事让你看到某个模型在过去一周、一个月里的用户反馈走向。2.2 一种可执行的口径定义为了让讨论落地我在这篇文章里给出一个最小可用口径。假设我们把一条用户帖子的“负面强度”定义为 0 到 100 的分数那么一个模型在某天的体验指数可以这样计算Score(post) 模型 m 在帖子 p 中的负面情绪强度0 表示完全不负面100 表示极其负面 Index(m, date) AVG(Score(post)) over posts mention m in recent 7 days neg_ratio(m, date) 负面帖子数 / 提及该模型的帖子总数这里的 Score 可以由 LLM 抽取也可以由规则模型给出。指数不追求单条文本的绝对正确而追求聚合结果在时间维度上的稳定性。换句话说单条情绪判断错了可以接受只要系统性偏差不大趋势信号仍然可信。2.3 与 Benchmark 的对比维度Benchmark用户观点体验指数数据来源人工构造的标准测试题用户在社区中的真实吐槽与好评回答的问题模型能力上限有多高用户最近觉得模型好不好用更新频率依赖评测集发布周期可以实时滚动更新可解释性分数和榜单便于横向比较需要看原帖上下文才能定位问题主要风险可能过拟合、离真实场景远噪声大、样本偏差大、不能代表能力更适合谁模型研发、学术评测应用开发者、运维、产品经理、Agent 工具链团队2.4 边界这是体验指标不是能力指标体验指数最大的边界在于用户口碑差不代表模型能力差。负面反馈上升可能是公共事件导致的情绪集中爆发比如一次糟糕的产品改版、一次模型下线、一个流传很广的“翻车视频”也可能是某个社区的用户本来就不喜欢某个厂商。所以做指数时不能把“用户不满意”直接等同于“模型退化”而是要把数据当成一个“待解释的信号”而不是“已证明的事实”。3. 体验指数的系统设计与数据采集方案3.1 整体分层架构从工程角度看这个项目可以拆成五层。下面用一个文字版的流程描述便于你对照实现采集层 - 清洗层 - 抽取层 - 聚合层 - 展示层 采集层获取各平台公开帖子或评论区内容 清洗层去 HTML、去重复、过滤无关讨论 抽取层识别“提到哪个模型 什么时间 态度倾向 负面强度” 聚合层按 model date 聚合计算平均分、负面占比、样本量 展示层Web 页面或 API输出趋势曲线和原始证据链接这个架构里最容易低估的是清洗层和归一化层。很多初学者会急着训练情绪分类器结果最后发现连“模型名没统一”“同一个帖子抓了三次”“时间字段有时区差异”都没解决。数据没有归一化之前一切高级算法都是空中楼阁。3.2 数据来源与抓取方式要做体验指数第一步是决定从哪些平台采集。不同平台的开放程度差异很大Hacker News 提供官方 Algolia Search API支持按时间、关键词查询适合做英文技术社区反馈Reddit 有 JSON 接口但风控和速率限制比较严格需要遵守平台条款X / Twitter 的采集成本高通常需要付费 API国内平台各有不同的开放策略抓取前务必阅读 robots 协议和相关规定。更稳妥的做法是从产品早期就设计“导入接口”允许通过 CSV/JSON 导入人工收集的帖子再逐步接入官方 API。不要在一开始就把时间浪费在对抗反爬上这既有合规风险也不是项目的核心价值。3.3 清洗与字段设计抓下来的原始数据五花八门。为了让后续的抽取层稳定工作清洗层应该统一输出这样的结构{ post_id: hn_123456, source: hackernews, content: 最近用 gpt-4o 写 SQL连续三次 JOIN 都写错了感觉变笨了, published_at: 2025-03-02T10:15:00Z, fetched_at: 2025-03-02T12:00:00Z, url: https://news.ycombinator.com/item?id123456 }字段越早统一后续模型名识别、情绪抽取、指数聚合就越省力。真正做过这类项目的人会告诉你后面 80% 的调试时间都花在字段不规范上而不是算法上。4. 环境准备与工程目录为了让你能直接跑通一个最小原型我用 Python 作为主语言技术栈选择尽量轻量。4.1 技术栈与版本建议Python 3.10 及以上FastAPI Uvicorn提供指数查询 APIPydantic做数据校验openai SDK作为兼容接口客户端连接 OpenAI 兼容的模型服务SQLite本地存储零部署成本httpx 或 requests采集层预留。以下版本并非限定重点是演示通用实现思路。4.2 工程目录结构ai-model-experience-index/ ├── .env.example ├── requirements.txt ├── data/ │ └── sample_posts.json └── indexer/ ├── __init__.py ├── models.py ├── extract.py ├── aggregate.py ├── cli.py └── api.py4.3 依赖清单# requirements.txt fastapi0.115.0 uvicorn0.30.0 pydantic2.7.0 openai1.40.0 python-dotenv1.0.1 httpx0.27.0# .env.example LLM_API_KEYyour_api_key_here LLM_BASE_URLhttps://api.openai.com/v1 LLM_MODELgpt-4o-mini DB_PATHindex.db配置环境变量是为了不把密钥写死在代码里。在真实项目中密钥管理应该接入专门的配置中心或密钥管理服务而不是只靠 .env。5. 完整示例从用户输入到指数 API下面是一个最小可运行实现。我把它拆成 4 个代码模块每个模块只做一件事。文章里不会真的请求外部模型样例数据也是本地模拟所以你可以直接复制运行。5.1 定义数据模型这一层用 Pydantic 定义采集层的统一结构和抽取结果结构。# indexer/models.py from datetime import datetime from typing import List, Optional from pydantic import BaseModel class RawPost(BaseModel): 清洗后的一条原始帖子。 post_id: str source: str content: str published_at: datetime fetched_at: datetime url: str class ExtractedOpinion(BaseModel): 从帖子中抽取出的观点记录。 post_id: str model: str negative_score: int # 0-100越高表示负面体验越强 sentiment: str # positive / neutral / negative reason: str # LLM 给出的简短解释 evidence: str # 保留可溯源的原始关键句需要说明的是RawPost 已经假定完成了 HTML 清洗和字段抽取。实际采集阶段要把网页正文、标题、评论作者等非结构化内容去掉这一层不在本文展开。5.2 观点抽取优先用 LLM失败时用规则兜底这是全链路的核心。给定一条用户帖子我们要判断它是否在吐槽某个模型、吐槽的强度有多大。# indexer/extract.py import json import os import re from typing import List from dotenv import load_dotenv from openai import OpenAI from indexer.models import ExtractedOpinion, RawPost load_dotenv() LLM_API_KEY os.getenv(LLM_API_KEY, ) LLM_BASE_URL os.getenv(LLM_BASE_URL, https://api.openai.com/v1) LLM_MODEL os.getenv(LLM_MODEL, gpt-4o-mini) MODEL_ALIASES { gpt4o: gpt-4o, gpt-4.o: gpt-4o, gpt4: gpt-4, claude3.5: claude-3.5-sonnet, claude sonnet: claude-3.5-sonnet, deepseek: deepseek-chat, ds: deepseek-chat, } NEGATIVE_WORDS [ 变笨, 变傻, 退步, 变慢, 答非所问, 幻觉, 错误, bad, worse, dumber, broken, slow, useless, fail ] def normalize_model_name(raw: str) - str: 模型名归一化先小写去空格再做常见别名映射。 if not raw: return unknown key raw.strip().lower().replace( , ) return MODEL_ALIASES.get(key, raw.strip().lower()) def _strip_code_fence(text: str) - str: text text.strip() if text.startswith(): lines text.splitlines() lines lines[1:] if lines and lines[-1].strip().startswith(): lines lines[:-1] text \n.join(lines).strip() return text def extract_with_llm(post: RawPost) - ExtractedOpinion: prompt f 你是 AI 模型体验分析师。请阅读下面的用户帖子完成两个任务 1. 判断帖子在讨论哪个 AI 模型模型名用规范英文名例如 gpt-4o、claude-3.5-sonnet。 2. 判断用户对该模型体验的负面强度negative_score 取值范围 0-100。 - 0 表示完全正面或中性 - 40-60 表示轻度不满/犹豫 - 70 以上表示强烈负面比如认为模型明显变笨、不可用、反复出错 只输出 JSON不要输出解释。 帖子内容 {post.content} 输出 JSON 格式 {{model: 模型名, sentiment: negative/neutral/positive, negative_score: 整数, reason: 一句话解释, evidence: 原文中的关键句}} client OpenAI(api_keyLLM_API_KEY, base_urlLLM_BASE_URL) resp client.chat.completions.create( modelLLM_MODEL, messages[{role: user, content: prompt}], temperature0, ) content _strip_code_fence(resp.choices[0].message.content or {}) data json.loads(content) return ExtractedOpinion( post_idpost.post_id, modelnormalize_model_name(data.get(model, unknown)), negative_scoreint(data.get(negative_score, 50)), sentimentdata.get(sentiment, neutral), reasondata.get(reason, ), evidencedata.get(evidence, post.content[:200]), ) def extract_with_rules(post: RawPost) - ExtractedOpinion: 规则兜底识别模型别名和负面词用于离线演示和降级。 text post.content.lower() hit_model unknown for alias, canonical in MODEL_ALIASES.items(): if alias in text: hit_model canonical break if hit_model unknown: for pattern in [rgpt-?4o?, rclaude[ -]?[\d.]*, rdeepseek]: m re.search(pattern, text) if m: hit_model m.group(0) break hit_model normalize_model_name(hit_model) hit_words [w for w in NEGATIVE_WORDS if w.lower() in text] if not hit_words: return ExtractedOpinion( post_idpost.post_id, modelhit_model, negative_score20, sentimentpositive, reason规则兜底未发现明显负面词, evidencepost.content[:200], ) score min(90, 40 10 * len(hit_words)) return ExtractedOpinion( post_idpost.post_id, modelhit_model, negative_scorescore, sentimentnegative, reason规则兜底命中负面词 {}.format(,.join(hit_words)), evidencepost.content[:200], ) def extract_opinion(post: RawPost, use_llm: bool True) - ExtractedOpinion: 优先使用 LLM 抽取若没有 API Key 或解析失败则走规则兜底。 if use_llm and LLM_API_KEY and LLM_API_KEY ! your_api_key_here: try: return extract_with_llm(post) except Exception as exc: # noqa: BLE001 print(fLLM 抽取失败切换到规则兜底{exc}) return extract_with_rules(post)这里的规则兜底不是工程上的最佳方案但它保证了教程在没有外部 API 的情况下可以完整跑通。真实项目里我建议保留这个 fallback 作为流控和降级策略避免模型服务一抖动整个数据管道就停摆。5.3 指数聚合抽取完成后每条帖子会变成带模型名和时间戳的结构化记录。接下来把它写入 SQLite 并聚合。# indexer/aggregate.py import sqlite3 from datetime import datetime from indexer.models import ExtractedOpinion, RawPost CREATE_TABLE_SQL CREATE TABLE IF NOT EXISTS extracted_posts ( post_id TEXT PRIMARY KEY, model TEXT, published_at TEXT, negative_score INTEGER, sentiment TEXT, reason TEXT, evidence TEXT, fetched_at TEXT ); CREATE_INDEX_SQL CREATE INDEX IF NOT EXISTS idx_model_time ON extracted_posts(model, published_at); def init_db(db_path: str) - None: conn sqlite3.connect(db_path) conn.execute(CREATE_TABLE_SQL) conn.execute(CREATE_INDEX_SQL) conn.commit() conn.close() def upsert_opinions(db_path: str, opinions: list[ExtractedOpinion]) - int: 写入抽取结果重复 post_id 直接覆盖。 conn sqlite3.connect(db_path) count 0 for op in opinions: conn.execute( INSERT INTO extracted_posts (post_id, model, published_at, negative_score, sentiment, reason, evidence, fetched_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(post_id) DO UPDATE SET modelexcluded.model, negative_scoreexcluded.negative_score, sentimentexcluded.sentiment, reasonexcluded.reason, evidenceexcluded.evidence , ( op.post_id, op.model, op.published_at.isoformat(), op.negative_score, op.sentiment, op.reason, op.evidence, datetime.utcnow().isoformat(), ), ) count 1 conn.commit() conn.close() return count def compute_daily_index(db_path: str, window_days: int 7) - list[dict]: 按 model 天 聚合只统计最近 window_days 天内的记录。 conn sqlite3.connect(db_path) conn.row_factory sqlite3.Row rows conn.execute( SELECT model, substr(published_at, 1, 10) AS day, COUNT(*) AS sample_count, ROUND(AVG(negative_score), 2) AS avg_negative_score, ROUND( SUM(CASE WHEN negative_score 60 THEN 1 ELSE 0 END) * 1.0 / COUNT(*), 2 ) AS neg_ratio, ROUND( SUM(CASE WHEN sentiment positive THEN 1 ELSE 0 END) * 1.0 / COUNT(*), 2 ) AS pos_ratio FROM extracted_posts WHERE published_at datetime(now, ?) GROUP BY model, day ORDER BY avg_negative_score DESC , (f-{window_days} days,), ).fetchall() conn.close() return [dict(row) for row in rows]注意SQL 里的datetime(now, ?)是按 SQLite 本地时间换算的。真实项目中时间字段应该统一成 UTC 并记录时区避免各国用户发帖时间带来聚合误差。5.4 命令行入口# indexer/cli.py import json import sys from pathlib import Path from indexer.aggregate import compute_daily_index, init_db, upsert_opinions from indexer.extract import extract_opinion from indexer.models import RawPost def load_posts(path: str) - list[RawPost]: with open(path, r, encodingutf-8) as fp: data json.load(fp) return [RawPost(**item) for item in data] def main() - None: if len(sys.argv) 2: print(用法: python -m indexer.cli ingest data.json [--aggregate]) return command sys.argv[1] if command ingest: posts_path sys.argv[2] posts load_posts(posts_path) print(f读取到 {len(posts)} 条帖子) init_db(index.db) opinions [extract_opinion(p, use_llmFalse) for p in posts] saved upsert_opinions(index.db, opinions) print(f写入 {saved} 条观点记录) if command aggregate: init_db(index.db) rows compute_daily_index(index.db, window_days30) for row in rows: print(row) if __name__ __main__: main()5.5 FastAPI 查询接口# indexer/api.py from fastapi import FastAPI, Query from indexer.aggregate import compute_daily_index, init_db app FastAPI(titleAI Model Experience Index API) app.on_event(startup) def on_startup() - None: init_db(index.db) app.get(/index) def get_index(model: str Query(, description模型名不传则返回全部)): rows compute_daily_index(index.db, window_days30) if model: rows [r for r in rows if r[model] model.lower().strip()] return { model: model or all, window_days: 30, data: rows, message: 体验指数只反映用户观点聚合不直接代表模型真实能力 }从这一段你能看出整条数据链路非常短RawPost - ExtractedOpinion - SQLite - API。真实系统比这要重但核心链路并没有本质区别。5.6 演示用样例数据为了不依赖外部采集我准备了一份本地样例数据。它模拟了清洗层输出的格式。// data/sample_posts.json [ { post_id: demo_001, source: demo, content: 最近让 gpt-4o 写一个 Python 装饰器结果参数名写错了三次感觉它真的变笨了。, published_at: 2025-03-01T10:00:00Z, fetched_at: 2025-03-01T11:00:00Z, url: https://example.com/demo/001 }, { post_id: demo_002, source: demo, content: deepseek-chat 今天回答数学题明显变慢了而且逻辑错误比上周多。, published_at: 2025-03-01T12:00:00Z, fetched_at: 2025-03-01T13:00:00Z, url: https://example.com/demo/002 }, { post_id: demo_003, source: demo, content: Claude 3.5 Sonnet 写重构代码还是很稳我挺满意的。, published_at: 2025-03-02T08:00:00Z, fetched_at: 2025-03-02T09:00:00Z, url: https://example.com/demo/003 }, { post_id: demo_004, source: demo, content: gpt-4o 在长文档摘要时又开始幻觉了越长的输入越不靠谱。, published_at: 2025-03-02T09:30:00Z, fetched_at: 2025-03-02T10:00:00Z, url: https://example.com/demo/004 } ]这里的链接是演示用的占位地址不构成真实数据来源。你在实际项目中替换成自己采集到的合法公开内容即可。6. 运行结果与效果验证6.1 启动方式先把工程文件按上面的结构放好然后执行pip install -r requirements.txt python -m indexer.cli ingest data/sample_posts.json python -m indexer.cli aggregate因为没有配置有效的 LLM API Key抽取层会自动走规则兜底所以整个过程不需要外部网络。预期输出大致如下读取到 4 条帖子 写入 4 条观点记录 {model: deepseek-chat, day: 2025-03-01, sample_count: 1, avg_negative_score: 80.0, neg_ratio: 1.0, pos_ratio: 0.0} {model: gpt-4o, day: 2025-03-01, sample_count: 1, avg_negative_score: 80.0, neg_ratio: 1.0, pos_ratio: 0.0} {model: gpt-4o, day: 2025-03-02, sample_count: 1, avg_negative_score: 80.0, neg_ratio: 1.0, pos_ratio: 0.0} {model: claude-3.5-sonnet, day: 2025-03-02, sample_count: 1, avg_negative_score: 20.0, neg_ratio: 0.0, pos_ratio: 1.0}如果看到这些数据说明链路已经走通。你也可以再启动 API 服务uvicorn indexer.api:app --reload --port 8000然后访问curl http://127.0.0.1:8000/index?modelgpt-4o6.2 怎样判断指数是可信的“能跑通”和“结果可信”是两回事。指数系统的可信度可以从三件事上验证抽取一致性随机抽 50 条帖子让两个人手工判断模型名和情绪再和 LLM 抽取结果对比时间稳定性同一个模型的数据如果前后两天指数突然从 30 跳到 90应该能定位到具体帖子样本量可见性低样本量时的指数起伏没有统计意义展示时必须同时呈现 sample_count否则很容易误导。真正上线时更推荐做一份“被纳入样本的帖子清单”点击指数曲线上的某个点可以下钻到具体原帖。没有原始证据支撑的指数本质上只是另一个“感觉”。7. 数据质量与工程陷阱做体验指数这类项目最终会发现难点不在模型能力而在数据质量。下面几个问题几乎每个做舆情聚合项目的人都会遇到。7.1 模型名归一化是最容易被低估的问题用户不会规范地写模型名。你会发现同一条模型有几十种写法gpt4o、GPT-4.o、gpt-4o(2024-11-20)、gpt-4o latest、new gpt。如果不在抽取层做别名映射聚合结果会被拆得七零八落。我在代码里用了一个简单的字典做映射真实项目里这一块会演变成规则引擎 定期人工维护的版本知识库。要注意同一个模型名在不同时间可能指向不同版本快照所以最好在字段里保留model_version或model_snapshot。7.2 时间字段不一致会让时间序列失真每条帖子的时间可能是用户发帖时间、采集时间、页面显示时间。如果你的系统不统一时区聚合出来的“每日指数”会带有很大的随机偏移。推荐的规范是存储层统一用 UTC ISO8601 字符串展示层再按读者本地时区换算分析滚动窗口时以发帖时间published_at为准而不是fetched_at。如果错误使用采集时间一次爬虫半夜重跑会把大量旧帖的“发布时间”错标到当天导致指数曲线出现假性尖峰。7.3 重复帖子会造成虚假信号同一条热门吐槽往往会在多个平台被转载甚至同一个平台也会重复出现。如果不做内容去重一条极端负面帖子可能贡献几十个样本量把指数拉离真实水平。去重思路通常有两种基于post_id的精确去重适合同一平台基于标题/正文 SimHash 或 MinHash 的近似去重适合跨平台转载。最小原型里我用了post_id作为主键。真实项目中建议再加上正文 Hash 字段用来快速发现重复内容。7.4 情绪打分会有系统性偏差规则兜底算法非常简单它倾向于把包含负面词的帖子打高分却没有理解上下文。例如“它没有以前那么笨了”会被规则判成负面而 LLM 能理解这是褒义。长期运行的系统必须避免单一打分模型而是定期抽样用自己的标注集来校准。情绪打分只要保持系统性偏差相对稳定趋势对比还有意义但如果今天用一个规则、明天用一个新模型没有校准就直接切换指标的前后可比性就断了。7.5 不要把“基础设施故障”当成“模型能力下降”做指数系统记录数据时要区分用户