综合案例: 记忆存储 + 人工干预 + 工具节点
需求: 开发一个智能小秘书。要求:可以帮我查询火车票,帮我生成分析图表,可以帮我获取网络上的数据。
一、MCP工具


调用工具: 异步,并发,参数验证,异常处理,人工介入等各种功能和要求。
|
Python
class BasicToolNode:
"""异步工具节点,用于并发执行AIMessage中请求的工具调用
功能:
1. 接收工具列表并建立名称索引
2. 并发执行消息中的工具调用请求
3. 自动处理同步/异步工具适配
"""
def __init__(self, tools: list) -> None:
"""初始化工具节点
Args:
tools: 工具列表,每个工具需包含name属性
"""
self.tools_by_name = {tool.name: tool for tool in tools} # 建立工具名称索引
async def __call__(self, inputs: Dict[str, Any]) -> Dict[str, List[ToolMessage]]:
"""异步调用入口
Args:
inputs: 输入字典,需包含"messages"字段
Returns:
包含ToolMessage列表的字典
Raises:
ValueError: 当输入无效时抛出
"""
# 1. 输入验证
if not (messages := inputs.get("messages")):
raise ValueError("输入数据中未找到消息内容") # 改进后的中文错误提示
message = messages[-1] # 取最新消息
# 2. 并发执行工具调用
outputs = await self._execute_tool_calls(message.tool_calls)
return {"messages": outputs}
async def _execute_tool_calls(self, tool_calls: List[Dict]) -> List[ToolMessage]:
"""执行实际工具调用
Args:
tool_calls: 工具调用请求列表
Returns:
ToolMessage结果列表
"""
async def _invoke_tool(tool_call: Dict) -> ToolMessage:
"""执行单个工具调用
Args:
tool_call: 工具调用请求字典,需包含name/args/id字段
Returns:
封装的ToolMessage
Raises:
KeyError: 工具未注册时抛出
RuntimeError: 工具调用失败时抛出
"""
try:
# 3. 异步调用工具
tool = self.tools_by_name.get(tool_call["name"])
if not tool:
raise KeyError(f"未注册的工具: {tool_call['name']}")
if hasattr(tool, 'ainvoke'): # 优先使用异步方法
tool_result = await tool.ainvoke(tool_call["args"])
else: # 同步工具通过线程池转异步
loop = asyncio.get_running_loop()
tool_result = await loop.run_in_executor(
None, # 使用默认线程池
tool.invoke, # 同步调用方法
tool_call["args"] # 参数
)
# 4. 构造ToolMessage
return ToolMessage(
content=json.dumps(tool_result, ensure_ascii=False),
name=tool_call["name"],
tool_call_id=tool_call["id"],
)
except Exception as e:
raise RuntimeError(f"工具调用失败: {tool_call['name']}") from e
# 5. 并发执行所有工具调用
# '''
# asyncio.gather() 是 Python 异步编程中用于并发调度多个协程的核心函数,其核心行为包括:
# 并发执行:所有传入的协程会被同时调度到事件循环中,通过非阻塞 I/O 实现并行处理。
# 结果收集:按输入顺序返回所有协程的结果(或异常),与任务完成顺序无关。
# 异常处理:默认情况下,任一任务失败会立即取消其他任务并抛出异常;若设置 return_exceptions=True,则异常会作为结果返回。
# '''
try:
return await asyncio.gather(*[
_invoke_tool(tool_call) for tool_call in tool_calls
])
except Exception as e:
raise RuntimeError("并发执行工具时发生错误") from e
|
三、LangGraph自带的工具执行
ToolNode
一个运行在最后一条 AIMessage 中调用的工具的节点。
它可以在 StateGraph 中与“messages”状态键一起使用(或通过 ToolNode 的“messages_key”传递自定义键)。如果请求多个工具调用,它们将并行运行。输出将是一个 ToolMessages 列表,每个工具调用对应一个。
对比

点击图片可查看完整电子表格
|
Python
from langgraph.prebuilt import ToolNode
from langchain_core.tools import tool
@tool
def calculator(a: int, b: int) -> int:
"""Add two numbers."""
return a + b
tool_node = ToolNode([calculator]) |
四、人机协作 (Human-in-the-loop)
要在代理或工作流中审查、编辑和批准工具调用,请使用LangGraph的人机协作功能,以在工作流的任何点启用人工干预。这在大型语言模型 (LLM) 驱动的应用程序中特别有用,因为模型输出可能需要验证、修正或额外的上下文。

您可以使用interrupt和Command实现四种典型的应用场景
- 批准或拒绝:在关键步骤(例如API调用)之前暂停图,以审查并批准操作。如果操作被拒绝,您可以阻止图执行该步骤,并可能采取替代操作。这种模式通常涉及根据人工输入路由图。
- 编辑图状态:暂停图以审查和编辑图状态。这对于纠正错误或用附加信息更新状态很有用。这种模式通常涉及用人工输入更新状态。
- 审查工具调用:在工具执行之前,暂停图以审查和编辑LLM请求的工具调用。
- 验证人工输入:在进行下一步之前,暂停图以验证人工输入。
这些功能功能的主要接口是interrupt函数。在节点内调用interrupt将暂停执行。通过传入Command,可以恢复执行并接收来自人工的新输入。interrupt在功能上类似于 Python 的内置input()。



使用 interrupt 暂停(中断)
要在图中使用 interrupt,您需要
- 指定一个检查点器,用于在每一步之后保存图状态。
- 在适当位置调用 interrupt(),第二种:传入interrupt_before[节点名字]。
- 使用线程 ID 运行图,直到触发 interrupt。
- 使用 invoke/ainvoke/stream/astream
- 恢复执行: interrupt_before--->agent.astream(None, config, stream_mode='values')
- 恢复执行:interrupt()---->agent.astream(Command(resume={"answer": user_input}), config, stream_mode='values')
综合案例: 多智能体的监督和管理
该课程在:携程AI智能助手项目增加了,链接:https://www.mashibing.com/subject/167?activeNav=1&courseNo=2801&courseVersionId=3750
langgraph-supervisor 可以为多个Agent的工作流构建一个监督者。
监督者 是一种多代理架构,其中专业化代理由一个中心监督代理协调。监督代理控制所有通信流和任务委托,根据当前上下文和任务要求决定调用哪个代理

多智能体交互中的一个常见模式是交接,即一个智能体将控制权移交给另一个智能体。交接允许您指定:
- 目的地:要导航到的目标智能体(例如,要前往的节点名称)
要在LangGraph中实现交接,智能体节点可以返回Command对象,该对象允许您结合控制流和状态更新。一种常见模式是将交接封装在工具调用中,例如:
|
Python
@tool # 使用tool装饰器将该函数注册为可调用工具
def transfer_to_bob():
"""交接给bob"""
# 工具功能描述:执行向bob交接的操作
return Command(
# 指定要跳转到的目标代理(节点)名称
goto="bob",
# 要传递给目标代理的数据内容
update={"my_state_key": "my_state_value"},
# 指示LangGraph需要导航到父级图中的代理节点
graph=Command.PARENT,
) |
所有评论(0)