📐 系统设计答题模板
面试中系统设计题的标准答题框架,覆盖 LLM 应用最常见的 5 个系统设计题
目录
通用答题框架
无论遇到什么系统设计题,都可以按以下 5 步展开。每步建议时间分配如下(45 分钟面试):
| 步骤 | 内容 | 建议时间 |
|---|---|---|
| Step 1 | 需求澄清 | 5 分钟 |
| Step 2 | 高层架构 | 10 分钟 |
| Step 3 | 核心流程 | 10 分钟 |
| Step 4 | 详细设计 | 12 分钟 |
| Step 5 | 扩展讨论 | 8 分钟 |
Step 1: 需求澄清
目的:明确边界,展示你的思考深度。
功能需求(Functional Requirements):
- 系统需要做什么?核心用例是什么?
- 用户是谁?(开发者 / 终端用户 / 内部平台)
- 输入输出是什么格式?
- 关键 API 接口长什么样?
非功能需求(Non-Functional Requirements):
- 延迟:P50 / P99 要求是多少?
- 吞吐量:QPS / 并发用户数?
- 可用性:几个 9?
- 一致性 vs 可用性:倾向哪边?
- 成本约束:预算范围?
- 安全合规:数据隐私 / 地域限制?
提问模板:
"在开始设计之前,我想确认几个关键问题……"
- "系统的日活用户 / QPS 量级大概是多少?"
- "对延迟的容忍度是多少?是同步返回还是可以异步?"
- "数据量级有多大?文档数量 / 存储大小?"
- "是否有成本限制?对模型调用费用的预算?"
- "是否有多租户需求?数据隔离要求?"
Step 2: 高层架构
目的:用最简单的图展示系统全貌。
原则:
- 先画核心路径(Happy Path)
- 标注每个组件的职责(一句话)
- 标注数据流向(箭头)
- 暂不深入细节,留到 Step 4
画图模板:
用户请求 → [接入层] → [核心逻辑层] → [存储层]
↓
[外部服务]Step 3: 核心流程
目的:描述一个典型请求从进到出的完整生命周期。
原则:
- 选一个最重要的用例展开
- 标注每一步的输入 / 输出
- 标注可能的失败点和处理方式
- 说明同步 vs 异步边界
Step 4: 详细设计
目的:挑 2-3 个关键组件深入。
选择标准:
- 最有技术挑战的组件
- 面试官可能追问的方向
- 你最擅长的领域
深入内容:
- 数据模型 / Schema
- 算法选择及理由
- 技术选型及 trade-off
- 性能优化手段
Step 5: 扩展讨论
目的:展示全局视野和工程经验。
常见扩展话题:
- 水平扩展:如何支撑 10x 流量?
- 监控告警:关键指标有哪些?如何设置告警?
- 成本优化:如何降低模型调用成本?
- 容灾:单点故障如何避免?
- A/B 测试:如何验证新模型 / 新策略的效果?
- 安全:如何防止 Prompt 注入?数据泄露?
模板一:RAG 系统设计
场景:设计一个企业级知识库问答系统,支持对海量文档进行语义检索并生成准确回答。
1. 需求分析
功能需求:
| ID | 需求 | 优先级 |
|---|---|---|
| F1 | 支持上传 PDF/Word/HTML/Markdown 文档 | P0 |
| F2 | 对文档内容进行语义检索 | P0 |
| F3 | 基于检索结果生成回答,附带引用来源 | P0 |
| F4 | 支持多轮对话,理解上下文 | P1 |
| F5 | 支持文档权限控制(多租户) | P1 |
| F6 | 支持增量更新(新文档实时可查) | P2 |
非功能需求:
| 指标 | 要求 |
|---|---|
| 检索延迟 | P99 < 200ms |
| 端到端延迟 | P99 < 3s(含生成) |
| 吞吐量 | 100 QPS(检索) |
| 可用性 | 99.9% |
| 文档规模 | 100 万篇文档,平均 5000 token/篇 |
| 准确率 | Recall@10 > 95%,Answer 准确率 > 90% |
2. 架构图
┌─────────────────────────────────────────────────────────────────────┐
│ 用户 / API 客户端 │
└──────────────────────────────┬──────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────┐
│ API Gateway / 接入层 │
│ 认证鉴权 · 限流 · 路由 · 请求日志 │
└──────────────────────────────┬───────────────────────────────────────┘
│
┌────────────────┼────────────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ 文档上传 │ │ 检索服务 │ │ 生成服务 │
│ & 处理 │ │ (Retrieval)│ │(Generation)│
└──────┬─────┘ └──────┬─────┘ └──────┬─────┘
│ │ │
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ 文档处理 │ │ 向量数据库 │ │ LLM API │
│ Pipeline │ │ (Milvus/ │ │ (GPT-4/ │
│ │ │ Pinecone) │ │ Claude) │
└──────┬─────┘ └────────────┘ └────────────┘
│ ▲
▼ │
┌────────────┐ ┌────────────┐
│ 对象存储 │ │ 关键词索引 │
│ (S3/MinIO) │ │(ES/Meilisearch)│
└────────────┘ └────────────┘3. 核心组件列表
| 组件 | 职责 | 技术选型 |
|---|---|---|
| API Gateway | 认证、限流、路由 | Kong / Nginx / 自建 |
| 文档处理服务 | 解析、分块、向量化 | LangChain / LlamaIndex |
| 向量数据库 | 存储和检索 embedding | Milvus / Pinecone / Qdrant |
| 关键词索引 | BM25 全文检索 | Elasticsearch / Meilisearch |
| 检索服务 | 混合检索 + 重排序 | 自建服务 |
| 生成服务 | Prompt 组装 + LLM 调用 | 自建服务 |
| LLM API | 大模型推理 | OpenAI / Claude / 自部署 |
| 对象存储 | 原始文档存储 | S3 / MinIO |
| 元数据库 | 文档元信息、权限 | PostgreSQL |
4. 数据流
文档索引流程:
1. 用户上传文档 → API Gateway → 文档上传服务
2. 文档存储到 S3,元信息写入 PostgreSQL
3. 文档处理 Pipeline 启动:
a. 文档解析(PDF → 纯文本,保留结构)
b. 文本分块(Chunk Size=512, Overlap=64)
c. 每个 Chunk 生成 embedding(text-embedding-3-small)
d. Chunk + embedding 写入向量数据库
e. Chunk 文本写入 ES 关键词索引
4. 索引完成,文档即可被检索查询应答流程:
1. 用户提问 → API Gateway → 检索服务
2. 检索服务执行混合检索:
a. 向量检索:query embedding → Top-K=50 from 向量库
b. 关键词检索:BM25 → Top-K=50 from ES
c. 融合排序(RRF / 加权合并)→ Top-K=10
d. 重排序(Cross-encoder Reranker)→ Top-5
3. 生成服务组装 Prompt:
a. System Prompt + 检索到的 5 个 Chunk + 用户问题
b. 调用 LLM 生成回答
4. 返回回答 + 引用来源5. 关键设计决策
5.1 分块策略(Chunking)
| 策略 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 固定大小分块 | 实现简单 | 可能切断语义 | 通用文本 |
| 语义分块 | 保持语义完整 | 计算开销大 | 结构化文档 |
| 递归分块 | 平衡效果和复杂度 | 需调参 | 推荐默认方案 |
| 文档结构分块 | 利用标题/段落 | 依赖文档格式 | PDF/Word |
推荐配置:
- Chunk Size: 512 tokens
- Overlap: 64 tokens(约 12.5%)
- 分隔优先级: ["\n\n", "\n", "。", ".", " "]5.2 检索策略
为什么需要混合检索?
- 向量检索擅长语义匹配("公司休假政策" ≈ "年假规定")
- 关键词检索擅长精确匹配(产品型号、人名、数字)
- 混合检索覆盖率最高
融合算法 — Reciprocal Rank Fusion (RRF):
score(d) = Σ 1 / (k + rank_i(d))
其中 k=60(经验常数),rank_i(d) 是 d 在第 i 个检索器中的排名5.3 重排序(Reranking)
第一阶段:粗召回(Bi-encoder)→ 快速,Recall 高
第二阶段:精排(Cross-encoder)→ 慢但精准
为什么不在第一阶段就用 Cross-encoder?
→ Cross-encoder 对每个 (query, doc) 对都要计算,O(n) 复杂度
→ 对 100 万文档做 Cross-encoder 排序不现实
→ 所以先用 Bi-encoder 缩小到 Top-50,再用 Cross-encoder 精排5.4 生成优化
Prompt 模板:
你是一个企业知识库助手。请基于以下参考文档回答用户问题。
规则:
1. 只基于参考文档回答,不要编造信息
2. 如果参考文档中没有相关信息,请明确说"未找到相关内容"
3. 在回答中标注引用来源,格式:[来源: 文档名]
参考文档:
{context_chunks}
用户问题:{question}减少幻觉的手段:
- 明确指令"不要编造"
- 要求引用来源(可验证性)
- 温度设低(temperature=0.1)
- 加入"不知道就说不知道"的指令
6. 评估体系
| 维度 | 指标 | 计算方式 |
|---|---|---|
| 检索质量 | Recall@K | Top-K 中包含正确文档的比例 |
| 检索质量 | MRR | 第一个正确结果的倒数排名 |
| 检索质量 | nDCG | 考虑排名位置的增益 |
| 生成质量 | Faithfulness | 回答是否忠于检索内容 |
| 生成质量 | Relevance | 回答是否回答了问题 |
| 生成质量 | 引用准确性 | 引用来源是否正确 |
| 端到端 | 人工评估 | 标注员评分(1-5 分) |
自动化评估 Pipeline:
1. 准备 Golden Dataset(问题 + 标准答案 + 相关文档)
2. 每次模型/策略变更后自动跑评估
3. 对比 Recall、Answer Quality 等指标
4. 回归检测:新版本不能比旧版本差7. 扩展方案
水平扩展:
- 向量数据库:分片 + 副本(Milvus 支持自动分片)
- 检索服务:无状态,水平扩展
- 生成服务:队列 + Worker 池
成本优化:
- 小问题用小模型,复杂问题用大模型(路由策略)
- 缓存高频问题的回答
- 批量 embedding 调用(降低 API 成本)
- 自部署 embedding 模型(高量级时更划算)
监控指标:
- 检索延迟 P50/P99
- 生成延迟 P50/P99
- 向量库 QPS 和内存使用
- LLM Token 消耗量
- 用户满意度(点赞/点踩)
模板二:Agent 平台设计
场景:设计一个通用 AI Agent 平台,支持注册工具、编排多步骤任务、多 Agent 协作。
1. 需求分析
功能需求:
| ID | 需求 | 优先级 |
|---|---|---|
| F1 | 支持注册自定义工具(API/函数/脚本) | P0 |
| F2 | Agent 能自主决定调用哪些工具完成任务 | P0 |
| F3 | 支持多步骤任务编排(ReAct / Plan-and-Execute) | P0 |
| F4 | 记忆系统:短期(会话)+ 长期(持久化) | P1 |
| F5 | 多 Agent 协作(主 Agent 分发子任务) | P1 |
| F6 | 安全沙箱执行代码 / 工具调用 | P0 |
| F7 | 可观测性:执行 trace、日志、成本追踪 | P1 |
非功能需求:
| 指标 | 要求 |
|---|---|
| 任务延迟 | 单步 < 10s,完整任务 < 2min |
| 并发任务 | 支持 1000 并发 Agent 会话 |
| 可用性 | 99.9% |
| 工具调用安全 | 沙箱隔离,权限控制 |
| 可扩展性 | 新工具注册 < 5 分钟 |
2. 架构图
┌──────────────────────────────────────────────────────────────┐
│ 用户 / 客户端 │
└─────────────────────────────┬────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ API Gateway │
└─────────────────────────────┬────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ Agent 调度引擎 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Planner │ │ Executor │ │ Memory │ │ Observer │ │
│ │ (规划器) │ │ (执行器) │ │ (记忆管理)│ │ (监控器) │ │
│ └──────────┘ └─────┬────┘ └──────────┘ └──────────┘ │
└────────────────────────┼─────────────────────────────────────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│ 工具注册表 │ │ 沙箱执行 │ │ LLM 调用 │
│ (Registry)│ │ (Sandbox) │ │ (多模型) │
└───────────┘ └───────────┘ └───────────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│ 数据库 │ │ 消息队列 │ │ 对象存储 │
│ (Postgres)│ │ (Redis) │ │ (S3) │
└───────────┘ └───────────┘ └───────────┘3. 核心组件列表
| 组件 | 职责 | 技术选型 |
|---|---|---|
| Agent 调度引擎 | 解析意图、规划步骤、协调执行 | 自建(核心) |
| Planner | 任务分解、制定执行计划 | LLM + Chain-of-Thought |
| Executor | 逐步执行计划、处理异常 | 自建 |
| Memory Manager | 短期/长期记忆管理 | Redis + PostgreSQL |
| Observer | 执行监控、trace 收集 | OpenTelemetry |
| 工具注册表 | 工具元信息、Schema 管理 | PostgreSQL + 缓存 |
| 沙箱执行环境 | 隔离执行代码/API 调用 | Docker / gVisor / WASM |
| LLM 服务 | 推理能力 | 多模型路由 |
4. 数据流
任务执行流程:
1. 用户提交任务:"帮我分析这个 CSV 文件并生成报告"
2. Agent 调度引擎接收任务
3. Planner 调用 LLM 生成执行计划:
- Step 1: 读取 CSV 文件(工具: file_reader)
- Step 2: 数据分析(工具: python_sandbox)
- Step 3: 生成图表(工具: python_sandbox)
- Step 4: 撰写报告(工具: text_generator)
4. Executor 逐步执行:
a. 从工具注册表获取工具 Schema
b. 组装工具调用参数
c. 在沙箱中执行
d. 收集结果,更新 Memory
e. 将结果传给下一步
5. Observer 记录每步的 trace、延迟、token 消耗
6. 最终结果返回给用户5. 关键设计决策
5.1 工具注册机制
// 工具注册 Schema
{
"tool_id": "sql_query",
"name": "数据库查询",
"description": "执行只读 SQL 查询并返回结果",
"parameters": {
"type": "object",
"properties": {
"query": { "type": "string", "description": "SQL 查询语句" },
"database": { "type": "string", "description": "数据库名" }
},
"required": ["query"]
},
"permissions": ["read:database"],
"sandbox": true,
"timeout_ms": 30000,
"retry_policy": { "max_retries": 2, "backoff_ms": 1000 }
}5.2 记忆系统设计
┌─────────────────────────────────────────────┐
│ 记忆系统架构 │
├─────────────┬───────────────────────────────┤
│ 工作记忆 │ 当前会话上下文(LLM context) │
│ (Working) │ 容量: 模型 context window │
│ │ 存储: 内存 │
├─────────────┼───────────────────────────────┤
│ 短期记忆 │ 最近 N 轮对话历史 │
│ (Short-term)│ 容量: 最近 50 轮 │
│ │ 存储: Redis TTL=24h │
├─────────────┼───────────────────────────────┤
│ 长期记忆 │ 用户偏好、历史任务、学到的知识 │
│ (Long-term) │ 容量: 无限 │
│ │ 存储: PostgreSQL + 向量检索 │
└─────────────┴───────────────────────────────┘记忆检索策略:
- 每次任务开始时,用当前任务描述做向量检索,拉取相关长期记忆
- 结合短期记忆(最近对话)一起注入 Prompt
- 记忆总量控制在 context window 的 30% 以内
5.3 安全沙箱
安全层级:
1. 权限控制:每个工具声明所需权限,Agent 需要授权才能调用
2. 输入校验:SQL 注入检测、路径遍历检测、命令注入检测
3. 执行隔离:Docker 容器 / gVisor / WASM 沙箱
4. 资源限制:CPU/内存/网络/磁盘限额
5. 审计日志:所有工具调用记录在案5.4 多 Agent 协作
┌─────────────┐
│ 主 Agent │ 接收用户任务,分解子任务
│ (Orchestrator)│
└──────┬──────┘
│ 分发子任务
┌────┼────┐
▼ ▼ ▼
┌────┐┌────┐┌────┐
│ A1 ││ A2 ││ A3 │ 专业 Agent(各自擅长不同领域)
│搜索 ││分析 ││写作 │
└──┬─┘└──┬─┘└──┬─┘
│ │ │
└─────┼─────┘
▼
┌──────────┐
│ 汇总结果 │ 主 Agent 汇总并返回
└──────────┘协调模式:
- Sequential:串行执行,前一步输出作为后一步输入
- Parallel:并行执行独立子任务
- Hierarchical:主 Agent 分发,子 Agent 独立执行后汇报
- Debate:多个 Agent 对同一问题给出不同观点,综合判断
6. 扩展讨论
如何处理 Agent 循环 / 跑飞?
- 设置最大步骤数(如 20 步)
- 设置总 Token 消耗上限
- 检测重复动作(相同工具 + 参数调用 3 次 → 中断)
- 人类介入机制(Human-in-the-Loop)
可观测性指标:
- 每个任务的执行 trace(步骤、耗时、token)
- 工具调用成功率 / 失败率
- 平均任务完成步骤数
- Token 消耗分布
- 用户满意度
成本优化:
- 简单任务用小模型(GPT-4o-mini),复杂任务用大模型
- 缓存常见工具调用结果
- 批量工具调用合并
模板三:模型网关设计
场景:设计一个统一的 LLM API 网关,对接多个模型供应商,提供路由、限流、缓存、计费等能力。
1. 需求分析
功能需求:
| ID | 需求 | 优先级 |
|---|---|---|
| F1 | 统一 API 接口(兼容 OpenAI 格式) | P0 |
| F2 | 支持多供应商路由(OpenAI/Claude/自部署) | P0 |
| F3 | 请求限流(按用户/按 API Key) | P0 |
| F4 | 响应缓存(语义缓存 + 精确缓存) | P1 |
| F5 | 用量计费和配额管理 | P0 |
| F6 | 请求/响应日志记录 | P1 |
| F7 | 流式响应支持(SSE) | P0 |
| F8 | 熔断降级(供应商故障时自动切换) | P1 |
非功能需求:
| 指标 | 要求 |
|---|---|
| 网关延迟 | 额外开销 < 50ms |
| 吞吐量 | 10,000 QPS |
| 可用性 | 99.99% |
| 数据一致性 | 计费数据不能丢失 |
2. 架构图
┌──────────────────────────────────────────────────────────┐
│ 应用 / API 客户端 │
└─────────────────────────┬────────────────────────────────┘
│ (OpenAI 兼容 API)
▼
┌──────────────────────────────────────────────────────────┐
│ API Gateway (Nginx/Kong) │
│ TLS 终止 · 负载均衡 · 基础限流 │
└─────────────────────────┬────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ 模型网关核心服务 │
│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐│
│ │认证鉴权 │ │路由引擎 │ │限流熔断 │ │ 缓存 │ │ 计费 ││
│ └────────┘ └───┬────┘ └────────┘ └────────┘ └────────┘│
└──────────────────┼───────────────────────────────────────┘
│
┌───────────┼───────────┐
▼ ▼ ▼
┌───────────┐┌───────────┐┌───────────┐
│ OpenAI ││ Claude ││ 自部署模型 │
│ API ││ API ││ (vLLM) │
└───────────┘└───────────┘└───────────┘
辅助服务:
┌───────────┐┌───────────┐┌───────────┐
│ PostgreSQL││ Redis ││ClickHouse │
│ (配额/计费)││ (限流/缓存)││ (日志/监控)│
└───────────┘└───────────┘└───────────┘3. 核心组件列表
| 组件 | 职责 | 技术选型 |
|---|---|---|
| 认证鉴权 | API Key 校验、权限验证 | 自建 + Redis |
| 路由引擎 | 根据策略选择模型供应商 | 自建 |
| 限流器 | 令牌桶 / 滑动窗口限流 | Redis + Lua |
| 熔断器 | 供应商健康检测和自动降级 | 自建(滑动窗口) |
| 缓存层 | 精确缓存 + 语义缓存 | Redis + 向量库 |
| 计费服务 | Token 统计、费用计算 | 异步写入 ClickHouse |
| 日志服务 | 请求/响应全量记录 | Kafka → ClickHouse |
4. 数据流
请求处理流程:
1. 客户端发送请求(OpenAI 格式)
2. 认证鉴权:校验 API Key → 获取用户信息和配额
3. 缓存检查:
a. 精确缓存:hash(prompt + model + params) → 命中则直接返回
b. 语义缓存:embedding similarity > 0.95 → 命中则返回
4. 限流检查:令牌桶算法,超限返回 429
5. 路由引擎选择供应商:
a. 根据 model 参数映射到供应商列表
b. 考虑:权重、延迟、成本、健康状态
c. 选择最优供应商
6. 熔断检查:供应商是否处于熔断状态
7. 转发请求到供应商 API
8. 流式响应透传 / 非流式响应收集
9. 异步写入:
a. Token 计数 → 计费服务
b. 请求日志 → ClickHouse
c. 响应写入缓存
10. 返回响应给客户端5. 关键设计决策
5.1 路由策略
# 路由决策逻辑(伪代码)
def select_provider(request):
candidates = MODEL_MAPPING[request.model] # 如 "gpt-4" → [openai, azure]
# 过滤熔断中的供应商
candidates = [c for c in candidates if not circuit_breaker.is_open(c)]
# 根据策略排序
if strategy == "latency":
candidates.sort(key=lambda c: c.avg_latency)
elif strategy == "cost":
candidates.sort(key=lambda c: c.cost_per_token)
elif strategy == "weighted":
candidates.sort(key=lambda c: c.weight, reverse=True)
return candidates[0]5.2 限流设计
三层限流:
1. 全局限流:保护后端总容量(如 10000 QPS)
2. 用户限流:按 API Key 限制(如 100 QPS/用户)
3. 模型限流:按模型限制(如 GPT-4 500 QPS)
算法:滑动窗口令牌桶
- 存储在 Redis 中(原子操作,Lua 脚本)
- 返回 X-RateLimit-* 响应头5.3 缓存策略
| 缓存类型 | 命中条件 | 存储 | 适用场景 |
|---|---|---|---|
| 精确缓存 | hash 完全匹配 | Redis | temperature=0 的请求 |
| 语义缓存 | embedding 相似度 > 0.95 | Redis + 向量库 | 常见问题 |
缓存 key 设计:
精确:hash(model + messages + temperature + max_tokens)
语义:embedding(messages[-1]) 最近邻搜索
缓存失效:
- TTL: 24h(可配置)
- 手动清除:按 prefix 清除
- 不缓存:stream=true 的请求5.4 计费设计
计费模型:
- 按 Token 计费(输入 Token + 输出 Token 分开计价)
- 不同模型不同单价
- 支持预付费(配额)和后付费(账单)
数据流:
请求完成 → 异步发送到 Kafka → 计费服务消费
→ 实时扣减 Redis 中的配额
→ 异步写入 ClickHouse 做账单聚合6. 扩展讨论
如何支撑 10x 流量?
- 网关无状态,水平扩展
- Redis Cluster 分片
- ClickHouse 分布式表
- 缓存命中率优化(减少后端调用)
监控关键指标:
- 每个供应商的 QPS、延迟 P50/P99、错误率
- 缓存命中率(精确 vs 语义)
- 限流触发率
- 熔断触发次数
- Token 消耗量和费用
模板四:多模型 Fallback 系统
场景:设计一个高可用的 LLM 调用层,当主模型不可用时自动降级到备选模型,保证服务连续性。
1. 需求分析
功能需求:
| ID | 需求 | 优先级 |
|---|---|---|
| F1 | 定义模型优先级链(主模型 → 备选模型 → 兜底模型) | P0 |
| F2 | 实时健康检查(延迟、错误率) | P0 |
| F3 | 自动故障检测和切换(秒级) | P0 |
| F4 | 动态路由(基于任务类型选择最优模型) | P1 |
| F5 | 降级策略(功能降级 vs 质量降级) | P1 |
| F6 | 故障恢复后自动切回主模型 | P1 |
| F7 | 成本追踪和优化 | P2 |
非功能需求:
| 指标 | 要求 |
|---|---|
| 故障检测时间 | < 5s |
| 切换时间 | < 2s |
| 切换期间可用性 | 99.9%(允许降级) |
| 数据一致性 | 切换不能丢请求 |
2. 架构图
┌─────────────────────────────────────────────────────┐
│ 应用服务层 │
└────────────────────────┬────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 模型 Fallback 引擎 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 健康检查 │ │ 路由决策 │ │ 降级策略 │ │
│ │ Monitor │ │ Router │ │ Degrader │ │
│ └──────────┘ └────┬─────┘ └──────────┘ │
│ │ │
│ ┌──────────────────┴──────────────────────┐ │
│ │ 优先级链 (Priority Chain) │ │
│ │ L1: GPT-4o → L2: Claude 3.5 → L3: 本地 │ │
│ └─────────────────────────────────────────┘ │
└────────────────────────┬────────────────────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ OpenAI │ │ Claude │ │ 本地模型 │
│ API │ │ API │ │ (vLLM) │
└──────────┘ └──────────┘ └──────────┘
健康状态存储:Redis (实时状态) + ClickHouse (历史数据)3. 核心组件列表
| 组件 | 职责 | 技术选型 |
|---|---|---|
| 健康检查器 | 定期探测模型可用性 | 自建(定时任务) |
| 路由决策器 | 根据健康状态和策略选择模型 | 自建 |
| 降级策略器 | 定义降级行为和质量边界 | 自建 |
| 状态存储 | 记录各模型实时健康状态 | Redis |
| 指标收集 | 延迟、错误率、token 用量 | Prometheus + ClickHouse |
| 配置中心 | 优先级链、阈值等动态配置 | etcd / Apollo |
4. 数据流
正常请求流程:
1. 请求进入 Fallback 引擎
2. 路由决策器查询各模型健康状态(Redis)
3. 选择优先级最高且健康的模型
4. 发送请求 → 返回响应
5. 更新该模型的延迟和成功率指标Fallback 流程:
1. 请求发送到主模型(GPT-4o)
2. 超时 / 5xx 错误 / 限流(429)
3. 触发 Fallback:
a. 记录主模型失败(更新 Redis 状态)
b. 检查下一级模型(Claude 3.5)健康状态
c. 如果健康 → 重试请求到 Claude 3.5
d. 如果不健康 → 继续下一级(本地模型)
4. 返回响应(标注实际使用的模型)
5. 异步告警:主模型故障通知恢复流程:
1. 健康检查器定期(每 10s)发送探测请求到故障模型
2. 连续 N 次(如 3 次)成功 → 标记为恢复
3. 使用金丝雀策略:先将 10% 流量切回
4. 观察 5 分钟无异常 → 逐步恢复到 100%5. 关键设计决策
5.1 健康状态机
状态转换图:
┌──────┐ 连续3次失败 ┌────────┐ 探测成功3次 ┌──────────┐
│ 健康 │──────────────→│ 故障 │─────────────→│ 恢复中 │
│(Healthy)│ │ (Down) │ │(Recovering)│
└───┬───┘ └────────┘ └─────┬────┘
│ │
│ 5分钟金丝雀无异常 │
│←───────────────────────────────────────────────┘
│ │
│ 金丝雀期间出错 │
│ ┌──────────────────────────────────────────┘
│ │
│ ▼
│ ┌────────┐
│ │ 故障 │
│ └────────┘
│
┌───┴────────────┐
│ 降级 (Degraded) │ 错误率 > 30% 但未完全不可用
└────────────────┘
健康检查指标:
- 主动探测:每 10s 发送简单请求
- 被动检测:实时统计请求错误率
- 综合判断:主动 OR 被动 任一触发 → 状态变更5.2 路由策略
def select_model(request, priority_chain, health_status):
"""选择最优模型"""
# 策略 1:简单优先级(默认)
for model in priority_chain:
if health_status[model] in ["healthy", "recovering"]:
return model
# 策略 2:基于任务类型路由
task_type = classify_task(request) # "code" / "creative" / "analysis"
model_scores = {
"code": {"gpt-4o": 0.95, "claude": 0.90, "local": 0.70},
"creative": {"claude": 0.95, "gpt-4o": 0.85, "local": 0.60},
"analysis": {"gpt-4o": 0.90, "claude": 0.88, "local": 0.75},
}
ranked = sorted(model_scores[task_type].items(), key=lambda x: -x[1])
for model, _ in ranked:
if health_status[model] in ["healthy", "recovering"]:
return model
# 策略 3:基于成本
for model in sorted_by_cost(priority_chain):
if health_status[model] in ["healthy", "recovering"]:
return model
return "local_model" # 最后兜底5.3 降级策略
| 降级级别 | 触发条件 | 行为 | 用户感知 |
|---|---|---|---|
| L0 | 正常 | 使用主模型 | 无 |
| L1 | 主模型延迟升高 | 切换到同级备选 | 延迟略增 |
| L2 | 主模型不可用 | 切换到次级模型 | 质量可能下降 |
| L3 | 所有云端不可用 | 切换到本地模型 | 质量明显下降 |
| L4 | 所有模型不可用 | 返回缓存结果 / 排队等待 | 功能受限 |
5.4 成本优化
成本感知路由:
1. 维护每个模型的 $/1K token 价格表
2. 简单任务(分类、提取)→ 用便宜模型
3. 复杂任务(推理、创作)→ 用贵模型
4. 设置每日预算上限,超限自动降级到便宜模型
成本计算公式:
task_cost = input_tokens * input_price + output_tokens * output_price
daily_budget = sum(all_tasks_cost)
if daily_budget > threshold:
auto_downgrade_to_cheaper_model()6. 扩展讨论
防止级联故障:
- 每个模型供应商独立超时设置
- Bulkhead 模式:隔离不同供应商的连接池
- 不要把所有请求同时切到同一个备用模型(可能把它也压垮)
关键监控指标:
- 每个模型的 QPS、延迟、错误率
- Fallback 触发频率
- 降级持续时间
- 每日成本分布
- 用户体验指标(端到端延迟)
模板五:实时语音 Agent 设计
场景:设计一个实时语音对话 Agent,用户可以像打电话一样与 AI 对话,要求低延迟、自然流畅。
1. 需求分析
功能需求:
| ID | 需求 | 优先级 |
|---|---|---|
| F1 | 实时音频采集和传输(麦克风输入) | P0 |
| F2 | 语音转文字(STT),支持流式识别 | P0 |
| F3 | LLM 理解和生成回答 | P0 |
| F4 | 文字转语音(TTS),支持流式合成 | P0 |
| F5 | 支持打断(Barge-in) | P1 |
| F6 | 多语言支持 | P1 |
| F7 | 情感识别和表达 | P2 |
| F8 | 通话录音和转写 | P1 |
非功能需求:
| 指标 | 要求 |
|---|---|
| 端到端延迟 | < 800ms(用户说完到听到回复) |
| STT 延迟 | < 300ms |
| LLM 首 Token 延迟 | < 200ms |
| TTS 首包延迟 | < 200ms |
| 音频质量 | 16kHz 采样率,Opus 编码 |
| 并发通话 | 10,000 路 |
| 可用性 | 99.9% |
2. 架构图
┌─────────────────────────────────────────────────────────────────┐
│ 用户设备(浏览器/App) │
│ ┌────────┐ ┌────────┐ │
│ │ 麦克风 │→ 音频采集 → Opus编码 → WebSocket →│ 扬声器 │ │
│ └────────┘ └────────┘ │
└────────────────────────────┬────────────────────────────────────┘
│ WebSocket (双向流式)
▼
┌─────────────────────────────────────────────────────────────────┐
│ 实时通信网关 │
│ WebSocket 管理 · 房间路由 · 负载均衡 │
└────────────────────────────┬────────────────────────────────────┘
│
┌───────────────────┼───────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ STT 服务 │ │ LLM 服务 │ │ TTS 服务 │
│ (流式语音识别) │ │ (流式文本生成) │ │ (流式语音合成) │
│ │ │ │ │ │
│ Whisper/ │ │ GPT-4o/ │ │ ElevenLabs/ │
│ Deepgram/ │ │ Claude/ │ │ Azure TTS/ │
│ Paraformer │ │ 自部署 │ │ CosyVoice │
└──────────────┘ └──────────────┘ └──────────────┘
│ │ │
└───────────────────┼───────────────────┘
▼
┌─────────────────────────────────────────────────────────────────┐
│ 状态管理 & 会话存储 │
│ Redis (会话状态) · PostgreSQL (历史记录) │
└─────────────────────────────────────────────────────────────────┘3. 核心组件列表
| 组件 | 职责 | 技术选型 |
|---|---|---|
| 音频采集模块 | 麦克风采集、降噪、VAD | Web Audio API / WebRTC |
| 音频编码 | Opus 编解码 | libopus |
| WebSocket 网关 | 双向实时通信 | Go / Rust 高性能服务 |
| STT 服务 | 流式语音识别 | Whisper / Deepgram / Paraformer |
| LLM 服务 | 流式文本生成 | GPT-4o / Claude / vLLM |
| TTS 服务 | 流式语音合成 | ElevenLabs / Azure TTS / CosyVoice |
| VAD 模块 | 语音活动检测(断句/打断) | Silero VAD / WebRTC VAD |
| 会话管理 | 通话状态、上下文维护 | Redis |
| 录音服务 | 通话录音存储 | S3 + FFmpeg |
4. 数据流
主通话流程(Pipeline 模式):
时间线:
T0: 用户开始说话
└→ 麦克风采集音频(16kHz, PCM)
└→ Opus 编码 → WebSocket 发送
T1: 实时通信网关接收音频包
└→ 转发到 STT 服务
T2: STT 流式识别(每 200ms 返回中间结果)
└→ VAD 检测到静音 → 确定用户说完了(end-of-speech)
└→ 返回最终文本
T3: 最终文本发送到 LLM 服务
└→ LLM 开始流式生成(每收到几个 token 就发送)
T4: LLM 流式输出 → TTS 流式合成
└→ TTS 收到第一个句子就开始合成
└→ 合成的音频包实时发送到客户端
T5: 客户端收到音频包 → 播放
目标:T5 - T0 < 800ms流式 Pipeline 优化(关键!):
传统串行(太慢):
完整STT → 完整LLM → 完整TTS → 播放
延迟 = STT + LLM_full + TTS_full ≈ 3-5s ❌
流式 Pipeline(推荐):
音频 → STT(流式)→ LLM(流式)→ TTS(流式)→ 播放
每个环节边产出边传递,延迟大幅降低 ✓
更激进的优化:双工模式
用户说话的同时,Agent 可以在处理之前的对话
支持打断:用户随时可以说话打断 Agent 的回复5. 关键设计决策
5.1 低延迟优化
| 环节 | 优化手段 | 预期延迟 |
|---|---|---|
| 音频传输 | WebSocket + Opus 编码(低比特率) | 20-50ms |
| STT | 流式识别 + 云端 GPU 推理 | 200-300ms |
| LLM | 流式生成 + 小模型优先 + KV Cache | 100-200ms(首 token) |
| TTS | 流式合成(按句子/短语合成) | 100-200ms(首包) |
| 网络 | 就近部署(边缘节点) | 20-50ms |
| 总计 | 440-800ms |
STT 优化:
- 使用流式 ASR,不等用户说完就开始识别
- 中间结果可以提前发送给 LLM 做预处理
- 语言模型解码用 GPU 加速
- 选择低延迟 ASR 服务(如 Deepgram,延迟约 200ms)LLM 优化:
- 流式生成,每生成一个句子就发给 TTS
- 短回答优先:System Prompt 中要求简洁回答
- Speculative decoding(投机采样)加速
- KV Cache 复用(同一会话内)TTS 优化:
- 流式合成:收到第一个句子就开始合成,不等全部文本
- 分句策略:句号/逗号/问号处分割
- 音频流式传输:合成一个 chunk 就发送一个
- 预合成常用回复("好的"、"明白了")5.2 打断(Barge-in)处理
打断检测流程:
1. VAD 持续检测用户是否在说话
2. 如果 Agent 正在播放回复,用户开始说话:
a. VAD 检测到语音活动
b. 立即停止 TTS 输出
c. 清空 TTS 缓冲区
d. 开始新的 STT 识别
3. LLM 需要理解打断上下文:
a. 将"用户打断了上一轮回复"作为上下文
b. 根据用户新输入重新生成回答
实现细节:
- VAD 灵敏度要调好:太灵敏→误触发,太迟钝→打断延迟
- 建议使用 Silero VAD(准确率高,延迟低)
- 打断阈值:连续 300ms 检测到语音 → 触发打断5.3 WebSocket 协议设计
// 客户端 → 服务端
{"type": "audio", "data": "<base64_opus_chunk>", "seq": 1}
{"type": "control", "action": "start_call"}
{"type": "control", "action": "end_call"}
// 服务端 → 客户端
{"type": "stt_partial", "text": "你好,我想"}
{"type": "stt_final", "text": "你好,我想了解一下"}
{"type": "llm_token", "text": "好的,"}
{"type": "llm_token", "text": "关于这个问题"}
{"type": "tts_audio", "data": "<base64_audio_chunk>", "seq": 1}
{"type": "control", "action": "turn_end"}
{"type": "control", "action": "interrupted"}5.4 并发架构
每路通话的资源消耗:
- WebSocket 连接:1 个
- STT 算力:~0.1 GPU(流式,非持续占用)
- LLM 算力:共享(流式,利用率高)
- TTS 算力:~0.05 GPU
- 内存:~50MB/路(会话状态 + 缓冲区)
10,000 路并发需要:
- WebSocket 网关:10-20 台(Go/Rust,每台 1000+ 连接)
- STT 集群:约 1000 GPU(或使用云服务如 Deepgram)
- LLM 集群:取决于模型大小(使用 vLLM + 多卡)
- TTS 集群:约 500 GPU(或使用云服务)6. 扩展讨论
多语言支持:
- STT:使用多语言模型(Whisper large-v3 支持 100+ 语言)
- LLM:天然支持多语言
- TTS:每个语言需要独立的声音模型
- 语言检测:自动检测用户语言,切换 TTS 声音
情感识别和表达:
- STT 扩展:识别语调、语速、情绪
- LLM Prompt:根据用户情绪调整回答风格
- TTS 扩展:支持不同情感的语音合成
监控指标:
- 端到端延迟分布
- 各环节延迟分布(STT/LLM/TTS)
- 打断率
- 通话质量评分(MOS)
- 掉线率
- 并发通话数
成本优化:
- STT/TTS 使用云服务时,按分钟计费,注意控制通话时长
- LLM 用小模型处理简单对话,大模型处理复杂推理
- 闲时自动缩容
- 缓存常见对话模式的 TTS 结果
附录:答题技巧总结
面试沟通技巧
| 阶段 | 技巧 |
|---|---|
| 开头 | 先花 2-3 分钟问清楚需求,不要急于画图 |
| 画图 | 从高层到细节,先框后线,边画边讲 |
| 讨论 | 主动提出 trade-off,展示工程思维 |
| 收尾 | 总结核心设计决策,提出未来改进方向 |
常见追问及应对
| 追问方向 | 应对策略 |
|---|---|
| "如何处理 XX 万 QPS?" | 先估算单机容量,再算需要多少实例,说明如何水平扩展 |
| "如果 XX 挂了怎么办?" | 冗余 + Fallback + 告警 + 降级 |
| "如何保证数据一致性?" | 讨论 CAP 权衡,说明选择 CP 还是 AP |
| "成本怎么优化?" | 缓存 + 小模型 + 按需扩缩容 + 批处理 |
| "如何监控系统健康?" | 关键指标 + 告警规则 + Dashboard + Trace |
LLM 系统设计特有关注点
| 关注点 | 说明 |
|---|---|
| Token 成本 | 模型调用按 token 计费,成本是核心约束 |
| 延迟链路 | STT + LLM + TTS 串行,每一段都影响总延迟 |
| 模型幻觉 | 需要设计机制减少和检测幻觉 |
| Context Window | 有限的上下文窗口,需要管理历史信息 |
| 模型版本管理 | 模型更新频繁,需要灰度和回滚机制 |
| 安全合规 | Prompt 注入防护、数据隐私、内容审核 |
📝 使用建议:
- 面试前通读一遍,理解每个模板的核心思路
- 重点记忆架构图和关键设计决策
- 根据面试官的追问方向灵活调整深入的内容
- 多练习手画架构图(白板面试需要)
- 每个模板准备 1-2 个你最熟悉的技术细节作为"亮点"