Spring AI Alibaba Deep Research:自动化深度调研与报告生成

Spring AI Alibaba Deep Research:自动化深度调研与报告生成

导读:Deep Research 是 Spring AI Alibaba 1.1 版中最令人兴奋的能力之一------它让 AI 能够自主进行多步骤研究:搜索信息、分析来源、批判性思考、迭代补充,最终生成结构化的深度报告。本文深入讲解 Deep Research 的工作机制、API 调用方式、四类研究模板以及性能优化策略。


一、什么是 Deep Research

普通的 AI 对话是"单步回答":你问一个问题,模型基于训练数据给出一个回答,完事。

Deep Research 是"多步研究":

  1. 理解研究目标:分析问题,确定研究方向;
  2. 多轮搜索:自主发起多次搜索,不断补充信息;
  3. 信息整合:对多个来源的信息做交叉验证和综合;
  4. 批判性分析:识别信息矛盾,评估来源可靠性;
  5. 报告生成:组织成结构化、有引用的深度报告。

这个过程与人类研究员的工作流程高度相似,区别在于 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 从"回答者"变成了"研究者":

  1. 多轮搜索循环:自主评估信息缺口,迭代补充;
  2. 四类研究模板:学术、竞品、技术、市场,覆盖主要场景;
  3. 并行搜索:多问题同时搜索,显著提升速度;
  4. 引用溯源:保留所有搜索来源,支持人工验证;
  5. 流式输出:实时展示研究进度,避免"黑盒"等待感。

下一篇进入 Prompt 工程与管理:如何对企业 Prompt 资产做版本控制、A/B 测试和热更新。


参考资料

相关推荐
Matrix_112 小时前
论文阅读:UltraFusion Ultra High Dynamic Imaging using Exposure Fusion
论文阅读·人工智能·计算摄影
kaoshi100app2 小时前
本周,河南二建报名公布!
开发语言·人工智能·职场和发展·学习方法
我材不敲代码2 小时前
基于 OpenCV 的票据图像矫正与透视变换实战
人工智能·opencv·计算机视觉
marteker2 小时前
研究发现,电商零售商计划在代理电商领域进行大规模投资
人工智能
人工智能AI技术2 小时前
Qwen3.5-Plus登顶|C#集成通义千问,高并发服务实战优化
人工智能·c#
阿_旭2 小时前
基于YOLO26深度学习的蓝莓成熟度检测与分割系统【python源码+Pyqt5界面+数据集+训练代码】图像分割、人工智能
人工智能·python·深度学习·毕业设计·蓝莓成熟度检测
恼书:-(空寄2 小时前
拦截器获取不到 POST 请求 JSON 结构体参数(完整解决方案)
java·spring boot·spring·servlet
智算菩萨2 小时前
【How Far Are We From AGI】4 AGI的“生理系统“——从算法架构到算力基座的工程革命
论文阅读·人工智能·深度学习·算法·ai·架构·agi
White-Legend2 小时前
GPT5.4每日200刀
人工智能·gpt