diff --git a/src/views/AgentChat/index.js b/src/views/AgentChat/index.js
index 5bb0ebd5..29d7097c 100644
--- a/src/views/AgentChat/index.js
+++ b/src/views/AgentChat/index.js
@@ -1,53 +1,857 @@
-// src/views/AgentChat/index.js
-// Agent聊天页面
+// src/views/AgentChat/index_v3.js
+// Agent聊天页面 V3 - 带左侧会话列表和用户信息集成
-import React from 'react';
+import React, { useState, useEffect, useRef } from 'react';
import {
Box,
- Container,
- Heading,
- Text,
+ Flex,
VStack,
+ HStack,
+ Text,
+ Input,
+ IconButton,
+ Button,
+ Avatar,
+ Heading,
+ Divider,
+ Spinner,
+ Badge,
useColorModeValue,
+ useToast,
+ Progress,
+ Fade,
+ Collapse,
+ useDisclosure,
+ InputGroup,
+ InputLeftElement,
+ Menu,
+ MenuButton,
+ MenuList,
+ MenuItem,
+ Modal,
+ ModalOverlay,
+ ModalContent,
+ ModalHeader,
+ ModalBody,
+ ModalCloseButton,
+ Tooltip,
} from '@chakra-ui/react';
-import { ChatInterfaceV2 } from '../../components/ChatBot';
+import {
+ FiSend,
+ FiSearch,
+ FiPlus,
+ FiMessageSquare,
+ FiTrash2,
+ FiMoreVertical,
+ FiRefreshCw,
+ FiDownload,
+ FiCpu,
+ FiUser,
+ FiZap,
+ FiClock,
+} from 'react-icons/fi';
+import { useAuth } from '@contexts/AuthContext';
+import { PlanCard } from '@components/ChatBot/PlanCard';
+import { StepResultCard } from '@components/ChatBot/StepResultCard';
+import { logger } from '@utils/logger';
+import axios from 'axios';
/**
- * Agent聊天页面
- * 提供基于MCP的AI助手对话功能
+ * Agent消息类型
*/
-const AgentChat = () => {
+const MessageTypes = {
+ USER: 'user',
+ AGENT_THINKING: 'agent_thinking',
+ AGENT_PLAN: 'agent_plan',
+ AGENT_EXECUTING: 'agent_executing',
+ AGENT_RESPONSE: 'agent_response',
+ ERROR: 'error',
+};
+
+/**
+ * Agent聊天页面 V3
+ */
+const AgentChatV3 = () => {
+ const { user } = useAuth(); // 获取当前用户信息
+ const toast = useToast();
+
+ // 会话相关状态
+ const [sessions, setSessions] = useState([]);
+ const [currentSessionId, setCurrentSessionId] = useState(null);
+ const [isLoadingSessions, setIsLoadingSessions] = useState(true);
+
+ // 消息相关状态
+ const [messages, setMessages] = useState([]);
+ const [inputValue, setInputValue] = useState('');
+ const [isProcessing, setIsProcessing] = useState(false);
+ const [currentProgress, setCurrentProgress] = useState(0);
+
+ // UI 状态
+ const [searchQuery, setSearchQuery] = useState('');
+ const { isOpen: isSidebarOpen, onToggle: toggleSidebar } = useDisclosure({ defaultIsOpen: true });
+
+ // Refs
+ const messagesEndRef = useRef(null);
+ const inputRef = useRef(null);
+
+ // 颜色主题
const bgColor = useColorModeValue('gray.50', 'gray.900');
- const cardBg = useColorModeValue('white', 'gray.800');
+ const sidebarBg = useColorModeValue('white', 'gray.800');
+ const chatBg = useColorModeValue('white', 'gray.800');
+ const inputBg = useColorModeValue('white', 'gray.700');
+ const borderColor = useColorModeValue('gray.200', 'gray.600');
+ const hoverBg = useColorModeValue('gray.100', 'gray.700');
+ const activeBg = useColorModeValue('blue.50', 'blue.900');
+ const userBubbleBg = useColorModeValue('blue.500', 'blue.600');
+ const agentBubbleBg = useColorModeValue('white', 'gray.700');
+
+ // ==================== 会话管理函数 ====================
+
+ // 加载会话列表
+ const loadSessions = async () => {
+ if (!user?.id) return;
+
+ setIsLoadingSessions(true);
+ try {
+ const response = await axios.get('/mcp/agent/sessions', {
+ params: { user_id: user.id, limit: 50 },
+ });
+
+ if (response.data.success) {
+ setSessions(response.data.data);
+ logger.info('会话列表加载成功', response.data.data);
+ }
+ } catch (error) {
+ logger.error('加载会话列表失败', error);
+ toast({
+ title: '加载失败',
+ description: '无法加载会话列表',
+ status: 'error',
+ duration: 3000,
+ });
+ } finally {
+ setIsLoadingSessions(false);
+ }
+ };
+
+ // 加载会话历史
+ const loadSessionHistory = async (sessionId) => {
+ if (!sessionId) return;
+
+ try {
+ const response = await axios.get(`/mcp/agent/history/${sessionId}`, {
+ params: { limit: 100 },
+ });
+
+ if (response.data.success) {
+ const history = response.data.data;
+
+ // 将历史记录转换为消息格式
+ const formattedMessages = history.map((msg, idx) => ({
+ id: `${sessionId}-${idx}`,
+ type: msg.message_type === 'user' ? MessageTypes.USER : MessageTypes.AGENT_RESPONSE,
+ content: msg.message,
+ plan: msg.plan ? JSON.parse(msg.plan) : null,
+ stepResults: msg.steps ? JSON.parse(msg.steps) : null,
+ timestamp: msg.timestamp,
+ }));
+
+ setMessages(formattedMessages);
+ logger.info('会话历史加载成功', formattedMessages);
+ }
+ } catch (error) {
+ logger.error('加载会话历史失败', error);
+ toast({
+ title: '加载失败',
+ description: '无法加载会话历史',
+ status: 'error',
+ duration: 3000,
+ });
+ }
+ };
+
+ // 创建新会话
+ const createNewSession = () => {
+ setCurrentSessionId(null);
+ setMessages([
+ {
+ id: Date.now(),
+ type: MessageTypes.AGENT_RESPONSE,
+ content: `你好${user?.nickname || ''}!我是价小前,北京价值前沿科技公司的AI投研助手。\n\n我会通过多步骤分析来帮助你深入了解金融市场。\n\n你可以问我:\n• 全面分析某只股票\n• 某个行业的投资机会\n• 今日市场热点\n• 某个概念板块的表现`,
+ timestamp: new Date().toISOString(),
+ },
+ ]);
+ };
+
+ // 切换会话
+ const switchSession = (sessionId) => {
+ setCurrentSessionId(sessionId);
+ loadSessionHistory(sessionId);
+ };
+
+ // 删除会话(需要后端API支持)
+ const deleteSession = async (sessionId) => {
+ // TODO: 实现删除会话的后端API
+ toast({
+ title: '删除会话',
+ description: '此功能尚未实现',
+ status: 'info',
+ duration: 2000,
+ });
+ };
+
+ // ==================== 消息处理函数 ====================
+
+ // 自动滚动到底部
+ const scrollToBottom = () => {
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
+ };
+
+ useEffect(() => {
+ scrollToBottom();
+ }, [messages]);
+
+ // 添加消息
+ const addMessage = (message) => {
+ setMessages((prev) => [...prev, { ...message, id: Date.now() }]);
+ };
+
+ // 发送消息
+ const handleSendMessage = async () => {
+ if (!inputValue.trim() || isProcessing) return;
+
+ // 权限检查
+ if (user?.id !== 'max') {
+ toast({
+ title: '权限不足',
+ description: '「价小前投研」功能目前仅对特定用户开放。如需使用,请联系管理员。',
+ status: 'warning',
+ duration: 5000,
+ isClosable: true,
+ });
+ return;
+ }
+
+ const userMessage = {
+ type: MessageTypes.USER,
+ content: inputValue,
+ timestamp: new Date().toISOString(),
+ };
+
+ addMessage(userMessage);
+ const userInput = inputValue;
+ setInputValue('');
+ setIsProcessing(true);
+ setCurrentProgress(0);
+
+ let currentPlan = null;
+ let stepResults = [];
+
+ try {
+ // 1. 显示思考状态
+ addMessage({
+ type: MessageTypes.AGENT_THINKING,
+ content: '正在分析你的问题...',
+ timestamp: new Date().toISOString(),
+ });
+
+ setCurrentProgress(10);
+
+ // 2. 调用后端API(非流式)
+ const response = await axios.post('/mcp/agent/chat', {
+ message: userInput,
+ conversation_history: messages
+ .filter((m) => m.type === MessageTypes.USER || m.type === MessageTypes.AGENT_RESPONSE)
+ .map((m) => ({
+ isUser: m.type === MessageTypes.USER,
+ content: m.content,
+ })),
+ user_id: user?.id || 'anonymous',
+ user_nickname: user?.nickname || '匿名用户',
+ user_avatar: user?.avatar || '',
+ session_id: currentSessionId,
+ });
+
+ // 移除思考消息
+ setMessages((prev) => prev.filter((m) => m.type !== MessageTypes.AGENT_THINKING));
+
+ if (response.data.success) {
+ const data = response.data;
+
+ // 更新 session_id(如果是新会话)
+ if (data.session_id && !currentSessionId) {
+ setCurrentSessionId(data.session_id);
+ }
+
+ // 显示执行计划
+ if (data.plan) {
+ currentPlan = data.plan;
+ addMessage({
+ type: MessageTypes.AGENT_PLAN,
+ content: '已制定执行计划',
+ plan: data.plan,
+ timestamp: new Date().toISOString(),
+ });
+ setCurrentProgress(30);
+ }
+
+ // 显示执行步骤
+ if (data.steps && data.steps.length > 0) {
+ stepResults = data.steps;
+ addMessage({
+ type: MessageTypes.AGENT_EXECUTING,
+ content: '正在执行步骤...',
+ plan: currentPlan,
+ stepResults: stepResults,
+ timestamp: new Date().toISOString(),
+ });
+ setCurrentProgress(70);
+ }
+
+ // 移除执行中消息
+ setMessages((prev) => prev.filter((m) => m.type !== MessageTypes.AGENT_EXECUTING));
+
+ // 显示最终结果
+ addMessage({
+ type: MessageTypes.AGENT_RESPONSE,
+ content: data.final_answer || data.message || '处理完成',
+ plan: currentPlan,
+ stepResults: stepResults,
+ metadata: data.metadata,
+ timestamp: new Date().toISOString(),
+ });
+
+ setCurrentProgress(100);
+
+ // 重新加载会话列表
+ loadSessions();
+ }
+ } catch (error) {
+ logger.error('Agent chat error', error);
+
+ // 移除思考/执行中消息
+ setMessages((prev) =>
+ prev.filter(
+ (m) => m.type !== MessageTypes.AGENT_THINKING && m.type !== MessageTypes.AGENT_EXECUTING
+ )
+ );
+
+ const errorMessage = error.response?.data?.error || error.message || '处理失败';
+
+ addMessage({
+ type: MessageTypes.ERROR,
+ content: `处理失败:${errorMessage}`,
+ timestamp: new Date().toISOString(),
+ });
+
+ toast({
+ title: '处理失败',
+ description: errorMessage,
+ status: 'error',
+ duration: 5000,
+ isClosable: true,
+ });
+ } finally {
+ setIsProcessing(false);
+ setCurrentProgress(0);
+ inputRef.current?.focus();
+ }
+ };
+
+ // 处理键盘事件
+ const handleKeyPress = (e) => {
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault();
+ handleSendMessage();
+ }
+ };
+
+ // 清空对话
+ const handleClearChat = () => {
+ createNewSession();
+ };
+
+ // 导出对话
+ const handleExportChat = () => {
+ const chatText = messages
+ .filter((m) => m.type === MessageTypes.USER || m.type === MessageTypes.AGENT_RESPONSE)
+ .map((msg) => `[${msg.type === MessageTypes.USER ? '用户' : '价小前'}] ${msg.content}`)
+ .join('\n\n');
+
+ const blob = new Blob([chatText], { type: 'text/plain' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = `chat_${new Date().toISOString().slice(0, 10)}.txt`;
+ a.click();
+ URL.revokeObjectURL(url);
+ };
+
+ // ==================== 初始化 ====================
+
+ useEffect(() => {
+ if (user) {
+ loadSessions();
+ createNewSession();
+ }
+ }, [user]);
+
+ // ==================== 渲染 ====================
+
+ // 快捷问题
+ const quickQuestions = [
+ '全面分析贵州茅台这只股票',
+ '今日涨停股票有哪些亮点',
+ '新能源概念板块的投资机会',
+ '半导体行业最新动态',
+ ];
+
+ // 筛选会话
+ const filteredSessions = sessions.filter(
+ (session) =>
+ !searchQuery ||
+ session.last_message?.toLowerCase().includes(searchQuery.toLowerCase())
+ );
return (
-
-
-
- {/* 页面标题 */}
-
- AI投资助手
-
- 基于MCP协议的智能投资顾问,支持股票查询、新闻搜索、概念分析等多种功能
-
+
+ {/* 左侧会话列表 */}
+
+
+ {/* 侧边栏头部 */}
+
+ }
+ colorScheme="blue"
+ w="100%"
+ onClick={createNewSession}
+ size="sm"
+ >
+ 新建对话
+
+
+ {/* 搜索框 */}
+
+
+
+
+ setSearchQuery(e.target.value)}
+ />
+
- {/* 聊天界面 */}
-
-
+ {isLoadingSessions ? (
+
+
+
+ ) : filteredSessions.length === 0 ? (
+
+
+
+ {searchQuery ? '没有找到匹配的对话' : '暂无对话记录'}
+
+
+ ) : (
+ filteredSessions.map((session) => (
+ switchSession(session.session_id)}
+ >
+
+
+
+ {session.last_message || '新对话'}
+
+
+
+
+ {new Date(session.last_timestamp).toLocaleDateString('zh-CN', {
+ month: 'numeric',
+ day: 'numeric',
+ hour: 'numeric',
+ minute: 'numeric',
+ })}
+
+
+ {session.message_count} 条
+
+
+
+
+
+
+
+ ))
+ )}
+
+
+ {/* 用户信息 */}
+
+
+
+
+
+ {user?.nickname || '未登录'}
+
+
+ {user?.id || 'anonymous'}
+
+
+
-
-
-
+
+
+
+ {/* 主聊天区域 */}
+
+ {/* 聊天头部 */}
+
+
+
+ }
+ size="sm"
+ variant="ghost"
+ aria-label="切换侧边栏"
+ onClick={toggleSidebar}
+ />
+ } />
+
+ 价小前投研
+
+
+
+
+ 智能分析
+
+
+
+ 多步骤深度研究
+
+
+
+
+
+
+ }
+ size="sm"
+ variant="ghost"
+ aria-label="清空对话"
+ onClick={handleClearChat}
+ />
+ }
+ size="sm"
+ variant="ghost"
+ aria-label="导出对话"
+ onClick={handleExportChat}
+ />
+
+
+
+ {/* 进度条 */}
+ {isProcessing && (
+
+ )}
+
+
+ {/* 消息列表 */}
+
+
+ {messages.map((message) => (
+
+
+
+ ))}
+
+
+
+
+ {/* 快捷问题 */}
+ {messages.length <= 2 && !isProcessing && (
+
+
+ 💡 试试这些问题:
+
+
+ {quickQuestions.map((question, idx) => (
+
+ ))}
+
+
+ )}
+
+ {/* 输入框 */}
+
+
+ setInputValue(e.target.value)}
+ onKeyPress={handleKeyPress}
+ placeholder="输入你的问题,我会进行深度分析..."
+ bg={inputBg}
+ border="1px"
+ borderColor={borderColor}
+ _focus={{ borderColor: 'blue.500', boxShadow: '0 0 0 1px #3182CE' }}
+ mr={2}
+ disabled={isProcessing}
+ size="lg"
+ />
+ : }
+ colorScheme="blue"
+ aria-label="发送"
+ onClick={handleSendMessage}
+ isLoading={isProcessing}
+ disabled={!inputValue.trim() || isProcessing}
+ size="lg"
+ />
+
+
+
+
);
};
-export default AgentChat;
+/**
+ * 消息渲染器
+ */
+const MessageRenderer = ({ message, userAvatar }) => {
+ const userBubbleBg = useColorModeValue('blue.500', 'blue.600');
+ const agentBubbleBg = useColorModeValue('white', 'gray.700');
+ const borderColor = useColorModeValue('gray.200', 'gray.600');
+
+ switch (message.type) {
+ case MessageTypes.USER:
+ return (
+
+
+
+
+ {message.content}
+
+
+ } />
+
+
+ );
+
+ case MessageTypes.AGENT_THINKING:
+ return (
+
+
+ } />
+
+
+
+
+ {message.content}
+
+
+
+
+
+ );
+
+ case MessageTypes.AGENT_PLAN:
+ return (
+
+
+ } />
+
+
+
+
+
+ );
+
+ case MessageTypes.AGENT_EXECUTING:
+ return (
+
+
+ } />
+
+
+ {message.stepResults?.map((result, idx) => (
+
+ ))}
+
+
+
+ );
+
+ case MessageTypes.AGENT_RESPONSE:
+ return (
+
+
+ } />
+
+ {/* 最终总结 */}
+
+
+ {message.content}
+
+
+ {/* 元数据 */}
+ {message.metadata && (
+
+ 总步骤: {message.metadata.total_steps}
+ ✓ {message.metadata.successful_steps}
+ {message.metadata.failed_steps > 0 && (
+ ✗ {message.metadata.failed_steps}
+ )}
+ 耗时: {message.metadata.total_execution_time?.toFixed(1)}s
+
+ )}
+
+
+ {/* 执行详情(可选) */}
+ {message.plan && message.stepResults && message.stepResults.length > 0 && (
+
+
+
+ 📊 执行详情(点击展开查看)
+
+ {message.stepResults.map((result, idx) => (
+
+ ))}
+
+ )}
+
+
+
+ );
+
+ case MessageTypes.ERROR:
+ return (
+
+
+ } />
+
+ {message.content}
+
+
+
+ );
+
+ default:
+ return null;
+ }
+};
+
+export default AgentChatV3;
diff --git a/src/views/AgentChat/index_backup.js b/src/views/AgentChat/index_backup.js
new file mode 100644
index 00000000..5bb0ebd5
--- /dev/null
+++ b/src/views/AgentChat/index_backup.js
@@ -0,0 +1,53 @@
+// src/views/AgentChat/index.js
+// Agent聊天页面
+
+import React from 'react';
+import {
+ Box,
+ Container,
+ Heading,
+ Text,
+ VStack,
+ useColorModeValue,
+} from '@chakra-ui/react';
+import { ChatInterfaceV2 } from '../../components/ChatBot';
+
+/**
+ * Agent聊天页面
+ * 提供基于MCP的AI助手对话功能
+ */
+const AgentChat = () => {
+ const bgColor = useColorModeValue('gray.50', 'gray.900');
+ const cardBg = useColorModeValue('white', 'gray.800');
+
+ return (
+
+
+
+ {/* 页面标题 */}
+
+ AI投资助手
+
+ 基于MCP协议的智能投资顾问,支持股票查询、新闻搜索、概念分析等多种功能
+
+
+
+ {/* 聊天界面 */}
+
+
+
+
+
+
+ );
+};
+
+export default AgentChat;
diff --git a/src/views/AgentChat/index_v3.js b/src/views/AgentChat/index_v3.js
new file mode 100644
index 00000000..29d7097c
--- /dev/null
+++ b/src/views/AgentChat/index_v3.js
@@ -0,0 +1,857 @@
+// src/views/AgentChat/index_v3.js
+// Agent聊天页面 V3 - 带左侧会话列表和用户信息集成
+
+import React, { useState, useEffect, useRef } from 'react';
+import {
+ Box,
+ Flex,
+ VStack,
+ HStack,
+ Text,
+ Input,
+ IconButton,
+ Button,
+ Avatar,
+ Heading,
+ Divider,
+ Spinner,
+ Badge,
+ useColorModeValue,
+ useToast,
+ Progress,
+ Fade,
+ Collapse,
+ useDisclosure,
+ InputGroup,
+ InputLeftElement,
+ Menu,
+ MenuButton,
+ MenuList,
+ MenuItem,
+ Modal,
+ ModalOverlay,
+ ModalContent,
+ ModalHeader,
+ ModalBody,
+ ModalCloseButton,
+ Tooltip,
+} from '@chakra-ui/react';
+import {
+ FiSend,
+ FiSearch,
+ FiPlus,
+ FiMessageSquare,
+ FiTrash2,
+ FiMoreVertical,
+ FiRefreshCw,
+ FiDownload,
+ FiCpu,
+ FiUser,
+ FiZap,
+ FiClock,
+} from 'react-icons/fi';
+import { useAuth } from '@contexts/AuthContext';
+import { PlanCard } from '@components/ChatBot/PlanCard';
+import { StepResultCard } from '@components/ChatBot/StepResultCard';
+import { logger } from '@utils/logger';
+import axios from 'axios';
+
+/**
+ * Agent消息类型
+ */
+const MessageTypes = {
+ USER: 'user',
+ AGENT_THINKING: 'agent_thinking',
+ AGENT_PLAN: 'agent_plan',
+ AGENT_EXECUTING: 'agent_executing',
+ AGENT_RESPONSE: 'agent_response',
+ ERROR: 'error',
+};
+
+/**
+ * Agent聊天页面 V3
+ */
+const AgentChatV3 = () => {
+ const { user } = useAuth(); // 获取当前用户信息
+ const toast = useToast();
+
+ // 会话相关状态
+ const [sessions, setSessions] = useState([]);
+ const [currentSessionId, setCurrentSessionId] = useState(null);
+ const [isLoadingSessions, setIsLoadingSessions] = useState(true);
+
+ // 消息相关状态
+ const [messages, setMessages] = useState([]);
+ const [inputValue, setInputValue] = useState('');
+ const [isProcessing, setIsProcessing] = useState(false);
+ const [currentProgress, setCurrentProgress] = useState(0);
+
+ // UI 状态
+ const [searchQuery, setSearchQuery] = useState('');
+ const { isOpen: isSidebarOpen, onToggle: toggleSidebar } = useDisclosure({ defaultIsOpen: true });
+
+ // Refs
+ const messagesEndRef = useRef(null);
+ const inputRef = useRef(null);
+
+ // 颜色主题
+ const bgColor = useColorModeValue('gray.50', 'gray.900');
+ const sidebarBg = useColorModeValue('white', 'gray.800');
+ const chatBg = useColorModeValue('white', 'gray.800');
+ const inputBg = useColorModeValue('white', 'gray.700');
+ const borderColor = useColorModeValue('gray.200', 'gray.600');
+ const hoverBg = useColorModeValue('gray.100', 'gray.700');
+ const activeBg = useColorModeValue('blue.50', 'blue.900');
+ const userBubbleBg = useColorModeValue('blue.500', 'blue.600');
+ const agentBubbleBg = useColorModeValue('white', 'gray.700');
+
+ // ==================== 会话管理函数 ====================
+
+ // 加载会话列表
+ const loadSessions = async () => {
+ if (!user?.id) return;
+
+ setIsLoadingSessions(true);
+ try {
+ const response = await axios.get('/mcp/agent/sessions', {
+ params: { user_id: user.id, limit: 50 },
+ });
+
+ if (response.data.success) {
+ setSessions(response.data.data);
+ logger.info('会话列表加载成功', response.data.data);
+ }
+ } catch (error) {
+ logger.error('加载会话列表失败', error);
+ toast({
+ title: '加载失败',
+ description: '无法加载会话列表',
+ status: 'error',
+ duration: 3000,
+ });
+ } finally {
+ setIsLoadingSessions(false);
+ }
+ };
+
+ // 加载会话历史
+ const loadSessionHistory = async (sessionId) => {
+ if (!sessionId) return;
+
+ try {
+ const response = await axios.get(`/mcp/agent/history/${sessionId}`, {
+ params: { limit: 100 },
+ });
+
+ if (response.data.success) {
+ const history = response.data.data;
+
+ // 将历史记录转换为消息格式
+ const formattedMessages = history.map((msg, idx) => ({
+ id: `${sessionId}-${idx}`,
+ type: msg.message_type === 'user' ? MessageTypes.USER : MessageTypes.AGENT_RESPONSE,
+ content: msg.message,
+ plan: msg.plan ? JSON.parse(msg.plan) : null,
+ stepResults: msg.steps ? JSON.parse(msg.steps) : null,
+ timestamp: msg.timestamp,
+ }));
+
+ setMessages(formattedMessages);
+ logger.info('会话历史加载成功', formattedMessages);
+ }
+ } catch (error) {
+ logger.error('加载会话历史失败', error);
+ toast({
+ title: '加载失败',
+ description: '无法加载会话历史',
+ status: 'error',
+ duration: 3000,
+ });
+ }
+ };
+
+ // 创建新会话
+ const createNewSession = () => {
+ setCurrentSessionId(null);
+ setMessages([
+ {
+ id: Date.now(),
+ type: MessageTypes.AGENT_RESPONSE,
+ content: `你好${user?.nickname || ''}!我是价小前,北京价值前沿科技公司的AI投研助手。\n\n我会通过多步骤分析来帮助你深入了解金融市场。\n\n你可以问我:\n• 全面分析某只股票\n• 某个行业的投资机会\n• 今日市场热点\n• 某个概念板块的表现`,
+ timestamp: new Date().toISOString(),
+ },
+ ]);
+ };
+
+ // 切换会话
+ const switchSession = (sessionId) => {
+ setCurrentSessionId(sessionId);
+ loadSessionHistory(sessionId);
+ };
+
+ // 删除会话(需要后端API支持)
+ const deleteSession = async (sessionId) => {
+ // TODO: 实现删除会话的后端API
+ toast({
+ title: '删除会话',
+ description: '此功能尚未实现',
+ status: 'info',
+ duration: 2000,
+ });
+ };
+
+ // ==================== 消息处理函数 ====================
+
+ // 自动滚动到底部
+ const scrollToBottom = () => {
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
+ };
+
+ useEffect(() => {
+ scrollToBottom();
+ }, [messages]);
+
+ // 添加消息
+ const addMessage = (message) => {
+ setMessages((prev) => [...prev, { ...message, id: Date.now() }]);
+ };
+
+ // 发送消息
+ const handleSendMessage = async () => {
+ if (!inputValue.trim() || isProcessing) return;
+
+ // 权限检查
+ if (user?.id !== 'max') {
+ toast({
+ title: '权限不足',
+ description: '「价小前投研」功能目前仅对特定用户开放。如需使用,请联系管理员。',
+ status: 'warning',
+ duration: 5000,
+ isClosable: true,
+ });
+ return;
+ }
+
+ const userMessage = {
+ type: MessageTypes.USER,
+ content: inputValue,
+ timestamp: new Date().toISOString(),
+ };
+
+ addMessage(userMessage);
+ const userInput = inputValue;
+ setInputValue('');
+ setIsProcessing(true);
+ setCurrentProgress(0);
+
+ let currentPlan = null;
+ let stepResults = [];
+
+ try {
+ // 1. 显示思考状态
+ addMessage({
+ type: MessageTypes.AGENT_THINKING,
+ content: '正在分析你的问题...',
+ timestamp: new Date().toISOString(),
+ });
+
+ setCurrentProgress(10);
+
+ // 2. 调用后端API(非流式)
+ const response = await axios.post('/mcp/agent/chat', {
+ message: userInput,
+ conversation_history: messages
+ .filter((m) => m.type === MessageTypes.USER || m.type === MessageTypes.AGENT_RESPONSE)
+ .map((m) => ({
+ isUser: m.type === MessageTypes.USER,
+ content: m.content,
+ })),
+ user_id: user?.id || 'anonymous',
+ user_nickname: user?.nickname || '匿名用户',
+ user_avatar: user?.avatar || '',
+ session_id: currentSessionId,
+ });
+
+ // 移除思考消息
+ setMessages((prev) => prev.filter((m) => m.type !== MessageTypes.AGENT_THINKING));
+
+ if (response.data.success) {
+ const data = response.data;
+
+ // 更新 session_id(如果是新会话)
+ if (data.session_id && !currentSessionId) {
+ setCurrentSessionId(data.session_id);
+ }
+
+ // 显示执行计划
+ if (data.plan) {
+ currentPlan = data.plan;
+ addMessage({
+ type: MessageTypes.AGENT_PLAN,
+ content: '已制定执行计划',
+ plan: data.plan,
+ timestamp: new Date().toISOString(),
+ });
+ setCurrentProgress(30);
+ }
+
+ // 显示执行步骤
+ if (data.steps && data.steps.length > 0) {
+ stepResults = data.steps;
+ addMessage({
+ type: MessageTypes.AGENT_EXECUTING,
+ content: '正在执行步骤...',
+ plan: currentPlan,
+ stepResults: stepResults,
+ timestamp: new Date().toISOString(),
+ });
+ setCurrentProgress(70);
+ }
+
+ // 移除执行中消息
+ setMessages((prev) => prev.filter((m) => m.type !== MessageTypes.AGENT_EXECUTING));
+
+ // 显示最终结果
+ addMessage({
+ type: MessageTypes.AGENT_RESPONSE,
+ content: data.final_answer || data.message || '处理完成',
+ plan: currentPlan,
+ stepResults: stepResults,
+ metadata: data.metadata,
+ timestamp: new Date().toISOString(),
+ });
+
+ setCurrentProgress(100);
+
+ // 重新加载会话列表
+ loadSessions();
+ }
+ } catch (error) {
+ logger.error('Agent chat error', error);
+
+ // 移除思考/执行中消息
+ setMessages((prev) =>
+ prev.filter(
+ (m) => m.type !== MessageTypes.AGENT_THINKING && m.type !== MessageTypes.AGENT_EXECUTING
+ )
+ );
+
+ const errorMessage = error.response?.data?.error || error.message || '处理失败';
+
+ addMessage({
+ type: MessageTypes.ERROR,
+ content: `处理失败:${errorMessage}`,
+ timestamp: new Date().toISOString(),
+ });
+
+ toast({
+ title: '处理失败',
+ description: errorMessage,
+ status: 'error',
+ duration: 5000,
+ isClosable: true,
+ });
+ } finally {
+ setIsProcessing(false);
+ setCurrentProgress(0);
+ inputRef.current?.focus();
+ }
+ };
+
+ // 处理键盘事件
+ const handleKeyPress = (e) => {
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault();
+ handleSendMessage();
+ }
+ };
+
+ // 清空对话
+ const handleClearChat = () => {
+ createNewSession();
+ };
+
+ // 导出对话
+ const handleExportChat = () => {
+ const chatText = messages
+ .filter((m) => m.type === MessageTypes.USER || m.type === MessageTypes.AGENT_RESPONSE)
+ .map((msg) => `[${msg.type === MessageTypes.USER ? '用户' : '价小前'}] ${msg.content}`)
+ .join('\n\n');
+
+ const blob = new Blob([chatText], { type: 'text/plain' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = `chat_${new Date().toISOString().slice(0, 10)}.txt`;
+ a.click();
+ URL.revokeObjectURL(url);
+ };
+
+ // ==================== 初始化 ====================
+
+ useEffect(() => {
+ if (user) {
+ loadSessions();
+ createNewSession();
+ }
+ }, [user]);
+
+ // ==================== 渲染 ====================
+
+ // 快捷问题
+ const quickQuestions = [
+ '全面分析贵州茅台这只股票',
+ '今日涨停股票有哪些亮点',
+ '新能源概念板块的投资机会',
+ '半导体行业最新动态',
+ ];
+
+ // 筛选会话
+ const filteredSessions = sessions.filter(
+ (session) =>
+ !searchQuery ||
+ session.last_message?.toLowerCase().includes(searchQuery.toLowerCase())
+ );
+
+ return (
+
+ {/* 左侧会话列表 */}
+
+
+ {/* 侧边栏头部 */}
+
+ }
+ colorScheme="blue"
+ w="100%"
+ onClick={createNewSession}
+ size="sm"
+ >
+ 新建对话
+
+
+ {/* 搜索框 */}
+
+
+
+
+ setSearchQuery(e.target.value)}
+ />
+
+
+
+ {/* 会话列表 */}
+
+ {isLoadingSessions ? (
+
+
+
+ ) : filteredSessions.length === 0 ? (
+
+
+
+ {searchQuery ? '没有找到匹配的对话' : '暂无对话记录'}
+
+
+ ) : (
+ filteredSessions.map((session) => (
+ switchSession(session.session_id)}
+ >
+
+
+
+ {session.last_message || '新对话'}
+
+
+
+
+ {new Date(session.last_timestamp).toLocaleDateString('zh-CN', {
+ month: 'numeric',
+ day: 'numeric',
+ hour: 'numeric',
+ minute: 'numeric',
+ })}
+
+
+ {session.message_count} 条
+
+
+
+
+
+
+
+ ))
+ )}
+
+
+ {/* 用户信息 */}
+
+
+
+
+
+ {user?.nickname || '未登录'}
+
+
+ {user?.id || 'anonymous'}
+
+
+
+
+
+
+
+ {/* 主聊天区域 */}
+
+ {/* 聊天头部 */}
+
+
+
+ }
+ size="sm"
+ variant="ghost"
+ aria-label="切换侧边栏"
+ onClick={toggleSidebar}
+ />
+ } />
+
+ 价小前投研
+
+
+
+
+ 智能分析
+
+
+
+ 多步骤深度研究
+
+
+
+
+
+
+ }
+ size="sm"
+ variant="ghost"
+ aria-label="清空对话"
+ onClick={handleClearChat}
+ />
+ }
+ size="sm"
+ variant="ghost"
+ aria-label="导出对话"
+ onClick={handleExportChat}
+ />
+
+
+
+ {/* 进度条 */}
+ {isProcessing && (
+
+ )}
+
+
+ {/* 消息列表 */}
+
+
+ {messages.map((message) => (
+
+
+
+ ))}
+
+
+
+
+ {/* 快捷问题 */}
+ {messages.length <= 2 && !isProcessing && (
+
+
+ 💡 试试这些问题:
+
+
+ {quickQuestions.map((question, idx) => (
+
+ ))}
+
+
+ )}
+
+ {/* 输入框 */}
+
+
+ setInputValue(e.target.value)}
+ onKeyPress={handleKeyPress}
+ placeholder="输入你的问题,我会进行深度分析..."
+ bg={inputBg}
+ border="1px"
+ borderColor={borderColor}
+ _focus={{ borderColor: 'blue.500', boxShadow: '0 0 0 1px #3182CE' }}
+ mr={2}
+ disabled={isProcessing}
+ size="lg"
+ />
+ : }
+ colorScheme="blue"
+ aria-label="发送"
+ onClick={handleSendMessage}
+ isLoading={isProcessing}
+ disabled={!inputValue.trim() || isProcessing}
+ size="lg"
+ />
+
+
+
+
+ );
+};
+
+/**
+ * 消息渲染器
+ */
+const MessageRenderer = ({ message, userAvatar }) => {
+ const userBubbleBg = useColorModeValue('blue.500', 'blue.600');
+ const agentBubbleBg = useColorModeValue('white', 'gray.700');
+ const borderColor = useColorModeValue('gray.200', 'gray.600');
+
+ switch (message.type) {
+ case MessageTypes.USER:
+ return (
+
+
+
+
+ {message.content}
+
+
+ } />
+
+
+ );
+
+ case MessageTypes.AGENT_THINKING:
+ return (
+
+
+ } />
+
+
+
+
+ {message.content}
+
+
+
+
+
+ );
+
+ case MessageTypes.AGENT_PLAN:
+ return (
+
+
+ } />
+
+
+
+
+
+ );
+
+ case MessageTypes.AGENT_EXECUTING:
+ return (
+
+
+ } />
+
+
+ {message.stepResults?.map((result, idx) => (
+
+ ))}
+
+
+
+ );
+
+ case MessageTypes.AGENT_RESPONSE:
+ return (
+
+
+ } />
+
+ {/* 最终总结 */}
+
+
+ {message.content}
+
+
+ {/* 元数据 */}
+ {message.metadata && (
+
+ 总步骤: {message.metadata.total_steps}
+ ✓ {message.metadata.successful_steps}
+ {message.metadata.failed_steps > 0 && (
+ ✗ {message.metadata.failed_steps}
+ )}
+ 耗时: {message.metadata.total_execution_time?.toFixed(1)}s
+
+ )}
+
+
+ {/* 执行详情(可选) */}
+ {message.plan && message.stepResults && message.stepResults.length > 0 && (
+
+
+
+ 📊 执行详情(点击展开查看)
+
+ {message.stepResults.map((result, idx) => (
+
+ ))}
+
+ )}
+
+
+
+ );
+
+ case MessageTypes.ERROR:
+ return (
+
+
+ } />
+
+ {message.content}
+
+
+
+ );
+
+ default:
+ return null;
+ }
+};
+
+export default AgentChatV3;