核心概念

模型路由(Model Routing)

使用 Router 在多模型之间进行简单路由,支持 cost-first / quality-first 等策略

模型路由(Model Routing)

在实际应用中,一个 Agent 往往需要在多个模型之间做选择,例如:

  • 交互式对话优先使用 高质量模型
  • 批量任务、工具调用优先使用 低成本模型
  • 特定项目或模板希望绑定特定模型。

aster 提供了一个轻量级的 Router 抽象,在不改变核心运行时的前提下,为模型选择增加一层路由能力。

核心概念

Router 接口

pkg/router 提供了一个通用的 Router 接口:

type Router interface {
    SelectModel(ctx context.Context, intent *RouteIntent) (*types.ModelConfig, error)
}

其中 RouteIntent 描述一次调用的“意图”:

type RouteIntent struct {
    Task       string                 // 任务类型,如 "chat"、"summarize"
    Priority   Priority               // "latency" / "cost" / "quality"
    TemplateID string                 // 当前 Agent 的模板 ID
    Metadata   map[string]interface{} // 预留的扩展信息
}

Priority 与 RoutingProfile

路由优先级使用简单的枚举:

const (
    PriorityLatency Priority = "latency"
    PriorityCost    Priority = "cost"
    PriorityQuality Priority = "quality"
)

AgentConfig 中,通过 RoutingProfile 字段指定路由偏好:

type AgentConfig struct {
    TemplateID     string `json:"template_id"`
    RoutingProfile string `json:"routing_profile,omitempty"` // 例如 "cost" 或 "quality"
}

在 Agent 创建时,RoutingProfile 会被转换为 Priority 传给 Router。

StaticRouter:内存静态路由表

当前版本内置了一个简单的静态路由实现 StaticRouter

type StaticRouteEntry struct {
    Task     string
    Priority Priority
    Model    *types.ModelConfig
}

type StaticRouter struct {
    defaultModel *types.ModelConfig
    routes       []StaticRouteEntry
}

匹配规则:

  1. 先找 Task + Priority 都匹配 的条目;
  2. 如果没有,再找 只匹配 Task (Priority 为空) 的条目;
  3. 否则使用 defaultModel
  4. 如果连 defaultModel 都不存在,则返回错误。

在 Agent 中启用 Router

Router 是一个 可选依赖,通过 agent.Dependencies 注入:

deps := &agent.Dependencies{
    Store:            store,
    SandboxFactory:   sbFactory,
    ToolRegistry:     toolReg,
    ProviderFactory:  provider.NewMultiProviderFactory(),
    Router:           myRouter,           // 可选
    TemplateRegistry: tplRegistry,
}

agent.Create 中,如果配置了 Router

  1. 先根据 AgentConfig 和模板构造默认的 ModelConfig
  2. 构造一个 RouteIntent
    intent := &router.RouteIntent{
        Task:       "chat",
        Priority:   router.Priority(config.RoutingProfile),
        TemplateID: config.TemplateID,
        Metadata:   config.Metadata,
    }
    
  3. 调用 deps.Router.SelectModel(ctx, intent) 获取最终的 ModelConfig
  4. 再交给 ProviderFactory.Create 创建具体 Provider。

如果没有配置 Router,则完全保持原有行为:使用 config.ModelConfig 或模板中的 Model

使用示例

完整示例参考:examples/router/main.go

下面是一个简化示例片段:

defaultModel := &types.ModelConfig{
    Provider: "anthropic",
    Model:    "claude-3-5-sonnet-20241022",
}

routes := []router.StaticRouteEntry{
    {
        Task:     "chat",
        Priority: router.PriorityQuality,
        Model: &types.ModelConfig{
            Provider: "anthropic",
            Model:    "claude-3-5-sonnet-20241022",
        },
    },
    {
        Task:     "chat",
        Priority: router.PriorityCost,
        Model: &types.ModelConfig{
            Provider: "deepseek",
            Model:    "deepseek-chat",
        },
    },
}

rt := router.NewStaticRouter(defaultModel, routes)

然后在创建 Agent 时指定不同的 RoutingProfile

qualityAgent, _ := agent.Create(ctx, &types.AgentConfig{
    TemplateID:     "router-demo",
    RoutingProfile: string(router.PriorityQuality),
}, deps)

costAgent, _ := agent.Create(ctx, &types.AgentConfig{
    TemplateID:     "router-demo",
    RoutingProfile: string(router.PriorityCost),
}, deps)

两个 Agent 会在相同模板下,根据不同的路由偏好选择不同的模型。

设计原则与后续扩展

当前 Router 设计遵循几个原则:

  • 可选:不配置 Router 时,行为与旧版本完全一致;
  • 轻量:只做模型选择,不做复杂工作流调度;
  • 可扩展:未来可以添加:
    • 基于延迟/错误率的动态路由;
    • A/B 实验与多臂赌博;
    • 从配置文件或远程配置中心加载路由规则。

这使得 aster 可以在保持简单易用的同时,逐步向更完整的“模型路由”能力靠拢。