# demo-langchain4j-rag **Repository Path**: devxz/demo-langchain4j-rag ## Basic Information - **Project Name**: demo-langchain4j-rag - **Description**: 基于 Spring Boot 3.5.0 + LangChain4j 1.11.0 构建的智能票务助手,集成阿里云通义千问大模型、MCP (Model Context Protocol) 协议和 RAG (检索增强生成) 技术,提供 12306 实时票务查询、自然的对话式预订服务以及准确的票务知识问答。 - **Primary Language**: Java - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 4 - **Forks**: 2 - **Created**: 2026-04-17 - **Last Updated**: 2026-05-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # AI 智能票务助手 - 基于 LangChain4j + RAG + MCP(Demo) 基于 **Spring Boot 3.5.0** + **LangChain4j 1.11.0** 构建的智能票务助手,集成阿里云通义千问大模型、MCP (Model Context Protocol) 协议和 RAG (检索增强生成) 技术,提供 12306 实时票务查询、自然的对话式预订服务以及准确的票务知识问答。 > **注意**: 本项目为 Demo 版本,仅供学习测试使用 ## ✨ 核心特性 - 🤖 **AI 智能对话** - 基于 LangChain4j Agent 框架,支持自然语言交互 - 💬 **流式响应** - 实时打字机效果,更快的响应体验 - 🧠 **对话记忆** - 基于 ChatMemoryProvider 的多轮对话上下文记忆 - 👥 **用户隔离** - 基于 SessionId 的多用户会话隔离 - 🚄 **12306 实时查询** - 通过 MCP 协议连接 12306 官方数据,实时查询车次、余票、票价 - 🎫 **弹窗订票** - 点击座位即可打开订票弹窗,自动填充车次、票价、日期等信息 - 📊 **完整订单** - 支持创建带完整行程信息的订单(车次、座位、票价、日期等) - 🔍 **订单查询** - AI 对话或 REST API 查询订单,展示完整行程信息 - 📚 **RAG 知识库问答** - 基于检索增强生成技术,准确回答票务规则和政策问题 - 📁 **自定义知识库** - 支持上传任意文本文档,构建个性化知识库并智能问答 - 📈 **可观测性** - 集成 Micrometer 监控 LLM 调用指标 - 🌐 **友好界面** - 精美的 Web 前端界面,支持移动端适配 ## 🛠️ 技术栈 | 技术 | 版本 | 说明 | |-------------------|---------------|----------------------------------| | **Java** | 17 | 开发语言 | | **Spring Boot** | 3.5.0 | 应用框架 | | **LangChain4j** | 1.11.0-beta19 | AI Agent 框架 | | **阿里云 DashScope** | 1.11.0-beta19 | 通义千问大模型 Spring Boot Starter | | **MCP** | 1.11.0-beta19 | Model Context Protocol(连接 12306) | | **Gradle** | 8.5 | 构建工具 | | **Lombok** | Latest | 代码简化 | | **Micrometer** | Latest | 指标监控 | | **Reactor** | Latest | 流式响应支持 | ## 📁 项目结构 ``` demo-langchain4j-mcp/ ├── src/main/java/com/devxz/mcp/ │ ├── Application.java # 应用入口(@SpringBootApplication) │ ├── ai/ │ │ ├── TicketAssistant.java # AI 服务接口 (@AiService) - 票务查询和订购 │ │ │ # - chat(): 普通对话 │ │ │ # - streamingChat(): 流式对话 │ │ └── KnowledgeAssistant.java # 知识库问答接口 (@AiService) │ │ # - answerFromKnowledge(): RAG 知识问答 │ ├── config/ │ │ ├── ChatMemoryConfig.java # 聊天记忆配置 (ChatMemoryProvider) │ │ │ # - MessageWindowChatMemory: 保留最近 20 条消息 │ │ ├── McpConfig.java # MCP 客户端配置 │ │ │ # - stdio 模式支持 │ │ │ # - HTTP SSE 模式支持 │ │ └── RagConfig.java # RAG 配置 │ │ # - 文档加载器和分割器 │ │ # - 嵌入模型和向量存储 │ │ # - 内容检索器配置 │ ├── controller/ │ │ ├── TicketAssistantController.java # AI 对话 REST 接口 │ │ │ # - GET /api/assistant/chat(普通对话) │ │ │ # - GET /api/assistant/stream(流式对话) │ │ ├── TicketController.java # 票务管理 REST 接口 │ │ │ # - GET /api/tickets/query-12306 │ │ │ # - POST /api/tickets/orders/details │ │ │ # - GET /api/tickets/orders/{id} │ │ │ # - POST /api/tickets/orders/{id}/cancel │ │ ├── KnowledgeController.java # RAG 知识库问答接口 │ │ │ # - GET /api/knowledge/ask │ │ │ # - GET /api/knowledge/examples │ │ └── CustomKnowledgeController.java # 自定义知识库接口 │ │ # - POST /api/knowledge/custom/upload │ │ # - POST /api/knowledge/custom/ask │ │ # - GET /api/knowledge/custom/stats │ │ # - GET /api/knowledge/custom/documents │ │ # - DELETE /api/knowledge/custom/clear │ ├── domain/ │ │ ├── TicketInfo.java # 票务信息模型 │ │ └── TicketOrder.java # 订单模型 │ ├── dto/ │ │ ├── CreateOrderRequest.java # 创建订单请求 DTO(简化版) │ │ └── CreateOrderWithDetailsRequest.java # 带详情的订单请求 DTO │ ├── service/ │ │ ├── TicketService.java # 票务业务服务 │ │ │ # - queryTicketsFrom12306(): MCP 查询 │ │ │ # - createOrder(): 创建订单 │ │ │ # - cancelOrder(): 取消订单 │ │ │ # - getOrderDetails(): 查询详情 │ │ ├── KnowledgeBaseService.java # 知识库管理服务 │ │ │ # - uploadDocuments(): 上传和处理文档 │ │ │ # - askQuestion(): 基于知识库问答 │ │ │ # - getStats(): 获取统计信息 │ │ └── UnifiedKnowledgeService.java # 统一知识检索服务 │ │ # - retrieveKnowledge(): 混合检索 │ ├── tools/ │ │ ├── McpTools.java # MCP 工具调用 (@Tool) │ │ │ # - queryTicketsByMcp(): 查询 12306 │ │ └── TicketOrderTools.java # 订单管理工具 (@Tool) │ │ # - createTicketOrderWithDetailsAndPrice(): 创建订单 │ │ # - cancelTicketOrder(): 取消订单 │ │ # - getOrderDetails(): 查询订单详情(JSON 格式) │ │ # - getOrderStatus(): 查询订单状态(文本格式,含行程信息) │ └── util/ │ └── TicketParserUtils.java # 票务数据解析工具 │ # - parseTrains(): 解析车次信息 ├── src/main/resources/ │ ├── knowledge/ │ │ └── railway_rules.md # 预设铁路票务规则知识库 │ ├── static/ │ │ ├── index.html # 主界面(多标签页集成所有功能) │ │ │ # - 💬 AI 对话(流式响应) │ │ │ # - 🚄 12306 实时查询 │ │ │ # - 🎫 点击座位打开订票弹窗 │ │ │ # - 📋 订单查询 │ │ │ # - 📚 RAG 知识库问答 │ │ │ # - 📁 自定义知识库(上传+问答) │ │ └── welcome.html # 欢迎页面 │ └── application.yaml # 应用配置 ├── test/java/com/devxz/mcp/ │ ├── TicketAgentApplicationTests.java # 应用集成测试 │ │ # - contextLoads(): Spring 上下文测试 │ │ # - testTicketAssistantChat(): AI 对话测试 │ └── TicketParserTest.java # 票务解析单元测试 │ # - testStringToTicket(): 解析测试 ├── .dockerignore # Docker 忽略文件 ├── .env.example # 环境变量示例 ├── .gitignore # Git 忽略文件 ├── Dockerfile # Docker 镜像构建文件 │ # - 多阶段构建 │ # - 包含 Node.js 运行时 ├── docker-compose.yml # Docker Compose 部署配置 ├── gradlew.bat # Gradle Windows 启动脚本 ├── build.gradle # Gradle 构建配置 └── settings.gradle # Gradle 设置 ``` ## 🚀 快速开始 ### 1. 环境要求 - Java 17+ - Gradle 8.5+ - 阿里云 DashScope API Key(访问 https://dashscope.console.aliyun.com/apiKey 获取) - Node.js(用于运行 12306 MCP 服务,Docker 部署时镜像已包含) ### 2. 获取 API Key 访问 [阿里云 DashScope 控制台](https://dashscope.console.aliyun.com/apiKey) 创建 API 密钥 ### 3. 配置应用 在 `src/main/resources/application.yaml` 中配置: ```yaml langchain4j: community: dashscope: chat-model: api-key: ${ALI_QWEN_API_KEY:your-dashscope-api-key-here} model-name: qwen-plus streaming-chat-model: api-key: ${ALI_QWEN_API_KEY:your-dashscope-api-key-here} model-name: qwen-plus mcp: 12306-server: enabled: true type: stdio command: npx args: - "-y" - "12306-mcp" ``` **或使用环境变量:** ```bash # Windows PowerShell $env:ALI_QWEN_API_KEY="sk-xxxxxxxxxxxxxx" # Windows CMD set ALI_QWEN_API_KEY=sk-xxxxxxxxxxxxxx # Linux/Mac export ALI_QWEN_API_KEY=sk-xxxxxxxxxxxxxx ``` **或创建 .env 文件:** ```bash cp .env.example .env # 编辑 .env 文件,填入你的 API Key ``` ### 4. 构建并运行 ```bash # Windows gradlew.bat build gradlew.bat bootRun # Linux/Mac ./gradlew build ./gradlew bootRun ``` 应用启动后访问:http://localhost:8088 ## 🐳 Docker 部署 ### 使用 Docker Compose(推荐) 1. **配置环境变量** ```bash # Windows PowerShell $env:ALI_QWEN_API_KEY="sk-your-api-key" # Linux/Mac export ALI_QWEN_API_KEY="sk-your-api-key" ``` 2. **启动服务** ```bash docker-compose up -d ``` 3. **查看日志** ```bash docker-compose logs -f ``` 4. **停止服务** ```bash docker-compose down ``` ### 使用 Dockerfile ```bash # 构建镜像 docker build -t langchain4j-12306-agent . # 运行容器 docker run -d \ -p 8088:8088 \ -e ALI_QWEN_API_KEY=your-api-key \ -e mcp.12306-server.enabled=true \ -e mcp.12306-server.type=stdio \ --name 12306-agent \ langchain4j-12306-agent ``` ## 🌐 Web 界面 ### 主界面(推荐) 访问:http://localhost:8088/index.html **功能模块:** - 💬 **AI 对话** - 与票务助手自然语言交流(支持流式输出,自动调用 MCP 查询实时余票) - 🚄 **12306 实时查询** - 直接查询 12306 官方车次、余票、票价信息 - 🎫 **弹窗订票** - 点击查询结果中的座位,自动打开订票弹窗并填充信息 - 📋 **订单查询** - 查询已创建订单的详细信息(包含完整行程信息) - 📚 **RAG 知识库问答** - 基于预设票务规则知识库,准确回答退票费、儿童票等政策问题 - 📁 **自定义知识库** - 上传任意文本文档,构建个性化知识库并智能问答 ### 订票流程 1. 在"12306 实时查询"页面输入出发地、目的地、日期 2. 点击"查询余票"按钮,查看车次和座位信息 3. **点击有票的座位卡片**,自动打开订票弹窗 4. 弹窗自动填充:车次、票类型、票价、出发地、目的地、日期 5. 填写乘客信息(姓名、电话)和数量 6. 点击"提交订单"完成预订 ### AI 对话示例 **用户**: "帮我查一下明天北京南到上海虹桥的高铁" **AI 执行流程**: 1. 理解意图 → 需要查询 12306 余票 2. 提取参数 → fromStation=北京南,toStation=上海虹桥,date=明天 3. 调用 MCP 工具 → `queryTicketsByMcp("北京南", "上海虹桥", "2026-03-11")` 4. MCP 返回实时票务数据 5. AI 整理并生成友好回复 **AI 回复**: ``` 好的,为您查询到 2026-03-11 从北京南到上海虹桥的高铁余票信息: 车次 G123 - 出发时间:09:00 - 到达时间:13:30 - 历时:4 小时 30 分 - 二等座:¥553 有票 - 一等座:¥933 剩余 12 张 - 商务座:¥1748 剩余 5 张 车次 G456 - 出发时间:10:15 ... ``` ## 📡 REST API ### AI 对话接口 ```bash # 普通对话(带会话记忆) curl "http://localhost:8088/api/assistant/chat?message=你好,有什么票可以买?&sessionId=user123" # 流式对话(推荐,打字机效果) curl "http://localhost:8088/api/assistant/stream?message=帮我查一下北京南到上海虹桥的票&sessionId=user123" ``` **接口对比:** | 接口 | 路径 | 响应类型 | 特点 | 适用场景 | |------|------|----------|------|----------| | 普通对话 | `/api/assistant/chat` | `String` | 等待完整响应后返回 | 简单查询、API 调用 | | 流式对话 | `/api/assistant/stream` | `Flux` | 实时流式输出(打字机效果) | 长文本、需要快速响应的场景 | **参数说明:** - `message` - 用户消息(必填) - `sessionId` - 会话 ID(可选,默认:default-user),用于标识不同用户,保持对话记忆 **响应示例:** **普通对话** - 返回完整文本响应: ```json "您好!我是您的智能票务助手,可以帮您查询票务信息、预订门票或处理订单。有什么可以帮您的吗?😊" ``` **流式对话** - 以 SSE (Server-Sent Events) 格式流式返回: ``` data: 您 data: 好 data: ! data: 我 data: 是 ... ``` ### 12306 票务查询接口 ```bash # 查询实时票务信息(返回结构化数据) curl "http://localhost:8088/api/tickets/query-12306?fromStation=北京南&toStation=上海虹桥&date=2026-03-11" ``` **响应示例:** ```json [ { "trainNumber": "G123", "departureStation": "北京南", "arrivalStation": "上海虹桥", "departureTime": "09:00", "arrivalTime": "13:30", "duration": "04:30", "seats": [ { "type": "二等座", "availability": "有票", "price": 553.0 }, { "type": "一等座", "availability": "剩余 12 张", "price": 933.0 } ] } ] ``` ### 订单管理接口 ```bash # 创建订单(简化版) curl -X POST http://localhost:8088/api/tickets/orders \ -H "Content-Type: application/json" \ -d '{ "customerName": "张三", "contactPhone": "13800138000", "ticketType": "二等座", "quantity": 2 }' # 创建订单(带完整行程信息,推荐) curl -X POST http://localhost:8088/api/tickets/orders/details \ -H "Content-Type: application/json" \ -d '{ "customerName": "张三", "contactPhone": "13800138000", "fromStation": "北京南", "toStation": "上海虹桥", "ticketType": "二等座", "quantity": 2, "travelDate": "2026-03-11", "trainNumber": "G123", "duration": 4.5 }' # 查询订单详情 curl http://localhost:8088/api/tickets/orders/ORD1234567890 # 查询订单状态 curl http://localhost:8088/api/tickets/orders/ORD1234567890/status # 取消订单 curl -X POST "http://localhost:8088/api/tickets/orders/ORD1234567890/cancel?reason=行程变更" ``` **响应示例:** ```json { "success": "true", "orderNumber": "ORD1710234567890", "message": "订单创建成功,行程:北京南 -> 上海虹桥" } ``` ## 🧠 核心功能 ### 1. AI 智能对话(支持多轮记忆 + MCP 工具调用 + 流式响应) ### 2. RAG 知识库问答(检索增强生成) 通过向量检索技术,从预设知识库中查找相关信息,结合 AI 生成准确回答。 #### 📚 什么是 RAG? RAG(Retrieval-Augmented Generation,检索增强生成)是一种结合知识库检索和 AI 生成的技术: 1. **检索** - 从知识库中查找与问题最相关的信息片段 2. **增强** - 将检索到的信息作为上下文提供给 AI 3. **生成** - AI 基于检索到的准确信息生成回答 #### 🏗️ 技术架构 ``` 用户提问 ↓ KnowledgeAssistant (AI 服务) ↓ [检索] → EmbeddingStore (向量库) ← [知识库文档] ↓ ↑ [相关片段] | embeddingModel (嵌入模型) ↓ AI 生成回答(基于检索到的知识) ↓ 返回给用户 ``` #### 💻 核心代码 ```java @AiService public interface KnowledgeAssistant { @SystemMessage(""" 你是一个专业的铁路票务知识助手,基于检索到的知识库内容回答问题。 核心能力: 1. 回答票务规则和政策问题(退票费、儿童票、学生票等) 2. 解释乘车规定和行李限制 3. 提供特殊旅客服务信息 4. 解答安全须知相关问题 重要规则: - 严格基于检索到的知识库内容回答,不要编造信息 - 如果检索到的内容与问题无关,明确告知用户 - 引用具体的规则条款和数据 - 保持专业、准确的回答风格 """) String answerFromKnowledge( @UserMessage String userMessage, @MemoryId String memoryId ); } ``` #### 🔄 RAG 工作流程 1. **文档加载** - 读取 `knowledge/railway_rules.md` 知识库文件 2. **文档分割** - 将大文档分成小片段(500 字符/段,重叠 50 字符) 3. **向量化** - 使用 all-minilm-l6-v2-q 模型生成向量表示 4. **向量存储** - 存入 InMemoryEmbeddingStore 5. **相似度检索** - 用户提问时,检索最相关的 top 3 片段 6. **增强生成** - 将检索结果作为上下文提供给 AI 生成回答 #### ✨ 特性 - ✅ 基于真实知识库内容,回答准确可靠 - ✅ 支持流式响应(打字机效果) - ✅ 自动记忆对话上下文 - ✅ 提供示例问题快速提问 - ✅ 最低相关度阈值过滤(0.6) #### 💡 使用示例 **可以问的问题:** - **退票规则**:退票费怎么收取? - **儿童票**:儿童票的规定是什么? - **学生票**:学生票有哪些优惠? - **改签**:改签规则是怎样的? - **行李**:可以携带多少千克的行李? - **证件**:如何办理临时身份证明? - **检票**:开车前多久停止检票? - **禁带物品**:哪些东西不能带上车? **预期回答特点:** - ✅ **准确性** - 基于知识库中的真实条款 - ✅ **详细性** - 包含具体的时间、金额、比例等信息 - ✅ **引用性** - 会提及具体的规则条款 - ✅ **专业性** - 使用规范的票务术语 #### 🔧 检索参数调优 在 `RagConfig.java` 中可以调整以下参数优化检索效果: ```java // 每个片段的最大字符数(影响检索粒度) DocumentSplitters.recursive(500, 50) // 第一个参数是片段大小,第二个是重叠字符数 // 每次检索返回的结果数量(影响上下文丰富度) .maxResults(3) // 改为其他数字调整返回数量,建议 2-5 // 最低相关度阈值(影响检索精度) .minScore(0.6) // 范围 0-1,越高越严格,推荐 0.5-0.7 ``` **调优建议:** - 📏 **片段大小**:较小片段(300-500)更精确,较大片段(800-1000)上下文更完整 - 🔢 **返回数量**:简单问题用 2-3 个,复杂问题用 4-5 个 - 🎯 **相关度阈值**:提高阈值减少噪音,降低阈值增加召回率 #### 🆚 与原有功能的区别 | 对比项 | TicketAssistant(票务助手) | KnowledgeAssistant(知识助手) | |--------|---------------------------|------------------------------| | **主要能力** | 实时查询 12306 余票、创建订单 | 回答票务规则和政策问题 | | **数据来源** | MCP 工具调用(动态数据) | 本地知识库(静态知识) | | **适用场景** | 查余票、订票、查订单 | 咨询退票费、儿童票、改签等规则 | | **响应速度** | 依赖网络请求(较慢) | 本地检索(较快) | | **准确性** | 实时数据,完全准确 | 基于知识库,可能存在时效性问题 | --- ### 3. 自定义知识库(用户上传文档) 支持用户上传任意文本文档,构建个性化知识库并智能问答: **支持的文件格式:** - `.txt` - 纯文本文件 - `.md` - Markdown 文档 - `.java`, `.py`, `.js` - 代码文件 - `.json` - JSON 数据文件 - 其他 UTF-8 编码的文本文件 **使用流程:** 1. **上传文档** - 点击选择文件或拖拽文件到上传区域 2. **批量上传** - 一次可选择多个文件 3. **自动处理** - 系统自动分割、向量化并存储 4. **智能问答** - 基于上传的文档进行问答 5. **查看统计** - 显示文档数量和文本片段数 **应用场景:** - 📖 学习个人文档资料 - 💻 分析代码项目结构 - 🏢 查询公司内部规定 - 📚 研究技术文档手册 - 📝 整理会议记录 **技术实现:** ```java @Service public class KnowledgeBaseService { // 上传文档并处理 public UploadResult uploadDocuments(String sessionId, List files) { // 1. 读取文件内容 // 2. 按换行符分割文档 // 3. 向量化每个片段 // 4. 存入用户专属向量库 // 5. 返回统计信息 } // 基于知识库问答 public String askQuestion(String sessionId, String question) { // 1. 检索用户知识库中的相关片段 // 2. 构建上下文信息 // 3. 调用 AI 生成回答 // 4. 返回流式响应 } } ``` **特性:** - ✅ 支持拖拽上传和点击选择 - ✅ 多文件批量上传 - ✅ 会话隔离(每个用户独立知识库) - ✅ 实时统计信息展示 - ✅ 流式问答输出 - ✅ 文档列表管理 #### 🎓 学习要点 通过本项目的 RAG 实现,你可以学习到: 1. **LangChain4j RAG 集成** - 文档加载和分割策略 - 向量化处理流程 - 相似度检索机制 2. **Spring Boot 集成** - Bean 配置和管理 - 依赖注入模式 - REST API 开发 3. **AI 服务设计** - System Message 设计技巧 - 上下文管理方法 - 检索增强生成实践 4. **最佳实践** - 知识库内容组织 - 文档分块策略选择 - 检索参数调优方法 #### 📝 下一步扩展 如需进一步提升 RAG 系统能力,可以考虑: - 🗄️ **持久化向量库** - 使用 PostgreSQL + pgvector 或 Milvus 替代内存存储 - 📄 **多格式支持** - 支持 PDF、Word、HTML 等更多文档格式 - 🔄 **增量更新** - 支持知识库的动态更新而不需重建索引 - 📈 **检索分析** - 记录检索日志,分析和优化检索效果 - 🎯 **混合检索** - 结合关键词检索和向量检索提高准确率 - 🔍 **重排序** - 引入 Cross-Encoder 模型对检索结果进行二次排序 --- ### 4. MCP 工具调用(Model Context Protocol) 通过 MCP 协议连接 12306 服务,支持 stdio(本地进程)和 HTTP(远程 SSE)两种连接模式: ```java @Component public class McpTools { @Tool("查询票务信息") public String queryTicketsByMcp(String fromStation, String toStation, String date) { // 通过 MCP 客户端调用 12306 服务 var result = server12306McpClient.executeTool(...); return result.resultText(); } } ``` **MCP 配置说明:** - **stdio 模式**:在本地启动一个子进程运行 12306-mcp(需要 Node.js) - **HTTP 模式**:连接到远程 MCP 服务(使用 SSE 通信) - **超时设置**:默认 180 秒,可在 `application.yaml` 中调整 ### 5. 聊天记忆配置 ```java @Configuration public class ChatMemoryConfig { @Bean public ChatMemoryProvider chatMemoryProvider() { return memoryId -> MessageWindowChatMemory.builder() .id(memoryId) .maxMessages(20) // 保留最近 20 条消息 .build(); } } ``` ### 6. MCP 配置 ```java @Configuration public class McpConfig { @Bean @ConditionalOnProperty(value = "mcp.12306-server.enabled", havingValue = "true") public McpClient server12306McpClient() { // 创建 MCP 客户端,支持 stdio 和 http 两种模式 return new DefaultMcpClient.Builder() .key("12306-mcp-client") .transport(transport) .toolExecutionTimeout(Duration.ofSeconds(180)) .build(); } } ``` ### 7. 前端弹窗订票 点击座位后自动打开订票弹窗,填充完整信息: ```javascript function selectSeat(trainNumber, seatType, price, fromStation, toStation, duration) { // 填充弹窗表单 document.getElementById('modal-train-number').value = trainNumber; document.getElementById('modal-ticket-type').value = seatType; document.getElementById('modal-ticket-price').value = price; document.getElementById('modal-from-station').value = fromStation; document.getElementById('modal-to-station').value = toStation; document.getElementById('modal-duration').value = duration; // 设置日期为查询日期 const queryDate = document.getElementById('query-date').value; document.getElementById('modal-travel-date').value = queryDate; // 显示弹窗 document.getElementById('booking-modal').style.display = 'block'; } ``` **支持的票类型(11 种):** - 座位类:二等座、一等座、商务座、特等座、优选一等座、无座 - 卧铺类:硬卧、软卧、二等卧、一等卧 **特性:** - ✅ 点击座位自动打开弹窗 - ✅ 自动填充车次、票类型、票价、日期等信息 - ✅ 显示完整行程信息卡片 - ✅ 支持模糊匹配票类型 - ✅ 订单成功后 3 秒自动关闭弹窗 ## 📊 可观测性 集成 Micrometer 监控 LLM 调用指标: ```bash # 查看 token 使用情况 curl http://localhost:8080/actuator/metrics/gen_ai.client.token.usage # Prometheus 格式指标 curl http://localhost:8080/actuator/prometheus ``` **监控端点:** - `/actuator/health` - 健康检查 - `/actuator/info` - 应用信息 - `/actuator/metrics` - 指标数据 - `/actuator/prometheus` - Prometheus 格式指标 ## 🔧 配置说明 详细配置请参考 `src/main/resources/application.yaml` 文件。 **MCP 配置项:** - `mcp.12306-server.enabled` - 是否启用 MCP 服务(默认:true) - `mcp.12306-server.type` - 连接模式:stdio(本地)或 http(远程) - `mcp.12306-server.command` - 本地命令(如:npx -y 12306-mcp) - `mcp.12306-server.args` - MCP 服务器参数数组(如:["-y", "12306-mcp"]) - `mcp.12306-server.url` - 远程 MCP 服务 URL(HTTP 模式使用,默认:http://localhost:8083) **AI 模型配置:** - `model-name` - 模型名称(默认:qwen-plus) - 可选:qwen-turbo, qwen-plus, qwen-max - qwen-plus:性能和成本的平衡选择(推荐) - qwen-max:最强性能,但成本更高 - `temperature` - 模型温度(默认:0.7,范围 0-2) - 较低值(0.3-0.6):更保守、一致的回答 - 较高值(0.8-1.5):更有创意、多样的回答 - `timeout` - 超时时间(默认:PT60S,ISO-8601 格式) - 示例:PT30S (30 秒), PT5M (5 分钟) - `log-requests` - 是否记录请求日志(默认:true) - `log-responses` - 是否记录响应日志(默认:true) **日志配置:** ```yaml logging: level: root: INFO # 根日志级别 com.devxz.mcp: DEBUG # 应用代码 DEBUG 级别 dev.langchain4j: DEBUG # LangChain4j 框架 DEBUG dev.langchain4j.mcp: DEBUG # MCP 协议详细日志 ``` **环境变量优先级:** 1. 系统环境变量(最高优先级) 2. .env 文件中的配置 3. application.yaml 中的默认值 **完整配置示例:** ```yaml spring: application: name: demo-langchain4j-agent # LangChain4j + DashScope 配置 langchain4j: community: dashscope: chat-model: api-key: ${ALI_QWEN_API_KEY:your-api-key} model-name: qwen-plus temperature: 0.7 timeout: PT60S log-requests: true log-responses: true streaming-chat-model: api-key: ${ALI_QWEN_API_KEY:your-api-key} model-name: qwen-plus # MCP 配置 mcp: 12306-server: enabled: true type: stdio command: npx args: - "-y" - "12306-mcp" url: http://localhost:8083 # HTTP 模式使用 # Actuator 监控 management: endpoints: web: exposure: include: health,info,metrics,prometheus metrics: tags: application: ${spring.application.name} # 服务器配置 server: port: 8088 ``` ## 📦 依赖说明 ```gradle dependencies { // Spring Boot starters implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-actuator' // LangChain4j Spring Boot starter implementation 'dev.langchain4j:langchain4j-spring-boot-starter:1.11.0-beta19' // 阿里云 DashScope(通义千问)Spring Boot starter implementation 'dev.langchain4j:langchain4j-community-dashscope-spring-boot-starter:1.11.0-beta19' // LangChain4j Reactor support for streaming implementation 'dev.langchain4j:langchain4j-reactor:1.11.0-beta19' // LangChain4j MCP (Model Context Protocol) support implementation 'dev.langchain4j:langchain4j-mcp:1.11.0-beta19' // Micrometer metrics for observability implementation 'io.micrometer:micrometer-registry-prometheus' // Lombok compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' // Jackson for JSON processing implementation 'com.fasterxml.jackson.core:jackson-databind' // Logging implementation 'org.slf4j:slf4j-api' // Testing testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0' } ``` **关键依赖说明:** | 依赖 | 说明 | |------|------| | `langchain4j-spring-boot-starter` | LangChain4j 核心功能 + Spring Boot 自动配置 | | `langchain4j-community-dashscope-spring-boot-starter` | 阿里云通义千问大模型集成 | | `langchain4j-mcp` | MCP 协议支持,用于连接外部工具和服务 | | `langchain4j-reactor` | 响应式编程支持,实现流式响应 | | `micrometer-registry-prometheus` | Prometheus 格式的监控指标导出 | | `lombok` | 简化 Java 代码(@Data, @Slf4j 等注解) | | `jackson-databind` | JSON 序列化和反序列化 | **可选依赖:** ```gradle // 如需更多监控功能,可添加以下依赖 implementation 'dev.langchain4j:langchain4j-micrometer-metrics:1.11.0-beta19' ``` ## 🧪 测试 运行单元测试: ```bash # Windows gradlew.bat test # Linux/Mac ./gradlew test ``` **测试类:** - `TicketAgentApplicationTests` - Spring 上下文加载和 AI 助手功能集成测试 - `TicketParserTest` - 票务数据解析单元测试 **运行特定测试:** ```bash # 运行单个测试类 ./gradlew test --tests TicketAgentApplicationTests # 运行所有测试并查看日志 ./gradlew test --info ``` ## ⚠️ 注意事项 ### 1. MCP 客户端首次连接失败问题 **现象描述:** 应用启动后第一次调用 MCP 工具(如查询 12306 余票)时,大概率会连接失败,但后续调用都能成功。 **根本原因:** 这是 **正常现象**,主要原因是 MCP 客户端初始化需要时间: 1. **stdio 模式启动延迟** - MCP 客户端通过 `StdioMcpTransport` 启动一个子进程(运行 `npx -y 12306-mcp`) - Node.js 需要加载 npm 包并初始化 MCP 服务器 - 这个过程通常需要 2-5 秒 2. **Spring Bean 初始化时序** - Spring 容器启动时立即创建 `McpClient` Bean - 此时 stdio 子进程刚启动,还未完全就绪 - 第一个请求到来时,MCP 通道可能还在建立中 3. **npx 首次执行慢** - 如果本地没有缓存 `12306-mcp` 包,npx 会先下载 - 即使有缓存,Node.js 进程启动也需要时间 **解决方案:** ✅ **方案 1:预热请求(推荐)** ```bash # 应用启动后,先发送一个简单的查询请求进行预热 curl "http://localhost:8088/api/tickets/query-12306?fromStation=北京&toStation=上海&date=2026-03-11" ``` ✅ **方案 2:增加初始化延迟** 在 `application.yaml` 中配置: ```yaml mcp: 12306-server: enabled: true type: stdio # 首次启动建议等待 5-10 秒 ``` ✅ **方案 3:健康检查** 添加启动后的健康检查逻辑,确保 MCP 服务就绪后再接受流量。 ✅ **方案 4:使用 HTTP 模式** 如果部署了独立的 MCP 服务,使用 HTTP 模式更稳定: ```yaml mcp: 12306-server: type: http url: http://localhost:8083 # 预先启动的 MCP 服务 ``` **最佳实践:** - 📌 开发环境:接受首次慢的情况,后续使用正常 - 📌 测试环境:在测试脚本前添加预热步骤 - 📌 生产环境:使用独立部署的 MCP 服务(HTTP 模式)+ 健康检查 --- ### 2. API Key 安全 - ❌ 不要将 API Key 提交到版本控制系统 - ✅ 使用环境变量或本地配置文件管理 - ✅ 生产环境使用密钥管理服务(如 AWS Secrets Manager、HashiCorp Vault) - ✅ 使用 `.gitignore` 忽略 `.env` 文件 --- ### 3. MCP 服务 - ✅ 确保已安装 Node.js(用于运行 npx 命令) - ⏱️ 首次启动可能需要下载 12306 MCP 服务(npm 包) - ⚙️ 可在 `application.yaml` 中调整 MCP 超时时间(默认 180 秒) - 🔍 设置 `dev.langchain4j.mcp: DEBUG` 查看详细 MCP 通信日志 --- ### 4. 会话记忆 - 💾 前端自动为每个用户生成唯一的 sessionId - 💾 sessionId 存储在 localStorage 中,刷新页面仍有效 - 🗑️ 清除浏览器缓存会重置会话 - 👥 不同 sessionId 的对话历史完全隔离 --- ### 5. 消息窗口 - 💬 默认保留最近 20 条消息 - 🔄 超出后会自动丢弃最早的消息 - ⚙️ 可在 `ChatMemoryConfig.java` 中调整 --- ### 6. 流式输出 - 🌐 需要浏览器支持 ReadableStream API - ✅ 现代浏览器均支持(Chrome、Firefox、Safari、Edge) - 📱 移动端浏览器也支持 --- ### 7. Docker 部署 - 🐳 Docker 镜像已包含 Node.js,无需单独安装 - 🌡️ 注意容器内存限制(建议至少 512MB) - 🔌 确保容器网络可以访问外部服务(阿里云 DashScope API) --- ### 8. 性能优化 - ⚡ 使用 qwen-plus 模型平衡性能和响应质量 - 🎯 降低 temperature 可以提高回答一致性(推荐 0.5-0.7) - 📊 监控 token 使用情况,避免超额消耗 ## 📚 参考资料 ### LangChain4j - [LangChain4j 官方文档](https://docs.langchain4j.dev/) - [LangChain4j Spring Boot 集成](https://docs.langchain4j.dev/tutorials/spring-boot-integration) - [LangChain4j Tools](https://docs.langchain4j.dev/tutorials/tools) - [LangChain4j Chat Memory](https://docs.langchain4j.dev/tutorials/chat-memory) - [LangChain4j MCP](https://docs.langchain4j.dev/tutorials/mcp) - [LangChain4j Streaming](https://docs.langchain4j.dev/tutorials/streaming) ### 阿里云 DashScope - [阿里云 DashScope 文档](https://help.aliyun.com/zh/dashscope/) - [通义千问模型介绍](https://help.aliyun.com/zh/dashscope/developer-reference/qwen) - [DashScope API 参考](https://help.aliyun.com/zh/dashscope/api-reference) ### 12306 MCP - [12306 MCP 服务](https://www.npmjs.com/package/12306-mcp) - [MCP 协议规范](https://modelcontextprotocol.io/) ### Spring Boot & Docker - [Spring Boot 官方文档](https://spring.io/projects/spring-boot) - [Docker 官方文档](https://docs.docker.com/) - [Docker Compose 配置参考](https://docs.docker.com/compose/) ## 👨‍💻 开发团队 **作者**: dev 小筑 **版权**: © 2020-2026 dev 小筑 **许可**: Apache 2.0 ## 📝 更新日志 ### v1.2.0 (2026-03-23) - 📚 **新增 RAG 知识库问答** - 基于检索增强生成技术,准确回答票务规则问题 - 📁 **新增自定义知识库** - 支持上传任意文本文档,构建个性化知识库 - 🎨 **前端界面升级** - 多标签页设计,集成所有功能模块 - ⚡ **流式响应优化** - 所有 AI 问答均支持打字机效果 - 👥 **会话隔离增强** - 每个用户拥有独立的知识库和对话记忆 - 📊 **统计信息展示** - 实时显示文档数量和文本片段数 - 🐛 **Bug 修复** - 修复样式兼容性和文件上传边界情况 ### v1.1.0 (2026-03-12) - 🎫 **新增弹窗订票功能** - 点击座位即可打开订票弹窗 - ✨ **自动填充订单信息** - 车次、票类型、票价、日期自动填充 - 🔧 **优化订单查询展示** - AI 查询订单时展示完整行程信息 - 🎨 **前端界面优化** - 移除独立订票标签页,改用弹窗交互 - 📦 **完善票类型支持** - 支持 11 种票类型(含二等卧、一等卧等) - 🐛 **修复票价问题** - 使用 12306 MCP 返回的真实票价 - 📖 **文档更新** - 更新 README 反映最新功能 ### v1.0.0 (2026-03-11) - ✨ 初始版本发布 - 🤖 集成 LangChain4j + 阿里云通义千问 - 🚄 支持 12306 实时票务查询(MCP 协议) - 💬 支持多轮对话记忆 - ⚡ 支持流式响应(打字机效果) - 🌐 提供友好的 Web 界面 - 📡 完整的 REST API - 🐳 支持 Docker 容器化部署 - 📊 集成 Micrometer 监控 - 🧪 包含单元测试和集成测试 --- **最后更新**: 2026-03-23 **版本**: 1.2.0 **状态**: ✅ 生产就绪