大家好,我是大志。
在 AI Agent 面试中,除了 LangChain、RAG、MCP 等相关知识点外,Python 相关基础知识也是面试官重点考察的内容。
其中,出现频率最高的几个知识点就是 FastAPI、Flask、异步编程、asyncio、协程、线程,以及 WebSocket 和 SSE。这些知识不仅会考察概念,更重要的是结合实际项目,考察面试者是否真正理解这些技术点的用法和使用场景。
这篇文章,我会结合实际开发经验,对这些问题进行系统梳理。
1、FastAPI 和 Flask 有什么区别?
FastAPI 和 Flask 都是常用的、使用 Python 开发的 Web 框架,但它们使用的场景有所不同。
Flask 是一个更轻量级的 Web 框架,生态非常成熟,适合快速迭代开发小型项目,但是它主要支持同步 API,整体并发能力不如 FastAPI。
FastAPI 是一个基于 Starlette 和 Pydantic 构建的 Web 框架,它原生支持 ASGI 和异步编程。FastAPI 还天然支持 Swagger 文档,方便 API 联调,因此,在需要高并发、开箱即用的中大型项目选择 FastAPI 更加适合。
2、FastAPI 为什么适合 Agent 项目?
一个 AI Agent 应用通常有以下特点:
- IO 密集:一次请求可能涉及多个如 LLM 调用、
RAG 检索、数据库查询、外部工具调用等操作。 - 结构化输入输出:在 Agent 应用的相关请求中,通常包含
message、session_id、权限数据等信息,需要结构化数据进行校验。 - 流式输出:LLM 生成答案往往耗时较长,为了保证用户体验,需要通过流式输出来返回结果。
- 依赖管理:需要对认证数据库、
Redis、向量数据库等依赖进行依赖注入。 - API 文档:Agent 应用需要对外提供 API 文档方便前端或者其他客户端调用。
因为 FastAPI 对这些需求支持比较好,所以 FastAPI 更适合 Agent 项目。
3、Python 异步编程如何实现?
在 Python 中,异步编程主要依赖 async、await 和 asyncio 事件循环。
在 Python 中,可以使用 async 来定义异步函数:
asyncdefget_data(url: str):await asyncio.sleep(2) # 模拟网络请求return {"data": f"{url} 请求执行完成"}
在调用异步函数时,代码不会立即执行,而是会返回一个协程对象。并且协程需要通过事件循环调度来执行。其中 await 关键字表示协程需要等待异步操作执行完成。在等待期间,事件循环可以调度其他协程。
asyncdefmain(): result = await get_data("https://api.test.com/v1/a") print(result)asyncio.run(main())
除此之外,多个异步方法可以并发执行:
import asyncioimport datetimeasyncdefget_data(url: str):await asyncio.sleep(2) # 模拟网络请求return {"data": f"{url} 请求执行完成"}asyncdefmain():await asyncio.gather( get_data("https://api.test.com/v1/a"), get_data("https://api.test.com/v1/b"), get_data("https://api.test.com/v1/c") )print(f"开始时间:{datetime.datetime.now()}")asyncio.run(main())print(f"结束时间:{datetime.datetime.now()}")
执行结果如下,三个异步方法调用 3 次,每次耗时 2s,但是使用异步编程就可以做到 2s 全部执行完成。
执行结果在 FastAPI 中,请求路由可以直接定义为 async def。框架内部会在 ASGI 运行环境中处理事件循环、调度请求。
4、asyncio 原理是什么?
asyncio 的核心实现机制是事件循环(Event Loop)。事件循环本质上可以理解为一个任务调度器,它在内部维护了一组协程任务,当某个任务遇到 await 需要等待 IO 时,事件循环就将这个任务挂起,切换去执行其他任务,等 IO 结束后,再恢复原来的任务执行。
asyncio 需要协程在 await 处主动让出控制权,事件循环才能调度执行其他任务,因此在异步方法中,不要直接执行阻塞操作。比如,在协程中用 time.sleep(10) 会阻塞整个事件循环,会导致其他任务也无法继续处理。所以应该使用 asyncio.sleep,或者把阻塞任务放到线程池中执行。
5、协程和线程有什么区别?
协程和线程都可以实现异步任务,但两者的调度方式、资源消耗、使用场景有所不同。
线程由操作系统调度,线程的切换是抢占式的。多个线程可以分配到不同 CPU 核心执行,但 CPython 中受 GIL(Global Interpreter Lock,全局解释器锁)影响,同一时刻只能有一个线程执行 Python 字节码,因此在处理 CPU 密集型任务时,无法通过多线程达到理想的效果。
协程是由事件循环进行调度的,任务间切换是协作式的。协程只有在遇到 await 时,才主动让出控制权,继续执行其他协程。
线程需要操作系统分配栈空间,创建和切换线程的成本较高。而协程更轻量,创建和切换成本较低。IO 密集型任务较多的系统,可以使用大量的协程任务。
线程适合并发数量不高、使用同步库(如 requests)或不方便改造成异步的 IO 密集型等场景。由于 CPython 存在 GIL,所以线程不适合 CPU 密集型计算。
协程更适合网络请求、LLM 调用、访问数据库、访问 Redis、调用第三方 API 等 IO 密集任务。
多线程开发需要关注锁、竞争条件、死锁等问题,使用起来比较复杂,而协程在单线程事件循环中运行,通过 await 主动让出执行权,一个线程就可以管理成百上千个并发任务。
6、WebSocket 和 SSE 区别是什么?
WebSocket 和 SSE 都可以实现服务器实时推送数据,但两者的通信模型不同。
SSE(Server-Sent Events) 本身是基于 HTTP 协议实现的,SSE 是服务器到客户端的单向推送。客户端通过一个普通 HTTP 请求建立连接,服务器可以持续返回数据,因此,非常适合 LLM 流式输出、日志推送、任务进度等场景。它实现简单,但是客户端不能主动发送数据给服务器端。
WebSocket 则是在 HTTP 握手完成后,升级为 WebSocket 协议,并建立了一个全双工的通信通道。连接建立之后,客户端和服务器端都可以随时主动发送消息,适用于在线聊天、实时游戏等需要频繁双向通信的场景。
在 AI 应用中,绝大多数场景都是将 LLM 生成的结果持续地推送给客户端,所以通常使用 SSE;如果 Agent 应用运行过程中需要客户端实时发送数据、确认操作等双向交互,则更适合使用 WebSocket。
本期 Python 相关面试题 就到这里。后面我会 每周至少更新 1 期面试题系列,想看后续 AI Agent 进阶面试题 的朋友,欢迎关注「大志说编程」!
觉得有用的话,转发给正在面试的小伙伴,咱们下期见~