12.6 面试场景题精选
前面五节我们从智能客服讲到代码生成、数据分析,从多 Agent 协作讲到稳定性设计。这些知识怎么在面试中用出来?面试场景题考的不仅是"知不知道",更是"能不能用 SCALE 框架拆解问题、能不能清晰表达设计思路和权衡"。
本节精选四道最高频的 Agent 面试场景题——智能客服 Agent 设计、输出一致性保障、RAG 检索质量优化、工具调用失败处理——每道题都给出完整的分析框架和代码实现。下一章我们将进入实战项目,把前面所有章节的知识融合到一个完整的 Agent 项目中。
生活类比:急诊科医生的思维
想象你是医院急诊科的值班医生。患者被送进来时,你不会一上来就开药,而是先快速判断病情(场景分析),再决定要做哪些检查(组件拆解),然后设计诊疗方案(架构设计),同时考虑药物副作用和替代方案(限制与权衡),最后从急救到康复分阶段治疗(演进路径)。
面试场景题就是这种"急诊思维"的考验。面试官给你一个模糊的业务需求,你要在 15-20 分钟内把它拆解成清晰的系统设计。推荐使用 SCALE 框架:
S - Scenario(场景分析):明确需求、用户量、约束条件
C - Component(组件拆解):拆解为可独立设计的模块
A - Architecture(架构设计):设计模块间交互和数据流
L - Limitations(限制与权衡):指出方案的局限性和取舍
E - Evolution(演进路径):从MVP到生产级的演进计划一、场景题1:设计智能客服 Agent
题目:设计一个电商平台的智能客服 Agent,支持售前咨询、售后处理、订单查询,日活 100 万用户。
S - 场景分析:
- 功能需求:售前咨询(产品推荐、比价)、售后处理(退换货、投诉)、订单查询(物流、状态)
- 非功能需求:日活 100 万,峰值 QPS 约 5000,P99 延迟 < 3 秒
- 约束:不能给出错误承诺(如退货金额),敏感操作需人工确认
C - 组件拆解:
# 核心组件架构
class CustomerServiceSystem: # 智能客服系统
"""
组件清单:
1. 意图分类器 - 将用户消息分为:咨询/售后/订单/闲聊/投诉
2. 对话管理器 - 维护多轮对话状态和上下文
3. 知识检索器 - 从FAQ、产品库、历史工单中检索
4. 业务执行器 - 查询订单、创建工单等
5. 情绪分析器 - 实时分析用户情绪
6. 人工转接器 - 在必要时转人工
7. 安全审核器 - 防止不当回复
"""
passA - 架构设计:
用户请求 -> API Gateway -> 意图分类 -> 路由分发
├── 咨询:RAG检索 -> LLM生成 -> 安全审核 -> 回复
├── 售后:情绪分析 -> 工单查询 -> 策略决策 -> 回复
├── 订单:身份验证 -> 订单查询 -> 格式化回复
└── 投诉:情绪分析 -> 自动升级 -> 转人工关键设计决策:
- 缓存策略:热点问题(如"如何退货")缓存到 Redis,减少 LLM 调用
- 异步处理:非紧急操作(如创建工单)异步处理,快速返回 ACK
- 分级响应:简单问题走规则引擎,复杂问题走 LLM+RAG
L - 限制与权衡:
- LLM 可能产生幻觉,需要事实核查层
- 100 万 DAU 的成本控制:缓存+规则引擎覆盖 80% 场景
- 安全性:敏感操作(退款)必须人工确认
E - 演进路径:
- V1:规则引擎+FAQ 匹配(1 周上线)
- V2:加入 LLM+RAG(1 个月)
- V3:多 Agent 协作+自动学习(3 个月)
这个演进路径的设计思路是"先解决 80% 的问题,再逐步优化"。V1 用规则引擎覆盖高频标准问题(如退货流程、物流查询),这类问题占客服总量的 70-80%,用规则引擎 1 周就能上线,立即降低人工压力。V2 引入 LLM+RAG 处理需要语义理解的复杂问题。V3 做多 Agent 协作和自动学习——系统自动从历史对话中学习新的问答模式,持续提升覆盖率。
面试时如果面试官追问"100 万 DAU 的成本怎么控",关键回答是:缓存+规则引擎覆盖 80% 流量,只有 20% 复杂问题走 LLM+RAG,这样 LLM 调用量控制在可控范围内。同时通过异步批处理非紧急请求,削峰填谷降低峰值 QPS 压力。
二、场景题2:确保输出一致性
题目:如何确保 Agent 在相同输入下产生一致的输出?特别是涉及金额、日期等关键信息时。
LLM 本质上是概率模型——同一输入可能产生不同输出。这在创意场景是优点,但在涉及金额、日期、订单号等关键信息时是致命的。解决方案是四重保障:结构化输出约束、确定性后处理、事实核查、温度参数控制。
class OutputConsistency: # 输出一致性保障
"""输出一致性保障""" # 四重保障确保关键信息准确
# 策略1:结构化输出约束
STRUCTURED_OUTPUT_PROMPT = """请严格按照以下JSON格式回复,不要添加任何额外内容。
## 输出格式
```json
{
"answer": "回答内容",
"confidence": 0.0-1.0,
"sources": ["来源1", "来源2"],
"requires_human_review": true/false
}严格规则
金额字段只使用数字,不要添加货币符号
日期使用YYYY-MM-DD格式
如果信息不确定,confidence设为0,并标记requires_human_review=true """
策略2:确定性后处理
@staticmethod def normalize_output(response: str) -> str: """标准化输出""" # 用正则统一格式 import re # 正则模块 date_patterns = [ # 日期格式统一 (r'(\d{4})年(\d{1,2})月(\d{1,2})日', r'\1-\2-\3'), # 2024年1月1日->2024-1-1 (r'(\d{1,2})/(\d{1,2})/(\d{4})', r'\3-\1-\2'), # 01/15/2024->2024-01-15 ] for pattern, replacement in date_patterns: # 遍历日期模式 response = re.sub(pattern, replacement, response) # 替换 response = re.sub(r'[¥¥]', 'CNY', response) # 统一货币符号 return response # 返回标准化结果
策略3:事实核查
@staticmethod async def fact_check(response: str, knowledge_base) -> Dict: """对比知识库验证事实""" # 提取声明并逐条验证 claims = OutputConsistency._extract_claims(response) # 提取声明性语句 verified = [] # 验证结果列表 for claim in claims: # 逐条验证 kb_result = await knowledge_base.search(claim) # 查知识库 verified.append({ # 记录验证结果 "claim": claim, # 声明内容 "verified": len(kb_result) > 0, # 是否验证通过 "evidence": kb_result[0] if kb_result else None # 证据 }) return { # 返回验证汇总 "all_verified": all(v["verified"] for v in verified), # 是否全部通过 "details": verified # 详细结果 }
策略4:温度参数控制
@staticmethod def get_consistency_config(task_type: str) -> Dict: """根据任务类型返回一致性配置""" # 不同任务不同温度 configs = { "factual_qa": {"temperature": 0.0, "top_p": 1.0}, # 事实问答:最确定 "explanation": {"temperature": 0.3, "top_p": 0.9}, # 解释说明:较确定 "creative": {"temperature": 0.7, "top_p": 0.9}, # 创意写作:一般 "brainstorm": {"temperature": 1.0, "top_p": 0.95}, # 头脑风暴:最随机 } return configs.get(task_type, configs["factual_qa"]) # 默认用最确定
**面试回答要点**:
1. 区分"需要一致性"和"需要多样性"的场景——事实问答要确定性,创意写作要多样性
2. 技术手段:结构化输出、验证层、低温度参数、多次采样投票
3. 关键信息(金额、日期)必须经过后处理验证,不能完全信任 LLM
4. 不确定时明确告知用户,而非给出看似确定的错误答案
**面试加分技巧**:如果面试官追问"多次采样投票怎么实现",可以这样回答——对同一问题用 temperature=0.3 生成 3 次回答,取多数一致的部分作为最终输出。如果 3 次答案各不相同,说明模型对这个问题不确定,应该标记 `requires_human_review=true` 转人工。这种"投票+置信度"的组合策略,在金融、医疗等高精度场景尤其重要。
#### 三、场景题3:RAG 检索质量优化
**题目**:RAG 系统检索到的文档不相关,导致回答质量差。如何诊断和优化?
RAG 是 Agent 的核心能力之一,但"检索到不相关文档"是 RAG 系统最常见的痛点。面试时不能只说"换个 embedding 模型"——要先诊断再优化,分层处理。
诊断的思路是逐一排查可能的故障点:查询是否被正确理解、文档分块是否合理、向量相似度分布是否正常、有没有重复文档。`RAGDiagnostics` 把这套诊断逻辑编码成了代码:
```python
class RAGDiagnostics: # RAG系统诊断工具
"""RAG系统诊断工具""" # 逐一排查检索质量问题
def __init__(self): # 初始化
self.metrics = {} # 诊断指标
def diagnose(self, query: str, retrieved_docs: List[str],
expected_docs: List[str] = None) -> Dict:
"""诊断RAG检索质量""" # 返回问题列表和建议
issues = [] # 发现的问题
# 检查1:Query是否被正确理解
if len(query.split()) < 3: # 查询太短
issues.append("查询过短,可能缺少上下文")
# 检查2:文档分块是否合理
avg_chunk_size = sum(len(d) for d in retrieved_docs) / max(len(retrieved_docs), 1) # 平均分块大小
if avg_chunk_size < 100: # 分块太小
issues.append("文档分块过小,缺少上下文")
elif avg_chunk_size > 2000: # 分块太大
issues.append("文档分块过大,噪声过多")
# 检查3:向量相似度分布(召回率)
if expected_docs: # 有标注的期望结果
recall = len(set(expected_docs) & set(retrieved_docs)) / len(expected_docs) # 计算召回率
if recall < 0.5: # 召回率太低
issues.append(f"召回率过低:{recall:.2f}")
# 检查4:重复文档
unique_docs = set(retrieved_docs) # 去重后的文档集
if len(unique_docs) < len(retrieved_docs): # 有重复
issues.append(f"存在重复文档:{len(retrieved_docs) - len(unique_docs)}个重复")
return { # 返回诊断报告
"issues": issues, # 问题列表
"severity": "high" if len(issues) > 2 else "medium" if issues else "low",
"recommendations": self._generate_recommendations(issues) # 优化建议
}
def _generate_recommendations(self, issues: List[str]) -> List[str]:
"""生成优化建议""" # 根据问题给建议
recommendations = [] # 建议列表
for issue in issues: # 遍历每个问题
if "分块过小" in issue: # 分块太小
recommendations.append("增大chunk_size到500-800,增加overlap到100")
elif "分块过大" in issue: # 分块太大
recommendations.append("减小chunk_size到300-500")
elif "召回率过低" in issue: # 召回率低
recommendations.append("尝试混合检索(BM25+向量),或使用重排序模型")
elif "重复" in issue: # 有重复
recommendations.append("添加去重逻辑,或使用MMR算法增加多样性")
elif "查询过短" in issue: # 查询太短
recommendations.append("使用Query Rewriting或HyDE技术扩充查询")
return recommendations # 返回建议列表诊断后要分层优化。RAGOptimizer 总结了八种主流优化策略:
class RAGOptimizer: # RAG优化策略集
"""RAG优化策略集""" # 八种策略覆盖全链路
STRATEGIES = {
"query_rewriting": "使用LLM改写用户查询,增加关键词和上下文", # Query层优化
"hyde": "先生成假设性答案,再用答案检索相关文档", # 假设性答案
"hybrid_search": "BM25(关键词)+ 向量(语义)混合检索", # 检索层优化
"reranking": "使用Cross-Encoder重排序检索结果", # 后处理优化
"chunk_optimization": "优化文档分块策略(大小、重叠、语义分块)", # 分块优化
"metadata_filtering": "利用元数据过滤(时间、类别、来源)", # 元数据过滤
"multi_query": "生成多个查询变体,合并检索结果", # 多查询
"self_query": "LLM自动提取查询中的过滤条件", # 自动过滤
}面试回答要点:
- 先诊断再优化:分块策略 -> Embedding 质量 -> 检索算法 -> 重排序,逐一排查
- 分层优化:Query 层(改写)-> 检索层(混合检索)-> 后处理层(重排序)
- 评估指标:Recall@K、MRR、NDCG,用数据说话而非凭感觉
- 权衡:提高召回率可能降低精确率,需根据场景选择
四、场景题4:工具调用失败处理
题目:Agent 调用工具时可能失败(超时、权限不足、结果异常),如何设计健壮的错误处理?
工具调用是 Agent 执行动作的核心途径,但外部工具不可能 100% 可靠。面试时不能只说"加个 try-catch"——要区分错误类型,对不同错误用不同策略。
核心思路是错误分类 -> 匹配策略 -> 降级兜底:可重试错误(超时、限流)用指数退避重试;不可重试错误(权限不足)直接降级;结果异常要验证后再决定是否重试。
class ToolCallErrorHandler: # 工具调用错误处理器
"""工具调用错误处理器""" # 分类错误+匹配策略+降级
# 错误分类与处理策略
ERROR_STRATEGIES = {
"timeout": { # 超时:可重试
"retry": True,
"max_retries": 3,
"backoff": "exponential",
"fallback": "use_cached_result"
},
"permission_denied": { # 权限不足:不可重试
"retry": False,
"fallback": "request_user_permission",
"user_message": "需要您的授权才能执行此操作"
},
"rate_limited": { # 限流:可重试但等更久
"retry": True,
"max_retries": 5,
"backoff": "exponential",
"fallback": "queue_and_retry_later"
},
"invalid_input": { # 输入无效:让LLM修复
"retry": True,
"strategy": "fix_input_with_llm",
"max_retries": 2,
"fallback": "ask_user_for_clarification"
},
"service_unavailable": { # 服务不可用:线性退避
"retry": True,
"max_retries": 3,
"backoff": "linear",
"fallback": "use_alternative_tool"
},
"unexpected_result": { # 结果异常:验证后重试
"retry": True,
"strategy": "validate_and_retry",
"max_retries": 2,
"fallback": "report_to_user"
}
}
def __init__(self): # 初始化
self.circuit_breakers = {} # 熔断器集合
self.tool_alternatives = {} # 替代工具表
def register_alternative(self, tool_name: str, alternative: callable):
"""注册替代工具""" # 主工具挂了用备胎
self.tool_alternatives[tool_name] = alternative
async def execute_with_recovery(self, tool_name: str, tool_func: callable,
*args, **kwargs) -> Dict:
"""带恢复策略的工具执行""" # 主入口:执行+重试+降级
strategy = self.ERROR_STRATEGIES.get("service_unavailable") # 默认策略
last_error = None # 记录最后一次错误
for attempt in range(strategy.get("max_retries", 3) + 1): # 最多重试N次
try:
result = await tool_func(*args, **kwargs) # 执行工具
if self._is_valid_result(result): # 验证结果
return {"success": True, "data": result, "attempts": attempt + 1}
else: # 结果无效
last_error = ToolResultError("结果验证失败")
except asyncio.TimeoutError: # 超时
last_error = ToolTimeoutError(f"{tool_name} 超时")
strategy = self.ERROR_STRATEGIES["timeout"] # 切换到超时策略
except PermissionError: # 权限不足
return await self._handle_permission_error(tool_name) # 直接处理
except Exception as e: # 其他异常
last_error = e
if attempt < strategy.get("max_retries", 3): # 还能重试
delay = self._calculate_backoff(attempt, strategy.get("backoff", "linear"))
await asyncio.sleep(delay) # 等待退避时间
return await self._fallback(tool_name, last_error) # 所有重试失败,走降级
async def _fallback(self, tool_name: str, error: Exception) -> Dict:
"""执行降级策略""" # 替代工具->告知用户
if tool_name in self.tool_alternatives: # 有替代工具
try:
result = await self.tool_alternatives[tool_name]() # 用备胎
return {
"success": True, "data": result, "degraded": True,
"message": f"使用了替代工具,原工具错误:{str(error)}"
}
except Exception: # 备胎也挂了
pass
return { # 最终降级:告知用户
"success": False,
"error": str(error),
"user_message": f"抱歉,{tool_name} 功能暂时不可用,请稍后重试或联系人工客服。"
}
def _calculate_backoff(self, attempt: int, strategy: str) -> float:
"""计算退避时间""" # 指数或线性
if strategy == "exponential": # 指数退避
return min(2 ** attempt, 30) # 最大30秒
return attempt * 2 # 线性退避:2,4,6...
def _is_valid_result(self, result: Any) -> bool:
"""验证结果有效性""" # 空值和错误都算无效
if result is None: # 返回空
return False
if isinstance(result, dict) and result.get("error"): # 返回错误
return False
return True # 有效
class ToolTimeoutError(Exception): pass # 超时异常
class ToolPermissionError(Exception): pass # 权限异常
class ToolResultError(Exception): pass # 结果异常面试回答要点:
- 错误分类:区分可重试错误(超时、限流)和不可重试错误(权限、参数错误)
- 退避策略:指数退避避免雪崩,添加随机抖动
- 降级方案:替代工具 -> 缓存结果 -> 告知用户,三级降级
- 熔断保护:连续失败 n 次后暂时跳过该工具(见 12.5 节的
CircuitBreaker)
五、常见误区
误区一:面试时直接跳到实现细节。很多人拿到题目就开始写代码,但面试官想看的是你的思考过程。应该先用 SCALE 框架做场景分析、组件拆解,再讲架构设计,最后才到代码。
误区二:方案没有权衡。好的设计不是"完美方案",而是"在约束条件下的最优选择"。面试官追问"为什么不用 X 方案"时,要能说出 X 方案的缺点和当前方案的取舍。
误区三:RAG 优化只谈 embedding。很多人一提到 RAG 质量差就说"换更好的 embedding 模型",但实际上分块策略、查询改写、重排序的影响往往比 embedding 更大。
误区四:错误处理只做重试。有人对所有错误都无脑重试,但权限错误重试 100 次还是权限错误。必须区分可重试和不可重试错误,不可重试的直接走降级。
误区五:演进路径缺失。面试官常问"如果给你 3 个月怎么做到生产级"。如果你只讲 V1 方案不讲演进路径,面试官会认为你缺乏工程思维。V1 用规则引擎快速上线,V2 加 LLM+RAG,V3 做多 Agent 协作+自动学习。
六、本节小结
| 场景题 | 核心考点 | 关键思路 |
|---|---|---|
| 客服 Agent 设计 | 系统设计、多轮对话、RAG | 意图分流 -> 知识检索 -> 安全审核 -> 人工兜底 |
| 输出一致性 | LLM 可控性、后处理 | 结构化输出 + 验证层 + 低温度 + 事实核查 |
| RAG 检索质量 | 信息检索、向量搜索 | Query 改写 -> 混合检索 -> 重排序 -> 评估指标 |
| 工具调用失败 | 错误处理、容错设计 | 错误分类 -> 退避重试 -> 降级 -> 熔断 -> 告知用户 |
面试场景题是本章所有知识的"综合考"。客服 Agent 考的是 12.1 的架构设计,输出一致性考的是 12.5 的不确定性管理,RAG 优化考的是 12.3 的数据分析能力,工具调用失败考的是 12.5 的熔断降级。掌握了 SCALE 框架和这四道题的分析思路,你就能在面试中从容应对大多数 Agent 系统设计题。下一章我们将进入实战项目,把全书的知识融合到一个完整的 Agent 项目中,从设计到实现到部署,走一遍完整流程。