feat: auth
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
## 1. 项目目标
|
||||
|
||||
本项目用于实现一个“多目标地点最优路线规划”服务。
|
||||
本项目用于实现一个“多目标地点最优路线规划 + 货物装载规划”服务。
|
||||
|
||||
核心能力如下:
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
- 根据策略选出最佳路线
|
||||
- 返回结构化 JSON,供前端直接消费
|
||||
- 在需要时生成高德 deep link
|
||||
- 接收区域和车牌号,获取待出货出货单与车辆容量
|
||||
- 根据针织/梭织规则选择可装载的出货单
|
||||
|
||||
当前阶段的目标不是构建一个复杂的调度系统,而是先交付一个可工作的第一版 Agent API。
|
||||
|
||||
@@ -30,25 +32,36 @@
|
||||
|
||||
- `main.py`
|
||||
- FastAPI 入口
|
||||
- 暴露 `/healthz` 和 `/route/plan`
|
||||
- 负责 HTTP 错误映射
|
||||
- 暴露 `/healthz`、`/route/plan` 和 `/load/plan`
|
||||
- 负责 Authorization 鉴权与 HTTP 错误映射
|
||||
- `schemas.py`
|
||||
- 定义请求和响应模型
|
||||
- 实现输入校验
|
||||
- `agent.py`
|
||||
- `agent/route_plan.py`
|
||||
- 路线规划 Agent
|
||||
- 负责 LLM、MCP、prompt、运行护栏、结果护栏
|
||||
- `agent/load_plan.py`
|
||||
- 装载规划 Agent
|
||||
- 负责装载模型、skills、工具接入与结构化输出
|
||||
- `agent/load_plan_tools.py`
|
||||
- 装载规划业务 API 工具
|
||||
- 当前提供车辆详情和未出货出货单查询
|
||||
- `skills/fabric-load-planning/SKILL.md`
|
||||
- 装载规划 skill
|
||||
- 定义面料判断、欠载与不混装规则
|
||||
- `.env`
|
||||
- 管理模型配置、MCP 配置和运行护栏配置
|
||||
|
||||
运行链路:
|
||||
|
||||
1. 前端请求进入 FastAPI
|
||||
2. Pydantic 校验请求结构
|
||||
3. 服务侧执行预护栏检查
|
||||
4. Agent 运行,并由 `system_prompt` 驱动模型调用高德 MCP 工具
|
||||
5. Agent 返回结构化结果
|
||||
6. 服务侧执行结果护栏检查
|
||||
7. 返回 JSON 给前端
|
||||
2. `/route/plan` 和 `/load/plan` 先通过 Authorization 鉴权
|
||||
3. Pydantic 校验请求结构
|
||||
4. 根据不同入口进入对应 Agent
|
||||
5. Agent 调用地图 MCP 或装载业务 API 工具
|
||||
6. Agent 返回结构化结果
|
||||
7. 服务侧执行错误映射
|
||||
8. 返回 JSON 给前端
|
||||
|
||||
## 4. 关键技术选型与决策依据
|
||||
|
||||
@@ -100,6 +113,22 @@
|
||||
- `maps_schema_navi`
|
||||
- `maps_weather`
|
||||
|
||||
### 5.2 装载规划 Agent 接入
|
||||
|
||||
- 装载规划 Agent 使用独立模型配置:
|
||||
- `LOAD_PLAN_BASE_URL`
|
||||
- `LOAD_PLAN_API_KEY`
|
||||
- `LOAD_PLAN_MODEL`
|
||||
- `LOAD_PLAN_REQUEST_TIMEOUT_SECONDS`
|
||||
- 装载规划业务 API 使用独立配置:
|
||||
- `LOAD_PLAN_API_HOST`
|
||||
- `LOAD_PLAN_AGENT_ACCESS_KEY`
|
||||
- `LOAD_PLAN_API_TIMEOUT_SECONDS`
|
||||
- 当前装载规划已接入两个业务工具:
|
||||
- 按车牌查询车辆详情
|
||||
- 按区域查询未出货出货单
|
||||
- 装载规则通过 `pydantic-ai-skills` 从本地 `skills/` 目录注入
|
||||
|
||||
### 5.2 Prompt 组织方式
|
||||
|
||||
`system_prompt` 中定义了以下行为约束:
|
||||
@@ -211,6 +240,9 @@
|
||||
- 超限请求可返回 422,并中止模型执行
|
||||
- 已改为严格 deep-link 模式:成功结果必须包含 deep link,否则直接失败
|
||||
- 已增加前置点位解析与 POI 校验阶段,缺少 `poi_id` 或命中模糊时直接失败
|
||||
- 已增加 `/load/plan` HTTP 入口
|
||||
- 已接入装载规划 Agent、业务 API tools 与本地 skills
|
||||
- 已验证 `merchant_id + area + license_plate` 可返回装载规划结果
|
||||
|
||||
当前尚未完成:
|
||||
|
||||
@@ -219,6 +251,7 @@
|
||||
- 深链策略的更强一致性校验
|
||||
- 自动化测试
|
||||
- 前端展示层 HTML 输出
|
||||
- 多出货单组合场景的稳定性增强
|
||||
|
||||
## 8. 关键取舍
|
||||
|
||||
|
||||
160
docs/api_v2_agent_api.md
Normal file
160
docs/api_v2_agent_api.md
Normal file
@@ -0,0 +1,160 @@
|
||||
# API v2 Agent 接口文档
|
||||
|
||||
本文档面向 AI Agent 及外部自动化调用方,描述 `/api/v2/ai/` 前缀下的所有接口。
|
||||
|
||||
## 基本约定
|
||||
|
||||
- Base URL: `/api/v2/ai`
|
||||
- 认证:所有接口使用固定 API Key,通过 `Authorization` 请求头直接传入(无 `Bearer` 前缀)
|
||||
- 多商户隔离:每个接口均需传入 `merchant_id` 查询参数,后端以此确定数据范围
|
||||
|
||||
### 认证方式
|
||||
|
||||
```
|
||||
Authorization: <AGENT_ACCESS_KEY>
|
||||
```
|
||||
|
||||
`AGENT_ACCESS_KEY` 由后端部署时通过同名环境变量配置。未配置时所有请求均返回 `401`。
|
||||
|
||||
---
|
||||
|
||||
## 数据结构
|
||||
|
||||
### TransportVehicle
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"merchant_id": 10,
|
||||
"name": "大卡车",
|
||||
"license_plate": "粤A12345",
|
||||
"material_capacities": [
|
||||
{"id": 1, "material_name": "坯布", "capacity": 500},
|
||||
{"id": 2, "material_name": "成品", "capacity": 300}
|
||||
],
|
||||
"created_at": "2026-04-01T08:00:00+08:00",
|
||||
"updated_at": "2026-04-01T08:00:00+08:00"
|
||||
}
|
||||
```
|
||||
|
||||
### Shipment(出货单)
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 100,
|
||||
"merchant_id": 10,
|
||||
"customer": 5,
|
||||
"customer_name": "客户A",
|
||||
"shipment_date": "2026-04-14",
|
||||
"address": "广州市天河区",
|
||||
"contact_name": "张三",
|
||||
"contact_phone": "13800000000",
|
||||
"area": "华东",
|
||||
"remark": "备注",
|
||||
"status": "pending",
|
||||
"status_display": "待出货",
|
||||
"external_id": null,
|
||||
"geo_coordinates": {"lat": 23.1291, "lng": 113.2644},
|
||||
"delivery": null,
|
||||
"sales_items": [
|
||||
{
|
||||
"id": 201,
|
||||
"name": "销售品甲",
|
||||
"quantity": "50.00",
|
||||
"unit": 1,
|
||||
"unit_display": "件",
|
||||
"position": "A-01",
|
||||
"remark": "",
|
||||
"printing_job_id": 88,
|
||||
"printing_job_width": "150cm"
|
||||
},
|
||||
{
|
||||
"id": 202,
|
||||
"name": "销售品乙",
|
||||
"quantity": "10.00",
|
||||
"unit": 1,
|
||||
"unit_display": "件",
|
||||
"position": "",
|
||||
"remark": "",
|
||||
"printing_job_id": null,
|
||||
"printing_job_width": null
|
||||
}
|
||||
],
|
||||
"created_at": "2026-04-14T10:00:00+08:00",
|
||||
"updated_at": "2026-04-14T10:00:00+08:00"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 运输车辆列表
|
||||
|
||||
- URL: `/api/v2/ai/transport-vehicles/`
|
||||
- Method: `GET`
|
||||
|
||||
查询参数:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `merchant_id` | int | 是 | 商户 ID |
|
||||
| `limit` | int | 否 | 分页每页数量(默认由服务端决定) |
|
||||
| `offset` | int | 否 | 分页偏移量 |
|
||||
|
||||
响应为分页结构,`results` 中每条为 `TransportVehicle`,包含该车辆的所有物料容量。
|
||||
|
||||
说明:
|
||||
|
||||
- `material_capacities` 表示该车辆对不同物料的最大装载量(单位:条)
|
||||
- 只返回 `merchant_id` 对应商户的车辆
|
||||
- `VehicleType`(旧版车辆类型字典)和 `VehicleTransportRecord`(旧版司机车次记录)是已废弃的模型,不在此接口返回
|
||||
|
||||
## 运输车辆详情
|
||||
|
||||
- URL: `/api/v2/ai/transport-vehicles/<license_plate>/`
|
||||
- Method: `GET`
|
||||
|
||||
路径参数:
|
||||
|
||||
| 参数 | 说明 |
|
||||
|------|------|
|
||||
| `license_plate` | 车牌号码(URL 编码,如 `粤A12345` → `%E7%B2%A4A12345`) |
|
||||
|
||||
查询参数:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `merchant_id` | int | 是 | 商户 ID |
|
||||
|
||||
说明:
|
||||
|
||||
- 同一商户内 `license_plate` 唯一(数据库 `unique_together: merchant + license_plate`),因此 `merchant_id + license_plate` 可精确定位一辆车
|
||||
- 不同商户可能存在相同车牌号,`merchant_id` 参数是必须的
|
||||
- 车辆不存在时返回 `404`
|
||||
|
||||
成功响应:`TransportVehicle`
|
||||
|
||||
---
|
||||
|
||||
## 未出货出货单列表
|
||||
|
||||
- URL: `/api/v2/ai/shipments/unshipped/`
|
||||
- Method: `GET`
|
||||
|
||||
查询参数:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `merchant_id` | int | 是 | 商户 ID |
|
||||
| `area` | string | 是 | 地区筛选(精确匹配) |
|
||||
| `limit` | int | 否 | 分页每页数量 |
|
||||
| `offset` | int | 否 | 分页偏移量 |
|
||||
|
||||
说明:
|
||||
|
||||
- "未出货"定义:`delivery` 为空,即尚未关联送货单
|
||||
- 结果按 `created_at` 降序排列
|
||||
- 只返回 `merchant_id` 对应商户的数据
|
||||
- `sales_items` 包含该出货单的所有销售品(软删除的条目自动排除)
|
||||
- `sales_items[].printing_job_width`:对应 `PrintingJob → PrintingOrder.width`;若无关联印染任务则为 `null`
|
||||
|
||||
响应为分页结构,`results` 中每条为 `Shipment`。
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
- `GET /healthz`
|
||||
- `POST /route/plan`
|
||||
- `POST /load/plan`
|
||||
|
||||
默认本地开发地址示例:
|
||||
|
||||
@@ -17,7 +18,24 @@
|
||||
http://127.0.0.1:8000
|
||||
```
|
||||
|
||||
### 2.1 CORS
|
||||
### 2.1 Authorization
|
||||
|
||||
除 `GET /healthz` 外,当前业务接口都需要通过 `Authorization` 请求头进行鉴权。
|
||||
|
||||
请求头格式:
|
||||
|
||||
```text
|
||||
Authorization: <AGENT_HTTP_AUTH_KEY>
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- 不使用 `Bearer` 前缀
|
||||
- 服务端从 `.env` 中读取 `AGENT_HTTP_AUTH_KEY`
|
||||
- 如果服务端未配置该值,请求会返回 `503`
|
||||
- 如果请求头缺失或值不匹配,请求会返回 `401`
|
||||
|
||||
### 2.2 CORS
|
||||
|
||||
当前服务已启用 CORS。
|
||||
|
||||
@@ -48,6 +66,7 @@ GET /healthz
|
||||
|
||||
```http
|
||||
POST /route/plan
|
||||
Authorization: <AGENT_HTTP_AUTH_KEY>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
@@ -444,3 +463,86 @@ Content-Type: application/json
|
||||
- 前端确认 deep link 的按钮交互形式
|
||||
- 前后端统一 422 错误展示文案
|
||||
- 后续若输出结构调整,需要同步更新本文档
|
||||
|
||||
## 11. 装载规划接口
|
||||
|
||||
### 11.1 请求
|
||||
|
||||
```http
|
||||
POST /load/plan
|
||||
Authorization: <AGENT_HTTP_AUTH_KEY>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
### 11.2 请求体
|
||||
|
||||
```json
|
||||
{
|
||||
"merchant_id": 1,
|
||||
"area": "中大",
|
||||
"license_plate": "粤A4Y0Y5"
|
||||
}
|
||||
```
|
||||
|
||||
### 11.3 请求字段说明
|
||||
|
||||
- `merchant_id`
|
||||
- 必填
|
||||
- 商户 ID
|
||||
- `area`
|
||||
- 必填
|
||||
- 按区域筛选待出货出货单,精确匹配
|
||||
- `license_plate`
|
||||
- 必填
|
||||
- 目标运输车辆车牌号
|
||||
|
||||
### 11.4 成功响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"license_plate": "粤A4Y0Y5",
|
||||
"selected_shipment_ids": [13],
|
||||
"summary": "待出货出货单共1个,出货单ID13的销售品幅宽均为170cm,判定为针织面料,包含2条销售品,未超过车辆针织最大装载容量120条,因此选择该出货单进行装载。",
|
||||
"warnings": [
|
||||
"当前装载条数为2条,未达到针织面料最大运输容量120条,属于欠载方案。"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 11.5 响应字段说明
|
||||
|
||||
- `success`
|
||||
- 是否成功完成装载规划
|
||||
- `license_plate`
|
||||
- 本次规划对应的车牌号
|
||||
- `selected_shipment_ids`
|
||||
- 被选中的出货单 ID 列表
|
||||
- `summary`
|
||||
- 可直接展示给用户的装载说明
|
||||
- `warnings`
|
||||
- 欠载、字段缺失、无法判定面料等提示信息
|
||||
|
||||
### 11.6 错误语义
|
||||
|
||||
- `401`
|
||||
- `Authorization` 请求头缺失或值不正确
|
||||
- `422`
|
||||
- 装载规则护栏错误或输入不满足规划要求
|
||||
- `503`
|
||||
- 装载模型配置、业务 API 配置或服务端鉴权配置缺失
|
||||
- `502`
|
||||
- 上游业务 API 返回错误,例如车辆不存在或接口请求失败
|
||||
- `504`
|
||||
- 装载模型或业务 API 超时
|
||||
- `500`
|
||||
- 未预期的内部错误
|
||||
|
||||
### 11.7 当前行为说明
|
||||
|
||||
- Agent 会先查询指定 `area` 的未出货出货单,再查询指定车牌的车辆容量
|
||||
- 面料类型仅通过 `sales_items[].printing_job_width` 判断
|
||||
- 装载量按销售品条数计算,不按 `quantity` 长度或米数计算
|
||||
- 默认不允许混装针织和梭织
|
||||
- 优先选择不超载且尽量接近最大容量的方案
|
||||
- 如果只能欠载,会在 `warnings` 中明确提示
|
||||
|
||||
Reference in New Issue
Block a user