Files
aurak/server/src/assessment/graph/nodes/interviewer.node.ts
T
Developer 1224a74e63 fix: natural follow-up conversation flow
- Grader: separate followup hint from scoring feedback
- Interviewer: use followup hint directly without prefix/suffix
- Restored standard and choice question presentation paths
2026-05-21 11:53:24 +08:00

89 lines
2.7 KiB
TypeScript

import { AIMessage } from '@langchain/core/messages';
import { RunnableConfig } from '@langchain/core/runnables';
import { EvaluationState } from '../state';
/**
* Node responsible for presenting the current question or follow-up to the user.
*/
export const interviewerNode = async (
state: EvaluationState,
config?: RunnableConfig,
): Promise<Partial<EvaluationState>> => {
const { questions, currentQuestionIndex, shouldFollowUp, messages } = state;
console.log('[InterviewerNode] Entering node...', {
numQuestions: questions?.length,
currentIndex: currentQuestionIndex,
shouldFollowUp,
numMessages: messages?.length,
});
if (!questions || questions.length === 0) {
const isZh = state.language === 'zh';
const isJa = state.language === 'ja';
const msg = isZh
? '很抱歉,我无法为此会话生成任何问题。'
: isJa
? '申し訳ありませんが、このセッションの問題を生成できませんでした。'
: "I'm sorry, I couldn't generate any questions for this session.";
return {
messages: [new AIMessage(msg)],
};
}
const currentQuestion = questions[currentQuestionIndex];
if (currentQuestionIndex >= questions.length) {
return { shouldFollowUp: false };
}
const isZh = state.language === 'zh';
const isJa = state.language === 'ja';
let prompt = '';
if (
shouldFollowUp &&
state.feedbackHistory &&
state.feedbackHistory.length > 0
) {
const lastFeedbackMsg =
state.feedbackHistory[state.feedbackHistory.length - 1];
prompt = lastFeedbackMsg.content.toString();
} else if (currentQuestion.questionType === 'MULTIPLE_CHOICE' && currentQuestion.options?.length > 0) {
const label = isZh
? `问题 ${currentQuestionIndex + 1}`
: isJa
? `質問 ${currentQuestionIndex + 1}`
: `Question ${currentQuestionIndex + 1}`;
const instruction = isZh
? '请选择一个选项'
: isJa
? '選択肢から1つ選んでください'
: 'Please select one option';
prompt = `${label}: ${currentQuestion.questionText}\n\n${instruction}`;
} else {
const label = isZh
? `问题 ${currentQuestionIndex + 1}`
: isJa
? `質問 ${currentQuestionIndex + 1}`
: `Question ${currentQuestionIndex + 1}`;
const instruction = isZh
? '请提供您的回答。'
: isJa
? '回答を入力してください。'
: 'Please provide your answer.';
prompt = `${label}: ${currentQuestion.questionText}\n\n${instruction}`;
}
console.log('[InterviewerNode] Returning question:', { currentQuestionIndex, questionText: currentQuestion?.questionText });
return {
messages: [new AIMessage(prompt)],
shouldFollowUp: false,
};
};