系统设计与项目实战学习博客(通俗原理 + 详细注释 · AI应用强化版)
当你把 RAG、Agent、微调等模块都学会后,面试官通常会抛出一个终极问题:"给我设计一个完整的 AI 应用系统。"这篇博客从实际问题出发 ,用生活化类比 建立直觉,通过术语详解 深入概念本质,再用原理剖析 和架构设计带你掌握 AI 系统设计的核心能力。重点覆盖面试中的高频考点。
一、初级篇:从零画出系统架构图
1. 🔥 画出完整 RAG 系统架构图,并解释每个组件选型
问题
面试官说:"请设计一个企业知识库问答系统。"你需要在白板上画出架构图,并解释每个组件为什么选它。这是一个几乎每轮 AI 应用开发面试都会涉及的题目。
生活化类比
系统架构就像盖大楼的蓝图:每层楼(组件)有特定功能,楼层之间有楼梯连接(数据流)。面试官要看的是------你知不知道每层楼为什么放在那里,而不是随机堆砌。
术语详解
- 系统架构图:用方框和箭头表示系统的组件和数据流向。每个组件是一个技术选型(如 vLLM、ChromaDB、LangChain 等)。
- 选型理由:不只是说"我们用 vLLM 做推理",而是解释"为什么是 vLLM 不是 TGI?因为 vLLM 的 PagedAttention 显存利用率更高,连续批处理吞吐量更大"。
- 分层设计:将系统划分为接入层、业务逻辑层、模型服务层、数据存储层,每层独立扩展。
原理
架构设计的核心是解耦和可扩展。每一层只关心自己的职责,通过标准化的接口(如 REST API、gRPC、消息队列)通信。这样模型更新时不影响业务逻辑,向量库扩容时不影响 API 层。
图解:完整 RAG 系统架构图
┌─────────────────────────────┐
│ 用户 / 前端应用 │
└─────────────┬───────────────┘
│ HTTPS
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 接入层 (Nginx + FastAPI) │
│ - 反向代理、负载均衡、SSL 终止、速率限制 │
└─────────────────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 业务逻辑层 (LangChain) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 意图路由 │ │ 查询改写 │ │ 安全过滤 │ │
│ │ (Router) │ │ (Rewriter) │ │ (Guardrails)│ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────┬───────────────────────────────────┘
│
┌─────────────┴─────────────┐
▼ ▼
┌──────────────────────────────┐ ┌──────────────────────────────┐
│ 检索服务 (RAG Pipeline) │ │ 模型推理层 (vLLM) │
│ ┌──────────┐ ┌──────────┐ │ │ - PagedAttention 显存管理 │
│ │ BM25 │ │ 向量检索 │ │ │ - 连续批处理 │
│ │ 关键词匹配│ │ Embedding │ │ │ - 前缀缓存 │
│ └──────────┘ └──────────┘ │ │ - GPU 利用率 > 90% │
│ ┌──────────────────────┐ │ │ │
│ │ RRF 融合 + 重排序 │ │ │ 选型理由: vLLM > TGI │
│ └──────────────────────┘ │ │ 显存利用率高 30%,吞吐高 10x │
└──────────────┬───────────────┘ └──────────────┬───────────────┘
│ │
▼ ▼
┌──────────────────────────────┐ ┌──────────────────────────────┐
│ 向量数据库 (ChromaDB) │ │ 模型存储 (本地挂载/OSS) │
│ 选型: 本地开发/中小规模 │ │ - 基础模型: Qwen2-7B (4-bit) │
│ 备选: Milvus(单索引10亿+向量) │ │ - Embedding: BGE-large-zh │
│ Pinecone(免运维) │ │ - LoRA 权重: 独立挂载 │
└──────────────────────────────┘ └──────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 可观测性 (Langfuse) │
│ - Trace 全链路追踪: 检索耗时 → LLM 耗时 → 总延迟 │
│ - Token 用量监控: 每日/每月费用报表 │
│ - 错误率告警: 推理失败、检索超时 │
└─────────────────────────────────────────────────────────────────────┘
一次请求的生命周期(帮助理解数据流):
- 用户在浏览器输入问题 → 经 HTTPS 到达 Nginx,Nginx 反向代理到 FastAPI。
- FastAPI 将请求交给 LangChain 业务逻辑层:先由意图路由 判断问题类型(如"退货政策"走知识库检索,"写代码"走代码助手),意图路由通常用轻量级分类模型(如微调 BERT)或 LLM few-shot 分类 实现。再由查询改写 优化措辞(如"怎么退"改写为"退货流程"),最后经过安全过滤(正则+Guardrails)拦截恶意输入。
- 业务逻辑层将改写后的问题同时发给 BM25 关键词检索和向量语义检索。
- 两路检索结果通过 RRF(倒数排名融合)合并排序,再经 Cross-Encoder 重排序精选 Top-3。
- Top-3 文档拼接为上下文,与原始问题一起发给 vLLM 模型推理层。
- vLLM 生成回答返回 LangChain → FastAPI → Nginx → 用户浏览器。
- Langfuse 在整个链路中埋点记录:每个步骤的耗时、token 消耗、检索命中率。
组件选型速查表
| 组件 | 选项 | 推荐场景 | 选型理由 |
|---|---|---|---|
| 推理引擎 | vLLM / TGI / Ollama | vLLM:生产高并发;Ollama:本地开发 | vLLM 的 PagedAttention 显存利用率高 30%,连续批处理吞吐高 10 倍 |
| 向量数据库 | ChromaDB / Milvus / Pinecone | <10万条:ChromaDB;百万级:Milvus;免运维:Pinecone | ChromaDB 零配置适合原型;Milvus 单索引支持 10 亿+ 向量;Pinecone 全托管 |
| 检索策略 | 向量 / BM25 / 混合 | 通用:混合检索;精确匹配多:BM25 | 向量擅长语义,BM25 擅长关键词(如产品型号),RRF 融合效果最优 |
| Embedding 模型 | BGE / text-embedding-3 | 中文:BGE-large-zh;多语言:text-embedding-3 | BGE 中文检索 SOTA,C-MTEB 榜首;OpenAI 多语言覆盖广但需网络 |
| 消息队列 | RabbitMQ / Redis / Kafka | 轻量:Redis Streams;高吞吐:Kafka | Redis 部署简单;Kafka 支持 TB 级消息堆积和精确回放 |
| 可观测性 | Langfuse / LangSmith | Langfuse:开源自托管,数据不出域;LangSmith:LangChain 深度集成 | Langfuse 免费自托管;LangSmith 开箱即用但需付费 |
面试必问:"画一下你的 RAG 系统架构,并解释为什么选这些组件。"------从接入层到存储层逐层展开,每个组件说出 1-2 个选型理由。vLLM 选型是最佳加分点,组件选型速查表可直接用于面试回答。
二、中级篇:让系统"抗造"
1. 🔥 高可用设计:消息队列异步化、缓存、熔断降级
问题
高并发时系统崩溃怎么办?依赖的外部 API(如 OpenAI)宕机了怎么办?
生活化类比
- 消息队列异步化就像银行排队机:用户取号后不用等柜台(立即返回受理),后台一个个处理,柜台不会被人挤爆。
- 熔断降级就像电路保险丝:电流过大时自动跳闸,保护电器不被烧毁。API 连续失败时自动停止调用,直接返回兜底回复。
术语详解
- 消息队列(Message Queue):将耗时任务(如生成回答)从同步 API 中分离,先返回"处理中",后台消费队列完成任务后通知用户。
- 缓存 :精确缓存(完全匹配复用)和语义缓存(相似问题复用),详细原理见"成本控制"章节。核心思路:相同/相似输入 → 返回缓存 → 零 token 消耗,可节省 30-50% API 费用。
- 熔断(Circuit Breaker):当外部 API 连续失败超过阈值时,自动停止调用一段时间(如 30 秒),直接返回兜底回复。
- 降级(Fallback):服务降级策略------GPT-4 超时→GPT-4o-mini→本地模型→固定回复。
原理
消息队列将同步的"请求-处理-回复"链路拆解为异步的"请求-入队-返回受理→消费-处理-通知"。前端立即拿到处理 ID,通过轮询或 WebSocket 获取结果。
熔断器通常有三种状态:关闭(正常调用)、打开(拒绝调用)、半开(试探性调用一次,成功则关闭)。实际生产中,熔断和重试配合使用 :熔断前先重试 2-3 次(指数退避),全部失败后触发熔断;熔断打开后直接降级,等 recovery_timeout 后半开试探,成功则关闭熔断。
生产级消息队列要点 :上面的 deque 仅用于本地模拟。生产环境需使用 Redis Streams (轻量,支持消费者组和 ACK 机制)或 RabbitMQ(成熟稳定,支持消息持久化和死信队列)。关键保障:① 消息持久化到磁盘,服务重启不丢失;② 消费者处理完后发送 ACK 确认,未 ACK 的消息会自动重投;③ 死信队列兜底,超过最大重试次数的消息转入死信等待人工处理。
演示用例:熔断降级
python
import time
import asyncio
from collections import deque
from enum import Enum
# ========== 1. 简单的熔断器实现 ==========
class CircuitState(Enum):
CLOSED = "closed" # 正常:允许调用外部 API
OPEN = "open" # 熔断:拒绝调用,直接降级返回
HALF_OPEN = "half_open" # 半开:试探性调用一次,成功则关闭熔断
class CircuitBreaker:
"""
熔断器:当外部 API 连续失败达到阈值时自动熔断,保护后端服务不被拖垮。
参数 failure_threshold: 连续失败多少次后触发熔断(默认3,太敏感会频繁熔断)
参数 recovery_timeout: 熔断后多久尝试恢复(秒,默认30,给后端足够恢复时间)
"""
def __init__(self, failure_threshold: int = 3, recovery_timeout: float = 30.0):
self.threshold = failure_threshold
self.timeout = recovery_timeout
self.state = CircuitState.CLOSED # 初始状态:正常
self.failure_count = 0 # 连续失败计数
self.last_failure_time = 0 # 最近一次失败的时间戳
def call(self, func, *args, **kwargs):
"""
调用外部 API,自动处理熔断逻辑。
参数 func: 要调用的函数(通常是 API 调用)
返回: func 的返回值(成功时)或抛出异常(熔断时)
"""
# 如果熔断器打开,检查是否可以半开试探
if self.state == CircuitState.OPEN:
if time.time() - self.last_failure_time > self.timeout:
# 已过恢复时间,进入半开状态试探
self.state = CircuitState.HALF_OPEN
print(" 熔断器半开,试探调用...")
else:
# 还没到恢复时间,直接拒绝(降级)
raise Exception("熔断器打开,拒绝调用(降级兜底)")
try:
result = func(*args, **kwargs)
# 调用成功 → 重置熔断器
if self.state == CircuitState.HALF_OPEN:
print(" 试探成功,熔断器关闭(恢复正常)")
self.state = CircuitState.CLOSED
self.failure_count = 0 # 重置失败计数
return result
except Exception as e:
# 调用失败 → 累加计数
self.failure_count += 1
self.last_failure_time = time.time()
if self.failure_count >= self.threshold:
# 达到失败阈值,触发熔断
self.state = CircuitState.OPEN
print(f" 连续失败{self.failure_count}次,熔断器打开!后续请求直接降级。")
raise e
# ========== 2. 简单的消息队列模拟 ==========
task_queue = deque() # 本地双端队列模拟(生产环境换 Redis Streams/RabbitMQ)
async def async_generate(prompt: str) -> str:
"""
异步生成回答:先入队列,立即返回处理 ID,后台消费者处理。
参数 prompt: 用户输入文本
返回: 处理 ID(用于后续轮询或 WebSocket 获取结果)
"""
task_id = f"task_{int(time.time()*1000)}" # 用时间戳生成唯一处理 ID
task_queue.append((task_id, prompt)) # 任务入队(生产环境写入 Redis Streams)
# 立即返回,消费者在后台异步处理
return task_id
# ========== 测试熔断器 ==========
print("===== 熔断器测试 =====")
breaker = CircuitBreaker(failure_threshold=2, recovery_timeout=5) # 2次失败熔断,5秒后试探
for i in range(6):
try:
# 模拟一个总是失败的 API 调用(如网络故障)
def failing_api():
raise ConnectionError("API 连接失败(模拟网络故障)")
breaker.call(failing_api)
except Exception as e:
print(f" 请求{i+1}: {e}")
输出结果
===== 熔断器测试 =====
请求1: API 连接失败(模拟网络故障)
请求2: 连续失败2次,熔断器打开!后续请求直接降级。
请求2: 熔断器打开,拒绝调用(降级兜底)
请求3: 熔断器打开,拒绝调用(降级兜底)
请求4: 熔断器打开,拒绝调用(降级兜底)
请求5: 熔断器半开,试探调用...
请求5: API 连接失败(模拟网络故障)
请求6: 连续失败3次,熔断器打开!后续请求直接降级。
面试必问:"如何保证服务高可用?"------消息队列异步化解耦(削峰填谷)+ 精确/语义缓存降本(相同/相似请求零 token 消耗)+ 熔断降级防雪崩。熔断前指数退避重试 2-3 次,全部失败后熔断,半开试探成功自动恢复。三者组合可应对绝大多数生产故障。
2. 🔥 多租户与数据隔离方案,权限控制设计
问题
你的 SaaS 产品服务多家企业(租户 A 和租户 B),如何确保 A 的员工搜不到 B 的公司文档?
生活化类比
多租户就像公寓楼的独立信箱:每个住户(租户)有自己的信箱(数据空间),钥匙只有自己持有。邮递员不会把 A 的信投到 B 的邮箱。
术语详解
- 多租户(Multi-Tenancy):一个系统实例服务多个客户(租户),每个租户的数据和配置逻辑隔离。
- 数据隔离方案:① 每租户独立数据库(最强隔离,成本高);② 共享数据库+租户 ID 字段过滤(成本低,需严格控制查询);③ 混合方案(敏感数据独立库,通用数据共享)。
- 权限控制:RBAC(基于角色的访问控制)------管理员可以看所有数据,普通用户只能看自己租户的数据。
原理
在 RAG 系统中,向量库的每条文档记录都附加 tenant_id 元数据。检索时,where={"tenant_id": current_user.tenant_id} 作为前置过滤条件,确保只返回当前租户的文档。
tenant_id 的注入路径(面试细节):
- 用户在客户端登录 → 认证服务返回 JWT token(内含
tenant_id字段)。 - 客户端每次请求携带 JWT → 网关层(Nginx/Kong)校验 JWT 签名 ,防止伪造。校验通过后将
tenant_id写入请求头(如X-Tenant-ID)。 - 下游业务服务(FastAPI/LangChain)从请求头直接读取
tenant_id,注入检索的where条件中。
演示用例:多租户向量检索
python
# 多租户数据隔离示例
import chromadb
client = chromadb.PersistentClient(path="./multi_tenant_db")
collection = client.get_or_create_collection(name="knowledge_base")
# ---- 添加两个租户的文档 ----
# 租户 A(公司A)的文档
collection.add(
documents=["公司A的退货政策:7天内无理由退货"],
ids=["a_doc1"],
metadatas=[{
"tenant_id": "company_a", # 租户 ID:来自 JWT 的 tenant_id 字段
"department": "客服部" # 部门标签,支持更细粒度的权限过滤
}]
)
# 租户 B(公司B)的文档
collection.add(
documents=["公司B的退货政策:30天内可退换,需保留原包装"],
ids=["b_doc1"],
metadatas=[{"tenant_id": "company_b", "department": "售后部"}]
)
# ---- 检索:只查租户 A 的文档 ----
# 当前租户 ID 从 JWT 解析后注入(网关层已校验签名)
current_tenant = "company_a"
results = collection.query(
query_texts=["退货政策"],
n_results=3,
# 关键:where 条件按租户 ID 过滤,实现数据隔离
# 注意:ChromaDB 的 where 过滤在向量检索之后执行
# 如果租户数据量极大(>100万),建议使用 Milvus 的 Partition Key 实现物理隔离
where={"tenant_id": current_tenant}
)
print(f"租户 {current_tenant} 的检索结果:")
for i, (doc, meta) in enumerate(zip(results["documents"][0], results["metadatas"][0])):
print(f" {i+1}. {doc} (租户: {meta['tenant_id']})")
输出结果
租户 company_a 的检索结果:
1. 公司A的退货政策:7天内无理由退货 (租户: company_a)
AI 应用场景:多租户是企业级 SaaS 的基础能力。面试中能说出"向量检索时 where 过滤 tenant_id"和"网关校验 JWT→注入请求头→业务层直接读取"的完整链路即可。
3. 🔥 数据隐私:PII 脱敏、本地部署考量、审计日志
问题
用户可能输入手机号、身份证号等敏感信息,如何防止这些信息被泄露?企业客户要求数据不出境怎么办?
生活化类比
- PII 脱敏就像打码身份证号:在复印身份证时,把中间几位遮住(如 320***1234)。
- 本地部署就像自建机房:数据不出公司大楼,所有处理在内部完成。
术语详解
- PII(Personally Identifiable Information):个人身份信息,包括姓名、手机号、邮箱、身份证号、住址等。
- 脱敏(Masking) :将 PII 替换为无意义的占位符或打码形式。如
13812345678→138****5678。 - 本地部署:将模型和向量库部署在客户自己的服务器上,数据不出公司网络。常见方案:① Docker Compose 单机部署(适合 POC);② Kubernetes 集群(高可用+弹性伸缩);③ 边缘盒子(工厂/医院等离线场景)。
- 审计日志:记录谁在什么时间做了什么操作,用于合规审查和问题追溯。
原理
PII 脱敏通常在输入过滤阶段完成------在用户问题到达 LLM 之前,用正则匹配手机号、身份证号并替换为掩码。如果业务需要保留原始信息用于回答(如客服查订单),则将原始信息加密存储,仅传加密后的 token 给 LLM。
审计日志需记录以下核心字段,并存储到不可篡改 的介质(如对象存储开启 WORM 模式,或专用日志服务),保留至少 90 天(满足多数合规要求)。
审计日志结构示例:
json
{
"timestamp": "2024-05-21T14:35:22.123Z", // 操作时间(ISO 8601 格式)
"user_id": "user_12345", // 操作人 ID(来自 JWT)
"tenant_id": "company_a", // 所属租户
"operation": "query", // 操作类型:query(查询)/ generate(生成)
"input_summary": "退货政策", // 输入摘要(脱敏后,不存原始 PII)
"output_summary": "7天内无理由退货...", // 输出摘要
"pii_detected": false, // 是否检测到 PII
"model": "qwen2-7b", // 使用的模型
"latency_ms": 850, // 处理耗时(毫秒)
"result": "success" // 结果:success / blocked / error
}
演示用例:PII 脱敏
python
import re
def mask_pii(text: str) -> tuple[str, list]:
"""
对文本中的 PII 进行脱敏处理,保留部分信息用于业务判断。
参数 text: 用户输入的原始文本
返回: (脱敏后的文本, 检测到的 PII 类型列表)
"""
detected = []
# 脱敏中国大陆手机号
# 正则说明:1开头,第二位3-9,后跟9位数字
# 替换为 138****5678 格式(保留前3位和后4位,中间4位用*替代)
phone_pattern = r"(1[3-9]\d)(\d{4})(\d{4})"
if re.search(phone_pattern, text):
detected.append("手机号")
text = re.sub(phone_pattern, r"\1****\3", text) # \1=前3位,\3=后4位
# 脱敏身份证号(18位或15位)
# 保留前6位(地区码)和后4位(校验位),中间用*替代
# \d{8,11} 匹配8到11位数字(15位身份证中间是8位,18位是11位)
id_pattern = r"(\d{6})\d{8,11}(\d{4})"
if re.search(id_pattern, text):
detected.append("身份证号")
text = re.sub(id_pattern, r"\1********\2", text)
# 脱敏邮箱
# 保留首字母和@后的域名,中间部分用***替代
# [\w._%+-]* 匹配邮箱用户名中的特殊字符
email_pattern = r"(\w)[\w._%+-]*@([\w.-]+\.[a-zA-Z]{2,})"
if re.search(email_pattern, text):
detected.append("邮箱")
text = re.sub(email_pattern, r"\1***@\2", text)
return text, detected
# 测试:包含多种 PII 的用户输入
user_input = "我的手机号是13812345678,身份证320102199001011234,邮箱是zhangsan@example.com"
masked, detected = mask_pii(user_input)
print(f"检测到 PII: {detected}")
print(f"脱敏前: {user_input}")
print(f"脱敏后: {masked}")
输出结果
检测到 PII: ['手机号', '身份证号', '邮箱']
脱敏前: 我的手机号是13812345678,身份证320102199001011234,邮箱是zhangsan@example.com
脱敏后: 我的手机号是138****5678,身份证320102********1234,邮箱是z***@example.com
AI 应用场景:PII 脱敏是合规底线(GDPR/个保法),本地部署满足数据不出境需求。审计日志记录操作人、时间、输入输出摘要、PII 检测结果,存储到不可篡改介质(如对象存储 WORM),保留至少 90 天。面试中能说出脱敏正则、加密存储、审计追溯三个要点即可。
4. 🔥 数据飞轮构建:反馈收集 → 数据清洗 → 微调/提示优化闭环
问题
系统上线后如何持续变好?用户反馈怎么利用起来?
生活化类比
数据飞轮就像滚雪球:最初只是一个小雪球(少量数据),滚的过程中吸附更多雪(收集反馈),越滚越大(数据越多模型越好),越滚越快(模型越好用户越多反馈越多)。
术语详解
- 数据飞轮(Data Flywheel):通过持续收集用户反馈和交互数据,反哺模型微调和提示优化,形成正向循环。
- 反馈收集:点赞/踩、用户纠错、人工抽检评分。
- 数据清洗:筛选高质量反馈(如点赞的回答 + 人工验证),去除噪声。
- 闭环迭代:收集→清洗→微调/优化提示→评估→上线→再收集。
原理
飞轮的每一步都需要明确的质量标准 和自动化流水线:
- 收集:在 UI 中添加 👍/👎 按钮,记录用户的每次反馈。同时自动记录所有对话日志。
- 筛选 :过滤掉踩的回答,保留点赞的对话对。随机抽取 5-10% 由 QA 或领域专家人工抽检,确认质量合格后纳入训练集。
- 转换:将筛选后的对话转为 SFT/DPO 训练格式(如 ChatML)。
- 微调:定期(如每月)用新增数据微调模型(LoRA/QLoRA),或更新提示模板(A/B 测试新版本)。
- 评估上线:对比新旧模型/提示在测试集上的表现,提升则上线。如果效果下降,回滚并分析原因。
图解:数据飞轮闭环
┌─────────────────────────────────────────────────────────────┐
│ 数据飞轮闭环 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 用户交互 │ ──→ │ 反馈收集 │ ──→ │ 数据筛选 │ │
│ │ (对话日志)│ │ (👍/👎) │ │ (点赞+5%抽检)│ │
│ └──────────┘ └──────────┘ └────┬─────┘ │
│ │ │
│ ┌────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 评估上线 │ ←── │ 模型微调 │ ←── │ 格式转换 │ │
│ │ (A/B 测试)│ │ (LoRA/DPO)│ │ (ChatML) │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │ │
│ └──────────────────────────────────────────────────→ 回到用户交互
└─────────────────────────────────────────────────────────────┘
AI 应用场景:数据飞轮是 AI 产品持续进化的核心引擎。面试中能画出飞轮图并解释每步做什么(收集→筛选→转换→微调→评估上线),是区分"会开发功能"和"懂产品迭代"的关键分水岭。
AI 应用场景速查表
| 知识点 | 核心用途 | 典型场景 |
|---|---|---|
| RAG 系统架构图 | 面试必画 | 企业知识库、客服系统 |
| 消息队列异步化 | 削峰填谷 | 高并发请求处理 |
| 熔断降级 | 防止雪崩 | 外部 API 故障时保护系统 |
| 多租户数据隔离 | SaaS 产品基础 | 企业级知识库服务 |
| RBAC 权限控制 | 数据安全 | 多角色管理后台 |
| PII 脱敏 | 合规底线 | 用户输入的敏感信息保护 |
| 审计日志 | 问题追溯与合规 | 操作记录、安全审查 |
| 数据飞轮 | 持续进化 | 模型/提示定期优化迭代 |
面试模拟题
1. 架构型:请画出你的 RAG 系统架构图,并解释每个组件的选型理由。
答案要点:从接入层(Nginx+FastAPI)→业务逻辑层(LangChain:意图路由、查询改写、安全过滤)→检索服务(BM25+向量+RRF+重排序)→模型推理(vLLM:PagedAttention+连续批处理)→存储层(ChromaDB/Milvus+Pinecone)→可观测层(Langfuse),逐层展开。描述一次请求的完整生命周期:用户问题→Nginx→意图路由→查询改写→BM25+向量检索→RRF→重排序→vLLM 生成→返回。每个组件说出 1-2 个选型理由,如 vLLM 选型是因为 PagedAttention 显存利用率高、连续批处理吞吐提升 10 倍。
2. 原理型:消息队列和熔断降级是如何保证系统高可用的?
答案要点:消息队列将同步处理异步化,前端立即返回受理 ID,后台排队处理,削峰填谷避免过载。生产环境使用 Redis Streams 或 RabbitMQ,需保证消息持久化和消费者 ACK 确认。熔断器监控外部 API 调用,连续失败超过阈值自动熔断(拒绝调用),一段时间后半开试探,成功则恢复。熔断前先指数退避重试 2-3 次。配合降级策略(强模型→弱模型→固定回复),保证系统始终可用。
3. 场景型:你服务两家企业客户,如何确保 A 公司搜不到 B 公司的文档?
答案要点 :多租户数据隔离------每个文档块附加 tenant_id 元数据。用户登录后 JWT 包含 tenant_id,网关层校验 JWT 签名防止伪造,将 tenant_id 写入请求头。业务层从请求头读取,向量检索时 where={"tenant_id": current_tenant_id} 作为前置过滤条件。权限控制采用 RBAC,管理员可跨租户,普通用户仅限自己租户。三种隔离方案:独立数据库(最强)、共享+过滤(常用)、混合(敏感数据独立)。
4. 综合型:你如何构建一个数据飞轮,让 AI 产品持续变好?
答案要点:闭环流程------① 收集用户反馈(点赞/踩、纠错)+ 对话日志;② 筛选高质量数据(点赞+5-10%人工抽检通过);③ 转换为 SFT/DPO 训练格式(ChatML);④ 定期微调模型(如每月 LoRA)或优化提示模板;⑤ A/B 测试对比新旧版本在独立测试集上的表现,提升则上线,下降则回滚分析原因。此闭环持续运转,数据越多模型越准。
总结
从画出完整 RAG 系统架构图并解释每个组件选型,到消息队列异步化、熔断降级的高可用设计,再到多租户数据隔离、PII 脱敏和数据飞轮构建,你已掌握 AI 系统设计的核心能力。面试中的高频考点------架构图绘制与选型理由、高可用设计手段、数据安全与合规、持续迭代闭环------都已覆盖。现在你可以自信地应对任何 AI 应用开发的系统设计面试了。