如果你问大模型企业私有化的问题(比如:怎么退款?),大模型很可能就答非所问了,倘若让大模型结合企业内部数据回答, 用户满意度大幅提升 。以下基于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个实现类:
- AstraDbEmbeddingStore
- AzureAiSearchEmbeddingStore
- CassandraEmbeddingStore
- ChromaEmbeddingStore
- ElasticsearchEmbeddingStore
- InMemoryEmbeddingStore
- InfinispanEmbeddingStore
- MemoryIdEmbeddingStore
- MilvusEmbeddingStore
- MinimalEmbeddingStore
- MongoDbEmbeddingStore
- Neo4jEmbeddingStore
- OpenSearchEmbeddingStore
- PgVectorEmbeddingStore
- PineconeEmbeddingStore
- QdrantEmbeddingStore
- RedisEmbeddingStore
- VearchEmbeddingStore
- VespaEmbeddingStore
- 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个工作日内,具体以账户的到账时间为准。