添加工具¶
为了处理你的聊天机器人无法从记忆中回答的查询,请集成一个网络搜索工具。聊天机器人可以使用此工具查找相关信息并提供更好的回复。
Note
本教程基于构建一个基本的聊天机器人。
先决条件¶
在开始本教程之前,请确保您具备以下内容:
- Tavily 搜索引擎的 API 密钥。
1. 安装搜索引擎¶
安装使用 Tavily 搜索引擎 所需的依赖项:
2. 配置你的环境¶
使用你的搜索引擎 API 密钥配置你的环境:
3. 定义工具¶
定义网络搜索工具:
API Reference: TavilySearch
from langchain_tavily import TavilySearch
tool = TavilySearch(max_results=2)
tools = [tool]
tool.invoke("What's a 'node' in LangGraph?")
结果是页面摘要,聊天机器人可以使用这些摘要来回答问题:
{'query': "What's a 'node' in LangGraph?",
'follow_up_questions': None,
'answer': None,
'images': [],
'results': [{'title': "Introduction to LangGraph: A Beginner's Guide - Medium",
'url': 'https://medium.com/@cplog/introduction-to-langgraph-a-beginners-guide-14f9be027141',
'content': 'Stateful Graph: LangGraph 围绕状态图的概念展开,其中图中的每个节点代表计算中的一个步骤,图维护一个随着计算过程传递和更新的状态。LangGraph 支持条件边,允许你根据图的当前状态动态确定要执行的下一个节点。我们定义了用于分类输入、处理问候语和处理搜索查询的节点。 def classify_input_node(state): LangGraph 是一个用于构建复杂、有状态应用的强大工具,它利用 LLM(大型语言模型)。通过理解其核心概念并通过简单的示例进行实践,初学者可以开始为其项目利用它的功能。请记住要注意状态管理、条件边以及确保图中没有死节点。',
'score': 0.7065353,
'raw_content': None},
{'title': 'LangGraph Tutorial: What Is LangGraph and How to Use It?',
'url': 'https://www.datacamp.com/tutorial/langgraph-tutorial',
'content': 'LangGraph 是 LangChain 生态系统中的一个库,它提供了一个框架,用于定义、协调和以结构化且高效的方式执行多个 LLM 代理(或链)。通过管理数据流和操作顺序,LangGraph 允许开发人员专注于应用程序的高层逻辑,而不是代理协调的细节。无论你需要一个能够处理各种用户请求的聊天机器人,还是一个执行复杂任务的多代理系统,LangGraph 都提供了构建所需功能的工具。LangGraph 通过提供一个结构化的框架来管理状态和协调代理交互,显著简化了复杂 LLM 应用程序的开发。',
'score': 0.5008063,
'raw_content': None}],
'response_time': 1.38}
4. 定义图¶
对于你在第一个教程中创建的 StateGraph
,在 LLM 上添加 bind_tools
。这可以让 LLM 知道如果它想使用搜索引擎时应该使用的正确 JSON 格式。
让我们首先选择我们的 LLM:
{}¶
import os
from langchain.chat_models import init_chat_model
os.environ["AZURE_OPENAI_API_KEY"] = "..."
os.environ["AZURE_OPENAI_ENDPOINT"] = "..."
os.environ["OPENAI_API_VERSION"] = "2025-03-01-preview"
llm = init_chat_model(
"azure_openai:gpt-4.1",
azure_deployment=os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"],
)
我们现在可以将其整合到 StateGraph
中:
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
class State(TypedDict):
messages: Annotated[list, add_messages]
graph_builder = StateGraph(State)
# 修改:告诉 LLM 它可以调用哪些工具
# highlight-next-line
llm_with_tools = llm.bind_tools(tools)
def chatbot(state: State):
return {"messages": [llm_with_tools.invoke(state["messages"])]}
graph_builder.add_node("chatbot", chatbot)
5. 创建一个运行工具的函数¶
现在,创建一个函数,用于在工具被调用时运行这些工具。通过将工具添加到一个新的节点BasicToolNode
中来实现这一点,该节点检查状态中的最新消息,并在消息包含tool_calls
时调用工具。它依赖于LLM的tool_calling
支持功能,该功能在Anthropic、OpenAI、Google Gemini以及一些其他LLM提供商中都可用。
API Reference: ToolMessage
import json
from langchain_core.messages import ToolMessage
class BasicToolNode:
"""一个节点,用于运行最后一条AIMessage中请求的工具。"""
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("在输入中未找到消息")
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)
Note
如果你将来不想自己构建这个节点,可以使用LangGraph预构建的 ToolNode。
6. 定义 conditional_edges
¶
在添加了工具节点后,现在可以定义 conditional_edges
。
Edges 控制流程从一个节点流向下一个节点。Conditional edges 从单个节点开始,通常包含 "if" 语句,根据当前图的状态路由到不同的节点。这些函数接收当前图的 state
,并返回一个字符串或字符串列表,表示下一步要调用的节点。
接下来,定义一个名为 route_tools
的路由器函数,该函数检查聊天机器人的输出中是否有 tool_calls
。通过调用 add_conditional_edges
将此函数提供给图,告诉图每当 chatbot
节点完成时,检查此函数以确定下一步去向。
如果存在工具调用,则条件将路由到 tools
;如果没有,则路由到 END
。由于条件可以返回 END
,因此这次不需要显式设置 finish_point
。
def route_tools(
state: State,
):
"""
在 conditional_edge 中使用,如果最后一条消息有工具调用,则路由到 ToolNode。
否则,路由到终点。
"""
if isinstance(state, list):
ai_message = state[-1]
elif messages := state.get("messages", []):
ai_message = messages[-1]
else:
raise ValueError(f"在 tool_edge 输入状态中未找到消息:{state}")
if hasattr(ai_message, "tool_calls") and len(ai_message.tool_calls) > 0:
return "tools"
return END
# `tools_condition` 函数如果聊天机器人要求使用工具,则返回 "tools";
# 如果可以直接响应,则返回 "END"。
# 这种条件路由定义了代理的主要循环。
graph_builder.add_conditional_edges(
"chatbot",
route_tools,
# 下面的字典允许你告诉图将条件的输出解释为特定的节点
# 默认是恒等函数,但如果你想使用除 "tools" 以外的其他命名节点,
# 可以更新字典的值为其他名称
# 例如:"tools": "my_tools"
{"tools": "tools", END: END},
)
# 每当调用工具时,我们返回到 chatbot 以决定下一步
graph_builder.add_edge("tools", "chatbot")
graph_builder.add_edge(START, "chatbot")
graph = graph_builder.compile()
Note
你可以用预构建的 tools_condition 来替代,使代码更简洁。
7. 可视化图(可选)¶
你可以使用 get_graph
方法和其中一个“绘制”方法,如 draw_ascii
或 draw_png
来可视化图。每个 draw
方法都需要额外的依赖项。
from IPython.display import Image, display
try:
display(Image(graph.get_graph().draw_mermaid_png()))
except Exception:
# 这需要一些额外的依赖项,并且是可选的
pass
8. 向机器人提问¶
现在你可以向聊天机器人提出超出其训练数据范围的问题:
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:
# 如果 input() 不可用时的后备方案
user_input = "What do you know about LangGraph?"
print("User: " + user_input)
stream_graph_updates(user_input)
break
Assistant: [{'text': "为了为您提供准确且最新的LangGraph信息,我需要搜索最新详情。让我为您查找。", 'type': 'text'}, {'id': 'toolu_01Q588CszHaSvvP2MxRq9zRD', 'input': {'query': 'LangGraph AI tool information'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}]
Assistant: [{"url": "https://www.langchain.com/langgraph", "content": "LangGraph为构建和扩展AI工作负载奠定了基础——从对话代理、复杂任务自动化到定制的LLM支持体验,这些都能‘开箱即用’。使用LLM构建复杂生产就绪功能的下一阶段是智能代理,通过LangGraph和LangSmith,LangChain提供了开箱即用的解决方案 ..."}, {"url": "https://github.com/langchain-ai/langgraph", "content": "概述。LangGraph是一个用于构建具有状态、多参与者应用的LLM库,用于创建代理和多代理工作流。与其它LLM框架相比,它提供了这些核心优势:循环、可控性和持久性。LangGraph允许你定义涉及循环的流程,这对于大多数智能代理架构至关重要 ..."}]
Assistant: 根据搜索结果,我可以为您提供有关LangGraph的信息:
1. 目的:
LangGraph是一个专为使用大型语言模型(LLMs)构建有状态、多参与者应用程序而设计的库。它特别适用于创建代理和多代理工作流。
2. 开发者:
LangGraph由LangChain开发,这是一家以AI和LLM领域工具及框架闻名的公司。
3. 关键特性:
- 循环:LangGraph允许定义包含循环的流程,这对大多数智能代理架构至关重要。
- 可控性:它提供对应用程序流程的增强控制。
- 持久性:该库提供了在基于LLM的应用程序中维持状态和持久性的方法。
4. 应用场景:
LangGraph可用于多种应用,包括:
- 对话代理
- 复杂任务自动化
- 定制的LLM支持体验
5. 集成:
LangGraph与LangChain的另一个工具LangSmith结合使用,为使用LLM构建复杂、生产就绪的功能提供开箱即用的解决方案。
6. 重要性:
...
LangGraph相较于其他LLM框架,具有独特的优势,尤其是在处理循环、提供可控性和维持持久性方面。
LangGraph在基于LLM的应用程序开发不断演变的领域中显得尤为重要,为开发者提供了创建更复杂、有状态和交互式AI系统的新方法。
Goodbye!
输出被截断。请作为可滚动元素查看或在文本编辑器中打开。调整单元格输出设置...
9. 使用预构建组件¶
为便于使用,请将代码中以下部分替换为 LangGraph 的预构建组件。这些组件内置了诸如并行执行 API 等功能。
BasicToolNode
被预构建的 ToolNode 替代route_tools
被预构建的 tools_condition 替代
{}¶
import os
from langchain.chat_models import init_chat_model
os.environ["AZURE_OPENAI_API_KEY"] = "..."
os.environ["AZURE_OPENAI_ENDPOINT"] = "..."
os.environ["OPENAI_API_VERSION"] = "2025-03-01-preview"
llm = init_chat_model(
"azure_openai:gpt-4.1",
azure_deployment=os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"],
)
from typing import Annotated
from langchain_tavily import TavilySearch
from langchain_core.messages import BaseMessage
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition
class State(TypedDict):
messages: Annotated[list, add_messages]
graph_builder = StateGraph(State)
tool = TavilySearch(max_results=2)
tools = [tool]
llm_with_tools = llm.bind_tools(tools)
def chatbot(state: State):
return {"messages": [llm_with_tools.invoke(state["messages"])]}
graph_builder.add_node("chatbot", chatbot)
tool_node = ToolNode(tools=[tool])
graph_builder.add_node("tools", tool_node)
graph_builder.add_conditional_edges(
"chatbot",
tools_condition,
)
# 每当调用工具时,我们返回到 chatbot 以决定下一步
graph_builder.add_edge("tools", "chatbot")
graph_builder.add_edge(START, "chatbot")
graph = graph_builder.compile()
恭喜! 你已经创建了一个可以在 LangGraph 中使用的会话代理,它可以根据需要使用搜索引擎检索最新信息。现在它可以处理更广泛范围的用户查询。要查看你的代理刚刚采取的所有步骤,请查看这个 LangSmith trace。
下一步¶
聊天机器人无法自行记住过去的交互,这限制了它进行连贯的多轮对话的能力。在接下来的部分中,你将添加**记忆**来解决这个问题。