引言

在上一篇教程中,我们使用 LangGraph 构建了一个简单的 Chatbot。虽然这个 Chatbot 能够处理基本的对话,但在面对一些需要实时信息或超出其知识范围的问题时,它的表现可能会显得力不从心。例如,当用户询问“今天的天气如何?”或“最新的新闻是什么?”时,Chatbot 无法“凭记忆”回答这些问题。

为了解决这个问题,我们可以为 Chatbot 集成一个网络搜索工具。通过这个工具,Chatbot 可以实时查找相关信息,并提供更准确、更及时的响应。在本教程中,我们将使用 tavily-pythonlangchain_community 库来实现这一功能。

什么是网络搜索工具?

网络搜索工具是一种能够通过 API 访问互联网资源并检索相关信息的工具。通过集成网络搜索工具,Chatbot 可以在对话过程中动态获取外部数据,从而增强其回答能力。例如,当用户询问某个特定话题的最新信息时,Chatbot 可以通过网络搜索工具查找相关内容,并将其整合到对话中。

在本教程中,我们将使用 tavily-python 库来实现网络搜索功能。tavily-python 是一个简单易用的 Python 库,能够快速检索网络上的相关信息。

安装依赖

在开始之前,我们需要安装一些额外的依赖库。你可以通过以下命令安装这些库:

1
pip install -U tavily-python langchain_community
  • tavily-python: 用于实现网络搜索功能。
  • langchain_community: 提供与 LangChain 相关的工具和集成。

安装完成后,我们就可以开始集成网络搜索工具了。

定义工具

在 LangGraph 中,工具(Tool)是一种可以扩展 Chatbot 功能的组件。我们可以通过定义工具来实现网络搜索功能。首先,我们需要创建一个工具类,并在其中实现搜索逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
from langchain_community.tools.tavily_search import TavilySearchResults

# 设置搜索密钥
import os
import getpass

if not os.environ.get("TAVILY_API_KEY"):
os.environ["TAVILY_API_KEY"] = getpass.getpass("Enter API key for Tavily: ")

tool = TavilySearchResults(max_results=2)
tools = [tool]
tool.invoke("What's a 'node' in LangGraph?")

在这个例子中,我们定义了一个 TavilySearchResults 工具,它接收一个查询字符串并返回搜索结果。max_results=2 表示每次搜索最多返回 2 个结果。

集成工具到 Chatbot

接下来,我们需要将这个工具集成到 Chatbot 中。我们可以通过修改 Chatbot 的逻辑,使其在遇到无法回答的问题时调用搜索工具。

1
2
3
4
5
6
7
8
9
10
from langchain_openai import ChatOpenAI

# 设置LLM密钥
if not os.environ.get("OPENAI_API_KEY"):
os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter API key for OpenAI: ")

llm = ChatOpenAI(model="gpt-4o-mini",base_url="https://api.chatanywhere.tech/v1")

# 绑定工具到LLM
llm_with_tools = llm.bind_tools(tools)

在这个例子中,我们使用 bind_tools 方法将工具绑定到语言模型(LLM)。这样,LLM 在生成响应时,可以根据需要调用工具来获取外部信息。

构建增强版对话图

现在,我们可以使用增强版的 Chatbot 函数来构建一个新的对话图。

1
2
3
4
5
6
7
8
9
10
11
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages

class State(TypedDict):
messages: Annotated[list, add_messages]

def chatbot(state: State):
return {"messages": [llm_with_tools.invoke(state["messages"])]}

graph_builder = StateGraph(State)
graph_builder.add_node("chatbot", chatbot)

在这个例子中,我们创建了一个新的对话图,并使用 chatbot 函数作为节点。对话图的结构与之前的版本类似,但 Chatbot 的功能得到了增强。

添加工具节点

为了让 Chatbot 能够调用工具,我们需要添加一个工具节点。这个节点负责执行工具调用,并将结果返回给 Chatbot。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import json
from langchain_core.messages import ToolMessage

class BasicToolNode:
def __init__(self, tools: list) -> None:
self.tools_by_name = {tool.name: tool for tool in tools}

def __call__(self, inputs: dict):
if messages := inputs.get("messages", []):
message = messages[-1]
else:
raise ValueError("No message found in input")
outputs = []
for tool_call in message.tool_calls:
tool_result = self.tools_by_name[tool_call["name"]].invoke(tool_call["args"])
outputs.append(
ToolMessage(
content=json.dumps(tool_result),
name=tool_call["name"],
tool_call_id=tool_call["id"],
)
)
return {"messages": outputs}

tool_node = BasicToolNode(tools=[tool])
graph_builder.add_node("tools", tool_node)

在这个例子中,我们定义了一个 BasicToolNode 类,它负责执行工具调用并返回结果。然后,我们将这个工具节点添加到对话图中。

定义路由器函数

为了让 Chatbot 在需要时调用工具,我们需要定义一个路由器函数。这个函数会根据 Chatbot 的输出决定下一步是调用工具还是结束对话。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def route_tools(state: State):
if isinstance(state, list):
ai_message = state[-1]
elif messages := state.get("messages", []):
ai_message = messages[-1]
else:
raise ValueError(f"No messages found in input state to tool_edge: {state}")
if hasattr(ai_message, "tool_calls") and len(ai_message.tool_calls) > 0:
return "tools"
return END

graph_builder.add_conditional_edges(
"chatbot",
route_tools,
{"tools": "tools", END: END},
)
graph_builder.add_edge("tools", "chatbot")
graph_builder.add_edge(START, "chatbot")
graph = graph_builder.compile()

在这个例子中,route_tools 函数会检查 Chatbot 的输出是否包含工具调用。如果有工具调用,则路由到工具节点;否则,结束对话。

根据上面建边情况,可以得到如下的路由图:

路由图

运行增强版 Chatbot

最后,我们可以运行增强版 Chatbot 并观察其行为。通过 graph.stream 方法,我们可以将用户输入传递给 Chatbot,并逐步生成对话。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def stream_graph_updates(user_input: str):
for event in graph.stream({"messages": [{"role": "user", "content": user_input}]}):
for value in event.values():
print("Assistant:", value["messages"][-1].content)

while True:
try:
user_input = input("User: ")
if user_input.lower() in ["quit", "exit", "q"]:
print("Goodbye!")
break

stream_graph_updates(user_input)
except:
# fallback if input() is not available
user_input = "What do you know about LangGraph?"
print("User: " + user_input)
stream_graph_updates(user_input)
break

在这个例子中,我们创建了一个简单的交互式循环,用户可以输入消息,增强版 Chatbot 会生成响应。如果用户输入 "quit""exit""q",程序将退出。

结论

通过本教程,我们学习了如何为 Chatbot 集成网络搜索工具,从而增强其回答能力。我们定义了搜索工具、修改了 Chatbot 的逻辑,并最终运行了增强版 Chatbot。这个增强版 Chatbot 能够处理更多类型的问题,并提供更准确、更及时的响应。

希望这篇教程对你理解如何增强 Chatbot 的功能有所帮助!如果你有任何问题或建议,欢迎在评论区留言。


作者: Mudrobot
日期: 2025.01.24
标签: LangGraph, Chatbot, 网络搜索, NLP, 自然语言处理


本站由 @anonymity 使用 Stellar 主题创建。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。