通过 AgentExecutor 封装 tools 至 Langchain Agent 及 Memory Agent 的使用 【Langchain Agent】

前言

大语言模型一般只会在自己学习的知识中进行搜索,无法根据每一步的输出内容来决定下一步的 action, 这个时候就需要用到 Agent 技术,Agent 其实就是封装了一系列的 tools 工具,当我们给 LLM 一个复杂的任务时,它会将该任务拆解为一步一步的,然后根据对应的上下文内容,选择使用哪一个或多个工具。

其中现在最流行的 Langchain 的核心之一就是封装了很多的 Agent 可以让我们调用,接下来我将演示如何通过 AgentExecutor 构造一个 Langchain Agent 并使用它。

前提条件

定义 tools

import os

from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores.dashvector import DashVector
from langchain_community.chat_models.tongyi import ChatTongyi
from langchain.tools.retriever import create_retriever_tool
from langchain_community.embeddings.dashscope import DashScopeEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.messages import HumanMessage

class TestAgent:

    def __init__(self):
        """在阿里云控制台开通 DASHSCOPE 和 DASHVECTOR"""
        os.environ["DASHSCOPE_API_KEY"] = ""
        os.environ["DASHVECTOR_API_KEY"] = ""
        os.environ["DASHVECTOR_ENDPOINT"] = ""

        # 一系列 tool 让 langchain 选择使用
        self.tools = []

    def build_retriever_tool(self):
        """爬取网站内容切分成 chunks 嵌入向量数据库"""
        loader = WebBaseLoader("https://docs.smith.langchain.com/overview")
        docs = loader.load()
        documents = RecursiveCharacterTextSplitter(
            chunk_size=1000, chunk_overlap=200
        ).split_documents(docs)
        vector = DashVector.from_documents(documents, DashScopeEmbeddings(), collection_name="langchain")
        retriever = vector.as_retriever()
        retriever_tool = create_retriever_tool(
            retriever,
            "langsmith_search",
            # 注意这里的 description 很重要,它描述的意思决定了 langchain 是否根据当前上下文选择该工具
            # 因此,description 要尽量描述的和 tool 所做内容吻合
            "Search for information about LangSmith. For any questions about LangSmith, you must use this tool!",
        )
        self.tools.append(retriever_tool)
  • 这里我们定义了一个关于 retriever_tool: 关于向 LLM 询问 LangSmith 的问题,将使用到该工具
  • 注意 description 很重要

使用大模型调用 tools

接下来我们向阿里云的通义千问大模型询问关于 LangSmith 的相关信息,它将会调用我们的 tools。

import os

from langchain.agents import AgentExecutor
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores.dashvector import DashVector
from langchain_community.chat_models.tongyi import ChatTongyi
from langchain.tools.retriever import create_retriever_tool
from langchain_community.embeddings.dashscope import DashScopeEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.messages import HumanMessage

class TestAgent:

    def __init__(self):
        """在阿里云控制台开通 DASHSCOPE 和 DASHVECTOR"""
        os.environ["DASHSCOPE_API_KEY"] = ""
        os.environ["DASHVECTOR_API_KEY"] = ""
        os.environ["DASHVECTOR_ENDPOINT"] = ""

        self.model = ChatTongyi()

        # 一系列 tool 让 langchain 选择使用
        self.tools = []

    def build_retriever_tool(self):
        """爬取网站内容切分成 chunks 嵌入向量数据库"""
        loader = WebBaseLoader("https://docs.smith.langchain.com/overview")
        docs = loader.load()
        documents = RecursiveCharacterTextSplitter(
            chunk_size=1000, chunk_overlap=200
        ).split_documents(docs)
        vector = DashVector.from_documents(documents, DashScopeEmbeddings(), collection_name="langchain")
        retriever = vector.as_retriever()
        retriever_tool = create_retriever_tool(
            retriever,
            "langsmith_search",
            # 注意这里的 description 很重要,它描述的意思决定了 langchain 是否根据当前上下文选择该工具
            # 因此,description 要尽量描述的和 tool 所做内容吻合
            "Search for information about LangSmith. For any questions about LangSmith, you must use this tool!",
        )
        self.tools.append(retriever_tool)

    def invoke_llm_with_tools(self, query=None):
        """
        向 LLM 提出问题,让大模型结合自己所学习的内容和我们自己构造的 tools 来回答
        """
        model_with_tools = self.model.bind_tools(self.tools)

        response = model_with_tools.invoke([HumanMessage(content=query)])
        print(f"ContentString: {response.content}")
        # 可以查看 LLM 在回答我们的问题的时候用到了哪些 tool
        print(f"ToolCalls: {response.tool_calls}")

    def create_agent_executor(self):
        """通过 AgentExecutor 将我们的 tools 封装为 Agent"""
        from langchain import hub
        from langchain.agents import create_tool_calling_agent
        from langchain.agents import AgentExecutor

        prompt = hub.pull("hwchase17/openai-functions-agent")
        agent = create_tool_calling_agent(self.model, self.tools, prompt)
        self.agent_executor = AgentExecutor(agent=agent, tools=self.tools)

if __name__ == '__main__':
    a = TestAgent()
    a.build_retriever_tool()
    a.invoke_llm_with_tools("告诉我关于 LangSmith 的相关信息")

封装 Agent 调用 tools

可以通过 AgentExecutor 将我们的 tools 封装为 Agent, 然后通过 Agent 执行查询。

import os

from langchain.agents import AgentExecutor
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores.dashvector import DashVector
from langchain_community.chat_models.tongyi import ChatTongyi
from langchain.tools.retriever import create_retriever_tool
from langchain_community.embeddings.dashscope import DashScopeEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.messages import HumanMessage

class TestAgent:

    def __init__(self):
        """在阿里云控制台开通 DASHSCOPE 和 DASHVECTOR"""
        os.environ["DASHSCOPE_API_KEY"] = ""
        os.environ["DASHVECTOR_API_KEY"] = ""
        os.environ["DASHVECTOR_ENDPOINT"] = ""

        self.model = ChatTongyi()

        # 一系列 tool 让 langchain 选择使用
        self.tools = []

        self.agent_executor = None

    def build_retriever_tool(self):
        """爬取网站内容切分成 chunks 嵌入向量数据库"""
        loader = WebBaseLoader("https://docs.smith.langchain.com/overview")
        docs = loader.load()
        documents = RecursiveCharacterTextSplitter(
            chunk_size=1000, chunk_overlap=200
        ).split_documents(docs)
        vector = DashVector.from_documents(documents, DashScopeEmbeddings(), collection_name="langchain")
        retriever = vector.as_retriever()
        retriever_tool = create_retriever_tool(
            retriever,
            "langsmith_search",
            # 注意这里的 description 很重要,它描述的意思决定了 langchain 是否根据当前上下文选择该工具
            # 因此,description 要尽量描述的和 tool 所做内容吻合
            "Search for information about LangSmith. For any questions about LangSmith, you must use this tool!",
        )
        self.tools.append(retriever_tool)

    def invoke_llm_with_tools(self, query=None):
        """
        向 LLM 提出问题,让大模型结合自己所学习的内容和我们自己构造的 tools 来回答
        """
        model_with_tools = self.model.bind_tools(self.tools)

        response = model_with_tools.invoke([HumanMessage(content=query)])
        print(f"ContentString: {response.content}")
        # 可以查看 LLM 在回答我们的问题的时候用到了哪些 tool
        print(f"ToolCalls: {response.tool_calls}")

    def create_agent_executor(self):
        """通过 AgentExecutor 将我们的 tools 封装为 Agent"""
        from langchain import hub
        from langchain.agents import create_tool_calling_agent
        from langchain.agents import AgentExecutor

        prompt = hub.pull("hwchase17/openai-functions-agent")
        agent = create_tool_calling_agent(self.model, self.tools, prompt)
        self.agent_executor = AgentExecutor(agent=agent, tools=self.tools)

    def query_with_agent(self, query=None):
        """通过 agent 执行查询,将会调用到我们的 tools"""
        res = self.agent_executor.invoke({"input": query})
        print(f"ContentString: {res['output']}")

if __name__ == '__main__':
    a = TestAgent()
    a.build_retriever_tool()
    # a.invoke_llm_with_tools("告诉我关于 LangSmith 的相关信息")
    a.create_agent_executor()
    a.query_with_agent("告诉我关于 LangSmith 的相关信息")

保存查询历史上下文

上边的查询都是没有历史上下文的,如果说我们需要像试用 ChatGPT 一样可以多次提问,并且带有历史上下文,该如何实现呢?
其实就是一种很直观简单的方式,将历史上下文给保存起来,然后在每次提问的时候,将历史上下文带上。

import os

from langchain.agents import AgentExecutor
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores.dashvector import DashVector
from langchain_community.chat_models.tongyi import ChatTongyi
from langchain.tools.retriever import create_retriever_tool
from langchain_community.embeddings.dashscope import DashScopeEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.messages import HumanMessage, AIMessage


class TestAgent:

    def __init__(self):
        """在阿里云控制台开通 DASHSCOPE 和 DASHVECTOR"""
        os.environ["DASHSCOPE_API_KEY"] = ""
        os.environ["DASHVECTOR_API_KEY"] = ""
        os.environ["DASHVECTOR_ENDPOINT"] = ""

        self.model = ChatTongyi()

        # 一系列 tool 让 langchain 选择使用
        self.tools = []

        self.agent_executor = None

    def build_retriever_tool(self):
        """爬取网站内容切分成 chunks 嵌入向量数据库"""
        loader = WebBaseLoader("https://docs.smith.langchain.com/overview")
        docs = loader.load()
        documents = RecursiveCharacterTextSplitter(
            chunk_size=1000, chunk_overlap=200
        ).split_documents(docs)
        vector = DashVector.from_documents(documents, DashScopeEmbeddings(), collection_name="langchain")
        retriever = vector.as_retriever()
        retriever_tool = create_retriever_tool(
            retriever,
            "langsmith_search",
            # 注意这里的 description 很重要,它描述的意思决定了 langchain 是否根据当前上下文选择该工具
            # 因此,description 要尽量描述的和 tool 所做内容吻合
            "Search for information about LangSmith. For any questions about LangSmith, you must use this tool!",
        )
        self.tools.append(retriever_tool)

    def invoke_llm_with_tools(self, query=None):
        """
        向 LLM 提出问题,让大模型结合自己所学习的内容和我们自己构造的 tools 来回答
        """
        model_with_tools = self.model.bind_tools(self.tools)

        response = model_with_tools.invoke([HumanMessage(content=query)])
        print(f"ContentString: {response.content}")
        # 可以查看 LLM 在回答我们的问题的时候用到了哪些 tool
        print(f"ToolCalls: {response.tool_calls}")

    def create_agent_executor(self):
        """通过 AgentExecutor 将我们的 tools 封装为 Agent"""
        from langchain import hub
        from langchain.agents import create_tool_calling_agent
        from langchain.agents import AgentExecutor

        prompt = hub.pull("hwchase17/openai-functions-agent")
        agent = create_tool_calling_agent(self.model, self.tools, prompt)
        self.agent_executor = AgentExecutor(agent=agent, tools=self.tools)

    def query_with_agent(self, query=None):
        """通过 agent 执行查询,将会调用到我们的 tools"""
        res = self.agent_executor.invoke({"input": query})
        print(f"ContentString: {res['output']}")

    def query_with_history(self):
        """带有历史上下文的查询, 新的提问时,将历史提问和回答信息一起手动带上"""
        self.agent_executor.invoke({"input": "hi! my name is bob", "chat_history": []})
        self.agent_executor.invoke(
            {
                "chat_history": [
                    HumanMessage(content="hi! my name is bob"),
                    AIMessage(content="Hello Bob! How can I assist you today?"),
                ],
                "input": "what's my name?",
            }
        )

    def query_with_memory(self):
        """
        让 langchain 自己将历史信息带上,无需我们手动带上,我们只需要在本地或者数据库存储历史信息即可
        """
        from langchain_community.chat_message_histories import ChatMessageHistory
        from langchain_core.chat_history import BaseChatMessageHistory
        from langchain_core.runnables.history import RunnableWithMessageHistory

        # 这里为了简单直接使用字典保存历史信息,生产环境应该使用数据库保存
        store = {}

        def get_session_history(session_id: str) -> BaseChatMessageHistory:
            if session_id not in store:
                store[session_id] = ChatMessageHistory()
            return store[session_id]

        agent_with_chat_history = RunnableWithMessageHistory(
            self.agent_executor,
            get_session_history,
            input_messages_key="input",
            history_messages_key="chat_history",
        )

        agent_with_chat_history.invoke(
            {"input": "hi! I'm bob"},
            config={"configurable": {"session_id": "<foo>"}},
        )

        res = agent_with_chat_history.invoke(
            {"input": "what's my name?"},
            config={"configurable": {"session_id": "<foo>"}},
        )
        print(res['output'])  # Your name is Bob.

if __name__ == '__main__':
    a = TestAgent()
    a.build_retriever_tool()
    # a.invoke_llm_with_tools("告诉我关于 LangSmith 的相关信息")
    a.create_agent_executor()
    # a.query_with_agent("告诉我关于 LangSmith 的相关信息")
    a.query_with_memory()
  • input_messages_key: 当前提问的内容的 key, 在上边的代码中就是 input, 该参数用于将当前提问内容加入历史。
  • history_messages_key:历史内容的 key, 用于加载历史消息
  • session_id: 用于保存当前对话内容,隔离不同对话

总结

本文总结了如何使用 Langchain 的 AgentExecutor 封装 tools, 构造我们自己的 Agent, 然后通过 LLM 调用,或者通过 Agent 调用,最后总结了如何让我们的 Agent 带有记忆功能,可以保存历史对话记录,这里为了简单就没有使用实际的数据库,实际生产中还是要使用数据库去保存这些信息的,感兴趣的朋友可以尝试下。

  • 13
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一切如来心秘密

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值