LazyLLM黑科技 | 不装全家桶也能跑?LazyLLM 的按需动态加载方案
如何才能在真正使用到某个依赖时动态加载该依赖呢? 在较为复杂的 Python 项目中,常见问题是:代码尚未进入核心逻辑,运行环境会因为依赖冲突、缺失或版本不兼容而失败;不同模块往往依赖不同的第三方库。若将所有依赖统一安装,环境会迅速膨胀;若仅安装部分依赖,又容易在执行过程中因缺少依赖而中断。更进一步,即使框架自身依赖关系已经梳理清楚,也仍可能与用户本地环境产生版本或分发差异。这是 Python 生态中常见的依赖管理上的典型挑战。 业界常见的解决方案以及难点: 1️⃣全量安装依赖 2️⃣在用到某依赖时动态import 3️⃣缺少依赖或依赖冲突的导致的问题直接打印在海量日志中:关键依赖缺失信息会淹没在其他日志中,增加用户修复环境的成本。 LazyLLM 采用按需加载策略:未使用的功能不预先要求安装 ;当用户首次调用相关能力时,框架对该功能组的依赖进行集中校验。 以 rag 为例,当用户首次使用相关能力时,LazyLLM 会: 1.一次性检查该功能组所需的全部依赖 2.明确列出缺失的包列表 3.给出统一的安装指令:lazyllm install rag 依赖组的版本约束由 LazyLLM 的预置逻辑管理,用户无需手动查阅兼容矩阵或推断版本组合。总体体验可以概括为:未使用的能力不引入依赖;使用时一次性补齐依赖并可直接继续运行。 下文进入实现细节👇 LazyLLM 的延迟加载三层模型: 接下来依次揭开他们的神秘面纱👇 1️⃣顶层懒加载:lazyllm.__getattr__ 加载顶层模块时直接加载所有依赖。 通过__getattr__ 实现动态加载用户想要加载的子模块,在模块名合法的情况下动态调用该子模块内部的依赖加载流程,能让用户省略子模块路径。 getattr 的作用:当用户访问不存在的属性时,python会调用 obj.__getattr__(name) 以动态实现属性加载逻辑。 👉顶层 API 暴露完整,但实际导入延后到首次访问 2️⃣工具子模块懒加载:lazyllm.tools.__getattr__ 加载子模块时直接加载所有依赖。 与顶层__init_.py 类似,lazyllm.tools 也不会一次性导入所有子模块,而是根据名称映射到具体模块并按需加载。 其中_SUBMOD_MAP, _SUBMOD_MAP_REVERSE 用于指定"类名 → 子模块"之间的映射。 当用户编写 from lazyllm.tools import Document 时,才会实际导入 lazyllm.tools.rag。 3️⃣依赖集中检查:子模块导入时统一检测 👉实际使用依赖时才暴露缺少依赖。 👉多个同时需要的依赖出现异常时,提示分散在不同的场景、log、时间等维度。 在子模块被导入时触发整个依赖组的检查。 👉对功能组进行整体校验 LazyLLM 将功能组(如 rag、rag-advanced、agent-advanced)的依赖声明在 pyproject.toml 的 extras 中,同时 lazyllm install 基于同一份配置解析并安装对应版本约束。因此,报错信息与安装命令在同一来源上生成:提示缺什么、安装什么、版本约束是什么保持一致。 👉提供明确的安装命令 4️⃣BONUS: 第三方包的延迟导入封装 当用户确实需要使用特定的三方依赖时也可以复用lazyllm中的延迟加载逻辑 LazyLLM 对第三方库(例如 torch、transformers)也提供了延迟加载封装。在lazyllm.thirdparty 中使用__getattribute__ 动态检查依赖的安装情况。 👉该机制将"运行到一半才报错"的问题前置到"首次使用即报错",并提供可直接执行的安装建议(包含版本约束时亦可体现)。 👉这样导入时 from lazyllm.thirdparty import transformers,lazyllm并不会立即导入真实库;直到首次访问其属性时才进行导入,并在缺失时给出明确的安装建议。 情况1️⃣:如果你想仅使用最简功能,可以仅加载顶层模块 核心模块属于 lazyllm 的基础依赖,不需要额外安装。 情况2️⃣:如果你的开发涉及 RAG,可以通过顶层模块加载子模块(推荐方式) 首次导入 Document 时会触发 rag 依赖检查。若缺少依赖,将提示: 然后用户可执行安装命令一次性补全依赖: 情况3️⃣:如果你清楚的知道自己需要什么,可以直接使用子模块 该方式会直接导入 lazyllm.tools.rag,因此依赖检查会立即触发。 情况4️⃣:如果你需要使用全部 RAG 或 Agent的能力,则需要手动安装相关依赖组 若使用向量数据库、Embedding 微调或 MCP 等高级能力,可安装对应扩展组: 这些扩展组来自 pyproject.toml 的 extras 配置,安装时会自动应用版本约束,通常无需人工拼装依赖版本。更多可安装的依赖场景见官网**安装引导文档(https://docs.lazyllm.ai/zh-cn/stable/)**或项目根目录中 pyproject.toml 的配置。 情况5️⃣:如果你想使用某个具体的第三方库,可以使用lazyllm.thirdparty 导入 若缺少依赖,会直接提示执行带版本约束的 pip install ...。 通过上面的介绍,相信你已经对 LazyLLM 这套"按需加载 + 集中检查"的依赖管理思路有了更直观的认识:不用的能力不必提前安装,真正用到时再一次性把依赖校验清楚,并给出可直接执行的安装指令。 简单回顾一下它解决的核心问题: 如果你也在维护一个功能模块多、依赖差异大的 Python 项目,这种设计思路通常会带来非常实际的收益:启动更快、环境更稳、问题暴露更早,用户使用门槛也更低。 欢迎升级体验 LazyLLM最新版本,请大家去github上点一个免费的star,支持一下~(也欢迎关注LazyLLM gzh哦!) LazyLLM项目仓库链接🔗:背景|依赖增多导致环境复杂度上升
import lazyllm
# 只用核心能力时,我们不希望触发 tools/rag 依赖检查
chat = lazyllm.OnlineChatModule(source="openai")
print("core ok")
# 一旦访问 Document,才会触发懒加载链路:
# lazyllm.Document
# -> import lazyllm.tools
# -> import lazyllm.tools.rag
# -> check_dependency_by_group('rag')
from lazyllm import Document # 若缺少 rag 依赖,会在这里抛 ImportError难点|依赖库难以管理,且相关报错不友好
import lazyllm
# 如果此句直接加载全量依赖会耗时很长,且会导致环境臃肿。用户不会用到的依赖白白占用空间。def func():
from lazyllm import OnlineChatModule
pass
# 调用到func时再加载某些依赖会过晚暴露问题,增加开发难度,降低开发者的用户体验解决方案|按需加载与集中检查相结合
机制总览

# 文件:lazyllm/__init__.py
def __getattr__(name: str):
if name == 'tools': # 调用中间模块的加载逻辑
return importlib.import_module('lazyllm.tools')
elif name in __all__:
tools = importlib.import_module('lazyllm.tools')
builtins.globals()[name] = value = getattr(tools, name) # 导入后会缓存导入结果以避免后续重复导入
return value
raise AttributeError(f"module 'lazyllm' has no attribute '{name}'") # 保证加载在顶层init中声明/暴露出来的子模块
👉导入后写入 globals(),后续访问几乎无额外开销# lazyllm/tools/__init__.py
def __getattr__(name: str):
if name == 'fc_register':
agent = import_module('.agent', package=__package__)
globals()['fc_register'] = value = agent.register
elif name in _SUBMOD_MAP:
return import_module(f'.{name}', package=__package__)
elif name in _SUBMOD_MAP_REVERSE:
module = import_module(f'.{_SUBMOD_MAP_REVERSE[name]}', package=__package__)
globals()[name] = value = getattr(module, name)
return value_SUBMOD_MAP = {
'rag': ['Document', 'Reranker', 'Retriever', 'SentenceSplitter', 'LLMParser'],
'agent': ['ToolManager', 'FunctionCall', 'ReactAgent', 'PlanAndSolveAgent', 'ReWOOAgent'],
'sql': ['SqlManager', 'MongoDBManager', 'DBManager', 'DBResult', 'DBStatus'],
# 其他模块略
}# lazyllm/tools/rag/__init__.py
from lazyllm.thirdparty import check_dependency_by_group
check_dependency_by_group('rag') # 模块init中指定该子模块隶属于哪个依赖组
# lazyllm/thirdparty/__init__.py
def check_dependency_by_group(group_name: str):
missing_pack = []
for name in load_toml_dep_group(group_name): # 依赖分组信息直接从整个工程的配置toml文件中获取
real_name = package_name_map_reverse.get(name, name)
if not (config['init_doc'] and real_name in module_names or check_package_installed(real_name)):
missing_pack.append(name)
if len(missing_pack) > 0:
msg = f'Missing package(s): {missing_pack}\nYou can install them by:\n lazyllm install {group_name}'
LOG.error(msg) # 提示安装依赖组的命令
raise ImportError(msg)
👉一次性列出缺失依赖# lazyllm/thirdparty/__init__.py
class PackageWrapper(object):
def __getattribute__(self, __name):
if self._Wrapper__lib is None:
try:
self._Wrapper__lib = importlib.import_module(self._Wrapper__key, package=self._Wrapper__package)
except ImportError:
pip_cmd = get_pip_install_cmd([self._Wrapper__key])
err_msg = f'Cannot import module `{self._Wrapper__key}`, please install it by `{pip_cmd}`'
raise ImportError(err_msg) from None
return getattr(self._Wrapper__lib, __name)使用方式(覆盖常见场景)
import lazyllm
llm = lazyllm.OnlineChatModule(source="openai")from lazyllm import Document, Retriever
docs = Document(dataset_path="/path/to/data", embed=lazyllm.OnlineEmbeddingModule())
retriever = Retriever(docs)Missing package(s): [...]
You can install them by:
lazyllm install raglazyllm install ragfrom lazyllm.tools.rag import Document, SentenceSplitterlazyllm install rag-advanced
lazyllm install agent-advancedfrom lazyllm.thirdparty import transformers
tokenizer = transformers.AutoTokenizer.from_pretrained("...")小结