From fa8c5dcd6de81fc5fa19ec186f85d262f703516c Mon Sep 17 00:00:00 2001 From: archer <545436317@qq.com> Date: Mon, 4 Mar 2024 17:20:09 +0800 Subject: [PATCH] perf: Extract field can use default value --- .../content/docs/development/upgrading/469.md | 13 ++++--- .../module/template/system/contextExtract.ts | 1 + packages/global/core/module/type.d.ts | 1 + pnpm-lock.yaml | 3 ++ projects/app/package.json | 1 + projects/app/public/docs/versionIntro.md | 9 +++-- projects/app/public/locales/en/common.json | 6 ++- projects/app/public/locales/zh/common.json | 9 ++++- .../nodes/NodeExtract/ExtractFieldModal.tsx | 31 +++++++++++---- .../components/nodes/NodeExtract/index.tsx | 1 - projects/app/src/global/core/prompt/agent.ts | 7 ++-- projects/app/src/pages/chat/share.tsx | 6 +-- .../service/moduleDispatch/agent/extract.ts | 39 +++++++++---------- 13 files changed, 78 insertions(+), 49 deletions(-) diff --git a/docSite/content/docs/development/upgrading/469.md b/docSite/content/docs/development/upgrading/469.md index f11372a50d2..d9be3879a80 100644 --- a/docSite/content/docs/development/upgrading/469.md +++ b/docSite/content/docs/development/upgrading/469.md @@ -30,9 +30,10 @@ curl --location --request POST 'https://{{host}}/api/init/v469' \ 2. 新增 - 完善了HTTP模块的变量提示。 3. 新增 - HTTP模块支持OpenAI单接口导入。 4. 新增 - 全局变量支持增加外部变量。可通过分享链接的Query或 API 的 variables 参数传入。 -5. 优化 - 问题补全。增加英文类型。同时可以设置为单独模块,方便复用。 -6. 优化 - 重写了计量模式 -7. 优化 - Token 过滤历史记录,保持偶数条,防止部分模型报错。 -8. 优化 - 分享链接SEO,可直接展示应用名和头像。 -9. 修复 - 标注功能。 -10. 修复 - qa生成线程计数错误。 +5. 新增 - 内容提取模块增加默认值。 +6. 优化 - 问题补全。增加英文类型。同时可以设置为单独模块,方便复用。 +7. 优化 - 重写了计量模式 +8. 优化 - Token 过滤历史记录,保持偶数条,防止部分模型报错。 +9. 优化 - 分享链接SEO,可直接展示应用名和头像。 +10. 修复 - 标注功能。 +11. 修复 - qa生成线程计数错误。 diff --git a/packages/global/core/module/template/system/contextExtract.ts b/packages/global/core/module/template/system/contextExtract.ts index ef85f78f2f6..aaa413ee189 100644 --- a/packages/global/core/module/template/system/contextExtract.ts +++ b/packages/global/core/module/template/system/contextExtract.ts @@ -76,6 +76,7 @@ export const ContextExtractModule: FlowModuleTemplateType = { { key: ModuleOutputKeyEnum.failed, label: '提取字段缺失', + description: '存在一个或多个字段未提取成功。尽管使用了默认值也算缺失。', valueType: ModuleIOValueTypeEnum.boolean, type: FlowNodeOutputTypeEnum.source, targets: [] diff --git a/packages/global/core/module/type.d.ts b/packages/global/core/module/type.d.ts index dfb630d644a..7ffc5bbfe3e 100644 --- a/packages/global/core/module/type.d.ts +++ b/packages/global/core/module/type.d.ts @@ -80,6 +80,7 @@ export type ContextExtractAgentItemType = { desc: string; key: string; required: boolean; + defaultValue?: string; enum?: string; }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d4819034b2c..36e7148acf1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -317,6 +317,9 @@ importers: jschardet: specifier: ^3.0.0 version: 3.0.0 + json5: + specifier: ^2.2.3 + version: 2.2.3 jsonwebtoken: specifier: ^9.0.2 version: 9.0.2 diff --git a/projects/app/package.json b/projects/app/package.json index 1d61f447e7f..aac4c38cd19 100644 --- a/projects/app/package.json +++ b/projects/app/package.json @@ -37,6 +37,7 @@ "immer": "^9.0.19", "js-yaml": "^4.1.0", "jschardet": "^3.0.0", + "json5": "^2.2.3", "jsonwebtoken": "^9.0.2", "lodash": "^4.17.21", "mermaid": "^10.2.3", diff --git a/projects/app/public/docs/versionIntro.md b/projects/app/public/docs/versionIntro.md index ced6580681a..ab085da1194 100644 --- a/projects/app/public/docs/versionIntro.md +++ b/projects/app/public/docs/versionIntro.md @@ -4,7 +4,8 @@ 2. 新增 - 完善了HTTP模块的变量提示。 3. 新增 - HTTP模块支持OpenAI单接口导入。 4. 新增 - 全局变量支持增加外部变量。可通过分享链接的Query或 API 的 variables 参数传入。 -5. 优化 - 问题补全。增加英文类型。同时可以设置为单独模块,方便复用。 -6. [点击查看高级编排介绍文档](https://doc.fastgpt.in/docs/workflow/intro) -7. [使用文档](https://doc.fastgpt.in/docs/intro/) -8. [点击查看商业版](https://doc.fastgpt.in/docs/commercial/) \ No newline at end of file +5. 新增 - 内容提取模块增加默认值。 +6. 优化 - 问题补全。增加英文类型。同时可以设置为单独模块,方便复用。 +7. [点击查看高级编排介绍文档](https://doc.fastgpt.in/docs/workflow/intro) +8. [使用文档](https://doc.fastgpt.in/docs/intro/) +9. [点击查看商业版](https://doc.fastgpt.in/docs/commercial/) \ No newline at end of file diff --git a/projects/app/public/locales/en/common.json b/projects/app/public/locales/en/common.json index 70ce0fd1054..ff5ccb7d3bf 100644 --- a/projects/app/public/locales/en/common.json +++ b/projects/app/public/locales/en/common.json @@ -774,6 +774,8 @@ "Input description": "", "label": "Dataset quote" }, + "Default value": "Default ", + "Default value placeholder": "Null characters are returned by default", "Field Description": "Description", "Field Name": "Name", "Field Type": "Type", @@ -798,7 +800,9 @@ "Enum Description": "Lists the possible values for the field, one per row", "Enum Value": "Enum", "Field Description Placeholder": "Name/age /sql statement......", - "Field Setting Title": "Extract field configuration" + "Field Setting Title": "Extract field configuration", + "Required": "Required", + "Required Description": "Even if the field cannot be extracted, it is returned with the default value" }, "http": { "Add props": "Add props", diff --git a/projects/app/public/locales/zh/common.json b/projects/app/public/locales/zh/common.json index 0b5f577df34..78f1ae412be 100644 --- a/projects/app/public/locales/zh/common.json +++ b/projects/app/public/locales/zh/common.json @@ -561,7 +561,8 @@ "success": "开始同步" } }, - "training": {} + "training": { + } }, "data": { "Auxiliary Data": "辅助数据", @@ -775,6 +776,8 @@ "Input description": "可接收知识库搜索的结果。", "label": "知识库引用" }, + "Default value": "默认值", + "Default value placeholder": "不填则默认返回空字符", "Field Description": "字段描述", "Field Name": "字段名", "Field Type": "字段类型", @@ -799,7 +802,9 @@ "Enum Description": "列举出该字段可能的值,每行一个", "Enum Value": "枚举值", "Field Description Placeholder": "姓名/年龄/sql语句……", - "Field Setting Title": "提取字段配置" + "Field Setting Title": "提取字段配置", + "Required": "必须返回", + "Required Description": "即使无法提取该字段,也会使用默认值进行返回" }, "http": { "Add props": "添加参数", diff --git a/projects/app/src/components/core/module/Flow/components/nodes/NodeExtract/ExtractFieldModal.tsx b/projects/app/src/components/core/module/Flow/components/nodes/NodeExtract/ExtractFieldModal.tsx index 3a51eb677cc..f063640dffd 100644 --- a/projects/app/src/components/core/module/Flow/components/nodes/NodeExtract/ExtractFieldModal.tsx +++ b/projects/app/src/components/core/module/Flow/components/nodes/NodeExtract/ExtractFieldModal.tsx @@ -16,10 +16,11 @@ import { useTranslation } from 'next-i18next'; import MyTooltip from '@/components/MyTooltip'; import { QuestionOutlineIcon } from '@chakra-ui/icons'; -export const defaultField = { +export const defaultField: ContextExtractAgentItemType = { + required: false, + defaultValue: '', desc: '', key: '', - required: true, enum: '' }; @@ -33,9 +34,10 @@ const ExtractFieldModal = ({ onSubmit: (data: ContextExtractAgentItemType) => void; }) => { const { t } = useTranslation(); - const { register, handleSubmit } = useForm({ + const { register, handleSubmit, watch } = useForm({ defaultValues: defaultField }); + const required = watch('required'); return ( - - {t('common.Require Input')} + + + {t('core.module.extract.Required')} + + + + + {required && ( + + {t('core.module.Default value')} + + + )} + - {t('core.module.Field key')} + {t('core.module.Field key')} - {t('core.module.Field Description')} + {t('core.module.Field Description')} ) => { const newOutput = { key: data.key, label: `提取结果-${data.desc}`, - description: '无法提取时不会返回', valueType: ModuleIOValueTypeEnum.string, type: FlowNodeOutputTypeEnum.source, targets: [] diff --git a/projects/app/src/global/core/prompt/agent.ts b/projects/app/src/global/core/prompt/agent.ts index a4e6b467c70..5e8efa9ca8f 100644 --- a/projects/app/src/global/core/prompt/agent.ts +++ b/projects/app/src/global/core/prompt/agent.ts @@ -31,10 +31,9 @@ export const Prompt_ExtractJson = `你可以从 <对话记录> <字段说明> 1. 下面的 JSON 字符串均按照 JSON Schema 的规则描述。 -2. key 代表字段名;description 代表字段的描述;required 代表字段是否必须;enum 是可选值,代表可选的 value。 -3. 如果字段内容为空,你可以返回空字符串。 - -{{json}} +2. key 代表字段名;description 代表字段的描述;enum 是可选值,代表可选的 value。 +3. 如果没有可提取的内容,忽略该字段。 +4. 本次需提取的JSON Schema:{{json}} <对话记录> diff --git a/projects/app/src/pages/chat/share.tsx b/projects/app/src/pages/chat/share.tsx index 52e0a04a7e9..83fb9ea453f 100644 --- a/projects/app/src/pages/chat/share.tsx +++ b/projects/app/src/pages/chat/share.tsx @@ -415,9 +415,9 @@ export async function getServerSideProps(context: any) { return { props: { - appName: app?.appId?.name, - appAvatar: app?.appId?.avatar, - appIntro: app?.appId?.intro, + appName: app?.appId?.name || '', + appAvatar: app?.appId?.avatar || '', + appIntro: app?.appId?.intro || '', ...(await serviceSideProps(context)) } }; diff --git a/projects/app/src/service/moduleDispatch/agent/extract.ts b/projects/app/src/service/moduleDispatch/agent/extract.ts index 54e1845f156..6bee61d1982 100644 --- a/projects/app/src/service/moduleDispatch/agent/extract.ts +++ b/projects/app/src/service/moduleDispatch/agent/extract.ts @@ -16,6 +16,7 @@ import { LLMModelItemType } from '@fastgpt/global/core/ai/model.d'; import { getHistories } from '../utils'; import { ModelTypeEnum, getLLMModel } from '@fastgpt/service/core/ai/model'; import { formatModelChars2Points } from '@fastgpt/service/support/wallet/usage/utils'; +import json5 from 'json5'; type Props = ModuleDispatchProps<{ [ModuleInputKeyEnum.history]?: ChatItemType[]; @@ -64,7 +65,8 @@ export async function dispatchContentExtract(props: Props): Promise { // remove invalid key for (let key in arg) { - if (!extractKeys.find((item) => item.key === key)) { + const item = extractKeys.find((item) => item.key === key); + if (!item) { delete arg[key]; } if (arg[key] === '') { @@ -72,12 +74,20 @@ export async function dispatchContentExtract(props: Props): Promise { } } + // auto fill required fields + extractKeys.forEach((item) => { + if (item.required && !arg[item.key]) { + arg[item.key] = item.defaultValue || ''; + } + }); + // auth fields let success = !extractKeys.find((item) => !(item.key in arg)); // auth empty value if (success) { for (const key in arg) { - if (arg[key] === '') { + const item = extractKeys.find((item) => item.key === key); + if (!item) { success = false; break; } @@ -125,14 +135,8 @@ async function toolChoice({ ...histories, { obj: ChatRoleEnum.Human, - value: `你的任务: -""" -${description || '根据用户要求获取适当的 JSON 字符串。'} + value: `你的任务是根据上下文获取适当的 JSON 字符串。要求: """ - -要求: -""" -- 如果字段为空,你返回空字符串。 - 字符串不要换行。 - 结合上下文和当前问题进行获取。 """ @@ -167,8 +171,7 @@ ${description || '根据用户要求获取适当的 JSON 字符串。'} description, parameters: { type: 'object', - properties, - required: extractKeys.filter((item) => item.required).map((item) => item.key) + properties } }; const tools: any = [ @@ -193,7 +196,7 @@ ${description || '根据用户要求获取适当的 JSON 字符串。'} const arg: Record = (() => { try { - return JSON.parse( + return json5.parse( response?.choices?.[0]?.message?.tool_calls?.[0]?.function?.arguments || '{}' ); } catch (error) { @@ -225,7 +228,7 @@ async function completions({ json: extractKeys .map( (item) => - `{"key":"${item.key}", "description":"${item.desc}", "required":${item.required}${ + `{"key":"${item.key}", "description":"${item.desc}"${ item.enum ? `, "enum":"[${item.enum.split('\n')}]"` : '' }}` ) @@ -240,7 +243,6 @@ Human: ${content}` userKey: user.openaiAccount, timeout: 480000 }); - const data = await ai.chat.completions.create({ model: extractModel.model, temperature: 0.01, @@ -260,19 +262,14 @@ Human: ${content}` arg: {} }; - const jsonStr = answer - .substring(start, end + 1) - .replace(/(\\n|\\)/g, '') - .replace(/ /g, ''); - try { return { rawResponse: answer, tokens: countMessagesTokens(messages), - - arg: JSON.parse(jsonStr) as Record + arg: json5.parse(answer) as Record }; } catch (error) { + console.log(error); return { rawResponse: answer, tokens: countMessagesTokens(messages),