AI 系统的效果从来不只由模型参数量决定。推理延迟、数据管道、缓存策略、失败重试、观测指标和成本控制,都会改变最终用户体验。真正成熟的 AI 基础设施工程,不是不断追逐“更大的模型”,而是把工程决策变成可验证、可复现、可回滚的实验。
这里的“科学判断”不是要求每个工程师都做学术研究,而是要求我们在做架构选择时明确假设、控制变量、测量结果,并承认不确定性。
先定义问题,而不是先选择模型
一个常见的决策顺序是:先选一个热门模型,再围绕它搭建服务,最后才发现延迟、成本或数据质量无法接受。更稳妥的顺序应该反过来:
- 明确用户任务和失败代价。
- 定义可观测指标,例如质量通过率、P95 延迟、单位请求成本和错误率。
- 提出可以被验证的假设。
- 设计小规模对照实验。
- 根据结果决定模型、硬件和服务架构。
例如,“换成更大的模型会提升客服答案质量”只是一个假设。它至少需要和以下因素区分开:提示词是否一致、检索文档是否一致、采样参数是否一致、评测集是否覆盖真实用户问题,以及质量提升是否值得额外延迟和成本。
可以把一次实验写成简单的决策记录:
假设:模型 B 能把答案通过率从 82% 提升到 88%。
控制变量:相同提示词、相同检索结果、相同测试集。
主要指标:质量通过率。
护栏指标:P95 延迟不超过 2 秒,单次请求成本不超过 0.02 美元。
停止条件:连续两轮评测都没有达到质量目标,或任一护栏指标超限。
这种记录看似简单,却能避免团队把“感觉更好”误认为“系统更好”。
AI 基础设施的瓶颈通常在模型之外
模型服务只是链路中的一个环节。一次真实请求可能经过身份认证、请求排队、提示词拼装、检索、重排序、模型推理、工具调用、结果校验和日志写入。任何一环出现长尾,都会放大整体延迟。
工程上需要分别测量端到端延迟和分阶段延迟。例如:
- 排队时间:请求是否因为并发限制等待。
- 检索时间:向量数据库或关键词搜索是否成为瓶颈。
- 首 token 延迟:流式输出能否快速给用户反馈。
- 完整响应时间:生成长度和后处理是否造成长尾。
- 重试比例:失败重试是否掩盖了底层不稳定。
- 单位请求成本:不仅包括 token,也包括 GPU、存储、网络和评测成本。
缓存也需要科学地评估。缓存命中率提高并不一定代表系统更好:如果缓存内容过期,质量可能下降;如果缓存键设计过宽,不同用户可能得到不合适的上下文;如果缓存键设计过窄,又几乎没有节省成本。
因此,建议把缓存命中率和质量、过期率、节省金额放在同一张看板中,而不是单独庆祝命中率上升。
用小实验替代大规模猜测
上线前不一定需要完整的生产流量。可以先建立一组固定评测集,并对候选方案执行离线或影子流量实验。评测集应覆盖正常问题、边界问题、长上下文、恶意输入以及工具调用失败等场景。
下面的脚本可以读取一份 JSONL 实验结果,计算平均延迟、P95 延迟、错误率、平均成本和质量通过率。它不依赖第三方库,适合先作为最小评测工具使用。
将代码保存为 evaluate_run.py:
import json
import math
import statistics
import sys
def percentile(values, p):
values = sorted(values)
index = min(len(values) - 1, math.ceil(len(values) * p) - 1)
return values[index]
def main(path):
rows = []
with open(path, "r", encoding="utf-8") as f:
for line_number, line in enumerate(f, 1):
if not line.strip():
continue
try:
row = json.loads(line)
rows.append(row)
except json.JSONDecodeError as exc:
raise SystemExit(f"第 {line_number} 行不是有效 JSON: {exc}")
if not rows:
raise SystemExit("没有可评估的记录")
latencies = [float(row["latency_ms"]) for row in rows]
errors = sum(1 for row in rows if not bool(row.get("ok", False)))
costs = [float(row.get("cost_usd", 0)) for row in rows]
qualities = [float(row["quality"]) for row in rows if "quality" in row]
result = {
"requests": len(rows),
"avg_latency_ms": round(statistics.mean(latencies), 2),
"p95_latency_ms": round(percentile(latencies, 0.95), 2),
"error_rate": round(errors / len(rows), 4),
"avg_cost_usd": round(statistics.mean(costs), 6),
}
if qualities:
result["quality_pass_rate"] = round(
sum(score >= 0.8 for score in qualities) / len(qualities), 4
)
result["avg_quality"] = round(statistics.mean(qualities), 4)
print(json.dumps(result, ensure_ascii=False, indent=2))
if __name__ == "__main__":
if len(sys.argv) != 2:
raise SystemExit("用法: python evaluate_run.py results.jsonl")
main(sys.argv[1])
准备一份最小输入并运行:
cat > results.jsonl <<'EOF'
{"latency_ms": 820, "ok": true, "cost_usd": 0.008, "quality": 0.91}
{"latency_ms": 1040, "ok": true, "cost_usd": 0.009, "quality": 0.84}
{"latency_ms": 1880, "ok": true, "cost_usd": 0.011, "quality": 0.79}
{"latency_ms": 2310, "ok": false, "cost_usd": 0.015, "quality": 0.42}
{"latency_ms": 960, "ok": true, "cost_usd": 0.008, "quality": 0.88}
EOF
python evaluate_run.py results.jsonl
输出结果可以直接用于候选方案比较。实际项目中,可以在每行加入 model、prompt_version、retriever_version、region 和 trace_id,这样才能定位性能变化究竟来自模型、提示词、检索器还是部署环境。
把不确定性纳入架构设计
AI 系统具有非确定性。同一提示词可能产生不同答案,外部工具可能超时,供应商接口可能改变限流策略。工程系统不能假设这些变化不存在,而应该显式设计边界。
几个实用做法包括:
- 为模型调用设置总超时、连接超时和单独的工具超时。
- 只对幂等请求重试,并使用指数退避和最大重试次数。
- 为输出定义结构化校验;校验失败时不要直接把结果交给下游。
- 保留模型、提示词、检索器和配置版本,确保问题可以复现。
- 使用灰度发布或影子流量验证新版本,而不是一次切换全部请求。
- 为质量下降、成本异常和延迟长尾设置自动告警。
- 保留降级路径,例如小模型、规则回复、人工审核或只读模式。
这里存在明显的权衡:更严格的校验会增加延迟,更完整的日志会增加存储成本,更高的重试次数可能提高成功率但也会放大流量和费用。科学判断不是寻找永远正确的配置,而是让每个权衡都可以被量化,并且能够在指标恶化时快速撤销。
一份可执行的落地清单
可以从一个小范围任务开始,而不是同时改造整个平台:
- 选定一个真实业务场景和一组固定评测样本。
- 为质量、P95 延迟、错误率和成本设定目标及护栏。
- 给模型、提示词、检索器和基础设施配置编号。
- 建立离线评测,再进行小流量线上验证。
- 记录每次实验的假设、变量、结果和结论。
- 预先写好回滚条件,不要等事故发生后才讨论。
- 定期检查评测集是否仍然代表真实流量。
模型能力仍然重要,但它只是系统能力的一部分。优秀的 AI 基础设施团队,不是最早宣布“用了最新模型”的团队,而是能清楚回答“为什么这样选、提升了多少、代价是什么、失败时如何恢复”的团队。