Files
common_agent/frontend/src/pages/agent/AgentDebugPage.vue

271 lines
6.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { ElMessage } from 'element-plus';
import { computed, onMounted, ref } from 'vue';
import { chatWithAgent, listAgents, type AgentDefinition, type AgentMessage, type AgentReferenceChunk } from '@/api/agent';
interface ChatBubble {
id: string;
role: 'user' | 'assistant';
content: string;
references?: AgentReferenceChunk[];
requestId?: string;
}
const loading = ref(false);
const sending = ref(false);
const agents = ref<AgentDefinition[]>([]);
const selectedAgentId = ref('');
const inputText = ref('');
const messages = ref<ChatBubble[]>([]);
const ragEnabled = ref(true);
const selectedAgent = computed(() => agents.value.find((agent) => agent.id === selectedAgentId.value));
async function loadAgents() {
loading.value = true;
try {
const response = await listAgents();
agents.value = (response.data ?? []).filter((item) => item.status === 'ENABLED');
if (!selectedAgentId.value && agents.value.length > 0) {
const firstAgent = agents.value[0];
selectedAgentId.value = firstAgent && firstAgent.id ? firstAgent.id : '';
}
} finally {
loading.value = false;
}
}
function buildRequestMessages(nextUserText: string): AgentMessage[] {
const historyMessages: AgentMessage[] = messages.value.map((message) => ({
role: message.role,
content: message.content,
}));
historyMessages.push({ role: 'user', content: nextUserText });
return historyMessages;
}
async function sendMessage() {
const trimmed = inputText.value.trim();
if (!selectedAgentId.value) {
ElMessage.warning('请先选择Agent');
return;
}
if (!trimmed) {
return;
}
const requestMessages = buildRequestMessages(trimmed);
const userBubble: ChatBubble = {
id: `${Date.now()}_u`,
role: 'user',
content: trimmed,
};
messages.value.push(userBubble);
inputText.value = '';
sending.value = true;
try {
const response = await chatWithAgent(selectedAgentId.value, {
messages: requestMessages,
ragEnabled: ragEnabled.value,
});
const result = response.data;
messages.value.push({
id: `${Date.now()}_a`,
role: 'assistant',
content: result?.answer ?? '',
references: result?.references ?? [],
requestId: result?.modelRequestId,
});
} finally {
sending.value = false;
}
}
function clearChat() {
messages.value = [];
}
onMounted(loadAgents);
</script>
<template>
<section class="page-panel agent-debug">
<div class="page-panel__header">
<h2>Agent 调试</h2>
<span>Chat Debugger</span>
</div>
<div class="debug-toolbar">
<el-select v-model="selectedAgentId" class="debug-toolbar__agent" :loading="loading" placeholder="请选择Agent">
<el-option
v-for="item in agents"
:key="item.id"
:label="`${item.agentName}(${item.agentCode})`"
:value="item.id"
/>
</el-select>
<el-switch v-model="ragEnabled" active-text="RAG对话" inactive-text="普通对话" />
<el-button @click="loadAgents">刷新Agent</el-button>
<el-button @click="clearChat">清空会话</el-button>
</div>
<div class="debug-chat">
<div v-for="bubble in messages" :key="bubble.id" class="chat-row" :class="`chat-row--${bubble.role}`">
<div class="chat-bubble">
<div class="chat-bubble__role">{{ bubble.role === 'user' ? '用户' : '助手' }}</div>
<div class="chat-bubble__content">{{ bubble.content }}</div>
<template v-if="bubble.role === 'assistant'">
<div v-if="bubble.references?.length" class="chat-bubble__refs">
<div class="chat-bubble__refs-title">引用切片</div>
<ul>
<li v-for="reference in bubble.references" :key="reference.chunkId">
<span class="ref-meta">#{{ reference.chunkId }} · 相似度 {{ (reference.score ?? 0).toFixed(4) }}</span>
<span>{{ reference.chunkContent }}</span>
</li>
</ul>
</div>
<div v-if="bubble.requestId" class="chat-bubble__request-id">requestId: {{ bubble.requestId }}</div>
</template>
</div>
</div>
<div v-if="messages.length === 0" class="chat-empty">
选择Agent后输入问题发起对话调试
</div>
</div>
<div class="debug-input">
<el-input
v-model="inputText"
type="textarea"
:rows="3"
resize="none"
:disabled="sending || !selectedAgent"
placeholder="输入问题回车发送Shift+Enter换行"
@keydown.enter.exact.prevent="sendMessage"
/>
<el-button type="primary" :loading="sending" :disabled="!selectedAgent" @click="sendMessage">发送</el-button>
</div>
</section>
</template>
<style scoped>
.agent-debug {
display: flex;
flex-direction: column;
}
.debug-toolbar {
display: flex;
gap: 10px;
padding: 16px 22px 12px;
}
.debug-toolbar__agent {
width: 320px;
}
.debug-chat {
flex: 1;
min-height: 420px;
max-height: 58vh;
padding: 12px 22px;
overflow-y: auto;
border-top: 1px solid var(--app-border-soft);
border-bottom: 1px solid var(--app-border-soft);
background: #fafcff;
}
.chat-row {
display: flex;
margin-bottom: 14px;
}
.chat-row--user {
justify-content: flex-end;
}
.chat-row--assistant {
justify-content: flex-start;
}
.chat-bubble {
width: min(80%, 860px);
padding: 12px;
border-radius: 8px;
border: 1px solid var(--app-border);
background: #ffffff;
}
.chat-row--user .chat-bubble {
background: #eef5ff;
border-color: #d3e5ff;
}
.chat-bubble__role {
margin-bottom: 6px;
color: var(--app-text-muted);
font-size: 12px;
font-weight: 600;
}
.chat-bubble__content {
white-space: pre-wrap;
line-height: 1.6;
}
.chat-bubble__refs {
margin-top: 12px;
padding: 10px;
border-radius: 8px;
background: #f8fafc;
}
.chat-bubble__refs-title {
margin-bottom: 8px;
color: #344054;
font-size: 12px;
font-weight: 600;
}
.chat-bubble__refs ul {
margin: 0;
padding-left: 16px;
}
.chat-bubble__refs li {
margin-bottom: 8px;
display: flex;
flex-direction: column;
gap: 4px;
}
.ref-meta {
color: var(--app-text-muted);
font-size: 12px;
}
.chat-bubble__request-id {
margin-top: 8px;
color: var(--app-text-muted);
font-size: 12px;
}
.chat-empty {
color: var(--app-text-muted);
text-align: center;
padding: 36px 0;
}
.debug-input {
display: flex;
flex-direction: column;
gap: 10px;
padding: 12px 22px 18px;
}
.debug-input .el-button {
align-self: flex-end;
}
</style>