From ce930560f88f898000f4c296f93b7d827927eaad Mon Sep 17 00:00:00 2001 From: thzxx Date: Sat, 4 Apr 2026 23:46:16 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20RAG=E5=9B=9E=E5=A4=8D=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E7=9F=A5=E8=AF=86=E5=BA=93=E6=9D=A5=E6=BA=90=EF=BC=8C=E5=90=AB?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=90=8D=E5=92=8C=E7=9B=B8=E4=BC=BC=E5=BA=A6?= =?UTF-8?q?=E5=88=86=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- css/style.css | 40 +++++++++++++++++++++++++++++++++++++ js/components/chat-area.js | 15 ++++++++++++++ js/components/input-area.js | 10 +++++++++- 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/css/style.css b/css/style.css index bb75113..3ccab8b 100644 --- a/css/style.css +++ b/css/style.css @@ -2009,3 +2009,43 @@ body::before { max-height: 90vh; } } + +/* RAG 知识库来源 */ +.rag-sources { + margin-top: 8px; + padding: 8px 12px; + background: rgba(0, 245, 212, 0.05); + border: 1px solid rgba(0, 245, 212, 0.15); + border-radius: 8px; + font-size: 12px; +} +.rag-sources-header { + color: var(--accent-cyan); + font-weight: 600; + margin-bottom: 6px; + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.5px; +} +.rag-source-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 3px 0; + color: var(--text-muted); +} +.rag-source-item + .rag-source-item { + border-top: 1px solid rgba(255,255,255,0.05); +} +.rag-source-name { + color: var(--text-secondary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.rag-source-score { + color: var(--accent-green); + font-weight: 500; + margin-left: 8px; + flex-shrink: 0; +} diff --git a/js/components/chat-area.js b/js/components/chat-area.js index 5f43f8d..7743af3 100644 --- a/js/components/chat-area.js +++ b/js/components/chat-area.js @@ -193,6 +193,21 @@ export function appendMessageDOM(msg, index) { contentHtml += `
${stats.map(s => `${s}`).join(' · ')}
`; } + // RAG 知识库来源 + if (msg.role === 'assistant' && msg.ragSources && msg.ragSources.length > 0) { + const sourcesHtml = msg.ragSources.map((s, i) => { + const scorePercent = s.score ? `${(s.score * 100).toFixed(0)}%` : ''; + return `
+ 📄 来源 ${i + 1}: ${escapeHtml(s.filename)} + ${scorePercent ? `${scorePercent}` : ''} +
`; + }).join(''); + contentHtml += `
+
🧠 基于知识库回答
+ ${sourcesHtml} +
`; + } + div.innerHTML = `
${avatar}
diff --git a/js/components/input-area.js b/js/components/input-area.js index f97c2a5..8ca9e6e 100644 --- a/js/components/input-area.js +++ b/js/components/input-area.js @@ -402,12 +402,19 @@ export async function sendMessage() { }; // RAG 检索增强 + let ragSources = null; if (isRagEnabled()) { const ragResult = await performRagRetrieval(text); if (ragResult && ragResult.ragPrompt) { chatParams.system = chatParams.system ? chatParams.system + '\n\n' + ragResult.ragPrompt : ragResult.ragPrompt; + // 保存检索来源信息 + ragSources = ragResult.results.map(r => ({ + filename: r.filename, + score: r.score, + text: truncate(r.text, 100) + })); } } @@ -434,7 +441,8 @@ export async function sendMessage() { timestamp: Date.now(), ...(modelName && { model: modelName }), ...(thinkContent && { think: thinkContent }), - ...(finalStats && { eval_count: finalStats.eval_count, total_duration: finalStats.total_duration }) + ...(finalStats && { eval_count: finalStats.eval_count, total_duration: finalStats.total_duration }), + ...(ragSources && { ragSources }) }; currentSession.messages.push(assistantMsg);