上一篇我们看了全景图。这一篇深入最底下的地基——
pkg/core。它不处理任何业务消息,只干两件事:把所有子系统分阶段组装成一个Application对象,以及把所有后台任务作用域化地托管起来。读懂这一篇,你就拿到了理解后续所有子系统「从哪被创建、归谁管」的钥匙。
LangBot 有近 30 个子系统管理器(平台、流水线、模型、插件、沙箱、持久化、各种 HTTP Service……)。它们之间有依赖顺序:配置必须先加载、日志必须先就绪、数据库必须先连上,业务管理器才能初始化。同时,启动后会有一堆永不退出的后台协程(监听平台、消费查询、跑 HTTP 服务……)需要统一创建、监控、按需取消。
pkg/core 用两个机制分别解决这两个问题:
TaskWrapper。main() 到 make_app()入口在 src/langbot/__main__.py,但真正的组装逻辑在 src/langbot/pkg/core/boot.py。核心是一个写死顺序的阶段列表:
# src/langbot/pkg/core/boot.py:17
stage_order = [
'LoadConfigStage',
'GenKeysStage',
'SetupLoggerStage',
'BuildAppStage',
'ShowNotesStage',
]
async def make_app(loop: asyncio.AbstractEventLoop) -> app.Application:
# ...
ap = app.Application()
ap.event_loop = loop
# Execute startup stage
for stage_name in stage_order:
stage_cls = stage.preregistered_stages[stage_name]
stage_inst = stage_cls()
await stage_inst.run(ap)
await ap.initialize()
return ap
注意几个细节,它们很能体现这个项目的风格:
Application 先被 new 出来,再被各阶段逐步「填充」字段。 ap 是一个贯穿始终的可变上下文对象,每个阶段往里塞东西。make_app 返回 ap,由 boot.main() 调用 ap.run() 进入运行态。每个阶段都是 BootingStage 的子类,通过装饰器注册进一个全局字典:
# src/langbot/pkg/core/stage.py:9
preregistered_stages: dict[str, typing.Type[BootingStage]] = {}
def stage_class(name: str):
def decorator(cls):
preregistered_stages[name] = cls
return cls
return decorator
class BootingStage(abc.ABC):
name: str = None
@abc.abstractmethod
async def run(self, ap: app.Application):
...
阶段实现都在 pkg/core/stages/ 目录下,文件名即职责:
flowchart LR
A["LoadConfigStage<br>load_config.py<br>读 data/config.yaml"]
B["GenKeysStage<br>genkeys.py<br>生成加密密钥"]
C["SetupLoggerStage<br>setup_logger.py<br>初始化日志"]
D["BuildAppStage<br>build_app.py<br>构建所有子系统"]
E["ShowNotesStage<br>show_notes.py<br>打印启动提示"]
A --> B --> C --> D --> E
前三个阶段是「准备地基」(配置 → 密钥 → 日志),最后一个是「打印须知」。真正的重头戏是第四个:BuildAppStage。