Spring AI Alibaba Deep Research:自动化深度调研与报告生成
导读:Deep Research 是 Spring AI Alibaba 1.1 版中最令人兴奋的能力之一------它让 AI 能够自主进行多步骤研究:搜索信息、分析来源、批判性思考、迭代补充,最终生成结构化的深度报告。本文深入讲解 Deep Research 的工作机制、API 调用方式、四类研究模板以及性能优化策略。
一、什么是 Deep Research
普通的 AI 对话是"单步回答":你问一个问题,模型基于训练数据给出一个回答,完事。
Deep Research 是"多步研究":
- 理解研究目标:分析问题,确定研究方向;
- 多轮搜索:自主发起多次搜索,不断补充信息;
- 信息整合:对多个来源的信息做交叉验证和综合;
- 批判性分析:识别信息矛盾,评估来源可靠性;
- 报告生成:组织成结构化、有引用的深度报告。
这个过程与人类研究员的工作流程高度相似,区别在于 AI 可以在几分钟内完成人类需要几小时的工作。
二、Deep Research 的技术架构
用户研究请求
|
v
[研究规划阶段]
识别研究主题,分解子问题
|
v
[多轮搜索循环]
┌─────────────────────────────┐
│ 搜索查询 → 获取搜索结果 │
│ | │
│ 内容抓取 → 提取关键信息 │
│ | │
│ 分析缺口 → 是否需要继续搜索│
│ | │
│ 判断:信息充分?→ 是 → 退出 │
│ ↓ 否 │
│ 调整搜索策略 → 继续循环 │
└─────────────────────────────┘
|
v
[信息整合与批判性分析]
交叉验证,识别矛盾,评估可信度
|
v
[报告撰写]
结构化 Markdown,含引用和数据
|
v
最终研究报告
三、环境准备与依赖
3.1 Maven 依赖
xml
<!-- Spring AI Alibaba(含 Deep Research 支持) -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<!-- Deep Research 需要 Agent Framework -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-agent-framework</artifactId>
</dependency>
<!-- Tavily 搜索 API(推荐,提供结构化搜索结果) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-tavily-search-api</artifactId>
</dependency>
<!-- Spring Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
3.2 配置
yaml
spring:
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY}
chat:
options:
# Deep Research 建议使用强力模型
model: qwen-max
# 开启推理增强(针对 qwen-max 和 qwen-plus)
enable-thinking: true
temperature: 0.7
max-tokens: 8192
# Tavily 搜索 API(需要申请 API Key)
tavily:
api-key: ${TAVILY_API_KEY}
deep-research:
# 最大搜索轮次
max-iterations: 5
# 每轮最大搜索数量
max-searches-per-round: 3
# 最终报告最大 Token 数
report-max-tokens: 8192
# 是否并行搜索(提升速度)
parallel-search: true
四、Deep Research Service 实现
4.1 核心 ResearchService
java
package com.example.research;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Deep Research Service
* 实现自动化多步骤深度调研
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class DeepResearchService {
private final ChatClient researchChatClient;
private final WebSearchTool webSearchTool;
private final ExecutorService executor = Executors.newFixedThreadPool(5);
/**
* 执行深度研究(同步版本)
*
* @param topic 研究主题
* @param template 研究模板类型
* @param maxRounds 最大迭代轮次
*/
public ResearchReport research(String topic, ResearchTemplate template, int maxRounds) {
log.info("开始深度研究:主题={}, 模板={}, 最大轮次={}", topic, template, maxRounds);
long startTime = System.currentTimeMillis();
// 1. 研究规划:分解研究目标
List<String> researchQuestions = planResearch(topic, template);
log.info("研究规划完成,共 {} 个子问题", researchQuestions.size());
// 2. 多轮搜索与信息收集
Map<String, List<SearchResult>> allResults = new HashMap<>();
Set<String> processedQuestions = new HashSet<>();
int round = 0;
while (round < maxRounds) {
round++;
log.info("第 {} 轮研究迭代", round);
// 确定本轮要搜索的问题
List<String> currentQuestions = researchQuestions.stream()
.filter(q -> !processedQuestions.contains(q))
.limit(3) // 每轮最多处理3个问题
.toList();
if (currentQuestions.isEmpty()) {
log.info("所有问题已处理,提前结束");
break;
}
// 并行搜索(如果开启)
if (Boolean.TRUE.equals(researchConfig.isParallelSearch())) {
List<CompletableFuture<Map.Entry<String, List<SearchResult>>>> futures =
currentQuestions.stream()
.map(q -> CompletableFuture.supplyAsync(() ->
Map.entry(q, webSearchTool.search(q, 5)), executor))
.toList();
futures.forEach(f -> {
try {
var entry = f.get();
allResults.put(entry.getKey(), entry.getValue());
processedQuestions.add(entry.getKey());
} catch (Exception e) {
log.warn("搜索任务失败:{}", e.getMessage());
}
});
} else {
// 串行搜索
currentQuestions.forEach(q -> {
List<SearchResult> results = webSearchTool.search(q, 5);
allResults.put(q, results);
processedQuestions.add(q);
});
}
// 判断是否需要补充搜索
List<String> newQuestions = evaluateAndExpand(topic, allResults);
researchQuestions.addAll(newQuestions);
log.info("本轮发现 {} 个新研究问题", newQuestions.size());
}
// 3. 整合信息,生成报告
String report = synthesizeReport(topic, template, allResults);
long duration = System.currentTimeMillis() - startTime;
log.info("深度研究完成,耗时:{}ms", duration);
return new ResearchReport(topic, report, allResults, round, duration);
}
/**
* 流式深度研究(边生成边返回)
*/
public Flux<String> researchStream(String topic, ResearchTemplate template) {
return Flux.create(sink -> {
executor.submit(() -> {
try {
sink.next("🔍 开始研究:" + topic + "\n\n");
// 规划阶段
sink.next("📋 **研究规划中...**\n");
List<String> questions = planResearch(topic, template);
sink.next("发现 " + questions.size() + " 个核心研究问题\n\n");
// 搜索阶段
Map<String, List<SearchResult>> allResults = new HashMap<>();
for (int i = 0; i < Math.min(questions.size(), 5); i++) {
String q = questions.get(i);
sink.next("🔎 搜索:" + q + "\n");
List<SearchResult> results = webSearchTool.search(q, 3);
allResults.put(q, results);
sink.next("找到 " + results.size() + " 条相关信息\n");
}
// 报告生成
sink.next("\n📝 **正在撰写研究报告...**\n\n");
String report = synthesizeReport(topic, template, allResults);
sink.next(report);
sink.complete();
} catch (Exception e) {
sink.error(e);
}
});
});
}
/**
* 研究规划:将主题分解为具体研究问题
*/
private List<String> planResearch(String topic, ResearchTemplate template) {
String systemPrompt = switch (template) {
case ACADEMIC -> """
你是一个学术研究助手。将研究主题分解为 5-7 个具体的学术研究问题。
问题应涵盖:定义与背景、现状与进展、方法与技术、挑战与局限、未来展望。
以 JSON 数组格式输出:["问题1", "问题2", ...]
""";
case COMPETITOR_ANALYSIS -> """
你是一个竞争情报分析师。将分析主题分解为 5-7 个具体调研维度。
涵盖:市场规模、主要竞争对手、产品功能对比、定价策略、用户口碑、技术优势。
以 JSON 数组格式输出:["问题1", "问题2", ...]
""";
case TECHNOLOGY_RESEARCH -> """
你是一个技术架构师。将技术主题分解为 5-7 个深度技术问题。
涵盖:技术原理、主流实现、最佳实践、性能比较、选型建议、未来趋势。
以 JSON 数组格式输出:["问题1", "问题2", ...]
""";
case MARKET_RESEARCH -> """
你是一个市场研究分析师。将市场主题分解为 5-7 个市场研究问题。
涵盖:市场规模、增长趋势、用户画像、痛点需求、市场机会、风险分析。
以 JSON 数组格式输出:["问题1", "问题2", ...]
""";
};
String planResult = researchChatClient.prompt()
.system(systemPrompt)
.user("研究主题:" + topic)
.options(DashScopeChatOptions.builder()
.withModel("qwen-max")
.withTemperature(0.3)
.build())
.call()
.content();
try {
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(
planResult.replaceAll("```json|```", "").trim(),
new TypeReference<>() {});
} catch (Exception e) {
log.warn("研究计划解析失败,使用默认问题");
return List.of(topic + " 的基本情况",
topic + " 的最新进展",
topic + " 的主要挑战");
}
}
/**
* 评估当前信息,决定是否需要补充搜索
*/
private List<String> evaluateAndExpand(String topic,
Map<String, List<SearchResult>> currentResults) {
// 将当前搜索结果摘要给模型,让它判断缺少哪些信息
String summary = buildResultSummary(currentResults);
String evaluation = researchChatClient.prompt()
.system("""
你是一个研究评估专家。分析已有研究结果,判断是否还有重要信息缺失。
如果有,输出最多3个补充搜索问题(JSON数组);如果信息充分,输出空数组[]。
只输出JSON,不要其他内容。
""")
.user("研究主题:" + topic + "\n\n已收集的信息摘要:\n" + summary)
.call()
.content();
try {
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(
evaluation.replaceAll("```json|```", "").trim(),
new TypeReference<>() {});
} catch (Exception e) {
return Collections.emptyList();
}
}
/**
* 综合生成研究报告
*/
private String synthesizeReport(String topic, ResearchTemplate template,
Map<String, List<SearchResult>> allResults) {
String researchContext = buildResearchContext(allResults);
String reportPrompt = switch (template) {
case ACADEMIC -> buildAcademicPrompt(topic, researchContext);
case COMPETITOR_ANALYSIS -> buildCompetitorPrompt(topic, researchContext);
case TECHNOLOGY_RESEARCH -> buildTechPrompt(topic, researchContext);
case MARKET_RESEARCH -> buildMarketPrompt(topic, researchContext);
};
return researchChatClient.prompt()
.system("""
你是一个专业的研究报告撰写专家。
基于提供的研究资料,撰写一份结构清晰、内容详实的研究报告。
要求:
1. 使用 Markdown 格式,层次清晰
2. 关键数据必须注明来源
3. 保持客观中立,区分事实和分析
4. 结论要有数据支撑
""")
.user(reportPrompt)
.options(DashScopeChatOptions.builder()
.withModel("qwen-max")
.withEnableThinking(true) // 开启深度思考模式
.withTemperature(0.5)
.withMaxTokens(8192)
.build())
.call()
.content();
}
}
五、四类研究模板
5.1 研究模板枚举
java
public enum ResearchTemplate {
/** 学术研究:适合学术论文、技术综述 */
ACADEMIC,
/** 竞品分析:适合市场竞争情报 */
COMPETITOR_ANALYSIS,
/** 技术调研:适合技术选型、架构决策 */
TECHNOLOGY_RESEARCH,
/** 市场调研:适合新市场进入分析 */
MARKET_RESEARCH
}
5.2 学术研究 Prompt 模板
java
private String buildAcademicPrompt(String topic, String context) {
return String.format("""
研究主题:%s
研究资料:
%s
请撰写一份学术研究综述,包含以下章节:
# %s 综述报告
## 摘要
(200字以内的核心内容总结)
## 1. 研究背景与意义
- 领域现状
- 研究必要性
## 2. 核心概念与理论基础
- 关键定义
- 理论框架
## 3. 研究现状与进展
- 主要研究成果
- 代表性工作
## 4. 技术方法与挑战
- 主流技术路线
- 现存挑战
## 5. 未来研究方向
- 发展趋势
- 开放问题
## 参考文献
(列出资料中提到的主要来源)
""", topic, context, topic);
}
5.3 竞品分析 Prompt 模板
java
private String buildCompetitorPrompt(String topic, String context) {
return String.format("""
分析目标:%s
市场情报资料:
%s
请撰写竞品分析报告,包含:
# %s 竞品分析报告
**分析日期:%s**
## 执行摘要
## 1. 市场格局
- 市场规模与增速
- 主要参与者分布
## 2. 竞争对手深度分析
(每个主要竞品的详细分析)
## 3. 功能对比矩阵
(表格形式展示各竞品功能对比)
## 4. 定价策略分析
## 5. 用户口碑与痛点
## 6. 竞争优劣势总结
## 7. 战略建议
""", topic, context, topic, LocalDate.now().toString());
}
六、结果结构化输出
java
/**
* 研究报告数据对象
*/
@Value
@Builder
public class ResearchReport {
String topic;
String reportContent; // Markdown 格式报告正文
Map<String, List<SearchResult>> references; // 原始搜索结果(引用溯源)
int totalRounds; // 实际执行的研究轮次
long durationMs; // 总耗时(毫秒)
/**
* 导出为 Markdown 文件
*/
public String toMarkdown() {
StringBuilder sb = new StringBuilder();
sb.append("---\n");
sb.append("研究主题: ").append(topic).append("\n");
sb.append("生成时间: ").append(LocalDateTime.now()).append("\n");
sb.append("研究轮次: ").append(totalRounds).append("\n");
sb.append("耗时: ").append(durationMs / 1000.0).append("秒\n");
sb.append("---\n\n");
sb.append(reportContent);
sb.append("\n\n---\n\n## 参考来源\n\n");
references.forEach((query, results) -> {
sb.append("### ").append(query).append("\n");
results.forEach(r ->
sb.append("- [").append(r.getTitle()).append("](")
.append(r.getUrl()).append(")\n"));
});
return sb.toString();
}
}
七、REST 接口封装
java
@RestController
@RequestMapping("/api/research")
@RequiredArgsConstructor
@Slf4j
public class DeepResearchController {
private final DeepResearchService researchService;
/**
* 同步深度研究(等待完成后返回)
*/
@PostMapping("/sync")
public ResponseEntity<Map<String, Object>> syncResearch(
@RequestBody ResearchRequest request) {
ResearchReport report = researchService.research(
request.getTopic(),
request.getTemplate(),
request.getMaxRounds()
);
return ResponseEntity.ok(Map.of(
"topic", report.getTopic(),
"report", report.getReportContent(),
"rounds", report.getTotalRounds(),
"durationMs", report.getDurationMs()
));
}
/**
* 流式深度研究(实时返回研究进度和结果)
*/
@GetMapping(value = "/stream", produces = "text/event-stream;charset=UTF-8")
public Flux<String> streamResearch(
@RequestParam String topic,
@RequestParam(defaultValue = "TECHNOLOGY_RESEARCH") ResearchTemplate template) {
return researchService.researchStream(topic, template)
.doOnError(e -> log.error("流式研究失败:{}", e.getMessage()));
}
}
八、性能优化
8.1 并行搜索策略
串行搜索(默认):
搜索1(2s) → 搜索2(2s) → 搜索3(2s) = 总计 6s
并行搜索:
搜索1(2s) ─┐
搜索2(2s) ─┤ → max(2s,2s,2s) = 总计 2s(节省 4s)
搜索3(2s) ─┘
8.2 搜索结果缓存
java
@Service
@RequiredArgsConstructor
public class CachedWebSearchTool {
private final WebSearchTool webSearchTool;
// 简单 LRU 缓存,24小时过期
private final Cache<String, List<SearchResult>> cache = Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(24, TimeUnit.HOURS)
.build();
public List<SearchResult> searchWithCache(String query, int maxResults) {
String cacheKey = query + "_" + maxResults;
return cache.get(cacheKey, k -> webSearchTool.search(query, maxResults));
}
}
九、总结
Deep Research 将 AI 从"回答者"变成了"研究者":
- 多轮搜索循环:自主评估信息缺口,迭代补充;
- 四类研究模板:学术、竞品、技术、市场,覆盖主要场景;
- 并行搜索:多问题同时搜索,显著提升速度;
- 引用溯源:保留所有搜索来源,支持人工验证;
- 流式输出:实时展示研究进度,避免"黑盒"等待感。
下一篇进入 Prompt 工程与管理:如何对企业 Prompt 资产做版本控制、A/B 测试和热更新。
参考资料