From 93cfef8aa9599bc291a296318c12a4e0b3baeaff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E4=B8=B0=E7=91=9E?= Date: Thu, 29 Feb 2024 15:54:55 +0800 Subject: [PATCH 1/4] Add polish flag to ChatDispatchProps and AppSimpleEditFormType --- packages/global/core/app/type.d.ts | 2 + packages/global/core/app/utils.ts | 4 +- packages/global/core/module/constants.ts | 1 + packages/global/core/module/type.d.ts | 1 + packages/global/core/module/utils.ts | 4 ++ packages/service/common/response/index.ts | 7 ++- .../web/components/common/Icon/constants.ts | 1 + .../common/Icon/icons/core/app/polish.svg | 1 + .../data/simpleTemplates/fastgpt-simple.json | 3 +- projects/app/public/locales/zh/common.json | 4 +- .../Flow/components/modules/PolishSwitch.tsx | 23 +++++++ .../Flow/components/nodes/NodeUserGuide.tsx | 33 ++++++++++ projects/app/src/global/core/app/constants.ts | 1 + projects/app/src/global/core/prompt/agent.ts | 17 +++++ .../app/src/pages/api/core/chat/chatTest.ts | 29 ++++++++- .../app/src/pages/api/v1/chat/completions.ts | 33 +++++++++- .../detail/components/SimpleEdit/EditForm.tsx | 14 +++++ projects/app/src/service/events/aiPolish.ts | 63 +++++++++++++++++++ .../src/service/moduleDispatch/chat/oneapi.ts | 10 ++- .../app/src/service/moduleDispatch/index.ts | 5 ++ .../service/moduleDispatch/tools/answer.ts | 2 + .../service/moduleDispatch/tools/runApp.ts | 2 + .../app/src/service/utils/chat/saveChat.ts | 3 + projects/app/src/web/core/app/templates.ts | 37 +++++++++++ projects/app/src/web/core/app/utils.ts | 6 ++ 25 files changed, 298 insertions(+), 8 deletions(-) create mode 100644 packages/web/components/common/Icon/icons/core/app/polish.svg create mode 100644 projects/app/src/components/core/module/Flow/components/modules/PolishSwitch.tsx create mode 100644 projects/app/src/service/events/aiPolish.ts diff --git a/packages/global/core/app/type.d.ts b/packages/global/core/app/type.d.ts index 6bba1ad288..2131d70c17 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 f8b24f219e..624506844a 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 16098f51f1..83f5bd37f8 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 7ffc5bbfe3..5b8f7a5c98 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 880946f55f..a1afc0854a 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 feb4fa183e..746f9aaf83 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,8 @@ export function responseWrite({ if (!Write) return; event && Write(`event: ${event}\n`); - Write(`data: ${data}\n\n`); + + if (!polish || event !== sseResponseEventEnum.response) { + Write(`data: ${data}\n\n`); + } } diff --git a/packages/web/components/common/Icon/constants.ts b/packages/web/components/common/Icon/constants.ts index 2417e7e698..99e2448e6b 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 0000000000..16d0c88591 --- /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 cc18023bb8..7301bf5f99 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/zh/common.json b/projects/app/public/locales/zh/common.json index 19990533fe..bdba96c924 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": "引用模板提示词", @@ -1501,4 +1503,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 0000000000..dd41c2fa7d --- /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 d603839132..4fbda37934 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 a397ff9923..086792650a 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 5e8efa9ca8..56253fc5fb 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 bee667a46f..d77303ce1a 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 332a50ace6..2fd2c16f2e 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 08a8988290..6b651c2e9a 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 0000000000..8e53cf8f9e --- /dev/null +++ b/projects/app/src/service/events/aiPolish.ts @@ -0,0 +1,63 @@ +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, + 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, + outputs: [], + inputs: [] + }); + + return polishedAnswerText; +} diff --git a/projects/app/src/service/moduleDispatch/chat/oneapi.ts b/projects/app/src/service/moduleDispatch/chat/oneapi.ts index b074b46394..8ab3efa143 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 2e60fb4d82..9dc02cb677 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 7ddf50abb7..01c24e79bc 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 98b73bb200..24520e9d5f 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 7ea0776f7c..057585e0c3 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, From 2571a8059c6dedc5f2e43c5250bb289595e2640a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E4=B8=B0=E7=91=9E?= Date: Thu, 29 Feb 2024 16:51:27 +0800 Subject: [PATCH 2/4] Fixes AI Polish feature chat inputs --- projects/app/public/locales/en/common.json | 4 +++- projects/app/public/locales/zh/common.json | 5 +++-- projects/app/src/service/events/aiPolish.ts | 11 ++++++++--- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/projects/app/public/locales/en/common.json b/projects/app/public/locales/en/common.json index 162667aac4..f17ab0d0e9 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 bdba96c924..bba143953c 100644 --- a/projects/app/public/locales/zh/common.json +++ b/projects/app/public/locales/zh/common.json @@ -563,8 +563,7 @@ "success": "开始同步" } }, - "training": { - } + "training": {} }, "data": { "Auxiliary Data": "辅助数据", @@ -902,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": "对话入口", diff --git a/projects/app/src/service/events/aiPolish.ts b/projects/app/src/service/events/aiPolish.ts index 8e53cf8f9e..4cf45baf75 100644 --- a/projects/app/src/service/events/aiPolish.ts +++ b/projects/app/src/service/events/aiPolish.ts @@ -38,6 +38,13 @@ Human: ${userChatInput}`, 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, @@ -54,9 +61,7 @@ Human: ${userChatInput}`, teamId: teamId, tmbId: tmbId, appId: appId, - variables: variables, - outputs: [], - inputs: [] + variables: variables }); return polishedAnswerText; From e98dd7c0fa024631a88f77153490719f9cb4de7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E4=B8=B0=E7=91=9E?= Date: Thu, 29 Feb 2024 17:48:14 +0800 Subject: [PATCH 3/4] Add AI Polish & workflow documentation --- docSite/assets/imgs/aiPolish.png | Bin 0 -> 75438 bytes docSite/content/docs/workflow/intro.md | 8 +++--- .../docs/workflow/modules/ai_polish.md | 26 ++++++++++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 docSite/assets/imgs/aiPolish.png create mode 100644 docSite/content/docs/workflow/modules/ai_polish.md diff --git a/docSite/assets/imgs/aiPolish.png b/docSite/assets/imgs/aiPolish.png new file mode 100644 index 0000000000000000000000000000000000000000..385201ec19ec2390e932af46a642c0fc893c5048 GIT binary patch literal 75438 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 k_&*#H0l literal 0 HcmV?d00001 diff --git a/docSite/content/docs/workflow/intro.md b/docSite/content/docs/workflow/intro.md index a815e42c57..d1b57ffcaf 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 0000000000..3b0541b23b --- /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 From 54d4c6fa2f1ed748ae90cbace19c87abff5c6089 Mon Sep 17 00:00:00 2001 From: Fengrui Liu Date: Wed, 6 Mar 2024 21:39:39 +0800 Subject: [PATCH 4/4] bug fixes of polish logic --- packages/service/common/response/index.ts | 5 ++++- projects/app/src/service/moduleDispatch/index.ts | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/service/common/response/index.ts b/packages/service/common/response/index.ts index 746f9aaf83..62b39b2268 100644 --- a/packages/service/common/response/index.ts +++ b/packages/service/common/response/index.ts @@ -133,7 +133,10 @@ export function responseWrite({ event && Write(`event: ${event}\n`); - if (!polish || event !== sseResponseEventEnum.response) { + if ( + !polish || + (event !== sseResponseEventEnum.response && event !== sseResponseEventEnum.answer) + ) { Write(`data: ${data}\n\n`); } } diff --git a/projects/app/src/service/moduleDispatch/index.ts b/projects/app/src/service/moduleDispatch/index.ts index 343da6ca21..ef7091ac90 100644 --- a/projects/app/src/service/moduleDispatch/index.ts +++ b/projects/app/src/service/moduleDispatch/index.ts @@ -188,6 +188,7 @@ export async function dispatchModules({ if (stream && detail && module.showStatus) { responseStatus({ res, + polish, name: module.name, status: 'running' });