Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions spring-ai-alibaba-deepresearch/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-tika-document-reader</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

</dependencies>
<dependencyManagement>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
import com.alibaba.cloud.ai.example.deepresearch.node.ReporterNode;
import com.alibaba.cloud.ai.example.deepresearch.node.ResearchTeamNode;
import com.alibaba.cloud.ai.example.deepresearch.node.ResearcherNode;
import com.alibaba.cloud.ai.example.deepresearch.service.ReportService;

import com.alibaba.cloud.ai.example.deepresearch.serializer.DeepResearchStateSerializer;
import com.alibaba.cloud.ai.graph.GraphRepresentation;
import com.alibaba.cloud.ai.graph.KeyStrategy;
Expand Down Expand Up @@ -71,7 +73,7 @@ public class DeepResearchConfiguration {

private static final Logger logger = LoggerFactory.getLogger(DeepResearchConfiguration.class);

@Autowired
@Autowired(required = false)
private TavilySearchService tavilySearchService;

@Autowired
Expand All @@ -98,6 +100,9 @@ public class DeepResearchConfiguration {
@Autowired(required = false)
private RetrievalAugmentationAdvisor retrievalAugmentationAdvisor;

@Autowired
private ReportService reportService;

@Bean
public StateGraph deepResearch(ChatClient researchAgent) throws GraphStateException {

Expand Down Expand Up @@ -151,7 +156,7 @@ public StateGraph deepResearch(ChatClient researchAgent) throws GraphStateExcept
.addNode("human_feedback", node_async(new HumanFeedbackNode()))
.addNode("research_team", node_async(new ResearchTeamNode()))
.addNode("parallel_executor", node_async(new ParallelExecutorNode(deepResearchProperties)))
.addNode("reporter", node_async((new ReporterNode(reporterAgent))))
.addNode("reporter", node_async((new ReporterNode(reporterAgent, reportService))))
.addNode("rag_node", node_async(new RagNode(retrievalAugmentationAdvisor, researchAgent)));

// 添加并行节点块
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.alibaba.cloud.ai.example.deepresearch.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
* Redis 配置类
*
* @author huangzhen
* @since 2025/6/18
*/
@Configuration
@ConditionalOnProperty(name = "spring.data.redis.enabled", havingValue = "true", matchIfMissing = false)
public class RedisConfig {

/**
* 配置 RedisTemplate
* @param redisConnectionFactory Redis 连接工厂
* @return RedisTemplate 实例
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);

// 设置键的序列化器
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());

// 设置值的序列化器
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

// 设置默认序列化器
template.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());

template.afterPropertiesSet();
return template;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.alibaba.cloud.ai.example.deepresearch.controller;

import com.alibaba.cloud.ai.example.deepresearch.model.response.BaseResponse;
import com.alibaba.cloud.ai.example.deepresearch.model.response.ExistsResponse;
import com.alibaba.cloud.ai.example.deepresearch.model.response.ReportResponse;
import com.alibaba.cloud.ai.example.deepresearch.service.ReportService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

/**
* 报告查询控制器
*
* @author huangzhen
* @since 2025/6/18
*/
@RestController
@RequestMapping("/api/reports")
public class ReportController {

private static final Logger logger = LoggerFactory.getLogger(ReportController.class);

private final ReportService reportService;

public ReportController(ReportService reportService) {
this.reportService = reportService;
}

/**
* 根据线程ID获取报告
* @param threadId 线程ID
* @return 报告内容
*/
@GetMapping("/{threadId}")
public ResponseEntity<ReportResponse> getReport(@PathVariable String threadId) {
try {
logger.info("查询报告,线程ID: {}", threadId);
String report = reportService.getReport(threadId);

if (report != null) {
ReportResponse response = new ReportResponse();
response.setThreadId(threadId);
response.setReport(report);
response.setStatus("success");
response.setMessage("报告获取成功");
return ResponseEntity.ok(response);
}
else {
ReportResponse response = new ReportResponse();
response.setThreadId(threadId);
response.setStatus("not_found");
response.setMessage("未找到指定线程ID的报告");
return ResponseEntity.notFound().build();
}
}
catch (Exception e) {
logger.error("获取报告失败,线程ID: {}", threadId, e);
ReportResponse response = new ReportResponse();
response.setThreadId(threadId);
response.setStatus("error");
response.setMessage("获取报告失败: " + e.getMessage());
return ResponseEntity.internalServerError().body(response);
}
}

/**
* 检查报告是否存在
* @param threadId 线程ID
* @return 是否存在
*/
@GetMapping("/{threadId}/exists")
public ResponseEntity<ExistsResponse> existsReport(@PathVariable String threadId) {
try {
logger.info("检查报告是否存在,线程ID: {}", threadId);
boolean exists = reportService.existsReport(threadId);

ExistsResponse response = new ExistsResponse();
response.setThreadId(threadId);
response.setExists(exists);
response.setStatus("success");
response.setMessage(exists ? "报告存在" : "报告不存在");

return ResponseEntity.ok(response);
}
catch (Exception e) {
logger.error("检查报告是否存在失败,线程ID: {}", threadId, e);
ExistsResponse response = new ExistsResponse();
response.setThreadId(threadId);
response.setExists(false);
response.setStatus("error");
response.setMessage("检查失败: " + e.getMessage());
return ResponseEntity.internalServerError().body(response);
}
}

/**
* 删除报告
* @param threadId 线程ID
* @return 删除结果
*/
@DeleteMapping("/{threadId}")
public ResponseEntity<BaseResponse> deleteReport(@PathVariable String threadId) {
try {
logger.info("删除报告,线程ID: {}", threadId);

if (!reportService.existsReport(threadId)) {
BaseResponse response = new BaseResponse();
response.setThreadId(threadId);
response.setStatus("not_found");
response.setMessage("未找到指定线程ID的报告");
return ResponseEntity.notFound().build();
}

reportService.deleteReport(threadId);

BaseResponse response = new BaseResponse();
response.setThreadId(threadId);
response.setStatus("success");
response.setMessage("报告删除成功");

return ResponseEntity.ok(response);
}
catch (Exception e) {
logger.error("删除报告失败,线程ID: {}", threadId, e);
BaseResponse response = new BaseResponse();
response.setThreadId(threadId);
response.setStatus("error");
response.setMessage("删除报告失败: " + e.getMessage());
return ResponseEntity.internalServerError().body(response);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.alibaba.cloud.ai.example.deepresearch.model.response;

/**
* 基础响应类
*
* @author huangzhen
* @since 2025/6/20
*/
public class BaseResponse {

private String threadId;

private String status;

private String message;

public String getThreadId() {
return threadId;
}

public void setThreadId(String threadId) {
this.threadId = threadId;
}

public String getStatus() {
return status;
}

public void setStatus(String status) {
this.status = status;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.alibaba.cloud.ai.example.deepresearch.model.response;

/**
* 存在性检查响应类
*
* @author huangzhen
* @since 2025/6/20
*/
public class ExistsResponse extends BaseResponse {

private boolean exists;

public boolean isExists() {
return exists;
}

public void setExists(boolean exists) {
this.exists = exists;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.alibaba.cloud.ai.example.deepresearch.model.response;

/**
* 报告响应类
*
* @author huangzhen
* @since 2025/6/20
*/
public class ReportResponse extends BaseResponse {

private String report;

public String getReport() {
return report;
}

public void setReport(String report) {
this.report = report;
}

}
Loading
Loading