标签 可靠性 下的文章

本文梳理了一套通过 6 个步骤清晰展示系统设计思维的应对框架,包括澄清需求、定义成功标准、画出高层架构、设计数据层,到扩展性与可靠性,最后考虑权衡取舍。

系统设计面试并不是考你会不会背各种技术名词,而是看你能不能在有限时间里,有条理的拆解问题、做出合理的架构决策,并把自己的思路讲清楚

面试的评分标准其实是“思考方式”,而不是“系统有多炫酷”。因此需要一套可重复执行的流程,把几十分钟的面试时间拆分成若干阶段,每一阶段回答一个明确的问题。

接下来就介绍这套能够帮助你顺利通过各种系统设计面试的框架。


50 分钟作战计划

下面是一份 50 分钟时间切片路线图:

- 0–5 分钟:澄清需求
- 6–12 分钟:定义成功标准
- 13–22 分钟:画出高层架构
- 23–32 分钟:设计数据层
- 33–42 分钟:讨论扩展性与可靠性
- 43–50 分钟:收尾与权衡总结

路线图可以按阶段展开,每个阶段都对应面试过程中呈现在白板或文档上的“可见成果”。

阶段 1:先澄清再设计(0–5 分钟)

永远不要直接开始画图。第一步应该是:用问题把“题目”变成“需求”。

可以围绕以下维度澄清:

  • 用户与规模:有多少用户、日活?是 100 万还是 10 亿?
  • 核心用例:最重要的 1~2 个场景是什么?例如仅做照片分享,还是要包含完整的社交功能?
  • 客户端形态:只考虑移动端,还是移动 + Web?
  • 地理分布:是否是全球分布,是否有多区域部署需求?
  • 时延要求:例如“Feed 打开时间需要控制在 500ms 以内”。

对于面试官抛出的“设计 Instagram”之类的问题,可以先反问:

“我们是只关注图片流(Photo Feed),还是要覆盖整个产品?是否支持视频?大致用户量级是多少?”

这一阶段的目标:用 2–3 分钟让双方对“要构建的东西”达成共识,让后面的设计有清晰边界。

阶段 2:写下什么叫“成功”(6–12 分钟)

在澄清了范围之后,第二步是明确定义功能性和非功能性需求,包括:

  • 功能性需求:比如“用户可以上传图片、关注他人、看到关注对象的动态流、对内容点赞与评论”等;
  • 非功能性需求:比如“高可用性(High Availability)达到 99.9%”、“Feed 加载延迟(Latency)小于 500ms”、“可以扩展到 1 亿日活用户”、“允许最终一致性(Eventual Consistency)”。

把这些需求点写在白板上或共享文档中,就相当于和面试官形成了“设计合约”:后续所有架构选择,都要能解释清楚“这是为了满足哪条需求”。

这一阶段的目标:让面试官看到你不是在“凭感觉设计”,而是在对齐“什么设计是成功的”。

阶段 3:先画大图,再补细节(13–22 分钟)

到了真正画架构图的时候,强调一个原则:先画大块(High-Level Components),再深入具体实现,而不是一开始就纠结字段、索引或具体中间件。

典型高层架构可以包括:

┌─────────┐
│  Users  │
└────┬────┘
     │
     ↓
┌─────────────┐
│  CDN/Cache  │
└─────┬───────┘
      │
      ↓
┌──────────────┐      ┌──────────────┐
│ Load Balancer│─────→│ Load Balancer│
└──────┬───────┘      └──────┬───────┘
       │                     │
       ↓                     ↓
┌─────────────┐      ┌─────────────┐
│ API Servers │      │Media Service│
└──────┬──────┘      └──────┬──────┘
       │                    │
       ↓                    ↓
┌─────────────┐      ┌─────────────┐
│  Database   │      │Object Storage│
└─────────────┘      └─────────────┘
  • 客户端(Mobile / Web);
  • CDN(Content Delivery Network)和缓存(Cache),用于分发静态资源与热门内容;
  • 负载均衡(Load Balancer),把流量分发到后端服务;
  • API 服务(API Servers),承载业务逻辑;
  • 媒体服务(Media Service),负责图片/视频的处理与存储;
  • 数据库(Database),保存用户、关系、元数据;
  • 对象存储(Object Storage),保存实际的图片/视频文件。

在讲解数据流时,可以用一句简短的“端到端路径”来串起来,例如:

用户上传图片 → API 服务处理请求 → 媒体服务转码与压缩 
                            ↓
                        写入对象存储
                            ↓
                        在数据库中记录元数据
                            ↓
                        返回可访问 URL

这一阶段的目标:让面试官在脑中形成清晰的“系统鸟瞰图”,知道所有关键组件长什么样、怎么互相连接。此时还不必深入到每个组件内部实现。

阶段 4:谈数据,而不是只谈服务(23–32 分钟)

后半段时间建议重点放在“数据层设计”上,因为这最能体现工程判断力。

可以从以下几个维度展开:

  1. 关系型数据库(SQL)还是非关系型数据库(NoSQL)?

    • 用户资料与关注关系这类强一致(ACID)需求高的场景,更适合用 SQL;
    • 时间线 / Feed 这类读多写少、允许最终一致性的场景,更适合用可横向扩展的 NoSQL。
  2. 数据模型与访问模式:

    • 例如关注关系可以用 follows 表建复合主键,避免重复关注;
    • Feed 可以预计算并按用户保存为去范式(Denormalized)结构,加快读取。
  3. 缓存策略:

    • 缓存哪些内容:用户资料、热门内容、活跃用户 Feed 等;
    • 为什么要缓存:相比直接查数据库,内存缓存(如 Redis)能把几十毫秒的查询压缩到几毫秒,在每秒上万请求的场景下能“挽救”大量数据库资源。
-- SQL 适用于用户与关注关系
CREATE TABLE users (
    user_id BIGINT PRIMARY KEY,
    username VARCHAR(50) UNIQUE,
    created_at TIMESTAMP
);

CREATE TABLE follows (
    follower_id BIGINT,
    followed_id BIGINT,
    created_at TIMESTAMP,
    PRIMARY KEY (follower_id, followed_id)
);
// NoSQL (比如 Cassandra) 更适合
{
  user_id: "user_123",
  feed: [
    {post_id: "post_456", timestamp: 1634567890},
    {post_id: "post_789", timestamp: 1634567850}
  ]
}

这一阶段的目标:展示你能够根据访问模式选择合适的存储,并且讲清楚“为什么这样选”以及“放弃了什么”。

阶段 5:把系统放进真实世界(33–42 分钟)

系统上线后会面对流量波动、节点故障、网络抖动等各种现实问题。这个阶段要重点回答两个问题:

  • 当流量变成 10 倍时,系统如何扩展?
  • 当部分组件失败时,系统如何优雅降级?

可以从以下角度展开:

  • 水平扩展:

    • 应用服务前增加更多无状态实例,通过负载均衡分发;
    • 数据库通过读写分离与只读副本承压。
  • 容错与高可用:

    • 复制:关键数据多副本存储;
    • 熔断器:下游服务异常时快速失败并降级到缓存结果;
    • 限流:防止恶意或异常流量;
    • 优雅降级:尽量提供“部分可用”的体验,例如主功能可用、部分统计或推荐暂时不可用。

熔断器示例代码:

class CircuitBreaker:
    def __init__(self, threshold=5):
        self.failures = 0
        self.threshold = threshold
        self.state = "CLOSED"  # CLOSED, OPEN, HALF_OPEN
    
    def call(self, func):
        if self.state == "OPEN":
            return cached_response()
        
        try:
            result = func()
            self.failures = 0
            return result
        except Exception:
            self.failures += 1
            if self.failures >= self.threshold:
                self.state = "OPEN"
            raise

上面用简短的伪代码演示了熔断器(Circuit Breaker)如何在失败次数超过阈值时“打开”并立即返回缓存数据,面试中不必照搬代码,但可以用语言说明:自己理解“失败隔离”与“自我恢复”的重要性

这一阶段的目标:让面试官看到你不仅会“搭系统”,还能放到高并发、高故障率的真实环境里去思考。

阶段 6:干净利落的收尾(43–50 分钟)

最后 5~7 分钟,重点不是继续加新组件,而是:

  1. 用 30~60 秒复述你的整体方案:

    • 系统主干架构;
    • 关键技术选择(例如 SQL 用在用户与关系,NoSQL 用在 Feed,与 CDN 配合做全局分发);
    • 如何扩展与保证可靠性。
  2. 主动点出几项关键权衡:

    • 比如“用 NoSQL 做 Feed,换来快速读取与易扩展,但牺牲了一些查询灵活性与强一致性”;
    • “预计算 Feed 提升打开速度,但增加了存储开销以及可能短时间内呈现旧数据的风险”。
  3. 抛出开放性问题:

    • 例如:“如果需要,我可以进一步深入某个组件,比如 Feed 生成策略或多区域容灾,您更希望听哪一块?”

这一阶段的目标

  • 把零散的讨论收拢成结构清晰的故事;
  • 让面试官感到“即使时间到了,这个人依然在有条理的思考权衡,而不是随意堆砌技术名词”。

真正的秘诀

系统设计面试中不需要做的事情

  • 不必一上来就报一堆云服务的品牌名;
  • 不必急着切成复杂的微服务;
  • 不必在一开始就画出所有细节;
  • 不必给出“这个就是最佳方案”的结论。

相反,更重要的是:

  • 从澄清问题开始,而不是从方案开始;
  • 按阶段逐步搭建系统,而不是一口气抛出完整架构图;
  • 所有选择都有理由,能讲出“为什么这样设计”;
  • 诚实面对权衡,承认每个选择都有利有弊;
  • 保持对话,主动和面试官互动,而不是独角戏式的画完就走。

这也是为什么同一套技术栈,在不同候选人嘴里,呈现出的“成熟度”会完全不同:真正拉开差距的是“解释方案的方式”和“面对不确定性的态度”。


行动清单

下面是一份非常务实的练习建议,简要整理成可执行清单:

  1. 选 5 个不同的系统设计题,用这套框架完整走一遍;
  2. 给自己计时,习惯在压力下也能按阶段推进;
  3. 录下自己的讲解过程,回看时关注“哪里讲得不清楚、哪里跳步太快”;
  4. 在练习中刻意练习“讲清楚权衡”的能力,而不是背标准答案;
  5. 面试时记住:对方要看的,是思考路径与沟通能力,而不是一张完美无缺的架构图。

要点回顾

  • 系统设计面试考察的是结构化思维与沟通,而不是技术名词堆砌。
  • 在面试过程中,可以用“澄清需求 → 定义成功 → 画大图 → 设计数据层 → 讨论扩展性与可靠性 → 收尾与权衡”这六个阶段来组织自己的输出。
  • 数据层设计是展现工程判断的关键环节,要能结合访问模式解释 SQL / NoSQL、缓存与预计算等选择。
  • 讨论扩展性与可靠性时,应从水平扩展、复制、限流、熔断与优雅降级等角度说明“系统如何在真实世界中生存”。
  • 收尾阶段用简短复盘与权衡总结,把整场讨论串成一个完整故事,并主动邀请面试官选择可以进一步深入的部分。

Hi,我是俞凡,一名兼具技术深度与管理视野的技术管理者。曾就职于 Motorola,现任职于 Mavenir,多年带领技术团队,聚焦后端架构与云原生,持续关注 AI 等前沿方向,也关注人的成长,笃信持续学习的力量。在这里,我会分享技术实践与思考。欢迎关注公众号「DeepNoMind」,星标不迷路。也欢迎访问独立站 www.DeepNoMind.com,一起交流成长。

本文由mdnice多平台发布

本系列介绍增强现代智能体系统可靠性的设计模式,以直观方式逐一介绍每个概念,拆解其目的,然后实现简单可行的版本,演示其如何融入现实世界的智能体系统。本系列一共 14 篇文章,这是第 14 篇。原文:Building the 14 Key Pillars of Agentic AI

优化智能体解决方案需要软件工程确保组件协调、并行运行并与系统高效交互。例如预测执行,会尝试处理可预测查询以降低时延,或者进行冗余执行,即对同一智能体重复执行多次以防单点故障。其他增强现代智能体系统可靠性的模式包括:

  • 并行工具:智能体同时执行独立 API 调用以隐藏 I/O 时延。
  • 层级智能体:管理者将任务拆分为由执行智能体处理的小步骤。
  • 竞争性智能体组合:多个智能体提出答案,系统选出最佳。
  • 冗余执行:即两个或多个智能体解决同一任务以检测错误并提高可靠性。
  • 并行检索和混合检索:多种检索策略协同运行以提升上下文质量。
  • 多跳检索:智能体通过迭代检索步骤收集更深入、更相关的信息。

还有很多其他模式。

本系列将实现最常用智能体模式背后的基础概念,以直观方式逐一介绍每个概念,拆解其目的,然后实现简单可行的版本,演示其如何融入现实世界的智能体系统。

所有理论和代码都在 GitHub 仓库里:🤖 Agentic Parallelism: A Practical Guide 🚀

代码库组织如下:

agentic-parallelism/
    ├── 01_parallel_tool_use.ipynb
    ├── 02_parallel_hypothesis.ipynb
    ...
    ├── 06_competitive_agent_ensembles.ipynb
    ├── 07_agent_assembly_line.ipynb
    ├── 08_decentralized_blackboard.ipynb
    ...
    ├── 13_parallel_context_preprocessing.ipynb
    └── 14_parallel_multi_hop_retrieval.ipynb

深度推理的多跳检索

许多复杂的用户查询并非单一问题,而是比较性的、多步骤的调研任务,需要从多个不同来源的文档中综合信息。

并行多跳

解决方案是 并行多跳检索(Parallel Multi-Hop Retrieval) 架构,这种模式将 RAG 系统提升为真正的调研代理,工作流模拟人类研究员如何处理复杂问题的过程:

  1. 分解(Decompose):高级元代理首先分析复杂的用户查询,将其分解为几个更简单、独立的子问题。
  2. 分散(并行检索):每个子问题都被派发给各自的专用检索代理。这些代理并行运行,每个代理执行标准 RAG 流程,为特定子问题寻找答案。
  3. 收集与综合:元代理收集所有子问题的答案,进行最终推理步骤,将它们综合为对原始复杂查询的单一、全面的答案。

我们将以一个无法通过单一检索回答的比较性问题为例,构建并比较简单 RAG 系统与多跳 RAG 系统,证明只有多跳系统才能成功收集必要的证据,以提供准确且富有洞察力的最终答案。

首先为初始分解步骤定义 Pydantic 模型,从而结构化元代理规划阶段输出的内容。

from langchain_core.pydantic_v1 import BaseModel, Field
from typing import List

class SubQuestions(BaseModel):
    """分解代理输出的Pydantic模型,包含一组独立的子问题"""
    questions: List[str] = Field(description="A list of 2-3 simple, self-contained questions that, when answered together, will fully address the original complex query.")

这个 SubQuestions 模型是元代理首次行动的合约,迫使 LLM 将复杂查询分解为一系列简单、可回答的问题,是并行"分而治之"策略的基础步骤。

然后构建高级多跳系统作为 LangGraph 图。第一个节点将是"分解器",即元代理的规划角色。

from typing import TypedDict, List, Dict, Annotated
import operator

class MultiHopRAGState(TypedDict):
    original_question: str
    sub_questions: List[str]
    # 字典以问题作为键,存储每个子问题的答案
    sub_question_answers: Annotated[Dict[str, str], operator.update]
    final_answer: str

# 节点 1:分解器(元代理的第一步)
decomposer_prompt = ChatPromptTemplate.from_template(
    "You are a query decomposition expert. Your job is to break down a complex question into simple, independent sub-questions that can be answered by a retrieval system. "
    "Do not try to answer the questions yourself.\n\n"
    "Question: {question}"
)

decomposer_chain = decomposer_prompt | llm.with_structured_output(SubQuestions)

def decomposer_node(state: MultiHopRAGState):
    """获取原始复杂问题并将其分解为子问题列表"""
    print("--- [Meta-Agent] Decomposing complex question... ---")
    result = decomposer_chain.invoke({"question": state['original_question']})
    print(f"--- [Meta-Agent] Generated {len(result.questions)} sub-questions. ---")
    return {"sub_questions": result.questions}

decomposer_node 是研究代理的战略大脑,它不会尝试回答查询,其唯一且关键的任务是分析用户意图并将其分解为一组独立、可并行化的研究任务。

下一个节点将并行为每个子问题协调执行标准的 RAG 流程。

from concurrent.futures import ThreadPoolExecutor, as_completed

# 标准、自包含的RAG链,是并行检索代理的“引擎”
sub_question_rag_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | generator_prompt
    | llm
    | StrOutputParser()
)

def retrieval_agent_node(state: MultiHopRAGState):
    """节点 2:为每个子问题并行运行完整 RAG 进程"""
    print(f"--- [Retrieval Agents] Answering {len(state['sub_questions'])} sub-questions in parallel... ---")
    
    answers = {}
    # 用 ThreadPoolExecutor 对每个子问题并发运行‘sub_question_rag_chain’
    with ThreadPoolExecutor(max_workers=len(state['sub_questions'])) as executor:
        # 为每个待回答子问题构建一个 future
        future_to_question = {executor.submit(sub_question_rag_chain.invoke, q): q for q in state['sub_questions']}
        for future in as_completed(future_to_question):
            question = future_to_question[future]
            try:
                answer = future.result()
                answers[question] = answer
                print(f"  - Answer found for sub-question: '{question}'")
            except Exception as e:
                answers[question] = f"Error answering question: {e}"
    # 将结果收集到“sub_question_answers”字典中
    return {"sub_question_answers": answers}

retrieval_agent_node 是系统中的分散-聚合核心,接收 sub_questions 列表,并用 ThreadPoolExecutor 将每个条目分配到各自独立的 RAG 链。这是一种强大的并行形式,同时运行多个完整 RAG 流程。在所有并行代理找到答案后,该节点将所有发现汇总到 sub_question_answers 字典中。

最后,“合成器”节点作为元代理的最终步骤,将并行发现整合为一个连贯的答案。

# 节点 3:合成器(元代理的最后一步)
synthesizer_prompt = ChatPromptTemplate.from_template(
    "You are a synthesis expert. Your job is to combine the answers to several sub-questions into a single, cohesive, and comprehensive answer to the user's original complex question.\n\n"
    "Original Question: {original_question}\n\n"
    "Sub-Question Answers:\n{sub_question_answers}"
)

synthesizer_chain = synthesizer_prompt | llm | StrOutputParser()

def synthesizer_node(state: MultiHopRAGState):
    """获取子问题的答案,并合成最终的全面答案"""
    print("--- [Meta-Agent] Synthesizing final answer... ---")
    
    # 将收集的子问题答案格式化为最终提示
    sub_answers_str = "\n".join([f"- Q: {q}\n- A: {a}" for q, a in state['sub_question_answers'].items()])
    
    final_answer = synthesizer_chain.invoke({
        "original_question": state['original_question'],
        "sub_question_answers": sub_answers_str
    })
    return {"final_answer": final_answer}

synthesizer_node 是至关重要的最终推理步骤,它本身不执行任何检索,任务是接收 sub_question_answers 中的预处理事实,并将其构造为能直接回应用户原始复杂查询的连贯叙述。

最后按线性顺序组装图:分解 -> 并行检索 -> 综合。

from langgraph.graph import StateGraph, END

workflow = StateGraph(MultiHopRAGState)
workflow.add_node("decompose", decomposer_node)
workflow.add_node("retrieve_in_parallel", retrieval_agent_node)
workflow.add_node("synthesize", synthesizer_node)

workflow.set_entry_point("decompose")

workflow.add_edge("decompose", "retrieve_in_parallel")
workflow.add_edge("retrieve_in_parallel", "synthesize")
workflow.add_edge("synthesize", END)
multi_hop_rag_app = workflow.compile()

并行多跳检索

给两个系统一个复杂且需要比较的问题,这个问题无法通过单次检索调用正确回答,从而对比分析两种查询方式。

# 查询需要比较两个产品,信息在独立、不重叠的文档中
user_query = "Compare the QLeap-V4 and the Eco-AI-M2, focusing on their target use case and power consumption."

# --- 执行简单 RAG ---
print("="*60)
print("                  SIMPLE RAG SYSTEM OUTPUT")
print("="*60 + "\n")
print(f"Final Answer:\n{simple_answer}")

# --- 执行多跳 RAG ---
print("\n" + "="*60)
print("                 MULTI-HOP RAG SYSTEM OUTPUT")
print("="*60 + "\n")
print("--- Sub-Question Answers ---")
for i, (q, a) in enumerate(multi_hop_result['sub_question_answers'].items()):
    print(f"{i+1}. Q: {q}\n   A: {a}")
print("\n--- Final Synthesized Answer ---")
print(multi_hop_result['final_answer'])

# --- 最终分析 ---
print("\n" + "="*60)
print("                     ACCURACY & QUALITY ANALYSIS")
print("="*60 + "\n")
print("**Simple RAG Performance:**")
print("- Result: COMPLETE FAILURE.")
print("- Reason: The user's query contained terms for both products. Vector search found the documents that were, on average, most semantically similar to the entire query, retrieving only documents about the Eco-AI-M2. It completely failed to retrieve any information about the QLeap-V4. Without the necessary context for both products, a comparison was impossible.\n")
print("**Multi-Hop RAG Performance:**")
print("- Result: COMPLETE SUCCESS.")
print("- Reason: The system's intelligence was in the initial decomposition step. The Meta-Agent broke the complex comparative query into two simple, focused sub-questions: 1. Get info on Product A. and 2. Get info on Product B. The parallel Retrieval Agents had no trouble answering these simple questions, each retrieving the correct, focused context. The final Synthesizer agent then received a perfect, complete set of facts about both products, making the final comparison trivial.")

输出为……

#### 输出 ####
============================================================
                  SIMPLE RAG SYSTEM OUTPUT
============================================================

Final Answer:
Based on the provided context, the Eco-AI-M2 chip is designed for edge computing and mobile devices, with a primary feature of low power consumption at only 15W under full load. The context does not contain information about the QLeap-V4, so I cannot provide a comparison.

============================================================
                 MULTI-HOP RAG SYSTEM OUTPUT
============================================================
--- Sub-Question Answers ---
1. Q: What is the target use case and power consumption of the QLeap-V4?
   A: The QLeap-V4 processor is designed for maximum performance in data centers, with a primary use case of large-scale AI model training. It consumes 1200W of power under full load.
2. Q: What is the target use case and power consumption of the Eco-AI-M2?
   A: The Eco-AI-M2 chip is designed for edge computing and mobile devices like drones and smart cameras. Its key feature is low power consumption, drawing only 15W under full load.
--- Final Synthesized Answer ---
The QLeap-V4 and the Eco-AI-M2 are designed for very different purposes, primarily distinguished by their target use case and power consumption.
-   **QLeap-V4**: This is a high-performance processor intended for data centers. Its main use case is large-scale AI model training, and it has a high power consumption of 1200W.
-   **Eco-AI-M2**: This is a low-power chip designed for edge computing and mobile devices. Its focus is on energy efficiency, consuming only 15W, making it suitable for applications like drones and smart cameras.

最终分析得出明确结论,性能差异并非渐进式,而是一次能力上的飞跃。

  • 单次检索步骤无法解决比较查询歧义,仅检索了两个产品中的一个上下文,从根本上无法收集必要的证据。
  • 多跳系统之所以成功,是因为没有试图一次性回答复杂问题,而是识别了查询的比较性质,并将问题分解。
  • 通过并行、专注的 RAG 代理来解决每个简单的子问题,确保收集了所有必要证据,最后的综合步骤只是简单的将预先处理的事实结合起来。

Hi,我是俞凡,一名兼具技术深度与管理视野的技术管理者。曾就职于 Motorola,现任职于 Mavenir,多年带领技术团队,聚焦后端架构与云原生,持续关注 AI 等前沿方向,也关注人的成长,笃信持续学习的力量。在这里,我会分享技术实践与思考。欢迎关注公众号「DeepNoMind」,星标不迷路。也欢迎访问独立站 www.DeepNoMind.com,一起交流成长。

本文由mdnice多平台发布