跳到主要内容

Provider 架构:注册、解析与自动路由装配

30 秒导读: OGX 对外只暴露一套稳定的 API(inference、vector_io、tool_runtime……),背后真正干活的是可插拔的 provider(OpenAI、vLLM、Faiss、Brave Search……)。这一章讲清楚一件事:一份 YAML 配置,是怎么变成一个「活的、可调用的实现字典」的——中间要经过 provider 声明(spec)、注册表收集、依赖拓扑排序、动态实例化、协议校验,还要为几个特殊 API 自动装配「路由表 + 路由器」这对搭档。

本章在全书的位置: 先读 index 建立全景、01 请求生命周期 了解服务器骨架(resolve_impls 就是在服务器启动时被调用的)。本章讲装配——把 provider 装进一个 impls 字典;运行时那个字典里的 Router 到底怎么查表、把一次模型调用落到某个后端,是 03 路由层与 OpenAI 适配 的事,本章不展开。


1. 这章解决什么问题(先建直觉)

一句话定义: OGX 是一台「OpenAI 兼容的 API 服务器」,但它自己不实现推理、不实现向量库——它把每个 API 的实现权外包给 provider,再用一套解析器把你选中的 provider 装配起来。

为什么需要这层抽象? 想象你写了一堆代码调 OpenAI SDK。今天想换成本地 vLLM,明天想接 Anthropic,后天要在向量检索里换 Faiss 为 pgvector。如果这些后端的差异会渗透到你的业务代码,那每换一次都要改代码。OGX 的答案是:

  • API 是契约(一个 Python Protocol,如 Inference),永远不变;
  • provider 是实现(一个 adapter 类),可随便换;
  • 换后端 = 改一行 YAML,业务代码一个字不动。

一句话类比: 把它想成操作系统的驱动模型。应用程序调的是统一的「打印」系统调用(= API),具体是惠普还是佳能打印机由驱动(= provider)负责;开机时内核扫描并加载驱动(= 本章的注册表 + 解析器),把「打印」这个调用接到真实硬件上。

这一章的主线,就是把下面这条链路讲透:

run config (YAML) ──► 一个可调用的 impls 字典 { Api.inference: <某个活的实现>, ... }

本节不碰代码;下面开始拆。


2. 顶层全景:从 YAML 到 impls 字典

先给一张「怎么读」的图:从左到右是启动时的装配顺序,数据一路被加工,最终产出右边那个 impls 字典

你写的 代码里"有哪些 把两者对上、排序、 启动完成后
run config provider 可选" 实例化、校验
(YAML) (注册表)

┌──────────┐ ┌──────────────┐ ┌────────────────────┐ ┌──────────────┐
│StackConfig│ ───► │provider │ ───► │ resolve_impls │ ───► │ impls 字典 │
│.providers │ │ registry │ │ ①校验 ②补路由 │ │ {Api: 实现} │
│ 每个 API │ │ {Api:{类型: │ │ ③拓扑排序 ④import │ │ │
│ 选哪些 │ │ spec}} │ │ ⑤实例化 ⑥协议校验 │ │ 交给服务器路由│
└──────────┘ └──────────────┘ └────────────────────┘ └──────────────┘
│ ▲
│ │ available_providers()
│ ┌──────┴───────┐
└─(选择的类型) │ registry/ │ 每个 API 一个文件:
│ inference.py│ inference.py / vector_io.py / tool_runtime.py …
│ vector_io.py│
└──────────────┘

部件一句话职责:

部件干什么在哪
StackConfig运行配置的根对象:providers 里写「每个 API 用哪些 provider」core/datatypes.py:844
ProviderSpec(及子类)一个 provider 的「身份证」:类型、模块路径、config 类、依赖、pip 包ogx_api/datatypes.py:228
available_providers()每个 API 一个文件,返回该 API 所有内置 provider 的 spec 列表providers/registry/*.py
get_provider_registry收集所有 API 的 spec,拼成 {Api: {provider_type: spec}}core/distribution.py:101
builtin_automatically_routed_apis声明三对「路由表↔路由器」配对core/distribution.py:59
resolve_impls解析引擎:校验 → 补自动路由 → 拓扑排序 → 实例化core/resolver.py:147
distributions/*预制发行版:半自动生成的 config.yaml + <name>.pydistributions/starter/

主线走一遍(高层): 服务器启动 → 读 YAML 得到 StackConfigget_provider_registry() 把代码里所有可选 provider 收成一张表 → resolve_impls() 拿着「你选的」和「所有可选的」对账、排序、逐个 importlib.import_module 并调工厂函数,得到实例 → 校验实例确实实现了 API 协议 → 返回 impls 字典交给服务器。装配就完成了。


3. Provider Spec:provider 的「身份证」

本节讲:一个 provider 在被实例化之前,是怎么用一份纯声明(不含任何实现代码)描述自己的。

3.1 基类 ProviderSpec

所有 provider 都先是一份 ProviderSpec——一个 Pydantic 模型,只有元数据,没有行为。关键字段:

字段含义
api这个 provider 实现的是哪个 API(Api.inference 等)
provider_type唯一类型名,如 remote::openaiinline::sentence-transformers
config_class全限定类名字符串,指向这个 provider 的 Pydantic config 类
module全限定模块名字符串,该模块里应有工厂函数 get_adapter_impl / get_provider_impl
api_dependencies依赖的其它 API(如 file-search 依赖 vector_io+inference+files)
optional_api_dependencies可选依赖(有就用,没有也能起)
pip_packages这个 provider 需要的 pip 依赖(用于 build/list-deps)
deprecation_warning / deprecation_error弃用告警/弃用报错(校验时触发)
toolgroup_id仅 tool_runtime 用:该 provider 服务的内置工具组 ID(如 builtin::websearch)

真实定义见 ogx_api/datatypes.py:228 class ProviderSpec。注意 config_classmodule 都是字符串——这是整套架构能「延迟加载」的关键:spec 只是写下「去哪 import」,真正的 import 推迟到解析阶段才发生(见 §6)。

字段里还藏着一个内部 hack:

# ogx_api/datatypes.py:283 # 真实源码
# used internally by the resolver; this is a hack for now
deps__: list[str] = Field(default_factory=list)

deps__ 是解析器内部临时用来做拓扑排序的依赖列表,不是给用户填的。§6 会看到它被怎么塞值、怎么用。

3.2 两种「用户可见」的 spec:Inline vs Remote

面向用户的 provider 分两类,对应两个子类:

InlineProviderSpecRemoteProviderSpec
语义进程内内置实现(库直接跑在服务器里)通过 adapter 连到外部服务
类型名前缀inline::<name>remote::<adapter_type>
工厂函数get_provider_impl(config, deps)get_adapter_impl(config, deps)
独有字段container_image(可选容器镜像)adapter_type(适配器唯一标识)
例子inline::sentence-transformersinline::file-searchremote::openairemote::vllmremote::brave-search

定义分别在 ogx_api/datatypes.py:298(InlineProviderSpec)与 :337(RemoteProviderSpec)。RemoteProviderSpeccontainer_image 直接被固定为 None(remote 不带镜像)。

一段注册声明长这样(这是真实源码,不是示意):

# providers/registry/inference.py:54 RemoteProviderSpec
RemoteProviderSpec(
api=Api.inference,
adapter_type="cerebras",
provider_type="remote::cerebras",
pip_packages=[],
module="ogx.providers.remote.inference.cerebras",
config_class="ogx.providers.remote.inference.cerebras.CerebrasImplConfig",
provider_data_validator="...CerebrasProviderDataValidator",
description="Cerebras inference provider ...",
),

一眼看懂:它没写任何实现,只写了「我是谁、去哪 import、配置类是哪个」。

3.3 两种「内部专用」的 spec:AutoRouted 与 RoutingTable

还有两个子类,用户永远不会在 YAML 里写,是解析器给自动路由 API 现造的(§5、§6):

spec代表的 API 例子干什么
AutoRoutedProviderSpec/inference/vector_io「路由器」侧:请求进来后按 key 分发到某个真实 provider
RoutingTableProviderSpec/models/vector_stores「路由表」侧:管理「哪个 model id / store 属于哪个 provider」的注册表

它们定义在 core/datatypes.py:102(AutoRoutedProviderSpec)与 :117(RoutingTableProviderSpec)——注意是在 core 而非 ogx_api,因为这纯属实现细节。两者的 module 都固定指向 ogx.core.routers。为什么要成对出现,下一节讲。


4. 注册表:每个 API 一个 available_providers()

本节讲:代码里「一共有哪些 provider 可选」这张表是怎么攒出来的。

4.1 一个 API 一个文件

src/ogx/providers/registry/每个 API 一个 .py 文件,每个文件导出一个 available_providers(),返回该 API 的全部内置 spec:

providers/registry/
inference.py → available_providers() 返回 ~25 个推理 provider
vector_io.py → Faiss / sqlite-vec / pgvector / qdrant / ...
tool_runtime.py → file-search / brave-search / bing-search / ...
files.py batches.py responses.py ...

providers/registry/inference.py:29 def available_providers 就是一个纯函数,return [ ...一堆 ProviderSpec... ](§3.2 那段就是它的一个元素)。加一个新推理后端 = 往这个列表里加一个 spec,不用改任何解析逻辑。

4.2 get_provider_registry:把所有文件收成一张表

core/distribution.py:101 get_provider_registryimportlib 动态导入每个 API 对应的注册模块,调用其 available_providers(),拼成一个两层字典:

# core/distribution.py:140 # 真实源码(节选)
for api in providable_apis():
name = api.name.lower()
module = importlib.import_module(f"ogx.providers.registry.{name}")
registry[api] = {a.provider_type: a for a in module.available_providers()}

产出结构就是全书反复出现的那个 ProviderRegistry:

{ Api.inference: { "remote::openai": <spec>, "remote::vllm": <spec>, ... },
Api.vector_io: { "inline::faiss": <spec>, ... },
... }

注意它只遍历 providable_apis()——即排除了内部 API 和自动生成的路由表 API(见 §5)。

4.3 外部 provider:两条旁路

get_provider_registry 还支持在内置之外塞第三方 provider,两条路:

  • 目录式(将弃用): 配置 external_providers_dir,从 providers.d/{remote,inline}/<api>/*.yaml 读 spec(get_external_providers_from_dir,core/distribution.py:184)。
  • 模块式(推荐): 直接在 provider 配置里写 module: some_pkg,解析时 import some_pkg.provider 并调其 get_provider_spec()(get_external_providers_from_module,core/distribution.py:255)。

这让「装一个 pip 包就多一个 provider」成为可能,无需改 OGX 源码。


5. 自动路由的配对表:为什么 /models/inference 是一对

本节讲:OGX 里最巧的一个设计——有几个 API 是自动成对生成的,用户不能也不用手写它们的 provider。

5.1 三对搭档

有些 API 天生分「管理端」和「执行端」两半:

  • 注册 model(/models),然后调用 model 做推理(/inference);
  • vector store(/vector_stores),然后 它(/vector_io);
  • tool group(/tool_groups),然后 工具(/tool_runtime)。

「管理端」需要一张路由表(记录 gpt-4o 属于哪个 provider);「执行端」需要一个路由器(拿到请求按 model id 查表、转发)。OGX 把这种配对写死成一张表:

# core/distribution.py:59 builtin_automatically_routed_apis() # 真实源码(节选)
return [
AutoRoutedApiInfo(routing_table_api=Api.models, router_api=Api.inference),
AutoRoutedApiInfo(routing_table_api=Api.tool_groups, router_api=Api.tool_runtime),
AutoRoutedApiInfo(routing_table_api=Api.vector_stores, router_api=Api.vector_io),
]
路由表 API(管理端)路由器 API(执行端)直觉
modelsinference注册模型 vs 调用模型
tool_groupstool_runtime声明工具 vs 执行工具
vector_storesvector_io建库 vs 查库

5.2 用户只配「执行端」,「管理端」白送

关键规则:用户只在 YAML 里为路由器 API(inference/vector_io/tool_runtime)配 provider;路由表 API(models/vector_stores/tool_groups)由系统自动装配,手写会直接报错:

# core/resolver.py:233 validate_and_prepare_providers # 真实源码
if api in routing_table_apis:
raise ValueError(f"Provider for `{api_str}` is automatically provided and cannot be overridden")

这也是为什么 §4.2 的 get_provider_registryprovidable_apis()——它故意跳过路由表 API(core/distribution.py:81,providable_apis 从全部 API 里减去 routing_table_apisINTERNAL_APIS)。INTERNAL_APIS(core/distribution.py:32,含 inspect/providers/prompts 等)则是根本不走 provider 机制、由内置实现直接提供的一批。


6. 解析引擎 resolve_impls:从 spec 到活实例

本节是本章的核心。讲清楚 core/resolver.py:147 resolve_impls 这一个函数怎么把配置变成可调用的 impls。它在服务器启动时被调用一次(见 core/stack.py:776,承接 01)。

6.1 五步全景

resolve_impls 的函数体只有五句,每句一个阶段:

① validate_and_prepare_providers 校验用户选的 provider,组织成 {key: {id: ProviderWithSpec}}
② specs_for_autorouted_apis 为每对自动路由 API 现造 RoutingTable + AutoRouted 两个 spec
③ sort_providers_by_deps 用 graphlib 按依赖做拓扑排序
④ instantiate_providers 按序 import + 调工厂函数,拿到实例
⑤ (在④内) check_protocol_compliance 校验实例真的实现了 API 协议


返回 impls: {Api: 实现}

下面逐步拆。

6.2 ① 校验与「inner-」前缀

validate_and_prepare_providers(core/resolver.py:225)遍历 run_config.providers,对每个 provider:

  1. 跳过被禁用的(provider_id 为空或 __disabled__);
  2. validate_provider 校验类型确实在注册表里、并触发弃用告警/报错(core/resolver.py:254);
  3. 从注册表取出对应 spec,deps__ 塞值:= api_dependencies + optional_api_dependencies(core/resolver.py:244);
  4. Provider(用户配置)和 ProviderSpec(注册表元数据)合成一个 ProviderWithSpec

有个关键细节:执行端 API 的 key 会被加上 inner- 前缀:

# core/resolver.py:248 # 真实源码
key = api_str if api not in router_apis else f"inner-{api_str}"

为什么?因为 inference 这个名字要留给自动路由器(那个对外的 Router);用户配的真实 provider(如 remote::openai)被藏到 inner-inference 下,成为 Router 内部转发的目标。这一步是「路由器包住真实 provider」这层结构的起点。

6.3 ② 现造自动路由的 spec

specs_for_autorouted_apis(core/resolver.py:178)对 §5.1 每一对,凭空造两个 ProviderWithSpec:

  • 路由表侧:RoutingTableProviderSpec,provider_id="__routing_table__",deps__=[f"inner-{router_api}"]——即它依赖那些被藏起来的真实 provider;
  • 路由器侧:AutoRoutedProviderSpec,provider_id="__autorouted__",依赖对应的路由表。

还有一处特判:vector_io 的路由器额外声明 inference 为可选依赖,以支持「查询改写」(query rewriting):

# core/resolver.py:203 # 真实源码
if info.router_api == Api.vector_io:
optional_deps = [Api.inference]
deps_list.append(Api.inference.value)

于是依赖图里出现了这样一条链:models 路由表 → inner-inference(真实 provider),inference 路由器 → models 路由表。拓扑排序要靠它。

6.4 ③ 拓扑排序(graphlib)

依赖必须先于被依赖者实例化(要先有 inference 实例,才能实例化依赖它的 file-search)。topological_sort(core/resolver.py:337)直接用标准库 graphlib.TopologicalSorter:

# core/resolver.py:351 # 真实源码(节选)
ts: graphlib.TopologicalSorter[str] = graphlib.TopologicalSorter()
for api_str, providers in providers_with_specs.items():
deps = set()
for provider in providers:
for dep in provider.spec.deps__: # 就是 §6.2 塞进去的 deps__
if dep in providers_with_specs:
deps.add(dep)
ts.add(api_str, *deps)

排序键是 API 名字(含 inner- 前缀),依赖来自各 spec 的 deps__。若存在环,graphlibCycleError,被包装成清晰的 RuntimeError(core/resolver.py:368)。这是 deps__ 这个 hack 的唯一用途:给排序器喂一张字符串依赖图。

6.5 ④ 实例化 + 工厂分发

instantiate_providers(core/resolver.py:286)按拓扑序逐个处理,维护一个累积的 impls 字典;实例化前先从 impls 里取出该 provider 的依赖注入:

# core/resolver.py:303 # 真实源码
deps = {a: impls[a] for a in provider.spec.api_dependencies}

真正 import 和调用发生在 instantiate_provider(core/resolver.py:373)。它先 importlib.import_module(provider_spec.module),再按 spec 的类型选不同工厂函数——这是整段的枢纽,可看成一张分发表:

spec 类型调用的工厂方法传入参数(简化)
RemoteProviderSpecget_adapter_impl(config, deps)
InlineProviderSpec(默认分支)get_provider_impl(config, deps)
AutoRoutedProviderSpecget_auto_router_impl(api, 路由表, deps, run_config, policy)
RoutingTableProviderSpecget_routing_table_impl(api, inner_impls, deps, dist_registry, policy)

对前两类,config_class 字符串在这里才被 instantiate_class_type 真正 import 成 Pydantic 类,并用 provider.config(YAML 里那段)实例化:

# core/resolver.py:442 # 真实源码(InlineProviderSpec 分支)
method = "get_provider_impl"
config_type = instantiate_class_type(provider_spec.config_class)
provider_config = _inject_config_defaults(config_type, provider.config.copy())
config = config_type(**provider_config)
args = [config, deps]

后两类(路由器/路由表)的工厂就在 core/routers/__init__.py:get_auto_router_impl(:45)按 API 名映射到 InferenceRouter/VectorIORouter/ToolRuntimeRouter;get_routing_table_impl(:19)映射到 ModelsRoutingTable/VectorStoresRoutingTable/ToolGroupsRoutingTable它们运行时怎么查表转发,是 03 的主题。

这里还能看到 §6.2 那个 inner- 前缀如何收口:实例化结果按 key 分流——inner- 开头的存进「按 provider_id 分组的内部表」,供路由表当作转发目标;其余的才进对外的 impls[api]:

# core/resolver.py:321 # 真实源码
if api_str.startswith("inner-"):
inner_impls_by_provider_id[api_str][provider.provider_id] = impl
else:
impls[Api(api_str)] = impl

6.6 ⑤ 协议合规校验

拿到实例后,instantiate_provider 立刻校验它真的实现了对应 API 的 Protocol——check_protocol_compliance(core/resolver.py:466)遍历协议里所有带 __webmethods__ 的方法,逐个检查实例:方法在不在、可不可调用、签名参数是否兼容,以及是不是「只有协议里有、实现里其实没写」的空壳。任一不满足就抛 ValueError,列出缺失方法。

这是一道启动期的静态保险:provider 少实现一个端点,服务器起不来,而不是等到线上被调用时才 500。

6.7 收尾:一个 hardcode 的注入

instantiate_providers 最后有一步手工接线——把 VectorIORouter 注入进 VectorStoresRoutingTable(core/resolver.py:328),因为二者的相互引用没法靠依赖图表达。作者也在注释里承认这是权宜(和 deps__ 一样,是「够用但不优雅」的实现细节)。


7. 配置与发行版:StackConfig 与 distributions/*

本节讲:上面吃进 resolve_impls 的那份 StackConfig 从哪来,以及 OGX 怎么半自动生成开箱即用的发行版。

7.1 StackConfig:运行配置的根

core/datatypes.py:844 class StackConfig 是整份 run config 的 Pydantic 根对象。和本章相关的核心字段:

字段作用
providers: dict[str, list[Provider]]核心:每个 API 用哪些 provider(就是 §6 的输入)
apis: list[str]要服务的 API 列表(空则由 provider_map 推断)
distro_name引用的发行版名(旧字段 image_name 已弃用,有迁移校验)
storage命名存储后端目录(kv/sql),详见 06
registered_resources预注册的 models / vector_stores(tool_groups 已弃用,改为按 tool_runtime provider 自动注册)
serverHTTP 服务器配置(端口、TLS、auth、多租户),详见 06
external_providers_dir§4.3 的目录式外部 provider

其中 providers 里每一项是 Provider(core/datatypes.py:129):provider_id + provider_type + config(自由 dict) + 可选 moduleprovider_idNone 表示「该 provider 未启用」——常见于用环境变量条件开关(见下一节 YAML 里的 ${env.X:+name} 写法)。

7.2 发行版:预制的 config.yaml

手写完整 run config 很累,OGX 在 src/ogx/distributions/ 提供预制发行版(starter、nvidia、watsonx、oci……),每个是一个目录,含生成好的 config.yaml。以 starter 为例,它的 inference 段用环境变量做条件启用:

# distributions/starter/config.yaml(真实节选)
providers:
inference:
- provider_id: ${env.OLLAMA_URL:+ollama} # 有 OLLAMA_URL 才启用
provider_type: remote::ollama
config:
base_url: ${env.OLLAMA_URL:=http://localhost:11434/v1}
- provider_id: openai
provider_type: remote::openai
config:
api_key: ${env.OPENAI_API_KEY:=}

${env.OLLAMA_URL:+ollama} 的意思:环境变量存在时 provider_idollama(启用),不存在则为空(§7.1 说的「未启用」)。同一个 starter 配置,按你设了哪些 API key,自动只点亮对应后端。

7.3 半自动生成:template.py

这些 config.yaml 不是纯手写,由 distributions/template.py 半自动生成。核心是两个类:

  • RunConfigSettings.run_config(distributions/template.py:150):调 get_provider_registry() 拿到注册表,为每个选中的 provider 找到其 config_class,若该类有 sample_run_config() 就调用它生成默认配置块,最后拼成完整 run config dict。
  • DistributionTemplate(distributions/template.py:259):一个发行版的顶层描述(名字、providers、多套 run_configs、环境变量说明);save_distribution(:336)把它 dump 成 YAML 和 Markdown 文档。

每个发行版目录里的 <name>.py(如 distributions/starter/starter.py:119 get_distribution_template)负责构造这个 DistributionTemplate——声明本发行版启用哪些 provider、默认模型、存储后端等。改完后跑 scripts/distro_codegen.py 重新生成 YAML(AGENTS 里的约定,数据而非指令)。

这样,「provider 的默认配置长什么样」这个知识只存在于各 provider 的 config 类里(通过 Field(description=...)sample_run_config()),发行版生成器去自动收集,避免手写漂移。


8. 边界与巧妙之处

值得借鉴的设计:

  • spec 用字符串引用实现(config_class/module),把 import 推迟到解析期。 好处:列依赖(list-deps)、build 时根本不需要装上所有 provider 的重型依赖(torch、boto3……)也能扫描注册表。get_provider_registry 的 docstring 明确说它被多处复用、且能容忍模块暂时导不进来。
  • 自动路由把「管理/执行」解耦成配对表,一处声明(builtin_automatically_routed_apis)驱动全部装配。 加一对新的自动路由 API,只改这张表 + 加对应 Router/RoutingTable 类。
  • 协议合规在启动期强校验(check_protocol_compliance),把「provider 没实现某端点」从线上运行时错误提前成启动失败。
  • 拓扑排序直接用标准库 graphlib,不自造轮子;依赖图靠一个内部字段 deps__ 喂进去。

边界与粗糙处(作者自己标了的):

  • deps__ 被注释为 this is a hack for now(ogx_api/datatypes.py:283)——依赖图借用了一个塞进 spec 的临时字段。
  • VectorIORouter → VectorStoresRoutingTable 的互相引用无法用依赖图表达,靠实例化末尾手工注入(core/resolver.py:328)。
  • Api.containers 目前不走 provider 机制,是内置实现,注释里标了 follow-up(core/distribution.py:28,issue #5892),将来才会像 tool_groups 一样成为自动路由 API。
  • 目录式外部 provider(external_providers_dir)已被标记弃用,推荐用 module: 方式。

9. 代码地图(导航索引)

主题文件路径符号名
Provider spec 基类src/ogx_api/datatypes.py:228ProviderSpec
Inline / Remote specsrc/ogx_api/datatypes.py:298 / :337InlineProviderSpec / RemoteProviderSpec
内部 spec(路由用)src/ogx/core/datatypes.py:102 / :117AutoRoutedProviderSpec / RoutingTableProviderSpec
依赖排序临时字段src/ogx_api/datatypes.py:283ProviderSpec.deps__
单个 API 的可选 providersrc/ogx/providers/registry/inference.py:29available_providers
收集注册表src/ogx/core/distribution.py:101get_provider_registry
可配置/内部 API 划分src/ogx/core/distribution.py:81 / :32providable_apis / INTERNAL_APIS
外部 provider(目录/模块)src/ogx/core/distribution.py:184 / :255get_external_providers_from_dir / _from_module
自动路由配对表src/ogx/core/distribution.py:59builtin_automatically_routed_apis
解析引擎入口src/ogx/core/resolver.py:147resolve_impls
校验+组织(inner- 前缀)src/ogx/core/resolver.py:225validate_and_prepare_providers
现造自动路由 specsrc/ogx/core/resolver.py:178specs_for_autorouted_apis
拓扑排序(graphlib)src/ogx/core/resolver.py:337topological_sort
逐个实例化src/ogx/core/resolver.py:286instantiate_providers
工厂分发(import + 调用)src/ogx/core/resolver.py:373instantiate_provider
协议合规校验src/ogx/core/resolver.py:466check_protocol_compliance
路由器/路由表工厂src/ogx/core/routers/__init__.py:45 / :19get_auto_router_impl / get_routing_table_impl
运行配置根对象src/ogx/core/datatypes.py:844StackConfig
单 provider 配置项src/ogx/core/datatypes.py:129Provider
发行版生成器src/ogx/distributions/template.py:150 / :259RunConfigSettings.run_config / DistributionTemplate
starter 发行版声明src/ogx/distributions/starter/starter.py:119get_distribution_template
解析器被调用处(启动)src/ogx/core/stack.py:776resolve_impls 调用