init
This commit is contained in:
46
docs/2026-04-13_summary.md
Normal file
46
docs/2026-04-13_summary.md
Normal file
@@ -0,0 +1,46 @@
|
||||
## 背景
|
||||
|
||||
- 新建 `geo-agent` 项目,使用 `uv init` 初始化。
|
||||
- 目标是基于 `pydantic_ai`、FastAPI 和高德远程 MCP 服务实现多目标地点最优路线规划 Agent。
|
||||
- 当前开发流程要求先搭框架,再逐步补齐 MCP 接入与业务实现。
|
||||
|
||||
## 今日完成
|
||||
|
||||
- 检查并确认项目 Python 版本为 3.14.2,满足 `pydantic-ai` 的版本要求。
|
||||
- 创建基础项目结构,加入 FastAPI 入口、Pydantic 请求/响应模型、Agent 骨架和环境变量模板。
|
||||
- 安装项目依赖并修正 `pydantic-ai` 当前版本的接入方式。
|
||||
- 修复运行时报错:
|
||||
- `OpenAIModel` 不接受 `openai_client=` 参数,改为 `OpenAIProvider`。
|
||||
- `Agent` 改为使用 `toolsets` 挂载 MCP。
|
||||
- `result_type` 改为 `output_type`。
|
||||
- `run_mcp_servers()` 改为 `async with agent:` 的当前推荐写法。
|
||||
- 增加 `/healthz` 健康检查接口。
|
||||
- 为高德远程 MCP 增加显式配置项:URL、传输类型、可选认证头。
|
||||
|
||||
## 当前状态
|
||||
|
||||
- FastAPI 应用可正常导入和启动。
|
||||
- `/healthz` 返回 200。
|
||||
- 高德远程 MCP 已配置到 `.env`,传输方式确认为 `streamable_http`。
|
||||
- 已验证可连通高德 MCP,并成功枚举出 15 个工具,包括 `maps_geo`、`maps_text_search`、`maps_search_detail`、`maps_direction_driving`、`maps_distance`、`maps_schema_personal_map`、`maps_schema_navi`、`maps_weather` 等。
|
||||
- `system_prompt` 已注入到 Agent。
|
||||
- 已实现第一版 prompt-first 路线规划流程:由 system prompt 驱动模型使用高德 MCP 工具完成点位解析、逐段算路、候选路线比较和结构化输出。
|
||||
- 已增加代码护栏:
|
||||
- 请求模型语义校验:固定起点必须提供起点地址;终点不能同时出现在途经点;空字符串会被标准化处理。
|
||||
- `.env` 中新增 `ROUTE_MAX_PERMUTATIONS=20`,作为服务侧候选顺序硬上限。
|
||||
- 当 `n!` 超过上限时,接口直接返回 422,不进入模型执行。
|
||||
- 对 Agent 输出做结构校验:`origin_mode` 必须一致、`resolved_origin`/`resolved_destination` 角色必须正确、`best_route` 必须对应 `candidates` 之一。
|
||||
- 已完成真实请求验证:
|
||||
- 单个途经点请求返回 200,并产生结构化路线规划结果。
|
||||
- 4 个途经点请求因 24 个排列超过 20 的限制而返回 422。
|
||||
- 已新增项目设计文档 `docs/agent_design.md`。
|
||||
- 已新增前端对接文档 `docs/frontend_api.md`。
|
||||
- 已补充上游 timeout 配置和 504 错误映射,避免外部超时被混淆为普通 500。
|
||||
- 已修正 `stops` 非空校验,并更新前端文档中 `deep_links` 与 `summary` 的语义边界说明。
|
||||
|
||||
## 下一步建议
|
||||
|
||||
- 补充高德远程 MCP 的真实 URL。
|
||||
- 确认 MCP 传输协议是 `streamable_http` 还是 `sse`。
|
||||
- 如果远程服务需要认证,补充自定义请求头。
|
||||
- 在此基础上实现地址解析、候选路线枚举、逐段路线计算和 deep link 生成。
|
||||
249
docs/agent_design.md
Normal file
249
docs/agent_design.md
Normal file
@@ -0,0 +1,249 @@
|
||||
# Geo Agent 设计文档
|
||||
|
||||
## 1. 项目目标
|
||||
|
||||
本项目用于实现一个“多目标地点最优路线规划”服务。
|
||||
|
||||
核心能力如下:
|
||||
|
||||
- 接收一个固定起点或当前位置语义的路线规划请求
|
||||
- 解析终点和多个途经点
|
||||
- 对候选送货顺序进行比较
|
||||
- 逐段调用高德地图 MCP 工具计算距离和时长
|
||||
- 根据策略选出最佳路线
|
||||
- 返回结构化 JSON,供前端直接消费
|
||||
- 在需要时生成高德 deep link
|
||||
|
||||
当前阶段的目标不是构建一个复杂的调度系统,而是先交付一个可工作的第一版 Agent API。
|
||||
|
||||
## 2. 设计原则
|
||||
|
||||
- Prompt-first:第一版以 `system_prompt` 驱动整体执行流程,优先保留灵活性
|
||||
- Strong output contract:最终输出必须符合固定的 Pydantic 结构
|
||||
- Guardrail-driven:对输入、执行规模和输出一致性增加代码护栏
|
||||
- Stateless:每次请求独立完成,不依赖长期记忆
|
||||
- Tool-grounded:坐标、POI、距离、时长、deep link 必须来自地图工具,而不是模型臆造
|
||||
|
||||
## 3. 架构概览
|
||||
|
||||
当前工程结构:
|
||||
|
||||
- `main.py`
|
||||
- FastAPI 入口
|
||||
- 暴露 `/healthz` 和 `/route/plan`
|
||||
- 负责 HTTP 错误映射
|
||||
- `schemas.py`
|
||||
- 定义请求和响应模型
|
||||
- 实现输入校验
|
||||
- `agent.py`
|
||||
- 负责 LLM、MCP、prompt、运行护栏、结果护栏
|
||||
- `.env`
|
||||
- 管理模型配置、MCP 配置和运行护栏配置
|
||||
|
||||
运行链路:
|
||||
|
||||
1. 前端请求进入 FastAPI
|
||||
2. Pydantic 校验请求结构
|
||||
3. 服务侧执行预护栏检查
|
||||
4. Agent 运行,并由 `system_prompt` 驱动模型调用高德 MCP 工具
|
||||
5. Agent 返回结构化结果
|
||||
6. 服务侧执行结果护栏检查
|
||||
7. 返回 JSON 给前端
|
||||
|
||||
## 4. 关键技术选型与决策依据
|
||||
|
||||
### 4.1 为什么使用 pydantic_ai
|
||||
|
||||
- 原生支持结构化输出
|
||||
- 可以直接挂接 MCP toolset
|
||||
- 非常适合“模型编排 + 强类型结果”场景
|
||||
- 与当前项目希望保留 prompt 灵活性的目标一致
|
||||
|
||||
### 4.2 为什么第一版采用 prompt-first
|
||||
|
||||
这次的核心业务不是纯算法问题,而是“模型理解任务 + 使用地图工具求事实 + 按规则组织返回”。
|
||||
|
||||
第一版采用 prompt-first 的原因:
|
||||
|
||||
- 业务规则还在收敛阶段,prompt 更容易快速调整
|
||||
- 高德 MCP 工具已经能直接提供底层能力,没有必要在第一版就再包一层 Python tool abstraction
|
||||
- 当前最重要的是先建立“可靠结果 + 清晰约束 + 可持续迭代”的骨架
|
||||
|
||||
### 4.3 为什么不是纯 prompt-only
|
||||
|
||||
虽然第一版以 `system_prompt` 为核心,但没有把全部控制权放给模型。原因是以下几类问题更适合代码层兜底:
|
||||
|
||||
- 输入明显非法
|
||||
- 候选排列爆炸
|
||||
- 输出结构不一致
|
||||
- 运行配置错误
|
||||
|
||||
因此最终方案是:
|
||||
|
||||
- Prompt 负责业务编排
|
||||
- Pydantic 负责结构约束
|
||||
- Python 代码负责运行护栏
|
||||
|
||||
## 5. 当前实现策略
|
||||
|
||||
### 5.1 LLM 和 MCP 接入
|
||||
|
||||
- LLM:豆包 Ark OpenAI-compatible 接口
|
||||
- MCP:高德远程 `streamable_http`
|
||||
- MCP 已验证可连通,当前可用工具包括:
|
||||
- `maps_geo`
|
||||
- `maps_text_search`
|
||||
- `maps_search_detail`
|
||||
- `maps_direction_driving`
|
||||
- `maps_distance`
|
||||
- `maps_schema_personal_map`
|
||||
- `maps_schema_navi`
|
||||
- `maps_weather`
|
||||
|
||||
### 5.2 Prompt 组织方式
|
||||
|
||||
`system_prompt` 中定义了以下行为约束:
|
||||
|
||||
- 必须显式调用地图工具
|
||||
- 必须逐段计算驾车路线
|
||||
- 必须区分 fixed origin 和 current location
|
||||
- 必须按 `route_strategy` 做选择
|
||||
- 不得伪造坐标、POI、距离、时长和 deep link
|
||||
- 必须输出结构化结果
|
||||
|
||||
在实际运行时,还会再注入一次“用户级 prompt”,其中包含:
|
||||
|
||||
- 当前请求 JSON
|
||||
- 本次候选顺序的硬上限
|
||||
- 对起点模式的额外提醒
|
||||
|
||||
这样做的目的是把“长期规则”和“本次任务上下文”拆开,减少 prompt 污染。
|
||||
|
||||
## 6. 已实现的代码护栏
|
||||
|
||||
### 6.1 输入护栏
|
||||
|
||||
在 `schemas.py` 中已经实现:
|
||||
|
||||
- 清理空白字符串
|
||||
- `stop.address` 不能为空
|
||||
- `stops` 列表不能为空
|
||||
- `destination_address` 不能为空
|
||||
- `origin_mode=fixed` 时必须提供 `origin_address`
|
||||
- 终点地址不能同时出现在 `stops`
|
||||
- `max_permutations` 如果传入,必须大于 0
|
||||
|
||||
### 6.2 执行规模护栏
|
||||
|
||||
在 `agent.py` 中已经实现:
|
||||
|
||||
- 从 `.env` 读取 `ROUTE_MAX_PERMUTATIONS`
|
||||
- 当前默认值是 `20`
|
||||
- 请求可以传更小值,但不能超过服务配置上限
|
||||
- 在模型执行前,根据 `stops` 数量计算排列数 $n!$
|
||||
- 如果排列数超过限制,直接报错并返回 422
|
||||
|
||||
当前策略是“超限直接拒绝”,暂不做启发式近似。
|
||||
|
||||
### 6.3 结果护栏
|
||||
|
||||
在 `agent.py` 中已经实现:
|
||||
|
||||
- 输出 `origin_mode` 必须和请求一致
|
||||
- `resolved_destination.role` 必须是 `destination`
|
||||
- `resolved_stops` 数量必须和输入一致
|
||||
- 所有 `resolved_stops.role` 都必须是 `stop`
|
||||
- `origin_mode=fixed` 时必须返回 `resolved_origin`
|
||||
- `origin_mode=current_location` 时禁止返回固定 `resolved_origin`
|
||||
- 成功结果必须至少有一个 candidate
|
||||
- `best_route` 必须能在 `candidates` 中找到对应项
|
||||
|
||||
### 6.4 配置护栏
|
||||
|
||||
已实现以下配置检查:
|
||||
|
||||
- 必须提供模型 URL、API key、模型名
|
||||
- MCP URL 必须存在
|
||||
- MCP transport 仅允许 `streamable_http`、`http`、`sse`
|
||||
- 自定义 MCP 认证头必须成对提供
|
||||
- `ROUTE_MAX_PERMUTATIONS` 必须是正整数
|
||||
- 模型和 MCP 的 timeout 参数必须是正数
|
||||
|
||||
### 6.5 超时护栏
|
||||
|
||||
已实现以下超时相关策略:
|
||||
|
||||
- 模型请求超时通过 `ARK_REQUEST_TIMEOUT_SECONDS` 配置
|
||||
- MCP 连接超时通过 `AMAP_MCP_TIMEOUT_SECONDS` 配置
|
||||
- MCP 读取超时通过 `AMAP_MCP_READ_TIMEOUT_SECONDS` 配置
|
||||
- 上游 timeout 会被映射为 HTTP 504,而不是泛化成 500
|
||||
|
||||
## 7. 当前已实施进展
|
||||
|
||||
当前已完成:
|
||||
|
||||
- FastAPI 服务可启动
|
||||
- `/healthz` 正常返回 200
|
||||
- `/route/plan` 已打通真实调用链
|
||||
- 高德远程 MCP 连通性已验证
|
||||
- 单个途经点请求可成功返回结构化结果
|
||||
- 超限请求可返回 422,并中止模型执行
|
||||
|
||||
当前尚未完成:
|
||||
|
||||
- 把文档中的每一步拆成显式 Python 服务函数
|
||||
- `current_location` 场景的更细粒度行为控制
|
||||
- 深链策略的更强一致性校验
|
||||
- 自动化测试
|
||||
- 前端展示层 HTML 输出
|
||||
|
||||
## 8. 关键取舍
|
||||
|
||||
### 8.1 已做取舍
|
||||
|
||||
- 选择 prompt-first,而不是一开始就把所有流程写死在 Python 中
|
||||
- 选择保留高德 MCP 原生工具,而不是先做二次工具封装
|
||||
- 选择超限直接报错,而不是第一版就引入启发式近似搜索
|
||||
- 选择结构校验和运行护栏优先,而不是先追求功能面最大化
|
||||
|
||||
### 8.2 这套取舍的收益
|
||||
|
||||
- 业务规则可快速迭代
|
||||
- 系统仍然保有基本可控性
|
||||
- 接口结构对前端稳定
|
||||
- 便于后续逐步从 prompt-first 演进到“prompt + service function”混合架构
|
||||
|
||||
### 8.3 当前代价
|
||||
|
||||
- 模型仍然承担了较多流程理解责任
|
||||
- 某些复杂场景的结果稳定性暂时依赖 prompt 质量
|
||||
- 当前没有启发式优化,超限就会拒绝
|
||||
- 当前上游调用仍然依赖外部服务稳定性,但 timeout 已具备可调能力和明确错误语义
|
||||
|
||||
## 9. 已知限制
|
||||
|
||||
- 当前版本的最优路线能力仍然高度依赖 LLM 遵守 prompt
|
||||
- 当前对 `best_route` 的一致性校验是结构级护栏,不是全量数学证明
|
||||
- `current_location` 模式尚未做专门增强
|
||||
- `need_html` 目前尚未实现独立展示层
|
||||
- 没有缓存机制,请求成本与工具调用次数直接相关
|
||||
|
||||
## 10. 下一阶段 TODO
|
||||
|
||||
- 为 `current_location` 模式增加专门提示和更严格结果校验
|
||||
- 对 `deep_link_mode` 增加更明确的输出校验规则
|
||||
- 追加自动化测试,覆盖成功路径和失败路径
|
||||
- 将部分高频子流程下沉为显式服务函数
|
||||
- 评估是否引入启发式近邻策略处理超限请求
|
||||
- 增加请求日志和运行观测能力
|
||||
- 增加 README 中的启动与调试说明
|
||||
|
||||
## 11. 文档维护建议
|
||||
|
||||
后续如果发生以下变更,应同步更新本文档:
|
||||
|
||||
- Prompt 主策略变化
|
||||
- 输出结构变化
|
||||
- 错误语义变化
|
||||
- 护栏策略变化
|
||||
- MCP 服务配置方式变化
|
||||
431
docs/frontend_api.md
Normal file
431
docs/frontend_api.md
Normal file
@@ -0,0 +1,431 @@
|
||||
# Geo Agent API 对接文档
|
||||
|
||||
## 1. 文档目的
|
||||
|
||||
本文档面向前端对接,说明当前可用接口、请求结构、响应结构、错误语义和对接建议。
|
||||
|
||||
## 2. 服务概览
|
||||
|
||||
当前服务提供两个 HTTP 接口:
|
||||
|
||||
- `GET /healthz`
|
||||
- `POST /route/plan`
|
||||
|
||||
默认本地开发地址示例:
|
||||
|
||||
```text
|
||||
http://127.0.0.1:8000
|
||||
```
|
||||
|
||||
## 3. 健康检查
|
||||
|
||||
### 3.1 请求
|
||||
|
||||
```http
|
||||
GET /healthz
|
||||
```
|
||||
|
||||
### 3.2 成功响应
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok"
|
||||
}
|
||||
```
|
||||
|
||||
## 4. 路线规划接口
|
||||
|
||||
### 4.1 请求
|
||||
|
||||
```http
|
||||
POST /route/plan
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
### 4.2 请求体
|
||||
|
||||
```json
|
||||
{
|
||||
"task_name": "multi-destination-route-planning",
|
||||
"origin_mode": "fixed",
|
||||
"origin_name": "北京站",
|
||||
"origin_address": "北京站",
|
||||
"origin_city": "北京市",
|
||||
"destination_name": "天安门",
|
||||
"destination_address": "天安门",
|
||||
"destination_city": "北京市",
|
||||
"stops": [
|
||||
{
|
||||
"name": "王府井",
|
||||
"address": "王府井",
|
||||
"city": "北京市",
|
||||
"contact": null
|
||||
}
|
||||
],
|
||||
"route_strategy": "shortest_distance",
|
||||
"transport_mode": "driving",
|
||||
"need_deep_link": true,
|
||||
"deep_link_mode": "auto",
|
||||
"need_html": false,
|
||||
"max_permutations": 10
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 请求字段说明
|
||||
|
||||
- `task_name`
|
||||
- 可选
|
||||
- 默认值为 `multi-destination-route-planning`
|
||||
- `origin_mode`
|
||||
- 必填
|
||||
- 可选值:`fixed`、`current_location`
|
||||
- `origin_name`
|
||||
- 可选
|
||||
- 起点展示名称
|
||||
- `origin_address`
|
||||
- `origin_mode=fixed` 时必填
|
||||
- `origin_city`
|
||||
- 可选
|
||||
- `destination_name`
|
||||
- 可选
|
||||
- `destination_address`
|
||||
- 必填
|
||||
- `destination_city`
|
||||
- 可选
|
||||
- `stops`
|
||||
- 必填
|
||||
- 至少 1 个元素
|
||||
- `route_strategy`
|
||||
- 可选
|
||||
- 可选值:`shortest_distance`、`fastest_time`、`balanced`
|
||||
- `transport_mode`
|
||||
- 当前固定为 `driving`
|
||||
- `need_deep_link`
|
||||
- 可选
|
||||
- 是否需要生成 deep link
|
||||
- `deep_link_mode`
|
||||
- 可选
|
||||
- 可选值:`personal_map`、`route_plan`、`auto`
|
||||
- `need_html`
|
||||
- 可选
|
||||
- 当前建议始终传 `false`
|
||||
- `max_permutations`
|
||||
- 可选
|
||||
- 本次请求希望允许的候选上限
|
||||
- 不能超过服务端上限
|
||||
|
||||
## 5. 成功响应结构
|
||||
|
||||
### 5.1 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"origin_mode": "fixed",
|
||||
"resolved_origin": {
|
||||
"role": "origin",
|
||||
"input_name": null,
|
||||
"input_address": "北京站",
|
||||
"resolved_name": "北京站",
|
||||
"city": "北京市",
|
||||
"district": "东城区",
|
||||
"location": "116.427354,39.902830",
|
||||
"lon": 116.427354,
|
||||
"lat": 39.90283,
|
||||
"poi_id": null,
|
||||
"source": "geo",
|
||||
"confidence_note": "地址解析高置信"
|
||||
},
|
||||
"resolved_destination": {
|
||||
"role": "destination",
|
||||
"input_name": null,
|
||||
"input_address": "天安门",
|
||||
"resolved_name": "天安门",
|
||||
"city": "北京市",
|
||||
"district": "东城区",
|
||||
"location": "116.397463,39.909187",
|
||||
"lon": 116.397463,
|
||||
"lat": 39.909187,
|
||||
"poi_id": null,
|
||||
"source": "geo",
|
||||
"confidence_note": "地址解析高置信"
|
||||
},
|
||||
"resolved_stops": [
|
||||
{
|
||||
"role": "stop",
|
||||
"input_name": null,
|
||||
"input_address": "王府井",
|
||||
"resolved_name": "王府井",
|
||||
"city": "北京市",
|
||||
"district": "东城区",
|
||||
"location": "116.412422,39.908966",
|
||||
"lon": 116.412422,
|
||||
"lat": 39.908966,
|
||||
"poi_id": "B000A8WS91",
|
||||
"source": "search_detail",
|
||||
"confidence_note": "北京市内热点地名,POI查询高置信"
|
||||
}
|
||||
],
|
||||
"candidates": [
|
||||
{
|
||||
"stop_order_labels": ["王府井"],
|
||||
"full_order_labels": ["北京站", "王府井", "天安门"],
|
||||
"legs": [
|
||||
{
|
||||
"from_label": "北京站",
|
||||
"to_label": "王府井",
|
||||
"origin_location": "116.427354,39.902830",
|
||||
"destination_location": "116.412422,39.908966",
|
||||
"distance_m": 3014,
|
||||
"duration_s": 845
|
||||
},
|
||||
{
|
||||
"from_label": "王府井",
|
||||
"to_label": "天安门",
|
||||
"origin_location": "116.412422,39.908966",
|
||||
"destination_location": "116.397463,39.909187",
|
||||
"distance_m": 2858,
|
||||
"duration_s": 1158
|
||||
}
|
||||
],
|
||||
"total_distance_m": 5872,
|
||||
"total_duration_s": 2003,
|
||||
"ranking_reason": "仅有的可行路线,总距离最短"
|
||||
}
|
||||
],
|
||||
"best_route": {
|
||||
"stop_order_labels": ["王府井"],
|
||||
"full_order_labels": ["北京站", "王府井", "天安门"],
|
||||
"legs": [
|
||||
{
|
||||
"from_label": "北京站",
|
||||
"to_label": "王府井",
|
||||
"origin_location": "116.427354,39.902830",
|
||||
"destination_location": "116.412422,39.908966",
|
||||
"distance_m": 3014,
|
||||
"duration_s": 845
|
||||
},
|
||||
{
|
||||
"from_label": "王府井",
|
||||
"to_label": "天安门",
|
||||
"origin_location": "116.412422,39.908966",
|
||||
"destination_location": "116.397463,39.909187",
|
||||
"distance_m": 2858,
|
||||
"duration_s": 1158
|
||||
}
|
||||
],
|
||||
"total_distance_m": 5872,
|
||||
"total_duration_s": 2003,
|
||||
"ranking_reason": "仅有的可行路线,总距离最短"
|
||||
},
|
||||
"deep_links": {
|
||||
"personal_map": null,
|
||||
"android_route_plan": "androidamap://route?...",
|
||||
"ios_route_plan": "iosamap://route?..."
|
||||
},
|
||||
"summary": "本次规划从北京站出发,途经王府井,最终到达天安门,总距离约5.87公里,总耗时约33分钟,符合最短距离策略要求。",
|
||||
"warnings": [
|
||||
"路线时长受实时交通状况影响,实际行驶可能存在偏差",
|
||||
"若起点为当前位置,最优路线可能随定位变化调整"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 顶层字段说明
|
||||
|
||||
- `success`
|
||||
- 是否成功生成路线规划结果
|
||||
- `origin_mode`
|
||||
- 与请求保持一致
|
||||
- `resolved_origin`
|
||||
- `origin_mode=fixed` 时通常不为 `null`
|
||||
- `origin_mode=current_location` 时通常为 `null`
|
||||
- `resolved_destination`
|
||||
- 终点解析结果
|
||||
- `resolved_stops`
|
||||
- 所有途经点解析结果
|
||||
- `candidates`
|
||||
- 所有候选路线
|
||||
- `best_route`
|
||||
- 被选中的最佳路线
|
||||
- `deep_links`
|
||||
- 给前端做按钮跳转使用
|
||||
- 这是唯一应被前端当作链接处理的字段
|
||||
- `summary`
|
||||
- 可直接展示给用户的简要说明
|
||||
- 这是纯展示文案,不是结构化链接字段,也不应被前端解析为跳转地址
|
||||
- `warnings`
|
||||
- 风险提示和降级说明,前端建议展示
|
||||
|
||||
## 6. 关键嵌套结构说明
|
||||
|
||||
### 6.1 ResolvedPoint
|
||||
|
||||
- `role`
|
||||
- `origin`、`stop`、`destination`
|
||||
- `input_name`
|
||||
- 原始输入名
|
||||
- `input_address`
|
||||
- 原始输入地址
|
||||
- `resolved_name`
|
||||
- 实际命中的名称
|
||||
- `city`
|
||||
- 城市名
|
||||
- `district`
|
||||
- 区县名
|
||||
- `location`
|
||||
- `lon,lat` 字符串
|
||||
- `lon`
|
||||
- 经度
|
||||
- `lat`
|
||||
- 纬度
|
||||
- `poi_id`
|
||||
- 高德 POI ID,可能为空
|
||||
- `source`
|
||||
- `geo`、`text_search`、`search_detail`、`manual_fallback`
|
||||
- `confidence_note`
|
||||
- 命中说明
|
||||
|
||||
### 6.2 CandidateRoute
|
||||
|
||||
- `stop_order_labels`
|
||||
- 仅包含中间途经点顺序
|
||||
- `full_order_labels`
|
||||
- 包含起点和终点的完整顺序
|
||||
- `legs`
|
||||
- 每段路线信息
|
||||
- `total_distance_m`
|
||||
- 总距离,单位米
|
||||
- `total_duration_s`
|
||||
- 总时长,单位秒
|
||||
- `ranking_reason`
|
||||
- 为什么这条路线被这样排序
|
||||
|
||||
### 6.3 DeepLinks
|
||||
|
||||
- `personal_map`
|
||||
- 点位导入型链接
|
||||
- 适合把一组点位导入到高德地图
|
||||
- 更偏“查看/导入点位方案”,不是严格的即时导航协议
|
||||
- `android_route_plan`
|
||||
- Android 导航链接
|
||||
- `ios_route_plan`
|
||||
- iOS 导航链接
|
||||
|
||||
补充说明:
|
||||
|
||||
- `deep_links` 中可能同时存在多个字段,也可能只有其中一个字段有值
|
||||
- 前端应只根据 `deep_links` 的字段值控制按钮展示,不要依赖 `summary` 推断应展示哪个按钮
|
||||
- `summary` 里可能会提到“个人地图链接”或“导航链接”,但这里只是说明文字,不保证包含真实 URL
|
||||
- 如果 `personal_map` 存在,表示当前更适合导入点位方案
|
||||
- 如果 `android_route_plan` 或 `ios_route_plan` 存在,表示当前可以直接拉起导航
|
||||
|
||||
前端建议:
|
||||
|
||||
- 如果值为 `null`,对应按钮不要展示
|
||||
- 如果 `warnings` 非空,建议在页面显式展示提示
|
||||
- `summary` 只用于文案展示,不要从 `summary` 中抽取链接或做业务判断
|
||||
|
||||
## 7. 错误响应
|
||||
|
||||
### 7.1 422 输入或护栏错误
|
||||
|
||||
出现以下情况时,接口会返回 422:
|
||||
|
||||
- 请求结构不合法
|
||||
- `stops` 为空
|
||||
- 固定起点缺少 `origin_address`
|
||||
- 终点同时出现在 `stops`
|
||||
- 请求候选上限超过服务上限
|
||||
- 实际排列数超过上限
|
||||
|
||||
错误可能有两种形态。
|
||||
|
||||
形态一:FastAPI/Pydantic 字段校验错误
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"type": "value_error",
|
||||
"loc": ["body"],
|
||||
"msg": "Value error, stops must contain at least one stop",
|
||||
"input": {
|
||||
"origin_mode": "fixed",
|
||||
"origin_address": "北京站",
|
||||
"destination_address": "天安门",
|
||||
"stops": []
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
形态二:服务护栏错误
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "Candidate permutations exceed the configured limit: stops=4, permutations=24, limit=20"
|
||||
}
|
||||
```
|
||||
|
||||
### 7.2 503 配置错误
|
||||
|
||||
出现以下情况时,接口会返回 503:
|
||||
|
||||
- 模型配置缺失
|
||||
- MCP 配置缺失
|
||||
- MCP transport 非法
|
||||
|
||||
错误示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "Missing required environment variable: AMAP_MCP_URL"
|
||||
}
|
||||
```
|
||||
|
||||
### 7.3 500 内部错误
|
||||
|
||||
模型运行失败、第三方异常或未预期错误会返回 500。
|
||||
|
||||
### 7.4 504 上游超时
|
||||
|
||||
当模型服务或地图 MCP 服务超时,接口会返回 504。
|
||||
|
||||
错误示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "Upstream request timed out: ..."
|
||||
}
|
||||
```
|
||||
|
||||
前端建议:
|
||||
|
||||
- 422 显示明确的用户提示
|
||||
- 503 显示“服务暂不可用”
|
||||
- 504 显示“请求处理超时,请稍后重试”
|
||||
- 500 显示通用错误提示,并建议重试
|
||||
|
||||
## 8. 前端对接建议
|
||||
|
||||
- 直接按 `summary`、`best_route`、`warnings` 渲染即可完成第一版页面
|
||||
- 如果需要路线详情页,可渲染 `candidates` 对比卡片
|
||||
- 统一使用 `deep_links` 控制跳转按钮显隐
|
||||
- 对 `warnings` 保持可见,不要吞掉
|
||||
- 对 `current_location` 场景,要准备接受 `resolved_origin=null`
|
||||
|
||||
## 9. 当前接口现状
|
||||
|
||||
- 当前接口已经可用
|
||||
- 当前已验证真实请求可成功返回结果
|
||||
- 当前返回结构已稳定,可作为第一版前端对接基础
|
||||
- 当前 `need_html` 还未真正实现 HTML 返回,不建议前端依赖该字段做页面内容请求
|
||||
|
||||
## 10. 对接 TODO
|
||||
|
||||
- 前端确认是否需要候选路线对比视图
|
||||
- 前端确认 deep link 的按钮交互形式
|
||||
- 前后端统一 422 错误展示文案
|
||||
- 后续若输出结构调整,需要同步更新本文档
|
||||
Reference in New Issue
Block a user