在实际旅行规划场景中用户常常面临一个割裂的体验行前需要花费大量时间在多个App和网页间切换手动收集景点信息、规划路线、预订票务抵达目的地后又要依赖离线地图、纸质攻略或临时搜索来应对实时变化规划好的行程往往难以灵活执行。这种割裂感正是AI技术可以弥合的关键点。Passage AI作为一个AI旅行规划器其核心价值在于提供了一个从“规划”到“执行”的无缝衔接体验。它不仅仅是一个行前生成行程单的工具更是一个在你落地后能实时响应、提供现场指引的“活向导”。本文将深入探讨如何从零开始构建一个具备类似理念的AI旅行助手原型涵盖从需求分析、技术选型结合Spring AI与本地模型、核心功能实现到将其部署为可交互的智能体AI Agent的完整工程实践。无论你是对AI应用开发感兴趣的开发者还是希望将大模型能力集成到具体业务场景中的产品经理都能通过本文理解一个完整AI旅行Agent的构建链路与关键技术细节。1. 理解AI旅行规划器的核心架构与工作流程一个真正的AI旅行规划器其智能体现在两个阶段静态规划与动态引导。静态规划阶段系统需要理解用户模糊的、非结构化的需求如“我想去一个温暖的海边城市度过一个放松的周末”并输出结构化的、包含时间、地点、活动的详细行程。动态引导阶段系统需要基于用户的实时位置、时间、突发状况如天气变化、景点关闭、交通延误以及可能的兴趣转移对原有规划进行动态调整并提供即时指引。1.1 核心组件拆解要实现上述流程系统至少需要以下几个核心组件用户意图理解与交互模块负责与用户进行多轮对话澄清需求收集约束条件如预算、时间、兴趣偏好、饮食禁忌等。这通常由一个对话管理模块驱动。知识检索与信息整合模块系统需要访问一个庞大的旅行知识库包括景点信息、开放时间、门票价格、用户评价、交通方式、餐厅推荐等。这些数据可能来自内部数据库、第三方API如地图、票务、点评或网络爬虫。行程规划与推理引擎这是AI的核心。它需要综合用户意图和检索到的知识运用空间、时间逻辑进行推理生成一个合理、高效、个性化的行程计划。例如它需要知道两个景点间的距离和交通时间避免将相距甚远的景点安排在相邻时段。实时上下文感知与决策模块当用户进入“现场模式”后系统需要接入实时数据源如GPS定位、实时交通、天气API、景点人流信息等。基于这些实时上下文它能判断当前行程是否可行并提供替代方案或下一步行动建议。多模态输出与交互接口规划结果和现场指引不应只是文本。理想情况下应结合地图可视化显示路线、语音播报边走边听、图片/AR实景导航等多种形式。对于原型我们可以从图文混合的聊天界面开始。1.2 技术栈选型为什么选择Spring AI与本地模型构建此类应用技术选型至关重要。结合当前流行的AI工程实践我们选择以下技术栈后端框架Spring Boot Spring AISpring Boot提供了成熟、高效的Web服务开发能力。Spring AI项目旨在简化Java应用中集成AI功能的过程它提供了对多种大模型OpenAI, Azure OpenAI, Ollama, 本地模型等的统一抽象接口极大地降低了集成复杂度。AI模型本地部署的大语言模型LLM考虑到旅行数据可能涉及用户隐私如位置、行程以及需要7x24小时稳定、低延迟的响应使用云端API可能存在成本、延迟和数据出境风险。因此选择在本地或私有云部署一个开源模型是更稳妥的方案。例如可以选择Qwen2.5-7B-Instruct、Llama 3.2或DeepSeek-Coder等尺寸适中、性能优秀的模型通过Ollama或vLLM等工具进行部署和管理。向量数据库用于知识检索为了高效地从海量旅行知识中检索相关信息我们需要将非结构化的文本数据景点描述、游记转换为向量Embeddings并存入向量数据库如ChromaDB、Milvus、PgVector。当用户提出需求时先将问题转换为向量然后从向量数据库中搜索最相关的知识片段作为上下文提供给LLM从而生成更准确、信息量更丰富的回答。这就是检索增强生成RAG的核心思想。Agent框架要让AI具备动态规划和实时决策能力需要将其构建成一个智能体AI Agent。Agent可以根据目标、使用工具如搜索API、计算器、地图服务、感知环境用户输入、实时数据并执行行动。LangChain4j或Spring AI自身的AiClient和Function Calling功能可以用于构建简单的Agent。注意生产环境选择本地模型时必须充分考虑服务器的GPU资源显存大小。一个7B参数的模型通常需要至少8GB显存才能流畅运行量化版本如4bit量化可以降低资源需求但可能会轻微影响效果。2. 环境准备与项目初始化在开始编码前我们需要搭建好基础的开发环境和项目结构。2.1 开发环境要求确保你的开发机器满足以下条件组件要求说明JDK17 或 21Spring Boot 3.x 的推荐版本。Maven3.6 或Gradle项目管理工具。本文使用Maven示例。Docker Docker Compose最新稳定版用于快速启动Ollama、向量数据库等依赖服务。IDEIntelliJ IDEA, VS Code等推荐使用支持Spring Boot和Lombok的IDE。GPU可选但推荐NVIDIA GPU, 显存 8GB用于本地运行7B及以上参数的LLM显著提升推理速度。CPU也可运行但速度慢。2.2 初始化Spring Boot项目使用 Spring Initializr 生成项目骨架选择以下依赖Spring Web构建RESTful API。Spring AI核心AI集成依赖。Lombok简化Java Bean代码。Spring Boot DevTools开发热部署。生成后pom.xml中应包含类似以下依赖Spring AI的版本请使用最新稳定版dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-ollama-spring-boot-starter/artifactId !-- 请检查Spring AI官方文档获取最新版本 -- /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency2.3 启动基础设施服务Docker Compose创建一个docker-compose.yml文件用于一键启动Ollama运行本地模型和ChromaDB向量数据库。version: 3.8 services: ollama: image: ollama/ollama:latest container_name: passage-ai-ollama ports: - 11434:11434 volumes: - ollama_data:/root/.ollama # 注意首次启动后需要进入容器拉取模型见下文步骤。 restart: unless-stopped chromadb: image: chromadb/chroma:latest container_name: passage-ai-chromadb environment: - IS_PERSISTENTTRUE - PERSIST_DIRECTORY/chroma/data ports: - 8000:8000 volumes: - chroma_data:/chroma/data restart: unless-stopped volumes: ollama_data: chroma_data:在终端中进入该文件所在目录运行docker-compose up -d2.4 下载并运行本地大语言模型Ollama服务启动后我们需要为其下载一个模型。以Qwen2.5-7B-Instruct模型为例进入Ollama容器docker exec -it passage-ai-ollama bash在容器内拉取模型ollama pull qwen2.5:7b-instruct这个过程会下载约4.5GB的模型文件耗时取决于网络。你也可以选择更小的模型如llama3.2:1b或qwen2.5:3b用于快速原型验证。退出容器。现在模型已经准备就绪可以通过http://localhost:11434访问Ollama的API。3. 构建核心功能从静态规划到动态引导我们的目标是构建一个简单的REST API它接收用户的旅行需求返回规划好的行程并能接受后续的实时交互。我们将分步实现。3.1 配置Spring AI连接本地Ollama首先在application.yml中配置Spring AI连接到我们本地运行的Ollama服务。spring: ai: ollama: base-url: http://localhost:11434 # Ollama服务地址 chat: options: model: qwen2.5:7b-instruct # 使用的模型名称 temperature: 0.7 # 创造性0-1之间越高回答越随机 max-tokens: 2000 # 最大输出token数3.2 实现基础的对话服务创建一个TripPlanningService它利用Spring AI的AiClient进行最简单的对话。import org.springframework.ai.chat.client.ChatClient; import org.springframework.stereotype.Service; import lombok.RequiredArgsConstructor; Service RequiredArgsConstructor public class TripPlanningService { private final ChatClient chatClient; public String planTrip(String userRequest) { // 构建一个简单的提示词Prompt String prompt 你是一个专业的旅行规划助手。请根据用户的需求为其规划一份详细的旅行行程。 用户需求%s 请以清晰的格式如按天、按时间段回复包含景点、活动、餐饮建议和交通提示。 .formatted(userRequest); // 调用AI模型生成回复 return chatClient.prompt() .user(prompt) .call() .content(); } }创建一个简单的控制器TripPlanningController来暴露API。import org.springframework.web.bind.annotation.*; import lombok.RequiredArgsConstructor; RestController RequestMapping(/api/trip) RequiredArgsConstructor public class TripPlanningController { private final TripPlanningService tripPlanningService; PostMapping(/plan) public String plan(RequestBody PlanRequest request) { return tripPlanningService.planTrip(request.getUserRequest()); } // 简单的请求体 public record PlanRequest(String userRequest) {} }此时启动应用并调用POST /api/trip/plan传入{userRequest: 我想在北京玩3天喜欢历史和文化}你应该能得到一段AI生成的行程文本。但这只是一个“聊天机器人”缺乏真正的知识和实时能力。3.3 集成知识库RAG实现智能规划要让规划更准确我们需要为其提供知识。假设我们有一个关于北京旅行的知识文档beijing_travel_guide.txt。步骤如下加载与分割文档使用Spring AI的文档加载器和文本分割器。生成向量并存储将分割后的文本块转换为向量存入ChromaDB。检索增强生成用户提问时先检索相关文本块再连同问题和文本块一起发给LLM。首先添加ChromaDB的依赖和配置。# application.yml 追加 spring: ai: vectorstore: chroma: host: localhost port: 8000然后创建文档加载和向量存储的服务。import org.springframework.ai.document.Document; import org.springframework.ai.reader.TextReader; import org.springframework.ai.transformer.splitter.TokenTextSplitter; import org.springframework.ai.vectorstore.VectorStore; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.stereotype.Service; import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import java.util.List; Service RequiredArgsConstructor public class KnowledgeBaseService { private final ResourceLoader resourceLoader; private final VectorStore vectorStore; PostConstruct // 应用启动时加载知识库 public void initKnowledgeBase() { Resource resource resourceLoader.getResource(classpath:beijing_travel_guide.txt); TextReader textReader new TextReader(resource); ListDocument documents textReader.get(); TokenTextSplitter splitter new TokenTextSplitter(); // 按Token分割文本 ListDocument splitDocuments splitter.apply(documents); vectorStore.add(splitDocuments); // 存入向量数据库 System.out.println(知识库加载完成文档块数量 splitDocuments.size()); } }最后改造TripPlanningService在规划时先进行检索。public String planTripWithKnowledge(String userRequest) { // 1. 检索相关文档 ListDocument similarDocuments vectorStore.similaritySearch(userRequest); // 2. 构建包含上下文的提示词 String context similarDocuments.stream() .map(Document::getContent) .collect(Collectors.joining(\n\n)); String prompt 你是一个专业的旅行规划助手请根据以下提供的旅行指南信息和用户需求规划行程。 【旅行指南信息】 %s 【用户需求】 %s 请基于以上信息规划一份详细、可行、个性化的行程。如果信息不足可以基于常识补充但请注明。 回复格式按天规划每天包含上午、下午、晚上每个时段注明地点、活动、交通和餐饮建议。 .formatted(context, userRequest); // 3. 调用AI生成回复 return chatClient.prompt() .user(prompt) .call() .content(); }现在AI生成的行程将基于我们提供的《北京旅行指南》推荐的地点、开放时间等信息会更准确。3.4 实现动态引导与实时交互Agent雏形“动态引导”意味着系统能记住之前的对话行程并根据用户的新输入如“我现在在故宫接下来去哪”或实时数据做出决策。这需要引入“对话记忆”和“工具调用”能力。对话记忆Spring AI的ChatClient可以关联一个ChatMemory对象自动维护对话历史。我们需要在服务层或控制器层管理不同用户的会话。import org.springframework.ai.chat.memory.InMemoryChatMemory; import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; public String chatWithMemory(String sessionId, String userMessage) { // 为每个用户会话创建或获取一个ChatMemory ChatMemory chatMemory sessionMemories.computeIfAbsent(sessionId, id - new InMemoryChatMemory()); return chatClient.prompt() .user(userMessage) .advisors(new MessageChatMemoryAdvisor(chatMemory)) // 关联记忆 .call() .content(); }工具调用Function Calling让AI能调用外部工具获取实时信息。例如定义一个“获取实时天气”的工具。import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.model.function.FunctionCallbackWrapper; // 定义一个工具函数 Bean public FunctionCallbackWrapper weatherFunction() { return FunctionCallbackWrapper.builder(new WeatherService()) // WeatherService是一个包含getWeather方法的Bean .name(getCurrentWeather) .description(获取指定城市的当前天气情况) .build(); } // 在ChatClient构建时注册工具 private final ChatClient chatClientWithTools; public String getDynamicSuggestion(String location) { String prompt “用户当前在” location “请根据实时天气给出接下来的活动建议。如果需要可以调用工具获取天气。”; ChatResponse response chatClientWithTools.prompt() .user(prompt) .call() .chatResponse(); // AI可能会在响应中要求调用工具Spring AI会自动处理调用并将结果返回给AI最终生成包含工具调用结果的完整回复。 return response.getResult().getOutput().getContent(); }WeatherService示例Component public class WeatherService { public String getCurrentWeather(JsonProperty(city) String city) { // 这里调用真实的天气API如和风天气、OpenWeatherMap等 // 返回模拟数据 return “城市” city “天气晴温度25°C适宜户外活动。”; } }通过结合记忆和工具AI旅行助手就具备了初步的Agent能力它能记住之前的对话已规划的行程能根据用户当前位置和实时天气动态地建议“接下来是去户外景点还是室内博物馆”。4. 系统集成、测试与常见问题排查将各个模块组合起来形成一个可测试的原型系统。4.1 定义完整的API接口设计两个核心接口POST /api/trip/plan初始规划创建新会话。POST /api/trip/chat实时交互需携带sessionId。4.2 编写集成测试使用SpringBootTest编写测试验证从用户输入到AI回复的完整流程特别是工具调用和记忆功能是否正常工作。SpringBootTest AutoConfigureMockMvc class TripPlanningIntegrationTest { Autowired private MockMvc mockMvc; Test void testPlanAndChatFlow() throws Exception { // 1. 测试初始规划 String planRequest “{userRequest: 上海两天一夜游}”; MvcResult planResult mockMvc.perform(post(“/api/trip/plan”) .contentType(MediaType.APPLICATION_JSON) .content(planRequest)) .andExpect(status().isOk()) .andReturn(); String sessionId // 从响应头或Cookie中提取会话ID根据你的实现; // 2. 测试带记忆和工具的实时聊天 String chatRequest “{sessionId: ” sessionId “, message: 现在外滩下雨吗}”; mockMvc.perform(post(“/api/trip/chat”) .contentType(MediaType.APPLICATION_JSON) .content(chatRequest)) .andExpect(status().isOk()) .andExpect(jsonPath(“$.content”).value(containsString(“天气”))); // 期望回复中包含天气信息 } }4.3 常见问题与排查路径在开发和运行过程中你可能会遇到以下典型问题问题现象可能原因检查方式处理建议调用/plan接口超时或无响应1. Ollama服务未启动或模型未加载。2. 模型推理速度过慢CPU模式。3. 网络问题。1.docker ps检查容器状态。2.curl http://localhost:11434/api/tags查看模型列表。3. 查看应用日志和Ollama容器日志。1. 确保docker-compose up -d已执行且模型已拉取。2. 考虑使用更小的模型或启用GPU加速。3. 增加Spring AI的请求超时配置。AI回复内容与知识库无关RAG失效1. 向量数据库未存入数据或数据不相关。2. 检索返回的文档数量k值设置不当。3. Embedding模型与LLM不匹配。1. 检查KnowledgeBaseService的init方法日志确认文档已加载。2. 调试vectorStore.similaritySearch的返回结果。3. 检查使用的Embedding模型Spring AI默认可能使用Ollama的嵌入模型。1. 确保知识文档路径正确且内容相关。2. 调整similaritySearch的topK参数。3. 考虑使用专门的嵌入模型如nomic-embed-text并统一配置。工具调用Function Calling不生效1. 模型不支持或未正确识别工具定义。2. 函数描述description不够清晰。3. 工具未正确注册到ChatClient。1. 确认模型支持函数调用如Qwen2.5-Instruct系列支持。2. 查看AI的原始响应看是否发出了工具调用请求。3. 检查FunctionCallbackWrapper的Bean是否被创建。1. 使用明确支持函数调用的模型。2. 完善函数名称和描述使其易于被AI理解。3. 确保构建ChatClient时通过.functions(...)方法注册了工具。对话记忆混乱或丢失1.ChatMemory作用域设置错误如用了单例。2. 会话IDsessionId未正确传递或管理。1. 检查是否为每个用户/会话创建了独立的ChatMemory实例。2. 在客户端调用时打印或日志记录 sessionId。1. 使用ConcurrentHashMap等结构以 sessionId 为键存储ChatMemory。2. 对于Web应用可将 sessionId 存储在HttpSession或通过Token管理。生产环境性能差1. 每次请求都重新检索向量数据库未缓存。2. LLM推理无并发控制拖慢整体响应。3. 知识库文档过大Embedding耗时。1. 使用APM工具如SkyWalking分析链路耗时。2. 监控服务器CPU/GPU和内存使用率。1. 对频繁查询的相似问题结果进行缓存如使用Redis。2. 对LLM调用实现限流和队列机制。3. 对知识库进行预处理和优化如提取关键信息生成摘要。5. 从原型到产品最佳实践与扩展方向构建一个可用的原型只是第一步。要将其发展为可靠的产品还需要考虑以下方面。5.1 工程化最佳实践配置外部化将模型参数temperature, maxTokens、API密钥、向量数据库连接信息等全部移至application-{profile}.yml或配置中心。切勿硬编码。结构化输出让AI返回JSON而非纯文本便于前端解析和展示。Spring AI支持通过指定responseFormat或使用StructuredOutput注解来实现。StructuredOutput public record TripPlan(ListDayPlan days) {} public record DayPlan(String date, ListTimeSlot schedule) {} // 在Prompt中明确要求AI返回指定格式的JSON异步处理行程规划可能是耗时操作。对于复杂请求应改为异步接口立即返回一个任务ID客户端通过轮询或WebSocket获取结果。监控与日志记录关键指标LLM调用耗时、Token使用量、工具调用成功率、用户会话数等。使用MDCMapped Diagnostic Context将请求ID、用户ID贯穿整个调用链路便于问题追踪。错误处理与降级LLM服务可能不稳定。设计降级策略例如当本地模型不可用时可暂时切换到备用云端API需注意成本和数据合规当RAG检索无结果时提供基于通用知识的回复并明确告知用户信息有限。5.2 功能扩展方向多模态输入/输出输入支持用户上传旅行照片AI识别地点并推荐周边活动。输出集成地图SDK如高德、百度地图API将行程自动生成可视化地图路线。结合TTSText-to-Speech服务提供语音导览。深度Agent化引入规划Planning、反思Reflection等更复杂的Agent机制。让AI不仅能调用工具还能评估当前状态与目标的差距自主拆解复杂任务如“规划一个欧洲半月游”并递归执行子任务。个性化与持续学习建立用户画像记录用户的历史行程、评分和反馈。在规划时优先推荐相似用户喜欢的项目实现越用越懂你的个性化推荐。供应商集成打通票务如门票、车票、住宿酒店API、餐饮预订平台系统。在规划行程时直接提供预订链接或比价信息并在行程中集成订单状态提醒。离线能力对于国际旅行可能面临的网络问题可以考虑将核心模型、知识库和地图数据打包在手机端实现轻量级离线推理和导航。5.3 安全与合规考量数据隐私行程数据是敏感的个人信息。必须对用户数据进行加密存储和传输明确告知用户数据使用范围并提供数据导出和删除功能。内容安全对AI生成的内容进行审核过滤避免产生不当、危险或虚假的旅行建议如推荐未开发的危险区域。可以接入内容安全API或在Prompt中设置严格的约束。模型合规确保所使用的开源模型符合商业使用许可。如果使用微调需注意训练数据的版权问题。构建一个像Passage AI这样“规划即服务落地即向导”的应用技术核心在于将大语言模型的推理能力、外部知识库的准确性、实时数据的时效性以及智能体的决策能力有机结合起来。从本文的原型出发你可以沿着工程化、产品化、智能化的方向持续迭代最终打造出一个真正理解用户、伴随旅程的智能旅行伙伴。在实践过程中始终牢记以解决用户真实痛点为出发点平衡技术的先进性与系统的稳定性。