第 3 讲:依赖注入与服务架构(Injector 模式)

OpenHands 源码解析 · 第 3 讲

OpenHands 源码解析 第 3 讲:依赖注入与服务架构(Injector 模式) 基于 dev 分支源码 · 2026-07-30 一、为什么需要 Injector 模式 OpenHands 是一个 重型长任务 Agent ——一次会话可能持续数小时甚至数天。这意味着: 🔹 数据库连接不能每请求新建/销毁 🔹 沙箱生命周期要跨请求保持 🔹 用户上下文要在多个 Service 间共享 🔹 不同部署环境(本地 / GCP / SaaS)需要不同实现 OpenHands 没有用传统的 DI 框架(如 Dependency Injector、Injectable),而是用一套 自研的 Injector 抽象 ——轻量、异步原生、与 FastAPI 深度集成。 二、Injector 基类:一切服务的起点 所有 Service 的注入器都继承自 Injector[T] ,定义极其精简: 📄 openhands/app_server/services/injector.py (第 1-26 行) class InjectorState: """Mutable state shared across injectors within a single request.""" pass class Injector(Generic[T], ABC): """Base class for all dependency injectors.""" @abstractmethod async def inject( self, state: InjectorState, request: Request | None = None ) -> AsyncGenerator[T, None]: """Inject a dependency into the request context. Args: state: Mutable state shared across all injectors in this request. request: The FastAPI request object. Yields: An instance of type T to be injected.""" def context( self, state: InjectorState, request: Request | None = None ) -> AsyncContextManager[T]: """Wrap inject() as an async context manager.""" return asynccontextmanager(self.inject)(state, request) def depends(self, *, scope: str | None = None) -> Callable[..., T]: """Create a FastAPI dependency for this injector.""" return Depends(self.context, scope=scope) 核心设计: 🔹 inject() 是抽象方法——每个 Injector 子类必须实现,签名固定:接收 state + request,yield 出服务实例 🔹 context() 将 inject() 包装为 AsyncContextManager,可在任意 async with 中使用 🔹 depends() 生成 FastAPI 的 Depends 对象,直接挂到路由函数参数上 🔹 InjectorState 是一个空壳——纯粹作为请求级共享状态的"钩子",通过 setattr/getattr 挂载任意属性 三、InjectorState:请求级共享总线 InjectorState 本身是个空类,但它承载了整个请求生命周期内的共享状态。这是 OpenHands 的 "鸭子类型状态总线" 设计: 📄 openhands/app_server/services/db_session.py (第 313-341 行) DB_SESSION_ATTR = 'db_session' DB_SESSION_KEEP_OPEN_ATTR = '…

阅读全文:OpenHands 源码解析 第 3 讲