第6节:路由链(Router Chain)详解:小白也能看懂的教程
什么是路由链?
路由链(Router Chain)是 LangChain 框架中的一个重要概念,它的作用就像一个"智能调度员"。想象一下你去银行办事,门口有一个引导员,他会根据你要办的业务把你引导到对应的窗口。路由链就是这样的"引导员",它会根据用户的问题,自动选择最合适的专家来回答。
比如:
你问游泳相关问题 → 路由链引导到"游泳教练"
你问跑步相关问题 → 路由链引导到"跑步教练"
你问篮球相关问题 → 路由链引导到"篮球教练"
核心代码解析
1. 定义路由信息结构体
type MultiPromptInfo struct { Key string // 问题类型的标识符(比如"swimming") Description string // 描述这个专家是做什么的 Prompt prompts.FormatPrompter // 这个专家具体的提示词模板 }
这个结构体就像一个"专家档案",记录了每个专家的:
唯一标识(Key)
专业介绍(Description)
回答问题的模板(Prompt)
2. 路由链的核心结构
type MultiChains struct { LLMChains *chains.LLMChain // 主路由链,负责判断问题类型 Routers map[string]chains.Chain // 存储所有专家的实际处理链 Destinations map[string]string // 存储所有可用的专家标识和描述 OutputParser outputparser.Structured // 结构化输出解析器 }
这就像一个调度中心的完整配置:
LLMChains: 调度员(主路由)
Routers: 所有专家的实际工作台
Destinations: 所有可选专家的名单
OutputParser: 格式化解析工具
3. 创建路由链
func NewMultiChains(llm llms.Model, multiPrompts []*MultiPromptInfo) *MultiChains { var ( routerLLChain = make(map[string]chains.Chain, len(multiPrompts)) destination = make(map[string]string, len(multiPrompts)) ) for i, _ := range multiPrompts { // 为每个专家创建专门的处理链 routerLLChain[multiPrompts[i].Key] = chains.NewLLMChain(llm, multiPrompts[i].Prompt) // 记录专家标识和描述的对应关系 destination[multiPrompts[i].Key] = multiPrompts[i].Description } return &MultiChains{ Destinations: destination, LLMChains: chains.NewLLMChain(llm, _prompt), // 创建主路由链 Routers: routerLLChain, OutputParser: _outputparser, } }
这段代码就像在建立一个专家团队:
1.为每个专家创建专门的工作台(处理链)
2.建立专家名单(标识和描述的对应关系)
3.设置总调度台(主路由链)
4. 路由决策过程
func (m MultiChains) Call(ctx context.Context, inputs map[string]any, options ...chains.ChainCallOption) (map[string]any, error) { input := inputs[_input] // 让主路由链判断应该交给哪个专家处理 res, err := chains.Predict(ctx, m.LLMChains, map[string]any{ _input: input, _destinations: m.Destinations, _formatting: m.OutputParser.GetFormatInstructions(), }, options...) if err != nil { return nil, err } // 根据判断结果交给对应专家处理 return m.processLLMResult(ctx, res) }
这就像调度员的工作流程:
1.接收用户问题
2.查看所有专家的资料
3.判断应该交给哪个专家
4.把问题转交给对应专家
5. 专家处理过程
func (m *MultiChains) processLLMResult(ctx context.Context, text string) (map[string]any, error) { // 解析调度员的判断结果 data, err := m.OutputParser.Parse(text) if err != nil { return nil, err } next := data.(map[string]string) // 获取调度员指定的专家标识 destination := next[_destinations] // 获取给专家的具体问题 nextInput := next[_nextInput] // 找到对应的专家并让专家回答问题 router := m.Routers[destination] return chains.Call(ctx, router, map[string]any{ _input: nextInput, }) }
这就像专家处理问题的过程:
1.理解调度员的安排(解析判断结果)
2.找到对应的专家
3.把具体问题交给专家回答
实际使用示例
func TestRouterChains(t *testing.T) { // 定义专家团队 multiPromptInfos := []*MultiPromptInfo{ { Key: "swimming", // 游泳专家标识 Description: "你是一个资深的游泳教练,精通游泳相关知识", // 专家介绍 Prompt: prompts.NewPromptTemplate( // 专家回答模板 "你是一个资深的游泳教练,精通游泳相关知识 请根据用户输入问题进行回答 \n {{.input}}", []string{"input"}), }, { Key: "racing", // 跑步专家标识 Description: "你是一个资深的赛跑冠军,精通赛跑相关知识", // 专家介绍 Prompt: prompts.NewPromptTemplate( // 专家回答模板 "你是一个资深的赛跑冠军,精通赛跑相关知识 请根据用户输入问题进行回答 \n {{.input}}", []string{"input"}), }, } // 创建LLM模型 llm := getLLmOpenaiClientNew(t) // 创建路由链系统 routerChains := NewMultiChains(llm, multiPromptInfos) // 用户提问并获取答案 predict, err := chains.Call(context.Background(), routerChains, map[string]any{"input": "请问如何游泳?"}) if err != nil { panic(err) return } fmt.Println(predict) }
工作流程总结
1.初始化阶段:
- 创建各个领域的专家(游泳教练、跑步教练等)
- 为每个专家设置专门的处理模板
- 建立调度系统
2.处理用户问题阶段:
- 用户提出问题:"请问如何游泳?"
- 调度员分析问题内容
- 调度员判断应该交给游泳教练处理
- 将问题转给游泳教练
- 游泳教练根据专业模板回答问题
- 返回最终答案给用户
为什么这样设计?
这种设计有几个重要优势:
- 专业性强:每个专家只处理自己擅长的领域,回答更专业
- 扩展性好:可以轻松添加新的专家领域
- 智能化:自动判断问题类型,无需用户手动选择
- 解耦合:各个专家相互独立,互不影响
常见问题和解决方案
为什么Key建议用英文?
虽然Key可以用中文,但建议用英文,因为:
- 大多数LLM模型对英文更敏感,处理更准确
- 英文标识更简洁,减少解析错误
- 避免中文编码问题
如果LLM判断错误怎么办?
可以通过优化提示词模板来提高判断准确性:
- 给出更明确的判断标准
- 提供更多示例
- 明确输出格式要求
总结
路由链就像一个智能的"问题分发系统",它通过以下步骤工作:
- 建立专家团队
- 设置调度中心
- 接收用户问题
- 智能分发给合适专家
- 返回专家答案
这种设计模式在实际应用中非常有用,比如客服系统、智能助手、专业咨询系统等场景都可以采用类似的架构
源代码
https://download.csdn.net/download/qq_36466946/91999333

浙公网安备 33010602011771号