路由工作流



1.路由流程图

  • 上面一张图选自langGraph
  • 下面一张选自SpingAi

2.适用场景

  • 主要适用于不同类别应用场景
  • 需要分类处理的应用场景

3.示例

3.1 输入部分

List<String> tickets = List.of(
                  """
                          主题:无法登录账户
                          内容:你好,我这一个小时一直在尝试登录,但总是提示“密码无效”。
                          我很确定我输入的是正确密码。你能帮我恢复访问权限吗?这很紧急,我需要在今天提交报告。
                          - John
                          """,
                  """
                          主题:信用卡上有一笔异常扣费
                          内容:您好,我刚刚注意到我的信用卡上有一笔.99元的扣费,但我记得我订阅的是.99元的套餐。
                          请问这笔费用是怎么来的?如果有误的话能否帮我处理一下?
                          谢谢,
                          Sarah
                          """,
                  """
                          主题:如何导出数据?
                          内容:我需要把我的项目数据导出到 Excel。我查阅了文档,但没找到如何批量导出的方法。
                          请问可以做到吗?如果可以,能不能指导我具体步骤?
                          此致
                          Mike
                          """
          );

3.2 路由与提示词

3.21 路由前置部分

String selectorPrompt = String.format("""
              请分析以下输入内容,并从以下支持团队中选择最合适的一个:%s
              请先解释你的判断依据,然后按照以下 JSON 格式提供你的选择:
              \\{
                  "reasoning": "简要说明为何将该请求分配给该支持团队。
                               请考虑关键词、用户意图以及紧急程度等因素。",
                  "selection": "所选择的团队名称"
              \\}
              输入:%s
              """, availableRoutes, input);

3.22 路由与提示词部分

  • 以key-value构成的路由
  • key代表路由
  • value 则为对应的提示词部分
Map<String, String> supportRoutes = Map.of(
                   "billing",
                   """
                           你是一名账单支持专员,请遵循以下指南:
                           1. 始终以“账单支持回复:”开头
                           2. 首先确认用户提出的具体账单问题
                           3. 清晰说明费用来源或差异原因
                           4. 列出明确的后续处理步骤及时间表
                           5. 如有必要,请说明可选的付款方式
                                           
                           回应应专业且友好。
                                           
                           输入:
                           """,

                   "technical",
                   """
                           你是一名技术支持工程师,请遵循以下指南:
                           1. 始终以“技术支持回复:”开头
                           2. 明确列出解决问题的具体步骤
                           3. 如有必要,说明系统要求
                           4. 提供常见问题的替代解决方案
                           5. 如需升级处理,请说明相关流程
                                           
                           使用清晰、有编号的步骤和技术细节。
                                           
                           输入:
                           """,

                   "account",
                   """
                           你是一名账户安全专员,请遵循以下指南:
                           1. 始终以“账户支持回复:”开头
                           2. 优先处理账户安全和身份验证
                           3. 提供账户找回或更改的清晰步骤
                           4. 包含安全提示和警示信息
                           5. 明确说明处理时间和用户期望
                                           
                           语气严肃,注重安全。
                                           
                           输入:
                           """,

                   "product",
                   """
                           你是一名产品专员,请遵循以下指南:
                           1. 始终以“产品支持回复:”开头
                           2. 注重功能讲解和使用建议
                           3. 提供具体的使用示例
                           4. 链接相关文档章节
                           5. 推荐其他可能有帮助的功能
                                           
                           保持教学式语气,积极鼓励用户。
                                           
                           输入:
                           """
           );

3.3 输出部分

Ticket 1
--------------------------------------------------------------------------------------------

主题:无法登录账户
内容:你好,我这一个小时一直在尝试登录,但总是提示“密码无效”。
我很确定我输入的是正确密码。你能帮我恢复访问权限吗?这很紧急,我需要在今天提交报告。
- John

--------------------------------------------------------------------------------------------

路由信息:

有效的路由: [product, account, technical, billing]
路由分析:用户无法登录账户并收到提示“密码无效”,这表明问题与账户访问相关。用户明确表示其密码正确,急需恢复账户访问以提交报告,因此最合适的支持团队是负责处理账户问题的团队。
选中的路由: account

--------------------------------------------------------------------------------------------
账户支持回复:

您好,John,

感谢您就账户访问问题与我们联系。我们优先关注您的账户安全,并将协助您恢复访问权限。请按照以下步骤操作:

1. **密码重置**- 请访问我们的账户登录页面,点击“忘记密码”选项。
   - 输入您的注册邮箱地址以接收密码重置链接。
   - 检查您的电子邮箱并点击重置链接,按照提示设置一个新密码。请确保新密码与之前的不同,并符合我们的密码安全标准(至少8个字符,包含字母、数字和符号)。

2. **身份验证**- 如果您已启用双重身份验证,请确保您的手机或身份验证设备可用,以便完成登录。

3. **账户安全提示**- 请不要与他人分享您的新密码。
   - 定期更新密码,避免在多个平台使用相同密码。
   - 小心处理来自不明来源的电子邮件或短信,防范网络钓鱼攻击。

4. **处理时间**- 通常情况下,密码重置和账户恢复步骤应在15分钟内完成。如果您在此过程中遇到任何问题,请立即回复此邮件或通过我们的客户支持电话联系我们。

鉴于您的时间紧迫,我们建议您尽快完成上述步骤。如有任何紧急情况,请与我们的支持团队直接联系以获取进一步帮助。

感谢您的理解与配合。

账户安全专员  
[您的公司名称]
--------------------------------------------------------------------------------------------

Ticket 2
--------------------------------------------------------------------------------------------

主题:信用卡上有一笔异常扣费
内容:您好,我刚刚注意到我的信用卡上有一笔.99元的扣费,但我记得我订阅的是.99元的套餐。
请问这笔费用是怎么来的?如果有误的话能否帮我处理一下?
谢谢,
Sarah

--------------------------------------------------------------------------------------------

路由信息:

有效的路由: [product, account, technical, billing]
路由分析:用户提到信用卡上有一笔异常扣费,并询问费用来源及是否有误,涉及财务问题,因此应由billing支持团队处理。
选中的路由: billing

--------------------------------------------------------------------------------------------
账单支持回复:

您好,Sarah,非常感谢您联系账单支持团队。

确认您的问题:您注意到信用卡上有一笔.99元的扣费,而您订阅的是.99元的套餐。

费用来源说明:根据我们的记录,这笔扣费可能是由于最近的价格调整或特定服务的附加费用引起的。我们需要进一步核实您的账户细节以确认这笔费用的具体来源。

后续处理步骤及时间表:
1. 我们将在24小时内审核您的账户记录。
2. 如果确认有误,我们将在审核后的48小时内处理退款或调整。
3. 我们会通过电子邮件通知您审核结果及任何进一步的步骤。

可选付款方式说明:如果您需要更改付款方式或有其他账单处理需求,请您访问我们的在线客户门户,或直接联系我们的客户服务团队以获取更多帮助。

希望以上信息能帮助您解决问题。如需进一步协助,请随时与我们联系。

谢谢,
账单支持团队
--------------------------------------------------------------------------------------------

Ticket 3
--------------------------------------------------------------------------------------------

主题:如何导出数据?
内容:我需要把我的项目数据导出到 Excel。我查阅了文档,但没找到如何批量导出的方法。
请问可以做到吗?如果可以,能不能指导我具体步骤?
此致
Mike

--------------------------------------------------------------------------------------------

路由信息:

有效的路由: [product, account, technical, billing]
路由分析:用户询问如何导出项目数据到 Excel,并希望获得具体操作步骤。这涉及到产品功能的使用和指导,因此最合适的支持团队是 product 团队。
选中的路由: product

--------------------------------------------------------------------------------------------
产品支持回复:

您好,Mike!感谢您的询问。导出项目数据到 Excel 是一个非常常见的需求,我很乐意为您提供帮助。

首先,您需要确保您使用的产品版本支持数据导出功能。通常情况下,您可以通过以下步骤来批量导出您的项目数据:

1. **访问项目数据页面**:打开您的项目,找到“数据”或“报告”选项,这通常位于项目的导航菜单中。

2. **选择导出选项**:在数据页面上,您应该能够找到一个“导出”按钮。点击此按钮后,您会看到不同的导出格式选项。选择“Excel”作为导出格式。

3. **批量选择数据**:如果需要批量导出,请确保在导出前选择所需的数据范围或批量选择所有项目数据。有些平台允许您通过过滤器或选择特定日期范围来批量选择数据。

4. **完成导出**:选择好数据后,点击“导出”或“下载”按钮,系统会生成一个 Excel 文件供您下载。

具体的步骤可能会根据您使用的平台有所不同,但一般流程是类似的。如果您有更多的疑问,建议您查看[文档的导出章节](#)(请插入具体链接),该章节详细描述了导出的步骤和注意事项。

此外,您可能会对其他相关功能感兴趣,如数据分析工具或自动化报告生成功能,这些功能可以帮助您更好地管理和分析项目数据。

请尝试以上步骤,并随时联系我获取更多帮助。祝您导出顺利!

此致  
产品支持团队
--------------------------------------------------------------------------------------------

4.编码实现

4.1 路由实体定义

  • 定义record 实体部分
  • selection 代表路由。路由集合:[product, account, technical, billing]
  • reasoning 选择对应路由的原因

public record RoutingResponse(String reasoning, String selection) {

}

4.2 路由实现

4.21 路由识别实现

  • determineRoute 返回的则是 RoutingResponse 实体中的selection,即路由部分,[product, account, technical, billing] 集合中的一部分
  • 提示词与record 实体返回固定参数
@SuppressWarnings("null")
private String determineRoute(String input, Iterable<String> availableRoutes) {
    System.out.println("\n有效的路由: " + availableRoutes);

    String selectorPrompt = String.format("""
            请分析以下输入内容,并从以下支持团队中选择最合适的一个:%s
            请先解释你的判断依据,然后按照以下 JSON 格式提供你的选择:
            \\{
                "reasoning": "简要说明为何将该请求分配给该支持团队。
                             请考虑关键词、用户意图以及紧急程度等因素。",
                "selection": "所选择的团队名称"
            \\}
            输入:%s
            """, availableRoutes, input);


    RoutingResponse routingResponse = chatClient.prompt(selectorPrompt).call().entity(RoutingResponse.class);

    System.out.println(String.format("路由分析:%s\n选中的路由: %s",
            routingResponse.reasoning(), routingResponse.selection()));
    System.out.println();
    return routingResponse.selection();
}

4.22 路由部分实现

  • 通过input ,及路由识别方法(大模型判断)获取对应的路由key及选择的提示词部分(类似意图识别与分析)
  • 通过input与路由识别后的提示词再次进行大模型交互,获取对应的output
public String route(String input, Map<String, String> routes) {
      Assert.notNull(input, "Input text cannot be null");
      Assert.notEmpty(routes, "Routes map cannot be null or empty");


      String routeKey = determineRoute(input, routes.keySet());
      String selectedPrompt = routes.get(routeKey);

      if (selectedPrompt == null) {
          throw new IllegalArgumentException("Selected route '" + routeKey + "' not found in routes map");
      }

      System.out.println("--------------------------------------------------------------------------------------------");
      return chatClient.prompt(selectedPrompt + "\nInput: " + input).call().content();
  }

4.23 完整RoutingWorkflow部分

package com.coderpwh.work;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.util.Assert;

import java.util.Map;

public class RoutingWorkflow {

    private final ChatClient chatClient;

    public RoutingWorkflow(ChatClient chatClient) {
        this.chatClient = chatClient;
    }

    public String route(String input, Map<String, String> routes) {
        Assert.notNull(input, "Input text cannot be null");
        Assert.notEmpty(routes, "Routes map cannot be null or empty");


        String routeKey = determineRoute(input, routes.keySet());
        String selectedPrompt = routes.get(routeKey);

        if (selectedPrompt == null) {
            throw new IllegalArgumentException("Selected route '" + routeKey + "' not found in routes map");
        }

        System.out.println("--------------------------------------------------------------------------------------------");
        return chatClient.prompt(selectedPrompt + "\nInput: " + input).call().content();
    }


    @SuppressWarnings("null")
    private String determineRoute(String input, Iterable<String> availableRoutes) {
        System.out.println("\n有效的路由: " + availableRoutes);

        String selectorPrompt = String.format("""
                请分析以下输入内容,并从以下支持团队中选择最合适的一个:%s
                请先解释你的判断依据,然后按照以下 JSON 格式提供你的选择:
                \\{
                    "reasoning": "简要说明为何将该请求分配给该支持团队。
                                 请考虑关键词、用户意图以及紧急程度等因素。",
                    "selection": "所选择的团队名称"
                \\}
                输入:%s
                """, availableRoutes, input);


        RoutingResponse routingResponse = chatClient.prompt(selectorPrompt).call().entity(RoutingResponse.class);

        System.out.println(String.format("路由分析:%s\n选中的路由: %s",
                routingResponse.reasoning(), routingResponse.selection()));
        System.out.println();
        return routingResponse.selection();
    }


}

5.归纳

  • 依据上面的两张流程图,先进行识别具体的路由值,后具体实现,共交互两次
  • 关于代码中的record ,是实体中的一部分,定义的参数的是private final 类型,常用于固定参数值

文章作者: coderpwh
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 coderpwh !
  目录