From b45c4dca048b782dcdb98584eb34909e949178ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BB=B7=E5=B0=8F=E5=89=8D?= Date: Tue, 20 Jan 2026 18:02:16 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0ios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MeAgent/src/constants/agentConstants.js | 1 + MeAgent/src/hooks/useAgentChat.js | 19 +++++++++++ .../Agent/components/MarkdownRenderer.js | 12 +++++-- mcp_elasticsearch.py | 1 + mcp_server.py | 34 ++++++++++++++++--- 5 files changed, 60 insertions(+), 7 deletions(-) diff --git a/MeAgent/src/constants/agentConstants.js b/MeAgent/src/constants/agentConstants.js index a73a4514..bcf699c7 100644 --- a/MeAgent/src/constants/agentConstants.js +++ b/MeAgent/src/constants/agentConstants.js @@ -16,6 +16,7 @@ export const MessageTypes = { // SSE 事件类型 export const SSEEventTypes = { + SESSION_START: 'session_start', // 会话开始(包含 session_id) STATUS: 'status', THINKING: 'thinking', THINKING_START: 'thinking_start', diff --git a/MeAgent/src/hooks/useAgentChat.js b/MeAgent/src/hooks/useAgentChat.js index a79d7315..e63afbc2 100644 --- a/MeAgent/src/hooks/useAgentChat.js +++ b/MeAgent/src/hooks/useAgentChat.js @@ -79,6 +79,17 @@ export const useAgentChat = () => { const { type, data } = event; switch (type) { + case SSEEventTypes.SESSION_START: + // 会话开始 - 保存 session_id(多轮对话的关键!) + if (data?.session_id) { + console.log('[useAgentChat] 收到 session_start,session_id:', data.session_id); + dispatch(setCurrentSession({ + sessionId: data.session_id, + title: null, // 标题稍后会通过 session_title 事件更新 + })); + } + break; + case SSEEventTypes.STATUS: // 状态更新(如 "正在思考...") if (data?.message) { @@ -266,6 +277,14 @@ export const useAgentChat = () => { type: MessageTypes.AGENT_RESPONSE, updates: { isStreaming: false }, })); + // 确保 session_id 被保存(兜底处理,正常应该在 session_start 事件中已保存) + if (data?.session_id && !currentSessionId) { + console.log('[useAgentChat] 在 done 事件中保存 session_id:', data.session_id); + dispatch(setCurrentSession({ + sessionId: data.session_id, + title: null, + })); + } dispatch(setIsProcessing(false)); // 重新加载会话列表 if (user?.id) { diff --git a/MeAgent/src/screens/Agent/components/MarkdownRenderer.js b/MeAgent/src/screens/Agent/components/MarkdownRenderer.js index af9ed2c4..eab5a8ac 100644 --- a/MeAgent/src/screens/Agent/components/MarkdownRenderer.js +++ b/MeAgent/src/screens/Agent/components/MarkdownRenderer.js @@ -21,8 +21,9 @@ import { AgentTheme } from '../../../constants/agentConstants'; const STOCK_CODE_REGEX = /\b(\d{6})(?:\.(SZ|SH|sz|sh|BJ|bj))?\b/g; const { width: SCREEN_WIDTH } = Dimensions.get('window'); -const CHART_WIDTH = SCREEN_WIDTH - 48; // 减去边距 -const CHART_HEIGHT = 300; +// 图表宽度计算:屏幕宽度 - 容器padding(16*2) - 气泡padding(16*2) - 图表容器padding(12*2) - 额外边距(8) +const CHART_WIDTH = SCREEN_WIDTH - 32 - 32 - 24 - 8; // = SCREEN_WIDTH - 96 +const CHART_HEIGHT = 280; /** * 简单的 Markdown 解析器 @@ -449,6 +450,13 @@ const generateEChartsHTML = (option, width, height) => { if (option.legend && !option.legend.textStyle) { option.legend.textStyle = darkTheme.legend.textStyle; } + // 确保 grid 有足够的边距,防止右侧被裁剪 + if (!option.grid) { + option.grid = { left: '3%', right: '8%', bottom: '12%', top: '15%', containLabel: true }; + } else if (!option.grid.containLabel) { + option.grid.containLabel = true; + if (!option.grid.right) option.grid.right = '8%'; + } if (option.xAxis) { var xAxis = Array.isArray(option.xAxis) ? option.xAxis : [option.xAxis]; xAxis.forEach(function(axis) { diff --git a/mcp_elasticsearch.py b/mcp_elasticsearch.py index 53d70e3c..e1c30d1f 100644 --- a/mcp_elasticsearch.py +++ b/mcp_elasticsearch.py @@ -268,6 +268,7 @@ class ESClient: "plan": doc.get("plan"), "steps": doc.get("steps"), "timestamp": doc["timestamp"], + "session_title": doc.get("session_title"), # 包含会话标题 }) return messages diff --git a/mcp_server.py b/mcp_server.py index 2272870b..4df58b36 100644 --- a/mcp_server.py +++ b/mcp_server.py @@ -2567,8 +2567,12 @@ A股交易时间: 上午 9:30-11:30,下午 13:00-15:00 - 如有数值数据,可使用 ECharts 图表展示(使用 ```echarts 代码块)""" try: - # 发送开始事件 - yield self._format_sse("status", {"stage": "start", "message": "开始处理查询"}) + # 发送开始事件(包含 session_id,让前端能够保存) + yield self._format_sse("session_start", { + "session_id": session_id, + "is_new_session": is_new_session, + "message": "开始处理查询" + }) # 构建消息列表 messages = [ @@ -3295,9 +3299,12 @@ A股交易时间: 上午 9:30-11:30,下午 13:00-15:00 ) logger.info(f"[ES] Agent 回复已保存到会话 {session_id}") - # 如果生成了标题,通过 SSE 发送给前端 + # 如果生成了标题,通过 SSE 发送给前端(包含 session_id) if session_title: - yield self._format_sse("session_title", {"title": session_title}) + yield self._format_sse("session_title", { + "session_id": session_id, + "title": session_title + }) except Exception as e: logger.error(f"[ES] 保存 Agent 回复失败: {e}", exc_info=True) @@ -3823,13 +3830,30 @@ async def get_chat_history(session_id: str, limit: int = 100): limit: 返回数量(默认100) Returns: - 聊天记录列表 + 聊天记录列表,包含会话标题 """ try: messages = es_client.get_chat_history(session_id, limit) + + # 从消息中提取会话标题(优先从首条消息获取) + title = None + for msg in messages: + if msg.get("session_title"): + title = msg["session_title"] + break + # 如果没有标题,使用首条用户消息的前 30 个字符 + if not title and messages: + for msg in messages: + if msg.get("message_type") == "user": + content = msg.get("message", "") + title = content[:30] + "..." if len(content) > 30 else content + break + return { "success": True, "data": messages, + "title": title, # 返回会话标题 + "session_id": session_id, "count": len(messages) } except Exception as e: