LangChain4j实战系列:将Agent打包成Web应用(前后端全栈+Docker部署)
真实的企业级应用,从来不是“能跑通就行”,而是要满足「可访问、可部署、可并发、有界面」的核心需求。今天这篇,我们就完成整个系列的闭环:把上一篇优化好的“企业智能数据分析Agent”,封装成一个完整的Web应用,用Spring Boot做后端、Vue3做前端,最后用Docker一键打包部署,让你一分钟把Agent丢到服务器上,任何人都能通过浏览器访问使用。
全程复用前两篇的核心代码,只做“封装化”和“界面化”改造,不新增复杂依赖,新手跟着步骤走,也能轻松拿下全栈项目。先明确本次实战目标,做到心中有数:
- 后端改造:用Spring Boot封装Agent核心逻辑,提供RESTful API接口,支持跨域访问,兼容前端请求;
- 前端开发:用Vue3 + Element Plus搭建简洁易用的聊天界面,支持用户输入问题、展示Agent响应结果、自动渲染图表;
- 部署优化:编写Dockerfile和docker-compose配置,实现后端、前端、依赖服务(MySQL、ES、Redis)一键启动,环境无关、部署无忧。
在开始之前,先回顾下我们已有的核心组件(复用前两篇内容,不用重新搭建):
- LLM:通义千问(主力)+ GPT-4(备用),带Redis缓存;
- RAG:Elasticsearch + 通义千问Embedding,带相似度筛选,检索精准;
- Skills:JdbcTool(查数据库)、ChartTool(生成图表)、VectorStoreTool(查知识库),带决策校验;
- 核心逻辑:MCP规则规范工具调用,Agent决策校验避免乱调用,整体稳定、省钱、精准。
一、整体架构设计(先搞懂“前后端怎么连”)
本次Web应用采用「前后端分离」架构,核心逻辑清晰,各组件职责明确,避免耦合,方便后续扩展和维护。整体架构图如下(新手可重点记住流程):
- 前端(Vue3):用户操作界面,负责接收用户输入的问题,发送请求到后端,展示Agent返回的结果(文本+图表);
- 后端(Spring Boot):核心服务层,封装Agent所有逻辑,提供API接口,接收前端请求,调用Agent处理,返回响应结果;
- 核心依赖:MySQL(存储业务数据,供JdbcTool查询)、Elasticsearch(存储知识库向量,供RAG检索)、Redis(缓存LLM响应,减少调用成本);
- 部署层:Docker打包所有服务,通过docker-compose一键启动,实现环境隔离、快速部署。
简单来说,用户在浏览器输入问题 → 前端发送请求 → 后端调用Agent处理 → Agent调用工具/LLM/RAG → 后端返回结果 → 前端展示结果,整个流程闭环,用户无需关注底层逻辑,只需专注于提问和查看结果。
二、后端改造:Spring Boot封装Agent(核心步骤)
后端改造的核心目标:将前两篇的Agent核心逻辑,封装成Spring Boot服务,提供API接口,同时优化配置、解决跨域,确保前端能正常调用。全程复用已有代码,只做“封装”和“适配”,不修改核心逻辑。
1. 项目结构调整(规范Spring Boot项目结构)
首先,将前两篇的代码调整为标准的Spring Boot项目结构,方便管理和维护,调整后结构如下(重点关注核心包):
agent-web-backend/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/enterprise/agent/
│ │ │ ├── config/ // 配置类(复用前两篇的Config)
│ │ │ │ ├── McpConfig.java
│ │ │ │ ├── LlmConfig.java
│ │ │ │ ├── RagConfig.java
│ │ │ │ └── SkillsConfig.java
│ │ │ ├── validator/ // 决策校验器(复用)
│ │ │ │ └── AgentDecisionValidator.java
│ │ │ ├── service/ // 新增:Agent服务封装
│ │ │ │ └── AgentService.java
│ │ │ ├── controller/ // 新增:API接口控制器
│ │ │ │ └── AgentController.java
│ │ │ └── AgentWebApplication.java // 启动类(优化)
│ │ └── resources/
│ │ └── application.yml // 配置文件(统一管理所有参数)
│ └── test/ // 测试类(可选)
└── pom.xml // 依赖管理(新增必要依赖)
2. 核心依赖调整(修改pom.xml)
在原有依赖基础上,新增Spring Boot Web依赖(提供API接口)、跨域依赖,确保后端能正常接收前端请求,依赖如下(直接替换原有pom.xml相关内容):
<?xmlversion="1.0" encoding="UTF-8"?>
<projectxmlns="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.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
<relativePath/>
</parent>
<groupId>com.enterprise.agent</groupId>
<artifactId>agent-web-backend</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- Spring Boot Web 依赖(提供API接口) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Redis缓存依赖(复用前两篇,缓存LLM响应) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- LangChain4j 核心依赖 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-core</artifactId>
<version>0.21.0</version>
</dependency>
<!-- 通义千问 LLM + Embedding 依赖 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-tongyi</artifactId>
<version>0.21.0</version>
</dependency>
<!-- GPT-4 依赖(备用LLM) -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-openai</artifactId>
<version>0.21.0</version>
</dependency>
<!-- Elasticsearch 向量存储依赖(RAG) -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-elasticsearch</artifactId>
<version>0.21.0</version>
</dependency>
<!-- MySQL + JDBC 依赖(JdbcTool) -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<!-- 跨域支持(解决前端调用后端的跨域问题) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- 测试依赖(可选) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Spring Boot 打包插件,生成可执行Jar包 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.18</version>
</plugin>
</plugins>
</build>
</project>
3. 配置文件优化(application.yml)
将前两篇的硬编码参数(API密钥、数据库连接、ES配置等)全部移到application.yml中,统一管理,方便不同环境(开发、测试、生产)切换,无需修改代码。配置如下(替换原有配置,根据自己的实际环境修改参数):
# 服务器配置
server:
port: 8080 # 后端服务端口,前端调用时需要对应
servlet:
context-path: /api # 接口前缀,所有API都以/api开头
# Spring 核心配置
spring:
# Redis配置(用于LLM缓存)
data:
redis:
host: localhost # 本地开发用localhost,服务器部署替换为服务器IP
port: 6379
database: 0
timeout: 10000ms
# MySQL配置(用于JdbcTool查询业务数据)
datasource:
url: jdbc:mysql://localhost:3306/enterprise_db?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
username: root # 自己的MySQL用户名
password: your-mysql-password # 自己的MySQL密码
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 10 # 连接池大小,优化性能
# 大模型配置(LLM)
llm:
tongyi:
api-key: your-tongyi-api-key # 自己的通义千问API密钥
model-name: qwen-plus # 通义千问主力模型
gpt4:
api-key: your-gpt4-api-key # 自己的GPT-4 API密钥(备用)
model-name: gpt-4
# RAG配置(知识库检索)
rag:
es:
host: localhost # 本地开发用localhost,服务器部署替换为服务器IP
port: 9200
similarity-threshold: 0.75 # 相似度标准,和前两篇一致
tongyi-api-key: your-tongyi-api-key # 通义千问Embedding API密钥
# 日志配置(可选,方便排查问题)
logging:
level:
com.enterprise.agent: info # 自己的包路径,日志级别info
dev.langchain4j: warn # LangChain4j日志级别warn,避免日志过多
root: info
file:
name: ./logs/agent-web.log # 日志文件存储路径
max-size: 100MB # 单个日志文件最大大小
max-history: 7 # 日志保留7天
4. 核心代码封装(新增Service和Controller)
这一步是后端改造的核心:将Agent的初始化、调用逻辑,封装到AgentService中,对外提供统一的调用方法;再通过AgentController提供RESTful API接口,供前端调用。全程复用前两篇的核心逻辑,不做修改。
(1)AgentService.java(服务层,封装Agent核心逻辑)
负责初始化Agent的所有依赖(LLM、RAG、Skills),提供execute方法供Controller调用,同时集成决策校验和缓存逻辑。
packagecom.enterprise.agent.service;
importcom.enterprise.agent.config.McpConfig;
importcom.enterprise.agent.config.RagConfig;
importcom.enterprise.agent.config.SkillsConfig;
importcom.enterprise.agent.validator.AgentDecisionValidator;
importdev.langchain4j.agent.DefaultAgent;
importdev.langchain4j.agent.tool.Tool;
importdev.langchain4j.model.chat.ChatLanguageModel;
importorg.springframework.beans.factory.annotation.Value;
importorg.springframework.data.redis.core.StringRedisTemplate;
importorg.springframework.stereotype.Service;
importjavax.annotation.PostConstruct;
importjava.util.List;
/**
* Agent服务类:封装Agent核心逻辑,对外提供调用方法
*/
@Service
publicclassAgentService {
// 从配置文件注入参数
@Value("${llm.tongyi.api-key}")
privateStringtongyiApiKey;
@Value("${llm.gpt4.api-key}")
privateStringgpt4ApiKey;
@Value("${rag.es.host}")
privateStringesHost;
@Value("${rag.es.port}")
privateintesPort;
@Value("${rag.similarity-threshold}")
privatedoublesimilarityThreshold;
@Value("${spring.datasource.url}")
privateStringdbUrl;
@Value("${spring.datasource.username}")
privateStringdbUser;
@Value("${spring.datasource.password}")
privateStringdbPwd;
// 注入RedisTemplate(用于LLM缓存)
privatefinalStringRedisTemplateredisTemplate;
// Agent实例(全局单例,初始化一次即可)
privateDefaultAgentagent;
// 构造器注入RedisTemplate
publicAgentService(StringRedisTemplateredisTemplate) {
this.redisTemplate=redisTemplate;
}
// 初始化方法:项目启动时自动初始化Agent,避免每次调用都重新初始化
@PostConstruct
publicvoidinitAgent() {
// 1. 初始化LLM(主力:通义千问,备用:GPT-4),并设置Redis缓存
com.enterprise.agent.config.LlmConfig.setRedisTemplate(redisTemplate);
ChatLanguageModelmainLlm=com.enterprise.agent.config.LlmConfig.getTongyiLlm();
// 2. 初始化RAG(知识库检索),加载知识库内容
RagConfigragConfig=newRagConfig(esHost, esPort, tongyiApiKey, similarityThreshold);
ragConfig.addKnowledgeToRag();
// 3. 初始化Skills(工具集)
TooljdbcTool=SkillsConfig.getJdbcTool(dbUrl, dbUser, dbPwd); // 优化:从配置文件获取数据库参数
ToolvectorStoreTool=SkillsConfig.getVectorStoreTool();
ToolchartTool=SkillsConfig.getChartTool();
List<Tool>tools=List.of(jdbcTool, vectorStoreTool, chartTool);
// 4. 初始化Agent(绑定MCP规则、LLM、工具)
this.agent=McpConfig.getAgentBuilder(mainLlm, tools).build();
System.out.println("Agent服务初始化完成,可正常调用");
}
// 对外提供的核心方法:接收用户问题,调用Agent处理,返回结果(带缓存)
publicStringexecuteAgent(StringuserQuestion) {
try {
// 1. 决策校验:筛选可用工具,避免乱调用
List<Tool>validTools=AgentDecisionValidator.filterValidTools(userQuestion, agent.getTools());
DefaultAgentvalidAgent=agent.toBuilder().tools(validTools).build();
// 2. 调用Agent处理问题,并用缓存优化(复用前两篇的缓存逻辑)
StringagentResult=validAgent.execute(userQuestion);
returncom.enterprise.agent.config.LlmConfig.callLlmWithCache(agentResult);
} catch (Exceptione) {
// 异常处理:返回友好提示,避免前端报错
return"Agent调用失败,请稍后再试!错误信息:"+e.getMessage().substring(0, 100);
}
}
}
(2)AgentController.java(控制器,提供API接口)
提供POST接口,接收前端发送的用户问题,调用AgentService的方法处理,返回响应结果,同时解决跨域问题,确保前端能正常调用。
packagecom.enterprise.agent.controller;
importcom.enterprise.agent.service.AgentService;
importorg.springframework.web.bind.annotation.*;
/**
* Agent接口控制器:提供RESTful API,供前端调用
*/
@RestController
@RequestMapping("/agent") // 接口路径:/api/agent(结合server.context-path)
@CrossOrigin(origins="*", maxAge=3600) // 解决跨域问题,开发环境允许所有来源
publicclassAgentController {
// 注入AgentService
privatefinalAgentServiceagentService;
// 构造器注入
publicAgentController(AgentServiceagentService) {
this.agentService=agentService;
}
/**
* 核心接口:接收用户问题,返回Agent响应结果
* 接口路径:POST /api/agent/chat
* 请求体:用户问题(字符串)
*/
@PostMapping("/chat")
publicStringchat(@RequestBodyStringuserQuestion) {
// 校验用户问题:非空校验
if (userQuestion==null||userQuestion.trim().isEmpty()) {
return"请输入有效的问题!";
}
// 调用AgentService处理问题,返回结果
returnagentService.executeAgent(userQuestion.trim());
}
}
(3)SkillsConfig.java 优化(适配配置文件参数)
修改SkillsConfig中的getJdbcTool方法,从配置文件获取数据库参数(之前是硬编码),适配Spring Boot的配置管理,修改后如下:
// 优化JdbcTool:从配置文件获取数据库参数,不再硬编码
publicstaticJdbcToolgetJdbcTool(StringdbUrl, StringdbUser, StringdbPwd) {
HikariConfigconfig=newHikariConfig();
config.setJdbcUrl(dbUrl); // 从配置文件注入
config.setUsername(dbUser); // 从配置文件注入
config.setPassword(dbPwd); // 从配置文件注入
DataSourcedataSource=newHikariDataSource(config);
// 优化后描述:说清楚什么时候用、什么时候不用
Stringdescription="只用来查企业MySQL数据库里的销售额、用户量等未知业务数据,输入合法的SQL语句,输出表格形式的查询结果;不用查数据的时候,绝对不能调用。";
returnJdbcTool.builder()
.dataSource(dataSource)
.description(description)
.build();
}
(4)启动类优化(AgentWebApplication.java)
修改启动类,确保Spring Boot能正常扫描到Service、Controller、Config等组件,无需额外修改核心逻辑。
packagecom.enterprise.agent;
importorg.springframework.boot.SpringApplication;
importorg.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Spring Boot启动类:启动整个后端服务
*/
@SpringBootApplication(scanBasePackages="com.enterprise.agent") // 扫描所有组件
publicclassAgentWebApplication {
publicstaticvoidmain(String[] args) {
SpringApplication.run(AgentWebApplication.class, args);
System.out.println("Agent后端服务启动成功,访问地址:http://localhost:8080/api");
}
}
5. 后端测试(确保接口能正常调用)
后端改造完成后,启动Spring Boot项目(运行AgentWebApplication.java),项目启动成功后,用Postman或浏览器测试API接口,确保能正常返回结果。
- 测试接口:POST http://localhost:8080/api/agent/chat
- 请求体:{“查询2026年2月销售额Top3的产品,生成柱状图,并解释为什么这三个产品销量高”}
- 预期结果:能正常返回Agent的响应(包含数据结论、图表HTML、原因解释),无报错。
测试通过后,说明后端接口已封装完成,前端可以正常调用了。
三、前端开发:Vue3搭建聊天界面(新手友好)前端开发目标:用Vue3 + Element Plus搭建一个简洁、易用的聊天界面,类似ChatGPT的交互逻辑,支持用户输入问题、发送请求、展示结果(文本+图表),新手也能快速上手,无需复杂的前端基础。
1. 项目初始化(创建Vue3项目)
首先,确保本地安装了Node.js(建议版本16+),然后执行以下命令,新建Vue3项目并安装依赖:
# 1. 新建Vue3项目(选择默认Vue3模板即可)
vue create agent-frontend
# 2. 进入项目目录
cd agent-frontend
# 3. 安装依赖(Element Plus:UI组件库;axios:发送HTTP请求)
npm install element-plus axios
2. 项目结构调整(简化,聚焦核心页面)
删除Vue3默认的多余文件,保留核心文件,调整后结构如下(重点关注src目录):
agent-frontend/
├── src/
│ ├── main.js // 入口文件,引入Element Plus和axios
│ ├── App.vue // 核心页面(聊天界面)
│ └── assets/ // 静态资源(可选,如图片)
├── package.json // 依赖管理
└── vue.config.js // Vue配置(可选,用于跨域代理)
3. 核心代码开发
(1)main.js(入口文件,引入依赖)
引入Element Plus(UI组件库)、axios(发送请求),全局注册,方便在页面中使用。
import { createApp } from'vue'
importElementPlusfrom'element-plus' // 引入Element Plus
import'element-plus/dist/index.css' // 引入Element Plus样式
importaxiosfrom'axios' // 引入axios
importAppfrom'./App.vue'
constapp=createApp(App)
// 全局注册Element Plus
app.use(ElementPlus)
// 配置axios(全局使用)
app.config.globalProperties.$axios=axios
// 配置后端接口基础路径(和后端server.port、context-path对应)
axios.defaults.baseURL='http://localhost:8080/api'
// 配置请求超时时间
axios.defaults.timeout=30000
app.mount('#app')
(2)App.vue(核心聊天界面)
搭建聊天界面,包含头部标题、聊天记录展示区、输入框、发送按钮,实现“发送问题→展示结果”的完整交互,支持自动渲染图表(Agent返回的HTML图表会直接渲染)。
<template>
<div class="agent-chat-container">
<!-- 头部标题 -->
<el-headerclass="chat-header">
<h1>企业智能数据分析Agent</h1>
<pclass="sub-title">支持查询数据、生成图表、解读原因,一键搞定数据分析</p>
</el-header>
<!-- 聊天记录展示区 -->
<el-mainclass="chat-content">
<!-- 空状态(无聊天记录时显示) -->
<divclass="empty-state"v-if="chatList.length === 0">
<el-iconsize="60"class="empty-icon"><Message/></el-icon>
<pclass="empty-tip">请输入问题开始对话</p>
<divclass="example-questions">
<el-tagtype="primary"@click="selectExample('查询2026年2月销售额Top3产品')">查询2026年2月销售额Top3产品</el-tag>
<el-tagtype="primary"@click="selectExample('生成2月销售额柱状图')">生成2月销售额柱状图</el-tag>
<el-tagtype="primary"@click="selectExample('解释产品A销量高的原因')">解释产品A销量高的原因</el-tag>
</div>
</el-icon>
</div>
<!-- 聊天记录列表 -->
<divclass="chat-list"v-else>
<div
v-for="(item, index) in chatList"
:key="index"
:class="item.role === 'user' ? 'user-chat' : 'agent-chat'"
>
<divclass="chat-avatar">
<el-avatar:icon="item.role === 'user' ? User : Bot"/>
</div>
<divclass="chat-message"v-html="item.content"></div>
</div>
</div>
</el-main>
<!-- 输入框区域 -->
<el-footerclass="chat-input-area">
<el-input
v-model="inputValue"
placeholder="请输入问题(例如:查询2026年2月销售额Top3产品)..."
class="input-box"
@keyup.enter="sendMessage"
clearable
>
<template#append>
<el-buttontype="primary"icon="Send"@click="sendMessage":disabled="!inputValue.trim()">
发送
</el-button>
</template>
</el-input>
<pclass="tip-text">提示:支持查询数据、生成图表、解读原因,输入后按回车或点击发送</p>
</el-footer>
</div>
</template>
<scriptsetup>
import { ref } from'vue'
import { ElMessage, ElTag, ElAvatar, ElIcon } from'element-plus'
import { Message, User, Bot, Send } from'@element-plus/icons-vue'
importaxiosfrom'axios'
// 聊天记录列表:存储用户和Agent的对话
constchatList=ref([])
// 输入框内容
constinputValue=ref('')
// 选择示例问题(点击标签自动填充输入框)
constselectExample= (question) => {
inputValue.value=question
}
// 发送消息
constsendMessage=async () => {
// 非空校验
constquestion=inputValue.value.trim()
if (!question) {
ElMessage.warning('请输入有效的问题!')
return
}
// 1. 添加用户消息到聊天列表
chatList.value.push({
role: 'user',
content: question
})
// 2. 添加加载中提示(提升用户体验)
constloadingIndex=chatList.value.length
chatList.value.push({
role: 'agent',
content: '<div class="loading">思考中...</div>'
})
// 3. 清空输入框
inputValue.value=''
try {
// 4. 调用后端API接口
constresponse=awaitaxios.post('/agent/chat', question, {
headers: {
'Content-Type': 'application/json'
}
})
// 5. 替换加载中提示为Agent的响应结果
chatList.value[loadingIndex] = {
role: 'agent',
content: response.data
}
// 6. 滚动到最新消息(自动定位到底部)
setTimeout(() => {
constchatContent=document.querySelector('.chat-content')
chatContent.scrollTop=chatContent.scrollHeight
}, 100)
} catch (error) {
// 异常处理:替换加载中提示为错误信息
chatList.value[loadingIndex] = {
role: 'agent',
content: '调用失败,请稍后再试!错误信息:'+ (error.message||'未知错误')
}
ElMessage.error('调用失败,请稍后再试!')
}
}
</script>
<stylescoped>
.agent-chat-container {
height: 100vh;
display: flex;
flex-direction: column;
background-color: #f8f9fa;
}
/* 头部样式 */
.chat-header {
background-color: #409eff;
color: #fff;
padding: 16px20px;
text-align: center;
box-shadow: 02px10pxrgba(0, 0, 0, 0.1);
}
.chat-headerh1 {
font-size: 24px;
margin: 008px0;
}
.chat-header.sub-title {
font-size: 14px;
opacity: 0.9;
margin: 0;
}
/* 聊天内容区域 */
.chat-content {
flex: 1;
overflow-y: auto;
padding: 20px;
background-color: #fff;
}
/* 空状态样式 */
.empty-state {
text-align: center;
padding: 50px0;
color: #666;
}
.empty-icon {
color: #409eff;
margin-bottom: 20px;
}
.empty-tip {
font-size: 16px;
margin-bottom: 30px;
}
.example-questions {
display: flex;
flex-wrap: wrap;
gap: 10px;
justify-content: center;
}
/* 聊天记录列表 */
.chat-list {
display: flex;
flex-direction: column;
gap: 20px;
}
/* 用户聊天样式 */
.user-chat {
display: flex;
justify-content: flex-end;
align-items: flex-start;
}
/* Agent聊天样式 */
.agent-chat {
display: flex;
justify-content: flex-start;
align-items: flex-start;
}
/* 头像样式 */
.chat-avatar {
margin: 010px;
}
/* 消息内容样式 */
.chat-message {
max-width: 70%;
padding: 12px16px;
border-radius: 16px;
line-height: 1.5;
font-size: 14px;
}
.user-chat.chat-message {
background-color: #e8f4fd;
color: #333;
border-bottom-right-radius: 4px;
}
.agent-chat.chat-message {
background-color: #f5f5f5;
color: #333;
border-bottom-left-radius: 4px;
}
/* 加载中样式 */
.loading {
display: flex;
align-items: center;
justify-content: center;
color: #666;
}
.loading::after {
content: '';
width: 16px;
height: 16px;
border: 2pxsolid#409eff;
border-top-color: transparent;
border-radius: 50%;
margin-left: 8px;
animation: spin1slinearinfinite;
}
@keyframesspin {
to { transform: rotate(360deg); }
}
/* 输入框区域样式 */
.chat-input-area {
padding: 16px20px;
background-color: #fff;
border-top: 1pxsolid#e6e6e6;
}
.input-box {
width: 100%;
margin-bottom: 8px;
}
.tip-text {
font-size: 12px;
color: #999;
margin: 0;
text-align: center;
}
</style>
4. 前端测试(确保界面能正常交互)
前端代码开发完成后,执行以下命令启动前端项目:
npm run dev
启动成功后,访问控制台输出的地址(默认是http://localhost:8081),即可看到聊天界面,测试以下功能:
- 点击示例问题标签,能自动填充输入框;
- 输入问题,点击发送或按回车,能正常发送请求;
- 能看到“思考中”的加载提示,加载完成后展示Agent的响应结果;
- Agent返回的图表HTML,能正常渲染显示。
测试通过后,说明前端界面开发完成,前后端能正常联动。
四、Docker一键部署(企业级落地关键)到这里,我们已经完成了前后端的开发,但如果要部署到服务器上供其他人使用,还需要解决“环境不一致”的问题——不同服务器的Java、Node.js、MySQL、ES版本可能不同,导致项目无法正常运行。
解决方案:用Docker打包所有服务(后端、前端、MySQL、ES、Redis),通过docker-compose一键启动,实现“环境无关、一键部署”,任何人拿到配置文件,执行一条命令就能启动整个应用。
1. 准备工作(服务器环境)
确保服务器已安装Docker和docker-compose,安装步骤参考官方文档(简单来说,执行以下命令即可,以CentOS为例):
# 安装Docker
yum install -y docker
systemctl start docker
systemctl enable docker
# 安装docker-compose
yum install -y docker-compose
2. 后端Dockerfile(打包后端服务)
在后端项目(agent-web-backend)根目录,创建Dockerfile文件,用于打包后端Spring Boot服务,生成Docker镜像。
# 构建阶段:使用Maven打包项目
FROM maven:3.8-openjdk-17 AS build
WORKDIR /app
# 复制pom.xml,下载依赖(缓存依赖,避免每次打包都重新下载)
COPY pom.xml .
RUN mvn dependency:go-offline
# 复制源码,打包项目
COPY src ./src
RUN mvn package -DskipTests # 跳过测试,加快打包速度
# 运行阶段:使用轻量的OpenJDK镜像,运行Jar包
FROM openjdk:17-jdk-slim
WORKDIR /app
# 从构建阶段复制打包好的Jar包
COPY --from=build /app/target/*.jar agent-backend.jar
# 暴露后端服务端口(和application.yml中的port一致)
EXPOSE 8080
# 启动命令:运行Jar包
ENTRYPOINT ["java", "-jar", "agent-backend.jar"]
3. 前端Dockerfile(打包前端服务)
在前端项目(agent-frontend)根目录,创建Dockerfile文件,用于打包前端Vue3项目,生成Docker镜像,用Nginx部署前端静态文件。
# 构建阶段:使用Node.js打包前端项目
FROM node:16 AS build
WORKDIR /app
# 复制package.json和package-lock.json,下载依赖
COPY package*.json ./
RUN npm install
# 复制源码,打包前端项目
COPY . .
RUN npm run build # 生成dist目录(静态文件)
# 运行阶段:使用Nginx部署静态文件
FROM nginx:alpine
# 复制打包好的静态文件到Nginx的默认目录
COPY --from=build /app/dist /usr/share/nginx/html
# 暴露Nginx端口(默认80端口,用户通过浏览器访问)
EXPOSE 80
# 启动Nginx(后台运行)
CMD ["nginx", "-g", "daemon off;"]
4. docker-compose.yml(一键启动所有服务)
在服务器上,创建一个新的目录(如agent-web-deployment),将后端项目、前端项目复制到该目录下,然后在该目录下创建docker-compose.yml文件,配置所有服务(后端、前端、MySQL、ES、Redis),实现一键启动。
version: '3.8' # docker-compose版本
services:
# 1. MySQL服务(存储业务数据)
mysql:
image: mysql:8.0 # 使用MySQL 8.0镜像
container_name: agent-mysql
ports:
- "3306:3306" # 端口映射:主机3306 → 容器3306
environment:
MYSQL_ROOT_PASSWORD: root # MySQL root密码(可修改)
MYSQL_DATABASE: enterprise_db # 数据库名称(和后端配置一致)
MYSQL_CHARSET: utf8mb4 # 字符集
volumes:
- ./mysql-data:/var/lib/mysql # 数据持久化:主机目录 → 容器目录,避免容器删除后数据丢失
restart: always # 容器异常退出后自动重启
networks:
- agent-network # 加入自定义网络,方便服务间通信
# 2. Elasticsearch服务(RAG知识库向量存储)
elasticsearch:
image: elasticsearch:8.10.0 # 使用ES 8.10.0镜像(和前两篇一致)
container_name: agent-es
ports:
- "9200:9200" # 端口映射:主机9200 → 容器9200
environment:
- discovery.type=single-node # 单节点模式(适合部署和测试)
- xpack.security.enabled=false # 关闭安全验证(简化部署,生产环境可开启)
- ES_JAVA_OPTS=-Xms512m -Xmx512m # 设置JVM内存(根据服务器配置调整)
volumes:
- ./es-data:/usr/share/elasticsearch/data # 数据持久化
restart: always
networks:
- agent-network
# 3. Redis服务(LLM缓存)
redis:
image: redis:alpine # 使用轻量的Redis镜像
container_name: agent-redis
ports:
- "6379:6379" # 端口映射:主机6379 → 容器6379
volumes:
- ./redis-data:/data # 数据持久化
restart: always
networks:
- agent-network
# 4. 后端服务(Spring Boot)
agent-backend:
build: ./agent-web-backend # 构建后端镜像(指定后端项目目录)
container_name: agent-backend
ports:
- "8080:8080" # 端口映射:主机8080 → 容器8080
depends_on:
- mysql # 依赖MySQL服务,MySQL启动后再启动后端
- elasticsearch # 依赖ES服务
- redis # 依赖Redis服务
environment:
# 环境变量:覆盖application.yml中的配置,适配Docker环境
- SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/enterprise_db?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
- SPRING_DATASOURCE_USERNAME=root
- SPRING_DATASOURCE_PASSWORD=root
- SPRING_REDIS_HOST=redis
- RAG_ES_HOST=elasticsearch
- RAG_ES_PORT=9200
- LLM_TONGYI_API_KEY=your-tongyi-api-key # 替换为自己的通义千问API密钥
- LLM_GPT4_API_KEY=your-gpt4-api-key # 替换为自己的GPT-4 API密钥
restart: always
networks:
- agent-network
# 5. 前端服务(Vue3)
agent-frontend:
build: ./agent-frontend # 构建前端镜像(指定前端项目目录)
container_name: agent-frontend
ports:
- "80:80" # 端口映射:主机80 → 容器80(浏览器默认访问80端口)
depends_on:
- agent-backend # 依赖后端服务,后端启动后再启动前端
restart: always
networks:
- agent-network
# 自定义网络:所有服务加入同一个网络,方便通信
networks:
agent-network:
driver: bridge
5. 一键启动命令(部署核心步骤)
在docker-compose.yml所在的目录,执行以下命令,一键启动所有服务:
# 一键构建并启动所有服务(后台运行)
docker-compose up -d
# 查看所有服务运行状态
docker-compose ps
# 查看后端服务日志(排查问题用)
docker-compose logs -f agent-backend
# 停止所有服务
docker-compose down
# 停止并删除所有服务和镜像(谨慎使用)
docker-compose down --rmi all
启动成功后,等待3-5分钟(让所有服务初始化完成),然后在浏览器访问服务器IP(无需加端口,默认80端口),即可看到Agent聊天界面,正常使用所有功能。
五、系列总结:从代码到产品的完整闭环至此,我们的LangChain4j实战系列已经完成了完整的闭环,从基础入门到企业级落地,一步步带你搞定Agent开发:
- 第一篇(入门篇):搭建基础的“企业智能数据分析Agent”,整合LLM、RAG、Skills、MCP,实现核心功能跑通;
- 第二篇(进阶篇):解决Agent开发的三大痛点(乱调用工具、RAG检索不准、LLM调用又慢又贵),优化稳定性、精准度和性价比;
- 第三篇(实战篇):将Agent封装成Web应用,用Spring Boot做后端、Vue3做前端,Docker一键部署,实现企业级落地。
现在,你拥有的不仅仅是一个“能跑通的代码示例”,而是一个「可部署、可维护、可扩展、可共用」的企业级智能数据分析Agent,无论是作为个人项目,还是企业内部工具,都可以直接投入使用。
六、后续扩展建议(可选,提升Agent实用性)
如果想让这个Agent更强大、更贴合企业实际需求,可以继续扩展以下功能(都是企业里常用的需求,新手可逐步尝试):
- 用户权限管理:新增用户登录、角色管理,支持多用户隔离,不同用户只能查看自己权限范围内的数据;
- 功能扩展:新增更多Skills,比如Excel导出(将查询结果导出为Excel)、邮件发送(将分析结果发送到指定邮箱)、定时任务(定时生成数据分析报告);
- 性能优化:给RAG检索结果加缓存,进一步减少Embedding调用成本;优化数据库查询,添加索引,提升查询速度;
- 监控告警:新增监控面板,实时查看Agent调用量、LLM响应时间、工具调用成功率;添加异常告警,工具调用失败、LLM切换备用方案时,自动发邮件/钉钉通知开发人员;
- 界面优化:美化前端界面,新增历史聊天记录保存、主题切换、图表下载等功能,提升用户体验。
学AI大模型的正确顺序,千万不要搞错了
🤔2026年AI风口已来!各行各业的AI渗透肉眼可见,超多公司要么转型做AI相关产品,要么高薪挖AI技术人才,机遇直接摆在眼前!
有往AI方向发展,或者本身有后端编程基础的朋友,直接冲AI大模型应用开发转岗超合适!
就算暂时不打算转岗,了解大模型、RAG、Prompt、Agent这些热门概念,能上手做简单项目,也绝对是求职加分王🔋

📝给大家整理了超全最新的AI大模型应用开发学习清单和资料,手把手帮你快速入门!👇👇
学习路线:
✅大模型基础认知—大模型核心原理、发展历程、主流模型(GPT、文心一言等)特点解析
✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑
✅开发基础能力—Python进阶、API接口调用、大模型开发框架(LangChain等)实操
✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用
✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代
✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经
以上6大模块,看似清晰好上手,实则每个部分都有扎实的核心内容需要吃透!
我把大模型的学习全流程已经整理📚好了!抓住AI时代风口,轻松解锁职业新可能,希望大家都能把握机遇,实现薪资/职业跃迁~
这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)