多领域智能客服:基于Java的智能分发与定制化问答系统

简介: 本文介绍了如何通过Dify平台构建一个多领域AI客服系统,以解决传统AI客服在复杂业务场景中回答不准确的问题。该系统将客服模块按售前、售后和日常业务划分,利用Dify的API进行集成和调用,并通过知识库提供上下文支持,确保精准回应。架构设计中,首先部署Dify,创建并配置AI应用,编写提示词,管理知识库,封装API接口,实现问题分类与转发逻辑。最终,通过日志记录测试过程,验证系统的有效性。此方案适用于希望提升AI客服效果的企业。

一、引子

AI客服的概念早已不再新鲜,随着自然语言处理技术的不断进步,越来越多的企业尝试将 AI 客服引入实际业务中。然而,尽管大模型具备强大的语言理解能力,受限于业务场景的复杂性和大模型自身的局限性,AI 客服在许多企业中的应用落地效果并不理想。尤其是在面对多领域、多场景的客户需求时,单一的 AI 模型往往难以提供精准且符合上下文的回答,进而影响了用户体验和企业的运营效率。

二、业务梳理

业务场景的多样性使得客户问题不仅限于某一个方面。客户可能会询问产品信息(售前)、订单和退换货等服务(售后),以及公司政策或流程等日常事务(业务支持)。面对这些高度垂直化的需求,传统的 AI 客服难以兼顾各个领域,导致服务体验割裂、回答不准确。

为了解决这一问题,我们采用了一种更为灵活的方式,将客服系统按照业务维度进行划分,拆解为多个专门负责不同领域的 AI 客服模块。比如,可以按照问题类型分别构建售前售后日常业务三大问题领域的AI客服,对于用户发送的问题,首先通过一个中转服务利用AI进行判断属于那个领域的问题,再转发到适配的客服。

三、架构设计

在本次的设计中,我们需要用到一个平台 - Dify,我们通过它去连接大模型可以单独部署AI应用,这样在项目里只需要封装API进行调用即可,把业务系统与AI应用进行解耦。因此,我们可以抽象出业务架构图如下:
01.png
通过架构图可以看出,我们这个系统需要4个AI应用来支持,每个应用的开发和维护都通过Dify进行,我们的业务系统直接调用即可。

四、技术实现

部署Dify

本文对Dify的部署不予演示,感兴趣的同学直接去官网下载即可,部署非常简单,通过docker可以一键部署。

创建应用

接下来开始创建AI应用,应用开发的重点在提示词的编写。我们先以判断客户需求的AI客服为例,如下:
02.png

提示词如下:
03.png
04.png

提示词编写完成后,可以在右侧测试效果,同时记得点击发布,如下:
05.png

同理,另外三个业务客服都采用上述方式创建。这里我以业务客服为例,给大家展示下提示词,如下:
06.png

可以看到提示词里提到了知识库,知识库简单地说可以为AI提供一些上下文,比如我们客服的场景里就可以把相关客服的培训手册提供给应用。我们可以通过Dify直接管理知识库,也可以通过API操作,我们这里方便管理,直接上传即可。如下:
07.png
我们可以在知识库里可以直接创建,如下:
08.png
可以看到知识库文本的格式支持很广泛,如下:
09.png
我这里为了方便演示,就直接建一个客服中心的word文档,输入以下内容,如下:
10.png
数据的清洗和分段都可以自动进行,我们这步也不做什么调整,直接下一步就行,如下:
11.png
最后为我们的应用关联上,我们可以测试下开发票的效果,可以看到它根据知识库的内容回答,如下:
12.png

API封装

Dify已经对API封装的格式做了说明,如下:
13.png

这里需要说明下,我们在每个应用里创建的API-Key都是调用这个应用的凭证,所以在项目里调用应用都需要先生成它的Key,并把它配置在项目里,如下:

public class DifyServerConstants {
   

    /**
     * The base URL of the Dify API.
     */
    public static final String BASE_URL = "https://dify.xxxxxxxx.com.cn/v1";

    /**
     * XX应用api key
     */
    public static final String APP_API_KEY = "XXXXXXXXXX";
}

在实际中根据自己的应用使用情况可以把key都统一管理起来,常量类是种实现,写在配置文件里也是。然后我们基于官方的文档简单封装下,代码如下:

private String getResultByDify(String text, String BearerAuth) {
   
    //设置请求体
    JSONObject body = new JSONObject();
    body.put("query", text);
    body.put("response_mode", "blocking");
    // 注:这个userId是调用dify应用的必须参数,一般取当前登录用户即可,如没有特殊要求也可随意指定
    Long userId = LoginHelper.getUserId();
    body.put("inputs", "");
    // 封装请求体和请求头
    String jsonString = body.toJSONString();
    //发送post请求,阻塞式
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    headers.setAccept(List.of(MediaType.APPLICATION_JSON));
    headers.setBearerAuth(BearerAuth);
    // 封装请求体和请求头
    HttpEntity<String> entity = new HttpEntity<>(jsonString, headers);
    //url
    String url = DifyServerConstants.BASE_URL + "/chat-messages";
    //发送请求
    ResponseEntity<String> stringResponseEntity =
        restTemplate.postForEntity(url, entity, String.class);
    //获取answer字段
    if (ObjectUtil.isNotEmpty(stringResponseEntity.getBody())) {
   
        JSONObject jsonObject = JSONObject.parseObject(stringResponseEntity.getBody());
        String answer = jsonObject.getString("answer");
        //去掉markdown
        answer = StrUtil.removeAll(answer, "```json");
        answer = StrUtil.removeAll(answer, "```");
        return answer;
    }
    return null;
}

我们可以把它声明为一个接口或者工具类,只需要传入问题和应用的key就可以实现通用调用了,如下:

String answer = getResultByDify(question, DifyServerConstants.XXX_KEY);

转发逻辑实现

编写一个接口用来接收用户输入的问题,代码如下:

  /**
     * 根据问题分类并调用相应的Dify服务获取答案
     *
     * @param userQuery 用户提出的问题
     * @param BearerAuth Dify的Bearer token
     * @return Dify返回的答案
     */
    public String handleUserQuery(String userQuery, String BearerAuth) {
   
        // 调用分类模型,获取类型
        String domainJson = getResultByDify(userQuery, BearerAuth);
        // 解析结果json并返回类型
        int domainType = extractTypeFromJson(domainJson);// 根据分类结果调用不同的Dify服务
        switch (domainType) {
   
            case 1:
                return getResultByDify(userQuery, BearerAuth);  // 售前
            case 2:
                return getResultByDify(userQuery, BearerAuth); // 售后
            case 3:
                return getResultByDify(userQuery, BearerAuth); // 日常业务
            // 后续可扩展的其他领域,如技术支持
            default:
                return "抱歉,我无法识别您的问题类别。";
        }
    }

另外,还需要对AI返回的结果解析下,代码如下:

    /**
     * 从分类结果的JSON中提取领域类型
     *
     * @param jsonResponse 分类结果的JSON字符串
     * @return 领域类型数字
     */
    private int extractTypeFromJson(String jsonResponse) {
   
        JSONObject jsonObject = JSONObject.parseObject(jsonResponse);
        return jsonObject.getInteger("type");
    }

至此,我们就简单地实现了这个AI客服智能分发系统,我们通过日志记录下一次测试过程,如下:

14.png

五、小结

其实上述的这个小demo仅仅依靠dify的工作流就可以实现多应用的协作,但实际业务中,我们可能会遇到在现有的客服系统中去集成AI;或者我们的系统需要先接入AI客服,但客户问题并未得到解决,再接入人工客服,自然就需要我们需要相应的服务管理。因此,本文重点在实现的思路和方案上,希望对大家有所帮助!

目录
相关文章
|
2月前
|
存储 自然语言处理 关系型数据库
基于阿里云通义千问开发智能客服与问答系统
在企业的数字化转型过程中,智能客服系统已成为提高客户满意度和降低运营成本的重要手段。阿里云的通义千问作为一款强大的大语言模型,具有自然语言理解、对话生成、知识检索等能力,非常适合用来开发智能客服与问答系统。 通过本博客,我们将演示如何基于阿里云的通义千问模型,结合阿里云相关产品如函数计算(FC)、API网关、RDS等,搭建一个功能齐全的智能客服系统。
248 5
|
9月前
|
JavaScript Java 测试技术
基于Java的线上招聘问答系统的设计与实现(源码+lw+部署文档+讲解等)
基于Java的线上招聘问答系统的设计与实现(源码+lw+部署文档+讲解等)
57 1
|
Java
客服端和服务器的原理高中生都知道,我还不懂-java网络编程技术
客服端和服务器的原理高中生都知道,我还不懂-java网络编程技术
103 0
|
Java 数据库连接 数据库
Java项目:在线博客问答系统(java+Springboot+jsp+maven+mysql)
Java项目:在线博客问答系统(java+Springboot+jsp+maven+mysql)
258 0
Java项目:在线博客问答系统(java+Springboot+jsp+maven+mysql)
|
Java
Java项目案例:酒店前台客服管理系统
import java.util.Scanner; public class HelloWorld { public static void main(String[] args){ String [][] room = new String[12][10]; System.
1225 0
|
2天前
|
安全 Java 开发者
【JAVA】封装多线程原理
Java 中的多线程封装旨在简化使用、提高安全性和增强可维护性。通过抽象和隐藏底层细节,提供简洁接口。常见封装方式包括基于 Runnable 和 Callable 接口的任务封装,以及线程池的封装。Runnable 适用于无返回值任务,Callable 支持有返回值任务。线程池(如 ExecutorService)则用于管理和复用线程,减少性能开销。示例代码展示了如何实现这些封装,使多线程编程更加高效和安全。
|
1月前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
100 17
|
2月前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
1月前
|
缓存 安全 算法
Java 多线程 面试题
Java 多线程 相关基础面试题
|
2月前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。