理解 LangChain 智能体:create_react_agent 与 create_tool_calling_agent

本文译者为 360 奇舞团前端开发工程师

原文标题:理解 LangChain 智能体:create_react_agentcreate_tool_calling_agent 原文作者:Anil Goyal 原文地址:medium.com/@anil.goyal...

当我们使用 LangChain 构建 AI 智能体时,首先要做的是选择正确的智能体架构。 目前常用的2种架构是create_react_agentcreate_tool_calling_agent。两者都可以让AI使用外部工具,但由于它们的运作方式不同,因此适合的场景也有区别。

本文将探讨create_react_agentcreate_tool_calling_agent有哪些区别,如何向大语言模型发送提示词,期望从模型获得哪种响应格式。它们的底层工作原理是什么,分别适合在哪种场景下使用。

什么是Agent?

在深入细节之前,让我们先了解一下什么是 Agent。Agent系统的工作流程如下:

  1. 推理---对问题和任务进行思考与分析;
  2. 行动---调用工具或外部服务处理问题;
  3. 观察---查看获取的结果;
  4. 迭代---不断重复上述过程,直到达到预期的目标。

不同类型智能体之间的关键区别在于:它们与 大语言模型 的交互方式不同,也就是它们在执行推理工具选择过程时,与模型之间的通信方式存在差异。

核心区别:提示词

无论是 create_react_agent 还是 create_tool_calling_agent,它们都会让模型自主决定使用哪些工具**、**何时使用。而两者之间最关键的区别在于:它们如何向 LLM 发送提示词,以及期望 LLM 返回的响应格式。

可以理解为它们是在用两种不同的"语言"与模型沟通:

  • ReAct Agent: "请一步一步地思考,并用文字告诉我你的推理过程。"
  • Tool Calling Agent: "这是函数定义,请直接用结构化数据调用它们。"

于是,LLM 会根据所接受的提示方式来回应,从而形成完全不同的交互模式。

一、create_react_agent:基于文本推理

什么是 ReAct?

ReAct 是 "Reasoning + Acting"(推理 + 行动)的缩写。它是一种让智能体通过"文本化思考循环"来展示自身思维过程的范式。这种循环的结构通常包括以下几个步骤:

  1. Thought(思考): 智能体思考下一步该做什么,分析问题或任务。
  2. Action(行动): 智能体决定使用哪个工具、以及如何使用它。
  3. Observation(观察): 智能体查看执行后的结果,并理解输出内容。
  4. Repeat(重复): 这一循环不断重复,直到任务完成为止。

以下是 ReAct agent实际上会发送给模型的内容:

csharp 复制代码
#(1)ReAct agent向大语言模型发送如下内容:

"你可以使用这些工具:[add, multiply]
问题:先计算 5+3,然后将结果乘以 2
思考:"

#(2)模型回复:

"我需要先把 5 和 3 相加,然后将结果乘以 2
行动:add
行动输入(Action Input):{"a": 5, "b": 3}"

#(3)智能体调用 add(5, 3),得到结果8。把这个结果加入提示词中,再将更新后的提示词发送给 模型:

"你可以使用这些工具:[add, multiply]
问题:先计算 5+3,然后将结果乘以 2
思考:我需要先把 5 和 3 相加,然后将结果乘以 2
行动:add
行动输入:{"a": 5, "b": 3}。
观察:8
思考:"

#(4)模型回复:"现在我得到了 8,我需要将它乘以 2。

"行动:multiply
行动输入:{"a": 8, "b": 2}"

# (5)智能体执行 multiply(8, 2),得到结果 16。然后再次将该结果加入提示词,形成最终的提示词:

"你可以使用这些工具:[add, multiply]。
问题:先计算 5+3,然后将结果乘以 2
思考:我需要先把 5 和 3 相加,然后将结果乘以 2
行动:add。行动输入:{"a": 5, "b": 3}
观察:8
思考:现在我得到了 8,我需要将它乘以 2
行动:multiply
行动输入:{"a": 8, "b": 2}
观察:16
思考:"

#(6)LLM 回复:"完美!我现在得到了最终答案。最终答案:(5+3) × 2 的结果是 16"

可以看到,ReAct agent 不是通过一轮对话就回答了问题,也就是说模型并不会一次性计划好所有步骤。相反,agent与模型之间会进行多轮对话,每轮对话结束后都会调用不同的工具。

正是这种迭代式的特性, 使得 ReAct agent非常透明且易于调试 ---你可以清楚地看到推理过程是如何逐步展开的!

create_react_agent如何工作

ini 复制代码
# 导入 LangChain 中的智能体创建方法
from langchain.agents import AgentExecutor, create_react_agent

# 导入用于创建自定义提示模板的类
from langchain_core.prompts import PromptTemplate

# 导入 MultiServer MCP 客户端,用于连接和调用外部工具服务
from langchain_mcp_adapters.client import MultiServerMCPClient

# 导入 Ollama LLM 语言模型
from langchain_ollama import OllamaLLM

from langchain import hub
import asyncio
import os
os.environ["USER_AGENT"] = "my-langchain-agent/1.0"

async def main():

    # 创建 MultiServerMCPClient 客户端,用于管理外部工具(这里是 math server)
    client = MultiServerMCPClient({
        "math": {
            "command": "python",              
            "args": ["mathserver.py"],         
            "transport": "stdio",           
        }
    })

    # 获取外部工具列表
    tools = await client.get_tools()

    # 创建 Ollama LLM 实例,选择使用 "mistral" 模型
    llm = OllamaLLM(model="mistral")

    # 定义自定义 ReAct 提示模板
    template = """尽可能回答以下问题。你可以访问这些工具:

    {tools}

    使用如下格式:

    Question: 你必须回答的问题
    Thought: 考虑该怎么做
    Action: 采取行动, 应该是 [{tool_names}] 中之一
    Action Input: 输入必须是有效的JSON格式, 例如 {{"a": 5, "b": 3}}
    Observation: 观察结果
    ... (这一 Thought/Action/Action Input/Observation 可以重复N次)
    Thought: 现在我知道了最终答案
    Final Answer: 获得最终答案

    IMPORTANT: 输入始终要采用JSON格式。针对数学运算,使用 {{"a": number1, "b": number2}}.

    开始!
    Question: {input}
    Thought:{agent_scratchpad}"""

    prompt = PromptTemplate.from_template(template)
    agent = create_react_agent(llm=llm, tools=tools, prompt=prompt)
    agent_executor = AgentExecutor(
        agent=agent,
        tools=tools,
        verbose=True,
        handle_parsing_errors=True
    )

    result = await agent_executor.ainvoke({"input": "What's 5 + 3, then multiply by 2"})
    print("\nResult:")
    print(result["output"])

if __name__ == "__main__":
    asyncio.run(main())

当运行代码时,控制台会打印类似如下输出:

vbnet 复制代码
Entering new AgentExecutor chain...

我需要先把两个数字相加,然后再将结果乘以 2。

Action: ["add", {"a": 5, "b": 3}]
Observation: {"result": 8}

Thought: 既然我已经得到了和,我将把它乘以 2。

Action: ["multiply", {"a": 8, "b": 2}]
Observation: {"result": 16}

Thought: 现在我知道最终答案了。

Final Answer: 结果是 16

ReAct Agents的关键特征

  • 基于文本的通信:使用自然语言进行推理和交流。
  • 透明的推理:可以清楚地看到智能体的思考过程。
  • 模型无关:可以与任何文本生成模型配合使用。
  • 易于调试: 决策过程容易追踪,便于查找问题。
  • 详细输出:显示所有推理步骤,从思考到行动再到观察。
  • 字符串参数:工具参数通常以字符串形式传递。

二、create_tool_calling_agent:基于函数调用

工具调用(也称为函数调用)利用了现代大型语言模型的原生能力,可以直接调用函数。与基于文本的推理不同,模型知道如何使用结构化数据调用工具。

工具调用提示策略(Tool Calling Prompting Strategy)

工具调用agent的效率更高,因为它可以在一次与模型的交互中进行多次工具调用,相比于 ReAct agent,以更少的交互完成整个流程。

以下是一个工具调用agent向模型发送的内容示例:

makefile 复制代码
# 工具调用消息
messages = [
        {
        "role": "system",
        "content": "你可以调用数学工具。"
        },
        {
        "role": "user",
        "content": "什么是 5 + 3?"
        }
        ]

        # 提供给模型的Tool格式
tools = [
        {
        "type": "function",
        "function": {
        "name": "add",
        "description": "把两个数字相加",
        "parameters": {
        "type": "object",
        "properties": {
        "a": {"type": "integer"},
        "b": {"type": "integer"}
        },
        "required": ["a", "b"]
        }
        }
        }
        ]

        # 模型用结构化的FUNCTION CALL响应:
        {
        "tool_calls": [
        {
        "id": "call_123",
        "type": "function",
        "function": {
        "name": "add",
        "arguments": '{"a": 5, "b": 3}'
        }
        }
        ]
        }

create_tool_calling_agent如何工作

ini 复制代码
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_groq import ChatGroq
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_mcp_adapters.client import MultiServerMCPClient
import asyncio
import os

os.environ["USER_AGENT"] = "my-langchain-agent/1.0"  
os.environ["GROQ_API_KEY"] = "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"

async def main():

  client = MultiServerMCPClient({
    "math": {
        "command": "python",
        "args": ["mathserver.py"],
        "transport": "stdio",
      }
    })

        # 获取tools
  tools = await client.get_tools()
  llm = ChatGroq(
          model="llama3-8b-8192",
          temperature=0
    )

  # 为tool calling agent创建正确的提示词模板
  prompt = ChatPromptTemplate.from_messages([
        ("system", "你是一个有用的助手,可以访问搜索tools。对于有关 DASA 的问题,请使用 DASA_search 工具;对于其他查询,请使用通用的 search 工具。"),
        ("human", "{input}"),
        MessagesPlaceholder("agent_scratchpad"),
      ])


  agent = create_tool_calling_agent(llm=llm, tools=tools, prompt=prompt)
 
        agent_executor = AgentExecutor(
        agent=agent,
        tools=tools,
        verbose=True,
        handle_parsing_errors=True
)

    # 现在我们可以使用agent回答问题了。
        result = await agent_executor.ainvoke({"input": "What's 5 + 3, then multiply by 2"})
        print("\nResult:")
        print(result["output"])

if __name__ == "__main__":
        asyncio.run(main())

当运行代码时,控制台会打印类似如下输出:

css 复制代码
> Entering new AgentExecutor chain...

Invoking: 对 `{'a': 5, 'b': 3}` 执行相加操作

Invoking: 对 `{'a': 8, 'b': 2}` 执行相乘操作

(5 + 3) × 2 的结果是 16.

        > Finished chain.

Tool Calling Agents的关键特征

  • **基于函数的通信:**使用结构化的函数调用方式进行通信。

  • **隐式推理:**决策过程在模型内部完成,不需要显式地展示推理步骤。

  • **依赖模型:**需要模型本身支持原生的工具调用功能。

  • **输出简洁:**输出中不会显示模型的推理过程,只返回最终结果或结构化调用。

  • **高效执行:**执行速度更快,使用的 token 更少。

  • 结构化参数: 始终以正确的 JSON 或字典格式传递参数。

上述例子中使用的MCP server如下:

python 复制代码
"""
使用 FastMCP 的Math Server

这是一个简单的 MCP 服务器,用于提供基本的数学运算功能。
"""

from fastmcp import FastMCP
import math

# 创建 MCP server 实例
        mcp = FastMCP("Math Server")

@mcp.tool()
def add(a: float, b: float) -> float:
        """将两个数字相加"""
        return a + b

@mcp.tool()
def subtract(a: float, b: float) -> float:
        """计算两个数字的差值."""
        return a - b

@mcp.tool()
def multiply(a: float, b: float) -> float:
        """将两个数字相乘"""
        return a * b

@mcp.tool()
def divide(a: float, b: float) -> float:
        """用第二个数字除以第一个数字"""
        if b == 0:
raise ValueError("不能用0做除数")
    return a / b

if __name__ == "__main__":
        # 运行server
    mcp.run()

三、注意事项:

如果你查看上面的 Python 代码,会发现我在 ReAct agent 中使用了 Mistral 模型 ,而在 Tool Calling Agent 中使用了 ChatGroq 模型

原因如下:

  • Mistral 擅长自然语言推理和基于文本的思维模式,因此非常适合用于 ReAct agent
  • 相反,ChatGroq 具备原生的函数调用(function calling)支持,并且能够理解 JSON schema ,因此非常适合于 Tool Calling Agent
相关推荐
破烂pan6 小时前
github精选Agent学习repo
llm·github·agent
PKNLP10 小时前
11.大模型Agent应用
python·agent·pip
推理幻觉10 小时前
IDE/编码代理架构与 Cursor 相关研究(汇总)
ide·人工智能·架构·agent
FreeCode12 小时前
LangChain1.0智能体开发:结构化输出
人工智能·langchain·agent
大模型教程13 小时前
给AI装个“超级大脑”!5分钟带你搞懂RAG,原来这么简单!
程序员·llm·agent
大模型教程13 小时前
RAG基础知识到高级实现:宝藏级开源指南,手把手教你搭建检索增强生成系统
程序员·llm·agent
AI大模型13 小时前
告别新手级RAG!一文掌握专业级后检索优化:重排
程序员·llm·agent
AI大模型14 小时前
17.6 K Star!MaxKB让企业部署AI知识库问答系统如此简单
程序员·llm·agent
Hello.Reader14 小时前
思维链(CoT)× 智能体(Agent)× 提示词(Prompt)讲解
prompt·agent·cot