langchain应用-agent

什么是 LangChain Agent

在 LangChain 中,Agent 是一个代理,接收用户的输入,采取相应的行动然后返回行动的结果。Agent 可以看作是一个自带路由消费 Chains 的代理,基于 MRKL 和 ReAct 的基本原理,Agent 可以使用工具和自然语言处理问题。官方也提供了对应的 Agent,包括 OpenAI Functions Agent、Plan-and-execute Agent、Self Ask With Search 类 AutoGPT 的 Agent 等。Agent 的作用是代表用户或其他系统完成任务,例如数据收集、数据处理、决策支持等。Agent 可以是自主的,具备一定程度的智能和自适应性,以便在不同的情境中执行任务。我们今天主要了解基于 ReAct 原理来实现的 Agent。

ReAct

ReAct 是一个结合了推理和行动的语言模型。虽然 LLM 在语言理解和交互决策制定方面展现出了令人印象深刻的能力,但它们的推理(例如链式思考提示)和行动(例如行动计划生成)的能力主要被视为两个独立的主题。ReAct 的目标是探索如何使用 LLM 以交错的方式生成推理痕迹和特定任务的行动,从而在两者之间实现更大的协同作用。

想象一下,你有一个智能助手机器人,名叫小明。你给小明一个任务:去厨房为你做一杯咖啡。小明不仅要完成这个任务,还要告诉你他是如何一步步完成的。

没有 ReAct 的小明:

  1. 小明直接跑到厨房。

  2. 你听到了一些声音,但不知道小明在做什么。

  3. 过了一会儿,小明回来给你一杯咖啡。

这样的问题是,你不知道小明是怎么做咖啡的,他是否加了糖或奶,或者他是否在过程中遇到了任何问题。

有 ReAct 的小明:

  1. 小明告诉你:"我现在去厨房。"

  2. 小明再说:"我找到了咖啡粉和咖啡机。"

  3. "我现在开始煮咖啡。"

  4. "咖啡煮好了,我要加点糖和奶。"

  5. "好了,咖啡做好了,我现在给你拿过去。"

这次,你完全知道小明是怎么做咖啡的,知道他的每一个步骤和决策。

ReAct 就是这样的原理。它不仅执行任务(行动),还会告诉你它是如何思考和决策的(推理)。这样,你不仅知道任务完成了,还知道为什么这样做,如果有问题,也更容易找出原因。

更多关于 ReAct 的内容可以查看这篇文章[1]。

提示词模板

要实现 Agent,我们需要先定义一套基于 ReAct 的提示词模板,示例中的 Agent 就是基于 ReAct 原理来实现的,为了方便理解,我们将官方的英文提示词模板换成中文和去掉一些没必要的内容,修改后的提示词模板内容如下:

bash 复制代码
template = """尽你所能回答以下问题,你可以使用以下工具:
{tools}
请按照以下格式:
问题:你必须回答的输入问题
思考:你应该始终考虑该怎么做
行动:要采取的行动,应该是[{tool_names}]中的一个
行动输入:行动的输入
观察:行动的结果
... (这个思考/行动/行动输入/观察可以重复N次)
思考:我现在知道最终答案了
最终答案:对原始输入问题的最终答案
开始吧!
问题:{input}
{agent_scratchpad}"""

简单介绍一下这个提示词模板,首先提示词模板中会引用到一些工具,可以看到模板中有 2 个变量:toolstool_names,tools 变量是一个列表,包含了所有的工具,列表中的每个元素包含了工具的名称和描述,而 tool_names 变量是工具名称的列表,传入具体的工具后,会生成对应的工具列表,比如我们有如下 2 个工具,解析后如下所示:

bash 复制代码
尽你所能回答以下问题,你可以使用以下工具:
search: 实时联网搜索的工具
math: 数学计算的工具
请按照以下格式:
......
行动:要采取的行动,应该是[search, math]中的一个
......

这样 LLM 在执行任务时就知道要使用哪些工具,以及在提取信息时可以提取到正确的工具名。

模板中下面的思考/行动/行动输入/观察就是标准的 ReAct 流程,思考是指思考如何解决问题,行动是具体的工具,行动输入是工具用到的参数,观察是工具执行完成后得到的结果,这个流程可以重复多次,直到最终得到最终答案。

模板最后还有 2 个变量:inputagent_scratchpad,input 是用户输入的问题,agent_scratchpad 是之前的思考过程(下面解析代码时会讲),包括了思考、行动、行动输入和观察等,这个变量在 Agent 执行过程中会被更新,代入具体的值后,模板会生成如下的提示词:

bash 复制代码
......
问题:北京的天气怎么样
思考: 我们需要通过 search 工具查找北京天气。
行动: search
行动输入: "北京天气"
观察: 6日(今天). 多云转晴. 32/22℃. <3级
思考:

从问题下面一句到最后结束就是agent_scratchpad的值。

Langchain 组件

Langchain Agent 相关有下面几个关键组件:

代理

这是负责决定下一步采取什么动作的类。 这是由语言模型和提示驱动的。 该提示可以包括以下内容:

  1. 代理的个性(对于以某种方式响应很有用)

  2. 代理的背景上下文(对于给予其更多关于所要求完成的任务类型的上下文很有用)

  3. 调用更好推理的提示策略(最著名/广泛使用的是ReAct

工具

工具是代理调用的函数。 这里有两个重要的考虑因素:

  1. 给代理访问正确工具的权限

  2. 以对代理最有帮助的方式描述工具

如果没有这两者,您想要构建的代理将无法工作。 如果您不给代理访问正确工具的权限,它将永远无法完成目标。 如果您不正确描述工具,代理将不知道如何正确使用它们。

代理执行器

代理执行器是代理的运行时。 这是实际调用代理并执行其选择的动作的部分。 以下是此运行时的伪代码:

bash 复制代码
next_action = agent.get_action(...)
while next_action != AgentFinish:
    observation = run(next_action)
    next_action = agent.get_action(..., next_action, observation)
return next_action

虽然这看起来很简单,但此运行时为您处理了几个复杂性,包括:

  1. 处理代理选择不存在的工具的情况

  2. 处理工具发生错误的情况

  3. 处理代理生成无法解析为工具调用的输出的情况

  4. 在所有级别上记录和可观察性(代理决策,工具调用)-可以输出到stdout或LangSmith

代理运行时

AgentExecutor类是LangChain支持的主要代理运行时.

记忆体

大多数(如果不是全部)内存模块的核心实用类之一是 ChatMessageHistory 类。这是一个超轻量级的包装器,它公开了方便的方法来保存人类消息、AI 消息,然后获取它们全部。

如果您在链外管理内存,可能需要直接使用此类。

bash 复制代码
from langchain.memory import ChatMessageHistory history = ChatMessageHistory() history.add_user_message("hi!") history.add_ai_message("whats up?")


history.messages


[HumanMessage(content='hi!', additional_kwargs={}), AIMessage(content='whats up?', additional_kwargs={})]

langchain通过在历史信息基础支持上进行封装,提供了 带窗口大小的以及缓冲 ConversationBufferWindowMemory,

对回话记录进行摘要使用的 ConversationSummaryMemory。

代码案例

python 复制代码
from pydantic import BaseModel, Field
import datetime

from langserve import add_routes
from typing import Any, List, Union
from fastapi import status as fastapistatus

from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse
from fastapi import FastAPI, HTTPException
from langchain_ollama import ChatOllama
from langchain.agents import tool
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.agents.format_scratchpad.openai_tools import (
    format_to_openai_tool_messages,
)
from langchain.agents import AgentExecutor
from langchain.agents.output_parsers.openai_tools import (
    OpenAIToolsAgentOutputParser
)

import logging
from langchain.memory import (
    ConversationSummaryBufferMemory,
    ConversationBufferWindowMemory
)

logger = logging.getLogger(__name__)


# 注册计算字符串长度的工具,当询问计算输入长度时即可通过agent_scratchpad 后的关键字
# 关联上该工具计算输出,从而在该类问题中使用工具计算结果回答
@tool
def get_word_length(word: str) -> int:
    """Returns the length of a word."""
    return len(word)


tools = [get_word_length]


class Agent:
  

    def __init__(self, name, agent_id, role_describe, model="qwen2:7b",
                 temperature=0.7, token_limit=75):
        self.name = name
        self.agent_id = agent_id

        # 使用 ollama 中的模型 "qwen2:7b" 作为llm model
        llm = ChatOllama(
            model=model,
            temperature=temperature,
            verbose=True
        )
        
        # 提示词构建
        prompt = ChatPromptTemplate.from_messages(
            [
                (
                    "system",
                    role_describe,
                ),
                ("user", "{input}"),
                # agent_scratchpad 记录了每步思考过程
                MessagesPlaceholder(variable_name="agent_scratchpad"),
                # chat_history 注入对话历史,使用对对话历史的管理可实现记忆
                MessagesPlaceholder(variable_name="chat_history"),
            ]
        )
        self.prompt = prompt
        
        # 窗口历史,即记忆只记录最近 token_limit 句对话
        memory = ConversationBufferWindowMemory(k=token_limit,
                                                memory_key="chat_history",
                                                return_messages=True)

        
        self.memory = memory
        # 绑定工具
        llm_with_tools = llm.bind_tools(tools)
        self.tools = llm_with_tools

        agent = (
            {
                "input": lambda x: x["input"],
                "agent_scratchpad": lambda x: format_to_openai_tool_messages(
                    x["intermediate_steps"]
                ),
                "chat_history": lambda x:
                memory.load_memory_variables({})["chat_history"],
            }
            | prompt
            | llm_with_tools
            | OpenAIToolsAgentOutputParser()
        )

        # 运行agent runner
        agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
        self.agent_executor = agent_executor

        

    def chat(self, input_text):   
        # 输入input, 进行对话 
        response = self.agent_executor.invoke({"input": input_text})  
        self.memory.save_context(inputs={'input': response['input']}, 
            outputs={'output': response['output']})
        if response:
           return response['output']
        else:
            return ('There are no result..')


def command_chat(agent):
    while True:
        user_input = input("input:")
        user_output = daxiong_agent.chat(user_input)
        print(user_output)


if __name__ == "__main__":
    daxiong_agent = AgentRole("daxiong", "1234567", "你是一个中二的智能助手")
    command_chat(daxiong_agent)
相关推荐
一切尽在,你来7 分钟前
1.2 LangChain 1.2.7 版本核心特性与升级点
人工智能·langchain
Bruk.Liu20 分钟前
(LangChain 实战14):基于 ChatMessageHistory 自定义实现对话记忆功能
人工智能·python·langchain·agent
JaydenAI6 小时前
[拆解LangChain执行引擎] ManagedValue——一种特殊的只读虚拟通道
python·langchain
OPEN-Source7 小时前
大模型实战:搭建一张“看得懂”的大模型应用可观测看板
人工智能·python·langchain·rag·deepseek
一切尽在,你来9 小时前
1.4 LangChain 1.2.7 核心架构概览
人工智能·langchain·ai编程
一切尽在,你来9 小时前
1.3 环境搭建
人工智能·ai·langchain·ai编程
蛇皮划水怪16 小时前
深入浅出LangChain4J
java·langchain·llm
、BeYourself17 小时前
LangChain4j 流式响应
langchain
、BeYourself17 小时前
LangChain4j之Chat and Language
langchain
qfljg19 小时前
langchain usage
langchain