上节我们主要使用了Command实现了多agent的流转,这节我们来学习,如何使用tool实现这个功能。
我们在每个代理节点中明确定义了自定义流转。另一种模式是创建直接返回 Command 对象的特殊交接工具。当代理调用此类工具时,它会将控制权移交给另一个代理。具体来说,代理中的工具执行节点会识别工具返回的命令对象并相应地路由控制流。注意:与前面的示例不同,tool call agent不是单个node,而是可以作为子图节点添加到多代理图中的另一个图。- 由于每个代理都是另一个图中的子图节点,并且工具将在其中一个代理子图节点(例如工具执行器)中调用,我们需要在命令中指定 graph=Command.PARENT,以便 LangGraph 知道在代理子图之外的路径
- 我们可以选择指定状态更新,该更新将在调用下一个代理之前apply到于父图状态。这些状态更新可用于控制目标代理看到的聊天消息历史记录量。例如,您可能选择仅分享来自当前代理的最后一个 AI 消息,或其完整的内部聊天历史记录等。在下面的示例中,我们将分享完整的内部聊天历史记录。
- graph state:通过injectedState;图形长期记忆(使用 InjectedStore);当前工具调用 ID(使用 InjectedToolCallId)
这些不是必需的,但对于创建传递给下一个代理的状态更新很有用。from typing import Annotated
from langchain_core.tools.base import InjectedToolCallId
from langgraph.prebuilt import InjectedState
# 此方法只是为了代码复用包装了一下tool
def make_handoff_tool(*, agent_name: str):
"""创建一个可以通过 Command 返回交接的工具"""
tool_name = f"transfer_to_{agent_name}"
# 这里入参使用了注解,这样在调用的时候可以自动注入,而不是在之前的我们手动判断获取然后设置返回
@tool(tool_name)
def handoff_to_agent(
# 可选地将当前图状态传递给工具(将被 LLM 忽略)
state: Annotated[dict, InjectedState],
# 可选地传递当前工具调用 ID(将被 LLM 忽略)
tool_call_id: Annotated[str, InjectedToolCallId],
):
"""请求另一个代理的帮助。"""
tool_message = ToolMessage(content=f"成功转移到 {agent_name}",
name=tool_name,
tool_call_id=tool_call_id)
return Command(
# 导航到 PARENT 图中的另一个代理节点
goto=agent_name,
graph=Command.PARENT,
# 这是代理 `agent_name` 被调用时将看到的状态更新。
# 我们传递代理的完整内部消息历史记录并添加一个工具消息以确保结果聊天历史记录有效。
update={"messages": state["messages"] + [tool_message]},
)
return handoff_to_agent
from langchain_core.messages import ToolMessage
from langchain_core.tools import tool
from langgraph.graph import MessagesState, StateGraph, START
from langgraph.types import Command
@tool
def add(a: int, b: int) -> int:
"""将两个数字相加。"""
return a + b
@tool
def multiply(a: int, b: int) -> int:
"""将两个数字相乘。"""
return a * b
from langchain_ollama import ChatOllama
import base_conf
model = ChatOllama(base_url=base_conf.base_url,model=base_conf.model_name,temperature=0)
from langgraph.prebuilt import create_react_agent
addition_expert = create_react_agent(
model,
[add, make_handoff_tool(agent_name="multiplication_expert")],
state_modifier="You are an addition expert, you can ask the multiplication expert for help with multiplication.",
)
multiplication_expert = create_react_agent(
model,
[multiply, make_handoff_tool(agent_name="addition_expert")],
state_modifier="You are a multiplication expert, you can ask an addition expert for help with addition.",
)
builder = StateGraph(MessagesState)
builder.add_node("addition_expert", addition_expert)
builder.add_node("multiplication_expert", multiplication_expert)
builder.add_edge(START, "addition_expert")
graph = builder.compile()
from langchain_core.messages import convert_to_messages
def pretty_print_messages(update):
if isinstance(update, tuple):
ns, update = update
# 在打印输出中跳过父图更新
if len(ns) == 0:
return
graph_id = ns[-1].split(":")[0]
print(f"来自子图 {graph_id} 的更新:")
print("\n")
for node_name, node_update in update.items():
print(f"来自节点 {node_name} 的更新:")
print("\n")
for m in convert_to_messages(node_update["messages"]):
m.pretty_print()
print("\n")
for chunk in graph.stream({"messages": [("user", "what's (3 + 5) * 12")]}):
pretty_print_messages(chunk)
来自节点 addition_expert 的更新:
================================ Human Message =================================
what's (3 + 5) * 12
================================== Ai Message ==================================
Tool Calls:
add (d6bbf9e9-235e-40ec-8970-48e7adbff070)
Call ID: d6bbf9e9-235e-40ec-8970-48e7adbff070
Args:
a: 3
b: 5
================================= Tool Message =================================
Name: add
8
================================== Ai Message ==================================
Tool Calls:
transfer_to_multiplication_expert (5a851781-4cf3-4bf9-8b30-347736c8a7f5)
Call ID: 5a851781-4cf3-4bf9-8b30-347736c8a7f5
Args:
================================= Tool Message =================================
Name: transfer_to_multiplication_expert
成功转移到 multiplication_expert
来自节点 multiplication_expert 的更新:
================================ Human Message =================================
what's (3 + 5) * 12
================================== Ai Message ==================================
Tool Calls:
add (d6bbf9e9-235e-40ec-8970-48e7adbff070)
Call ID: d6bbf9e9-235e-40ec-8970-48e7adbff070
Args:
a: 3
b: 5
================================= Tool Message =================================
Name: add
8
================================== Ai Message ==================================
Tool Calls:
transfer_to_multiplication_expert (5a851781-4cf3-4bf9-8b30-347736c8a7f5)
Call ID: 5a851781-4cf3-4bf9-8b30-347736c8a7f5
Args:
================================= Tool Message =================================
Name: transfer_to_multiplication_expert
成功转移到 multiplication_expert
================================== Ai Message ==================================
Tool Calls:
multiply (aa714a78-e60f-469e-bed7-dad9d2a73e50)
Call ID: aa714a78-e60f-469e-bed7-dad9d2a73e50
Args:
a: 8
b: 12
================================= Tool Message =================================
Name: multiply
96
================================== Ai Message ==================================
The result of (3 + 5) * 12 is 96.
核心点的思路是原来手动判断然后设置的信息,换成了Injecte的方式。