系列文章目录
第一章 「Java AI实战」LangChain4J - 接入Xinference本地大模型
第二章 「Java AI实战」LangChain4J - ChatAPI 及常用配置
第三章 「Java AI实战」LangChain4J - 向量数据库接入与语义检索
文章目录
前言:为什么要在 Java 里玩 Agent 智能体?
这两年,很多团队都已经把「大模型问答」接入到系统里了:
前端一个输入框,后端调一下 OpenAI / DeepSeek / 本地模型,返回一段回答——这类 「简单问答型」AI 功能,确实能解决一部分问题,但很快就会遇到瓶颈:
- 它只能说,不会做:不会主动查数据库,不会调用你的接口,不会帮你真正执行脚本;
- 面对复杂一点的需求,比如「帮我生成这个报表的 SQL 并执行一下」,普通 ChatGPT 式的问答就开始力不从心了。
而 Agent 智能体 想解决的,就是这件事:
不只是回答你「怎么做」,而是能 自己思考、自己决定要不要用工具、再用工具把事做完。
在实际项目里,这类需求其实随处可见,比如:
- SQL 助手:
数据开发中心里,工程师每天写大量 SQL,如果 AI 能读懂“用自然语言描述的需求”,自动查元数据、生成 SQL、跑一下再把结果解释出来,开发效率会高一大截。 - 字段对标 & 映射助手:
对接多源异构表时,要把各种 sfzh / sfzhm / id_no 这类字段映射到同一个标准字段,如果有个 Agent 能结合 Embedding 相似度 + 业务字典自动推荐 Top-N 映射,人只需要点选确认,就非常香。 - 运维脚本自动化
重启服务、查看日志、检查磁盘空间这些重复活,如果封装为工具,让 Agent 根据自然语言指令「选择合适脚本」来执行,既能提升效率,又能减少人为失误。
这些场景有一个共同点:
👉 只是把 LLM 当聊天机器人完全不够,它必须学会“用工具”并嵌入你的业务系统。
一、Agent 智能体到底是什么?先把概念讲明白
在动手写 LangChain4J Agent 之前,先想清楚一个问题:
普通 LLM 和 Agent 智能体,到底差在哪?
很多人现在用大模型,还停留在「更聪明的模板引擎」阶段:给一段 prompt,它回一段文本,本质上还是 只会说,不会做。
1. LLM vs Agent:从“会说话”到“会办事”
LLM(大语言模型)擅长的是:
- 根据上下文生成文本(问答、翻译、润色、摘要等);
- 所有“知识”都来自训练语料;
- 不会真的去查你的库、调你的接口,更不会登录服务器执行脚本。
简单讲:它只能给“答案”,不会真的去“操作”。
Agent(智能体)多了一层能力:
能够 理解你的目标 → 规划步骤 → 主动调用工具 → 再把结果整理给你。
例如你说:
“帮我查下上个月每个支行的不良贷款余额,并生成一个汇总表。”
一个 Agent 会:
- 先理解需求:要查哪段时间、哪类指标;
- 决定是否需要查数据库、用哪张表、怎么拼 SQL;
- 调用你提供的
queryDatabase(sql)这类工具; - 拿到结果后生成表格/Markdown,并给出简单分析。
可以粗暴地理解为:
Agent = LLM(大脑) + 工具使用能力 + 任务规划能力
2. 在 LangChain4J 里,Agent 是怎么抽象出来的?
在 LangChain4J 里,可以用一个公式来概括 Agent 思想:
Agent = LLM + Tools + Memory + Orchestration(编排)
-
LLM(大脑)
由ChatLanguageModel/StreamingChatLanguageModel提供,
可以是 DeepSeek / OpenAI / Moonshot / Qwen,
也可以是 Xinference、Ollama 等本地模型(通过 OpenAI 协议接入)。 -
Tools(工具 / 手脚)
就是你暴露给 Agent 的一组“可执行能力”,例如:- 查询数据库:
getTables()/getTableSchema(table) - 字段对标:
matchStandardField(localName) - 运维操作:
restartService(name)/tailLog(name)
在 LangChain4J 中可以通过
@Tool注解或ToolSpecification定义,
模型会把它们当成“可调用函数”来选择和使用。 - 查询数据库:
-
Memory(记忆)
保存对话历史和业务上下文(如当前用户、当前项目、当前库等),
避免 Agent 每轮对话都“失忆”。 -
Orchestration(编排)
由 LangChain4J 帮你处理:- 把用户输入 + 历史上下文 + 可用工具一起发给 LLM;
- 解析模型是否要调工具、调哪个、带什么参数;
- 执行工具后再把结果喂回 LLM,得到最终回答。
作为 Java 开发,你主要做两件事:
- 把
model / tools / memory配好; - 把 Agent 封装成一个普通的
Service或Controller对外提供能力。
3. 和 Python 生态里的 Agent 有啥关系?
如果你用过 Python 里的 LangChain Agent、AutoGen 等,可以这样理解:
LangChain4J 就是把这些成熟的 Agent 思路,用 Java / Spring 体系重新实现了一套。
差异更多体现在生态和工程实践上:
- Python 更偏探索和原型验证,示例多;
- Java 更关注:
- 如何和 Spring Boot 集成;
- 如何工程化、可维护;
- 如何在现有系统里“渐进式”引入 Agent,而不是推倒重来。
4. 典型业务 Agent 场景一览
结合几个真实场景,你会更容易把 Agent 和业务联想起来:
-
SQL Copilot(数据库 AI 助手)
- 用户用自然语言提需求;
- Agent 自动查元数据、生成 SQL、执行查询;
- 再把结果整理成报表 + 解释。
-
字段自动对标助手(数据治理)
- 输入外部表字段列表(如
sfzh,sfzhm,id_no); - Agent 调用 Embedding + 标准字段库做相似度匹配;
- 输出 Top-N 候选和置信度,供人工确认。
- 输入外部表字段列表(如
-
智能运维 / 报表生成 / 文档问答
- 运维:自然语言触发日志查询、服务重启等脚本工具;
- 报表:根据描述自动选 SQL + 模板,生成日报/周报骨架;
- 文档问答:把 RAG 查询封装成 Tool,Agent 自动决定何时查知识库、如何整合答案。
在这些场景里,Agent 不再只是一个“聊天窗口”,而更像你系统里的「虚拟同事」:
它知道什么时候问模型,什么时候查数据库、调接口、跑脚本,
并把这些步骤串联起来,给出一个真正可用的业务结果。
二、开发环境与项目初始化
1. 技术栈与版本选择
- JDK:17+
- Spring Boot:3.x
- 构建工具:Maven
2. 模型选型:本地模型优先
本文选用 本地模型 作为主角,通过 OpenAI 协议接入 LangChain4J:本地部署继续选用:
Xinference。其使用见本系列第一篇文章:
https://blog.youkuaiyun.com/OFFTime_we/article/details/147198108?spm=1001.2014.3001.5501
三、从 0 实现「天气 / 快递查询 Agent」
有了前面 Agent 概念和开发环境的铺垫,下面实现一个「能查天气、也能查快递状态」的 Agent。
这里选用 langchain4j + MCP 协议 的方式来做:
- 端口 8082:MCP 客户端 + DeepSeek 模型(大脑)
- 端口 8080:MCP 服务端(工具中心:天气查询 + 快递查询)
大模型负责“理解问题 + 决定是否用工具”, MCP 服务端负责“提供具体能力(天气 / 快递接口)”。
3.1 整体流程示意
可以先用一句话概括这个 Agent 的工作流:
用户用自然语言提问 → DeepSeek 理解意图 → 通过 MCP 调用天气/快递工具 → 获取结果 → DeepSeek 基于结果生成自然语言回答 → 返回给前端。
下面我们分别看客户端和服务端的实现。
3.2 MCP 客户端:把 DeepSeek 变成会用工具的 Agent
客户端应用(端口 8082)是一个标准的 Spring Boot 项目,主要做两件事:
- 配置
ChatClient,让它能访问 DeepSeek 模型; - 通过 MCP 配置,把服务端暴露的工具挂载到
ChatClient上。
3.2.1 pom依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.example</groupId>
<artifactId>langchain4j-example</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>langchain4j-12mcp-client</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<name>Central Portal Snapshots</name>
<id>central-portal-snapshots</id>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
</project>
3.2.2 ChatClient 配置:挂上 MCP 工具回调
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class McpConfig {
@Bean
public ChatClient chatClient(ChatClient.Builder chatClientBuilder,
ToolCallbackProvider mcpToolProvider) {
return chatClientBuilder
// 关键:为 ChatClient 挂上 MCP 提供的 Tool 回调
.defaultToolCallbacks(mcpToolProvider)
.build();
}
}
这里的 mcpToolProvider 是 Spring AI 根据 spring.ai.mcp.client 配置自动生成的,
里面包含了 MCP 服务端暴露出来的所有工具。defaultToolCallbacks 的意思就是:
" 以后这个 ChatClient 在对话中,如果模型想用工具,就用这批 MCP 工具。”
3.2.3 Controller:对外提供一个对话接口
package org.example.controller;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ClientController {
@Resource
private ChatClient chatClient;
@GetMapping("/chat")
public String chat(@RequestParam(value = "msg") String msg) {
return chatClient.prompt().user(msg).call().content();
}
}
这个接口很简单:
- 前端通过 GET /chat?msg=xxx 发起请求;
- ChatClient 会把这条消息 + 工具列表交给 DeepSeek;
- DeepSeek 决定要不要调用天气/快递工具;
- 最终返回一段自然语言回复。
3.2.4 客户端启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class McpClientApplication {
public static void main(String[] args) {
SpringApplication.run(McpClientApplication.class, args);
}
}
3.2.5 客户端配置:DeepSeek + MCP 客户端
server:
port: 8082
spring:
ai:
openai:
api-key: sk-xxxxxx # 你的 DeepSeek 或 OpenAI API Key
base-url: https://api.deepseek.com # DeepSeek 的 OpenAI 兼容地址
chat:
options:
model: deepseek-chat # 使用的具体模型
mcp:
client:
type: ASYNC
request-timeout: 30s
toolcallback:
enabled: true # 启用 MCP 工具回调
sse:
connections:
mcp-server: # MCP 连接名称
url: http://localhost:8080 # MCP 服务端地址
重点关注两点:
- openai 部分:配置大模型(这里用的是 DeepSeek)
- mcp.client 部分:配置 MCP 客户端,指向 http://localhost:8080 这个 MCP Server。
3.3 MCP 服务端:把天气 / 快递能力封装成 @Tool 工具
服务端应用(端口 8080)同样是一个 Spring Boot 项目,它扮演的是:
“工具中心” —— 对外暴露各种可以被 LLM 调用的业务方法。
这里我们提供两个工具:
- getExpressByState(state):根据快递状态返回快递信息;
- getWeatherByCity(city):根据城市名称返回天气预报。
3.3.1 pom依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.example</groupId>
<artifactId>langchain4j-example</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>langchain4j-13mcp-server</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<name>Central Portal Snapshots</name>
<id>central-portal-snapshots</id>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
</project>
3.3.2 ToolsConfig:注册工具 Provider
package org.example.config;
import jakarta.annotation.Resource;
import org.example.service.ExpressService;
import org.example.service.WeatherService;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ToolsConfig {
@Resource
private ExpressService expressService;
@Bean
public ToolCallbackProvider expressTools() {
return MethodToolCallbackProvider.builder()
.toolObjects(expressService)
.build();
}
@Bean
public ToolCallbackProvider weatherTools(WeatherService weatherService) {
return MethodToolCallbackProvider.builder()
.toolObjects(weatherService)
.build();
}
}
这里使用 MethodToolCallbackProvider:
- 会自动扫描你传入对象上的所有 @Tool 方法;
- 并把它们注册为 MCP Tool,供客户端那边的大模型调用。
3.3.3 ExpressService:快递查询工具
package org.example.service;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
public class ExpressService {
@Tool(description = "根据快递状态获取快递信息")
public String getExpressByState(String state) {
Map<String, String> map = Map.of(
"已发货", "快递编号0001,目前状态:在北京发往西安中心途中,最后更新时间:2025.6.12",
"已签收", "快递编号0002,目前状态:已签收,最后更新时间:2025.5.12",
"已退货", "快递编号0003,目前状态:在西安发往北京中心途中,最后更新时间:2025.5.11"
);
return map.getOrDefault(state, "抱歉:未查询到对应快递信息!");
}
}
这里为了示例简洁,直接用 Map 做了一个“假数据字典”,
实际项目里可以替换成调用三方快递平台的接口。
3.3.4 WeatherService:天气查询工具
package org.example.service;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
public class WeatherService {
@Tool(description = "根据城市名称获取天气预报")
public String getWeatherByCity(String city) {
Map<String, String> map = Map.of(
"北京", "降雨频繁,其中今天和后天雨势较强,部分地区有暴雨,并伴强对流天气,需注意防范",
"上海", "多云,15℃~27℃,南风3级,当前温度27℃。",
"深圳", "多云40天,阴16天,雨30天,晴3天"
);
return map.getOrDefault(city, "抱歉:未查询到对应城市!");
}
}
同样是用 Map 模拟天气数据,
真实场景中,你可以在这里对接气象 API 或自己的天气数据服务。
3.3.5 MCP 服务端启动类与配置
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class McpServerApplication {
public static void main(String[] args) {
SpringApplication.run(McpServerApplication.class, args);
}
}
yml配置文件
server.port=8080
spring.application.name=mcp-server
spring.ai.mcp.server.type=async
到这里,8080 这个服务就成为一个 MCP Server,对外暴露了两个 Tool:
- getExpressByState(state)
- getWeatherByCity(city)
3.4 完整调用链(含结果回填)
以一句自然语言请求为例:
用户访问:GET http://localhost:8082/chat?msg=帮我查一下上海的天气,要不要带伞?
完整链路大致如下:
-
用户调用接口
- 浏览器 / 前端调用 GET /chat?msg=…;
- ClientController.chat(msg) 收到请求,调用:chatClient.prompt().user(msg).call().content();
-
ChatClient 组装请求并发给 DeepSeek
- 带上:用户输入 + 当前对话上下文 + 所有 MCP 工具列表;
- DeepSeek 知道自己现在不仅能聊天,还有“天气查询 / 快递查询”两个工具可用。
-
大模型决定是否调用工具
- DeepSeek 分析语义:这是一个“天气 + 城市 + 是否需要带伞”的问题;
- 决定调用 getWeatherByCity 工具,并构造出诸如:
{ "tool": "getWeatherByCity", "arguments": { "city": "上海" } } -
MCP 客户端把工具调用转发到服务端
-
Spring AI MCP Client 解析出“要调用哪个工具 + 参数是什么”;
-
通过 SSE 与 http://localhost:8080 的 MCP Server 通信;
-
在服务端触发 WeatherService.getWeatherByCity(“上海”) 执行。
-
-
服务端执行 @Tool 方法并返回结果
- WeatherService 根据 city 从 Map 中取出上海的天气描述;
- 返回:上海, 多云,15℃~27℃,南风3级,当前温度27℃。
-
结果回填给大模型(关键步骤)
- MCP Server 把这个结果返回给 MCP Client;
- MCP Client 再把“工具输出”封装进对话上下文,回填给 DeepSeek;
- DeepSeek 拿到这个“真实天气数据”后,会在此基础上生成一段更自然的回答,例如:
“上海今天多云,气温 15~27℃,南风 3 级,体感比较舒适。如果你是晚上出门,建议带一件薄外套;是否带伞可以根据停留时间和行程安排灵活选择,但目前看并没有明显的强降水预警。”
-
Controller 将最终回答返回给前端
-
content() 中就是模型基于工具结果生成的完整回答;
-
通过 HTTP 响应返回给前端页面 / Postman / 其他调用方。
-
总结
本文结合 LangChain4J ,把 LLM、Tools、Memory、Orchestration 之间的关系讲清楚,并说明对 Java 开发来说,本质就是把模型和业务方法通过注解、配置组装成一个可以像普通 Service 一样调用的智能体。通过一个「天气 / 快递查询 Agent」完整演示了 Spring Boot + Spring AI + MCP 的落地方案:前端只发自然语言,DeepSeek 负责决策是否调用 MCP 工具,服务端提供天气、快递查询能力,结果再回填给模型生成最终回复。后续可以在此基础上替换为自己的 SQL 助手、字段对标、运维脚本等业务场景。
4618

被折叠的 条评论
为什么被折叠?



