From b6aff78aacc2b681b6c70a0a75c51ae833cf5ca6 Mon Sep 17 00:00:00 2001 From: Nelson <1475262689@qq.com> Date: Fri, 31 Jul 2026 18:21:36 +0800 Subject: [PATCH] feat: align agent preview with learner topics --- .../src/components/AgentManagementView.vue | 129 ++++++++++++++---- .../apps/admin-web/src/main.ts | 3 + .../apps/admin-web/src/services/api.ts | 3 + .../apps/admin-web/src/styles.css | 14 ++ .../apps/admin-web/src/types/api.ts | 1 + .../apps/backend/app/api/admin_users.py | 42 +++++- .../apps/backend/app/schemas/admin.py | 1 + .../app/services/agent_debug_service.py | 103 ++++++++++++-- .../apps/backend/app/services/chat_service.py | 26 +++- .../app/services/chat_stream_service.py | 51 ++++++- .../app/services/entitlement_service.py | 13 ++ .../app/services/knowledge_agent_service.py | 4 + .../backend/app/services/rag_async_service.py | 4 + .../apps/backend/app/services/rag_service.py | 14 ++ .../app/services/topic_session_service.py | 49 +++++++ .../backend/tests/test_admin_pagination.py | 43 +++++- .../tests/test_agent_debug_user_context.py | 119 +++++++++++++++- .../backend/tests/test_entitlements_topics.py | 81 ++++++++++- 18 files changed, 656 insertions(+), 44 deletions(-) diff --git a/ai_knowledge_base_v2/apps/admin-web/src/components/AgentManagementView.vue b/ai_knowledge_base_v2/apps/admin-web/src/components/AgentManagementView.vue index 7498bb2..fd83462 100644 --- a/ai_knowledge_base_v2/apps/admin-web/src/components/AgentManagementView.vue +++ b/ai_knowledge_base_v2/apps/admin-web/src/components/AgentManagementView.vue @@ -3,7 +3,7 @@ import { ElMessage, ElMessageBox } from "element-plus"; import { computed, nextTick, onMounted, reactive, ref, watch } from "vue"; import { api, streamDebugAgent } from "../services/api"; -import type { AdminUser, AgentRuntimeConfig, KnowledgeItem, ModelItem, PromptDetail, PromptHistoryItem } from "../types/api"; +import type { AdminUser, AgentRuntimeConfig, KnowledgeItem, ModelItem, PromptDetail, PromptHistoryItem, TopicSessionRecord } from "../types/api"; import AgentGenerationParameters from "./AgentGenerationParameters.vue"; import AgentResponseDepthControl from "./AgentResponseDepthControl.vue"; import AdminPagination from "./AdminPagination.vue"; @@ -27,6 +27,8 @@ const models = ref([]); const knowledge = ref([]); const debugUsers = ref([]); const debugUserLoading = ref(false); +const debugTopics = ref([]); +const debugTopicLoading = ref(false); const history = ref([]); const historyPager = reactive({ page: 1, pageSize: 10, total: 0 }); const historyDetailOpen = ref(false); @@ -47,6 +49,7 @@ const agentPreviewMessages = ref<{ const agentForm = reactive({ modelId: undefined as number | undefined, userId: undefined as number | undefined, + topicSessionId: undefined as number | undefined, knowledgeIds: [] as number[], temperature: 0.2 as number | null, topP: null as number | null, @@ -89,6 +92,11 @@ const selectedDebugUserLabel = computed(() => { const user = debugUsers.value.find((item) => item.id === agentForm.userId); return user ? `${user.name || user.nickname || user.phone} · #${user.id}` : `学员 #${agentForm.userId}`; }); +const selectedDebugTopicLabel = computed(() => { + if (!agentForm.topicSessionId) return "未指定主题"; + const topic = debugTopics.value.find((item) => item.id === agentForm.topicSessionId); + return topic ? topic.title : `主题 #${agentForm.topicSessionId}`; +}); onMounted(load); @@ -135,6 +143,55 @@ async function searchDebugUsers(keyword: string) { } } +async function handleDebugUserChange(userId?: number) { + agentForm.topicSessionId = undefined; + debugTopics.value = []; + clearAgentPreview("模拟学员已切换,可以选择其中一个主题继续调试。"); + if (!userId) return; + debugTopicLoading.value = true; + try { + await loadDebugTopics(userId); + } catch (error) { + ElMessage.error(errorMessage(error, "学员主题加载失败")); + } finally { + debugTopicLoading.value = false; + } +} + +async function loadDebugTopics(userId: number, keyword = "") { + const selected = debugTopics.value.find((item) => item.id === agentForm.topicSessionId); + const result = await api.userTopics(userId, { keyword, page: 1, pageSize: 100 }); + debugTopics.value = selected && !result.items.some((item) => item.id === selected.id) + ? [selected, ...result.items] + : result.items; +} + +async function searchDebugTopics(keyword: string) { + if (!agentForm.userId) return; + debugTopicLoading.value = true; + try { + await loadDebugTopics(agentForm.userId, keyword); + } catch (error) { + ElMessage.error(errorMessage(error, "学员主题搜索失败")); + } finally { + debugTopicLoading.value = false; + } +} + +function handleDebugTopicChange() { + clearAgentPreview( + agentForm.topicSessionId + ? "模拟主题已切换,下一条调试会加载该主题的摘要和最近对话。" + : "已恢复通用学员模拟,不加载具体主题对话。", + ); +} + +function debugTopicOptionLabel(topic: TopicSessionRecord) { + const statusLabel = topic.status === "active" ? "进行中" : topic.status === "completed" ? "已完成" : topic.status; + const summaryLabel = topic.summaryAvailable ? "有摘要" : "无摘要"; + return `${topic.title} · ${statusLabel} · ${topic.messageCount} 条消息 · ${summaryLabel}`; +} + function applyRuntimeConfig(value: AgentRuntimeConfig) { runtimeConfig.value = value; Object.assign(runtimeForm, { @@ -325,6 +382,7 @@ async function debugAgent() { promptContent: promptContent.value, modelId: agentForm.modelId, userId: agentForm.userId || null, + topicSessionId: agentForm.topicSessionId || null, knowledgeIds: agentForm.knowledgeIds, question, history: conversationHistory, @@ -385,8 +443,8 @@ function scrollAgentPreview() { }); } -function clearAgentPreview() { - agentPreviewMessages.value = [{ role: "assistant", content: "预览已清空,可以继续发起新的 Agent 调试。" }]; +function clearAgentPreview(message = "预览已清空,可以继续发起新的 Agent 调试。") { + agentPreviewMessages.value = [{ role: "assistant", content: message }]; agentDebugTrace.value = []; lastDebugRoute.value = null; } @@ -501,25 +559,50 @@ function errorMessage(error: unknown, fallback: string) { - - - - -
用于让后台预览加载该学员的权益、主题额度和长期成长档案;不会消耗额度,也不会影响正式会话。
-
+
+ + + + +
加载该学员的权益和长期成长档案;不会消耗额度或改动正式会话。
+
+ + + + +
选择后会加载该主题摘要和最近对话,用于验证追问效果。
+
+
@@ -593,7 +676,7 @@ function errorMessage(error: unknown, fallback: string) {