LLM成长笔记(十三):系统设计与项目实战

系统设计与项目实战学习博客(通俗原理 + 详细注释 · 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 用量监控: 每日/每月费用报表                                  │
│  - 错误率告警: 推理失败、检索超时                                      │
└─────────────────────────────────────────────────────────────────────┘

一次请求的生命周期(帮助理解数据流):

  1. 用户在浏览器输入问题 → 经 HTTPS 到达 Nginx,Nginx 反向代理到 FastAPI。
  2. FastAPI 将请求交给 LangChain 业务逻辑层:先由意图路由 判断问题类型(如"退货政策"走知识库检索,"写代码"走代码助手),意图路由通常用轻量级分类模型(如微调 BERT)或 LLM few-shot 分类 实现。再由查询改写 优化措辞(如"怎么退"改写为"退货流程"),最后经过安全过滤(正则+Guardrails)拦截恶意输入。
  3. 业务逻辑层将改写后的问题同时发给 BM25 关键词检索和向量语义检索。
  4. 两路检索结果通过 RRF(倒数排名融合)合并排序,再经 Cross-Encoder 重排序精选 Top-3。
  5. Top-3 文档拼接为上下文,与原始问题一起发给 vLLM 模型推理层。
  6. vLLM 生成回答返回 LangChain → FastAPI → Nginx → 用户浏览器。
  7. 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 的注入路径(面试细节):

  1. 用户在客户端登录 → 认证服务返回 JWT token(内含 tenant_id 字段)。
  2. 客户端每次请求携带 JWT → 网关层(Nginx/Kong)校验 JWT 签名 ,防止伪造。校验通过后将 tenant_id 写入请求头(如 X-Tenant-ID)。
  3. 下游业务服务(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 替换为无意义的占位符或打码形式。如 13812345678138****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):通过持续收集用户反馈和交互数据,反哺模型微调和提示优化,形成正向循环。
  • 反馈收集:点赞/踩、用户纠错、人工抽检评分。
  • 数据清洗:筛选高质量反馈(如点赞的回答 + 人工验证),去除噪声。
  • 闭环迭代:收集→清洗→微调/优化提示→评估→上线→再收集。

原理

飞轮的每一步都需要明确的质量标准自动化流水线

  1. 收集:在 UI 中添加 👍/👎 按钮,记录用户的每次反馈。同时自动记录所有对话日志。
  2. 筛选 :过滤掉踩的回答,保留点赞的对话对。随机抽取 5-10% 由 QA 或领域专家人工抽检,确认质量合格后纳入训练集。
  3. 转换:将筛选后的对话转为 SFT/DPO 训练格式(如 ChatML)。
  4. 微调:定期(如每月)用新增数据微调模型(LoRA/QLoRA),或更新提示模板(A/B 测试新版本)。
  5. 评估上线:对比新旧模型/提示在测试集上的表现,提升则上线。如果效果下降,回滚并分析原因。

图解:数据飞轮闭环

复制代码
  ┌─────────────────────────────────────────────────────────────┐
  │                       数据飞轮闭环                          │
  │                                                             │
  │   ┌──────────┐     ┌──────────┐     ┌──────────┐           │
  │   │ 用户交互  │ ──→ │ 反馈收集  │ ──→ │ 数据筛选  │           │
  │   │ (对话日志)│     │ (👍/👎)  │     │ (点赞+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 应用开发的系统设计面试了。

相关推荐
searchforAI7 小时前
AI多模态技术:从语音识别到AI结构化笔记是怎么实现的
人工智能·经验分享·笔记·gpt·whisper·语音识别
凉、介7 小时前
深入理解 ARMv7-A|异常/中断处理
笔记·学习·嵌入式·arm
koo3647 小时前
周报5.24
笔记
玄米乌龙茶1238 小时前
LLM成长笔记(十一):模型部署与工程化
笔记
会编程的土豆8 小时前
结构体标签与数据流向 笔记
笔记
玄米乌龙茶1239 小时前
LLM成长笔记(十):多模态应用开发
人工智能·笔记·语音识别
秦明月139 小时前
电气安全回路设计实战:皮尔兹安全继电器应用
经验分享·笔记·安全·职场和发展·创业创新·学习方法
笑鸿的学习笔记9 小时前
计算机笔记之沙盒(Sandbox)
笔记
ljt272496066110 小时前
Vue笔记(六)--响应式
javascript·vue.js·笔记