Files
examination/topics/interview-prep/ai-engineering/code_reading.json
T
wonder 0f68a64829
Deploy Examination / deploy (push) Successful in 10s
feat: add interview-prep topic group with 250 questions (6 subtopics, 5 question types)
Subtopics:
- distributed-microservice: 45 questions (分布式微服务架构)
- message-queue: 45 questions (消息队列)
- k8s-observability: 45 questions (K8s与可观测性)
- go-java-concurrency: 45 questions (Go/Java并发模型)
- database-advanced: 35 questions (数据库进阶)
- ai-engineering: 35 questions (AI工程实践)

Question types: single_choice, true_false, fill_blank, short_answer, code_reading
2026-09-09 16:36:27 +08:00

315 lines
28 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"topic": "ai-engineering",
"type": "code_reading",
"schema_version": "1.0.0",
"generated": "2026-09-09T10:00:00+08:00",
"questions": [
{
"id": "cr-001",
"type": "code_reading",
"difficulty": 3,
"tags": [
"tool-calling",
"ai-agent",
"harness-engineering"
],
"question": "以下是一个 AI Agent 调用外部工具的 Python 实现。该 Agent 接收用户查询,根据 LLM 判断是否需要调用工具,并在获取工具结果后生成最终回复。请阅读代码并回答子问题。",
"code": "import json\nfrom typing import Any, Callable\n\n# 工具注册表:name -> {description, parameters, handler}\ntool_registry: dict[str, dict] = {}\n\ndef register_tool(name: str, description: str, params: dict, handler: Callable):\n \"\"\"注册一个可被 Agent 调用的工具\"\"\"\n tool_registry[name] = {\n \"description\": description,\n \"parameters\": params,\n \"handler\": handler,\n }\n\ndef call_tool(name: str, arguments: dict[str, Any]) -> str:\n \"\"\"执行工具调用,带参数校验和异常处理\"\"\"\n if name not in tool_registry:\n return json.dumps({\"error\": f\"Unknown tool: {name}\"})\n tool = tool_registry[name]\n try:\n # 按 schema 校验必填参数\n for param, spec in tool[\"parameters\"].items():\n if spec.get(\"required\") and param not in arguments:\n return json.dumps({\"error\": f\"Missing required param: {param}\"})\n result = tool[\"handler\"](**arguments)\n return json.dumps({\"result\": result})\n except Exception as e:\n return json.dumps({\"error\": str(e)})\n\ndef agent_loop(user_query: str, llm_client) -> str:\n \"\"\"Agent 主循环:查询 -> 判断 -> 调用工具 -> 拼接回复\"\"\"\n tools_schema = [\n {\"name\": k, \"description\": v[\"description\"], \"parameters\": v[\"parameters\"]}\n for k, v in tool_registry.items()\n ]\n response = llm_client.chat(user_query, tools=tools_schema)\n\n # LLM 返回 tool_calls 时执行工具\n if response.get(\"tool_calls\"):\n tool_results = []\n for tc in response[\"tool_calls\"]:\n result = call_tool(tc[\"name\"], tc[\"arguments\"])\n tool_results.append({\"tool\": tc[\"name\"], \"output\": result})\n # 将工具结果反馈给 LLM 生成最终回答\n followup = llm_client.chat(\n f\"Tools returned: {json.dumps(tool_results)}\",\n tools=tools_schema,\n )\n return followup[\"content\"]\n return response[\"content\"]",
"language": "python",
"sub_questions": [
{
"index": 1,
"type": "single_choice",
"question": "当 LLM 返回的 tool_calls 中包含多个工具调用时,当前代码对这些调用的执行方式是?",
"options": {
"A": "并行异步执行,使用 asyncio.gather",
"B": "串行顺序执行,逐个调用",
"C": "只执行第一个工具调用,忽略其余",
"D": "随机选择一个工具调用执行"
},
"answer": "B",
"explanation": "代码中使用 for tc in response[\"tool_calls\"] 串行遍历并逐个调用 call_tool(),所有工具调用按顺序依次执行,没有使用异步或并发机制。"
},
{
"index": 2,
"type": "single_choice",
"question": "call_tool 函数在遇到工具不存在时的处理策略是?",
"options": {
"A": "抛出 KeyError 异常中断流程",
"B": "返回包含 error 字段的 JSON 字符串",
"C": "自动注册该工具并返回空结果",
"D": "从 tool_registry 中删除该条目后重试"
},
"answer": "B",
"explanation": "代码中 if name not in tool_registry 分支直接返回 json.dumps({\"error\": f\"Unknown tool: {name}\"}),即返回一个描述错误信息的 JSON 字符串,不会抛出异常。"
},
{
"index": 3,
"type": "short_answer",
"question": "当前 agent_loop 在执行完工具调用后,将结果反馈给 LLM 生成最终回复。请指出这种设计中可能存在的一个安全风险,并简要说明如何缓解。",
"answer": "LLM 可能产生幻觉,在最终回复中编造不存在的工具调用结果",
"keywords": [
"prompt injection",
"工具结果注入",
"结果篡改",
"二次验证",
"权限控制",
"沙箱"
],
"scoring_rubric": "答出工具结果可能被恶意利用(如 prompt injection)或 LLM 可能忽略/篡改工具返回内容得 2 分;提出缓解措施(如对工具返回值做转义、限制 LLM 只能引用原始返回、结果二次验证等)再得 2 分;总分 4 分。",
"explanation": "工具返回的内容被直接拼入 prompt,攻击者可能通过操控工具返回来实施 prompt injection,或 LLM 可能忽略/错误引用工具结果。"
}
],
"explanation": "这段代码展示了一个经典的 Tool Calling Agent 架构:工具注册 -> LLM 判断 -> 工具执行 -> 结果回传。核心要点包括:1) 工具通过注册表管理,支持动态扩展;2) call_tool 包含参数校验和异常兜底,保证鲁棒性;3) 串行执行工具调用在简单场景够用,但高延迟场景需要并发优化;4) 工具结果直接拼入 prompt 可能带来 prompt injection 风险。",
"source": null,
"related": []
},
{
"id": "cr-002",
"type": "code_reading",
"difficulty": 4,
"tags": [
"ai-agent",
"context-engineering",
"harness-engineering"
],
"question": "以下是一个 RAG(检索增强生成)流程的核心实现,包含向量检索、上下文拼接和最终生成。请阅读代码并回答子问题。",
"code": "from dataclasses import dataclass\nfrom typing import Optional\n\n@dataclass\nclass RetrievalResult:\n content: str\n score: float\n source: str\n\ndef retrieve(\n query: str,\n vector_store, # 向量数据库客户端\n top_k: int = 5,\n score_threshold: float = 0.7,\n) -> list[RetrievalResult]:\n \"\"\"从向量库中检索相关文档片段\"\"\"\n embeddings = vector_store.encode(query)\n raw_results = vector_store.search(embeddings, k=top_k * 2) # 过量召回\n\n # 过滤低分结果并去重\n seen = set()\n filtered = []\n for doc, score in raw_results:\n if score < score_threshold:\n continue\n dedup_key = hash(doc.content[:100])\n if dedup_key in seen:\n continue\n seen.add(dedup_key)\n filtered.append(RetrievalResult(doc.content, score, doc.source))\n return filtered[:top_k]\n\ndef build_prompt(\n query: str,\n contexts: list[RetrievalResult],\n max_context_tokens: int = 3000,\n) -> str:\n \"\"\"将检索结果拼接进 LLM prompt\"\"\"\n header = \"根据以下参考资料回答用户问题。如果资料不足请说明。\\n\\n\"\n context_parts = []\n token_budget = max_context_tokens\n\n for i, ctx in enumerate(contexts):\n chunk = f\"[参考{i+1}] (来源: {ctx.source}, 相关度: {ctx.score:.2f})\\n{ctx.content}\\n\"\n # 粗略估算 token 数(1 中文 ≈ 2 token)\n est_tokens = len(chunk) * 2\n if est_tokens > token_budget:\n break\n context_parts.append(chunk)\n token_budget -= est_tokens\n\n context_block = \"\\n\".join(context_parts)\n return f\"{header}--- 参考资料 ---\\n{context_block}\\n---\\n\\n用户问题: {query}\\n\"",
"language": "python",
"sub_questions": [
{
"index": 1,
"type": "single_choice",
"question": "retrieve 函数中为什么要用 top_k * 2 进行过量召回(oversampling)?",
"options": {
"A": "为了提高向量搜索的速度",
"B": "因为后续的分数过滤和去重可能淘汰部分结果,需预留余量",
"C": "为了减少向量数据库的查询次数",
"D": "为了让 LLM 有更多 token 可用"
},
"answer": "B",
"explanation": "过量召回是为了给后续的 score_threshold 过滤和 hash 去重留出余量。如果恰好召回 top_k 条,经过过滤后可能不足 top_k 条有效结果,导致返回结果数少于预期。"
},
{
"index": 2,
"type": "short_answer",
"question": "build_prompt 使用了 token_budget 机制来控制上下文长度。请说明这种策略的潜在问题,并提出至少一种改进方案。",
"answer": "粗略的字符数估算不够精确,可能严重高估或低估实际 token 数",
"keywords": [
"token 计算",
"字符估算",
"tiktoken",
"tokenizer",
"动态截断",
"压缩",
"摘要"
],
"scoring_rubric": "指出字符级估算的不准确性(如中英文混合、标点符号差异)得 2 分;提出改进方案(使用 tiktoken 精确计算、对长 chunk 做摘要压缩、按语义段落截断等)得 2 分;总分 4 分。",
"explanation": "字符级 token 估算在中英文混合场景误差大,应使用 tokenizer 库精确计算。"
},
{
"index": 3,
"type": "single_choice",
"question": "以下关于这段 RAG 代码的说法,哪一项最准确地描述了它在生产环境中的局限?",
"options": {
"A": "代码没有调用 LLM,所以无法生成最终回答",
"B": "缺少对检索结果的相关性重排序(re-ranking),在噪声文档较多时检索质量会下降",
"C": "代码使用了同步调用,无法支持并发请求",
"D": "vector_store 的 search 方法不支持批量查询"
},
"answer": "B",
"explanation": "这段代码仅靠向量相似度排序,没有使用 cross-encoder 或 LLM-based re-ranking 对结果进行精排。在实际生产中,向量检索的初始召回往往包含噪声,re-ranking 可以显著提升最终送入 LLM 的上下文质量。"
}
],
"explanation": "这段 RAG 代码涵盖了三个核心阶段:1) 过量召回 + 过滤去重的检索策略,保证结果质量;2) 基于 token budget 的上下文拼接,防止超出模型窗口限制;3) 结构化 prompt 模板引导 LLM 利用参考资料。生产级 RAG 还需考虑 re-ranking、query rewriting、混合检索(向量+关键词)、上下文压缩等优化。",
"source": null,
"related": []
},
{
"id": "cr-003",
"type": "code_reading",
"difficulty": 3,
"tags": [
"mcp",
"ai-agent",
"tool-calling"
],
"question": "以下是一个 MCP(Model Context Protocol)Server 的 Python 实现,展示了如何注册和提供 Tool 给外部 Agent 调用。请阅读代码并回答子问题。",
"code": "from mcp.server import Server\nfrom mcp.types import Tool, TextContent\nimport json, os\n\nserver = Server(\"weather-server\")\n\n# 定义 MCP Tool 的元信息和参数 schema\nWEATHER_TOOL = Tool(\n name=\"get_weather\",\n description=\"获取指定城市的当前天气信息\",\n inputSchema={\n \"type\": \"object\",\n \"properties\": {\n \"city\": {\"type\": \"string\", \"description\": \"城市名称,如 Beijing\"},\n \"unit\": {\n \"type\": \"string\",\n \"enum\": [\"celsius\", \"fahrenheit\"],\n \"description\": \"温度单位\",\n \"default\": \"celsius\",\n },\n },\n \"required\": [\"city\"],\n },\n)\n\n# 注册可用工具列表\n@server.list_tools()\nasync def list_tools() -> list[Tool]:\n return [WEATHER_TOOL]\n\n# 处理工具调用请求\n@server.call_tool()\nasync def handle_call(name: str, arguments: dict) -> list[TextContent]:\n if name != \"get_weather\":\n raise ValueError(f\"Unsupported tool: {name}\")\n city = arguments[\"city\"]\n unit = arguments.get(\"unit\", \"celsius\")\n # 模拟调用外部天气 API\n api_key = os.environ.get(\"WEATHER_API_KEY\")\n if not api_key:\n return [TextContent(type=\"text\", text=json.dumps({\"error\": \"Missing API key\"}))]\n weather_data = fetch_weather_from_api(city, unit, api_key) # 外部 API 调用\n return [TextContent(type=\"text\", text=json.dumps(weather_data))]",
"language": "python",
"sub_questions": [
{
"index": 1,
"type": "single_choice",
"question": "MCP Server 中 inputSchema 的作用是什么?",
"options": {
"A": "定义 MCP Server 自身的配置参数",
"B": "描述 Tool 接受的输入参数结构,供调用方(如 LLM)生成符合格式的请求",
"C": "定义 Tool 返回结果的数据格式",
"D": "限制调用方只能调用该 Tool 一次"
},
"answer": "B",
"explanation": "inputSchema 是 JSON Schema 格式的参数描述,MCP Client(通常是 LLM Agent)会读取这个 schema 来了解 tool 需要哪些参数、参数类型和是否必填,从而生成正确的调用参数。这与 OpenAI Function Calling 中的 parameters 字段作用一致。"
},
{
"index": 2,
"type": "single_choice",
"question": "handle_call 函数返回的是 list[TextContent] 而非单个字符串,这样设计的主要原因是?",
"options": {
"A": "为了支持异步并发调用",
"B": "为了兼容 MCP 协议中一次 Tool 调用可以返回多个内容块的设计",
"C": "因为 Python 的 json.dumps 只能处理列表",
"D": "为了隐藏实际的返回数据"
},
"answer": "B",
"explanation": "MCP 协议设计上支持 Tool 返回多种类型的内容块(文本、图片、嵌入数据等),所以返回类型是 list[Content]。当前只返回 TextContent 是最简单的情况,但如果需要返回混合内容(如图表+文本),这种设计提供了扩展性。"
},
{
"index": 3,
"type": "short_answer",
"question": "在生产环境中,这个 MCP Server 缺少哪些关键的健壮性保障措施?请列举至少两项。",
"answer": "缺少输入参数校验、请求限流、API Key 安全管理、超时控制、调用日志记录、错误重试",
"keywords": [
"参数校验",
"限流",
"rate limit",
"超时",
"timeout",
"日志",
"logging",
"重试",
"安全",
"认证"
],
"scoring_rubric": "每正确列举一项健壮性措施并简要说明得 1.5 分,最高 4 分。示例:缺少参数校验(应验证 city 非空且长度合理)1.5 分;缺少限流(应防止恶意高频调用)1.5 分;缺少超时控制(外部 API 调用应有 timeout)1.5 分;缺少调用日志(应记录每次调用的入参和结果)1.5 分。",
"explanation": "生产级 MCP Server 需要完整的健壮性保障,包括参数校验、限流、超时、日志等。"
}
],
"explanation": "这段代码展示了 MCP Server 的核心模式:1) 通过 Tool 元信息(name、description、inputSchema)声明式地注册工具能力;2) 使用装饰器模式分别处理 list_tools 和 call_tool 请求;3) 返回标准化的 Content 类型。MCP 的关键设计是将 Tool 定义与实现分离,使得 Agent 可以在运行时动态发现和调用 Tool,实现即插即用的能力扩展。",
"source": null,
"related": []
},
{
"id": "cr-004",
"type": "code_reading",
"difficulty": 3,
"tags": [
"guardrail",
"ai-agent",
"llm-ops"
],
"question": "以下是一个 LLM 输出 Guardrail 过滤器的 Python 实现,用于在将 LLM 回复发送给用户之前进行安全检查。请阅读代码并回答子问题。",
"code": "import re\nfrom dataclasses import dataclass, field\nfrom enum import Enum\n\nclass SafetyLevel(Enum):\n SAFE = \"safe\"\n WARNING = \"warning\"\n BLOCKED = \"blocked\"\n\n@dataclass\nclass GuardrailResult:\n level: SafetyLevel\n original: str\n sanitized: str = \"\"\n violations: list[str] = field(default_factory=list)\n\ndef check_pii(text: str) -> list[str]:\n \"\"\"检测文本中的个人身份信息(PII)\"\"\"\n violations = []\n # 检测手机号(中国大陆 11 位)\n if re.search(r'1[3-9]\\d{9}', text):\n violations.append(\"phone_number\")\n # 检测身份证号(18 位)\n if re.search(r'\\d{17}[\\dXx]', text):\n violations.append(\"id_card\")\n # 检测邮箱地址\n if re.search(r'[\\w.-]+@[\\w.-]+\\.\\w+', text):\n violations.append(\"email\")\n return violations\n\ndef check_prompt_leakage(text: str) -> list[str]:\n \"\"\"检测是否泄露系统提示词\"\"\"\n leak_patterns = [\n r'(?i)system\\s*prompt',\n r'(?i)you\\s+are\\s+a\\s+',\n r'(?i)ignore\\s+(previous|above|all)\\s+instructions',\n ]\n return [\"prompt_leakage\"] if any(re.search(p, text) for p in leak_patterns) else []\n\ndef apply_guardrails(output: str, strict: bool = False) -> GuardrailResult:\n \"\"\"应用所有安全检查规则,返回过滤结果\"\"\"\n violations = []\n violations.extend(check_pii(output))\n violations.extend(check_prompt_leakage(output))\n\n if not violations:\n return GuardrailResult(SafetyLevel.SAFE, output)\n # PII 敏感信息直接脱敏\n sanitized = re.sub(r'1[3-9]\\d{9}', '[手机号已脱敏]', output)\n sanitized = re.sub(r'\\d{17}[\\dXx]', '[身份证已脱敏]', sanitized)\n sanitized = re.sub(r'[\\w.-]+@[\\w.-]+\\.\\w+', '[邮箱已脱敏]', sanitized)\n # 在非严格模式下,PII 只警告不拦截;prompt leakage 始终拦截\n has_leakage = \"prompt_leakage\" in violations\n if has_leakage or strict:\n return GuardrailResult(SafetyLevel.BLOCKED, output, \"\", violations)\n return GuardrailResult(SafetyLevel.WARNING, output, sanitized, violations)",
"language": "python",
"sub_questions": [
{
"index": 1,
"type": "single_choice",
"question": "当 strict=False 且 LLM 输出仅包含 PII 信息(不含 prompt leakage)时,apply_guardrails 的返回结果是?",
"options": {
"A": "level=BLOCKED,sanitized 为空字符串",
"B": "level=WARNING,sanitized 包含脱敏后的文本",
"C": "level=SAFE,原样返回",
"D": "抛出异常,要求必须在 strict 模式下运行"
},
"answer": "B",
"explanation": "代码中,当 strict=False 且没有 prompt_leakage 时,走 else 分支返回 GuardrailResult(SafetyLevel.WARNING, output, sanitized, violations),level 为 WARNING,sanitized 包含脱敏后的文本。"
},
{
"index": 2,
"type": "short_answer",
"question": "当前的 check_pii 函数使用正则表达式检测手机号。请指出这种方法在实际生产中可能遗漏的一个 PII 类型,并简要说明为什么它难以用简单正则捕获。",
"answer": "自然语言中嵌入的地址信息难以用正则捕获",
"keywords": [
"地址",
"姓名",
"自然语言",
"NER",
"上下文",
"语义理解",
"NLP"
],
"scoring_rubric": "指出一种难以用正则检测的 PII 类型(如地址、姓名、银行卡号、IP 地址等)得 2 分;解释为什么难以捕获(如需要语义理解、NER 模型、上下文推理等)得 2 分;总分 4 分。",
"explanation": "地址、姓名等自然语言中的 PII 缺乏固定格式,需要 NER 模型进行语义级检测。"
},
{
"index": 3,
"type": "single_choice",
"question": "关于这段 Guardrail 代码的设计,以下哪项评价最准确?",
"options": {
"A": "使用了 LLM-as-Judge 方案,判断精度最高",
"B": "基于规则的过滤方案,速度快但无法检测语义级别的安全问题",
"C": "完全依赖外部 API 进行安全检查,延迟最低",
"D": "使用了深度学习模型,对所有类型的内容都有效"
},
"answer": "B",
"explanation": "该 Guardrail 使用正则表达式和关键词匹配等规则方法,优点是延迟低、可控性强、可解释性好,但缺点是无法处理语义级别的问题(如隐晦的有害内容、方言表达、上下文相关的攻击等),这些需要 NLU 模型或 LLM-based guardrail 来补充。"
}
],
"explanation": "这段 Guardrail 代码展示了生产级 LLM 输出过滤的常见模式:1) 分层检查(PII + Prompt Leakage 分开检测);2) 脱敏而非拦截(对 PII 优先脱敏而非直接阻断,平衡安全与用户体验);3) 严格/宽松模式切换(strict 模式用于高安全场景)。实际生产中,还需补充有害内容检测(toxicity)、幻觉检测、事实性校验等模块,通常会组合使用规则引擎 + 分类模型 + LLM Judge 的多层架构。",
"source": null,
"related": []
},
{
"id": "cr-005",
"type": "code_reading",
"difficulty": 4,
"tags": [
"llm-ops",
"ai-agent",
"harness-engineering"
],
"question": "以下是一个 AI Agent 调用 LLM 时的重试与降级机制的 Python 实现。当主模型调用失败或返回异常时,系统会自动重试或切换到备用模型。请阅读代码并回答子问题。",
"code": "import time\nimport logging\nfrom dataclasses import dataclass\nfrom typing import Optional\n\nlogger = logging.getLogger(__name__)\n\n@dataclass\nclass LLMConfig:\n provider: str # 如 \"openai\", \"anthropic\", \"local\"\n model: str # 如 \"gpt-4o\", \"claude-3\"\n api_key: str\n max_retries: int = 2\n timeout: float = 30.0\n\ndef call_llm_with_retry(\n prompt: str,\n configs: list[LLMConfig], # 按优先级排列的模型配置列表\n fallback_response: str = \"抱歉,服务暂时不可用,请稍后重试。\",\n) -> str:\n \"\"\"带重试和降级的 LLM 调用\"\"\"\n last_error = None\n\n for config in configs:\n for attempt in range(config.max_retries + 1):\n try:\n logger.info(f\"Calling {config.provider}/{config.model} (attempt {attempt + 1})\")\n response = _call_provider(config, prompt)\n\n # 检查响应质量(空内容或错误标记)\n if not response or response.strip() == \"\":\n raise ValueError(\"Empty response from LLM\")\n if response.startswith(\"[ERROR]\"):\n raise ValueError(f\"LLM returned error: {response}\")\n\n logger.info(f\"Success with {config.provider}/{config.model}\")\n return response\n\n except Exception as e:\n last_error = e\n logger.warning(\n f\"{config.provider}/{config.model} failed \"\n f\"(attempt {attempt + 1}): {e}\"\n )\n # 指数退避:等待 1s, 2s, 4s ...\n if attempt < config.max_retries:\n wait = 2 ** attempt\n logger.info(f\"Retrying in {wait}s...\")\n time.sleep(wait)\n\n # 当前 provider 的所有重试耗尽,切换到下一个\n logger.warning(f\"All retries exhausted for {config.provider}, falling back...\")\n\n # 所有 provider 都失败,返回兜底响应\n logger.error(f\"All LLM providers failed. Last error: {last_error}\")\n return fallback_response\n\ndef _call_provider(config: LLMConfig, prompt: str) -> str:\n \"\"\"实际调用 LLM provider(此处为占位实现)\"\"\"\n if config.provider == \"openai\":\n # 实际会调用 OpenAI API\n return _call_openai(config.model, prompt, config.api_key, config.timeout)\n elif config.provider == \"anthropic\":\n return _call_anthropic(config.model, prompt, config.api_key, config.timeout)\n else:\n raise ValueError(f\"Unknown provider: {config.provider}\")",
"language": "python",
"sub_questions": [
{
"index": 1,
"type": "single_choice",
"question": "代码中使用了 2 ** attempt 作为重试间隔。当某个 provider 的 max_retries=3 时,前四次尝试(含首次)的等待时间序列是?",
"options": {
"A": "0s, 1s, 2s, 4s",
"B": "1s, 2s, 4s, 8s",
"C": "1s, 1s, 1s, 1s",
"D": "0s, 0s, 0s, 0s"
},
"answer": "A",
"explanation": "attempt 从 0 开始:第 1 次 attempt=0(首次调用,不等待),第 2 次 attempt=0 失败后 wait=2^0=1s,第 3 次 attempt=1 失败后 wait=2^1=2s,第 4 次 attempt=2 失败后 wait=2^2=4s。所以等待序列为 0s, 1s, 2s, 4s。"
},
{
"index": 2,
"type": "single_choice",
"question": "当 configs 列表中有 2 个 provider 且都调用失败时,函数最终返回什么?",
"options": {
"A": "抛出异常,不返回任何值",
"B": "返回最后一个 provider 最后一次调用的原始异常信息",
"C": "返回 fallback_response 参数指定的兜底文本",
"D": "返回空字符串"
},
"answer": "C",
"explanation": "两个 for 循环(provider 循环 + attempt 循环)都执行完后,函数执行最后的 return fallback_response,返回调用方传入的兜底响应文本。这是防御性编程的典型模式——永远给用户一个有意义的回复。"
},
{
"index": 3,
"type": "short_answer",
"question": "当前实现使用了 time.sleep() 进行同步阻塞等待。如果这个函数在异步 Web 服务中被调用,会有什么问题?请提出一种不阻塞事件循环的替代方案。",
"answer": "time.sleep 会阻塞整个事件循环,导致同进程内其他并发请求无法处理",
"keywords": [
"阻塞",
"事件循环",
"asyncio",
"异步",
"await",
"asyncio.sleep",
"线程池"
],
"scoring_rubric": "正确指出 time.sleep 会阻塞事件循环导致并发能力丧失得 2 分;提出 asyncio.sleep 或将重试逻辑放入线程池(run_in_executor)等非阻塞方案得 2 分;总分 4 分。",
"explanation": "同步阻塞等待在异步框架中是致命的,会严重影响服务并发能力,应使用 asyncio.sleep 替代。"
}
],
"explanation": "这段代码展示了生产级 LLM 调用的重试与降级策略:1) 多级 fallback:按配置优先级依次尝试不同 provider;2) 指数退避:避免失败时立即重试造成 provider 过载;3) 响应质量检查:不仅捕获异常,还检查空响应和错误标记;4) 兜底响应:所有渠道失败时仍给用户一个有意义的回复。实际生产中还需考虑 circuit breaker(熔断器)、成功率监控、不同 provider 的 token 计费差异、以及异步化改造以适配高并发场景。",
"source": null,
"related": []
}
]
}