旅行AI Agent技术拆解:从行程规划到工具调用的工程实践

发布时间:2026/8/29 15:23:41
旅行AI Agent技术拆解:从行程规划到工具调用的工程实践 AI 大模型进入旅游行业后最早一批产品做的是“攻略生成器”用户说一句话模型回一段看起来很完整的行程文字。飞猪帮帮这类新一代旅行 AI 上线后产品定位明显变了。从公开产品形态看它要解决的不再是“生成一段内容”而是让用户“一句话就出发”。这意味着系统既要完成行程规划还要接着处理门票、酒店、航班、用车、提醒这些具体服务动作。“能规划更能办事”听起来只是一句产品口号落到技术层面却是一整套工程改造。规划需要的是信息组织和排序能力办事需要的是工具调用、状态管理、权限控制、交易安全和异常恢复能力。两者缺一不可。这篇文章以旅行 AI Agent 为技术主线结合飞猪帮帮这类产品形态拆解一个可落地的系统应该怎么设计先说清“能办事”和“会聊天”的差别再按意图识别、行程规划、工具调用、状态管理、线上评估、排错与上线准备六个层次展开。适合正在做 AI 应用、智能体、本地生活或旅行产品方向的开发者阅读。1. 先理解“能规划更能办事”对技术架构意味着什么1.1 从“生成内容”到“执行任务”传统大模型对话应用的核心链路是“输入文本 - 模型生成文本 - 输出文本”。用户问“杭州三天怎么玩”模型返回一段 Markdown 行程。这个过程对知识要求高但对系统能力要求低因为模型只需要组织文字不需要改变任何现实世界状态。旅行 AI Agent 的差别在于它需要改变现实世界状态。用户说“帮我预订下周三杭州到北京的往返高铁”系统不能只回复“好的建议你乘坐 9 点那班”而是必须完成查询余票、创建订单、发起支付、确认出票这一串动作。这种系统属于任务执行型 Agent核心特征是输出结构面向机器可执行的结构化动作而不是面向人阅读的自然语言段落。副作用调用真实业务接口会产生订单、锁定库存、扣款必须有权限和确认机制。可验证任务是否完成不能靠模型自我判断要靠业务系统回执。状态性规划、确认、支付、售后这些阶段之间需要连续状态不是一次问答就结束。“能规划更能办事”实际是两套能力的组合。规划负责把用户意图转成可执行的方案办事负责把方案转成真实的业务动作。如果只做规划系统本质上还是内容生成如果规划之后接不上办事用户仍然要在多个 App 之间来回切换。1.2 旅行场景为什么是 AI Agent 的典型战场旅行是少数能把“多约束规划”和“多系统执行”耦合得特别紧的场景。用户需求天然包含时间、地点、人数、预算、偏好、体力等多种约束。一个典型需求可能是“下周三带爸妈去北京玩三天预算八千不想太累。”这条需求里至少包含出发日期下周三需要计算具体日期。同行人员父母需要评估行程强度。目的地北京。游玩时长三天。预算八千。节奏偏好不想太累需要降低每日景点数量和移动距离。这些约束之间存在冲突。例如预算有限、时间又短、还要住得离景点近系统必须做权衡。同时真实服务还依赖实时库存和价格周一闭馆的博物馆、售罄的景区票、涨价的酒店、临时取消的航班都会让一个“看起来合理”的行程变成不可执行。这也是为什么旅行场景适合 AI Agent 落地。因为它的需求复杂度足够高调用工具种类足够多且最终效果能被交易结果验证。用户成功出票、入住、入园就是一次明确的完成信号。1.3 一句话变成完整动作链路从技术视角看“一句话就出发”背后是这样一条链路用户输入 - 意图识别与槽位抽取 - 约束校验与信息补齐 - 行程规划引擎生成多套候选方案 - 方案展示并等待用户确认 - 工具调度层拆解执行步骤 - 查询库存/价格/余票 - 创建订单并锁定资源 - 发起支付或引导用户确认 - 异步任务更新订单状态 - 推送预订结果与出行提醒每一环都有可能失败。用户输入缺少日期规划引擎无法生成方案商家库存不足订单无法创建支付超时资源被释放用户中途修改人数整个方案需要重新计算。生产环境里真正的难点不是在第一步生成一段漂亮话术而是让后面的每一步都可控、可重试、可解释。2. 旅行 AI Agent 的整体架构与核心模块2.1 分层架构接入、编排、工具、数据一个面向生产环境的旅行 Agent不建议把所有逻辑写在一个 Prompt 里。更稳妥的做法是分层设计各层职责独立。------------------------------------------ | 接入层App / 小程序 / Web / 语音助手 | ------------------------------------------ | 编排层意图识别、槽位管理、Agent 调度 | ------------------------------------------ | 工具层航班、酒店、门票、列车、用车、攻略 | ------------------------------------------ | 数据层用户画像、知识库、订单状态、日志 | ------------------------------------------接入层负责对话交互和渠道适配。编排层是 Agent 的大脑决定当前该调用哪个模型、该问用户什么问题、该执行哪个工具。工具层把业务系统包装成模型可调用的函数或 API。数据层为规划提供攻略知识为状态管理提供订单存储为排查问题提供日志。分层的好处是隔离复杂度。工具层接口发生变化时编排层不需要改新增一个“景区导览”服务时只需要注册一个新工具接一个语音入口时接入层扩展即可。2.2 核心模块不能只靠一个 LLM实践中一个完整的旅行 Agent 至少要包含这些模块意图识别与槽位抽取判断用户要规划、查询、预订还是改签并提取结构化参数。对话管理维护多轮上下文决定是否需要主动反问。规划引擎基于约束生成行程候选并对候选做排序。工具调用把模型输出的动作转成真实 API 请求。状态管理保存任务、订单、支付、售后的流转状态。安全策略确认、鉴权、幂等、风控和敏感信息过滤。评价模块记录任务完成情况用于离线评估和线上监控。人工兜底Agent 无法处理时无缝转接人工客服。这几块不是独立系统。规划引擎产生的结果要交给工具层去验证工具层的返回要回填到对话上下文中状态管理又要记录工具层创建的订单。因此模块之间必须有清晰的协议。2.3 技术选型的核心判断模块常用技术思路生产环境关注点大模型支持 Function Calling 的对话模型输出格式稳定性、延迟、成本、上下文长度Agent 编排当前常见工作流框架通常基于图或状态机重试策略、分支条件、可观测性知识库向量数据库或检索服务攻略数据时效、更新频率、检索准确率工具层统一 API 网关将业务接口包装为函数协议一致性、鉴权、超时、限流状态存储关系型数据库或带事务的 KV 存储状态流转一致性、幂等控制消息队列异步处理预订、支付回执、通知消息不丢、不重、可追溯选型时最容易犯的错误是追求“模型能力一步到位”。实际上工具层协议的稳定性比模型聪明程度更重要。模型可以换工具协议不可轻易变。2.4 为什么需要独立规划引擎而不是纯提示词旅行行程本质是一个带约束的组合优化问题。用户希望三天玩得轻松又要覆盖标志性景点还要考虑景点之间通勤时间、营业时间、预约政策。这些问题由 LLM 直接生成文本结果可能流畅但不可验证。独立规划引擎负责把“行程生成”从“语言生成”中分离出来。先由模型做意图理解和偏好解析再由规划引擎用规则、搜索或约束求解生成结构化方案最后由模型把方案转成用户能看懂的自然语言。这样即使模型替换行程质量仍然有规则层兜底。3. 从“一句话”到结构化输入意图识别与槽位抽取3.1 一个请求要怎么“拆”先看这条用户输入“下周三带爸妈从杭州去北京玩三天预算八千不想太累。”模型第一层输出应该是意图和槽位而不是直接写行程。合理的解析结果类似{ intent: create_itinerary, slots: { departure_city: 杭州, destination_city: 北京, people: [ {type: adult, count: 2, relation: parent}, {type: self, count: 1} ], start_date: 2025-07-16, duration_days: 3, budget_total: 8000, pace: relaxed, preferences: [历史, 美食, 少走路] } }这里有两个细节值得注意。第一start_date不能由模型随便填最好由规则层根据“下周三”计算避免模型算错日期。第二同行人员不能只记数字要记录年龄和关系因为带老人和带朋友出游的节奏完全不同。3.2 槽位抽取必须带校验生产系统中模型抽取结果必须通过结构校验。可以用 JSON Schema 或 Pydantic 这类工具做约束。from pydantic import BaseModel, Field, ValidationError class Traveler(BaseModel): type: str Field(..., pattern^(adult|child|senior)$) count: int Field(..., ge1, le20) class ItineraryRequest(BaseModel): intent: str departure_city: str destination_city: str travelers: list[Traveler] start_date: str duration_days: int Field(..., ge1, le30) budget_total: float Field(..., gt0) pace: str Field(normal, pattern^(relaxed|normal|intensive)$) try: req ItineraryRequest(**parsed_from_llm) except ValidationError as e: # 进入提问澄清流程而不是直接发下游服务 print(e.errors())校验拦截的不只是错误参数还能避免模型幻觉产生的非法值。例如天数取到 999、人数填成负数这些数据如果不拦截下游预订接口很可能直接报错甚至造成错误订单。3.3 信息缺失时优先问哪个问题用户一开始不会提供全部信息。系统必须决定反问顺序。推荐的优先级是影响资源查