feat(feedback): 置信度评分与用户反馈机制 & 归档 confidence-feedback change
This commit is contained in:
@@ -632,8 +632,12 @@ class SuperBizAgentApp {
|
||||
if (data.code === 200 || data.message === 'success') {
|
||||
// data.data 是 ChatResponse 对象
|
||||
const chatResponse = data.data;
|
||||
|
||||
|
||||
if (chatResponse && chatResponse.success) {
|
||||
// 保存后端返回的 sessionId,用于 feedback 提交
|
||||
if (chatResponse.sessionId) {
|
||||
this.lastSessionId = chatResponse.sessionId;
|
||||
}
|
||||
// 成功:添加实际响应消息(即使 answer 为空也显示)
|
||||
const answer = chatResponse.answer || '(无回复内容)';
|
||||
this.addMessage('assistant', answer);
|
||||
@@ -848,24 +852,78 @@ class SuperBizAgentApp {
|
||||
}
|
||||
|
||||
messageContentWrapper.appendChild(messageContent);
|
||||
|
||||
// assistant 消息末尾加反馈栏(流式消息完成后由 handleStreamComplete 添加)
|
||||
if (type === 'assistant' && !isStreaming) {
|
||||
messageContentWrapper.appendChild(this.createFeedbackBar(this.lastSessionId));
|
||||
}
|
||||
|
||||
messageDiv.appendChild(messageContentWrapper);
|
||||
|
||||
if (this.chatMessages) {
|
||||
this.chatMessages.appendChild(messageDiv);
|
||||
|
||||
|
||||
// 如果是第一条消息,移除居中样式并添加动画
|
||||
if (isFirstMessage && this.chatContainer) {
|
||||
this.chatContainer.classList.remove('centered');
|
||||
// 添加动画类
|
||||
this.chatContainer.style.transition = 'all 0.5s ease';
|
||||
}
|
||||
|
||||
|
||||
this.scrollToBottom();
|
||||
}
|
||||
|
||||
return messageDiv;
|
||||
}
|
||||
|
||||
// 创建反馈栏,sessionId 闭包绑定,避免多轮对话时错位
|
||||
createFeedbackBar(sessionId) {
|
||||
const bar = document.createElement('div');
|
||||
bar.className = 'feedback-bar';
|
||||
|
||||
const usefulBtn = document.createElement('button');
|
||||
usefulBtn.className = 'feedback-btn';
|
||||
usefulBtn.title = '有用';
|
||||
usefulBtn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3H14z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
|
||||
|
||||
const notUsefulBtn = document.createElement('button');
|
||||
notUsefulBtn.className = 'feedback-btn';
|
||||
notUsefulBtn.title = '无用';
|
||||
notUsefulBtn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3H10z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M17 2h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
|
||||
|
||||
usefulBtn.addEventListener('click', () => this.submitFeedback('useful', bar, sessionId));
|
||||
notUsefulBtn.addEventListener('click', () => this.submitFeedback('not_useful', bar, sessionId));
|
||||
|
||||
bar.appendChild(usefulBtn);
|
||||
bar.appendChild(notUsefulBtn);
|
||||
return bar;
|
||||
}
|
||||
|
||||
// 提交反馈
|
||||
async submitFeedback(feedback, barElement, sessionId) {
|
||||
if (!sessionId) return;
|
||||
|
||||
barElement.querySelectorAll('.feedback-btn').forEach(btn => btn.disabled = true);
|
||||
|
||||
try {
|
||||
const response = await fetch(`${this.apiBaseUrl}/feedback`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ sessionId, feedback })
|
||||
});
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
barElement.innerHTML = `<span class="feedback-done">${feedback === 'useful' ? '已标记为有用' : '已标记为无用'}</span>`;
|
||||
} else {
|
||||
barElement.querySelectorAll('.feedback-btn').forEach(btn => btn.disabled = false);
|
||||
this.showNotification('反馈提交失败', 'error');
|
||||
}
|
||||
} catch (e) {
|
||||
barElement.querySelectorAll('.feedback-btn').forEach(btn => btn.disabled = false);
|
||||
this.showNotification('反馈提交失败: ' + e.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 添加带加载动画的消息
|
||||
addLoadingMessage(content) {
|
||||
const messageDiv = document.createElement('div');
|
||||
@@ -946,12 +1004,17 @@ class SuperBizAgentApp {
|
||||
handleStreamComplete(assistantMessageElement, fullResponse) {
|
||||
if (assistantMessageElement) {
|
||||
assistantMessageElement.classList.remove('streaming');
|
||||
const messageContentWrapper = assistantMessageElement.querySelector('.message-content-wrapper');
|
||||
const messageContent = assistantMessageElement.querySelector('.message-content');
|
||||
if (messageContent) {
|
||||
messageContent.innerHTML = this.renderMarkdown(fullResponse);
|
||||
// 高亮代码块
|
||||
this.highlightCodeBlocks(messageContent);
|
||||
}
|
||||
// 流式完成后追加反馈栏
|
||||
if (messageContentWrapper && !messageContentWrapper.querySelector('.feedback-bar')) {
|
||||
messageContentWrapper.appendChild(this.createFeedbackBar(this.lastSessionId));
|
||||
}
|
||||
}
|
||||
// 保存流式消息到历史记录
|
||||
if (fullResponse) {
|
||||
|
||||
@@ -1157,9 +1157,58 @@ body {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
|
||||
.mode-dropdown {
|
||||
width: 100%;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 反馈栏 */
|
||||
.feedback-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-top: 8px;
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.feedback-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border: 1px solid #e8eaed;
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
color: #9aa0a6;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s, color 0.2s, background 0.2s;
|
||||
padding: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.feedback-btn svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.feedback-btn:hover {
|
||||
border-color: #1a73e8;
|
||||
color: #1a73e8;
|
||||
background: #e8f0fe;
|
||||
}
|
||||
|
||||
.feedback-btn:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.feedback-done {
|
||||
font-size: 0.75rem;
|
||||
color: #34a853;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user