工程化落地
相关笔记:04 框架技术栈 · 8. Spring AI、项目复盘:心理健康 AI 助手、OpenTelemetry 可观测性
前面六篇讲「怎么让它聪明」,这篇讲「怎么让它因为不聪明也不出事」。面试里最值钱的一篇 —— 能讲清成本、稳定性、可观测,说明你真上过线。
1. 分层架构
接入层 HTTP/SSE 接口、鉴权、限流、审计
编排层 Prompt 组装 → 记忆 → RAG → 工具 → 输出校验
能力层 模型网关(多厂商路由/熔断/降级)、向量库、工具集
数据层 会话/消息、知识库、向量、用量账单、评测集
观测层 trace + token 统计 + 质量指标 + 告警接入层 HTTP/SSE 接口、鉴权、限流、审计
编排层 Prompt 组装 → 记忆 → RAG → 工具 → 输出校验
能力层 模型网关(多厂商路由/熔断/降级)、向量库、工具集
数据层 会话/消息、知识库、向量、用量账单、评测集
观测层 trace + token 统计 + 质量指标 + 告警分层的目的:换模型、换向量库、加一个模型厂商,只改一层。反例是把 chatClient.call() 散落在 20 个 Service 里,换模型要改一天。
2. 模型网关:所有调用的唯一出口
必须做网关的五个理由:
| 能力 | 说明 |
|---|---|
| 多厂商路由 | 主备切换、按能力/成本分流 |
| 熔断降级 | 某厂商连续失败就切断,半开探测恢复 |
| 配额与限流 | 按用户/租户/全局三层 |
| 统一日志 | 每次调用记录 model/prompt 版本/token/耗时 |
| 成本归因 | 谁在花钱、花在哪个功能上 |
2.1 三态熔断器
CLOSED(正常放行) --失败率超阈值--> OPEN(直接拒绝)
↑ │
└──── HALF_OPEN(放少量探测) <──── 冷却时间到CLOSED(正常放行) --失败率超阈值--> OPEN(直接拒绝)
↑ │
└──── HALF_OPEN(放少量探测) <──── 冷却时间到参数经验:统计窗口 10s、最小请求数 20、失败率阈值 50%、OPEN 冷却 30s、HALF_OPEN 放行 3 个。
2.2 降级链
主力模型 → 重试 1 次 → 备用模型(另一厂商)→ 规则/模板回答 → 固定话术主力模型 → 重试 1 次 → 备用模型(另一厂商)→ 规则/模板回答 → 固定话术关键是每一层都有明确的触发条件和用户可见的表现,不能静默失败。
3. 成本工程
| 手段 | 典型收益 |
|---|---|
max_tokens 限制输出 | 防跑飞,兜底 |
| 上下文瘦身(Top-K 片段) | 输入 token 降一个数量级 |
| 模型路由(简单问题走小模型) | 成本降 50%~80% |
| 结果缓存(问题归一化后哈希) | 高频问题零成本 |
| Prompt 缓存(厂商前缀缓存) | 长系统提示词场景显著 |
| 流式 + 用户中断 | 省下未输出的 token |
| 批处理(离线任务) | 部分厂商有批量折扣 |
先把账算清:
单次成本 = 输入token × 输入单价 + 输出token × 输出单价
日成本 = 日请求数 × 单次成本单次成本 = 输入token × 输入单价 + 输出token × 输出单价
日成本 = 日请求数 × 单次成本一个带 3 段 RAG 的问答大约 2000~4000 输入 token + 300~800 输出 token。按单价一乘就知道量级,先算再优化。
4. 性能:三个数决定体验
| 指标 | 含义 | 优化方向 |
|---|---|---|
| TTFT(首 token 延迟) | 用户点完到看到第一个字 | 缩短输入、prefill 前置、模型路由、边缘部署 |
| TPOT(每 token 时间) | 出字速度 | 换更快的模型/推理引擎 |
| P95 总时长 | 长回答的尾部延迟 | max_tokens、控制输出长度、流式 |
工程要点:
- 一定要流式(SSE),否则用户白屏等待;
- 流式占线程 → 用 WebFlux/Netty 或异步 Servlet,别用 Tomcat 线程硬扛(详见 项目复盘 的踩坑 10.6);
- 网关/代理要关缓冲(
proxy_buffering off)、加长超时、发心跳。
5. 稳定性四件套
| 件 | 要点 |
|---|---|
| 超时 | 首 token 超时(如 10s)+ 总超时(如 120s),别用默认值 |
| 重试 | 只重试幂等请求;指数退避 + 抖动;最多 1~2 次 |
| 幂等 | 有副作用的操作(下单)带幂等键,重试不重复执行 |
| 限流 | 用户级(防刷)+ 租户级(防挤占)+ 全局级(防打爆) |
限流算法按场景选:令牌桶(允许突发)、漏桶(平滑)、滑动窗口(精确)。
6. 可观测:四个必看指标
| 指标 | 为什么 |
|---|---|
| TTFT / 总耗时 P95 | 用户体验的主指标 |
| token 消耗(输入/输出分开) | 成本 |
| 失败率 / 降级率 | 稳定性 |
| 检索命中率 / 空召回率 | RAG 效果(有 RAG 才需要) |
日志必须能串起来:traceId → prompt 版本 → 检索到的 docId → 模型 → token → 耗时 → 是否降级。
出了问题第一句话应该是「把 traceId 给我」,而不是「我本地复现一下」。
7. 评测与发布:别靠感觉上线
| 环节 | 做法 |
|---|---|
| 评测集 | 50~200 条真实问题 + 期望输出/评分规则,覆盖主流程 + 边界 + 拒答 |
| 离线回归 | 每次改 Prompt/模型/检索参数都跑一遍,看指标不能倒退 |
| 灰度 | 按用户/流量比例切新版,观察 24h |
| A/B | 关键改动做对照,看业务指标(不是只看模型指标) |
| 回滚 | Prompt、模型、检索参数都要能一键切回 |
发布清单(很实用):
- [ ] 评测集指标不倒退
- [ ] 成本增量在预算内
- [ ] 降级链路演练过(手动切断主力模型能正常兜底)
- [ ] 日志能定位到本次改动的版本号
- [ ] 灰度人群和回滚方式明确
8. 安全与合规
| 项 | 做法 |
|---|---|
| 数据脱敏 | 手机号/身份证/银行卡在进模型前替换占位符 |
| 数据出境 | 确认模型服务的部署地域与合规要求 |
| 内容安全 | 输入输出双向过滤,命中走固定话术 |
| 越权 | 工具内部鉴权,不信模型参数 |
| 留痕 | 完整的请求/响应/工具调用记录,可审计 |
| 用户权利 | 支持删除会话与记忆 |
9. 团队协作:把 AI 当软件工程做
| 资产 | 管理方式 |
|---|---|
| Prompt | 独立目录 + 版本号 + review + 变更记录 |
| 评测集 | 版本化,和 Prompt 版本对应 |
| 标注规范 | 写清「什么算好答案」,否则标注不一致 |
| 失败样本库 | 线上 badcase 持续回流,作为下一轮评测集 |
| 上线记录 | 每次变更记「改了什么、为什么、指标变化」 |
这条最容易被忽略:AI 项目的技术债主要是「不可复现」 —— 换个人跑同样的输入得到不同结果,还没人知道当时用的是哪个 Prompt 版本。
10. 面试怎么讲(把这七篇串成一段)
「我做的是一条可控的 AI 链路:模型侧做网关,多厂商 + 三态熔断 + 降级到固定话术;Prompt 进版本库,配 50 条评测集做回归;RAG 用混合检索 + Rerank,只看 Recall@5 和忠实度这两个指标;上下文做了滑动窗口 + 摘要;成本上建了用量表,按用户和模型归因,再用模型路由把简单请求切到小模型;稳定性上超时/重试/幂等/限流四件套齐全,SSE 关掉 Nginx 缓冲、异常不交给全局处理器。上线前跑评测集,灰度 24 小时再放量。」
这段话里几乎每个短语都能被追问,而追问的深度刚好由前六篇撑着。
11. 速查表
| 问题 | 一句话 |
|---|---|
| 为什么要模型网关 | 多厂商路由、熔断、配额、统一日志、成本归因 |
| 三态熔断 | CLOSED / OPEN / HALF_OPEN,半开探测恢复 |
| 成本怎么砍 | max_tokens + 上下文瘦身 + 模型路由 + 缓存 + 配额 |
| TTFT 怎么优化 | 砍输入、模型路由、流式、边缘部署 |
| 稳定性四件套 | 超时、重试(幂等)、幂等键、三层限流 |
| 必看指标 | TTFT P95、token 消耗、失败/降级率、空召回率 |
| 上线流程 | 评测集回归 → 灰度 → A/B → 可回滚 |
| 最容易被忽略 | Prompt 版本管理,出事时不知道用的哪版 |
| 安全底线 | 工具内部鉴权、输入输出过滤、全量留痕、可删除 |
Cao Note