用 AI 打造你的专属旅行计划:多智能体旅行规划工具(实战篇)

 

在如今快速变化的 AI 世界里,能把各种信息整合成个性化、实用的旅行计划,简直是无价之宝!马上开启小孩们的暑假模式,旅行变热了。不管是随性的周末小逃亡,还是精心设计的长假期,旅行者们都越来越想要一个智能系统,能实时从网上抓取数据,转化成详细的行程安排、活动推荐和旅行小贴士。

这篇博客要介绍一个用 LangGraph 和 LLaMA 3.x 模型驱动的 Multi-Agent Travel Itinerary Planner。这个系统集结了一支“智能团队”,每个智能体各司其职:

  • • Generate Itinerary Agent —— 根据你的喜好,打造逐日的旅行计划
  • • Recommend Activities Agent —— 推荐当地独特的体验活动
  • • Fetch Useful Links Agent —— 通过 Google Serper API 抓取相关旅行指南
  • • Weather Forecaster Agent —— 提供旅行期间的天气预报
  • • Packing List Generator Agent —— 定制个性化的打包清单
  • • Food & Culture Recommender Agent —— 分享当地美食和文化礼仪
  • • Chat Agent —— 以对话方式解答你的后续问题

系统用 Streamlit 打造了一个超友好的界面,展示如何将现代 LLM(大型语言模型)和多智能体工作流结合,带来无缝的旅行规划体验。

完整代码关注公众号:AI大模型观察站, 并回复:0945 获取代码下载地址

跟着这篇博客,你可以一步步学会如何搭建这个项目——从环境配置到多智能体协作,还能了解如何根据自己的旅行需求定制这个系统。

欢迎关注公众号,我们会持续输出更多实用的工具类实战。


项目运行前提条件

以下是为运行这个基于 Streamlit 的旅行规划工具(使用 LangGraph 和 LLaMA 3.2)准备的清晰且结构化的前提条件:

  1. 1. Python 3.10+
    确保你的系统已安装 Python 3.10 或更高版本。可以用以下命令检查版本:

    python --version
  2. 2. 安装所需的 Python 库
    项目依赖多个 Python 库,通过以下命令安装所有依赖:

    pip install -r requirements.txt

    一些关键库包括:

    • • streamlit —— 用于交互式网页界面
    • • langgraph —— 构建多智能体工作流
    • • langchain-community —— 集成 Ollama 和外部工具
    • • python-dotenv —— 安全管理环境变量
    • • fpdf —— 生成可下载的 PDF 行程
    • • serper-wrapper —— 通过 Google Serper API 抓取网页结果
  3. 3. Ollama + LLaMA 3.x 模型
    这个规划工具的核心依赖 LLaMA 3.x,通过 Ollama 运行。请确保:

    这样你的智能体就能利用强大的本地 LLM 功能。

    • • 从 ollama.com 安装 Ollama
    • • 在本地启动 Ollama 服务器
    • • 下载并运行 LLaMA 3.x 模型:

      ollama run llama3
  4. 4. API 密钥
    Fetch Useful Links Agent 使用 Serper.dev API 来获取实时的旅行贴士和指南。设置方法:
    • • 在 Serper.dev 注册
    • • 生成你的 API 密钥
    • • 在项目根目录创建 .env 文件,添加:

      SERPER_API_KEY=你的_api_密钥
  5. 5. 项目结构(模块化代码)
    智能体被拆分成独立模块,项目目录结构可能如下:

    .
    ├── travel_agent.py
    ├── agents/
    │   ├── generate_itinerary.py
    │   ├── recommend_activities.py
    │   ├── fetch_useful_links.py
    │   ├── weather_forecaster.py
    │   ├── packing_list_generator.py
    │   ├── food_culture_recommender.py
    │   └── chat_agent.py
    ├── utils_export.py
    ├── .env
    ├── requirements.txt
    └── README.md

代码逐步解析

1. 导入依赖和环境设置

项目开始时会导入必要的 Python 库并加载环境变量,包括核心库如 jsondotenvtempfile,以及 LangChain 的组件,比如 ChatOllama 用于连接本地 LLaMA 3.2 模型。.env 文件存储敏感配置,比如 API 密钥。

import streamlit as st
import json
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, END
from langchain_core.messages import HumanMessage
from langchain_community.chat_models import ChatOllama
from langchain_community.utilities import GoogleSerperAPIWrapper
from dotenv import load_dotenv
import os

# 加载环境变量
load_dotenv()

# 初始化 LLM
llm = ChatOllama(model="llama3.2", base_url="http://localhost:11434")

# 初始化 Google 搜索工具
search = GoogleSerperAPIWrapper()

2. 导入智能体

环境设置好后,我们会导入各个模块化的智能体,每个智能体负责旅行规划的一个特定部分。这种设计让每个智能体专注于单一职责(比如生成行程、推荐活动或预报天气),系统更易扩展和调试。

from agents import generate_itinerary, recommend_activities, fetch_useful_links, weather_forecaster, packing_list_generator, food_culture_recommender, chat_agent

2.1 🗺️ 行程生成智能体

这个智能体根据用户偏好(目的地、预算、旅行月份、时长、旅行类型)生成逐日的旅行行程。

from langchain_core.messages import HumanMessage
from langchain_community.chat_models import ChatOllama
import json 

def generate_itinerary(state):
    llm = ChatOllama(model="llama3.2", base_url="http://localhost:11434")
    prompt = f"""
    根据以下偏好,创建详细的行程:
    {json.dumps(state['preferences'], indent=2)}

    包含每天的安排、餐饮选择和休息时间。
    """

    try:
        result = llm.invoke([HumanMessage(content=prompt)]).content
        return {"itinerary": result.strip()}
    except Exception as e:
        return {"itinerary""""warning"str(e)}

它将结构化的偏好转化为可读、可执行的多日计划,借助本地运行的 LLaMA 3.x 模型。

2.2 🎯 活动推荐智能体

这个智能体根据用户偏好和已生成的行程,推荐当地独特的、具有文化特色的活动。

from langchain_core.messages import HumanMessage
from langchain_community.chat_models import ChatOllama
import json 

def recommend_activities(state):
    llm = ChatOllama(model="llama3.2", base_url="http://localhost:11434")
    prompt = f"""
    根据以下偏好和行程,推荐独特的本地活动:
    偏好:{json.dumps(state['preferences'], indent=2)}
    行程:{state['itinerary']}

    尽可能为每天提供项目符号列表形式的建议。
    """

    try:
        result = llm.invoke([HumanMessage(content=prompt)]).content
        return {"activity_suggestions": result.strip()}
    except Exception as e:
        return {"activity_suggestions""""warning"str(e)}

2.3 🔗 有用链接智能体

这个智能体通过 Serper API 获取最新的博客、旅行建议和指南,提升用户对内容的信任度。

from langchain_community.utilities import GoogleSerperAPIWrapper

deffetch_useful_links(state):
    search = GoogleSerperAPIWrapper()
    destination = state['preferences'].get('destination''')
    month = state['preferences'].get('month''')
    query = f"{destination} {month} 的旅行贴士和指南"
    try:
        search_results = search.results(query)
        organic_results = search_results.get("organic", [])
        links = [
            {"title": result.get("title""无标题"), "link": result.get("link""")}
            for result in organic_results[:5]
        ]
        return {"useful_links": links}
    except Exception as e:
        return {"useful_links": [], "warning"f"获取链接失败:{str(e)}"}

它会抓取前 5 个相关结果,以可点击的标题形式返回,方便用户进一步阅读。

2.4 🌦️ 天气预报智能体

这个智能体为选定的目的地和旅行月份提供天气预报,并给出自然语言总结和实用建议,比如带伞或防晒霜。

from langchain_core.messages import HumanMessage
from langchain_community.chat_models import ChatOllama

def weather_forecaster(state):
    llm = ChatOllama(model="llama3.2", base_url="http://localhost:11434")
    prompt = f"""
    根据目的地和月份,提供详细的天气预报,包括温度、降水情况和旅行者的建议:
    目的地:{state['preferences'].get('destination''')}
    月份:{state['preferences'].get('month''')}
    """

    try:
        result = llm.invoke([HumanMessage(content=prompt)]).content
        return {"weather_forecast": result.strip()}
    except Exception as e:
        return {"weather_forecast""""warning"str(e)}

它确保旅行者为实际天气做好准备。

2.5 🧳 打包清单智能体

根据目的地、旅行时长、天气和旅行类型,这个智能体生成一份全面的打包清单,考虑季节和具体场景。

from langchain_core.messages import HumanMessage
from langchain_community.chat_models import ChatOllama

def packing_list_generator(state):
    llm = ChatOllama(model="llama3.2", base_url="http://localhost:11434")
    prompt = f"""
    为 {state['preferences'].get('destination''')} 的 {state['preferences'].get('holiday_type''常规')} 假期生成一份全面的打包清单,旅行时间为 {state['preferences'].get('month''')},时长 {state['preferences'].get('duration'0)} 天。
    根据预计天气和旅行类型包含必需品。
    """

    try:
        result = llm.invoke([HumanMessage(content=prompt)]).content
        return {"packing_list": result.strip()}
    except Exception as e:
        return {"packing_list""""warning"str(e)}

2.6 🍲 美食与文化推荐智能体

这个智能体融合美食探索和文化沉浸,推荐必尝的当地菜肴、餐厅,并提供礼仪建议,帮助旅行者避免失礼并享受当地热情。

from langchain_core.messages import HumanMessage
from langchain_community.chat_models import ChatOllama

def food_culture_recommender(state):
    llm = ChatOllama(model="llama3.2", base_url="http://localhost:11434")
    prompt = f"""
    对于前往 {state['preferences'].get('destination''')} 的旅行,预算为 {state['preferences'].get('budget_type''中档')}
    1. 推荐当地特色菜肴和餐厅。
    2. 提供重要的文化规范、礼仪建议和旅行者需注意的事项。
    回复格式分为“美食与餐饮”和“文化与礼仪”两部分。
    """

    try:
        result = llm.invoke([HumanMessage(content=prompt)]).content
        return {"food_culture_info": result.strip()}
    except Exception as e:
        return {"food_culture_info""""warning"str(e)}

2.7 💬 对话智能体

这个智能体为界面提供交互式问答功能,用户可以提出后续问题、请求澄清或调整计划,获得类人化的即时回答。

from langchain_core.messages import HumanMessage
from langchain_community.chat_models import ChatOllama
import json

defchat_node(state):
    llm = ChatOllama(model="llama3.2", base_url="http://localhost:11434")
    prompt = f"""
    上下文:
    偏好:{json.dumps(state['preferences'], indent=2)}
    行程:{state['itinerary']}

    用户问题:
    {state['user_question']}

    以对话方式提供见解或建议:保持回复简洁
    {{ "chat_response": "此处为你的回答" }}
    """

    try:
        result = llm.invoke([HumanMessage(content=prompt)]).content
        try:
            parsed = json.loads(result.strip())
            response = parsed.get("chat_response", result.strip())
        except json.JSONDecodeError:
            response = result.strip()
        chat_entry = {"question": state['user_question'], "response": response}
        chat_history = state.get('chat_history', []) + [chat_entry]
        return {"chat_response": response, "chat_history": chat_history}
    except Exception as e:
        return {"chat_response""""warning"str(e)}

它会存储对话历史,根据完整的上下文(用户偏好 + 行程)进行回答,让对话更连贯、智能。

3. 用 LangGraph 连接智能体

我们用 LangGraph 协调模块化智能体之间的流程,定义一个节点图,明确每个节点的入口和出口。LangGraph 在后台处理状态转换,确保每个函数都能干净地更新并传递状态。

3.1 定义全局状态

在连接智能体之前,我们定义一个共享的 GraphState,用于跟踪每个智能体的中间输出——从行程到天气预报再到对话历史。

from typing import TypedDict, Annotated
class GraphState(TypedDict):
    preferences_text: str
    preferences: dict
    itinerary: str
    activity_suggestions: str
    useful_links: list[dict]
    weather_forecast: str
    packing_list: str
    food_culture_info: str
    chat_history: Annotated[list[dict], "问答列表"]
    user_question: str
    chat_response: str

这让所有智能体都能读写一个动态更新的状态字典。

3.2 将智能体注册为节点

将每个导入的智能体函数添加到 LangGraph 工作流中作为节点:

from langgraph.graph import StateGraph, END
workflow = StateGraph(GraphState)
# 注册智能体为节点
workflow.add_node("generate_itinerary", generate_itinerary)
workflow.add_node("recommend_activities", recommend_activities)
workflow.add_node("fetch_useful_links", fetch_useful_links)
workflow.add_node("weather_forecaster", weather_forecaster)
workflow.add_node("packing_list_generator", packing_list_generator)
workflow.add_node("food_culture_recommender", food_culture_recommender)
workflow.add_node("chat", chat_agent)

每个节点名称是一个字符串标识符,用于在图中引用该函数。

3.3 定义流程

我们指定入口点并将每个节点连接到终点(END)。为简单起见,每个用户操作运行一个节点,但你也可以根据需要定义复杂路径。

workflow.set_entry_point("generate_itinerary")
# 定义单次流程的终点
workflow.add_edge("generate_itinerary", END)
workflow.add_edge("recommend_activities", END)
workflow.add_edge("fetch_useful_links", END)
workflow.add_edge("weather_forecaster", END)
workflow.add_edge("packing_list_generator", END)
workflow.add_edge("food_culture_recommender", END)
workflow.add_edge("chat", END)

最后,编译图:

graph = workflow.compile()

这会生成一个可调用的对象,接受 GraphState 并在选定节点运行后返回更新版本。

3.4 使用示例

调用图中的任意智能体:

result = graph.invoke(st.session_state.state)

也可以直接调用特定节点(如果需要):

workflow.get_node("recommend_activities")(st.session_state.state)

这对于 Streamlit 前端的按钮驱动交互尤其有用,接下来会介绍。

4. 构建 Streamlit 界面

Streamlit 界面将所有功能整合在一起,让用户可以输入旅行偏好、调用各个智能体并查看结果,界面简洁且交互性强。

4.1 初始化会话状态

我们初始化一个全面的会话状态字典,持久保存所有智能体的输入和输出:

if "state"notin st.session_state:
    st.session_state.state = {
        "preferences_text""",
        "preferences": {},
        "itinerary""",
        "activity_suggestions""",
        "useful_links": [],
        "weather_forecast""",
        "packing_list""",
        "food_culture_info""",
        "chat_history": [],
        "user_question""",
        "chat_response"""
    }

这确保系统各部分(从行程到对话历史)保持同步。

4.2 用户输入表单

我们提供一个表单,让用户输入旅行详情,如目的地、月份、时长、旅行类型和预算:

with st.form("travel_form"):
    col1, col2 = st.columns(2)
    with col1:
        destination = st.text_input("目的地")
        month = st.selectbox("旅行月份", [...])
        duration = st.slider("旅行天数"1307)
        num_people = st.selectbox("人数", [...])
    with col2:
        holiday_type = st.selectbox("旅行类型", [...])
        budget_type = st.selectbox("预算类型", [...])
        comments = st.text_area("额外备注")
    submit_btn = st.form_submit_button("生成行程")

提交后,我们会更新会话状态并触发行程生成智能体。

4.3 智能体操作按钮

生成行程后,我们显示一排按钮,让用户获取活动建议、有用链接、天气预报、打包清单和美食文化信息。每个按钮触发对应的智能体,并动态更新界面:

col_btn1, col_btn2, col_btn3, col_btn4, col_btn5 = st.columns(5)
with col_btn1:
    if st.button("获取活动建议"):
        result = recommend_activities(st.session_state.state)
        st.session_state.state.update(result)
with col_btn2:
    if st.button("获取有用链接"):
        result = fetch_useful_links(st.session_state.state)
        st.session_state.state.update(result)
# ... 类似地处理天气、打包清单、美食与文化

这种模块化方式让用户可以交互式探索旅行的不同方面。

4.4 显示智能体输出

我们将每个智能体的输出显示在可展开的面板中,保持界面整洁有序:

if st.session_state.state.get("activity_suggestions"):
    with st.expander("🎯 活动建议"):
        st.markdown(st.session_state.state["activity_suggestions"])
if st.session_state.state.get("useful_links"):
    with st.expander("🔗 有用链接"):
        for link in st.session_state.state["useful_links"]:
            st.markdown(f"- [{link['title']}]({link['link']})")
# ... 类似地处理天气、打包清单、美食与文化信息

这让用户可以按需深入查看具体信息。

4.5 交互式聊天

为了完善体验,我们加入了一个由 chat_agent 驱动的聊天界面,用户可以询问行程相关问题,获得基于上下文的对话式回答:

st.markdown("### 聊聊你的行程")
for chat in st.session_state.state["chat_history"]:
    with st.chat_message("user"):
        st.markdown(chat["question"])
    with st.chat_message("assistant"):
        st.markdown(chat["response"])
if user_input := st.chat_input("询问行程相关问题"):
    st.session_state.state["user_question"] = user_input
    result = chat_agent(st.session_state.state)
    st.session_state.state.update(result)
    st.experimental_rerun()

这种无缝的聊天体验让旅行规划更具互动性和个性化。

4.6 PDF 导出

最后,我们提供一个导出按钮,让用户将行程下载为 PDF 文档:

if st.button("导出为 PDF"):
    pdf_path = export_to_pdf(st.session_state.state["itinerary"])
    if pdf_path:
        with open(pdf_path, "rb"as f:
            st.download_button("下载行程 PDF", f, file_name="itinerary.pdf")

这个界面设计让用户旅程流畅直观,同时高效整合了由 LangGraph 和 LLaMA 3.x 模型驱动的多智能体后端。

总结

这个项目的亮点在于,它将复杂的旅行规划问题拆分成多个专注的小智能体,每个智能体处理特定任务,比如生成行程、推荐活动、抓取实时网页数据或提供文化贴士。这种模块化设计不仅让代码更清晰、易于维护,还能让你轻松替换或改进单个智能体,而无需重构整个系统。加上 LangGraph 的流程管理,智能体之间的协作和数据流转变得超级顺畅。

更强的是,Streamlit 界面让整个系统易于使用且交互性强,用户只需几次点击就能获得实时反馈,探索旅行的方方面面。这种模块化 AI 智能体、灵活的流程编排和用户友好的界面结合,造就了一个实用、可扩展、随时能投入实际使用的工具。如果你对开发 AI 驱动的应用感兴趣,这种多智能体模式是一个很棒的学习和适配模板。

 


(文:PyTorch研习社)

发表评论