公司动态

基于LangChain与LLM的AI旅行助手:从Agent原理到工程实践

📅 2026/8/24 5:37:53
基于LangChain与LLM的AI旅行助手:从Agent原理到工程实践
1. 项目背景与核心概念在规划一次旅行时你是否也经历过这样的困境出发前花费数小时甚至数天时间在十几个浏览器标签页间反复横跳只为拼凑出一份看似完美的行程单。然而当你真正踏上异国他乡的土地这份精心准备的“攻略”却瞬间失效——餐厅关门了、景点临时维护、交通方式没写清楚或者你突然想临时改变计划却不知从何下手。传统的旅行规划工具无论是笔记软件、地图收藏夹还是专门的行程App都只解决了“计划”阶段的问题它们本质上是静态的、离线的文档。一旦进入“执行”阶段这些工具就失去了活力无法根据实时情况如你的位置、天气、兴趣变化、突发状况提供动态的、上下文相关的指导。这正是Passage AI这类智能旅行助手试图解决的痛点。它不仅仅是一个行程规划器更是一个在你落地后能“活过来”的实时向导。其核心概念可以概括为一个由大语言模型驱动的、具备上下文感知能力的旅行Agent。它解决了什么问题规划阶段的决策疲劳通过自然语言对话理解你的旅行偏好如“我想进行一次放松的文化之旅预算中等喜欢小众咖啡馆”自动生成结构化的行程建议省去大量手动搜索和比对的时间。执行阶段的信息断层将静态的行程计划转化为一个能与你实时交互的智能体。当你到达目的地它可以基于你的GPS位置主动推送附近的景点介绍、餐厅推荐、交通指引甚至在你提问时如“这附近有什么评价不错的素食餐厅”给出精准回答。动态调整与个性化旅行充满变数。Passage AI能够根据实时信息如天气突变、景点拥挤度、你的体力状态建议你调整当天的行程顺序实现真正的个性化旅行体验。为什么开发者需要关注对于开发者而言Passage AI代表了一个非常典型的AI Agent智能体应用场景。它完美结合了多个技术栈大语言模型作为理解和生成自然语言、进行复杂推理的“大脑”。Agent框架用于规划拆解用户目标为步骤、工具调用搜索、地图、预订、记忆记住用户偏好和对话历史。实时数据集成接入地图API、POI兴趣点数据库、天气API、实时交通API等为决策提供事实依据。多模态交互未来可能整合图像识别识别地标、语音交互等。构建这样一个应用是对提示词工程、工具调用、上下文管理、数据管道构建等AI工程化能力的绝佳实践。接下来我们将从零开始探讨如何构建一个简化版的旅行AI Agent核心系统。2. 技术栈选型与环境准备要构建一个类似Passage AI的应用我们需要一个模块化的技术栈。以下是一个基于Python的、高性价比且易于上手的方案。核心环境与版本说明操作系统macOS / Linux (推荐) 或 Windows (WSL2)。本文示例在 Ubuntu 22.04 LTS 上开发。Python: 3.9 或 3.10。这是目前大多数AI库兼容性最好的版本。包管理使用pip和venv创建虚拟环境避免依赖冲突。IDEVS Code 或 PyCharm安装Python插件即可。核心库与框架大语言模型接入OpenAI API或开源模型。初期开发推荐使用OpenAI GPT-4/3.5 Turbo API稳定且效果最佳。后续可考虑用ollama或vllm本地部署开源模型如Qwen2.5、Llama 3。openai库用于调用官方API。langchain库一个强大的框架用于简化构建基于LLM的应用程序特别是其Agent和Tools模块。Agent框架LangChain是当前生态最成熟的选择。它提供了构建Agent所需的核心抽象AgentExecutor,Tools,Memory。工具Tools集成网络搜索langchain的SerpAPIWrapper或DuckDuckGoSearchRun。地图与地点Google Places API或OpenStreetMap (Nominatim)。前者更精确商业后者免费但有限制。天气OpenWeatherMap API。计算与代码langchain内置的llm-math工具。记忆Memory使用langchain的ConversationBufferMemory或ConversationSummaryMemory来维持对话上下文。后端与APIFastAPI。轻量、异步、高性能非常适合构建AI应用的API层。前端可选简单的演示可以用Gradio或Streamlit快速构建Web界面。生产环境可用React/Vue。向量数据库进阶用于存储和检索本地旅行知识库如你整理的精品攻略。可选ChromaDB(轻量) 或Qdrant。环境搭建步骤# 1. 创建项目目录并进入 mkdir ai_travel_agent cd ai_travel_agent # 2. 创建Python虚拟环境 python3 -m venv venv # 3. 激活虚拟环境 # Linux/macOS source venv/bin/activate # Windows venv\Scripts\activate # 4. 安装核心依赖 pip install openai langchain langchain-openai langchain-community fastapi uvicorn python-dotenv gradio # 5. 安装可选工具依赖按需 pip install googlemaps python-weather duckduckgo-search项目结构预览在开始编码前我们先规划一个清晰的项目结构。ai_travel_agent/ ├── .env # 存储API密钥等敏感信息 ├── app.py # 主应用入口FastAPI或Gradio ├── core/ │ ├── __init__.py │ ├── agent_builder.py # Agent构建逻辑 │ ├── tools/ # 自定义工具集 │ │ ├── __init__.py │ │ ├── search_tool.py │ │ ├── map_tool.py │ │ └── weather_tool.py │ └── memory_manager.py # 记忆管理 ├── config.py # 配置文件 └── requirements.txt # 依赖列表重要API密钥管理永远不要将API密钥硬编码在代码中。使用.env文件和环境变量。# 在项目根目录创建 .env 文件 touch .env在.env文件中填入你的密钥需要先去相应平台注册获取OPENAI_API_KEYsk-your-openai-api-key-here GOOGLE_PLACES_API_KEYyour-google-places-api-key OPENWEATHER_API_KEYyour-openweather-api-key SERPAPI_API_KEYyour-serpapi-key # 如果使用SerpAPI在config.py中读取配置# config.py import os from dotenv import load_dotenv load_dotenv() # 加载 .env 文件中的变量 OPENAI_API_KEY os.getenv(OPENAI_API_KEY) GOOGLE_PLACES_API_KEY os.getenv(GOOGLE_PLACES_API_KEY) # ... 其他配置3. 核心原理与Agent架构拆解在深入代码之前理解LangChain中Agent的工作原理至关重要。一个AI Agent的核心循环可以简化为感知 - 规划 - 行动 - 观察 - 循环。在LangChain的语境下感知用户输入的自然语言指令如“为我规划一个在东京的三天行程”。规划LLM大脑根据指令和当前上下文决定下一步该做什么。是直接回答还是调用某个工具LangChain通过AgentType如ZERO_SHOT_REACT_DESCRIPTION和提示词模板来引导LLM进行这种“思考”。行动LLM决定调用一个工具Tool并生成调用该工具所需的参数。例如调用SearchTool参数是“东京三天经典行程 2024”。观察工具执行返回结果例如搜索到的网页摘要。这个结果被反馈给LLM。循环LLM根据工具返回的结果再次“思考”规划判断是继续调用其他工具还是已经收集到足够信息来生成最终答案给用户。关键组件LLM提供推理能力。我们使用ChatOpenAI包装器。工具ToolsAgent可以调用的函数。每个工具必须有name和descriptionLLM通过描述来决定何时调用它。代理类型AgentType定义了Agent的决策逻辑。ZERO_SHOT_REACT_DESCRIPTION是最常用的之一它指示LLM以“Thought/Action/Observation”的格式进行推理。代理执行器AgentExecutor负责运行上述循环处理LLM和工具之间的交互直到LLM给出最终答案或达到最大迭代次数。记忆Memory存储对话历史使Agent拥有上下文感知能力。4. 完整实战构建简易旅行AI Agent我们将分步构建一个具备行程规划和实时问答能力的简易版Passage AI。4.1 创建基础Agent与搜索工具首先我们实现一个能使用搜索引擎规划行程的Agent。# core/agent_builder.py import os from langchain_openai import ChatOpenAI from langchain.agents import AgentExecutor, create_react_agent from langchain.tools import Tool from langchain_community.tools import DuckDuckGoSearchRun from langchain.prompts import PromptTemplate from langchain.memory import ConversationBufferMemory from config import OPENAI_API_KEY def create_basic_travel_agent(): 创建一个具备网络搜索能力的旅行规划Agent。 # 1. 初始化LLM llm ChatOpenAI( modelgpt-3.5-turbo, # 或 gpt-4 temperature0.7, # 创造性0-1之间越高越随机 api_keyOPENAI_API_KEY ) # 2. 定义工具 search DuckDuckGoSearchRun() # 使用DuckDuckGo进行搜索 # 将搜索函数包装成LangChain Tool并给出清晰的描述 # 描述至关重要LLM根据描述决定是否以及如何调用它。 search_tool Tool( nameWeb Search, funcsearch.run, descriptionUseful for when you need to answer questions about current events, find information about places, attractions, restaurants, or general travel tips. Input should be a clear search query. ) # 3. 定义提示词模板 # ReAct格式的提示词引导LLM进行推理 prompt_template You are a helpful and knowledgeable travel assistant named PassageAI. Your goal is to help users plan their trips and answer travel-related questions. You have access to the following tool: {tools} Use the following format: Question: the input question you must answer Thought: you should always think about what to do Action: the action to take, should be one of [{tool_names}] Action Input: the input to the action Observation: the result of the action ... (this Thought/Action/Action Input/Observation can repeat N times) Thought: I now know the final answer Final Answer: the final answer to the original input question Begin! Previous conversation history: {history} Question: {input} Thought:{agent_scratchpad} prompt PromptTemplate.from_template(prompt_template) # 4. 创建记忆用于多轮对话 memory ConversationBufferMemory(memory_keyhistory, return_messagesTrue) # 5. 创建Agent # 使用 create_react_agent 简化创建过程 agent create_react_agent(llmllm, tools[search_tool], promptprompt) # 6. 创建执行器 agent_executor AgentExecutor( agentagent, tools[search_tool], memorymemory, verboseTrue, # 设置为True可以看到Agent的思考过程调试时非常有用 handle_parsing_errorsTrue, # 处理LLM输出解析错误 max_iterations5 # 防止无限循环 ) return agent_executor if __name__ __main__: # 测试代码 agent create_basic_travel_agent() response agent.invoke({input: 帮我规划一个周末在上海的行程要包含艺术展览和本地美食。}) print(Agent Response:, response[output])运行这个脚本你会看到类似以下的输出verbose模式 Entering new AgentExecutor chain... Thought: 用户想要一个上海周末行程重点是艺术展览和本地美食。我需要搜索当前上海正在举办的艺术展览信息以及推荐的美食地点。 Action: Web Search Action Input: 上海 周末 艺术展览 2024 近期 推荐 Observation: 【搜索结果列出了几个当前上海的热门艺术展如浦东美术馆的XX展、西岸美术馆的YY展等】 Thought: 我找到了几个艺术展。现在需要结合美食来规划行程。可以按天来安排。 Action: Web Search Action Input: 上海 本帮菜 推荐 地道 餐厅 靠近 西岸美术馆 Observation: 【搜索结果推荐了西岸附近的几家知名本帮菜馆如ZZ餐厅等】 Thought: 我已经有足够的信息来规划一个行程了。 Final Answer: 为您规划一个上海周末艺术美食之旅 【Day 1 (周六)】上午参观浦东美术馆的“XX艺术展”... 午餐推荐附近的AA本帮菜馆... 下午逛外滩... 晚餐BB餐厅。 【Day 2 (周日)】上午前往西岸美术馆观看“YY当代艺术展”... 午餐步行至ZZ餐厅品尝地道本帮菜... 下午在徐汇滨江散步结束行程。4.2 集成地图与天气工具仅有搜索是不够的。一个真正的“实时向导”需要知道位置和天气。我们来集成Google Places API和天气API。首先安装必要的库并获取API KeyGoogle Cloud Platform和OpenWeatherMap。pip install googlemaps python-weather然后创建自定义工具# core/tools/map_tool.py import googlemaps from datetime import datetime from config import GOOGLE_PLACES_API_KEY class NearbyPlacesTool: 查找附近地点餐厅、景点等的工具 name Find Nearby Places description Useful for finding restaurants, attractions, or other points of interest near a specific location (latitude and longitude). Input should be a comma-separated string: latitude,longitude,place_type,radius米. Place type can be: restaurant, cafe, museum, park, etc. def __init__(self): self.gmaps googlemaps.Client(keyGOOGLE_PLACES_API_KEY) def run(self, query: str) - str: try: params query.split(,) if len(params) 3: return 输入格式错误。请提供纬度,经度,地点类型,半径(可选默认1000米)。 lat, lng, place_type params[0], params[1], params[2] radius int(params[3]) if len(params) 3 else 1000 # 调用Google Places Nearby Search places_result self.gmaps.places_nearby( location(float(lat), float(lng)), radiusradius, typeplace_type, rank_byprominence # 按知名度排序 ) results places_result.get(results, [])[:5] # 取前5个 if not results: return f在您指定的位置附近没有找到类型为 {place_type} 的地点。 output [] for place in results: name place.get(name, N/A) rating place.get(rating, 无评分) address place.get(vicinity, 地址未知) open_now place.get(opening_hours, {}).get(open_now, 未知) status 营业中 if open_now is True else 已关门 if open_now is False else 营业状态未知 output.append(f- **{name}** (评分: {rating}) - {address} - {status}) return f附近找到的 {place_type}\n \n.join(output) except Exception as e: return f调用地图API时出错{str(e)} # core/tools/weather_tool.py import python_weather import asyncio from config import OPENWEATHER_API_KEY # 注意python_weather 使用不同源此处仅为示例结构 class WeatherTool: 获取某地天气的工具 name Get Current Weather description Useful for getting the current weather and forecast in a city. Input should be the city name, e.g., Tokyo, Japan or Shanghai. async def run_async(self, query: str) - str: # 注意python_weather 是异步库 async with python_weather.Client(unitpython_weather.METRIC) as client: weather await client.get(query) current weather.current forecast_today weather.forecasts[0] if weather.forecasts else None report f{query}的当前天气\n report f- 温度{current.temperature}°C\n report f- 体感温度{current.feels_like}°C\n report f- 天气状况{current.description}\n report f- 湿度{current.humidity}%\n report f- 风速{current.wind_speed} km/h\n if forecast_today: report f\n今日预报最高{forecast_today.highest_temperature}°C 最低{forecast_today.lowest_temperature}°C。 return report def run(self, query: str) - str: # 同步包装器 return asyncio.run(self.run_async(query))重要提示python_weather库是异步的而LangChain工具默认期望同步函数。我们需要一个适配器。更简单的做法是使用同步的天气API库如pyowm但为了演示异步集成我们这样处理# core/tools/__init__.py from .map_tool import NearbyPlacesTool from .weather_tool import WeatherTool from langchain.tools import Tool # 创建LangChain可用的Tool对象 def get_custom_tools(): map_tool_instance NearbyPlacesTool() weather_tool_instance WeatherTool() tools [ Tool( namemap_tool_instance.name, funcmap_tool_instance.run, descriptionmap_tool_instance.description ), Tool( nameweather_tool_instance.name, funcweather_tool_instance.run, # 注意这里调用的是同步包装器 descriptionweather_tool_instance.description ) ] return tools4.3 构建功能完整的旅行Agent现在我们将搜索、地图、天气工具整合到一个更强大的Agent中。# core/agent_builder.py (更新版) import os from langchain_openai import ChatOpenAI from langchain.agents import AgentExecutor, create_react_agent from langchain.tools import Tool from langchain_community.tools import DuckDuckGoSearchRun from langchain.prompts import PromptTemplate from langchain.memory import ConversationBufferMemory from config import OPENAI_API_KEY from core.tools import get_custom_tools # 导入自定义工具 def create_advanced_travel_agent(): 创建一个集成了搜索、地图、天气的旅行Agent。 llm ChatOpenAI(modelgpt-3.5-turbo, temperature0.7, api_keyOPENAI_API_KEY) # 获取所有工具 search_tool DuckDuckGoSearchRun() custom_tools get_custom_tools() tools [ Tool( nameGeneral Search, funcsearch_tool.run, descriptionUseful for general questions, finding information, travel blogs, news, or when other specific tools are not applicable. Input is a search query. ) ] custom_tools # 更详细的提示词让Agent了解每个工具的用途 prompt_template You are PassageAI, an expert travel assistant that helps with planning and real-time guidance. You have access to the following tools: {tools} Use the following format strictly: Question: the users question Thought: you must always think about what to do next. Consider the conversation history. Action: the action to take, must be exactly one of [{tool_names}] Action Input: the input to the action Observation: the result of the action ... (repeat Thought/Action/Action Input/Observation as needed) Thought: I now have enough information to answer. Final Answer: a detailed, helpful, and friendly answer to the user. If suggesting places, include practical details like location hints or status. Previous conversation: {history} Question: {input} Thought:{agent_scratchpad} prompt PromptTemplate.from_template(prompt_template) memory ConversationBufferMemory(memory_keyhistory, return_messagesTrue) agent create_react_agent(llmllm, toolstools, promptprompt) agent_executor AgentExecutor( agentagent, toolstools, memorymemory, verboseTrue, handle_parsing_errorsTrue, max_iterations8 ) return agent_executor # app.py (使用Gradio构建简单Web界面) import gradio as gr from core.agent_builder import create_advanced_travel_agent # 初始化Agent全局变量简单演示生产环境需优化 agent create_advanced_travel_agent() def chat_with_agent(message, history): 处理用户消息并返回Agent响应 try: response agent.invoke({input: message}) return response[output] except Exception as e: return f抱歉处理您的请求时出现了错误{str(e)} # 创建Gradio界面 demo gr.ChatInterface( fnchat_with_agent, titlePassage AI - 你的智能旅行助手, description你好我是Passage AI可以帮你规划行程、查询附近地点和天气。试试问我我在巴黎埃菲尔铁塔附近推荐一家咖啡馆 或 为我在京都规划一个两天的文化之旅。, themesoft ) if __name__ __main__: demo.launch(shareFalse) # 设置 shareTrue 可生成临时公网链接运行python app.py一个本地的Web应用就会启动。你可以在浏览器中与你的旅行AI Agent对话了4.4 运行与验证启动应用在终端执行python app.py。打开浏览器访问http://127.0.0.1:7860。进行对话测试测试规划能力“为我规划一个在北京的三天家庭游行程孩子5岁。”测试实时向导能力“假设我现在在东京涩谷站35.6580, 139.7016附近有什么好吃的拉面店”注意需要你提供真实的经纬度Agent会调用地图工具。测试多轮对话先问“纽约的天气怎么样”再问“那明天呢”。观察记忆是否生效。测试工具选择问一个需要计算的问题如“如果我的预算是1000美元在日本玩7天平均每天预算多少”。观察Agent是否会尝试调用不存在的计算工具它应该直接使用LLM的数学能力回答。5. 常见问题与排查思路在开发和运行此类AI Agent应用时你可能会遇到以下典型问题问题现象可能原因排查与解决思路Agent陷入循环不输出最终答案1. 提示词中Thought/Action/Observation循环没有终止条件。2.max_iterations设置过高。3. 工具描述不清晰导致LLM无法正确选择或解析结果。1. 检查提示词模板确保有明确的Final Answer格式要求。2. 将verboseTrue观察Agent的思考链看它是否在重复无意义的动作。3. 降低max_iterations(如设为5)。4. 优化工具的描述使其职责更单一、明确。调用API时出现认证错误1. API密钥未正确设置或已失效。2. 环境变量未加载。3. 代码中密钥写死或泄露。1. 确认.env文件在项目根目录且变量名与代码中读取的一致。2. 在代码开头打印os.getenv(“KEY”)检查是否为None。3. 前往对应平台如OpenAI, Google Cloud检查API密钥的额度、状态和IP限制。地图或天气工具返回“未找到结果”或错误1. 输入格式不符合工具要求。2. 地理位置坐标或城市名有误。3. API服务本身无数据或受限。1. 仔细阅读工具的description确保输入字符串的格式如用逗号分隔。2. 在工具函数的run方法内添加详细的日志打印出接收到的参数和API返回的原始数据。3. 直接使用对应API的官方测试工具验证请求是否有效。LLM回答与工具结果无关或幻觉1. 工具返回的结果太长或格式混乱LLM无法有效提取信息。2. 提示词没有强制要求LLM基于Observation来回答。1. 在工具函数中对API返回的数据进行清洗和格式化只提取关键信息如名称、评分、地址以清晰列表或简短段落的形式返回。2. 强化提示词例如加入“你必须严格基于你得到的Observation信息来生成Final Answer不要编造Observation中没有的信息。”多轮对话中Agent忘记之前的内容ConversationBufferMemory的memory_key未在提示词模板中正确引用或者记忆没有被传递给Agent执行器。1. 检查PromptTemplate中是否包含了{history}占位符。2. 检查创建AgentExecutor时是否传入了memory参数。3. 对于长对话考虑使用ConversationSummaryMemory来压缩历史避免上下文过长。应用响应速度慢1. LLM API调用延迟高。2. 工具调用如网络搜索、地图API是同步且耗时的。3. Agent迭代次数过多。1. 考虑使用更快的模型如gpt-3.5-turbo而非gpt-4。2. 对于可以并行的工具调用研究LangChain的异步支持或使用Tool的coroutine属性。3. 优化工具增加缓存机制对相同查询缓存结果。4. 设置合理的max_iterations。6. 最佳实践与工程化建议将原型转化为一个稳定、可维护、可扩展的生产级应用需要考虑以下方面1. 提示词工程优化角色设定与约束在系统提示词中明确Agent的角色、能力和边界。例如“你是一个旅行助手只回答与旅行相关的问题。对于政治、暴力等无关问题应礼貌拒绝。”输出结构化引导LLM输出更结构化的数据如JSON便于前端渲染。可以使用LangChain的OutputParser。少样本学习在提示词中提供几个高质量的输入输出示例Few-Shot Learning能显著提升Agent在复杂任务上的表现。2. 工具设计的健壮性输入验证与清洗在工具的run方法内部必须对输入参数进行严格的验证和类型转换并提供清晰的错误信息。错误处理与降级任何外部API调用都必须有try-except块。当主要API失败时应有备用方案如地图API失败降级为网络搜索。结果标准化不同工具返回的数据格式差异很大。设计一个内部的标准数据格式如Place、Weather对象所有工具的结果都转换为此格式方便LLM理解和后续处理。3. 记忆与上下文管理长期记忆ConversationBufferMemory会无限增长导致上下文窗口爆炸。对于长对话应使用ConversationSummaryMemory定期总结历史对话。ConversationSummaryBufferMemory结合摘要和最近几条原始记录。向量存储记忆将对话片段存入向量数据库根据当前问题语义检索相关历史。这是实现“记住用户偏好”的关键。记忆分区将记忆分为“会话记忆”本次聊天和“用户档案记忆”长期偏好如“用户喜欢素食”分别管理。4. Agent流程控制超时与重试为每个工具调用和LLM调用设置超时和重试机制。验证步骤在Agent给出最终答案前可以设计一个“验证”步骤让LLM自我检查答案是否基于工具返回的事实减少幻觉。多Agent协作复杂任务可以拆解。例如一个“规划Agent”负责生成行程大纲一个“详情Agent”负责为每个景点填充详细信息一个“预订Agent”负责查询价格和可用性。使用LangGraph来编排多Agent工作流。5. 性能与成本缓存对频繁且结果不变的查询如“故宫的开放时间”进行缓存减少LLM和API调用。令牌计数监控每次交互消耗的令牌数特别是输入上下文包含长记忆和历史的长度。设置合理的截断策略。模型路由根据问题复杂度路由到不同成本的模型。简单问答用小型/廉价模型复杂规划用大型模型。6. 安全与合规内容过滤在LLM调用前后加入内容安全过滤器防止生成有害或不当建议。用户数据隐私妥善处理用户位置等敏感信息。明确告知用户数据如何使用并遵守相关法律法规如GDPR。依赖管理使用requirements.txt或Poetry严格锁定依赖版本避免因库更新导致应用崩溃。构建一个像Passage AI这样成熟的旅行助手是一个持续迭代的过程。从本文的基础框架出发你可以逐步添加更多功能如酒店比价、交通路线规划、语音交互、多语言支持并将其部署到云服务器通过小程序或App提供服务。