diff --git a/docSite/assets/imgs/aiPolish.png b/docSite/assets/imgs/aiPolish.png new file mode 100644 index 00000000000..385201ec19e Binary files /dev/null and b/docSite/assets/imgs/aiPolish.png differ diff --git a/docSite/content/docs/workflow/intro.md b/docSite/content/docs/workflow/intro.md index a815e42c576..d1b57ffcaf0 100644 --- a/docSite/content/docs/workflow/intro.md +++ b/docSite/content/docs/workflow/intro.md @@ -40,7 +40,7 @@ FastGPT 从 V4 版本开始采用新的交互方式来构建 AI 应用。使用 每个模块会包含 3 个核心部分:固定参数、外部输入(左边有个圆圈)和输出(右边有个圆圈)。 ![](/imgs/flow-intro3.png) - + - 对话模型、温度、回复上限、系统提示词和限定词为固定参数,同时系统提示词和限定词也可以作为外部输入,意味着如果你有输入流向了系统提示词,那么原本填写的内容就会被**覆盖**。 - 触发器、引用内容、聊天记录和用户问题则为外部输入,需要从其他模块的输出流入。 - 回复结束则为该模块的输出。 @@ -89,10 +89,10 @@ FastGPT 从 V4 版本开始采用新的交互方式来构建 AI 应用。使用 ### 想合并多个输出结果怎么实现? 1. 文本加工,可以对字符串进行合并。 -2. 知识库搜索合并,可以合并多个知识库搜索结果 -3. 其他结果,无法直接合并,可以考虑传入到`HTTP`模块中进行合并,使用`[Laf](https://laf.run/)`可以快速实现一个无服务器HTTP接口。 +2. 在Workflow中存在多个输出节点时,使用AI回复润色,可以拦截每个输出内容,并在Workflow结束时统一使用AI进行润色后回复,保持对话连贯且风格统一。 +3. 知识库搜索合并,可以合并多个知识库搜索结果 +4. 其他结果,无法直接合并,可以考虑传入到`HTTP`模块中进行合并,使用`[Laf](https://laf.run/)`可以快速实现一个无服务器HTTP接口。 ### 模块为什么有2个用户问题 左侧的`用户问题`是指该模块所需的输入。右侧的`用户问题`是为了方便后续的连线,输出的值和传入的用户问题一样。 - diff --git a/docSite/content/docs/workflow/modules/ai_polish.md b/docSite/content/docs/workflow/modules/ai_polish.md new file mode 100644 index 00000000000..3b0541b23bd --- /dev/null +++ b/docSite/content/docs/workflow/modules/ai_polish.md @@ -0,0 +1,26 @@ +--- +title: "AI 回复润色" +description: "FastGPT AI 回复润色功能介绍" +icon: "auto_awesome" +draft: false +toc: true +weight: 361 +--- + +## 特点 + +- 默认开启,需要手动关闭。 +- 会影响输出类模块的行为,拦截“指定回复”、“AI对话”在Workflow中的输出,并在Workflow结束时统一输出。 +- 全局生效(被调用的应用也会生效)。 + +## 说明 + +在不开启此功能时,如果Workflow的运行过程中如果触发了多个回复节点(例如“指定回复”、“AI对话”等),回复内容是多个节点输出的字符串拼接,这对于实际使用体验来说不够友好。这在多个应用调用的情景下特别常见。 + +因此我们增加了 *AI回复润色* 功能,如下图,定义了一个全局开关: + +![](/imgs/aiPolish.png) + +当用户开启此功能时,所有Workflow中间节点所输出的内容都会被拦截并记录下来,直到Workflow结束时,会使用 AI 进行润色并完成输出。 + +**建议**:如果Workflow中只有一个输出节点时,建议将此功能关闭。 \ No newline at end of file diff --git a/packages/global/core/app/type.d.ts b/packages/global/core/app/type.d.ts index 6bba1ad288c..2131d70c179 100644 --- a/packages/global/core/app/type.d.ts +++ b/packages/global/core/app/type.d.ts @@ -86,6 +86,7 @@ export type AppSimpleEditFormType = { }[]; }[]; questionGuide: boolean; + polish: boolean; tts: { type: 'none' | 'web' | 'model'; model?: string | undefined; @@ -121,6 +122,7 @@ export type AppSimpleEditConfigTemplateType = { welcomeText?: boolean; variables?: boolean; questionGuide?: boolean; + polish?: boolean; tts?: boolean; }; }; diff --git a/packages/global/core/app/utils.ts b/packages/global/core/app/utils.ts index f8b24f219eb..624506844a7 100644 --- a/packages/global/core/app/utils.ts +++ b/packages/global/core/app/utils.ts @@ -31,6 +31,7 @@ export const getDefaultAppForm = (): AppSimpleEditFormType => { welcomeText: '', variables: [], questionGuide: false, + polish: true, tts: { type: 'web' } @@ -116,13 +117,14 @@ export const appModules2Form = ({ modules }: { modules: ModuleItemType[] }) => { target?.inputs?.find((item) => item.key === ModuleInputKeyEnum.answerText)?.value || ''; } } else if (module.flowType === FlowNodeTypeEnum.userGuide) { - const { welcomeText, variableModules, questionGuide, ttsConfig } = splitGuideModule( + const { welcomeText, variableModules, questionGuide, polish, ttsConfig } = splitGuideModule( getGuideModule(modules) ); defaultAppForm.userGuide = { welcomeText: welcomeText, variables: variableModules, questionGuide: questionGuide, + polish: polish, tts: ttsConfig }; } diff --git a/packages/global/core/module/constants.ts b/packages/global/core/module/constants.ts index 16098f51f1f..83f5bd37f8a 100644 --- a/packages/global/core/module/constants.ts +++ b/packages/global/core/module/constants.ts @@ -33,6 +33,7 @@ export enum ModuleInputKeyEnum { history = 'history', userChatInput = 'userChatInput', questionGuide = 'questionGuide', + polish = 'polish', tts = 'tts', answerText = 'text', agents = 'agents', // cq agent key diff --git a/packages/global/core/module/type.d.ts b/packages/global/core/module/type.d.ts index 7ffc5bbfe3e..5b8f7a5c982 100644 --- a/packages/global/core/module/type.d.ts +++ b/packages/global/core/module/type.d.ts @@ -120,6 +120,7 @@ export type ChatDispatchProps = { responseChatItemId?: string; histories: ChatItemType[]; variables: Record; + polish: boolean; stream: boolean; detail: boolean; // response detail }; diff --git a/packages/global/core/module/utils.ts b/packages/global/core/module/utils.ts index 880946f55f5..a1afc0854a9 100644 --- a/packages/global/core/module/utils.ts +++ b/packages/global/core/module/utils.ts @@ -24,6 +24,9 @@ export const splitGuideModule = (guideModules?: ModuleItemType) => { !!guideModules?.inputs?.find((item) => item.key === ModuleInputKeyEnum.questionGuide)?.value || false; + const polish: boolean = + !!guideModules?.inputs?.find((item) => item.key === ModuleInputKeyEnum.polish)?.value || false; + const ttsConfig: AppTTSConfigType = guideModules?.inputs?.find( (item) => item.key === ModuleInputKeyEnum.tts )?.value || { type: 'web' }; @@ -32,6 +35,7 @@ export const splitGuideModule = (guideModules?: ModuleItemType) => { welcomeText, variableModules, questionGuide, + polish, ttsConfig }; }; diff --git a/packages/service/common/response/index.ts b/packages/service/common/response/index.ts index feb4fa183e2..62b39b22687 100644 --- a/packages/service/common/response/index.ts +++ b/packages/service/common/response/index.ts @@ -118,10 +118,12 @@ export function responseWrite({ res, write, event, + polish = false, data }: { res?: NextApiResponse; write?: (text: string) => void; + polish?: boolean; event?: string; data: string; }) { @@ -130,5 +132,11 @@ export function responseWrite({ if (!Write) return; event && Write(`event: ${event}\n`); - Write(`data: ${data}\n\n`); + + if ( + !polish || + (event !== sseResponseEventEnum.response && event !== sseResponseEventEnum.answer) + ) { + Write(`data: ${data}\n\n`); + } } diff --git a/packages/web/components/common/Icon/constants.ts b/packages/web/components/common/Icon/constants.ts index 2417e7e6987..99e2448e6b7 100644 --- a/packages/web/components/common/Icon/constants.ts +++ b/packages/web/components/common/Icon/constants.ts @@ -62,6 +62,7 @@ export const iconPaths = { 'core/app/logsLight': () => import('./icons/core/app/logsLight.svg'), 'core/app/markLight': () => import('./icons/core/app/markLight.svg'), 'core/app/questionGuide': () => import('./icons/core/app/questionGuide.svg'), + 'core/app/polish': () => import('./icons/core/app/polish.svg'), 'core/app/simpleMode/ai': () => import('./icons/core/app/simpleMode/ai.svg'), 'core/app/simpleMode/chat': () => import('./icons/core/app/simpleMode/chat.svg'), 'core/app/simpleMode/dataset': () => import('./icons/core/app/simpleMode/dataset.svg'), diff --git a/packages/web/components/common/Icon/icons/core/app/polish.svg b/packages/web/components/common/Icon/icons/core/app/polish.svg new file mode 100644 index 00000000000..16d0c885913 --- /dev/null +++ b/packages/web/components/common/Icon/icons/core/app/polish.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/data/simpleTemplates/fastgpt-simple.json b/projects/app/data/simpleTemplates/fastgpt-simple.json index cc18023bb89..7301bf5f998 100644 --- a/projects/app/data/simpleTemplates/fastgpt-simple.json +++ b/projects/app/data/simpleTemplates/fastgpt-simple.json @@ -22,7 +22,8 @@ "welcomeText": true, "variables": false, "questionGuide": false, + "polish": true, "tts": true } } -} +} \ 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 162667aac4e..f17ab0d0e9d 100644 --- a/projects/app/public/locales/en/common.json +++ b/projects/app/public/locales/en/common.json @@ -898,6 +898,8 @@ "template": { "Ai chat": "LLM Chat", "Ai chat intro": "Request LLM chat", + "Ai Polish": "AI Polish", + "Ai Polish intro": "Using AI to polish response content, connecting multiple response nodes in the workflow and ensuring a consistent response style.", "Assigned reply": "Assigned reply", "Assigned reply intro": "The module can respond directly to a specified piece of content. Often used to guide and prompt. When non-string content is passed in, it is converted to a string for output.", "Chat entrance": "Chat entrance", @@ -1499,4 +1501,4 @@ } } } -} +} \ No newline at end of file diff --git a/projects/app/public/locales/zh/common.json b/projects/app/public/locales/zh/common.json index 19990533fe5..bba143953c0 100644 --- a/projects/app/public/locales/zh/common.json +++ b/projects/app/public/locales/zh/common.json @@ -262,6 +262,8 @@ "Max tokens": "回复上限", "Name and avatar": "头像 & 名称", "Next Step Guide": "下一步指引", + "Ai Polish": "AI 回复润色", + "Ai Polish Tip": "使用AI润色回复内容,衔接多回复节点的workflow以及并使回复风格统一。", "Question Guide": "问题引导", "Question Guide Tip": "对话结束后,会为生成 3 个引导性问题。", "Quote prompt": "引用模板提示词", @@ -561,8 +563,7 @@ "success": "开始同步" } }, - "training": { - } + "training": {} }, "data": { "Auxiliary Data": "辅助数据", @@ -900,6 +901,8 @@ "template": { "Ai chat": "AI 对话", "Ai chat intro": "AI 大模型对话", + "Ai Polish": "AI 回复润色", + "Ai Polish intro": "使用AI润色回复内容,衔接多回复节点的workflow以及并使回复风格统一。", "Assigned reply": "指定回复", "Assigned reply intro": "该模块可以直接回复一段指定的内容。常用于引导、提示。非字符串内容传入时,会转成字符串进行输出。", "Chat entrance": "对话入口", @@ -1501,4 +1504,4 @@ } } } -} +} \ No newline at end of file diff --git a/projects/app/src/components/core/module/Flow/components/modules/PolishSwitch.tsx b/projects/app/src/components/core/module/Flow/components/modules/PolishSwitch.tsx new file mode 100644 index 00000000000..dd41c2fa7db --- /dev/null +++ b/projects/app/src/components/core/module/Flow/components/modules/PolishSwitch.tsx @@ -0,0 +1,23 @@ +import MyIcon from '@fastgpt/web/components/common/Icon'; +import MyTooltip from '@/components/MyTooltip'; +import { QuestionOutlineIcon } from '@chakra-ui/icons'; +import { Box, Flex, Switch, type SwitchProps } from '@chakra-ui/react'; +import React from 'react'; +import { useTranslation } from 'next-i18next'; + +const PolishSwitch = (props: SwitchProps) => { + const { t } = useTranslation(); + return ( + + + {t('core.app.Ai Polish')} + + + + + + + ); +}; + +export default PolishSwitch; diff --git a/projects/app/src/components/core/module/Flow/components/nodes/NodeUserGuide.tsx b/projects/app/src/components/core/module/Flow/components/nodes/NodeUserGuide.tsx index d6038391325..4fbda37934e 100644 --- a/projects/app/src/components/core/module/Flow/components/nodes/NodeUserGuide.tsx +++ b/projects/app/src/components/core/module/Flow/components/nodes/NodeUserGuide.tsx @@ -14,6 +14,7 @@ import Container from '../modules/Container'; import NodeCard from '../render/NodeCard'; import type { VariableItemType } from '@fastgpt/global/core/module/type.d'; import QGSwitch from '@/components/core/module/Flow/components/modules/QGSwitch'; +import PolishSwitch from '@/components/core/module/Flow/components/modules/PolishSwitch'; import TTSSelect from '@/components/core/module/Flow/components/modules/TTSSelect'; import { splitGuideModule } from '@fastgpt/global/core/module/utils'; import { useTranslation } from 'next-i18next'; @@ -34,6 +35,9 @@ const NodeUserGuide = ({ data, selected }: NodeProps) => { + + + @@ -143,6 +147,35 @@ function QuestionGuide({ data }: { data: FlowModuleItemType }) { ); } +function Polish({ data }: { data: FlowModuleItemType }) { + const { inputs, moduleId } = data; + + const polish = useMemo( + () => + (inputs.find((item) => item.key === ModuleInputKeyEnum.polish)?.value as boolean) || false, + [inputs] + ); + + return ( + { + const value = e.target.checked; + onChangeNode({ + moduleId, + key: ModuleInputKeyEnum.polish, + type: 'updateInput', + value: { + ...inputs.find((item) => item.key === ModuleInputKeyEnum.polish), + value + } + }); + }} + /> + ); +} + function TTSGuide({ data }: { data: FlowModuleItemType }) { const { inputs, moduleId } = data; const { ttsConfig } = splitGuideModule({ inputs } as ModuleItemType); diff --git a/projects/app/src/global/core/app/constants.ts b/projects/app/src/global/core/app/constants.ts index a397ff99233..086792650a8 100644 --- a/projects/app/src/global/core/app/constants.ts +++ b/projects/app/src/global/core/app/constants.ts @@ -26,6 +26,7 @@ export const SimpleModeTemplate_FastGPT_Universal: AppSimpleEditConfigTemplateTy welcomeText: true, variables: true, questionGuide: true, + polish: true, tts: true } } diff --git a/projects/app/src/global/core/prompt/agent.ts b/projects/app/src/global/core/prompt/agent.ts index 5e8efa9ca8f..56253fc5fbe 100644 --- a/projects/app/src/global/core/prompt/agent.ts +++ b/projects/app/src/global/core/prompt/agent.ts @@ -60,3 +60,20 @@ Human:"{{question}}" `; export const Prompt_QuestionGuide = `我不太清楚问你什么问题,请帮我生成 3 个问题,引导我继续提问。问题的长度应小于20个字符,按 JSON 格式返回: ["问题1", "问题2", "问题3"]`; + +export const Prompt_AIPolish = `你需要将 <对话记录> 作为背景信息对 <本次回复> 进行润色改写,你仅需返回改写后的结果,无需回答问题。 +<润色要求> +1. 不能改变<本次回复>中的内容含义,只需要进行语言上的润色与修饰。 +2. 不要重复<对话记录>中已经出现过的内容。 +3. 允许使用Markdown,且不要修改<本次回复>中的Markdown。 +4. 请尽量保持<本次回复>中的内容顺序。 + + +<对话记录> +{{text}} + + +<本次回复> +"{{question}}" + +`; diff --git a/projects/app/src/pages/api/core/chat/chatTest.ts b/projects/app/src/pages/api/core/chat/chatTest.ts index bee667a46f8..d77303ce1a0 100644 --- a/projects/app/src/pages/api/core/chat/chatTest.ts +++ b/projects/app/src/pages/api/core/chat/chatTest.ts @@ -11,12 +11,15 @@ import { authApp } from '@fastgpt/service/support/permission/auth/app'; import { dispatchModules } from '@/service/moduleDispatch'; import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team'; +import { getGuideModule, splitGuideModule } from '@fastgpt/global/core/module/utils'; +import { aiPolish } from '@/service/events/aiPolish'; export type Props = { history: ChatItemType[]; prompt: string; modules: ModuleItemType[]; variables: Record; + polish: boolean; appId: string; appName: string; }; @@ -31,6 +34,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) }); let { modules = [], history = [], prompt, variables = {}, appName, appId } = req.body as Props; + + let { polish } = splitGuideModule(getGuideModule(modules)); try { await connectToDatabase(); if (!history || !modules || !prompt) { @@ -53,7 +58,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const { user } = await getUserChatInfoAndAuthTeamPoints(tmbId); /* start process */ - const { responseData, moduleDispatchBills } = await dispatchModules({ + const { responseData, answerText, moduleDispatchBills } = await dispatchModules({ res, mode: 'test', teamId, @@ -62,6 +67,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) appId, modules, variables, + polish, histories: history, startParams: { userChatInput: prompt @@ -70,13 +76,34 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) detail: true }); + // ai polish + if (answerText && polish) { + await aiPolish({ + res, + teamId, + tmbId, + user, + appId, + variables, + polish: false, + histories: history, + stream: true, + detail: true, + mode: 'test', + userChatInput: prompt, + answerText: answerText + }); + } + responseWrite({ res, + polish, event: sseResponseEventEnum.answer, data: '[DONE]' }); responseWrite({ res, + polish, event: sseResponseEventEnum.appStreamResponse, data: JSON.stringify(responseData) }); diff --git a/projects/app/src/pages/api/v1/chat/completions.ts b/projects/app/src/pages/api/v1/chat/completions.ts index 332a50ace68..2fd2c16f2e8 100644 --- a/projects/app/src/pages/api/v1/chat/completions.ts +++ b/projects/app/src/pages/api/v1/chat/completions.ts @@ -18,6 +18,7 @@ import { authOutLinkChatStart } from '@/service/support/permission/auth/outLink' import { pushResult2Remote, addOutLinkUsage } from '@fastgpt/service/support/outLink/tools'; import requestIp from 'request-ip'; import { getUsageSourceByAuthType } from '@fastgpt/global/support/wallet/usage/tools'; +import { getGuideModule, splitGuideModule } from '@fastgpt/global/core/module/utils'; import { authTeamSpaceToken } from '@/service/support/permission/auth/team'; import { selectSimpleChatResponse } from '@/utils/service/core/chat'; import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools'; @@ -31,6 +32,7 @@ import { AuthOutLinkChatProps } from '@fastgpt/global/support/outLink/api'; import { MongoChat } from '@fastgpt/service/core/chat/chatSchema'; import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat'; import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat'; +import { aiPolish } from '@/service/events/aiPolish'; type FastGptWebChatProps = { chatId?: string; // undefined: nonuse history, '': new chat, 'xxxxx': use history @@ -113,7 +115,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex throw new Error('Question is empty'); } - /* + /* 1. auth app permission 2. auth balance 3. get app @@ -150,6 +152,9 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex }); })(); + // get polish config + const { polish } = splitGuideModule(getGuideModule(app.modules)); + // get and concat history const { history } = await getChatItems({ appId: app._id, @@ -161,7 +166,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex const responseChatItemId: string | undefined = messages[messages.length - 1].dataId; /* start flow controller */ - const { responseData, moduleDispatchBills, answerText } = await dispatchModules({ + let { responseData, moduleDispatchBills, answerText } = await dispatchModules({ res, mode: 'chat', user, @@ -172,6 +177,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex responseChatItemId, modules: app.modules, variables, + polish, histories: concatHistories, startParams: { userChatInput: question.value @@ -180,6 +186,25 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex detail }); + // ai polish + if (answerText && polish) { + answerText = await aiPolish({ + res, + teamId: String(teamId), + tmbId: String(tmbId), + user, + appId: String(app._id), + variables, + polish: false, + histories: history, + stream: true, + detail: true, + mode: 'test', + userChatInput: question.value, + answerText: answerText + }); + } + // save chat if (chatId) { const isOwnerUse = !shareId && !spaceTeamId && String(tmbId) === String(app.tmbId); @@ -189,6 +214,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex teamId, tmbId: tmbId, variables, + polish: polish || false, updateUseTime: isOwnerUse, // owner update use time shareId, outLinkUid: outLinkUserId, @@ -227,6 +253,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex if (stream) { responseWrite({ res, + polish, event: detail ? sseResponseEventEnum.answer : undefined, data: textAdaptGptResponse({ text: null, @@ -235,6 +262,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex }); responseWrite({ res, + polish, event: detail ? sseResponseEventEnum.answer : undefined, data: '[DONE]' }); @@ -242,6 +270,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex if (responseDetail && detail) { responseWrite({ res, + polish, event: sseResponseEventEnum.appStreamResponse, data: JSON.stringify(feResponseData) }); diff --git a/projects/app/src/pages/app/detail/components/SimpleEdit/EditForm.tsx b/projects/app/src/pages/app/detail/components/SimpleEdit/EditForm.tsx index 08a89882901..6b651c2e9ae 100644 --- a/projects/app/src/pages/app/detail/components/SimpleEdit/EditForm.tsx +++ b/projects/app/src/pages/app/detail/components/SimpleEdit/EditForm.tsx @@ -35,6 +35,7 @@ import SelectAiModel from '@/components/Select/SelectAiModel'; import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor'; import { formatEditorVariablePickerIcon } from '@fastgpt/global/core/module/utils'; import SearchParamsTip from '@/components/core/dataset/SearchParamsTip'; +import PolishSwitch from '@/components/core/module/Flow/components/modules/PolishSwitch'; const DatasetSelectModal = dynamic(() => import('@/components/core/module/DatasetSelectModal')); const DatasetParamsModal = dynamic(() => import('@/components/core/module/DatasetParamsModal')); @@ -402,6 +403,19 @@ const EditForm = ({ }} /> + + {/* polish guide */} + + { + const value = e.target.checked; + setValue('userGuide.polish', value); + setRefresh((state) => !state); + }} + /> + diff --git a/projects/app/src/service/events/aiPolish.ts b/projects/app/src/service/events/aiPolish.ts new file mode 100644 index 00000000000..4cf45baf759 --- /dev/null +++ b/projects/app/src/service/events/aiPolish.ts @@ -0,0 +1,68 @@ +import { dispatchChatCompletion } from '../moduleDispatch/chat/oneapi'; +import { Prompt_AIPolish } from '@/global/core/prompt/agent'; +import { ChatDispatchProps } from '@fastgpt/global/core/module/type'; +import { ModuleOutputKeyEnum, ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants'; +import { replaceVariable } from '@fastgpt/global/common/string/tools'; + +export async function aiPolish({ + res, + mode, + teamId, + tmbId, + user, + appId, + responseChatItemId, + histories, + variables, + polish, + stream, + detail, + userChatInput, + answerText +}: ChatDispatchProps & { + [ModuleInputKeyEnum.userChatInput]: string; + [ModuleOutputKeyEnum.answerText]: string; +}) { + const polishModel = global.llmModels[0].model; + + userChatInput = replaceVariable(Prompt_AIPolish, { + text: `${histories.map((item) => `${item.obj}:${item.value}`).join('\n')} +Human: ${userChatInput}`, + question: answerText + }); + + const { answerText: polishedAnswerText } = await dispatchChatCompletion({ + res, + stream, + detail, + user, + histories, + polish, + module: { + name: 'core.modulte.template.Ai polish', + moduleId: '', // Add the moduleId property + flowType: 'userGuide', // Add the flowType property + inputs: [], + outputs: [] + }, + params: { + model: polishModel, + temperature: 0, + maxToken: 4000, + history: 6, + quoteQA: [], + userChatInput, + isResponseAnswerText: true, + systemPrompt: '', + quoteTemplate: '', + quotePrompt: '' + }, + mode: mode, + teamId: teamId, + tmbId: tmbId, + appId: appId, + variables: variables + }); + + return polishedAnswerText; +} diff --git a/projects/app/src/service/moduleDispatch/chat/oneapi.ts b/projects/app/src/service/moduleDispatch/chat/oneapi.ts index b074b46394e..8ab3efa1439 100644 --- a/projects/app/src/service/moduleDispatch/chat/oneapi.ts +++ b/projects/app/src/service/moduleDispatch/chat/oneapi.ts @@ -45,6 +45,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise): AnswerResponse => { res, detail, stream, + polish, params: { text = '' } } = props as AnswerProps; @@ -23,6 +24,7 @@ export const dispatchAnswer = (props: Record): AnswerResponse => { if (stream) { responseWrite({ res, + polish, event: detail ? sseResponseEventEnum.response : undefined, data: textAdaptGptResponse({ text: `\n${formatText}` diff --git a/projects/app/src/service/moduleDispatch/tools/runApp.ts b/projects/app/src/service/moduleDispatch/tools/runApp.ts index 2e60fb4d820..9dc02cb677b 100644 --- a/projects/app/src/service/moduleDispatch/tools/runApp.ts +++ b/projects/app/src/service/moduleDispatch/tools/runApp.ts @@ -28,6 +28,7 @@ export const dispatchAppRequest = async (props: Props): Promise => { res, teamId, stream, + polish, detail, histories, params: { userChatInput, history, app } @@ -50,6 +51,7 @@ export const dispatchAppRequest = async (props: Props): Promise => { if (stream) { responseWrite({ res, + polish, event: detail ? sseResponseEventEnum.answer : undefined, data: textAdaptGptResponse({ text: '\n' diff --git a/projects/app/src/service/utils/chat/saveChat.ts b/projects/app/src/service/utils/chat/saveChat.ts index 7ddf50abb74..01c24e79bc3 100644 --- a/projects/app/src/service/utils/chat/saveChat.ts +++ b/projects/app/src/service/utils/chat/saveChat.ts @@ -13,6 +13,7 @@ type Props = { teamId: string; tmbId: string; variables?: Record; + polish?: boolean; updateUseTime: boolean; source: `${ChatSourceEnum}`; shareId?: string; @@ -27,6 +28,7 @@ export async function saveChat({ teamId, tmbId, variables, + polish, updateUseTime, source, shareId, @@ -78,6 +80,7 @@ export async function saveChat({ tmbId, appId, variables, + polish, title, source, shareId, diff --git a/projects/app/src/web/core/app/templates.ts b/projects/app/src/web/core/app/templates.ts index 98b73bb2000..24520e9d5f6 100644 --- a/projects/app/src/web/core/app/templates.ts +++ b/projects/app/src/web/core/app/templates.ts @@ -52,6 +52,16 @@ export const appTemplates: (AppItemType & { showTargetInPlugin: false, connected: false }, + { + key: 'polish', + valueType: 'boolean', + value: true, + type: 'switch', + label: '', + showTargetInApp: false, + showTargetInPlugin: false, + connected: false + }, { key: 'tts', type: 'hidden', @@ -336,6 +346,15 @@ export const appTemplates: (AppItemType & { value: false, connected: false }, + { + key: 'polish', + valueType: 'boolean', + type: 'switch', + label: '', + showTargetInApp: false, + showTargetInPlugin: false, + connected: false + }, { key: 'tts', type: 'hidden', @@ -813,6 +832,15 @@ export const appTemplates: (AppItemType & { value: false, connected: false }, + { + key: 'polish', + valueType: 'boolean', + type: 'switch', + label: '', + showTargetInApp: false, + showTargetInPlugin: false, + connected: false + }, { key: 'tts', type: 'hidden', @@ -1709,6 +1737,15 @@ export const appTemplates: (AppItemType & { showTargetInPlugin: false, connected: false }, + { + key: 'polish', + valueType: 'boolean', + type: 'switch', + label: '', + showTargetInApp: false, + showTargetInPlugin: false, + connected: false + }, { key: 'tts', type: 'hidden', diff --git a/projects/app/src/web/core/app/utils.ts b/projects/app/src/web/core/app/utils.ts index 7ea0776f7cd..057585e0c39 100644 --- a/projects/app/src/web/core/app/utils.ts +++ b/projects/app/src/web/core/app/utils.ts @@ -32,6 +32,12 @@ export async function postForm2Modules(data: AppSimpleEditFormType) { label: 'core.app.Question Guide', value: formData.userGuide.questionGuide }, + { + key: ModuleInputKeyEnum.polish, + type: FlowNodeInputTypeEnum.hidden, + label: 'core.app.Question Guide', + value: formData.userGuide.polish + }, { key: ModuleInputKeyEnum.tts, type: FlowNodeInputTypeEnum.hidden,