Commit 06be5781 by Archer Committed by GitHub

v4.4.3 (#316)

parent 81e37a57
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
"UnKnow": "UnKnow", "UnKnow": "UnKnow",
"Warning": "Warning", "Warning": "Warning",
"app": { "app": {
"Quote Prompt Settings": "Quote Prompt Settings",
"Advance App TestTip": "The current application is advanced editing mode \n. If you need to switch to [simple mode], please click the save button on the left", "Advance App TestTip": "The current application is advanced editing mode \n. If you need to switch to [simple mode], please click the save button on the left",
"App Detail": "App Detail", "App Detail": "App Detail",
"Chat Logs Tips": "Logs record the app's online, shared, and API(chatId is existing) conversations", "Chat Logs Tips": "Logs record the app's online, shared, and API(chatId is existing) conversations",
...@@ -219,6 +220,10 @@ ...@@ -219,6 +220,10 @@
"system": { "system": {
"Help Document": "Document" "Help Document": "Document"
}, },
"template": {
"Quote Content Tip": "This configuration takes effect only when reference content is passed in (knowledge base search). You can customize the structure of the reference content to better fit different scenarios. You can use {{q}}, {{a}}, {{source}} as \"search content\", \"expected content\", and \"source\", they are all optional, and here are the default values: \n{instruction:\"{{q}}\",output:\"{{a}}\"}",
"Quote Prompt Tip": "This configuration takes effect only when reference content is passed in (knowledge base search). \n You can insert references with {{quote}}, here are the default values: \n\"\"\"{{quote}}\"\"\" The three quotes are the knowledge base I gave you, they have the highest priority. instruction is a relevant introduction and output is an expected answer or supplement."
},
"user": { "user": {
"Account": "Account", "Account": "Account",
"Amount of earnings": "Earnings", "Amount of earnings": "Earnings",
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
"UnKnow": "未知", "UnKnow": "未知",
"Warning": "提示", "Warning": "提示",
"app": { "app": {
"Quote Prompt Settings": "引用提示词配置",
"Advance App TestTip": "当前应用为高级编排模式\n如需切换为【简易模式】请点击左侧保存按键", "Advance App TestTip": "当前应用为高级编排模式\n如需切换为【简易模式】请点击左侧保存按键",
"App Detail": "应用详情", "App Detail": "应用详情",
"Chat Logs Tips": "日志会记录该应用的在线、分享和 API(需填写 chatId) 对话记录", "Chat Logs Tips": "日志会记录该应用的在线、分享和 API(需填写 chatId) 对话记录",
...@@ -219,6 +220,10 @@ ...@@ -219,6 +220,10 @@
"system": { "system": {
"Help Document": "帮助文档" "Help Document": "帮助文档"
}, },
"template": {
"Quote Content Tip": "该配置只有传入引用内容(知识库搜索)时生效。\n可以自定义引用内容的结构,以更好的适配不同场景。可以使用 {{q}}, {{a}}, {{source}} 来作为 “检索内容”、“预期内容”和“来源”,他们都是可选的,下面是默认值:\n{{default}}",
"Quote Prompt Tip": "该配置只有传入引用内容(知识库搜索)时生效。\n可以用 {{quote}} 来插入引用内容,使用 {{question}} 来插入问题。下面是默认值:\n{{default}}"
},
"user": { "user": {
"Account": "账号", "Account": "账号",
"Amount of earnings": "收益(¥)", "Amount of earnings": "收益(¥)",
......
...@@ -613,11 +613,6 @@ const ChatBox = ( ...@@ -613,11 +613,6 @@ const ChatBox = (
flexDirection={'column'} flexDirection={'column'}
alignItems={item.obj === 'Human' ? 'flex-end' : 'flex-start'} alignItems={item.obj === 'Human' ? 'flex-end' : 'flex-start'}
py={5} py={5}
_hover={{
'& .control': {
display: item.status === 'finish' ? 'flex' : 'none'
}
}}
> >
{item.obj === 'Human' && ( {item.obj === 'Human' && (
<> <>
......
...@@ -10,11 +10,10 @@ import { ...@@ -10,11 +10,10 @@ import {
} from '@chakra-ui/react'; } from '@chakra-ui/react';
interface Props extends ModalContentProps { interface Props extends ModalContentProps {
showCloseBtn?: boolean;
title?: any; title?: any;
isCentered?: boolean; isCentered?: boolean;
isOpen: boolean; isOpen: boolean;
onClose: () => void; onClose?: () => void;
} }
const MyModal = ({ const MyModal = ({
...@@ -22,14 +21,18 @@ const MyModal = ({ ...@@ -22,14 +21,18 @@ const MyModal = ({
onClose, onClose,
title, title,
children, children,
showCloseBtn = true,
isCentered, isCentered,
w = 'auto', w = 'auto',
maxW = ['90vw', '600px'], maxW = ['90vw', '600px'],
...props ...props
}: Props) => { }: Props) => {
return ( return (
<Modal isOpen={isOpen} onClose={onClose} autoFocus={false} isCentered={isCentered}> <Modal
isOpen={isOpen}
onClose={() => onClose && onClose()}
autoFocus={false}
isCentered={isCentered}
>
<ModalOverlay /> <ModalOverlay />
<ModalContent <ModalContent
display={'flex'} display={'flex'}
...@@ -43,7 +46,7 @@ const MyModal = ({ ...@@ -43,7 +46,7 @@ const MyModal = ({
> >
{!!title && <ModalHeader>{title}</ModalHeader>} {!!title && <ModalHeader>{title}</ModalHeader>}
<Box overflow={'overlay'} h={'100%'}> <Box overflow={'overlay'} h={'100%'}>
{showCloseBtn && <ModalCloseButton />} {onClose && <ModalCloseButton />}
{children} {children}
</Box> </Box>
</ModalContent> </ModalContent>
......
...@@ -163,19 +163,23 @@ export const ChatModule: FlowModuleTemplateType = { ...@@ -163,19 +163,23 @@ export const ChatModule: FlowModuleTemplateType = {
value: '' value: ''
}, },
{ {
key: 'limitPrompt', key: 'quoteTemplate',
type: FlowInputItemTypeEnum.textarea, type: FlowInputItemTypeEnum.hidden,
label: '引用内容模板',
valueType: FlowValueTypeEnum.string,
value: ''
},
{
key: 'quotePrompt',
type: FlowInputItemTypeEnum.hidden,
label: '引用内容提示词',
valueType: FlowValueTypeEnum.string, valueType: FlowValueTypeEnum.string,
label: '限定词',
max: 500,
description: ChatModelLimitTip,
placeholder: ChatModelLimitTip,
value: '' value: ''
}, },
Input_Template_TFSwitch, Input_Template_TFSwitch,
{ {
key: 'quoteQA', key: 'quoteQA',
type: FlowInputItemTypeEnum.target, type: FlowInputItemTypeEnum.custom,
label: '引用内容', label: '引用内容',
description: "对象数组格式,结构:\n [{q:'问题',a:'回答'}]", description: "对象数组格式,结构:\n [{q:'问题',a:'回答'}]",
valueType: FlowValueTypeEnum.kbQuote valueType: FlowValueTypeEnum.kbQuote
...@@ -665,19 +669,6 @@ export const appTemplates: (AppItemType & { avatar: string; intro: string })[] = ...@@ -665,19 +669,6 @@ export const appTemplates: (AppItemType & { avatar: string; intro: string })[] =
connected: true connected: true
}, },
{ {
key: 'limitPrompt',
type: 'textarea',
valueType: 'string',
label: '限定词',
max: 500,
description:
'限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。可使用变量,例如 {{language}}。引导例子:\n1. 知识库是关于 Laf 的介绍,参考知识库回答问题,与 "Laf" 无关内容,直接回复: "我不知道"。\n2. 你仅回答关于 "xxx" 的问题,其他问题回复: "xxxx"',
placeholder:
'限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。可使用变量,例如 {{language}}。引导例子:\n1. 知识库是关于 Laf 的介绍,参考知识库回答问题,与 "Laf" 无关内容,直接回复: "我不知道"。\n2. 你仅回答关于 "xxx" 的问题,其他问题回复: "xxxx"',
value: '',
connected: true
},
{
key: 'switch', key: 'switch',
type: 'target', type: 'target',
label: '触发器', label: '触发器',
...@@ -1014,18 +1005,6 @@ export const appTemplates: (AppItemType & { avatar: string; intro: string })[] = ...@@ -1014,18 +1005,6 @@ export const appTemplates: (AppItemType & { avatar: string; intro: string })[] =
connected: true connected: true
}, },
{ {
key: 'limitPrompt',
type: 'textarea',
valueType: 'string',
label: '限定词',
description:
'限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。可使用变量,例如 {{language}}。引导例子:\n1. 知识库是关于 Laf 的介绍,参考知识库回答问题,与 "Laf" 无关内容,直接回复: "我不知道"。\n2. 你仅回答关于 "xxx" 的问题,其他问题回复: "xxxx"',
placeholder:
'限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。可使用变量,例如 {{language}}。引导例子:\n1. 知识库是关于 Laf 的介绍,参考知识库回答问题,与 "Laf" 无关内容,直接回复: "我不知道"。\n2. 你仅回答关于 "xxx" 的问题,其他问题回复: "xxxx"',
value: '',
connected: true
},
{
key: 'switch', key: 'switch',
type: 'target', type: 'target',
label: '触发器', label: '触发器',
...@@ -1320,18 +1299,6 @@ export const appTemplates: (AppItemType & { avatar: string; intro: string })[] = ...@@ -1320,18 +1299,6 @@ export const appTemplates: (AppItemType & { avatar: string; intro: string })[] =
connected: true connected: true
}, },
{ {
key: 'limitPrompt',
type: 'textarea',
valueType: 'string',
label: '限定词',
description:
'限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。可使用变量,例如 {{language}}。引导例子:\n1. 知识库是关于 Laf 的介绍,参考知识库回答问题,与 "Laf" 无关内容,直接回复: "我不知道"。\n2. 你仅回答关于 "xxx" 的问题,其他问题回复: "xxxx"',
placeholder:
'限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。可使用变量,例如 {{language}}。引导例子:\n1. 知识库是关于 Laf 的介绍,参考知识库回答问题,与 "Laf" 无关内容,直接回复: "我不知道"。\n2. 你仅回答关于 "xxx" 的问题,其他问题回复: "xxxx"',
value: '将我的问题直接翻译成英语{{language}}',
connected: true
},
{
key: 'switch', key: 'switch',
type: 'target', type: 'target',
label: '触发器', label: '触发器',
...@@ -1704,19 +1671,6 @@ export const appTemplates: (AppItemType & { avatar: string; intro: string })[] = ...@@ -1704,19 +1671,6 @@ export const appTemplates: (AppItemType & { avatar: string; intro: string })[] =
connected: true connected: true
}, },
{ {
key: 'limitPrompt',
type: 'textarea',
valueType: 'string',
label: '限定词',
description:
'限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。可使用变量,例如 {{language}}。引导例子:\n1. 知识库是关于 Laf 的介绍,参考知识库回答问题,与 "Laf" 无关内容,直接回复: "我不知道"。\n2. 你仅回答关于 "xxx" 的问题,其他问题回复: "xxxx"',
placeholder:
'限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。可使用变量,例如 {{language}}。引导例子:\n1. 知识库是关于 Laf 的介绍,参考知识库回答问题,与 "Laf" 无关内容,直接回复: "我不知道"。\n2. 你仅回答关于 "xxx" 的问题,其他问题回复: "xxxx"',
value:
'我的问题都是关于 laf 的。根据知识库回答我的问题,与 laf 无关问题,直接回复:“我不清楚,我仅能回答 laf 相关的问题。”。',
connected: true
},
{
key: 'switch', key: 'switch',
type: 'target', type: 'target',
label: '触发器', label: '触发器',
......
...@@ -64,16 +64,7 @@ const PayModal = ({ onClose }: { onClose: () => void }) => { ...@@ -64,16 +64,7 @@ const PayModal = ({ onClose }: { onClose: () => void }) => {
); );
return ( return (
<MyModal <MyModal isOpen={true} onClose={payId ? onClose : undefined} title={t('user.Pay')} isCentered>
isOpen={true}
onClose={() => {
if (payId) return;
onClose();
}}
title={t('user.Pay')}
isCentered
showCloseBtn={!payId}
>
<ModalBody py={0}> <ModalBody py={0}>
{!payId && ( {!payId && (
<> <>
......
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { authUser } from '@/service/utils/auth';
import { connectToDatabase, App } from '@/service/mongo';
import { FlowModuleTypeEnum, SpecialInputKeyEnum } from '@/constants/flow';
import { TaskResponseKeyEnum } from '@/constants/chat';
import { FlowInputItemType } from '@/types/flow';
const chatModelInput = ({
model,
temperature,
maxToken,
systemPrompt,
limitPrompt,
kbList
}: {
model: string;
temperature: number;
maxToken: number;
systemPrompt: string;
limitPrompt: string;
kbList: { kbId: string }[];
}): FlowInputItemType[] => [
{
key: 'model',
value: model,
type: 'custom',
label: '对话模型',
connected: true
},
{
key: 'temperature',
value: temperature,
label: '温度',
type: 'slider',
connected: true
},
{
key: 'maxToken',
value: maxToken,
type: 'custom',
label: '回复上限',
connected: true
},
{
key: 'systemPrompt',
value: systemPrompt,
type: 'textarea',
label: '系统提示词',
connected: true
},
{
key: 'limitPrompt',
label: '限定词',
type: 'textarea',
value: limitPrompt,
connected: true
},
{
key: 'switch',
type: 'target',
label: '触发器',
connected: kbList.length > 0
},
{
key: 'quoteQA',
type: 'target',
label: '引用内容',
connected: kbList.length > 0
},
{
key: 'history',
type: 'target',
label: '聊天记录',
connected: true
},
{
key: 'userChatInput',
type: 'target',
label: '用户问题',
connected: true
}
];
const chatTemplate = ({
model,
temperature,
maxToken,
systemPrompt,
limitPrompt
}: {
model: string;
temperature: number;
maxToken: number;
systemPrompt: string;
limitPrompt: string;
}) => {
return [
{
flowType: FlowModuleTypeEnum.questionInput,
inputs: [
{
key: 'userChatInput',
connected: true
}
],
outputs: [
{
key: 'userChatInput',
targets: [
{
moduleId: 'chatModule',
key: 'userChatInput'
}
]
}
],
position: {
x: 464.32198615344566,
y: 1602.2698463081606
},
moduleId: 'userChatInput'
},
{
flowType: FlowModuleTypeEnum.historyNode,
inputs: [
{
key: 'maxContext',
value: 10,
connected: true
},
{
key: 'history',
connected: true
}
],
outputs: [
{
key: 'history',
targets: [
{
moduleId: 'chatModule',
key: 'history'
}
]
}
],
position: {
x: 452.5466249541586,
y: 1276.3930310334215
},
moduleId: 'history'
},
{
flowType: FlowModuleTypeEnum.chatNode,
inputs: chatModelInput({
model,
temperature,
maxToken,
systemPrompt,
limitPrompt,
kbList: []
}),
outputs: [
{
key: TaskResponseKeyEnum.answerText,
targets: []
}
],
position: {
x: 981.9682828103937,
y: 890.014595014464
},
moduleId: 'chatModule'
}
];
};
const kbTemplate = ({
model,
temperature,
maxToken,
systemPrompt,
limitPrompt,
kbList = [],
searchSimilarity,
searchLimit,
searchEmptyText
}: {
model: string;
temperature: number;
maxToken: number;
systemPrompt: string;
limitPrompt: string;
kbList: { kbId: string }[];
searchSimilarity: number;
searchLimit: number;
searchEmptyText: string;
}) => {
return [
{
flowType: FlowModuleTypeEnum.questionInput,
inputs: [
{
key: 'userChatInput',
connected: true
}
],
outputs: [
{
key: 'userChatInput',
targets: [
{
moduleId: 'chatModule',
key: 'userChatInput'
},
{
moduleId: 'kbSearch',
key: 'userChatInput'
}
]
}
],
position: {
x: 464.32198615344566,
y: 1602.2698463081606
},
moduleId: 'userChatInput'
},
{
flowType: FlowModuleTypeEnum.historyNode,
inputs: [
{
key: 'maxContext',
value: 10,
connected: true
},
{
key: 'history',
connected: true
}
],
outputs: [
{
key: 'history',
targets: [
{
moduleId: 'chatModule',
key: 'history'
}
]
}
],
position: {
x: 452.5466249541586,
y: 1276.3930310334215
},
moduleId: 'history'
},
{
flowType: FlowModuleTypeEnum.kbSearchNode,
inputs: [
{
key: 'kbList',
value: kbList,
connected: true
},
{
key: 'similarity',
value: searchSimilarity,
connected: true
},
{
key: 'limit',
value: searchLimit,
connected: true
},
{
key: 'switch',
connected: false
},
{
key: 'userChatInput',
connected: true
}
],
outputs: [
{
key: 'isEmpty',
targets: searchEmptyText
? [
{
moduleId: 'emptyText',
key: 'switch'
}
]
: [
{
moduleId: 'chatModule',
key: 'switch'
}
]
},
{
key: 'unEmpty',
targets: [
{
moduleId: 'chatModule',
key: 'switch'
}
]
},
{
key: 'quoteQA',
targets: [
{
moduleId: 'chatModule',
key: 'quoteQA'
}
]
}
],
position: {
x: 956.0838440206068,
y: 887.462827870246
},
moduleId: 'kbSearch'
},
...(searchEmptyText
? [
{
flowType: FlowModuleTypeEnum.answerNode,
inputs: [
{
key: 'switch',
connected: true
},
{
key: SpecialInputKeyEnum.answerText,
value: searchEmptyText,
connected: true
}
],
outputs: [],
position: {
x: 1553.5815811529146,
y: 637.8753731306779
},
moduleId: 'emptyText'
}
]
: []),
{
flowType: FlowModuleTypeEnum.chatNode,
inputs: chatModelInput({ model, temperature, maxToken, systemPrompt, limitPrompt, kbList }),
outputs: [
{
key: TaskResponseKeyEnum.answerText,
targets: []
}
],
position: {
x: 1551.71405495818,
y: 977.4911578918461
},
moduleId: 'chatModule'
}
];
};
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await authUser({ req, authRoot: true });
await connectToDatabase();
const { limit = 1000 } = req.body as { limit: number };
let skip = 0;
const total = await App.countDocuments();
let promise = Promise.resolve();
console.log(total);
for (let i = 0; i < total; i += limit) {
const skipVal = skip;
skip += limit;
promise = promise
.then(() => init(limit, skipVal))
.then(() => {
console.log(skipVal);
});
}
await promise;
jsonRes(res, {});
} catch (error) {
jsonRes(res, {
code: 500,
error
});
}
}
async function init(limit: number, skip: number) {
// 遍历 app
const apps = await App.find(
{
chat: { $ne: null },
modules: { $exists: false }
// userId: '63f9a14228d2a688d8dc9e1b'
},
'_id chat'
).limit(limit);
return Promise.all(
apps.map(async (app) => {
if (!app.chat) return app;
const modules = (() => {
if (app.chat.relatedKbs.length === 0) {
return chatTemplate({
model: app.chat.chatModel,
temperature: app.chat.temperature,
maxToken: app.chat.maxToken,
systemPrompt: app.chat.systemPrompt,
limitPrompt: app.chat.limitPrompt
});
} else {
return kbTemplate({
model: app.chat.chatModel,
temperature: app.chat.temperature,
maxToken: app.chat.maxToken,
systemPrompt: app.chat.systemPrompt,
limitPrompt: app.chat.limitPrompt,
kbList: app.chat.relatedKbs.map((id) => ({ kbId: id })),
searchEmptyText: app.chat.searchEmptyText,
searchLimit: app.chat.searchLimit,
searchSimilarity: app.chat.searchSimilarity
});
}
})();
await App.findByIdAndUpdate(app.id, {
modules
});
return modules;
})
);
}
...@@ -31,6 +31,7 @@ import { SystemInputEnum } from '@/constants/app'; ...@@ -31,6 +31,7 @@ import { SystemInputEnum } from '@/constants/app';
import { getSystemTime } from '@/utils/user'; import { getSystemTime } from '@/utils/user';
import { authOutLinkChat } from '@/service/support/outLink/auth'; import { authOutLinkChat } from '@/service/support/outLink/auth';
import requestIp from 'request-ip'; import requestIp from 'request-ip';
import { replaceVariable } from '@/utils/common/tools/text';
export type MessageItemType = ChatCompletionRequestMessage & { dataId?: string }; export type MessageItemType = ChatCompletionRequestMessage & { dataId?: string };
type FastGptWebChatProps = { type FastGptWebChatProps = {
...@@ -424,10 +425,7 @@ function loadModules( ...@@ -424,10 +425,7 @@ function loadModules(
} }
// variables replace // variables replace
const replacedVal = item.value.replace( const replacedVal = replaceVariable(item.value, variables);
/{{(.*?)}}/g,
(match, key) => variables[key.trim()] || match
);
return { return {
key: item.key, key: item.key,
......
import React from 'react';
import MyModal from '@/components/MyModal';
import { useTranslation } from 'react-i18next';
import { EditFormType } from '@/utils/app';
import { useForm } from 'react-hook-form';
import {
Box,
BoxProps,
Button,
Flex,
Link,
ModalBody,
ModalFooter,
Textarea
} from '@chakra-ui/react';
import MyTooltip from '@/components/MyTooltip';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import { defaultQuotePrompt, defaultQuoteTemplate } from '@/prompts/core/AIChat';
import { feConfigs } from '@/store/static';
const AIChatSettingsModal = ({
onClose,
onSuccess,
defaultData
}: {
onClose: () => void;
onSuccess: (e: EditFormType['chatModel']) => void;
defaultData: EditFormType['chatModel'];
}) => {
const { t } = useTranslation();
const { register, handleSubmit } = useForm({
defaultValues: defaultData
});
const LabelStyles: BoxProps = {
fontWeight: 'bold',
mb: 1,
fontSize: ['sm', 'md']
};
return (
<MyModal
isOpen
title={
<Flex alignItems={'flex-end'}>
{t('app.Quote Prompt Settings')}
{feConfigs?.show_doc && (
<Link
href={'https://doc.fastgpt.run/docs/use-cases/prompt/'}
target={'_blank'}
ml={1}
textDecoration={'underline'}
fontWeight={'normal'}
fontSize={'md'}
>
查看说明
</Link>
)}
</Flex>
}
w={'700px'}
>
<ModalBody>
<Box>
<Box {...LabelStyles}>
引用内容模板
<MyTooltip
label={t('template.Quote Content Tip', { default: defaultQuoteTemplate })}
forceShow
>
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
</MyTooltip>
</Box>
<Textarea
rows={4}
placeholder={t('template.Quote Content Tip', { default: defaultQuoteTemplate }) || ''}
borderColor={'myGray.100'}
{...register('quoteTemplate')}
/>
</Box>
<Box mt={4}>
<Box {...LabelStyles}>
引用内容提示词
<MyTooltip
label={t('template.Quote Prompt Tip', { default: defaultQuotePrompt })}
forceShow
>
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
</MyTooltip>
</Box>
<Textarea
rows={6}
placeholder={t('template.Quote Prompt Tip', { default: defaultQuotePrompt }) || ''}
borderColor={'myGray.100'}
{...register('quotePrompt')}
/>
</Box>
</ModalBody>
<ModalFooter>
<Button variant={'base'} onClick={onClose}>
{t('Cancel')}
</Button>
<Button ml={4} onClick={handleSubmit(onSuccess)}>
{t('Confirm')}
</Button>
</ModalFooter>
</MyModal>
);
};
export default AIChatSettingsModal;
...@@ -4,21 +4,36 @@ import NodeCard from '../modules/NodeCard'; ...@@ -4,21 +4,36 @@ import NodeCard from '../modules/NodeCard';
import { FlowModuleItemType } from '@/types/flow'; import { FlowModuleItemType } from '@/types/flow';
import Divider from '../modules/Divider'; import Divider from '../modules/Divider';
import Container from '../modules/Container'; import Container from '../modules/Container';
import RenderInput from '../render/RenderInput'; import RenderInput, { Label } from '../render/RenderInput';
import RenderOutput from '../render/RenderOutput'; import RenderOutput from '../render/RenderOutput';
import { FlowOutputItemTypeEnum } from '@/constants/flow'; import { FlowOutputItemTypeEnum } from '@/constants/flow';
import MySelect from '@/components/Select'; import MySelect from '@/components/Select';
import { chatModelList } from '@/store/static'; import { chatModelList } from '@/store/static';
import MySlider from '@/components/Slider'; import MySlider from '@/components/Slider';
import { Box } from '@chakra-ui/react'; import { Box, Button, Flex, useDisclosure } from '@chakra-ui/react';
import { formatPrice } from '@/utils/user'; import { formatPrice } from '@/utils/user';
import MyIcon from '@/components/Icon';
import dynamic from 'next/dynamic';
import { AIChatProps } from '@/types/core/aiChat';
const AIChatSettingsModal = dynamic(() => import('../../../AIChatSettingsModal'));
const NodeChat = ({ data }: NodeProps<FlowModuleItemType>) => { const NodeChat = ({ data }: NodeProps<FlowModuleItemType>) => {
const { moduleId, inputs, outputs, onChangeNode } = data; const { moduleId, inputs, outputs, onChangeNode } = data;
const outputsLen = useMemo(
() => outputs.filter((item) => item.type !== FlowOutputItemTypeEnum.hidden).length, const chatModulesData = useMemo(() => {
[outputs] const obj: Record<string, any> = {};
); inputs.forEach((item) => {
obj[item.key] = item.value;
});
return obj as AIChatProps;
}, [inputs]);
const {
isOpen: isOpenAIChatSetting,
onOpen: onOpenAIChatSetting,
onClose: onCloseAIChatSetting
} = useDisclosure();
return ( return (
<NodeCard minW={'400px'} {...data}> <NodeCard minW={'400px'} {...data}>
...@@ -109,21 +124,48 @@ const NodeChat = ({ data }: NodeProps<FlowModuleItemType>) => { ...@@ -109,21 +124,48 @@ const NodeChat = ({ data }: NodeProps<FlowModuleItemType>) => {
/> />
</Box> </Box>
); );
},
quoteQA: (inputItem) => {
return (
<Button
variant={'base'}
leftIcon={<MyIcon name={'settingLight'} w={'14px'} />}
onClick={onOpenAIChatSetting}
>
引用提示词设置
</Button>
);
} }
}} }}
/> />
</Container> </Container>
{outputsLen > 0 && (
<>
<Divider text="Output" /> <Divider text="Output" />
<Container> <Container>
<RenderOutput <RenderOutput onChangeNode={onChangeNode} moduleId={moduleId} flowOutputList={outputs} />
onChangeNode={onChangeNode}
moduleId={moduleId}
flowOutputList={outputs}
/>
</Container> </Container>
</>
{isOpenAIChatSetting && (
<AIChatSettingsModal
onClose={onCloseAIChatSetting}
onSuccess={(e) => {
for (let key in e) {
const item = inputs.find((input) => input.key === key);
if (!item) continue;
onChangeNode({
moduleId,
type: 'inputs',
key,
value: {
...item,
// @ts-ignore
value: e[key]
}
});
}
onCloseAIChatSetting();
}}
defaultData={chatModulesData}
/>
)} )}
</NodeCard> </NodeCard>
); );
......
...@@ -63,6 +63,7 @@ import { useDatasetStore } from '@/store/dataset'; ...@@ -63,6 +63,7 @@ import { useDatasetStore } from '@/store/dataset';
const VariableEditModal = dynamic(() => import('../VariableEditModal')); const VariableEditModal = dynamic(() => import('../VariableEditModal'));
const InfoModal = dynamic(() => import('../InfoModal')); const InfoModal = dynamic(() => import('../InfoModal'));
const KBSelectModal = dynamic(() => import('../KBSelectModal')); const KBSelectModal = dynamic(() => import('../KBSelectModal'));
const AIChatSettingsModal = dynamic(() => import('../AIChatSettingsModal'));
const Settings = ({ appId }: { appId: string }) => { const Settings = ({ appId }: { appId: string }) => {
const theme = useTheme(); const theme = useTheme();
...@@ -102,6 +103,11 @@ const Settings = ({ appId }: { appId: string }) => { ...@@ -102,6 +103,11 @@ const Settings = ({ appId }: { appId: string }) => {
}); });
const { const {
isOpen: isOpenAIChatSetting,
onOpen: onOpenAIChatSetting,
onClose: onCloseAIChatSetting
} = useDisclosure();
const {
isOpen: isOpenKbSelect, isOpen: isOpenKbSelect,
onOpen: onOpenKbSelect, onOpen: onOpenKbSelect,
onClose: onCloseKbSelect onClose: onCloseKbSelect
...@@ -335,7 +341,14 @@ const Settings = ({ appId }: { appId: string }) => { ...@@ -335,7 +341,14 @@ const Settings = ({ appId }: { appId: string }) => {
+&ensp;新增 +&ensp;新增
</Flex> </Flex>
</Flex> </Flex>
<Box mt={2} borderRadius={'lg'} overflow={'hidden'} borderWidth={'1px'} borderBottom="none"> {variables.length > 0 && (
<Box
mt={2}
borderRadius={'lg'}
overflow={'hidden'}
borderWidth={'1px'}
borderBottom="none"
>
<TableContainer> <TableContainer>
<Table bg={'white'}> <Table bg={'white'}>
<Thead> <Thead>
...@@ -373,13 +386,16 @@ const Settings = ({ appId }: { appId: string }) => { ...@@ -373,13 +386,16 @@ const Settings = ({ appId }: { appId: string }) => {
</Table> </Table>
</TableContainer> </TableContainer>
</Box> </Box>
)}
</Box> </Box>
{/* model */} {/* model */}
<Box mt={5} {...BoxStyles}> <Box mt={5} {...BoxStyles}>
<Flex alignItems={'center'}> <Flex alignItems={'center'}>
<Avatar src={'/imgs/module/AI.png'} w={'18px'} /> <Avatar src={'/imgs/module/AI.png'} w={'18px'} />
<Box ml={2}>AI 配置</Box> <Box ml={2} flex={1}>
AI 配置
</Box>
</Flex> </Flex>
<Flex alignItems={'center'} mt={5}> <Flex alignItems={'center'} mt={5}>
...@@ -452,20 +468,6 @@ const Settings = ({ appId }: { appId: string }) => { ...@@ -452,20 +468,6 @@ const Settings = ({ appId }: { appId: string }) => {
{...register('chatModel.systemPrompt')} {...register('chatModel.systemPrompt')}
></Textarea> ></Textarea>
</Flex> </Flex>
<Flex mt={5} alignItems={'flex-start'}>
<Box {...LabelStyles}>
限定词
<MyTooltip label={ChatModelLimitTip} forceShow>
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
</MyTooltip>
</Box>
<Textarea
rows={5}
placeholder={ChatModelLimitTip}
borderColor={'myGray.100'}
{...register('chatModel.limitPrompt')}
></Textarea>
</Flex>
</Box> </Box>
{/* kb */} {/* kb */}
...@@ -483,6 +485,10 @@ const Settings = ({ appId }: { appId: string }) => { ...@@ -483,6 +485,10 @@ const Settings = ({ appId }: { appId: string }) => {
<MyIcon name={'edit'} w={'14px'} mr={1} /> <MyIcon name={'edit'} w={'14px'} mr={1} />
参数 参数
</Flex> </Flex>
<Flex {...BoxBtnStyles} onClick={onOpenAIChatSetting}>
<MyIcon mr={1} name={'settingLight'} w={'14px'} />
提示词
</Flex>
</Flex> </Flex>
<Flex mt={1} color={'myGray.600'} fontSize={['sm', 'md']}> <Flex mt={1} color={'myGray.600'} fontSize={['sm', 'md']}>
相似度: {getValues('kb.searchSimilarity')}, 单次搜索数量: {getValues('kb.searchLimit')}, 相似度: {getValues('kb.searchSimilarity')}, 单次搜索数量: {getValues('kb.searchLimit')},
...@@ -548,6 +554,16 @@ const Settings = ({ appId }: { appId: string }) => { ...@@ -548,6 +554,16 @@ const Settings = ({ appId }: { appId: string }) => {
}} }}
/> />
)} )}
{isOpenAIChatSetting && (
<AIChatSettingsModal
onClose={onCloseAIChatSetting}
onSuccess={(e) => {
setValue('chatModel', e);
onCloseAIChatSetting();
}}
defaultData={getValues('chatModel')}
/>
)}
{isOpenKbSelect && ( {isOpenKbSelect && (
<KBSelectModal <KBSelectModal
activeKbs={selectedKbList.map((item) => ({ activeKbs={selectedKbList.map((item) => ({
......
...@@ -55,7 +55,6 @@ const InfoModal = ({ ...@@ -55,7 +55,6 @@ const InfoModal = ({
name: data.name, name: data.name,
avatar: data.avatar, avatar: data.avatar,
intro: data.intro, intro: data.intro,
chat: data.chat,
share: data.share share: data.share
}); });
}, },
......
...@@ -282,8 +282,6 @@ export function EditLinkModal({ ...@@ -282,8 +282,6 @@ export function EditLinkModal({
return ( return (
<MyModal <MyModal
isOpen={true} isOpen={true}
showCloseBtn={false}
onClose={() => {}}
title={isEdit ? titleMap.current.edit[type] : titleMap.current.create[type]} title={isEdit ? titleMap.current.edit[type] : titleMap.current.create[type]}
> >
<ModalBody> <ModalBody>
......
...@@ -168,7 +168,7 @@ const InputDataModal = ({ ...@@ -168,7 +168,7 @@ const InputDataModal = ({
</Box> </Box>
<Box flex={1} h={['50%', '100%']}> <Box flex={1} h={['50%', '100%']}>
<Flex> <Flex>
<Box h={'30px'}>{'预期答案'}</Box> <Box h={'30px'}>{'补充内容'}</Box>
<MyTooltip <MyTooltip
label={'匹配的知识点被命中后,这部分内容会随匹配知识点一起注入模型,引导模型回答'} label={'匹配的知识点被命中后,这部分内容会随匹配知识点一起注入模型,引导模型回答'}
> >
...@@ -177,9 +177,8 @@ const InputDataModal = ({ ...@@ -177,9 +177,8 @@ const InputDataModal = ({
</Flex> </Flex>
<Textarea <Textarea
placeholder={ placeholder={
'预期答案。这部分内容不会被搜索,但会作为"匹配的知识点"的内容补充,通常是问题的答案。总和最多 3000 字。' '这部分内容不会被搜索,但会作为"匹配的知识点"的内容补充,通常是问题的答案。'
} }
maxLength={3000}
resize={'none'} resize={'none'}
h={'calc(100% - 30px)'} h={'calc(100% - 30px)'}
{...register('a')} {...register('a')}
......
export const defaultQuoteTemplate = `{instruction:"{{q}}",output:"{{a}}"}`;
export const defaultQuotePrompt = `你的背景知识:
"""
{{quote}}
"""
对话要求:
1. 背景知识是最新的,其中 instruction 是相关介绍,output 是预期回答或补充。
2. 使用背景知识回答问题。
3. 背景知识无法满足问题时,你需严谨的回答问题。
我的问题是:"{{question}}"`;
...@@ -17,12 +17,12 @@ import { ChatCompletionRequestMessageRoleEnum } from 'openai'; ...@@ -17,12 +17,12 @@ import { ChatCompletionRequestMessageRoleEnum } from 'openai';
import { AppModuleItemType } from '@/types/app'; import { AppModuleItemType } from '@/types/app';
import { countMessagesTokens, sliceMessagesTB } from '@/utils/common/tiktoken'; import { countMessagesTokens, sliceMessagesTB } from '@/utils/common/tiktoken';
import { adaptChat2GptMessages } from '@/utils/common/adapt/message'; import { adaptChat2GptMessages } from '@/utils/common/adapt/message';
import { defaultQuotePrompt, defaultQuoteTemplate } from '@/prompts/core/AIChat';
import type { AIChatProps } from '@/types/core/aiChat';
import { replaceVariable } from '@/utils/common/tools/text';
export type ChatProps = { export type ChatProps = AIChatProps & {
res: NextApiResponse; res: NextApiResponse;
model: string;
temperature?: number;
maxToken?: number;
history?: ChatItemType[]; history?: ChatItemType[];
userChatInput: string; userChatInput: string;
stream?: boolean; stream?: boolean;
...@@ -52,7 +52,9 @@ export const dispatchChatCompletion = async (props: Record<string, any>): Promis ...@@ -52,7 +52,9 @@ export const dispatchChatCompletion = async (props: Record<string, any>): Promis
quoteQA = [], quoteQA = [],
userChatInput, userChatInput,
systemPrompt = '', systemPrompt = '',
limitPrompt = '', limitPrompt,
quoteTemplate,
quotePrompt,
userOpenaiAccount, userOpenaiAccount,
outputs outputs
} = props as ChatProps; } = props as ChatProps;
...@@ -67,16 +69,16 @@ export const dispatchChatCompletion = async (props: Record<string, any>): Promis ...@@ -67,16 +69,16 @@ export const dispatchChatCompletion = async (props: Record<string, any>): Promis
return Promise.reject('The chat model is undefined, you need to select a chat model.'); return Promise.reject('The chat model is undefined, you need to select a chat model.');
} }
const { filterQuoteQA, quotePrompt, hasQuoteOutput } = filterQuote({ const { filterQuoteQA, quoteText, hasQuoteOutput } = filterQuote({
quoteQA, quoteQA,
model: modelConstantsData model: modelConstantsData,
quoteTemplate
}); });
if (modelConstantsData.censor) { if (modelConstantsData.censor) {
await textCensor({ await textCensor({
text: `${systemPrompt} text: `${systemPrompt}
${quotePrompt} ${quoteText}
${limitPrompt}
${userChatInput} ${userChatInput}
` `
}); });
...@@ -85,6 +87,7 @@ export const dispatchChatCompletion = async (props: Record<string, any>): Promis ...@@ -85,6 +87,7 @@ export const dispatchChatCompletion = async (props: Record<string, any>): Promis
const { messages, filterMessages } = getChatMessages({ const { messages, filterMessages } = getChatMessages({
model: modelConstantsData, model: modelConstantsData,
history, history,
quoteText,
quotePrompt, quotePrompt,
userChatInput, userChatInput,
systemPrompt, systemPrompt,
...@@ -189,39 +192,40 @@ export const dispatchChatCompletion = async (props: Record<string, any>): Promis ...@@ -189,39 +192,40 @@ export const dispatchChatCompletion = async (props: Record<string, any>): Promis
function filterQuote({ function filterQuote({
quoteQA = [], quoteQA = [],
model model,
quoteTemplate
}: { }: {
quoteQA: ChatProps['quoteQA']; quoteQA: ChatProps['quoteQA'];
model: ChatModelItemType; model: ChatModelItemType;
quoteTemplate?: string;
}) { }) {
const sliceResult = sliceMessagesTB({ const sliceResult = sliceMessagesTB({
maxTokens: model.quoteMaxToken, maxTokens: model.quoteMaxToken,
messages: quoteQA.map((item) => ({ messages: quoteQA.map((item) => ({
obj: ChatRoleEnum.System, obj: ChatRoleEnum.System,
value: item.a ? `${item.q}\n${item.a}` : item.q value: replaceVariable(quoteTemplate || defaultQuoteTemplate, item)
})) }))
}); });
// slice filterSearch // slice filterSearch
const filterQuoteQA = quoteQA.slice(0, sliceResult.length); const filterQuoteQA = quoteQA.slice(0, sliceResult.length);
const quotePrompt = const quoteText =
filterQuoteQA.length > 0 filterQuoteQA.length > 0
? `"""${filterQuoteQA ? `${filterQuoteQA
.map((item) => .map((item) => replaceVariable(quoteTemplate || defaultQuoteTemplate, item))
item.a ? `{instruction:"${item.q}",output:"${item.a}"}` : `{instruction:"${item.q}"}` .join('\n')}`
)
.join('\n')}"""`
: ''; : '';
return { return {
filterQuoteQA, filterQuoteQA,
quotePrompt, quoteText,
hasQuoteOutput: !!filterQuoteQA.find((item) => item.a) hasQuoteOutput: !!filterQuoteQA.find((item) => item.a)
}; };
} }
function getChatMessages({ function getChatMessages({
quotePrompt, quotePrompt,
quoteText,
history = [], history = [],
systemPrompt, systemPrompt,
limitPrompt, limitPrompt,
...@@ -229,32 +233,28 @@ function getChatMessages({ ...@@ -229,32 +233,28 @@ function getChatMessages({
model, model,
hasQuoteOutput hasQuoteOutput
}: { }: {
quotePrompt: string; quotePrompt?: string;
quoteText: string;
history: ChatProps['history']; history: ChatProps['history'];
systemPrompt: string; systemPrompt: string;
limitPrompt: string; limitPrompt?: string;
userChatInput: string; userChatInput: string;
model: ChatModelItemType; model: ChatModelItemType;
hasQuoteOutput: boolean; hasQuoteOutput: boolean;
}) { }) {
const { quoteGuidePrompt } = getDefaultPrompt({ hasQuoteOutput }); const question = hasQuoteOutput
? replaceVariable(quotePrompt || defaultQuotePrompt, {
const systemText = `${quotePrompt ? `${quoteGuidePrompt}\n\n` : ''}${systemPrompt}`; quote: quoteText,
question: userChatInput
})
: userChatInput;
const messages: ChatItemType[] = [ const messages: ChatItemType[] = [
...(systemText ...(systemPrompt
? [
{
obj: ChatRoleEnum.System,
value: systemText
}
]
: []),
...(quotePrompt
? [ ? [
{ {
obj: ChatRoleEnum.System, obj: ChatRoleEnum.System,
value: quotePrompt value: systemPrompt
} }
] ]
: []), : []),
...@@ -269,7 +269,7 @@ function getChatMessages({ ...@@ -269,7 +269,7 @@ function getChatMessages({
: []), : []),
{ {
obj: ChatRoleEnum.Human, obj: ChatRoleEnum.Human,
value: userChatInput value: question
} }
]; ];
...@@ -375,11 +375,3 @@ async function streamResponse({ ...@@ -375,11 +375,3 @@ async function streamResponse({
answer answer
}; };
} }
function getDefaultPrompt({ hasQuoteOutput }: { hasQuoteOutput?: boolean }) {
return {
quoteGuidePrompt: `三引号引用的内容是我提供给你的知识库,它们拥有最高优先级。instruction 是相关介绍${
hasQuoteOutput ? ',output 是预期回答或补充。' : '。'
}`
};
}
export type AIChatProps = {
model: string;
systemPrompt: string;
temperature: number;
maxToken: number;
quoteTemplate?: string;
quotePrompt?: string;
frequency: number;
presence: number;
};
...@@ -50,17 +50,6 @@ export interface AppSchema { ...@@ -50,17 +50,6 @@ export interface AppSchema {
collection: number; collection: number;
}; };
modules: AppModuleItemType[]; modules: AppModuleItemType[];
chat?: {
relatedKbs: string[];
searchSimilarity: number;
searchLimit: number;
searchEmptyText: string;
systemPrompt: string;
limitPrompt: string;
temperature: number;
maxToken: number;
chatModel: ChatModelType; // 聊天时用的模型,训练后就是训练的模型
};
} }
export interface CollectionSchema { export interface CollectionSchema {
......
import type { AppModuleItemType, VariableItemType } from '@/types/app'; import type { AppModuleItemType, VariableItemType } from '@/types/app';
import { chatModelList, vectorModelList } from '@/store/static'; import { chatModelList } from '@/store/static';
import { import {
FlowInputItemTypeEnum, FlowInputItemTypeEnum,
FlowModuleTypeEnum, FlowModuleTypeEnum,
...@@ -7,20 +7,12 @@ import { ...@@ -7,20 +7,12 @@ import {
SpecialInputKeyEnum SpecialInputKeyEnum
} from '@/constants/flow'; } from '@/constants/flow';
import { SystemInputEnum } from '@/constants/app'; import { SystemInputEnum } from '@/constants/app';
import { TaskResponseKeyEnum } from '@/constants/chat';
import type { SelectedKbType } from '@/types/plugin'; import type { SelectedKbType } from '@/types/plugin';
import { FlowInputItemType } from '@/types/flow'; import { FlowInputItemType } from '@/types/flow';
import type { AIChatProps } from '@/types/core/aiChat';
export type EditFormType = { export type EditFormType = {
chatModel: { chatModel: AIChatProps;
model: string;
systemPrompt: string;
limitPrompt: string;
temperature: number;
maxToken: number;
frequency: number;
presence: number;
};
kb: { kb: {
list: SelectedKbType; list: SelectedKbType;
searchSimilarity: number; searchSimilarity: number;
...@@ -41,8 +33,9 @@ export const getDefaultAppForm = (): EditFormType => { ...@@ -41,8 +33,9 @@ export const getDefaultAppForm = (): EditFormType => {
chatModel: { chatModel: {
model: defaultChatModel.model, model: defaultChatModel.model,
systemPrompt: '', systemPrompt: '',
limitPrompt: '',
temperature: 0, temperature: 0,
quotePrompt: '',
quoteTemplate: '',
maxToken: defaultChatModel.contextMaxToken / 2, maxToken: defaultChatModel.contextMaxToken / 2,
frequency: 0.5, frequency: 0.5,
presence: -0.5 presence: -0.5
...@@ -109,9 +102,14 @@ export const appModules2Form = (modules: AppModuleItemType[]) => { ...@@ -109,9 +102,14 @@ export const appModules2Form = (modules: AppModuleItemType[]) => {
key: 'systemPrompt' key: 'systemPrompt'
}); });
updateVal({ updateVal({
formKey: 'chatModel.limitPrompt', formKey: 'chatModel.quoteTemplate',
inputs: module.inputs,
key: 'quoteTemplate'
});
updateVal({
formKey: 'chatModel.quotePrompt',
inputs: module.inputs, inputs: module.inputs,
key: 'limitPrompt' key: 'quotePrompt'
}); });
} else if (module.flowType === FlowModuleTypeEnum.kbSearchNode) { } else if (module.flowType === FlowModuleTypeEnum.kbSearchNode) {
updateVal({ updateVal({
...@@ -178,16 +176,23 @@ const chatModelInput = (formData: EditFormType): FlowInputItemType[] => [ ...@@ -178,16 +176,23 @@ const chatModelInput = (formData: EditFormType): FlowInputItemType[] => [
}, },
{ {
key: 'systemPrompt', key: 'systemPrompt',
value: formData.chatModel.systemPrompt, value: formData.chatModel.systemPrompt || '',
type: 'textarea', type: 'textarea',
label: '系统提示词', label: '系统提示词',
connected: true connected: true
}, },
{ {
key: 'limitPrompt', key: 'quoteTemplate',
type: 'textarea', value: formData.chatModel.quoteTemplate || '',
value: formData.chatModel.limitPrompt, type: 'hidden',
label: '限定词', label: '引用内容模板',
connected: true
},
{
key: 'quotePrompt',
value: formData.chatModel.quotePrompt || '',
type: 'hidden',
label: '引用内容提示词',
connected: true connected: true
}, },
{ {
......
...@@ -81,7 +81,7 @@ export function sliceMessagesTB({ ...@@ -81,7 +81,7 @@ export function sliceMessagesTB({
const tokens = countPromptTokens(item.content, item.role); const tokens = countPromptTokens(item.content, item.role);
reduceTokens -= tokens; reduceTokens -= tokens;
if (tokens > 0) { if (reduceTokens > 0) {
result.push(messages[i]); result.push(messages[i]);
} else { } else {
break; break;
......
/*
replace {{variable}} to value
*/
export function replaceVariable(text: string, obj: Record<string, string>) {
for (const key in obj) {
const val = obj[key];
if (typeof val !== 'string') continue;
text = text.replace(new RegExp(`{{(${key})}}`, 'g'), val);
}
return text || '';
}
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"*": [ "*": [
"../../../../../Library/Caches/hugo_cache/modules/filecache/modules/pkg/mod/github.com/gohugoio/hugo-mod-jslibs-dist/popperjs/v2@v2.21100.20000/package/dist/cjs/popper.js/*", "../../../../../.cache/hugo_cache/modules/filecache/modules/pkg/mod/github.com/gohugoio/hugo-mod-jslibs-dist/popperjs/v2@v2.21100.20000/package/dist/cjs/popper.js/*",
"../../../../../Library/Caches/hugo_cache/modules/filecache/modules/pkg/mod/github.com/twbs/bootstrap@v5.3.0+incompatible/js/*" "../../../../../.cache/hugo_cache/modules/filecache/modules/pkg/mod/github.com/twbs/bootstrap@v5.3.0+incompatible/js/*"
] ]
} }
} }
......
---
title: '升级到 V4.4.2'
description: 'FastGPT 从旧版本升级到 V4.4.2 操作指南'
icon: 'upgrade'
draft: false
toc: true
weight: 994
---
## 执行初始化 API
发起 1 个 HTTP 请求(记得携带 `headers.rootkey`,这个值是环境变量里的)
1. https://xxxxx/api/admin/initv442
```bash
curl --location --request POST 'https://{{host}}/api/admin/initv442' \
--header 'rootkey: {{rootkey}}' \
--header 'Content-Type: application/json'
```
会给初始化 Mongo 的 Bill 表的索引,之前过期时间有误。
--- ---
title: "提示词 & 限定词" title: "提示词 & 引用提示词"
description: "FastGPT 提示词 & 限定词说明" description: "FastGPT 提示词 & 引用提示词说明"
icon: "sign_language" icon: "sign_language"
draft: false draft: false
toc: true toc: true
weight: 310 weight: 310
--- ---
限定词从 V4.4.3 版本后去除,被“引用提示词”和“引用模板”替代。
# AI 对话消息组成 # AI 对话消息组成
传递给 AI 模型的消息是一个数组,FastGPT 中这个数组的组成形式为: 传递给 AI 模型的消息是一个数组,FastGPT 中这个数组的组成形式为:
...@@ -14,12 +16,9 @@ weight: 310 ...@@ -14,12 +16,9 @@ weight: 310
```json ```json
[ [
内置提示词(config.json 配置,一般为空) 内置提示词(config.json 配置,一般为空)
搜索引导词(有引用内容时候会默认携带)
提示词 (用户输入的提示词) 提示词 (用户输入的提示词)
引用内容(结构看第二节)
历史记录 历史记录
限定词(由用户填写,无默认值) 问题(会由输入的问题、引用提示词和引用模板来决定)
问题
] ]
``` ```
...@@ -27,43 +26,84 @@ weight: 310 ...@@ -27,43 +26,84 @@ weight: 310
Tips: 可以通过点击上下文按键查看完整的 Tips: 可以通过点击上下文按键查看完整的
{{% /alert %}} {{% /alert %}}
# 引用内容结构 # 引用模板和提示词设计
知识库采用 QA 对的格式存储,在转义成字符串时候会对应的转成 instruction 和 output。搜索引导词中会对这两个字段做说明,不需要重复补充。 知识库采用 QA 对的格式存储,在转义成字符串时候会根据**引用模板**来进行格式化。知识库包含 3 个变量: q,a 和 source,可以通过 {{q}} {{a}} {{source}} 按需引入。下面一个模板例子:
{{% alert icon="🤖" context="success" %}} **引用模板**
三引号引用的内容是我提供给你的知识库,它们拥有最高优先级。instruction 是相关介绍,output 是预期回答或补充。
{{% /alert %}} ```
{instruction:"{{q}}",output:"{{a}}",source:"{{source}}"}
```
搜索到的知识库,会自动将 q,a,source 替换成对应的内容。每条搜索到的内容,会通过 `\n` 隔开。例如:
```
{instruction:"电影《铃芽之旅》的导演是谁?",output:"电影《铃芽之旅》的导演是新海诚。",source:"手动输入"}
{instruction:"本作的主人公是谁?",output:"本作的主人公是名叫铃芽的少女。",source:""}
{instruction:"电影《铃芽之旅》男主角是谁?",output:"电影《铃芽之旅》男主角是宗像草太,由松村北斗配音。",source:""}
{instruction:"电影《铃芽之旅》的编剧是谁?22",output:"新海诚是本片的编剧。",source:"手动输入"}
```
**引用提示词**
引用模板需要和引用提示词一起使用,提示词中可以写引用模板的格式说明以及对话的要求等。可以使用 {{quote}} 来使用 **引用模板**,使用 {{question}} 来引入问题。例如:
``` ```
你的背景知识:
""" """
{instruction:"本作的故事背景是什么?",output:"本作的故事背景是发生在日本灾难时期的东北地区。"} {{quote}}
{instruction:"电影《铃芽之旅》讲述了什么故事?",output:"电影《铃芽之旅》讲述了少女岩户铃芽和关门师宗像草太为了关闭灾难之门展开的冒险旅程。"}
{instruction:"电影《铃芽之旅》的故事背景是什么?",output:"日本"}
""" """
对话要求:
1. 背景知识是最新的,其中 instruction 是相关介绍,output 是预期回答或补充。
2. 使用背景知识回答问题。
3. 背景知识无法回答问题时,你可以礼貌的的回答用户问题。
我的问题是:"{{question}}"
``` ```
# 提示词案例 # 提示词案例
## 仅回复知识库里的内容 ## 仅回复知识库里的内容
{{% alert icon="🤖" context="warning" %}} **引用提示词**里添加:
**限定词**里添加: ```
你的背景知识:
回答内容限制:你目前仅能回答三引号中提及的内容,超出引用的内容,请直接回复:“我不知道” """
{{% /alert %}} {{quote}}
"""
对话要求:
1. 回答前,请先判断背景知识是否足够回答问题,如果无法回答,请直接回复:“对不起,我无法回答你的问题~”。
2. 背景知识是最新的,其中 instruction 是相关介绍,output 是预期回答或补充。
3. 使用背景知识回答问题。
我的问题是:"{{question}}"
```
注意,限定词会一定程度上打断上下文连贯性,且并不是 100% 生效。随着上下文和引用长度越多,限定词的效果会被削弱。实在控不住,用 GPT4 吧。 ## 说明引用来源
## 为回答添加引用序号 **引用模板:**
如果你希望回答内容带上引用的第几条的序号,可以参考下面的提示词: ```
{instruction:"{{q}}",output:"{{a}}",source:"{{source}}"}
```
{{% alert icon="🤖" context="warning" %}} **引用提示词:**
**提示词**里添加:
我希望你的回答会附加上引用的序号: ```
1.每个 {instruction,output} 包裹的内容是一条引用 你的背景知识:
2.从上往下,序列号从 1-n """
3.回答的内容应使用 [1][2] 这个特殊的格式来标记引用序列号 {{quote}}
{{% /alert %}} """
对话要求:
1. 背景知识是最新的,其中 instruction 是相关介绍,output 是预期回答或补充,source是背景来源。
2. 使用背景知识回答问题。
3. 在回答问题后,你需要给出本次回答对应的背景来源,来源展示格式如下:
这是AI作答。本次知识来源:
1. source1
2. source2
......
我的问题是:"{{question}}"
```
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment