(18-5-01)Agents(智能代理):自定义代理

经过本书前面内容的介绍,已经了解了创建并使用LangChain代理的知识,在本节的内容中,将通过具体实战进一步讲解使用LangChain代理实现复杂功能的知识。

6.5  Agent代理操作实战

经过本书前面内容的介绍,已经了解了创建并使用LangChain代理的知识,在本节的内容中,将通过具体实战进一步讲解使用LangChain代理实现复杂功能的知识。

6.5.1  自定义代理

在LangChain中,自定义代理是一个强大的功能,它允许用户根据特定需求创建和定制代理的功能。在下面的内容中,将详细讲解使用OpenAI工具调用来创建自定义代理的流程。

(1)加载语言模型(Load the LLM):首先,加载将用于控制代理的语言模型。

from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)

(2)定义工具(Define Tools):定义一些要使用的工具,例如在下面编写了一个简单的Python函数来计算传入单词的长度。

from langchain.agents import tool
@tool
def get_word_length(word: str) -> int:
    ""返回一个单词的长度。""
    return len(word)
tools = [get_word_length]

(3)创建提示(Create Prompt)

在构建自定义代理时,创建提示(prompt)是一个关键步骤,它决定了代理如何接收和处理用户输入。由于OpenAI的函数调用机制已经对工具使用进行了特别优化,因此在设计提示时,开发者不必过多关注代理的推理过程或输出格式的具体细节。在LangChain中,应该主要关注两个核心的输入变量:

  1. input:这个变量用于接收用户的直接输入,它通常是一个字符串,清晰地表达了用户的具体需求或目标。例如,用户可能提出一个问题或请求,这个请求就会作为input被传递给代理。
  2. agent_scratchpad:这个变量用于记录代理在交互过程中的关键信息,它包含了代理之前调用的工具以及这些工具的输出结果,形成一个消息序列。这个序列本质上是代理的工作记忆,它允许代理回顾和利用之前的交互信息,以更准确地响应用户的当前需求。

通过精心设计这两个输入变量,可以构建一个高效的提示模板,使得代理能够更好地理解用户意图,并在对话的上下文中生成恰当的响应。

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "你是一个非常强大的助手,但不了解当前事件"),
        ("user", "{input}"),
        MessagesPlaceholder(variable_name="agent_scratchpad"),
    ]
)

(4)将工具绑定到语言模型(Bind tools to LLM)

在构建自定义代理的过程中,确保代理了解其可使用的工具是至关重要的。为了让代理知道它能够利用哪些工具来执行任务,可以采用了OpenAI的工具调用机制来实现这一目标。这个机制允许将工具作为参数传递给语言模型,并且这些语言模型已经被特别训练,能够理解何时以及如何使用这些工具。例如在下面的这行代码中,llm是我们的语言模型实例,而tools是之前定义的工具列表。通过调用bind_tools方法,实现了工具与语言模型的绑定,从而为创建一个功能完备的代理奠定了基础。

llm_with_tools = llm.bind_tools(tools)

通过这种方式,我们的代理在处理用户请求时,可以灵活地选择并使用最适合解决当前任务的工具,从而提高代理的性能和效率。

(5)创建代理(Create the Agent)

在定义了工具、创建了提示、并将工具绑定到语言模型之后,现在可以将这些组件组合起来,正式构建我们的代理。

from langchain.agents.format_scratchpad.openai_tools import format_to_openai_tool_messages
from langchain.agents.output_parsers.openai_tools import OpenAIToolsAgentOutputParser

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

from langchain.agents import AgentExecutor
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

list(agent_executor.stream({"input": "How many letters in the word eudca"}))

上述代码的实现流程如下所示:

  1. 格式化工具消息:首先使用format_to_openai_tool_messages函数来格式化代理在执行任务时可能产生的中间步骤,这些步骤将被转换为OpenAI工具格式的消息。
  2. 结合提示:然后,将之前创建的提示(prompt)与格式化的工具消息结合起来,以指导代理如何理解和响应用户的输入。
  3. 绑定语言模型:接着,将绑定了工具的语言模型(llm_with_tools)加入到这个组合中,确保代理在生成回答时可以调用这些工具。
  4. 输出解析:最后,使用OpenAIToolsAgentOutputParser来解析代理的输出,将其转换为可理解的格式。

在上述代码中,代理被用来计算单词"eudca"的字母数量。通过这种方式,我们创建了一个能够理解和响应特定用户输入的代理,并且可以跟踪对话历史,从而提供更加连贯和上下文相关的回答。执行后会输出:

[
    {
        'output': 'The word "eudca" has 5 letters.',
        'intermediate_steps': [
            {
                'tool': 'get_word_length',
                'tool_input': {'word': 'eudca'},
                'observation': 5
            }
        ],
        'log': [
            "Invoking: `get_word_length` with `{'word': 'eudca'}`",
            "The word 'eudca' has 5 letters."
        ]
    }
]

上面的输出表明,代理已经识别出用户的查询是关于计算单词 "eudca" 的字母数量,并成功调用了 get_word_length 工具来执行此操作。最终结果是一个包含单词字母数量的人性化回答,以及可能的中间步骤和日志信息。

(6)添加记忆(Adding memory)

在构建自定义代理时,添加记忆机制是一个重要的步骤,它允许代理在连续的交互中保持上下文感知。为了能够让代理能够记住之前的交互信息,需要对它进行如下所示的改进:

  1. 在提示中添加记忆变量的位置:在提示模板中包含一个用于记忆的特殊占位符,这个占位符将存储对话历史。为了保持对话流程的连贯性,这个占位符被放置在用户新输入的上方。
  2. 跟踪聊天记录:创建一个列表来跟踪每一次的用户输入和代理的输出,形成聊天记录。
MEMORY_KEY = "chat_history"
prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "你是一个非常强大的助手,但不擅长计算单词长度。"),
        MessagesPlaceholder(variable_name=MEMORY_KEY),
        ("user", "{input}"),
        MessagesPlaceholder(variable_name="agent_scratchpad"),
    ]
)

chat_history = []

agent = (
    {
        "input": lambda x: x["input"],
        "agent_scratchpad": lambda x: format_to_openai_tool_messages(x["intermediate_steps"]),
        "chat_history": lambda x: x["chat_history"],
    }
    | prompt
    | llm_with_tools
    | OpenAIToolsAgentOutputParser()
)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

# 在运行时,我们现在需要跟踪输入和输出作为聊天记录
input1 = "how many letters in the word educa?"
result = agent_executor.invoke({"input": input1, "chat_history": chat_history})
chat_history.extend(
    [
        HumanMessage(content=input1),
        AIMessage(content=result["output"]),
    ]
)
agent_executor.invoke({"input": "is that a real word?", "chat_history": chat_history})

通过这种方式,现在的自定义代理可以访问和参考之前的对话内容,从而更准确地处理后续问题和上下文相关的查询。这大大提升了代理的交互能力和用户体验。

执行后第一轮的交互输出如下,代理会计算出 "educa" 有 5 个字母,并将这个信息作为回答返回。

> Entering new AgentExecutor chain...
> Invoking: `get_word_length` with `{'word': 'educa'}`

The word "educa" has 5 letters.

> Finished chain.

下面是更新后的聊天记录:

chat_history = [
    HumanMessage(content="how many letters in the word educa?"),
    AIMessage(content="The word "educa" has 5 letters.")
]

第二轮交互输出如下,代理会检查 "educa" 是否是一个真实存在的单词,并给出回答。

> Entering new AgentExecutor chain...
> Checking if "educa" is a real word...

No, "educa" is not a recognized English word.

> Finished chain.

下面是更新后的聊天记录:

chat_history = [
    HumanMessage(content="how many letters in the word educa?"),
    AIMessage(content="The word "educa" has 5 letters."),
    HumanMessage(content="is that a real word?"),
    AIMessage(content="No, "educa" is not a recognized English word.")
]

通过上面的步骤,详细展示了使用OpenAI的工具调用和语言模型来创建一个自定义代理的过程,包括如何定义工具、创建提示、绑定工具到语言模型、创建代理,以及如何为代理添加记忆以跟踪对话历史。通过添加记忆,代理能够更好地处理后续问题和上下文相关的查询。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

码农三叔

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

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

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

打赏作者

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

抵扣说明:

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

余额充值