🎉Blades 已发布 v0.1.1 版本,欢迎大家使用 https://github.com/go-kratos/blades/releases/tag/v0.1.1
想象一下,你有一个只会按照固定程序工作的助手:你问他"今天天气怎么样",他会回答天气;你问他"帮我订张机票",他还是会回答天气。这样的助手显然不够智能。
真正的智能助手应该像人一样,能够理解不同的问题,并给出相应的回应。这就是路由模式要解决的核心问题——让 AI Agent 学会"随机应变"。
路由本质上就是一个智能的"指挥中心",它负责分析接收到的任务,然后决定应该把这个任务交给哪个专门的"专家"去处理。
举个生活中的例子:当你走进一家大医院时,首先是挂号台的护士问你哪里不舒服。根据你的回答,她会把你引导到不同的科室——感冒去内科,骨折去骨科,心脏问题去心内科。这个护士和科室分配系统,就是一个完美路由模式。
在 AI 系统中,路由模式扮演着同样的角色:接收用户的请求,分析其意图,然后转发给最合适的处理模块。
例如,一个客户服务 Agent 通过路由模式可以分析用户查询的意图,然后将预订相关问题导向订单数据库,将产品信息查询导向搜索功能,将技术问题导向支持工具或人工客服。
路由中最核心的部分就是决策机制,也就是判断将任务分配给谁来做。以下是几种常见的路由决策方式:
if strings.Contains(userQuery, "订单") {
routeToOrderModule()
} else if strings.Contains(userQuery, "技术支持") {
routeToSupportModule()
}
优点:简单快速,结果确定,容易理解缺点:不够灵活,难以处理复杂语义(用户连话都说不明白,自己都不知道自己想要什么)适用场景:简单明确的任务分配
2.基于 LLM 的意图识别:更高级的路由方式是利用 AI 来理解用户的意图。通过分析用户的自然语言输入,AI 可以判断出用户真正想要什么,然后做出路由决策。
用户输入:"我想知道这个产品的规格。"
AI 分析后识别出意图为"查询产品信息",于是将请求路由到产品信息模块。
优点:理解能力强,灵活适应性强缺点:需要更多 Tokens,可能会判断错误适用场景:复杂的对话系统,需要理解语义的场景
3.基于机器学习的路由:通过训练一个分类模型,AI 可以根据历史数据学习如何路由不同类型的请求。随着时间的推移,这种方法可以不断优化路由决策,提高准确率。优点:准确率高,处理复杂情况能力强缺点:需要训练数据,前期投入大适用场景:大规模的成熟系统
我只是列举了部分常见的路由决策方式,实际应用中你可以根据具体需求选择合适的方法,或者同时使用多种方式以达到最佳效果。
因为系列文章主要围绕在 Blades 平台上构建 AI Agent,所以这里重点介绍基于 LLM 的路由设计思路。
总经理
├─────────────────┬─────────────────┬─────────────────┬─────────────────┐
技术部 市场部 人事部 财务部 运营部
│ │ │ │ │
├─────┬─────┐ ├─────┬─────┐ │ ├─────┬─────┐ │
前端组 后端组 推广组 销售组 招聘培训组 会计组 审计组 客服组
路由系统也可以采用类似的分层处理方式,例如:第一层 Agent 负责大致分类(比如订单、技术支持、产品信息),第二层 Agent 再根据具体需求进行更细化的路由(比如订单查询、订单修改、订单取消等)。
我们来实现一个简单的“智能助手”,它有数学 Agent和地理 Agent, 可以根据用户的对话内容,路由到不同的 Agent 进行处理。 基于 LLM 的路由,主要考验的就是提示词设计能力,使用相同模型的条件下,提示词写的越好,路由就越准确。所以我们需要精心设计提示词,让路由 Agent 会根据用户的输入内容,选择最合适的子 Agent 进行处理。
You are a routing agent.
Choose the single best route key for handling the user's request.
User message: <user_message>
Available route keys (choose exactly one):
- math_agent: You provide help with math problems. Explain your reasoning at each step and include examples.
- history_agent: You provide assistance with historical queries. Explain important events and context clearly.
代码部分的实现很简单,核心逻辑都在 selectRoute 方法中。它通过提示词让路由 Agent 根据用户的输入内容, 选择最合适的子 Agent 进行处理。
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"strings"
"github.com/go-kratos/blades"
"github.com/go-kratos/blades/contrib/openai"
)
// RoutingWorkflow is a workflow that routes requests to different agents based on the content of the prompt.
type RoutingWorkflow struct {
blades.Agent
routes map[string]string
agents map[string]blades.Agent
}
// NewRoutingWorkflow creates a new RoutingWorkflow with the given model provider and routes.
func NewRoutingWorkflow(routes map[string]string) (*RoutingWorkflow, error) {
provider := openai.NewChatProvider()
router, err := blades.NewAgent(
"triage_agent",
blades.WithModel("gpt-5"),
blades.WithProvider(provider),
blades.WithInstructions("You determine which agent to use based on the user's homework question"),
)
if err != nil {
returnnil, err
}
agents := make(map[string]blades.Agent, len(routes))
for name, instructions := range routes {
agent, err := blades.NewAgent(
name,
blades.WithModel("gpt-5"),
blades.WithProvider(provider),
blades.WithInstructions(instructions),
)
if err != nil {
returnnil, err
}
agents[name] = agent
}
return &RoutingWorkflow{
Agent: router,
routes: routes,
agents: agents,
}, nil
}
// Run selects a route using the prompt content and streams from the chosen runner.
func (r *RoutingWorkflow) Run(ctx context.Context, invocation *blades.Invocation) blades.Generator[*blades.Message, error] {
returnfunc(yield func(*blades.Message, error) bool) {
agent, err := r.selectRoute(ctx, invocation)
if err != nil {
yield(nil, err)
return
}
stream := agent.Run(ctx, invocation)
for msg, err := range stream {
if !yield(msg, err) {
break
}
}
}
}
// selectRoute determines the best route key and runner for the given prompt.
func (r *RoutingWorkflow) selectRoute(ctx context.Context, invocation *blades.Invocation) (blades.Agent, error) {
var buf strings.Builder
buf.WriteString("You are a routing agent.\n")
buf.WriteString("Choose the single best route key for handling the user's request.\n")
buf.WriteString("User message: " + invocation.Message.Text() + "\n")
buf.WriteString("Available route keys (choose exactly one):\n")
routes, err := json.Marshal(r.routes)
if err != nil {
returnnil, err
}
buf.WriteString(string(routes))
for res, err := range r.Agent.Run(ctx, blades.NewInvocation(blades.UserMessage(buf.String()))) {
if err != nil {
returnnil, err
}
choice := strings.TrimSpace(res.Text())
return r.agents[choice], nil
}
returnnil, fmt.Errorf("no route selected")
}
func main() {
var (
routes = map[string]string{
"math_agent": "You provide help with math problems. Explain your reasoning at each step and include examples.",
"history_agent": "You provide assistance with historical queries. Explain important events and context clearly.",
}
)
routing, err := NewRoutingWorkflow(routes)
if err != nil {
log.Fatal(err)
}
// Example prompt that will be routed to the history_agent
input := blades.UserMessage("What is the capital of France?")
runner := blades.NewRunner(routing)
res, err := runner.Run(context.Background(), input)
if err != nil {
log.Fatal(err)
}
log.Println(res.Text())
}
https://github.com/go-kratos/blades/blob/main/examples/workflow-routing
路由模式是构建智能 AI Agent 的关键技术之一。通过合理设计路由机制,AI 系统能够更高效、更准确地处理用户请求,提升整体性能和用户体验。在实际应用中,可以根据具体需求选择合适的路由决策方式,并结合上下文信息和反馈机制不断优化路由策略。
后续的文章中,我会继续分享使用 Blades,通过 flow、graph 等组件方式实现更复杂路由模式的设计思路。