Langchain学习心得(三)-Agent

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


一、代理的类型?

行动代理
代理使用 LLM 确定采取哪些行动以及顺序。 行动可以是使用工具并观察其输出,或向用户返回响应。 以下是 LangChain 中可用的代理。

零-shot ReAct
此代理使用 ReAct 框架确定使用哪个工具 仅基于工具的描述。可以提供任意数量的工具。 此代理要求为每个工具提供描述。

注意:这是最通用的行动代理。

结构化输入 ReAct
结构化工具聊天代理能够使用多个输入工具。 旧的代理配置为将行动输入指定为单个字符串,但此代理可以使用工具的参数 模式创建结构化行动输入。这对于更复杂的工具使用很有用,例如精确 导航浏览器。

OpenAI 函数
特定的 OpenAI 模型(如 gpt-3.5-turbo-0613 和 gpt-4-0613)已被明确微调,以检测何时 应调用函数,并响应应该传递给函数的输入。 OpenAI 函数代理旨在与这些模型配合使用。

对话型
此代理旨在用于对话环境。 提示旨在使代理有帮助性和对话性。 它使用 ReAct 框架决定使用哪个工具,并使用内存记住先前的对话交互。

带搜索的自问自答
此代理利用一个名为 Intermediate Answer 的单个工具。 此工具应该能够查找问题的事实性答案。此代理 等同于原始的 self ask with search paper, 其中提供了一个 Google 搜索 API 作为工具。

ReAct 文档存储
此代理使用 ReAct 框架与文档存储进行交互。必须提供两个工具 :一个 Search 工具和一个 Lookup 工具(必须准确命名)。 Search 工具应搜索文档,而 Lookup 工具应在最近找到的文档中查找 术语。 此代理等同于 原始的 ReAct 论文,特别是维基百科示例。

计划和执行代理
计划和执行代理通过首先规划要做什么,然后执行子任务来实现目标。这个想法主要受到 BabyAGI 的启发,然后是 “计划与解决” 论文。

二、实践

1.会话

本教程演示了如何使用针对对话优化的代理。这是通过一种特定类型的代理(conversational-react-description)实现的,该代理期望与内存组件一起使用。代码如下:

from langchain.agents import Tool
from langchain.agents import AgentType
from langchain.memory import ConversationBufferMemory
from langchain import OpenAI
from langchain.utilities import SerpAPIWrapper
from langchain.agents import initialize_agent

search = SerpAPIWrapper()
tools = [
    Tool(
        name = "Current Search",
        func=search.run,
        description="useful for when you need to answer questions about current events or the current state of the world"
    ),
]

memory = ConversationBufferMemory(memory_key="chat_history")

llm=OpenAI(temperature=0)
agent_chain = initialize_agent(tools, llm, agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION, verbose=True, memory=memory)

agent_chain.run(input="hi, i am bob")
agent_chain.run(input="what's my name?")
agent_chain.run("what are some good dinners to make this week, if i like thai food?")

2.使用聊天模型

chat-conversational-react-description 代理类型允许我们使用聊天模型创建对话代理,而不是 LLM。代码如下(示例):

from langchain.memory import ConversationBufferMemory
from langchain.chat_models import ChatOpenAI

memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
llm = ChatOpenAI(openai_api_key=OPENAI_API_KEY, temperature=0)
agent_chain = initialize_agent(tools, llm, agent=AgentType.CHAT_CONVERSATIONAL_REACT_DESCRIPTION, verbose=True, memory=memory)

agent_chain.run(input="hi, i am bob")
agent_chain.run(input="what's my name?")
agent_chain.run("what are some good dinners to make this week, if i like thai food?")

3.OpenAI Functions Agent

使用一个代理来使用 OpenAI 函数的能力,以回应用户的提示,使用一个大型语言模型。安装 openai,google-search-results 包,这些包是作为 langchain 包内部调用它们的

from langchain import (
    LLMMathChain,
    OpenAI,
    SerpAPIWrapper,
    SQLDatabase,
    SQLDatabaseChain,
)
from langchain.agents import initialize_agent, Tool
from langchain.agents import AgentType
from langchain.chat_models import ChatOpenAI

# Initialize the OpenAI language model
#Replace <your_api_key> in openai_api_key="<your_api_key>" with your actual OpenAI key.
llm = ChatOpenAI(temperature=0, model="gpt-3.5-turbo-0613",openai_api_key="<your_api_key>")

# Initialize the SerpAPIWrapper for search functionality
#Replace <your_api_key> in openai_api_key="<your_api_key>" with your actual SerpAPI key.
search = SerpAPIWrapper(serpapi_api_key="<your_api_key>")

# Initialize the LLMMathChain
llm_math_chain = LLMMathChain.from_llm(llm=llm, verbose=True)

# Initialize the SQL database using the Chinook database file
# Replace the file location to the custom Data Base
db = SQLDatabase.from_uri("sqlite:///../../../../../notebooks/Chinook.db")

# Initialize the SQLDatabaseChain with the OpenAI language model and SQL database
db_chain = SQLDatabaseChain.from_llm(llm, db, verbose=True)

# Define a list of tools offered by the agent
tools = [
    Tool(
        name="Search",
        func=search.run,
        description="Useful when you need to answer questions about current events. You should ask targeted questions."
    ),
    Tool(
        name="Calculator",
        func=llm_math_chain.run,
        description="Useful when you need to answer questions about math."
    ),
    Tool(
        name="FooBar-DB",
        func=db_chain.run,
        description="Useful when you need to answer questions about FooBar. Input should be in the form of a question containing full context."
    )
]


mrkl = initialize_agent(tools, llm, agent=AgentType.OPENAI_FUNCTIONS, verbose=True)

mrkl.run(
    "Who is Leo DiCaprio's girlfriend? What is her current age raised to the 0.43 power?"
)

4.ReAct 代理

from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.agents import AgentType
from langchain.llms import OpenAI

#加载要用来控制代理的语言模型
llm = OpenAI(temperature=0)
# llm-math 工具使用了 LLM,所以我们需要传入 LLM。
tools = load_tools(["serpapi", "llm-math"], llm=llm)
# 让我们用这些工具、语言模型和我们想要使用的代理类型初始化一个代理。
agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
agent.run("Who is Leo DiCaprio's girlfriend? What is her current age raised to the 0.43 power?")

您还可以创建使用聊天模型而不是 LLMs 作为代理驱动程序的 ReAct 代理。

from langchain.chat_models import ChatOpenAI

chat_model = ChatOpenAI(temperature=0)
agent = initialize_agent(tools, chat_model, agent=AgentType.CHAT_ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
agent.run("Who is Leo DiCaprio's girlfriend? What is her current age raised to the 0.43 power?")

5.ReAct 文档存储库

展示了如何使用代理来实现与文档存储库特定的 ReAct 逻辑。

from langchain import OpenAI, Wikipedia
from langchain.agents import initialize_agent, Tool
from langchain.agents import AgentType
from langchain.agents.react.base import DocstoreExplorer

docstore = DocstoreExplorer(Wikipedia())
tools = [
    Tool(
        name="Search",
        func=docstore.search,
        description="useful for when you need to ask with search",
    ),
    Tool(
        name="Lookup",
        func=docstore.lookup,
        description="useful for when you need to ask with lookup",
    ),
]

llm = OpenAI(temperature=0, model_name="text-davinci-002")
react = initialize_agent(tools, llm, agent=AgentType.REACT_DOCSTORE, verbose=True)

question = "Author David Chanoff has collaborated with a U.S. Navy admiral who served as the ambassador to the United Kingdom under which President?"
react.run(question)

6.自问自答带搜索

from langchain import OpenAI, SerpAPIWrapper
from langchain.agents import initialize_agent, Tool
from langchain.agents import AgentType

llm = OpenAI(temperature=0)
search = SerpAPIWrapper()
tools = [
    Tool(
        name="Intermediate Answer",
        func=search.run,
        description="useful for when you need to ask with search",
    )
]

self_ask_with_search = initialize_agent(
    tools, llm, agent=AgentType.SELF_ASK_WITH_SEARCH, verbose=True
)
self_ask_with_search.run(
    "What is the hometown of the reigning men's U.S. Open champion?"
)

三 、How-to

1.给 OpenAI Functions Agent 添加记忆

本笔记本介绍了如何给 OpenAI Functions agent 添加记忆功能。

from langchain import (
    LLMMathChain,
    OpenAI,
    SerpAPIWrapper,
    SQLDatabase,
    SQLDatabaseChain,
)
from langchain.agents import initialize_agent, Tool
from langchain.agents import AgentType
from langchain.chat_models import ChatOpenAI

llm = ChatOpenAI(temperature=0, model="gpt-3.5-turbo-0613")
search = SerpAPIWrapper()
llm_math_chain = LLMMathChain.from_llm(llm=llm, verbose=True)
db = SQLDatabase.from_uri("sqlite:///../../../../../notebooks/Chinook.db")
db_chain = SQLDatabaseChain.from_llm(llm, db, verbose=True)
tools = [
    Tool(
        name="Search",
        func=search.run,
        description="useful for when you need to answer questions about current events. You should ask targeted questions",
    ),
    Tool(
        name="Calculator",
        func=llm_math_chain.run,
        description="useful for when you need to answer questions about math",
    ),
    Tool(
        name="FooBar-DB",
        func=db_chain.run,
        description="useful for when you need to answer questions about FooBar. Input should be in the form of a question containing full context",
    ),
]
from langchain.prompts import MessagesPlaceholder
from langchain.memory import ConversationBufferMemory
agent_kwargs = {
    "extra_prompt_messages": [MessagesPlaceholder(variable_name="memory")],
}
memory = ConversationBufferMemory(memory_key="memory", return_messages=True)
agent = initialize_agent(
    tools, 
    llm, 
    agent=AgentType.OPENAI_FUNCTIONS, 
    verbose=True, 
    agent_kwargs=agent_kwargs, 
    memory=memory
)
agent.run("hi")
agent.run("my name is bob")
agent.run("whats my name")

2.自定义代理

这个笔记本介绍了如何创建自己的自定义代理。
代理由两部分组成:
工具:代理可用的工具。
代理类本身:决定采取什么行动。
在这个笔记本中,我们将介绍如何创建自定义代理。

from langchain.agents import Tool, AgentExecutor, BaseSingleActionAgent
from langchain import OpenAI, SerpAPIWrapper

search = SerpAPIWrapper()
tools = [
    Tool(
        name="Search",
        func=search.run,
        description="有助于回答有关当前事件的问题",
        return_direct=True,
    )
]

from typing import List, Tuple, Any, Union
from langchain.schema import AgentAction, AgentFinish

class FakeAgent(BaseSingleActionAgent):
    """虚拟自定义代理。"""

    @property
    def input_keys(self):
        return ["input"]

    def plan(
        self, intermediate_steps: List[Tuple[AgentAction, str]], **kwargs: Any
    ) -> Union[AgentAction, AgentFinish]:
        """根据输入决定要做什么。

        Args:
            intermediate_steps: LLM到目前为止采取的步骤以及观察结果
            **kwargs: 用户输入

        Returns:
            指定要使用的工具的行动。
        """
        return AgentAction(tool="Search", tool_input=kwargs["input"], log="")

    async def aplan(
        self, intermediate_steps: List[Tuple[AgentAction, str]], **kwargs: Any
    ) -> Union[AgentAction, AgentFinish]:
        """根据输入决定要做什么。

        Args:
            intermediate_steps: LLM到目前为止采取的步骤以及观察结果
            **kwargs: 用户输入

        Returns:
            指定要使用的工具的行动。
        """
        return AgentAction(tool="Search", tool_input=kwargs["input"], log="")


agent = FakeAgent()

agent_executor.run("2023年加拿大有多少人口?")

3.使用工具检索的自定义代理

本笔记基于 此笔记本 进行开发,并假定您已熟悉代理的工作原理。

本笔记引入的新概念是使用检索来选择要用于回答代理查询的工具集。当您有很多工具可供选择时,这非常有用。您无法将所有工具的描述放在提示中(由于上下文长度的问题),因此您可以在运行时动态选择要考虑使用的 N 个工具。

在本笔记中,我们将创建一个有些牵强的示例。我们将有一个合法的工具(搜索)和 99 个假工具,这些假工具只是一些无意义的工具。然后,我们将在提示模板中添加一个步骤,该步骤接收用户输入并检索与查询相关的工具。

from langchain.agents import (
    Tool,
    AgentExecutor,
    LLMSingleActionAgent,
    AgentOutputParser,
)
from langchain.prompts import StringPromptTemplate
from langchain import OpenAI, SerpAPIWrapper, LLMChain
from typing import List, Union
from langchain.schema import AgentAction, AgentFinish
import re


# Define which tools the agent can use to answer user queries
search = SerpAPIWrapper()
search_tool = Tool(
    name="Search",
    func=search.run,
    description="useful for when you need to answer questions about current events",
)


def fake_func(inp: str) -> str:
    return "foo"


fake_tools = [
    Tool(
        name=f"foo-{i}",
        func=fake_func,
        description=f"a silly function that you can use to get more information about the number {i}",
    )
    for i in range(99)
]
ALL_TOOLS = [search_tool] + fake_tools

from langchain.vectorstores import FAISS
from langchain.embeddings import OpenAIEmbeddings
from langchain.schema import Document
#工具检索器我们将使用 vectorstore 为每个工具描述创建嵌入。然后,对于传入的查询,我们可以为该查询创建嵌入并对相关工具进行相似性搜索。
docs = [
    Document(page_content=t.description, metadata={"index": i})
    for i, t in enumerate(ALL_TOOLS)
]
vector_store = FAISS.from_documents(docs, OpenAIEmbeddings())
retriever = vector_store.as_retriever()
def get_tools(query):
    docs = retriever.get_relevant_documents(query)
    return [ALL_TOOLS[d.metadata["index"]] for d in docs]
get_tools("whats the weather?")
# Set up the base template
template = """Answer the following questions as best you can, but speaking as a pirate might speak. You have access to the following tools:

{tools}

Use the following format:

Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question

Begin! Remember to speak as a pirate when giving your final answer. Use lots of "Arg"s

Question: {input}
{agent_scratchpad}"""
from typing import Callable
# Set up a prompt template
class CustomPromptTemplate(StringPromptTemplate):
    # The template to use
    template: str
    ############## NEW ######################
    # The list of tools available
    tools_getter: Callable

    def format(self, **kwargs) -> str:
        # Get the intermediate steps (AgentAction, Observation tuples)
        # Format them in a particular way
        intermediate_steps = kwargs.pop("intermediate_steps")
        thoughts = ""
        for action, observation in intermediate_steps:
            thoughts += action.log
            thoughts += f"\nObservation: {observation}\nThought: "
        # Set the agent_scratchpad variable to that value
        kwargs["agent_scratchpad"] = thoughts
        ############## NEW ######################
        tools = self.tools_getter(kwargs["input"])
        # Create a tools variable from the list of tools provided
        kwargs["tools"] = "\n".join(
            [f"{tool.name}: {tool.description}" for tool in tools]
        )
        # Create a list of tool names for the tools provided
        kwargs["tool_names"] = ", ".join([tool.name for tool in tools])
        return self.template.format(**kwargs)
prompt = CustomPromptTemplate(
    template=template,
    tools_getter=get_tools,
    # This omits the `agent_scratchpad`, `tools`, and `tool_names` variables because those are generated dynamically
    # This includes the `intermediate_steps` variable because that is needed
    input_variables=["input", "intermediate_steps"],
)
class CustomOutputParser(AgentOutputParser):
    def parse(self, llm_output: str) -> Union[AgentAction, AgentFinish]:
        # Check if agent should finish
        if "Final Answer:" in llm_output:
            return AgentFinish(
                # Return values is generally always a dictionary with a single `output` key
                # It is not recommended to try anything else at the moment :)
                return_values={"output": llm_output.split("Final Answer:")[-1].strip()},
                log=llm_output,
            )
        # Parse out the action and action input
        regex = r"Action\s*\d*\s*:(.*?)\nAction\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)"
        match = re.search(regex, llm_output, re.DOTALL)
        if not match:
            raise ValueError(f"Could not parse LLM output: `{llm_output}`")
        action = match.group(1).strip()
        action_input = match.group(2)
        # Return the action and action input
        return AgentAction(
            tool=action, tool_input=action_input.strip(" ").strip('"'), log=llm_output
        )
llm = OpenAI(temperature=0)
# LLM chain consisting of the LLM and a prompt
llm_chain = LLMChain(llm=llm, prompt=prompt)
tools = get_tools("whats the weather?")
tool_names = [tool.name for tool in tools]
agent = LLMSingleActionAgent(
    llm_chain=llm_chain,
    output_parser=output_parser,
    stop=["\nObservation:"],
    allowed_tools=tool_names,
)
agent_executor = AgentExecutor.from_agent_and_tools(
    agent=agent, tools=tools, verbose=True
)
agent_executor.run("What's the weather in SF?")




  • 21
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值