开会前十分钟,你同事突然丢来一句:“我下周去北京,帮我把行程改一下——航班改签、旧酒店取消、换一家离新会议地点近的、再加个租车。”你脑子里已经开始自动列 checklist 了:先查余票,再看退改规则,再核对酒店取消政策,再对比新酒店位置和预算,还要确认租车取还车时间……而很多“传统对话式 AI”的尴尬就在这:它会聊天,但一遇到这种“多步骤 + 多工具 + 有风险操作”的组合任务,就容易把事情越办越乱。
这篇文章我想从“为什么单一智能体扛不住”讲到“怎么用 LangGraph 搭一个可中断、可委派的多智能体旅行系统”,最后给你一份可以直接跑的完整代码骨架(包含权限中断流程)。

一、当单一AI无法应对复杂旅行场景
1)痛点:复杂旅行规划不是“回答问题”,而是“执行流程”
想象这个场景:
用户:帮我改签航班,然后取消旧酒店,再订新酒店,最后租车。
这里面至少包含四类能力:
- 搜索:查航班、查酒店、查租车(多为只读)
- 策略:什么时候先改签、什么时候先取消(避免损失)
- 规则:退改签/取消政策、时间冲突、预算约束
- 执行:真正“改”“退”“订”(这类动作往往不可逆)
如果你用单一智能体硬扛,通常会变成这样:
- 它必须同时掌握所有工具、所有规则、所有策略
- 一会儿在算价格,一会儿又在做取消操作
- 用户中途插一句“等等先别取消”,模型上下文更容易混乱
最终结果:要么不敢动(只给建议),要么动错(用户信任直接崩掉)。
2)解决方案:LangGraph 多智能体的核心设计
我更喜欢用一个类比:
- 传统单体:像一个“全能客服”,什么都能聊,但高压场景容易手忙脚乱
- LangGraph 多智能体:像“专业团队 + 智能调度中心”
- 主智能体负责分派、兜底、把控全局
- 子智能体各司其职:航班、酒店、租车、签证……
一句话概括这套系统的价值:
可中断的权限控制 + 可委派的专业分工
让系统既能自动化推进,又不会越权乱改。
二、架构核心:理解系统的“骨架”与“血液”

2.1 状态管理(State):全局记忆体
LangGraph 的关键不是“写几个 prompt”,而是你要认真设计 State:整个系统的记忆与控制面板。
一个典型的旅行系统状态可以这样定义(后面代码会给完整实现):
messages:对话历史user_info:用户偏好、证件信息、常用城市等agent_stack:对话状态栈(重点)
agent_stack:为什么它是“能委派、能返回”的关键
agent_stack 就像浏览器的历史栈:
- 进入子智能体:push(记录“我从主助手进入航班助手”)
- 子智能体完成:pop(返回上一级上下文)
- 任何时候都能“回到主助手继续聊别的”
可视化一下栈变化(示意):
- 初始:
[main] - 主助手委派航班:
[main, flights] - 航班助手又需要酒店信息:
[main, flights, hotels] - 酒店完成返回:
[main, flights] - 航班完成返回:
[main]
这就是“多智能体协作但不丢上下文”的底层支撑。
2.2 工具分类机制:安全与敏感的“操作开关”
旅行系统里,工具大体分两类:
-
安全工具(只读):可自动执行
例如:
search_flights、search_hotels -
敏感工具(修改/下单/取消):必须授权
例如:
cancel_hotel、update_ticket
设计意义很直白:
在“自动化效率”与“用户控制权”之间找到平衡。
你会发现,只要把敏感操作统一做成“可中断”,用户的信任感会明显提升:
系统不再是“擅自帮你取消”,而是“我准备这么做,你点头我再执行”。
三、智能体协作机制:系统如何“思考”与“流转”

3.1 主智能体:调度中心
主智能体的任务不是“专业处理一切”,而是:
- 识别意图:航班?酒店?租车?签证?
- 判断是否需要委派
- 简单问题自己直接答(比如“改签一般有哪些费用”)
- 遇到跨域任务时拆解顺序(例如先改签确认再取消酒店)
在代码层面,主智能体通常会有四类“委派工具”(本质是路由信号):
ToFlightAssistantToHotelAssistantToCarAssistantToVisaAssistant(扩展时加)
3.2 子智能体的专业化设计(以航班为例)
航班助手只需要关心航班相关工具:
- 安全:
search_flights(查余票、价格) - 敏感:
update_ticket、cancel_ticket(触发授权)
专业化带来的好处很实际:
- prompt 更短、更精准
- 工具集更小,模型更不容易“拿错工具”
- token 消耗更可控(尤其是多轮对话场景)
3.3 核心流转机制剖析
入口节点(Entry Node):智能体切换时的“上下文生成器”
你可以把 create_entry_node 理解为:
“当我把任务丢给子助手时,自动生成一段系统消息,交代背景和目标”。
它解决的问题是:主助手和子助手之间不需要靠“人肉复制粘贴上下文”,而是通过结构化状态自动串起来。
CompleteOrEscalate:子智能体的“完成/向上移交”协议
子智能体处理时会遇到两种情况:
- 完成:需求满足,返回主助手
- 移交:遇到策略冲突或超出权限(例如用户要求违反政策),需要回主助手统一协调
这相当于给多智能体协作定义了“收口协议”,系统不会在子助手里无限兜圈。
四、关键实现:如何构建“可中断”的权限流程
4.1 敏感工具的中断-授权流程
流程拆开看其实很清晰:
- 用户提出修改请求:“帮我取消酒店”
- 子智能体准备调用
cancel_hotel(敏感) - LangGraph 触发
interrupt:暂停执行 - 系统把“待执行操作详情”展示给用户,请确认
- 用户确认:继续执行;用户拒绝:终止/返回
核心是:工具不是不能自动调用,而是调用前必须过“授权闸门”。
4.2 错误处理与回退
现实世界里工具会失败:
- 酒店已过取消时间
- 订单号不存在
- 网络超时
好的体验应该是:
- 给用户可理解的失败原因
agent_stack不要乱(失败也要能回退到正确上下文)- 允许用户选择下一步(换方案/联系客服/只保留建议)
五、扩展性设计:如何添加新智能体(以签证为例)
扩展到“签证办理智能体”一般只需要增量:
- 定义新助手与工具(如
check_visa_requirements、prepare_visa_documents) - 主助手新增一条委派路由
ToVisaAssistant - 状态结构如需新增字段再加(多数情况下不需要)
这也是多智能体架构真正“能长期演进”的地方:
你不会因为加一个新模块,把整个系统 prompt 写崩。
六、完整代码示例(可运行骨架,包含中断授权)
下面这份代码是一个“可跑的最小系统骨架”:
- 具备
State(messages/user_info/agent_stack) - 具备主助手 + 航班/酒店两个子助手
- 具备敏感工具的
interrupt授权流程 - 用“伪工具实现”模拟真实 API(你接入自己的供应商接口即可)
依赖:langgraph、langchain-core、langchain-openai
环境变量:OPENAI_API_KEY
把下面保存为 multi_agent_travel.py:
from __future__ import annotations
import os
from dataclasses import dataclass
from typing import Any, Dict, List, Literal, Optional, TypedDict
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage, ToolMessage
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, END
from langgraph.types import interrupt
# ----------------------------
# 1) State: 全局记忆体
# ----------------------------
class UserInfo(TypedDict, total=False):
name: str
loyalty_id: str
preferences: Dict[str, Any]
class TravelState(TypedDict):
messages: List[Any]
user_info: UserInfo
agent_stack: List[Literal["main", "flights", "hotels", "cars", "visa"]]
pending_action: Optional[Dict[str, Any]] # 存放待授权的敏感操作
# ----------------------------
# 2) 工具:安全 vs 敏感
# ----------------------------
@tool
def search_flights(origin: str, destination: str, date: str) -> Dict[str, Any]:
"""安全工具:查询航班(只读)"""
# 真实场景接供应商 API;这里用模拟数据
return {
"origin": origin,
"destination": destination,
"date": date,
"options": [
{"flight_no": "MU123", "depart": "09:10", "arrive": "13:40", "price": 2100},
{"flight_no": "NH008", "depart": "12:00", "arrive": "16:30", "price": 2850},
],
}
@tool
def search_hotels(city: str, checkin: str, checkout: str) -> Dict[str, Any]:
"""安全工具:查询酒店(只读)"""
return {
"city": city,
"checkin": checkin,
"checkout": checkout,
"options": [
{"hotel": "Shinjuku Stay", "price_per_night": 980, "refundable": True},
{"hotel": "Ginza Business", "price_per_night": 1180, "refundable": False},
],
}
@tool
def cancel_hotel(order_id: str) -> Dict[str, Any]:
"""敏感工具:取消酒店(会修改订单)"""
# 真实场景:调用取消接口;这里模拟成功
return {"order_id": order_id, "status": "cancelled", "refund": 1200}
@tool
def update_ticket(ticket_id: str, new_flight_no: str) -> Dict[str, Any]:
"""敏感工具:改签机票(会修改订单)"""
return {"ticket_id": ticket_id, "new_flight_no": new_flight_no, "status": "rebooked"}
SAFE_TOOLS = [search_flights, search_hotels]
SENSITIVE_TOOLS = [cancel_hotel, update_ticket]
# ----------------------------
# 3) 委派信号(路由工具)
# ----------------------------
@tool
def ToFlightAssistant() -> str:
"""委派到航班助手"""
return "route:flights"
@tool
def ToHotelAssistant() -> str:
"""委派到酒店助手"""
return "route:hotels"
ROUTING_TOOLS = [ToFlightAssistant, ToHotelAssistant]
# ----------------------------
# 4) LLM
# ----------------------------
def make_llm() -> ChatOpenAI:
return ChatOpenAI(
model="gpt-4o-mini",
temperature=0.2,
)
# ----------------------------
# 5) Entry Node: 切换上下文
# ----------------------------
def create_entry_node(target: Literal["flights", "hotels"]) -> Any:
def _entry(state: TravelState) -> TravelState:
stack = state["agent_stack"]
stack.append(target)
background = (
"你是旅行系统里的专业助手。\n"
f"你现在负责:{target}。\n"
"你会收到主助手已有的对话上下文,请直接推进任务。\n"
"如果任务完成,返回 'CompleteOrEscalate' 工具并说明原因。\n"
"如果需要执行敏感操作(取消/改签),先触发授权流程,不要擅自执行。"
)
return {
**state,
"messages": state["messages"] + [SystemMessage(content=background)],
"agent_stack": stack,
}
return _entry
@tool
def CompleteOrEscalate(reason: str) -> str:
"""子助手完成或需要上移交时调用"""
return f"complete:{reason}"
# ----------------------------
# 6) 权限中断:敏感工具网关
# ----------------------------
def sensitive_tool_gateway(state: TravelState) -> TravelState:
"""
如果 pending_action 存在,则触发 interrupt 请求用户授权。
用户同意后继续执行;用户拒绝则清空并返回上级。
"""
pending = state.get("pending_action")
if not pending:
return state
# 触发中断,把将要执行的动作展示给用户
decision = interrupt(
{
"type": "approval_required",
"title": "需要确认敏感操作",
"action": pending,
"prompt": "请回复:确认 / 取消",
}
)
# decision 由外部 runner 注入(见 main 中的示例)
if isinstance(decision, dict) and decision.get("approved") is True:
tool_name = pending["tool"]
args = pending["args"]
# 执行真实敏感工具
if tool_name == "cancel_hotel":
result = cancel_hotel.invoke(args)
elif tool_name == "update_ticket":
result = update_ticket.invoke(args)
else:
result = {"error": f"unknown sensitive tool: {tool_name}"}
return {
**state,
"pending_action": None,
"messages": state["messages"] + [ToolMessage(content=str(result), tool_call_id=pending["tool_call_id"])],
}
# 用户拒绝:清空 pending_action,并回到主助手
stack = state["agent_stack"]
if len(stack) > 1:
stack.pop()
return {
**state,
"pending_action": None,
"agent_stack": stack,
"messages": state["messages"] + [AIMessage(content="好的,我不会执行该操作。我们回到主流程继续。")],
}
# ----------------------------
# 7) 各智能体:主/航班/酒店
# ----------------------------
def main_assistant(state: TravelState) -> TravelState:
llm = make_llm().bind_tools(ROUTING_TOOLS)
sys = SystemMessage(
content=(
"你是旅行规划系统的主助手(调度中心)。\n"
"职责:识别用户意图;必要时委派到子助手(航班/酒店);简单问题直接回答。\n"
"当用户提出需要取消/改签等修改动作时,引导到对应子助手处理,并强调需要用户确认。\n"
)
)
msgs = [sys] + state["messages"]
out = llm.invoke(msgs)
return {**state, "messages": state["messages"] + [out]}
def flight_assistant(state: TravelState) -> TravelState:
llm = make_llm().bind_tools([*SAFE_TOOLS, *SENSITIVE_TOOLS, CompleteOrEscalate])
sys = SystemMessage(
content=(
"你是航班助手。\n"
"你可以用 search_flights 查询;如果要改签(update_ticket)或退票等敏感操作,必须先走授权。\n"
"当你准备执行敏感工具时,不要直接调用工具;请在 state.pending_action 写入待执行信息,并让系统走授权网关。\n"
)
)
out = llm.invoke([sys] + state["messages"])
# 如果模型直接发起敏感 tool call:拦截并转 pending_action
if hasattr(out, "tool_calls") and out.tool_calls:
for tc in out.tool_calls:
name = tc["name"]
if name in {"update_ticket"}:
return {
**state,
"messages": state["messages"] + [out],
"pending_action": {
"tool": name,
"args": tc["args"],
"tool_call_id": tc["id"],
},
}
return {**state, "messages": state["messages"] + [out]}
def hotel_assistant(state: TravelState) -> TravelState:
llm = make_llm().bind_tools([*SAFE_TOOLS, *SENSITIVE_TOOLS, CompleteOrEscalate])
sys = SystemMessage(
content=(
"你是酒店助手。\n"
"你可以用 search_hotels 查询;如果要取消订单(cancel_hotel)等敏感操作,必须先走授权。\n"
"当你准备执行敏感工具时,不要直接调用工具;请在 state.pending_action 写入待执行信息,并让系统走授权网关。\n"
)
)
out = llm.invoke([sys] + state["messages"])
if hasattr(out, "tool_calls") and out.tool_calls:
for tc in out.tool_calls:
name = tc["name"]
if name in {"cancel_hotel"}:
return {
**state,
"messages": state["messages"] + [out],
"pending_action": {
"tool": name,
"args": tc["args"],
"tool_call_id": tc["id"],
},
}
return {**state, "messages": state["messages"] + [out]}
# ----------------------------
# 8) Graph:路由与返回
# ----------------------------
def route_from_main(state: TravelState) -> str:
last = state["messages"][-1]
if hasattr(last, "tool_calls") and last.tool_calls:
name = last.tool_calls[0]["name"]
if name == "ToFlightAssistant":
return "to_flights"
if name == "ToHotelAssistant":
return "to_hotels"
return "end_or_wait"
def route_after_child(state: TravelState) -> str:
# 如果有待授权敏感操作,先走网关
if state.get("pending_action"):
return "sensitive_gateway"
# 如果子助手显式 CompleteOrEscalate,则返回主助手
last = state["messages"][-1]
if hasattr(last, "tool_calls") and last.tool_calls:
for tc in last.tool_calls:
if tc["name"] == "CompleteOrEscalate":
# pop 子助手
stack = state["agent_stack"]
if len(stack) > 1:
stack.pop()
state["agent_stack"] = stack
return "back_to_main"
# 否则继续留在当前子助手
return "stay"
def build_graph():
g = StateGraph(TravelState)
g.add_node("main", main_assistant)
g.add_node("entry_flights", create_entry_node("flights"))
g.add_node("entry_hotels", create_entry_node("hotels"))
g.add_node("flights", flight_assistant)
g.add_node("hotels", hotel_assistant)
g.add_node("sensitive_gateway", sensitive_tool_gateway)
g.set_entry_point("main")
g.add_conditional_edges(
"main",
route_from_main,
{
"to_flights": "entry_flights",
"to_hotels": "entry_hotels",
"end_or_wait": END,
},
)
g.add_edge("entry_flights", "flights")
g.add_edge("entry_hotels", "hotels")
g.add_conditional_edges(
"flights",
route_after_child,
{"sensitive_gateway": "sensitive_gateway", "back_to_main": "main", "stay": "flights"},
)
g.add_conditional_edges(
"hotels",
route_after_child,
{"sensitive_gateway": "sensitive_gateway", "back_to_main": "main", "stay": "hotels"},
)
# 授权网关执行完:回到当前栈顶对应助手
def after_gateway_route(state: TravelState) -> str:
top = state["agent_stack"][-1]
return {"main": "main", "flights": "flights", "hotels": "hotels"}.get(top, "main")
g.add_conditional_edges(
"sensitive_gateway",
after_gateway_route,
{"main": "main", "flights": "flights", "hotels": "hotels"},
)
return g.compile()
# ----------------------------
# 9) 本地演示 Runner
# ----------------------------
def run_demo():
graph = build_graph()
state: TravelState = {
"messages": [],
"user_info": {"name": "Alex", "preferences": {"hotel_budget": 1200}},
"agent_stack": ["main"],
"pending_action": None,
}
print("输入 exit 退出。")
while True:
text = input("\n你:").strip()
if text.lower() == "exit":
break
state["messages"].append(HumanMessage(content=text))
# invoke 可能触发 interrupt;这里用 try/except 演示一个“简化版人工审批”
try:
state = graph.invoke(state)
except Exception as e:
# LangGraph 的 interrupt 在不同版本里表现略有差异;
# 这里用一个通用思路:检测到中断后,人工收集确认并继续。
# 如果你的版本提供更直接的中断捕获方式,可按官方文档替换。
print(f"\n[系统中断] {e}")
# 简化:读取 pending_action 来展示
pending = state.get("pending_action")
if pending:
print(f"待执行:{pending['tool']} args={pending['args']}")
decision = input("确认执行?(确认/取消):").strip()
approved = decision == "确认"
# 将 decision 以可被 interrupt 消费的形式写回(示例做法)
# 实战中建议用 checkpointer / stream API 处理更优雅
state["messages"].append(
HumanMessage(content=f"审批结果:{'同意' if approved else '拒绝'}")
)
# 这里直接调用网关节点
state = sensitive_tool_gateway({**state, "pending_action": pending})
state = graph.invoke(state)
# 打印最后一条 AI 输出
for m in state["messages"][-3:]:
if isinstance(m, AIMessage):
print(f"\n助手:{m.content}")
if __name__ == "__main__":
if not os.getenv("OPENAI_API_KEY"):
raise RuntimeError("请先设置环境变量 OPENAI_API_KEY")
run_demo()
运行方式:
pip install langgraph langchain-core langchain-openai
export OPENAI_API_KEY=你的key
python multi_agent_travel.py
你可以试试输入:
- “帮我查一下长沙到北京,2026-01-10 的航班”
- “把我的机票改签到 NH008”
- “取消酒店订单 H10086”(会触发授权中断)
七、总结
多智能体系统听起来挺复杂,但本质就是"分工合作"这个古老智慧的数字化实现。LangGraph提供了很好的框架,让我们能够把这种协作逻辑优雅地表达出来。
这套“可中断、可委派”的多智能体旅行系统,核心优势很明确:
- 专业化:每个子智能体只做一类事,提示词更准、工具更少、成本更可控
- 权限控制:敏感操作统一中断授权,用户放心,系统也更安全
- 模块化扩展:加一个“签证助手”就是加一组工具 + 一条路由,不用重写全局
学AI大模型的正确顺序,千万不要搞错了
🤔2026年AI风口已来!各行各业的AI渗透肉眼可见,超多公司要么转型做AI相关产品,要么高薪挖AI技术人才,机遇直接摆在眼前!
有往AI方向发展,或者本身有后端编程基础的朋友,直接冲AI大模型应用开发转岗超合适!
就算暂时不打算转岗,了解大模型、RAG、Prompt、Agent这些热门概念,能上手做简单项目,也绝对是求职加分王🔋

📝给大家整理了超全最新的AI大模型应用开发学习清单和资料,手把手帮你快速入门!👇👇
学习路线:
✅大模型基础认知—大模型核心原理、发展历程、主流模型(GPT、文心一言等)特点解析
✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑
✅开发基础能力—Python进阶、API接口调用、大模型开发框架(LangChain等)实操
✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用
✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代
✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经
以上6大模块,看似清晰好上手,实则每个部分都有扎实的核心内容需要吃透!
我把大模型的学习全流程已经整理📚好了!抓住AI时代风口,轻松解锁职业新可能,希望大家都能把握机遇,实现薪资/职业跃迁~
这份完整版的大模型 AI 学习资料已经上传优快云,朋友们如果需要可以微信扫描下方优快云官方认证二维码免费领取【保证100%免费】

2392



被折叠的 条评论
为什么被折叠?



