diff --git a/docSite/content/zh-cn/docs/development/upgrading/492.md b/docSite/content/zh-cn/docs/development/upgrading/492.md index 06999a3eb..37950109f 100644 --- a/docSite/content/zh-cn/docs/development/upgrading/492.md +++ b/docSite/content/zh-cn/docs/development/upgrading/492.md @@ -30,10 +30,15 @@ weight: 799 6. 工作流节点数组字符串类型,自动适配 string 输入。 7. 工作流节点数组类型,自动进行 JSON parse 解析 string 输入。 8. AI proxy 日志优化,去除重试失败的日志,仅保留最后一份错误日志。 +9. 分块算法小调整: + * 跨处理符号之间连续性更强。 + * 代码块分割时,用 LLM 模型上下文作为分块大小,尽可能保证代码块完整性。 + * 表格分割时,用 LLM 模型上下文作为分块大小,尽可能保证表格完整性。 ## 🐛 修复 1. 飞书和语雀知识库无法同步。 2. 渠道测试时,如果配置了模型自定义请求地址,会走自定义请求地址,而不是渠道请求地址。 3. 语音识别模型测试未启用的模型时,无法正常测试。 -4. 管理员配置系统插件时,如果插件包含其他系统应用,无法正常鉴权。 \ No newline at end of file +4. 管理员配置系统插件时,如果插件包含其他系统应用,无法正常鉴权。 +5. 移除 TTS 自定义请求地址时,必须需要填 requestAuth 字段。 \ No newline at end of file diff --git a/packages/global/common/string/textSplitter.ts b/packages/global/common/string/textSplitter.ts index c0b335af7..32863dbb6 100644 --- a/packages/global/common/string/textSplitter.ts +++ b/packages/global/common/string/textSplitter.ts @@ -134,8 +134,11 @@ const commonSplit = (props: SplitProps): SplitResponse => { { reg: /^(####\s[^\n]+\n)/gm, maxLen: chunkSize }, { reg: /^(#####\s[^\n]+\n)/gm, maxLen: chunkSize }, - { reg: /([\n]([`~]))/g, maxLen: chunkSize }, // code block - { reg: /([\n](?=\s*[0-9]+\.))/g, maxLen: chunkSize }, // 增大块,尽可能保证它是一个完整的段落。 (?![\*\-|>`0-9]): markdown special char + { reg: /([\n](```[\s\S]*?```|~~~[\s\S]*?~~~))/g, maxLen: maxSize }, // code block + { + reg: /(\n\|(?:(?:[^\n|]+\|){1,})\n\|(?:[:\-\s]+\|){1,}\n(?:\|(?:[^\n|]+\|)*\n)*)/g, + maxLen: maxSize + }, // Table 尽可能保证完整性 { reg: /(\n{2,})/g, maxLen: chunkSize }, { reg: /([\n])/g, maxLen: chunkSize }, // ------ There's no overlap on the top @@ -150,7 +153,7 @@ const commonSplit = (props: SplitProps): SplitResponse => { const checkIsCustomStep = (step: number) => step < customRegLen; const checkIsMarkdownSplit = (step: number) => step >= customRegLen && step <= markdownIndex + customRegLen; - +customReg.length; + const checkForbidOverlap = (step: number) => step <= forbidOverlapIndex + customRegLen; // if use markdown title split, Separate record title @@ -159,7 +162,8 @@ const commonSplit = (props: SplitProps): SplitResponse => { return [ { text, - title: '' + title: '', + chunkMaxSize: chunkSize } ]; } @@ -167,7 +171,7 @@ const commonSplit = (props: SplitProps): SplitResponse => { const isCustomStep = checkIsCustomStep(step); const isMarkdownSplit = checkIsMarkdownSplit(step); - const { reg } = stepReges[step]; + const { reg, maxLen } = stepReges[step]; const replaceText = (() => { if (typeof reg === 'string') { @@ -194,15 +198,19 @@ const commonSplit = (props: SplitProps): SplitResponse => { })() ); })(); + const splitTexts = replaceText.split(splitMarker).filter((part) => part.trim()); return splitTexts .map((text) => { const matchTitle = isMarkdownSplit ? text.match(reg)?.[0] || '' : ''; + // 如果一个分块没有匹配到,则使用默认块大小,否则使用最大块大小 + const chunkMaxSize = text.match(reg) === null ? chunkSize : maxLen; return { text: isMarkdownSplit ? text.replace(matchTitle, '') : text, - title: matchTitle + title: matchTitle, + chunkMaxSize }; }) .filter((item) => !!item.title || !!item.text?.trim()); @@ -252,9 +260,9 @@ const commonSplit = (props: SplitProps): SplitResponse => { const isCustomStep = checkIsCustomStep(step); const forbidConcat = isCustomStep; // forbid=true时候,lastText肯定为空 - // oversize + // Over step if (step >= stepReges.length) { - if (text.length < chunkSize * 3) { + if (text.length < maxSize) { return [text]; } // use slice-chunkSize to split text @@ -268,19 +276,18 @@ const commonSplit = (props: SplitProps): SplitResponse => { // split text by special char const splitTexts = getSplitTexts({ text, step }); - const maxLen = splitTexts.length > 1 ? stepReges[step].maxLen : chunkSize; - const minChunkLen = chunkSize * 0.7; - const chunks: string[] = []; for (let i = 0; i < splitTexts.length; i++) { const item = splitTexts[i]; + const maxLen = item.chunkMaxSize; // 当前块最大长度 + const lastTextLen = lastText.length; const currentText = item.text; const newText = lastText + currentText; const newTextLen = newText.length; - // Markdown 模式下,会强制向下拆分最小块,并再最后一个标题时候,给小块都补充上所有标题(包含父级标题) + // Markdown 模式下,会强制向下拆分最小块,并再最后一个标题深度,给小块都补充上所有标题(包含父级标题) if (isMarkdownStep) { // split new Text, split chunks must will greater 1 (small lastText) const innerChunks = splitTextRecursively({ @@ -290,11 +297,13 @@ const commonSplit = (props: SplitProps): SplitResponse => { parentTitle: parentTitle + item.title }); + // 只有标题,没有内容。 if (innerChunks.length === 0) { chunks.push(`${parentTitle}${item.title}`); continue; } + // 在合并最深级标题时,需要补充标题 chunks.push( ...innerChunks.map( (chunk) => @@ -307,7 +316,16 @@ const commonSplit = (props: SplitProps): SplitResponse => { // newText is too large(now, The lastText must be smaller than chunkSize) if (newTextLen > maxLen) { - // lastText greater minChunkLen, direct push it to chunks, not add to next chunk. (large lastText) + const minChunkLen = maxLen * 0.8; // 当前块最小长度 + const maxChunkLen = maxLen * 1.2; // 当前块最大长度 + + // 新文本没有非常大,直接认为它是一个新的块 + if (newTextLen < maxChunkLen) { + chunks.push(newText); + lastText = getOneTextOverlapText({ text: newText, step }); // next chunk will start with overlayText + continue; + } + // 上一个文本块已经挺大的,单独做一个块 if (lastTextLen > minChunkLen) { chunks.push(lastText); @@ -317,13 +335,13 @@ const commonSplit = (props: SplitProps): SplitResponse => { continue; } - // 说明是新的文本块比较大,需要进一步拆分 + // 说明是当前文本比较大,需要进一步拆分 - // split new Text, split chunks must will greater 1 (small lastText) + // 把新的文本块进行一个拆分,并追加到 latestText 中 const innerChunks = splitTextRecursively({ - text: newText, + text: currentText, step: step + 1, - lastText: '', + lastText, parentTitle: parentTitle + item.title }); const lastChunk = innerChunks[innerChunks.length - 1]; @@ -351,11 +369,11 @@ const commonSplit = (props: SplitProps): SplitResponse => { // Not overlap if (forbidConcat) { - chunks.push(item.text); + chunks.push(currentText); continue; } - lastText += item.text; + lastText = newText; } /* If the last chunk is independent, it needs to be push chunks. */ diff --git a/packages/service/core/ai/audio/speech.ts b/packages/service/core/ai/audio/speech.ts index 0bacaae4e..bb270cee4 100644 --- a/packages/service/core/ai/audio/speech.ts +++ b/packages/service/core/ai/audio/speech.ts @@ -30,11 +30,11 @@ export async function text2Speech({ response_format: 'mp3', speed }, - modelData.requestUrl && modelData.requestAuth + modelData.requestUrl ? { path: modelData.requestUrl, headers: { - Authorization: `Bearer ${modelData.requestAuth}` + ...(modelData.requestAuth ? { Authorization: `Bearer ${modelData.requestAuth}` } : {}) } } : {} diff --git a/packages/service/core/ai/utils.ts b/packages/service/core/ai/utils.ts index 43fdda3bb..6b950b2e3 100644 --- a/packages/service/core/ai/utils.ts +++ b/packages/service/core/ai/utils.ts @@ -65,6 +65,7 @@ export const llmCompletionsBodyFormat = ( const requestBody: T = { ...body, + model: modelData.model, temperature: typeof body.temperature === 'number' ? computedTemperature({ diff --git a/projects/app/src/pageComponents/account/team/OrgManage/index.tsx b/projects/app/src/pageComponents/account/team/OrgManage/index.tsx index 1c7fe5e53..4c831164d 100644 --- a/projects/app/src/pageComponents/account/team/OrgManage/index.tsx +++ b/projects/app/src/pageComponents/account/team/OrgManage/index.tsx @@ -196,7 +196,7 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) { isLoading={isLoadingOrgs} > - + @@ -420,7 +420,7 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) { setManageMemberOrg(currentOrg ?? rootOrg)} + onClick={() => setManageMemberOrg(currentOrg)} /> {currentOrg && currentOrg?.path !== '' && ( <> diff --git a/projects/app/src/pages/api/core/dataset/file/getPreviewChunks.ts b/projects/app/src/pages/api/core/dataset/file/getPreviewChunks.ts index dfa34727f..d27b048f9 100644 --- a/projects/app/src/pages/api/core/dataset/file/getPreviewChunks.ts +++ b/projects/app/src/pages/api/core/dataset/file/getPreviewChunks.ts @@ -94,7 +94,7 @@ async function handler( per: WritePermissionVal }); - if (fileAuthRes && (String(fileAuthRes.tmbId) !== String(tmbId) || !fileAuthRes.isRoot)) { + if (fileAuthRes && String(fileAuthRes.tmbId) !== String(tmbId) && !fileAuthRes.isRoot) { return Promise.reject(CommonErrEnum.unAuthFile); } diff --git a/projects/app/src/service/core/dataset/data/controller.ts b/projects/app/src/service/core/dataset/data/controller.ts index 3867b56e1..f860d4c87 100644 --- a/projects/app/src/service/core/dataset/data/controller.ts +++ b/projects/app/src/service/core/dataset/data/controller.ts @@ -15,18 +15,19 @@ import { MongoDatasetDataText } from '@fastgpt/service/core/dataset/data/dataTex import { DatasetDataIndexTypeEnum } from '@fastgpt/global/core/dataset/data/constants'; import { splitText2Chunks } from '@fastgpt/global/common/string/textSplitter'; import { countPromptTokens } from '@fastgpt/service/common/string/tiktoken'; -import { getLLMMaxChunkSize } from '@fastgpt/global/core/dataset/training/utils'; const formatIndexes = async ({ indexes, q, a = '', - indexSize + indexSize, + maxIndexSize }: { indexes?: (Omit & { dataId?: string })[]; q: string; a?: string; indexSize: number; + maxIndexSize: number; }): Promise< { type: `${DatasetDataIndexTypeEnum}`; @@ -46,9 +47,12 @@ const formatIndexes = async ({ }) => { const qChunks = splitText2Chunks({ text: q, - chunkSize: indexSize + chunkSize: indexSize, + maxSize: maxIndexSize }).chunks; - const aChunks = a ? splitText2Chunks({ text: a, chunkSize: indexSize }).chunks : []; + const aChunks = a + ? splitText2Chunks({ text: a, chunkSize: indexSize, maxSize: maxIndexSize }).chunks + : []; return [ ...qChunks.map((text) => ({ @@ -100,7 +104,11 @@ const formatIndexes = async ({ // If oversize tokens, split it const tokens = await countPromptTokens(item.text); if (tokens > indexSize) { - const splitText = splitText2Chunks({ text: item.text, chunkSize: 512 }).chunks; + const splitText = splitText2Chunks({ + text: item.text, + chunkSize: 512, + maxSize: maxIndexSize + }).chunks; return splitText.map((text) => ({ text, type: item.type @@ -151,7 +159,8 @@ export async function insertData2Dataset({ indexes, q, a, - indexSize + indexSize, + maxIndexSize: embModel.maxToken }); // insert to vector store @@ -236,7 +245,13 @@ export async function updateData2Dataset({ if (!mongoData) return Promise.reject('core.dataset.error.Data not found'); // 2. Compute indexes - const formatIndexesResult = await formatIndexes({ indexes, q, a, indexSize }); + const formatIndexesResult = await formatIndexes({ + indexes, + q, + a, + indexSize, + maxIndexSize: getEmbeddingModel(model).maxToken + }); // 3. Patch indexes, create, update, delete const patchResult: PatchIndexesProps[] = []; diff --git a/test/cases/function/packages/global/common/string/textSplitter.test.ts b/test/cases/function/packages/global/common/string/textSplitter.test.ts index 0407cf4da..3a78be5b0 100644 --- a/test/cases/function/packages/global/common/string/textSplitter.test.ts +++ b/test/cases/function/packages/global/common/string/textSplitter.test.ts @@ -1,8 +1,13 @@ import { it, expect } from 'vitest'; // 必须显式导入 import { splitText2Chunks } from '@fastgpt/global/common/string/textSplitter'; +import * as fs from 'fs'; + +const simpleChunks = (chunks: string[]) => { + return chunks.map((chunk) => chunk.replace(/\s+/g, '')); +}; // 简单的嵌套测试 -it(`Test splitText2Chunks`, () => { +it(`Test splitText2Chunks 1`, () => { const mock = { text: `# A @@ -56,7 +61,7 @@ dsgsgfsgs22sddddddd` const { chunks } = splitText2Chunks({ text: mock.text, chunkSize: 2000 }); expect(chunks).toEqual(mock.result); }); -it(`Test splitText2Chunks`, () => { +it(`Test splitText2Chunks 2`, () => { const mock = { text: `# A @@ -81,7 +86,7 @@ dsgsgfsgs22` }); // 普通文本测试:单段不超过 500 字符 -it(`Test splitText2Chunks`, () => { +it(`Test splitText2Chunks 3`, () => { const mock = { text: `快速了解 FastGPT FastGPT 的能力与优势 @@ -232,13 +237,16 @@ FastGPT 在线使用:https://tryfastgpt.ai FastGPT 能力 1. 专属 AI 客服 通过导入文档或已有问答对进行训练,让 AI 模型能根据你的文档以交互式对话方式回答问题。 + 2. 简单易用的可视化界面 FastGPT 采用直观的可视化界面设计,为各种应用场景提供了丰富实用的功能。通过简洁易懂的操作步骤,可以轻松完成 AI 客服的创建和训练流程。 + 3. 自动数据预处理 -提供手动输入、直接分段、LLM 自动处理和 CSV 等多种数据导入途径,其中“直接分段”支持通过 PDF、WORD、Markdown 和 CSV 文档内容作为上下文。FastGPT 会自动对文本数据进行预处理、向量化和 QA 分割,节省手动训练时间,提升效能。`, - `4. 工作流编排 -基于 Flow 模块的工作流编排,可以帮助你设计更加复杂的问答流程。例如查询数据库、查询库存、预约实验室等。 -5. 强大的 API 集成 +提供手动输入、直接分段、LLM 自动处理和 CSV 等多种数据导入途径,其中“直接分段”支持通过 PDF、WORD、Markdown 和 CSV 文档内容作为上下文。FastGPT 会自动对文本数据进行预处理、向量化和 QA 分割,节省手动训练时间,提升效能。 + +4. 工作流编排 +基于 Flow 模块的工作流编排,可以帮助你设计更加复杂的问答流程。例如查询数据库、查询库存、预约实验室等。`, + `5. 强大的 API 集成 FastGPT 对外的 API 接口对齐了 OpenAI 官方接口,可以直接接入现有的 GPT 应用,也可以轻松集成到企业微信、公众号、飞书等平台。 FastGPT 特点 @@ -262,16 +270,16 @@ FastGPT 遵循附加条件 Apache License 2.0 开源协议,你可以 Fork 之 提供搜索测试、引用修改、完整对话预览等多种调试途径。 -支持多种模型`, - `支持 GPT、Claude、文心一言等多种 LLM 模型,未来也将支持自定义的向量模型。 +支持多种模型 + +支持 GPT、Claude、文心一言等多种 LLM 模型,未来也将支持自定义的向量模型。 知识库核心流程 FastGPT AI 相关参数配置说明 -在 FastGPT 的 AI 对话模块中,有一个 AI 高级配置,里面包含了 AI 模型的参数配置,本文详细介绍这些配置的含义。 - -返回AI内容(高级编排特有) +在 FastGPT 的 AI 对话模块中,有一个 AI 高级配置,里面包含了 AI 模型的参数配置,本文详细介绍这些配置的含义。`, + `返回AI内容(高级编排特有) 这是一个开关,打开的时候,当 AI 对话模块运行时,会将其输出的内容返回到浏览器(API响应);如果关闭,AI 输出的内容不会返回到浏览器,但是生成的内容仍可以通过【AI回复】进行输出。你可以将【AI回复】连接到其他模块中。 最大上下文 @@ -290,23 +298,27 @@ FastGPT AI 相关参数配置说明 被放置在上下文数组的最前面,role 为 system,用于引导模型。 引用模板 & 引用提示词 -这两个参数与知识库问答场景相关,可以控制知识库相关的提示词。`, - `AI 对话消息组成 +这两个参数与知识库问答场景相关,可以控制知识库相关的提示词。 + +AI 对话消息组成 想使用明白这两个变量,首先要了解传递传递给 AI 模型的消息格式。它是一个数组,FastGPT 中这个数组的组成形式为: + [ 内置提示词(config.json 配置,一般为空) 系统提示词 (用户输入的提示词) 历史记录 问题(由引用提示词、引用模板和用户问题组成) ] -🍅 -Tips: 可以通过点击上下文按键查看完整的上下文组成,便于调试。 +🍅`, + `Tips: 可以通过点击上下文按键查看完整的上下文组成,便于调试。 + 引用模板和提示词设计 简易模式已移除该功能,仅在工作流中可配置,可点击工作流中AI对话节点内,知识库引用旁边的setting icon进行配置。随着模型的增强,这部分功能将逐步弱化。 + 引用模板和引用提示词通常是成对出现,引用提示词依赖引用模板。 -FastGPT 知识库采用 QA 对(不一定都是问答格式,仅代表两个变量)的格式存储,在转义成字符串时候会根据引用模板来进行格式化。`, - `引用模板和引用提示词通常是成对出现,引用提示词依赖引用模板。 + FastGPT 知识库采用 QA 对(不一定都是问答格式,仅代表两个变量)的格式存储,在转义成字符串时候会根据引用模板来进行格式化。知识库包含多个可用变量: q, a, sourceId(数据的ID), index(第n个数据), source(数据的集合名、文件名),score(距离得分,0-1) 可以通过 {{q}} {{a}} {{sourceId}} {{index}} {{source}} {{score}} 按需引入。下面一个模板例子: + 可以通过 知识库结构讲解 了解详细的知识库的结构。 引用模板 @@ -324,14 +336,13 @@ FastGPT 知识库采用 QA 对(不一定都是问答格式,仅代表两个变 """ {{quote}} """ -对话要求:`, - `1. 背景知识是最新的,其中 instruction 是相关介绍,output 是预期回答或补充。 +对话要求: +1. 背景知识是最新的,其中 instruction 是相关介绍,output 是预期回答或补充。 2. 使用背景知识回答问题。 3. 背景知识无法回答问题时,你可以礼貌的的回答用户问题。 我的问题是:"{{question}}" -转义后则为: - -你的背景知识: +转义后则为:`, + `你的背景知识: """ {instruction:"电影《铃芽之旅》的导演是谁?",output:"电影《铃芽之旅》的导演是新海诚。",source:"手动输入"} {instruction:"本作的主人公是谁?",output:"本作的主人公是名叫铃芽的少女。",source:""} @@ -339,8 +350,8 @@ FastGPT 知识库采用 QA 对(不一定都是问答格式,仅代表两个变 """ 对话要求: 1. 背景知识是最新的,其中 instruction 是相关介绍,output 是预期回答或补充。 -2. 使用背景知识回答问题。`, - `3. 背景知识无法回答问题时,你可以礼貌的的回答用户问题。 +2. 使用背景知识回答问题。 +3. 背景知识无法回答问题时,你可以礼貌的的回答用户问题。 我的问题是:"{{question}}" 总结 引用模板规定了搜索出来的内容如何组成一句话,其由 q,a,index,source 多个变量组成。 @@ -355,23 +366,22 @@ FastGPT 知识库采用 QA 对(不一定都是问答格式,仅代表两个变 const { chunks } = splitText2Chunks({ text: mock.text, chunkSize: 500 }); - const normalizedChunks = chunks.map((chunk) => chunk.replace(/\n/g, '')); - const normalizedExpected = mock.result.map((result) => result.replace(/\n/g, '')); + const normalizedChunks = simpleChunks(chunks); + const normalizedExpected = simpleChunks(mock.result); expect(normalizedChunks).toEqual(normalizedExpected); }); // 普通文本测试:单段超过 500 字符,有 20% 重叠 -it(`Test splitText2Chunks`, () => { +it(`Test splitText2Chunks 4`, () => { const mock = { text: `FastGPT是一款基于大语言模型(LLM)的智能问答系统,专为提供高效、准确的知识库问答服务而设计。它支持多种数据导入方式,包括手动输入、PDF、WORD、Markdown和CSV等格式,能够自动进行数据预处理、向量化和QA分割,大幅提升数据处理效率。FastGPT的可视化界面设计简洁直观,用户可以通过Flow模块进行工作流编排,轻松实现复杂的问答场景。此外,FastGPT支持多种LLM模型,如GPT、Claude、文心一言等,未来还将支持自定义的向量模型。其强大的API集成能力使得用户可以轻松将其接入现有的GPT应用或其他平台,如企业微信、公众号、飞书等。FastGPT的开源项目遵循Apache License 2.0协议,用户可以进行二次开发和发布。其独特的QA结构设计提高了在大量数据场景中的问答准确性,而可视化工作流则展示了从问题输入到模型输出的完整流程,便于调试和设计复杂流程。FastGPT还提供了多种调试途径,如搜索测试、引用修改和完整对话预览等,方便用户进行调试。其知识库核心流程包括AI相关参数配置说明,如返回AI内容、最大上下文、函数调用、温度、回复上限、系统提示词、引用模板和引用提示词等。通过这些配置,用户可以更好地控制和优化AI模型的表现。FastGPT的引用模板和提示词设计使得用户可以根据具体需求进行灵活配置,从而提高问答的准确性和效率。总之,FastGPT是一款功能强大、易于使用的智能问答系统,适用于各种应用场景,帮助用户实现高效、准确的知识库问答服务。FastGPT的设计理念是通过先进的技术手段简化用户的操作流程,使得即便是没有技术背景的用户也能轻松上手。其多样化的数据导入方式确保了用户可以根据自身需求选择最合适的方式进行数据输入,而自动化的数据预处理功能则大大减少了用户的工作量。通过对数据的向量化处理,FastGPT能够更好地理解和分析用户的问题,从而提供更为精准的回答。其支持的多种LLM模型使得用户可以根据不同的应用场景选择最合适的模型进行问答,而未来的自定义向量模型支持则为用户提供了更多的可能性。FastGPT的API集成能力不仅体现在与现有GPT应用的无缝对接上,还可以轻松集成到企业微信、公众号、飞书等平台,使得用户可以在不同的平台上享受到FastGPT带来的便利。其开源项目的开放性使得用户可以根据自身需求进行二次开发和发布,从而实现个性化的问答服务。FastGPT的QA结构设计独具匠心,通过对问答流程的优化,提高了在大量数据场景中的问答准确性。可视化工作流的引入则使得用户可以直观地看到从问题输入到模型输出的完整流程,便于调试和设计复杂流程。FastGPT提供的多种调试途径,如搜索测试、引用修改和完整对话预览等,使得用户可以在使用过程中随时进行调试,确保问答的准确性和效率。其知识库核心流程中的AI相关参数配置说明详细介绍了如何通过配置来优化AI模型的表现,使得用户可以根据具体需求进行灵活配置。FastGPT的引用模板和提示词设计则为用户提供了更多的定制化选项,从而提高问答的准确性和效率。总之,FastGPT是一款功能强大、易于使用的智能问答系统,适用于各种应用场景,帮助用户实现高效、准确的知识库问答服务。FastGPT的设计理念是通过先进的技术手段简化用户的操作流程,使得即便是没有技术背景的用户也能轻松上手。其多样化的数据导入方式确保了用户可以根据自身需求选择最合适的方式进行数据输入,而自动化的数据预处理功能则大大减少了用户的工作量。通过对数据的向量化处理,FastGPT能够更好地理解和分析用户的问题,从而提供更为精准的回答。其支持的多种LLM模型使得用户可以根据不同的应用场景选择最合适的模型进行问答,而未来的自定义向量模型支持则为用户提供了更多的可能性。FastGPT的API集成能力不仅体现在与现有GPT应用的无缝对接上,还可以轻松集成到企业微信、公众号、飞书等平台,使得用户可以在不同的平台上享受到FastGPT带来的便利。其开源项目的开放性使得用户可以根据自身需求进行二次开发和发布,从而实现个性化的问答服务。FastGPT的QA结构设计独具匠心,通过对问答流程的优化,提高了在大量数据场景中的问答准确性。可视化工作流的引入则使得用户可以直观地看到从问题输入到模型输出的完整流程,便于调试和设计复杂流程。FastGPT提供的多种调试途径,如搜索测试、引用修改和完整对话预览等,使得用户可以在使用过程中随时进行调试,确保问答的准确性和效率。其知识库核心流程中的AI相关参数配置说明详细介绍了如何通过配置来优化AI模型的表现,使得用户可以根据具体需求进行灵活配置。FastGPT的引用模板和提示词设计则为用户提供了更多的定制化选项,从而提高问答的准确性和效率。总之,FastGPT是一款功能强大、易于使用的智能问答系统,适用于各种应用场景,帮助用户实现高效、准确的知识库问答服务。`, result: [ - `FastGPT是一款基于大语言模型(LLM)的智能问答系统,专为提供高效、准确的知识库问答服务而设计。它支持多种数据导入方式,包括手动输入、PDF、WORD、Markdown和CSV等格式,能够自动进行数据预处理、向量化和QA分割,大幅提升数据处理效率。FastGPT的可视化界面设计简洁直观,用户可以通过Flow模块进行工作流编排,轻松实现复杂的问答场景。此外,FastGPT支持多种LLM模型,如GPT、Claude、文心一言等,未来还将支持自定义的向量模型。其强大的API集成能力使得用户可以轻松将其接入现有的GPT应用或其他平台,如企业微信、公众号、飞书等。FastGPT的开源项目遵循Apache License 2.0协议,用户可以进行二次开发和发布。其独特的QA结构设计提高了在大量数据场景中的问答准确性,而可视化工作流则展示了从问题输入到模型输出的完整流程,便于调试和设计复杂流程。FastGPT还提供了多种调试途径,如搜索测试、引用修改和完整对话预览等,方便用户进行调试。`, - `其独特的QA结构设计提高了在大量数据场景中的问答准确性,而可视化工作流则展示了从问题输入到模型输出的完整流程,便于调试和设计复杂流程。FastGPT还提供了多种调试途径,如搜索测试、引用修改和完整对话预览等,方便用户进行调试。其知识库核心流程包括AI相关参数配置说明,如返回AI内容、最大上下文、函数调用、温度、回复上限、系统提示词、引用模板和引用提示词等。通过这些配置,用户可以更好地控制和优化AI模型的表现。FastGPT的引用模板和提示词设计使得用户可以根据具体需求进行灵活配置,从而提高问答的准确性和效率。总之,FastGPT是一款功能强大、易于使用的智能问答系统,适用于各种应用场景,帮助用户实现高效、准确的知识库问答服务。FastGPT的设计理念是通过先进的技术手段简化用户的操作流程,使得即便是没有技术背景的用户也能轻松上手。其多样化的数据导入方式确保了用户可以根据自身需求选择最合适的方式进行数据输入,而自动化的数据预处理功能则大大减少了用户的工作量。通过对数据的向量化处理,FastGPT能够更好地理解和分析用户的问题,从而提供更为精准的回答。`, - `其多样化的数据导入方式确保了用户可以根据自身需求选择最合适的方式进行数据输入,而自动化的数据预处理功能则大大减少了用户的工作量。通过对数据的向量化处理,FastGPT能够更好地理解和分析用户的问题,从而提供更为精准的回答。其支持的多种LLM模型使得用户可以根据不同的应用场景选择最合适的模型进行问答,而未来的自定义向量模型支持则为用户提供了更多的可能性。FastGPT的API集成能力不仅体现在与现有GPT应用的无缝对接上,还可以轻松集成到企业微信、公众号、飞书等平台,使得用户可以在不同的平台上享受到FastGPT带来的便利。其开源项目的开放性使得用户可以根据自身需求进行二次开发和发布,从而实现个性化的问答服务。FastGPT的QA结构设计独具匠心,通过对问答流程的优化,提高了在大量数据场景中的问答准确性。可视化工作流的引入则使得用户可以直观地看到从问题输入到模型输出的完整流程,便于调试和设计复杂流程。FastGPT提供的多种调试途径,如搜索测试、引用修改和完整对话预览等,使得用户可以在使用过程中随时进行调试,确保问答的准确性和效率。`, - `可视化工作流的引入则使得用户可以直观地看到从问题输入到模型输出的完整流程,便于调试和设计复杂流程。FastGPT提供的多种调试途径,如搜索测试、引用修改和完整对话预览等,使得用户可以在使用过程中随时进行调试,确保问答的准确性和效率。其知识库核心流程中的AI相关参数配置说明详细介绍了如何通过配置来优化AI模型的表现,使得用户可以根据具体需求进行灵活配置。FastGPT的引用模板和提示词设计则为用户提供了更多的定制化选项,从而提高问答的准确性和效率。总之,FastGPT是一款功能强大、易于使用的智能问答系统,适用于各种应用场景,帮助用户实现高效、准确的知识库问答服务。FastGPT的设计理念是通过先进的技术手段简化用户的操作流程,使得即便是没有技术背景的用户也能轻松上手。其多样化的数据导入方式确保了用户可以根据自身需求选择最合适的方式进行数据输入,而自动化的数据预处理功能则大大减少了用户的工作量。通过对数据的向量化处理,FastGPT能够更好地理解和分析用户的问题,从而提供更为精准的回答。`, - `其多样化的数据导入方式确保了用户可以根据自身需求选择最合适的方式进行数据输入,而自动化的数据预处理功能则大大减少了用户的工作量。通过对数据的向量化处理,FastGPT能够更好地理解和分析用户的问题,从而提供更为精准的回答。其支持的多种LLM模型使得用户可以根据不同的应用场景选择最合适的模型进行问答,而未来的自定义向量模型支持则为用户提供了更多的可能性。FastGPT的API集成能力不仅体现在与现有GPT应用的无缝对接上,还可以轻松集成到企业微信、公众号、飞书等平台,使得用户可以在不同的平台上享受到FastGPT带来的便利。其开源项目的开放性使得用户可以根据自身需求进行二次开发和发布,从而实现个性化的问答服务。FastGPT的QA结构设计独具匠心,通过对问答流程的优化,提高了在大量数据场景中的问答准确性。可视化工作流的引入则使得用户可以直观地看到从问题输入到模型输出的完整流程,便于调试和设计复杂流程。FastGPT提供的多种调试途径,如搜索测试、引用修改和完整对话预览等,使得用户可以在使用过程中随时进行调试,确保问答的准确性和效率。`, - `可视化工作流的引入则使得用户可以直观地看到从问题输入到模型输出的完整流程,便于调试和设计复杂流程。FastGPT提供的多种调试途径,如搜索测试、引用修改和完整对话预览等,使得用户可以在使用过程中随时进行调试,确保问答的准确性和效率。其知识库核心流程中的AI相关参数配置说明详细介绍了如何通过配置来优化AI模型的表现,使得用户可以根据具体需求进行灵活配置。FastGPT的引用模板和提示词设计则为用户提供了更多的定制化选项,从而提高问答的准确性和效率。总之,FastGPT是一款功能强大、易于使用的智能问答系统,适用于各种应用场景,帮助用户实现高效、准确的知识库问答服务。` + `FastGPT是一款基于大语言模型(LLM)的智能问答系统,专为提供高效、准确的知识库问答服务而设计。它支持多种数据导入方式,包括手动输入、PDF、WORD、Markdown和CSV等格式,能够自动进行数据预处理、向量化和QA分割,大幅提升数据处理效率。FastGPT的可视化界面设计简洁直观,用户可以通过Flow模块进行工作流编排,轻松实现复杂的问答场景。此外,FastGPT支持多种LLM模型,如GPT、Claude、文心一言等,未来还将支持自定义的向量模型。其强大的API集成能力使得用户可以轻松将其接入现有的GPT应用或其他平台,如企业微信、公众号、飞书等。FastGPT的开源项目遵循Apache License 2.0协议,用户可以进行二次开发和发布。其独特的QA结构设计提高了在大量数据场景中的问答准确性,而可视化工作流则展示了从问题输入到模型输出的完整流程,便于调试和设计复杂流程。FastGPT还提供了多种调试途径,如搜索测试、引用修改和完整对话预览等,方便用户进行调试。其知识库核心流程包括AI相关参数配置说明,如返回AI内容、最大上下文、函数调用、温度、回复上限、系统提示词、引用模板和引用提示词等。`, + `FastGPT还提供了多种调试途径,如搜索测试、引用修改和完整对话预览等,方便用户进行调试。其知识库核心流程包括AI相关参数配置说明,如返回AI内容、最大上下文、函数调用、温度、回复上限、系统提示词、引用模板和引用提示词等。通过这些配置,用户可以更好地控制和优化AI模型的表现。FastGPT的引用模板和提示词设计使得用户可以根据具体需求进行灵活配置,从而提高问答的准确性和效率。总之,FastGPT是一款功能强大、易于使用的智能问答系统,适用于各种应用场景,帮助用户实现高效、准确的知识库问答服务。FastGPT的设计理念是通过先进的技术手段简化用户的操作流程,使得即便是没有技术背景的用户也能轻松上手。其多样化的数据导入方式确保了用户可以根据自身需求选择最合适的方式进行数据输入,而自动化的数据预处理功能则大大减少了用户的工作量。通过对数据的向量化处理,FastGPT能够更好地理解和分析用户的问题,从而提供更为精准的回答。其支持的多种LLM模型使得用户可以根据不同的应用场景选择最合适的模型进行问答,而未来的自定义向量模型支持则为用户提供了更多的可能性。FastGPT的API集成能力不仅体现在与现有GPT应用的无缝对接上,还可以轻松集成到企业微信、公众号、飞书等平台,使得用户可以在不同的平台上享受到FastGPT带来的便利。`, + `其支持的多种LLM模型使得用户可以根据不同的应用场景选择最合适的模型进行问答,而未来的自定义向量模型支持则为用户提供了更多的可能性。FastGPT的API集成能力不仅体现在与现有GPT应用的无缝对接上,还可以轻松集成到企业微信、公众号、飞书等平台,使得用户可以在不同的平台上享受到FastGPT带来的便利。其开源项目的开放性使得用户可以根据自身需求进行二次开发和发布,从而实现个性化的问答服务。FastGPT的QA结构设计独具匠心,通过对问答流程的优化,提高了在大量数据场景中的问答准确性。可视化工作流的引入则使得用户可以直观地看到从问题输入到模型输出的完整流程,便于调试和设计复杂流程。FastGPT提供的多种调试途径,如搜索测试、引用修改和完整对话预览等,使得用户可以在使用过程中随时进行调试,确保问答的准确性和效率。其知识库核心流程中的AI相关参数配置说明详细介绍了如何通过配置来优化AI模型的表现,使得用户可以根据具体需求进行灵活配置。FastGPT的引用模板和提示词设计则为用户提供了更多的定制化选项,从而提高问答的准确性和效率。总之,FastGPT是一款功能强大、易于使用的智能问答系统,适用于各种应用场景,帮助用户实现高效、准确的知识库问答服务。`, + `FastGPT的引用模板和提示词设计则为用户提供了更多的定制化选项,从而提高问答的准确性和效率。总之,FastGPT是一款功能强大、易于使用的智能问答系统,适用于各种应用场景,帮助用户实现高效、准确的知识库问答服务。FastGPT的设计理念是通过先进的技术手段简化用户的操作流程,使得即便是没有技术背景的用户也能轻松上手。其多样化的数据导入方式确保了用户可以根据自身需求选择最合适的方式进行数据输入,而自动化的数据预处理功能则大大减少了用户的工作量。通过对数据的向量化处理,FastGPT能够更好地理解和分析用户的问题,从而提供更为精准的回答。其支持的多种LLM模型使得用户可以根据不同的应用场景选择最合适的模型进行问答,而未来的自定义向量模型支持则为用户提供了更多的可能性。FastGPT的API集成能力不仅体现在与现有GPT应用的无缝对接上,还可以轻松集成到企业微信、公众号、飞书等平台,使得用户可以在不同的平台上享受到FastGPT带来的便利。其开源项目的开放性使得用户可以根据自身需求进行二次开发和发布,从而实现个性化的问答服务。FastGPT的QA结构设计独具匠心,通过对问答流程的优化,提高了在大量数据场景中的问答准确性。`, + `FastGPT的API集成能力不仅体现在与现有GPT应用的无缝对接上,还可以轻松集成到企业微信、公众号、飞书等平台,使得用户可以在不同的平台上享受到FastGPT带来的便利。其开源项目的开放性使得用户可以根据自身需求进行二次开发和发布,从而实现个性化的问答服务。FastGPT的QA结构设计独具匠心,通过对问答流程的优化,提高了在大量数据场景中的问答准确性。可视化工作流的引入则使得用户可以直观地看到从问题输入到模型输出的完整流程,便于调试和设计复杂流程。FastGPT提供的多种调试途径,如搜索测试、引用修改和完整对话预览等,使得用户可以在使用过程中随时进行调试,确保问答的准确性和效率。其知识库核心流程中的AI相关参数配置说明详细介绍了如何通过配置来优化AI模型的表现,使得用户可以根据具体需求进行灵活配置。FastGPT的引用模板和提示词设计则为用户提供了更多的定制化选项,从而提高问答的准确性和效率。总之,FastGPT是一款功能强大、易于使用的智能问答系统,适用于各种应用场景,帮助用户实现高效、准确的知识库问答服务。` ] }; @@ -381,7 +391,7 @@ it(`Test splitText2Chunks`, () => { }); // 自定义分隔符测试:分割后,内容少 -it(`Test splitText2Chunks`, () => { +it(`Test splitText2Chunks 5`, () => { const mock = { text: `这是测试文本 1 ---- @@ -407,7 +417,7 @@ it(`Test splitText2Chunks`, () => { }); // 自定义分隔符测试:分割后,内容超长,二次分割,有重叠。 -it(`Test splitText2Chunks`, () => { +it(`Test splitText2Chunks 6`, () => { const mock = { text: `这是测试文本 1,短的 ---- @@ -416,8 +426,8 @@ FastGPT是一款基于大语言模型(LLM)的智能问答系统,专为提 result: [ `这是测试文本 1,短的`, `这是测试文本 2,长的。 -FastGPT是一款基于大语言模型(LLM)的智能问答系统,专为提供高效、准确的知识库问答服务而设计。它支持多种数据导入方式,包括手动输入、PDF、WORD、Markdown和CSV等格式,能够自动进行数据预处理、向量化和QA分割,大幅提升数据处理效率。FastGPT的可视化界面设计简洁直观,用户可以通过Flow模块进行工作流编排,轻松实现复杂的问答场景。此外,FastGPT支持多种LLM模型,如GPT、Claude、文心一言等,未来还将支持自定义的向量模型。其强大的API集成能力使得用户可以轻松将其接入现有的GPT应用或其他平台,如企业微信、公众号、飞书等。FastGPT的开源项目遵循Apache License 2.0协议,用户可以进行二次开发和发布。其独特的QA结构设计提高了在大量数据场景中的问答准确性,而可视化工作流则展示了从问题输入到模型输出的完整流程,便于调试和设计复杂流程。FastGPT还提供了多种调试途径,如搜索测试、引用修改和完整对话预览等,方便用户进行调试。`, - `其独特的QA结构设计提高了在大量数据场景中的问答准确性,而可视化工作流则展示了从问题输入到模型输出的完整流程,便于调试和设计复杂流程。FastGPT还提供了多种调试途径,如搜索测试、引用修改和完整对话预览等,方便用户进行调试。其知识库核心流程包括AI相关参数配置说明,如返回AI内容、最大上下文、函数调用、温度、回复上限、系统提示词、引用模板和引用提示词等。通过这些配置,用户可以更好地控制和优化AI模型的表现。FastGPT的引用模板和提示词设计使得用户可以根据具体需求进行灵活配置,从而提高问答的准确性和效率。总之,FastGPT是一款功能强大、易于使用的智能问答系统,适用于各种应用场景,帮助用户实现高效、准确的知识库问答服务。FastGPT的设计理念是通过先进的技术手段简化用户的操作流程,使得即便是没有技术背景的用户也能轻松上手。其多样化的数据导入方式确保了用户可以根据自身需求选择最合适的方式进行数据输入,而自动化的数据预处理功能则大大减少了用户的工作量。通过对数据的向量化处理,FastGPT能够更好地理解和分析用户的问题,从而提供更为精准的回答。` +FastGPT是一款基于大语言模型(LLM)的智能问答系统,专为提供高效、准确的知识库问答服务而设计。它支持多种数据导入方式,包括手动输入、PDF、WORD、Markdown和CSV等格式,能够自动进行数据预处理、向量化和QA分割,大幅提升数据处理效率。FastGPT的可视化界面设计简洁直观,用户可以通过Flow模块进行工作流编排,轻松实现复杂的问答场景。此外,FastGPT支持多种LLM模型,如GPT、Claude、文心一言等,未来还将支持自定义的向量模型。其强大的API集成能力使得用户可以轻松将其接入现有的GPT应用或其他平台,如企业微信、公众号、飞书等。FastGPT的开源项目遵循Apache License 2.0协议,用户可以进行二次开发和发布。其独特的QA结构设计提高了在大量数据场景中的问答准确性,而可视化工作流则展示了从问题输入到模型输出的完整流程,便于调试和设计复杂流程。FastGPT还提供了多种调试途径,如搜索测试、引用修改和完整对话预览等,方便用户进行调试。其知识库核心流程包括AI相关参数配置说明,如返回AI内容、最大上下文、函数调用、温度、回复上限、系统提示词、引用模板和引用提示词等。`, + `FastGPT还提供了多种调试途径,如搜索测试、引用修改和完整对话预览等,方便用户进行调试。其知识库核心流程包括AI相关参数配置说明,如返回AI内容、最大上下文、函数调用、温度、回复上限、系统提示词、引用模板和引用提示词等。通过这些配置,用户可以更好地控制和优化AI模型的表现。FastGPT的引用模板和提示词设计使得用户可以根据具体需求进行灵活配置,从而提高问答的准确性和效率。总之,FastGPT是一款功能强大、易于使用的智能问答系统,适用于各种应用场景,帮助用户实现高效、准确的知识库问答服务。FastGPT的设计理念是通过先进的技术手段简化用户的操作流程,使得即便是没有技术背景的用户也能轻松上手。其多样化的数据导入方式确保了用户可以根据自身需求选择最合适的方式进行数据输入,而自动化的数据预处理功能则大大减少了用户的工作量。通过对数据的向量化处理,FastGPT能够更好地理解和分析用户的问题,从而提供更为精准的回答。` ] }; @@ -425,3 +435,600 @@ FastGPT是一款基于大语言模型(LLM)的智能问答系统,专为提 expect(chunks).toEqual(mock.result); }); + +// 长代码块分割 +it(`Test splitText2Chunks 7`, () => { + const mock = { + text: `这是一个测试的内容,包含代码块 + +快速了解 FastGPT +FastGPT 的能力与优势 + +FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排,从而实现复杂的问答场景! + +FastGPT 在线使用:https://tryfastgpt.ai + +FastGPT 能力 +1. 专属 AI 客服 +通过导入文档或已有问答对进行训练,让 AI 模型能根据你的文档以交互式对话方式回答问题。 + +2. 简单易用的可视化界面 +FastGPT 采用直观的可视化界面设计,为各种应用场景提供了丰富实用的功能。通过简洁易懂的操作步骤,可以轻松完成 AI 客服的创建和训练流程。 + +~~~js +import { defaultMaxChunkSize } from '../../core/dataset/training/utils'; +import { getErrText } from '../error/utils'; + +const getOneTextOverlapText = ({ text, step }: { text: string; step: number }): string => { + const forbidOverlap = checkForbidOverlap(step); + const maxOverlapLen = chunkSize * 0.4; + + // step >= stepReges.length: Do not overlap incomplete sentences + if (forbidOverlap || overlapLen === 0 || step >= stepReges.length) return ''; + + const splitTexts = getSplitTexts({ text, step }); + let overlayText = ''; + + for (let i = splitTexts.length - 1; i >= 0; i--) { + const currentText = splitTexts[i].text; + const newText = currentText + overlayText; + const newTextLen = newText.length; + + if (newTextLen > overlapLen) { + if (newTextLen > maxOverlapLen) { + const text = getOneTextOverlapText({ text: newText, step: step + 1 }); + return text || overlayText; + } + return newText; + } + + overlayText = newText; + } + return overlayText; + }; + + const getOneTextOverlapText = ({ text, step }: { text: string; step: number }): string => { + const forbidOverlap = checkForbidOverlap(step); + const maxOverlapLen = chunkSize * 0.4; + + // step >= stepReges.length: Do not overlap incomplete sentences + if (forbidOverlap || overlapLen === 0 || step >= stepReges.length) return ''; + + const splitTexts = getSplitTexts({ text, step }); + let overlayText = ''; + + for (let i = splitTexts.length - 1; i >= 0; i--) { + const currentText = splitTexts[i].text; + const newText = currentText + overlayText; + const newTextLen = newText.length; + + if (newTextLen > overlapLen) { + if (newTextLen > maxOverlapLen) { + const text = getOneTextOverlapText({ text: newText, step: step + 1 }); + return text || overlayText; + } + return newText; + } + + overlayText = newText; + } + return overlayText; + }; + + const getOneTextOverlapText = ({ text, step }: { text: string; step: number }): string => { + const forbidOverlap = checkForbidOverlap(step); + const maxOverlapLen = chunkSize * 0.4; + + // step >= stepReges.length: Do not overlap incomplete sentences + if (forbidOverlap || overlapLen === 0 || step >= stepReges.length) return ''; + + const splitTexts = getSplitTexts({ text, step }); + let overlayText = ''; + + for (let i = splitTexts.length - 1; i >= 0; i--) { + const currentText = splitTexts[i].text; + const newText = currentText + overlayText; + const newTextLen = newText.length; + + if (newTextLen > overlapLen) { + if (newTextLen > maxOverlapLen) { + const text = getOneTextOverlapText({ text: newText, step: step + 1 }); + return text || overlayText; + } + return newText; + } + + overlayText = newText; + } + return overlayText; + }; + + const getOneTextOverlapText = ({ text, step }: { text: string; step: number }): string => { + const forbidOverlap = checkForbidOverlap(step); + const maxOverlapLen = chunkSize * 0.4; + + // step >= stepReges.length: Do not overlap incomplete sentences + if (forbidOverlap || overlapLen === 0 || step >= stepReges.length) return ''; + + const splitTexts = getSplitTexts({ text, step }); + let overlayText = ''; + + for (let i = splitTexts.length - 1; i >= 0; i--) { + const currentText = splitTexts[i].text; + const newText = currentText + overlayText; + const newTextLen = newText.length; + + if (newTextLen > overlapLen) { + if (newTextLen > maxOverlapLen) { + const text = getOneTextOverlapText({ text: newText, step: step + 1 }); + return text || overlayText; + } + return newText; + } + + overlayText = newText; + } + return overlayText; + }; + + const getOneTextOverlapText = ({ text, step }: { text: string; step: number }): string => { + const forbidOverlap = checkForbidOverlap(step); + const maxOverlapLen = chunkSize * 0.4; + + // step >= stepReges.length: Do not overlap incomplete sentences + if (forbidOverlap || overlapLen === 0 || step >= stepReges.length) return ''; + + const splitTexts = getSplitTexts({ text, step }); + let overlayText = ''; + + for (let i = splitTexts.length - 1; i >= 0; i--) { + const currentText = splitTexts[i].text; + const newText = currentText + overlayText; + const newTextLen = newText.length; + + if (newTextLen > overlapLen) { + if (newTextLen > maxOverlapLen) { + const text = getOneTextOverlapText({ text: newText, step: step + 1 }); + return text || overlayText; + } + return newText; + } + + overlayText = newText; + } + return overlayText; + }; +~~~ + +3. 自动数据预处理 +提供手动输入、直接分段、LLM 自动处理和 CSV 等多种数据导入途径,其中“直接分段”支持通过 PDF、WORD、Markdown 和 CSV 文档内容作为上下文。FastGPT 会自动对文本数据进行预处理、向量化和 QA 分割,节省手动训练时间,提升效能。 + +4. 工作流编排 +基于 Flow 模块的工作流编排,可以帮助你设计更加复杂的问答流程。例如查询数据库、查询库存、预约实验室等。 + +5. 强大的 API 集成 +FastGPT 对外的 API 接口对齐了 OpenAI 官方接口,可以直接接入现有的 GPT 应用,也可以轻松集成到企业微信、公众号、飞书等平台。 + +FastGPT 特点 +项目开源 + +FastGPT 遵循附加条件 Apache License 2.0 开源协议,你可以 Fork 之后进行二次开发和发布。FastGPT 社区版将保留核心功能,商业版仅在社区版基础上使用 API 的形式进行扩展,不影响学习使用。 + +独特的 QA 结构 + +针对客服问答场景设计的 QA 结构,提高在大量数据场景中的问答准确性。 + +可视化工作流 + +通过 Flow 模块展示了从问题输入到模型输出的完整流程,便于调试和设计复杂流程。 + +无限扩展 + +基于 API 进行扩展,无需修改 FastGPT 源码,也可快速接入现有的程序中。 + +便于调试 + +提供搜索测试、引用修改、完整对话预览等多种调试途径。 + +支持多种模型 + +支持 GPT、Claude、文心一言等多种 LLM 模型,未来也将支持自定义的向量模型。 + +知识库核心流程 + +FastGPT AI 相关参数配置说明 + +在 FastGPT 的 AI 对话模块中,有一个 AI 高级配置,里面包含了 AI 模型的参数配置,本文详细介绍这些配置的含义。 + +返回AI内容(高级编排特有) +这是一个开关,打开的时候,当 AI 对话模块运行时,会将其输出的内容返回到浏览器(API响应);如果关闭,AI 输出的内容不会返回到浏览器,但是生成的内容仍可以通过【AI回复】进行输出。你可以将【AI回复】连接到其他模块中。 + +最大上下文 +代表模型最多容纳的文字数量。`, + result: [ + `这是一个测试的内容,包含代码块 + +快速了解 FastGPT +FastGPT 的能力与优势 + +FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排,从而实现复杂的问答场景! + +FastGPT 在线使用:https://tryfastgpt.ai + +FastGPT 能力 +1. 专属 AI 客服 +通过导入文档或已有问答对进行训练,让 AI 模型能根据你的文档以交互式对话方式回答问题。 + +2. 简单易用的可视化界面 +FastGPT 采用直观的可视化界面设计,为各种应用场景提供了丰富实用的功能。通过简洁易懂的操作步骤,可以轻松完成 AI 客服的创建和训练流程。 + +~~~js +import { defaultMaxChunkSize } from '../../core/dataset/training/utils'; +import { getErrText } from '../error/utils'; + +const getOneTextOverlapText = ({ text, step }: { text: string; step: number }): string => { + const forbidOverlap = checkForbidOverlap(step); + const maxOverlapLen = chunkSize * 0.4; + + // step >= stepReges.length: Do not overlap incomplete sentences + if (forbidOverlap || overlapLen === 0 || step >= stepReges.length) return ''; + + const splitTexts = getSplitTexts({ text, step }); + let overlayText = ''; + + for (let i = splitTexts.length - 1; i >= 0; i--) { + const currentText = splitTexts[i].text; + const newText = currentText + overlayText; + const newTextLen = newText.length; + + if (newTextLen > overlapLen) { + if (newTextLen > maxOverlapLen) { + const text = getOneTextOverlapText({ text: newText, step: step + 1 }); + return text || overlayText; + } + return newText; + } + + overlayText = newText; + } + return overlayText; + }; + + const getOneTextOverlapText = ({ text, step }: { text: string; step: number }): string => { + const forbidOverlap = checkForbidOverlap(step); + const maxOverlapLen = chunkSize * 0.4; + + // step >= stepReges.length: Do not overlap incomplete sentences + if (forbidOverlap || overlapLen === 0 || step >= stepReges.length) return ''; + + const splitTexts = getSplitTexts({ text, step }); + let overlayText = ''; + + for (let i = splitTexts.length - 1; i >= 0; i--) { + const currentText = splitTexts[i].text; + const newText = currentText + overlayText; + const newTextLen = newText.length; + + if (newTextLen > overlapLen) { + if (newTextLen > maxOverlapLen) { + const text = getOneTextOverlapText({ text: newText, step: step + 1 }); + return text || overlayText; + } + return newText; + } + + overlayText = newText; + } + return overlayText; + }; + + const getOneTextOverlapText = ({ text, step }: { text: string; step: number }): string => { + const forbidOverlap = checkForbidOverlap(step); + const maxOverlapLen = chunkSize * 0.4; + + // step >= stepReges.length: Do not overlap incomplete sentences + if (forbidOverlap || overlapLen === 0 || step >= stepReges.length) return ''; + + const splitTexts = getSplitTexts({ text, step }); + let overlayText = ''; + + for (let i = splitTexts.length - 1; i >= 0; i--) { + const currentText = splitTexts[i].text; + const newText = currentText + overlayText; + const newTextLen = newText.length; + + if (newTextLen > overlapLen) { + if (newTextLen > maxOverlapLen) { + const text = getOneTextOverlapText({ text: newText, step: step + 1 }); + return text || overlayText; + } + return newText; + } + + overlayText = newText; + } + return overlayText; + }; + + const getOneTextOverlapText = ({ text, step }: { text: string; step: number }): string => { + const forbidOverlap = checkForbidOverlap(step); + const maxOverlapLen = chunkSize * 0.4; + + // step >= stepReges.length: Do not overlap incomplete sentences + if (forbidOverlap || overlapLen === 0 || step >= stepReges.length) return ''; + + const splitTexts = getSplitTexts({ text, step }); + let overlayText = ''; + + for (let i = splitTexts.length - 1; i >= 0; i--) { + const currentText = splitTexts[i].text; + const newText = currentText + overlayText; + const newTextLen = newText.length; + + if (newTextLen > overlapLen) { + if (newTextLen > maxOverlapLen) { + const text = getOneTextOverlapText({ text: newText, step: step + 1 }); + return text || overlayText; + } + return newText; + } + + overlayText = newText; + } + return overlayText; + }; + + const getOneTextOverlapText = ({ text, step }: { text: string; step: number }): string => { + const forbidOverlap = checkForbidOverlap(step); + const maxOverlapLen = chunkSize * 0.4; + + // step >= stepReges.length: Do not overlap incomplete sentences + if (forbidOverlap || overlapLen === 0 || step >= stepReges.length) return ''; + + const splitTexts = getSplitTexts({ text, step }); + let overlayText = ''; + + for (let i = splitTexts.length - 1; i >= 0; i--) { + const currentText = splitTexts[i].text; + const newText = currentText + overlayText; + const newTextLen = newText.length; + + if (newTextLen > overlapLen) { + if (newTextLen > maxOverlapLen) { + const text = getOneTextOverlapText({ text: newText, step: step + 1 }); + return text || overlayText; + } + return newText; + } + + overlayText = newText; + } + return overlayText; + }; +~~~`, + `3. 自动数据预处理 +提供手动输入、直接分段、LLM 自动处理和 CSV 等多种数据导入途径,其中“直接分段”支持通过 PDF、WORD、Markdown 和 CSV 文档内容作为上下文。FastGPT 会自动对文本数据进行预处理、向量化和 QA 分割,节省手动训练时间,提升效能。 + +4. 工作流编排 +基于 Flow 模块的工作流编排,可以帮助你设计更加复杂的问答流程。例如查询数据库、查询库存、预约实验室等。 + +5. 强大的 API 集成 +FastGPT 对外的 API 接口对齐了 OpenAI 官方接口,可以直接接入现有的 GPT 应用,也可以轻松集成到企业微信、公众号、飞书等平台。 + +FastGPT 特点 +项目开源 + +FastGPT 遵循附加条件 Apache License 2.0 开源协议,你可以 Fork 之后进行二次开发和发布。FastGPT 社区版将保留核心功能,商业版仅在社区版基础上使用 API 的形式进行扩展,不影响学习使用。 + +独特的 QA 结构 + +针对客服问答场景设计的 QA 结构,提高在大量数据场景中的问答准确性。 + +可视化工作流 + +通过 Flow 模块展示了从问题输入到模型输出的完整流程,便于调试和设计复杂流程。 +`, + `无限扩展 + +基于 API 进行扩展,无需修改 FastGPT 源码,也可快速接入现有的程序中。 + +便于调试 + +提供搜索测试、引用修改、完整对话预览等多种调试途径。 + +支持多种模型 + +支持 GPT、Claude、文心一言等多种 LLM 模型,未来也将支持自定义的向量模型。 + +知识库核心流程 + +FastGPT AI 相关参数配置说明 + +在 FastGPT 的 AI 对话模块中,有一个 AI 高级配置,里面包含了 AI 模型的参数配置,本文详细介绍这些配置的含义。 + +返回AI内容(高级编排特有) +这是一个开关,打开的时候,当 AI 对话模块运行时,会将其输出的内容返回到浏览器(API响应);如果关闭,AI 输出的内容不会返回到浏览器,但是生成的内容仍可以通过【AI回复】进行输出。你可以将【AI回复】连接到其他模块中。 + +最大上下文 +代表模型最多容纳的文字数量。` + ] + }; + + const { chunks } = splitText2Chunks({ + text: mock.text, + chunkSize: 500, + maxSize: 100000, + customReg: ['----'] + }); + + const normalizedChunks = simpleChunks(chunks); + const normalizedExpected = simpleChunks(mock.result); + + expect(normalizedChunks).toEqual(normalizedExpected); +}); + +// 表格分割测试 - 不超出maxSize +it(`Test splitText2Chunks 1`, () => { + const mock = { + text: `测试的呀 + +| 序号 | 姓名 | 年龄 | 职业 | 城市 | +|------|------|------|------|------| +| 1 | 张三 | 25 | 工程师 | 北京 | +| 2 | 李四 | 30 | 教师 | 上海 | +| 3 | 王五 | 28 | 医生 | 广州 | +| 4 | 赵六 | 35 | 律师 | 深圳 | +| 5 | 孙七 | 27 | 设计师 | 杭州 | +| 6 | 周八 | 32 | 会计 | 成都 | +| 7 | 吴九 | 29 | 销售 | 武汉 | +| 8 | 郑十 | 31 | 记者 | 南京 | +| 9 | 刘一 | 33 | 建筑师 | 天津 | +| 10 | 陈二 | 26 | 程序员 | 重庆 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 1001 | 杨一 | 34 | 程序员 | 厦门 | +| 1002 | 杨二 | 34 | 程序员 | 厦门 | +| 1003 | 杨三 | 34 | 程序员 | 厦门 | +| 1004 | 杨四 | 34 | 程序员 | 厦门 | +| 1005 | 杨五 | 34 | 程序员 | 厦门 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 9 | 刘一 | 33 | 建筑师 | 天津 | +| 10 | 陈二 | 26 | 程序员 | 重庆 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 1001 | 杨一 | 34 | 程序员 | 厦门 | +| 1002 | 杨二 | 34 | 程序员 | 厦门 | +| 1003 | 杨三 | 34 | 程序员 | 厦门 | +| 1004 | 杨四 | 34 | 程序员 | 厦门 | +| 1005 | 杨五 | 34 | 程序员 | 厦门 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | + +这是第二段了`, + result: [ + `测试的呀 + +| 序号 | 姓名 | 年龄 | 职业 | 城市 | +|------|------|------|------|------| +| 1 | 张三 | 25 | 工程师 | 北京 | +| 2 | 李四 | 30 | 教师 | 上海 | +| 3 | 王五 | 28 | 医生 | 广州 | +| 4 | 赵六 | 35 | 律师 | 深圳 | +| 5 | 孙七 | 27 | 设计师 | 杭州 | +| 6 | 周八 | 32 | 会计 | 成都 | +| 7 | 吴九 | 29 | 销售 | 武汉 | +| 8 | 郑十 | 31 | 记者 | 南京 | +| 9 | 刘一 | 33 | 建筑师 | 天津 | +| 10 | 陈二 | 26 | 程序员 | 重庆 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 1001 | 杨一 | 34 | 程序员 | 厦门 | +| 1002 | 杨二 | 34 | 程序员 | 厦门 | +| 1003 | 杨三 | 34 | 程序员 | 厦门 | +| 1004 | 杨四 | 34 | 程序员 | 厦门 | +| 1005 | 杨五 | 34 | 程序员 | 厦门 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 9 | 刘一 | 33 | 建筑师 | 天津 | +| 10 | 陈二 | 26 | 程序员 | 重庆 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 1001 | 杨一 | 34 | 程序员 | 厦门 | +| 1002 | 杨二 | 34 | 程序员 | 厦门 | +| 1003 | 杨三 | 34 | 程序员 | 厦门 | +| 1004 | 杨四 | 34 | 程序员 | 厦门 | +| 1005 | 杨五 | 34 | 程序员 | 厦门 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | + +这是第二段了` + ] + }; + + const { chunks } = splitText2Chunks({ text: mock.text, chunkSize: 2000 }); + expect(chunks).toEqual(mock.result); +}); +// 表格分割测试 - 超出maxSize +it(`Test splitText2Chunks 1`, () => { + const mock = { + text: `测试的呀 + +| 序号 | 姓名 | 年龄 | 职业 | 城市 | +|------|------|------|------|------| +| 1 | 张三 | 25 | 工程师 | 北京 | +| 2 | 李四 | 30 | 教师 | 上海 | +| 3 | 王五 | 28 | 医生 | 广州 | +| 4 | 赵六 | 35 | 律师 | 深圳 | +| 5 | 孙七 | 27 | 设计师 | 杭州 | +| 6 | 周八 | 32 | 会计 | 成都 | +| 7 | 吴九 | 29 | 销售 | 武汉 | +| 8 | 郑十 | 31 | 记者 | 南京 | +| 9 | 刘一 | 33 | 建筑师 | 天津 | +| 10 | 陈二 | 26 | 程序员 | 重庆 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 1001 | 杨一 | 34 | 程序员 | 厦门 | +| 1002 | 杨二 | 34 | 程序员 | 厦门 | +| 1003 | 杨三 | 34 | 程序员 | 厦门 | +| 1004 | 杨四 | 34 | 程序员 | 厦门 | +| 1005 | 杨五 | 34 | 程序员 | 厦门 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 9 | 刘一 | 33 | 建筑师 | 天津 | +| 10 | 陈二 | 26 | 程序员 | 重庆 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 1001 | 杨一 | 34 | 程序员 | 厦门 | +| 1002 | 杨二 | 34 | 程序员 | 厦门 | +| 1003 | 杨三 | 34 | 程序员 | 厦门 | +| 1004 | 杨四 | 34 | 程序员 | 厦门 | +| 1005 | 杨五 | 34 | 程序员 | 厦门 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | + +这是第二段了`, + result: [ + `测试的呀 + +| 序号 | 姓名 | 年龄 | 职业 | 城市 | +|------|------|------|------|------| +| 1 | 张三 | 25 | 工程师 | 北京 | +| 2 | 李四 | 30 | 教师 | 上海 | +| 3 | 王五 | 28 | 医生 | 广州 | +| 4 | 赵六 | 35 | 律师 | 深圳 | +| 5 | 孙七 | 27 | 设计师 | 杭州 | +| 6 | 周八 | 32 | 会计 | 成都 | +| 7 | 吴九 | 29 | 销售 | 武汉 | +| 8 | 郑十 | 31 | 记者 | 南京 | +| 9 | 刘一 | 33 | 建筑师 | 天津 | +| 10 | 陈二 | 26 | 程序员 | 重庆 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 1001 | 杨一 | 34 | 程序员 | 厦门 | +| 1002 | 杨二 | 34 | 程序员 | 厦门 | +| 1003 | 杨三 | 34 | 程序员 | 厦门 | +| 1004 | 杨四 | 34 | 程序员 | 厦门 | +| 1005 | 杨五 | 34 | 程序员 | 厦门 |`, + `| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 9 | 刘一 | 33 | 建筑师 | 天津 | +| 10 | 陈二 | 26 | 程序员 | 重庆 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 1001 | 杨一 | 34 | 程序员 | 厦门 | +| 1002 | 杨二 | 34 | 程序员 | 厦门 | +| 1003 | 杨三 | 34 | 程序员 | 厦门 | +| 1004 | 杨四 | 34 | 程序员 | 厦门 | +| 1005 | 杨五 | 34 | 程序员 | 厦门 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | +| 1000 | 黄末 | 28 | 作家 | 厦门 | + +这是第二段了` + ] + }; + + const { chunks } = splitText2Chunks({ text: mock.text, chunkSize: 512, maxSize: 512 }); + + expect(chunks).toEqual(mock.result); +});