agent_proj/docs/INTERVIEW_PREP.md

9.6 KiB
Raw Blame History

面试知识点总结

基于本项目的技术栈,总结面试常问知识点


一、LangChain 核心知识点

1.1 LangChain Agent 机制

Q1: 什么是 LangChain Agent? 它与 Chain 的区别?

特性 Chain Agent
执行方式 固定流程,按顺序执行 动态决策,自主选择下一步
工具调用 预定义,需手动编写 自动判断是否调用工具
适用场景 固定流程 (如 RAG) 需要推理和决策的场景

项目中的应用:

# 使用 create_agent 创建 Agent
agent = create_agent(
    model=ChatTongyi(model="qwen3-max"),
    tools=[rag_summarize, get_weather, get_user_id],
    system_prompt="你是一个智能助手"
)

Q2: 解释 ReAct 框架在 Agent 中的应用

ReAct = Reasoning + Acting

Thought: 我需要知道用户所在城市的天气
Action: get_weather
Action Input: 深圳
Observation: 天气晴朗26°C
Thought: 现在我有天气信息,可以回答用户问题了

项目中的体现:

  • 系统提示词明确要求遵循 ReAct 框架
  • Agent 自动完成 "思考→行动→观察→再思考" 循环
  • 最多支持 5 次工具调用

1.2 LangChain Middleware 中间件机制

Q3: LangChain 中间件是什么? 如何实现?

LangChain 中间件是 LangGraph 提供的钩子机制,用于在 Agent 执行过程中注入自定义逻辑。

项目中的三种中间件:

  1. wrap_tool_call - 工具调用包装
@wrap_tool_call
def monitor_tool(request, handler):
    # 工具调用前
    logger.info(f"执行工具: {request.tool_call['name']}")
    result = handler(request)  # 调用实际工具
    # 工具调用后
    return result
  1. before_model - 模型调用前
@before_model
def log_before_model(state, runtime):
    # 每次调用 LLM 前记录日志
    logger.info(f"即将调用模型,当前有 {len(state['messages'])} 条消息")
    return None
  1. dynamic_prompt - 动态提示词
@dynamic_prompt
def report_prompt_switch(request):
    is_report = request.runtime.context.get("report", False)
    if is_report:
        return load_report_prompts()  # 切换为报告生成 Prompt
    return load_system_prompts()

Q4: 中间件的应用场景有哪些?

场景 中间件类型
日志记录 before_model, wrap_tool_call
性能监控 wrap_tool_call
动态提示词 dynamic_prompt
权限控制 wrap_tool_call
结果缓存 wrap_tool_call

二、RAG (检索增强生成)

2.1 RAG 核心流程

用户 query
    │
    ▼
┌─────────────────┐
│  向量检索       │ ← Chroma Retriever
└────────┬────────┘
         │ 返回 Top-K 相关文档
         ▼
┌─────────────────┐
│  构建 Prompt    │ ← 拼接 query + context
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  LLM 生成回答   │ ← 通义千问
└────────┬────────┘
         │
         ▼
    返回结果

2.2 关键面试题

Q5: 为什么需要 RAG? 直接用 LLM 不可以吗?

方案 优点 缺点
直接 LLM 简单 知识截止、可能幻觉、无法访问私域知识
RAG 实时性、可引用私域知识、减少幻觉 增加复杂度、依赖检索质量

Q6: RAG 中的关键环节有哪些?

  1. 文档加载: PyPDFLoader, TextLoader
  2. 文本分片: RecursiveCharacterTextSplitter
    • chunk_size: 每片大小
    • chunk_overlap: 重叠区域 (保持上下文)
    • separators: 分隔符优先级
  3. 向量化: Embedding 模型
  4. 向量存储: Chroma
  5. 相似度检索: Cosine similarity, Top-K
  6. 结果拼接: Query + Context → Prompt

Q7: 如何优化 RAG 效果?

优化方向 方法
检索质量 调整 chunk_size、overlap、使用 better embedding
召回率 增加 Top-K、使用混合检索
排序rerank 使用 rerank 模型二次排序
Prompt 工程 优化 RAG Prompt 模板

2.3 项目中的 RAG 实现

# rag_service.py
class RagSummarizeService:
    def rag_summarize(self, query: str) -> str:
        # 1. 向量检索
        context_docs = self.retriever.invoke(query)
        
        # 2. 拼接上下文
        context = ""
        for doc in context_docs:
            context += f"[参考资料]: {doc.page_content} | {doc.metadata}\n"
        
        # 3. 调用 LLM 生成
        return self.chain.invoke({
            "input": query,
            "context": context
        })

三、向量数据库 (Chroma)

3.1 Chroma 核心概念

概念 说明
Collection 类似表,存储一类文档
Embedding Function 向量化函数
Document 包含 page_content 和 metadata
Retriever 检索器,支持相似度搜索

3.2 面试题

Q8: Chroma 的优势?

  • 轻量级,易于部署
  • Python 原生,集成方便
  • 支持持久化存储
  • 与 LangChain 无缝集成

Q9: Chroma 与其他向量库 (Milvus/Pinecone) 的区别?

特性 Chroma Milvus Pinecone
部署方式 本地/嵌入 服务端 SaaS
规模 中小 大规模 大规模
成熟度 一般

四、Streamlit Web 开发

4.1 项目中的应用

# app.py
import streamlit as st

st.title("智扫通智能机器人客服")

# 会话状态管理
if "agent" not in st.session_state:
    st.session_state["agent"] = ReactAgent()

# 聊天记录
for message in st.session_state["message"]:
    st.chat_message(message["role"]).write(message["content"])

# 用户输入
prompt = st.chat_input()

# 流式输出
if prompt:
    stream = agent.excute_stream(prompt)
    st.chat_message("assistant").write_stream(capture(stream))

4.2 关键面试题

Q10: Streamlit 的核心机制?

  • 声明式 UI: 通过 st.xxx() 声明组件
  • 脚本式: 每次交互重新运行整个脚本
  • 状态管理: session_state 保持会话状态
  • 热重载: 修改代码自动刷新

Q11: 如何实现流式输出?

# 方法1: write_stream
st.write_stream(generator_function)

# 方法2: 自定义 (本项目使用)
def capture(generator, cache_list):
    for chunk in generator:
        cache_list.append(chunk)
        for char in chunk:
            time.sleep(0.05)  # 模拟打字效果
            yield char

五、设计模式

5.1 工厂模式 (Factory Pattern)

# model/factory.py
class BaseModelFactory(ABC):
    @abstractmethod
    def generator(self) -> Optional[Embeddings | BaseChatModel]:
        pass

class ChatModelFactory(BaseModelFactory):
    def generator(self) -> Optional[BaseChatModel]:
        return ChatTongyi(model=rag_conf["chat_model_name"])

class EmbeddingsFactory(BaseModelFactory):
    def generator(self) -> Optional[Embeddings]:
        return DashScopeEmbeddings(model=rag_conf["embeddings_model_name"])

好处: 统一创建入口,便于后续更换模型

5.2 配置中心化

所有配置集中在 config/ 目录的 YAML 文件中:

  • rag.yaml: 模型配置
  • chroma.yaml: 向量库配置
  • agent.yaml: Agent 配置
  • prompts.yaml: Prompt 路径配置

5.3 中间件模式

利用装饰器和钩子实现横切关注点:

  • 日志
  • 监控
  • 动态提示词切换

六、工程实践

6.1 日志管理

# utils/logger_handler.py
def get_logger(name: str = "Agent"):
    logger = logging.getLogger(name)
    # 双写: 控制台 + 文件
    console_handler = logging.StreamHandler()
    file_handler = logging.FileHandler(log_file, encoding='utf-8')
    return logger

6.2 路径管理

# utils/path_tool.py
def get_project_root():
    cur_file = os.path.abspath(__file__)
    proj_dir = os.path.dirname(os.path.dirname(cur_file))
    return proj_dir

def get_abs_path(relative_path):
    return os.path.join(get_project_root(), relative_path)

6.3 MD5 去重

# vector_store.py
# 通过 MD5 避免重复加载文档
md5_hex = get_file_md5_hex(path)
if check_md5_hex(md5_hex):
    continue  # 已存在,跳过
self.vector_store.add_documents(split_doc)
save_md5(md5_hex)

七、LLM & Embedding

7.1 通义千问 (Qwen)

  • 模型: qwen3-max (阿里云)
  • 调用: langchain_community.chat_models.tongyi.ChatTongyi

7.2 DashScope Embedding

  • 模型: text-embedding-v4
  • 用途: 将文本转为向量
  • 调用: langchain_community.embeddings.DashScopeEmbeddings

八、常见面试扩展问题

Q12: 如何保证 Agent 工具调用的安全性?

  1. 工具入参校验: Pydantic 类型提示
  2. 调用次数限制: 最多 N 次
  3. 工具白名单: 只允许调用指定工具
  4. 中间件监控: 记录所有调用

Q13: Agent 如何实现"思考过程"可视化?

# 项目中的实现
for chunk in res:
    last_msg = chunk["messages"][-1]
    if last_msg.tool_calls:
        # 显示思考过程
        print(f"工具调用: {last_msg.tool_calls}")

Q14: RAG 召回率低怎么排查?

  1. 检查 chunk_size 是否合适
  2. 检查 embedding 质量
  3. 查看检索到的文档是否相关
  4. 调整 Top-K 值
  5. 检查分片是否有意义 (避免切断句子)

Q15: 生产环境部署注意什么?

  1. API 限流: 添加重试机制
  2. 错误处理: 工具调用失败处理
  3. 日志: 完整日志记录
  4. 监控: 调用次数、耗时监控
  5. 缓存: 热门 query 缓存结果