feat: add interview-prep topic group with 250 questions (6 subtopics, 5 question types)
Deploy Examination / deploy (push) Successful in 10s

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
This commit is contained in:
2026-09-09 16:36:27 +08:00
parent 515acbcc7f
commit 0f68a64829
38 changed files with 6835 additions and 2 deletions
@@ -0,0 +1,315 @@
{
"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": []
}
]
}
@@ -0,0 +1,217 @@
{
"topic": "ai-engineering",
"type": "fill_blank",
"schema_version": "1.0.0",
"generated": "2026-09-09T10:00:00+08:00",
"questions": [
{
"id": "fb-001",
"type": "fill_blank",
"difficulty": 2,
"tags": [
"context-engineering"
],
"question": "在 RAG(Retrieval-Augmented Generation)系统中,向量数据库的核心作用是将文本块(chunk)通过 Embedding 模型转化为高维向量后进行存储,并在查询时通过______匹配返回与用户问题语义最相近的文档片段,从而为 LLM 提供上下文信息。",
"answer": [
"相似度",
"向量相似度",
"语义相似度",
"余弦相似度",
"近似"
],
"answer_rule": "any",
"explanation": "RAG 系统的工作流程分为检索(Retrieval)和生成(Generation)两个阶段。在检索阶段,用户的问题先通过 Embedding 模型编码为向量,然后在向量数据库中执行相似度搜索(如余弦相似度、欧氏距离、内积等),找到与问题语义最接近的 top-k 文档片段。向量数据库(如 Milvus、Pinecone、Chroma、FAISS)的核心价值在于高效地进行近似最近邻(ANN)搜索,避免了传统关键词匹配无法处理语义理解的局限性。检索到的文档片段作为上下文注入 Prompt,帮助 LLM 生成更准确、有依据的回答。",
"source": null,
"related": []
},
{
"id": "fb-002",
"type": "fill_blank",
"difficulty": 3,
"tags": [
"context-engineering"
],
"question": "在 Prompt 模板设计中,系统角色定义(System Role)通常放在对话的最前面,用于约束 LLM 的行为边界和输出格式。这种将角色、指令和约束统一注入 Prompt 的工程方法被称为______,它强调通过系统性地构造上下文来控制 LLM 的行为。",
"answer": [
"Prompt Engineering",
"prompt engineering",
"提示工程",
"提示词工程"
],
"answer_rule": "any",
"explanation": "Prompt Engineering(提示工程)是指通过精心设计输入给 LLM 的文本(Prompt),来引导模型产生期望输出的工程方法。System Role 是 Prompt Engineering 中的关键组成部分,它定义了模型的身份、能力边界、行为准则和输出格式。例如,在一个客服助手中的 System Role 可能定义为「你是一个专业的客服助手,只能回答与产品相关的问题,回复时使用简洁的语气」。好的 System Role 设计能显著降低模型幻觉率、提高输出一致性和可控性。在更广泛的 Context Engineering(上下文工程)视角下,System Role 只是上下文构建的一个环节,还包括检索注入、Few-shot 示例、工具描述等多个维度。",
"source": null,
"related": []
},
{
"id": "fb-003",
"type": "fill_blank",
"difficulty": 3,
"tags": [
"harness-engineering",
"ai-agent"
],
"question": "ReAct(Reasoning + Acting)模式是一种将 LLM 的推理和行动能力结合的智能体框架。其核心循环包含三个步骤:首先 LLM 进行内部推理(Thought),然后决定并执行一个外部操作(Action),最后根据操作结果获取反馈(______),并将反馈作为下一轮推理的输入。",
"answer": [
"Observation",
"observation",
"观察"
],
"answer_rule": "any",
"explanation": "ReAct 模式由 Yao et al. (2022) 在论文《ReAct: Synergizing Reasoning and Acting in Language Models》中提出。其核心思想是让 LLM 交替进行推理(Thought)和行动(Action),形成 Thought → Action → Observation 的循环。具体流程为:(1) Thought:LLM 分析当前状态,制定下一步计划;(2) Action:LLM 选择并调用一个工具(如搜索引擎、计算器、API);(3) Observation:将工具返回的结果作为观察反馈给 LLM。这个循环不断重复,直到 LLM 认为已获得足够信息可以给出最终答案。ReAct 的优势在于将 Chain-of-Thought(思维链)推理与工具调用无缝结合,既保持了推理的可解释性,又扩展了模型的能力边界。LangChain、LlamaIndex 等主流框架都实现了 ReAct Agent。",
"source": null,
"related": []
},
{
"id": "fb-004",
"type": "fill_blank",
"difficulty": 4,
"tags": [
"tool-calling",
"harness-engineering"
],
"question": "在 LLM 的 Tool Calling(函数调用)机制中,当模型决定使用某个工具时,它会输出一个符合 JSON Schema 规范的结构化参数。这个参数对象中,最外层必须包含 \"name\"(工具名称)和 \"______\"(工具所需的输入参数)两个核心字段。",
"answer": [
"arguments",
"parameters",
"input",
"params",
"args"
],
"answer_rule": "any",
"explanation": "现代 LLM 的 Tool Calling 机制(如 OpenAI 的 Function Calling、Anthropic 的 Tool Use)要求模型以结构化 JSON 格式输出工具调用请求。标准格式包含两个核心字段:(1) name:要调用的工具/函数名称,必须与系统中注册的工具名精确匹配;(2) arguments(或 parameters):一个 JSON 对象,包含该工具所需的全部输入参数,参数结构需符合预先定义的 JSON Schema。例如,调用天气查询工具时,输出可能为 {\"name\": \"get_weather\", \"arguments\": {\"city\": \"北京\", \"unit\": \"celsius\"}}。框架层负责解析这个 JSON 并执行实际的函数调用,然后将结果返回给 LLM 继续推理。这种设计将 LLM 的「意图理解」与「工具执行」解耦,是 Harness Engineering(驾驭工程)的核心模式之一。",
"source": null,
"related": []
},
{
"id": "fb-005",
"type": "fill_blank",
"difficulty": 4,
"tags": [
"ai-agent"
],
"question": "在多智能体架构中,Agent 之间协作的一种常见模式是「共享状态」模式:多个 Agent 共同访问和修改一个中心化的______,每个 Agent 读取共享信息、执行自己的子任务后,将结果写回该状态,从而实现跨 Agent 的信息流转与协作。",
"answer": [
"状态",
"上下文",
"shared state",
"上下文状态",
"状态空间",
"黑板",
"黑板系统",
"共享上下文",
"全局状态"
],
"answer_rule": "any",
"explanation": "在多智能体系统(Multi-Agent System)中,Agent 间的协作模式主要有三种:(1) 共享状态(Shared State/Blackboard)模式:所有 Agent 共享一个中心化的状态存储(如 Blackboard),每个 Agent 可以读取全局状态、执行子任务并将结果写回,实现松耦合协作。LangGraph 的 StateGraph 就是这种模式的典型实现。(2) 消息传递(Message Passing)模式:Agent 之间通过直接发送消息通信,没有共享状态。(3) 层级委托(Hierarchical Delegation)模式:由一个主 Agent 将任务分解并委派给子 Agent。共享状态模式的优势是实现简单、状态透明,但需要注意并发写入时的竞态条件和状态一致性问题。在实际工程中,常结合状态快照(Checkpoint)和事务机制来保证可靠性。",
"source": null,
"related": []
},
{
"id": "fb-006",
"type": "fill_blank",
"difficulty": 3,
"tags": [
"mcp",
"ai-agent"
],
"question": "MCP(Model Context Protocol)是由 Anthropic 提出的开放协议,用于标准化 LLM 应用与外部数据源和工具之间的连接。MCP 的三大核心要素是:Server(提供能力的服务端)、Client(消费能力的客户端),以及定义在 Server 和 Client 之间通信的______。",
"answer": [
"协议",
"通信协议",
"消息协议",
"Protocol",
"transport",
"传输层",
"JSON-RPC"
],
"answer_rule": "any",
"explanation": "MCP 的架构围绕三个核心要素展开:(1) MCP Server:暴露工具(Tools)、资源(Resources)和提示模板(Prompts)的服务端程序,负责与具体的外部数据源或 API 对接。(2) MCP Client:嵌入在 LLM 应用(如 IDE、聊天界面)中的客户端,负责发现和调用 Server 提供的能力。(3) 协议本身:定义了 Server 和 Client 之间基于 JSON-RPC 2.0 的通信规范,包括能力发现(initialize)、工具调用(tools/call)、资源读取(resources/read)等标准方法。MCP 的核心价值在于解决了「M×N 集成问题」——传统方式下 M 个 LLM 应用对接 N 个工具需要 M×N 个适配器,而 MCP 将其降低为 M+N 个。协议支持 stdio 和 HTTP+SSE 两种传输方式。",
"source": null,
"related": []
},
{
"id": "fb-007",
"type": "fill_blank",
"difficulty": 4,
"tags": [
"permission"
],
"question": "在 AI Agent 的权限治理中,沙箱(Sandbox)执行环境通过进程隔离、文件系统隔离和______隔离等机制,确保 Agent 执行的代码或命令不会影响宿主系统安全。常见的沙箱技术包括 Docker 容器、gVisor 和 Firecracker。",
"answer": [
"网络",
"网络隔离",
"namespace",
"命名空间",
"资源",
"资源隔离",
"cgroup"
],
"answer_rule": "any",
"explanation": "沙箱执行环境是 AI Agent 权限治理的核心防线,通过多层隔离机制限制 Agent 的能力边界:(1) 进程隔离:使用 Linux namespace 或容器技术,使 Agent 进程无法看到或访问宿主系统的其他进程。(2) 文件系统隔离:通过只读挂载或 overlay filesystem 限制 Agent 只能访问特定目录,阻止读取/修改敏感文件。(3) 网络隔离:通过网络命名空间、iptables 规则或代理层限制 Agent 的网络访问范围,防止未授权的外部请求。(4) 资源隔离:使用 cgroup 限制 CPU、内存、磁盘 I/O 等资源使用,防止资源耗尽攻击。在 QwenPaw 等框架中,sandbox_config 支持配置 mount 权限和网络限制,确保 Agent 只能在授权范围内执行操作。gVisor 和 Firecracker 提供了比传统 Docker 更强的内核级隔离。",
"source": null,
"related": []
},
{
"id": "fb-008",
"type": "fill_blank",
"difficulty": 3,
"tags": [
"pluggable-config"
],
"question": "在 AI 框架的可插拔配置设计中,Provider 抽象层通过统一的接口(如 chat、embed、complete 等方法)屏蔽不同 LLM 供应商的 API 差异。当需要切换底层模型(如从 OpenAI 切换到 Qwen)时,只需修改配置中的______字段,无需改动业务代码。",
"answer": [
"provider",
"provider_id",
"providerId",
"provider_name",
"模型供应商"
],
"answer_rule": "any",
"explanation": "Provider 抽象层是现代 AI 框架实现可插拔架构的关键设计模式。其核心思想是定义一套标准化接口(通常包括 chat/completion、embedding、function calling 等方法),然后为不同的 LLM 供应商(OpenAI、Anthropic、Qwen、本地模型等)实现具体的适配器。配置层面通常通过 provider 字段(或 provider_id)来指定使用哪个后端。例如在 QwenPaw 中,切换模型只需在配置文件中修改 provider 和 model 字段,框架会自动路由到对应的适配器实现。这种设计遵循了 SOLID 原则中的依赖倒置原则(DIP)——业务代码依赖于抽象接口而非具体实现。Provider 抽象层还统一处理了认证管理、请求重试、响应格式标准化、Token 计费等横切关注点,极大降低了切换和维护成本。",
"source": null,
"related": []
},
{
"id": "fb-009",
"type": "fill_blank",
"difficulty": 3,
"tags": [
"guardrail"
],
"question": "在 LLM 应用的 Guardrail(安全护栏)系统中,PII(Personally Identifiable Information,个人身份信息)检测是重要的防护环节。通过预定义的______表达式(如匹配手机号、身份证号、邮箱地址等模式),可以在输入和输出阶段自动识别并脱敏敏感信息,防止数据泄露。",
"answer": [
"正则",
"正则表达式",
"regex",
"Regular Expression",
"regexp"
],
"answer_rule": "any",
"explanation": "PII 检测是 Guardrail 系统的基础能力之一。正则表达式(Regular Expression)因其高效、确定性强的特点,是实现 PII 模式匹配的首选技术。常见的正则匹配策略包括:(1) 中国大陆手机号:以 1 开头、第二位 3-9、后续 9 位数字;(2) 身份证号:18 位,包含地区码、出生日期、顺序码和校验位;(3) 邮箱地址:用户名 + @ + 域名格式。在实际 Guardrail 架构中,正则匹配通常作为第一层快速筛查(低延迟、高召回),配合 NER(命名实体识别)模型作为第二层精细检测(高精度、低误报)。检测到 PII 后的处理策略包括:阻断(Block)、脱敏(Mask/Redact,如将身份证号替换为占位符)、或告警。Guardrail 系统还应支持正则规则的热更新,以便快速应对新出现的 PII 模式。",
"source": null,
"related": []
},
{
"id": "fb-010",
"type": "fill_blank",
"difficulty": 4,
"tags": [
"llm-ops"
],
"question": "在 LLM 应用的可靠性工程中,当 API 调用失败(如限流 429 错误)时,指数退避重试是标准策略。其等待时间的计算公式为:delay = min(base * 2^n + random, max_delay),其中 base 是基础延迟,n 是当前重试次数,max_delay 是最大等待上限,random 是随机抖动值。增加随机抖动(jitter)的目的是防止多个客户端在重试时产生______效应。",
"answer": [
"惊群",
"惊群效应",
"thundering herd",
"Thundering Herd",
"同步",
"同步重试"
],
"answer_rule": "any",
"explanation": "指数退避(Exponential Backoff)是分布式系统中最常用的重试策略,公式为 delay = min(base * 2^n + jitter, max_delay)。其中 base 通常设为 1 秒,n 从 0 开始递增。例如第 1 次重试等待约 1 秒,第 2 次约 2 秒,第 3 次约 4 秒,依此类推直到 max_delay(通常 60-120 秒)。引入随机抖动(jitter)至关重要——如果不加抖动,所有遇到 429 错误的客户端会在完全相同的时间点重试,形成「惊群效应」(Thundering Herd),导致服务端在短时间内再次承受峰值压力,陷入反复限流的恶性循环。常见的 jitter 策略有:(1) Full Jitter:在 [0, base*2^n] 范围内随机取值;(2) Equal Jitter:基础值的一半 + 随机值;(3) Decorrelated Jitter:基于上一次等待时间计算。在 LLM Ops 实践中,建议结合 Circuit Breaker(熔断器)使用,当连续失败超过阈值时暂停重试,避免无意义的资源消耗。",
"source": null,
"related": []
}
]
}
@@ -0,0 +1,25 @@
{
"slug": "ai-engineering",
"name": "AI工程实践",
"description": "Context Engineer、Harness Engineer、智能体架构、MCP协议、权限治理、可插拔配置",
"tags": [],
"question_files": {
"single_choice": "single_choice.json",
"true_false": "true_false.json",
"fill_blank": "fill_blank.json",
"short_answer": "short_answer.json",
"code_reading": "code_reading.json"
},
"stats": {
"total": 35,
"by_type": {
"single_choice": 10,
"true_false": 5,
"fill_blank": 10,
"short_answer": 5,
"code_reading": 5
}
},
"created": "2026-09-09",
"updated": "2026-09-09"
}
@@ -0,0 +1,148 @@
{
"topic": "ai-engineering",
"type": "short_answer",
"schema_version": "1.0.0",
"generated": "2026-09-09T10:00:00+08:00",
"questions": [
{
"id": "sa-001",
"type": "short_answer",
"difficulty": 3,
"tags": [
"context-engineering",
"llm-ops"
],
"question": "Context Engineer 在 AI 工程体系中的核心职责是什么?请从信息组织、上下文窗口管理、动态检索策略三个维度阐述其工作范围,并说明它与传统 Prompt Engineering 的关键区别。",
"answer": "Context Engineer 的核心职责是设计和管理 LLM 的输入上下文,确保模型在推理时获得最相关、最精炼的信息。具体包括三个维度:(1) 信息组织:构建结构化的上下文体系,包括系统提示、用户对话历史、检索文档、工具调用结果等多源信息的编排与优先级排序;(2) 上下文窗口管理:在有限的 token 预算内进行信息压缩、截断、摘要,处理长上下文场景下的注意力衰减问题,必要时实施分块策略;(3) 动态检索策略:根据用户意图实时决定何时检索、检索什么、检索多少,实现 RAG 管线的精准触发与结果融合。与传统 Prompt Engineering 的关键区别在于:Prompt Engineering 关注单次调用的提示词措辞优化,而 Context Engineer 关注的是一个持续的、系统性的上下文生命周期管理——涵盖信息的采集、筛选、组织、缓存、刷新和评估,是一种工程化而非手艺化的思维方式。",
"keywords": [
"信息组织",
"上下文窗口",
"token 预算",
"动态检索",
"RAG",
"Prompt Engineering 区别",
"上下文生命周期",
"信息压缩",
"分块策略",
"优先级排序"
],
"scoring_rubric": "满分10分。信息组织维度(2分):需提及多源信息编排和优先级排序。上下文窗口管理维度(2分):需提及 token 预算、压缩/摘要、注意力衰减等概念。动态检索策略维度(2分):需提及 RAG 管线和实时触发。与 Prompt Engineering 的区别(2分):需明确对比单次措辞优化 vs 系统性生命周期管理。整体表达(2分):逻辑清晰、术语准确。",
"explanation": "Context Engineering 是 2024-2025 年 AI 工程领域兴起的核心概念,由 Andrej Karpathy 等人推广。它标志着 AI 应用开发从「调提示词」到「管上下文」的范式转变。传统 Prompt Engineering 像是编辑一篇文章的措辞,而 Context Engineer 则像是设计整个信息管道——决定什么信息在什么时候以什么形式进入模型的视野。这一角色在 RAG 系统、多轮对话、Agent 系统中尤为重要,因为这些场景下上下文的质量直接决定了输出的质量。",
"source": null,
"related": []
},
{
"id": "sa-002",
"type": "short_answer",
"difficulty": 4,
"tags": [
"ai-agent",
"harness-engineering",
"tool-calling"
],
"question": "请对比 ReAct 与 Plan-and-Execute 两种 Agent 执行模式的核心差异,分别说明它们的执行流程、适用场景和各自的优缺点,并给出在实际项目中选择这两种模式的决策依据。",
"answer": "ReAct(Reasoning + Acting)模式采用「思考-行动-观察」的交替循环:每一步先推理下一步该做什么,执行工具调用,获取结果后再推理下一步。其执行流程是线性的、即时决策的。优点是实现简单、对简单任务响应快、每步都有充分的环境反馈;缺点是缺乏全局规划,在多步骤复杂任务中容易偏离目标、陷入循环或重复无效操作。Plan-and-Execute 模式分为两阶段:先由 Planner 生成完整的执行计划(任务分解),再由 Executor 按计划逐步执行,执行过程中可反馈修正计划。优点是对复杂任务有全局视野、可预估资源消耗、支持并行执行子任务;缺点是计划生成本身消耗额外 token、初始计划可能不准确需要修正机制、对动态变化的环境适应性较弱。选择决策依据:(1) 任务步骤少于 3-4 步且逻辑简单时选 ReAct;(2) 任务可清晰分解、有明确子目标时选 Plan-and-Execute;(3) 需要实时适应环境变化的场景选 ReAct;(4) 对执行成本和时间有预算约束时选 Plan-and-Execute(可提前预估)。",
"keywords": [
"ReAct",
"Plan-and-Execute",
"思考-行动-观察",
"任务分解",
"执行计划",
"全局规划",
"循环执行",
"并行执行",
"token 消耗",
"决策依据"
],
"scoring_rubric": "满分10分。ReAct 模式描述(2分):需提及交替循环和即时决策特征。Plan-and-Execute 模式描述(2分):需提及两阶段分离和计划修正机制。优缺点对比(3分):双方各至少一个优点和一个缺点,共需覆盖至少4个对比维度。选择决策依据(2分):需给出至少2条具体的决策条件。整体表达(1分):逻辑清晰、术语准确。",
"explanation": "ReAct 来自 Yao et al. 2022 年的论文,将 CoT 推理与工具调用交替进行,是当前大多数 Agent 框架(如 LangChain AgentExecutor)的默认模式。Plan-and-Execute 则参考了人类「先想后做」的策略,LangGraph、AutoGPT 等框架支持这种模式。在实际工程中,很多成熟的 Agent 系统会混合使用:对顶层任务使用 Plan-and-Execute 做全局规划,对子任务内部使用 ReAct 做灵活执行。选择时还需考虑 LLM 的能力——较弱的模型可能无法生成可靠的全局计划,此时 ReAct 的逐步决策反而更稳健。",
"source": null,
"related": []
},
{
"id": "sa-003",
"type": "short_answer",
"difficulty": 3,
"tags": [
"ai-agent",
"pluggable-config"
],
"question": "在智能体架构设计中,单 Agent 与多 Agent 架构分别适用于什么场景?请从任务复杂度、系统可靠性、开发维护成本、扩展性四个方面进行对比分析,并举出一个适合采用多 Agent 架构的典型业务场景。",
"answer": "单 Agent 架构适用于:任务逻辑清晰、工具调用链路短、领域单一的场景。多 Agent 架构适用于:任务涉及多个专业领域、需要角色分工协作、工作流复杂的场景。四维度对比:(1) 任务复杂度:单 Agent 适合步骤少于 5-7 步的线性任务;多 Agent 适合需要并行处理、条件分支、多轮协商的复杂工作流。(2) 系统可靠性:单 Agent 故障点集中,一个环节出错影响整体;多 Agent 通过职责隔离降低级联故障风险,但引入了 Agent 间通信的故障点。(3) 开发维护成本:单 Agent 开发周期短、调试简单;多 Agent 需要设计通信协议、状态同步、错误恢复机制,维护成本显著更高。(4) 扩展性:单 Agent 功能扩展受上下文窗口限制,新工具增加会稀释已有能力;多 Agent 可通过新增专业 Agent 实现水平扩展,各 Agent 独立迭代。典型多 Agent 场景:企业级代码审查系统——由代码分析 Agent 负责静态检查、安全审计 Agent 负责漏洞扫描、架构评审 Agent 负责设计模式评估、报告生成 Agent 汇总结果,各 Agent 并行工作后由编排器合并输出。",
"keywords": [
"单 Agent",
"多 Agent",
"职责隔离",
"角色分工",
"通信协议",
"上下文窗口",
"水平扩展",
"编排器",
"故障点",
"级联故障"
],
"scoring_rubric": "满分10分。单 Agent 适用场景(1分)。多 Agent 适用场景(1分)。四维度对比(各1.5分,共6分):需覆盖任务复杂度、可靠性、成本、扩展性。典型场景举例(2分):需具体且合理。整体表达(1分)。",
"explanation": "架构选择的核心原则是「够用就好」——单 Agent 能解决的问题不要过度设计为多 Agent。多 Agent 的优势在于专业化分工(每个 Agent 的 prompt 更聚焦、工具集更精简),但代价是协调开销。在实际工程中,很多团队采用「混合架构」:核心流程用单 Agent,特定子流程拆分为独立 Agent。编排方式也有多种选择——中心化编排器(Orchestrator)、去中心化协商(Chat/Debate)、流水线(Pipeline)等,需要根据业务特点选择。",
"source": null,
"related": []
},
{
"id": "sa-004",
"type": "short_answer",
"difficulty": 4,
"tags": [
"mcp",
"tool-calling",
"permission"
],
"question": "MCP(Model Context Protocol)协议在设计时需要考虑哪些安全问题?请从身份认证、权限控制、数据隔离、传输安全、防注入攻击五个方面阐述 MCP 的安全考量与设计原则,并说明 MCP Host、MCP Server、MCP Client 三者在安全责任上的分工。",
"answer": "MCP 协议的安全考量涵盖五个核心方面:(1) 身份认证:MCP Client 与 MCP Server 之间需要建立双向身份验证,通常基于 OAuth 2.0 或 API Key,确保只有授权的 Client 能访问 Server 暴露的工具和资源。(2) 权限控制:MCP Server 应遵循最小权限原则,每个工具声明所需的权限级别,Host 层面实施用户确认机制(human-in-the-loop),对敏感操作(如文件写入、数据库修改)要求用户显式授权。(3) 数据隔离:不同 Client 的会话数据应严格隔离,Server 不得在未授权情况下跨会话共享数据;Server 本地存储的凭证和配置文件需要加密保护。(4) 传输安全:所有 MCP 通信应使用 TLS/HTTPS 加密传输,本地 stdio 传输模式需确保进程间通信不被第三方窃听。(5) 防注入攻击:MCP Server 需要对 LLM 生成的工具调用参数进行严格校验和转义,防止 Prompt Injection 通过工具参数传递到下游系统;Server 不应盲目执行未经验证的指令。安全责任分工:MCP Host(如 IDE、聊天应用)负责用户身份管理、权限策略制定和 human-in-the-loop 审批;MCP Client(协议客户端)负责建立安全连接、传输层加密和会话隔离;MCP Server(工具提供方)负责输入校验、权限边界执行和数据保护。",
"keywords": [
"MCP",
"身份认证",
"OAuth 2.0",
"最小权限",
"human-in-the-loop",
"数据隔离",
"TLS",
"Prompt Injection",
"输入校验",
"安全责任分工"
],
"scoring_rubric": "满分10分。五个安全方面(各1.2分,共6分):每方面需给出具体的安全措施或设计原则。安全责任分工(2分):需清晰区分 Host、Client、Server 的职责。整体表达(2分):术语准确、逻辑清晰。",
"explanation": "MCP 协议由 Anthropic 于 2024 年底提出并开源,旨在为 LLM 应用提供标准化的工具和资源访问协议。安全是 MCP 生态的核心关注点——因为 MCP Server 本质上是让 LLM 代理用户执行操作,一旦被恶意利用(如通过 Prompt Injection 诱导 LLM 调用危险工具),后果可能很严重。MCP 规范中推荐的 OAuth 2.0 PKCE 流程、工具的 annotation 标注(如 readOnlyHint、destructiveHint)以及 Host 层的用户确认机制,都是为了在便利性和安全性之间取得平衡。在企业场景中,还需要额外考虑审计日志、操作回滚、沙箱隔离等措施。",
"source": null,
"related": []
},
{
"id": "sa-005",
"type": "short_answer",
"difficulty": 4,
"tags": [
"guardrail",
"llm-ops",
"tool-calling",
"permission"
],
"question": "请描述一个生产级 LLM 应用中 Guardrail(护栏)系统的完整实现方案,包括输入侧护栏和输出侧护栏各自承担的职责、具体的技术实现手段,以及 Guardrail 系统在整体架构中的位置和与主 LLM 调用链路的关系。",
"answer": "生产级 Guardrail 系统分为输入侧和输出侧两道防线,包裹在主 LLM 调用链路的前后。输入侧护栏职责:在用户输入到达 LLM 之前进行安全过滤和预处理。具体技术手段包括:(1) Prompt Injection 检测——使用分类器或规则引擎识别恶意指令注入尝试;(2) 敏感信息脱敏——正则匹配或 NER 模型识别并替换手机号、身份证号等 PII 数据;(3) 输入长度和格式校验——防止超长输入导致 token 溢出;(4) 意图分类和话题限制——识别超出系统能力范围或不在授权领域内的请求。输出侧护栏职责:在 LLM 响应返回用户之前进行质量和安全审核。具体技术手段包括:(1) 幻觉检测——将输出与检索到的源文档交叉验证,检测无据可依的事实陈述;(2) 有害内容过滤——使用毒性分类模型检测仇恨、暴力、违法内容;(3) 格式合规检查——验证 JSON Schema、工具调用格式等结构化输出是否合规;(4) 敏感信息防泄露——检测模型输出中是否包含训练数据泄露或内部系统信息;(5) 事实一致性校验——确保多轮对话中的输出不与已确认的事实矛盾。架构位置与关系:Guardrail 系统作为独立的中间件层,以管道(Pipeline)模式嵌入主调用链路——输入护栏在用户请求之后、LLM 调用之前执行,输出护栏在 LLM 返回之后、用户接收之前执行。两者都采用异步可选的执行模式:拦截到高风险问题时直接拒绝并返回安全提示,低风险问题可标记后放行。在高吞吐场景下,Guardrail 可使用轻量级模型(如 BERT-based 分类器)以控制延迟。",
"keywords": [
"Guardrail",
"输入护栏",
"输出护栏",
"Prompt Injection 检测",
"PII 脱敏",
"幻觉检测",
"有害内容过滤",
"格式合规",
"管道模式",
"分类器",
"human-in-the-loop"
],
"scoring_rubric": "满分10分。输入侧护栏(3分):需涵盖至少3种具体技术手段。输出侧护栏(3分):需涵盖至少3种具体技术手段。架构位置与关系(2分):需说明管道模式和独立中间件层。整体表达(2分):结构清晰、技术细节准确。",
"explanation": "Guardrail 是 LLM 应用从实验走向生产的必备组件。业界常用的实现方案包括:NVIDIA NeMo Guardrails(开源,支持对话流控制和主题限制)、Guardrails AI(专注输出校验和自动修复)、Lakera Guard(专注 Prompt Injection 检测)。在实际工程中,Guardrail 的设计需要权衡安全性和延迟——过严的过滤会降低用户体验,过松则可能放行有害内容。最佳实践是分层防御(Defense in Depth):轻量级规则引擎做快速拦截,重量级模型做精细判断,结合人工审核处理边界案例。此外,Guardrail 系统本身也需要监控和迭代——通过 A/B 测试和人工标注持续优化检测模型的准确率和召回率。",
"source": null,
"related": []
}
]
}
@@ -0,0 +1,200 @@
{
"topic": "ai-engineering",
"type": "single_choice",
"schema_version": "1.0.0",
"generated": "2026-09-09T10:00:00+08:00",
"questions": [
{
"id": "sc-001",
"type": "single_choice",
"difficulty": 3,
"tags": [
"context-engineering"
],
"question": "在 RAG(Retrieval-Augmented Generation)系统中,以下哪种检索策略最能有效提升最终生成答案的相关性?",
"options": {
"A": "始终返回 Top-1 相关性最高的文档片段",
"B": "使用语义向量检索 + 重排序(Re-ranking)的两阶段检索",
"C": "将所有检索到的文档一次性拼接后发送给 LLM",
"D": "仅依赖关键词 BM25 检索,不做向量化处理"
},
"answer": "B",
"explanation": "两阶段检索(先粗召回、再精排)是当前 RAG 系统的最佳实践。第一阶段用向量检索从大量文档中快速召回候选集,第二阶段用 Cross-Encoder 等重排序模型对候选集精排,能显著提升检索到的文档与查询的相关性。A 选项只返回一个片段,信息量不足且容错性差;C 选项将所有文档拼接会导致上下文窗口浪费和信息噪声;D 选项仅依赖关键词检索无法处理语义相近但词汇不同的查询,召回率不足。",
"source": null,
"related": []
},
{
"id": "sc-002",
"type": "single_choice",
"difficulty": 2,
"tags": [
"context-engineering"
],
"question": "当 LLM 的上下文窗口有限时,以下哪种策略最适合处理一篇超过窗口大小的长文档摘要任务?",
"options": {
"A": "将文档截断至上下文窗口大小,直接丢弃超出部分",
"B": "采用 Map-Reduce 策略:先分段摘要,再对摘要进行二次综合",
"C": "多次重复发送同一文档以「加深」模型记忆",
"D": "将文档拆分后并行发送给多个独立 LLM 实例,取第一个返回的结果"
},
"answer": "B",
"explanation": "Map-Reduce 是长文档处理的经典模式:Map 阶段将文档切分为多个 chunk 并行生成局部摘要,Reduce 阶段将所有局部摘要合并生成最终摘要。这种方法既保证了信息覆盖的完整性,又避免了上下文溢出。A 选项会丢失大量关键信息;C 选项重复发送并不能增加有效信息量,反而浪费 token;D 选项只取第一个结果忽略了其他分段的信息,且无综合步骤。",
"source": null,
"related": []
},
{
"id": "sc-003",
"type": "single_choice",
"difficulty": 3,
"tags": [
"harness-engineering",
"ai-agent"
],
"question": "在 Agent 编排框架中,ReAct 模式与 Plan-and-Execute 模式的核心区别是什么?",
"options": {
"A": "ReAct 模式不支持工具调用,仅用于对话场景",
"B": "ReAct 采用逐步推理-行动的循环,Plan-and-Execute 先制定完整计划再逐步执行",
"C": "Plan-and-Execute 模式不支持在执行过程中动态调整计划",
"D": "ReAct 模式只能处理单步任务,无法处理多步骤复杂任务"
},
"answer": "B",
"explanation": "ReAct(Reasoning + Acting)的核心是 Thought → Action → Observation 的逐步循环,每一步根据当前观察决定下一步行动,适合需要动态调整的探索型任务。Plan-and-Execute 则先让 LLM 生成一个完整的多步计划,再由执行器逐步实施,适合目标明确、步骤可预知的任务。A 错误:ReAct 广泛用于工具调用场景。C 错误:大多数 Plan-and-Execute 实现支持在执行中根据反馈修改计划。D 错误:ReAct 的循环结构天然支持多步任务。",
"source": null,
"related": []
},
{
"id": "sc-004",
"type": "single_choice",
"difficulty": 4,
"tags": [
"ai-agent"
],
"question": "在多 Agent 系统中,「共享黑板(Blackboard)」通信模式的核心特征是?",
"options": {
"A": "所有 Agent 通过点对点消息直接通信,无需中间媒介",
"B": "设置一个中心调度器,由它决定哪个 Agent 在何时执行任务",
"C": "Agent 通过读写共享的全局状态空间进行间接通信,无需直接对接",
"D": "所有 Agent 共享同一个 LLM 实例,通过内存隔离实现并行"
},
"answer": "C",
"explanation": "黑板(Blackboard)模式源自人工智能经典架构,核心思想是维护一个共享的全局数据结构(黑板),各 Agent 独立地读取黑板上的信息、执行推理并将结果写回黑板,从而实现间接的松耦合通信。A 描述的是点对点(Peer-to-Peer)通信模式;B 描述的是中心调度(Central Orchestrator)模式;D 描述的是模型共享架构,与通信模式无关。黑板模式的优势在于 Agent 之间不需要了解彼此的存在,便于扩展和替换。",
"source": null,
"related": []
},
{
"id": "sc-005",
"type": "single_choice",
"difficulty": 3,
"tags": [
"mcp",
"tool-calling"
],
"question": "在 MCP(Model Context Protocol)协议中,以下关于 Tool、Resource、Prompt 三个核心要素的描述,正确的是?",
"options": {
"A": "Tool 是 LLM 可调用的外部函数,Resource 是服务端提供的数据源,Prompt 是预定义的提示词模板",
"B": "Tool 和 Resource 的区别仅在于命名不同,功能完全相同",
"C": "Prompt 是 LLM 生成的输出格式约束,Tool 是客户端向服务端发送的请求",
"D": "Resource 只能返回文本数据,不支持图片或结构化数据"
},
"answer": "A",
"explanation": "MCP 协议定义了三个核心原语:Tool 代表 LLM 可以调用的外部函数(如 API 调用、代码执行),由客户端发起调用;Resource 是服务端暴露的数据源(如文件、数据库记录),通常由客户端按 URI 读取;Prompt 是服务端提供的预定义提示词模板,供客户端在构造请求时使用。B 错误:Tool 涉及执行操作,Resource 涉及数据读取,语义和实现不同。C 错误:Prompt 不是输出约束,而是输入模板。D 错误:Resource 支持多种内容类型,包括图片和结构化数据。",
"source": null,
"related": []
},
{
"id": "sc-006",
"type": "single_choice",
"difficulty": 4,
"tags": [
"permission"
],
"question": "在设计 LLM Agent 的权限治理体系时,「最小权限原则(Principle of Least Privilege)」的最佳实践是?",
"options": {
"A": "为 Agent 配置管理员权限,确保它在任何场景下都能顺利完成任务",
"B": "每次工具调用时动态申请权限,根据当前任务上下文授予刚好够用的最小权限集",
"C": "所有工具调用一律禁止,需要人工逐条审批后才能执行",
"D": "将权限配置写死在代码中,运行时不允许任何变更"
},
"answer": "B",
"explanation": "最小权限原则要求 Agent 仅获得完成当前任务所必需的最小权限。动态权限申请是最佳实践——Agent 在执行每个工具调用前声明其意图和所需权限,系统根据任务上下文和安全策略决定是否授权。这既保证了安全性(避免过度授权),又保证了灵活性(不同任务授予不同权限)。A 违反最小权限原则,过度授权带来安全风险;C 过于严格会阻塞正常工作流,适用于高风险操作而非所有操作;D 的静态权限无法适应不同任务的差异化需求。",
"source": null,
"related": []
},
{
"id": "sc-007",
"type": "single_choice",
"difficulty": 3,
"tags": [
"pluggable-config"
],
"question": "在可插拔配置架构中,Provider 抽象层的核心价值是什么?",
"options": {
"A": "让系统只能使用特定厂商的 LLM,确保输出质量一致",
"B": "统一不同 LLM 供应商的接口差异,使上层应用无需感知底层模型切换",
"C": "自动生成 Prompt 模板,不需要人工编写任何提示词",
"D": "为每个 LLM 供应商创建独立的应用程序,互不干扰"
},
"answer": "B",
"explanation": "Provider 抽象层(如 LiteLLM、OpenAI 兼容接口)的核心价值在于屏蔽不同 LLM 供应商的 API 差异(请求格式、认证方式、响应结构等),为上层应用提供统一的调用接口。当需要切换底层模型时(如从 GPT-4 切换到 Claude),只需修改配置而不必改动业务代码。A 与可插拔理念相悖;C 属于 Prompt Engineering 工具的功能,与 Provider 抽象层无关;D 违背了抽象层「统一接口」的初衷。",
"source": null,
"related": []
},
{
"id": "sc-008",
"type": "single_choice",
"difficulty": 2,
"tags": [
"guardrail"
],
"question": "以下关于 LLM Guardrail 中 PII(个人可识别信息)检测的描述,正确的是?",
"options": {
"A": "PII 检测只需要在输入端执行,输出端无需处理,因为 LLM 不会凭空生成个人信息",
"B": "PII 检测应同时覆盖输入和输出,且需要支持正则匹配与上下文感知的 NER 模型结合",
"C": "PII 检测会显著降低系统性能,因此在生产环境中应该完全跳过",
"D": "PII 检测只适用于金融行业,其他行业不需要"
},
"answer": "B",
"explanation": "PII 检测需要在输入和输出两个方向上都进行。输入端防止敏感信息泄露给 LLM,输出端防止 LLM 在生成内容中包含用户提供的或训练数据中的敏感信息。技术实现上,正则匹配适合检测格式固定的 PII(如手机号、身份证号),NER 模型适合检测上下文相关的 PII(如人名、地址),两者结合效果最佳。A 错误:LLM 可能通过 RAG 或记忆机制在输出中泄露输入中的 PII;C 错误:PII 检测是合规要求,性能问题可以通过异步检测、缓存等手段优化;D 错误:所有处理用户数据的行业都应考虑 PII 检测。",
"source": null,
"related": []
},
{
"id": "sc-009",
"type": "single_choice",
"difficulty": 4,
"tags": [
"llm-ops"
],
"question": "在 LLM 应用的可靠性保障中,使用 JSON Mode / Structured Output 的主要目的是什么?",
"options": {
"A": "提升 LLM 的推理速度,减少响应延迟",
"B": "确保 LLM 输出符合预定义的结构化格式,降低下游解析失败的风险",
"C": "限制 LLM 只能回答特定领域的问题",
"D": "减少 LLM 的幻觉问题,提升事实准确性"
},
"answer": "B",
"explanation": "JSON Mode / Structured Output(如 OpenAI 的 response_format、Function Calling 的参数约束)的核心目的是确保 LLM 的输出符合预定义的 schema 结构,从而让下游系统可以可靠地解析和处理输出。这解决了 LLM 输出格式不稳定导致的集成问题。A 错误:结构化输出约束甚至可能略微增加延迟(需要额外的格式校验);C 错误:结构化输出约束的是格式而非内容领域;D 错误:结构化输出不直接解决幻觉问题——模型仍可能在有效 JSON 中生成虚假内容,解决幻觉需要 RAG、事实校验等其他手段。",
"source": null,
"related": []
},
{
"id": "sc-010",
"type": "single_choice",
"difficulty": 3,
"tags": [
"llm-ops"
],
"question": "当 LLM 服务出现临时性故障(如 rate limit 或 503 错误)时,以下哪种重试策略最为合理?",
"options": {
"A": "立即无限次重试,直到成功为止",
"B": "采用指数退避(Exponential Backoff)+ 抖动(Jitter)策略,并设置最大重试次数",
"C": "不进行任何重试,直接将错误返回给用户",
"D": "每次固定间隔 1 秒重试,重试 100 次"
},
"answer": "B",
"explanation": "指数退避 + 抖动是处理临时性故障的标准策略。指数退避(每次重试间隔翻倍,如 1s → 2s → 4s → 8s)避免了在服务恢复前持续施压;抖动(在退避间隔上加入随机偏移)防止多个客户端同时重试造成「惊群效应(Thundering Herd)」。设置最大重试次数可避免无限循环。A 会加剧服务端压力并可能触发更严厉的限流;C 无法应对可恢复的临时故障,用户体验差;D 的固定间隔策略在大规模部署时容易引发同步重试风暴。",
"source": null,
"related": []
}
]
}
@@ -0,0 +1,78 @@
{
"topic": "ai-engineering",
"type": "true_false",
"schema_version": "1.0.0",
"generated": "2026-09-09T10:00:00+08:00",
"questions": [
{
"id": "tf-001",
"type": "true_false",
"difficulty": 3,
"tags": [
"context-engineering",
"ai-agent"
],
"question": "在 RAG(检索增强生成)架构中,向量数据库的语义检索结果可以完全替代 LLM 的参数化知识,因此 RAG 系统不需要依赖 LLM 自身的预训练知识。",
"answer": false,
"explanation": "这个说法是错误的。RAG 的核心设计理念是「检索 + 生成」协同工作,而非替代关系。向量数据库提供的语义检索结果作为上下文(context)注入 prompt,但 LLM 仍然需要利用自身的参数化知识来理解检索结果、进行推理、整合信息并生成最终回答。检索结果通常只是片段化的、可能包含噪声的参考资料,LLM 的预训练知识起到语义理解、逻辑推理和知识补全的关键作用。两者是互补关系,缺一不可。",
"source": null,
"related": []
},
{
"id": "tf-002",
"type": "true_false",
"difficulty": 4,
"tags": [
"harness-engineering",
"ai-agent"
],
"question": "ReAct 模式与 Plan-and-Execute 模式的核心区别在于:ReAct 采用「思考→行动→观察」的即时循环,而 Plan-and-Execute 先生成完整计划再逐步执行,因此 Plan-and-Execute 更适合需要多步回溯和动态调整的复杂任务。",
"answer": false,
"explanation": "这个说法是错误的。虽然前半部分对两种模式的描述是正确的——ReAct 是 Thought→Action→Observation 的交替循环,Plan-and-Execute 是先规划再执行——但结论恰好相反。ReAct 的逐步交替模式天然支持每一步根据观察结果动态调整策略,更适合需要频繁回溯和动态调整的任务。Plan-and-Execute 的计划一旦生成,调整成本较高(需要重新规划),更适合目标明确、步骤可预判的任务。在实际工程中,Plan-and-Execute 通常需要加入 re-planning 机制来弥补这一不足。",
"source": null,
"related": []
},
{
"id": "tf-003",
"type": "true_false",
"difficulty": 3,
"tags": [
"mcp",
"tool-calling"
],
"question": "MCP(Model Context Protocol)协议定义的三大核心要素是 Tool、Resource 和 Prompt,其中 Tool 允许服务端暴露可执行函数供 LLM 调用,Resource 提供只读的数据访问接口,Prompt 则定义了预构建的交互模板。",
"answer": true,
"explanation": "这个说法是正确的。MCP 协议确实定义了这三个核心要素:Tool 是服务端提供的可执行函数(如查询数据库、调用 API),LLM 可以主动调用它来完成操作;Resource 是只读的数据源(如文件内容、数据库记录),为 LLM 提供上下文信息但不允许修改;Prompt 是预定义的 prompt 模板,用于标准化交互模式。这种设计将「可执行操作」「只读数据」和「交互模板」三者清晰分离,各有不同的权限级别和交互语义,是 MCP 安全模型的基础。",
"source": null,
"related": []
},
{
"id": "tf-004",
"type": "true_false",
"difficulty": 2,
"tags": [
"permission",
"guardrail"
],
"question": "在 AI Agent 的权限治理中,最小权限原则(Principle of Least Privilege)意味着 Agent 在每个任务阶段只应被授予完成该任务所必需的最低限度权限,而不是在整个会话期间拥有固定的全量权限。",
"answer": true,
"explanation": "这个说法是正确的。最小权限原则是 AI Agent 安全设计的基石。具体实施中,Agent 的权限应该随任务阶段动态调整:例如在数据读取阶段只授予只读权限,在需要写入时才临时提升权限,任务完成后立即收回。这与传统的「全量授权」模式有本质区别。Human-in-the-Loop 机制通常配合最小权限使用——当 Agent 需要超出当前权限的操作时,请求人类审批而非默认放行。这种设计显著降低了 Agent 误操作或被攻击后的损害范围。",
"source": null,
"related": []
},
{
"id": "tf-005",
"type": "true_false",
"difficulty": 4,
"tags": [
"guardrail",
"llm-ops"
],
"question": "当 LLM 输出需要作为结构化数据被下游系统消费时,使用 JSON Mode(或 Structured Output)配合 JSON Schema 校验,可以完全消除输出格式错误的问题,无需额外的容错处理。",
"answer": false,
"explanation": "这个说法是错误的。JSON Mode / Structured Output 虽然能大幅提升输出格式的合规率,但不能「完全消除」问题。原因包括:(1)即使 JSON 语法正确,语义层面仍可能不符合业务预期(如枚举值在合法范围内但不符合业务逻辑);(2)不同模型对 JSON Mode 的支持程度不同,部分模型可能在边界情况下仍然输出不合规内容;(3)Token 限制可能导致 JSON 被截断;(4)网络传输和序列化过程也可能引入问题。工程实践中,JSON Schema 校验应作为第一道防线,之后仍需业务层的容错处理(如重试、fallback、日志告警等),形成多层防御体系。",
"source": null,
"related": []
}
]
}