0

知了-FastAPI+LangChain打造智能招聘系统2026教程

一人一套
1月前 13

获课:xingkeit.top/16272/


底层干货:FastAPI 依赖注入管理 LangChain 大模型调用会话资源

从混乱到优雅:一个差点失控的 AI 项目

半年前,我们的 AI Agent 项目遇到了一个典型的“成长烦恼”。最初的版本只有三五个 API 接口,每个接口各自创建 LangChain 的 LLM 实例、各自管理对话历史、各自处理工具调用。代码虽然冗余,但还能跑。

但随着业务扩张,接口数量涨到了二十多个,问题开始爆发了。每个请求都重新加载一次模型配置、重复初始化相同的工具链、对话历史散落在各个角落无法共享。 更可怕的是,资源泄漏导致内存一天比一天高,服务每 48 小时就必须重启一次。

当时团队内部争论不休,有人提议重构整个架构,有人建议换框架。最后我们用了一个非常“Java”的思路解决了 Python 项目的问题——依赖注入。FastAPI 内置的依赖注入系统,配上 LangChain 的资源管理,让整个项目从一团乱麻变得井井有条。

为什么 LangChain 调用需要“管起来”

LangChain 的调用看起来简单——创建一个 ChatOpenAI 对象,调用 predict 方法就完事了。但真实场景里,一个 AI Agent 涉及的可不止这么点东西:

模型实例本身是有状态的,不同的模型配置(temperature、max_tokens、model_name)需要不同的实例。每个用户会话要独立维护对话记忆,不同场景需要挂载不同的工具集合。这些资源如果每次请求都从头创建,CPU 和内存的开销会非常可观。

更关键的是资源释放。LangChain 底层会创建 HTTP 连接池、向量检索客户端、数据库连接等。如果这些资源不统一管理,泄漏就是必然结果。你每创建一个 ChatOpenAI 对象,背后就可能多了一个未关闭的 aiohttp 连接。

FastAPI 依赖注入的妙处:声明你需要什么,而不是去拿什么

传统的做法是“主动获取”——在函数里直接 new 一个对象出来。依赖注入反过来,变成“声明需要”——你在函数签名里说你需要什么,框架帮你准备好。

FastAPI 的 Depends 机制特别适合做这件事。你把资源的创建逻辑封装成一个个“依赖函数”,然后在路由函数里声明需要哪些依赖。 框架会保证每个请求进来时,依赖被正确地创建、传递,并且在请求结束时做好清理。

这个模式带来的最直接的好处是:路由函数变得极其干净。 它只关心业务逻辑,不关心模型从哪来、连接池怎么管、配置怎么加载。每个依赖的创建和销毁都有明确的生命周期管理,想泄漏都难。

会话资源的三种生命周期

在实际项目中,我把 LangChain 相关的资源分成了三个层次来管理:

应用级单例——模型配置、全局的 Embedding 模型、向量数据库客户端。这些资源整个应用生命周期只需要一份,创建一次,到处复用。用 FastAPI 的 lifespan 事件来做初始化和清理最合适。

请求级资源——每一次 HTTP 请求独立使用的资源。比如当前请求的 LangChain 回调处理器、日志上下文、请求追踪 ID。这些资源跟随请求创建,请求结束就销毁。

会话级资源——这是 AI Agent 项目里最特殊的部分。同一个用户的多轮对话共享同一个 ConversationBufferMemory 或者 VectorStoreRetriever。会话资源既要跨请求复用,又要隔离不同用户,还不能无限增长导致内存爆炸。

依赖注入正好能处理这种“既要又要”的场景。通过自定义依赖函数,我可以根据请求里的 session_id 参数,从会话池里获取或创建对应的记忆对象,然后在请求结束时决定是保留还是销毁。

从混乱到清晰:重构前后的对比

重构之前,每个接口函数里都有大段的初始化代码:

text
创建 LLM 实例 → 创建 Memory → 创建 Agent → 绑定工具 → 执行业务

二十个接口,这段代码重复了二十遍。改一个配置参数要改二十个地方,漏掉一个就会产生不一致的行为。

重构之后,这些初始化逻辑全部被抽到了依赖函数里。路由函数从平均 80 行缩减到了不到 20 行,只剩下核心的业务调度逻辑。新增一个接口只需要声明依赖,不需要复制粘贴任何初始化代码。

更关键的是可测试性大幅提升。依赖注入让 Mock 变得异常简单——测试的时候替换一个依赖函数,整个接口的行为就可以被完全控制,再也不需要真的去调大模型 API 做单元测试了。

资源隔离:多租户场景的救命稻草

我们的系统服务多个客户,每个客户有自己的模型偏好、独立的知识库、分开的对话历史。如果用全局单例,所有客户的数据就会混在一起——这是绝对不能接受的。

依赖注入的请求级隔离能力在这里发挥了关键作用。每个请求进来时,根据请求头里的 tenant_id 动态加载对应的配置、绑定对应的知识库、创建独立的 Memory 实例。不同租户的数据在内存层面就是隔离的,互不干扰。

而且这个逻辑完全封装在依赖层,路由函数里一行租户判断的代码都没有。业务代码只关心“当前会话”,不关心“哪个租户的当前会话”。

性能优化:池化和复用

资源的创建是有成本的。创建一次 LangChain Agent 需要加载工具定义、编译 Prompt 模板、初始化推理链路,这个过程在复杂场景下可能要花几百毫秒。

通过依赖注入结合池化策略,我把高频复用的资源做了缓存。同一个会话 ID 的多轮请求复用同一个 Agent 实例,跳过了重复初始化的开销。 在测试中,第二轮及以后的请求延迟比第一轮降低了 70% 以上。

同时也要防止无限缓存导致的内存膨胀。我设了最大会话数限制,配合 LRU 淘汰策略,确保内存占用可控。这个过程里,依赖注入的工厂函数负责检查缓存、创建新对象、注册销毁回调,把复杂度全部隔离在依赖层。

写在最后:架构的整洁,从资源管理开始

很多做 AI 应用的同学关注的是 Prompt 怎么写、模型怎么调、RAG 怎么优化,这些都是对外的能力。但一个项目能不能长期稳定地跑下去,往往取决于那些看不见的“内功”——资源怎么创建、怎么复用、怎么释放、怎么隔离。

FastAPI 的依赖注入加上 LangChain 的资源管理模式,让我学会了一件事:好的架构不是把复杂的东西做复杂,而是把复杂的东西藏起来,让业务代码保持简单。

现在每次 review 新同事写的 AI 接口,我都会先看两件事:资源有没有做依赖注入管理,会话有没有做生命周期控制。这两件事做好了,项目再大也不乱。

框架会过时,模型会迭代,但“管理好你的资源”这条原则,永远不会过时。



本站不存储任何实质资源,该帖为网盘用户发布的网盘链接介绍帖,本文内所有链接指向的云盘网盘资源,其版权归版权方所有!其实际管理权为帖子发布者所有,本站无法操作相关资源。如您认为本站任何介绍帖侵犯了您的合法版权,请发送邮件 [email protected] 进行投诉,我们将在确认本文链接指向的资源存在侵权后,立即删除相关介绍帖子!
最新回复 (0)

    暂无评论

请先登录后发表评论!

返回
请先登录后发表评论!