perf: 知识库录入

This commit is contained in:
archer
2023-03-31 18:23:07 +08:00
parent a3d74ec4a6
commit 5759cbeae0
5 changed files with 142 additions and 156 deletions

View File

@@ -119,20 +119,20 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
.select('text q') .select('text q')
.then((res) => { .then((res) => {
if (!res) return ''; if (!res) return '';
const questions = res.q.map((item) => item.text).join(' '); // const questions = res.q.map((item) => item.text).join(' ');
const answer = res.text; const answer = res.text;
return `${questions} ${answer}`; return `${answer}`;
}); });
}) })
) )
).filter((item) => item); ).filter((item) => item);
// textArr 筛选,最多 3000 tokens // textArr 筛选,最多 3000 tokens
const systemPrompt = systemPromptFilter(textArr, 2800); const systemPrompt = systemPromptFilter(textArr, 3400);
prompts.unshift({ prompts.unshift({
obj: 'SYSTEM', obj: 'SYSTEM',
value: `根据下面的知识回答问题: ${systemPrompt}` value: `${model.systemPrompt}。 我的知识库: "${systemPrompt}"`
}); });
// 控制在 tokens 数量,防止超出 // 控制在 tokens 数量,防止超出

View File

@@ -190,91 +190,97 @@ const Chat = ({ chatId }: { chatId: string }) => {
/** /**
* 发送一个内容 * 发送一个内容
*/ */
const sendPrompt = useCallback(async () => { const sendPrompt = useCallback(
const storeInput = inputVal; async (e?: React.MouseEvent<HTMLDivElement>) => {
// 去除空行 e?.stopPropagation();
const val = inputVal e?.preventDefault();
.trim()
.split('\n')
.filter((val) => val)
.join('\n');
if (!chatData?.modelId || !val || !ChatBox.current || isChatting) {
return;
}
// 长度校验 const storeInput = inputVal;
const tokens = encode(val).length; // 去除空行
const model = modelList.find((item) => item.model === chatData.modelName); const val = inputVal
.trim()
if (model && tokens >= model.maxToken) { .split('\n')
toast({ .filter((val) => val)
title: '单次输入超出 4000 tokens', .join('\n');
status: 'warning' if (!chatData?.modelId || !val || !ChatBox.current || isChatting) {
}); return;
return;
}
const newChatList: ChatSiteItemType[] = [
...chatData.history,
{
obj: 'Human',
value: val,
status: 'finish'
},
{
obj: 'AI',
value: '',
status: 'loading'
} }
];
// 插入内容 // 长度校验
setChatData((state) => ({ const tokens = encode(val).length;
...state, const model = modelList.find((item) => item.model === chatData.modelName);
history: newChatList
}));
// 清空输入内容 if (model && tokens >= model.maxToken) {
resetInputVal(''); toast({
scrollToBottom(); title: '单次输入超出 4000 tokens',
status: 'warning'
try {
await gptChatPrompt(newChatList[newChatList.length - 2]);
// 如果是 Human 第一次发送,插入历史记录
const humanChat = newChatList.filter((item) => item.obj === 'Human');
if (humanChat.length === 1) {
pushChatHistory({
chatId,
title: humanChat[0].value
}); });
return;
} }
} catch (err: any) {
toast({
title: typeof err === 'string' ? err : err?.message || '聊天出错了~',
status: 'warning',
duration: 5000,
isClosable: true
});
resetInputVal(storeInput); const newChatList: ChatSiteItemType[] = [
...chatData.history,
{
obj: 'Human',
value: val,
status: 'finish'
},
{
obj: 'AI',
value: '',
status: 'loading'
}
];
// 插入内容
setChatData((state) => ({ setChatData((state) => ({
...state, ...state,
history: newChatList.slice(0, newChatList.length - 2) history: newChatList
})); }));
}
}, [ // 清空输入内容
inputVal, resetInputVal('');
chatData, scrollToBottom();
isChatting,
resetInputVal, try {
scrollToBottom, await gptChatPrompt(newChatList[newChatList.length - 2]);
toast,
gptChatPrompt, // 如果是 Human 第一次发送,插入历史记录
pushChatHistory, const humanChat = newChatList.filter((item) => item.obj === 'Human');
chatId if (humanChat.length === 1) {
]); pushChatHistory({
chatId,
title: humanChat[0].value
});
}
} catch (err: any) {
toast({
title: typeof err === 'string' ? err : err?.message || '聊天出错了~',
status: 'warning',
duration: 5000,
isClosable: true
});
resetInputVal(storeInput);
setChatData((state) => ({
...state,
history: newChatList.slice(0, newChatList.length - 2)
}));
}
},
[
inputVal,
chatData,
isChatting,
resetInputVal,
scrollToBottom,
toast,
gptChatPrompt,
pushChatHistory,
chatId
]
);
// 删除一句话 // 删除一句话
const delChatRecord = useCallback( const delChatRecord = useCallback(

View File

@@ -19,7 +19,7 @@ import { DeleteIcon } from '@chakra-ui/icons';
import { customAlphabet } from 'nanoid'; import { customAlphabet } from 'nanoid';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12); const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
type FormData = { text: string; q: { val: string }[] }; type FormData = { text: string; q: string };
const InputDataModal = ({ const InputDataModal = ({
onClose, onClose,
@@ -36,17 +36,9 @@ const InputDataModal = ({
const { register, handleSubmit, control } = useForm<FormData>({ const { register, handleSubmit, control } = useForm<FormData>({
defaultValues: { defaultValues: {
text: '', text: '',
q: [{ val: '' }] q: ''
} }
}); });
const {
fields: inputQ,
append: appendQ,
remove: removeQ
} = useFieldArray({
control,
name: 'q'
});
const sureImportData = useCallback( const sureImportData = useCallback(
async (e: FormData) => { async (e: FormData) => {
@@ -58,10 +50,12 @@ const InputDataModal = ({
data: [ data: [
{ {
text: e.text, text: e.text,
q: e.q.map((item) => ({ q: [
id: nanoid(), {
text: item.val id: nanoid(),
})) text: e.q
}
]
} }
] ]
}); });
@@ -81,50 +75,46 @@ const InputDataModal = ({
); );
return ( return (
<Modal isOpen={true} onClose={onClose}> <Modal isOpen={true} onClose={onClose} isCentered>
<ModalOverlay /> <ModalOverlay />
<ModalContent maxW={'min(900px, 90vw)'} maxH={'80vh'} position={'relative'}> <ModalContent
m={0}
display={'flex'}
flexDirection={'column'}
h={'90vh'}
maxW={'90vw'}
position={'relative'}
>
<ModalHeader></ModalHeader> <ModalHeader></ModalHeader>
<ModalCloseButton /> <ModalCloseButton />
<Box px={6} pb={2} overflowY={'auto'}>
<Box mb={2}>:</Box> <Flex flex={'1 0 0'} h={0} px={6} pb={2}>
<Textarea <Box flex={2} mr={4} h={'100%'}>
mb={4} <Box h={'30px'}></Box>
placeholder="知识点" <Textarea
rows={3} placeholder="相关问题,可以回车输入多个问法, 最多500字"
maxH={'200px'} maxLength={500}
{...register(`text`, { resize={'none'}
required: '知识点' h={'calc(100% - 30px)'}
})} {...register(`q`, {
/> required: '相关问题,可以回车输入多个问法'
{inputQ.map((item, index) => ( })}
<Box key={item.id} mb={5}> />
<Box mb={2}>{index + 1}:</Box> </Box>
<Flex> <Box flex={3} h={'100%'}>
<Input <Box h={'30px'}></Box>
placeholder="问法" <Textarea
{...register(`q.${index}.val`, { placeholder="知识点"
required: '问法不能为空' resize={'none'}
})} h={'calc(100% - 30px)'}
></Input> {...register(`text`, {
{inputQ.length > 1 && ( required: '知识点'
<IconButton })}
icon={<DeleteIcon />} />
aria-label={'delete'} </Box>
colorScheme={'gray'} </Flex>
variant={'unstyled'}
onClick={() => removeQ(index)}
/>
)}
</Flex>
</Box>
))}
</Box>
<Flex px={6} pt={2} pb={4}> <Flex px={6} pt={2} pb={4}>
<Button alignSelf={'flex-start'} variant={'outline'} onClick={() => appendQ({ val: '' })}>
</Button>
<Box flex={1}></Box> <Box flex={1}></Box>
<Button variant={'outline'} mr={3} onClick={onClose}> <Button variant={'outline'} mr={3} onClick={onClose}>

View File

@@ -50,7 +50,7 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
pageNum pageNum
} = usePagination<ModelDataSchema>({ } = usePagination<ModelDataSchema>({
api: getModelDataList, api: getModelDataList,
pageSize: 10, pageSize: 8,
params: { params: {
modelId: model._id modelId: model._id
} }
@@ -119,7 +119,7 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
</Flex> </Flex>
{data && data.length > 0 && <Box fontSize={'xs'}>{data.length}...</Box>} {data && data.length > 0 && <Box fontSize={'xs'}>{data.length}...</Box>}
<Box mt={4}> <Box mt={4}>
<TableContainer h={'600px'} overflowY={'auto'}> <TableContainer>
<Table variant={'simple'}> <Table variant={'simple'}>
<Thead> <Thead>
<Tr> <Tr>
@@ -141,10 +141,7 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
whiteSpace={'pre-wrap'} whiteSpace={'pre-wrap'}
_notLast={{ mb: 1 }} _notLast={{ mb: 1 }}
> >
Q{i + 1}:{' '} {item.text}
<Box as={'span'} userSelect={'all'}>
{item.text}
</Box>
</Box> </Box>
))} ))}
</Td> </Td>

View File

@@ -108,7 +108,7 @@ const ModelEditForm = ({
<Slider <Slider
aria-label="slider-ex-1" aria-label="slider-ex-1"
min={1} min={0}
max={10} max={10}
step={1} step={1}
value={getValues('temperature')} value={getValues('temperature')}
@@ -138,24 +138,17 @@ const ModelEditForm = ({
</Flex> </Flex>
</FormControl> </FormControl>
<Box mt={4}> <Box mt={4}>
{canTrain ? ( <Box mb={1}></Box>
<Box fontWeight={'bold'}> <Textarea
prompt rows={6}
使 tokens maxLength={-1}
</Box> {...register('systemPrompt')}
) : ( placeholder={
<> canTrain
<Box mb={1}></Box> ? '训练的模型会根据知识库内容,生成一部分系统提示词,因此在对话时需要消耗更多的 tokens。你仍可以增加一些提示词让其效果更精确。'
<Textarea : '模型默认的 prompt 词,通过调整该内容,可以生成一个限定范围的模型。\n\n注意改功能会影响对话的整体朝向'
rows={6} }
maxLength={-1} />
{...register('systemPrompt')}
placeholder={
'模型默认的 prompt 词,通过调整该内容,可以生成一个限定范围的模型。\n\n注意改功能会影响对话的整体朝向'
}
/>
</>
)}
</Box> </Box>
</Card> </Card>
{/* <Card p={4}> {/* <Card p={4}>