[LangGraph教程]LangGraph02——使用工具增强聊天机器人

在上一篇文章中,我们用LangGraph搭建了一个最简单的聊天机器人,这个机器人可以通过接收用户输入并使用 LLM 生成响应来进行基本对话。

但是,这个机器人的知识仅限于其训练数据中的内容。在这篇博客中,我们将添加一个网络搜索工具,以扩展机器人的知识,使其更强大。

基本的聊天机器人

基本的聊天机器人代码如下所示,详细教程请看[LangGraph教程]LangGraph01——用LangGraph构建基本的聊天机器人

from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph.message import add_messages
from langgraph.graph import StateGraph

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

from langchain_community.chat_models import ChatOllama

local_llm = "qwen2.5:latest"
llm = ChatOllama(model=local_llm, format="json", temperature=0)

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

# 第一个参数是节点名称,第二个是要调用的函数
graph_builder.add_node("chatbot", chatbot)

from langgraph.graph import START, END


graph_builder.add_edge(START, "chatbot")
graph_builder.add_edge("chatbot", END)

graph = graph_builder.compile()

graph.invoke({"messages":"你好"})

使用工具增强聊天机器人

定义状态类并构建图

首先定义状态类并构建图,这部分和之前讲的一样,因此不过多赘述

from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph.message import add_messages
from langgraph.graph import StateGraph

# 1. 定义状态类
class State(TypedDict):
    messages:Annotated[list, add_messages]
# 2. 构建图    
graph_builder = StateGraph(State)

定义工具节点

这里 ,TavilySearchResults 是一个利用 Tavily 定义的搜索 API工具类,用于执行自定义的网络搜索并返回搜索结果。Tavily是一款专为 AI 代理 (LLM) 构建的搜索引擎,能够快速提供实时、准确和基于事实的结果。

max_results=2表示每次搜索最多返回 2 条结果。

然后创建了一个列表 tools,并将之前创建的 tool 实例添加到列表中。 tools列表包含了所有可以被语言模型调用的工具,这里我们只有一个工具。

tool = TavilySearchResults(max_results=2)
tools = [tool]

之前调用大模型用的是ChatOllama

#from langchain_community.chat_models import ChatOllama
#llm = ChatOllama(model="qwen2.5:latest", format="json", temperature=0)

但是ChatOllama没有bind_tools函数,所以这里我们使用ChatOpenAI来调用Ollama模型,并把工具绑定到大模型上:

# 3.定义工具节点 
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_openai import ChatOpenAI

local_llm = ["deepseek-r1:8b","qwen2.5:latest"]

llm = ChatOpenAI(model=local_llm[1], temperature=0.0, api_key="ollama", base_url="http://localhost:11434/v1")

llm_with_tools = llm.bind_tools(tools)

然后用 llm_with_tools 构建工具节点

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

把节点和边加入图

ToolNode 是 LangGraph 提供的一个预构建节点类,专门用于表示和处理工具调用的节点。它封装了工具的调用逻辑,使得在状态图中可以方便地集成和调用工具。

# 4. 把节点加入图 
# 第一个参数是节点名称,第二个是要调用的函数
from langgraph.prebuilt import ToolNode

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

graph_builder.add_node("chatbot", chatbot)

tools_condition是一个预构建的条件函数,用于判断是否需要调用工具。如果要用tools_condition,则节点必须叫tools,因为tools_condition里写死了名称。源码如下:

def tools_condition(
    state: Union[list[AnyMessage], dict[str, Any], BaseModel],
    messages_key: str = "messages",
) -> Literal["tools", "__end__"]:

    if isinstance(state, list):
        ai_message = state[-1]
    elif isinstance(state, dict) and (messages := state.get(messages_key, [])):
        ai_message = messages[-1]
    elif messages := getattr(state, messages_key, []):
        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__"

如果节点换成别的名字,会报错

ValueError: At ‘chatbot’ node, ‘tools_condition’ branch found unknown target ‘tools’

# 5. 把边加入图
from langgraph.prebuilt import tools_condition

graph_builder.add_conditional_edges(
    "chatbot",
    tools_condition,
)
graph_builder.add_edge("tools", "chatbot")
graph_builder.set_entry_point("chatbot")

构建完节点和边后,可以用代码绘制流程图进一步理解整个流程:

from IPython.display import Image, display
display(Image(graph.get_graph().draw_mermaid_png()))

绘制的流程图如下所示:
在这里插入图片描述
最后编译图并用invoke调用:

graph = graph_builder.compile()

graph.invoke({"messages":"今天武汉天气怎么样"})

结果如下:

{'messages': [HumanMessage(content='今天武汉天气怎么样', additional_kwargs={}, response_metadata={}, id='69f7b557-4129-4b15-82b5-22b578cf00a5'),
  AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_fos1y52p', 'function': {'arguments': '{"query":"今天 武汉 天气"}', 'name': 'tavily_search_results_json'}, 'type': 'function', 'index': 0}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 49, 'prompt_tokens': 191, 'total_tokens': 240, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'qwen2.5:latest', 'system_fingerprint': 'fp_ollama', 'id': 'chatcmpl-977', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-7c002089-7dad-40ed-ab84-2a4f862da9ef-0', tool_calls=[{'name': 'tavily_search_results_json', 'args': {'query': '今天 武汉 天气'}, 'id': 'call_fos1y52p', 'type': 'tool_call'}], usage_metadata={'input_tokens': 191, 'output_tokens': 49, 'total_tokens': 240, 'input_token_details': {}, 'output_token_details': {}}),
  ToolMessage(content='[{"title": "武汉市天气预报_天气查询- 墨迹天气", "url": "https://tianqi.moji.com/weather/china/hubei/wuhan", "content": "武汉市今天实况:16度雾,湿度:95%,北风:0级。白天:27度,雾。 夜间:多云,16度,天气较热,墨迹天气建议您选择短袖上衣加七分裤的搭配,针织衫是进出空调房的必备单品。", "score": 0.8464638}, {"title": "武汉市, 湖北省, 中华人民共和国天气预报和情况 - The Weather Channel", "url": "https://weather.com/zh-SG/weather/today/l/2637660151899903e8cbdd23636051470b6731863286ec74b3033421cb87e1e8", "content": "武汉市, 湖北省, 中华人民共和国今日天气 ; 高/ 低. --/8° ; 大风. 6 公里/小时 ; 湿度. 91% ; 露点. 8° ; 气压. 1019.0 毫巴.", "score": 0.82795376}]', name='tavily_search_results_json', id='2d2359f9-19bd-463b-9593-d0a7d18bd589', tool_call_id='call_fos1y52p', artifact={'query': '今天 武汉 天气', 'follow_up_questions': None, 'answer': None, 'images': [], 'results': [{'url': 'https://tianqi.moji.com/weather/china/hubei/wuhan', 'title': '武汉市天气预报_天气查询- 墨迹天气', 'content': '武汉市今天实况:16度雾,湿度:95%,北风:0级。白天:27度,雾。 夜间:多云,16度,天气较热,墨迹天气建议您选择短袖上衣加七分裤的搭配,针织衫是进出空调房的必备单品。', 'score': 0.8464638, 'raw_content': None}, {'url': 'https://weather.com/zh-SG/weather/today/l/2637660151899903e8cbdd23636051470b6731863286ec74b3033421cb87e1e8', 'title': '武汉市, 湖北省, 中华人民共和国天气预报和情况 - The Weather Channel', 'content': '武汉市, 湖北省, 中华人民共和国今日天气 ; 高/ 低. --/8° ; 大风. 6 公里/小时 ; 湿度. 91% ; 露点. 8° ; 气压. 1019.0 毫巴.', 'score': 0.82795376, 'raw_content': None}], 'response_time': 1.26}),
  AIMessage(content='根据查询结果,今天武汉的天气情况如下:\n\n- 实况:温度为16度并伴有雾,湿度达到95%,北风微弱(0级)。\n- 白天最高气温预计在27度左右,并且会有雾;夜间最低气温约为16度,天气以多云为主。\n\n墨迹天气建议您选择短袖上衣加七分裤的搭配,同时针织衫也是进出空调房时的好选择。请注意保暖和防风哦!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 105, 'prompt_tokens': 562, 'total_tokens': 667, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'qwen2.5:latest', 'system_fingerprint': 'fp_ollama', 'id': 'chatcmpl-631', 'finish_reason': 'stop', 'logprobs': None}, id='run-e64359cd-9122-40f3-86c5-719df507bc63-0', usage_metadata={'input_tokens': 562, 'output_tokens': 105, 'total_tokens': 667, 'input_token_details': {}, 'output_token_details': {}})]}

完整代码

from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph.message import add_messages
from langgraph.graph import StateGraph

# 1. 定义状态类
class State(TypedDict):
    messages:Annotated[list, add_messages]
# 2. 构建图    
graph_builder = StateGraph(State)

# 3.定义工具节点 
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_openai import ChatOpenAI

local_llm = ["deepseek-r1:8b","qwen2.5:latest"]

llm = ChatOpenAI(model=local_llm[1], temperature=0.0, api_key="ollama", base_url="http://localhost:11434/v1")

tool = TavilySearchResults(max_results=2)
tools = [tool]
llm_with_tools = llm.bind_tools(tools)

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

# 4. 把节点加入图 
# 第一个参数是节点名称,第二个是要调用的函数
from langgraph.prebuilt import ToolNode

graph_builder.add_node("chatbot", chatbot)

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

# 5. 把边加入图
from langgraph.prebuilt import tools_condition

graph_builder.add_conditional_edges(
    "chatbot",
    tools_condition,
)
graph_builder.add_edge("tools", "chatbot")
graph_builder.set_entry_point("chatbot")

graph = graph_builder.compile()

graph.invoke({"messages":"今天武汉天气怎么样"})

from IPython.display import Image, display
display(Image(graph.get_graph().draw_mermaid_png()))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值