电商领域的RAG智能客服机器人的设计与实现

电商领域的RAG智能客服机器人的设计与实现

系统设计

image-20250426230938963

image-20250426221352294

1
2
3
4
5
6
7
8
sequenceDiagram
用户->>+API网关: 输入问题
API网关->>+路由引擎: 请求分发
路由引擎->>+向量检索: 语义匹配
向量检索-->>-路由引擎: Top5片段
路由引擎->>+LLM引擎: 组合Prompt
LLM引擎-->>-API网关: 生成回答
API网关-->>用户: 返回响应

image-20250425151908640

知识库系统

元数据筛选

1
2
3
4
5
SearchRequest request = SearchRequest.builder()
.query("your query")
.withMetadataFilter("knowledge_lib_id", "your-knowledge-lib-id")
.build();
List<Document> results = pgVectorStore.similaritySearch(request);

数据库设计

智能体表:agent

字段 含义 类型 默认值 是否可空 备注
id ID bigint(20) NO 主键
name 智能体名称 varchar(32)
description 智能体描述 varchar(512)
prologue 开场白 varchar(512)
role_description 系统角色描述 varchar(512)
create_time 创建时间 datetime 当前时间
update_time 更新时间 datetime 当前时间

知识库表:knowledge

字段 含义 类型 默认值 是否可空 备注
id ID bigint(20) NO 主键
name 知识库名称 varchar(32)
description 知识库描述 varchar(512)
create_time 创建时间 datetime 当前时间
update_time 更新时间 datetime 当前时间

智能体_知识库关联表:agent_knowledge

字段 含义 类型 默认值 是否可空 备注
agent_id bigint(20)
knowledge_id bigint(20)
create_time 创建时间 datetime 当前时间

文件表:file

字段 含义 类型 默认值 是否可空 备注
id ID bigint(20) 主键
fileName 名称 varchar(32)
size 大小 BIGINT
status 状态 TINYINT 0 1-可用、0-不可用
type 文件类型 varchar(32) pdf、md、txt、doc(docx)
path 存储路径 varchar(256)
embedding_status 文件嵌入状态 TINYINT 0 1-已嵌入、0-未嵌入
create_time 创建时间 datetime 当前时间
update_time 更新时间 datetime 当前时间

知识库_文件关联表:knowledg_file

字段 含义 类型 默认值 是否可空 约束
knowledge_id bigint(20)
file_id bigint(20)
1

基础知识

向量索引区别

bm_7-1710952188

前置操作

向量库配置

这里以PgSQL为例子

下载pgvectorhttps://blog.csdn.net/typeracer/article/details/140711057

下载好后配置:

1
2
3
4
5
6
7
8
9
10
11
12
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS hstore;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

CREATE TABLE IF NOT EXISTS vector_store (
id uuid DEFAULT uuid_generate_v4() PRIMARY KEY,
content text,
metadata json,
embedding vector(768) # 768 是embedding模型nomic-embed-text的维度
);

CREATE INDEX ON vector_store USING HNSW (embedding vector_cosine_ops);

模型配置

embedding模型:nomic-embed-text

1
ollama pull mofanke/dmeta-embedding-zh

后端配置

xml配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<properties>
<java.version>17</java.version>
<spring-ai.version>1.0.0-M6</spring-ai.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
<!-- Ollama 本地模型的 EmbeddingClient -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pdf-document-reader</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.6.0</version>
</dependency>
</dependencies>

yml配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
spring:
datasource:
url: jdbc:postgresql://localhost:5432/aichat
username: postgres
password: 123456
ai:
vectorstore:
pgvector:
##embedding的向量维度,这里的768是根据nomic-embed-text返回的向量维度配置的
dimensions: 768
openai:
embedding:
enabled: false # 关闭openai的embedding自动配置,防止与Ollama的embedding冲突
api-key: sk-9fba15d576ec47529925b14e526478f0
base-url: https://dashscope.aliyuncs.com/compatible-mode
chat:
options:
model: qwen-plus
temperature: 0.9

ollama:
chat:
enabled: false # 关闭Ollama的chat自动配置,防止与openai的chat冲突
embedding:
model: mofanke/dmeta-embedding-zh


logging:
level: # 配置启动查看对话日志
org.springframework.ai.chat.client.advisor: debug
com.fansea.ai: debug

多轮会话/默认角色配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Configuration
public class CommonConfiguration {

// 配置会话记录advisor
@Bean
public ChatMemory chatMemory(){
// 基于map的内存会话保存
return new InMemoryChatMemory();
}


@Bean
public ChatClient chatClient(OpenAiChatModel model, ChatMemory chatMemory){
return ChatClient
.builder(model)
.defaultAdvisors(
new MessageChatMemoryAdvisor(chatMemory),
new SimpleLoggerAdvisor()
)
.defaultSystem("你是一个可爱的AI助手,名字叫帆帆,你要热心回答用户的问题")
.build();
}
}

接口设计

文件处理

rag/file

1
文件分块 ---> 向量化处理 ---> 存入pgvector

文件分块

采用的是PagePdfDocumentReader(按页读取)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class PagePdfDocumentUtil {
public static List<Document> handle(String path){
// 将文件路径path转化为Resource
Resource resource = new FileSystemResource(path);
PagePdfDocumentReader pdfReader = new PagePdfDocumentReader(resource,
PdfDocumentReaderConfig.builder()
.withPageExtractedTextFormatter(ExtractedTextFormatter.builder()
.withNumberOfBottomTextLinesToDelete(3)
.withNumberOfTopPagesToSkipBeforeDelete(1)
.build())
.withPagesPerDocument(1)
.build());
return pdfReader.get();
}
}

向量化处理 / 存入pgvector

1
2
3
4
5
6
7
public void vectorize(String filePath) {
// TODO: 文件分块 ---> 向量化处理 ---> 存入pgvector
List<Document> documents = PagePdfDocumentUtil.handle(filePath);
var tokenTextSplitter = new TokenTextSplitter();
// tokenTextSplitter 将文本分割成较小块
this.vectorStore.accept(tokenTextSplitter.apply(documents));
}

知识库问答

/ai/knowledge/chat

1
2
3
提取prompt ---> 查询Tool调用 -否-> 向量化处理 ---> 在pgvector匹配相似向量 ---> 重组prompt ---> 提交prompt给模型 ---> 反馈
|
是 ---> 反馈

在pgvector匹配相似向量

1
2
3
public List<Document> search(String keyword) {
return vectorStore.similaritySearch(SearchRequest.builder().query(keyword).topK(1).build());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public Flux<String> knowledgeChat(@RequestBody String prompt, String chatId, String knowledgeId){
repositoryHistory.save("chat",chatId);
List<Document> documents = ragService.search(prompt);
//提取文本内容
String content = documents.stream()
.map(Document::getText)
.collect(Collectors.joining("n"));

return chatClient.prompt()
.user(getChatPrompt2String(prompt, content))
.advisors(a -> a.param(CHAT_MEMORY_CONVERSATION_ID_KEY,chatId))
.stream()
.content();
}

private String getChatPrompt2String(String message, String context) {
String promptText = """
请仅用以下内容回答"%s":
%s
""";
return String.format(promptText, message, context);
}

推荐功能

image-20250428103459907

RAG优化

Prompt工程

1
2
3
4
5
6
7
8
9
10
11
请将文档内容整理为问答形式,不同类型的问题用分隔线分开:
处理后格式:
\## 产品使用问题
\### 如何使用产品X?
[答案内容]

\---

\## 配送问题
\### 发货地在哪里?
[答案内容]

前端设计

提示词

1
2
3
4
5
6
7
8
请根据提供的页面原型的截图,基于Vue2 + ElementPlus帮我制作 “小达智能体” 的页面,首先先完成页面的基本布局 。 具体要求如下:
页面的顶部,包括三个导航栏:智能体、知识库、系统管理。点击导航栏,在导航栏下面要展示出对应的页面,比如点击 "知识库",核心展示区域就要展示知识库的页面.要求PC布局、样式精美
1.智能体
2.知识库
2.2知识库展示所有的知识库列表,每个知识库用圆角长方形展示,展示:知识库名称、描述、文档数、关联智能体数。点击进入知识库设置
2.2.1知识库设置包含左右布局。左侧侧边栏:文档、设置。右侧是选择侧边栏具体的内容,比如点击 "文档",核心展示区域就要展示文档列表的页面
2.1知识库第一个矩形用于创建知识库,点击之后弹出新增窗口,新增窗口包含字段:name、description
3.系统管理
1
2
完成创建知识库页面,要求样式精美
点击之后弹出新增窗口,新增窗口包含字段:name、description
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
基于以下后端接口完成查询知识库列表
- 请求方法: GET
- 请求路径: /knowledge/list/vo
- 请求参数: 无
- 响应示例:
{
"msg": "操作成功",
"code": 200,
"data": [
{
"name": "知识库1",
"description": "测试知识库",
"fileCount": 1,
"agentCount": 1
},
{
"name": "知识库2",
"description": "知识库2",
"fileCount": 0,
"agentCount": 0
}
]
}
- 响应数据结构说明:
* msg: 操作结果消息(string)
* code: 状态码(integer)
* data: 知识库列表(array)
- name: 知识库名称(string)
- description: 知识库描述(string)
- fileCount: 文件数量(integer)
- agentCount: 关联智能体数量(integer)

文件列表:

image-20250504153331591

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
文件列表的字段修改为:文件名称、文件大小、类型、状态、嵌入状态、操作
操作为每一个文档定义了两个按钮:文本嵌入、删除文件
注意:
- 文件大小后端上传的的单位为B需要前端转化为KB
- 状态:0-不可用、1-可用,定义为开关按钮
- 嵌入状态:0-未嵌入、1-失败,2-成功,美化图标
基于以下后端接口完成,基于知识库ID查询文件列表
3.1 文件嵌入
- 请求方法: POST
- 请求路径: /knowledge/file
- 请求参数:
* Query参数: fileId(整数,必填), knowledgeId(整数,可选)
- 响应示例: {"msg":"操作成功","code":200,"data":null}

2.3 删除文件
- 请求方法: DELETE
- 请求路径: /file/delete/{fileId}
- 请求参数: fileId(integer,整数)
- 响应示例: {"msg":"操作成功","code":200,"data":null}

2.2 上传文件到知识库
- 请求方法: POST
- 请求路径: /file/uploadToKnow/{knowledgeId}
- 请求参数:
* Path参数: knowledgeId(整数,必填)
* Body参数: file(二进制文件,必填)
- 响应示例:
* 成功: {"msg":"操作成功","code":200,"data":3}
* 失败: {"msg":"文件已存在!","code":500,"data":null}


3.5 查询知识库文档列表
- 请求方法: GET
- 请求路径: /knowledge/file/list
- 请求参数: knowledgeId(整数)
- 响应示例:
{
"msg":"操作成功",
"code":200,
"data":[
{
"id":3,
"fileName":"产品介绍.md",
"size":2567,
"status":1,
"type":"md",
"embeddingStatus":1,
"createTime":"2025-05-04T02:54:03.930+00:00",
"updateTime":"2025-05-04T02:56:15.045+00:00"
}
]
}
1
修复嵌入状态展示失效的问题、更改状态按钮为可使用
1
2
3
4
5
6
更改状态按钮调用更改状态接口:
- 本地环境基础URL: http://localhost:8080
- 请求方法: GET
- 请求路径: /file/updateStatus/{fileId}
- 请求参数: fileId(字符串,必填)、status(整数)
- 响应示例: {"msg":"操作成功","code":200,"data":null}
1
2
3
4
5
6
7
8
9
10
知识库设置中的保存按钮参照以下接口:
- 本地环境基础URL: http://localhost:8080
- 请求方法: put
- 请求路径: /knowledge/update/{knowledgeId}
- 请求参数:
knowledgeId(整数,必须)
* Body参数:
- name(字符串,必填)
- description(字符串,必填)
- 响应示例: {"msg":"操作成功","code":200,"data":null}
1
2
3
4
5
6
7
8
9
10
11
知识库设置中名称和描述按照以下接口请求补全
- 本地环境基础URL: http://localhost:8080
- 请求方法: get
- 请求路径: /knowledge/{knowledgeId}
- 请求参数:
knowledgeId(整数,必须)
- 响应示例: {"msg":"操作成功","code":200,"data":{
"id": 2,
"name": "maxKB",
"description": "max使用手册"
}}

智能体页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
参照知识库的页面,仿写一个智能体页面,包含新增智能体、智能体列表
智能体列表中单个展示的信息为:名称、描述
点击智能体将会进入智能体的设置
调用后端参考以下接口
- 本地环境基础URL: http://localhost:8080
5.1 新增智能体
- 请求方法: POST
- 请求路径: /agent/add
- 请求参数:
* Body参数:
- name(字符串,必填)
- description(字符串,必填)
- prologue(字符串,必填)
- roleDescription(字符串,必填)
- 响应示例: {"msg":"操作成功","code":200,"data":null}

5.2 获取智能体列表
GET /agent/list
- 请求参数: 无
- 响应示例: {"msg":"操作成功","code":200,"data":{
"id": 1,
"name": "智能客服1号",
"description": "智能客服"
}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
参照以下内容完成:智能体详情页面
当点击智能体时会进入:智能体详情页面
智能体详情页面采用左右布局,左占总屏幕4/10,右占屏幕的6/10
左部分可以设置智能体的:名称、描述、开场白、角色描述、关联的知识库列表(标题侧包含新增知识库关联智能体的按键)(用小卡片展示,小卡片上分别是图标和文件名,不同的类型使用不同的图标,小卡片上包含删除键)

右部分为智能体的聊天窗口:
- 标题为调试预览
- 底部为聊天栏,聊天栏最右边有一个发送按钮
- 第一条消息为智能体设置的开场白
- 智能体回复采用markdown格式,且采用流式输出,(java后端返回类型为:Flux<String>)
注意:开场白的内容为markdown格式,需要将格式中列表解析为可以选择的问题,如果格式错误则不渲染问题,直接展示,点击问题用户可以直接发送
非开场白的回复不需要解析为可选择的问题


调用后端参考以下接口
- 本地环境基础URL: http://localhost:8080


5.2 将知识库关联智能体(新增关联)
- 请求方法: GET
- 请求路径: /agent/agentToKnowledge
- 请求参数: agentId(整数,必填), knowledgeId(整数,必填)
- 响应示例: {"msg":"操作成功","code":200,"data":null}

5.3智能体关联的知识库列表
- 请求方法: GET
- 请求路径: /agent/knowledge/list
- 请求参数: agentId(整数,必填)
- 响应示例: {"msg":"操作成功","code":200,"data":{
"id": 1
"name": "text.txt",
"type": "txt"
}}

5.4根据ID查询智能体
- 请求方法: GET
- 请求路径: /agent/{id}
- 请求参数: id(整数,必填)
- 响应示例: {"msg":"操作成功","code":200,"data":{
"id": 1
"name": "小暖",
"description": "AI聊天机器人",
"prologue": "开场白
- 你有什么想问我的嘛",
"roleDescription": "角色描述"
}}

5.5智能体问答
- 请求方法: post
- 请求路径: /ai/agent/chat
- 请求参数:
- Param:chatId(字符串,可以为uuid,必填)、agentId
- Body:(json)
{ "prompt": "你会做什么" }
- 响应示例: 小暖呀,我是你的专属客服小助手😘 亲亲有什么问题都可以问我哦!

5.6智能体聊天记录
- 请求方法: get
- 请求路径: /ai/history/{chatId}
- 请求参数:
- chatId(字符串,可以为uuid,必填)
- 响应示例:
[
{
"role": "user",
"content": "介绍一下这款耳机"
},
{
"role": "assistant",
"content": "宝子,这款耳机虽然没有明确的主动降噪功能,但通话效果超棒哦~智能识别环境音,让每一次通话都清晰得像面对面聊天呢!而且它用的是蓝牙5.4技术,延迟优化得很低很低,打游戏或者追剧都非常友好哦~还有超方便的触控操作:单击就能播放/暂停或者接听/挂断电话;双击左右耳可以切换上下曲;三击能拒接来电;长按3秒还能唤醒语音助手,是不是特别便捷呀?❤️"
}
]
1
2
3
4
5
6
7
8
完成删除删除知识库与智能体的关联功能
调用后端参考以下接口
- 本地环境基础URL: http://localhost:8080
5.2 删除知识库与智能体的关联
- 请求方法: DELETE
- 请求路径: /agent/delete/knowledge/{knowledgeId}
- 请求参数: agentId(整数,必填), knowledgeId(整数,必填)
- 响应示例: {"msg":"操作成功","code":200,"data":null}
1
2
根据以下需求,在功能上进行更改:
聊天窗口中,智能体会默认发出一条开场白消息,开场白的内容同样为markdown格式,需要将格式中列表解析为可以选择的问题,如果格式错误则不渲染问题,直接展示,点击问题用户可以直接发送,非开场白的回复不需要解析为可选择的问题
1
2
3
4
5
6
7
8
9
10
11
12
请在调试预览的同一行中的左侧增加一个保存agent的按钮,要求样式精美
5.2 更新agent内容
- 请求方法: PUT
- 请求路径: http://localhost:8080/agent/update/{id}
- 请求参数: id(整数,必填)
- body:(Agent对象内容){
name: "小暖",
description: "xx",
prologue: "xx",
roleDescription: "xxx"
}
- 响应示例: {"msg":"操作成功","code":200,"data":null}
1
将按钮名称更改为保存,并放在调试预览的最右侧

删除智能体

1
2
3
4
5
6
参照知识库页面,在智能体页面中对每个智能体卡片上新增一个删除按钮,使用垃圾箱图标,位于卡片右下角
后端调用参照以下接口
- 请求方法: DELETE
- 请求路径: http://localhost:8080/agent/delete/{id}
- 请求参数: id(整数,必填)
- 响应示例: {"msg":"操作成功","code":200,"data":null}
1
距离父容器右侧50px,并且和标题分列两端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<el-container style="height: 500px; border: 1px solid #eee">
<el-aside width="200px" style="background-color: rgb(238, 241, 246)">
<el-menu :default-openeds="['1', '3']">
<el-submenu index="1">
<template slot="title"><i class="el-icon-message"></i>导航一</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="1-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<template slot="title">选项4</template>
<el-menu-item index="1-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="2">
<template slot="title"><i class="el-icon-menu"></i>导航二</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="2-1">选项1</el-menu-item>
<el-menu-item index="2-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="2-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="2-4">
<template slot="title">选项4</template>
<el-menu-item index="2-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="3">
<template slot="title"><i class="el-icon-setting"></i>导航三</template>
<el-menu-item-group>
<template slot="title">分组一</template>
<el-menu-item index="3-1">选项1</el-menu-item>
<el-menu-item index="3-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="3-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="3-4">
<template slot="title">选项4</template>
<el-menu-item index="3-4-1">选项4-1</el-menu-item>
</el-submenu>
</el-submenu>
</el-menu>
</el-aside>

<el-container>
<el-header style="text-align: right; font-size: 12px">
<el-dropdown>
<i class="el-icon-setting" style="margin-right: 15px"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>查看</el-dropdown-item>
<el-dropdown-item>新增</el-dropdown-item>
<el-dropdown-item>删除</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<span>王小虎</span>
</el-header>

<el-main>
<el-table :data="tableData">
<el-table-column prop="date" label="日期" width="140">
</el-table-column>
<el-table-column prop="name" label="姓名" width="120">
</el-table-column>
<el-table-column prop="address" label="地址">
</el-table-column>
</el-table>
</el-main>
</el-container>
</el-container>
参考以上代码的左右布局,左右页面都能撑满屏幕(固定大小),左右页面各自有自己的滚轮
1
需求:智能体的回答的内容需要携带头像,用户的提问无需修改

知识库操作

删除知识库

1
2
3
4
5
6
在知识库页面中对每个知识库卡片上新增一个删除按钮,使用垃圾箱图标,"与关联智能体"处于同一行
后端调用参照以下接口
- 请求方法: DELETE
- 请求路径: http://localhost:8080/knowledge/delete/{knowledgeId}
- 请求参数: knowledgeId(整数,必填)
- 响应示例: {"msg":"操作成功","code":200,"data":null}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120

Spring AI 接口文档 (完整版)

1. 基础信息
- 本地环境基础URL: http://localhost:8080

2. 文件操作接口
2.1 上传文件
- 请求方法: GET
- 请求路径: /file/upload
- 请求参数: file(二进制文件,必填)
- 响应示例: {}

2.2 上传文件到知识库
- 请求方法: POST
- 请求路径: /file/uploadToKnow/{knowledgeId}
- 请求参数:
* Path参数: knowledgeId(整数,必填)
* Body参数: file(二进制文件,必填)
- 响应示例:
* 成功: {"msg":"操作成功","code":200,"data":3}
* 失败: {"msg":"文件已存在!","code":500,"data":null}

2.3 删除文件
- 请求方法: DELETE
- 请求路径: /file/delete/{fileId}
- 请求参数: fileId(字符串,必填)
- 响应示例: {}

3. 知识库操作接口
3.1 文件嵌入
- 请求方法: POST
- 请求路径: /knowledge/file
- 请求参数:
* Query参数: fileId(整数,必填), knowledgeId(整数,可选)
- 响应示例: {"msg":"操作成功","code":200,"data":null}

3.2 新增知识库
- 请求方法: POST
- 请求路径: /knowledge/add
- 请求参数:
* Body参数: name(字符串,必填), description(字符串,必填)
- 响应示例: {"msg":"操作成功","code":200,"data":null}

3.3 查询知识库文件数
- 请求方法: GET
- 请求路径: /knowledge/file/count
- 请求参数: knowledgeId(整数,必填)
- 响应示例: {}

3.4 查询知识库列表
- 请求方法: GET
- 请求路径: /knowledge/list/vo
- 响应示例:
{
"msg":"操作成功",
"code":200,
"data":[
{"name":"知识库1","description":"测试知识库","fileCount":1,"agentCount":1},
{"name":"知识库2","description":"知识库2","fileCount":0,"agentCount":0}
]
}

3.5 查询知识库文档列表
- 请求方法: GET
- 请求路径: /knowledge/file/list
- 请求参数: knowledgeId(字符串,可选)
- 响应示例:
{
"msg":"操作成功",
"code":200,
"data":[
{
"id":3,
"fileName":"产品介绍.md",
"size":2567,
"status":1,
"type":"md",
"embeddingStatus":1,
"createTime":"2025-05-04T02:54:03.930+00:00",
"updateTime":"2025-05-04T02:56:15.045+00:00"
}
]
}

4. 问答操作接口
4.1 知识库问答
- 请求方法: POST
- 请求路径: /ai/knowledge/chat
- 请求参数:
* Query参数: chatId(字符串,可选), knowledgeId(字符串,可选)
* Body参数: prompt(字符串,必填)
- 响应示例: {}

4.2 智能体问答
- 请求方法: POST
- 请求路径: /ai/agent/chat
- 请求参数:
* Query参数: chatId(字符串,可选), agentId(整数,可选)
* Body参数: prompt(字符串,必填)
- 响应示例: "问:你叫什么名字 答:小暖呀,我是你的专属客服小助手😘..."

5. 智能体操作接口
5.1 新增智能体
- 请求方法: POST
- 请求路径: /agent/add
- 请求参数:
* Body参数:
- name(字符串,必填)
- description(字符串,必填)
- prologue(字符串,必填)
- roleDescription(字符串,必填)
- 响应示例: {"msg":"操作成功","code":200,"data":null}

5.2 智能体关联知识库
- 请求方法: GET
- 请求路径: /agent/agentToKnowledge
- 请求参数: agentId(整数,必填), knowledgeId(整数,必填)
- 响应示例: {"msg":"操作成功","code":200,"data":null}
```

工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
请在顶部导航栏中新增一个“工具”的选项,点击工具进入工具页面。
工具页面为左右布局,左边为工具列表,右边为具体工具的使用页面。
工具列表先完成“md文件转化”。

“md文件转化”工具页面顶部定义一个输入框,用于输入md文件的标题
标题输入框下面是一个MD格式的编辑栏,长宽要占右边页面的3/5,用于编辑md内容
其底部包含一个按钮,名称为"转换为md",点击进行转化,响应数据为文件输出的路径

5.2 将文本转化为MD文件
- 请求方法: POST
- 请求路径: http://localhost:8080/tool/convmd
- 请求参数:
* Query参数: fileName(字符串,必填)
* Body参数: mdContent(字符串,必填)
- 响应示例: {"msg":"操作成功","code":200,"data":"D://test.md"}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
## 产品基本信息  
### 这是哪款联想耳机?
这是联想EA230真无线蓝牙耳机,2025年度爆款,具备9大核心技术,号称"百元内全能卷王"。

### 有哪些颜色可选?
提供黑色、白色、肤色三种配色。

---
## 核心功能问题
### 音质表现如何?
- 采用德国纳米碳纤维振膜+DRC调节芯片
- 低音提升58%,整体音质提升2倍
- 联想空间音效(LISA)技术实现立体环绕声
- 高清通话支持环境音智能识别

### 佩戴舒适度怎样?
- 单耳仅3.0g超轻设计
- 45°黄金入耳角度+亲肤耳帽
- 符合人体工学,久戴不累

### 蓝牙连接稳定吗?
搭载蓝牙5.4技术:
- 10米稳定连接
- 游戏/追剧低延迟
- 支持安卓/iOS/Win多设备兼容

---
## 续航充电问题
### 续航时间多久?
- 单次使用:约5小时
- 搭配充电仓:总续航24小时
- 充电时间:1.5小时(Type-C接口)

---
## 操作控制问题
### 怎么控制耳机?
| 操作方式 | 左耳功能 | 右耳功能 |
|----------|----------|----------|
| 单击 | 播放/暂停/接听 | 播放/暂停/接听 |
| 双击 | 上一曲 | 下一曲 |
| 三击 | 拒接来电 | 拒接来电 |
| 长按3秒 | 唤醒语音助手 | 唤醒语音助手 |

---
## 配送售后问题
### 发货时效如何?
- 全国24小时内发货
- 广东18点前下单可次日达
- 支持拼多多极速达服务

### 有哪些售后保障?
- 90天超长质保
- 1年非人为损坏只换不修
- 30天无理由退换(拆封可用)
- 全国联保服务

---
## 品牌保障问题
### 联想产品质量可靠吗?
作为世界500强企业:
- 40年专业沉淀
- 4260亿+年营收
- 3000+自主研发专利
- 全球销售体系

---
## 注意事项
### 购买需注意什么?
- 页面参数可能有误差,以实物为准
- 客服电话:400-966-5611
- 支持先用后付服务

测试

偏远地区为什么不包邮?

表述问题

1
宝子,偏远地区不包邮是因为那边的运输成本比较高呢😉 根据A2的答案,邮费是按照实际情况收取的,而且运费是由物流公司定价的哦。虽然偏远地区不包邮,但满299元部分地区是可以包邮的,你可以看看是否能达到这个金额享受包邮呀~

广东地区能次日达吗?

表述掺杂英语

1
广东地区 Within 24 hours of shipping, orders placed before 18:00 can be delivered the next day.

支持官方验货吗?

知识库未搜到答案将会直接告诉客户

1
亲亲,关于官方验货的问题,这里没有直接的答案呢。不过可以看到支持“先用后付”,可以免费试用后再付款哦,这说明官方对产品质量还是很有信心的~如果担心验货问题,建议联系官方客服电话:400-966-5611咨询具体情况呀🧐

核心问题

2025/4/27:PDF解析能力根据其质量读取表现不一样

效果最好的:

分点明确,无特殊字符导致的读取失败问题

image-20250427104434935

效果一般的:

左右排版读取质量低、无法理解图片、无法处理特殊字符

为左右排版,很多内容集成在一页,读取能力较差,且包含许多模型无法理解的图片

image-20250427104545667

包含许多特殊字符,容易导致解析失败

image-20250427104757658

1
2
org.postgresql.util.PSQLException: 错误: 无效的 "UTF8" 编码字节顺序: 0x00
在位置:unnamed portal parameter $2

2025/4/27:RAG固有缺陷

  • 未检索到有用信息则会导致胡乱做答

a3340986acc4763cf666474335ef1754

基于知识图谱,增强关键信息的关系,提升检索精度

分块优化、优化图片处理

image-20250427145003831

参考文献

其他

1
2
3
4
5
6
7
我的毕业设计课题为:电商领域的RAG智能客服机器人的设计与实现
基础的技术栈包括:SpringBoot3(服务端搭建技术) + Spring AI(AI框架)+ pgvector(向量数据库) + nomic-embed-text(解析词向量的Embedding模型+qwen模型)
目的是搭建一个智能客服机器人,主要功能包括:可以学习文档内容(例如PDF),基于文档内容对用户问题进行解答,主要场景包括:询问产品的使用,作用、参数/商家具体信息:发货地、发货快递、多久发货 2.知识库可以服用

目前基本的服务已经搭建完成,AI能根据知识库内容进行作答,测试效果较好。但目前仍然包含些问题:1.PDF解析方面:左右排版读取质量低、无法理解图片、无法处理特殊字符。2.RAG精度未满足生产环境要求,特殊场景(知识库中未包含需要的内容、知识库质量本身有待优化)下回答效果较差

构建知识图谱、分块优化、增加图片处理
1
2
3
4
5
6
7
8
修改大纲中的"RAG优化实现"、“前后端交互实现”,参考以下内容
RAG优化实现我采用的方法是:
知识库质量方面:
将文件首先按一个设定好的prompt交给大语言模型处理一下转化,问和答的md格式内容,不同类型的提问使用md中的分割线分开,有利于减少拆分时的错误
向量化方面:
对于纯文本内容,采用拆分更小的块,减少RAG查询无关向量

前后端交互采用的是http通信,没有定义网关,前端直接调用后端
1
{"title": "有优惠吗?/满减活动怎么参加?/六一有什么促销?/买耳机能减钱吗?/满200减20怎么凑?", "fileId": 14, "category": "header_3", "knowledgeId": 9}