AI应用开发工程师面试题:深度技术拷打

候选人 : xxx
应聘岗位 : AI应用开发工程师(RAG/智能体方向)
面试官视角 : 资深AI应用开发工程师
面试目的: 验证简历真实性,考察技术深度,评估实战能力


📊 简历技术栈分析

简历声称的技术能力

技术领域 声称掌握 需验证深度
RAG系统 向量检索、关键词召回、精排策略 ⭐⭐⭐⭐⭐
LangChain/LangGraph 状态机工作流、Agent构建 ⭐⭐⭐⭐⭐
知识图谱 图谱推理、学习路径优化 ⭐⭐⭐⭐
Memory机制 三层Memory设计 ⭐⭐⭐⭐⭐
向量数据库 Milvus、ChromaDB ⭐⭐⭐⭐
Python/SQL 基础能力 ⭐⭐⭐

一、Python基础能力拷打

1.1 基础语法(难度:⭐⭐)

Q1: 请解释Python中的深拷贝和浅拷贝的区别,并举例说明在RAG系统中可能遇到的问题。

python 复制代码
# 追问:以下代码输出什么?
import copy

class Document:
    def __init__(self, content, metadata):
        self.content = content
        self.metadata = metadata

doc1 = Document("Hello", {"author": "张三", "tags": ["AI", "RAG"]})
doc2 = copy.copy(doc1)  # 浅拷贝
doc3 = copy.deepcopy(doc1)  # 深拷贝

doc2.metadata["tags"].append("LLM")
doc3.metadata["tags"].append("Vector")

print(doc1.metadata["tags"])  # 输出什么?
print(doc2.metadata["tags"])  # 输出什么?
print(doc3.metadata["tags"])  # 输出什么?

预期答案:

  • doc1.metadata["tags"] → ["AI", "RAG", "LLM"](浅拷贝共享嵌套对象)
  • doc2.metadata["tags"] → ["AI", "RAG", "LLM"](与doc1共享)
  • doc3.metadata["tags"] → ["AI", "RAG", "Vector"](深拷贝独立)

追问:在RAG系统中,如果文档对象被多个检索器共享,浅拷贝可能导致什么问题?


1.2 异步编程(难度:⭐⭐⭐)

Q2: 你的简历提到"批量简历处理",请解释以下代码的问题并优化:

python 复制代码
# 候选人可能写的代码
async def process_resumes(resume_list):
    results = []
    for resume in resume_list:
        result = await parse_resume(resume)  # 单个解析
        results.append(result)
    return results

# 问题:这段代码有什么性能问题?如何优化?

预期答案:

python 复制代码
# 优化方案1:并发执行
async def process_resumes_v1(resume_list):
    tasks = [parse_resume(resume) for resume in resume_list]
    results = await asyncio.gather(*tasks)
    return results

# 优化方案2:限制并发数
async def process_resumes_v2(resume_list, max_concurrent=10):
    semaphore = asyncio.Semaphore(max_concurrent)
    
    async def limited_parse(resume):
        async with semaphore:
            return await parse_resume(resume)
    
    tasks = [limited_parse(resume) for resume in resume_list]
    results = await asyncio.gather(*tasks)
    return results

追问:

  1. 为什么需要限制并发数?
  2. 如果parse_resume内部是CPU密集型操作,asyncio还有用吗?

1.3 装饰器与上下文管理器(难度:⭐⭐⭐⭐)

Q3: 请实现一个计时装饰器,用于统计RAG检索各阶段的耗时:

python 复制代码
# 要求:
# 1. 支持同步和异步函数
# 2. 支持嵌套调用时正确显示层级
# 3. 输出格式:[RAG] vector_search took 0.123s

# 请现场实现

预期答案:

python 复制代码
import time
import functools
from contextvars import ContextVar

# 用于追踪调用层级
call_depth = ContextVar('call_depth', default=0)

def timing_decorator(stage_name):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            depth = call_depth.get()
            call_depth.set(depth + 1)
            
            indent = "  " * depth
            start = time.time()
            try:
                return func(*args, **kwargs)
            finally:
                elapsed = time.time() - start
                print(f"{indent}[RAG] {stage_name} took {elapsed:.3f}s")
                call_depth.set(depth)
        
        @functools.wraps(func)
        async def async_wrapper(*args, **kwargs):
            depth = call_depth.get()
            call_depth.set(depth + 1)
            
            indent = "  " * depth
            start = time.time()
            try:
                return await func(*args, **kwargs)
            finally:
                elapsed = time.time() - start
                print(f"{indent}[RAG] {stage_name} took {elapsed:.3f}s")
                call_depth.set(depth)
        
        import asyncio
        if asyncio.iscoroutinefunction(func):
            return async_wrapper
        return wrapper
    
    return decorator


# 使用示例
@timing_decorator("vector_search")
def vector_search(query):
    time.sleep(0.1)
    return ["doc1", "doc2"]

@timing_decorator("rerank")
def rerank(docs):
    time.sleep(0.05)
    return docs[:5]

@timing_decorator("full_retrieval")
def full_retrieval(query):
    docs = vector_search(query)
    return rerank(docs)

# 输出:
# [RAG] vector_search took 0.100s
#   [RAG] rerank took 0.050s
# [RAG] full_retrieval took 0.150s

二、SQL与数据库能力拷打

2.1 SQL查询优化(难度:⭐⭐⭐)

Q4: 你的简历提到"错题记录、知识点标签",请设计表结构并优化以下查询:

sql 复制代码
-- 需求:查询每个学生的薄弱知识点(正确率低于60%的知识点)
-- 已有表:students, questions, knowledge_points, student_answers

-- 请写出SQL并分析性能瓶颈

预期答案:

sql 复制代码
-- 方案1:基础查询
SELECT 
    s.student_id,
    s.student_name,
    kp.knowledge_point_id,
    kp.knowledge_point_name,
    COUNT(*) as total_questions,
    SUM(CASE WHEN sa.is_correct THEN 1 ELSE 0 END) as correct_count,
    SUM(CASE WHEN sa.is_correct THEN 1 ELSE 0 END) * 100.0 / COUNT(*) as accuracy_rate
FROM students s
JOIN student_answers sa ON s.student_id = sa.student_id
JOIN questions q ON sa.question_id = q.question_id
JOIN question_knowledge_points qkp ON q.question_id = qkp.question_id
JOIN knowledge_points kp ON qkp.knowledge_point_id = kp.knowledge_point_id
GROUP BY s.student_id, s.student_name, kp.knowledge_point_id, kp.knowledge_point_name
HAVING SUM(CASE WHEN sa.is_correct THEN 1 ELSE 0 END) * 100.0 / COUNT(*) < 60
ORDER BY s.student_id, accuracy_rate ASC;

-- 性能分析:
-- 1. 多表JOIN可能导致性能问题
-- 2. GROUP BY + HAVING在大数据量下较慢
-- 3. 建议添加索引:
--    - student_answers(student_id, question_id)
--    - question_knowledge_points(question_id, knowledge_point_id)

追问:

  1. 如果数据量达到千万级,如何优化?
  2. 是否考虑用Redis缓存热点数据?

2.2 向量数据库(难度:⭐⭐⭐⭐)

Q5: 你使用过Milvus,请解释以下概念并回答问题:

python 复制代码
# 问题1:以下索引类型有什么区别?如何选择?
# - FLAT
# - IVF_FLAT
# - IVF_PQ
# - HNSW

# 问题2:以下代码有什么问题?
from pymilvus import Collection

collection = Collection("documents")
collection.load()

# 搜索
results = collection.search(
    data=[query_vector],
    anns_field="embedding",
    param={"metric_type": "L2", "params": {"nprobe": 10}},
    limit=10
)

预期答案:

索引类型对比:

索引类型 特点 适用场景
FLAT 暴力搜索,精度最高 数据量小(<10万)
IVF_FLAT 聚类分桶,精度与速度平衡 中等数据量(10-100万)
IVF_PQ 量化压缩,内存占用低 大数据量、内存受限
HNSW 图索引,速度快 实时性要求高

代码问题:

  1. 没有指定搜索参数中的nprobe应该根据索引类型调整
  2. 没有处理加载状态检查
  3. 没有考虑分区搜索

追问:

  1. 如果向量维度从768增加到1536,对性能有什么影响?
  2. 如何实现混合检索(向量+标量过滤)?

三、LangChain/LangGraph深度拷打

3.1 LangChain核心概念(难度:⭐⭐⭐)

Q6: 请解释LangChain中以下组件的作用和关系:

python 复制代码
# 请解释以下代码的工作流程
from langchain.chains import RetrievalQA
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
from langchain.llms import OpenAI

# 1. 这里的embedding模型和LLM模型有什么区别?
# 2. RetrievalQA内部是如何工作的?
# 3. 如果要自定义prompt模板,如何修改?

vectorstore = Chroma(
    embedding_function=OpenAIEmbeddings(),
    persist_directory="./chroma_db"
)

qa_chain = RetrievalQA.from_chain_type(
    llm=OpenAI(),
    chain_type="stuff",
    retriever=vectorstore.as_retriever(search_kwargs={"k": 4})
)

result = qa_chain.run("什么是RAG?")

预期答案:

  1. Embedding模型 vs LLM模型:

    • Embedding模型:将文本转换为向量,用于相似度计算
    • LLM模型:生成文本,用于回答问题
  2. RetrievalQA工作流程:

    复制代码
    用户问题 → Embedding编码 → 向量检索 → 获取Top-K文档 → 
    构建Prompt(问题+文档) → LLM生成 → 返回答案
  3. 自定义Prompt模板:

python 复制代码
from langchain.prompts import PromptTemplate

prompt_template = """你是一个专业的AI助手。请基于以下参考文档回答问题。

参考文档:
{context}

问题:{question}

请给出准确、详细的回答:"""

PROMPT = PromptTemplate(
    template=prompt_template,
    input_variables=["context", "question"]
)

qa_chain = RetrievalQA.from_chain_type(
    llm=OpenAI(),
    chain_type="stuff",
    retriever=vectorstore.as_retriever(search_kwargs={"k": 4}),
    chain_type_kwargs={"prompt": PROMPT}
)

3.2 LangGraph状态机(难度:⭐⭐⭐⭐⭐)

Q7: 你的简历提到"基于LangGraph设计简历处理状态机工作流",请回答:

python 复制代码
# 问题:以下代码实现了什么功能?有什么问题?

from langgraph.graph import StateGraph, END
from typing import TypedDict

class ResumeState(TypedDict):
    resume_text: str
    parsed_data: dict
    screening_result: dict
    is_qualified: bool

def parse_resume(state: ResumeState) -> ResumeState:
    # 解析简历
    parsed = llm.invoke(f"解析以下简历:{state['resume_text']}")
    state["parsed_data"] = parsed
    return state

def screen_resume(state: ResumeState) -> ResumeState:
    # 筛选简历
    result = llm.invoke(f"筛选以下简历:{state['parsed_data']}")
    state["screening_result"] = result
    state["is_qualified"] = result.get("qualified", False)
    return state

# 构建图
graph = StateGraph(ResumeState)
graph.add_node("parse", parse_resume)
graph.add_node("screen", screen_resume)

graph.set_entry_point("parse")
graph.add_edge("parse", "screen")
graph.add_edge("screen", END)

app = graph.compile()

# 问题:
# 1. 这个状态机有什么问题?
# 2. 如果解析失败怎么办?
# 3. 如何添加重试机制?
# 4. 如何添加条件分支(不合格直接结束)?

预期答案:

python 复制代码
from langgraph.graph import StateGraph, END
from langgraph.checkpoint.memory import MemorySaver
from typing import TypedDict, Optional
import logging

class ResumeState(TypedDict):
    resume_text: str
    parsed_data: Optional[dict]
    screening_result: Optional[dict]
    is_qualified: Optional[bool]
    error: Optional[str]
    retry_count: int

# 1. 添加错误处理
def parse_resume(state: ResumeState) -> ResumeState:
    try:
        parsed = llm.invoke(f"解析以下简历:{state['resume_text']}")
        state["parsed_data"] = parsed
        state["error"] = None
    except Exception as e:
        state["error"] = str(e)
        logging.error(f"解析失败: {e}")
    return state

# 2. 添加重试逻辑
def should_retry(state: ResumeState) -> str:
    if state.get("parsed_data"):
        return "screen"
    if state.get("retry_count", 0) < 3:
        state["retry_count"] = state.get("retry_count", 0) + 1
        return "parse"
    return "error"

# 3. 条件分支
def check_qualified(state: ResumeState) -> str:
    if state.get("is_qualified"):
        return "save"
    return "reject"

# 构建完整的状态机
graph = StateGraph(ResumeState)

# 添加节点
graph.add_node("parse", parse_resume)
graph.add_node("screen", screen_resume)
graph.add_node("save", save_to_db)
graph.add_node("reject", send_rejection)
graph.add_node("error", handle_error)

# 设置入口
graph.set_entry_point("parse")

# 添加条件边
graph.add_conditional_edges(
    "parse",
    should_retry,
    {
        "screen": "screen",
        "parse": "parse",  # 重试
        "error": "error"
    }
)

graph.add_conditional_edges(
    "screen",
    check_qualified,
    {
        "save": "save",
        "reject": "reject"
    }
)

# 添加结束边
graph.add_edge("save", END)
graph.add_edge("reject", END)
graph.add_edge("error", END)

# 编译(带检查点)
checkpointer = MemorySaver()
app = graph.compile(checkpointer=checkpointer)

追问:

  1. LangGraph和LangChain Chain有什么区别?
  2. 如何实现人工审核节点?
  3. 如何持久化状态?

3.3 Memory机制(难度:⭐⭐⭐⭐⭐)

Q8: 你的简历提到"设计教育场景三层Memory机制",请深入回答:

python 复制代码
# 问题1:请解释你的三层Memory是如何设计的?

# 问题2:以下代码实现了短期记忆,有什么问题?
class ShortTermMemory:
    def __init__(self, max_messages=10):
        self.messages = []
        self.max_messages = max_messages
    
    def add(self, message):
        self.messages.append(message)
        if len(self.messages) > self.max_messages:
            self.messages.pop(0)
    
    def get_context(self):
        return "\n".join(self.messages)

# 问题3:如何实现摘要压缩?请写出伪代码

# 问题4:长期记忆如何与短期记忆结合?

预期答案:

三层Memory架构:

复制代码
┌─────────────────────────────────────────┐
│           即时记忆(Immediate)           │
│        当前对话上下文(LLM窗口)           │
└─────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────┐
│           短期记忆(Short-term)          │
│     会话历史(Redis/内存,TTL=1小时)      │
└─────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────┐
│           长期记忆(Long-term)           │
│    用户画像/知识库(向量数据库/Neo4j)      │
└─────────────────────────────────────────┘

短期记忆问题:

  1. 没有考虑token限制
  2. 没有重要性权重
  3. 没有时间衰减

摘要压缩实现:

python 复制代码
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate

class SummaryMemory:
    def __init__(self, llm_model="gpt-4", max_tokens=2000):
        self.llm = ChatOpenAI(model=llm_model, temperature=0.3)
        self.max_tokens = max_tokens
        self.messages = []
        self.summary = ""
    
    def add(self, message: str):
        self.messages.append(message)
        
        # 检查是否需要压缩
        if self._estimate_tokens() > self.max_tokens:
            self._compress()
    
    def _estimate_tokens(self) -> int:
        # 简化估算:1 token ≈ 4 characters
        total_chars = len(self.summary) + sum(len(m) for m in self.messages)
        return total_chars // 4
    
    def _compress(self):
        # 保留最近5条消息
        to_compress = self.messages[:-5]
        recent = self.messages[-5:]
        
        # 构建压缩Prompt
        prompt = ChatPromptTemplate.from_messages([
            ("system", """请将以下对话历史压缩为简洁的摘要。

要求:
1. 保留关键信息:用户意图、重要决策、任务状态
2. 保留用户偏好和个性化信息
3. 省略寒暄、重复内容"""),
            ("human", "对话历史:\n{conversation}\n\n摘要:")
        ])
        
        chain = prompt | self.llm
        result = chain.invoke({
            "conversation": "\n".join(to_compress)
        })
        
        # 更新摘要
        self.summary = f"{self.summary}\n{result.content}" if self.summary else result.content
        self.messages = recent
    
    def get_context(self) -> str:
        parts = []
        
        if self.summary:
            parts.append(f"【历史摘要】\n{self.summary}")
        
        if self.messages:
            parts.append(f"【最近对话】\n" + "\n".join(self.messages))
        
        return "\n\n".join(parts)

追问:

  1. 如何判断哪些信息应该存入长期记忆?
  2. 如何实现记忆的重要性评分?
  3. MemGPT的Core Memory和Archival Memory有什么区别?

四、RAG系统深度拷打

4.1 RAG架构设计(难度:⭐⭐⭐⭐)

Q9: 你的简历提到"语义检索、关键词召回和精排策略",请回答:

python 复制代码
# 问题1:请画出你设计的RAG架构图

# 问题2:以下代码实现了混合检索,有什么问题?
def hybrid_search(query, vector_store, keyword_index, alpha=0.5):
    # 向量检索
    vector_results = vector_store.similarity_search(query, k=10)
    
    # 关键词检索
    keyword_results = keyword_index.search(query, k=10)
    
    # 简单合并
    all_results = vector_results + keyword_results
    return all_results[:10]

# 问题3:如何实现RRF融合?请写出公式和代码

预期答案:

RAG架构图:

复制代码
┌─────────────────────────────────────────────────────────┐
│                      用户Query                           │
└─────────────────────────────────────────────────────────┘
                           ↓
┌─────────────────────────────────────────────────────────┐
│                    Query预处理                           │
│  - Query改写/扩展                                        │
│  - 实体识别                                              │
└─────────────────────────────────────────────────────────┘
                           ↓
┌─────────────────────────────────────────────────────────┐
│                    多路召回层                            │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐              │
│  │向量检索   │  │关键词检索 │  │图谱检索   │              │
│  │Top-100   │  │Top-50    │  │Top-30    │              │
│  └──────────┘  └──────────┘  └──────────┘              │
└─────────────────────────────────────────────────────────┘
                           ↓
┌─────────────────────────────────────────────────────────┐
│                    融合排序层                            │
│  - RRF融合                                               │
│  - 去重合并                                               │
│  - 候选文档池 Top-150                                     │
└─────────────────────────────────────────────────────────┘
                           ↓
┌─────────────────────────────────────────────────────────┐
│                    精排层                                │
│  - Reranker模型重排                                       │
│  - 业务规则增强                                           │
│  - 最终结果 Top-10                                        │
└─────────────────────────────────────────────────────────┘
                           ↓
┌─────────────────────────────────────────────────────────┐
│                    生成层                                │
│  - Prompt构建                                             │
│  - LLM生成                                                │
│  - 答案后处理                                             │
└─────────────────────────────────────────────────────────┘

混合检索问题:

  1. 没有分数归一化
  2. 没有去重
  3. 简单截断,没有融合策略

RRF融合实现:

python 复制代码
def rrf_fusion(
    vector_results: List[Tuple[str, float]],
    keyword_results: List[Tuple[str, float]],
    k: int = 60
) -> List[Tuple[str, float]]:
    """
    RRF (Reciprocal Rank Fusion) 融合
    
    公式: RRF(d) = Σ 1 / (k + rank(d))
    
    Args:
        vector_results: [(doc_id, score), ...]
        keyword_results: [(doc_id, score), ...]
        k: RRF参数,通常取60
    """
    rrf_scores = {}
    
    # 向量检索结果
    for rank, (doc_id, _) in enumerate(vector_results, 1):
        if doc_id not in rrf_scores:
            rrf_scores[doc_id] = 0
        rrf_scores[doc_id] += 1 / (k + rank)
    
    # 关键词检索结果
    for rank, (doc_id, _) in enumerate(keyword_results, 1):
        if doc_id not in rrf_scores:
            rrf_scores[doc_id] = 0
        rrf_scores[doc_id] += 1 / (k + rank)
    
    # 排序
    sorted_results = sorted(
        rrf_scores.items(),
        key=lambda x: x[1],
        reverse=True
    )
    
    return sorted_results

追问:

  1. RRF中的k参数如何选择?
  2. 如何评估融合效果?
  3. 如果向量检索和关键词检索结果差异很大怎么办?

4.2 Reranker策略(难度:⭐⭐⭐⭐⭐)

Q10: 你的简历提到"精排策略优化题目匹配效果",请深入回答:

python 复制代码
# 问题1:什么是Cross-Encoder和Bi-Encoder?有什么区别?

# 问题2:以下代码使用BGE-Reranker,有什么问题?
from FlagEmbedding import FlagReranker

reranker = FlagReranker('BAAI/bge-reranker-large', use_fp16=True)

def rerank(query, documents):
    pairs = [[query, doc] for doc in documents]
    scores = reranker.compute_score(pairs)
    
    # 直接返回排序结果
    results = list(zip(documents, scores))
    results.sort(key=lambda x: x[1], reverse=True)
    return results[:10]

# 问题3:如何优化Reranker的性能?

预期答案:

Cross-Encoder vs Bi-Encoder:

维度 Bi-Encoder Cross-Encoder
编码方式 Query和Doc分别编码 Query和Doc联合编码
计算速度 快(可预计算) 慢(需实时计算)
精度 较低 较高
交互能力 无深层交互 深层语义交互
适用场景 粗排(召回) 精排(重排)

代码问题:

  1. 没有批量处理优化
  2. 没有缓存机制
  3. 没有处理长文档截断

性能优化:

python 复制代码
from FlagEmbedding import FlagReranker
from functools import lru_cache
import hashlib

class OptimizedReranker:
    def __init__(self, model_name='BAAI/bge-reranker-large', batch_size=32):
        self.reranker = FlagReranker(model_name, use_fp16=True)
        self.batch_size = batch_size
        self.cache = {}
    
    def _get_cache_key(self, query: str, doc: str) -> str:
        """生成缓存Key"""
        content = f"{query}|||{doc}"
        return hashlib.md5(content.encode()).hexdigest()
    
    def rerank(
        self,
        query: str,
        documents: List[str],
        top_k: int = 10,
        use_cache: bool = True
    ) -> List[Tuple[str, float]]:
        """
        优化后的重排
        
        优化点:
        1. 批量处理
        2. 缓存机制
        3. 长文档截断
        """
        results = []
        uncached_docs = []
        uncached_indices = []
        
        # 1. 检查缓存
        for i, doc in enumerate(documents):
            if use_cache:
                cache_key = self._get_cache_key(query, doc)
                if cache_key in self.cache:
                    results.append((doc, self.cache[cache_key], i))
                    continue
            
            uncached_docs.append(doc)
            uncached_indices.append(i)
        
        # 2. 批量处理未缓存的文档
        if uncached_docs:
            # 截断长文档
            max_length = 512
            truncated_docs = [
                doc[:max_length] if len(doc) > max_length else doc
                for doc in uncached_docs
            ]
            
            # 批量推理
            all_scores = []
            for i in range(0, len(truncated_docs), self.batch_size):
                batch_docs = truncated_docs[i:i + self.batch_size]
                pairs = [[query, doc] for doc in batch_docs]
                batch_scores = self.reranker.compute_score(
                    pairs,
                    normalize=True
                )
                all_scores.extend(batch_scores)
            
            # 更新结果和缓存
            for doc, score, idx in zip(uncached_docs, all_scores, uncached_indices):
                results.append((doc, score, idx))
                
                if use_cache:
                    cache_key = self._get_cache_key(query, doc)
                    self.cache[cache_key] = score
        
        # 3. 排序并返回
        results.sort(key=lambda x: x[1], reverse=True)
        
        return [(doc, score) for doc, score, _ in results[:top_k]]

追问:

  1. ColBERT的Late Interaction机制是什么?
  2. 如何选择Reranker模型?
  3. Reranker的延迟如何优化?

五、知识图谱深度拷打

5.1 图谱构建(难度:⭐⭐⭐⭐)

Q11: 你的简历提到"知识图谱推理、学习路径优化",请回答:

python 复制代码
# 问题1:请解释你构建的教育知识图谱包含哪些实体和关系?

# 问题2:以下Cypher查询有什么问题?
MATCH (s:Student)-[:ANSWERED]->(q:Question)-[:BELONGS_TO]->(kp:KnowledgePoint)
WHERE s.id = $student_id
RETURN kp.name, COUNT(q) as question_count
ORDER BY question_count DESC

# 问题3:如何实现"查找学生的知识薄弱点"?

预期答案:

教育知识图谱实体和关系:

复制代码
实体类型:
- Student(学生)
- Question(题目)
- KnowledgePoint(知识点)
- Chapter(章节)
- Subject(学科)

关系类型:
- BELONGS_TO(属于)
- PREREQUISITE(前置依赖)
- RELATED_TO(相关)
- MASTERED(掌握)
- WEAK_IN(薄弱)

Cypher查询问题:

  1. 没有考虑正确率
  2. 没有过滤时间范围
  3. 性能可能有问题(缺少索引)

查找知识薄弱点:

cypher 复制代码
// 查询学生的薄弱知识点(正确率低于60%)
MATCH (s:Student {id: $student_id})-[a:ANSWERED]->(q:Question)-[:BELONGS_TO]->(kp:KnowledgePoint)
WITH kp, 
     COUNT(q) as total_questions,
     SUM(CASE WHEN a.is_correct THEN 1 ELSE 0 END) as correct_count,
     SUM(CASE WHEN a.is_correct THEN 1 ELSE 0 END) * 100.0 / COUNT(q) as accuracy_rate
WHERE accuracy_rate < 60
RETURN kp.name as knowledge_point,
       total_questions,
       correct_count,
       accuracy_rate
ORDER BY accuracy_rate ASC
LIMIT 10;

追问:

  1. 如何实现学习路径推荐?
  2. 知识图谱和向量数据库如何结合?
  3. GraphRAG是什么?

5.2 图算法(难度:⭐⭐⭐⭐⭐)

Q12: 你的简历提到"图论、拓扑排序",请回答:

python 复制代码
# 问题1:拓扑排序在教育场景中有什么应用?

# 问题2:请实现一个基于拓扑排序的学习路径推荐算法

# 问题3:以下代码有什么问题?
from collections import defaultdict, deque

def topological_sort(graph):
    in_degree = defaultdict(int)
    for node in graph:
        for neighbor in graph[node]:
            in_degree[neighbor] += 1
    
    queue = deque([node for node in graph if in_degree[node] == 0])
    result = []
    
    while queue:
        node = queue.popleft()
        result.append(node)
        for neighbor in graph[node]:
            in_degree[neighbor] -= 1
            if in_degree[neighbor] == 0:
                queue.append(neighbor)
    
    return result

预期答案:

拓扑排序应用:

  • 学习路径规划:确保先学前置知识点
  • 课程安排:避免循环依赖
  • 任务调度:确定执行顺序

学习路径推荐算法:

python 复制代码
from collections import defaultdict, deque
from typing import List, Dict, Set

class LearningPathRecommender:
    """
    基于拓扑排序的学习路径推荐
    
    考虑因素:
    1. 知识点依赖关系
    2. 学生已掌握的知识点
    3. 学生薄弱的知识点
    """
    
    def __init__(self, knowledge_graph):
        self.graph = knowledge_graph
    
    def recommend_path(
        self,
        target_knowledge_point: str,
        mastered_points: Set[str],
        weak_points: Set[str]
    ) -> List[str]:
        """
        推荐学习路径
        
        Args:
            target_knowledge_point: 目标知识点
            mastered_points: 已掌握的知识点
            weak_points: 薄弱的知识点
        
        Returns:
            学习路径(知识点列表)
        """
        # 1. 获取目标知识点的所有前置依赖
        prerequisites = self._get_all_prerequisites(target_knowledge_point)
        
        # 2. 过滤已掌握的知识点
        to_learn = prerequisites - mastered_points
        
        # 3. 构建子图
        subgraph = self._build_subgraph(to_learn)
        
        # 4. 拓扑排序
        sorted_path = self._topological_sort_with_priority(
            subgraph,
            weak_points
        )
        
        return sorted_path
    
    def _get_all_prerequisites(self, kp: str) -> Set[str]:
        """获取所有前置知识点(递归)"""
        visited = set()
        self._dfs_prerequisites(kp, visited)
        return visited
    
    def _dfs_prerequisites(self, kp: str, visited: Set[str]):
        """DFS遍历前置知识点"""
        if kp in visited:
            return
        
        visited.add(kp)
        
        # 获取直接前置知识点
        direct_prereqs = self.graph.get_prerequisites(kp)
        
        for prereq in direct_prereqs:
            self._dfs_prerequisites(prereq, visited)
    
    def _build_subgraph(self, nodes: Set[str]) -> Dict[str, List[str]]:
        """构建子图"""
        subgraph = defaultdict(list)
        
        for node in nodes:
            prereqs = self.graph.get_prerequisites(node)
            for prereq in prereqs:
                if prereq in nodes:
                    subgraph[prereq].append(node)
        
        return subgraph
    
    def _topological_sort_with_priority(
        self,
        graph: Dict[str, List[str]],
        priority_nodes: Set[str]
    ) -> List[str]:
        """
        带优先级的拓扑排序
        
        薄弱知识点优先学习
        """
        in_degree = defaultdict(int)
        for node in graph:
            for neighbor in graph[node]:
                in_degree[neighbor] += 1
        
        # 使用优先队列,薄弱知识点优先
        import heapq
        queue = []
        for node in graph:
            if in_degree[node] == 0:
                # 薄弱知识点优先级更高(负数)
                priority = -1 if node in priority_nodes else 0
                heapq.heappush(queue, (priority, node))
        
        result = []
        while queue:
            _, node = heapq.heappop(queue)
            result.append(node)
            
            for neighbor in graph[node]:
                in_degree[neighbor] -= 1
                if in_degree[neighbor] == 0:
                    priority = -1 if neighbor in priority_nodes else 0
                    heapq.heappush(queue, (priority, neighbor))
        
        return result

代码问题:

  1. 没有处理环的情况
  2. 没有处理孤立节点
  3. 没有返回所有节点(如果存在环)

追问:

  1. 如何检测知识图谱中的循环依赖?
  2. 如何处理大规模知识图谱的性能问题?
  3. PageRank算法在知识图谱中有什么应用?

六、大模型选型与认知

6.1 模型选型(难度:⭐⭐⭐⭐)

Q13: 你的简历提到"多模型路由",请回答:

python 复制代码
# 问题1:在RAG系统中,如何选择合适的Embedding模型?

# 问题2:以下模型有什么区别?适用什么场景?
# - GPT-4
# - GPT-3.5-turbo
# - Claude-3
# - 文心一言
# - 通义千问
# - DeepSeek

# 问题3:如何设计一个多模型路由系统?

预期答案:

Embedding模型选型:

模型 维度 特点 适用场景
BGE-large-zh 1024 中文效果好 中文RAG
OpenAI text-embedding-3 1536/3072 多语言 多语言场景
Cohere embed-v3 1024 检索优化 检索密集型

LLM模型对比:

模型 优势 劣势 适用场景
GPT-4 推理能力强 价格高 复杂推理
GPT-3.5-turbo 性价比高 推理弱 简单任务
Claude-3 长上下文 中文弱 长文档
文心一言 中文好 推理弱 中文场景
DeepSeek 性价比高 生态弱 成本敏感

多模型路由设计:

python 复制代码
from typing import Dict, Any
from enum import Enum
import time

class TaskType(Enum):
    SIMPLE_QA = "simple_qa"
    COMPLEX_REASONING = "complex_reasoning"
    LONG_CONTEXT = "long_context"
    CODE_GENERATION = "code_generation"

class ModelRouter:
    """
    多模型路由系统
    
    路由策略:
    1. 根据任务类型选择模型
    2. 根据成本预算选择模型
    3. 根据延迟要求选择模型
    """
    
    def __init__(self):
        self.models = {
            "gpt-4": {"cost": 0.03, "latency": 2.0, "capability": 0.95},
            "gpt-3.5-turbo": {"cost": 0.002, "latency": 0.5, "capability": 0.7},
            "claude-3": {"cost": 0.015, "latency": 1.5, "capability": 0.9},
            "deepseek": {"cost": 0.001, "latency": 0.8, "capability": 0.75},
        }
        
        self.task_model_mapping = {
            TaskType.SIMPLE_QA: ["gpt-3.5-turbo", "deepseek"],
            TaskType.COMPLEX_REASONING: ["gpt-4", "claude-3"],
            TaskType.LONG_CONTEXT: ["claude-3"],
            TaskType.CODE_GENERATION: ["gpt-4", "deepseek"],
        }
    
    def route(
        self,
        task_type: TaskType,
        prompt: str,
        max_cost: float = None,
        max_latency: float = None
    ) -> str:
        """
        路由到最合适的模型
        
        Args:
            task_type: 任务类型
            prompt: 输入提示
            max_cost: 最大成本限制
            max_latency: 最大延迟限制
        
        Returns:
            模型名称
        """
        candidates = self.task_model_mapping.get(task_type, ["gpt-3.5-turbo"])
        
        # 过滤满足约束的模型
        valid_models = []
        for model_name in candidates:
            model_info = self.models[model_name]
            
            if max_cost and model_info["cost"] > max_cost:
                continue
            if max_latency and model_info["latency"] > max_latency:
                continue
            
            valid_models.append((model_name, model_info))
        
        if not valid_models:
            # 没有满足约束的模型,选择最便宜的
            return min(self.models.items(), key=lambda x: x[1]["cost"])[0]
        
        # 选择能力最强的模型
        valid_models.sort(key=lambda x: x[1]["capability"], reverse=True)
        
        return valid_models[0][0]
    
    def invoke(
        self,
        task_type: TaskType,
        prompt: str,
        **kwargs
    ) -> str:
        """执行推理"""
        model_name = self.route(task_type, prompt, **kwargs)
        
        # 调用对应模型
        # ...
        
        return model_name

追问:

  1. 如何评估不同模型在特定任务上的效果?
  2. 如何实现模型的A/B测试?
  3. 如何处理模型调用失败的情况?

6.2 Prompt工程(难度:⭐⭐⭐)

Q14: 请优化以下Prompt:

python 复制代码
# 原始Prompt
prompt = "请回答以下问题:" + question

# 问题:
# 1. 这个Prompt有什么问题?
# 2. 如何优化?
# 3. 如何评估Prompt效果?

预期答案:

问题分析:

  1. 没有角色定义
  2. 没有上下文
  3. 没有输出格式要求
  4. 没有约束条件

优化方案:

python 复制代码
# 优化后的Prompt
prompt = """你是一个专业的AI教育助手,擅长解答学生的学习问题。

## 任务
请回答学生的以下问题。

## 要求
1. 回答要准确、详细、有条理
2. 如果涉及知识点,请解释清楚
3. 如果问题不明确,请追问澄清
4. 回答长度控制在200-500字

## 输出格式
【核心答案】
...

【知识点】
...

【拓展建议】
...

## 学生问题
{question}

## 请回答:"""

Prompt评估方法:

python 复制代码
def evaluate_prompt(prompt_template, test_cases):
    """
    评估Prompt效果
    
    Args:
        prompt_template: Prompt模板
        test_cases: 测试用例 [{"question": ..., "expected": ...}, ...]
    """
    results = []
    
    for case in test_cases:
        prompt = prompt_template.format(question=case["question"])
        response = llm.invoke(prompt)
        
        # 评估
        score = evaluate_response(response, case["expected"])
        
        results.append({
            "question": case["question"],
            "response": response,
            "expected": case["expected"],
            "score": score
        })
    
    return {
        "avg_score": sum(r["score"] for r in results) / len(results),
        "details": results
    }

七、机器视觉与机器学习

7.1 机器视觉(难度:⭐⭐⭐)

Q15: 你的简历提到"PaddleOCR + NLP",请回答:

python 复制代码
# 问题1:OCR在简历解析中有什么难点?

# 问题2:如何提高OCR的准确率?

# 问题3:以下代码有什么问题?
from paddleocr import PaddleOCR

ocr = PaddleOCR(use_angle_cls=True, lang='ch')

def parse_resume(image_path):
    result = ocr.ocr(image_path, cls=True)
    
    # 直接返回所有文本
    text = ""
    for line in result:
        text += line[1][0] + "\n"
    
    return text

预期答案:

OCR难点:

  1. 排版复杂:表格、多栏
  2. 字体多样:手写、艺术字
  3. 图像质量:模糊、倾斜、水印
  4. 结构化提取:需要理解语义

提高准确率:

  1. 图像预处理:去噪、矫正
  2. 模型优化:微调、集成
  3. 后处理:纠错、规则修正

代码问题:

  1. 没有图像预处理
  2. 没有结构化提取
  3. 没有错误处理

优化方案:

python 复制代码
from paddleocr import PaddleOCR
import cv2
import numpy as np

class ResumeOCRParser:
    def __init__(self):
        self.ocr = PaddleOCR(
            use_angle_cls=True,
            lang='ch',
            use_gpu=True
        )
    
    def preprocess(self, image_path: str) -> np.ndarray:
        """图像预处理"""
        img = cv2.imread(image_path)
        
        # 1. 灰度化
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        
        # 2. 去噪
        denoised = cv2.fastNlMeansDenoising(gray)
        
        # 3. 二值化
        _, binary = cv2.threshold(
            denoised, 0, 255,
            cv2.THRESH_BINARY + cv2.THRESH_OTSU
        )
        
        # 4. 倾斜矫正
        coords = np.column_stack(np.where(binary > 0))
        angle = cv2.minAreaRect(coords)[-1]
        
        if angle < -45:
            angle = -(90 + angle)
        else:
            angle = -angle
        
        (h, w) = binary.shape[:2]
        center = (w // 2, h // 2)
        M = cv2.getRotationMatrix2D(center, angle, 1.0)
        rotated = cv2.warpAffine(
            binary, M, (w, h),
            flags=cv2.INTER_CUBIC,
            borderMode=cv2.BORDER_REPLICATE
        )
        
        return rotated
    
    def parse(self, image_path: str) -> dict:
        """解析简历"""
        # 1. 预处理
        processed_img = self.preprocess(image_path)
        
        # 2. OCR识别
        result = self.ocr.ocr(processed_img, cls=True)
        
        # 3. 结构化提取
        text_blocks = []
        for line in result:
            for word_info in line:
                text_blocks.append({
                    "text": word_info[1][0],
                    "confidence": word_info[1][1],
                    "position": word_info[0]
                })
        
        # 4. 按位置排序
        text_blocks.sort(key=lambda x: (x["position"][0][1], x["position"][0][0]))
        
        # 5. 提取结构化信息
        structured_info = self._extract_structured_info(text_blocks)
        
        return structured_info
    
    def _extract_structured_info(self, text_blocks: list) -> dict:
        """提取结构化信息"""
        # 使用规则或LLM提取
        # ...
        pass

追问:

  1. 如何处理表格识别?
  2. 如何处理手写内容?
  3. OCR和LLM如何结合?

7.2 机器学习基础(难度:⭐⭐⭐)

Q16: 请回答以下机器学习问题:

python 复制代码
# 问题1:什么是过拟合?如何防止?

# 问题2:请解释以下评估指标:
# - Precision
# - Recall
# - F1-Score
# - AUC-ROC

# 问题3:在推荐系统中,如何评估模型效果?

预期答案:

过拟合与防止:

方法 说明
正则化 L1/L2正则化
Dropout 随机丢弃神经元
早停 监控验证集
数据增强 增加训练数据
交叉验证 K折交叉验证

评估指标:

python 复制代码
# Precision = TP / (TP + FP)
# Recall = TP / (TP + FN)
# F1 = 2 * (Precision * Recall) / (Precision + Recall)

def calculate_metrics(y_true, y_pred, y_prob):
    from sklearn.metrics import (
        precision_score,
        recall_score,
        f1_score,
        roc_auc_score
    )
    
    return {
        "precision": precision_score(y_true, y_pred),
        "recall": recall_score(y_true, y_pred),
        "f1": f1_score(y_true, y_pred),
        "auc_roc": roc_auc_score(y_true, y_prob)
    }

推荐系统评估:

python 复制代码
# 离线指标
- Hit Rate @ K
- NDCG @ K
- MAP @ K

# 在线指标
- 点击率(CTR)
- 转化率(CVR)
- 用户停留时间

八、Git与部署

8.1 Git工作流(难度:⭐⭐)

Q17: 请回答以下Git问题:

bash 复制代码
# 问题1:以下Git工作流有什么问题?
git add .
git commit -m "update"
git push origin main

# 问题2:如何处理合并冲突?

# 问题3:如何回滚到上一个版本?

预期答案:

Git工作流问题:

  1. git add . 可能添加不需要的文件
  2. commit message不规范
  3. 直接push到main分支

规范工作流:

bash 复制代码
# 1. 创建功能分支
git checkout -b feature/rag-optimization

# 2. 添加文件(选择性)
git add src/rag/optimizer.py
git add tests/test_optimizer.py

# 3. 规范的commit message
git commit -m "feat(rag): add hybrid search with RRF fusion

- Implement RRF fusion for vector and keyword search
- Add configurable k parameter
- Add unit tests for fusion algorithm

Closes #123"

# 4. 推送到远程
git push origin feature/rag-optimization

# 5. 创建Pull Request

合并冲突处理:

bash 复制代码
# 1. 拉取最新代码
git fetch origin
git rebase origin/main

# 2. 解决冲突
# 编辑冲突文件,选择保留的内容

# 3. 标记解决
git add <conflicted_file>
git rebase --continue

# 4. 推送
git push origin feature/rag-optimization

回滚操作:

bash 复制代码
# 方式1:创建新commit回滚
git revert <commit_hash>

# 方式2:硬回滚(不推荐)
git reset --hard <commit_hash>
git push origin main --force

8.2 Docker部署(难度:⭐⭐⭐)

Q18: 请回答以下部署问题:

dockerfile 复制代码
# 问题1:以下Dockerfile有什么问题?
FROM python:3.9
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt
CMD ["python", "app.py"]

# 问题2:如何优化镜像大小?

# 问题3:如何实现零停机部署?

预期答案:

Dockerfile问题:

  1. 没有指定具体版本
  2. 没有利用缓存层
  3. 没有清理缓存
  4. 没有非root用户

优化方案:

dockerfile 复制代码
# 优化后的Dockerfile
FROM python:3.9-slim AS builder

WORKDIR /app

# 1. 先复制依赖文件,利用缓存
COPY requirements.txt .

# 2. 安装依赖
RUN pip install --no-cache-dir -r requirements.txt -t /app/libs

# 3. 第二阶段:运行镜像
FROM python:3.9-slim

WORKDIR /app

# 4. 复制依赖
COPY --from=builder /app/libs /app/libs
ENV PYTHONPATH=/app/libs

# 5. 复制应用代码
COPY src/ /app/src/

# 6. 创建非root用户
RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser

# 7. 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
  CMD curl -f http://localhost:8000/health || exit 1

# 8. 启动命令
CMD ["python", "-m", "uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]

零停机部署:

yaml 复制代码
# Kubernetes部署配置
apiVersion: apps/v1
kind: Deployment
metadata:
  name: rag-api
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    spec:
      containers:
      - name: rag-api
        image: rag-api:v2
        readinessProbe:
          httpGet:
            path: /health
            port: 8000
          initialDelaySeconds: 5
          periodSeconds: 10
        lifecycle:
          preStop:
            exec:
              command: ["/bin/sh", "-c", "sleep 10"]

九、综合面试评分表

评分标准

维度 权重 评分标准
Python基础 15% 异步、装饰器、内存管理
SQL/数据库 15% 查询优化、向量数据库
LangChain/LangGraph 20% 核心概念、状态机设计
RAG系统 25% 架构设计、优化策略
知识图谱 10% 图算法、业务应用
大模型认知 10% 模型选型、Prompt工程
工程能力 5% Git、Docker、部署

预期评分

维度 预期得分 理由
Python基础 70-80分 简历未体现深度,需验证
SQL/数据库 75-85分 有Milvus经验
LangChain/LangGraph 80-90分 简历提到状态机设计
RAG系统 85-95分 核心能力,简历详细
知识图谱 75-85分 有应用经验
大模型认知 70-80分 需验证选型能力
工程能力 70-80分 简历未体现

十、面试官总结

优势

  1. 技术栈前沿:RAG、LangGraph、知识图谱都是热门方向
  2. 项目落地:有政务级项目经验
  3. 学习能力:连续三年国奖,证明学习能力强

需验证点

  1. 技术深度:简历描述是否真实?
  2. 独立能力:是主导还是参与?
  3. 问题解决:遇到困难如何解决?

建议面试流程

  1. 一面:Python基础 + SQL + 简历项目深挖
  2. 二面:RAG架构设计 + LangGraph实战
  3. 三面:系统设计 + 综合评估

文档版本 : v1.0
生成时间 : 2024年
面试官: 资深AI应用开发工程师

相关推荐
198******1263442 分钟前
2026深度解读:Work Agent长程任务的技术机制与落地能力
人工智能
爱倒腾的老唐1 小时前
0、元器件——电阻
学习
老金带你玩AI1 小时前
别只让AI解释,让它做个你能看懂的东西
人工智能
iffy11 小时前
Deepseek hardness 桌面版 + ollama
人工智能
Xiaofeng36932 小时前
知漫剧入门科普:一站式工作台一句话生成漫剧
人工智能
“AI国潮设计-小江”2 小时前
《Python+SDXL实战:用ControlNet精准控制“英歌舞翻糖吐司”构图,附批量生成脚本》
开发语言·人工智能·python·prompt·aigc
sunshine22 girl2 小时前
Java学习七 Java 项目实战2-注册业务
java·学习
丑陋小蚊子2 小时前
邮件投简历,正文和附件各写什么?
面试·求职招聘·大学生·简历·简历下载
MobotStone2 小时前
万物皆插件:DeepSeek Harness 底层揭秘,脚手架如何蜕变为产品
人工智能
youdexiang3 小时前
Android录音软件时间关键词检索功能分析
java·人工智能