当前位置: 首页 > ops >正文

典籍知识问答重新生成逻辑修改

1.需求

希望点击重新生成后就在原回答上进行修改,不要再生成新的回答

2.后端代码修改

//QAService

// 修改 regenerateResponse 方法
@Transactional
public QaMessage regenerateResponse(Long messageId) {
    QaMessage original = messageRepository.findById(messageId)
            .orElseThrow(() -> new RuntimeException("Message not found"));

    // 验证:只能重新生成AI消息
    if (!"assistant".equals(original.getRole())) {
        throw new IllegalArgumentException("只能重新生成AI消息");
    }

    // 生成新的回复
    String improvedContent = aiService.getEnhancedResponse(original);
    
    // 更新原消息内容
    original.setContent(improvedContent);
    
    // 重置反馈状态
    original.setFeedback(null);
    
    // 删除所有后续消息(子消息)
    deleteChildrenMessages(original);
    
    return messageRepository.save(original);
}

// 新增辅助方法:递归删除子消息
private void deleteChildrenMessages(QaMessage message) {
    List<QaMessage> children = message.getQaMessages();
    for (QaMessage child : children) {
        deleteChildrenMessages(child);
        messageRepository.delete(child);
    }
    message.getQaMessages().clear();
}

// 修改 streamRegenerateResponse 方法
public void streamRegenerateResponse(Long messageId, SseEmitter emitter) {
    try {
        QaMessage original = messageRepository.findById(messageId)
                .orElseThrow(() -> new RuntimeException("Message not found"));

        // 验证:只能重新生成AI消息
        if (!"assistant".equals(original.getRole())) {
            throw new IllegalArgumentException("只能重新生成AI消息");
        }

        // 删除所有后续消息
        deleteChildrenMessages(original);
        
        // 重置原消息内容
        original.setContent("");
        original.setFeedback(null);
        messageRepository.save(original);

        // 生成改进提示(使用原消息的父消息)
        QaMessage parentMessage = original.getParent();
        String prompt = parentMessage != null ? parentMessage.getContent() : original.getContent();

        aiService.streamAIResponse(prompt, new AIService.StreamCallback() {
            final StringBuilder fullContent = new StringBuilder();

            @Override
            public void onStart() throws IOException {
                // 发送原消息ID
                emitter.send(SseEmitter.event().name("START").data(original.getId()));
            }

            @Override
            public void onContent(String token) throws IOException {
                fullContent.append(token);
                original.setContent(fullContent.toString());
                emitter.send(SseEmitter.event().data(token));
            }

            @Override
            public void onComplete() throws IOException {
                original.setContent(fullContent.toString());
                messageRepository.save(original);
                emitter.send(SseEmitter.event().name("COMPLETE"));
                emitter.complete();
            }

            @Override
            public void onError(Exception e) {
                original.setContent("重新生成失败: " + e.getMessage());
                messageRepository.save(original);
                emitter.completeWithError(e);
            }
        });
    } catch (Exception e) {
        emitter.completeWithError(e);
    }
}

3.前端修改

//qa-stores

const regenerate = async (messageId) => {
    try {
        loadingMessage.value[messageId] = true;
        
        // 找到原消息
        const originalMsgIndex = currentMessages.value.findIndex(msg => msg.id === messageId);
        if (originalMsgIndex === -1) {
            throw new Error('消息未找到');
        }
        
        // 重置原消息状态
        currentMessages.value[originalMsgIndex].content = '';
        currentMessages.value[originalMsgIndex].streaming = true;
        currentMessages.value[originalMsgIndex].error = false;

        const response = await fetch(`/api/qa/stream-regenerate`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ messageId })
        });

        const reader = response.body.getReader();
        const decoder = new TextDecoder();
        let buffer = '';
        let currentEvent = null;

        while (true) {
            const { done, value } = await reader.read();
            if (done) break;

            buffer += decoder.decode(value, { stream: true });
            const lines = buffer.split('\n');
            buffer = lines.pop() || '';

            for (const line of lines) {
                if (line.startsWith('event:')) {
                    currentEvent = line.replace('event:', '').trim();
                    continue;
                }

                if (line.startsWith('data:')) {
                    const data = line.replace('data:', '').trim();

                    // START 事件 - 确认原消息ID
                    if (currentEvent === 'START') {
                        if (data !== messageId.toString()) {
                            console.warn('消息ID不匹配', data, messageId);
                        }
                        currentEvent = null;
                        continue;
                    }

                    // 普通内容 - 更新原消息
                    if (currentEvent === null) {
                        currentMessages.value[originalMsgIndex].content += data;
                    }

                    // 完成事件
                    if (currentEvent === 'COMPLETE') {
                        currentMessages.value[originalMsgIndex].streaming = false;
                    }
                }
            }
        }

        // 确保结束状态
        currentMessages.value[originalMsgIndex].streaming = false;
        await fetchSessions(classic.value.id);
    } catch (error) {
        console.error('重新生成失败:', error);
        const originalMsgIndex = currentMessages.value.findIndex(msg => msg.id === messageId);
        if (originalMsgIndex !== -1) {
            currentMessages.value[originalMsgIndex].content = '重新生成失败: ' + error.message;
            currentMessages.value[originalMsgIndex].streaming = false;
            currentMessages.value[originalMsgIndex].error = true;
        }
    } finally {
        loadingMessage.value[messageId] = false;
    }
};

http://www.xdnf.cn/news/9728.html

相关文章:

  • 线程安全问题的原因和解决方案
  • String类中的常用方法
  • RapidOCR集成PP-OCRv5_det mobile模型记录
  • 【AI论文】ScienceBoard:评估现实科学工作流程中的多模态自主代理
  • 【FPGA开发】Ubuntu16.04环境下配置Vivado2018.3—附软件包
  • mysql执行sql语句报错事务锁住
  • Python爬虫实战:研究Aiohttp库相关技术
  • 【C语言】指针详解(接)
  • 游戏盾在非游戏行业的应用实践与价值分析
  • 立志成为一名优秀测试开发工程师(第九天)——使用fiddler工具、request库进行接口测试
  • GitCode镜像门法律分析:PL协议在中国的司法实践
  • Python 生成器:从基础到高级
  • 【Ubuntu】Ubuntu网络管理
  • Vscode 解决 #include <> 找不到的问题
  • x86_64-apple-ios-simulator 错误
  • 政策+技术双轮驱动:MiC建筑如何成为“好房子”建设的破局之道
  • UE5.5 pixelstreaming插件打包报错
  • UE5打包项目设置Project Settings(打包widows exe安装包)
  • 《深入解析UART协议及其硬件实现》-- 第三篇:UART ASIC实现优化与低功耗设计
  • 图像数据与显存
  • WebFuture:后台修改内容链接地址保存提示内容链接地址禁止输入外部url链接
  • Spring Boot自动装配原理
  • QT+opecv如何更改图片的拍摄路径
  • Oracle 慢sql排查
  • 前端面试准备2
  • Axure设计案例——科技感渐变柱状图
  • 24点游戏的最小数字组合问题
  • 常见关系型数据库对比指南
  • 制造业的未来图景:超自动化与劳动力转型的双重革命
  • 数据库中常见的锁机制详解