优化智能体的体验
This commit is contained in:
parent
4c2f44ed6c
commit
b7df4ecd4a
@ -16,6 +16,11 @@
|
||||
|
||||
.message-md pre code.hljs {
|
||||
font-size: 0.8rem;
|
||||
font-family: 'Menlo', 'Monaco', 'Consolas', 'PingFang SC', 'Microsoft YaHei', 'Hiragino Sans GB', 'Source Han Sans CN', 'Courier New', monospace;
|
||||
line-height: 1.5;
|
||||
letter-spacing: 0.025em;
|
||||
tab-size: 4;
|
||||
-moz-tab-size: 4;
|
||||
background-color: var(--gray-100);
|
||||
}
|
||||
|
||||
|
||||
@ -3,31 +3,33 @@
|
||||
<div class="chat">
|
||||
<div class="chat-header">
|
||||
<div class="header__left">
|
||||
<a-dropdown v-if="!useSingleMode">
|
||||
<div class="current-agent nav-btn">
|
||||
<RobotOutlined />
|
||||
<span v-if="currentAgent">{{ currentAgent.name }}</span>
|
||||
<span v-else>请选择智能体</span>
|
||||
</div>
|
||||
<template #overlay>
|
||||
<a-menu @click="({key}) => selectAgent(key)">
|
||||
<a-menu-item v-for="(agent, name) in agents" :key="name">
|
||||
<RobotOutlined /> {{ agent.name }}
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<div class="newchat nav-btn" @click="resetThread" :disabled="isProcessing">
|
||||
<PlusCircleOutlined /> <span class="text">新对话</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header__right">
|
||||
<div v-if="!props.agentId" class="current-agent nav-btn">
|
||||
<a-dropdown>
|
||||
<div class="current-agent nav-btn">
|
||||
<RobotOutlined />
|
||||
<span v-if="currentAgent">{{ currentAgent.name }}</span>
|
||||
<span v-else>请选择智能体</span>
|
||||
</div>
|
||||
<template #overlay>
|
||||
<a-menu @click="({key}) => selectAgent(key)">
|
||||
<a-menu-item v-for="(agent, name) in agents" :key="name">
|
||||
<RobotOutlined /> {{ agent.name }}
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</div>
|
||||
<div v-else class="current-agent nav-btn">
|
||||
<RobotOutlined />
|
||||
<span v-if="currentAgent">{{ currentAgent.name }}</span>
|
||||
<span v-else>加载中...</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header__right">
|
||||
<div class="newchat nav-btn" @click="resetThread" :disabled="isProcessing">
|
||||
<PlusCircleOutlined /> <span class="text">新对话</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="messages.length === 0" class="chat-examples">
|
||||
@ -118,10 +120,17 @@ import {
|
||||
import MessageInputComponent from '@/components/MessageInputComponent.vue'
|
||||
import MessageComponent from '@/components/MessageComponent.vue'
|
||||
|
||||
// ========= 加载路由参数并获取 agent id ====================
|
||||
// 新增props属性,允许父组件传入agentId
|
||||
const props = defineProps({
|
||||
agentId: {
|
||||
type: String,
|
||||
default: null
|
||||
}
|
||||
});
|
||||
|
||||
// 移除路由相关逻辑,使用props
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const useSingleMode = computed(() => !!route.params.agent_id);
|
||||
|
||||
// ==================== 状态管理 ====================
|
||||
|
||||
@ -262,7 +271,6 @@ const prepareMessageHistory = (msgs) => {
|
||||
|
||||
// 选择智能体
|
||||
const selectAgent = (agentName) => {
|
||||
if (useSingleMode.value) return; // 单页面模式下不允许切换智能体
|
||||
currentAgent.value = agents.value[agentName];
|
||||
messages.value = [];
|
||||
threadId.value = null;
|
||||
@ -638,8 +646,7 @@ onMounted(async () => {
|
||||
await fetchAgents();
|
||||
|
||||
// 检查加载状态
|
||||
console.log("单页面模式:", useSingleMode.value);
|
||||
console.log("路由参数:", route.params.agent_id);
|
||||
console.log("路由参数:", props.agentId);
|
||||
console.log("智能体列表:", Object.keys(agents.value));
|
||||
|
||||
// 初始加载 - 确保使用 Vue Router 的解析后路由
|
||||
@ -653,26 +660,6 @@ onMounted(async () => {
|
||||
}
|
||||
});
|
||||
|
||||
// 添加路由参数变化监听
|
||||
watch(() => route.params.agent_id, async (newAgentId, oldAgentId) => {
|
||||
try {
|
||||
console.log("路由参数变化", oldAgentId, "->", newAgentId);
|
||||
|
||||
// 如果路由参数变化了(包括从有参数变为无参数的情况)
|
||||
if (oldAgentId !== newAgentId) {
|
||||
// 重置会话
|
||||
messages.value = [];
|
||||
threadId.value = null;
|
||||
resetStatusSteps();
|
||||
|
||||
// 加载新的智能体数据
|
||||
await loadAgentData();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('路由参数变化处理出错:', error);
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
// 处理元数据
|
||||
const handleMetadata = (data) => {
|
||||
// 更新线程ID
|
||||
@ -708,8 +695,8 @@ const toggleToolCall = (toolCallId) => {
|
||||
const loadState = () => {
|
||||
try {
|
||||
// 确定存储前缀
|
||||
const storagePrefix = useSingleMode.value ?
|
||||
(route.params.agent_id ? `agent-single-${route.params.agent_id}` : null) :
|
||||
const storagePrefix = props.agentId ?
|
||||
`agent-${props.agentId}` :
|
||||
'agent-multi';
|
||||
|
||||
if (!storagePrefix) {
|
||||
@ -719,17 +706,6 @@ const loadState = () => {
|
||||
|
||||
console.log("loadState with prefix:", storagePrefix);
|
||||
|
||||
// 在单页面模式下,直接从路由参数加载智能体
|
||||
if (useSingleMode.value) {
|
||||
// 智能体已在 loadAgentData 中设置,这里不再需要重复设置
|
||||
} else {
|
||||
// 多智能体模式下,从本地存储加载当前选择的智能体
|
||||
const savedAgent = localStorage.getItem(`${storagePrefix}-current-agent`);
|
||||
if (savedAgent && agents.value && agents.value[savedAgent]) {
|
||||
currentAgent.value = agents.value[savedAgent];
|
||||
}
|
||||
}
|
||||
|
||||
// 加载设置选项
|
||||
const savedOptions = localStorage.getItem(`${storagePrefix}-options`);
|
||||
if (savedOptions) {
|
||||
@ -768,6 +744,62 @@ const loadState = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 监听agentId变化
|
||||
watch(() => props.agentId, async (newAgentId, oldAgentId) => {
|
||||
try {
|
||||
console.log("智能体ID变化", oldAgentId, "->", newAgentId);
|
||||
|
||||
// 如果变化了,重置会话并加载新数据
|
||||
if (newAgentId !== oldAgentId) {
|
||||
// 重置会话
|
||||
messages.value = [];
|
||||
threadId.value = null;
|
||||
resetStatusSteps();
|
||||
|
||||
// 加载新的智能体数据
|
||||
await loadAgentData();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('智能体ID变化处理出错:', error);
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
// 加载智能体数据的方法
|
||||
const loadAgentData = async () => {
|
||||
try {
|
||||
// 确保智能体列表已加载
|
||||
if (Object.keys(agents.value).length === 0) {
|
||||
await fetchAgents();
|
||||
}
|
||||
|
||||
// 设置当前智能体
|
||||
if (props.agentId && agents.value && agents.value[props.agentId]) {
|
||||
// 如果传入了指定的agentId,就加载对应的智能体
|
||||
currentAgent.value = agents.value[props.agentId];
|
||||
console.log("设置当前智能体", currentAgent.value.name);
|
||||
} else if (!props.agentId) {
|
||||
// 多智能体模式下,尝试从本地存储恢复上次选择的智能体
|
||||
const storagePrefix = 'agent-multi';
|
||||
const savedAgent = localStorage.getItem(`${storagePrefix}-current-agent`);
|
||||
if (savedAgent && agents.value && agents.value[savedAgent]) {
|
||||
currentAgent.value = agents.value[savedAgent];
|
||||
console.log("从存储中恢复智能体", currentAgent.value.name);
|
||||
}
|
||||
}
|
||||
|
||||
// 加载保存的状态
|
||||
loadState();
|
||||
|
||||
// 处理消息历史
|
||||
if (messages.value && messages.value.length > 0) {
|
||||
console.log("处理消息历史:", messages.value.length);
|
||||
messages.value = prepareMessageHistory(messages.value);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载智能体数据出错:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 保存状态到localStorage
|
||||
const saveState = () => {
|
||||
try {
|
||||
@ -777,21 +809,12 @@ const saveState = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// 确定存储前缀 - 确保agent_id总是可用
|
||||
let prefix = 'agent-multi';
|
||||
if (useSingleMode.value) {
|
||||
if (route.params.agent_id) {
|
||||
prefix = `agent-single-${route.params.agent_id}`;
|
||||
} else {
|
||||
console.error("保存状态时缺少agent_id");
|
||||
return; // 不保存
|
||||
}
|
||||
}
|
||||
|
||||
// 确定存储前缀
|
||||
const prefix = props.agentId ? `agent-${props.agentId}` : 'agent-multi';
|
||||
console.log("saveState with prefix:", prefix);
|
||||
|
||||
// 多智能体模式下,保存当前选择的智能体
|
||||
if (!useSingleMode.value && currentAgent.value) {
|
||||
// 如果是多智能体模式,保存当前选择的智能体
|
||||
if (!props.agentId && currentAgent.value) {
|
||||
localStorage.setItem(`${prefix}-current-agent`, currentAgent.value.name);
|
||||
}
|
||||
|
||||
@ -818,38 +841,6 @@ const saveState = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 加载智能体数据的方法
|
||||
const loadAgentData = async () => {
|
||||
try {
|
||||
// 确保智能体列表已加载
|
||||
if (Object.keys(agents.value).length === 0) {
|
||||
await fetchAgents();
|
||||
}
|
||||
|
||||
// 在单页面模式下,设置当前智能体
|
||||
if (useSingleMode.value && route.params.agent_id) {
|
||||
const agentId = route.params.agent_id;
|
||||
if (agents.value && agents.value[agentId]) {
|
||||
currentAgent.value = agents.value[agentId];
|
||||
console.log("设置当前智能体", currentAgent.value.name);
|
||||
} else {
|
||||
console.error("未找到指定的智能体:", agentId);
|
||||
}
|
||||
}
|
||||
|
||||
// 加载保存的状态 - 在设置好currentAgent后再加载状态
|
||||
loadState();
|
||||
|
||||
// 处理消息历史
|
||||
if (messages.value && messages.value.length > 0) {
|
||||
console.log("处理消息历史:", messages.value.length);
|
||||
messages.value = prepareMessageHistory(messages.value);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载智能体数据出错:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 监听状态变化并保存
|
||||
watch([currentAgent, options, messages, threadId], () => {
|
||||
try {
|
||||
|
||||
@ -1,7 +1,24 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Agent Single View</h1>
|
||||
<div class="agent-single-view">
|
||||
<AgentChatComponent :agent-id="agentId" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import AgentChatComponent from '@/components/AgentChatComponent.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const agentId = computed(() => route.params.agent_id);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.agent-single-view {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
@ -1,7 +1,164 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Agent</h1>
|
||||
<div class="agent-view">
|
||||
<div class="sidebar">
|
||||
<h2 class="sidebar-title">智能体列表</h2>
|
||||
<div class="agent-list">
|
||||
<div v-for="(agent, name) in agents"
|
||||
:key="name"
|
||||
class="agent-item"
|
||||
:class="{ active: selectedAgentId === name }"
|
||||
@click="selectAgent(name)">
|
||||
<a-avatar :style="{ backgroundColor: getAgentColor(name) }">
|
||||
<template #icon><RobotOutlined /></template>
|
||||
</a-avatar>
|
||||
<div class="agent-info">
|
||||
<div class="agent-name">{{ agent.name }}</div>
|
||||
<div class="agent-desc">{{ agent.short_description || '智能助手' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<AgentChatComponent :agent-id="selectedAgentId" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { RobotOutlined } from '@ant-design/icons-vue';
|
||||
import AgentChatComponent from '@/components/AgentChatComponent.vue';
|
||||
|
||||
// 状态
|
||||
const agents = ref({});
|
||||
const selectedAgentId = ref(null);
|
||||
|
||||
// 获取智能体列表
|
||||
const fetchAgents = async () => {
|
||||
try {
|
||||
const response = await fetch('/api/chat/agent');
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
// 将数组转换为对象
|
||||
agents.value = data.agents.reduce((acc, agent) => {
|
||||
acc[agent.name] = agent;
|
||||
return acc;
|
||||
}, {});
|
||||
console.log("agents", agents.value);
|
||||
} else {
|
||||
console.error('获取智能体失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取智能体错误:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 选择智能体
|
||||
const selectAgent = (agentId) => {
|
||||
selectedAgentId.value = agentId;
|
||||
// 保存选择到本地存储
|
||||
localStorage.setItem('last-selected-agent', agentId);
|
||||
};
|
||||
|
||||
// 生成智能体头像颜色
|
||||
const getAgentColor = (name) => {
|
||||
// 简单的哈希函数生成颜色
|
||||
const hash = name.split('').reduce((acc, char) => {
|
||||
return char.charCodeAt(0) + ((acc << 5) - acc);
|
||||
}, 0);
|
||||
return `hsl(${Math.abs(hash) % 360}, 70%, 60%)`;
|
||||
};
|
||||
|
||||
// 初始化
|
||||
onMounted(async () => {
|
||||
// 获取智能体列表
|
||||
await fetchAgents();
|
||||
|
||||
// 恢复上次选择的智能体
|
||||
const lastSelectedAgent = localStorage.getItem('last-selected-agent');
|
||||
if (lastSelectedAgent && agents.value[lastSelectedAgent]) {
|
||||
selectedAgentId.value = lastSelectedAgent;
|
||||
} else if (Object.keys(agents.value).length > 0) {
|
||||
// 默认选择第一个智能体
|
||||
selectedAgentId.value = Object.keys(agents.value)[0];
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.agent-view {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 230px;
|
||||
max-width: 230px;
|
||||
border-right: 1px solid var(--main-light-3);
|
||||
background-color: var(--bg-sider);
|
||||
padding: 16px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.sidebar-title {
|
||||
font-weight: bold;
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
padding-bottom: 1rem;
|
||||
font-size: 1rem;
|
||||
border-bottom: 1px solid var(--main-light-3);
|
||||
}
|
||||
|
||||
.agent-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.agent-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--main-light-4);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: var(--main-light-3);
|
||||
}
|
||||
}
|
||||
|
||||
.agent-info {
|
||||
margin-left: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.agent-name {
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.agent-desc {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user