智能工单路由系统(Java)

取代或辅助人工,自动分析新提交的工单内容(文本描述),准确判断其问题类别(Classification)和紧急程度(Sentiment Analysis),并自动分配给最合适的处理团队或工程师。

微服务架构设计:

  • 工单接入服务 (Ticket Ingestion Service):接收来自Web前端、邮件或其他渠道的新工单。
  • 智能分析服务 (AI Analysis Service):负责调用大模型API对工单内容进行分析。
  • 路由决策服务 (Routing Decision Service):根据分析结果和执行规则,决定分配给谁。
  • 工单管理服务 (Ticket Management Service):负责工单的持久化、状态更新和分配操作。
  • 消息队列 (Message Queue, e.g. RabbitMQ/Kafka):用于服务间的异步通信,解耦并提高可靠性

技术栈选型:

  • Java框架: Spring Boot + Spring Cloud Stream (用于消息队列集成)
  • 大模型API: OpenAI GPT-4 / GPT-3.5-Turbo 或 Azure OpenAI(企业级首选,因其合规性和安全性更佳) 或 本地部署的Mistral/Mixtral模型。
  • 消息队列: RabbitMQ
  • 数据库: PostgreSQL (存储工单信息、分析结果、分配记录)
  • 向量数据库 (可选,用于高级RAG): Redis with RedisStack (RedisSearch)
  • 开发工具: LangChain4j (极大简化LLM集成代码)

一、主要依赖

xml 复制代码
<!-- LangChain4j for OpenAI -->
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-open-ai</artifactId>
    <version>0.28.0</version> <!-- 请使用最新版本 -->
</dependency>
<!-- 如果你用Azure OpenAI -->
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-azure-open-ai</artifactId>
    <version>0.28.0</version>
</dependency>

二、创建工单实体和分析结果实体

java 复制代码
@Entity
public class Ticket {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;
    @Column(columnDefinition = "TEXT")
    private String description; // 工单详细描述,这是给LLM分析的核心内容
    private String submitterEmail;
    private LocalDateTime createdAt;
    private String status; // e.g., "NEW", "ANALYZING", "ASSIGNED", "CLOSED"
    private String assignedTeam; // 被分配到的团队,e.g., "NETWORK", "DATABASE", "BILLING"
    // ... getters and setters
}

@Entity
public class TicketAnalysis {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private Long ticketId;
    private String category; // LLM分析出的类别,e.g., "硬件故障", "软件bug", "账户问题"
    private String urgency; // LLM分析出的紧急程度,e.g., "高", "中", "低"
    private String suggestedTeam; // LLM建议分配的团队
    @Column(columnDefinition = "TEXT")
    private String reasoning; // LLM做出判断的推理过程,用于审计和调试
    private LocalDateTime analyzedAt;
    // ... getters and setters
}

三、实现工单接入服务(创建)---发送---监听

java 复制代码
@RestController
@RequestMapping("/api/tickets")
public class TicketController {

    @Autowired
    private TicketService ticketService;

    @PostMapping
    public ResponseEntity<Ticket> createTicket(@RequestBody Ticket ticket) {
        ticket.setStatus("NEW");
        ticket.setCreatedAt(LocalDateTime.now());
        Ticket savedTicket = ticketService.save(ticket);
        
        // 发送消息到消息队列,触发后续分析
        ticketService.notifyNewTicket(savedTicket);
        
        return ResponseEntity.ok(savedTicket);
    }
}
java 复制代码
@Service
@EnableBinding(Source.class) // Spring Cloud Stream 旧版注解,新版本可用函数式方式
public class TicketService {
    @Autowired
    private Source source;

    public void notifyNewTicket(Ticket ticket) {
        source.output().send(MessageBuilder.withPayload(ticket.getId()).build());
    }
}
java 复制代码
@Service
@EnableBinding(Sink.class)
public class TicketAnalysisListener {

    @Autowired
    private TicketAnalysisService analysisService;

    @StreamListener(Sink.INPUT)
    public void handleNewTicket(Long ticketId) {
        analysisService.analyzeAndRouteTicket(ticketId);
    }
}

四、构建核心------智能分析服务

①、LLM客户端

yml 复制代码
langchain4j:
  open-ai:
    chat-model:
      api-key: ${OPENAI_API_KEY}
      model-name: "gpt-3.5-turbo" # 或者 "gpt-4"
      temperature: 0.0 # 越低输出越确定
      max-tokens: 500

②、设计Prompt

设计一个结构化的Prompt是成功的关键。它需要明确指令、格式和示例。

③、实现分析服务

java 复制代码
@Service
public class TicketAnalysisService {
    
    @Autowired
    private TicketRepository ticketRepository;
    @Autowired
    private TicketAnalysisRepository analysisRepository;
    @Autowired
    private RoutingDecisionService routingService;

    // 注入LangChain4j的OpenAiChatModel
    private final OpenAiChatModel chatModel;

    public TicketAnalysisService(OpenAiChatModel chatModel) {
        this.chatModel = chatModel;
    }

    @Transactional
    public void analyzeAndRouteTicket(Long ticketId) {
        Ticket ticket = ticketRepository.findById(ticketId)
            .orElseThrow(() -> new RuntimeException("Ticket not found: " + ticketId));
        
        // 更新状态为分析中
        ticket.setStatus("ANALYZING");
        ticketRepository.save(ticket);

        // 构建Prompt
        String prompt = """
            你是一个资深的IT支持专家。请分析以下工单描述,并严格按照JSON格式输出分析结果。
            
            工单标题: %s
            工单描述: %s
            
            请执行以下步骤:
            1. **分类**:判断问题属于哪个类别("NETWORK", "SERVER", "DATABASE", "APPLICATION", "BILLING", "ACCOUNT", "OTHER")。
            2. **紧急程度**:判断紧急程度("HIGH", "MEDIUM", "LOW")。依据:是否影响大量用户、是否导致业务完全中断、用户情绪是否激动。
            3. **建议分配团队**:根据分类,建议应分配给哪个团队处理。
            4. **推理**:用一句话简要说明你的推理原因。
            
            输出格式必须是以下JSON,不要有任何其他文字:
            {
              "category": "分类结果",
              "urgency": "紧急程度",
              "suggestedTeam": "建议团队",
              "reasoning": "你的推理"
            }
            """.formatted(ticket.getTitle(), ticket.getDescription());

        // 调用LLM
        String aiResponse = chatModel.generate(prompt);

        // 解析LLM的JSON响应 (这里需要简单的JSON解析)
        TicketAnalysis analysis = parseAiResponse(aiResponse);
        analysis.setTicketId(ticketId);
        analysis.setAnalyzedAt(LocalDateTime.now());
        analysisRepository.save(analysis);

        // 将分析结果传递给路由决策服务
        routingService.makeRoutingDecision(ticketId, analysis);
    }

    private TicketAnalysis parseAiResponse(String jsonResponse) {
        // 使用Jackson或Gson解析JSON字符串到TicketAnalysis对象
        ObjectMapper mapper = new ObjectMapper();
        try {
            return mapper.readValue(jsonResponse, TicketAnalysis.class);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("Failed to parse AI response", e);
        }
    }
}

④、实现路由决策服务

决策服务可以包含简单的规则引擎,例如即使LLM建议分配给"NETWORK"团队,但如果紧急程度为"HIGH",则可能直接分配给"NETWORK_ON_CALL"团队。

java 复制代码
@Service
public class RoutingDecisionService {

    @Autowired
    private TicketRepository ticketRepository;

    public void makeRoutingDecision(Long ticketId, TicketAnalysis analysis) {
        Ticket ticket = ticketRepository.findById(ticketId).orElseThrow();

        String finalTeam = analysis.getSuggestedTeam();

        // 示例:简单的规则覆盖
        if ("HIGH".equals(analysis.getUrgency()) && "NETWORK".equals(finalTeam)) {
            finalTeam = "NETWORK_ON_CALL"; // 网络团队 on-call 小组
        }
        if ("BILLING".equals(finalTeam) && analysis.getReasoning().toLowerCase().contains("refund")) {
            finalTeam = "BILLING_SUPERVISOR"; // 退款问题需要主管处理
        }

        // 执行分配
        ticket.setAssignedTeam(finalTeam);
        ticket.setStatus("ASSIGNED");
        ticketRepository.save(ticket);

        // TODO: 这里可以触发邮件或通知,告知团队有新工单分配
        System.out.println("Ticket #" + ticketId + " assigned to team: " + finalTeam);
    }
}
相关推荐
Source.Liu3 小时前
【Python基础】 13 Rust 与 Python 注释对比笔记
开发语言·笔记·python·rust
qq_195551693 小时前
代码随想录70期day3
开发语言·python
XXYBMOOO3 小时前
Qt UDP 通信类详解与实现
开发语言·网络·c++·qt·网络协议·ui·udp
pusue_the_sun3 小时前
C语言强化训练(12)
c语言·开发语言·算法
失散133 小时前
分布式专题——1.1 Redis单机、主从、哨兵、集群部署
java·数据库·redis·分布式·架构
刘一说3 小时前
Linux调试命令速查:Java/微服务必备
java·linux·微服务
IT·陈寒3 小时前
怎么这么多 StringUtils —— Apache、Spring、Hutool 全面对比
java·spring·apache
AAA修煤气灶刘哥4 小时前
MySQL 查文本查哭了?来唠唠 ES 这货:从 “啥是 ES” 到 Java 撸代码,一篇整明白!
java·后端·elasticsearch
金銀銅鐵4 小时前
[Java] 浅析密封类(Sealed Classes) 在 class 文件中是如何实现的
java·后端