Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Use AI to polish the response of the conversation. #908

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Binary file added docSite/assets/imgs/aiPolish.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions docSite/content/docs/workflow/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ FastGPT 从 V4 版本开始采用新的交互方式来构建 AI 应用。使用
每个模块会包含 3 个核心部分:固定参数、外部输入(左边有个圆圈)和输出(右边有个圆圈)。

![](/imgs/flow-intro3.png)

- 对话模型、温度、回复上限、系统提示词和限定词为固定参数,同时系统提示词和限定词也可以作为外部输入,意味着如果你有输入流向了系统提示词,那么原本填写的内容就会被**覆盖**。
- 触发器、引用内容、聊天记录和用户问题则为外部输入,需要从其他模块的输出流入。
- 回复结束则为该模块的输出。
Expand Down Expand Up @@ -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个用户问题

左侧的`用户问题`是指该模块所需的输入。右侧的`用户问题`是为了方便后续的连线,输出的值和传入的用户问题一样。

26 changes: 26 additions & 0 deletions docSite/content/docs/workflow/modules/ai_polish.md
Original file line number Diff line number Diff line change
@@ -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中只有一个输出节点时,建议将此功能关闭。
2 changes: 2 additions & 0 deletions packages/global/core/app/type.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export type AppSimpleEditFormType = {
}[];
}[];
questionGuide: boolean;
polish: boolean;
tts: {
type: 'none' | 'web' | 'model';
model?: string | undefined;
Expand Down Expand Up @@ -121,6 +122,7 @@ export type AppSimpleEditConfigTemplateType = {
welcomeText?: boolean;
variables?: boolean;
questionGuide?: boolean;
polish?: boolean;
tts?: boolean;
};
};
Expand Down
4 changes: 3 additions & 1 deletion packages/global/core/app/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const getDefaultAppForm = (): AppSimpleEditFormType => {
welcomeText: '',
variables: [],
questionGuide: false,
polish: true,
tts: {
type: 'web'
}
Expand Down Expand Up @@ -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
};
}
Expand Down
1 change: 1 addition & 0 deletions packages/global/core/module/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export enum ModuleInputKeyEnum {
history = 'history',
userChatInput = 'userChatInput',
questionGuide = 'questionGuide',
polish = 'polish',
tts = 'tts',
answerText = 'text',
agents = 'agents', // cq agent key
Expand Down
1 change: 1 addition & 0 deletions packages/global/core/module/type.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export type ChatDispatchProps = {
responseChatItemId?: string;
histories: ChatItemType[];
variables: Record<string, any>;
polish: boolean;
stream: boolean;
detail: boolean; // response detail
};
Expand Down
4 changes: 4 additions & 0 deletions packages/global/core/module/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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' };
Expand All @@ -32,6 +35,7 @@ export const splitGuideModule = (guideModules?: ModuleItemType) => {
welcomeText,
variableModules,
questionGuide,
polish,
ttsConfig
};
};
Expand Down
10 changes: 9 additions & 1 deletion packages/service/common/response/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}) {
Expand All @@ -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`);
}
}
1 change: 1 addition & 0 deletions packages/web/components/common/Icon/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion projects/app/data/simpleTemplates/fastgpt-simple.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"welcomeText": true,
"variables": false,
"questionGuide": false,
"polish": true,
"tts": true
}
}
}
}
4 changes: 3 additions & 1 deletion projects/app/public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -1499,4 +1501,4 @@
}
}
}
}
}
9 changes: 6 additions & 3 deletions projects/app/public/locales/zh/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "引用模板提示词",
Expand Down Expand Up @@ -561,8 +563,7 @@
"success": "开始同步"
}
},
"training": {
}
"training": {}
},
"data": {
"Auxiliary Data": "辅助数据",
Expand Down Expand Up @@ -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": "对话入口",
Expand Down Expand Up @@ -1501,4 +1504,4 @@
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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 (
<Flex alignItems={'center'}>
<MyIcon name={'core/app/polish'} mr={2} w={'20px'} />
<Box>{t('core.app.Ai Polish')}</Box>
<MyTooltip label={t('core.app.Ai Polish Tip')} forceShow>
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
</MyTooltip>
<Box flex={1} />
<Switch {...props} />
</Flex>
);
};

export default PolishSwitch;
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -34,6 +35,9 @@ const NodeUserGuide = ({ data, selected }: NodeProps<FlowModuleItemType>) => {
<Box mt={3} pt={3} borderTop={theme.borders.base}>
<QuestionGuide data={data} />
</Box>
<Box mt={3} pt={3} borderTop={theme.borders.base}>
<Polish data={data} />
</Box>
</Container>
</NodeCard>
</>
Expand Down Expand Up @@ -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 (
<PolishSwitch
isChecked={polish}
size={'lg'}
onChange={(e) => {
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);
Expand Down
1 change: 1 addition & 0 deletions projects/app/src/global/core/app/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const SimpleModeTemplate_FastGPT_Universal: AppSimpleEditConfigTemplateTy
welcomeText: true,
variables: true,
questionGuide: true,
polish: true,
tts: true
}
}
Expand Down
17 changes: 17 additions & 0 deletions projects/app/src/global/core/prompt/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}}"
</本次回复>
`;