ai面试设计模式的使用

一、由于业务场景需要,面试分为两种模式:数智人播报面试,语音合成面试,后续会陆续衍生出其他形式的面试

所以针对面试行为进行统一整合

二、

定义面试模式枚举

/**
 * 模式面试枚举
 */
public enum InterviewModeEnum {

    /**
     * 数智人面试模式
     */
    VIRTUAL_HUMAN_INTERVIEW("virtualHumanInterview", "数智人面试模式"),


    /**
     * 文本播报面试模式
     */
    TEXT_SPEECH_INTERVIEW("textSpeechInterview", "文本播报面试模式");

    private String value;

    private String description;

    InterviewModeEnum(String value, String description) {
        this.value = value;
        this.description = description;
    }

    public String getValue() {
        return value;
    }

    public String getDescription() {
        return description;
    }


    /**
     * 获取枚举对象
     *
     * @param value 枚举值
     * @return 枚举
     */
    public static InterviewModeEnum getEnum(String value) {
        return getEnumMap().get(value);
    }

    /**
     * 获取值与枚举对象Map
     *
     * @return {@code Map<String, StatisticDimEnum>} 值与枚举对象Map
     */
    public static Map<String, InterviewModeEnum> getEnumMap() {
        return Arrays.stream(InterviewModeEnum.values())
                .collect(Collectors.toMap(InterviewModeEnum::getValue, InterviewModeEnum -> InterviewModeEnum));
    }
}

定义面试相关上下文进行参数传递

/**
 * 面试上下文
 */
@Data
public class InterviewContext {

    /**
     * 面试模式
     *
     * @see InterviewModeEnum
     */
    private InterviewModeEnum interviewMode;

    /**
     * 用户id
     */
    private Integer userId;

    /**
     * 用户Token
     */
    private String userToken;

    /**
     * 用户回答
     */
    private String userAnswer;

    /**
     * 版本
     */
    private Integer version;

    /**
     * asr DTO
     */
    private TencentConnectConfigDTO asrConnectConfigDTO;



    /**
     * 问题DTO
     */
    private AiInterviewQuestionDTO questionDTO;
}

数智人模式面试上下文

/**
 * 数智人面试上下文
 */
@Data
public class VirtualHumanInterviewContext extends InterviewContext {

    /**
     *
     */
    private VhNewSessionResultDTO newSessionResultDTO;

    /**
     *
     */
    private InterviewStatusDTO interviewStatusDTO;
}

文本播报模式面试上下文

/**
 * 文本播报面试上下文
 */
@Data
public class TextSpeechInterviewContext extends InterviewContext {



    /**
     * tts DTO
     */
    private TencentConnectConfigDTO ttsConnectConfigDTO;

    /**
     * 问题id
     */
    private Integer questionId;

    /**
     * 总结集合
     */
    List<InterviewShowContentItemVO> summaryContents;


}

三、定义面试相关操作接口

public interface InterviewManager<T extends InterviewContext> {

    /**
     * 创建面试房间
     *
     * @param context
     */
    void createInterviewRoom(T context);

    /**
     * 开始面试
     *
     * @param context
     */
    void startInterview(T context);

    /**
     * 检查面试状态
     * @param context
     */
    void checkInterviewStatus(T context);

    /**
     * 面试对话
     *
     * @param context
     */
    void talk(T context);


    /**
     * 离开面试房间
     *
     * @param context
     */
    void leaveInterviewRoom(T context);

    /**
     * 面试总结
     * @param context
     */
    void summaryInterview(T context);
}

定义面试接口方法模板

**
 * 面试模板
 */
@Slf4j
public abstract class InterviewManagerTemplate<T extends InterviewContext> implements InterviewManager<T> {


    @Override
    public void createInterviewRoom(T context) {
        //执行创建面试间
        toCreateInterviewRoom(context);
    }

    @Override
    public void startInterview(T context) {
        toStartInterview(context);
    }

    @Override
    public void checkInterviewStatus(T context) {
        toCheckInterviewStatus(context);
    }

    @Override
    public void talk(T context) {
        toTalk(context);
    }


    @Override
    public void leaveInterviewRoom(T context) {
        toLeaveInterviewRoom(context);
    }

    @Override
    public void summaryInterview(T context) {
        toSummaryInterview(context);
    }

    /**
     * 执行创建面时间
     *
     * @param context
     */
    protected abstract void toCreateInterviewRoom(T context);

    /**
     * 执行开始面试
     *
     * @param context
     */
    protected abstract void toStartInterview(T context);

    /**
     * 执行对话
     *
     * @param context
     */
    protected abstract void toTalk(T context);

    /**
     * 执行检查状态
     *
     * @param context
     */
    protected abstract void toCheckInterviewStatus(T context);

    /**
     * 执行离开面试间
     *
     * @param context
     */
    protected abstract void toLeaveInterviewRoom(T context);

    /**
     * 执行面试总结
     *
     * @param context
     */
    protected abstract void toSummaryInterview(T context);
}

定义抽象面试公共基类


/**
 * 抽象公用基类
 */
@Slf4j
public abstract class AbstractInterviewManager<T extends InterviewContext> extends InterviewManagerTemplate<T> {

    @Autowired
    private BotUtil botUtil;

    @Autowired
    private InterviewSessionManager interviewSessionManager;


    @Autowired
    private AiInterviewAssembler aiInterviewAssembler;

    @Autowired
    private AiInterviewService aiInterviewService;

    @Autowired
    private InterviewStatusManager interviewStatusManager;

    @Autowired
    private AiInterviewQuestHandler aiInterviewQuestHandler;

    /**
     * 面试botId
     */
    @Value("${ai-bot.interview-bot-id}")
    private String BOT_ID;

    /**
     * 面试数值人prompt
     */
    @Value("${ai-bot.interview-prompt-id}")
    private Integer PROMPT_ID;

    @Override
    protected void toSummaryInterview(T context) {
        log.info("AbstractInterviewManager.toSummaryInterview-->context:{}", context);
    }

    @Override
    protected void toCheckInterviewStatus(T context) {
        log.info("AbstractInterviewManager.toCheckInterviewStatus-->context:{}", context);
    }

    /**
     * 初始化会话bot
     */
    protected String initChatBot(Integer userId, AiInterviewQuestionDTO questionDTO) {
        log.info("AbstractInterviewManager.initChatBot-->userId:{},questionDTO:{}", userId, questionDTO);
        String chatId = null;
        try {
            JSONObject params = new JSONObject();
            params.put(TencentConstants.QUESTION, questionDTO.getQuestion());
            List<QuestionInfo> otherQuestion = questionDTO.getOtherQuestion();
            if (CollectionUtils.isNotEmpty(otherQuestion)) {
                StringBuilder sb = new StringBuilder();
                for (int i = Constants.ONE; i <= otherQuestion.size(); i++) {
                    sb.append("问题");
                    sb.append(i);
                    sb.append(StringPool.COLON);
                    sb.append(otherQuestion.get(i - Constants.ONE).getQuestion());
                    sb.append(Constants.SEPETR);
                }
                params.put(TencentConstants.QUESTION_B, sb.toString());
            }
            log.info("AbstractInterviewManager.initChatBot-->params:{}", params);
            chatId = botUtil.changePrompt(BOT_ID, userId, PROMPT_ID, params);
        } catch (Exception e) {
            log.error("AbstractInterviewManager.initChatBot-->", e);
        }
        log.info("AbstractInterviewManager.initChatBot-->chatId:{}", chatId);
        return chatId;
    }


    /**
     * 发送会话到bot
     *
     * @param userId
     * @param content
     * @param token
     * @return
     */
    protected String sendAnswerToBot(AiInterviewQuestionDTO questionDTO, Integer userId, String content, String token) {
        String botMessage = null;
        InterviewSessionDTO sessionDTO = this.getAndCheckInterviewSession(userId);
        try {
            String chatId = sessionDTO.getBotChatId();
            log.info("AbstractInterviewManager.sendAnswerToBot-->start!content:{}", content);
            //调用gtp回答内容
            botMessage = botUtil.getBotContent(userId, BOT_ID, content, token, chatId);
            log.info("AbstractInterviewManager.sendAnswerToBot-->end!botMessage:{}", botMessage);
        } catch (Exception e) {
            log.error("AbstractInterviewManager.sendAnswerToBot-->", e);
        }
        //数智人的回答表示追问
        this.logRecord(questionDTO.getResumeId(), userId, questionDTO.getQuestionId(), AiInterviewChatRoleEnum.GPT.getCode(), AiInterviewChatTypeEnum.PROBE.getCode(), botMessage);
        return botMessage;
    }


    /**
     * getAndCheckInterviewSession
     *
     * @param userId
     * @return
     */
    protected InterviewSessionDTO getAndCheckInterviewSession(Integer userId) {
        InterviewSessionDTO interviewSessionDTO = interviewSessionManager.get(userId);
        if (ObjectUtils.isEmpty(interviewSessionDTO)) {
            log.error("AbstractInterviewManager.getAndCheckInterviewSession-->interviewSessionDTO is empty!");
            throw new BusinessException(HttpResultEnum.PRODUCT_STATE_ERROR);
        }
        log.info("AbstractInterviewManager.getAndCheckInterviewSession-->interviewSessionDTO:{}", interviewSessionDTO);
        return interviewSessionDTO;
    }

    /**
     * getInterviewStatus
     *
     * @param userId
     * @return
     */
    protected InterviewStatusDTO getInterviewStatus(Integer userId) {
        InterviewStatusDTO interviewStatusDTO = interviewStatusManager.get(userId);
        if (ObjectUtils.isEmpty(interviewStatusDTO)) {
            log.error("AbstractInterviewManager.getInterviewStatus-->interviewStatusDTO is empty!");
            throw new BusinessException(HttpResultEnum.PRODUCT_STATE_ERROR);
        }
        return interviewStatusDTO;
    }


    /**
     * submitCreateGptReport
     *
     * @param userId
     * @param userToken
     * @param questionId
     * @param isFinish
     */
    protected void submitCreateGptReport(Integer userId, String userToken, Integer questionId, Boolean isFinish, Integer version) {
        try {
            log.info("AbstractInterviewManager.submitCreateGptReport-->userId:{},questionId:{},isFinish:{},version:{}", userId, questionId, isFinish, version);
            //生成报告
            aiInterviewQuestHandler.createGptReport(userId, userToken, questionId, isFinish, version);
        } catch (Exception e) {
            log.error("AbstractInterviewManager.submitCreateGptReport-->", e);
        }
    }


    /**
     * getQuestionShowContentItemVO
     *
     * @param resumeId
     * @param questionInfo
     * @return
     */
    protected InterviewShowContentItemVO getQuestionShowContentItemVO(Integer resumeId, QuestionInfo questionInfo) {
        InterviewShowContentItemVO contentItemVO = null;
        Integer questionId = questionInfo.getQuestionId();
        List<AiInterviewRecord> suggestionList = aiInterviewService.getSuggestion(resumeId, questionId);
        if (CollectionUtils.isEmpty(suggestionList)) {
            log.warn("AbstractInterviewManager.getQuestionShowContentItemVO-->suggestionDTOS is empty!");
            return contentItemVO;
        }
        String suggestion = null;
        String advantage = null;
        String summary = null;
        for (AiInterviewRecord suggestionItem : suggestionList) {
            AiInterviewChatTypeEnum chatType = AiInterviewChatTypeEnum.parse(suggestionItem.getChatType());
            switch (chatType) {
                case SUGGESTION:
                    suggestion = suggestionItem.getTextContent();
                    break;
                case ADVANTAGE:
                    advantage = suggestionItem.getTextContent();
                    break;
                case SUMMARY:
                    summary = suggestionItem.getTextContent();
                    break;
            }
        }
        contentItemVO = aiInterviewAssembler.buildInterviewShowContentItemVO(questionId, questionInfo.getQuestion(), suggestion, advantage, summary);
        log.info("AbstractInterviewManager.getQuestionShowContentItemVO-->contentItemVO:{}", contentItemVO);
        return contentItemVO;
    }


    /**
     * 尝试获取用户问题
     *
     * @param userId
     * @return
     */
    protected AiInterviewQuestionDTO tryQueryUserGptQuestion(Integer userId) {
        AiInterviewQuestionDTO questionDTO = null;
        Integer times = 0;
        try {
            do {
                questionDTO = aiInterviewService.queryUserGptQuestion(userId);
                if (ObjectUtils.isNotEmpty(questionDTO) || times > 8) {
                    log.info("AbstractInterviewManager.tryQueryUserGptQuestion-->questionDTO:{},times:{}", questionDTO, times);
                    break;
                }
                Thread.sleep(1000);
                times++;
            } while (true);
        } catch (Exception e) {
            log.error("AbstractInterviewManager.tryQueryUserGptQuestion-->", e);
        }
        return questionDTO;
    }

    /**
     * 会话记录
     *
     * @param interviewId
     * @param userId
     * @param questionId
     * @param chatRole
     * @param chatType
     * @param textContent
     */
    protected void logRecord(Integer interviewId, Integer userId, Integer questionId, Integer chatRole, Integer chatType, String textContent) {
        try {
            AiInterviewRecordRequest recordRequest = aiInterviewAssembler.buildAiInterviewRecordRequest(interviewId, userId, questionId, chatRole, chatType, textContent);
            log.info("AbstractInterviewManager.logRecord-->recordRequest:{}", recordRequest);
            aiInterviewService.record(recordRequest);
        } catch (Exception e) {
            log.error("AbstractInterviewManager.talkRecord-->", e);
        }
    }
}

具体面试模式实现类

数智人模式执行器


/**
 * 数智人面试执行器
 */
@Slf4j
@Component("virtualHumanInterviewActuator")
public class VirtualHumanInterviewActuator extends AbstractInterviewManager<VirtualHumanInterviewContext> {

    @Autowired
    private VirtualHumanPerformer virtualHumanPerformer;

    @Autowired
    private AutomaticSpeechRecognitionPerformer automaticSpeechRecognitionPerformer;

    @Autowired
    private InterviewStatusManager interviewStatusManager;

    @Autowired
    private InterviewSessionManager interviewSessionManager;

    @Autowired
    private InterviewSessionUserRelaManger interviewSessionUserRelaManger;

    @Autowired
    private InterviewShowContentManager interviewShowContentManager;

    @Autowired
    private AiInterviewAssembler aiInterviewAssembler;

    @Autowired
    private AiInterviewService aiInterviewService;

    @Autowired
    private Executor interviewSchedulerExecutor;

    @Autowired
    private InterviewStatusChecker answerOverChecker;

    @Autowired
    private InterviewStatusChecker interviewOverChecker;

    @Override
    protected void toCreateInterviewRoom(VirtualHumanInterviewContext context) {
        Integer userId = context.getUserId();
        VhNewSessionResultDTO newSessionResultDTO = virtualHumanPerformer.newSession();
        if (ObjectUtils.isEmpty(newSessionResultDTO)) {
            log.warn("VirtualHumanInterviewActuator.toCreateInterviewRoom-->newSessionResultDTO is empty!");
            newSessionResultDTO = new VhNewSessionResultDTO();
        }
        String sessionId = newSessionResultDTO.getSessionId();
        //websocket监听数智人
        virtualHumanPerformer.commandChannelWebsocket(sessionId);
        InterviewSessionDTO interviewSessionDTO = aiInterviewAssembler.buildInterviewSessionDTO(userId, context.getInterviewMode().getValue(), sessionId);
        //维护用户会话缓存
        interviewSessionManager.update(userId, interviewSessionDTO);
        //维护会话与用户的关系缓存
        interviewSessionUserRelaManger.update(sessionId, userId);
        TencentConnectConfigDTO connectConfigDTO = automaticSpeechRecognitionPerformer.generalAsrWebSocketUrl();
        context.setNewSessionResultDTO(newSessionResultDTO);
        context.setAsrConnectConfigDTO(connectConfigDTO);
    }

    @Override
    protected void toStartInterview(VirtualHumanInterviewContext context) {
        Integer userId = context.getUserId();
        //step 1/init interviewStatus
        InterviewStatusDTO interviewStatusDTO = aiInterviewAssembler.buildInterviewStatusDTO(userId, false, true, true, false, false);
        interviewStatusManager.update(userId, interviewStatusDTO);

        //初始化展示内容信息
        InterviewShowContentVO contentVO = aiInterviewAssembler.buildInterviewShowContentVO(userId, Lists.newLinkedList(), Boolean.FALSE);
        interviewShowContentManager.update(userId, contentVO);

        //开始会话
        this.startVhSession(userId);

        //发送开场白
        this.sendCommandToVh(userId, TencentConstants.WELCOME_WORD);

        AiInterviewQuestionDTO questionDTO = this.askNextQuestion(userId, context.getUserToken(), true, context.getVersion());
        context.setQuestionDTO(questionDTO);

    }

    @Override
    protected void toTalk(VirtualHumanInterviewContext context) {
        Integer userId = context.getUserId();
        //获取用户回答
        String userAnswer = context.getUserAnswer();
        InterviewSessionDTO sessionDTO = this.getAndCheckInterviewSession(userId);
        AiInterviewQuestionDTO sessionQuestionDTO = ObjectUtils.isEmpty(sessionDTO.getQuestionDTO()) ? new AiInterviewQuestionDTO() : sessionDTO.getQuestionDTO();
        Integer chatType = AiInterviewChatTypeEnum.DIALOGUE.getCode();
        //为空表示为追问回答
        if (ObjectUtils.isEmpty(sessionQuestionDTO.getIsProbe())) {
            chatType = AiInterviewChatTypeEnum.PROBE.getCode();
        }
        //记录用户对话
        this.logRecord(sessionQuestionDTO.getResumeId(), userId, sessionQuestionDTO.getQuestionId(), AiInterviewChatRoleEnum.USER.getCode(), chatType, userAnswer);
        if (BooleanUtils.isTrue(sessionQuestionDTO.getIsProbe())) {
            //需要追问
            //调用gtp回答内容
            InterviewStatusDTO enterStatus = this.getInterviewStatus(userId);
            enterStatus.setIsGtpAnswerOver(false);
            interviewStatusManager.update(userId, enterStatus);
            String botMessage = this.sendAnswerToBot(sessionQuestionDTO, userId, userAnswer, context.getUserToken());
            InterviewStatusDTO exitStatus = this.getInterviewStatus(userId);
            exitStatus.setIsGtpAnswerOver(true);
            interviewStatusManager.update(userId, exitStatus);
            //发送数智人播报,数智人播报需去除特殊符号
            this.sendCommandToVh(userId, new String(botMessage));
            //追问结束
            sessionQuestionDTO.setQuestion(botMessage);
            //维护为null,用户回答后区分为追问回答
            sessionQuestionDTO.setIsProbe(null);
            sessionDTO.setQuestionDTO(sessionQuestionDTO);
            interviewSessionManager.update(userId, sessionDTO);
            context.setQuestionDTO(sessionQuestionDTO);
        } else {
            //下一个问题
            AiInterviewQuestionDTO questionDTO = this.askNextQuestion(userId, context.getUserToken(), false, context.getVersion());
            context.setQuestionDTO(questionDTO);
        }
    }

    @Override
    protected void toCheckInterviewStatus(VirtualHumanInterviewContext context) {
        Integer userId = context.getUserId();
        InterviewStatusDTO interviewStatusDTO = this.getInterviewStatus(userId);
        try {
            Boolean isInterviewOver = interviewOverChecker.check(userId);
            Boolean isAnswerOver = answerOverChecker.check(userId);
            interviewStatusDTO.setIsAnswerOver(isAnswerOver);
            //系统验证结果设置
            interviewStatusDTO.setIsInterviewOver(isInterviewOver);
        } catch (Exception e) {
            log.error("VirtualHumanInterviewActuator.checkInterviewStatus-->", e);
        }
        context.setInterviewStatusDTO(interviewStatusDTO);
    }

    @Override
    protected void toLeaveInterviewRoom(VirtualHumanInterviewContext context) {
        this.releaseSource(context.getUserId());
    }

    /**
     * 发送数智人开始说话
     *
     * @param message
     */
    private void sendCommandToVh(Integer userId, String message) {
        if (ObjectUtils.isEmpty(message)) {
            log.warn("VirtualHumanInterviewActuator.sendCommandToVh-->message is empty!");
            return;
        }
        message = message.replaceAll(TencentConstants.QUESTION_END_TAG, "");
        message = message.replaceAll(TencentConstants.QUESTION_LINE_TAG, "");
        message = message.replaceAll(TencentConstants.QUESTION_LINE_END_TAG, "");
        virtualHumanPerformer.trySendCommand(userId, message);
    }

    /**
     * 开启会话
     *
     * @param userId
     */
    private void startVhSession(Integer userId) {
        InterviewSessionDTO interviewSessionDTO = this.getAndCheckInterviewSession(userId);
        virtualHumanPerformer.startSession(interviewSessionDTO.getVhSessionId());
    }


    /**
     * 提问下一个问题
     *
     * @param userId
     * @param userToken
     * @param isFirst   是否未第一次提问
     * @return
     */
    private AiInterviewQuestionDTO askNextQuestion(Integer userId, String userToken, Boolean isFirst, Integer version) {
        AiInterviewQuestionDTO questionDTO = aiInterviewService.queryUserGptQuestion(userId);
        log.info("VirtualHumanInterviewActuator.askNextQuestion-->userId:{},isFirst:{},questionDTO:{},version:{}", userId, isFirst, questionDTO, version);
        if (isFirst) {
            //获取简历id对应面试id
            Integer resumeId = ObjectUtils.isNotEmpty(questionDTO) ? questionDTO.getResumeId() : null;
            this.logRecord(resumeId, userId, null, null, AiInterviewChatTypeEnum.BEGIN.getCode(), StringUtils.EMPTY);
        }
        String question = null;
        Integer resumeId = null;
        Boolean isFinish = false;
        InterviewSessionDTO sessionDTO = this.getAndCheckInterviewSession(userId);
        AiInterviewQuestionDTO lastQuestionDTO = sessionDTO.getQuestionDTO();
        if (ObjectUtils.isNotEmpty(questionDTO)) {
            //维护上下文参数信息
            question = questionDTO.getQuestion();
            resumeId = questionDTO.getResumeId();
            //记录问题
            this.logRecord(questionDTO.getResumeId(), userId, questionDTO.getQuestionId(), AiInterviewChatRoleEnum.GPT.getCode(), AiInterviewChatTypeEnum.DIALOGUE.getCode(), question);
            sessionDTO.setQuestionDTO(questionDTO);
            if (BooleanUtils.isTrue(questionDTO.getIsProbe())) {
                //初始化bot信息
                String chatId = this.initChatBot(userId, questionDTO);
                sessionDTO.setBotChatId(chatId);
            }
            interviewSessionManager.update(userId, sessionDTO);
        } else {
            isFinish = true;
        }
        if (ObjectUtils.isEmpty(resumeId) && ObjectUtils.isNotEmpty(lastQuestionDTO)) {
            resumeId = lastQuestionDTO.getResumeId();
        }
        if (isFinish) {
            //面试结束,更新对应状态
            InterviewStatusDTO interviewStatusDTO = this.getInterviewStatus(userId);
            interviewStatusDTO.setIsGptFinish(true);
            log.info("VirtualHumanInterviewActuator.askNextQuestion-->interviewStatusDTO:{}", interviewStatusDTO);
            interviewStatusManager.update(userId, interviewStatusDTO);
            //结束日志保存
            this.logRecord(resumeId, userId, null, null, AiInterviewChatTypeEnum.END.getCode(), StringUtils.EMPTY);
        }
        if (ObjectUtils.isNotEmpty(question)) {
            //发送播报
            this.sendCommandToVh(userId, question);
        }
        if (ObjectUtils.isNotEmpty(lastQuestionDTO)) {
            this.submitCreateGptReport(userId, userToken, lastQuestionDTO.getQuestionId(), isFinish, version);
        }
        //进行总结
        //问题结束,需要进行面试总结
        if (isFinish) {
            interviewSchedulerExecutor.execute(() -> {
                log.info("VirtualHumanInterviewActuator.askNextQuestion-->interviewSchedulerExecutor userId:{}", userId);
                summaryInterview(userId);
            });
        }
        return questionDTO;
    }


    /**
     * 进行面试总结
     *
     * @param userId
     */
    private void summaryInterview(Integer userId) {
        log.info("VirtualHumanInterviewActuator.summaryInterview-->userId:{}", userId);
        try {
            //发送面试播报引言
            this.sendCommandToVh(userId, TencentConstants.SUMMARY_FOREWORD);
            InterviewSessionDTO sessionDTO = this.getAndCheckInterviewSession(userId);
            //获取简历id
            Integer resumeId = ObjectUtils.isEmpty(sessionDTO.getQuestionDTO()) ? -1 : sessionDTO.getQuestionDTO().getResumeId();
            List<QuestionInfo> questionInfos = aiInterviewService.queryAllQuestionByResumeId(resumeId);
            if (CollectionUtils.isNotEmpty(questionInfos)) {
                questionInfos.forEach(questionInfo -> {
                    InterviewShowContentItemVO contentItemVO = getQuestionShowContentItemVO(resumeId, questionInfo);
                    if (ObjectUtils.isEmpty(contentItemVO)) {
                        return;
                    }
                    InterviewShowContentVO contentVO = interviewShowContentManager.get(userId);
                    contentVO.setIsLast(Boolean.FALSE);
                    LinkedList<InterviewShowContentItemVO> contents = contentVO.getContents();
                    if (ObjectUtils.isEmpty(contents)) {
                        contents = Lists.newLinkedList();
                    }
                    contents.add(contentItemVO);
                    contentVO.setContents(contents);

                    log.info("VirtualHumanInterviewActuator.summaryInterview-->contentVO:{}", contentVO);
                    //存放展示内容信息
                    interviewShowContentManager.update(userId, contentVO);
                    this.sendCommandToVh(userId, contentItemVO.getPlayContent());
                });
            }
        } catch (Exception e) {
            log.error("VirtualHumanInterviewActuator.summaryInterview-->", e);
        }
        //维护展示内容信息
        InterviewShowContentVO contentVO = interviewShowContentManager.get(userId);
        contentVO.setIsLast(Boolean.TRUE);
        log.info("VirtualHumanInterviewActuator.summaryInterview-->contentVO:{}", contentVO);
        interviewShowContentManager.update(userId, contentVO);

        InterviewStatusDTO interviewStatusDTO = this.getInterviewStatus(userId);
        interviewStatusDTO.setIsSummaryOver(true);
        interviewStatusManager.update(userId, interviewStatusDTO);
        //面试结束
        String content = TencentConstants.COMPLETION_WORD;
        //发送播报
        this.sendCommandToVh(userId, content);
    }


    /**
     * 释放资源
     *
     * @param userId
     */
    private void releaseSource(Integer userId) {
        log.info("VirtualHumanInterviewActuator.releaseSource-->userId:{}", userId);
        try {
            InterviewSessionDTO sessionDTO = this.getAndCheckInterviewSession(userId);
            String vhSessionId = sessionDTO.getVhSessionId();
            //关闭数智人会话
            virtualHumanPerformer.closeCommandChannelWebsocket(vhSessionId);
            virtualHumanPerformer.closeSession(vhSessionId);
            interviewSessionManager.remove(userId);
            interviewStatusManager.remove(userId);
            interviewSessionUserRelaManger.remove(vhSessionId);
        } catch (Exception e) {
            log.error("VirtualHumanInterviewActuator.releaseSource-->", e);
        }
    }
}

文本播报模式执行器

/**
 * 文本播报面试执行器
 */
@Slf4j
@Component("textSpeechInterviewActuator")
public class TextSpeechInterviewActuator extends AbstractInterviewManager<TextSpeechInterviewContext> {

    @Autowired
    private TextToSpeechPerformer textToSpeechPerformer;

    @Autowired
    private AutomaticSpeechRecognitionPerformer automaticSpeechRecognitionPerformer;

    @Autowired
    private AiInterviewAssembler aiInterviewAssembler;

    @Autowired
    private InterviewSessionManager interviewSessionManager;

    @Autowired
    private AiInterviewService aiInterviewService;


    @Override
    protected void toCreateInterviewRoom(TextSpeechInterviewContext context) {
        Integer userId = context.getUserId();
        TencentConnectConfigDTO ttsConnectConfigDTO = textToSpeechPerformer.generalTtsWebSocketUrl();
        TencentConnectConfigDTO asrConnectConfigDTO = automaticSpeechRecognitionPerformer.generalAsrWebSocketUrl();
        context.setAsrConnectConfigDTO(asrConnectConfigDTO);
        context.setTtsConnectConfigDTO(ttsConnectConfigDTO);
        InterviewSessionDTO interviewSessionDTO = aiInterviewAssembler.buildInterviewSessionDTO(userId, context.getInterviewMode().getValue(), null);
        //维护用户会话缓存
        interviewSessionManager.update(userId, interviewSessionDTO);
    }

    @Override
    protected void toStartInterview(TextSpeechInterviewContext context) {
        Integer userId = context.getUserId();
        AiInterviewQuestionDTO questionDTO = this.getNextQuestion(userId, context.getUserToken(), true, context.getVersion());
        context.setQuestionDTO(questionDTO);
    }

    @Override
    protected void toTalk(TextSpeechInterviewContext context) {
        log.info("TextSpeechInterviewActuator.toTalk-->context:{}", context);
        Integer userId = context.getUserId();
        //获取用户回答
        String userAnswer = context.getUserAnswer();
        InterviewSessionDTO sessionDTO = this.getAndCheckInterviewSession(userId);
        AiInterviewQuestionDTO sessionQuestionDTO = ObjectUtils.isEmpty(sessionDTO.getQuestionDTO()) ? new AiInterviewQuestionDTO() : sessionDTO.getQuestionDTO();
        Integer chatType = AiInterviewChatTypeEnum.DIALOGUE.getCode();
        //为空表示为追问回答
        if (ObjectUtils.isEmpty(sessionQuestionDTO.getIsProbe())) {
            chatType = AiInterviewChatTypeEnum.PROBE.getCode();
        }
        //记录用户对话
        this.logRecord(sessionQuestionDTO.getResumeId(), userId, sessionQuestionDTO.getQuestionId(), AiInterviewChatRoleEnum.USER.getCode(), chatType, userAnswer);
        if (BooleanUtils.isTrue(sessionQuestionDTO.getIsProbe())) {
            //需要追问
            //调用gtp回答内容
            String botMessage = this.sendAnswerToBot(sessionQuestionDTO, userId, userAnswer, context.getUserToken());
            //追问结束
            sessionQuestionDTO.setQuestion(botMessage);
            //维护为null,用户回答后区分为追问回答
            sessionQuestionDTO.setIsProbe(null);
            sessionDTO.setQuestionDTO(sessionQuestionDTO);
            interviewSessionManager.update(userId, sessionDTO);
            context.setQuestionDTO(sessionQuestionDTO);
        } else {
            //下一个问题
            AiInterviewQuestionDTO questionDTO = this.getNextQuestion(userId, context.getUserToken(), false, context.getVersion());
            context.setQuestionDTO(questionDTO);
        }
    }

    @Override
    protected void toLeaveInterviewRoom(TextSpeechInterviewContext context) {
        this.releaseSource(context.getUserId());
    }

    @Override
    protected void toSummaryInterview(TextSpeechInterviewContext context) {
        log.info("TextSpeechInterviewActuator.toSummaryInterview-->context:{}", context);
        InterviewSessionDTO sessionDTO = this.getAndCheckInterviewSession(context.getUserId());
        //获取简历id
        Integer resumeId = ObjectUtils.isEmpty(sessionDTO.getQuestionDTO()) ? -1 : sessionDTO.getQuestionDTO().getResumeId();
        List<QuestionInfo> questionInfos = aiInterviewService.queryAllQuestionByResumeId(resumeId);
        if (CollectionUtils.isEmpty(questionInfos)) {
            log.warn("TextSpeechInterviewActuator.toSummaryInterview-->questionInfos is empty!");
            return;
        }
        Integer questionId = context.getQuestionId();
        if (ObjectUtils.isNotEmpty(questionId)) {
            questionInfos.removeIf(temp -> !Objects.equals(temp.getQuestionId(), questionId));
        }
        if (CollectionUtils.isEmpty(questionInfos)) {
            log.warn("TextSpeechInterviewActuator.toSummaryInterview--> remove after questionInfos is empty!questionId:{}", questionId);
            return;
        }
        List<InterviewShowContentItemVO> summaryContents = Lists.newArrayList();
        questionInfos.forEach(questionInfo -> {
            InterviewShowContentItemVO contentItemVO = getQuestionShowContentItemVO(resumeId, questionInfo);
            if (ObjectUtils.isEmpty(contentItemVO)) {
                log.warn("TextSpeechInterviewActuator.toSummaryInterview-->contentItemVO is empty!");
                return;
            }
            summaryContents.add(contentItemVO);
        });
        context.setSummaryContents(summaryContents);
    }

    /**
     * @param userId
     * @param isFirst
     * @return
     */
    private AiInterviewQuestionDTO getNextQuestion(Integer userId, String userToken, Boolean isFirst, Integer version) {
        AiInterviewQuestionDTO questionDTO = aiInterviewService.queryUserGptQuestion(userId);
        log.info("TextSpeechInterviewActuator.getNextQuestion-->userId:{},isFirst:{},questionDTO:{},version:{}", userId, isFirst, questionDTO, version);
        if (isFirst) {
            //获取简历id对应面试id
            Integer resumeId = ObjectUtils.isNotEmpty(questionDTO) ? questionDTO.getResumeId() : null;
            this.logRecord(resumeId, userId, null, null, AiInterviewChatTypeEnum.BEGIN.getCode(), StringUtils.EMPTY);
        }
        Integer resumeId = null;
        Boolean isFinish = false;
        InterviewSessionDTO sessionDTO = this.getAndCheckInterviewSession(userId);
        AiInterviewQuestionDTO lastQuestionDTO = sessionDTO.getQuestionDTO();
        if (ObjectUtils.isNotEmpty(questionDTO)) {
            resumeId = questionDTO.getResumeId();
            //维护上下文参数信息
            //记录问题
            this.logRecord(questionDTO.getResumeId(), userId, questionDTO.getQuestionId(), AiInterviewChatRoleEnum.GPT.getCode(), AiInterviewChatTypeEnum.DIALOGUE.getCode(), questionDTO.getQuestion());
            sessionDTO.setQuestionDTO(questionDTO);
            if (BooleanUtils.isTrue(questionDTO.getIsProbe())) {
                //初始化bot信息
                String chatId = this.initChatBot(userId, questionDTO);
                sessionDTO.setBotChatId(chatId);
            }
            interviewSessionManager.update(userId, sessionDTO);
        } else {
            isFinish = true;
        }
        if (ObjectUtils.isEmpty(resumeId) && ObjectUtils.isNotEmpty(lastQuestionDTO)) {
            resumeId = lastQuestionDTO.getResumeId();
        }
        if (isFinish) {
            //结束日志保存
            this.logRecord(resumeId, userId, null, null, AiInterviewChatTypeEnum.END.getCode(), StringUtils.EMPTY);
        }
        if (ObjectUtils.isNotEmpty(lastQuestionDTO)) {
            this.submitCreateGptReport(userId, userToken, lastQuestionDTO.getQuestionId(), isFinish, version);
        }
        return questionDTO;
    }

    /**
     * 释放资源
     *
     * @param userId
     */
    private void releaseSource(Integer userId) {
        log.info("TextSpeechInterviewActuator.releaseSource-->userId:{}", userId);
        try {
            interviewSessionManager.remove(userId);
        } catch (Exception e) {
            log.error("TextSpeechInterviewActuator.releaseSource-->", e);
        }
    }

}

统一面试入口执行器 


/**
 * 面试入口执行器
 */
@Slf4j
@Component
public class InterviewActuator implements InterviewManager<InterviewContext> {

    private Map<InterviewModeEnum, InterviewManager> actuators;

    @Autowired
    public InterviewActuator(Map<InterviewModeEnum, InterviewManager> actuators) {
        this.actuators = actuators;
    }


    @Override
    public void createInterviewRoom(InterviewContext context) {
        this.getActuator(context).createInterviewRoom(context);
    }

    @Override
    public void startInterview(InterviewContext context) {
        this.getActuator(context).startInterview(context);
    }

    @Override
    public void checkInterviewStatus(InterviewContext context) {
        this.getActuator(context).checkInterviewStatus(context);
    }

    @Override
    public void talk(InterviewContext context) {
        this.getActuator(context).talk(context);
    }


    @Override
    public void leaveInterviewRoom(InterviewContext context) {
        this.getActuator(context).leaveInterviewRoom(context);
    }

    @Override
    public void summaryInterview(InterviewContext context) {
        this.getActuator(context).summaryInterview(context);
    }

    /**
     * @param context
     * @return
     */
    private InterviewManager getActuator(InterviewContext context) {
        if (Objects.isNull(context) || Objects.isNull(context.getInterviewMode()) || !actuators.containsKey(context.getInterviewMode())) {
            log.error("InterviewActuator.getActuator-->context:{}", context);
            throw new BusinessException(HttpResultEnum.BUSINESS_ERROR);
        }
        return actuators.get(context.getInterviewMode());
    }
}

面试模式配置类

/**
 * 面试配置类
 */
@Configuration
public class InterviewConfig {

    @Bean
    public Map<InterviewModeEnum, InterviewManager> actuators(InterviewManager virtualHumanInterviewActuator,
                                                              InterviewManager textSpeechInterviewActuator) {
        return new ImmutableMap.Builder<InterviewModeEnum, InterviewManager>()
                .put(InterviewModeEnum.VIRTUAL_HUMAN_INTERVIEW, virtualHumanInterviewActuator)
                .put(InterviewModeEnum.TEXT_SPEECH_INTERVIEW, textSpeechInterviewActuator)
                .build();
    }
}

调用示例,面试对话

    @Override
    public InterviewQuestionVO receiveAsrMessage(InterviewAsrMessageRequest messageRequest) {
        log.info("AiInterviewFacadeImpl.receiveAsrMessage-->messageRequest:{}", messageRequest);
        if (ObjectUtils.isEmpty(messageRequest) || ObjectUtils.isEmpty(messageRequest.getMessage())) {
            log.warn("AiInterviewFacadeImpl.receiveAsrMessage-->messageRequest is empty!");
            return null;
        }
        Integer userId = this.getAndCheckUserId();
        InterviewModeEnum interviewMode = this.getInterviewMode(null);
        InterviewContext context = aiInterviewAssembler.buildInterviewContext(interviewMode, userId, messageRequest.getUserToken(), messageRequest.getMessage(), messageRequest.getVersion());
        interviewActuator.talk(context);
        return aiInterviewAssembler.convertToInterviewQuestionVO(context.getQuestionDTO());
    }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值