精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

探索LangGraph:構建多專家協作模型

發布于 2024-5-27 11:56
瀏覽
0收藏

探索LangGraph:如何創建一個既智能又可控的航空客服AI利用單一提示的方法確實能覆蓋很廣的應用場景。但是,如果想要為特定的用戶需求提供穩定且出色的體驗,僅靠這種方法就顯得有些力不從心了。

取而代之的是,我們可以通過識別用戶的意圖,并將其引導至相應的定制化流程或“技能”,來滿足用戶的具體需求。每個流程都可以專注于特定的領域,這樣不僅可以實現各自領域的優化提升,還不會影響到整體助手的性能。

在本節中,我們將用戶交互體驗劃分為多個子圖,形成一個類似下面的結構:

探索LangGraph:構建多專家協作模型-AI.x社區

在上圖中,每個方框都代表一個具有特定功能的獨立工作流程。主要助手負責接收用戶的初步詢問,然后根據詢問內容將任務分配給相應的專家。

狀態管理

我們需要跟蹤在任何特定時刻哪個子圖正在控制交互過程。雖然我們可以通過消息列表上的一些計算來實現這一點,但更簡單的方法是使用一個專門的堆棧來跟蹤。

在下面的State?中添加一個dialog_state?列表。每當一個node?運行并返回dialog_state?的值時,就會調用update_dialog_stack函數來決定如何更新堆棧。

from typing import Annotated, Literal, Optional

from typing_extensions import TypedDict

from langgraph.graph.message import AnyMessage, add_messages


def update_dialog_stack(left: list[str], right: Optional[str]) -> list[str]:
    """推入或彈出狀態。"""
    if right is None:
        return left
    if right == "pop":
        return left[:-1]
    return left + [right]


class State(TypedDict):
    messages: Annotated[list[AnyMessage], add_messages]
    user_info: str
    dialog_state: Annotated[
        list[
            Literal[
                "assistant",
                "update_flight",
                "book_car_rental",
                "book_hotel",
                "book_excursion",
            ]
        ],
        update_dialog_stack,
    ]

助手

這次我們將為每個工作流程創建一個助手。這意味著:

  1. 航班預訂助手
  2. 酒店預訂助手
  3. 汽車租賃助手
  4. 旅行助手
  5. 最后,一個“主要助手”來在這些助手之間進行切換

如果你仔細觀察,你會發現這實際上是我們在多代理示例中提到的監督者設計模式的一個實例。

下面,定義每個助手的Runnable?對象。每個Runnable?都有一個提示、LLM以及針對該助手的工具集。每個專門的助手還可以調用CompleteOrEscalate工具,以指示控制權應該交回給主要助手。這可能發生在助手成功完成任務,或者用戶改變主意或需要該特定工作流程范圍之外的幫助時。

from langchain_anthropic import ChatAnthropic
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_core.runnables import Runnable, RunnableConfig


class Assistant:
    def __init__(self, runnable: Runnable):
        self.runnable = runnable

    def __call__(self, state: State, config: RunnableConfig):
        while True:
            result = self.runnable.invoke(state)

            if not result.tool_calls and (
                not result.content
                or isinstance(result.content, list)
                and not result.content[0].get("text")
            ):
                messages = state["messages"] + [("user", "用真實的輸出回應。")]
                state = {**state, "messages": messages}
                messages = state["messages"] + [("user", "用真實的輸出回應。")]
                state = {**state, "messages": messages}
            else:
                break
        return {"messages": result}


class CompleteOrEscalate(BaseModel):
    """一個工具,標記當前任務為已完成和/或將對話控制權升級到主助手,
    主助手可以根據用戶的需求重新路由對話。"""

    cancel: bool = True
    reason: str

    class Config:
        schema_extra = {
            "example": {
                "cancel": True,
                "reason": "用戶改變了他們對當前任務的想法。",
            },
            "example 2": {
                "cancel": True,
                "reason": "我已經完全完成了任務。",
            },
            "example 3": {
                "cancel": False,
                "reason": "我需要搜索用戶的電子郵件或日歷以獲取更多信息。",
            },
        }

航班預訂助手

創建一個專門的助手來處理航班更新和取消預訂的任務。

# 航班預訂助手

flight_booking_prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "您是專門處理航班更新的助手。"
            "每當用戶需要幫助更新他們的預訂時,主要助手就會委派工作給您。"
            "與客戶確認更新的航班詳情,并告知他們任何額外費用。"
            "搜索時,要堅持不懈。如果第一次搜索沒有結果,就擴大您的查詢范圍。"
            "如果您需要更多信息或客戶改變了主意,將任務升級回主助手。"
            "記住,只有在相關工具成功使用后,預訂才算完成。"
            "\n\n當前用戶航班信息:\n<Flights>\n{user_info}\n</Flights>"
            "\n當前時間:{time}。"
            "\n\n如果用戶需要幫助,而且您的工具都不適合,那么"
            '"CompleteOrEscalate" 對話到主機助手。不要浪費用戶的時間。不要編造無效的工具或功能。',
        ),
        ("placeholder", "{messages}"),
    ]
).partial(time=datetime.now())

update_flight_safe_tools = [search_flights]
update_flight_sensitive_tools = [update_ticket_to_new_flight, cancel_ticket]
update_flight_tools = update_flight_safe_tools + update_flight_sensitive_tools
update_flight_runnable = flight_booking_prompt | llm.bind_tools(
    update_flight_tools + [CompleteOrEscalate]
)

汽車租賃助手

接下來,創建一個汽車租賃助手,以滿足所有租車需求。

# 汽車租賃助手
book_car_rental_prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "您是專門處理汽車租賃預訂的助手。"
            "每當用戶需要幫助預訂汽車租賃時,主要助手就會委派工作給您。"
            "根據用戶的偏好搜索可用的汽車租賃,并與客戶確認預訂詳情。"
            "搜索時,要堅持不懈。如果第一次搜索沒有結果,就擴大您的查詢范圍。"
            "如果您需要更多信息或客戶改變了主意,將任務升級回主助手。"
            "記住,只有在相關工具成功使用后,預訂才算完成。"
            "\n當前時間:{time}。"
            "\n\n如果用戶需要幫助,而且您的工具都不適合,那么 "
            '"CompleteOrEscalate" 對話到主機助手。不要浪費用戶的時間。不要編造無效的工具或功能。'
            "\n\n一些您應該 CompleteOrEscalate 的示例:"
            " - '今年這個時候的天氣怎么樣?'"
            " - '有哪些航班可用?'"
            " - '沒關系,我想我會單獨預訂'"
            " - '哦等等,我還沒有預訂我的航班,我會先做這件事'"
            " - '汽車租賃預訂確認'",
        ),
        ("placeholder", "{messages}"),
    ]
).partial(time=datetime.now())

book_car_rental_safe_tools = [search_car_rentals]
book_car_rental_sensitive_tools = [
    book_car_rental,
    update_car_rental,
    cancel_car_rental,
]
book_car_rental_tools = book_car_rental_safe_tools + book_car_rental_sensitive_tools
book_car_rental_runnable = book_car_rental_prompt | llm.bind_tools(
    book_car_rental_tools + [CompleteOrEscalate]
)

酒店預訂助手

然后定義酒店預訂的工作流程。

# 酒店預訂助手
book_hotel_prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "您是專門處理酒店預訂的助手。"
            "每當用戶需要幫助預訂酒店時,主要助手就會委派工作給您。"
            "根據用戶的偏好搜索可用的酒店,并與客戶確認預訂詳情。"
            "搜索時,要堅持不懈。如果第一次搜索沒有結果,就擴大您的查詢范圍。"
            "如果您需要更多信息或客戶改變了主意,將任務升級回主助手。"
            "記住,只有在相關工具成功使用后,預訂才算完成。"
            "\n當前時間:{time}。"
            '\n\n如果用戶需要幫助,而且您的工具都不適合,那么 "CompleteOrEscalate" 對話到主機助手。'
            "不要浪費用戶的時間。不要編造無效的工具或功能。"
            "\n\n一些您應該 CompleteOrEscalate 的示例:"
            " - '今年這個時候的天氣怎么樣?'"
            " - '沒關系,我想我會單獨預訂'"
            " - '我需要弄清楚我在那里的時候的交通'"
            " - '哦等等,我還沒有預訂我的航班,我會先做這件事'"
            " - '酒店預訂確認'",
        ),
        ("placeholder", "{messages}"),
    ]
).partial(time=datetime.now())

book_hotel_safe_tools = [search_hotels]
book_hotel_sensitive_tools = [book_hotel, update_hotel, cancel_hotel]
book_hotel_tools = book_hotel_safe_tools + book_hotel_sensitive_tools
book_hotel_runnable = book_hotel_prompt | llm.bind_tools(
    book_hotel_tools + [CompleteOrEscalate]
)

旅行助手

之后,定義旅行助手。

# 旅行助手

book_excursion_prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "您是專門處理旅行建議的助手。"
            "每當用戶需要幫助預訂推薦的旅行時,主要助手就會委派工作給您。"
            "根據用戶的偏好搜索可用的旅行建議,并與客戶確認預訂詳情。"
            "如果您需要更多信息或客戶改變了主意,將任務升級回主助手。"
            "搜索時,要堅持不懈。如果第一次搜索沒有結果,就擴大您的查詢范圍。"
            "記住,只有在相關工具成功使用后,預訂才算完成。"
            "\n當前時間:{time}。"
            '\n\n如果用戶需要幫助,而且您的工具都不適合,那么 "CompleteOrEscalate" 對話到主機助手。不要浪費用戶的時間。不要編造無效的工具或功能。'
            "\n\n一些您應該 CompleteOrEscalate 的示例:"
            " - '沒關系,我想我會單獨預訂'"
            " - '我需要在那里的時候弄清楚交通'"
            " - '哦等等,我還沒有預訂我的航班,我會先做這件事'"
            " - '旅行預訂確認!'",
        ),
        ("placeholder", "{messages}"),
    ]
).partial(time=datetime.now())

book_excursion_safe_tools = [search_trip_recommendations]
book_excursion_sensitive_tools = [book_excursion, update_excursion, cancel_excursion]
book_excursion_tools = book_excursion_safe_tools + book_excursion_sensitive_tools
book_excursion_runnable = book_excursion_prompt | llm.bind_tools(
    book_excursion_tools + [CompleteOrEscalate]
)

主要助手

最后,創建主要助手。

# 主要助手
class ToFlightBookingAssistant(BaseModel):
    """將工作轉交給專門助手來處理航班更新和取消。"""

    request: str = Field(
        descriptinotallow="更新航班助手在繼續操作之前需要澄清的任何必要的后續問題。"
    )


class ToBookCarRental(BaseModel):
    """將工作轉交給專門助手來處理汽車租賃預訂。"""

    location: str = Field(
        descriptinotallow="用戶想要租車的地點。"
    )
    start_date: str = Field(descriptinotallow="汽車租賃的開始日期。")
    end_date: str = Field(descriptinotallow="汽車租賃的結束日期。")
    request: str = Field(
        descriptinotallow="用戶關于汽車租賃的任何額外信息或請求。"
    )

    class Config:
        schema_extra = {
            "example": {
                "location": "巴塞爾",
                "start_date": "2023-07-01",
                "end_date": "2023-07-05",
                "request": "我需要一輛自動變速箱的緊湊型汽車。",
            }
        }


class ToHotelBookingAssistant(BaseModel):
    """將工作轉交給專門助手來處理酒店預訂。"""

    location: str = Field(
        descriptinotallow="用戶想要預訂酒店的地點。"
    )
    checkin_date: str = Field(descriptinotallow="酒店的入住日期。")
    checkout_date: str = Field(descriptinotallow="酒店的退房日期。")
    request: str = Field(
        descriptinotallow="用戶關于酒店預訂的任何額外信息或請求。"
    )

    class Config:
        schema_extra = {
            "example": {
                "location": "蘇黎世",
                "checkin_date": "2023-08-15",
                "checkout_date": "2023-08-20",
                "request": "我更喜歡靠近市中心的酒店,有風景的房間。",
            }
        }


class ToBookExcursion(BaseModel):
    """將工作轉交給專門助手來處理旅行推薦和其他旅行預訂。"""

    location: str = Field(
        descriptinotallow="用戶想要預訂推薦旅行的地點。"
    )
    request: str = Field(
        descriptinotallow="用戶關于旅行建議的任何額外信息或請求。"
    )

    class Config:
        schema_extra = {
            "example": {
                "location": "盧塞恩",
                "request": "用戶對戶外活動和風景感興趣。",
            }
        }


# 最高級助手執行一般問答,并將專業任務委派給其他助手。
# 任務委派是一種簡單的語義路由/簡單的意圖檢測
# llm = ChatAnthropic(model="claude-3-haiku-20240307")
llm = ChatAnthropic(model="claude-3-sonnet-20240229", temperature=1)

primary_assistant_prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "您是瑞士航空的樂于助人的客服助手。"
            "您的主要角色是搜索航班信息和公司政策,以回答客戶查詢。"
            "如果客戶請求更新或取消預訂、租車、預訂酒店或獲取旅行建議,"
            "通過調用相應的工具,將任務委派給適當的專業助手。您自己無法進行這些類型的更改。"
            "只有專業助手才有權為用戶執行此操作。"
            "用戶不知道不同的專業助手,所以不要提及他們;只需通過功能調用來靜靜地委派。"
            "向客戶提供詳細信息,并在得出信息不可用的結論之前,始終再次檢查數據庫。"
            "當搜索時,要堅持不懈。如果第一次搜索沒有結果,就擴大您的查詢范圍。"
            "\n\n當前用戶航班信息:\n<Flights>\n{user_info}\n</Flights>"
            "\n當前時間:{time}。",
        ),
        ("placeholder", "{messages}"),
    ]
).partial(time=datetime.now())
primary_assistant_tools = [
    TavilySearchResults(max_results=1),
    search_flights,
    lookup_policy,
]
assistant_runnable = primary_assistant_prompt | llm.bind_tools(
    primary_assistant_tools
    + [
        ToFlightBookingAssistant,
        ToBookCarRental,
        ToHotelBookingAssistant,
        ToBookExcursion,
    ]
)

創建助手

我們即將創建圖。在前一節中,我們做出了設計決策,讓所有節點之間共享messages?狀態。這在每個委派助手都可以看到整個用戶旅程并擁有共享上下文方面非常強大。然而,這意味著較弱的LLMs很容易對它們特定的范圍感到困惑。為了標記主助手和委派工作流程之一之間的“交接”(并完成路由器的工具調用),我們將向狀態中添加一個ToolMessage。

實用工具

創建一個函數,為每個工作流程制作一個“入口”節點,聲明“當前助手是 assistant_name”。

from typing import Callable

from langchain_core.messages import ToolMessage


def create_entry_node(assistant_name: str, new_dialog_state: str) -> Callable:
    def entry_node(state: State) -> dict:
        tool_call_id = state["messages"][-1].tool_calls[0]["id"]
        return {
            "messages": [
                ToolMessage(
                    cnotallow=f"現在助手是 {assistant_name}。回想一下主機助手和用戶之間的上述對話。"
                    f" 用戶的意圖尚未得到滿足。使用提供的工具來幫助用戶。記住,你是 {assistant_name},"
                    " 預訂、更新、其他操作或其他動作只有在您成功調用適當的工具后才完成。"
                    " 如果用戶改變主意或需要其他任務的幫助,請調用 CompleteOrEscalate 函數,讓主機助手接管控制權。"
                    " 不要提及你是誰 - 只作為助手的代理行事。",
                    tool_call_id=tool_call_id,
                )
            ],
            "dialog_state": new_dialog_state,
        }

    return entry_node

定義圖

現在是我們開始構建圖的時候了。和以前一樣,我們將從一個節點開始,用用戶的當前信息預填充狀態。

from typing import Literal

from langgraph.checkpoint.sqlite import SqliteSaver
from langgraph.graph import END, StateGraph
from langgraph.prebuilt import tools_condition

builder = StateGraph(State)


def user_info(state: State):
    return {"user_info": fetch_user_flight_information.invoke({})}


builder.add_node("fetch_user_info", user_info)
builder.set_entry_point("fetch_user_info")

現在,讓我們開始構建我們定制的工作流程。每個小工作流程的結構都和我們在第3部分中展示的完整工作流程圖非常相似,它們都包含5個節點:

  1. enter_*?: 使用你之前定義的create_entry_node工具來創建一個ToolMessage,這個ToolMessage表明新的專業助手已經接管了工作。
  2. 助手: 這個由提示和大型語言模型(LLM)組成的模塊會根據當前狀態來決定是使用一個工具、向用戶提問還是結束整個工作流程(返回到主助手)。
  3. *_safe_tools: 這些是助手可以在不需要用戶確認的情況下使用的“只讀”工具。
  4. *_sensitive_tools?: 這些具有“寫入”權限的工具需要用戶的確認,并且在我們編譯工作流程圖時,它們會被設置一個interrupt_before。
  5. leave_skill?: 通過彈出dialog_state來表示主助手重新掌握了控制權。

由于這些工作流程的相似性,我們本可以定義一個工廠函數來生成它們。但因為這是一個教程,我們會逐一明確地定義它們。

首先,我們來創建一個航班預訂助手,它專門負責管理用戶更新和取消預訂航班的流程。

# 航班預訂助手
# Flight booking assistant
builder.add_node(
    "enter_update_flight",
    create_entry_node("Flight Updates & Booking Assistant", "update_flight"),
)
builder.add_node("update_flight", Assistant(update_flight_runnable))
builder.add_edge("enter_update_flight", "update_flight")
builder.add_node(
    "update_flight_sensitive_tools",
    create_tool_node_with_fallback(update_flight_sensitive_tools),
)
builder.add_node(
    "update_flight_safe_tools",
    create_tool_node_with_fallback(update_flight_safe_tools),
)


def route_update_flight(
    state: State,
) -> Literal[
    "update_flight_sensitive_tools",
    "update_flight_safe_tools",
    "leave_skill",
    "__end__",
]:
    route = tools_condition(state)
    if route == END:
        return END
    tool_calls = state["messages"][-1].tool_calls
    did_cancel = any(tc["name"] == CompleteOrEscalate.__name__ for tc in tool_calls)
    if did_cancel:
        return "leave_skill"
    safe_toolnames = [t.name for t in update_flight_safe_tools]
    if all(tc["name"] in safe_toolnames for tc in tool_calls):
        return "update_flight_safe_tools"
    return "update_flight_sensitive_tools"


builder.add_edge("update_flight_sensitive_tools", "update_flight")
builder.add_edge("update_flight_safe_tools", "update_flight")
builder.add_conditional_edges("update_flight", route_update_flight)


# This node will be shared for exiting all specialized assistants
def pop_dialog_state(state: State) -> dict:
    """Pop the dialog stack and return to the main assistant.

    This lets the full graph explicitly track the dialog flow and delegate control
    to specific sub-graphs.
    """
    messages = []
    if state["messages"][-1].tool_calls:
        # Note: Doesn't currently handle the edge case where the llm performs parallel tool calls
        messages.append(
            ToolMessage(
                cnotallow="Resuming dialog with the host assistant. Please reflect on the past conversation and assist the user as needed.",
                tool_call_id=state["messages"][-1].tool_calls[0]["id"],
            )
        )
    return {
        "dialog_state": "pop",
        "messages": messages,
    }


builder.add_node("leave_skill", pop_dialog_state)
builder.add_edge("leave_skill", "primary_assistant")

接下來,創建一個租車助手的工作流程圖,它將負責處理所有的租車需求。

# 租車助手

# Car rental assistant

builder.add_node(
    "enter_book_car_rental",
    create_entry_node("Car Rental Assistant", "book_car_rental"),
)
builder.add_node("book_car_rental", Assistant(book_car_rental_runnable))
builder.add_edge("enter_book_car_rental", "book_car_rental")
builder.add_node(
    "book_car_rental_safe_tools",
    create_tool_node_with_fallback(book_car_rental_safe_tools),
)
builder.add_node(
    "book_car_rental_sensitive_tools",
    create_tool_node_with_fallback(book_car_rental_sensitive_tools),
)


def route_book_car_rental(
    state: State,
) -> Literal[
    "book_car_rental_safe_tools",
    "book_car_rental_sensitive_tools",
    "leave_skill",
    "__end__",
]:
    route = tools_condition(state)
    if route == END:
        return END
    tool_calls = state["messages"][-1].tool_calls
    did_cancel = any(tc["name"] == CompleteOrEscalate.__name__ for tc in tool_calls)
    if did_cancel:
        return "leave_skill"
    safe_toolnames = [t.name for t in book_car_rental_safe_tools]
    if all(tc["name"] in safe_toolnames for tc in tool_calls):
        return "book_car_rental_safe_tools"
    return "book_car_rental_sensitive_tools"


builder.add_edge("book_car_rental_sensitive_tools", "book_car_rental")
builder.add_edge("book_car_rental_safe_tools", "book_car_rental")
builder.add_conditional_edges("book_car_rental", route_book_car_rental)

然后,創建一個酒店預訂的工作流程。

# 酒店預訂助手
# Hotel booking assistant
builder.add_node(
    "enter_book_hotel", create_entry_node("Hotel Booking Assistant", "book_hotel")
)
builder.add_node("book_hotel", Assistant(book_hotel_runnable))
builder.add_edge("enter_book_hotel", "book_hotel")
builder.add_node(
    "book_hotel_safe_tools",
    create_tool_node_with_fallback(book_hotel_safe_tools),
)
builder.add_node(
    "book_hotel_sensitive_tools",
    create_tool_node_with_fallback(book_hotel_sensitive_tools),
)


def route_book_hotel(
    state: State,
) -> Literal[
    "leave_skill", "book_hotel_safe_tools", "book_hotel_sensitive_tools", "__end__"
]:
    route = tools_condition(state)
    if route == END:
        return END
    tool_calls = state["messages"][-1].tool_calls
    did_cancel = any(tc["name"] == CompleteOrEscalate.__name__ for tc in tool_calls)
    if did_cancel:
        return "leave_skill"
    tool_names = [t.name for t in book_hotel_safe_tools]
    if all(tc["name"] in tool_names for tc in tool_calls):
        return "book_hotel_safe_tools"
    return "book_hotel_sensitive_tools"


builder.add_edge("book_hotel_sensitive_tools", "book_hotel")
builder.add_edge("book_hotel_safe_tools", "book_hotel")
builder.add_conditional_edges("book_hotel", route_book_hotel)

之后,定義一個旅行預訂助手。

# 旅行預訂助手
# Excursion assistant
builder.add_node(
    "enter_book_excursion",
    create_entry_node("Trip Recommendation Assistant", "book_excursion"),
)
builder.add_node("book_excursion", Assistant(book_excursion_runnable))
builder.add_edge("enter_book_excursion", "book_excursion")
builder.add_node(
    "book_excursion_safe_tools",
    create_tool_node_with_fallback(book_excursion_safe_tools),
)
builder.add_node(
    "book_excursion_sensitive_tools",
    create_tool_node_with_fallback(book_excursion_sensitive_tools),
)


def route_book_excursion(
    state: State,
) -> Literal[
    "book_excursion_safe_tools",
    "book_excursion_sensitive_tools",
    "leave_skill",
    "__end__",
]:
    route = tools_condition(state)
    if route == END:
        return END
    tool_calls = state["messages"][-1].tool_calls
    did_cancel = any(tc["name"] == CompleteOrEscalate.__name__ for tc in tool_calls)
    if did_cancel:
        return "leave_skill"
    tool_names = [t.name for t in book_excursion_safe_tools]
    if all(tc["name"] in tool_names for tc in tool_calls):
        return "book_excursion_safe_tools"
    return "book_excursion_sensitive_tools"


builder.add_edge("book_excursion_sensitive_tools", "book_excursion")
builder.add_edge("book_excursion_safe_tools", "book_excursion")
builder.add_conditional_edges("book_excursion", route_book_excursion)

最后,創建一個主助手。

# Primary assistant
builder.add_node("primary_assistant", Assistant(assistant_runnable))
builder.add_node(
    "primary_assistant_tools", create_tool_node_with_fallback(primary_assistant_tools)
)


def route_primary_assistant(
    state: State,
) -> Literal[
    "primary_assistant_tools",
    "enter_update_flight",
    "enter_book_hotel",
    "enter_book_excursion",
    "__end__",
]:
    route = tools_condition(state)
    if route == END:
        return END
    tool_calls = state["messages"][-1].tool_calls
    if tool_calls:
        if tool_calls[0]["name"] == ToFlightBookingAssistant.__name__:
            return "enter_update_flight"
        elif tool_calls[0]["name"] == ToBookCarRental.__name__:
            return "enter_book_car_rental"
        elif tool_calls[0]["name"] == ToHotelBookingAssistant.__name__:
            return "enter_book_hotel"
        elif tool_calls[0]["name"] == ToBookExcursion.__name__:
            return "enter_book_excursion"
        return "primary_assistant_tools"
    raise ValueError("Invalid route")


# The assistant can route to one of the delegated assistants,
# directly use a tool, or directly respond to the user
builder.add_conditional_edges(
    "primary_assistant",
    route_primary_assistant,
    {
        "enter_update_flight": "enter_update_flight",
        "enter_book_car_rental": "enter_book_car_rental",
        "enter_book_hotel": "enter_book_hotel",
        "enter_book_excursion": "enter_book_excursion",
        "primary_assistant_tools": "primary_assistant_tools",
        END: END,
    },
)
builder.add_edge("primary_assistant_tools", "primary_assistant")


# Each delegated workflow can directly respond to the user
# When the user responds, we want to return to the currently active workflow
def route_to_workflow(
    state: State,
) -> Literal[
    "primary_assistant",
    "update_flight",
    "book_car_rental",
    "book_hotel",
    "book_excursion",
]:
    """If we are in a delegated state, route directly to the appropriate assistant."""
    dialog_state = state.get("dialog_state")
    if not dialog_state:
        return "primary_assistant"
    return dialog_state[-1]


builder.add_conditional_edges("fetch_user_info", route_to_workflow)

# Compile graph
memory = SqliteSaver.from_conn_string(":memory:")
part_4_graph = builder.compile(
    checkpointer=memory,
    # Let the user approve or deny the use of sensitive tools
    interrupt_before=[
        "update_flight_sensitive_tools",
        "book_car_rental_sensitive_tools",
        "book_hotel_sensitive_tools",
        "book_excursion_sensitive_tools",
    ],
)

這里是一個圖片鏈接

對話

那真是很多內容!讓我們在下面的對話輪次列表上運行它。這次,我們將有更少的確認。

import shutil
import uuid

# Update with the backup file so we can restart from the original place in each section
shutil.copy(backup_file, db)
thread_id = str(uuid.uuid4())

config = {
    "configurable": {
        # The passenger_id is used in our flight tools to
        # fetch the user's flight information
        "passenger_id": "3442 587242",
        # Checkpoints are accessed by thread_id
        "thread_id": thread_id,
    }
}

_printed = set()
# We can reuse the tutorial questions from part 1 to see how it does.
for question in tutorial_questions:
    events = part_4_graph.stream(
        {"messages": ("user", question)}, config, stream_mode="values"
    )
    for event in events:
        _print_event(event, _printed)
    snapshot = part_4_graph.get_state(config)
    while snapshot.next:
        # We have an interrupt! The agent is trying to use a tool, and the user can approve or deny it
        # Note: This code is all outside of your graph. Typically, you would stream the output to a UI.
        # Then, you would have the frontend trigger a new run via an API call when the user has provided input.
        user_input = input(
            "Do you approve of the above actions? Type 'y' to continue;"
            " otherwise, explain your requested changed.\n\n"
        )
        if user_input.strip() == "y":
            # Just continue
            result = part_4_graph.invoke(
                None,
                config,
            )
        else:
            # Satisfy the tool invocation by
            # providing instructions on the requested changes / change of mind
            result = part_4_graph.invoke(
                {
                    "messages": [
                        ToolMessage(
                            tool_call_id=event["messages"][-1].tool_calls[0]["id"],
                            cnotallow=f"API call denied by user. Reasoning: '{user_input}'. Continue assisting, accounting for the user's input.",
                        )
                    ]
                },
                config,
            )
        snapshot = part_4_graph.get_state(config)

結論

您現在開發了一個能夠處理多種任務的客戶支持機器人,它使用了專注的工作流程。更重要的是,您已經學會了如何使用LangGraph的核心功能來設計和根據產品需求重構應用程序。

上述示例并不是針對您的特定需求進行優化的 - 大型語言模型(LLMs)可能會出錯,每個流程都可以通過更好的提示和實驗來提高可靠性。一旦您創建了初始支持機器人,下一步就是開始添加評估,這樣您就可以自信地改進您的系統。

本文轉載自?? AI小智??,作者: AI小智

收藏
回復
舉報
回復
相關推薦
国外成人免费在线播放| 日韩视频免费观看高清完整版| 欧美一区二区视频17c | 欧美精品自拍偷拍| 日韩在线观看a| 国产一区电影| 国产精品88av| 国产精品99久久久久久白浆小说| 视频这里只有精品| 蜜臀91精品国产高清在线观看| 欧美久久免费观看| 久久久久久久久久久久久国产精品| 男女啪啪在线观看| 91色乱码一区二区三区| 亚洲999一在线观看www| 亚洲自拍一区在线观看| 欧美婷婷在线| 日韩在线观看免费av| 毛片网站免费观看| 亚洲天堂av资源在线观看| 在线亚洲一区观看| 老太脱裤子让老头玩xxxxx| 男人在线资源站| 国产亚洲人成网站| 国产午夜精品一区| 精品国产18久久久久久| 奇米色777欧美一区二区| 亚洲2020天天堂在线观看| 日韩欧美综合视频| 久久一区二区三区喷水| 亚洲欧美一区二区激情| 精品影片一区二区入口| 日韩精品免费视频一区二区三区| 欧美亚洲综合久久| 午夜肉伦伦影院| 91破解版在线观看| 亚洲综合一区二区| 黄色成人在线免费观看| 岛国成人毛片| 亚洲欧洲日韩综合一区二区| 午夜一区二区三视频在线观看| 视频一区二区在线播放| 成人福利视频网站| 国产一区二区三区奇米久涩| 亚洲av永久纯肉无码精品动漫| 精品一区二区三区香蕉蜜桃| 国产精品久久久久久久av大片| 久久久久久久久黄色| 国产亚洲在线| 日本一区二区三区在线播放 | а√天堂在线官网| 成人欧美一区二区三区1314 | 91香蕉在线观看| 亚洲欧美经典视频| 国产精品日韩三级| av影院在线| 疯狂做受xxxx欧美肥白少妇 | 国产黄色片视频| 狠狠爱成人网| 韩国三级电影久久久久久| 国产无套粉嫩白浆内谢| 国产亚洲永久域名| 日韩av理论片| 一级二级三级视频| 国产精品资源在线| 国产精品久久九九| 亚洲国产精品成人久久蜜臀| 成人免费精品视频| 久草一区二区| 国产一二三区在线视频| 亚洲国产精品99久久久久久久久| 一区二区三区四区欧美日韩| 成人无遮挡免费网站视频在线观看| 一区二区三区成人| 亚洲 欧美 日韩 国产综合 在线| 色多多在线观看| 欧美午夜在线一二页| 九九九九九九九九| 亚洲国产aⅴ精品一区二区| 亚洲成人国产精品| 久久久久久久久久久久| 欧美成人自拍| 欧美精品videos| 一级片视频在线观看| 久久精品国产色蜜蜜麻豆| 91一区二区三区| 你懂的免费在线观看| 日韩理论片网站| 欧美又粗又长又爽做受| 欧美舌奴丨vk视频| 日韩一区二区三区在线| 高潮毛片无遮挡| 在线中文字幕第一区| 欧美亚洲另类在线| 国产精品亚洲lv粉色| aaa亚洲精品| 在线视频福利一区| 人狥杂交一区欧美二区| 8x8x8国产精品| 国产精品三级在线观看无码| 亚洲草久电影| 国产精品高潮呻吟视频 | 亚洲欧美制服综合另类| 亚洲国产精品免费在线观看| 久久电影一区| 97在线中文字幕| 成人高清网站| 亚洲www啪成人一区二区麻豆| 91福利国产成人精品播放| 里番精品3d一二三区| 色偷偷噜噜噜亚洲男人| 亚洲男人的天堂在线视频| 国产一区二区三区在线观看免费| 欧美一区少妇| 电影在线观看一区| 日韩午夜在线播放| 久久久精品少妇| 天堂av在线一区| 精品高清视频| 丁香花在线高清完整版视频| 777午夜精品免费视频| 成人激情五月天| 亚洲欧美日韩视频二区| 国产高清精品一区| 香蕉久久aⅴ一区二区三区| 欧美午夜一区二区三区免费大片| 波多野结衣办公室33分钟| 欧美黄在线观看| 成人欧美在线观看| 欧美jizzhd欧美| 欧美人体做爰大胆视频| 国产视频123区| 青娱乐精品视频| 日韩资源av在线| xxxxxx欧美| 亚洲精品在线不卡| 国产精品久久久久久久妇| 成人av在线网站| 成年人看的毛片| 国产一级成人av| 97精品一区二区三区| 亚洲国产精品久久久久爰性色| 亚洲天堂久久久久久久| 红桃视频 国产| 66视频精品| 亚洲a∨日韩av高清在线观看| 男人天堂手机在线| 欧美一区二区三区视频| 欧美黄片一区二区三区| 国产成人午夜高潮毛片| 青青草综合视频| 视频一区日韩| 欧美激情手机在线视频| 日韩中文字幕观看| 欧美日韩另类字幕中文| 久久久亚洲av波多野结衣| 久久午夜视频| 伊人久久大香线蕉综合75| 在线观看亚洲精品福利片| 久久久久北条麻妃免费看| a在线观看免费| 一区二区三区在线视频观看| 911亚洲精选| 亚洲欧美视频| 亚洲一区bb| 综合欧美亚洲| 26uuu日韩精品一区二区| 韩日视频在线| 欧美一区欧美二区| 日本黄色片视频| 国产亚洲欧美一区在线观看| www.se五月| 亚洲天堂偷拍| 日本一区二区三区四区在线观看| 亚洲爽爆av| 91精品国产91久久久久福利| www.亚洲.com| 精品少妇一区二区| 无码人妻丰满熟妇区bbbbxxxx| 国产精品毛片大码女人| zjzjzjzjzj亚洲女人| 水蜜桃久久夜色精品一区的特点 | 日本在线观看高清完整版| 日韩av在线免费| 91丨九色丨丰满| 天天av天天翘天天综合网色鬼国产| xxxx日本黄色| 国产成人av一区二区三区在线| 国产最新免费视频| 亚洲成人免费| 日本高清视频一区二区三区| 久久丁香四色| 国产精品久久久久久久久久久不卡| 伊人精品影院| 一区二区欧美在线| 人妻va精品va欧美va| 欧美制服丝袜第一页| 国产一级视频在线观看| 欧美激情综合五月色丁香 | 国产精品毛片久久久| 国产精品揄拍一区二区| 中文在线免费二区三区| 欧美另类第一页| 337p日本欧洲亚洲大胆鲁鲁| 日韩av在线一区二区| av免费在线观看不卡| 在线观看视频一区二区| 日本午夜小视频| 亚洲男人电影天堂| 黄色片在线观看免费| 99视频精品在线| 色综合久久久无码中文字幕波多| 日本免费在线视频不卡一不卡二| 国产精品国产亚洲精品看不卡| 91久久电影| 亚洲激情图片| 精品国产一区二区三区噜噜噜| 激情五月综合色婷婷一区二区| 国产精品日本一区二区三区在线| 国产精品久久999| 中文字幕在线直播| 午夜精品久久久久久久99热浪潮| av在线免费网站| 日韩视频―中文字幕| 国产69精品久久app免费版| 亚洲精品色婷婷福利天堂| 国产成人无码www免费视频播放| 欧美电影一区二区三区| 亚洲性生活大片| 欧美羞羞免费网站| 自拍偷拍校园春色| 日本大香伊一区二区三区| 日日夜夜综合网| 精品毛片网大全| 日韩精品在线不卡| 偷拍与自拍一区| 日韩精品无码一区二区| 亚洲成人午夜影院| 日韩特黄一级片| 欧美日韩国产一中文字不卡| 欧美热在线视频精品999| 中文字幕一区二区三区在线不卡| 四虎影成人精品a片| 99精品一区二区| 亚洲激情 欧美| jlzzjlzz亚洲日本少妇| 亚洲av成人精品一区二区三区| 国产电影一区二区三区| 无套内谢丰满少妇中文字幕 | 日本sm极度另类视频| 国产直播在线| 8x拔播拔播x8国产精品| 另类专区亚洲| 国产玖玖精品视频| 亚洲精品777| 51国偷自产一区二区三区| 日韩一区二区三区精品视频第3页| 114国产精品久久免费观看| 天堂va欧美ⅴa亚洲va一国产| 91入口在线观看| 看全色黄大色大片免费久久久| 精品一区二区视频| 国产精品密蕾丝视频下载| 亚洲精品国产精品国自产| 91亚洲成人| 日本一道在线观看| 99国产成+人+综合+亚洲欧美| 国产91在线视频观看| 日本不卡中文字幕| 三级av免费看| 91丨porny丨中文| 99国产精品免费| 一级中文字幕一区二区| 五月婷婷亚洲综合| 欧美视频一区在线| 亚洲国产精品久久久久久久| 亚洲男人的天堂在线播放| 午夜免费福利在线观看| 欧美激情一二三| 日韩成人动漫| 亚洲精品免费网站| 妖精一区二区三区精品视频| 亚洲日本无吗高清不卡| 国产精品观看| 亚洲精品乱码久久久久久自慰 | 成人网中文字幕| 给我免费播放日韩视频| 日韩一区二区三区高清| 国产精品观看| 欧美精品久久久久久久久25p| 国产精品小仙女| 少妇人妻好深好紧精品无码| 亚洲精品国产精品乱码不99| 国产黄色免费观看| 日韩一区二区在线观看视频| 狠狠狠综合7777久夜色撩人| 久久91精品国产91久久跳| 亚洲综合在线电影| 成人在线观看av| 999国产精品| 久久精品.com| 成人三级伦理片| 天天爽天天爽天天爽| 一本到三区不卡视频| 性中国古装videossex| 中文字幕无线精品亚洲乱码一区 | 久久综合九色欧美狠狠| 亚洲成人三区| 亚洲福利精品视频| 91麻豆文化传媒在线观看| 乱h高h女3p含苞待放| 欧美性猛交xxxxxx富婆| 性xxxxbbbb| 欧美丰满少妇xxxxx| 黄色日韩网站| 日韩福利一区二区三区| 国产欧美日韩亚洲一区二区三区| 97超碰人人看| 国产精品欧美久久久久无广告| 成年人午夜视频| 亚洲精品在线三区| 色呦呦在线视频| 91在线观看免费| 欧美激情偷拍自拍| 五月婷婷丁香综合网| 久久久久久久av麻豆果冻| 亚州国产精品视频| 日韩av一区在线观看| 岛国毛片av在线| 成人三级视频在线观看一区二区| 日韩精品第一区| www.日本一区| 中文字幕av免费专区久久| 国产免费www| 在线观看亚洲区| 外国电影一区二区| 日韩欧美99| 欧美96一区二区免费视频| 舐め犯し波多野结衣在线观看| 欧美色视频日本版| 免费理论片在线观看播放老| 欧洲成人在线视频| 蜜桃精品wwwmitaows| 99精品视频播放| 国产日韩欧美亚洲| 精品乱码一区内射人妻无码| 一区二区在线视频| 狠狠久久伊人中文字幕| 手机成人av在线| 国产成人无遮挡在线视频| 久久婷婷一区二区| 亚洲国产成人精品一区二区| segui88久久综合9999| 鲁鲁视频www一区二区| 老司机精品导航| 美国美女黄色片| 91麻豆精品91久久久久久清纯| 久草免费在线| 成人三级在线| 久久综合五月| 久久成人小视频| 亚洲精品一区二区三区蜜桃下载 | 欧美网站在线| 亚洲男人在线天堂| 色菇凉天天综合网| 欧美96在线| aaa级精品久久久国产片| av成人毛片| 日韩影视一区二区三区| 91精品国产91久久久久久一区二区 | 国产精品美女久久| 欧美a级在线| 菠萝菠萝蜜网站| 欧美日韩国产小视频在线观看| 性欧美高清come| 久精品国产欧美| 九九**精品视频免费播放| 国产无遮挡免费视频| 中文字幕亚洲欧美在线| 亚洲综合网站| av在线无限看| 亚洲一区国产视频| av网站大全在线观看| 国产精选在线观看91| 免费亚洲电影在线| www.99re7.com| 在线亚洲男人天堂| 欧美日韩大片免费观看| 中文字幕在线视频精品| 欧美日韩国产一中文字不卡| 欧美被日视频| 欧美亚洲丝袜| 国产成人啪免费观看软件| 草久久免费视频| 久久99精品久久久久久琪琪| 国产精品一区二区三区av麻| 久久国产免费视频| 欧美午夜精品久久久久久超碰| av资源一区| 日韩中文字幕亚洲精品欧美|