diff --git a/MeAgent/src/screens/Agent/components/MarkdownRenderer.js b/MeAgent/src/screens/Agent/components/MarkdownRenderer.js index 53de6e8a..af9ed2c4 100644 --- a/MeAgent/src/screens/Agent/components/MarkdownRenderer.js +++ b/MeAgent/src/screens/Agent/components/MarkdownRenderer.js @@ -263,10 +263,10 @@ const StockCodeLink = memo(({ code, exchange, navigation }) => { } const handlePress = useCallback(() => { - const stockCode = exchangeSuffix ? `${code}.${exchangeSuffix}` : code; + const fullStockCode = exchangeSuffix ? `${code}.${exchangeSuffix}` : code; navigation?.navigate('StockDetail', { - code: stockCode, - name: stockCode, // 名称可以在详情页获取 + stockCode: fullStockCode, + stockName: fullStockCode, // 名称可以在详情页获取 }); }, [code, exchangeSuffix, navigation]); diff --git a/mcp_server.py b/mcp_server.py index b23d9ea0..b7585470 100644 --- a/mcp_server.py +++ b/mcp_server.py @@ -2662,27 +2662,36 @@ A股交易时间: 上午 9:30-11:30,下午 13:00-15:00 # 将 assistant 消息添加到历史(包含 tool_calls) messages.append(assistant_message) + # 构建当前轮次的步骤列表 + current_round_steps = [] + for tc in native_tool_calls: + try: + args = json.loads(tc.function.arguments) if tc.function.arguments else {} + except: + args = {} + current_round_steps.append({ + "tool": tc.function.name, + "arguments": args, + "reason": f"调用 {tc.function.name}" + }) + # 如果是第一次工具调用,发送计划事件 if step_index == 0: - # 构建计划数据 plan_data = { "goal": f"分析用户问题:{user_query[:50]}...", "reasoning": "使用工具获取相关数据进行分析", - "steps": [] + "steps": current_round_steps } - for tc in native_tool_calls: - try: - args = json.loads(tc.function.arguments) if tc.function.arguments else {} - except: - args = {} - plan_data["steps"].append({ - "tool": tc.function.name, - "arguments": args, - "reason": f"调用 {tc.function.name}" - }) - yield self._format_sse("plan", plan_data) yield self._format_sse("status", {"stage": "executing", "message": f"开始执行 {len(native_tool_calls)} 个工具调用"}) + else: + # 多轮调用时,发送 plan_update 事件追加新步骤 + yield self._format_sse("plan_update", { + "new_steps": current_round_steps, + "round": step_index // len(native_tool_calls) + 1 if native_tool_calls else 1, + "message": f"模型决定继续调用 {len(native_tool_calls)} 个工具" + }) + yield self._format_sse("status", {"stage": "executing", "message": f"继续执行第 {step_index + 1} 步"}) # 执行每个工具调用 for tool_call in native_tool_calls: @@ -2795,18 +2804,29 @@ A股交易时间: 上午 9:30-11:30,下午 13:00-15:00 # 将 assistant 消息添加到历史 messages.append({"role": "assistant", "content": assistant_message.content}) + # 构建当前轮次的步骤列表 + current_round_steps = [ + {"tool": tc["name"], "arguments": tc["arguments"], "reason": f"调用 {tc['name']}"} + for tc in text_tool_calls + ] + # 如果是第一次工具调用,发送计划事件 if step_index == 0: plan_data = { "goal": f"分析用户问题:{user_query[:50]}...", "reasoning": "使用工具获取相关数据进行分析", - "steps": [ - {"tool": tc["name"], "arguments": tc["arguments"], "reason": f"调用 {tc['name']}"} - for tc in text_tool_calls - ] + "steps": current_round_steps } yield self._format_sse("plan", plan_data) yield self._format_sse("status", {"stage": "executing", "message": f"开始执行 {len(text_tool_calls)} 个工具调用"}) + else: + # 多轮调用时,发送 plan_update 事件追加新步骤 + yield self._format_sse("plan_update", { + "new_steps": current_round_steps, + "round": step_index // len(text_tool_calls) + 1 if text_tool_calls else 1, + "message": f"模型决定继续调用 {len(text_tool_calls)} 个工具" + }) + yield self._format_sse("status", {"stage": "executing", "message": f"继续执行第 {step_index + 1} 步"}) # 执行每个工具调用 for tc in text_tool_calls: diff --git a/src/views/AgentChat/components/ChatArea/MessageRenderer.js b/src/views/AgentChat/components/ChatArea/MessageRenderer.js index bcc865eb..6a5cb324 100644 --- a/src/views/AgentChat/components/ChatArea/MessageRenderer.js +++ b/src/views/AgentChat/components/ChatArea/MessageRenderer.js @@ -389,6 +389,7 @@ const MessageRenderer = ({ message, userAvatar }) => { ); case MessageTypes.AGENT_PLAN: + const planStepsToShow = message.plan?.steps || []; return ( @@ -420,13 +421,13 @@ const MessageRenderer = ({ message, userAvatar }) => { 执行计划 - {message.plan?.steps && ( + {planStepsToShow.length > 0 && ( - {message.plan.steps.length} 个步骤 + {planStepsToShow.length} 个步骤 )} @@ -437,38 +438,41 @@ const MessageRenderer = ({ message, userAvatar }) => { )} - {message.plan?.steps && ( + {planStepsToShow.length > 0 && ( - {message.plan.steps.map((step, idx) => ( - - + {planStepsToShow.map((step, idx) => ( + - - {idx + 1} - - - {step.tool} - - - - ))} + + {idx + 1} + + + {step.tool} + + + + ))} + )} @@ -479,10 +483,42 @@ const MessageRenderer = ({ message, userAvatar }) => { ); case MessageTypes.AGENT_EXECUTING: - const steps = message.plan?.steps || []; + // 从 plan 中获取步骤列表,如果没有 plan,则从 stepResults 中构建 + const planSteps = message.plan?.steps || []; const completedSteps = message.stepResults || []; - const currentStepIndex = completedSteps.length; - const progress = steps.length > 0 ? (completedSteps.length / steps.length) * 100 : 0; + + // 如果有当前步骤但不在 planSteps 中,需要动态添加 + const currentStepInfo = message.currentStep; + const activeStepIndex = message.currentStepIndex; + + // 构建完整的步骤列表:已完成的步骤 + 当前步骤(如果有) + // 这样即使在多轮调用时 plan 还没更新,也能显示当前正在执行的步骤 + let displaySteps = [...planSteps]; + + // 如果 planSteps 为空但有 completedSteps,从 completedSteps 构建 + if (displaySteps.length === 0 && completedSteps.length > 0) { + displaySteps = completedSteps.map(s => ({ tool: s.tool, arguments: {} })); + } + + // 如果当前有正在执行的步骤,且不在显示列表中,添加它 + if (currentStepInfo && activeStepIndex !== null && activeStepIndex !== undefined) { + while (displaySteps.length <= activeStepIndex) { + if (displaySteps.length === activeStepIndex) { + displaySteps.push({ tool: currentStepInfo.tool, arguments: currentStepInfo.arguments }); + } else { + // 填充中间可能缺失的步骤(从已完成结果中获取) + const stepResult = completedSteps[displaySteps.length]; + if (stepResult) { + displaySteps.push({ tool: stepResult.tool, arguments: {} }); + } else { + displaySteps.push({ tool: '未知步骤', arguments: {} }); + } + } + } + } + + const totalSteps = Math.max(displaySteps.length, completedSteps.length + (activeStepIndex !== null ? 1 : 0)); + const progress = totalSteps > 0 ? (completedSteps.length / totalSteps) * 100 : 0; return ( @@ -520,8 +556,13 @@ const MessageRenderer = ({ message, userAvatar }) => { 正在执行 - {completedSteps.length} / {steps.length} + {completedSteps.length}{totalSteps > 0 ? ` / ${totalSteps}` : ''} + {activeStepIndex !== null && activeStepIndex !== undefined && currentStepInfo && ( + + → {currentStepInfo.tool} + + )} {/* 进度条 */} @@ -539,15 +580,17 @@ const MessageRenderer = ({ message, userAvatar }) => { {/* 步骤列表 */} - {steps.map((step, idx) => { + {displaySteps.map((step, idx) => { const result = completedSteps[idx]; const isCompleted = idx < completedSteps.length; - const isRunning = idx === currentStepIndex; - const isPending = idx > currentStepIndex; + const isRunning = activeStepIndex !== null && activeStepIndex !== undefined + ? idx === activeStepIndex + : idx === completedSteps.length; + const isPending = !isCompleted && !isRunning; return ( { {result.execution_time.toFixed(2)}s )} + + {/* 执行失败时显示错误提示 */} + {result?.status === 'failed' && result?.error && ( + + + 失败 + + + )} ); diff --git a/src/views/AgentChat/constants/messageTypes.ts b/src/views/AgentChat/constants/messageTypes.ts index 6a7c650c..fd3fabca 100644 --- a/src/views/AgentChat/constants/messageTypes.ts +++ b/src/views/AgentChat/constants/messageTypes.ts @@ -59,6 +59,16 @@ export interface UserMessage extends BaseMessage { files?: MessageFile[]; } +/** + * 当前执行步骤信息 + */ +export interface CurrentStep { + /** 工具名称 */ + tool: string; + /** 工具参数 */ + arguments: Record; +} + /** * Agent 消息接口 */ @@ -79,6 +89,10 @@ export interface AgentMessage extends BaseMessage { execution_time?: number; error?: string; }>; + /** 当前正在执行的步骤索引(用于 AGENT_EXECUTING 类型)*/ + currentStepIndex?: number | null; + /** 当前正在执行的步骤信息(用于 AGENT_EXECUTING 类型)*/ + currentStep?: CurrentStep | null; /** 深度思考内容(用于 AGENT_DEEP_THINKING 类型)*/ thinkingContent?: string; /** 是否正在思考中 */ diff --git a/src/views/AgentChat/hooks/useAgentChat.ts b/src/views/AgentChat/hooks/useAgentChat.ts index e0fc1544..42715c5e 100644 --- a/src/views/AgentChat/hooks/useAgentChat.ts +++ b/src/views/AgentChat/hooks/useAgentChat.ts @@ -126,6 +126,7 @@ export const useAgentChat = ({ isDeepThinking: boolean; // 是否正在进行深度思考 plan: any; stepResults: any[]; + currentStep: { tool: string; arguments: any } | null; // 当前正在执行的步骤 sessionId: string | null; sessionTitle: string | null; }>({ @@ -135,6 +136,7 @@ export const useAgentChat = ({ isDeepThinking: false, plan: null, stepResults: [], + currentStep: null, sessionId: null, sessionTitle: null, }); @@ -196,6 +198,7 @@ export const useAgentChat = ({ isDeepThinking: false, plan: null, stepResults: [], + currentStep: null, sessionId: null, sessionTitle: null, }; @@ -409,11 +412,35 @@ export const useAgentChat = ({ }); break; + case 'plan_update': + // 多轮工具调用时,追加新的步骤到计划中 + if (data?.new_steps && streamStateRef.current.plan) { + streamStateRef.current.plan.steps = [ + ...(streamStateRef.current.plan.steps || []), + ...data.new_steps + ]; + // 更新 AGENT_PLAN 消息 + updateLastMessage(MessageTypes.AGENT_PLAN, { + plan: { ...streamStateRef.current.plan }, + }); + // 更新 AGENT_EXECUTING 消息 + updateLastMessage(MessageTypes.AGENT_EXECUTING, { + plan: { ...streamStateRef.current.plan }, + }); + } + break; + case 'step_start': - // 步骤开始执行 - 保留已完成的步骤结果,更新当前执行状态 + // 步骤开始执行 - 记录当前步骤,更新执行状态 + streamStateRef.current.currentStep = { + tool: data?.tool || '工具', + arguments: data?.arguments || {}, + }; updateLastMessage(MessageTypes.AGENT_EXECUTING, { content: `正在执行步骤 ${(data?.step_index || 0) + 1}: ${data?.tool || '工具'}...`, - stepResults: [...streamStateRef.current.stepResults], // 保留已完成的步骤 + stepResults: [...streamStateRef.current.stepResults], + currentStepIndex: data?.step_index, + currentStep: streamStateRef.current.currentStep, }); break; @@ -424,11 +451,15 @@ export const useAgentChat = ({ tool: data.tool, status: data.status, result: data.result, + error: data.error, execution_time: data.execution_time, }); + streamStateRef.current.currentStep = null; updateLastMessage(MessageTypes.AGENT_EXECUTING, { content: `已完成 ${streamStateRef.current.stepResults.length} 个步骤`, stepResults: [...streamStateRef.current.stepResults], + currentStepIndex: null, + currentStep: null, }); } break;