🎯 大模型应用面试题库¶
⚠️ 时效性说明:本章涉及前沿模型/价格/榜单等信息,可能随版本快速变化;请以论文原文、官方发布页和 API 文档为准。
自含式LLM应用面试完全手册,覆盖Prompt Engineering、RAG、Agent、微调、推理优化、评估六大方向,每题含面试官追问点和标准答案。
📋 题库总览¶
| 方向 | 题目数 | 高频指数 | 考察重点 |
|---|---|---|---|
| Prompt Engineering | 8题 | ⭐⭐⭐⭐⭐ | 提示词设计与优化 |
| RAG系统 | 10题 | ⭐⭐⭐⭐⭐ | 检索增强生成全链路 |
| Agent | 8题 | ⭐⭐⭐⭐ | 智能体架构与工具调用 |
| 微调 | 8题 | ⭐⭐⭐⭐ | LoRA/数据/训练策略 |
| 推理优化 | 8题 | ⭐⭐⭐⭐⭐ | KV-Cache/量化/加速 |
| LLM评估 | 6题 | ⭐⭐⭐ | Benchmark/安全/对齐 |
🪄 一、Prompt Engineering¶
Q1:Few-shot Prompting的设计原则是什么?¶
标准答案:
Few-shot通过在Prompt中提供数个示例(exemplar)来引导LLM输出格式和推理模式。
核心原则:
- 示例选择:选与目标场景最相似的示例,多样性覆盖不同case
- 格式一致:所有示例和目标保持严格一致的输入输出格式
- 数量平衡:通常3-5个示例最优,过多会挤占context window
- 顺序敏感:最后一个示例对输出影响最大(recency bias)
# Few-shot模板示例
PROMPT = """你是一个情感分析助手。请判断文本的情感倾向。
示例1:
输入: "这家餐厅的菜品真的太好吃了,环境也很棒"
输出: {"sentiment": "positive", "confidence": 0.95}
示例2:
输入: "等了一个小时还没上菜,服务态度极差"
输出: {"sentiment": "negative", "confidence": 0.92}
示例3:
输入: "菜品还行,就是价格偏贵了点"
输出: {"sentiment": "neutral", "confidence": 0.78}
请分析以下文本:
输入: "{user_text}"
输出: """
面试官追问: Few-shot vs Fine-tuning如何选择? - 数据量<100:Few-shot - 100<数据量<10000且任务稳定:参数高效微调(LoRA) - 数据量>10000且高精度要求:全参微调
Q2:Chain-of-Thought (CoT)提示的原理和变体?¶
标准答案:
CoT通过让模型显式输出中间推理步骤来提升复杂任务的准确率,本质是将隐式推理(在模型内部注意力中完成)转化为显式推理(在token序列中展开)。
三大变体:
| 变体 | 方式 | 优势 |
|---|---|---|
| Manual CoT | 手写推理步骤示例 | 最可控 |
| Zero-shot CoT | 加"Let's think step by step" | 最简单 |
| Auto-CoT | 自动生成推理链 | 自动化 |
# Zero-shot CoT
prompt_zs = f"""
问题:一个商店有45个苹果,卖掉了18个,又进了27个,现在有多少个?
请一步步思考后给出答案。
"""
# Manual CoT (更可靠)
prompt_manual = f"""
问题:小明有12块钱,买了3本书每本2块,还剩多少钱?
思考过程:
1. 3本书的总价 = 3 × 2 = 6块
2. 剩余 = 12 - 6 = 6块
答案:6块
问题:{user_question}
思考过程:
"""
面试官追问: CoT在什么模型上效果好? - 模型参数 >~60B 时CoT效果明显提升(涌现能力) - 小模型可通过CoT蒸馏(用大模型生成的推理链微调小模型)
Q3:Self-Consistency(自一致性)如何提升推理可靠性?¶
标准答案:
Self-Consistency对同一问题多次采样(高温度),然后对最终答案多数投票。
流程: 1. 设置高温度(temperature=0.7-1.0) 2. 对同一问题生成N条不同推理路径(N=5-20) 3. 提取每条路径的最终答案 4. 多数投票选出最终答案
import collections
def self_consistency(client, prompt, n_samples=10, temperature=0.8):
"""自一致性推理"""
answers = []
for _ in range(n_samples):
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
temperature=temperature,
max_tokens=1024,
)
# 提取最终答案(假设格式为 "答案:XXX")
text = response.choices[0].message.content
answer = extract_final_answer(text)
answers.append(answer)
# 多数投票
counter = collections.Counter(answers) # Counter统计每个答案的出现次数
best_answer, count = counter.most_common(1)[0] # most_common(1)取最高频的(答案, 次数)元组并解包
confidence = count / len(answers)
return {
"answer": best_answer,
"confidence": confidence,
"vote_distribution": dict(counter),
}
面试官追问: Self-Consistency的缺点? - 成本线性增长(N次API调用) - 只适用于有确定答案的任务,开放生成不适合
Q4:Prompt中的角色设定(System Prompt)如何影响输出?¶
标准答案:
System Prompt设定模型的"人格"和行为边界,影响: 1. 输出风格:专业/口语/学术 2. 安全边界:拒绝什么类型的请求 3. 领域知识:领域专家角色可激活相关知识 4. 输出格式:JSON/Markdown/代码
# 生产级System Prompt模板
SYSTEM_PROMPT = """## 角色
你是一位资深的Python后端开发专家。
## 能力
- 精通Python 3.10+, FastAPI, SQLAlchemy
- 熟悉设计模式和架构原则
- 了解安全最佳实践
## 约束
- 所有代码示例使用Python 3.10+语法
- 遵循PEP 8规范
- 不输出未经验证的安全敏感代码
- 如果不确定,明确说明而非猜测
## 输出格式
- 代码使用```python代码块
- 先给出简要解释,再给代码
- 关键部分添加注释"""
面试官追问: System Prompt会被用户Prompt覆盖吗? - 存在Prompt Injection风险,可通过指令隔离、输入过滤、输出检测缓解
Q5:Structured Output(结构化输出)如何保证?¶
三层保障策略:
# 方法1: Function Calling (最可靠)
tools = [{
"type": "function",
"function": {
"name": "extract_entities",
"parameters": {
"type": "object",
"properties": {
"persons": {"type": "array", "items": {"type": "string"}},
"locations": {"type": "array", "items": {"type": "string"}},
},
"required": ["persons", "locations"]
}
}
}]
# 方法2: JSON Mode
response = client.chat.completions.create(
model="gpt-4o-mini",
response_format={"type": "json_object"},
messages=[{"role": "user", "content": "以JSON格式提取..."}],
)
# 方法3: Pydantic + Instructor (第三方库)
import instructor
from pydantic import BaseModel
class Sentiment(BaseModel): # Pydantic BaseModel:自动数据验证和序列化
label: str
score: float
reasoning: str
client = instructor.patch(OpenAI())
result = client.chat.completions.create(
model="gpt-4o-mini",
response_model=Sentiment,
messages=[{"role": "user", "content": "分析情感..."}],
)
Q6:Prompt优化的系统方法?¶
优化闭环:
- 基线建立:用简单Prompt在评测集上跑baseline
- 错误分析:分类错误case(格式错误/推理错误/知识缺失)
- 针对优化:针对错误类型调整Prompt
- A/B测试:对比新旧Prompt在全量评测集上的效果
- 迭代循环
常用技巧: - 明确输出格式和约束 - 提供反例("不要输出XXX格式") - 分解复杂任务为多步骤 - 使用XML标签分隔不同部分
🔍 二、RAG系统¶
Q7:RAG的完整Pipeline是什么?各阶段有什么优化点?¶
标准答案:
各阶段优化:
| 阶段 | 关键技术 | 优化方向 |
|---|---|---|
| 解析 | PDF/OCR/表格提取 | 保留结构信息 |
| 分块 | 固定/语义/递归分块 | 分块大小、重叠比 |
| 向量化 | BGE/E5/Cohere | 领域适配微调 |
| 检索 | 稠密/稀疏/混合 | 召回率 |
| 重排序 | Cross-Encoder | 精排精度 |
| 生成 | Prompt设计/引用 | 忠实度 |
面试官追问: RAG vs 长上下文窗口(128K+)哪个好? - 长窗口:简单直接,但成本高、存在"中间遗忘"问题 - RAG:可扩展到任意规模知识库,成本可控 - 最佳实践:RAG检索 + 长窗口兜底
Q8:文档分块(Chunking)策略对比?¶
标准答案:
| 策略 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 固定大小分块 | 按字符/token数切分 | 简单 | 可能切断语义 |
| 递归分块 | 按多级分隔符(段→句→词) | 保留段落结构 | 通用性好 |
| 语义分块 | Embedding相似度断点 | 语义完整 | 计算成本高 |
| 文档结构分块 | 按标题/章节切分 | 结构清晰 | 依赖文档格式 |
| 父子分块 | 小块检索,返回父大块 | 兼顾精度和上下文 | 实现复杂 |
# 父子分块 (Parent-Child Chunking)
from langchain_text_splitters import RecursiveCharacterTextSplitter
parent_splitter = RecursiveCharacterTextSplitter(chunk_size=2000, chunk_overlap=200)
child_splitter = RecursiveCharacterTextSplitter(chunk_size=400, chunk_overlap=50)
parent_chunks = parent_splitter.split_documents(docs)
for parent in parent_chunks:
children = child_splitter.split_documents([parent])
for child in children:
child.metadata["parent_id"] = parent.metadata["doc_id"]
# 检索时匹配child,返回对应parent的完整内容
面试官追问: 最优chunk_size如何确定? - 经验值:256-512 token(对话场景偏小,文档QA偏大) - 最佳方法:在评测集上grid search不同大小,看检索命中率
Q9:向量检索 vs 关键词检索 vs 混合检索?¶
标准答案:
| 方法 | 原理 | 优势 | 劣势 |
|---|---|---|---|
| 稠密检索 | Embedding语义匹配 | 理解同义词/上下位 | 精确匹配弱 |
| 稀疏检索 | BM25关键词匹配 | 精确匹配强 | 无语义理解 |
| 混合检索 | 两者加权融合 | 互补优势 | 权重需调优 |
# 混合检索实现 (Reciprocal Rank Fusion)
def reciprocal_rank_fusion(results_list, k=60):
"""RRF融合多路检索结果"""
fused_scores = {}
for results in results_list:
for rank, (doc_id, _) in enumerate(results): # enumerate返回(序号, 元素),(doc_id, _)再解包元素,_丢弃第二项
if doc_id not in fused_scores:
fused_scores[doc_id] = 0
fused_scores[doc_id] += 1 / (k + rank + 1)
# 按分数降序排列:items()返回(doc_id, score)对,lambda x: x[1]取score作为排序依据
return sorted(fused_scores.items(), key=lambda x: x[1], reverse=True) # lambda匿名函数
# 实际使用
dense_results = vector_store.similarity_search(query, k=20)
sparse_results = bm25_retriever.get_relevant_documents(query)[:20]
fused = reciprocal_rank_fusion([
[(doc.metadata['id'], doc) for doc in dense_results],
[(doc.metadata['id'], doc) for doc in sparse_results],
])
面试官追问: Embedding模型如何选择? - 中文:BGE-large-zh > M3E > text2vec - 英文:text-embedding-3-large > E5-large > GTE - 领域场景:在领域数据上微调Embedding效果显著
Q10:重排序(Reranking)的原理和必要性?¶
标准答案:
重排序用Cross-Encoder对query-document对做精细打分,弥补Bi-Encoder检索的精度不足。
为什么需要重排序: - Bi-Encoder(检索阶段):query和doc独立编码,效率高但交互信息少 - Cross-Encoder(重排序阶段):query和doc拼接后联合编码,精度高但速度慢
from sentence_transformers import CrossEncoder
reranker = CrossEncoder("BAAI/bge-reranker-large", max_length=512)
def rerank(query, documents, top_k=5):
"""Cross-Encoder重排序"""
pairs = [(query, doc.page_content) for doc in documents]
scores = reranker.predict(pairs)
ranked = sorted(zip(documents, scores), key=lambda x: x[1], reverse=True) # zip将文档和分数一一配对,sorted按分数降序
return [doc for doc, score in ranked[:top_k]] # 取前K个,解包时用doc保留文档、丢弃score
面试官追问: 重排序增加了多少延迟? - 典型: 检索(~50ms) + 重排序(~100ms for 20 docs) = ~150ms总延迟 - 优化:用更小的reranker模型 / 减少候选数量 / 异步执行
Q11:RAG系统如何评估?¶
标准答案:
检索质量评估: - Hit Rate@K:Top-K结果中是否包含正确答案 - MRR (Mean Reciprocal Rank):正确答案的平均排名倒数 - NDCG:考虑位置的评分
生成质量评估(RAGAS框架):
| 指标 | 含义 | 计算方式 |
|---|---|---|
| Faithfulness | 回答是否忠于上下文 | LLM判断回答中每句话是否有上下文支撑 |
| Answer Relevancy | 回答是否切题 | 从回答反推query,与原始query对比 |
| Context Precision | 检索到的是否相关 | 相关文档在Top-K中的排名 |
| Context Recall | 是否遗漏了相关信息 | 参考答案中的信息是否被检索覆盖 |
面试官追问: 线上RAG系统如何监控? - 记录每次query+检索结果+回答+用户反馈 - 监控拒答率、用户满意度、检索命中率 - 定期人工评估badcase → 反馈到索引和Prompt优化
Q12:Query改写(Query Rewriting)有哪些技术?¶
标准答案:
- HyDE (Hypothetical Document Embedding):让LLM先生成假设性答案,用答案的embedding去检索
- Multi-Query:LLM将原始query改写为多个角度的子query,分别检索后合并
- Step-back Prompting:将具体问题抽象为更一般的问题
- Query Decomposition:将复杂问题拆解为多个子问题
# HyDE实现
def hyde_retrieval(query, llm, retriever):
"""HyDE: 先生成假设答案,用答案做检索"""
# Step 1: 生成假设答案
hypothetical_answer = llm.invoke(
f"请直接回答以下问题(不需要确认准确性): {query}"
).content
# Step 2: 用假设答案的embedding检索
docs = retriever.get_relevant_documents(hypothetical_answer)
return docs
# Multi-Query实现
def multi_query_retrieval(query, llm, retriever, n_queries=3):
"""多角度Query改写"""
expanded_queries = llm.invoke(
f"请将以下问题从{n_queries}个不同角度改写:\n{query}\n只输出改写后的问题,每行一个"
).content.strip().split('\n') # 链式调用:strip去除空白
all_docs = []
seen_ids = set()
for q in [query] + expanded_queries:
for doc in retriever.get_relevant_documents(q):
doc_id = doc.metadata.get('doc_id')
if doc_id not in seen_ids:
all_docs.append(doc)
seen_ids.add(doc_id)
return all_docs
Q13:如何处理RAG中的多跳推理(Multi-hop Reasoning)?¶
标准答案:
多跳问题需要结合多个文档片段推理。
方案: 1. Iterative RAG:检索 → 部分回答 → 生成子问题 → 再检索 → 完善答案 2. Graph RAG:用知识图谱连接实体,沿图谱路径检索 3. Agentic RAG:让Agent自主决定检索策略和次数
# Iterative RAG简化实现
def iterative_rag(query, retriever, llm, max_steps=3):
context = ""
for step in range(max_steps):
docs = retriever.get_relevant_documents(query if step == 0 else sub_query)
context += "\n".join([d.page_content for d in docs])
response = llm.invoke(f"""
基于已有信息回答问题,如果信息不够,输出"需要更多信息:<子问题>"。
已有信息:{context}
问题:{query}
""").content
if "需要更多信息" not in response:
return response
sub_query = response.split("需要更多信息:")[1].strip()
return response
🤖 三、Agent¶
Q14:ReAct框架的原理是什么?¶
标准答案:
ReAct = Reasoning + Acting,让LLM交替执行推理(思考)和行动(调用工具),形成"思考→行动→观察"的循环。
核心流程:
Thought: 我需要查询今天北京的天气
Action: search_weather(city="北京")
Observation: 北京今天晴,最高温度25°C
Thought: 用户还问了穿什么衣服,25度适合穿...
Action: 返回最终答案
Answer: 北京今天晴朗,最高25°C,建议穿长袖薄外套...
# ReAct Agent简化实现
class ReActAgent:
def __init__(self, llm, tools, max_steps=5):
self.llm = llm
self.tools = {t.name: t for t in tools}
self.max_steps = max_steps
def run(self, query):
prompt = self._build_prompt(query)
history = []
for step in range(self.max_steps):
response = self.llm.invoke(prompt + "\n".join(history))
if "Answer:" in response.content:
return response.content.split("Answer:")[1].strip()
# 解析Action
action, action_input = self._parse_action(response.content)
if action in self.tools:
observation = self.tools[action].invoke(action_input)
history.append(f"Thought: {response.content}")
history.append(f"Observation: {observation}")
else:
history.append(f"Observation: 工具 {action} 不存在")
return "达到最大步数,无法完成任务"
面试官追问: ReAct vs Plan-and-Execute的区别? - ReAct:边想边做,灵活但可能走弯路 - Plan-and-Execute:先制定计划再逐步执行,适合复杂任务
Q15:Function Calling / Tool Use的实现原理?¶
标准答案:
LLM通过特殊的训练学会在需要时输出结构化的函数调用请求,而非自然语言。
流程: 1. 在System Prompt中提供工具的schema定义 2. LLM判断是否需要调用工具 3. 输出JSON格式的函数名+参数 4. 应用层执行函数,将结果返回给LLM 5. LLM基于结果生成最终回答
# OpenAI Function Calling
tools = [
{
"type": "function",
"function": {
"name": "get_stock_price",
"description": "获取股票当前价格",
"parameters": {
"type": "object",
"properties": {
"symbol": {"type": "string", "description": "股票代码,如AAPL"},
},
"required": ["symbol"]
}
}
}
]
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "苹果公司现在股价多少?"}],
tools=tools,
tool_choice="auto",
)
# 处理tool call
if response.choices[0].message.tool_calls:
tool_call = response.choices[0].message.tool_calls[0]
func_name = tool_call.function.name
func_args = json.loads(tool_call.function.arguments) # json.loads将JSON字符串→Python对象
# 执行工具
result = execute_tool(func_name, func_args)
# 将结果返回给LLM
messages.append(response.choices[0].message)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result) # json.dumps将Python对象→JSON字符串
})
final_response = client.chat.completions.create(model="gpt-4o", messages=messages)
面试官追问: Function Calling如何训练的? - 在SFT阶段加入工具调用的示例数据 - 输出格式通常是特殊token包裹的JSON
Q16:多Agent协作的架构模式?¶
标准答案:
| 模式 | 描述 | 适用场景 |
|---|---|---|
| 顺序链式 | A→B→C,每个处理一步 | Pipeline任务 |
| 主从模式 | Orchestrator分派子Agent | 复杂任务分解 |
| 辩论模式 | 多Agent讨论达成共识 | 需要多角度分析 |
| 群聊模式 | 多Agent自由对话 | 头脑风暴 |
# 主从模式示例(简化)
class OrchestratorAgent:
def __init__(self, planner_llm, worker_agents):
self.planner = planner_llm
self.workers = worker_agents # {"researcher": ..., "coder": ..., "reviewer": ...}
def run(self, task):
# 1. 规划子任务
plan = self.planner.invoke(f"将以下任务分解为子任务,指定负责的Agent:\n{task}")
subtasks = self._parse_plan(plan)
# 2. 分派执行
results = {}
for subtask in subtasks:
agent_name = subtask["agent"]
result = self.workers[agent_name].run(subtask["description"])
results[subtask["id"]] = result
# 3. 汇总
summary = self.planner.invoke(f"汇总以下子任务结果:\n{results}")
return summary
面试官追问: 多Agent系统最大的挑战? - 状态同步:Agent之间的信息共享和上下文传递 - 错误传播:一个Agent错误会影响下游 - 成本控制:多次LLM调用的token消耗
Q17:MCP(Model Context Protocol)是什么?¶
标准答案:
MCP是Anthropic提出的开放协议,标准化了LLM与外部工具/数据源的通信方式,类似于"AI的USB接口"。
核心概念: - MCP Server:提供工具/资源的服务端 - MCP Client:LLM应用(如Claude Desktop, VS Code) - Transport:通信方式(stdio/SSE/HTTP)
与Function Calling的区别: - Function Calling: 工具定义和执行都在应用代码中 - MCP: 工具以独立服务存在,可跨应用复用
Q18:Agent的记忆(Memory)系统如何设计?¶
标准答案:
| 记忆类型 | 实现方式 | 生命周期 |
|---|---|---|
| 短期记忆 | 对话历史窗口 | 单次会话 |
| 长期记忆 | 向量数据库存储 | 跨会话 |
| 工作记忆 | Scratchpad/变量 | 单次任务 |
| 实体记忆 | 结构化KV存储 | 持久化 |
# 带记忆的Agent简化实现
class MemoryAgent:
def __init__(self, llm, vector_store, max_history=20):
self.llm = llm
self.long_term = vector_store # 长期记忆
self.short_term = [] # 短期记忆(对话历史)
self.max_history = max_history
def chat(self, user_message):
# 1. 从长期记忆检索相关信息
relevant_memories = self.long_term.similarity_search(user_message, k=3)
memory_context = "\n".join([m.page_content for m in relevant_memories])
# 2. 构建Prompt
messages = [
{"role": "system", "content": f"相关记忆:\n{memory_context}"},
*self.short_term[-self.max_history:],
{"role": "user", "content": user_message}
]
# 3. 生成回复
response = self.llm.invoke(messages).content
# 4. 更新记忆
self.short_term.append({"role": "user", "content": user_message})
self.short_term.append({"role": "assistant", "content": response})
# 5. 重要信息存入长期记忆
if self._is_important(user_message, response):
self.long_term.add_texts([f"用户: {user_message}\n助手: {response}"])
return response
🔧 四、微调(Fine-tuning)¶
Q19:LoRA的原理是什么?为什么有效?¶
标准答案:
LoRA (Low-Rank Adaptation) 冻结原始权重,在旁路中训练低秩矩阵。
数学原理:
其中 \(W_0 \in \mathbb{R}^{d \times k}\) 冻结,\(B \in \mathbb{R}^{d \times r}\),\(A \in \mathbb{R}^{r \times k}\),\(r \ll \min(d, k)\)。
为什么有效: 1. 预训练权重已有很强的通用能力,微调时的变化量 \(\Delta W\) 本身就是低秩的 2. 实验表明 \(r=8\) 或 \(r=16\) 就能达到全参微调95%+的效果
| 方法 | 可训练参数 | 以7B模型为例 |
|---|---|---|
| 全参微调 | 100% | ~7B |
| LoRA (r=16) | ~0.1% | ~8M |
| QLoRA (4bit+LoRA) | ~0.1%+4bit量化 | ~8M (显存从28GB→6GB) |
from peft import LoraConfig, get_peft_model, TaskType
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=16, # 秩
lora_alpha=32, # 缩放因子 (alpha/r 为实际缩放)
lora_dropout=0.05,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"], # 作用模块
bias="none",
)
model = get_peft_model(base_model, lora_config)
model.print_trainable_parameters()
# trainable params: 8,388,608 || all params: 6,746,804,224 || trainable%: 0.124%
面试官追问: LoRA的rank如何选择? - 简单任务(分类): r=4-8 - 复杂任务(对话/指令跟随): r=16-64 - 经验法则: 先用r=16,在验证集上对比r=8和r=32
Q20:微调数据如何准备?质量如何保证?¶
标准答案:
数据格式(指令微调):
{
"instruction": "将以下英文翻译成中文",
"input": "Machine learning is a subset of AI.",
"output": "机器学习是人工智能的一个子集。"
}
数据质量保障:
| 维度 | 检查方法 | 工具 |
|---|---|---|
| 格式正确性 | JSON Schema验证 | jsonschema |
| 答案准确性 | LLM评分+人工抽检 | GPT-4 as judge |
| 多样性 | 去重+分布分析 | MinHash/Embedding聚类 |
| 长度分布 | 统计token长度 | tiktoken |
| 安全性 | 毒性/偏见检测 | Perspective API |
# 数据质量检查Pipeline
def validate_training_data(data):
"""验证微调数据质量"""
issues = []
for i, item in enumerate(data): # enumerate同时获取索引和元素
# 1. 格式检查
if not all(k in item for k in ['instruction', 'output']): # all()全部为True才返回True
issues.append(f"[{i}] 缺少必要字段")
continue
# 2. 长度检查
total_tokens = count_tokens(item['instruction'] + item.get('input','') + item['output']) # get('input',''):若该键不存在则返回空字符串,避免KeyError
if total_tokens > 2048:
issues.append(f"[{i}] 超长: {total_tokens} tokens")
if len(item['output'].strip()) < 10:
issues.append(f"[{i}] 回答过短")
# 3. 重复检查
# ... MinHash去重
print(f"共 {len(data)} 条, {len(issues)} 个问题")
return issues
面试官追问: 微调数据量多少合适? - 领域适配: 1000-5000条高质量数据 - 对话能力: 10000-50000条 - 关键是质量 > 数量,1000条精标数据 > 10000条低质量数据
Q21:全参微调 vs LoRA vs QLoRA如何选择?¶
标准答案:
| 维度 | 全参微调 | LoRA | QLoRA |
|---|---|---|---|
| 显存 (7B) | ~56GB | ~28GB | ~6GB |
| 训练速度 | 基准 | ~1.2x慢 | ~1.5x慢 |
| 效果上限 | 最高 | 接近全参 | 略低 |
| 适用场景 | 充足资源+大量数据 | 主流选择 | 消费级显卡 |
| 灾难遗忘 | 较严重 | 较轻 | 较轻 |
决策树:
Q22:SFT(监督微调)和RLHF/DPO的区别?¶
标准答案:
| 阶段 | 方法 | 数据格式 | 目标 |
|---|---|---|---|
| SFT | 监督学习 | (prompt, response) | 学会指令跟随 |
| RLHF | PPO强化学习 | (prompt, chosen, rejected) | 对齐人类偏好 |
| DPO | 直接偏好优化 | (prompt, chosen, rejected) | 简化RLHF |
DPO损失函数:
面试官追问: 为什么DPO正在替代RLHF? - RLHF需要训练奖励模型+PPO,流程复杂 - DPO直接用偏好数据优化,效果相当但训练更稳定
⚡ 五、推理优化¶
Q23:KV-Cache的原理是什么?¶
标准答案:
自回归生成时,每个token的生成都需要对前面所有token做注意力计算。KV-Cache缓存已计算的Key和Value向量,避免重复计算。
数学理解:
- 无Cache:生成第 \(t\) 个token需计算 \(t\) 个K和V → 总计算量 \(O(n^2)\)
- 有Cache:只计算新token的Q,与缓存的K和V做注意力 → 增量计算 \(O(n)\)
显存占用:
以Llama-2-7B为例(32层, 32头, d=128, FP16): - seq_len=2048, batch=1: \(2 \times 32 \times 32 \times 128 \times 2048 \times 2 = 1.07\text{GB}\)
面试官追问: KV-Cache占用太大怎么办? - GQA (Grouped Query Attention): 多个Q头共享K/V头,如Llama-2用GQA将KV头从32减到8 - MQA (Multi-Query Attention): 所有Q头共享一组KV - 滑动窗口注意力 (Mistral): 限制attention范围
Q24:PagedAttention(vLLM)的核心创新?¶
标准答案:
传统KV-Cache为每个请求预分配最大长度的连续显存,导致大量浪费。
PagedAttention借鉴操作系统虚拟内存的分页思想: 1. 将KV-Cache分成固定大小的block(类似内存页) 2. 用page table映射逻辑位置→物理block 3. 按需分配,不需要连续内存 4. 支持block共享(beam search/prefix caching)
效果: - 显存利用率从50%→95%+ - 相同显存下吞吐量提升2-4倍
# vLLM使用示例
from vllm import LLM, SamplingParams
llm = LLM(
model="meta-llama/Llama-2-7b-chat-hf",
tensor_parallel_size=1,
gpu_memory_utilization=0.90,
max_num_seqs=256, # 最大并发
max_model_len=4096,
)
sampling_params = SamplingParams(
temperature=0.7,
top_p=0.9,
max_tokens=512,
)
outputs = llm.generate(["你好,请介绍一下自己"], sampling_params)
面试官追问: vLLM vs TGI vs TensorRT-LLM如何选? - vLLM:通用性好,易用,社区活跃 - TGI:HuggingFace生态,部署简单 - TensorRT-LLM:性能最优,但配置复杂
Q25:模型量化的方法对比?¶
标准答案:
| 方法 | 精度 | 速度 | 质量损失 | 特点 |
|---|---|---|---|---|
| FP16 | 16bit | 基准 | 无 | 训练/推理标准 |
| INT8 (LLM.int8()) | 8bit | 1.2-1.5x | 极小 | 混合精度分解 |
| GPTQ | 4bit | 1.5-2x | 小 | 训练后量化,需校准数据 |
| AWQ | 4bit | 1.5-2x | 小 | 保护重要权重通道 |
| GGUF (llama.cpp) | 2-8bit | 1-3x | 可变 | CPU友好 |
面试官追问: 4bit量化真的能保持效果吗? - 7B模型4bit量化后,MMLU下降通常<1% - 越大的模型对量化越鲁棒 - 关键:保护attention层中少数重要通道
Q26:投机解码(Speculative Decoding)原理?¶
标准答案:
用小模型(Draft Model)快速生成多个候选token,大模型(Target Model)并行验证,接受概率匹配的token。
流程: 1. Draft Model自回归生成 \(\gamma\) 个token(如5个) 2. Target Model一次前向传播并行验证这5个token 3. 按照rejection sampling: 如果小模型置信度≤大模型,直接接受 4. 否则以一定概率接受/拒绝,从拒绝位置重新采样
加速比: 通常 1.5-3x,取决于Draft Model与Target Model的一致率
Draft Model: "今天 天气 真 不错 想 出去" (6个token, 很快生成)
Target Model: ✅ ✅ ✅ ✅ ❌ (验证前4个ok, 第5个拒绝)
-> 接受 "今天 天气 真 不错", 从第5个位置用Target采样
面试官追问: 投机解码的输出分布与原始Target Model一致吗? - 是的,通过rejection sampling数学保证输出分布完全一致
Q27:Continuous Batching(持续批处理)原理?¶
标准答案:
传统Static Batching等所有请求都完成才处理下一批,导致短请求等长请求。
Continuous Batching在iteration级别动态调度: - 每个iteration后,检查是否有请求完成(EOS) - 已完成的请求立即移出,新请求立即插入 - 显存利用率大幅提升
效果: 吞吐量可提升10-20x(高并发场景)
Q28:Prefill和Decode阶段的计算特性区别?¶
标准答案:
| 阶段 | 计算模式 | 瓶颈 | 特点 |
|---|---|---|---|
| Prefill | 并行处理所有输入token | Compute-bound | 一次前向,批处理高效 |
| Decode | 逐token自回归生成 | Memory-bound | 每次只生成1个token |
优化策略: - Prefill: Tensor并行、FlashAttention - Decode: KV-Cache、投机解码、量化
面试官追问: 什么是TTFT和TPS? - TTFT (Time To First Token): Prefill延迟,用户感知的等待时间 - TPS (Tokens Per Second): Decode速度,流式输出的速度
📊 六、LLM评估¶
Q29:主流LLM Benchmark有哪些?各评估什么能力?¶
标准答案:
| Benchmark | 评估能力 | 方式 |
|---|---|---|
| MMLU | 学科知识(57科) | 多选题 |
| HumanEval / MBPP | 代码生成 | Pass@k |
| GSM8K / MATH | 数学推理 | 精确匹配 |
| MT-Bench | 多轮对话 | GPT-4打分 |
| AlpacaEval | 指令跟随 | GPT-4对比评分 |
| TruthfulQA | 真实性 | 多选+生成 |
| BBH | 复杂推理 | CoT |
面试官追问: Benchmark的局限性? - 数据污染:模型可能在训练时见过测试题 - 刷榜优化:针对Benchmark优化,不代表真实能力 - 静态评估:无法覆盖开放式生成的质量
Q30:LLM-as-Judge评估方法的原理和问题?¶
标准答案:
用强LLM(如GPT-4)评判其他模型的回答质量。
JUDGE_PROMPT = """请评估以下AI回答的质量。
用户问题: {question}
AI回答: {answer}
参考答案: {reference}
请从以下维度打分(1-5分):
1. 准确性: 信息是否正确
2. 完整性: 是否覆盖关键点
3. 清晰度: 表述是否清楚
4. 有用性: 对用户的帮助程度
输出JSON: {"accuracy": x, "completeness": x, "clarity": x, "helpfulness": x, "overall": x}
"""
已知问题: - 位置偏见:倾向于给第一个出现的回答更高分 → 解决:交换位置取平均 - 长度偏见:更长的回答更容易获高分 → 解决:控制回答长度范围 - 自我偏好:GPT-4可能偏好GPT-4生成的内容 → 解决:多个judge交叉评估
Q31:人类对齐(Alignment)的目标和方法?¶
标准答案:
对齐三原则(HHH): - Helpful: 有用、准确、完整 - Harmless: 无害、安全、不歧视 - Honest: 诚实、承认不确定
技术路线: 1. RLHF: 人类标注偏好 → 训练奖励模型 → PPO优化 2. DPO: 直接使用偏好数据优化 3. Constitutional AI: LLM自我评判+修改 4. RLAIF: AI生成反馈替代人类
Q32:LLM安全性评估包括哪些方面?¶
标准答案:
| 维度 | 评估内容 | 测试方法 |
|---|---|---|
| 毒性 | 生成有害/攻击性内容 | 毒性分类器评分 |
| 偏见 | 性别/种族/年龄偏见 | 偏见基准测试 |
| 幻觉 | 生成不存在的事实 | 事实核查 |
| 泄露 | 输出训练数据 | 成员推断攻击 |
| 越狱 | Prompt注入/越狱 | 红队测试 |
| 隐私 | 输出个人信息 | PII检测 |
红队测试要点: - 直接要求 → 角色扮演诱导 → 编码绕过 → 多步骤攻击 - 持续更新攻击策略,防御是动态过程
📝 面试备考清单¶
高频必会¶
- Few-shot / CoT / Self-Consistency原理和代码
- RAG全链路(分块→检索→重排→生成)
- LoRA原理和参数选择
- KV-Cache / PagedAttention原理
- 量化方法对比(GPTQ/AWQ/GGUF)
进阶加分¶
- ReAct / Function Calling实现
- 多Agent协作架构
- DPO vs RLHF对比
- 投机解码 / Continuous Batching
- RAG评估(RAGAS框架)
系统设计题准备¶
- 设计一个企业RAG系统
- 设计一个多模态对话系统
- 设计一个LLM推理服务(高并发)
- 设计一个Agent开发平台
💡 高频出题公司参考:字节跳动(豆包/推理优化)、腾讯(混元/Agent)、阿里(通义/RAG)、百度(文心/微调)、月之暗面(Kimi/长文本)、智谱(GLM/评估)