Java基于大模型实现客服系统

如果你问大模型企业私有化的问题(比如:怎么退款?),大模型很可能就答非所问了,倘若让大模型结合企业内部数据回答, 用户满意度大幅提升 。以下基于RAG技术实现客服系统

1、引入相关包

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <langchain4j.version>0.33.0</langchain4j.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j</artifactId>
            <version>${langchain4j.version}</version>
        </dependency>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-open-ai</artifactId>
            <version>${langchain4j.version}</version>
        </dependency>

        <dependency>
            <groupId>org.tinylog</groupId>
            <artifactId>tinylog-impl</artifactId>
            <version>2.6.2</version>
        </dependency>
        <dependency>
            <groupId>org.tinylog</groupId>
            <artifactId>slf4j-tinylog</artifactId>
            <version>2.6.2</version>
        </dependency>
    </dependencies>    

官网链接: OpenAI | LangChain4j

2、文本转向量

向量是数学中的一个基本概念,表示具有大小(gpt的向量长度是1536位)和方向的量。在深度学习中,向量通常用于表示数据点或特征。

向量数据库是一种专门用于存储和查询向量数据的数据库系统。它能够高效地处理高维向量数据,支持基于向量相似性的搜索。

OpenAiEmbeddingModel embeddingModel = OpenAiEmbeddingModel.builder()
                .baseUrl("http://langchain4j.dev/demo/openai/v1").apiKey("demo").build();

        Response<Embedding> embeddingResponse = embeddingModel.embed("hello word");
        String string = embeddingResponse.content().toString();
        System.out.println(string);
        System.out.println('length:' + embeddingResponse.content().vector().length);

控制台输出:

Embedding { vector = [-0.014903838, 0.0013317588, -0.018488139, -0.031072723, -0.024273094, 0.0075046294, -0.023034403, -0.0010262037, -0.012795426, -0.022441411,
… … …
0.020333, 0.0042761234, -0.023324309, -0.034920577, 0.0142844925, 5.39457E-4, -0.0304402, -0.0035184128, -0.011095519] }
length:1536

3、保存向量

RAG(Retrieval-Augmented Generation,检索增强生成) 技术是一种结合信息检索(Retrieval)和生成(Generation)的混合模型方法,旨在通过检索相关信息来增强生成模型的性能。 RAG技术的核心思想是在生成回复时,不仅依赖模型自身的语言理解能力,还通过检索外部知识库(如产品手册等)来增强回复的准确性和丰富性。

基于客服系统需要涉及企业内部私有的信息,需要事先转为向量存储在向量数据库

向量数据库有很多,在LangChain4j中,EmbeddingStore表示向量数据库,它有20个实现类:

  1. AstraDbEmbeddingStore
  2. AzureAiSearchEmbeddingStore
  3. CassandraEmbeddingStore
  4. ChromaEmbeddingStore
  5. ElasticsearchEmbeddingStore
  6. InMemoryEmbeddingStore
  7. InfinispanEmbeddingStore
  8. MemoryIdEmbeddingStore
  9. MilvusEmbeddingStore
  10. MinimalEmbeddingStore
  11. MongoDbEmbeddingStore
  12. Neo4jEmbeddingStore
  13. OpenSearchEmbeddingStore
  14. PgVectorEmbeddingStore
  15. PineconeEmbeddingStore
  16. QdrantEmbeddingStore
  17. RedisEmbeddingStore
  18. VearchEmbeddingStore
  19. VespaEmbeddingStore
  20. WeaviateEmbeddingStore

比如redis,使用docker安装

docker run --name redis-stack-server -p 6379:6379 redis/redis-stack-server:latest

redis官网链接:https://redis.io/docs/latest/operate/oss_and_stack/install/install-stack/
如果你还不知道docker:docker安装部署
注意,我们平常安装的redis默认是没有向量存储功能的,需要安装插件,redis-stack-server是已经集成好了的。

删除索引和数据命令

redis-cli FT.DROPINDEX embedding-index DD

文档:https://redis.io/docs/latest/commands/ft.dropindex/#examples

maven中引入相关包

<dependency>
	<groupId>dev.langchain4j</groupId>
	<artifactId>langchain4j-redis</artifactId>
	<version>${langchain4j.version}</version>
</dependency>

以下以客服电话举例

OpenAiEmbeddingModel embeddingModel = OpenAiEmbeddingModel.builder()
                .baseUrl("http://langchain4j.dev/demo/openai/v1").apiKey("demo").build();

        RedisEmbeddingStore embeddingStore = RedisEmbeddingStore.builder()
                .host("47.xxx.xxx.16").port(6379).dimension(1536)
                // 不设置默认:embedding-index
//                .indexName("test")
                .build();
		TextSegment textSegment1 = TextSegment.textSegment("客服电话:95152");
        TextSegment textSegment2 = TextSegment.textSegment("客服时间:周一至周日 08:30-22:00");
        TextSegment textSegment3 = TextSegment.textSegment("违法和不良信息举报电话:400-140-2108");

        Response<Embedding> embed1 = embeddingModel.embed(textSegment1);
        Response<Embedding> embed2 = embeddingModel.embed(textSegment2);
        Response<Embedding> embed3 = embeddingModel.embed(textSegment3);

		// 把向量和对应的文本一并存入
        embeddingStore.add(embed1.content(), textSegment1);
        embeddingStore.add(embed2.content(), textSegment2);
        embeddingStore.add(embed3.content(), textSegment3);

查询,看下是否保存成功

命令行查询:redis-cli FT.SEARCH embedding-index "*" LIMIT 0 1

redis-cli FT.SEARCH embedding-index "*" LIMIT 0 1
1) (integer) 3
2) "embedding:c013b1ff-e20a-4124-895b-ebfc1abdca23"
3) 1) "$"
   2) "{\"vector\":[0.0077204346,-0.011896749,-0.0042433655,0.001396096,-0.017382152,0.018825343,-0.00072119647,0.011034666,-0.007969481............"

java查询

OpenAiEmbeddingModel embeddingModel = OpenAiEmbeddingModel.builder()
                .baseUrl("http://langchain4j.dev/demo/openai/v1").apiKey("demo").build();

        RedisEmbeddingStore embeddingStore = RedisEmbeddingStore.builder()
                .host("47.xxx.xxx.16").port(6379).dimension(1536).build();

        Response<Embedding> embed = embeddingModel.embed("客服电话多少");

        List<EmbeddingMatch<TextSegment>> result = embeddingStore.findRelevant(embed.content(), 3);
        for (EmbeddingMatch<TextSegment> embeddingMatch : result){
            System.out.println(embeddingMatch.embedded().text() + ",分数为:" + embeddingMatch.score());
        }

代码指定了查询3条,第一条匹配度最高

控制台输出如下:

客服电话:95152,分数为:0.9283385276795
客服时间:周一至周日 08:30-22:00,分数为:0.924619972706
违法和不良信息举报电话:400-140-2108,分数为:0.912132680416

以上介绍了如何把文字转为向量,并存入向量数据库中。接下来用上面的知识来实现客服系统

4、客服系统实现

4.1 知识库数据处理

假设知识是如下格式(myDocument.txt),一个问题紧接着一个答案,每个问答之间有一个空行

Q:余额提现到账时间是多久?
1-7个工作日内可退回您的支付账户。由于银行处理可能有延迟,具体以账户的到账时间为准。

Q:申请退款后,商家拒绝了怎么办?
申请退款后,如果商家拒绝,此时回到订单页面点击“退款申诉”,美团客服介入处理。

Q:怎么取消退款呢?
请在订单页点击“不退款了”,商家还会正常送餐的。

4.1.1 解析文件为document对象
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.DocumentParser;
import dev.langchain4j.data.document.DocumentSplitter;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.document.parser.TextDocumentParser;
....
Document document;
// 读取文本
Path documentPath = Paths.get("src/main/resources/myDocument.txt");
DocumentParser documentParser = new TextDocumentParser();
document = FileSystemDocumentLoader.loadDocument(documentPath, documentParser);

4.1.2 使用正则\\s*\\R\\s*\\R\\s*切分文件

先实现DocumentSplitter接口

class CustomerServiceDocumentSplitter implements DocumentSplitter {

        @Override
        public List<TextSegment> split(Document document) {
            List<TextSegment> segments = new ArrayList<>();
             String[] parts = split(document.text());
             for (String part : parts){
                 segments.add(TextSegment.from(part));
             }
            return segments;
        }

        public String[] split(String text) {
            return text.split("\\s*\\R\\s*\\R\\s*");
        }
    }

调用方法切分

import dev.langchain4j.data.segment.TextSegment;
....
CustomerServiceDocumentSplitter splitter = new CustomerServiceDocumentSplitter();
List<TextSegment> segments = splitter.split(document);

TextSegment对象有个属性text存储了我们的问答对

4.2 问答对向量存储

把知识库的文本向量化并存储至向量数据库中

// 文本向量化
EmbeddingModel embeddingModel = OpenAiEmbeddingModel.builder()
.apiKey("demo")
.baseUrl("http://langchain4j.dev/demo/openai/v1")
.build();
List<Embedding> embeddings = embeddingModel.embedAll(segments).content();
System.out.println("向量化完毕");

// 存redis
EmbeddingStore<TextSegment> embeddingStore = RedisEmbeddingStore.builder()
.host("47.xxx.xxx.16")
.port(6379)
.dimension(1536)
.build();
embeddingStore.addAll(embeddings, segments);
System.out.println("向量存储完毕");

4.3 从向量库中检索内容

创建了一个ContentRetriever对象,用于从向量库中检索内容

 // 内容查找对象
 ContentRetriever contentRetriever = EmbeddingStoreContentRetriever.builder()
 // 指定模型
 .embeddingModel(embeddingModel)
 // 指定向量库
 .embeddingStore(embeddingStore)
 // 最多返回5条数据
 .maxResults(5)
 // 最低匹配分数为0.8
 .minScore(0.8)
 .build();

Query query = Query.from("余额什么时候到账?");
List<Content> retrieve = contentRetriever.retrieve(query);
// 输出检索到的内容数量:5
System.out.println("size:" + retrieve.size());
// 打印第一条检索结果的文本内容
System.out.println(retrieve.get(0).textSegment().text());

4.4 借助大模型和向量数据库查询答案

根据以上知识,使用AiServices编写代码
AiServices基础参考

    interface CustomerServiceAgent {
        String answer(String question);

        // 利用AiServices创建一个CustomerServiceAgent的代理对象
        static CustomerServiceAgent create() {
            // 构造ChatMemory,用来保存历史聊天记录
            ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);

            EmbeddingModel embeddingModel = OpenAiEmbeddingModel.builder()
                    .apiKey("demo")
                    .baseUrl("http://langchain4j.dev/demo/openai/v1")
                    .build();

            EmbeddingStore<TextSegment> embeddingStore = RedisEmbeddingStore.builder()
                    .host("47.xxx.xxx.16")
                    .port(6379)
                    .dimension(1536)
                    .build();

            ContentRetriever contentRetriever = EmbeddingStoreContentRetriever.builder()
                    .embeddingModel(embeddingModel)
                    .embeddingStore(embeddingStore)
                    .maxResults(3)
                    .minScore(0.8)
                    .build();

            ChatLanguageModel model = OpenAiChatModel.builder()
                    .apiKey("demo")
                    .baseUrl("http://langchain4j.dev/demo/openai/v1")
                    .build();

            return AiServices.builder(CustomerServiceAgent.class)
                    .chatLanguageModel(model)
                    .chatMemory(chatMemory)
                    .contentRetriever(contentRetriever)
                    .build();
        }

    }

写个main函数测试

    public static void main(String[] args) {
 		String answer = CustomerServiceAgent.create().answer("余额什么时候到账?");
        System.out.println("-------answer:" +answer);
    }

控制台输出:

-------answer:余额提现到账时间是1-7个工作日内,具体以账户的到账时间为准。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值