内容
# IntelliRide — 智能驾驶后台
IntelliRide 是一个**完整的叫车后台** — 乘客、司机、整个行程生命周期(请求 → 匹配 → OTP 开始 → 结束 → 支付 → 评分)、钱包和评分 — 以纯粹、身份验证的 **REST API** 形式公开。整个系统在没有任何请求涉及 LLM 的情况下端到端运行;参见 [核心叫车后台](#核心叫车后台的工作原理与AI层无关)。
在此基础上,构建了一个**智能 AI 层**:一个多智能体系统,将请求路由到专门的智能体,**自主规划和执行多步骤目标**,在更改数据之前请求人类确认,并返回每个决策的完整**推理轨迹**。AI 可以做的所有事情也是直接的 REST 调用 — AI 是一个*增强*,而不是应用程序。
AI 层演示了五个协同工作的构建块:
| 概念 | 在此处的作用 | 关键代码 |
|--------|-------------------|----------|
| **LLM** | 自然语言理解和生成 (OpenAI `gpt-4o`) | `AIConfig`,每个智能体的 `ChatClient` |
| **RAG** | 通过 pgvector 存储将策略/FAQ 答案置于您自己的文档中 | `RAGService`,`QuestionAnswerAdvisor`,`resources/knowledge/*.md` |
| **AI 智能体** | 决定调用哪些工具并执行它们(预订/取消/评分,…) | `RiderTools`,`DriverTools`,`RiderAgent`,`DriverAgent` |
| **智能体编排** | 一个监督者,**路由**,**规划** 多步骤目标,**确认** 操作,并**恢复** 暂停的计划 | `SupervisorService`,`PlannerService`,`PendingPlanStore`,`agents/` |
| **MCP** | 通过模型上下文协议公开相同的工具,以便外部客户端(例如 Claude Desktop)可以驱动应用程序 | `McpConfig`,MCP WebMVC/SSE 服务器 |
---
## 技术栈
- **Java 21**,**Spring Boot 4.x**
- **Spring AI 2.0** — `ChatClient`,顾问,工具调用,**结构化输出**,pgvector 矢量存储,MCP 服务器
- **OpenAI** — `gpt-4o`(聊天)+ `text-embedding-3-small`(嵌入)
- **PostgreSQL + PostGIS**(空间)+ **pgvector**(RAG 矢量存储)
- **Spring Security + JWT**,Redis,OSRM(距离),ModelMapper
---
## 核心叫车后台(与AI层无关)
在智能层之下,IntelliRide 是一个完整的叫车 REST API。助手执行的每个操作也是一个纯粹的身份验证 REST 调用,并且整个叫车 API 在没有任何请求涉及 LLM 的情况下运行。
### 行程生命周期
```
乘客请求 → 司机接受 → 司机开始(OTP) → 司机结束 → 双方评分
(RideRequest, (Ride + 4 位 OTP (验证 OTP; 支付 (费用确定, (评分司机 /
PENDING) 创建,CONFIRMED) PENDING + 评分行) 钱包结算, 评分乘客)
30% 佣金)
```
状态流 — `RideRequest`: `PENDING → CONFIRMED`; `Ride`: `CONFIRMED → ONGOING → ENDED` (或 `CANCELLED`)。
### 直接 REST API
| 区域 | 端点 |
|------|-----------|
| **身份验证** *(公共)* | `POST /auth/signup` (创建乘客),`POST /auth/login` (→ JWT),`POST /auth/mcp-token`,`POST /auth/onBoardNewDriver/{userId}` *(ROLE_ADMIN)* |
| **乘客** *(`ROLE_RIDER`)* | `POST /rider/requestRide`,`POST /rider/cancelRide/{rideId}`,`POST /rider/rateDriver`,`GET /rider/getMyProfile`,`GET /rider/getMyRides` |
| **司机** *(`ROLE_DRIVER`)* | `POST /drivers/acceptRide/{rideRequestId}`,`POST /drivers/startRide/{rideId}`,`POST /drivers/endRide/{rideId}`,`POST /drivers/cancelRide/{rideId}`,`POST /drivers/rateRider`,`GET /drivers/getMyProfile`,`GET /drivers/getMyRides` |
响应由 `GlobalResponseHandler` 包装为 `{ "data": ..., "error": ... }`,并且始终从 JWT 中解析出操作用户 — 端点从不接受调用者 ID。
### 领域模型和业务规则
- **实体** — `User` (角色:RIDER / DRIVER / ADMIN),`Rider`,`Driver`,`RideRequest`,`Ride`,
`Payment`,`Wallet` + `WalletTransaction` (账本),`Rating`。
- **策略模式** (`strategies/`)— 运行时由 `RideStrategyManager` 选择的规则:
- *费用* — 默认 vs. **高峰时段涨价** (18:00–21:00)。
- *司机匹配* — **最近** 的司机,或 **评分最高** 的司机(适用于评分为 ≥ 4.8 的乘客)。
- **支付和钱包** — `CASH` 或 `WALLET`; 结算时扣除乘客费用,司机获得积分(减去
**30% 平台佣金**),并写入 `WalletTransaction` 账本行。
- **幂等,支持并发的结算** — `endRide` → 支付由乐观锁定
(`@Version` on `Ride`) 和唯一账本密钥保护,因此重试或并发 `endRide` 永远不会
重复收费(详细信息见 [`docs/IDEMPOTENCY_CHANGES.md`](docs/IDEMPOTENCY_CHANGES.md))。
- **地理空间** — 拾取/放下存储为 **PostGIS** 点(SRID 4326);通过 **OSRM** 计算道路距离。
- **安全** — 无状态 **JWT**; 方法级 `@Secured` 角色检查; 在领域服务中验证所有权/状态。
---
## 智能层(使其不仅仅是一个聊天机器人)
每个 `/ai/chat` 请求都由 **`SupervisorService`** 处理,该服务选择以下三个路径之一:
1. **直接路由** (简单请求)— 基于规则的路由将消息发送到正确的智能体并
在一步中返回答案。
2. **计划和执行** (多步骤目标)— `PlannerService` 将目标分解为类型化的 `Plan`
(Spring AI **结构化输出**),然后监督者在循环中运行步骤。
3. **恢复** — 如果计划暂停等待确认,则消息被视为是/否回复。
**专用智能体** — 每个智能体都是其自己的 `ChatClient`,具有自己的系统提示、工具和顾问:
| 智能体 | 工具 | 顾问 |
|-------|-------|----------|
| `RiderAgent` | `RiderTools` | 聊天内存 |
| `DriverAgent` | `DriverTools` | 聊天内存 |
| `SupportAgent` | *(无)* | RAG (`QuestionAnswerAdvisor`) |
**人在环中(两层):**
- **计划级别** — 监督者在任何更改数据的步骤中暂停,保存在 `PendingPlanStore` 中,
并询问 *"我是否应该继续?"*; 它在下一个请求中恢复。
- **工具级别** — 每个操作工具都使用 `confirmed=false` 预览,只有在 `confirmed=true` 时才进行变更。
**推理轨迹** — 一个共享列表,每层都追加到(路由,计划,委托,工具调用,
确认)。它在每个响应的 `reasoning` 字段中返回,使智能体的
决策完全可观察。
> **安全防护:** `MAX_STEPS`上限拒绝失控的计划,并且如果LLM返回格式错误的计划,则规划者会退回到单个
> 安全的只读步骤。
---
## 架构
<img width="672" height="548" alt="Screenshot 2026-06-21 at 15 37 39" src="https://github.com/user-attachments/assets/469ef636-13a6-424e-ad0a-5aa7f81c2768" />
> **注意:** **MCP路径绕过智能层** — 当Claude Desktop调用工具时,它直接命中
> `RiderTools`/`DriverTools`,因此*其*模型成为编排者。监督者/规划者/智能体
> 仅在HTTP `/ai/chat`端点后运行。
### 多步骤目标流程(计划 + 确认 + 恢复)
```mermaid
sequenceDiagram
participant User
participant Sup as SupervisorService
participant Plan as PlannerService
participant Agent as RiderAgent
participant LLM as gpt-4o
User->>Sup: "取消我的上次行程并给司机5星评分"
Sup->>Plan: 分解目标
Plan->>LLM: 结构化输出请求
LLM-->>Plan: 计划[ 定位行程,取消(确认),评分(确认) ]
Sup->>Agent: 步骤 1 — 定位行程(只读)
Agent-->>Sup: 结果
Sup-->>User: "我将:取消行程。是否继续?(是/否)" %% 暂停
User->>Sup: "是"
Sup->>Agent: 步骤 2 — 取消(确认)
Agent-->>Sup: 结果
Sup-->>User: "我将:给司机评分。是否继续?(是/否)" %% 再次暂停
User->>Sup: "是"
Sup->>Agent: 步骤 3 — 评分(确认)
Sup-->>User: 最终合成答案 + 完整推理轨迹
```
---
## 构建块,详细说明
### 1. LLM
`AIConfig` 提供共享的 `ChatClient.Builder`,`ChatMemory` (JDBC 支持),和 `VectorStore`
bean。每个智能体都使用构建器创建自己的 **独立 `ChatClient`**,并带有聚焦的系统提示。
### 2. RAG(检索增强生成)
- 策略/FAQ 文档位于 `src/main/resources/knowledge/*.md`。
- `RAGService` 将它们分段并嵌入到 **pgvector** `vector_store`。
- `SupportAgent` 使用 `QuestionAnswerAdvisor` 检索最相关的分段并
将其答案置于 *您的数据* 中,而不是凭空生成。
### 3. AI 智能体(工具调用)
乘客/司机智能体是绑定到其工具的 `ChatClient`;LLM 自主决定调用哪个
`@Tool`:
- **`RiderTools`** — `requestRide`,`rider_cancelRide`,`rateDriver`,`getWalletBalance`,`rider_getMyRides`,`rider_getMyProfile`
- **`DriverTools`** — `acceptRide`,`startRide`,`endRide`,`driver_cancelRide`,`rateRider`,`setAvailability`,…
设计原则:
- **工具从不接受操作用户 ID** — 身份验证在服务器端从 JWT 解析,因此
模型无法冒充其他人。
- **确认门控** — 变异工具需要 `confirmed=true` 的二次调用。
- **服务器端授权是真正的保证** — 所有权/状态检查位于领域服务中。
- **审计日志** — 工具调用记录 `TOOL <name> <outcome> user=<id>`。
### 4. 智能体编排
- **`SupervisorService`** — 路由(基于规则),决定计划与直接,运行执行循环,
处理确认/恢复,并综合最终答案。
- **`PlannerService`** — 通过 `.entity(PlanDto.class)` 将目标转换为类型化的 `PlanDto`; 每个步骤
携带其 `targetAgent` 和 `requiresConfirmation` 标志。
- **`PendingPlanStore`** — 保存暂停的计划(内存中)以 `conversationId` 为键进行跨请求恢复。
- **推理轨迹** — `ReasoningStepDto` 条目 (`agent`, `type`, `detail`, `timestamp`) 在
每个响应中返回。
### 5. MCP(模型上下文协议)
`McpConfig` 通过 MCP SSE 服务器 (`/sse` + `/mcp/message`) 公开相同的工具。外部 MCP
客户端(Claude Desktop,MCP Inspector)连接并使用工具 — **其** 模型成为智能体。
- **身份验证**:MCP 连接携带长寿命 JWT via `Authorization: Bearer`; `JwtAuthFilter`
解析用户,因此每个用户规则仍然适用。使用 `POST /auth/mcp-token` 铸造一个(有效期 30 天)。
- 工具名称全局唯一 (`rider_*` / `driver_*`) 以适应 MCP 的扁平命名空间。
---
## 本地运行
### 先决条件
- Java 21,Maven(包含包装器),带有 **PostGIS** 和 **pgvector** 扩展的 PostgreSQL,Redis
- OpenAI API 密钥
### 配置
机密信息从环境中读取 — **不要提交它们**:
```bash
export OPENAI_API_KEY=sk-...
```
`application.yaml` 引用 `${OPENAI_API_KEY}`。
### 启动
```bash
./mvnw spring-boot:run
```
> `spring.jpa.hibernate.ddl-auto=create-drop` — 数据库在每次重启时 **重新创建**,然后
> `data.sql` 播种演示数据。
### 创建测试乘客并与助手对话
```bash
# 1. 注册乘客(注册始终创建乘客)
curl -s -X POST localhost:8080/auth/signup -H 'Content-Type: application/json' \
-d '{"name":"Test Rider","email":"testrider@example.com","password":"Test@1234"}'
# 2. 登录 -> JWT 访问令牌(有效 10 分钟)
TOKEN=$(curl -s -X POST localhost:8080/auth/login -H 'Content-Type: application/json' \
-d '{"email":"testrider@example.com","password":"Test@1234"}' \
| sed -n 's/.*"accessToken":"\([^"]*\)".*/\1/p')
# 3. 聊天 — 响应包括 **推理** 跟踪记录
curl -s -X POST localhost:8080/ai/chat -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"message":"取消我最近的行程,然后给司机5星评分"}'
```
响应示例:
```json
{
"data": {
"reply": "我将:取消最近的行程。是否继续?(是/否)",
"conversationId": "user-43",
"reasoning": [
{ "agent": "PLANNER", "type": "PLAN", "detail": "具有 3 个步骤的计划:[...] },
{ "agent": "RIDER", "type": "DELEGATE", "detail": "RiderAgent 处理:..." },
{ "agent": "SUPERVISOR", "type": "CONFIRMATION", "detail": "等待确认:..." }
]
},
"error": null
}
```
在后续请求中用 **"yes"** 回复(相同的令牌 → 相同的 `conversationId`)以恢复计划。
### 使用纯 REST 驱动完整行程(无 AI)
助手自动执行的相同流程,直接完成 — 不涉及 LLM。 使用预先播种的测试帐户
(`testrider@uber.com` / `testdriver@uber.com`,密码 `Test@1234`):
```bash
# 乘客 + 司机访问令牌
RTOK=$(curl -s -X POST localhost:8080/auth/login -H 'Content-Type: application/json' \
-d '{"email":"testrider@uber.com","password":"Test@1234"}' | sed -n 's/.*"accessToken":"\([^"]*\)".*/\1/p')
DTOK=$(curl -s -X POST localhost:8080/auth/login -H 'Content-Type: application/json' \
-d '{"email":"testdriver@uber.com","password":"Test@1234"}' | sed -n 's/.*"accessToken":"\([^"]*\)".*/\1/p')
# 1. 乘客请求行程(返回行程请求 ID)
curl -s -X POST localhost:8080/rider/requestRide -H "Authorization: Bearer $RTOK" \
-H 'Content-Type: application/json' \
-d '{"pickupLocation":{"type":"Point","coordinates":[77.2090,28.6139]},
"dropOffLocation":{"type":"Point","coordinates":[77.2500,28.6500]},"paymentMethod":"WALLET"}'
# 2. 司机接受它 → 返回行程 ID + OTP
curl -s -X POST localhost:8080/drivers/acceptRide/1 -H "Authorization: Bearer $DTOK"
# 3. 司机使用乘客的 OTP 开始行程(创建 PENDING 支付)
curl -s -X POST localhost:8080/drivers/startRide/1 -H "Authorization: Bearer $DTOK" \
-H 'Content-Type: application/json' -d '{"otp":"<otp-from-step-2>"}'
# 4. 司机结束行程 → 费用确定,钱包结算(幂等且支持并发)
curl -s -X POST localhost:8080/drivers/endRide/1 -H "Authorization: Bearer $DTOK"
```
### 从 Claude Desktop 连接到 MCP
`~/Library/Application Support/Claude/claude_desktop_config.json`:
```json
{
"mcpServers": {
"intelliride": {
"command": "/opt/homebrew/bin/npx",
"args": ["-y", "mcp-remote", "http://localhost:8080/sse", "--transport", "sse-only",
"--header", "Authorization:${AUTH_HEADER}"],
"env": { "AUTH_HEADER": "Bearer <token from /auth/mcp-token>" }
}
}
}
```
使用 **30 天的 MCP 令牌** (`POST /auth/mcp-token`) 而不是 10 分钟的访问令牌,这样你就不需要
重启 Claude 时它过期。 需要 Node 18+。
---
## 关键端点
| 方法 | 路径 | 目的 |
|--------|------|---------|
| POST | `/auth/signup`, `/auth/login` | 身份验证(返回 JWT) |
| POST | `/auth/mcp-token` | 铸造 30 天的令牌用于 MCP 客户端 |
| POST | `/ai/chat` | 与智能体助手对话( `{ "message": "..." }` )— 返回 `reply` + `reasoning` |
| POST/GET | `/rider/*` | 乘客操作 — 请求/取消/评分,个人资料,行程([详情](#核心叫车后台的工作原理与AI层无关)) |
| POST/GET | `/drivers/*` | 司机操作 — 接受/开始/结束/取消/评分,个人资料,行程([详情](#核心叫车后台的工作原理与AI层无关)) |
| GET/POST | `/sse`, `/mcp/message` | MCP 服务器(SSE 传输) |
| GET | `/swagger-ui.html` | API 文档 |
`/ai/chat` 从 JWT 中的 `user-<id>` 派生出服务器端的 `conversationId` — 请求正文仅为
`{ "message": "..." }`。所有响应都由 `GlobalResponseHandler` 包装为 `{ "data": ..., "error": ... }`。
---
## 安全注意事项
- MCP 令牌长期有效(30 天) — 将其视为 API 密钥,并在公开服务器之前使其可撤销。
---
## 项目布局
```
src/main/java/com/flourish/intelliride/
├── agents/ 智能体,智能体请求,智能体结果,乘客智能体,司机智能体,支持智能体
├── configs/ AIConfig(ChatClient/内存/向量 bean),McpConfig,WebSecurityConfig,MapperConfig
├── controllers/ AuthController,乘客控制器,司机控制器,AIController
├── tools/ RiderTools,DriverTools ← 智能体工具(@Tool)
├── services/ SupervisorService,PlannerService,PendingPlanStore,RAGService + 领域服务
├── strategies/ 费用(默认/高峰)+ 司机匹配策略
├── security/ JwtAuthFilter,JWTService
├── advices/ GlobalResponseHandler,GlobalExceptionHandler
├── dtos/ ChatResponseDto,ReasoningStepDto,PlanDto,PlanStepDto,PendingPlanDto,...
├── entities/ + enums/ (AgentType,StepType,StepStatus,Role,…)
└── resources/
├── knowledge/ RAG 源文档(费用,钱包,取消,FAQ)
├── application.yaml
└── data.sql 演示种子数据
```
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器