尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Spring AI与Ollama本地模型实战:从零构建AI代理助手

Spring AI与Ollama本地模型实战:从零构建AI代理助手 最近在技术社区交流时发现很多开发者对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风格如AiClient、PromptTemplate来调用AI能力。“本地模型”指的是在开发者自己的机器或服务器上部署运行的大模型例如通过Ollama拉取和运行的Llama 3、Qwen等开源模型。使用本地模型的好处是数据隐私性强、无网络延迟、调用成本低无需API费用非常适合开发测试、内部工具或对数据安全要求高的场景。1.3 本项目的目标我们将构建一个“AI代理助手”它能够基础对话回答用户的一般性问题。工具调用根据用户需求动态选择并调用预定义的工具如获取天气、计算器、查询数据库。本地运行核心模型调用基于本地部署的Ollama保障隐私和可控性。Web服务提供简单的HTTP API方便前端或其他系统集成。接下来我们将从环境准备开始一步步实现这个系统。2. 环境准备与版本说明工欲善其事必先利其器。以下是构建本项目所需的环境和工具清单。请务必注意版本兼容性这是后续步骤能顺利运行的基础。2.1 基础开发环境操作系统macOS / Linux / Windows (WSL2推荐)。本文演示环境为 macOS。JavaJDK 17 或 21。Spring AI 对版本有要求推荐使用LTS版本。可通过java -version验证。构建工具Maven 3.6 或 Gradle 7.x。本文使用 Maven。IDEIntelliJ IDEA推荐、VS Code 或 Eclipse。2.2 核心服务OllamaOllama 是运行本地大模型的利器。我们需要先安装并启动它。安装访问 Ollama官网 下载对应系统的安装包或使用命令行安装Linux/macOS。拉取模型安装后打开终端拉取一个适合你电脑配置的模型。例如拉取轻量级的llama3.2:1b模型约1B参数ollama pull llama3.2:1b如果你的硬件性能较强可以尝试llama3.2:3b或qwen2.5:3b。运行与验证运行该模型并测试其基础对话能力。ollama run llama3.2:1b在出现的提示符后输入Hello看是否能得到正常回复。按CtrlD退出交互模式。关键点Ollama服务默认会在http://localhost:11434启动一个API服务。我们的Spring Boot应用将通过这个地址与模型通信。2.3 初始化Spring Boot项目使用 Spring Initializr 快速生成项目骨架。Project: MavenLanguage: JavaSpring 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 version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.2.5/version !-- 使用确定的Boot版本 -- relativePath/ /parent groupIdcom.example/groupId artifactIdai-agent-assistant/artifactId version0.0.1-SNAPSHOT/version nameai-agent-assistant/name descriptionDemo project for Spring AI Agent with Local Model/description properties java.version17/java.version !-- 定义Spring AI版本 -- spring-ai.version0.8.1/spring-ai.version /properties dependencyManagement dependencies !-- 引入Spring AI BOM管理所有Spring AI组件版本 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version${spring-ai.version}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement dependencies !-- Spring Boot 基础依赖 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Spring AI Ollama 集成 -- !-- 这是连接本地Ollama服务的核心依赖 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-ollama-spring-boot-starter/artifactId /dependency !-- 开发工具 -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency /dependencies build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId configuration excludes exclude groupIdorg.projectlombok/groupId artifactIdlombok/artifactId /exclude /excludes /configuration /plugin /plugins /build /project配置解析dependencyManagement中引入spring-ai-bom这是最佳实践确保所有Spring AI相关依赖如未来可能添加的redis、vector-store等版本一致。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) 基础对话API2) 自定义工具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 定义自定义工具ToolAI代理的“智能”很大程度上体现在它能调用哪些工具。我们来定义两个简单的工具一个计算器和一个模拟的天气查询工具。首先创建一个工具类其中的方法将被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 FunctionCalculatorTool.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 FunctionWeatherTool.Request, WeatherTool.Response { public record Request(String city) {} public record Response(String city, String weather, int temperature) {} // 模拟天气数据 private static final java.util.MapString, 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代理AgentSpring 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 启动应用确保Ollama服务正在运行终端执行ollama serve或确保服务已启动。在IDE中运行AiAgentAssistantApplication的main方法或使用命令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工具参数为 a125, b37, 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在实际搭建过程中你可能会遇到以下问题。这里提供系统的排查思路。问题现象可能原因排查步骤与解决方案应用启动失败报OllamaConnectionException或ConnectException1. 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.Builder的defaultSystem()方法为代理设定角色和能力边界。例如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调用、文件处理优化代理的推理逻辑或为其添加一个简单的前端界面从而打造出一个真正实用的内部智能助手。
返回列表