智能旅行规划助手 --- 实习面试问答手册
本文档基于项目
helloagents-trip-planner的实际代码整理,供软件开发实习生面试使用。建议配合项目 README 和本地 Demo 演示一起准备。
一、30 秒电梯演讲(开场必背)
面试官:先简单介绍一下这个项目。
回答示例:
这是一个 AI 驱动的智能旅行规划 Web 应用 。用户在首页填写目的地、日期、预算、偏好等信息后,后端会先调用 高德地图 API 获取真实景点、酒店和天气数据,再把这些结构化信息交给 大语言模型(LLM) 生成多日行程;前端用 Vue 3 展示每日路线、地图标记、预算汇总,并支持行程伴游问答、历史记录和 PDF 导出。
我主要负责(或参与)前后端联调、Agent 行程生成链路、POI 坐标准确性治理、异步任务接口 等工作。项目采用 前后端分离:FastAPI 提供 REST API,Vue 3 + Ant Design Vue 负责交互与可视化。
二、项目背景与目标
Q1:为什么要做这个项目?解决了什么问题?
回答:
传统旅行规划要么靠人工查攻略、拼表格,耗时长;要么纯靠 AI 生成,容易出现 虚构景点、坐标不准、地图无法展示 的问题。
我的项目核心思路是:「真实数据 + AI 编排」------
- 用高德地图拿到 真实 POI(景点/酒店)和天气;
- 用 LLM 负责 行程安排、餐饮建议、预算估算、文字描述;
- 后端再做 坐标回填与校验,保证前端地图能正确打点。
这样既保留了 AI 的灵活性,又提高了结果的可信度和可执行性。
Q2:项目的用户是谁?核心使用场景是什么?
回答:
面向 有出行计划但不想花大量时间做攻略的用户,典型场景包括:
- 周末/小长假城市游,快速生成 2~5 日行程;
- 按偏好(历史文化、美食、自然风光等)筛选景点;
- 生成后在结果页查看地图路线、调整关注点后导出 PDF;
- 对已生成行程进行 伴游问答(如「第 2 天下午适合去哪?」)。
三、技术栈说明
Q3:项目用了哪些技术?为什么选它们?
回答:
| 层级 | 技术 | 选型理由 |
|---|---|---|
| 后端框架 | FastAPI | 异步友好、自动生成 OpenAPI 文档、Pydantic 数据校验成熟 |
| LLM | OpenAI 兼容接口(OpenAI / DeepSeek 等) | 通过统一 HelloAgentsLLM 封装,便于切换模型和 Base URL |
| 地图 | 高德 Web 服务 API + JS API | 国内 POI、天气、路线数据准确;前后端分别调用 HTTP API 和 JS SDK |
| HTTP 客户端 | httpx(后端)、axios(前端) | 异步/拦截器支持好,对接第三方 API 方便 |
| 数据模型 | Pydantic v2 | 请求/响应强类型校验,减少接口脏数据 |
| 前端 | Vue 3 + TypeScript + Vite | 组合式 API + 类型安全,开发效率高 |
| UI | Ant Design Vue | 表单、卡片、步骤条等组件齐全,适合 B 端/工具类产品 |
| 路由 | Vue Router | 首页 / 结果 / 历史 / 探索页分离 |
| PDF 导出 | html2canvas + jsPDF | 将 DOM 渲染为图片再写入 PDF,实现成本低 |
| 图片 | Unsplash API(可选) | 为景点补充展示图 |
| Agent 框架 | 本地 hello_agents 兼容层 |
参考 Datawhale Hello-Agents 教程,轻量封装 LLM 与 MCP 工具 |
Q4:前后端是如何通信的?
回答:
- 前端通过
VITE_API_BASE_URL(默认http://localhost:8000)访问后端; - 后端开启 CORS ,允许
localhost:5173等开发源; - 主要接口:
POST /api/trip/plan_async--- 提交异步规划任务GET /api/trip/plan_result/{job_id}--- 轮询任务结果POST /api/assistant/chat--- 行程伴游GET /api/map/poi--- POI 搜索POST /api/map/route--- 路线规划
- 数据格式统一为 JSON,后端用 Pydantic 模型约束字段。
四、系统架构与运行流程
Q5:请描述一下项目的整体架构。
回答:
项目是典型的 三层前后端分离架构:
┌─────────────────────────────────────────────────────────┐
│ 前端 (Vue 3) │
│ Home → 提交表单 │ Result → 地图/导出 │ History/Explore │
└───────────────────────────┬─────────────────────────────┘
│ HTTP / JSON
┌───────────────────────────▼─────────────────────────────┐
│ 后端 (FastAPI) │
│ API 路由层 → Agent/Service 层 → 外部 API (LLM/高德/Unsplash) │
└─────────────────────────────────────────────────────────┘
- API 路由层 :
trip.py、assistant.py、map.py、poi.py - 业务层 :
MultiAgentTripPlanner(行程规划)、AmapService(地图)、HelloAgentsLLM(大模型) - 数据层 :本项目无独立数据库,历史记录存 浏览器 localStorage
Q6:用户点击「生成旅行计划」后,完整流程是什么?
回答(按时间顺序):
前端:
- 用户在
Home.vue填写城市、日期、偏好、预算等,点击提交; - 调用
generateTripPlan():- 先
POST /api/trip/plan_async,立即拿到job_id; - 每 2 秒轮询
GET /api/trip/plan_result/{job_id},更新进度条;
- 先
- 成功后把计划写入
sessionStorage和localStorage历史,跳转Result.vue。
后端(MultiAgentTripPlanner.plan_trip):
- 步骤 1 --- 搜索景点:调用高德 POI 文本搜索,获取真实名称与坐标;
- 步骤 2 --- 查询天气:地理编码拿 adcode,再查天气预报;
- 步骤 3 --- 搜索酒店:按住宿偏好搜索酒店 POI;
- 步骤 4 --- LLM 生成行程:把用户需求 + 景点列表 + 天气 + 酒店列表拼成 Prompt,要求 LLM 输出固定 JSON 结构;
- 解析与治理 :
- 从 LLM 回复中提取 JSON(支持 markdown 代码块);
- 坐标回填:用高德搜索结果覆盖/修正景点坐标;
- 酒店强制对齐:名称无法匹配时按天轮换回填真实酒店;
- POI 一致性校验:检查坐标是否在中国范围、是否使用模板默认坐标等;
- 若任一步失败,走
_create_fallback_plan()返回兜底行程,避免接口完全不可用。
结果页:
- 加载高德 JS API,按景点坐标打 Marker、画 Polyline;
- 可调用伴游 API 问答、导出 PDF。
Q7:你说的「多智能体」具体是怎么协作的?
回答(诚实且准确):
从代码实现上看,当前版本是 「多步骤流水线 + 单一 LLM 规划」,而不是多个独立 Agent 互相对话:
- 「景点搜索」「天气查询」「酒店搜索」由
AmapService直接调用; - 「行程编排」由 一个 LLM 调用 完成,Prompt 里注入了前三步的结构化结果;
- 项目保留了
hello_agents里的SimpleAgent、MCPTool兼容层,便于后续扩展成真正的 Tool Calling Agent。
面试时可以这样说:「当前是 Pipeline 式多阶段协作,架构上预留了 Agent + Tool 扩展能力。」
五、核心模块深入
Q8:TripRequest 里有哪些字段?后端如何校验?
回答:
主要字段包括:city、destinations(多城市)、start_date、end_date、travel_days(1~30)、transportation、accommodation、preferences、budget_per_person、travel_pace、companions、dietary_restrictions、free_text_input。
使用 Pydantic 做类型和范围校验,例如 travel_days 必须 ≥1 且 ≤30。FastAPI 会在入参不符合模型时直接返回 422,减少业务层重复校验。
Q9:LLM 返回的不是纯 JSON 怎么办?
回答:
在 _parse_response() 里做了 多层提取策略:
- 优先找 ` ```json ... ````代码块;
- 否则找普通 ` ```... ````;
- 再否则从第一个
{到最后一个}截取; - 用
json.loads()解析; - 解析失败则记录日志,返回 fallback 计划,保证用户体验不中断。
这是对接 LLM 时的常见工程化处理,因为模型偶尔会加解释性文字或格式不规范。
Q10:为什么要把景点/酒店坐标从 LLM 结果里「强制回填」?
回答:
这是项目里最关键的 工程问题之一。
LLM 容易:
- 改写 POI 名称(如「如家快捷酒店」vs「如家酒店(XX路店)」);
- 编造或复制模板坐标(代码里专门检测默认北京坐标
116.397128, 39.916527); - 名称和坐标 张冠李戴(名字是 A 景点,坐标却是 B 景点)。
解决方案:
- Prompt 里明确要求 必须从给定列表逐字复制 name/address/location;
- 解析后
_enforce_hotels_from_search_results():精确匹配 + 子串模糊匹配 + 按天轮换回填; - 景点侧建立
attraction_map,按名称匹配后替换location; _validate_plan_against_poi_search()打日志告警,便于排查。
我还写了 diagnose_coordinates.py 脚本,专门验证某城市 POI 坐标范围和名称匹配率。
Q11:前端为什么用异步任务 + 轮询,而不是一次同步请求?
回答:
行程生成链路包含 多次外部 API + 一次长文本 LLM 调用 ,整体可能耗时 1~3 分钟甚至更久。
如果只用同步 POST /api/trip/plan:
- 浏览器/网关可能 超时断开;
- 用户看不到真实进度,体验差。
因此前端改为:
plan_async立即返回job_id;- 后端用
asyncio.create_task+asyncio.to_thread在后台线程跑同步规划逻辑; - 前端每 2 秒轮询,最多约 10 分钟;
- axios 全局 timeout 设为 5 分钟,轮询单次 30 秒。
这是典型的 长任务异步化 方案。生产环境可进一步升级为 WebSocket 或 Redis 任务队列,但实习项目用内存 _job_store 足够演示。
Q12:历史记录存在哪里?有什么限制?
回答:
存在浏览器 localStorage (tripPlanHistory),最多保留 50 条;当前激活 ID 在 sessionStorage。
优点:无需后端数据库,部署简单;
缺点:换设备/清缓存会丢失,不支持多用户 ------ 面试时可主动说 「这是 MVP 阶段的权衡,后续可接 SQLite/PostgreSQL + 用户体系」。
六、开发过程中遇到的问题与解决方案
这一部分是实习面试的高频考点,建议结合具体例子讲,体现 发现问题 → 定位 → 解决 → 验证 的能力。
问题 1:地图上的景点位置不准确 / 全部打在一个点
现象: 结果页高德地图 Marker 偏移,或多个景点坐标相同。
原因:
- LLM 自行「编造」经纬度;
- 景点名与高德搜索结果不一致,导致坐标和名称错配;
- 高德返回的
location是"lng,lat"字符串,解析错误会导致异常。
解决:
- 后端先用高德搜索拿 权威 POI 列表;
- LLM 只负责选哪些景点、怎么排 day,不负责造坐标;
_parse_response阶段强制用搜索结果回填location;- 校验中国大陆经纬度范围(经度 73~136,纬度 18~54);
- 编写
test_poi_fix.py、diagnose_coordinates.py做回归测试。
收获: 明白了 「LLM 适合生成语义内容,不适合作为地理数据源」,结构化数据要以 API 为准。
问题 2:LLM 改写酒店名称,导致 POI 对不上
现象: 行程里酒店名不在高德返回列表中,前端无法关联真实位置。
解决:
- Prompt 增加 正反示例,强调 hotel.name 必须逐字复制;
- 实现
_match_hotel_poi():先精确匹配,再单向子串匹配; - 仍匹配不上则
_enforce_hotels_from_search_results()按天轮换填入搜索结果; _validate_plan_against_poi_search()输出不一致告警。
问题 3:请求超时,前端显示「生成失败」
现象: 同步接口在 LLM 慢时经常失败。
解决:
- 后端新增
plan_async+plan_result; - 前端
api.ts改为提交任务后轮询; - 进度条结合后端
stage/progress与前端模拟进度,提升感知体验。
问题 4:高德 API 返回字段类型不一致
现象: POI 的 tel 有时是字符串,有时是列表,直接赋值导致 Pydantic 校验失败或前端展示异常。
解决: 在 AmapService.search_poi() 里统一处理:列表取第一个,非字符串置 None。
问题 5:LLM JSON 解析失败导致 500
解决:
- 多策略提取 JSON;
- 解析失败走
_create_fallback_plan()兜底; - 降低
temperature(如 0.4)提高输出稳定性。
问题 6:CORS / 环境变量配置问题
现象: 本地前端调后端跨域失败,或 Key 未加载。
解决:
- FastAPI 配置
CORSMiddleware,允许 Vite 开发端口; - 使用
python-dotenv+pydantic-settings统一管理.env; - 启动时
validate_config()检查必要 Key 是否存在。
七、面试官可能追问的技术问题
Q13:FastAPI 的异步在这里是怎么用的?
回答:
路由函数是 async def,但 LLM 和 httpx 同步调用较重,所以异步任务通过 asyncio.to_thread() 把 同步规划逻辑放到线程池,避免阻塞事件循环。这是 Python 异步 Web 开发里常见的折中做法。
Q14:如果 LLM API 挂了怎么办?
回答:
当前有 _create_fallback_plan() 返回基础模板行程;生产环境还可以:重试 + 指数退避、切换备用模型、缓存热门城市模板、向用户明确提示「AI 服务暂不可用」。
Q15:如何保证 Prompt 输出稳定?
回答:
- 提供 完整 JSON Schema 示例 在 system prompt;
- 约束必须使用的 POI 列表(含正确/错误示例);
- 较低 temperature;
- 后端 Pydantic 二次校验;
- 关键字段(坐标、酒店名)不信任 LLM,以 API 数据为准。
Q16:项目有哪些不足?如果继续迭代你会做什么?
回答(展示思考深度):
| 现状 | 改进方向 |
|---|---|
| 任务队列在内存 | 接 Redis/Celery,支持重启不丢任务 |
| 无用户系统 | JWT 登录 + 数据库存行程 |
| Agent 是 Pipeline | 引入 Function Calling,让 LLM 自主决定何时查 POI/路线 |
| 路线规划 API 已有 | 可在生成阶段就把相邻景点路线算进日程 |
| 缺少自动化测试 | 为 POI 回填、JSON 解析写单元测试 |
| 伴游无上下文记忆 | 加 session 或多轮对话 history |
Q17:你在这个项目里具体负责什么?
回答模板(请按真实情况修改):
我主要负责后端行程规划模块和前后端联调。包括:设计
MultiAgentTripPlanner的四步流水线;实现 LLM 响应 JSON 解析与 POI 坐标回填;排查并修复地图打点不准的问题;对接异步任务接口,解决长请求超时;前端完成结果页地图展示和历史记录功能。
原则: 只讲自己真正做过的部分,细节要能答上来。
Q18:遇到过最难的 bug 是什么?
回答示例:
最难的是 「景点名称和坐标不匹配」------表面上行程 JSON 合法,Pydantic 也能过,但地图把「鼓浪屿」打到了另一个 POI 的位置。
我通过日志对比 LLM 输出与高德搜索结果,发现模型会轻微改写名称;随后加了名称映射、坐标强制回填和诊断脚本,并补充 POI 校验日志。这个问题让我理解了 AI 应用里「看起来正确」和「事实正确」是两回事。
八、行为面试 / 软技能问题
Q19:这个项目是你独立完成的还是团队完成的?
回答模板:
如果是课程/教程延伸:说明基于 Hello-Agents 教程,我在其基础上 扩展了 POI 治理、异步接口、前端完整流程 ;
如果是团队:说明分工,强调自己模块的 owner 意识 和联调沟通。
Q20:开发周期多长?怎么管理进度?
回答模板:
例如 2~3 周:第 1 周打通 MVP(表单 → LLM → 展示);第 2 周接高德地图和坐标修复;第 3 周做历史、PDF、伴游和 UI polish。用 README 和 TODO 列表跟踪,优先保证 主链路可 Demo。
Q21:从项目中学到了什么?
回答:
- LLM 应用工程化:Prompt 设计、输出解析、兜底策略同样重要;
- 第三方 API 集成:要处理超时、字段不一致、Key 配置;
- 全栈协作:Pydantic 模型最好与前端 TypeScript 类型对齐;
- 用户体验:长任务需要进度反馈和异步化;
- 问题定位方法:写诊断脚本、打结构化日志,比盲目改 Prompt 更高效。
九、Demo 演示建议(面试现场加分)
- 提前启动 前后端,确认
.env中 LLM 和高德 Key 有效; - 演示路径:填表单 → 看进度 → 结果页地图 → 问伴游一个问题 → 打开历史记录;
- 准备 1 个失败案例(如断网)说明 fallback 机制;
- 打开
http://localhost:8000/docs展示 API 文档,体现 FastAPI 优势; - 若被问代码,能快速打开:
backend/app/agents/trip_planner_agent.py--- 主流程frontend/src/services/api.ts--- 异步轮询backend/app/services/amap_service.py--- 地图封装
十、快速记忆卡片
| 关键词 | 一句话 |
|---|---|
| 项目定位 | AI + 高德真实数据的旅行规划助手 |
| 后端 | FastAPI + Pydantic + LLM + 高德 HTTP API |
| 前端 | Vue3 + TS + Ant Design Vue + 高德 JS API |
| 核心流程 | 搜 POI → 查天气 → 搜酒店 → LLM 编排 → 坐标回填 → 返回 |
| 最大难点 | LLM 幻觉导致 POI/坐标不准 |
| 核心解法 | API 数据为准,LLM 只编排;解析后强制回填与校验 |
| 长任务 | plan_async + 轮询 job_id |
| 存储 | localStorage 历史,无数据库 |
| 亮点 | 伴游问答、知识图谱、PDF 导出、探索页主题 POI |
十一、常见「坑」提醒(避免面试翻车)
- 不要说「完全是多个 AI Agent 自主对话」------ 与当前实现不符,除非你真的改了架构;
- 不要说 「坐标都是 AI 生成的」------ 恰恰相反,核心是 不信任 AI 的坐标;
- 准备好 API Key 成本:LLM 和高德都有调用费用,面试官可能问;
- 诚实说明数据持久化局限:无后端 DB 是已知 trade-off,不是疏忽;
- 能解释
hello_agents是本地兼容层,参考 Datawhale 教程,避免被问「框架源码在哪」答不上来。
祝你面试顺利。 建议把本文档中标注「请按真实情况修改」的部分,换成你自己的贡献描述,并对着镜子练习 项目介绍 1 分钟版 和 技术难点 2 分钟版 各一遍。