智能旅行规划助手:结构化输出解析与兜底机制

写在前面

做 AI 应用时,模型能不能回答不是唯一问题,更重要的是:模型输出能不能被系统稳定消费。

在智能旅行规划助手里,前端需要展示每日行程、景点、餐饮、住宿、交通、预算等结构化内容。如果模型输出一段自然语言,前端就无法稳定渲染。

所以这一篇记录结构化输出解析和兜底机制。

为什么必须做结构化输出

旅行计划的展示不是普通文本,而是多个模块:

  • 行程概览
  • 景点卡片
  • 餐饮安排
  • 酒店信息
  • 交通方式
  • 天气建议
  • 预算信息

如果后端返回一段 Markdown,前端只能整体展示,用户体验会很差。

因此后端要求模型输出 JSON,并且必须符合 TripPlan Schema。

输出解析流程

整体流程是:

1
2
3
4
5
6
LLM 原始响应
-> 提取 JSON 文本
-> json.loads 解析
-> Pydantic 模型校验
-> 转换成 TripPlan 响应
-> 返回前端

1. 提取 JSON

模型有时会输出:

1
2
3
下面是你的旅行计划:
```json
{ ... }
1
2
3
4
5
6
7
8
9
10
11
12
13

所以不能直接 `json.loads(response)`,需要先提取 JSON 部分。

常见处理方式是:

```python
def extract_json(text: str) -> str:
text = text.strip()
if "```json" in text:
return text.split("```json")[1].split("```")[0].strip()
if "```" in text:
return text.split("```")[1].strip()
return text

2. JSON 解析

提取后再做解析:

1
data = json.loads(json_text)

这一层主要处理语法问题,比如多了逗号、少了引号、字段格式不合法等。

3. Pydantic 校验

即使 JSON 语法合法,也不代表业务结构合法。

所以还需要:

1
plan = TripPlan.model_validate(data)

Pydantic 会检查字段是否缺失、类型是否正确、嵌套结构是否符合预期。

常见失败场景

项目里遇到过几类问题:

  • 模型输出了 Markdown,而不是纯 JSON
  • JSON 少字段,比如没有 daily_plans
  • 日期格式不符合要求
  • 景点列表为空
  • 酒店字段结构不一致
  • 模型把数字写成中文描述

这些问题都不能直接暴露给用户,否则体验很差。

兜底机制设计

兜底的目标不是生成完美计划,而是保证系统可用。

当模型输出解析失败时,我会基于真实 POI 数据生成备用计划:

1
2
3
4
5
真实 POI 数据
-> 按天数分组
-> 每天安排 2-3 个景点
-> 填充基础交通和餐饮建议
-> 返回符合 TripPlan Schema 的结果

这个计划可能没有模型生成得那么自然,但它有两个优点:

  • 数据是真实的
  • 结构是稳定的

对于线上应用来说,稳定性很重要。

异常处理思路

我把异常分成几类处理:

1. 工具 API 异常

地图 API 超时或失败时,不直接让接口崩溃,而是记录错误并尝试使用已有数据。

2. LLM 调用异常

模型接口失败时,进入备用计划。

3. JSON 解析异常

说明模型输出格式不稳定,进入兜底。

4. Pydantic 校验异常

说明字段结构不符合前端要求,也进入兜底。

工程化收获

这个阶段让我意识到,AI 应用的“可用性”不只是模型能力,还包括:

  • 输入校验
  • 输出校验
  • 异常捕获
  • 兜底返回
  • 日志记录
  • 前端错误提示

如果没有这些,项目很容易在本地 Demo 时看起来正常,一上线就各种报错。

小结

结构化输出和兜底机制是这个项目里最重要的工程化部分之一。它让系统即使在模型输出不稳定时,也能尽量返回可展示的结果。

下一篇会记录前端可视化和服务器部署,也就是如何把这个项目真正上线到自己的博客里。


智能旅行规划助手:结构化输出解析与兜底机制
https://zxyblog.top/2026/04/26/智能旅行规划助手-结构化输出解析与兜底机制/
作者
zxy
发布于
2026年4月26日
许可协议