从意图经济到旅行Agent:用大模型工具调用实现“甩手掌柜”

发布时间:2026/9/4 2:04:14
从意图经济到旅行Agent:用大模型工具调用实现“甩手掌柜” “想做旅行里的甩手掌柜”这其实是当下「意图经济」讨论里最容易被误读的一句话。多数人以为所谓意图经济就是“AI帮我搜攻略、推荐酒店、拼一条行程”。如果只做到这一步那它仍然是搜索不是执行。真正让“甩手掌柜”成为一个技术问题的是另一件事用户把接下来的选择、比价、订票、突发改签这一连串流程全部授权给一个能理解自然语言、能调用外部服务、能按步骤执行的智能体。这篇文章不打算复述“意图经济”的各种热搜定义。我想把它拆成一个CSDN读者能动手验证的问题如果你要用现有的大模型 工具调用能力做一个“能听懂旅行想法、能把想法变成结构化任务、再调用旅行服务完成步骤”的最小Agent技术链路长什么样哪些环节最值得做哪些地方最容易被过高期待坑到。全文按“概念 - 架构 - 代码 - 验证 - 排错 - 工程建议”的顺序展开最后会给出一套适合普通后端或AI工程师上手的落地路径。1. 为什么“甩手掌柜”是个技术问题先想一个真实场景一个人说“我下周想去厦门玩四天预算三千五不太想去全是网红店的地方”。这句话在传统旅行平台那里会怎么被处理平台会做关键词切分提取“厦门”“四天”“三千五”然后给出酒店列表、机票列表、一堆攻略。后面的所有比较、切换、下决心、下单消费者还是得自己做。平台完成的是“供给检索”不是“目标执行”。真正的意图经济希望完成的链路是用户表达目标而不是表达关键词系统把目标翻译成可执行的约束条件智能体调用不同服务方比较不同方案并给出建议用户在关键节点确认授权而不是逐页搜索执行结果全程可追踪、可撤回、可人工兜底。注意这里最难的不是第一步也就是用户意图的识别。今天的大模型已经能把“我不想去网红店”转成一个偏好标签列表。难点在于后面几步它要在真实环境中做连续决策跨平台调用数据还要保证每一步出错都能暴露给用户。所以“甩手掌柜”本质上是消费决策权和控制权的转移。传统OTA的产品逻辑是“让用户自己比较”意图经济的产品逻辑是“让Agent帮用户完成比较并把决策点以更小的粒度交还给用户”。这个转移对技术栈的考验大于对某个大模型推理能力的考验。还有一个很多人没注意到的点年轻人愿意当甩手掌柜不代表他们愿意放弃知情权。他们要的是“我不要太累但你做的每件事我都能看懂能随时叫停”。这对Agent的可解释性提出了很高要求。一个只会给出最终推荐、却不说明这个推荐基于什么约束的智能体很难真正被信任。2. “意图经济”和过去的推荐系统有什么本质区别“意图经济”这几年在海外科技圈讨论升温国内跟着也有不少关于“Agent电商”“管家式服务”的讨论。它听起来像新的商业概念但放到技术里你可以把它理解成一次交互范式切换。过去的互联网商业模式核心是流量经济和注意力经济。平台猜测你想要什么用推荐流和广告的方式把商品推到你面前。这里有个隐藏问题用户的真实意图是被猜的平台永远通过你的点击、停留时长、搜索历史来推断。意图经济的逻辑不一样。用户主动说出目标Agent接收的是一个比较明确的任务而不是一堆行为信号。比如“下周四从上海去成都出差希望下午到且到市区方便”和“我想买一双适合雨天通勤的鞋”这些是目标不是“关键词”。这两种模式放到技术里有根本差异维度传统推荐/搜索意图经济与Agent输入关键词、点击、浏览记录自然语言目标与约束条件主流程召回、排序、展示意图解析、任务拆解、工具调用服务边界平台内列表与详情页多个外部服务和订单系统用户参与用户比较和下单用户在决策点授权并审核核心指标点击率、转化率、GMV任务完成率、纠错成本、用户信任度从这个表能看到意图经济并不是把推荐对话框换成了聊天框。它需要的是“任务执行架构”而不只是“对话生成架构”。这也是为什么今天大量团队在做Agent编排、工具协议、流程记忆、订单状态同步因为这些才是执行层要面对的事。现在的旅行场景还远没有到“全自动”阶段真正的机会在于把其中某一段做深比如把“行前规划”变成可执行清单把“机酒绑定方案”做成一个API把“行程动态调整”做成一个带记忆和规则的动作系统。这比再造一个超级App更现实。3. 一个旅行Agent的技术链路拆解从工程视角看一个能落地的旅行Agent至少包含四个模块意图理解层、规则与工具层、记忆层、执行与确认层。先解释一下什么叫做Agent。在本文语境里Agent不是简单的“接入大模型的聊天机器人”而是指一个能通过多轮推理决定下一步动作并能调用外部工具或API来完成目标的系统。它通常由模型、工具、循环控制三部分组成。3.1 意图理解层负责把用户的自然语言转换成结构化意图。这里要识别的不只是“目的地”“日期”“预算”还包含隐性约束比如“不要红眼航班”“希望走路就能到地铁”“亲子游不想太累”“雨天不影响体验”。很多开发者一开始只做实体抽取这远远不够。意图理解层应该输出整个任务的约束集并允许用户后续追加或修改约束。比如用户看了方案后说“第二天不想爬山”系统要把新的约束合并进原任务而不是重新问一遍所有信息。3.2 规则与工具层工具层是Agent能够“做事情”的基础。旅行场景里需要的工具包括航班搜索、酒店搜索、天气查询、城市交通、景点开放时间、订单预订等。每一个工具都应该有清晰的输入输出Schema。Agent要能根据意图从工具列表里选出正确的工具并把结构化参数传给工具再把工具返回的结果与用户约束做比对。3.3 记忆层如果用户连续说“我带着父母”“我们每天尽量不换酒店”“预算可以略微超一点”Agent必须记住这些对话期内的约束。更完整的系统还需要跨会话记忆比如用户上次去过哪里、偏好什么住宿风格这些会在下一次规划时作为画像输入。相比通用知识记忆层更重要的作用是避免重复。用户已经推翻过“早上赶景点”的方案如果下一轮Agent又推荐一个早上七点出发的安排那就说明记忆没有真正参与决策。3.4 执行与确认层这是最容易出错也最需要安全设计的一层。Agent可以替你查询不等于它可以替你下单。涉及支付和预订的操作必须有明确的人工授权点。工程实现上应该把Agent的任务分成两类只读类操作比如查航班、比价、查询退改政策写操作比如创建订单、锁定库存、发起支付。写操作必须走“Agent提交意图 - 用户确认 - 服务端执行”的流程。这四层不是顺序执行一次就结束。真实场景里用户会不断调整计划Agent需要反复运行“理解 — 拆解 — 调用 — 反馈”的循环。4. 环境准备与最小工程目录下面进入可操作部分。为了不让你把时间浪费在复杂的框架依赖上我选择用最通用的Python 大模型Function Calling能力来演示一个最小旅行规划Agent。本演示要求Python 3.10 及以上一个支持Function Calling或Tool Calling的模型示例中不依赖真实机票和酒店服务用本地Mock Service代替便于跑通流程。具体模型选择请以你实际拿到的服务为准。不同的模型对工具调用的支持程度和令牌格式有差异但核心思路一致。4.1 创建项目目录mkdir traveler-agent cd traveler-agent python3 -m venv .venv source .venv/bin/activate pip install openai python-dotenv pydantic4.2 配置环境变量创建.env文件cat .env EOF OPENAI_API_KEYsk-your-key LLM_MODELgpt-4o-mini EOF如果你的服务是兼容OpenAI接口的国内模型或私有化模型也可以额外配置BASE_URL。示例代码里会通过os.getenv(BASE_URL)读取不配置就使用默认值。4.3 目录结构traveler-agent/ ├── .env ├── intent_schema.py # 意图解析的Pydantic模型与工具Schema ├── tools.py # 本地工具服务 └── agent.py # Agent主循环这个工程规模足够说明问题又不至于把重点淹没在框架配置里。5. 把自然语言旅行想法变成结构化意图第一段代码解决“意图理解层”的问题。用户说“下周想去厦门四天预算3500偏好老城区和本地小吃”我们不能直接把这句话发给后端服务而是要先把内容整理成结构化对象。创建intent_schema.py# 文件路径traveler-agent/intent_schema.py from typing import Optional from pydantic import BaseModel, Field class TravelIntent(BaseModel): destination: str Field(description目的地城市通常是用户明确提到的地名) start_date: Optional[str] Field(None, description出发日期格式YYYY-MM-DD) days: int Field(0, description旅行天数) budget: Optional[float] Field(None, description总预算单位默认为元) must_have: list[str] Field(default_factorylist, description用户明确要体验的事物) avoid: list[str] Field(default_factorylist, description用户希望避免的体验或场景)这段代码不承担最终推荐逻辑它的作用是把模型输出的JSON固定成后续所有模块都能引用的类型。接下来定义工具描述。旅行工具比较适合轻量级的Function Calling即让模型从两个操作中选择“查询航班”或“查询酒店”。在tools.py中写一个本地Mock服务模拟工具的调用结果# 文件路径traveler-agent/tools.py from typing import Any def search_flights(city: str, date: str) - dict[str, Any]: 演示用Mock航班查询真实项目中替换为航司或聚合服务API。 print(f[tool] 查询 {city} 在 {date} 的航班) return { city: city, date: date, flights: [ {flight_no: MF8101, depart: 08:20, arrive: 10:05, price: 860}, {flight_no: MU5883, depart: 14:40, arrive: 16:30, price: 720}, ], } def search_hotels(city: str, check_in: str, nights: int) - dict[str, Any]: 演示用Mock酒店查询返回符合位置的酒店列表。 print(f[tool] 查询 {city} 从 {check_in} 起 {nights} 晚酒店) return { city: city, check_in: check_in, hotels: [ {name: 老城区附近舒适型酒店, avg_price: 420, tags: [老城区, 交通便利]}, {name: 海景度假酒店, avg_price: 890, tags: [海景, 偏度假]}, ], }为什么先写成Mock因为这样能让你完全聚焦在Agent控制逻辑上。等整条链路跑通后再替换成真实接口排查范围会小很多。6. Function Calling让Agent学会“调用工具”Intention模型和工具函数都有了现在是核心环节让大模型根据用户输入的旅行意图决定调用哪个工具、传什么参数。我们使用OpenAI SDK的Function Calling机制。它做的事情可以简单概括为告诉模型系统里存在哪些工具模型根据对话内容返回一个“请求调用工具”的结构化指令而不是直接生成一段人类自然语言。创建agent.py# 文件路径traveler-agent/agent.py import json import os from dotenv import load_dotenv from openai import OpenAI from intent_schema import TravelIntent from tools import search_flights, search_hotels load_dotenv() client OpenAI( api_keyos.getenv(OPENAI_API_KEY), base_urlos.getenv(BASE_URL) or None, ) MODEL os.getenv(LLM_MODEL, gpt-4o-mini) def build_tools(): return [ { type: function, function: { name: search_flights, description: 查询某城市某一天的航班信息。, parameters: { type: object, properties: { city: {type: string, description: 目的地城市}, date: {type: string, description: 出发日期格式YYYY-MM-DD}, }, required: [city, date], }, }, }, { type: function, function: { name: search_hotels, description: 查询某城市的酒店列表。, parameters: { type: object, properties: { city: {type: string, description: 目的地城市}, check_in: {type: string, description: 入住日期}, nights: {type: integer, description: 入住晚数}, }, required: [city, check_in, nights], }, }, }, ] def extract_intent(user_input: str) - TravelIntent: 第一轮让模型把自然语言解析为结构化约束。 resp client.chat.completions.create( modelMODEL, response_format{type: json_object}, messages[ {role: system, content: 你是旅行意图解析器只输出JSON。}, {role: user, content: user_input}, ], ) return TravelIntent.model_validate_json(resp.choices[0].message.content) def run_agent(user_input: str): intent extract_intent(user_input) print(结构化意图, intent.model_dump()) tool_map { search_flights: search_flights, search_hotels: search_hotels, } messages [ { role: system, content: 你是一个旅行管家Agent。请分析用户当前问题选择需要的工具来查询信息。, }, {role: user, content: user_input}, ] for step in range(3): resp client.chat.completions.create( modelMODEL, messagesmessages, toolsbuild_tools(), tool_choiceauto, ) msg resp.choices[0].message if not msg.tool_calls: print(Agent最终回答, msg.content) return messages.append(msg) for tool_call in msg.tool_calls: func_name tool_call.function.name func_args json.loads(tool_call.function.arguments) print(f[step {step 1}] 调用工具{func_name}参数{func_args}) if func_name not in tool_map: content json.dumps({error: f未知工具 {func_name}}) else: result tool_map[func_name](**func_args) content json.dumps(result, ensure_asciiFalse) messages.append( { role: tool, tool_call_id: tool_call.id, content: content, } ) print(Agent达到最大步骤数需要人工介入或追问信息。) if __name__ __main__: demo_input 我想下周四去厦门玩4天预算3500住在老城区附近尽量不赶早班飞机。 run_agent(demo_input)这段代码最关键的地方在于messages列表的循环追加逻辑。每次模型请求一个工具调用我们就执行工具并把工具结果以roletool的消息返回给模型。模型看到真实结果后才会继续规划下一步。没有这一步模型只能“猜测”工具能返回什么就会出现大量幻觉。运行命令python agent.py在真实模型可用的情况下你会看到类似这样的执行轨迹结构化意图 {destination: 厦门, days: 4, budget: 3500.0, must_have: [老城区, 本地小吃], avoid: [早班飞机]} [step 1] 调用工具search_flights参数{date: 2025-XX-XX, city: 厦门} [tool] 查询 厦门 在 2025-XX-XX 的航班 ... Agent最终回答 我建议选择下午到达厦门的航班...这里你应该注意一件事模型要用工具需要把意图层解析出来的日期、城市等字段作为工具参数传给后端。如果意图解析环节丢字段后面工具调用就会缺参数或传错值。7. 运行结果验证不要只看“回答是否漂亮”不少初学者跑通一次Agent对话看到模型说出了一段完整回答就以为成功了。但在真实场景里我们要验证的不只是回答内容而是整个执行链路是否正确。建议按三个层次验证。第一层是意图解析的准确性。打印intent.model_dump()人工检查“目的地、天数、预算、偏好、规避项”是否都对。最容易错的是日期模型可能根据当前时间推算错“下周”其次是用户表达偏好时会说反话比如“不要太赶”解析器需要把“不要太赶”当成一个真实约束。第二层是工具调用序列的合理性。检查Agent是先查机票还是先查酒店查询时是否把用户偏好放进了约束一个糟糕的Agent可能会在用户没说不考虑价格时直接忽略价格信息。验证方法很简单把整个调用过程的JSON日志保存下来人工看每一步的参数。第三层是最终答案的可执行性。Agent给用户输出的结果必须包含可操作信息比如航班时间、航班号、酒店区域、大致的价格区间。如果模型只回答了“我建议你乘坐下午的航班住在老城区”却没有提供任何可预订的对象那它只是把用户的话换了一种说法。为了便于观察建议在agent.py里把每一步的工具调用结果都写入日志文件with open(trace.jsonl, a, encodingutf-8) as f: f.write(json.dumps({user: user_input, intent: intent.model_dump()}, ensure_asciiFalse) \n)生产环境通常会把trace.jsonl换成日志平台用request_id串起一次Agent会话的全部事件。如果在实际运行时出现结果异常先不要急着换模型按下面这个方向排查问题现象可能原因排查方式解决方案工具没有被调用模型不支持Function Calling或工具描述不清晰查看API返回是否有tool_calls字段换支持工具调用的模型简化工具名称和描述工具参数明显错误意图解析没有保留用户约束打印结构化意图检查字段是否缺失在系统提示词中要求模型补充缺失参数并二次确认模型反复调用同一个工具缺少终止条件或最大步骤限制检查消息历史和调用次数设置max_steps并让模型在信息充足时直接回答工具返回内容模型看不懂返回纯JSON但缺少说明阅读模型下一轮回答在工具返回内容中加入简单中文摘要字段Agent回答不基于工具结果代码里把工具结果丢弃了检查roletool消息是否确实追加进了对话把工具结果以tool消息传回而不是只打印涉及支付时存在误下单风险缺少人工确认流程审查代码全部调用点写操作一律拆到独立模块并增加用户确认接口这个表的最后一行最容易忽略。工具调用在Demo里是查询到了生产里就会变成“创建订单、锁定库存、发起支付”。任何写操作都必须与Agent推理主循环做物理隔离不能因为模型多轮对话“表现得聪明”就让它直接执行。8. 旅行场景里做Agent最容易踩的坑现在不少团队做旅行助手失败失败点不在模型不够聪明而在工程上低估了旅行场景特有的复杂性。下面几个坑是这个领域比较典型的。第一个坑是“把动态信息当静态知识”。大模型的知识截止时间是有限的。航班时刻、酒店价格、景区开放状态全部是时效性数据Agent必须走实时查询接口。如果你依赖模型的内部记忆去回答“明天厦门的天气”“最近大兴机场的航班变化”系统永远不可靠。第二个坑是“低估约束冲突处理”。用户口中的偏好经常互相冲突预算三千五又要求海景房时间很短又希望所有景点都覆盖。朴素实现会把用户约束都塞进搜索条件然后发现搜不到结果。实际情况里需要Agent能主动跟用户澄清冲突提出“海景房和预算只能先满足一个你要哪个优先”。现在的对话Agent通常缺少这种“主动暴露冲突”的设计。第三个坑是“退改和取消没有纳入执行链”。旅行订单不是一手交钱一手交货航班延误、酒店不可取消、天气突变这些都是执行中的常态。意图经济里Agent要能对已经做过的决策做回滚或改期操作。这要求后端提供完整的取消与改期API而不是只提供一个预订接口。第四个坑是“不设计人工兜底”。即使在技术上允许Agent自动完成支付真实业务也不建议一上来就全自动。稳妥的落地方式是“Agent建议 用户确认”先让Agent处理信息搜集和方案生成把决策权保留给用户。等运行数据和用户信任积累起来再逐步开放更多自动化权限。9. 给开发者和产品团队的三条工程建议如果你正在考虑做一款“旅游甩手掌柜”类产品下面几条经验值得留在方案评审里。第一把“意图”和“操作”分两个系统实现。不要让同一个大模型既负责高层的行程规划又负责低层的航班预订。可以用“规划模型”负责拆解和推荐用“执行服务”负责和真实旅行服务商打交道。这条边界能最大程度减少模型犯错带来的业务风险。第二所有写操作接口都要做到幂等。Agent在超时后很可能会重试一次请求。如果接口没有幂等机制用户可能收到两笔相同订单。工程上可以使用订单号或请求ID去重每笔操作在进入支付前必须生成唯一业务编号。具体代码如下def create_order_with_idempotency(user_id: str, idempotency_key: str, order_data: dict): 根据 idempotency_key 保证同一请求只创建一个订单。 existing order_store.get(idempotency_key) if existing: return existing, False order order_store.create(user_id, order_data) order_store.save_with_key(order.id, idempotency_key) return order, True幂等逻辑不复杂但它是自动化交易场景的安全底线。第三用“可被Agent调用的API”思维改造旅行服务。如果你不准备做面向用户的Agent也可以做面向Agent的供应商。比如把酒店预订整理成统一Schema支持JSON输入输出、稳定返回“可订/不可订/价格”、把退改规则结构化。未来一定会有大量Agent需要调用这类服务谁能提供稳定、低幻觉、带明确错误码的工具接口谁就能在意图经济里占据位置。对后端开发者的启发是意图经济带来的不一定只是“对话式App”也可能是新一轮API经济。Agent是消费方你的服务是供给方。要提前考虑接口被机器调用时的行为错误信息要结构化字段命名要稳定API版本要兼容。10. 总结回到开头那道题。年轻人想当旅行里的甩手掌柜这句话真正的含义是用户愿意把目标表达清楚也愿意在关键节点做确认但不希望被搜索列表和比价过程消耗掉所有精力。这背后对应的不是更聪明的聊天机器人而是一整套“意图解析 - 任务拆解 - 工具调用 - 结果反馈 - 人工确认”的系统能力。本文用不到一百行代码跑通了一个最小旅行Agent的完整链路。它能做的事情还非常有限但用来理解意图经济的技术骨架已经足够。你可以在这个基础上继续往下做把Mock工具换成真实供应商API加入跨会话记忆增加用户确认界面再逐步把行程动态调整、订单状态同步等能力接进来。如果要用一句话给这个方向定调我会说意图经济真正的门槛不是“让AI听懂人话”而是“让AI做对事并在做错前停下来问你”。谁先把后者做好谁才能真正赢得想做甩手掌柜的那群年轻人。