一个 SDK 管多家模型:harness-sdk 的路由、聚合与故障转移这样配才不翻车
发布时间:2026/10/11 3:20:26 锦皓数字建站

一个 SDK 管多家模型harness-sdk 的路由、聚合与故障转移这样配才不翻车【免费下载链接】harness-sdkBuild an agent harness and control it end-to-end. Open-source SDK for production AI agents in Python TypeScript - any model, any cloud.项目地址: https://gitcode.com/GitHub_Trending/sdkpython13/harness-sdk生产环境里的 Agent 从来不是只调一个模型那么简单主模型偶尔限流、区域故障、供应商维护或者某类任务根本不该浪费旗舰模型的 token。把这些都塞进业务代码路由逻辑会迅速腐烂成一团 if-else。harness-sdkStrands Agents Harness SDK给出的答案是三层解耦统一接入层把provider/model字符串归一成同一个 Model 接口路由层用可插拔策略决定每次调用走哪个候选故障转移层在调用失败时按策略自动切换并重置重试预算。本文基于仓库源码逐层拆解这套机制并给出直接可抄的配置与容易踩的边角。统一模型接入一行字符串切换全云厂商harness 的核心入口create_harness()接受一个provider/name字符串、裸 Bedrock 模型 id或现成的Model实例。模型解析逻辑集中在 strands_harness/models.py内部维护了一张_PROVIDERS表为每个供应商注册了构建函数、推荐的 reasoning 档位、思考级别、是否支持原生 web search 与缓存_PROVIDERS { bedrock: Provider(_bedrock, high, _ANTHROPIC_LEVELS, web_searchFalse, cachingTrue), bedrock-mantle: Provider(_bedrock_mantle, high, _OPENAI_LEVELS, web_searchTrue, cachingTrue), anthropic: Provider(_anthropic, high, _ANTHROPIC_LEVELS, web_searchTrue, cachingTrue), openai: Provider(_openai, high, _OPENAI_LEVELS, web_searchTrue, cachingTrue), google: Provider(_gemini, high, _GOOGLE_LEVELS, web_searchTrue, cachingTrue), ollama: Provider(_ollama, None, (), web_searchFalse, cachingFalse), litellm: Provider(_litellm, None, (), web_searchFalse, cachingTrue), }这意味着业务代码只写模型名不写供应商 SDKfrom strands_harness import create_harness create_harness(modelanthropic/claude-opus-5) # 直连 Anthropic create_harness(modelopenai/gpt-5.6-sol) # OpenAI create_harness(modelgoogle/gemini-3.5-flash) # Google create_harness(modelbedrock/global.anthropic.claude-opus-5) # 默认Bedrock 上的 Opus 5 create_harness(modelbedrock-mantle/openai.gpt-5.6-sol) # Bedrock 的 OpenAI 兼容端点换后端时改一个字符串即可Agent 的其余代码原封不动。更关键的是 reasoning effort 的归一化你只设置一次efforthigh_effort()会把档位映射到各家 API 的字段——Claude 走thinking预算、OpenAI 走reasoning: {effort}、Gemini 走thinking_level、Qwen/xAI 系走reasoning_effort。一个供应商不支持的档位会在构造期直接抛错而不是请求打到云端才报 400。默认值定义在 strands_harness/defaults.pyDEFAULT_MODEL bedrock/global.anthropic.claude-opus-5所有默认项都可在create_harness()里覆盖。基于候选池的路由声明式候选 可插拔策略聚合多家模型的核心构件是ModelRouter实现位于 strands-py/src/strands/models/routing/router.py。它的设计原则是router 只编排决策全归 strategyrouter 在首次模型调用前问一次策略选哪个候选调用失败且没有被重试钩子认领时再问一次并携带到目前为止的尝试记录。from strands import Agent from strands.models import ModelRouter, FallbackStrategy from strands.models.openai import OpenAIModel from strands.models.anthropic import AnthropicModel router ModelRouter([ OpenAIModel(model_idgpt-5.6-sol), # 候选 0默认 AnthropicModel(model_idclaude-opus-5), # 候选 1备用 ]) agent Agent(modelrouter)每个候选可以包一层RoutingCandidate附带name、description和 JSON 可序列化的metadata——这就是基于标签的路由元数据来源供后续的智能分类策略读取。候选还可以嵌套一个嵌套的ModelRouter在外部视作一个不透明候选内部不自己做故障转移外层失败就直接换掉整个候选组。默认策略按失败次数排序的声明式故障转移默认的FallbackStrategyfallback_strategy.py逻辑非常克制排除掉上次成功之后已经试过的候选然后在剩余候选中选失败次数最少的平局按声明顺序。所以一次调用没有任何失败记录时就是纯粹的声明顺序 failoverA → B → C某个模型持续失败会沉到健康候选之下而不是每次都先撞一遍一次成功会让本轮已试的排除失效同时清零成功候选的失败计数其他候选保留失败历史——持续宕机的模型不会因为一次碰巧成功就恢复优先级。再加上max_switches参数限制单次调用内切换次数和每个失败轮次每个候选最多用一次的兜底路由永远不会死循环。智能路由用分类器模型选候选想要真正的按请求复杂度路由用ClassifierStrategyclassifier_strategy.py。它把一个额外的模型当作分类器把候选的 name/description/metadata、最新用户请求、父 Agent 指令裁剪到限额后交给分类模型做结构化输出返回候选下标。其默认策略是先排除证据显示无法满足硬需求的候选再在剩余候选中选能满足任务的最小模型并明令禁止按声明顺序推断能力。分类器自身失败超时、异常时降级返回 Nonerouter 落回候选 0不会让路由本身拖垮调用。注意它把候选元数据和请求内容发给分类模型代码注释里明确警告这些数据不能含密钥且证据有字符限额超限直接抛错而不是静默截断。故障转移的实测配置要点结合源码故障转移配置有几个容易翻车的地方1. 必须给候选独立实例。router 构造时_reject_duplicates会拒绝同一个 Model 实例被路由两次包括嵌套 router 里的重复。原因写在注释里策略按候选身份记录健康状态同一个模型放在两个候选后面会有两份失败预算永远降不了级。所以备用模型要OpenAIModel(...)各建一个实例不能复用。2. 有状态模型不能进候选池。_reject_stateful直接抛错带会话状态的模型路由切换会撕裂对话状态这是设计上的红线。3. 候选的上下文窗口要尽量一致。router 模块顶部注释列了已知限制路由只作用于InvokeModelStageagent.model始终是第一个候选主动压缩按它的上下文窗口估算。候选窗口差异过大时压缩不足会让被路由到的模型溢出。结构化输出Agent.structured_output()直接调模型、完全绕过路由——要用路由就不能依赖这个路径。4. 切换会重置重试预算。切换成功时_advance会调用_retry_strategy._reset_retry_state()给新候选一个全新的重试额度避免把旧模型的限流惩罚带过去。同时_on_model_result里event.retry已为真重试策略已认领时 router 不再插一脚两层故障处理不会打架。5. 子 Agent 与模型路由的关系。harness 的子 Agent 机制harness-py/src/strands_harness/agent.py 中的build_default_subagent默认让子任务继承父模型的候选池modelInherit()是 multiagent 规格strands-py/src/strands/multiagent/spec.py的默认轴策略——想把某些子任务固定到便宜模型用Choice轴给模型列一个封闭选项表模型只能从中选不能自由发挥。容易踩的边角工具函数序列化约束与名字碰撞社区对 harness-sdk 的高频吐槽是插件加载失败、工具莫名消失源码揭示了几个根因工具名碰撞是构造期硬失败。_check_name_collisionsagent.py会在构建时把内置工具、你传的tools、插件 vend 的工具、memory 工具全部拉进同一张表查重连-和_视为同一名字。碰撞直接抛ValueError并点名冲突来源修复方式是改你的工具名或通过builtin_tools先关掉内置的。这比静默覆盖掉一个工具要安全得多——后者会让模型拿到一个不存在的工具。MCP 工具按服务器名前缀命名空间。同一 MCP 服务器下的工具暴露为server_tool规避跨服务器重名但服务器名撞上内置名如服务器叫web暴露fetch→web_fetch仍会与内置冲突agent.py注释里明确提示了这条漏网之鱼。programmatic_tool_caller的序列化约束。这是 harness 内置的一个特殊工具tools/programmatic_tool_caller.py让模型写 Python 代码在 Monty 沙箱里串起其他工具。它暴露给代码的工具函数只接受关键字参数名字不是合法 Python 标识符的工具比如 MCP 工具的fetch-url、ns.fetch会生成下划线别名fetch_url、ns_fetch别名冲突时直接不注入并告警。且该工具永不暴露自身或其他实例防止递归。代码里工具结果若是 JSON 文本会被自动解析成 dict/list 方便索引结构化内容优先返回——但任何print()之外的东西都不回传超大中间结果靠这个机制留在沙箱里不进上下文。web_search 的供应商依赖陷阱。_web_search_modeagent.py的逻辑是模型有原生搜索OpenAI、Anthropic、Gemini、bedrock-mantle 的 GPT-5/6 系就开原生Bedrock Converse 等没有显式点名web_search会抛错防止静默失效想用就显式{web_search: exa}走第三方。默认状态下不支持原生搜索的模型只是关闭该工具并打一条 info 日志——这是默认静默降级 显式请求硬失败的典型组合排查搜索工具去哪了先看日志级别。从单模型到候选池的迁移路径把这套机制落地的顺序建议先用create_harness(model...)固定一个主模型验证工具、会话、记忆链路引入ModelRouter包两个候选默认FallbackStrategy先拿到主模型挂掉自动切备用的兜底max_switches设为 1~2 防止故障期间抖动任务复杂度分化明显时换成ClassifierStrategy用RoutingCandidate的description/metadata写好每个候选的适用证据多 Agent 场景下用spec.py的轴策略Fixed/Inherit/Open/Choice把模型选择权收回到开发者手里——Choice是闭集模型只能从你给的选项里挑。这套 SDK 的价值不在于多接几个供应商而在于把路由、故障转移、重试这三件最容易在业务代码里腐烂的事下沉成了声明式配置和可测试的策略对象。接多家模型之前先想清楚你的路由决策到底该由谁做出——是声明顺序是失败计数还是一个独立的分类模型——harness-sdk 把这三种答案都做成了可替换的插槽。【免费下载链接】harness-sdkBuild an agent harness and control it end-to-end. Open-source SDK for production AI agents in Python TypeScript - any model, any cloud.项目地址: https://gitcode.com/GitHub_Trending/sdkpython13/harness-sdk创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。