从零搭建基于 AI 问答平台(14) 定义 OpenEnded组件

效果

实现步骤如下:

添加 Check Answer 逻辑

安装依赖:

1
2
bun add string-similarity 
bun add --save-dev @types/string-similarity

添加逻辑,src/app/api/checkAnswer/route.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (question.questionType === "open_ended") {
let percentageSimilar = stringSimilarity.compareTwoStrings(
question.answer.toLowerCase().trim(),
userInput.toLowerCase().trim()
);
percentageSimilar = Math.round(percentageSimilar * 100);
await prisma.question.update({
where: { id: questionId },
data: { percentageCorrect: percentageSimilar },
});
return NextResponse.json({
percentageSimilar,
});
}

添加页面检查逻辑

添加逻辑,src/app/api/checkAnswer/route.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
  const { mutate: checkAnswer, isPending : isChecking } = useMutation({
mutationFn: async () => {
let filledAnswer = blankAnswer;
document.querySelectorAll("#user-blank-input").forEach((input) => {
filledAnswer = filledAnswer.replace("_____", input.value);
input.value = "";
});
const payload: z.infer<typeof checkAnswerSchema> = {
questionId: currentQuestion.id,
userInput: filledAnswer,
};
const response = await axios.post(`/api/checkAnswer`, payload);
return response.data;
},
});


const handleNext = React.useCallback(() => {
checkAnswer(undefined, {
onSuccess: ({ percentageSimilar }) => {
toast({
title: `Your answer is ${percentageSimilar}% similar to the correct answer`,
});
setAveragePercentage((prev) => {
return (prev + percentageSimilar) / (questionIndex + 1);
});
if (questionIndex === game.questions.length - 1) {
endGame();
setHasEnded(true);
return;
}
setQuestionIndex((prev) => prev + 1);
},
onError: (error) => {
console.error(error);
toast({
title: "Something went wrong",
variant: "destructive",
});
},
});
}, [checkAnswer, questionIndex, toast, endGame, game.questions.length]);

随机提取关键字并替换为空格

添加 src/components/play/BlankAnswerInput.tsx:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import React from "react";
import keyword_extractor from "keyword-extractor";

type Props = {
answer: string;
setBlankAnswer: React.Dispatch<React.SetStateAction<string>>;
};

const blank = "_____";

const BlankAnswerInput = ({ answer, setBlankAnswer }: Props) => {
const keywords = React.useMemo(() => {
const words = keyword_extractor.extract(answer, {
language: "english",
remove_digits: true,
return_changed_case: false,
remove_duplicates: false,
});
// mix the keywords and pick 2
const shuffled = words.sort(() => 0.5 - Math.random());
return shuffled.slice(0, 2);
}, [answer]);

const answerWithBlanks = React.useMemo(() => {
const answerWithBlanks = keywords.reduce((acc, curr) => {
return acc.replaceAll(curr, blank);
}, answer);
setBlankAnswer(answerWithBlanks);
return answerWithBlanks;
}, [answer, keywords, setBlankAnswer]);

return (
<div className="flex justify-start w-full mt-4">
<h1 className="text-xl font-semibold">
{/* replace the blanks with input elements */}
{answerWithBlanks.split(blank).map((part, index) => {
return (
<React.Fragment key={index}>
{part}
{index === answerWithBlanks.split(blank).length - 1 ? (
""
) : (
<input
id="user-blank-input"
className="text-center border-b-2 border-black dark:border-white w-28 focus:border-2 focus:border-b-4 focus:outline-none"
type="text"
/>
)}
</React.Fragment>
);
})}
</h1>
</div>
);
};

export default BlankAnswerInput;

如果感兴趣本项目,请访问项目目录: 从零搭建基于 AI 问答平台 - 目录


作者:Bearalise
出处:从零搭建基于 AI 问答平台(14) 定义 OpenEnded组件
版权:本文版权归作者所有
转载:欢迎转载,但未经作者同意,必须保留此段声明,必须在文章中给出原文链接。

请我喝杯咖啡吧~

支付宝
微信