使用多智能体人工智能和语言图构建人工智能驱动的智能旅行规划器(二)

124 阅读5分钟

在上次的文章中我们已经介绍了将会用到的多种智能体,接下来我们将继续探索如何构建一个智能旅行规划器。

  1. 借助 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 前端的按钮驱动交互中尤为实用(后续章节将详细介绍)。

  1. 构建 Streamlit 用户界面

Streamlit 界面将所有功能整合在一起,使用户能够输入旅行偏好、调用单个智能体并查看结果 —— 全部通过简洁的交互式布局实现。

4.1 会话状态初始化

我们初始化一个完整的会话状态字典,用于在用户交互过程中持久化存储所有智能体的输入和输出:

if "state" not in 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("天数", 1, 30, 7)
        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 智能体操作按钮

生成行程后,我们显示一排按钮,用户可通过按钮获取活动建议、实用链接、天气预报、行李清单和饮食文化信息 —— 每个按钮触发相应智能体并动态更新 UI:

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 交互式聊天

为完善体验,我们加入由聊天智能体驱动的聊天界面,允许用户询问行程相关问题并获得上下文相关的对话式回复:

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")

这种 UI 设置使用户旅程流畅直观,同时有效集成了基于 LangGraph 和 llama3.x 模型的多智能体后端。

结论

本项目的优势在于将旅行规划这类复杂问题拆解为多个专注的智能体 —— 每个智能体处理特定任务(如创建行程、推荐活动、获取实时网络数据或提供文化提示)。这种模块化设计不仅让代码更简洁易维护,还允许在不重构整个系统的前提下替换或优化单个智能体。此外,借助 LangGraph 串联所有组件,能够高效管理智能体间的工作流和数据流。

再者,Streamlit 界面让整个系统变得易于使用且交互性强,用户可获得实时反馈,只需几次点击就能探索行程的各个方面。模块化 AI 智能体、灵活的编排层与用户友好的 UI 相结合,正是这段代码的独特之处 —— 它兼具实用性、可扩展性,且适用于真实场景。如果您对构建 AI 驱动的应用感兴趣,这种多智能体架构是值得学习和适配的优秀范式。