OpenHuman 模型层迁移 Phase 3:RouterProvider 到 crate ModelRegistry 的设计、验证与落地全解
OpenHuman 模型层迁移 Phase 3RouterProvider 到 crate ModelRegistry 的设计、验证与落地全解【免费下载链接】openhumanOpenHuman is an open source personal AI for Mac, Windows and Linux — local-first memory, agent orchestration, and deep research.项目地址: https://gitcode.com/GitHub_Trending/op/openhuman导读本文以 OpenHuman 仓库中的历史设计文档《Phase 3 — RouterProvider → crate ModelRegistry: design ground truth》为核心骨架剖析一次看似需要上游改动、实际已在接缝层完成的模型注册表迁移它解释了为什么assemble_turn_harness已经把主模型与各工作负载 tier 路由注册进 tinyagents crate 的ModelRegistry以及迁移真正剩余的 host 侧工作Motion A 与 Motion B是什么。读完本文你将掌握TurnModels的结构与访问器设计、TurnModelSource/build_turn_models_crate的构建链路、OH_WORKLOAD_ROUTER的声明式路由与能力门控以及 provider-string 语法在 crate 原生模型构建中的实际作用并能在当前仓库源码中逐行印证这些设计。1. 文档定位一份被取代但仍具历史价值的设计理据该文档docs/tinyagents-phase3-router-registry-design.md的状态标注非常特殊Status: superseded on 2026-07-22被 docs/tinyagents-migration-plan-2026-07-22.md 取代。文档本身明确指出Phase 3s client cutover landed in #4783/#4784; this document is retained as historical design rationale, not current status.即 Phase 3 的客户端切换已经在 #4783/#4784 两个 PR 中完成本文档仅作为历史设计理据保留。它并非当前状态的描述而是研究以下三块内容后沉淀的ground truthcrate 侧vendor/tinyagents/src/harness/model、harness/agent_loop、harness/retry、registry/host 接缝层src/openhuman/agent/tinyagents/{mod,routes,model}.rs推理层inference/provider/{router,factory}.rs。同时它关联了两份姊妹文档docs/tinyagents-inference-migration-plan.mdPhase 3 计划与 docs/tinyagents-drift-ledger.mdP1-9 漂移台账并对应 issue #4249。因此阅读本文的正确姿势是把它当作设计结论与实现路径的说明书再结合当前仓库源码验证其落地状态。2. 前提纠正注册表迁移其实已经完成了这是全文最重要的洞见。原计划把 Phase 3 表述为在vendor/tinyagents中实现 RouterProvider → crate ModelRegistry即以为需要给上游 crate 补功能但调查结果显示注册表迁移已经在接缝层完成crate 本身已经具备 host 路由器所需的全部能力不存在硬性的上游缺口。证据集中在assemble_turn_harness当前仓库中实现于 src/openhuman/agent/tinyagents/mod_part_04.rs它已经完成以下四件事注册主模型与全部工作负载 tier 路由到 crateModelRegistry主模型通过harness.register_model(model, primary)set_default_model(model)成为 registry 默认与分发目标每条 workload-tier 路由通过循环harness.register_model(name, route_model)追加为附加注册项见mod_part_04.rs中capability_registry.replace_model/register_model的对应逻辑。跨路由 fallback 以 crate 的RunPolicy.fallback承载通过routes::route_fallback_policy(model)生成同族 fallback 链并写入policy.fallback由agent_loop::invoke_model_resolving原生遍历。当前实现见 src/openhuman/agent/tinyagents/routes.rs 与mod_part_04.rs中policy.fallback route_fallback.clone()的装配。视觉能力门控由 crate 完成RequiredCapabilitiesMiddleware把image_in盖戳到ModelRequest.required_capabilities上crate 的resolve_request再用ModelProfile::satisfies过滤命名候选模型——不满足能力需求的模型在分发前即被拒绝。发出FallbackSelected对等事件通过FallbackObserverMiddleware把 crate 内部的静默 fallback 转换变成 OpenHuman 进度/可观测性桥接上可见的AgentEvent::FallbackSelected详见下文第 6 节。2.1 crate 缺失的两件事host 其实都不需要按 crate 审计crate 确实缺少两个能力但host 都不需要crate 缺少的能力host 为何不需要基于能力的选择扫描 registry 找一个有能力的模型host 从不这样做——调用方在上游就选好 tiersubagent_runner/ops/graph.rs在图片轮次直接设model vision-v1中间件只做校验/强制执行不做选择能力感知的fallbackhost 的 fallback 链是手工构造且保证能力安全的routes::same_family_fallbacksvision-v1 → []同族文本备选全部具备文本能力所以 crate 不做能力过滤的next_after在这里是无害的结论RouterProvider 其实已经被投影到 crate registry 上了。剩余工作全部在 host 侧且不需要发布新的 crate 版本。3. 真正存活的东西RouterProvider 的角色收缩RouterProvider位于inference/provider/router.rs作为宿主Provider存活的唯一理由变成了被包裹 Provider 内部的按调用 BYOK 别名解析器分发时把 tier 别名reasoning-v1等映射为具体的(provider, model)。这一层之所以必要是因为 issue #2079 指出原始别名在 OpenAI/DeepSeek 上会返回 400 错误。注册进 harness 的 tier 模型仍然是包装了宿主Provider的ProviderModel。所以 harness 持有Provider只有一个原因build_turn_models需要用同一个Provider句柄构建每轮主模型 路由 摘要器的ProviderModel集合对应tinyagents/mod.rs:1139-1175与routes::build_route_models。由此得出两个独立的后续动作按优先级排序Motion A — Harness 持有 crateChatModel当前 Phase仅 host 侧改动把build_turn_models的构造位置从 harness 轮次路径agent/harness/graph.rs::run_channel_turn_via_graph、session 轮次路径上移到生产者/工厂边界让agent/只持有 crate 模型类型、不再持有Provider。harness 轮次路径今天在构建前读取的四个Provider方法全部已可脱离 trait 获得harness 现在读取的内容crate 原生来源provider.supports_native_tools()TurnModels.primary.profile().tool_callingprovider.supports_vision()…profile().modalities.image_inprovider.effective_context_window(model).await构建时解析 →…profile().max_input_tokensprovider.telemetry_provider_id()新增TurnModels.provider_id: String于是TurnModels成为 harness 持有的唯一单元。设计要点如下扩展TurnModels新增provider_id: String以及小型访问器native_tools()、supports_vision()、context_window()全部读取primary.profile()。这删除了 harness 图中所有对裸Provider的读取。新增工厂入口inference::provider::factory::create_turn_models(role_or_model, config, temperature) - anyhow::ResultTurnModels内部走既有create_chat_provider路径构建Provider解析异步的effective_context_window计算telemetry_provider_id再调用接缝层build_turn_models。所有Provider命名留在inference/provider/与接缝层。生命周期build_turn_models按(provider, model)粒度构建harness 今天就是每轮构建全新的TurnModels。保持这个行为——由生产者channels processor、session 轮次入口、subagent runner在轮次请求组装时构建TurnModels而不是 harness 图来做。error_slot保持每轮一个这是正确的——它只恢复本轮的 provider 错误。类型替换AgentTurnRequest.provider: Arcdyn Provider改为携带构建好的TurnModels连同已有的model/provider_nameAgent/AgentBuilder.provider同理。run_channel_turn_via_graph与 session 的turn/graph.rs接收TurnModels通过访问器读能力位直接传给run_turn_via_tinyagents_shared它本来就已经接收TurnModels。IntelligentRoutingProvider仍然是Provider实现provider 栈成员工厂在build_turn_models之前把它包起来。退出条件没有任何agent/文件再命名ProvidertraitProviderModel只会在接缝层model.rs/build_turn_models构建且只能通过工厂入口触达。行为零变化同样的ProviderModel、同样的 registry/fallback 装配。Motion B — 注册模型改为 crate 原生后续最大的 LOC 收益用 crate 的providers::openai客户端替换ProviderModel包装器——这些客户端直接由配置构建BYOK slugs、Ollama/LM Studio 的 base URL把每 tier 的客户端直接注册进 registry从而让RouterProvider的按调用别名解析消失每个 tier 就是它自己的已注册客户端。这对应推理计划文档的Phase 2客户端切换删除compatible*.rs加 Phase 3 的剩余部分同时把 bespoke providersmanaged backend、claude-code、codex保留为 host 的ChatModel实现Phase 4。Motion B 不在 Motion A 范围内但被 Motion A 解除阻塞。值得注意从 docs/tinyagents-migration-plan-2026-07-22.md 的审计记录看Motion B 所描绘的客户端层反转后来确实发生了——managed backend、wire-equivalent BYOK slugs、openai/codex/custom slugs 都已成为 crate 原生ChatModelcrateModelRouter被采纳compatible*.rs已删除并合并为legacy_provider.rs门面对应 PR #4769、#4780、#4782、#4783、#4784。4. 当前源码中的落地证据Motion A 已闭合4.1TurnModels结构、访问器与语义当前仓库中TurnModels定义于 src/openhuman/agent/tinyagents/mod_part_03.rs与设计文档 Motion A 的构想完全一致pub(crate) struct TurnModels { /// 本轮有效/主模型registry 默认 分发目标 primary: TurnChatModel, /// 附加工作负载 tier 路由registry 名 → 模型不含主模型 /// crate registry 跨它们解析 fallback/selection routes: TierRoutes, /// 上下文窗口摘要器模型独立适配器实例 /// 其 provider 错误不触碰本轮的 error_slot summarizer: TurnChatModel, /// 失败时恢复主模型原始可 downcast 的provider 错误 error_slot: crate::openhuman::agent::tinyagents::model::ModelErrorSlot, /// 提供方遥测 idLangfuse 中为 {provider_id}.{model} /// 构建时从源 Provider 捕获——harness 轮次路径不再读裸 Provider provider_id: String, /// 主模型有效上下文窗口驱动上下文窗口摘要步骤 /// 由生产者/工厂在构建前解析——harness 图不再发起异步调用 context_window: Optionu64, /// 源 provider 是否原生工具调用——仅用于选择历史后缀分发器 native_tools: bool, /// 源 provider 是否视觉能力——用于门控多模态占位符恢复 supports_vision: bool, }对应的四个访问器provider_id()、context_window()、native_tools()、supports_vision()全部是行为中立的只读方法mod_part_03.rs第 55-75 行印证了seam-internal; behavior-neutral的设计要求。4.2TurnModelSource构建链路与上下文窗口解析src/openhuman/agent/tinyagents/mod_part_03.rs 的TurnModelSource是 Motion A 的关键抽象——它把Provider隔离在接缝层new_crate_native(role, config)crate 原生来源build()时走build_turn_models_crate而非包装 providernew_crate_native_from_string(role, provider_string, config)triage 路径的 #1257 强制托管覆盖build_remote_provider选出有效字符串主模型用显式 provider-string 构建effective_context_window(self, model)解析模型的有效上下文窗口——这是驱动上下文窗口摘要步骤的值。它在build()之前解析使 harness 图不再发起任何异步Provider调用解析时对本地运行时Ollama / LM Studio走context_window_for_model_with_local_fallback带回退build(model, temperature, context_window)组装TurnModels。crate 原生分支里provider_id的推导规则是openhuman/空/cloud→managed否则取 provider-string 的:前缀如ollama、lmstudionative_tools与supports_vision对本地 provider 为falsebuild_summarizer(model, temperature)构建独立的摘要器ChatModel自带 error slot供主轮次之外的摘要调用使用调用方无需命名Providertrait。4.3build_turn_models_crateP3-B 的 crate 原生构建build_turn_models_cratemod_part_03.rs是设计文档 Motion B 方向在 seam 层的早期投影不再为每个 tier 包装一个宿主Provider而是通过factory::create_turn_chat_model系列把每个 tier 构建成 crate 原生ChatModelmanaged →OpenHumanBackendModellocal/cloud → crateOpenAiModel。TurnModels的形状与build_turn_models完全一致所以assemble_turn_harness无需改动。error_slot使用全新空槽——crate 原生模型直接暴露TinyAgentsError没有可 downcast 的anyhow需要保留Sentry 抑制不受影响。4.4 harness 图读取访问器而非裸 Providersrc/openhuman/agent/harness/graph.rs 的run_channel_turn_via_graph现在接收TurnModelSource构建流程完全符合 Motion A 的设计let context_window source.effective_context_window(model).await; let turn_models source.build(model, temperature, context_window)?; let native_tools turn_models.native_tools(); let provider_id turn_models.provider_id().to_string(); // 视觉能力门控多模态占位符恢复 if turn_models.supports_vision() has_image_placeholders(prepared) { … }随后把turn_models、provider_id、context_window直接传给共享接缝run_turn_via_tinyagents_shared——其中native_tools用于选择原生 envelope vs prompt-guided text的历史后缀分发器supports_vision用于多模态占位符恢复与[IMAGE:…]/[FILE:…]标记展开。这正是文档表格中四行harness reads → crate-native source的代码级落地。5. 可选的 crate 优化能力感知的 fallback非必需如果未来想要能力感知的 fallback让手工构造的链不再是唯一安全网crate 侧只需一行改动在invoke_model_resolving中对FallbackPolicy::next_after的目标重新应用model_eligible过滤对应路径vendor/tinyagents/src/harness/agent_loop/model_call.rs:186-204。设计文档给出的建议是仅当 Motion B 引入能力分叉的 fallback 链时才把它作为独立的小型上游 PR 提交。对 Motion A 而言完全不需要——因为当前 host 的 fallback 链见下节天然能力安全。6. 声明式路由表与能力门控OH_WORKLOAD_ROUTER作为纵深补充当前仓库已经把文档提到的手工same_family_fallbacksturn_required_capabilities整合为一份声明式路由表OH_WORKLOAD_ROUTERsrc/openhuman/agent/tinyagents/routes.rs它是一个基于 crateModelRouter的静态表同时回答route_fallback_policy与turn_required_capabilities两个问题tier 路由fallback 链能力门控chat-v1→burst-v1—burst-v1→chat-v1—reasoning-v1→agentic-v1—agentic-v1→reasoning-v1—coding-v1→agentic-v1—summarization-v1→chat-v1—vision-v1无primary-only要求image_inhint:vision无primary-only要求image_in同 gate关键设计语义与文档第 1 节的能力安全论证一一对应轻量对话兄弟chat-v1 ⇄ burst-v1、重型推理/agentic 兄弟reasoning-v1 ⇄ agentic-v1互为同族备选全部文本能力安全coding-v1 → agentic-v1coding 工具密集、与 agentic 相邻summarization-v1 → chat-v1摘要搭乘通用聊天模型vision-v1是image_in门控且 primary-only——文本 fallback 无法满足该门控所以链为空这正好是文档vision-v1 → []的声明式表达。6.1RequiredCapabilitiesMiddleware分发前拒绝不合格模型src/openhuman/agent/tinyagents/routes.rs 的wrap_model实现当request.required_capabilities为空时用本轮推导出的能力集今天仅视觉盖戳request.with_required_capabilities(...)。这样在vision-v1轮次中只有携带image_in能力的模型可以被选为分发目标不合格的备选在分发前就被过滤——校验/强制执行、不选择的角色定位与文档完全吻合。6.2FallbackObserverMiddleware让静默 fallback 可见crate 的 registry-backedRunPolicy.fallback遍历是静默的——不发出AgentEvent::FallbackSelected。FallbackObserverMiddlewareroutes.rs包装模型解析核心成功时比较响应中的resolved_model与轮次主模型名若不同即发生 fallback于是发出对等事件并记录[fallback]日志。它从不重新发起调用因此不增加额外 provider 分发无双重 fallback。该中间件仅在存在 fallback 链时安装mod_part_04.rs中if route_fallback.is_some()并配合UsageCarryMiddleware完成每调用的成本用量捕获。7. 首次实现切片Motion A 的六步执行清单设计文档给出了精确到步骤的实现切片TurnModels扩展增加provider_id与native_tools()/supports_vision()/context_window()访问器seam 内部、行为中立——已在当前源码中完成。create_turn_models(...)工厂入口包裹create_chat_provider 异步上下文窗口解析 build_turn_models。裁剪run_channel_turn_via_graph改为接收TurnModels并通过访问器读能力位删除 4 处裸 provider 读取channel 生产者channels/runtime/dispatch/processor.rs通过工厂构建TurnModels并放进AgentTurnRequest。重复执行session 轮次路径 subagent runner替换Agent.provider。更新测试改为构建TurnModels/crateMockModel替代手工实现的Provider。验证两个 Cargo world 全绿json_rpc_e2e在 mock-backend 轮次上确认 streaming/cost/tool-timeline 对等#4460 / 零美元轮次 / tool-timeline 三个风险点。8. 验证与对等性锁Verification parity locks迁移不能破坏任何已确立的行为契约以下内容必须全部保持而这些现在都已由 crate 装配完成——Motion A 只移动模型在何处构建不改变路由方式Provider-string 语法openhumanmanaged backendmodel config.default_model、cloud/缺失primary_cloud迁移后 legacy custom inference_url 在 primary 仍指向 OpenHuman 时优先、ollama:model[temp]、lmstudio:model[temp]、mlx:model[temp]、local-openai:model[temp]、slug:model[temp]cloud_providers 按 slug 建 key按 auth_style 构建 crate 原生 OpenAI Bearer 客户端或 Anthropic 变体。temp后缀为 per-workload 温度覆盖上游发送的 model id 不含后缀。该语法当前完整实现于 src/openhuman/inference/provider/factory.rs。inference.*RPC 行为。tier 别名集合chat-v1/burst-v1/reasoning-v1/agentic-v1/coding-v1/summarization-v1/vision-v1/hint:vision。fallback 顺序单一同族备选vision 仅 primary。每逻辑调用一次的 FIFO 用量推送charged-USD-over-estimate 优先级、优雅降级。FallbackSelected事件。在 docs/tinyagents-migration-plan-2026-07-22.md 中可看到这些锁的最终状态agent loop 自 #4249/#4399 起已运行在 tinyagents 上Phase 0 漂移行全部 CLOSEDPhase 1 基本关闭crate 版本固定在 v2.1.0Cargo.toml:107声明tinyagents { version 2.1, features [sqlite] }Cargo.toml:677的[patch.crates-io]指向vendor/tinyagents。9. 结语一次无上游改动的迁移范本这份设计文档最有价值的启示在于它的前提纠正迁移类任务的第一步不是动手实现而是验证现状与计划的差距。调查证明 registry 迁移已在接缝层完成、crate 能力已够用剩余工作被精确收敛为 host 侧的两个动作Motion A 立即可做、Motion B 顺水推舟且都不需要 crate 发版。今天回看当前仓库Motion A 的TurnModels/TurnModelSource/工厂链路均已落地而 docs/tinyagents-migration-plan-2026-07-22.md 与 docs/tinyagents-drift-ledger.md 继续承载着行级漂移台账与后续工作包削平legacy_provider.rs门面、合并routing//tool_timeout//tool_status//model_council/到 crate 原语、对账工具模型等。对想要深入理解 OpenHuman 模型层架构的读者建议按本文 → 迁移总计划 → 漂移台账 →src/openhuman/agent/tinyagents/与src/openhuman/inference/provider/源码的顺序阅读即可完整还原一次大型模型层迁移的设计、执行与沉淀全过程。【免费下载链接】openhumanOpenHuman is an open source personal AI for Mac, Windows and Linux — local-first memory, agent orchestration, and deep research.项目地址: https://gitcode.com/GitHub_Trending/op/openhuman创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考