Spring AI与Ollama本地模型实战:从零构建AI代理助手
2026/8/21 3:12:39 网站建设 项目流程

最近在技术社区交流时,发现很多开发者对AI应用开发,特别是结合Spring AI这类框架构建智能代理(AI Agent)系统,表现出浓厚的兴趣。然而,从环境搭建、模型集成到最终部署,整个过程常会遇到各种“拦路虎”,比如依赖冲突、本地模型加载失败、代理逻辑不清晰等。本文将以一个实战项目为例,手把手带你从零构建一个具备本地模型能力的AI代理助手,涵盖Spring AI集成、Ollama本地模型调用、Agent核心逻辑设计以及前后端交互,并提供完整的可运行代码和避坑指南。无论你是想入门AI应用开发,还是希望将大模型能力集成到现有Java项目中,都能从中获得一套可直接复用的解决方案。

1. 背景与核心概念:为什么需要AI代理助手?

在深入代码之前,我们有必要厘清几个关键概念。AI技术的快速发展,尤其是大语言模型(LLM)的普及,催生了“AI代理”(AI Agent)这一重要范式。它不再是简单的问答接口,而是一个能够感知环境、进行规划、使用工具并执行任务以达成目标的自主或半自主系统。

1.1 什么是AI代理(AI Agent)?你可以将其理解为一个“数字员工”。给定一个目标(如“分析这份财报”),AI代理会自主拆解步骤:调用工具获取数据、使用模型进行分析、生成报告。其核心在于**工具使用(Tool Calling)任务规划(Planning)**能力。这与传统的仅能完成单轮对话的Chatbot有本质区别。

1.2 Spring AI 与 本地模型Spring AI是Spring官方社区提供的项目,旨在为基于JVM的应用程序(如Spring Boot)集成人工智能功能提供便捷的抽象和接口。它统一了对接不同AI提供商(如OpenAI、Azure OpenAI、Ollama等)的方式,让开发者能以熟悉的Spring风格(如AiClientPromptTemplate)来调用AI能力。

“本地模型”指的是在开发者自己的机器或服务器上部署运行的大模型,例如通过Ollama拉取和运行的Llama 3、Qwen等开源模型。使用本地模型的好处是数据隐私性强、无网络延迟、调用成本低(无需API费用),非常适合开发测试、内部工具或对数据安全要求高的场景。

1.3 本项目的目标我们将构建一个“AI代理助手”,它能够:

  1. 基础对话:回答用户的一般性问题。
  2. 工具调用:根据用户需求,动态选择并调用预定义的工具(如获取天气、计算器、查询数据库)。
  3. 本地运行:核心模型调用基于本地部署的Ollama,保障隐私和可控性。
  4. Web服务:提供简单的HTTP API,方便前端或其他系统集成。

接下来,我们将从环境准备开始,一步步实现这个系统。

2. 环境准备与版本说明

工欲善其事,必先利其器。以下是构建本项目所需的环境和工具清单。请务必注意版本兼容性,这是后续步骤能顺利运行的基础。

2.1 基础开发环境

  • 操作系统:macOS / Linux / Windows (WSL2推荐)。本文演示环境为 macOS。
  • Java:JDK 17 或 21。Spring AI 对版本有要求,推荐使用LTS版本。可通过java -version验证。
  • 构建工具:Maven 3.6+ 或 Gradle 7.x+。本文使用 Maven。
  • IDE:IntelliJ IDEA(推荐)、VS Code 或 Eclipse。

2.2 核心服务:OllamaOllama 是运行本地大模型的利器。我们需要先安装并启动它。

  1. 安装:访问 Ollama官网 下载对应系统的安装包,或使用命令行安装(Linux/macOS)。
  2. 拉取模型:安装后,打开终端,拉取一个适合你电脑配置的模型。例如,拉取轻量级的llama3.2:1b模型(约1B参数):
    ollama pull llama3.2:1b
    如果你的硬件性能较强,可以尝试llama3.2:3bqwen2.5:3b
  3. 运行与验证:运行该模型,并测试其基础对话能力。
    ollama run llama3.2:1b
    在出现的提示符后输入Hello,看是否能得到正常回复。按Ctrl+D退出交互模式。关键点:Ollama服务默认会在http://localhost:11434启动一个API服务。我们的Spring Boot应用将通过这个地址与模型通信。

2.3 初始化Spring Boot项目使用 Spring Initializr 快速生成项目骨架。

  • Project: Maven
  • Language: Java
  • Spring Boot: 3.2.x (确保与Spring AI版本兼容,当前推荐3.2.5+)
  • Dependencies:
    • Spring Web(构建Web API)
    • Spring AI(核心AI依赖)
    • Lombok(简化代码,可选但推荐)

生成并下载项目,用IDE打开。接下来,我们需要细化pom.xml中的依赖。

3. 核心依赖与配置详解

Spring AI是一个相对较新的项目,其依赖配置是第一步,也是容易出错的一步。

3.1 完善pom.xml依赖打开pom.xml文件,确保包含以下关键依赖。特别注意Spring AI的BOM(物料清单)管理,这是统一版本、避免冲突的关键。

<?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 https://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>3.2.5</version> <!-- 使用确定的Boot版本 --> <relativePath/> </parent> <groupId>com.example</groupId> <artifactId>ai-agent-assistant</artifactId> <version>0.0.1-SNAPSHOT</version> <name>ai-agent-assistant</name> <description>Demo project for Spring AI Agent with Local Model</description> <properties> <java.version>17</java.version> <!-- 定义Spring AI版本 --> <spring-ai.version>0.8.1</spring-ai.version> </properties> <dependencyManagement> <dependencies> <!-- 引入Spring AI BOM,管理所有Spring AI组件版本 --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-bom</artifactId> <version>${spring-ai.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- Spring Boot 基础依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring AI Ollama 集成 --> <!-- 这是连接本地Ollama服务的核心依赖 --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-ollama-spring-boot-starter</artifactId> </dependency> <!-- 开发工具 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </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> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>

配置解析

  1. dependencyManagement中引入spring-ai-bom:这是最佳实践,确保所有Spring AI相关依赖(如未来可能添加的redis、vector-store等)版本一致。
  2. spring-ai-ollama-spring-boot-starter:这个starter包封装了与Ollama API交互的客户端OllamaChatClient,开箱即用。

3.2 配置Ollama连接src/main/resources/application.yml(或application.properties) 中配置Ollama服务地址和默认使用的模型。

# application.yml spring: ai: ollama: # Ollama服务的基础URL,默认即本地11434端口 base-url: http://localhost:11434 # 默认使用的聊天模型,需与Ollama中拉取的模型名一致 chat: options: model: llama3.2:1b # 可调节模型创造性,0.0更确定,1.0更多样 temperature: 0.7 # 可选:设置应用端口 server: port: 8080

至此,基础环境与配置已完成。你可以启动Spring Boot应用 (AiAgentAssistantApplication),如果控制台没有报错,并且能看到类似OllamaChatClient initialized的日志,说明连接本地模型成功。

4. 构建AI代理助手:从基础对话到工具调用

现在进入核心开发阶段。我们将分步实现:1) 基础对话API;2) 自定义工具;3) 代理执行器。

4.1 实现基础对话控制器首先,创建一个简单的REST接口,验证Spring AI能否通过Ollama正常工作。

// 文件路径:src/main/java/com/example/aiagent/controller/ChatController.java package com.example.aiagent.controller; 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; import lombok.RequiredArgsConstructor; @RestController @RequiredArgsConstructor public class ChatController { // Spring AI 会自动注入一个配置好的ChatClient Bean private final ChatClient chatClient; @GetMapping("/chat") public String chat(@RequestParam(value = "message", defaultValue = "Hello") String message) { // 调用ChatClient进行单轮对话 String response = chatClient.prompt() .user(message) .call() .content(); return "AI Agent 回复: " + response; } }

启动应用,访问http://localhost:8080/chat?message=介绍一下你自己。如果一切正常,你将看到来自本地Llama模型的回复。这证明了基础通路是畅通的。

4.2 定义自定义工具(Tool)AI代理的“智能”很大程度上体现在它能调用哪些工具。我们来定义两个简单的工具:一个计算器和一个模拟的天气查询工具。

首先,创建一个工具类,其中的方法将被AI代理识别和调用。

// 文件路径:src/main/java/com/example/aiagent/tool/CalculatorTool.java package com.example.aiagent.tool; import org.springframework.stereotype.Component; import java.util.function.Function; @Component public class CalculatorTool implements Function<CalculatorTool.Request, CalculatorTool.Response> { // 定义工具的输入参数结构 public record Request(double a, double b, String operator) {} // 定义工具的输出结构 public record Response(double result) {} @Override public Response apply(Request request) { double result; switch (request.operator()) { case "+": result = request.a() + request.b(); break; case "-": result = request.a() - request.b(); break; case "*": result = request.a() * request.b(); break; case "/": if (request.b() == 0) throw new IllegalArgumentException("除数不能为零"); result = request.a() / request.b(); break; default: throw new IllegalArgumentException("不支持的运算符: " + request.operator()); } return new Response(result); } // 工具的描述,对于AI理解工具功能至关重要 public String getDescription() { return """ 一个简单的计算器工具,用于执行基础算术运算。 输入参数: - a: 第一个数字 (double) - b: 第二个数字 (double) - operator: 运算符,支持 '+', '-', '*', '/' 输出: 计算结果 (double) """; } }
// 文件路径:src/main/java/com/example/aiagent/tool/WeatherTool.java package com.example.aiagent.tool; import org.springframework.stereotype.Component; import java.util.function.Function; @Component public class WeatherTool implements Function<WeatherTool.Request, WeatherTool.Response> { public record Request(String city) {} public record Response(String city, String weather, int temperature) {} // 模拟天气数据 private static final java.util.Map<String, Response> WEATHER_DATA = java.util.Map.of( "北京", new Response("北京", "晴", 25), "上海", new Response("上海", "多云", 28), "深圳", new Response("深圳", "阵雨", 30) ); @Override public Response apply(Request request) { // 模拟查询,实际项目中可替换为调用真实API return WEATHER_DATA.getOrDefault(request.city(), new Response(request.city(), "未知", 0)); } public String getDescription() { return """ 查询指定城市的天气情况。 输入参数: - city: 城市名称,例如 '北京'、'上海' 输出: 包含城市、天气状况和温度(摄氏度)的对象。 """; } }

关键点:工具类必须是一个Spring Bean (@Component),并实现Function接口。getDescription()方法提供的清晰描述,是AI模型能否正确理解和使用该工具的关键。

4.3 配置并启用AI代理(Agent)Spring AI提供了强大的ChatClient来构建代理。我们需要将定义的工具注册进去,并配置代理的行为。

// 文件路径:src/main/java/com/example/aiagent/config/AgentConfig.java package com.example.aiagent.config; import com.example.aiagent.tool.CalculatorTool; import com.example.aiagent.tool.WeatherTool; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor; import org.springframework.ai.chat.memory.InMemoryChatMemory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.List; @Configuration public class AgentConfig { @Bean public ChatClient aiAgent(ChatClient.Builder chatClientBuilder, CalculatorTool calculatorTool, WeatherTool weatherTool) { // 1. 构建工具列表,并转换为Spring AI可识别的格式 var tools = List.of( ChatClient.AdvisorFunctionCall.advisorFunctionTool( "calculator", calculatorTool.getDescription(), calculatorTool ), ChatClient.AdvisorFunctionCall.advisorFunctionTool( "weather", weatherTool.getDescription(), weatherTool ) ); // 2. 构建并返回一个具备工具调用和记忆能力的ChatClient (即我们的Agent) return chatClientBuilder .defaultAdvisors( // 工具调用顾问:让Agent学会在需要时使用我们注册的工具 ChatClient.AdvisorFunctionCall.builder() .functionTools(tools) .build(), // 简单日志顾问:在控制台输出交互过程,便于调试 new SimpleLoggerAdvisor(), // 记忆顾问:为对话提供短期记忆,使Agent能联系上下文 new MessageChatMemoryAdvisor(new InMemoryChatMemory()) ) .build(); } }

配置解析

  • ChatClient.AdvisorFunctionCall:这是实现工具调用的核心。它将我们的工具函数包装成模型能理解的格式。
  • SimpleLoggerAdvisor:在控制台打印详细的请求、响应、工具调用信息,调试神器
  • MessageChatMemoryAdvisor:为对话提供简单的内存,使得Agent能记住当前会话中的历史消息,实现多轮对话。

4.4 创建高级代理控制器现在,创建一个新的控制器,使用我们配置好的、具备工具调用能力的ChatClient(即AI代理)。

// 文件路径:src/main/java/com/example/aiagent/controller/AgentController.java package com.example.aiagent.controller; import org.springframework.ai.chat.client.ChatClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import lombok.RequiredArgsConstructor; import lombok.Data; @RestController @RequiredArgsConstructor public class AgentController { // 注入我们配置的、具备工具调用能力的AI代理 private final ChatClient aiAgent; @PostMapping("/agent/chat") public AgentResponse chatWithAgent(@RequestBody UserRequest request) { // 调用代理处理用户请求 String agentResponse = aiAgent.prompt() .user(request.getMessage()) .call() .content(); return new AgentResponse(agentResponse); } // 内部请求/响应对象 @Data public static class UserRequest { private String message; } @Data public static class AgentResponse { private final String response; public AgentResponse(String response) { this.response = response; } } }

5. 运行、测试与效果验证

完成所有代码编写后,让我们启动项目并进行全面测试。

5.1 启动应用

  1. 确保Ollama服务正在运行(终端执行ollama serve或确保服务已启动)。
  2. 在IDE中运行AiAgentAssistantApplicationmain方法,或使用命令mvn spring-boot:run

观察控制台日志,应无错误,并看到Spring AI和Ollama相关的初始化信息。

5.2 测试基础对话使用curl、Postman或浏览器测试基础对话接口:

GET http://localhost:8080/chat?message=你好,世界!

预期返回一个由本地模型生成的问候回复。

5.3 测试AI代理工具调用这是核心测试。我们向代理发送需要计算或查询的请求。

# 使用curl测试代理接口 curl -X POST http://localhost:8080/agent/chat \ -H "Content-Type: application/json" \ -d '{"message": "请计算一下125加上37等于多少?"}'

观察控制台!你会看到SimpleLoggerAdvisor打印的详细日志,类似:

User: 请计算一下125加上37等于多少? AI (思考): 我需要使用计算器工具。我将调用`calculator`工具,参数为 a=125, b=37, operator=‘+’。 Function Call: calculator({“a”:125, “b”:37, “operator”:“+”}) Function Response: {“result”:162.0} AI (最终回复): 125加上37等于162。

最终API返回的响应内容将是:“125加上37等于162。”

再测试天气查询:

curl -X POST http://localhost:8080/agent/chat \ -H "Content-Type: application/json" \ -d '{"message": "今天北京的天气怎么样?"}'

代理会识别出需要调用weather工具,查询模拟数据后返回:“今天北京天气晴,气温25摄氏度。”

5.4 测试多轮对话(记忆能力)发送连续请求:

# 第一轮 curl ... -d '{"message": "我叫小明"}' # 预期回复可能是问候或确认。 # 第二轮 curl ... -d '{"message": "我的名字是什么?"}'

由于配置了MessageChatMemoryAdvisor,代理有很大概率能回答“你叫小明”。这证明了其基础的会话记忆能力。

6. 常见问题与排查思路(FAQ)

在实际搭建过程中,你可能会遇到以下问题。这里提供系统的排查思路。

问题现象可能原因排查步骤与解决方案
应用启动失败,报OllamaConnectionExceptionConnectException1. Ollama服务未启动。
2.application.yml中配置的base-url错误。
3. 防火墙/端口被占用。
1. 终端执行ollama serve并确保无报错。
2. 浏览器访问http://localhost:11434,应看到Ollama的欢迎页。
3. 检查spring.ai.ollama.base-url配置是否正确。
4. 使用lsof -i:11434查看端口监听情况。
调用/chat接口超时或返回空1. 本地模型首次响应慢或配置的模型不存在。
2. 模型参数(如temperature)导致生成异常。
3. 硬件资源(内存、CPU)不足。
1. 直接在终端用ollama run llama3.2:1b测试模型是否正常。
2. 检查application.yml中的model名称是否与Ollama中的完全一致。
3. 尝试调低temperature(如0.1) 或减少输入长度。
4. 监控系统资源,考虑换用更小的模型(如tinyllama)。
代理不调用工具,而是直接回答“我不会”或胡言乱语1. 工具描述 (getDescription) 不够清晰,模型无法理解。
2. 模型能力不足,无法进行有效的工具调用规划。
3. 请求的Prompt不够明确。
1.重点检查:优化工具描述,使用清晰、结构化的英文或中文描述输入输出格式。
2. 升级本地模型到能力更强的版本(如llama3.2:3b)。
3. 在用户请求中给予更明确的指令,如“请使用计算器工具计算...”。
4. 查看SimpleLoggerAdvisor日志,观察模型在收到请求后的“思考”过程。
工具调用参数错误,如类型不匹配1. 工具函数apply的输入输出类型与描述不符。
2. 模型生成的参数格式错误。
1. 确保Request记录(Record)的字段名和类型与描述一致。
2. 在AdvisorFunctionCall中,Spring AI会尝试进行类型转换,确保它是可序列化的POJO。
3. 在工具函数内部增加日志,打印接收到的参数。
多轮对话记忆失效1.InMemoryChatMemory是会话级,可能未正确关联。
2. 默认记忆长度有限。
1. 确保每次对话来自同一HTTP会话(对于简单测试,快速连续调用可能被视为同一会话)。
2. 考虑实现更稳定的记忆存储,如基于会话ID的Map或集成Spring AI Redis Chat Memory

7. 最佳实践与工程建议

将Demo提升到可工程化水平,需要注意以下几点:

7.1 工具设计与描述优化

  • 单一职责:每个工具应只做一件事。不要设计一个“万能工具”。
  • 描述即契约:工具描述是AI理解工具的“说明书”。务必清晰、准确、结构化。建议包含:工具名称、功能简述、输入参数(名称、类型、说明)、输出格式示例。
  • 错误处理:在工具的apply方法中做好健壮的错误处理(如参数校验、异常捕获),并返回友好的错误信息,让AI能将其反馈给用户。

7.2 代理(Agent)的提示词工程

  • 系统提示词(System Prompt):可以通过ChatClient.BuilderdefaultSystem()方法为代理设定角色和能力边界。例如:
    return chatClientBuilder .defaultSystem(""" 你是一个专业的AI助手,擅长使用计算器和查询天气。 你必须遵循以下规则: 1. 当用户涉及数学计算时,必须使用计算器工具。 2. 当用户询问天气时,必须使用天气查询工具。 3. 其他问题请友好、简洁地回答。 """) .defaultAdvisors(...) .build();
    一个清晰的系统提示能极大提升代理行为的可控性和准确性。

7.3 性能与稳定性

  • 模型选择:在生产环境,根据业务需求权衡模型大小、速度和精度。可考虑使用量化模型。
  • 超时与重试:在application.yml中配置Ollama客户端的超时和重试策略。
    spring: ai: ollama: chat: options: model: qwen2.5:7b # 客户端配置 client: connect-timeout: 30s read-timeout: 60s
  • 异步处理:对于耗时的AI生成或工具调用,考虑使用@Async或消息队列进行异步处理,避免阻塞HTTP请求。

7.4 可观测性与监控

  • 日志:充分利用SimpleLoggerAdvisor进行调试。生产环境可将其替换为自定义的Advisor,将交互日志写入ELK等系统。
  • 指标:集成Micrometer,监控AI调用延迟、工具调用次数、Token使用量(如果模型支持)等关键指标。

7.5 安全与权限

  • 输入校验:对所有用户输入进行严格的校验和清理,防止Prompt注入攻击。
  • 工具权限:不是所有工具都应被任意调用。可以根据用户角色或上下文,动态地注册或禁用某些工具。
  • 输出过滤:对AI生成的内容进行必要的安全过滤,避免产生不当言论。

通过以上步骤,你已经成功搭建了一个基于Spring AI和本地Ollama模型的AI代理助手原型。这个项目具备了核心的对话、工具调用和记忆能力。你可以在此基础上,继续扩展更多工具(如数据库查询、API调用、文件处理),优化代理的推理逻辑,或为其添加一个简单的前端界面,从而打造出一个真正实用的内部智能助手。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询