✦ 干货分享 ✦
第1章:一个文档丢进去能聊?我们先来做这件事
◆
你好,欢迎来到这门"Java + Spring AI 开发 Agent 应用"的保姆级连载。
做这个连载之前,我默认你已经: - 会写 Java,懂 Spring Boot 那一套(Controller、Service、注入) - 会用 Maven 管理依赖 - 但从来没碰过 AI 应用,不懂什么是 Agent、什么是 RAG
有这些底子就够了,剩下的我们一章一章补。
这一章,我不打算上来就画大饼讲什么"Agent 智能体改变世界"。我们先做一件最小的、能跑的事:把文档丢给程序,然后它陪我们聊天。就这么简单。
为什么先做这个?因为秦国统一六国,也是一步一步来的。AI 应用也一样——先让它"动起来",你才能看见后面的路。
01什么叫 Agent?一句话就够了
先把这个概念敲碎。
Agent 的完整定义可以写三篇论文,但你想快速理解,记住这一句就够:
"
Agent 是一个能自主决定调工具、进行多步推理、并且带记忆的 LLM 应用。
"
拆开看,就是三个零件: - 调工具:它能自己决定"我要不要调用一下这个函数" - 多步推理:它不会一步出结果,而是想一步、走一步、再想下一步 - 带记忆:它记得你们之前聊过什么
那它跟你已经听过无数次的 Chatbot 和 RAG 问答有什么区别?这张表一看就懂:
Chatbot 是"你问我答",RAG 是"带上下文地问答",Agent 是"你交代一件事,我自己想办法做完"。区别就在这个"自己想办法"——它开始有主观能动性了。
这一章,我们先不做完整的 Agent,我们先把地基打出来。地基是什么?就是那个能聊的壳。
02我们这套连载到底做的是啥:DocMind
连载贯穿始终的项目,我给起了个名字,叫 DocMind,一个个人文档资料助手。
设想一下场景:你的硬盘里躺着一堆 PDF、Markdown、技术文档、面试笔记。你想问它"我上个月那篇关于微服务的笔记里,重点说了啥",难道还一篇篇翻?
DocMind 就是想干这件事:把文档喂进去,之后你有任何问题,直接问,它从文档里找答案回答你。
但这里我必须诚实一点,管理一下你的预期——因为这两年"AI 能写代码""AI 取代程序员"这种调调太多了,我不想让你抱着不切实际的幻想入门。
DocMind 能做到的: - 基于你文档内容,回答相关问题 - 总结、找要点、对比不同文档的观点 - 引用原文出处(后面章节我们会做)
DocMind 暂时不能做到的: - 不能凭空编造文档里没有的事实(我们用了技术手段去避免它胡说) - 不能实时上网查最新资料(那是联网搜索的能力,我们没规划) - 不能像科幻片那样"全知全能"地帮你决策人生
一句话总结预期:它是一个很听话、记得住你资料的"读后问答专家",不是一个神。
诚实管理预期,比夸大其词更能让你在这条路上走下去。
03为什么是 Java + Spring AI?
你可能一上来就有个疑问:外面铺天盖地都是 Python + LangChain 做 AI,你怎么带我做 Java?
理由很实在,我说给你听:
第一,企业生态成熟。 Java 干了二十多年企业级开发,稳定性、事务、并发、监控、部署,一套成熟的体系摆在那。真要落到生产环境里,Java 的底气是别的语言不好比的。你要是写企业内部的 AI 应用,Java 几乎是绕不开的选择。
第二,Spring AI 抽象层干净,换 Provider 零成本。 这是我最看重的一点。所谓 Provider,就是你调的是哪个大模型——DeepSeek、OpenAI、通义千问、豆包都算是不同 Provider。
Spring AI 干了一件事特别好的事:它把这些乱七八糟的大模型 API 全部统一成一套接口。今天你调 DeepSeek,明天你想换 OpenAI,改一个配置项,代码几乎不用动。这种"换底不换皮"的抽象,就是工程上的体面。
第三,对 Java 开发者极其友好。 它的编程模型就是你熟悉的 Spring 那一套:注入、Bean、配置。你已经会 Spring Boot 了,那学 Spring AI 的曲线基本是平的。
第四,Maven 依赖管理规范。 版本冲突、传递依赖、BOM 管理,这些都是 Java 生态帮你处理好了的。你加依赖,就像你平常加速成包一样。
技术选型这件事,没有绝对的对错,只有合不合适。对于企业 JAVA 团队而言,Java + Spring AI 就是那个"合适"。
04先跑起来!DocMind v0 最小可跑版本
空谈没用,咱们直接开干。
DocMind v0 的目标极简到不能再简:一个 Spring Boot 服务 + 一个极简 HTML 页面。你在网页里输入一句话,发出去,服务端调用 DeepSeek,把回答流式地一个字一个字吐回页面给你看。
没错,这一步我们还没接文档,就纯聊天。为什么先做这个?因为要走通那根最关键的"管子":前端 ↔ 后端 ↔ 大模型 这条数据流。管子通了,后面往里面灌什么都行。
技术栈就三样: - Spring Boot + Spring AI - DeepSeek 大模型 - EventSource(SSE)做流式输出
第一步:pom.xml 关键依赖
打开你的 pom.xml,加这三个东西:
1<parent>
2<groupId>org.springframework.boot</groupId>
3<artifactId>spring-boot-starter-parent</artifactId>
4<version>3.4.0</version>
5<relativePath/>
6</parent>
7
8<dependencies>
9<!-- 最基础的 Web 能力 -->
10<dependency>
11<groupId>org.springframework.boot</groupId>
12<artifactId>spring-boot-starter-web</artifactId>
13</dependency>
14
15<!-- Spring AI:用 BOM 统一管理版本,免得版本打架 -->
16<dependency>
17<groupId>org.springframework.ai</groupId>
18<artifactId>spring-ai-bom</artifactId>
19<version>1.0.0</version>
20<type>pom</type>
21<scope>import</scope>
22</dependency>
23
24<!-- 接 DeepSeek 的适配器,配置好就能用 -->
25<dependency>
26<groupId>org.springframework.ai</groupId>
27<artifactId>spring-ai-starter-model-deepseek</artifactId>
28</dependency>
29</dependencies>
注意两个细节: 1. spring-ai-bom 的 version 写在 dependencyManagement 的 import 下,统一管理下面所有 Spring AI 模块的版本。 2. 因为要引 BOM,建议用 spring-boot-starter-parent 作为 parent。
第二步:application.yml 配置
1spring:
2ai:
3deepseek:
4# 从环境变量读,别把 key 硬编码写进文件!
5api-key: ${DEEPSEEK_API_KEY}
6base-url: https://api.deepseek.com
7chat:
8options:
9model: deepseek-chat
base-url 用默认的 https://api.deepseek.com 就行,也可以不写。
第三步:写 Controller——核心就一行流式调用
这才是精髓所在。Spring AI 的 ChatClient 已经被自动装配好了,你直接注入就能用:
1importorg.springframework.ai.chat.client.ChatClient;
2importorg.springframework.web.bind.annotation.*;
3importreactor.core.publisher.Flux;
4importorg.springframework.http.MediaType;
5
6@RestController
7publicclassChatController {
8
9privatefinal ChatClient chatClient;
10
11// 构造注入,ChatClient 由 starter 自动装配
12publicChatController(ChatClient.Builder builder) {
13this.chatClient = builder.build();
14 }
15
16@GetMapping(value = "/api/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
17public Flux<String> chat(@RequestParam String message) {
18// .prompt(...) 设置用户输入
19// .stream() 开启流式
20// .content() 返回 Flux<String>,一个字一个字往外吐
21return chatClient
22 .prompt(message)
23 .stream()
24 .content();
25 }
26}
你能看到,逻辑就一行链式调用: - .prompt(message)——把用户说的话喂给模型 - .stream()——打开流式开关 - .content()——返回 Flux<String>,也就是一个字符串流
返回值类型 Flux<String> 是 Reactor 的响应式流。前端用 EventSource 接住它,就能实现"打字机效果"。
第四步:极简前端的 HTML
把下面这个文件放到 src/main/resources/static/index.html,一个 input 加一个文本区,用 EventSource 接流:
1<!DOCTYPE html>
2<htmllang="zh">
3<head>
4 <metacharset="UTF-8">
5 <title>DocMind v0</title>
6</head>
7<body>
8 <h1>DocMind v0 —— 先聊起来</h1>
9 <inputid="question"type="text"placeholder="输入你的问题,回车发送"style="width:80%" />
10 <divid="answer"style="margin-top:20px; white-space:pre-wrap;"></div>
11
12 <script>
13const input = document.getElementById('question');
14const answer = document.getElementById('answer');
15
16 input.addEventListener('keydown', async (e) => {
17if (e.key !== 'Enter') return;
18const msg = input.value;
19 input.value = '';
20 answer.textContent = '';
21
22// 用 EventSource 就能接 SSE 流,注意这里要把中文编码一下
23const es = new EventSource('/api/chat?message=' + encodeURIComponent(msg));
24 es.onmessage = (event) => {
25 answer.textContent += event.data;
26 };
27 es.onerror = () => es.close();
28 });
29 </script>
30</body>
31</html>
启动你的 Spring Boot 服务,浏览器打开 http://localhost:8080,输入一句话,回车——你会看到 DeepSeek 的回答一个字一个字地蹦出来。这就是流式输出,这就是 DocMind 的骨架。
恭喜,你的第一个 AI 应用跑起来了。
05本章踩坑
每个章节我都会保留这个小节,把我自己踩过的坑、你大概率也会踩的坑直接摊开。现象 → 根因 → 解法,三行讲清。
坑一:DeepSeek 的 key 在哪申请?怎么配?为什么不能传 GitHub?
- 现象
:不知道上哪搞 key;key 该怎么写进配置;有人截图把 key 晒到网上。 - 根因
:DeepSeek 的 key 要先去它的开放平台注册。进去之后在"API Keys"页面创建。创建后这个 key 就是你的"密码",谁拿到谁就能替你花钱调模型。 - 解法
: 1. 访问 DeepSeek 开放平台 → 注册账号 → 「API Keys」→ 创建新的 key,复制保存(注意 key 只显示一次)。 2. 配置里用环境变量 ${DEEPSEEK_API_KEY},别把 key 直接写进 application.yml。 3. 在项目根目录建一个 .gitignore 文件,把 key、配置文件之类敏感的东西排除掉。绝不要把含 key 的配置推到 GitHub——这是 AI 初学者最容易犯、也最致命的错。
1# .gitignore
2target/
3*.yml
4application.yml
5.env
坑二:Spring AI 版本和 Spring Boot BOM 绑定,不匹配必报错
- 现象
:启动报错,比如 No qualifying bean of type 'ChatClient.Builder' 或者一堆依赖找不到的诡异异常。 - 根因
:Spring AI 的版本和 Spring Boot 版本是绑定的,不能随便配。Spring AI 1.0.0 对应 Spring Boot 3.4.0+。你如果用了 Spring AI 1.0.0 却配了 Boot 3.2,就会因为少了某块自动配置而报一堆错,报错还不在点子上。 - 解法
:记住对应关系,Spring AI 1.0.0 就配 Boot 3.4.0 及以上。用 spring-boot-starter-parent 做 parent,让 BOM 帮你锁版本,别自己手动拼。
坑三:国内调 DeepSeek 要不要代理?
- 现象
:总担心国内网络访问不了 DeepSeek,想挂代理或者换服务器,结果越弄越复杂。 - 根因
:很多人把 DeepSeek 跟 OpenAI(国外服务)搞混了。DeepSeek 是国产模型,API 就在国内,服务器也在国内。 - 解法
:直连即可,国内网络直接可用,不需要代理。这一点比调 OpenAI 省心一百倍。如果你之前为了 OpenAI 配过代理,记得确认没把代理指到手疼——否则反而可能把请求代理到国外绕一圈,更慢。
06下一章预告
这一章,我们让一个"能聊天的壳"跑了起来,走通了 前端 → 后端 → 大模型 的管子。但你大概也感觉到了:这还只是一个 Chatbot,离 Agent 还差得远。
别急。还记得开头那个 Agent 定义的三个零件吗?
下一章,我们就来把它补全。《第2章:Agent 的三个零件——模型、工具、记忆》。我们会把"工具"这个概念拆开——让 Agent 自己去调用函数、去检索你的文档,让那个"能聊"的壳,长出手和脑子。
先聊起来,再学会干活。路一步一步走,Agent 也是。
我们,第2章见。
夜雨聆风