Files
OCS_service/server/utils/answer.ts
T
2026-05-22 15:02:31 +08:00

124 lines
3.8 KiB
TypeScript

// server/utils/answer.ts - OCS 答题提示词构建、答案清洗和响应格式化
/** `/api/search` 从 OCS 请求中最终抽取出的标准参数 */
export interface SearchParams {
/** 题目正文,对应 OCS 的 `${title}` */
title: string;
/** 题型:single、multiple、judgement、completion;未知值也允许透传给提示词 */
type: string;
/** 选项文本,通常是 A/B/C/D 多行内容;没有选项时为空字符串 */
options: string;
}
/** OCS AnswererWrapper handler 期望的成功响应结构 */
export interface OcsSuccessResponse {
code: 1;
/** 返回原题,handler 会将它作为匹配题目展示 */
question: string;
/** 返回最终答案;多选题必须是 `#` 分隔字符串,而不是数组 */
answer: string;
}
/** OCS AnswererWrapper handler 期望的失败响应结构 */
export interface OcsErrorResponse {
code: 0;
/** 只放本地业务文案,不暴露上游或内部异常详情 */
msg: string;
}
/**
* 不同题型对应的补充说明
*
* 这里保留旧 Python 服务的语义:题型只影响提示词,不做硬编码答题
*/
const QUESTION_TYPE_PROMPTS: Record<string, string> = {
single: "这是一道单选题。",
multiple: "这是一道多选题,答案请用#符号分隔。",
judgement: "这是一道判断题,需要回答:正确/对/true/√ 或者 错误/错/false/×。",
completion: "这是一道填空题。"
};
/**
* 全局系统提示词
*
* 重点约束“只给答案、不解释”,因为 OCS 需要的是可直接填入的答案文本,
* 多余解释会降低自动匹配和展示效果
*/
export const ANSWER_SYSTEM_PROMPT =
"你是一个专业的考试答题助手。请直接回答答案,不要解释。选择题只回答选项的内容(如:地球);多选题用#号分隔答案,只回答选项的内容(如中国#世界#地球);判断题只回答: 正确/对/true/√ 或 错误/错/false/×;填空题直接给出答案。";
/**
* 将 OCS 请求参数整理成发给模型的用户提示词
*
* 旧 Python 服务就是在这里把题目、题型和选项拼成结构化文本;
* Nuxt 版保持同样逻辑,方便答案行为尽量一致
*/
export const parseQuestionAndOptions = (
question: string,
options: string,
questionType: string
) => {
let prompt = `问题: ${question}\n`;
const typePrompt = QUESTION_TYPE_PROMPTS[questionType];
if (typePrompt) {
prompt += `${typePrompt}\n`;
}
if (options) {
prompt += `选项:\n${options}\n`;
}
prompt += "请直接给出答案,不要解释。";
return prompt;
};
/**
* 从模型返回文本里提取最终答案
*
* 旧服务对多选题做了一个兼容处理:如果模型返回类似 “答案是 A、B、C”,
* 就尝试抽取 A-F 并改成 OCS 需要的 `A#B#C`。如果模型已经返回 `#`
* 分隔内容,或者不是多选题,则直接返回原文本
*/
export const extractAnswer = (aiResponse: string, questionType: string) => {
const trimmedResponse = aiResponse.trim();
if (questionType === "multiple") {
const lines = trimmedResponse.split("\n");
for (const line of lines) {
if (/[A-D]/iu.test(line) && !line.includes("#")) {
const options = ["A", "B", "C", "D", "E", "F"].filter((option) =>
line.toUpperCase().includes(option)
);
if (options.length > 0) {
return options.join("#");
}
}
}
}
return trimmedResponse;
};
/** 生成 OCS 兼容成功响应,集中在一个函数里避免各 handler 自己拼结构 */
export const formatAnswerForOcs = (
question: string,
answer: string
): OcsSuccessResponse => {
return {
code: 1,
question,
answer
};
};
/** 生成 OCS 兼容失败响应;错误文案由调用处决定 */
export const createOcsErrorResponse = (msg: string): OcsErrorResponse => {
return {
code: 0,
msg
};
};