Langchain 自定义Agent

第一步,需要加载一个语言模型来控制Agent,常见的使用OpenAI

from langchain_openai import ChatOpenAI

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

第二步,自定义一个工具,比如定义一个测量字符长度的工具

from langchain.agents import tool


@tool
def get_word_length(word: str) -> int:
    """Returns the length of a word."""
    return len(word)


get_word_length.invoke("abc")

第三步,创建prompt

由于 OpenAI 函数调用针对工具使用进行了微调,因此我们几乎不需要任何有关如何推理或如何输出格式的说明。我们只有两个输入变量: inputagent_scratchpadinput 应该是包含用户输入的字符串。 agent_scratchpad 应是包含先前代理工具调用和相应工具输出的消息序列。

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "You are very powerful assistant, but don't know current events",
        ),
        ("user", "{input}"),
        MessagesPlaceholder(variable_name="agent_scratchpad"),
    ]
)

第四步,绑定工具到LLM

让Agent知道它可以使用哪些工具

llm_with_tools = llm.bind_tools(tools)

第五步,创建代理

将上面的零碎部分整合在一起,就创建了一个自定义的Agent。这里还会使用两个重要的工具函数:其中一个用来格式化中间步骤(Agent行为、工具输出对),另外一个将输出的消息传递给下一步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"}))

下一步优化,给自定义Agent增加记忆,保存历史消息,使Agent具备状态。为了达到这个目的需要做两件事情

第一件:在prompt中增加一个历史消息的位置

第二件:始终追踪历史消息

先看第一件事情:

首先,让我们在提示中添加一个内存位置。我们通过为带有键 "chat_history" 的消息添加占位符来实现此目的。请注意,我们将其放在新用户输入之上(以遵循对话流程)。

from langchain.prompts import MessagesPlaceholder

MEMORY_KEY = "chat_history"
prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "You are very powerful assistant, but bad at calculating lengths of words.",
        ),
        MessagesPlaceholder(variable_name=MEMORY_KEY),
        ("user", "{input}"),
        MessagesPlaceholder(variable_name="agent_scratchpad"),
    ]
)

第二件事,使用一个数组来保持对对话消息的持续追踪

from langchain_core.messages import AIMessage, HumanMessage

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})