语言模型本身不能采取行动,它们只是输出文本。LangChain的一个重要用例是创建代理。代理是使用LLM作为推理引擎来确定要采取哪些行动,以及传递哪些输入的系统。执行操作后,可以将结果反馈到LLM中,以确定是否需要更多操作,或者是否可以完成。
本文我们将构建一个可以与搜索引擎交互的代理。您将能够向该代理提问,观看它调用搜索工具,并与之进行对话。
阅读本文后,你将大致了解以下内容:
1、Tavily 搜索引擎;
2、Using Language Models 使用语言模型;
3、Create the agent 创建代理;
4、Streaming Messages 流式消息;
5、Adding in memory 添加内存。
一、Installation安装
LangChain安装:
pip install -U langchain-community langgraph langchain-anthropic tavily-python
二、使用Tavily 搜索引擎
为了使用它,您需要获取并设置一个 API 密钥:
点击前往:Tavily AI
注册并登录账号,会自动生成一个API Keys
配置TAVILY_API_KEY环境:
import os
os.environ["TAVILY_API_KEY"]="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"
我们首先需要创建我们想要使用的工具。我们选择的主要工具将是 Tavily 搜索引擎。我们在LangChain中有一个内置工具,可以轻松地使用Tavily搜索引擎作为工具。
from langchain_community.tools.tavily_search import TavilySearchResults
search = TavilySearchResults(max_results=2)
search_results = search.invoke("深圳天气怎么样?")
print(search_results)
# If we want, we can create other tools.
# Once we have all the tools we want, we can put them in a list that we will reference later.
tools = [search]
三、使用语言模型
LangChain支持许多不同的语言模型,包含:OpenAI、Anthropic、Azure、Google、Cohere、FireworksAI、Groq、MistralAI、TogetherAI等,您可以互换使用 ,选择您要使用的语言模型!
1)下面内容将居于OpenAI语言模型进行演示:
pip install -qU langchain-openai
2)配置API KEY环境
import os
os.environ["OPENAI_API_KEY"]="填写自己的API KEY"
os.environ["LANGCHAIN_TRACING_V2"]="true"
os.environ["LANGCHAIN_API_KEY"]="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"
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4")
3)通过传入消息列表来调用语言模型。默认情况下,响应是一个字符串。
from langchain_core.messages import HumanMessage
response = model.invoke([HumanMessage(content="hi!")])
response.content
4)现在,我们可以看到启用此模型进行工具调用的感觉。为了实现这一点,我们用来给语言模型提供这些工具的知识;让我们首先用普通消息调用它,看看它是如何响应的。我们既可以看content,也可以看tool_calls。
model_with_tools = model.bind_tools(tools)
response = model_with_tools.invoke([HumanMessage(content="Hi!")])
print(f"ContentString: {response.content}")
print(f"ToolCalls: {response.tool_calls}")
5)让我们尝试使用一些需要调用工具的输入来调用它。
response = model_with_tools.invoke([HumanMessage(content="What's the weather in Beijin?")])
print(f"ContentString: {response.content}")
print(f"ToolCalls: {response.tool_calls}")
我们可以看到现在没有文本内容,但有一个工具调用!它希望我们调用 Tavily Search 工具。这还没有调用该工具,它只是告诉我们可以调用。为了实际调用它,我们需要创建我们的代理。
四、Create the agent 创建代理
现在我们已经定义了工具和 LLM,我们可以创建代理了。我们将使用 LangGraph 来构建代理。
目前,我们正在使用一个高级接口来构建代理,但 LangGraph 的好处是,这个高级接口由一个低级、高度可控的 API 支持,以防你想修改代理逻辑。
1)现在,我们可以使用 LLM 和工具启动代理。请注意,我们传递的是model,而不是model_with_tools。这是因为create_react_agent会在幕后为我们调用.bind_tools。
from langgraph.prebuilt import create_react_agent
agent_executor = create_react_agent(model, tools)
现在,我们可以在一些查询上运行代理!请注意,目前,这些都是查询(它不会记住以前的交互)。请注意,代理将在交互结束时返回状态(包括任何输入,我们将在后面看到如何仅获取输出)。
2)首先,让我们看看当不需要调用工具时它是如何响应的:
response = agent_executor.invoke({"messages": [HumanMessage(content="hi!")]})
response["messages"]
3)现在让我们看看调用该工具时它是如何响应的:
response = agent_executor.invoke(
{"messages": [HumanMessage(content="whats the weather in Beijin?")]}
)
response["messages"]
五、Streaming Messages 流式消息
我们已经了解了如何调用代理来获得最终响应。如果代理正在执行多个步骤,则可能需要一段时间。为了显示中间进度,我们可以在消息发生时将其流式传输回。
for chunk in agent_executor.stream(
{"messages": [HumanMessage(content="whats the weather in Beijin?")]}
):
print(chunk)
print("----")
Streaming tokens 流式tokens
除了流式传输回消息外,流式传输回token也很有用。我们可以用这种方法做到这一点。
async for event in agent_executor.astream_events(
{"messages": [HumanMessage(content="whats the weather in sf?")]}, version="v1"
):
kind = event["event"]
if kind == "on_chain_start":
if (
event["name"] == "Agent"
): # Was assigned when creating the agent with `.with_config({"run_name": "Agent"})`
print(
f"Starting agent: {event['name']} with input: {event['data'].get('input')}"
)
elif kind == "on_chain_end":
if (
event["name"] == "Agent"
): # Was assigned when creating the agent with `.with_config({"run_name": "Agent"})`
print()
print("--")
print(
f"Done agent: {event['name']} with output: {event['data'].get('output')['output']}"
)
if kind == "on_chat_model_stream":
content = event["data"]["chunk"].content
if content:
# Empty content in the context of OpenAI means
# that the model is asking for a tool to be invoked.
# So we only print non-empty content
print(content, end="|")
elif kind == "on_tool_start":
print("--")
print(
f"Starting tool: {event['name']} with inputs: {event['data'].get('input')}"
)
elif kind == "on_tool_end":
print(f"Done tool: {event['name']}")
print(f"Tool output was: {event['data'].get('output')}")
print("--")
六、Adding in memory 添加内存
如前所述,此代理是无状态的。这意味着它不记得以前的交互。为了给它内存,我们需要传入一个检查点。传入检查点时,我们还必须在调用代理时传入一个(以便它知道要从哪个线程/对话恢复)。
from langgraph.checkpoint.sqlite import SqliteSaver
memory = SqliteSaver.from_conn_string(":memory:")
agent_executor = create_react_agent(model, tools, checkpointer=memory)
config = {"configurable": {"thread_id": "abc123"}}
for chunk in agent_executor.stream(
{"messages": [HumanMessage(content="hi im bob!")]}, config
):
print(chunk)
print("----")
for chunk in agent_executor.stream(
{"messages": [HumanMessage(content="whats my name?")]}, config
):
print(chunk)
print("----")
如果我想开始一个新的对话,我所要做的就是改变使用过的thread_id
config = {"configurable": {"thread_id": "xyz123"}}
for chunk in agent_executor.stream(
{"messages": [HumanMessage(content="whats my name?")]}, config
):
print(chunk)
print("----")
至此,我们介绍了如何创建简单的代理,并展示了如何流式传输响应;我们还添加了内存,以便您可以与他们进行历史记录对话。