【AI Agent开发】相关知识整理
【AI Agent开发】相关知识整理
- 1. AI Agent 核心概念与架构
- 2. Agent 设计范式
- 3. Agent 记忆系统
- 4. 多Agent协作
- 5. RAG 检索增强生成
- 6. LLM 工具调用体系
- 7. MCP 模型上下文协议
- 8. LangChain 框架核心
- 9. 大模型工程基础
- 10. SSE 协议
- 11. 后端基础设施
1. AI Agent 核心概念与架构
AI Agent是2026年最热门的技术方向,也是面试必问的核心模块。
一、基础概念
1. 什么是AI Agent?与普通大模型调用有什么本质区别?
AI Agent定义:能够自主感知环境、做出决策、执行动作并通过反馈迭代优化的智能体。它是大模型的"执行器",让大模型从"只能聊天"变成"能真正做事"。
本质区别:
| 对比项 | 普通大模型调用 | AI Agent |
|---|---|---|
| 交互模式 | 单轮问答,一问一答 | 多轮自主循环,不需要人类干预 |
| 能力边界 | 只能输出文本 | 可以调用工具、操作文件、执行命令、访问网络 |
| 决策能力 | 被动响应,没有自主决策 | 主动规划、拆分任务、选择工具、反思优化 |
| 记忆能力 | 无状态,每次调用都是新的 | 有短期和长期记忆,能记住历史对话和执行结果 |
| 目标导向 | 完成单次请求 | 为了达成一个复杂目标,自主执行多步操作 |
2. Agent的基本架构由哪些核心组件构成?
一个完整的Agent架构包含以下6个核心组件:
- LLM大脑:Agent的核心,负责思考、决策、规划、生成文本
- 工具集:Agent可以调用的外部能力(搜索、文件、命令、数据库等)
- 记忆系统:存储历史对话、执行结果、长期知识
- 规划器:将复杂目标拆分为多个可执行的子任务
- 执行器:调用工具执行具体的动作
- 反思器:评估执行结果,调整计划,优化后续步骤
3. Workflow、Agent、Tools三个概念的区别是什么?
| 概念 | 定义 | 特点 | 适用场景 |
|---|---|---|---|
| Tools | 单一的原子能力,完成一个具体的动作 | 无状态、无决策能力、输入输出明确 | 读取文件、搜索网页、执行命令 |
| Workflow | 固定的、预定义的步骤流程 | 顺序执行、没有自主决策、流程不可变 | 数据处理流水线、自动化脚本 |
| Agent | 自主决策的智能体,能够动态调整执行路径 | 有状态、有决策能力、可以根据结果调整计划 | 复杂任务、不确定流程、需要推理的场景 |
核心区别:Workflow是"人告诉它怎么做",Agent是"人告诉它做什么,它自己想怎么做"。
二、核心能力
4. Agent的核心能力有哪些?
- 工具调用能力:能够识别何时需要调用工具,以及如何正确调用工具
- 任务规划能力:将复杂的目标拆分为多个可执行的子任务
- 记忆能力:记住历史对话、执行结果和长期知识
- 反思优化能力:评估执行结果,发现问题并调整计划
- 多轮迭代能力:能够自主执行多步操作,直到达成目标
5. 为什么Agent需要工具调用能力?
大模型本身存在以下局限性:
- 知识截止日期:无法获取训练数据之后的最新信息
- 实时数据:无法获取实时的天气、股票、新闻等数据
- 计算能力:数学计算、逻辑推理能力弱
- 操作能力:无法操作外部系统、文件、数据库
工具调用能力让Agent突破了这些局限性,能够连接到真实世界,完成各种实际任务。
2. Agent 设计范式
Agent设计范式是面试最高频的考点,必须掌握三种主流范式的区别和选型。
一、主流设计范式
1. ReAct 范式(最基础、最常用)
核心思想:Reasoning + Acting,将推理和动作交替进行。
执行流程:
- 思考(Thought):我现在需要做什么
- 动作(Action):调用某个工具,传入参数
- 观察(Observation):获取工具执行的结果
- 重复以上步骤,直到达成目标
优点:简单易懂、容易实现、适合大多数场景
缺点:容易陷入循环、规划能力弱、不适合复杂任务
2. Plan-and-Execute 范式
核心思想:先制定完整的计划,再分步骤执行。
执行流程:
- 规划(Plan):将目标拆分为多个步骤的执行计划
- 执行(Execute):按照计划逐个执行步骤
- 重新规划(Replan):如果执行失败或遇到意外,重新调整计划
优点:规划能力强、适合复杂任务、执行效率高
缺点:实现复杂、灵活性不如ReAct
3. Reflection 范式
核心思想:在执行过程中加入反思步骤,评估结果并优化。
执行流程:
- 执行任务
- 反思(Reflection):评估执行结果是否符合预期
- 修正(Correction):如果有问题,修正错误并重新执行
- 总结(Summary):任务完成后总结经验教训
优点:准确率高、能够自我修正、适合需要高精度的任务
缺点:token消耗大、执行速度慢
二、范式对比与选型
4. ReAct、Plan-and-Execute、Reflection三种范式的核心区别是什么?
| 对比项 | ReAct | Plan-and-Execute | Reflection |
|---|---|---|---|
| 执行模式 | 边想边做 | 先想后做 | 做了再想 |
| 规划粒度 | 单步 | 多步 | 全程 |
| 灵活性 | 高 | 中 | 低 |
| 执行效率 | 中 | 高 | 低 |
| 准确率 | 中 | 中 | 高 |
| 实现难度 | 低 | 中 | 高 |
5. 实际项目中如何选择Agent设计范式?
- 简单任务:使用ReAct范式,实现简单,足够用
- 复杂任务:使用Plan-and-Execute范式,规划能力强
- 高精度任务:使用Reflection范式,准确率高
- 混合场景:组合使用,先用Plan-and-Execute制定计划,每个步骤用ReAct执行,关键步骤用Reflection验证
3. Agent 记忆系统
记忆系统是Agent区别于普通大模型的核心特征,也是面试必问的重点。
一、记忆分类
1. Agent的记忆系统分为哪几类?
| 记忆类型 | 定义 | 存储位置 | 生命周期 | 作用 |
|---|---|---|---|---|
| 短期记忆 | 当前会话的上下文 | 内存 | 会话期间 | 记住当前对话的历史和执行过程 |
| 长期记忆 | 跨会话的知识和经验 | 向量数据库 | 永久 | 记住用户的偏好、历史经验、通用知识 |
| 工作记忆 | 当前正在处理的信息 | 上下文窗口 | 单步执行 | 存储当前思考和决策所需的信息 |
二、短期记忆实现
2. 短期记忆的滑动窗口机制是如何实现的?
滑动窗口机制是为了解决大模型上下文窗口有限的问题:
- 当对话历史超过窗口大小时,自动删除最早的消息
- 保留最新的N条消息,确保总token数不超过上下文窗口限制
- 可以保护系统提示词和重要的工具执行结果不被删除
窗口大小:通常设置为上下文窗口的70%-80%,预留足够的空间给当前思考和生成内容。
3. 哪些消息需要被保护不被截断?
- 系统提示词(System Prompt)
- 工具定义(Tool Definitions)
- 重要的工具执行结果
- 用户的原始问题
- 最终的回答
三、长期记忆实现
4. 长期记忆的存储引擎是什么?
长期记忆通常使用向量数据库存储,如Chroma、Pinecone、Milvus等。
存储流程:
- 将记忆内容转换为向量(Embedding)
- 将向量和原始文本一起存入向量数据库
- 给每个记忆添加元数据(时间戳、类型、标签等)
5. 长期记忆的提取流程是怎样的?
- 将当前的问题或上下文转换为向量
- 在向量数据库中进行相似度搜索,找到最相关的N条记忆
- 对搜索结果进行重排,过滤掉不相关的内容
- 将提取到的记忆注入到当前的上下文中
6. 记忆提取时的评分公式是什么?
通常结合语义相似度和时间新鲜度进行评分:
最终得分 = 语义相似度 * 0.7 + 时间新鲜度 * 0.3
- 语义相似度:向量之间的余弦相似度,范围0-1
- 时间新鲜度:根据时间衰减函数计算,越新的记忆得分越高
四、记忆优化
7. Agent记忆压缩通常有哪些方法?
- 摘要压缩:使用大模型将多条历史消息总结为一条摘要
- 重要性过滤:只保留重要的记忆,删除无关紧要的内容
- 分块存储:将长记忆分成多个小块,只提取相关的块
- 分层记忆:将记忆分为不同的层次,常用的记忆放在上层,不常用的放在下层
4. 多Agent协作
多Agent是复杂系统的必然选择,也是面试中区分高级工程师的考点。
一、基础概念
1. 什么是Multi-Agent(多Agent)系统?
多Agent系统是指由多个相互协作的Agent组成的系统,每个Agent负责一个特定的角色或任务,通过通信和协作共同完成一个复杂的目标。
2. Single-Agent和Multi-Agent的设计方案有什么区别?
| 对比项 | Single-Agent | Multi-Agent |
|---|---|---|
| 复杂度 | 低 | 高 |
| 可扩展性 | 差 | 好 |
| 分工明确度 | 低 | 高 |
| 执行效率 | 中 | 高 |
| 调试难度 | 低 | 高 |
| 适用场景 | 简单任务、个人助理 | 复杂项目、团队协作、企业级应用 |
二、多Agent核心机制
3. 多Agent之间的通信方式有哪些?
- 消息传递:Agent之间直接发送消息进行通信,最常用的方式
- 共享状态:所有Agent共享一个全局的状态空间,通过读写状态进行通信
- 黑板模式:有一个公共的黑板,Agent将结果写在黑板上,其他Agent可以读取
4. 多Agent的路由机制有哪些?
- 静态路由:预定义好每个Agent的职责和调用顺序,固定不变
- 动态路由:由一个路由Agent根据当前的任务动态选择合适的Agent执行
- 混合路由:结合静态和动态路由,核心流程固定,细节动态调整
5. 常见的多Agent协作模式有哪些?
- 主从模式:一个主Agent负责规划和协调,多个从Agent负责执行具体任务
- 流水线模式:每个Agent负责一个环节,任务在Agent之间顺序传递
- 团队模式:多个Agent平等协作,共同讨论和决策
- 竞争模式:多个Agent同时解决同一个问题,选择最好的结果
5. RAG 检索增强生成
RAG是将外部知识接入大模型的最主流方案,几乎所有AI应用都会用到。
一、基础概念
1. 什么是RAG?详细描述一个完整RAG系统的工作流程。
RAG定义:Retrieval-Augmented Generation(检索增强生成),通过检索外部知识库的相关信息,将其注入到大模型的上下文中,让大模型能够生成基于外部知识的准确回答。
完整工作流程:
-
索引构建阶段:
- 文档加载:读取各种格式的文档(PDF、Word、Markdown等)
- 文档切割:将长文档切成多个小块(Chunk)
- 向量化:将每个Chunk转换为向量(Embedding)
- 存储:将向量和原始文本存入向量数据库
-
检索生成阶段:
- 查询向量化:将用户的问题转换为向量
- 向量检索:在向量数据库中搜索最相似的N个Chunk
- 上下文构建:将检索到的Chunk和用户的问题组合成提示词
- 大模型生成:大模型根据提示词生成回答
2. RAG主要用来解决大模型的哪些问题?
- 知识截止问题:大模型的知识截止到训练数据的日期,无法获取最新信息
- 幻觉问题:大模型会编造不存在的事实,RAG提供事实依据
- 私有知识问题:大模型无法获取企业内部的私有知识
- 长文本处理问题:大模型的上下文窗口有限,无法处理超长文档
3. RAG和微调的优劣势对比是什么?
| 对比项 | RAG | 微调 |
|---|---|---|
| 知识更新 | 实时更新,只需要更新知识库 | 需要重新训练,周期长、成本高 |
| 事实准确性 | 高,有明确的来源依据 | 中,仍然可能产生幻觉 |
| 实现成本 | 低,不需要训练数据 | 高,需要大量高质量的训练数据 |
| 部署成本 | 低,只需要向量数据库 | 高,需要GPU资源 |
| 知识规模 | 大,可以接入无限多的知识 | 小,受限于模型参数量 |
| 风格适配 | 差,只能改变内容,不能改变风格 | 好,可以改变模型的说话风格和行为 |
选型建议:
- 需要更新频繁的事实性知识 → 用RAG
- 需要改变模型的风格和行为 → 用微调
- 复杂场景 → RAG + 微调结合使用
二、索引构建
4. RAG中的文档切割(Chunking)策略有哪些?
- 固定长度切割:按固定的字符数或token数切割,最简单但效果最差
- 语义切割:根据语义段落切割,保留完整的语义单元
- 递归切割:先按大的分隔符切割,再递归切割成小块
- 重叠切割:相邻的Chunk之间有一定的重叠,避免语义被切断
- 结构化切割:根据文档的结构(标题、段落、列表)切割
5. 如何确定文档切割的粒度?
切割粒度的选择是RAG效果的关键:
- 粒度太小:每个Chunk的语义不完整,检索到的信息无法回答问题
- 粒度太大:包含太多无关信息,引入噪音,影响大模型生成
最佳实践:
- 通用场景:512-1024 token
- 问答场景:256-512 token
- 长文档场景:1024-2048 token
- 代码场景:1024-4096 token
6. 怎么规避语义被切割掉的问题?
- 重叠切割:相邻Chunk之间保留10%-20%的重叠
- 语义切割:使用语义切割工具,在语义边界处切割
- 父文档检索:先检索小块,再返回对应的父文档
- 上下文窗口扩展:检索到小块后,扩展前后的上下文
三、检索优化
7. 什么是Query Rewrite(查询改写)?目的是什么?
定义:使用大模型将用户的原始问题改写为更适合检索的查询语句。
目的:
- 解决用户表达不清晰、不完整的问题
- 将复杂问题拆分为多个简单问题
- 扩展同义词和相关词,提高召回率
- 消除歧义,明确查询意图
8. 什么是多路召回?具体怎么做?
定义:使用多种不同的检索方式,分别召回结果,然后合并去重。
常见的检索方式:
- 向量检索:基于语义相似度检索
- 关键词检索:基于BM25算法的全文检索
- 结构化检索:基于元数据的过滤检索
流程:
- 分别使用多种检索方式召回结果
- 对所有结果进行去重
- 使用重排模型对结果进行排序
- 选择Top-N结果作为最终的上下文
9. 了解哪些更复杂的RAG范式?
- Self-RAG:大模型自己决定是否需要检索,以及检索什么内容
- Corrective RAG:检索到的信息如果不正确,自动纠正或重新检索
- Adaptive RAG:根据问题的复杂程度自适应选择检索策略
- Graph RAG:使用图数据库存储知识,支持关系检索和多跳推理
四、生产落地
10. 如何规避RAG系统中大模型的幻觉?
- 提高检索精度:优化切割策略和检索算法,确保检索到的信息准确相关
- 强制引用来源:要求大模型在回答中引用检索到的来源
- 事实校验:使用大模型对生成的回答进行事实校验
- 限制回答范围:明确告诉大模型只能使用检索到的信息回答问题
- 添加免责声明:对于不确定的内容,明确告知用户
11. 怎么量化RAG的效果?
常用指标:
- 召回率(Recall):检索到的相关文档占所有相关文档的比例
- 精确率(Precision):检索到的文档中相关文档的比例
- F1值:召回率和精确率的调和平均
- BLEU/ROUGE:评估生成回答和标准答案的相似度
- 人工评估:由人来评估回答的准确性、相关性、完整性
12. RAG知识库如何实现动态与持续更新?
- 增量更新:只更新新增或修改的文档,不需要重新构建整个索引
- 定时更新:定期从数据源同步最新的文档
- 实时更新:当文档发生变化时,立即更新索引
- 版本管理:保留知识库的历史版本,支持回滚
6. LLM 工具调用体系
工具调用是Agent"能做事"的基础,也是面试中区分理论和实战的关键。
一、Function Calling基础
1. 什么是Function Calling?原理是什么?
定义:大模型的一种能力,能够识别用户的问题需要调用外部工具,并输出符合特定格式的工具调用请求。
原理:
- 在提示词中告诉大模型有哪些工具可以调用,以及每个工具的参数格式
- 大模型根据用户的问题,决定是否需要调用工具
- 如果需要调用,大模型输出JSON格式的工具调用请求
- 后端解析JSON,调用对应的工具
- 将工具的返回结果返回给大模型
- 大模型根据工具的返回结果生成最终回答
2. LLM是如何学会调用外部工具的?
大模型的工具调用能力是通过微调学会的:
- 收集大量的工具调用示例数据
- 使用这些数据对大模型进行监督微调(SFT)
- 训练大模型学习工具调用的格式和逻辑
- 通过RLHF进一步优化工具调用的准确性
二、工具调用体系对比
3. Function Calling、Skill、MCP三者的区别是什么?
| 概念 | 定义 | 层级 | 作用 |
|---|---|---|---|
| Function Calling | 大模型输出JSON格式调用请求的能力 | 语言层 | 定义了大模型和工具之间的通信语言 |
| MCP | 模型上下文协议,统一的工具接入标准 | 接口层 | 定义了工具的注册、发现和调用接口 |
| Skill | 将使用工具完成任务的知识和流程打包成可复用模块 | 应用层 | 定义了如何使用工具完成一个复杂任务 |
三者关系:Function Calling是语言,MCP是工具箱,Skill是操作手册。
4. 什么场景下使用Function Calling,什么场景下使用MCP?
-
Function Calling适用场景:
- 简单的工具调用,只需要调用一两个工具
- 项目早期,工具数量少
- 不需要跨项目共享工具
-
MCP适用场景:
- 复杂的工具调用,需要调用多个工具
- 工具数量多,需要统一管理
- 需要跨项目、跨语言共享工具
- 希望接入生态中已有的MCP工具
7. MCP 模型上下文协议
MCP是2026年AI工具生态最热门的话题,也是你正在使用的技术。
一、基础概念
1. 什么是MCP(模型上下文协议)?
MCP(Model Context Protocol)是由Anthropic推出的开放标准,定义了大模型和外部工具之间的统一通信协议。它允许大模型无缝接入各种外部工具和服务,而不需要为每个工具编写专门的集成代码。
2. MCP由哪几部分组成?
- MCP服务器:提供工具能力的服务,每个服务器可以提供多个工具
- MCP客户端:集成在Agent中的客户端,负责与MCP服务器通信
- 传输协议:定义了客户端和服务器之间的通信方式(Stdio、HTTP、WebSocket)
- 消息格式:定义了工具注册、调用、返回的JSON消息格式
3. MCP的通信方式有哪些?
- Stdio(本地子进程):最常用的方式,MCP服务器作为子进程运行,通过标准输入输出通信
- HTTP(远程服务器):MCP服务器运行在远程,通过HTTP接口通信
- WebSocket:支持双向实时通信,适合需要持续交互的工具
二、MCP实战
4. Docker环境下部署MCP需要注意哪些问题?
- 路径映射:所有路径必须使用容器内的路径,不能使用宿主机路径
- 命令路径:必须使用完整的命令路径,不能使用npx/uvx等短命令
- 依赖预装:所有MCP服务器的依赖必须在Dockerfile中预装
- 卷映射:需要访问宿主机文件时,必须配置Docker卷映射
5. 如何解决MCP文件系统工具的路径不匹配问题?
- 在系统提示词中明确告诉Agent运行在Docker环境中,只能访问容器内的路径
- 实现路径自动转换,将宿主机路径自动转换为容器内的路径
- 添加错误处理,当访问不存在的路径时,提示用户使用容器内的路径
8. LangChain 框架核心
LangChain是最流行的Agent开发框架,几乎所有AI工程师都用过。
一、核心组件
1. LangChain的核心组件有哪些?
- LLM:大语言模型的抽象,支持各种主流大模型
- Prompt Template:提示词模板,方便复用和管理提示词
- Chain:将多个组件组合成一个执行流程
- Agent:自主决策的智能体
- Tool:外部工具的抽象
- Memory:记忆系统的抽象
- Retriever:检索器的抽象,支持各种检索方式
- Document Loader:文档加载器,支持各种格式的文档
- Text Splitter:文本切割器,支持各种切割策略
- Vector Store:向量数据库的抽象,支持各种主流向量数据库
二、Agent框架
2. LangChain支持哪些Agent类型?
- ReAct Agent:实现ReAct范式的Agent,最常用
- Plan-and-Execute Agent:实现Plan-and-Execute范式的Agent
- OpenAI Functions Agent:专门针对OpenAI Function Calling优化的Agent
- Structured Chat Agent:支持结构化输出的Agent
- Multi-Agent:支持多Agent协作
3. 在工程实践中,为什么有时候选择"手搓"Agent,而不是直接用LangChain?
- 灵活性:LangChain的封装太死,难以定制化修改
- 性能:LangChain有很多额外的开销,性能不如手搓的
- 调试难度:LangChain的执行流程不透明,调试困难
- 依赖问题:LangChain的版本更新快,兼容性差
- 功能冗余:很多功能用不到,增加了项目的复杂度
选型建议:
- 快速原型验证 → 用LangChain
- 生产环境部署 → 手搓Agent,只使用LangChain的部分组件
9. 大模型工程基础
大模型工程是AI应用的地基,了解基础原理能让你更好地优化Agent性能。
一、推理与生成
1. 大模型生成文本时的解码策略有哪些?
- 贪心解码:每次选择概率最高的token,速度快但容易重复
- Beam Search:同时保留多个候选路径,选择概率最高的路径,生成质量高但速度慢
- 采样解码:根据概率分布随机选择token,生成更有多样性
- Top-K采样:只从概率最高的K个token中选择
- Top-P采样:只从概率和为P的token中选择
2. 大模型的采样参数:温度值、Top-P、Top-K分别是什么?
- 温度值(Temperature):控制生成的随机性,值越高越随机,值越低越确定。常用值:0.1-1.0
- Top-K:只考虑概率最高的K个token,过滤掉低概率的token。常用值:20-100
- Top-P:只考虑概率和为P的最小token集合。常用值:0.7-0.9
3. KV Cache是什么?原理是什么?
定义:一种优化大模型推理速度的技术,缓存注意力计算的中间结果。
原理:
- 在生成每个token时,注意力机制需要计算所有之前token的Key和Value
- KV Cache将这些Key和Value缓存起来,不需要每次都重新计算
- 生成第N个token时,只需要计算第N个token的Key和Value,然后和之前缓存的结果合并
效果:推理速度提升10倍以上,内存占用增加约2倍。
4. 大模型量化是什么?常见的量化方式有哪些?
定义:将大模型的参数从高精度(FP16/BF16)转换为低精度(INT8/INT4),减少内存占用和计算量。
常见量化方式:
- INT8量化:精度损失小,速度提升中等,适合大多数场景
- INT4量化:精度损失中等,速度提升大,内存占用小
- AWQ:专门针对大模型优化的量化方法,精度损失比普通INT4小
- GPTQ:基于训练的量化方法,精度损失最小,但量化过程慢
10. SSE 协议
2026年AI大模型行业标准面试题大全,按难度分级整理,覆盖从基础概念到生产级实战的所有核心考点。
一、初级题(基础概念·必问)
1. 什么是SSE?全称是什么?
SSE全称是Server-Sent Events(服务器发送事件),是HTML5标准定义的一种服务器主动向客户端推送数据的单向通信协议。它基于HTTP协议,允许服务器在建立连接后持续向客户端发送流式数据,客户端通过事件监听的方式接收数据。
2. SSE和WebSocket的核心区别是什么?
| 对比项 | SSE | WebSocket |
|---|---|---|
| 通信方向 | 单向(仅服务器→客户端) | 双向全双工 |
| 协议基础 | 基于HTTP/1.1或HTTP/2 | 独立的WebSocket协议(ws/wss) |
| 连接建立 | 标准HTTP握手 | 专用的WebSocket握手 |
| 数据格式 | 仅支持文本(UTF-8) | 支持文本和二进制 |
| 自动重连 | 原生支持 | 需要手动实现 |
| 浏览器兼容性 | 所有现代浏览器 | 所有现代浏览器 |
| 使用场景 | 大模型流式输出、日志推送、实时通知 | 聊天、游戏、实时协作 |
3. 为什么现在AI大模型几乎都用SSE而不是WebSocket?
- 简单性:SSE基于标准HTTP,不需要额外的协议支持,部署和调试更简单
- 兼容性:完美兼容所有现有的HTTP代理、防火墙、负载均衡器
- 功能匹配:大模型生成回答是服务器单向流式输出,SSE的单向通信完全满足需求
- 生态成熟:OpenAI的SSE格式已经成为行业事实标准,所有客户端和工具链都已支持
- 自动重连:SSE原生支持断线自动重连,不需要额外开发
4. SSE和长轮询(Long Polling)的区别?
- 长轮询:客户端发送请求,服务器保持连接直到有数据返回,然后关闭连接,客户端立即发送下一个请求。本质上是多个短连接的循环。
- SSE:建立一次持久连接,服务器可以持续多次向客户端发送数据,连接一直保持直到主动关闭。
- 性能:SSE的延迟更低,服务器开销更小,没有频繁的连接建立和断开开销。
二、中级题(协议细节与实现)
5. SSE标准响应头必须包含哪几个?缺一不可的是哪个?
标准响应头:
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
X-Accel-Buffering: no
缺一不可的是Content-Type: text/event-stream,浏览器只有识别到这个头才会将响应作为SSE流处理。其中X-Accel-Buffering: no是生产环境最容易遗漏的,它强制Nginx和FastCGI不缓冲响应,确保数据立即发送到客户端。
6. SSE的标准数据格式是什么?
每个SSE事件由一行或多行组成,每个事件之间用**两个换行符\n\n**分隔:
# 普通数据事件
data: 这是一条数据\n\n
# 带ID的事件
id: msg_001\n
data: 这是带ID的事件\n\n
# 自定义事件类型
event: step\n
data: {"content":"正在检索知识库"}\n\n
# 重连间隔设置
retry: 3000\n\n
# 结束标记(OpenAI标准)
data: [DONE]\n\n
7. 什么是SSE的粘包问题?如何解决?
粘包问题是指:由于TCP协议的流特性,多个SSE事件可能会被合并成一个TCP数据包发送,客户端收到时是多个事件连在一起的字符串。
解决方法:
- 客户端维护一个缓冲区,将每次收到的数据追加到缓冲区
- 以两个换行符
\n\n为分隔符,将缓冲区分割成多个完整的事件 - 处理所有完整的事件,将剩余的不完整部分留在缓冲区等待下一次数据
8. 原生EventSource API有哪些局限性?
- 仅支持GET请求,不支持POST、PUT等其他方法
- 不能自定义请求头,无法传递Authorization等认证信息
- 只能接收UTF-8文本,不支持二进制数据
- 错误处理能力弱,只能监听error事件,无法获取具体的错误状态码
- 重连逻辑不可控,原生重连间隔固定为3秒,无法自定义
这就是为什么2026年所有行业标准实现都改用fetch + ReadableStream替代原生EventSource的原因。
9. OpenAI的SSE格式为什么成为了行业事实标准?
- 生态完整:几乎所有的大模型提供商(Anthropic、Google、字节、百度)都兼容OpenAI的SSE格式
- 工具链丰富:有大量的开源客户端、SDK和调试工具支持
- 设计合理:格式简洁,易于解析,同时支持扩展字段
- 向后兼容:可以很容易地在现有SSE实现的基础上改造
OpenAI标准格式示例:
data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","created":1718000000,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":"你"},"finish_reason":null}]}\n\n
三、高级题(生产环境与性能优化)
10. 生产环境中SSE连接为什么会被网关断开?如何解决?
断开原因:
- Nginx、Apache等反向代理服务器默认有连接超时时间(通常60秒)
- 防火墙和负载均衡器会自动关闭长时间没有数据传输的连接
- 浏览器和操作系统也有自己的连接超时限制
解决方法:
- 心跳包机制:服务器每15秒发送一个注释行心跳
: ping\n\n,浏览器会忽略注释行,但会重置连接超时计时器 - 延长代理超时:在Nginx配置中设置
proxy_read_timeout 300s;,将超时时间延长到5分钟 - 禁用缓冲:设置
proxy_buffering off; proxy_cache off;,确保数据立即发送
11. 什么是伪流式SSE?它有什么问题?
伪流式是指:服务器先完整生成所有数据,然后再将数据分成多个块发送给客户端。
问题:
- 用户体验差:用户需要等待所有内容生成完成后才能看到第一个字
- 服务器内存占用高:需要在内存中缓存完整的响应内容
- 延迟高:第一个字节的延迟等于整个响应的生成时间
真流式:服务器每生成一个token就立即发送给客户端,第一个字节的延迟<100ms。
12. SSE如何支持请求取消?
使用fetch + ReadableStream配合AbortController实现:
const controller = new AbortController();
// 发起请求
const response = await fetch('/api/chat/stream', {
method: 'POST',
signal: controller.signal
});
// 取消请求
controller.abort();
当调用abort()时,浏览器会关闭连接,服务器会收到一个断开事件,可以停止生成内容释放资源。
13. HTTP/2和HTTP/3对SSE有什么优化?
- 多路复用:HTTP/2和HTTP/3支持在一个TCP/QUIC连接上同时进行多个SSE流,不需要建立多个TCP连接
- 头部压缩:HPACK/QPACK压缩请求和响应头,减少额外开销
- 服务器推送:可以主动推送相关资源,进一步降低延迟
- 连接复用:同一个域名的所有请求都复用同一个连接,减少握手开销
注意:HTTP/2和HTTP/3只支持HTTPS,这也是现在所有生产环境必须使用HTTPS的原因之一。
14. 如何实现SSE的断点续传?
使用SSE的id字段实现:
- 服务器在每个事件中都包含一个唯一的
id - 当连接断开时,浏览器会自动在重连请求的
Last-Event-ID头中带上最后收到的事件ID - 服务器收到
Last-Event-ID头后,从该ID之后的事件开始继续发送
示例:
# 服务器发送
id: msg_005\n
data: 第5条消息\n\n
# 连接断开后浏览器重连
GET /api/stream HTTP/1.1
Last-Event-ID: msg_005
四、实战综合题(项目经验考察)
15. 现有一个伪流式的SSE实现,如何将其升级为真流式?
- 移除所有手动分块代码:删除按固定字符数分块的逻辑
- 直接迭代大模型的原生流:调用大模型的
astream()方法,每个token生成后立即yield - 修改响应格式:改为OpenAI兼容的标准SSE格式
- 添加心跳包机制:每15秒发送一个心跳包
- 前端升级:从
EventSource改为fetch + ReadableStream - 添加请求取消支持:使用
AbortController实现取消功能
16. 设计一个支持工具调用的SSE流协议,需要包含哪些事件类型?
需要设计4种核心事件类型:
- token事件:大模型生成的文本token,使用标准
data:事件 - step事件:状态更新事件,使用
event: step,包含当前执行的步骤 - observation事件:工具执行结果事件,使用
event: observation,包含工具名称和返回结果 - done事件:流结束事件,使用标准
data: [DONE]标记
示例:
event: step
data: {"step_id":"plan","content":"正在制定执行计划"}\n\n
event: observation
data: {"tool_name":"tavily_web_search","content":"搜索结果..."}\n\n
data: {"choices":[{"delta":{"content":"根据搜索结果"}}]}\n\n
data: [DONE]\n\n
17. 生产环境部署SSE服务,Nginx需要做哪些特殊配置?
location /api/chat/stream {
proxy_pass http://backend:8000;
# SSE核心配置
proxy_buffering off; # 禁用代理缓冲
proxy_cache off; # 禁用缓存
proxy_read_timeout 300s; # 延长超时到5分钟
proxy_set_header X-Accel-Buffering "no"; # 强制FastCGI不缓冲
# 通用代理配置
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Connection ""; # 保持长连接
}
18. 如何处理SSE流中的异常?
- 后端异常处理:捕获所有异常,发送标准错误事件并优雅关闭连接
event: error data: {"code":500,"message":"服务器内部错误"}\n\n data: [DONE]\n\n - 前端异常处理:监听fetch的error事件,实现指数退避自动重连
- 超时处理:设置全局请求超时,超时后发送finish_reason=“timeout”
- 资源清理:连接断开时,立即停止大模型生成,释放所有资源
五、陷阱题(考察细节)
19. SSE的retry字段单位是什么?
毫秒。例如retry: 3000表示重连间隔为3秒。
20. SSE的注释行有什么用?
以冒号:开头的行是注释行,浏览器会完全忽略。主要用于发送心跳包,防止连接被网关断开。
21. SSE可以发送二进制数据吗?
原生SSE协议只能发送UTF-8文本。如果需要发送二进制数据,可以先进行Base64编码,客户端收到后再解码。
22. 一个浏览器最多可以同时打开多少个SSE连接?
HTTP/1.1下,同一个域名最多同时打开6个SSE连接。HTTP/2和HTTP/3下没有这个限制,可以同时打开数百个连接。
11. 后端基础设施
2026年AI大模型后端面试核心模块,覆盖FastAPI、Redis、MySQL三大技术栈,包含生产环境实战和AI项目专属问题。
一、FastAPI 框架核心(Python后端必问)
1. 基础概念
1. FastAPI的核心优势是什么?为什么它能取代Flask成为Python后端首选?
- 高性能:基于Starlette异步框架,性能接近Node.js和Go,是最快的Python Web框架之一
- 自动文档:原生支持Swagger UI和ReDoc,代码即文档,自动生成交互式API文档
- 类型安全:基于Python类型提示和Pydantic,自动进行数据验证和序列化
- 异步原生:完整支持async/await语法,适合I/O密集型任务(如大模型调用)
- 依赖注入:强大的依赖注入系统,代码解耦、可测试性强
- 生态丰富:支持OAuth2、JWT、WebSocket、GraphQL等现代Web标准
2. FastAPI的底层依赖是什么?
- Starlette:提供异步Web服务器和路由功能
- Pydantic:提供数据验证、序列化和类型提示功能
- Uvicorn:ASGI服务器,用于运行FastAPI应用
3. 路径参数和查询参数的区别是什么?
- 路径参数:URL的一部分,用于标识资源,如
/items/{item_id} - 查询参数:URL中
?后面的键值对,用于过滤和排序,如/items?page=1&size=10
2. 核心机制
4. FastAPI的依赖注入系统是如何工作的?
依赖注入是FastAPI最强大的功能之一,它允许你声明依赖项,并由FastAPI自动解析和注入:
- 定义依赖函数,返回需要的对象
- 在路径操作函数中使用
Depends()声明依赖 - FastAPI在调用路径操作函数之前,自动执行依赖函数并注入结果
示例:
from fastapi import Depends, FastAPI
app = FastAPI()
async def get_db():
db = Database()
try:
yield db
finally:
db.close()
@app.get("/items/")
async def read_items(db=Depends(get_db)):
return db.query("SELECT * FROM items")
5. Pydantic模型在FastAPI中有什么作用?
- 数据验证:自动验证请求体、查询参数、路径参数的类型和格式
- 数据序列化:自动将Python对象转换为JSON响应
- 自动生成文档:根据Pydantic模型自动生成API文档的请求和响应格式
- 类型安全:提供编译时类型检查,减少运行时错误
6. FastAPI如何处理异步请求?和同步请求有什么区别?
FastAPI同时支持同步和异步路径操作函数:
- 异步函数:使用
async def定义,由Uvicorn的事件循环执行,适合I/O密集型任务 - 同步函数:使用
def定义,FastAPI会自动将其放到线程池中执行
关键区别:
- 异步函数不能调用阻塞操作,否则会阻塞整个事件循环
- 同步函数可以调用阻塞操作,不会影响其他请求
- 大模型调用、数据库查询、网络请求等I/O密集型任务优先使用异步
3. 生产环境实战
7. 如何优化FastAPI的性能?
- 使用异步函数:所有I/O密集型操作都使用异步版本
- 启用HTTP/2:在Nginx中配置HTTP/2,提高并发性能
- 使用连接池:数据库、Redis、大模型API都使用连接池
- 启用Gzip压缩:在Nginx中启用Gzip压缩,减少传输数据量
- 静态资源分离:静态资源由Nginx托管,不经过FastAPI
- 使用进程管理器:使用Gunicorn+Uvicorn部署,充分利用多核CPU
- 添加缓存:使用Redis缓存频繁访问的数据和大模型生成结果
8. FastAPI如何实现后台任务?
FastAPI提供了BackgroundTasks类,用于在请求返回后执行后台任务:
from fastapi import BackgroundTasks, FastAPI
app = FastAPI()
def write_notification(email: str, message: str = ""):
with open("log.txt", mode="a") as email_file:
email_file.write(f"notification for {email}: {message}\n")
@app.post("/send-notification/{email}")
async def send_notification(email: str, background_tasks: BackgroundTasks):
background_tasks.add_task(write_notification, email, message="some notification")
return {"message": "Notification sent in the background"}
注意:后台任务适合执行时间短的任务,长时间任务应该使用消息队列(如Celery、RabbitMQ)。
9. FastAPI如何实现限流?
使用slowapi库实现限流,基于Redis存储限流计数器:
from fastapi import FastAPI
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded
limiter = Limiter(key_func=get_remote_address, storage_uri="redis://localhost:6379")
app = FastAPI()
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
@app.get("/chat")
@limiter.limit("10/minute")
async def chat(request: Request):
return {"message": "Hello"}
4. AI项目专属问题
10. 为什么大模型后端必须使用异步接口?
- 高并发:大模型生成响应需要几秒钟到几十秒钟,同步接口会导致服务器很快被占满
- 资源利用率:异步接口可以在等待大模型响应的同时处理其他请求,提高CPU和内存利用率
- 用户体验:异步接口配合SSE流式输出,可以实现打字机效果,提升用户体验
- 容错性:异步接口更容易实现超时控制和重试机制
11. FastAPI中如何实现SSE流式输出?
使用EventSourceResponse返回流式响应:
from fastapi import FastAPI
from fastapi.responses import EventSourceResponse
import asyncio
import json
app = FastAPI()
async def generate_tokens():
tokens = ["你", "好", ",", "我", "是", "BaseAgent"]
for token in tokens:
yield f"data: {json.dumps({'choices': [{'delta': {'content': token}}])}\n\n"
await asyncio.sleep(0.1)
yield "data: [DONE]\n\n"
@app.get("/chat/stream")
async def chat_stream():
return EventSourceResponse(generate_tokens())
12. FastAPI项目的目录结构如何设计?(AI项目最佳实践)
backend/
├── app/
│ ├── api/
│ │ ├── v1/
│ │ │ ├── chat.py # 聊天接口
│ │ │ ├── knowledge.py # 知识库接口
│ │ │ ├── mcp.py # MCP接口
│ │ │ └── __init__.py
│ │ └── __init__.py
│ ├── core/
│ │ ├── config.py # 配置文件
│ │ ├── exceptions.py # 异常处理
│ │ └── security.py # 安全认证
│ ├── db/
│ │ ├── database.py # 数据库连接
│ │ └── models.py # 数据库模型
│ ├── schemas/
│ │ ├── chat.py # 聊天相关Pydantic模型
│ │ └── knowledge.py # 知识库相关Pydantic模型
│ ├── services/
│ │ ├── chat_service.py # 聊天服务
│ │ ├── rag_service.py # RAG服务
│ │ └── mcp_service.py # MCP服务
│ ├── utils/
│ │ ├── logger.py # 日志工具
│ │ └── stream.py # 流式输出工具
│ └── main.py # 应用入口
├── tests/ # 测试用例
├── Dockerfile # Docker构建文件
└── requirements.txt # 依赖列表
二、Redis 核心原理与实战(高并发必问)
1. 基础原理
1. Redis为什么这么快?
- 内存存储:所有数据都存储在内存中,避免了磁盘I/O
- 高效数据结构:底层使用跳表、哈希表等高效数据结构,时间复杂度多为O(1)或O(logN)
- 单线程模型:避免了多线程的上下文切换和锁竞争
- I/O多路复用:使用epoll机制,单线程能高效处理大量并发连接
- 优化的代码:Redis是用C语言编写的,执行效率高
2. Redis有哪些核心数据类型?应用场景是什么?
| 数据类型 | 底层结构 | 应用场景 |
|---|---|---|
| String | SDS动态字符串 | 缓存、计数器、分布式锁、会话存储 |
| Hash | 哈希表 | 存储对象(用户信息、商品详情) |
| List | 双向链表 | 消息队列、最新文章列表、任务队列 |
| Set | 哈希表 | 去重、共同好友、标签系统 |
| ZSet | 跳表+哈希表 | 排行榜、带权重的队列、延时队列 |
| Bitmap | 字符串 | 签到统计、用户在线状态 |
| HyperLogLog | 基数统计 | UV统计、独立访客计数 |
| Geo | ZSet | 地理位置查询、附近的人 |
| Stream | 消息队列 | 可靠消息队列、消费者组 |
3. Redis的过期键删除策略有哪些?
- 惰性删除:只有当访问一个键时,才检查它是否过期,如果过期就删除。优点是CPU开销小,缺点是内存浪费大。
- 定期删除:每隔一段时间,随机抽取一部分键进行检查,删除过期的键。优点是平衡了CPU和内存开销,缺点是可能有大量过期键没有被及时删除。
- 内存淘汰:当内存使用达到maxmemory限制时,执行内存淘汰策略,删除部分键释放内存。
Redis默认使用惰性删除+定期删除的组合策略。
4. Redis的内存淘汰策略有哪些?
| 策略 | 说明 |
|---|---|
| noeviction | 不淘汰,当内存满时,新的写操作会报错 |
| allkeys-lru | 淘汰所有键中最近最少使用的键(最常用) |
| allkeys-lfu | 淘汰所有键中使用频率最低的键 |
| allkeys-random | 随机淘汰所有键中的一部分 |
| volatile-lru | 淘汰设置了过期时间的键中最近最少使用的键 |
| volatile-lfu | 淘汰设置了过期时间的键中使用频率最低的键 |
| volatile-random | 随机淘汰设置了过期时间的键中的一部分 |
| volatile-ttl | 淘汰设置了过期时间的键中最早过期的键 |
2. 缓存问题与解决方案
5. 什么是缓存穿透、缓存击穿、缓存雪崩?如何解决?
| 问题 | 现象 | 原因 | 解决方案 |
|---|---|---|---|
| 缓存穿透 | 大量请求查询不存在的数据,直接打到数据库 | 请求的数据在缓存和数据库中都不存在 | 1. 布隆过滤器 2. 缓存空值 3. 接口参数校验 |
| 缓存击穿 | 某个热点key过期,大量请求同时打到数据库 | 热点key过期,同时有大量并发请求 | 1. 热点key永不过期 2. 互斥锁 3. 提前更新缓存 |
| 缓存雪崩 | 大量key同时过期,数据库压力剧增甚至宕机 | 大量key在同一时间过期 Redis服务器宕机 |
1. 过期时间加随机值 2. 搭建Redis集群 3. 服务熔断和降级 4. 多级缓存 |
6. 如何保证缓存与数据库的双写一致性?
最佳实践:Cache Aside Pattern(旁路缓存模式)
- 读操作:先读缓存,命中返回;未命中读数据库,写入缓存后返回
- 写操作:先更新数据库,再删除缓存(不是更新缓存)
为什么删除缓存而不是更新缓存?
- 懒加载:避免无效更新,只有当数据被访问时才更新缓存
- 并发安全:防止并发更新时因顺序错乱导致的脏数据问题
- 幂等性:删除操作是幂等的,重复删除不会有问题
进阶方案:
- 延迟双删:先删缓存 → 更新数据库 → 延时1秒 → 再删缓存(解决更新期间的脏读)
- 异步监听:通过Canal监听MySQL Binlog,自动删除/更新缓存
3. 分布式锁与高可用
7. 如何用Redis实现分布式锁?需要注意哪些问题?
基本实现:
SET key value NX PX 30000
NX:键不存在才设置PX:设置过期时间(防止死锁)
核心问题与解决方案:
- 误删问题:解锁时需要校验Value(线程ID),且判断+删除操作必须用Lua脚本保证原子性
- 锁过期问题:业务未执行完锁就过期了。解决方案:Redisson的看门狗(Watch Dog)机制,自动续期
- 集群安全问题:Redlock算法(向半数以上节点申请锁),但争议较大,一般单机或主从+哨兵配合过期时间已足够
8. Redis主从复制的原理是什么?
- 从节点发送
PSYNC命令给主节点 - 主节点执行BGSAVE生成RDB快照,并将快照发送给从节点
- 从节点清空自己的数据,加载RDB快照
- 主节点将快照生成期间的写命令发送给从节点,从节点执行这些命令
- 主节点后续的写命令都会异步复制给从节点
9. Redis哨兵(Sentinel)机制的作用是什么?
哨兵是Redis的高可用解决方案,主要作用:
- 监控:持续监控主节点和从节点的健康状态
- 自动故障转移:当主节点宕机时,自动将一个从节点提升为新的主节点
- 通知:当发生故障转移时,通知客户端新的主节点地址
- 配置管理:客户端通过哨兵获取主节点的地址
4. AI项目专属问题
10. Redis在AI Agent项目中有哪些应用场景?
- 会话存储:存储用户的会话信息和短期记忆
- 缓存:缓存大模型的生成结果、向量检索结果、嵌入结果
- 分布式锁:防止并发请求导致的重复生成和重复工具调用
- 消息队列:实现异步任务处理,如知识库构建、大模型批量生成
- 限流:限制用户的请求频率,防止滥用大模型API
- 计数器:统计用户的API调用次数、token使用量
- 滑动窗口限流:实现更精细的限流控制
- 分布式缓存:多实例部署时共享缓存数据
11. 如何用Redis实现滑动窗口限流?
import redis
import time
r = redis.Redis(host='localhost', port=6379, db=0)
def is_rate_limited(user_id: str, window_size: int = 60, max_requests: int = 10) -> bool:
"""
滑动窗口限流
:param user_id: 用户ID
:param window_size: 窗口大小(秒)
:param max_requests: 窗口内最大请求数
:return: True表示被限流,False表示允许
"""
now = int(time.time() * 1000) # 毫秒级时间戳
key = f"rate_limit:{user_id}"
# 使用Lua脚本保证原子性
lua_script = """
local key = KEYS[1]
local now = tonumber(ARGV[1])
local window_size = tonumber(ARGV[2])
local max_requests = tonumber(ARGV[3])
-- 删除窗口外的记录
redis.call('ZREMRANGEBYSCORE', key, 0, now - window_size * 1000)
-- 统计当前窗口内的请求数
local count = redis.call('ZCARD', key)
if count >= max_requests then
return 1
else
-- 添加当前请求
redis.call('ZADD', key, now, now)
redis.call('EXPIRE', key, window_size)
return 0
end
"""
result = r.eval(lua_script, 1, key, now, window_size, max_requests)
return result == 1
12. 如何用Redis实现Agent的短期记忆?
import redis
import json
r = redis.Redis(host='localhost', port=6379, db=0)
def add_message(conversation_id: str, role: str, content: str):
"""添加一条消息到短期记忆"""
key = f"conversation:{conversation_id}"
message = {"role": role, "content": content, "timestamp": int(time.time())}
r.rpush(key, json.dumps(message))
r.expire(key, 86400) # 24小时过期
def get_messages(conversation_id: str, limit: int = 10) -> list:
"""获取最近的N条消息"""
key = f"conversation:{conversation_id}"
messages = r.lrange(key, -limit, -1)
return [json.loads(msg) for msg in messages]
def clear_messages(conversation_id: str):
"""清空会话的短期记忆"""
key = f"conversation:{conversation_id}"
r.delete(key)
三、MySQL 核心原理与实战(数据存储必问)
1. 索引原理
1. MySQL InnoDB索引的底层数据结构是什么?为什么用B+树?
底层结构:B+树
为什么用B+树而不用其他结构?
- 对比哈希索引:哈希索引只支持等值查询,不支持范围查询和排序
- 对比二叉树:二叉树在数据量大时深度太大,磁盘I/O次数多
- 对比B树:B+树的非叶子节点不存储数据,只存储索引,能存储更多的索引项,树的深度更小,磁盘I/O次数更少;B+树的叶子节点形成一个有序链表,方便范围查询和排序
2. 聚簇索引和非聚簇索引的区别是什么?
| 对比项 | 聚簇索引(主键索引) | 非聚簇索引(二级索引) |
|---|---|---|
| 叶子节点存储 | 整行数据 | 主键值 |
| 数量 | 一个表只能有一个聚簇索引 | 一个表可以有多个非聚簇索引 |
| 查询效率 | 高,一次查询就能得到数据 | 低,需要先查二级索引得到主键,再查聚簇索引(回表) |
3. 什么是覆盖索引?有什么好处?
覆盖索引是指一个索引包含了查询需要的所有字段,不需要回表查询聚簇索引。
好处:
- 大大提高查询性能,减少磁盘I/O
- 避免回表操作,减少锁竞争
- 降低内存占用
示例:
-- 创建联合索引
CREATE INDEX idx_name_age ON user(name, age);
-- 这个查询可以使用覆盖索引,不需要回表
SELECT name, age FROM user WHERE name = '张三';
4. 什么是最左前缀原则?
最左前缀原则是指联合索引按照最左优先的方式进行匹配,查询时会从联合索引的最左边开始匹配,直到遇到范围查询(>、<、between、like)就停止匹配。
示例:
联合索引idx_a_b_c(a, b, c)
- 可以匹配:
a、a,b、a,b,c - 不能匹配:
b、b,c、c - 部分匹配:
a,c(只匹配a,c不会用到索引)
2. 事务与并发
5. 事务的ACID特性是什么?
- 原子性(Atomicity):事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败
- 一致性(Consistency):事务执行前后,数据库的完整性约束没有被破坏
- 隔离性(Isolation):多个事务并发执行时,一个事务的执行不能被其他事务干扰
- 持久性(Durability):一个事务一旦提交,它对数据库的修改是永久性的
6. MySQL的事务隔离级别有哪些?分别解决了什么问题?
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| 读未提交(Read Uncommitted) | ❌ | ❌ | ❌ |
| 读已提交(Read Committed) | ✅ | ❌ | ❌ |
| 可重复读(Repeatable Read) | ✅ | ✅ | ❌(InnoDB通过MVCC解决) |
| 串行化(Serializable) | ✅ | ✅ | ✅ |
MySQL InnoDB默认的隔离级别是可重复读(RR)。
7. 什么是MVCC?原理是什么?
MVCC(多版本并发控制)是InnoDB实现隔离级别的基础,它允许多个事务同时读写数据库,而不会互相阻塞。
原理:
- 每行数据都有两个隐藏字段:
DB_TRX_ID(事务ID)和DB_ROLL_PTR(回滚指针) - 事务修改数据时,会生成一个新版本的数据,旧版本的数据会被保留在undo log中
- 事务读取数据时,根据事务的启动时间和数据的版本号,读取符合条件的版本
- 不同的事务可以看到不同版本的数据,实现了读写不阻塞
8. InnoDB是如何解决幻读问题的?
InnoDB在可重复读隔离级别下,通过MVCC+间隙锁解决了幻读问题:
- 快照读:使用MVCC,读取数据的历史版本,不会看到其他事务插入的新数据
- 当前读:使用间隙锁(Gap Lock),锁定索引之间的间隙,防止其他事务在间隙中插入新数据
3. 性能优化与架构
9. 如何定位和优化慢查询?
定位慢查询:
- 开启慢查询日志:
slow_query_log = ON,long_query_time = 1(超过1秒的查询记录下来) - 使用
EXPLAIN分析查询计划,查看索引使用情况 - 使用
SHOW PROFILE查看查询的执行时间分布
优化方法:
- 添加合适的索引:避免全表扫描
- 优化SQL语句:避免SELECT *,避免在WHERE子句中使用函数和运算
- 分页优化:使用游标分页替代OFFSET分页
- 避免大事务:将大事务拆分为小事务
- 读写分离:主库写,从库读
- 分库分表:单表数据量超过5000万行时考虑分库分表
10. 什么是分库分表?什么时候需要分库分表?
分库分表:将一个大的数据库拆分为多个小的数据库,将一个大的表拆分为多个小的表,以提高数据库的性能和可扩展性。
什么时候需要分库分表?
- 单表数据量超过5000万行且持续快速增长
- 单库并发量(QPS)超过数据库承载能力(通常写QPS>1000)
- 业务数据有明显的冷热区分
- 未来有明确的大规模扩容需求
- 单库磁盘使用率超过80%且无法通过清理数据解决
拆分策略:
- 垂直分库:根据业务模块拆分,如用户库、订单库、商品库
- 垂直分表:根据字段拆分,将大字段和不常用字段拆分到单独的表中
- 水平分库:将一个库的数据按某个字段(如用户ID)拆分到多个库中
- 水平分表:将一个表的数据按某个字段拆分到多个表中
11. MySQL的备份策略有哪些?
| 备份方式 | 类型 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| mysqldump | 逻辑备份 | 简单、灵活、兼容性强 | 大库备份慢 | 中小库、单库单表恢复、数据迁移 |
| Percona XtraBackup | 物理备份 | 快、不锁表、支持增量备份 | 粒度粗、跨平台差 | 大库、全量/增量备份、灾难恢复 |
| 主从复制 | - | 快速回滚 | 需要主从架构 | 有主从的环境 |
| binlog时间点恢复 | - | 精确到时间/位置 | 依赖binlog | 数据误删应急恢复 |
生产环境最佳实践:
- 中小库:每天凌晨用mysqldump做全量压缩备份,保留7天
- 大库:每周日用XtraBackup做全量备份,周一到周六做增量备份,保留2周
- 必须开启binlog:用于误删后的时间点恢复
4. AI项目专属问题
12. MySQL在AI Agent项目中有哪些应用场景?
- 用户管理:存储用户信息、权限、角色
- 会话管理:存储永久的对话历史记录
- 知识库管理:存储知识库的元信息、文档信息、分片信息
- MCP配置管理:存储MCP服务器的配置信息
- 工具调用日志:存储工具调用的日志和统计信息
- 模型配置管理:存储大模型的配置信息和API密钥
- 用户配额管理:存储用户的API调用配额和使用量
- 系统日志:存储系统的运行日志和错误日志
13. 如何设计Agent项目的MySQL表结构?
-- 用户表
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 对话表
CREATE TABLE conversations (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
title VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- 消息表
CREATE TABLE messages (
id INT PRIMARY KEY AUTO_INCREMENT,
conversation_id INT NOT NULL,
role ENUM('user', 'assistant', 'system', 'tool') NOT NULL,
content TEXT NOT NULL,
tool_name VARCHAR(100),
tool_args JSON,
tool_result JSON,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE
);
-- 知识库表
CREATE TABLE knowledge_bases (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
name VARCHAR(100) NOT NULL,
description TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- 文档表
CREATE TABLE documents (
id INT PRIMARY KEY AUTO_INCREMENT,
knowledge_base_id INT NOT NULL,
name VARCHAR(255) NOT NULL,
file_type VARCHAR(50) NOT NULL,
file_size INT NOT NULL,
status ENUM('uploading', 'processing', 'completed', 'failed') NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (knowledge_base_id) REFERENCES knowledge_bases(id) ON DELETE CASCADE
);
-- MCP服务器表
CREATE TABLE mcp_servers (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
name VARCHAR(100) NOT NULL,
type ENUM('stdio', 'http') NOT NULL,
command TEXT,
args JSON,
env JSON,
url VARCHAR(255),
status ENUM('stopped', 'running', 'error') NOT NULL DEFAULT 'stopped',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
14. 如何处理大模型项目中的长文本存储?
- 短文本(<65535字符):使用VARCHAR或TEXT类型
- 中长文本(<16MB):使用MEDIUMTEXT类型
- 超长文本(>16MB):不要存储在MySQL中,使用文件系统或对象存储(如S3、OSS),MySQL中只存储文件路径
- 聊天记录:使用TEXT类型,单条消息一般不会太长
- 知识库文档:文档内容存储在文件系统或向量数据库,MySQL中只存储元信息
四、生产环境部署与运维
1. Docker与容器化
1. 为什么AI项目必须使用Docker部署?
- 环境一致性:开发、测试、生产环境完全一致,避免"在我电脑上能跑"的问题
- 部署简单:一键部署,不需要手动安装依赖和配置环境
- 隔离性:不同服务之间相互隔离,不会互相影响
- 可移植性:可以在任何支持Docker的平台上运行
- 扩缩容方便:配合Kubernetes可以轻松实现水平扩缩容
- 版本管理:镜像版本化,方便回滚和升级
2. 你的BaseAgent项目的Docker Compose配置应该是什么样的?
version: '3.8'
services:
frontend:
build: ./frontend
ports:
- "3000:80"
depends_on:
- backend
restart: always
backend:
build: ./backend
ports:
- "8000:8000"
environment:
- DATABASE_URL=mysql+pymysql://user:password@mysql:3306/baseagent
- REDIS_URL=redis://redis:6379/0
- OPENAI_API_KEY=${OPENAI_API_KEY}
- TAVILY_API_KEY=${TAVILY_API_KEY}
volumes:
- ./uploads:/app/uploads
- ./mcp-test-files:/mcp-test-files
depends_on:
- mysql
- redis
restart: always
mysql:
image: mysql:8.0
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=rootpassword
- MYSQL_DATABASE=baseagent
- MYSQL_USER=user
- MYSQL_PASSWORD=password
volumes:
- mysql_data:/var/lib/mysql
restart: always
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
restart: always
volumes:
mysql_data:
redis_data:
2. Nginx反向代理
3. 生产环境中Nginx需要做哪些配置?
完整的Nginx配置参考之前的SSE部分,核心要点:
- HTTPS配置:使用Let’s Encrypt免费证书,自动续期
- HTTP重定向:将所有HTTP请求重定向到HTTPS
- 静态资源托管:前端静态资源由Nginx直接托管
- 反向代理:API请求转发到FastAPI后端
- SSE专属配置:禁用缓冲,延长超时时间
- Gzip压缩:启用Gzip压缩,减少传输数据量
- 安全头:添加X-Frame-Options、X-Content-Type-Options等安全头
- 限流:配置Nginx限流,防止恶意攻击
3. 监控与告警
4. AI项目需要监控哪些指标?
- 业务指标:
- 对话次数、活跃用户数
- 平均响应时间、首字延迟
- 工具调用成功率、错误率
- token使用量、API调用次数
- 系统指标:
- CPU使用率、内存使用率、磁盘使用率
- 网络带宽、连接数
- 数据库QPS、慢查询数
- Redis命中率、内存使用率
- 大模型指标:
- 大模型API响应时间、错误率
- 并发请求数、排队数
- token消耗速度、成本
需要我帮你把这些面试题整理成一页纸速记版,方便你面试前快速复习吗?
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)