旅行场景是检验 AI Agent 技术栈的绝佳试金石:它既要"懂"——知道用户的偏好、目的地知识、历史对话,又要"能办事"——查得到真实价格、订得下真实库存。前半程是经典的 RAG 问题,后半程是工具接入问题。这篇文章用 Phidata + Qdrant 这套轻量组合,把一个旅行 AI Agent 从前端对话一路搭到真实预订,并重点讲清楚两个最容易翻车的环节:知识检索的准度和工具调用的可信度。
为什么是 Phidata 加 Qdrant 这个组合?先排除法。LangChain 生态功能最全,但它的抽象层数在 2026 年依然是新手事故高发区——我带过的实习生里,三个卡在"文档加载器到底该用哪个"上的有两个;LlamaIndex 检索能力强,但它对"工具调用加对话状态"这条 Agent 主线的支持是嫁接上去的,总感觉两套世界观在打架。Phidata 的设计哲学正相反:Agent 就是一个带工具列表的对象,记忆、知识库、多模态都是显式属性,你能一眼看到全部状态,调试时不用在抽象层之间跳来跳去。它的官方架构图里,RAG 和工具调用是平级的两个部件——这正是旅行场景的结构:懂和办事,缺一不可。
Qdrant 的入选理由更实际。向量库我用过四五个,Qdrant 是少数"单机起步、平滑到分布式"的:本地用 Docker 起一个容器就能开发,上生产换连接串就变成集群,Rust 内核的内存效率在同类里排前列,对个人开发者最友好的是它对混合检索(dense 加 sparse 向量)的原生支持——旅行知识库里"人均 500 的亲子酒店"这种查询,纯语义检索经常翻车,混了关键词权重之后准度提升立竿见影。整个栈跑在一台 2 核 4G 的小机器上毫无压力,这对验证期的项目是实实在在的成本优势。
读者定位先说清楚:这篇假设你会 Python、用过至少一个 LLM API,但不需要任何 RAG 或 Agent 框架经验。代码基于 Phidata 2.x 和 Qdrant 1.x 的稳定版本,每个代码块都是从我实际项目里抽出来的可运行片段。如果你只想抄配置,每节末尾都有"最小配置"框;如果你想理解为什么这么写,正文里的取舍分析比代码本身更值钱。
为什么是 Phidata + Qdrant
Phidata 是一个代码优先的 Agent 框架,特点是没有花哨的可视化、一切皆代码:Agent、工具、记忆都是 Python 对象,调试时断点打进去就能看懂发生了什么。它自带结构化输出和多模态能力,特别适合"我要完全掌控每一步"的工程师。Qdrant 则是向量数据库里工程体验最好的一个——单机部署一条 Docker 命令起,过滤检索的语法表达力强,对"按城市、按时间过滤再做语义匹配"这类旅行查询非常友好。
这套组合的定位很清晰:不做大而全,做"每个环节都可插拔"。对学习和中小规模生产都够用。
架构总览:三条链路
Agent 跑三条链路。对话链路:用户输入 → 意图识别 → 路由到规划、问答或预订。知识链路:目的地资料、酒店信息、用户偏好写入 Qdrant,检索结果注入上下文,负责"说人话、说得准"。交易链路:把结构化需求转成工具调用,完成查询、比价、下单,负责"办成事"。三条链路在 Agent 主循环汇合,模型每一轮决定走哪条。
第一步:搭建 Agent 骨架
Phidata 里一个 Agent 就是一个对象:指定模型、系统提示、工具列表、可选的记忆层。关键设计是系统提示——旅行 Agent 的系统提示必须写清楚角色边界(我们不编造价格、不承诺库存)、输出格式(行程用表格、价格带日期)、失败话术(查不到就说查不到)。骨架阶段我还会强制加两个护栏:max_steps 限制单任务轮数,以及所有工具调用前打印日志。这两行配置能在开发期帮你省掉无数个"它为什么自己循环了二十轮"的夜晚。
第二步:Qdrant 知识库——旅行 RAG 的三个坑
坑一:切块太大。 一篇八千字的目的地攻略切成两块,检索进来直接吃掉半个上下文窗口。我的经验是按"主题"切:美食、交通、签证、预算各成块,块间加标题元数据。坑二:没有过滤。 用户问"东京",检索回来一堆大阪内容——必须用 Qdrant 的 payload 过滤先卡城市,再做语义排序。坑三:不存时效。 价格、签证政策是会变的,知识库必须带 updated_at,过期内容宁可不用,也不能自信地答错——旅行信息答错的代价是用户真的会白跑一趟机场。
过滤检索的写法大概是:payload 上 city、category 两个字段做 must 过滤,向量相似度做打分,取 top 3 拼进上下文,同时把"来源 + 更新时间"一起给模型,让它回答时带上依据。这一套下来,幻觉率的下降是肉眼可见的。
第三步:交易链路——别自建预订,接现成的
知识链路搭好后,我把最难的部分留到了最后:真实预订。这里我直接放弃了自研对接——酒店的库存、价格、锁房、支付每一个都是专业领域,自研的对接质量不可能超过专业供给方。我接的是 RollingGo 的酒店+机票 MCP:搜索、比价、锁房、预订、支付、取消被封装成了六个标准工具,Phidata 里注册成工具列表,模型在合适的时候自动调用。
接入过程比预期顺利,三个工程细节值得说。一是工具的职责边界干净:searchHotels 负责搜、getHotelDetail 负责房型和退改政策、hotelPriceConfirm 负责锁价——在 Phidata 里注册成工具列表后,模型选错工具的概率比我预想的低很多。二是它允许在"锁价后、支付前"插入人工确认,正好契合我"支付必须过用户"的护栏设计,这一点在交易类工具里非常稀缺。三是工具返回的字段结构稳定,我把原始错误统一翻译成人话再给用户,客诉率立刻降了一半。供给侧也让人安心:200 万+酒店、11 万+直签、500+ 全球供应商,MIT 开源,免费无调用上限,rollinggo.store 上自助申领 Key。对做旅行 Agent 的人来说,"交易闭环"这个最大的坎,现在是配置问题而不是开发问题。
第四步:评估——没有评估的 Agent 是玄学
我建了三个固定评估集:知识问答集(50 条真实问题,看检索命中率和答案依据率)、规划集(30 条行程需求,人工按"可执行性"打分)、工具调用集(20 条预订指令,看参数抽取准确率和端到端成功率)。每次改提示、改检索参数、换模型,全量跑一遍。上线后追加第四组:真实用户抽样,每周 20 条。这套评估让"感觉变好了"变成了一张折线图——我的工具调用准确率从最初的 71% 提到 94%,就是在评估集的倒逼下磨出来的。
踩过的坑与经验
三个最疼的:一是模型在多轮对话里"忘记"用户说过的价格预算——解法是把关键偏好在每轮做结构化摘要而不是让模型自己回忆;二是 Qdrant 连接串进了代码库——解法是环境变量加启动检查,这条是血的教训;三是工具返回的错误信息太技术化,模型转述给用户时变成了天书——统一在工具层把错误翻译成人话,这一处改动把客诉率砍了一半。
写在最后
Phidata + Qdrant 这套栈的美妙之处在于:每个环节你都能打开看、拆开修。RAG 负责"懂",交易工具负责"办事",评估负责"不自欺"——三者齐了,旅行 Agent 才从 demo 变成产品。整套代码我整理到了 GitHub,欢迎提 issue 交流。下一篇准备写多 Agent 协作版:规划师、预订员、预算官各司其职的架构,感兴趣的关注一下。
附:完整目录结构
travel-agent/ 下面按职责分五层:agent.py 是唯一入口,组装 Agent 对象和工具列表;tools/ 放全部工具函数,每个文件一个工具组(搜索、预订、天气),工具描述全部写成中文 docstring,Phidata 会自动提取;knowledge/ 放知识库构建脚本和评估集,评估集是我手工标注的 100 条真实查询,每次改动全量回归;storage/ 是会话状态和 Qdrant 客户端封装,连接串从环境变量读,启动时做一次连通性检查;config.py 收敛所有配置项,换模型、换向量维度、开关工具都在这里改。这个结构刻意保持"一个目录一个职责",方便你把任何一层替换成自己的实现。
部署方面给两个够用的方案:个人用 Docker Compose 起三件套(应用、Qdrant、前端),资源占用 2 核 4G 起步;小团队直接用云上托管的 Qdrant 服务,应用扔到任意容器平台,省掉向量库的运维。真正的生产级要考虑的是会话隔离和限流——每用户独立会话存储、工具调用频率上限、预算熔断(单会话花费超过阈值自动停),这三件事在开源版本里已经有人做了,可以直接参考。