Skip to content

如何构建一个多代理网络

前提条件

本指南假设您熟悉以下内容:

在本指南中,我们将演示如何实现一个多代理网络架构,其中每个代理都可以与其他所有代理通信(多对多连接),并且可以决定下一个调用哪个代理。每个代理将被定义为图节点。

为了实现代理之间的通信,我们将使用交接

def agent(state) -> Command[Literal["agent", "another_agent"]]:
    # 路由/停止的条件可以是任何内容,例如 LLM 工具调用/结构化输出等
    goto = get_next_agent(...)  # 'agent' / 'another_agent'
    return Command(
        # 指定下一个调用哪个代理
        goto=goto,
        # 更新图状态
        update={"my_state_key": "my_state_value"}
    )

设置环境

首先,让我们安装所需的包

%%capture --no-stderr
%pip install -U langgraph langchain-anthropic

import getpass
import os


def _set_env(var: str):
    if not os.environ.get(var):
        os.environ[var] = getpass.getpass(f"{var}: ")


_set_env("ANTHROPIC_API_KEY")
ANTHROPIC_API_KEY:  ········

为LangGraph开发设置LangSmith

注册LangSmith,可以快速发现并解决您的LangGraph项目中的问题,提高项目性能。LangSmith允许您使用跟踪数据来调试、测试和监控使用LangGraph构建的LLM应用程序——更多关于如何开始的信息,请参阅这里

使用自定义代理实现

在这个示例中,我们将构建一个旅行助手代理团队,它们可以通过交接来相互沟通。

我们将创建两个代理:

  • travel_advisor:可以提供旅行目的地的推荐。可以请求hotel_advisor的帮助。
  • hotel_advisor:可以提供酒店推荐。可以请求travel_advisor的帮助。

这是一个完全连接的网络——每个代理都可以与其他任何代理通信。

每个代理将有一个对应的节点函数,该函数可以有条件地返回一个Command对象(交接)。节点函数将使用一个带有系统提示的LLM和一个工具,该工具可以让代理在需要交接给其他代理时发出信号。如果LLM响应包含工具调用,则我们将返回一个Command(goto=<其他代理>)

注意:虽然我们使用工具让LLM发出交接信号,但交接的条件可以是任何内容:LLM的特定响应文本、结构化的输出、任何其他自定义逻辑等。

现在,让我们定义我们的代理节点和图!

from typing_extensions import Literal

from langchain_core.messages import ToolMessage
from langchain_core.tools import tool
from langchain_anthropic import ChatAnthropic
from langgraph.graph import MessagesState, StateGraph, START
from langgraph.types import Command


model = ChatAnthropic(model="claude-3-5-sonnet-latest")


# Define a helper for each of the agent nodes to call


@tool
def transfer_to_travel_advisor():
    """Ask travel advisor for help."""
    # This tool is not returning anything: we're just using it
    # as a way for LLM to signal that it needs to hand off to another agent
    # (See the paragraph above)
    return


@tool
def transfer_to_hotel_advisor():
    """Ask hotel advisor for help."""
    return


def travel_advisor(
    state: MessagesState,
) -> Command[Literal["hotel_advisor", "__end__"]]:
    system_prompt = (
        "You are a general travel expert that can recommend travel destinations (e.g. countries, cities, etc). "
        "If you need hotel recommendations, ask 'hotel_advisor' for help."
    )
    messages = [{"role": "system", "content": system_prompt}] + state["messages"]
    ai_msg = model.bind_tools([transfer_to_hotel_advisor]).invoke(messages)
    # If there are tool calls, the LLM needs to hand off to another agent
    if len(ai_msg.tool_calls) > 0:
        tool_call_id = ai_msg.tool_calls[-1]["id"]
        # NOTE: it's important to insert a tool message here because LLM providers are expecting
        # all AI messages to be followed by a corresponding tool result message
        tool_msg = {
            "role": "tool",
            "content": "Successfully transferred",
            "tool_call_id": tool_call_id,
        }
        return Command(goto="hotel_advisor", update={"messages": [ai_msg, tool_msg]})

    # If the expert has an answer, return it directly to the user
    return {"messages": [ai_msg]}


def hotel_advisor(
    state: MessagesState,
) -> Command[Literal["travel_advisor", "__end__"]]:
    system_prompt = (
        "You are a hotel expert that can provide hotel recommendations for a given destination. "
        "If you need help picking travel destinations, ask 'travel_advisor' for help."
    )
    messages = [{"role": "system", "content": system_prompt}] + state["messages"]
    ai_msg = model.bind_tools([transfer_to_travel_advisor]).invoke(messages)
    # If there are tool calls, the LLM needs to hand off to another agent
    if len(ai_msg.tool_calls) > 0:
        tool_call_id = ai_msg.tool_calls[-1]["id"]
        # NOTE: it's important to insert a tool message here because LLM providers are expecting
        # all AI messages to be followed by a corresponding tool result message
        tool_msg = {
            "role": "tool",
            "content": "Successfully transferred",
            "tool_call_id": tool_call_id,
        }
        return Command(goto="travel_advisor", update={"messages": [ai_msg, tool_msg]})

    # If the expert has an answer, return it directly to the user
    return {"messages": [ai_msg]}


builder = StateGraph(MessagesState)
builder.add_node("travel_advisor", travel_advisor)
builder.add_node("hotel_advisor", hotel_advisor)
# we'll always start with a general travel advisor
builder.add_edge(START, "travel_advisor")

graph = builder.compile()

from IPython.display import display, Image

display(Image(graph.get_graph().draw_mermaid_png()))

API Reference: ToolMessage | tool | ChatAnthropic | StateGraph | START | Command

首先,让我们用一个通用的输入来调用它:

from langchain_core.messages import convert_to_messages


def pretty_print_messages(update):
    if isinstance(update, tuple):
        ns, update = update
        # skip parent graph updates in the printouts
        if len(ns) == 0:
            return

        graph_id = ns[-1].split(":")[0]
        print(f"Update from subgraph {graph_id}:")
        print("\n")

    for node_name, node_update in update.items():
        print(f"Update from node {node_name}:")
        print("\n")

        for m in convert_to_messages(node_update["messages"]):
            m.pretty_print()
        print("\n")

API Reference: convert_to_messages

for chunk in graph.stream(
    {"messages": [("user", "i wanna go somewhere warm in the caribbean")]}
):
    pretty_print_messages(chunk)
Update from node travel_advisor:


================================== Ai Message ==================================

I'd be happy to help you plan a Caribbean vacation! The Caribbean is perfect for warm weather getaways. Let me suggest some fantastic destinations:

1. Dominican Republic
- Known for beautiful beaches, all-inclusive resorts, and tropical climate
- Popular areas include Punta Cana and Puerto Plata
- Great mix of beaches, culture, and activities

2. Jamaica
- Famous for its laid-back atmosphere and beautiful beaches
- Popular spots include Montego Bay, Negril, and Ocho Rios
- Known for reggae music, delicious cuisine, and water sports

3. Bahamas
- Crystal clear waters and stunning beaches
- Perfect for island hopping
- Great for water activities and swimming with pigs at Pig Beach

4. Turks and Caicos
- Pristine beaches and luxury resorts
- Excellent for snorkeling and diving
- More peaceful and less crowded than some other Caribbean destinations

5. Aruba
- Known for constant sunny weather and minimal rainfall
- Beautiful white sand beaches
- Great shopping and dining options

Would you like me to provide more specific information about any of these destinations? Also, if you'd like hotel recommendations for any of these locations, I can transfer you to our hotel advisor for specific accommodation suggestions. Just let me know which destination interests you most!
你可以看到在这种情况下只有第一个代理(travel_advisor)运行了。现在让我们请求更多的推荐:

for chunk in graph.stream(
    {
        "messages": [
            (
                "user",
                "i wanna go somewhere warm in the caribbean. pick one destination and give me hotel recommendations",
            )
        ]
    }
):
    pretty_print_messages(chunk)
Update from node travel_advisor:


================================== Ai Message ==================================

[{'text': "I'll help you with a Caribbean destination recommendation! Given the vast number of beautiful Caribbean islands, I'll recommend one popular destination: the Dominican Republic, specifically Punta Cana. It offers pristine beaches, warm weather year-round, crystal-clear waters, and excellent resorts.\n\nLet me get some hotel recommendations for Punta Cana by consulting our hotel advisor.", 'type': 'text'}, {'id': 'toolu_01B9djUstpDKHVSy3o3rfzsG', 'input': {}, 'name': 'transfer_to_hotel_advisor', 'type': 'tool_use'}]
Tool Calls:
  transfer_to_hotel_advisor (toolu_01B9djUstpDKHVSy3o3rfzsG)
 Call ID: toolu_01B9djUstpDKHVSy3o3rfzsG
  Args:
================================= Tool Message =================================

Successfully transferred


Update from node hotel_advisor:


================================== Ai Message ==================================

For Punta Cana, here are some top hotel recommendations:

1. Hyatt Zilara Cap Cana - Adults-only, all-inclusive luxury resort with pristine beachfront location, multiple pools, and upscale dining options.

2. Hard Rock Hotel & Casino Punta Cana - Perfect for entertainment lovers, featuring 13 pools, 9 restaurants, a casino, and extensive amenities.

3. Excellence Punta Cana - Adults-only, all-inclusive resort known for its romantic atmosphere and excellent service.

4. Secrets Cap Cana Resort & Spa - Sophisticated adults-only resort with beautiful swim-out suites and gourmet dining options.

5. The Reserve at Paradisus Palma Real - Family-friendly luxury resort with dedicated family concierge, kids' activities, and beautiful pools.

These resorts all offer:
- Direct beach access
- Multiple restaurants
- Swimming pools
- Spa facilities
- High-quality accommodations

Would you like more specific information about any of these hotels or would you prefer to explore hotels in a different Caribbean destination?
好了 - travel_advisor 选择了一个目的地,然后决定调用 hotel_advisor 获取更多信息!

与预构建的ReAct代理一起使用

现在让我们看看如何实现同一组旅行代理,但为每个代理提供一些工具来调用。我们将使用预构建的create_react_agent来实现这些代理。首先,让我们创建一些代理将要使用的工具:

import random
from typing_extensions import Literal


@tool
def get_travel_recommendations():
    """Get recommendation for travel destinations"""
    return random.choice(["aruba", "turks and caicos"])


@tool
def get_hotel_recommendations(location: Literal["aruba", "turks and caicos"]):
    """Get hotel recommendations for a given destination."""
    return {
        "aruba": [
            "The Ritz-Carlton, Aruba (Palm Beach)"
            "Bucuti & Tara Beach Resort (Eagle Beach)"
        ],
        "turks and caicos": ["Grace Bay Club", "COMO Parrot Cay"],
    }[location]

让我们也编写一个辅助工具来创建交接工具。请参阅此操作指南以获取更详细的交接工具制作步骤。

from typing import Annotated

from langchain_core.tools import tool
from langchain_core.tools.base import InjectedToolCallId
from langgraph.prebuilt import InjectedState


def make_handoff_tool(*, agent_name: str):
    """Create a tool that can return handoff via a Command"""
    tool_name = f"transfer_to_{agent_name}"

    @tool(tool_name)
    def handoff_to_agent(
        state: Annotated[dict, InjectedState],
        tool_call_id: Annotated[str, InjectedToolCallId],
    ):
        """Ask another agent for help."""
        tool_message = {
            "role": "tool",
            "content": f"Successfully transferred to {agent_name}",
            "name": tool_name,
            "tool_call_id": tool_call_id,
        }
        return Command(
            # navigate to another agent node in the PARENT graph
            goto=agent_name,
            graph=Command.PARENT,
            # This is the state update that the agent `agent_name` will see when it is invoked.
            # We're passing agent's FULL internal message history AND adding a tool message to make sure
            # the resulting chat history is valid.
            update={"messages": state["messages"] + [tool_message]},
        )

    return handoff_to_agent

API Reference: tool | InjectedToolCallId | InjectedState

现在让我们定义代理节点并将它们组合成一个图:

from langgraph.graph import MessagesState, StateGraph, START, END
from langgraph.prebuilt import create_react_agent
from langgraph.types import Command


model = ChatAnthropic(model="claude-3-5-sonnet-latest")

# Define travel advisor ReAct agent
travel_advisor_tools = [
    get_travel_recommendations,
    make_handoff_tool(agent_name="hotel_advisor"),
]
travel_advisor = create_react_agent(
    model,
    travel_advisor_tools,
    prompt=(
        "You are a general travel expert that can recommend travel destinations (e.g. countries, cities, etc). "
        "If you need hotel recommendations, ask 'hotel_advisor' for help. "
        "You MUST include human-readable response before transferring to another agent."
    ),
)


def call_travel_advisor(
    state: MessagesState,
) -> Command[Literal["hotel_advisor", "__end__"]]:
    # You can also add additional logic like changing the input to the agent / output from the agent, etc.
    # NOTE: we're invoking the ReAct agent with the full history of messages in the state
    return travel_advisor.invoke(state)


# Define hotel advisor ReAct agent
hotel_advisor_tools = [
    get_hotel_recommendations,
    make_handoff_tool(agent_name="travel_advisor"),
]
hotel_advisor = create_react_agent(
    model,
    hotel_advisor_tools,
    prompt=(
        "You are a hotel expert that can provide hotel recommendations for a given destination. "
        "If you need help picking travel destinations, ask 'travel_advisor' for help."
        "You MUST include human-readable response before transferring to another agent."
    ),
)


def call_hotel_advisor(
    state: MessagesState,
) -> Command[Literal["travel_advisor", "__end__"]]:
    return hotel_advisor.invoke(state)


builder = StateGraph(MessagesState)
builder.add_node("travel_advisor", call_travel_advisor)
builder.add_node("hotel_advisor", call_hotel_advisor)
# we'll always start with a general travel advisor
builder.add_edge(START, "travel_advisor")

graph = builder.compile()
display(Image(graph.get_graph().draw_mermaid_png()))

API Reference: StateGraph | START | END | create_react_agent | Command

让我们使用与原多智能体系统相同的输入来进行测试:

for chunk in graph.stream(
    {
        "messages": [
            (
                "user",
                "i wanna go somewhere warm in the caribbean. pick one destination and give me hotel recommendations",
            )
        ]
    },
    subgraphs=True,
):
    pretty_print_messages(chunk)
Update from subgraph travel_advisor:


Update from node agent:


================================== Ai Message ==================================

[{'text': "I'll help you find a warm Caribbean destination and get some hotel recommendations for you.\n\nLet me first get some travel recommendations for Caribbean destinations.", 'type': 'text'}, {'id': 'toolu_01GGDP6XSoJZFCYVA9Emhg89', 'input': {}, 'name': 'get_travel_recommendations', 'type': 'tool_use'}]
Tool Calls:
  get_travel_recommendations (toolu_01GGDP6XSoJZFCYVA9Emhg89)
 Call ID: toolu_01GGDP6XSoJZFCYVA9Emhg89
  Args:


Update from subgraph travel_advisor:


Update from node tools:


================================= Tool Message =================================
Name: get_travel_recommendations

turks and caicos


Update from subgraph travel_advisor:


Update from node agent:


================================== Ai Message ==================================

[{'text': 'Based on the recommendations, I suggest Turks and Caicos! This beautiful British Overseas Territory is known for its stunning white-sand beaches, crystal-clear turquoise waters, and perfect warm weather year-round. The main island, Providenciales (often called "Provo"), is home to the famous Grace Bay Beach, consistently rated one of the world\'s best beaches.\n\nNow, let me connect you with our hotel advisor to get some specific hotel recommendations for Turks and Caicos.', 'type': 'text'}, {'id': 'toolu_01JbPSSbTdbWSPNPwsKxifKR', 'input': {}, 'name': 'transfer_to_hotel_advisor', 'type': 'tool_use'}]
Tool Calls:
  transfer_to_hotel_advisor (toolu_01JbPSSbTdbWSPNPwsKxifKR)
 Call ID: toolu_01JbPSSbTdbWSPNPwsKxifKR
  Args:


Update from subgraph hotel_advisor:


Update from node agent:


================================== Ai Message ==================================

[{'text': 'Let me get some hotel recommendations for Turks and Caicos:', 'type': 'text'}, {'id': 'toolu_01JfcmUUmpdiYEFXaDFEkh1G', 'input': {'location': 'turks and caicos'}, 'name': 'get_hotel_recommendations', 'type': 'tool_use'}]
Tool Calls:
  get_hotel_recommendations (toolu_01JfcmUUmpdiYEFXaDFEkh1G)
 Call ID: toolu_01JfcmUUmpdiYEFXaDFEkh1G
  Args:
    location: turks and caicos


Update from subgraph hotel_advisor:


Update from node tools:


================================= Tool Message =================================
Name: get_hotel_recommendations

["Grace Bay Club", "COMO Parrot Cay"]


Update from subgraph hotel_advisor:


Update from node agent:


================================== Ai Message ==================================

Here are two excellent hotel options in Turks and Caicos:

1. Grace Bay Club: This luxury resort is located on the world-famous Grace Bay Beach. It offers elegant accommodations, multiple swimming pools, a spa, and several dining options. The resort is divided into different sections including adults-only and family-friendly areas.

2. COMO Parrot Cay: This exclusive private island resort offers the ultimate luxury escape. It features pristine beaches, world-class spa facilities, and exceptional dining experiences. The resort is known for its serene atmosphere and excellent service.

Would you like more specific information about either of these properties?

Comments