From c42e319300b821577472ba3c5a89a3871e4a2320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8C=85=E6=A1=91?= <948125001@qq.com> Date: Thu, 4 Jun 2026 21:25:33 +0800 Subject: [PATCH 1/3] fix: use backend sessionId as threadId to fix broken multi-turn conversations Previously, threadId was managed by the frontend in-memory sessionState, causing each browser tab to generate a different threadId. Multi-turn context was effectively broken across tabs. Now the backend sessionId (from ChatSession) is used as the threadId, making the backend the source of truth. Same session = same threadId, regardless of browser tab. --- data-agent-frontend/src/services/graph.ts | 4 ++++ data-agent-frontend/src/views/AgentRun.vue | 1 + .../cloud/ai/dataagent/controller/GraphController.java | 4 +++- .../com/alibaba/cloud/ai/dataagent/dto/GraphRequest.java | 2 ++ .../ai/dataagent/service/graph/GraphServiceImpl.java | 8 ++++++++ 5 files changed, 18 insertions(+), 1 deletion(-) diff --git a/data-agent-frontend/src/services/graph.ts b/data-agent-frontend/src/services/graph.ts index ef50143c2..4d848d5ba 100644 --- a/data-agent-frontend/src/services/graph.ts +++ b/data-agent-frontend/src/services/graph.ts @@ -17,6 +17,7 @@ export interface GraphRequest { agentId: string; threadId?: string; + sessionId?: string; query: string; humanFeedback: boolean; humanFeedbackContent?: string; @@ -72,6 +73,9 @@ class GraphService { params.append('rejectedPlan', request.rejectedPlan.toString()); params.append('nl2sqlOnly', request.nl2sqlOnly.toString()); + if (request.sessionId) { + params.append('sessionId', request.sessionId); + } if (request.humanFeedbackContent) { params.append('humanFeedbackContent', request.humanFeedbackContent); } diff --git a/data-agent-frontend/src/views/AgentRun.vue b/data-agent-frontend/src/views/AgentRun.vue index 1381af373..1c2a1f30d 100644 --- a/data-agent-frontend/src/views/AgentRun.vue +++ b/data-agent-frontend/src/views/AgentRun.vue @@ -612,6 +612,7 @@ rejectedPlan: false, humanFeedbackContent: null, threadId: sessionState.lastRequest?.threadId || null, + sessionId: currentSession.value.id, }; userInput.value = ''; diff --git a/data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/controller/GraphController.java b/data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/controller/GraphController.java index b8b58d664..f4ca60feb 100644 --- a/data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/controller/GraphController.java +++ b/data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/controller/GraphController.java @@ -49,7 +49,8 @@ public Flux> streamSearch(@RequestParam("agen @RequestParam(value = "humanFeedback", required = false) boolean humanFeedback, @RequestParam(value = "humanFeedbackContent", required = false) String humanFeedbackContent, @RequestParam(value = "rejectedPlan", required = false) boolean rejectedPlan, - @RequestParam(value = "nl2sqlOnly", required = false) boolean nl2sqlOnly, ServerHttpResponse response) { + @RequestParam(value = "nl2sqlOnly", required = false) boolean nl2sqlOnly, + @RequestParam(value = "sessionId", required = false) String sessionId, ServerHttpResponse response) { // Set SSE-related HTTP headers response.getHeaders().add("Cache-Control", "no-cache"); response.getHeaders().add("Connection", "keep-alive"); @@ -65,6 +66,7 @@ public Flux> streamSearch(@RequestParam("agen .humanFeedbackContent(humanFeedbackContent) .rejectedPlan(rejectedPlan) .nl2sqlOnly(nl2sqlOnly) + .sessionId(sessionId) .build(); graphService.graphStreamProcess(sink, request); diff --git a/data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/dto/GraphRequest.java b/data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/dto/GraphRequest.java index be10cc82c..52fe096b0 100644 --- a/data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/dto/GraphRequest.java +++ b/data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/dto/GraphRequest.java @@ -40,4 +40,6 @@ public class GraphRequest { private boolean nl2sqlOnly; + private String sessionId; + } diff --git a/data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/service/graph/GraphServiceImpl.java b/data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/service/graph/GraphServiceImpl.java index 6325b9bbf..81caec885 100644 --- a/data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/service/graph/GraphServiceImpl.java +++ b/data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/service/graph/GraphServiceImpl.java @@ -82,6 +82,13 @@ public void graphStreamProcess(Sinks.Many> si graphRequest.setThreadId(UUID.randomUUID().toString()); } String threadId = graphRequest.getThreadId(); + // 如果前端传了 sessionId,用 sessionId 统一多轮对话的 threadId + if (StringUtils.hasText(graphRequest.getSessionId())) { + threadId = graphRequest.getSessionId(); + graphRequest.setThreadId(threadId); + } + log.info("[多轮调试] graphStreamProcess 入口, threadId={}, sessionId={}, query={}", threadId, + graphRequest.getSessionId(), graphRequest.getQuery()); // 创建或获取 StreamContext StreamContext context = streamContextMap.computeIfAbsent(threadId, k -> new StreamContext()); context.setSink(sink); @@ -138,6 +145,7 @@ private void handleNewProcess(GraphRequest graphRequest) { context.setSpan(span); String multiTurnContext = multiTurnContextManager.buildContext(threadId); + log.info("[多轮调试] handleNewProcess, threadId={}, multiTurnContext={}", threadId, multiTurnContext); multiTurnContextManager.beginTurn(threadId, query); Flux nodeOutputFlux = compiledGraph.stream( Map.of(IS_ONLY_NL2SQL, nl2sqlOnly, INPUT_KEY, query, AGENT_ID, agentId, HUMAN_REVIEW_ENABLED, From b034f089f5e40d62a44b17e245a7929f4c69476a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8C=85=E6=A1=91?= <948125001@qq.com> Date: Sun, 14 Jun 2026 12:42:36 +0800 Subject: [PATCH 2/3] fix: update GraphControllerTest to match new streamSearch signature with sessionId --- .../cloud/ai/dataagent/controller/GraphControllerTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data-agent-management/src/test/java/com/alibaba/cloud/ai/dataagent/controller/GraphControllerTest.java b/data-agent-management/src/test/java/com/alibaba/cloud/ai/dataagent/controller/GraphControllerTest.java index d9e5df858..a5f08e238 100644 --- a/data-agent-management/src/test/java/com/alibaba/cloud/ai/dataagent/controller/GraphControllerTest.java +++ b/data-agent-management/src/test/java/com/alibaba/cloud/ai/dataagent/controller/GraphControllerTest.java @@ -62,7 +62,7 @@ void streamSearch_validRequest_invokesGraphServiceAndReturnsFlux() { doNothing().when(graphService).graphStreamProcess(any(Sinks.Many.class), any(GraphRequest.class)); Flux> result = graphController.streamSearch("agent-1", "thread-1", - "show me sales data", false, null, false, false, serverHttpResponse); + "show me sales data", false, null, false, false, null, serverHttpResponse); assertNotNull(result); @@ -81,7 +81,7 @@ void streamSearch_humanFeedback_passesHumanFeedbackParams() { doNothing().when(graphService).graphStreamProcess(any(Sinks.Many.class), any(GraphRequest.class)); Flux> result = graphController.streamSearch("agent-1", "thread-2", - "approve this plan", true, "looks good", false, false, serverHttpResponse); + "approve this plan", true, "looks good", false, false, null, serverHttpResponse); assertNotNull(result); @@ -99,7 +99,7 @@ void streamSearch_nl2sqlOnly_setsNl2sqlOnlyFlag() { doNothing().when(graphService).graphStreamProcess(any(Sinks.Many.class), any(GraphRequest.class)); Flux> result = graphController.streamSearch("agent-1", null, "SELECT query", - false, null, false, true, serverHttpResponse); + false, null, false, true, null, serverHttpResponse); assertNotNull(result); From ef8110dc34ba70d8a15f9970e871649ba75b2ce1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8C=85=E6=A1=91?= <948125001@qq.com> Date: Sun, 14 Jun 2026 12:52:14 +0800 Subject: [PATCH 3/3] chore: update debug log prefix for before/after comparison --- .../cloud/ai/dataagent/service/graph/GraphServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/service/graph/GraphServiceImpl.java b/data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/service/graph/GraphServiceImpl.java index 81caec885..e6fb7595d 100644 --- a/data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/service/graph/GraphServiceImpl.java +++ b/data-agent-management/src/main/java/com/alibaba/cloud/ai/dataagent/service/graph/GraphServiceImpl.java @@ -87,7 +87,7 @@ public void graphStreamProcess(Sinks.Many> si threadId = graphRequest.getSessionId(); graphRequest.setThreadId(threadId); } - log.info("[多轮调试] graphStreamProcess 入口, threadId={}, sessionId={}, query={}", threadId, + log.info("[多轮调试-修复后] graphStreamProcess 入口, threadId={}, sessionId={}, query={}", threadId, graphRequest.getSessionId(), graphRequest.getQuery()); // 创建或获取 StreamContext StreamContext context = streamContextMap.computeIfAbsent(threadId, k -> new StreamContext());