feat: align agent preview with learner topics
This commit is contained in:
@@ -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<ModelItem[]>([]);
|
||||
const knowledge = ref<KnowledgeItem[]>([]);
|
||||
const debugUsers = ref<AdminUser[]>([]);
|
||||
const debugUserLoading = ref(false);
|
||||
const debugTopics = ref<TopicSessionRecord[]>([]);
|
||||
const debugTopicLoading = ref(false);
|
||||
const history = ref<PromptHistoryItem[]>([]);
|
||||
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) {
|
||||
<el-option v-for="model in models" :key="model.id" :label="model.displayName || model.modelName" :value="model.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="模拟学员(可选)">
|
||||
<el-select
|
||||
v-model="agentForm.userId"
|
||||
clearable
|
||||
filterable
|
||||
remote
|
||||
:remote-method="searchDebugUsers"
|
||||
:loading="debugUserLoading"
|
||||
placeholder="不选择则不注入学员权益和成长档案"
|
||||
>
|
||||
<el-option
|
||||
v-for="user in debugUsers"
|
||||
:key="user.id"
|
||||
:label="`${user.name || user.nickname || user.phone} · ${user.phone} · #${user.id}`"
|
||||
:value="user.id"
|
||||
/>
|
||||
</el-select>
|
||||
<div class="agent-form-help">用于让后台预览加载该学员的权益、主题额度和长期成长档案;不会消耗额度,也不会影响正式会话。</div>
|
||||
</el-form-item>
|
||||
<div class="agent-debug-context-grid">
|
||||
<el-form-item label="模拟学员(可选)">
|
||||
<el-select
|
||||
v-model="agentForm.userId"
|
||||
clearable
|
||||
filterable
|
||||
remote
|
||||
:remote-method="searchDebugUsers"
|
||||
:loading="debugUserLoading"
|
||||
:disabled="agentDebugging"
|
||||
placeholder="不选择则不注入学员权益和成长档案"
|
||||
@change="handleDebugUserChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="user in debugUsers"
|
||||
:key="user.id"
|
||||
:label="`${user.name || user.nickname || user.phone} · ${user.phone} · #${user.id}`"
|
||||
:value="user.id"
|
||||
/>
|
||||
</el-select>
|
||||
<div class="agent-form-help">加载该学员的权益和长期成长档案;不会消耗额度或改动正式会话。</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="模拟主题(可选)">
|
||||
<el-select
|
||||
v-model="agentForm.topicSessionId"
|
||||
clearable
|
||||
filterable
|
||||
remote
|
||||
:remote-method="searchDebugTopics"
|
||||
:loading="debugTopicLoading"
|
||||
:disabled="!agentForm.userId || agentDebugging"
|
||||
placeholder="选择学员后可加载具体主题"
|
||||
@change="handleDebugTopicChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="topic in debugTopics"
|
||||
:key="topic.id"
|
||||
:label="debugTopicOptionLabel(topic)"
|
||||
:value="topic.id"
|
||||
/>
|
||||
</el-select>
|
||||
<div class="agent-form-help">选择后会加载该主题摘要和最近对话,用于验证追问效果。</div>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-form-item label="调试知识库">
|
||||
<el-select v-model="agentForm.knowledgeIds" class="debug-knowledge-select" multiple filterable collapse-tags collapse-tags-tooltip :max-collapse-tags="2" popper-class="debug-knowledge-popper" placeholder="默认使用全部已开放知识库">
|
||||
<el-option v-for="item in knowledge" :key="item.id" :label="`${item.name}${item.status === 0 ? ' · 已关闭(仅本次预览)' : ''}`" :value="item.id" />
|
||||
@@ -593,7 +676,7 @@ function errorMessage(error: unknown, fallback: string) {
|
||||
|
||||
<aside class="agent-preview-panel">
|
||||
<div class="agent-preview-head">
|
||||
<div><h3>调试预览</h3><small>{{ selectedKnowledgeSummary }} · {{ selectedDebugUserLabel }}</small></div>
|
||||
<div><h3>调试预览</h3><small>{{ selectedKnowledgeSummary }} · {{ selectedDebugUserLabel }} · {{ selectedDebugTopicLabel }}</small></div>
|
||||
<div class="agent-preview-head-actions"><span :title="lastDebugRoute?.routeReason || ''">{{ lastDebugRoute?.modelName || selectedModelName }}</span><el-button link :disabled="agentDebugging" @click="clearAgentPreview">清空</el-button></div>
|
||||
</div>
|
||||
<div ref="agentPreviewChat" class="agent-preview-chat">
|
||||
|
||||
Reference in New Issue
Block a user