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);