1.概述

        langchain智能体有两种模式:

       1)工具调用:有一个智能体作为主控,其他的智能体均作为工具,工具智能体仅被主控调用并返回结果,工具调用模式时一种中心化架构

       2)交接模式:该模式对应langgraph多智能体网状架构模式,所有智能体地位平等。每个智能体自主决定把控制权移交给其他的哪个智能体。交接模式时一种中心化架构

        两种模式各有长短,如果需要对工作流实现集中控制,则选用工具调用模式;如果每个智能体时自己领域的专家并且能够直接跟用户交互,或者像人类之间一样进行复杂的对话则选用交接模式。

        当然,可以把两种模式结合起来取长补短,有多个工具调用模式多智能体组成交接模式多智能体集群,工具调用模式多智能体中的主控称为主智能体,工具智能体称为子智能体;主智能体之间通过交接模式进行控制转移,主智能体仍把子智能体作为工具来看待。

        实现多智能体按照预期进行协调和移交的核心是上下文工程,上下文工程能确保每个智能体都,无论是工具智能体还是主智能体均能访问执行任务所需的正确数据,在多智能体架构下上下文工程具体包括:

        1)确定对话或状态数据对于每个代理的可见性,不同智能体关注点不同
        2)专门为每个子智能体量身定制的提示词,使术有专攻,学有所长
        3)根据需要纳入/排除中间推理步骤
        4)根据智能体的定位为每其定制专门的输入/输出格式

     2.工具调用模式

     2.1原理简述

        工具调用模式多智能体数据流程如下图所示:

        具体流程说明如下:

        1)主智能体接收用户请求,根据上下文决定调用哪个工具智能体

        2)被调用的工具智能体执行自己的任务,并把结果返回给主智能体

        3)主智能体根据上下文决定给用户返回应答还是继续调用其他的工具智能体

      2.2创建多智能体

       2.2.1定义工具

        本文中使用两个工具,一个搜索引擎工具,一个除法工具。

        搜索引擎工具定义如下:

import os
from langchain_tavily import TavilySearch
os.environ["TAVILY_API_KEY"] = "tvly-*"
search_tool = TavilySearch(max_results=2)

        除法工具定义如下:

from langchain.tools import tool
@tool
def math_tool(a: float, b:float)->int:
    '''Divide two numbers'''
    return a / b   

        2.2.2定义智能体

        基于上面的两个工具,分别定义对应的两个智能体。

        搜索智能体如下:

from langchain.agents import create_agent
SEARCH_AGENT_PROMPT = (
    "你是一个搜索引擎智能体\n\n"
    "指令:\n"
    "-调用搜索引擎搜索互联网\n"
    "-完成搜索后,直接给监督者智能体返回应答\n"
)
search_agent = create_agent(
    model=llm,
    tools=[search_tool, ],
    system_prompt=SEARCH_AGENT_PROMPT,
)

        除法计算智能体如下:

MATH_AGENT_PROMPT = (
    "你是一个数学计算智能体.\n\n"
    "指令:\n"
    "- 调用math tool执行除法计算任务\n"
    "- 完成计算任务后,直接给监督者智能体返回应答\n"
)
math_agent = create_agent(
    model=llm,
    tools=[math_tool, ],
    system_prompt=MATH_AGENT_PROMPT,
)

        2.2.3智能体工具化

        分别把两个智能体定义成工具。

        搜索智能体工具定义如下:

search_internet_tool_description = (
    "Search information from internet using natural language.\n\n"
    "Use this when the user wants to search up-to-date informatin.\n\n"
    "Input: Natural language search request (e.g., 'who is current president of USA')"
)
@tool("search_agent", description=search_internet_tool_description)
def search_internet(request: str) -> str:
    result = search_agent.invoke({
        "messages": [{"role": "user", "content": request}]
    })
    return result["messages"][-1].text

        除法智能体工具如下:

@tool
def calculate_quotient (request: str) -> str:
    """Calculate quotient of two numbers using natural language.
    Use this when the user wants to calculate quotient.
    Handles number parsing,  and divide two number.
    Input: Natural language text includeing two number (e.g., 'USA 0.13billon China 1.4billion')
    """
    result = math_agent.invoke({
        "messages": [{"role": "user", "content": request}]
    })
    return result["messages"][-1].text      

        2.2.4创建主智能体

        主智能体代码如下:

supervisor_agent = create_agent(
    model=llm,
    tools=[search_internet, calculate_quotient],
)

        调用主智能体,并打印输出:

query = "美国人口是古巴人口的多少倍?"

for step in supervisor_agent.stream(
    {"messages": [{"role": "user", "content": query}]}
):
    for update in step.values():
        for message in update.get("messages", []):
            message.pretty_print()

      2.3修改子智能体输入

        很多情况下子智能体并不需要主智能体的全部状态数据,可以自定义方法从主智能体传递进来的全部状态数据中提取出子智能体必需的数据,示例代码如下:

from langchain.agents import AgentState
from langchain.tools import tool, ToolRuntime

class CustomState(AgentState):#自定义属性
    custom_state_key: str

@tool(
    "search_agent",
    description="Search information from Internet"
)
def call_search_agent(query: str, runtime: ToolRuntime[None, CustomState]):
    #自定义foo方法,根据主智能体传入的所有状态数据生成子智能体所必需的数据
    search_agent_input = foo(query, runtime.state["messages"])
    result = subagent1.invoke({
        "messages": search_agent_input,
        "custom_state_key": runtime.state["custom_state_key"]
    })
    return result["messages"][-1].content

      2.3修改子智能体输出       

        当子智能体的输出不完整、过于繁琐或者遗漏关键信息时,需要对子智能体的输出进行修改,以补全或简化。修改子智能的输出有两种方式,一种是在创建子智能体在传入的system_prompt中说明输出要求,另一种是同步通过编码实现。此时为了完成修改,需要使用Command,以下示例代码使用编码实现:

from typing import Annotated
from langchain.agents import AgentState
from langchain.tools import InjectedToolCallId
from langgraph.types import Command
from langchain_core.messages import ToolMessage
class CustomState(AgentState): #自定义状态
    custom_state_key: str

@tool(
    "some_agent_name",
    description="description
)

def call_some_agent(
    query: str,
    tool_call_id: Annotated[str, InjectedToolCallId],

) -> Command:
    result = some_agent.invoke({
        "messages": [{"role": "user", "content": query}]
    })
    return Command(update={
        # This is the example state key we are passing back
        "custom_state_key": result["custom_state_key"],
        "messages": [
            ToolMessage(
                content=result["messages"][-1].content,
                #返回数据中增加了工具调用唯一标识
                tool_call_id=tool_call_id
            )
        ]
    })

     3.交接模式

     3.1原理描述

       交接模式做智能体数据流程如下图所示:

       多个智能体地位相同,同一个时刻只有一个智能体处于激活状态,也就是直接面向用户,活动智能体可以调用其他的智能体。

     3.2创建多智能体

        待实现。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐