feat: persist cleaned question insights

This commit is contained in:
2026-07-31 16:50:11 +08:00
parent 6c03faf10c
commit a589a25bdc
10 changed files with 505 additions and 21 deletions

View File

@@ -927,7 +927,26 @@ async function resetChatFilters() {
async function searchQuestionInsights() {
pagers.questionInsights.page = 1;
await loadRecordTab("questionInsights");
loading.value = true;
try {
const refresh = await api.refreshQuestionInsights({
dateFrom: formatRecordDateTime(questionInsightFilters.dateFrom, "start"),
dateTo: formatRecordDateTime(questionInsightFilters.dateTo, "end"),
maxMessages: questionInsightFilters.maxMessages,
});
await loadQuestionInsights(1, pagers.questionInsights.pageSize);
if (refresh.processedMessages > 0) {
ElMessage.success(
`新增清洗 ${refresh.processedMessages} 条消息,得到 ${refresh.acceptedQuestions} 个有效问题${refresh.hasMore ? ";仍有历史消息待清洗" : ""}`,
);
} else {
ElMessage.success("没有新增消息,已直接使用持久化清洗结果统计");
}
} catch (error) {
ElMessage.error(error instanceof Error ? error.message : "问题洞察刷新失败");
} finally {
loading.value = false;
}
}
async function resetQuestionInsightFilters() {
@@ -1555,17 +1574,17 @@ function formatRecordDateTime(value: string, boundary: "start" | "end") {
<el-input-number v-model="questionInsightFilters.minCount" :min="1" :max="50" controls-position="right" />
</label>
<label class="insight-number-field">
<span>最多扫描</span>
<span>单次清洗上限</span>
<el-input-number v-model="questionInsightFilters.maxMessages" :min="100" :max="20000" :step="500" controls-position="right" />
</label>
<div class="record-filter-actions">
<el-button type="primary" @click="searchQuestionInsights">统计问题</el-button>
<el-button type="primary" :loading="loading" @click="searchQuestionInsights">刷新并统计</el-button>
<el-button @click="resetQuestionInsightFilters">重置</el-button>
</div>
</div>
<p class="question-insight-help">一期先对用户消息做去噪拆问同义词归一和相似问法合并后续可把清洗后的结果交给大模型做更细的主题命名</p>
<p class="question-insight-help">清洗结果会持久化保存点击刷新并统计只处理尚未清洗的用户消息再按当前时间范围聚合翻页不会重新扫描聊天原文</p>
<section v-if="questionInsights" class="question-insight-summary">
<div><span>扫描用户消息</span><strong>{{ questionInsights.summary.scannedMessages }}</strong></div>
<div><span>纳入清洗消息</span><strong>{{ questionInsights.summary.scannedMessages }}</strong></div>
<div><span>有效问题</span><strong>{{ questionInsights.summary.cleanedQuestions }}</strong></div>
<div><span>过滤低价值</span><strong>{{ questionInsights.summary.filteredMessages }}</strong></div>
<div><span>高频问题组</span><strong>{{ questionInsights.summary.visibleClusterCount }}</strong></div>

View File

@@ -32,6 +32,7 @@ import type {
PeriodicReportRecord,
PromptDetail,
PromptHistoryItem,
QuestionInsightRefreshResult,
QuestionInsightSummary,
} from "../types/api";
@@ -230,6 +231,10 @@ export const api = {
operationLogs: (query: { module?: string; page?: number; pageSize?: number } = {}) => request<PageResult<Record<string, unknown>>>(`/admin/log/list${queryString(query)}`),
questionInsights: (query: { dateFrom?: string; dateTo?: string; minCount?: number; maxMessages?: number; page?: number; pageSize?: number } = {}) =>
request<QuestionInsightSummary>(`/admin/question-insights/summary${queryString(query)}`),
refreshQuestionInsights: (query: { dateFrom?: string; dateTo?: string; maxMessages?: number } = {}) =>
request<QuestionInsightRefreshResult>(`/admin/question-insights/refresh${queryString(query)}`, {
method: "POST",
}),
retrievalLogs: (query: { page?: number; pageSize?: number } = {}) => request<PageResult<RetrievalLogItem>>(`/admin/retrieval-log/list${queryString(query)}`),
estimateRetrievalCleanup: (before: string) => request<{ before: string; estimatedCount: number }>("/admin/retrieval-log/cleanup/estimate", { method: "POST", body: JSON.stringify({ before }) }),
cleanupRetrievalLogs: (before: string) => request<{ before: string; deleted: number }>("/admin/retrieval-log/cleanup", { method: "POST", body: JSON.stringify({ before }) }),

View File

@@ -556,6 +556,7 @@ export interface QuestionInsightSummary {
clusterCount: number;
visibleClusterCount: number;
minCount: number;
cleanerVersion: string;
};
items: QuestionInsightCluster[];
total: number;
@@ -563,6 +564,15 @@ export interface QuestionInsightSummary {
pageSize: number;
}
export interface QuestionInsightRefreshResult {
processedMessages: number;
acceptedQuestions: number;
filteredMessages: number;
concurrentSkips: number;
hasMore: boolean;
cleanerVersion: string;
}
export interface QuestionInsightCluster {
rank: number;
title: string;