使用LangGraph構建多Agent系統架構!

公众号-JavaEdge發表於2024-10-18

0 前言

Agent是一個使用大語言模型決定應用程式控制流的系統。隨著這些系統的開發,它們隨時間推移變得複雜,使管理和擴充套件更困難。如你可能會遇到:

  • Agent擁有太多的工具可供使用,對接下來應該呼叫哪個工具做出糟糕決策
  • 上下文過於複雜,以至於單個Agent無法跟蹤
  • 系統中需要多個專業領域(例如規劃者、研究員、數學專家等)。

為解決這些問題,你可能考慮將應用程式拆分成多個更小、獨立的代理,並將它們組合成一個多Agent系統。這些獨立的Agent可以簡單到一個提示和一個LLM呼叫,或者複雜到像一個ReActAgent(甚至更多!)。

1 多Agent系統的好處

  • 模組化:獨立的Agent使得開發、測試和維護Agent系統更加容易。
  • 專業化:你可以建立專注於特定領域的專家Agent,這有助於提高整個系統的效能。
  • 控制:你可以明確控制Agent之間的通訊(而不是依賴於函式呼叫)。

2 多Agent架構

多Agent系統中有幾種方式連線Agent:

  • 網路:每個Agent都可與其他Agent通訊。任何Agent都可以決定接下來呼叫哪個其他Agent
  • 監督者:每個Agent與一個監督者Agent通訊。監督者Agent決定接下來應該呼叫哪個Agent。
  • 監督者(工具呼叫):這是監督者架構的一個特殊情況。個別Agent可以被表示為工具。在這種情況下,監督者Agent使用一個工具呼叫LLM來決定呼叫哪個Agent工具,以及傳遞哪些引數給這些Agent。
  • 層次結構:你可以定義一個有監督者的多Agent系統。這是監督者架構的概括,並允許更復雜的控制流。
  • 自定義多Agent工作流:每個Agent只與Agent子集中的其他Agent通訊。流程的部分是確定性的,只有一些Agent可以決定接下來呼叫哪個其他Agent。

網路

這種架構中,Agent被定義為圖節點。每個Agent都可以與每個其他Agent通訊(多對多連線),並且可以決定接下來呼叫哪個Agent。雖然非常靈活,但隨著Agent數量的增加,這種架構擴充套件性並不好:

  • 很難強制執行接下來應該呼叫哪個Agent
  • 很難確定應該在Agent之間傳遞多少資訊

建議生產避免使用這架構,而是使用以下架構之一。

監督者

這種架構中,定義Agent為節點,並新增一個監督者節點(LLM),它決定接下來應該呼叫哪個Agent節點。使用條件邊根據監督者的決策將執行路由到適當的Agent節點。這種架構也適用於並行執行多個Agent或使用map-reduce模式。

from typing import Literal
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, MessagesState, START

model = ChatOpenAI()

class AgentState(MessagesState):
    next: Literal["agent_1", "agent_2"]

def supervisor(state: AgentState):
    response = model.invoke(...)
    return {"next": response["next_agent"]}

def agent_1(state: AgentState):
    response = model.invoke(...)
    return {"messages": [response]}

def agent_2(state: AgentState):
    response = model.invoke(...)
    return {"messages": [response]}

builder = StateGraph(AgentState)
builder.add_node(supervisor)
builder.add_node(agent_1)
builder.add_node(agent_2)

builder.add_edge(START, "supervisor")
# 根據監督者的決策路由到Agent之一或退出
builder.add_conditional_edges("supervisor", lambda state: state["next"])
builder.add_edge("agent_1", "supervisor")
builder.add_edge("agent_2", "supervisor")

supervisor = builder.compile()

教程以獲取有關監督者多Agent架構的示例。

監督者(工具呼叫)

在這種監督者架構的變體中,我們定義個別Agent為工具,並在監督者節點中使用一個工具呼叫LLM。這可以作為一個ReAct風格的Agent實現,有兩個節點——一個LLM節點(監督者)和一個執行工具(在這種情況下是Agent)的工具呼叫節點。

from typing import Annotated
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import InjectedState, create_react_agent

model = ChatOpenAI()

def agent_1(state: Annotated[dict, InjectedState]):
    tool_message = ...
    return {"messages": [tool_message]}

def agent_2(state: Annotated[dict, InjectedState]):
    tool_message = ...
    return {"messages": [tool_message]}

tools = [agent_1, agent_2]
supervisor = create_react_agent(model, tools)

自定義多Agent工作流

在這種架構中,我們新增個別Agent作為圖節點,並提前定義Agent被呼叫的順序,以自定義工作流。在LangGraph中,工作流可以以兩種方式定義:

  • 顯式控制流(普通邊):LangGraph允許你透過普通圖邊顯式定義應用程式的控制流(即Agent通訊的順序)。這是上述架構中最確定性的變體——我們總是提前知道接下來將呼叫哪個Agent。
  • 動態控制流(條件邊):在LangGraph中,你可以允許LLM決定應用程式控制流的部分。這可以透過使用條件邊實現。一個特殊情況是監督者工具呼叫架構。在這種情況下,驅動監督者Agent的工具呼叫LLM將決定工具(Agent)被呼叫的順序。
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, MessagesState, START

model = ChatOpenAI()

def agent_1(state: MessagesState):
    response = model.invoke(...)
    return {"messages": [response]}

def agent_2(state: MessagesState):
    response = model.invoke(...)
    return {"messages": [response]}

builder = StateGraph(MessagesState)
builder.add_node(agent_1)
builder.add_node(agent_2)
# 明確定義流程
builder.add_edge(START, "agent_1")
builder.add_edge("agent_1", "agent_2")

3 Agent之間通訊

構建多Agent系統時最重要的事情是弄清楚Agent如何通訊。有幾個不同的考慮因素:

  • Agent是透過圖狀態或工具呼叫進行通訊的嗎?
  • 如果兩個Agent有不同的狀態模式怎麼辦?
  • 如何透過共享訊息列表進行通訊?

3.1 圖狀態與工具呼叫

Agent之間傳遞的“有效載荷”是什麼?在上述討論的大多數架構中,Agent透過圖狀態進行通訊。在監督者帶工具呼叫的情況下,有效載荷是工具呼叫引數。

圖狀態

要透過圖狀態進行通訊,各個Agent需要被定義為圖節點。這些可以作為函式或整個子圖新增。在圖執行的每一步中,Agent節點接收當前的圖狀態,執行Agent程式碼,然後將更新的狀態傳遞給下一個節點。

通常,Agent節點共享一個單一的狀態模式。然而,你可能想要設計具有不同狀態模式的Agent節點。

3.2 不同的狀態模式

一個Agent可能需要與其餘Agent有不同的狀態模式。例如,搜尋Agent可能只需要跟蹤查詢和檢索到的文件。在LangGraph中有兩種方法可以實現這一點:

  • 定義具有單獨狀態模式的子圖Agent。如果子圖和父圖之間沒有共享狀態鍵(通道),則需要新增輸入/輸出轉換,以便父圖知道如何與子圖通訊。
  • 定義具有私有輸入狀態模式的Agent節點函式,該模式與整個圖的狀態模式不同。這允許傳遞僅需要用於執行該特定Agent的資訊。

3.3 共享訊息列表

Agent之間通訊的最常見方式是透過共享狀態通道,通常是訊息列表。這假設狀態中至少有一個通道(鍵)由Agent共享。當透過共享訊息列表通訊時,還有一個額外的考慮因素:Agent是共享完整的歷史記錄還是僅共享最終結果

共享完整歷史記錄

Agent可以共享他們的思維過程的完整歷史記錄(即“草稿墊”)與其他所有Agent。這種“草稿墊”通常看起來像一個訊息列表。共享完整思維過程的好處是,它可能有助於其他Agent做出更好的決策,提高整個系統的整體推理能力。缺點是,隨著Agent數量和複雜性的增長,“草稿墊”將迅速增長,可能需要額外的策略進行記憶體管理

共享最終結果

Agent可以擁有自己的私有“草稿墊”,並且只與其餘Agent共享最終結果。這種方法可能更適合擁有許多Agent或更復雜的Agent的系統。在這種情況下,你需要定義具有不同狀態模式的Agent。

對於作為工具呼叫的Agent,監督者根據工具模式確定輸入。此外,LangGraph允許在執行時傳遞狀態給單個工具,以便從屬Agent在需要時可以訪問父狀態。

關注我,緊跟本系列專欄文章,咱們下篇再續!

作者簡介:魔都架構師,多家大廠後端一線研發經驗,在分散式系統設計、資料平臺架構和AI應用開發等領域都有豐富實踐經驗。

各大技術社群頭部專家博主。具有豐富的引領團隊經驗,深厚業務架構和解決方案的積累。

負責:

  • 中央/分銷預訂系統效能最佳化
  • 活動&券等營銷中臺建設
  • 交易平臺及資料中臺等架構和開發設計
  • 車聯網核心平臺-物聯網連線平臺、大資料平臺架構設計及最佳化
  • LLM Agent應用開發
  • 區塊鏈應用開發
  • 大資料開發挖掘經驗
  • 推薦系統專案

目前主攻市級軟體專案設計、構建服務全社會的應用系統。

參考:

  • 程式設計嚴選網

本文由部落格一文多發平臺 OpenWrite 釋出!

相關文章