后端结构
修改记录
日期 |
变更 |
|---|---|
2026-07-18 |
从 back.md 重写为 backend.md:全量结构重写 + 引擎链路/POI流程/数据流图修正,清理 ca.py 废弃参数 |
一、架构总览
后端采用三条路径并列的架构:
用户请求
│
├── POST /api/poi-lookup ──→ 高德 API → LLM 解析营业时间
│
├── POST /api/suggest ────→ Engine (CA 全参数搜索)
│ 用户选天数
│ POST /api/plan ───────→ Engine (CA 或 VNS+)
│
└── POST /api/chat ───────→ Agent (Function Calling)
核心原则:Agent 是交互层,Engine 是计算层。Agent 不参与路径计算,只做意图识别与参数提取。当前 Agent 通过 chat.py + tools/ 直接调用函数,后续 MCP 迁移后可统一接管 API 入口。
二、目录结构
backend/
├── config.py 环境变量(AMap Key、LLM Key、DATABASE_URL 等)
├── typedefs.py 内部 TypedDict 定义(零运行时开销)
│
├── api/ HTTP 接口层
│ ├── server.py FastAPI 工厂 + CORS + lifespan(DB 初始化/关闭)
│ ├── routes.py 8 个 API 端点
│ └── schemas.py Pydantic 请求/响应模型
│
├── agent/ LLM Agent 层
│ ├── chat.py 对话调度(chat_stream、build_chat_messages、MOCK_MODE)
│ ├── planner.py 行程调整三指令(加/删景点、调天数)
│ ├── commentator.py 方案评语生成
│ └── tools/ 工具函数子包
│ ├── __init__.py TOOL_REGISTRY 注册表(统一导出)
│ ├── poi.py POI 工具(parse_biz_hours、poi_lookup)
│ ├── prompts.py LLM prompt 模板(CHAT_SYSTEM、PARSE_PROMPT、TOOL_DEFINITIONS)
│ └── rag.py RAG 工具(预留)
│
├── engine/ 求解引擎核心
│ ├── pipeline.py 流程编排入口(run_planning)
│ ├── search.py 建议/深度求解(ca_suggest / cluster_and_solve)
│ ├── ca.py 压缩退火求解器
│ ├── vns.py 变邻域搜索求解器
│ ├── clustering.py 6 种聚类方法注册表
│ └── fitness.py 适应度计算(距离/等待/迟到/晚归惩罚)
│
├── data/ 数据层
│ ├── amap_loader.py 高德 API → 成本/距离矩阵(build_real_data)
│ ├── model/
│ │ ├── database.py async SQLAlchemy 引擎 + 会话工厂
│ │ └── models.py HistoryRecord ORM(详见 data.md)
│ └── chroma_db/ 向量数据库(待填充)
│
└── utils/ 通用工具
├── deprecated.py @legacy_only 装饰器(标记遗留函数)
├── gen_openapi.py 导出 OpenAPI 规范 JSON
└── sync_all.py 自动同步 __init__.py 的 __all__
三、配置层
config.py
环境变量配置入口,支持 .env 文件注入。关键变量:
变量 |
默认值 |
用途 |
|---|---|---|
|
|
高德 Web 服务 API(路线/POI) |
|
|
高德 JS API(前端地图) |
|
|
LLM 调用密钥 |
|
|
LLM API 地址 |
|
|
PostgreSQL 连接 |
|
|
uvicorn 热重载开关 |
typedefs.py
内部数据模型定义(TypedDict),零运行时开销,只在类型约束时使用。
API 边界用 Pydantic(schemas.py),内部数据传递用 TypedDict。
详见 docs/structure/data.md 统一数据字典。
四、HTTP 接口层 (api/)
server.py
FastAPI 应用工厂:
init_db()/close_db()— 生命周期中管理连接池CORS 允许
localhost:5173和127.0.0.1:5173(Vue 开发服务器)DEV_RELOAD环境变量控制热重载
routes.py — 8 个端点
方法 |
路径 |
用途 |
|---|---|---|
|
|
批量查询 POI 坐标 + LLM 解析营业时间 |
|
|
获取方案建议列表(fast 模式) |
|
|
执行完整规划(deep 模式) |
|
|
LLM Agent 对话(SSE 流式) |
|
|
历史记录列表(分页) |
|
|
历史记录详情 |
|
|
创建历史记录(分享方案) |
|
|
删除历史记录(device_id 鉴权) |
schemas.py
Pydantic 请求/响应模型,按功能分组:
POI 查询:
POILookupRequest/POILookupItem/POILookupResponse规划请求:
PlanRequest(含酒店/景点/算法参数)Agent 对话:
ChatRequest历史记录:
HistoryCreate/HistorySummary/HistoryDetail/HistoryListResponse/HistoryDeleteRequest
详见 docs/ADR/005.md 营业时间 LLM 解析决策。
五、LLM Agent 层 (agent/)
负责 LLM 对话、工具调用和评语生成。详见 agent.md 独立文档。
组件 |
文件 |
说明 |
|---|---|---|
对话流 |
|
SSE 流式聊天入口 |
工具系统 |
|
Function Calling 工具注册与执行 |
评语生成 |
|
规划结果解说 |
规划调整 |
|
加/删景点、调天数(函数已就绪,未接入 Function Calling) |
六、求解引擎层 (engine/)
引擎核心,使用独立求解器 + 聚类方法的组合策略。
模块职责
模块 |
关键函数 |
定位 |
|---|---|---|
|
|
流程编排:矩阵构建 → 求解 → 行程生成 → 评语 |
|
|
建议/求解入口 |
|
|
快速求解器(压缩退火) |
|
|
VNS+ 增强求解器(压缩成本 VND + 自适应算子权重 + 动态 Shake + 精英池后优化) |
|
|
6 种聚类方法注册表 |
|
|
成本计算 + 可行性判定 |
聚类方法注册表
6 种方法(clustering.py):
基于距离的 K-means 聚类
基于时间窗的聚类
基于时空特征的聚类
基于时间窗重叠的启发式分组
基于时间窗密度的聚类
混合分组方法
详见 docs/ADR/001.md 引擎并行架构决策。
引擎内部调用链路
cluster_and_solve 是 run_planning 中的核心调度入口,根据外部参数分支:
cluster_and_solve(spots, cost_mat, mode, n_days)
│
├─ n_days=None (Suggest 阶段) ────────────────
│ └─ ca_suggest()
│ ├─ 外层:遍历 6 种聚类方法
│ ├─ 内层:天数递增(min_days → n_spots)
│ ├─ solve_groups(solver_type="CA") 求解各组
│ ├─ 增益阈值早退(<1.0% × 3 次 → stop)
│ ├─ 去重 + 按成本排序
│ └─ 返回 type="suggestion"(多条方案,每条含完整 routes/daily_schedules/cost)
│
└─ n_days 已指定 (Plan 阶段) ─────────────────
├─ solver_type = "VNS"(固定,仅 deep 模式)
├─ 遍历 6 种聚类方法,固定天数分组
├─ solve_groups(solver_type="VNS")
└─ 返回 type="solution"(单条最优方案)
run_planning设计要点:
suggest 和 plan 共用数据准备与后处理
共享范围:成本矩阵构建 → spots 时间窗收缩 → polyline 补调
分叉点仅在
cluster_and_solve内部拆为两个入口函数收益不高,当前保持统一
七、数据层 (data/)
amap_loader.py
get_poi_details(city, spot_names)→ 高德 POI 批量查询build_real_data(poi_cache)→ 构造成本矩阵、距离矩阵、真实轨迹 polylines
model/(数据库 ORM)
database.py— async SQLAlchemy 引擎连接池 +get_session依赖注入models.py—HistoryRecordORM 模型(id / device_id / note / city / hotel / n_days / cost / spot_count / plan_result / request_params / created_at)
详见 docs/structure/data.md 数据定义。
chroma_db/
向量数据库存储目录(预留)。
八、工具层 (tools/)
脚本 |
用途 |
|---|---|
|
|
|
导出 |
|
扫描 |
九、数据流图
POI 查找 → LLM 营业时间解析
前端传入 city + names[](景点名称列表)
↓
POST /api/poi-lookup
↓
逐个调用 get_poi_details(name, city)
├── 高德 API 三策略搜索
│ ① types=风景名胜 + city_limit(高德分类准确)
│ ② 去掉 types 按关键词排序(如岭南印象园→中山纪念堂误配补救)
│ ③ 全国搜索 + 跨城市判定(确认不在本市 → 返回提示)
│
├── 成功 → (lon, lat, opentime2, address, ...)
│ ↓
│ parse_biz_hours(opentime2) → LLM 解析营业时间
│ ├── 成功 → (start_min, end_min)
│ └── 失败 → None(tw_start/tw_end 均置 None)
│ ↓
│ 加入 items[] → POILookupItem
│
└── 失败 → 加入 failed[](返回错误信息字符串)
↓
返回 { items: POILookupItem[], failed: string[] }
详见 docs/ADR/005.md 营业时间 LLM 解析与 Agent 架构决策。
端到端业务流程(从前端视角)
整个规划业务分4 个阶段,前后端配合完成:
Stage 1 — POI 查找
目的:获取景点坐标 + 营业时间
前端 → POST /api/poi-lookup(传入 city + names[])
后端 → 高德 API 三策略搜索 → LLM 解析 opentime2
返回 → POILookupItem[](含坐标/地址/时间窗)
前端 → 展示结果,用户确认
Stage 2 — Suggest(CA 固定)
目的:让用户看到所有可能的行程方案
前端 → POST /api/suggest(传入酒店 + 已确认景点 + 参数)
后端 → run_planning(mode="fast", n_days=None)
└── ca_suggest()
├── 遍历 6 种聚类 × 天数递增
├── CASolver.solve() 求解各组
└── 返回 type="suggestion"(多条方案 + 矩阵)
返回 → { suggestions[], cost_matrix, dist_matrix, polylines }
每条建议已含完整 routes / daily_schedules / cost
前端 → 展示方案卡片,用户可点击预览
┌── fast 路径(不调后端)──────────────────────────────┐
│ 用户点击卡片 → frontend/buildPlanResultFromSuggestion │
│ → store.planResult → router.push("/plan") │
└──────────────────────────────────────────────────────┘
Stage 3 — Plan(VNS 仅限 deep 模式)
目的:对选定天数做深度优化
前置:用户在 Suggest 页选好天数,点击"深度规划"
前端 → POST /api/plan(mode="deep", n_days=用户选定)
后端 → run_planning(mode="deep", n_days=指定)
└── cluster_and_solve()
├── 遍历 6 种聚类,固定天数
├── solve_groups(solver_type="VNS") # 分钟级
└── 返回 type="solution"
返回 → PlanResult(含 best_days/daily_schedules/commentary)
前端 → 用户点击深度结果卡片
→ store.planResult = data → router.push("/plan")
Stage 4 — 结果展示
PlanPage 读取 store.planResult
纯展示,不调用任何后端 API
组件: metrics-bar + commentary + AmapMap + SchedulePanel