mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 05:12:39 +00:00
doc and config rerank (#475)
This commit is contained in:
@@ -7,16 +7,16 @@ toc: true
|
|||||||
weight: 836
|
weight: 836
|
||||||
---
|
---
|
||||||
|
|
||||||
# V4.6 版本加入了简单的团队功能,可以邀请其他用户进来管理资源。该版本升级后无法执行旧的升级脚本,且无法回退。
|
**V4.6 版本加入了简单的团队功能,可以邀请其他用户进来管理资源。该版本升级后无法执行旧的升级脚本,且无法回退。**
|
||||||
|
|
||||||
# 1. 更新镜像并变更配置文件
|
## 1. 更新镜像并变更配置文件
|
||||||
|
|
||||||
更新镜像至 latest 或者 v4.6 版本。商业版镜像更新至 V0.2.1
|
更新镜像至 latest 或者 v4.6 版本。商业版镜像更新至 V0.2.1
|
||||||
|
|
||||||
最新配置可参考: [V46版本最新 config.json](/docs/development/configuration),商业镜像配置文件也更新,参考最新的飞书文档。
|
最新配置可参考: [V46版本最新 config.json](/docs/development/configuration),商业镜像配置文件也更新,参考最新的飞书文档。
|
||||||
|
|
||||||
|
|
||||||
# 2. 执行初始化 API
|
## 2. 执行初始化 API
|
||||||
|
|
||||||
发起 2 个 HTTP 请求({{rootkey}} 替换成环境变量里的`rootkey`,{{host}}替换成自己域名)
|
发起 2 个 HTTP 请求({{rootkey}} 替换成环境变量里的`rootkey`,{{host}}替换成自己域名)
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ curl --location --request POST 'https://{{host}}/api/admin/initv46-2' \
|
|||||||
4. 初始化 Mongo Data
|
4. 初始化 Mongo Data
|
||||||
|
|
||||||
|
|
||||||
# V4.6功能介绍
|
## V4.6功能介绍
|
||||||
|
|
||||||
1. 新增 - 团队空间
|
1. 新增 - 团队空间
|
||||||
2. 新增 - 多路向量(多个向量映射一组数据)
|
2. 新增 - 多路向量(多个向量映射一组数据)
|
||||||
|
@@ -9,23 +9,23 @@ weight: 310
|
|||||||
|
|
||||||
在 FastGPT 的 AI 对话模块中,有一个 AI 高级配置,里面包含了 AI 模型的参数配置,本文详细介绍这些配置的含义。
|
在 FastGPT 的 AI 对话模块中,有一个 AI 高级配置,里面包含了 AI 模型的参数配置,本文详细介绍这些配置的含义。
|
||||||
|
|
||||||
# 返回AI内容
|
## 返回AI内容
|
||||||
|
|
||||||
这是一个开关,打开的时候,当 AI 对话模块运行时,会将其输出的内容返回到浏览器(API响应);如果关闭,AI 输出的内容不会返回到浏览器,但是生成的内容仍可以通过【AI回复】进行输出。你可以将【AI回复】连接到其他模块中。
|
这是一个开关,打开的时候,当 AI 对话模块运行时,会将其输出的内容返回到浏览器(API响应);如果关闭,AI 输出的内容不会返回到浏览器,但是生成的内容仍可以通过【AI回复】进行输出。你可以将【AI回复】连接到其他模块中。
|
||||||
|
|
||||||
# 温度
|
## 温度
|
||||||
|
|
||||||
可选范围0-10,约大代表生成的内容约自由扩散,越小代表约严谨。调节能力有限,知识库问答场景通常设置为0。
|
可选范围0-10,约大代表生成的内容约自由扩散,越小代表约严谨。调节能力有限,知识库问答场景通常设置为0。
|
||||||
|
|
||||||
# 回复上限
|
## 回复上限
|
||||||
|
|
||||||
控制 AI 回复的最大 Tokens,较小的值可以一定程度上减少 AI 的废话,但也可能导致 AI 回复不完整。
|
控制 AI 回复的最大 Tokens,较小的值可以一定程度上减少 AI 的废话,但也可能导致 AI 回复不完整。
|
||||||
|
|
||||||
# 引用模板 & 引用提示词
|
## 引用模板 & 引用提示词
|
||||||
|
|
||||||
这两个参数与知识库问答场景相关,可以控制知识库相关的提示词。
|
这两个参数与知识库问答场景相关,可以控制知识库相关的提示词。
|
||||||
|
|
||||||
## AI 对话消息组成
|
### AI 对话消息组成
|
||||||
|
|
||||||
想使用明白这两个变量,首先要了解传递传递给 AI 模型的消息格式。它是一个数组,FastGPT 中这个数组的组成形式为:
|
想使用明白这两个变量,首先要了解传递传递给 AI 模型的消息格式。它是一个数组,FastGPT 中这个数组的组成形式为:
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ weight: 310
|
|||||||
Tips: 可以通过点击上下文按键查看完整的上下文组成,便于调试。
|
Tips: 可以通过点击上下文按键查看完整的上下文组成,便于调试。
|
||||||
{{% /alert %}}
|
{{% /alert %}}
|
||||||
|
|
||||||
## 引用模板和提示词设计
|
### 引用模板和提示词设计
|
||||||
|
|
||||||
引用模板和引用提示词通常是成对出现,引用提示词依赖引用模板。
|
引用模板和引用提示词通常是成对出现,引用提示词依赖引用模板。
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ FastGPT 知识库采用 QA 对(不一定都是问答格式,仅代表两个变
|
|||||||
|
|
||||||
可以通过 [知识库结构讲解](/docs/use-cases/datasetEngine/) 了解详细的知识库的结构。
|
可以通过 [知识库结构讲解](/docs/use-cases/datasetEngine/) 了解详细的知识库的结构。
|
||||||
|
|
||||||
### 引用模板
|
#### 引用模板
|
||||||
|
|
||||||
```
|
```
|
||||||
{instruction:"{{q}}",output:"{{a}}",source:"{{source}}"}
|
{instruction:"{{q}}",output:"{{a}}",source:"{{source}}"}
|
||||||
@@ -64,7 +64,7 @@ FastGPT 知识库采用 QA 对(不一定都是问答格式,仅代表两个变
|
|||||||
{instruction:"电影《铃芽之旅》的编剧是谁?22",output:"新海诚是本片的编剧。",source:"手动输入"}
|
{instruction:"电影《铃芽之旅》的编剧是谁?22",output:"新海诚是本片的编剧。",source:"手动输入"}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 引用提示词
|
#### 引用提示词
|
||||||
|
|
||||||
引用模板需要和引用提示词一起使用,提示词中可以写引用模板的格式说明以及对话的要求等。可以使用 {{quote}} 来使用 **引用模板**,使用 {{question}} 来引入问题。例如:
|
引用模板需要和引用提示词一起使用,提示词中可以写引用模板的格式说明以及对话的要求等。可以使用 {{quote}} 来使用 **引用模板**,使用 {{question}} 来引入问题。例如:
|
||||||
|
|
||||||
@@ -95,15 +95,15 @@ FastGPT 知识库采用 QA 对(不一定都是问答格式,仅代表两个变
|
|||||||
我的问题是:"{{question}}"
|
我的问题是:"{{question}}"
|
||||||
```
|
```
|
||||||
|
|
||||||
### 总结
|
#### 总结
|
||||||
|
|
||||||
引用模板规定了搜索出来的内容如何组成一句话,其由 q,a,index,source 多个变量组成。
|
引用模板规定了搜索出来的内容如何组成一句话,其由 q,a,index,source 多个变量组成。
|
||||||
|
|
||||||
引用提示词由`引用模板`和`提示词`组成,提示词通常是对引用模板的一个描述,加上对模型的要求。
|
引用提示词由`引用模板`和`提示词`组成,提示词通常是对引用模板的一个描述,加上对模型的要求。
|
||||||
|
|
||||||
## 引用模板和提示词设计 示例
|
### 引用模板和提示词设计 示例
|
||||||
|
|
||||||
### 通用模板与问答模板对比
|
#### 通用模板与问答模板对比
|
||||||
|
|
||||||
我们通过一组`你是谁`的手动数据,对通用模板与问答模板的效果进行对比。此处特意打了个搞笑的答案,通用模板下 GPT35 就变得不那么听话了,而问答模板下 GPT35 依然能够回答正确。这是由于结构化的提示词,在大语言模型中具有更强的引导作用。
|
我们通过一组`你是谁`的手动数据,对通用模板与问答模板的效果进行对比。此处特意打了个搞笑的答案,通用模板下 GPT35 就变得不那么听话了,而问答模板下 GPT35 依然能够回答正确。这是由于结构化的提示词,在大语言模型中具有更强的引导作用。
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@ Tips: 建议根据不同的场景,每种知识库仅选择1类数据类型,
|
|||||||
|  |  |
|
|  |  |
|
||||||
|  |  |
|
|  |  |
|
||||||
|
|
||||||
### 严格模板
|
#### 严格模板
|
||||||
|
|
||||||
使用非严格模板,我们随便询问一个不在知识库中的内容,模型通常会根据其自身知识进行回答。
|
使用非严格模板,我们随便询问一个不在知识库中的内容,模型通常会根据其自身知识进行回答。
|
||||||
|
|
||||||
@@ -125,7 +125,7 @@ Tips: 建议根据不同的场景,每种知识库仅选择1类数据类型,
|
|||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
|  |  | |
|
|  |  | |
|
||||||
|
|
||||||
### 提示词设计思路
|
#### 提示词设计思路
|
||||||
|
|
||||||
1. 使用序号进行不同要求描述。
|
1. 使用序号进行不同要求描述。
|
||||||
2. 使用首先、然后、最后等词语进行描述。
|
2. 使用首先、然后、最后等词语进行描述。
|
||||||
|
@@ -7,7 +7,7 @@ toc: true
|
|||||||
weight: 311
|
weight: 311
|
||||||
---
|
---
|
||||||
|
|
||||||
# 理解向量
|
## 理解向量
|
||||||
|
|
||||||
FastGPT 采用了 RAG 中的 Embedding 方案构建知识库,要使用好 FastGPT 需要简单的理解`Embedding`向量是如何工作的及其特点。
|
FastGPT 采用了 RAG 中的 Embedding 方案构建知识库,要使用好 FastGPT 需要简单的理解`Embedding`向量是如何工作的及其特点。
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ FastGPT 采用了 RAG 中的 Embedding 方案构建知识库,要使用好 Fast
|
|||||||
|
|
||||||
检索器的精度比较容易解决,向量模型的训练略复杂,因此数据和检索词质量优化成了一个重要的环节。
|
检索器的精度比较容易解决,向量模型的训练略复杂,因此数据和检索词质量优化成了一个重要的环节。
|
||||||
|
|
||||||
# FastGPT 中向量的结构设计
|
## FastGPT 中向量的结构设计
|
||||||
|
|
||||||
FastGPT 采用了 `PostgresSQL` 的 `PG Vector` 插件作为向量检索器,索引为`HNSW`。且`PostgresSQL`仅用于向量检索,`MongoDB`用于其他数据的存取。
|
FastGPT 采用了 `PostgresSQL` 的 `PG Vector` 插件作为向量检索器,索引为`HNSW`。且`PostgresSQL`仅用于向量检索,`MongoDB`用于其他数据的存取。
|
||||||
|
|
||||||
@@ -29,13 +29,13 @@ FastGPT 采用了 `PostgresSQL` 的 `PG Vector` 插件作为向量检索器,
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 多向量的目的和使用方式
|
### 多向量的目的和使用方式
|
||||||
|
|
||||||
在一组数据中,如果我们希望它尽可能长,但语义又要在向量中尽可能提现,则没有办法通过一组向量来表示。因此,我们采用了多向量映射的方式,将一组数据映射到多组向量中,从而保障数据的完整性和语义的提现。
|
在一组向量中,内容的长度和语义的丰富度通常是矛盾的,无法兼得。因此,FastGPT 采用了多向量映射的方式,将一组数据映射到多组向量中,从而保障数据的完整性和语义的丰富度。
|
||||||
|
|
||||||
你可以为一组较长的文本,添加多组向量,从而在检索时,只要其中一组向量被检索到,该数据也将被召回。
|
你可以为一组较长的文本,添加多组向量,从而在检索时,只要其中一组向量被检索到,该数据也将被召回。
|
||||||
|
|
||||||
## 提高向量搜索精度的方法
|
### 提高向量搜索精度的方法
|
||||||
|
|
||||||
1. 更好分词分段:当一段话的结构和语义是完整的,并且是单一的,精度也会提高。因此,许多系统都会优化分词器,尽可能的保障每组数据的完整性。
|
1. 更好分词分段:当一段话的结构和语义是完整的,并且是单一的,精度也会提高。因此,许多系统都会优化分词器,尽可能的保障每组数据的完整性。
|
||||||
2. 精简`index`的内容,减少向量内容的长度:当`index`的内容更少,更准确时,检索精度自然会提高。但与此同时,会牺牲一定的检索范围,适合答案较为严格的场景。
|
2. 精简`index`的内容,减少向量内容的长度:当`index`的内容更少,更准确时,检索精度自然会提高。但与此同时,会牺牲一定的检索范围,适合答案较为严格的场景。
|
||||||
@@ -43,7 +43,7 @@ FastGPT 采用了 `PostgresSQL` 的 `PG Vector` 插件作为向量检索器,
|
|||||||
4. 优化检索词:在实际使用过程中,用户的问题通常是模糊的或是缺失的,并不一定是完整清晰的问题。因此优化用户的问题(检索词)很大程度上也可以提高精度。
|
4. 优化检索词:在实际使用过程中,用户的问题通常是模糊的或是缺失的,并不一定是完整清晰的问题。因此优化用户的问题(检索词)很大程度上也可以提高精度。
|
||||||
5. 微调向量模型:由于市面上直接使用的向量模型都是通用型模型,在特定领域的检索精度并不高,因此微调向量模型可以很大程度上提高专业领域的检索效果。
|
5. 微调向量模型:由于市面上直接使用的向量模型都是通用型模型,在特定领域的检索精度并不高,因此微调向量模型可以很大程度上提高专业领域的检索效果。
|
||||||
|
|
||||||
# FastGPT 构建知识库方案
|
## FastGPT 构建知识库方案
|
||||||
|
|
||||||
在 FastGPT 中,整个知识库由库、集合和数据 3 部分组成。集合可以简单理解为一个`文件`。一个`库`中可以包含多个`集合`,一个`集合`中可以包含多组`数据`。最小的搜索单位是`库`,也就是说,知识库搜索时,是对整个`库`进行搜索,而集合仅是为了对数据进行分类管理,与搜索效果无关。(起码目前还是)
|
在 FastGPT 中,整个知识库由库、集合和数据 3 部分组成。集合可以简单理解为一个`文件`。一个`库`中可以包含多个`集合`,一个`集合`中可以包含多组`数据`。最小的搜索单位是`库`,也就是说,知识库搜索时,是对整个`库`进行搜索,而集合仅是为了对数据进行分类管理,与搜索效果无关。(起码目前还是)
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ FastGPT 采用了 `PostgresSQL` 的 `PG Vector` 插件作为向量检索器,
|
|||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
|  |  |  |
|
|  |  |  |
|
||||||
|
|
||||||
## 导入数据方案1 - 直接分段导入
|
### 导入数据方案1 - 直接分段导入
|
||||||
|
|
||||||
选择文件导入时,可以选择直接分段方案。直接分段会利用`句子分词器`对文本进行一定长度拆分,最终分割中多组的`q`。如果使用了直接分段方案,我们建议在`应用`设置`引用提示词`时,使用`通用模板`即可,无需选择`问答模板`。
|
选择文件导入时,可以选择直接分段方案。直接分段会利用`句子分词器`对文本进行一定长度拆分,最终分割中多组的`q`。如果使用了直接分段方案,我们建议在`应用`设置`引用提示词`时,使用`通用模板`即可,无需选择`问答模板`。
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ FastGPT 采用了 `PostgresSQL` 的 `PG Vector` 插件作为向量检索器,
|
|||||||
|  |  |
|
|  |  |
|
||||||
|
|
||||||
|
|
||||||
## 导入数据方案2 - QA导入
|
### 导入数据方案2 - QA导入
|
||||||
|
|
||||||
选择文件导入时,可以选择QA拆分方案。仍然需要使用到`句子分词器`对文本进行拆分,但长度比直接分段大很多。在导入后,会先调用`大模型`对分段进行学习,并给出一些`问题`和`答案`,最终问题和答案会一起被存储到`q`中。注意,新版的 FastGPT 为了提高搜索的范围,不再将问题和答案分别存储到 qa 中。
|
选择文件导入时,可以选择QA拆分方案。仍然需要使用到`句子分词器`对文本进行拆分,但长度比直接分段大很多。在导入后,会先调用`大模型`对分段进行学习,并给出一些`问题`和`答案`,最终问题和答案会一起被存储到`q`中。注意,新版的 FastGPT 为了提高搜索的范围,不再将问题和答案分别存储到 qa 中。
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ FastGPT 采用了 `PostgresSQL` 的 `PG Vector` 插件作为向量检索器,
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
|  |  |
|
|  |  |
|
||||||
|
|
||||||
## 导入数据方案3 - 手动录入
|
### 导入数据方案3 - 手动录入
|
||||||
|
|
||||||
在 FastGPT 中,你可以在任何一个`集合`中点击右上角的`插入`手动录入知识点,或者使用`标注`功能手动录入。被搜索的内容为`q`,补充内容(可选)为`a`。
|
在 FastGPT 中,你可以在任何一个`集合`中点击右上角的`插入`手动录入知识点,或者使用`标注`功能手动录入。被搜索的内容为`q`,补充内容(可选)为`a`。
|
||||||
|
|
||||||
@@ -76,16 +76,16 @@ FastGPT 采用了 `PostgresSQL` 的 `PG Vector` 插件作为向量检索器,
|
|||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
|  |  |  |
|
|  |  |  |
|
||||||
|
|
||||||
## 导入数据方案4 - CSV录入
|
### 导入数据方案4 - CSV录入
|
||||||
|
|
||||||
有些数据较为独特,可能需要单独的进行预处理分割后再导入 FastGPT,此时可以选择 csv 导入,可批量的将处理好的数据导入。
|
有些数据较为独特,可能需要单独的进行预处理分割后再导入 FastGPT,此时可以选择 csv 导入,可批量的将处理好的数据导入。
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 导入数据方案5 - API导入
|
### 导入数据方案5 - API导入
|
||||||
|
|
||||||
参考[FastGPT OpenAPI使用](/docs/development/openapi/#知识库添加数据)。
|
参考[FastGPT OpenAPI使用](/docs/development/openapi/#知识库添加数据)。
|
||||||
|
|
||||||
# QA的组合与引用提示词构建
|
## QA的组合与引用提示词构建
|
||||||
|
|
||||||
参考[引用模板与引用提示词示例](/docs/use-cases/ai_settings/#示例)
|
参考[引用模板与引用提示词示例](/docs/use-cases/ai_settings/#示例)
|
||||||
|
1
packages/global/core/module/node/type.d.ts
vendored
1
packages/global/core/module/node/type.d.ts
vendored
@@ -32,6 +32,7 @@ export type FlowNodeInputItemType = {
|
|||||||
connected?: boolean;
|
connected?: boolean;
|
||||||
description?: string;
|
description?: string;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
|
plusField?: boolean;
|
||||||
max?: number;
|
max?: number;
|
||||||
min?: number;
|
min?: number;
|
||||||
step?: number;
|
step?: number;
|
||||||
|
@@ -317,6 +317,9 @@
|
|||||||
},
|
},
|
||||||
"deleteDatasetTips": "Are you sure to delete the knowledge base? Data cannot be recovered after deletion, please confirm!",
|
"deleteDatasetTips": "Are you sure to delete the knowledge base? Data cannot be recovered after deletion, please confirm!",
|
||||||
"deleteFolderTips": "Are you sure to delete this folder and all the knowledge bases it contains? Data cannot be recovered after deletion, please confirm!",
|
"deleteFolderTips": "Are you sure to delete this folder and all the knowledge bases it contains? Data cannot be recovered after deletion, please confirm!",
|
||||||
|
"recall": {
|
||||||
|
"rerank": "Rerank"
|
||||||
|
},
|
||||||
"test": {
|
"test": {
|
||||||
"noResult": "Search results are empty"
|
"noResult": "Search results are empty"
|
||||||
}
|
}
|
||||||
|
@@ -317,6 +317,9 @@
|
|||||||
},
|
},
|
||||||
"deleteDatasetTips": "确认删除该知识库?删除后数据无法恢复,请确认!",
|
"deleteDatasetTips": "确认删除该知识库?删除后数据无法恢复,请确认!",
|
||||||
"deleteFolderTips": "确认删除该文件夹及其包含的所有知识库?删除后数据无法恢复,请确认!",
|
"deleteFolderTips": "确认删除该文件夹及其包含的所有知识库?删除后数据无法恢复,请确认!",
|
||||||
|
"recall": {
|
||||||
|
"rerank": "结果重排"
|
||||||
|
},
|
||||||
"test": {
|
"test": {
|
||||||
"noResult": "搜索结果为空"
|
"noResult": "搜索结果为空"
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,8 @@ import {
|
|||||||
useTheme,
|
useTheme,
|
||||||
Textarea,
|
Textarea,
|
||||||
Grid,
|
Grid,
|
||||||
Divider
|
Divider,
|
||||||
|
Switch
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import Avatar from '@/components/Avatar';
|
import Avatar from '@/components/Avatar';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
@@ -30,6 +31,7 @@ export type KbParamsType = {
|
|||||||
searchSimilarity: number;
|
searchSimilarity: number;
|
||||||
searchLimit: number;
|
searchLimit: number;
|
||||||
searchEmptyText: string;
|
searchEmptyText: string;
|
||||||
|
rerank: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DatasetSelectModal = ({
|
export const DatasetSelectModal = ({
|
||||||
@@ -225,10 +227,11 @@ export const DatasetSelectModal = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const KbParamsModal = ({
|
export const DatasetParamsModal = ({
|
||||||
searchEmptyText,
|
searchEmptyText,
|
||||||
searchLimit,
|
searchLimit,
|
||||||
searchSimilarity,
|
searchSimilarity,
|
||||||
|
rerank,
|
||||||
onClose,
|
onClose,
|
||||||
onChange
|
onChange
|
||||||
}: KbParamsType & { onClose: () => void; onChange: (e: KbParamsType) => void }) => {
|
}: KbParamsType & { onClose: () => void; onChange: (e: KbParamsType) => void }) => {
|
||||||
@@ -237,7 +240,8 @@ export const KbParamsModal = ({
|
|||||||
defaultValues: {
|
defaultValues: {
|
||||||
searchEmptyText,
|
searchEmptyText,
|
||||||
searchLimit,
|
searchLimit,
|
||||||
searchSimilarity
|
searchSimilarity,
|
||||||
|
rerank
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -245,6 +249,24 @@ export const KbParamsModal = ({
|
|||||||
<MyModal isOpen={true} onClose={onClose} title={'搜索参数调整'} minW={['90vw', '600px']}>
|
<MyModal isOpen={true} onClose={onClose} title={'搜索参数调整'} minW={['90vw', '600px']}>
|
||||||
<Flex flexDirection={'column'}>
|
<Flex flexDirection={'column'}>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
|
{feConfigs?.isPlus && (
|
||||||
|
<Box display={['block', 'flex']} py={5} pt={[0, 5]}>
|
||||||
|
<Box flex={'0 0 100px'} mb={[8, 0]}>
|
||||||
|
结果重排
|
||||||
|
<MyTooltip label={'将召回的结果进行进一步重排,可增加召回率'} forceShow>
|
||||||
|
<QuestionOutlineIcon ml={1} />
|
||||||
|
</MyTooltip>
|
||||||
|
</Box>
|
||||||
|
<Switch
|
||||||
|
size={'lg'}
|
||||||
|
isChecked={getValues('rerank')}
|
||||||
|
onChange={(e) => {
|
||||||
|
setValue('rerank', e.target.checked);
|
||||||
|
setRefresh(!refresh);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
<Box display={['block', 'flex']} py={5} pt={[0, 5]}>
|
<Box display={['block', 'flex']} py={5} pt={[0, 5]}>
|
||||||
<Box flex={'0 0 100px'} mb={[8, 0]}>
|
<Box flex={'0 0 100px'} mb={[8, 0]}>
|
||||||
相似度
|
相似度
|
||||||
|
@@ -14,7 +14,8 @@ import {
|
|||||||
useDisclosure,
|
useDisclosure,
|
||||||
Button,
|
Button,
|
||||||
useTheme,
|
useTheme,
|
||||||
Grid
|
Grid,
|
||||||
|
Switch
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/module/node/constant';
|
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/module/node/constant';
|
||||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||||
@@ -35,6 +36,7 @@ import type { SelectedDatasetType } from '@fastgpt/global/core/module/api.d';
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
||||||
import type { EditFieldModeType, EditFieldType } from '../modules/FieldEditModal';
|
import type { EditFieldModeType, EditFieldType } from '../modules/FieldEditModal';
|
||||||
|
import { feConfigs } from '@/web/common/system/staticData';
|
||||||
|
|
||||||
const FieldEditModal = dynamic(() => import('../modules/FieldEditModal'));
|
const FieldEditModal = dynamic(() => import('../modules/FieldEditModal'));
|
||||||
const SelectAppModal = dynamic(() => import('../../SelectAppModal'));
|
const SelectAppModal = dynamic(() => import('../../SelectAppModal'));
|
||||||
@@ -163,7 +165,10 @@ const RenderInput = ({
|
|||||||
editFiledType?: EditFieldModeType;
|
editFiledType?: EditFieldModeType;
|
||||||
}) => {
|
}) => {
|
||||||
const sortInputs = useMemo(
|
const sortInputs = useMemo(
|
||||||
() => flowInputList.sort((a, b) => (a.key === FlowNodeInputTypeEnum.switch ? -1 : 1)),
|
() =>
|
||||||
|
flowInputList
|
||||||
|
.filter((item) => !item.plusField || feConfigs.isPlus)
|
||||||
|
.sort((a, b) => (a.key === FlowNodeInputTypeEnum.switch ? -1 : 1)),
|
||||||
[flowInputList]
|
[flowInputList]
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
@@ -187,6 +192,9 @@ const RenderInput = ({
|
|||||||
{item.type === FlowNodeInputTypeEnum.input && (
|
{item.type === FlowNodeInputTypeEnum.input && (
|
||||||
<TextInputRender item={item} moduleId={moduleId} />
|
<TextInputRender item={item} moduleId={moduleId} />
|
||||||
)}
|
)}
|
||||||
|
{item.type === FlowNodeInputTypeEnum.switch && (
|
||||||
|
<SwitchRender item={item} moduleId={moduleId} />
|
||||||
|
)}
|
||||||
{item.type === FlowNodeInputTypeEnum.textarea && (
|
{item.type === FlowNodeInputTypeEnum.textarea && (
|
||||||
<TextareaRender item={item} moduleId={moduleId} />
|
<TextareaRender item={item} moduleId={moduleId} />
|
||||||
)}
|
)}
|
||||||
@@ -277,6 +285,26 @@ var TextInputRender = React.memo(function TextInputRender({ item, moduleId }: Re
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var SwitchRender = React.memo(function SwitchRender({ item, moduleId }: RenderProps) {
|
||||||
|
return (
|
||||||
|
<Switch
|
||||||
|
size={'lg'}
|
||||||
|
isChecked={item.value}
|
||||||
|
onChange={(e) => {
|
||||||
|
onChangeNode({
|
||||||
|
moduleId,
|
||||||
|
type: 'updateInput',
|
||||||
|
key: item.key,
|
||||||
|
value: {
|
||||||
|
...item,
|
||||||
|
value: e.target.checked
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
var TextareaRender = React.memo(function TextareaRender({ item, moduleId }: RenderProps) {
|
var TextareaRender = React.memo(function TextareaRender({ item, moduleId }: RenderProps) {
|
||||||
return (
|
return (
|
||||||
<Textarea
|
<Textarea
|
||||||
|
@@ -292,6 +292,14 @@ export const DatasetSearchModule: FlowModuleTemplateType = {
|
|||||||
{ label: '20', value: 20 }
|
{ label: '20', value: 20 }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'rerank',
|
||||||
|
type: FlowNodeInputTypeEnum.switch,
|
||||||
|
label: '结果重排',
|
||||||
|
description: '将召回的结果进行进一步重排,可增加召回率',
|
||||||
|
plusField: true,
|
||||||
|
value: false
|
||||||
|
},
|
||||||
Input_Template_UserChatInput
|
Input_Template_UserChatInput
|
||||||
],
|
],
|
||||||
outputs: [
|
outputs: [
|
||||||
|
@@ -22,6 +22,7 @@ export type SearchTestProps = {
|
|||||||
datasetId: string;
|
datasetId: string;
|
||||||
text: string;
|
text: string;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
|
rerank?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ======= collections =========== */
|
/* ======= collections =========== */
|
||||||
|
@@ -16,7 +16,7 @@ import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants';
|
|||||||
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
try {
|
try {
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
const { datasetId, text, limit = 20 } = req.body as SearchTestProps;
|
const { datasetId, text, limit = 20, rerank } = req.body as SearchTestProps;
|
||||||
|
|
||||||
if (!datasetId || !text) {
|
if (!datasetId || !text) {
|
||||||
throw new Error('缺少参数');
|
throw new Error('缺少参数');
|
||||||
@@ -38,7 +38,8 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||||||
text,
|
text,
|
||||||
model: dataset.vectorModel,
|
model: dataset.vectorModel,
|
||||||
limit: Math.min(limit, 50),
|
limit: Math.min(limit, 50),
|
||||||
datasetIds: [datasetId]
|
datasetIds: [datasetId],
|
||||||
|
rerank
|
||||||
});
|
});
|
||||||
|
|
||||||
// push bill
|
// push bill
|
||||||
|
@@ -52,7 +52,7 @@ import MyIcon from '@/components/Icon';
|
|||||||
import ChatBox, { type ComponentRef, type StartChatFnProps } from '@/components/ChatBox';
|
import ChatBox, { type ComponentRef, type StartChatFnProps } from '@/components/ChatBox';
|
||||||
|
|
||||||
import { addVariable } from '@/components/core/module/VariableEditModal';
|
import { addVariable } from '@/components/core/module/VariableEditModal';
|
||||||
import { KbParamsModal } from '@/components/core/module/DatasetSelectModal';
|
import { DatasetParamsModal } from '@/components/core/module/DatasetSelectModal';
|
||||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||||
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
|
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
|
||||||
import { useAppStore } from '@/web/core/app/store/useAppStore';
|
import { useAppStore } from '@/web/core/app/store/useAppStore';
|
||||||
@@ -585,15 +585,15 @@ const Settings = ({ appId }: { appId: string }) => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{isOpenKbParams && (
|
{isOpenKbParams && (
|
||||||
<KbParamsModal
|
<DatasetParamsModal
|
||||||
searchEmptyText={getValues('dataset.searchEmptyText')}
|
{...getValues('dataset')}
|
||||||
searchLimit={getValues('dataset.searchLimit')}
|
|
||||||
searchSimilarity={getValues('dataset.searchSimilarity')}
|
|
||||||
onClose={onCloseKbParams}
|
onClose={onCloseKbParams}
|
||||||
onChange={({ searchEmptyText, searchLimit, searchSimilarity }) => {
|
onChange={(e) => {
|
||||||
setValue('dataset.searchEmptyText', searchEmptyText);
|
setValue('dataset', {
|
||||||
setValue('dataset.searchLimit', searchLimit);
|
...getValues('dataset'),
|
||||||
setValue('dataset.searchSimilarity', searchSimilarity);
|
...e
|
||||||
|
});
|
||||||
|
|
||||||
setRefresh((state) => !state);
|
setRefresh((state) => !state);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect, useMemo, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import { Box, Textarea, Button, Flex, useTheme, Grid, Progress } from '@chakra-ui/react';
|
import { Box, Textarea, Button, Flex, useTheme, Grid, Progress, Switch } from '@chakra-ui/react';
|
||||||
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
|
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
|
||||||
import { useSearchTestStore, SearchTestStoreItemType } from '@/web/core/dataset/store/searchTest';
|
import { useSearchTestStore, SearchTestStoreItemType } from '@/web/core/dataset/store/searchTest';
|
||||||
import { getDatasetDataItemById, postSearchText } from '@/web/core/dataset/api';
|
import { getDatasetDataItemById, postSearchText } from '@/web/core/dataset/api';
|
||||||
@@ -15,6 +15,7 @@ import MyTooltip from '@/components/MyTooltip';
|
|||||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||||
import { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
|
import { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
import { feConfigs } from '@/web/common/system/staticData';
|
||||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
|
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
|
||||||
|
|
||||||
const Test = ({ datasetId }: { datasetId: string }) => {
|
const Test = ({ datasetId }: { datasetId: string }) => {
|
||||||
@@ -28,6 +29,7 @@ const Test = ({ datasetId }: { datasetId: string }) => {
|
|||||||
const [inputText, setInputText] = useState('');
|
const [inputText, setInputText] = useState('');
|
||||||
const [datasetTestItem, setDatasetTestItem] = useState<SearchTestStoreItemType>();
|
const [datasetTestItem, setDatasetTestItem] = useState<SearchTestStoreItemType>();
|
||||||
const [editInputData, setEditInputData] = useState<InputDataType & { collectionId: string }>();
|
const [editInputData, setEditInputData] = useState<InputDataType & { collectionId: string }>();
|
||||||
|
const [rerank, setRerank] = useState(false);
|
||||||
|
|
||||||
const kbTestHistory = useMemo(
|
const kbTestHistory = useMemo(
|
||||||
() => datasetTestList.filter((item) => item.datasetId === datasetId),
|
() => datasetTestList.filter((item) => item.datasetId === datasetId),
|
||||||
@@ -35,7 +37,7 @@ const Test = ({ datasetId }: { datasetId: string }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const { mutate, isLoading } = useRequest({
|
const { mutate, isLoading } = useRequest({
|
||||||
mutationFn: () => postSearchText({ datasetId, text: inputText.trim() }),
|
mutationFn: () => postSearchText({ datasetId, text: inputText.trim(), rerank, limit: 20 }),
|
||||||
onSuccess(res: SearchDataResponseItemType[]) {
|
onSuccess(res: SearchDataResponseItemType[]) {
|
||||||
if (!res || res.length === 0) {
|
if (!res || res.length === 0) {
|
||||||
return toast({
|
return toast({
|
||||||
@@ -91,7 +93,13 @@ const Test = ({ datasetId }: { datasetId: string }) => {
|
|||||||
onChange={(e) => setInputText(e.target.value)}
|
onChange={(e) => setInputText(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<Flex alignItems={'center'} justifyContent={'flex-end'}>
|
<Flex alignItems={'center'} justifyContent={'flex-end'}>
|
||||||
<Box mr={3} color={'myGray.500'}>
|
{feConfigs?.isPlus && (
|
||||||
|
<Flex alignItems={'center'}>
|
||||||
|
{t('dataset.recall.rerank')}
|
||||||
|
<Switch ml={1} isChecked={rerank} onChange={(e) => setRerank(e.target.checked)} />
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
<Box mx={3} color={'myGray.500'}>
|
||||||
{inputText.length}
|
{inputText.length}
|
||||||
</Box>
|
</Box>
|
||||||
<Button isDisabled={inputText === ''} isLoading={isLoading} onClick={mutate}>
|
<Button isDisabled={inputText === ''} isLoading={isLoading} onClick={mutate}>
|
||||||
|
@@ -131,13 +131,15 @@ export async function searchDatasetData({
|
|||||||
model,
|
model,
|
||||||
similarity = 0,
|
similarity = 0,
|
||||||
limit,
|
limit,
|
||||||
datasetIds = []
|
datasetIds = [],
|
||||||
|
rerank = false
|
||||||
}: {
|
}: {
|
||||||
text: string;
|
text: string;
|
||||||
model: string;
|
model: string;
|
||||||
similarity?: number; // min distance
|
similarity?: number; // min distance
|
||||||
limit: number;
|
limit: number;
|
||||||
datasetIds: string[];
|
datasetIds: string[];
|
||||||
|
rerank?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const { vectors, tokenLen } = await getVectorsByText({
|
const { vectors, tokenLen } = await getVectorsByText({
|
||||||
model,
|
model,
|
||||||
@@ -219,6 +221,13 @@ export async function searchDatasetData({
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!rerank) {
|
||||||
|
return {
|
||||||
|
searchRes: filterData.slice(0, limit),
|
||||||
|
tokenLen
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// ReRank result
|
// ReRank result
|
||||||
const reRankResult = await reRankSearchResult({
|
const reRankResult = await reRankSearchResult({
|
||||||
query: text,
|
query: text,
|
||||||
|
@@ -208,7 +208,7 @@ function filterQuote({
|
|||||||
source: item.sourceName,
|
source: item.sourceName,
|
||||||
sourceId: String(item.sourceId || 'UnKnow'),
|
sourceId: String(item.sourceId || 'UnKnow'),
|
||||||
index: index + 1,
|
index: index + 1,
|
||||||
score: item.score.toFixed(4)
|
score: item.score?.toFixed(4)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const sliceResult = sliceMessagesTB({
|
const sliceResult = sliceMessagesTB({
|
||||||
|
@@ -11,6 +11,7 @@ type DatasetSearchProps = ModuleDispatchProps<{
|
|||||||
datasets: SelectedDatasetType;
|
datasets: SelectedDatasetType;
|
||||||
similarity: number;
|
similarity: number;
|
||||||
limit: number;
|
limit: number;
|
||||||
|
rerank: boolean;
|
||||||
userChatInput: string;
|
userChatInput: string;
|
||||||
}>;
|
}>;
|
||||||
export type KBSearchResponse = {
|
export type KBSearchResponse = {
|
||||||
@@ -20,9 +21,9 @@ export type KBSearchResponse = {
|
|||||||
quoteQA: SearchDataResponseItemType[];
|
quoteQA: SearchDataResponseItemType[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function dispatchDatasetSearch(props: Record<string, any>): Promise<KBSearchResponse> {
|
export async function dispatchDatasetSearch(props: DatasetSearchProps): Promise<KBSearchResponse> {
|
||||||
const {
|
const {
|
||||||
inputs: { datasets = [], similarity = 0.4, limit = 5, userChatInput }
|
inputs: { datasets = [], similarity = 0.4, limit = 5, rerank, userChatInput }
|
||||||
} = props as DatasetSearchProps;
|
} = props as DatasetSearchProps;
|
||||||
|
|
||||||
if (datasets.length === 0) {
|
if (datasets.length === 0) {
|
||||||
@@ -41,7 +42,8 @@ export async function dispatchDatasetSearch(props: Record<string, any>): Promise
|
|||||||
model: vectorModel.model,
|
model: vectorModel.model,
|
||||||
similarity,
|
similarity,
|
||||||
limit,
|
limit,
|
||||||
datasetIds: datasets.map((item) => item.datasetId)
|
datasetIds: datasets.map((item) => item.datasetId),
|
||||||
|
rerank
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@@ -95,12 +95,13 @@ export const streamFetch = ({
|
|||||||
});
|
});
|
||||||
read();
|
read();
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
if (err?.message === 'The user aborted a request.') {
|
if (abortSignal.signal.aborted) {
|
||||||
return resolve({
|
return resolve({
|
||||||
responseText,
|
responseText,
|
||||||
responseData
|
responseData
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
reject({
|
reject({
|
||||||
responseText,
|
responseText,
|
||||||
message: getErrText(err, '请求异常')
|
message: getErrText(err, '请求异常')
|
||||||
|
@@ -20,6 +20,7 @@ export type EditFormType = {
|
|||||||
searchSimilarity: number;
|
searchSimilarity: number;
|
||||||
searchLimit: number;
|
searchLimit: number;
|
||||||
searchEmptyText: string;
|
searchEmptyText: string;
|
||||||
|
rerank: boolean;
|
||||||
};
|
};
|
||||||
guide: {
|
guide: {
|
||||||
welcome: {
|
welcome: {
|
||||||
@@ -49,7 +50,8 @@ export const getDefaultAppForm = (): EditFormType => {
|
|||||||
list: [],
|
list: [],
|
||||||
searchSimilarity: 0.4,
|
searchSimilarity: 0.4,
|
||||||
searchLimit: 5,
|
searchLimit: 5,
|
||||||
searchEmptyText: ''
|
searchEmptyText: '',
|
||||||
|
rerank: false
|
||||||
},
|
},
|
||||||
guide: {
|
guide: {
|
||||||
welcome: {
|
welcome: {
|
||||||
@@ -136,6 +138,11 @@ export const appModules2Form = (modules: ModuleItemType[]) => {
|
|||||||
inputs: module.inputs,
|
inputs: module.inputs,
|
||||||
key: 'limit'
|
key: 'limit'
|
||||||
});
|
});
|
||||||
|
updateVal({
|
||||||
|
formKey: 'dataset.rerank',
|
||||||
|
inputs: module.inputs,
|
||||||
|
key: 'rerank'
|
||||||
|
});
|
||||||
// empty text
|
// empty text
|
||||||
const emptyOutputs = module.outputs.find((item) => item.key === 'isEmpty')?.targets || [];
|
const emptyOutputs = module.outputs.find((item) => item.key === 'isEmpty')?.targets || [];
|
||||||
const emptyOutput = emptyOutputs[0];
|
const emptyOutput = emptyOutputs[0];
|
||||||
@@ -475,6 +482,15 @@ const kbTemplate = (formData: EditFormType): ModuleItemType[] => [
|
|||||||
type: FlowNodeInputTypeEnum.target,
|
type: FlowNodeInputTypeEnum.target,
|
||||||
label: '用户问题',
|
label: '用户问题',
|
||||||
connected: true
|
connected: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'rerank',
|
||||||
|
type: FlowNodeInputTypeEnum.switch,
|
||||||
|
label: '结果重排',
|
||||||
|
description: '将召回的结果进行进一步重排,可增加召回率',
|
||||||
|
plusField: true,
|
||||||
|
connected: true,
|
||||||
|
value: formData.dataset.rerank
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
outputs: [
|
outputs: [
|
||||||
|
Reference in New Issue
Block a user