6.4 KiB
id, title, type, domain, stack, tags, level, related, week
| id | title | type | domain | stack | tags | level | related | week | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| python.fastapi.interview | FastAPI 面试题 | interview | python |
|
|
intermediate |
|
|
FastAPI 面试题
基础
【基础】FastAPI 有什么特点?为什么 AI 服务常用它?
参考答案:基于 Starlette(ASGI)和 pydantic,原生支持 async,适合 IO 密集的模型调用;通过类型标注自动完成参数解析和校验、生成 OpenAPI 文档;依赖注入方便组织鉴权、数据库会话和测试替换;支持流式响应和 WebSocket。AI 生态(模型 SDK、LangChain、向量库客户端)以 Python 为主,用 FastAPI 封装服务很自然。
追问:FastAPI 和 Flask、Django 怎么选?→ AI 服务、异步 I/O、需要自动接口文档时选 FastAPI;需要完整后台管理和 ORM 的传统业务系统可以选 Django。
【基础】FastAPI 怎么区分路径参数、查询参数和请求体?
参考答案:在路径模板中出现的参数是路径参数;简单类型(int、str、bool 等)的其他参数是查询参数;pydantic 模型类型的参数是请求体;请求头、Cookie 用 Header()、Cookie() 显式声明。也可以用 Path()、Query()、Body() 显式指定并添加校验规则。
追问:请求体可以是多个 Pydantic 模型吗?→ 可以,多个模型参数会按参数名嵌套在 JSON 中;单个模型参数想嵌套时用 Body(embed=True)。
【基础】路由函数用 async def 和 def 有什么区别?
参考答案:async def 在事件循环中直接执行,里面只能调用非阻塞的异步代码;普通 def 会被放到线程池中执行,适合调用同步阻塞库。在 async def 中调用阻塞函数会卡住整个事件循环,是最常见的性能问题。
追问:def 路由的线程池大小有限制吗?→ 有,默认由 AnyIO 的线程池限制(默认 40 个线程,以官方文档为准),大量阻塞调用会排队。
【基础】响应模型有什么作用?
参考答案:声明接口返回的数据结构:自动过滤掉未声明的字段(防止泄露内部字段),校验和序列化返回数据,并体现在 OpenAPI 文档中,前端可以据此生成类型。
追问:怎么避免把密码等字段返回给前端?→ 定义单独的输出模型(不包含敏感字段)并设置为 response_model,而不是直接返回数据库对象。
进阶
【进阶】Depends 的原理和常见用途?
参考答案:FastAPI 在处理请求时解析路由函数签名,递归解析依赖树,按顺序调用依赖函数并把返回值注入参数;同一请求内相同依赖默认只调用一次;带 yield 的依赖可以在请求结束后执行清理。用途:鉴权和获取当前用户 / 租户、数据库会话、分页参数、共享客户端;测试时通过 dependency_overrides 替换。
追问:依赖中的 yield 有什么用?→ yield 之前创建资源(数据库会话),之后清理资源;测试时可以用 app.dependency_overrides 替换依赖。
【进阶】中间件和依赖都能做鉴权,怎么选?
参考答案:中间件作用于所有请求,适合与业务无关的横切逻辑:请求 ID、日志、耗时、CORS。依赖按路由或路由器声明,能拿到类型化的结果(当前用户)并注入处理函数,能在测试中替换,适合鉴权、权限、租户。通常鉴权用依赖。
追问:中间件能访问路由的参数吗?→ 中间件在路由匹配之前执行,拿不到解析后的参数;需要按路由做权限控制的,用依赖。
【进阶】怎么实现大模型的流式输出?要注意什么?
参考答案:用 StreamingResponse 包装异步生成器,media_type="text/event-stream",每个事件 data: ...\n\n。注意:响应头发出后不能改状态码,错误要以事件形式发送;检查客户端断开及时停止生成;内容用 JSON 编码避免换行破坏格式;经过 Nginx 时关闭缓冲;前端 POST 请求需要用 fetch 读取流。
追问:客户端中途断开,服务端还在调用大模型怎么办?→ 在生成器中检查 await request.is_disconnected(),或者依赖取消机制,及时停止上游调用以节省成本。
场景题
【场景题】压测发现 FastAPI 服务 QPS 很低,CPU 也不高,可能是什么原因?
参考答案:最常见的是在 async def 路由里调用了阻塞操作(同步数据库驱动、requests、time.sleep、同步 SDK),事件循环被阻塞。其他原因:每个请求新建 HTTP 客户端或数据库连接、连接池太小、下游(模型、数据库)本身慢、只启动了一个 worker。排查:看慢请求的耗时分布,用 py-spy 看事件循环线程在做什么。
追问:怎么验证是不是事件循环被阻塞了?→ 开启 asyncio 的调试模式(会记录执行过慢的回调),或者在压测时观察一个简单的 /health 接口延迟是否也升高。
【场景题】一个接口依赖大模型和向量数据库,怎么写单元测试?
参考答案:把模型客户端和检索器抽象成接口(Protocol),通过依赖注入获取;测试中用 app.dependency_overrides 替换为假实现,返回固定内容或模拟异常;用 TestClient 调用接口,断言状态码、响应格式、流式事件顺序、错误处理。真实模型的效果评估另外做(评测集),不放在单元测试里。
追问:测试流式接口要注意什么?→ 用 TestClient 的 stream 方法或 httpx 异步客户端逐块读取,断言事件格式和结束标记,而不只是最终文本。
【场景题】给多个业务系统提供 AI 能力,怎么防止某个系统把额度用光?
参考答案:每个调用方一个 API Key,鉴权依赖解析出租户;在服务端按租户做限流(Redis 滑动窗口 / 令牌桶)和配额(按日 / 月的 token 数);记录每次调用的 token 用量写入数据库;超过配额返回 429 并给出提示;对异常增长告警。Nginx 层可以再加一层按 IP 的粗粒度限流。
追问:额度用完时返回什么?→ 返回 429 和明确的错误码,带上 Retry-After 头;同时通知对应业务方,提供额度查询接口。