基于LangGraph、Groq和Tavily打造可以调用外部搜索引擎工具的对话机器人(核心代码 万字详解)
一、python环境 & 相关库版本信息
代码运行在 conda
创建的python环境下,python和相关库的版本信息如下:
$ python --version
Python 3.12.3$ pip list | grep langchain
langchain 0.3.15
langchain-community 0.3.15
langchain-core 0.3.31
langchain-groq 0.2.3
langchain-text-splitters 0.3.5
langgraph 0.2.64
langgraph-checkpoint 2.0.10
langgraph-sdk 0.1.51
langsmith 0.2.11
导入需要用的库:
import os
import json
from langchain_community.tools.tavily_search import TavilySearchResults
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langchain_groq import ChatGroq
from langchain_core.messages import ToolMessage
(若不确定相关库是否有缺失,可以先运行,根据库缺失等报错信息进行 pip install
)
安装 langchain
:pip install langchain
安装 langgraph
:pip install -U langgraph
安装 langchain-groq
:pip install langchain-groq
二、申请 API Key
本次代码运行需要用到三个不同的 API Key:
- GROQ_API_KEY(用于借助groq云平台实现高效LLM推理):申请页面
- TAVILY_API_KEY(专为大型语言模型(LLMs)和检索增强生成(RAG)应用设计的搜索引擎,在本次代码示例中作为LLM可使用的Tool):获取页面
- LANGCHAIN_API_KEY(为了使用LangSmith需要用到,LangSmith可以帮助我们清晰直观地跟踪搭建的LangGraph的每一次状态变化过程):申请页面
获取完以上 API Key 之后,可以将它们统一放在代码同级的 .env
文件中进行管理,并使用 python-dotenv
库在运行时加载这些环境变量。因为我使用该方法时有点问题,所以我还是直接把环境变量写在代码里了。
因为我们要使用LangSmith用于调试和跟踪,涉及到另外一个环境变量 LANGCHAIN_TRACING_V2,默认为 false
,设置为 true
就可以开启跟踪功能。
os.environ["TAVILY_API_KEY"] = "TAVILY_API_KEY"
os.environ["LANGCHAIN_API_KEY"] = "LANGCHAIN_API_KEY" # Get this from smith.langchain.com
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["GROQ_API_KEY"] = "GROQ_API_KEY"
三、Graph功能描述
Graph图如下所示:
我们主要想实现的功能就是,用户可以不断提问,LLM将根据用户提问内容判断是否需要使用作为外部搜索引擎的tavily API服务获取更多信息,若需要则结合这些信息对用户提问做出回答。
Graph处理逻辑流程描述
- 用户输入问题;
chatbot
节点接收用户输入并更新 Graph 的状态信息;- 通过
conditional edge
,根据相应的代码逻辑判断 Graph 的下一步操作,决定是进入tools
节点调用工具,还是直接跳转至END
节点结束流程; - 如果进入
tools
节点,则执行工具调用逻辑,通过调用 Tavily API 服务进行联网检索。在获取返回结果后,更新 Graph 的状态信息,然后通过与chatbot
节点之间的有向边返回chatbot
; chatbot
接收更新后的状态信息,作为 LLM 的输入进行推理。如果推理结果不需要再次调用工具,则通过conditional edge
判断后直接进入END
节点;否则继续执行,直到流程到达END
节点,完成一次 Graph 的迭代。
四、核心代码详解
以下代码全部来自 Langgraph 官方教程文档 。
1. 定义用于保存graph状态的数据结构
首先,创建一个继承自 TypedDict
类的子类 State
,且 State
类定义了一个 messages
键,用于定义描述graph状态的数据结构。messages
键的类型被指定为 list
,意味着该键的值是一个列表,用于存储Graph状态变化时产生的消息。Annotated
则是用来为 messages
类型(列表)添加额外的元数据 add_messages
,以此控制 messages
键的列表值的更新方式为追加而不是默认的覆盖。即当graph的状态,也就是消息列表被更新时,上一次的消息列表不会被新产生的消息列表所覆盖,而是会将新消息追加到上一次的消息列表中,以达到每一次graph流迭代时,所有产生的状态都会保存在消息列表中。
# 定义状态
class State(TypedDict):# Messages have the type "list". The `add_messages` function# in the annotation defines how this state key should be updated# (in this case, it appends messages to the list, rather than overwriting them)messages: Annotated[list, add_messages]
2. 定义 tool 节点
2.1 创建工具列表
# 创建tavily搜索引擎工具,max_results用来设置返回检索结果的数量
tool = TavilySearchResults(max_results=2)
# 放入列表中
tools = [tool]
2.2 定义tool节点类
该类的主要作用是执行上一个 AI 消息中请求的工具,并返回执行结果。
class BasicToolNode:"""A node that runs the tools requested in the last 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("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}
更为详细的代码解释如下(ChatGPT生成):
BasicToolNode
类是一个工具节点,它的主要作用是执行上一个 AI 消息中请求的工具,并返回执行结果。下面是对类中每个部分的详细解释:
类的总体作用:该类的核心功能是接收输入信息(通常是包含工具请求的消息),找到对应的工具,执行工具,然后将执行结果以消息的形式返回。它主要处理与工具相关的操作,通过调用不同的工具来完成任务。
__init__
方法:
- 构造函数
__init__(self, tools: list)
用于初始化BasicToolNode
实例。 tools: list
是传递给该类的一个工具列表。每个工具都有一个name
属性(工具的名称),并且可以通过invoke()
方法被调用。self.tools_by_name
是一个字典,将工具名称与工具对象映射在一起,方便后续根据名称快速查找对应的工具。
self.tools_by_name = {tool.name: tool for tool in tools}
- 例如,如果有一个名为
calculator
的工具,它就会被存储为{"calculator": calculator}
,以便根据名称查找工具并调用。
__call__
方法:
- 该方法允许将类实例当作函数来调用。接收一个字典
inputs
作为参数,执行工具请求,并返回工具执行结果。
具体流程如下:
一、获取消息:
if messages := inputs.get("messages", []):message = messages[-1]
else:raise ValueError("No message found in input")
- 首先检查
inputs
字典中是否有键"messages"
。如果存在,则提取messages
列表的最后一条消息(即最近的一条AIMessage
)。 - 如果找不到消息,则抛出错误
ValueError("No message found in input")
,防止后续操作出错。
二、执行工具请求:
for tool_call in message.tool_calls:tool_result = self.tools_by_name[tool_call["name"]].invoke(tool_call["args"])
- 该消息包含了
tool_calls
,即消息中请求的工具列表。tool_calls
列表中的每一项代表一次工具调用,里面有工具名称name
和调用参数args
。 - 根据工具名称
tool_call["name"]
,从self.tools_by_name
中找到相应的工具,并调用它的invoke
方法,将调用参数args
传递给工具。
三、保存工具执行结果:
outputs.append(ToolMessage(content=json.dumps(tool_result),name=tool_call["name"],tool_call_id=tool_call["id"],)
)
工具执行完成后,将结果存储到 outputs
列表中。每个执行结果通过 ToolMessage
封装,包含以下内容:
content
:工具的执行结果,使用json.dumps()
将其转换为 JSON 格式。name
:工具名称,用于标识是哪一个工具产生的结果。tool_call_id
:工具调用的唯一 ID,用于追踪工具调用。
四、返回结果:
return {"messages": outputs}
最后,返回一个字典 {"messages": outputs}
,其中 messages
是包含所有工具执行结果的列表。
总结:
BasicToolNode
类的作用是接收带有工具请求的消息,提取出工具请求后调用相应的工具执行操作,并将执行结果打包为消息返回。- 它可以被视为一个工具执行器,自动根据消息中的请求调用工具,并返回相应的结果。
2.3 创建 tool 节点类的对象
# define a tool node
tool_node = BasicToolNode(tools=tools)
3. 定义 chatbot 节点
3.1 创建绑定工具列表的LLM
使用的模型是 mixtral-8x7b-32768
,如果想尝试其他模型,可以在 Groq 官网提供的模型列表 中进行选择。llm
调用的 bind_tools()
函数是可以将一组工具与 llm
进行绑定关联,以此扩展语言模型的功能,使llm
在对话或推理过程中,可以根据上下文选择合适的工具并调用它们。
llm = ChatGroq(temperature=0.8, model_name="mixtral-8x7b-32768")
llm_with_tools = llm.bind_tools(tools)
3.2 定义 chatbot 节点
chatbot 节点的定义比较简洁,就是一个调用LLM进行推理并将生成内容以State数据格式返回的函数。
def chatbot(state: State):llm_response = llm_with_tools.invoke(state["messages"])return {"messages": [llm_response]}
4. 定义 conditional edge
当graph流走到chatbot节点,并执行完成后,将根据 route_tools
函数作为conditional edge决定下一步去向。
def route_tools(state: State,
):"""Use in the conditional_edge to route to the ToolNode if the last messagehas tool calls. Otherwise, route to the end."""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
更为详细的代码解释如下(ChatGPT生成):
route_tools
函数的作用是决定工作流的路由方向:当收到一条消息时,检查其中是否有工具调用(tool_calls
)。如果有工具调用,路由到工具节点;否则,路由到结束(END
)。
一、函数参数:
state: State
:传递进来的state
是一个状态对象,通常包含有关当前对话或任务的上下文信息,尤其是消息历史等。这个state
可以是一个list
或者是一个包含messages
的字典。
二、函数逻辑概述:
- 该函数首先尝试从
state
中提取最近的一条消息(ai_message
)。 - 然后,检查该消息是否包含工具调用(
tool_calls
)。 - 如果发现有工具调用,则返回
"tools"
,表示应该转到工具节点执行工具;如果没有工具调用,则返回END
,表示任务已完成或不需要调用工具。
三、详细解释:
- 从
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}")
- 首先检查
state
是否是一个列表。如果是列表,直接取state
的最后一个元素作为最近的消息(ai_message = state[-1]
)。 - 如果
state
不是列表,则假定它是一个字典,并尝试获取键"messages"
对应的消息列表(messages := state.get("messages", [])
),同样从中提取最后一条消息messages[-1]
。 - 如果
state
既不是列表,也没有包含消息列表,则抛出一个错误,提示找不到有效的消息。
- 检查消息是否包含工具调用:
if hasattr(ai_message, "tool_calls") and len(ai_message.tool_calls) > 0:return "tools"
return END
- 一旦成功提取到
ai_message
,该部分逻辑检查这条消息中是否有tool_calls
属性。 - 如果消息中包含
tool_calls
属性并且它的长度大于 0(即有工具请求),则函数返回"tools"
,表示将工作流引导至工具节点执行工具。 - 如果没有工具调用,函数返回
END
,表示流程可以结束或不需要再进行工具调用。
route_tools
的主要功能是根据对话状态中的最后一条消息,检查是否存在工具调用,并动态路由到相应的节点。有工具调用时,路由到工具节点执行工具;没有工具调用时,路由到结束节点,表示任务不需要进一步的工具执行。
5. 创建并编译状态图
在定义好状态图所需的chatbot和tool这两个节点,以及路由到下一个节点的condition edge函数后,我们来构建Graph(代码注释由 ChatGPT 生成)。
# 创建一个状态图对象,初始状态为 State 类型
graph_builder = StateGraph(State)# 在状态图中添加一个 "chatbot" 节点
graph_builder.add_node("chatbot", chatbot)
# 在状态图中添加一个 "tools" 节点,表示调用工具的节点
graph_builder.add_node("tools", tool_node)# 定义初始边:状态图开始时,进入 "chatbot" 节点
graph_builder.add_edge(START, "chatbot")
# 在聊天机器人节点设置条件边,根据不同条件决定路由
graph_builder.add_conditional_edges("chatbot", # 源节点:聊天机器人节点route_tools, # 条件函数:用于判断是否需要调用工具{ # 条件函数的返回值与目标节点的映射"tools": "tools", # 如果 route_tools 返回 "tools",跳转到工具节点END: END # 如果 route_tools 返回 END,表示结束流程}
)
# 定义一个边:当工具节点完成任务后,返回到 "chatbot" 节点,以决定下一步操作
graph_builder.add_edge("tools", "chatbot")# 编译状态图,将节点、边和条件函数转换成可执行的状态机
graph = graph_builder.compile()
6. 用户无限问答实现
最后我们来实现上一次构建并编译好的graph的运行。
6.1 定义推理函数
stream_graph_updates
函数的主要作用是基于用户输入,通过状态图(graph
)进行推理并实时输出 AI 助手的对话回应,以实现一种流式对话交互的效果(代码注释由 ChatGPT 生成)。
def stream_graph_updates(user_input: str):"""根据用户输入更新对话状态,并实时流式输出 AI 助手的回应。参数:user_input (str): 用户输入的消息内容,作为对话的开始。返回:None: 通过打印助手的响应实时输出消息。"""# 调用状态图的 stream 方法,将用户输入传递给图进行处理,并获取对话事件的流式更新for event in graph.stream({"messages": [{"role": "user", "content": user_input}]}):# 遍历每个事件中的所有值。一个事件可能包含多个值(例如多个工具调用或消息)for value in event.values():# 取出最新一条助手的回复消息,并输出内容# messages 是一个列表,[-1] 表示列表中的最后一条消息(最新消息)# content 是消息的实际文本内容print("Assistant:", value["messages"][-1].content)
6.2 实现用户无限输入模式
我们可以让这个对话无限进行下去,只到用户输入 “quit”, “exit”, “q” 中的任何一个字符串以终止对话。
while True:try:user_input = input("User: ")if user_input.lower() in ["quit", "exit", "q"]:print("Goodbye!")breakstream_graph_updates(user_input)except:# fallback if input() is not availableuser_input = "What do you know about LangGraph?"print("User: " + user_input)stream_graph_updates(user_input)break
五、效果演示
因为 stream_graph_updates
做了循环打印,所以每到一个节点产生的新的message都会被打印出来。
User: 英伟达现在的股价是多少?
Assistant:
Assistant: [{"url": "https://companiesmarketcap.com/nvidia/marketcap/", "content": "Market cap history of NVIDIA from 2001 to 2023\nEnd of year Market Cap\nEnd of Day market cap according to different sources\nOn Dec 16th, 2023 the market cap of NVIDIA was reported to be:\nMarket capitalization for similar companies or competitors\nThe market capitalization sometimes referred as Marketcap, is the value of a publicly listed company.\n The market capitalization, commonly called market cap, is the total market value of a publicly traded company's outstanding shares and is commonly used to measure how much a company is worth.\n Market capitalization of NVIDIA (NVDA)\nMarket cap: $1.207 Trillion\nAs of December 2023 NVIDIA has a market cap of $1.207 Trillion.\n CompaniesMarketCap is receiving financial compensation for Delta App installs.\nCompaniesMarketCap is not associated in any way with CoinMarketCap.com\nStock prices are delayed, the delay can range from a few minutes to several hours.\n In January 1999, Nvidia was included in the NASDAQ (NVDA) and delivered the ten millionth graphics chip in the same year."}, {"url": "https://stockanalysis.com/stocks/nvda/market-cap/", "content": "NVIDIA has a market cap or net worth of $3.37 trillion as of January 17, 2025. Its market cap has increased by 187.03% in one year. Market Cap 3.37T. Enterprise Value ... Market capitalization, also called net worth, is the total value of all of a company's outstanding shares. It is calculated by multiplying the stock price by the number of"}]
Assistant: Nvidia's market cap was reported to be $1.207 trillion on December 16th, 2023, according to CompaniesMarketCap. However, as of January 17, 2025, NVIDIA has a market cap or net worth of $3.37 trillion, according to StockAnalysis. Therefore, the market cap of NVIDIA has significantly increased by 187.03% in one year.
我们最终要看的是最后一个输出内容,这是 LLM 基于tavily搜索引擎工具拿到的返回内容生成的回复。(根据回复内容可以看出,tavily索引到的结果也不是那么实时)
Assistant: Nvidia's market cap was reported to be $1.207 trillion on December 16th, 2023, according to CompaniesMarketCap. However, as of January 17, 2025, NVIDIA has a market cap or net worth of $3.37 trillion, according to StockAnalysis. Therefore, the market cap of NVIDIA has significantly increased by 187.03% in one year.
相关阅读
[1] 又一AI搜索引擎开源了
以上就是本篇博客全部内容,大家一起玩起来吧!用 LangGraph 开发出更多有趣实用的LLM智能体。
PS:关于 LangSmith 的使用我将在后续的博客中介绍,大家感兴趣可以自行了解或关注follow后续更新。
相关文章:

基于LangGraph、Groq和Tavily打造可以调用外部搜索引擎工具的对话机器人(核心代码 万字详解)
一、python环境 & 相关库版本信息 代码运行在 conda 创建的python环境下,python和相关库的版本信息如下: $ python --version Python 3.12.3$ pip list | grep langchain langchain 0.3.15 langchain-community 0.3.15 lang…...

衡量算法性能的量级标准:算法复杂度
今天开始数据结构的学习!作为一大重点,拿出态度很重要,想要真实掌握,博客笔记自然少不了!重点全部上色!避免疏忽 下面我们从0基础开始学习今天的第一节!不用担心看不懂,拒绝枯燥的理…...

PHP校园助手系统小程序
🔑 校园助手系统 —— 智慧校园生活 📱一款基于ThinkPHPUniapp框架深度定制的校园助手系统,犹如一把智慧之钥,专为校园团队精心打造,解锁智慧校园生活的无限精彩。它独家适配微信小程序,无需繁琐的下载与安…...

如何在Spring Boot项目中高效集成Spring Security
1 Spring Security 介绍 Spring Security 是一个功能强大且高度可定制的安全框架,专为保护基于Java的应用程序而设计。它不仅提供了认证(Authentication)和授权(Authorization)的功能,还支持防止各种常见的安全攻击模式。本文将详细介绍Spring Security的主要特点、功能…...

【PostgreSQL内核学习 —— (WindowAgg(一))】
WindowAgg 窗口函数介绍WindowAgg理论层面源码层面WindowObjectData 结构体WindowStatePerFuncData 结构体WindowStatePerAggData 结构体eval_windowaggregates 函数update_frameheadpos 函数 声明:本文的部分内容参考了他人的文章。在编写过程中,我们尊…...

PAT甲级-1020 Tree Traversals
题目 题目大意 给出一棵树的后序遍历和中序遍历,要求输出该树的层序遍历。 思路 非常典型的树的构建与遍历问题。后序遍历和中序遍历可以得出一个树的结构,用递归锁定根节点,然后再遍历左右子树,我之前发过类似题目的博客&…...

LVGL+FreeRTOS实战项目:智能健康助手(Max30102篇)
MAX30102 心率血氧模块简介 功能:用于检测心率和血氧饱和度,集成了红外和红光 LED 以及光电二极管。 接口:支持 I2C 通信,默认 I2C 地址为 0x57。 应用:广泛用于健康监测设备中,如智能手环、手表等。 硬…...

人脸识别【python-基于OpenCV】
1. 导入并显示图片 #导入模块 import cv2 as cv#读取图片 imgcv.imread(img/wx(1).jpg) #路径名为全英文,出现中文 图片加载失败,"D:\picture\wx.jpg" #显示图片 (显示标题,显示图片对象) cv.imshow(read_picture,im…...

redis常用命令和内部编码
文章目录 redis 为什么快redis中的Stringsetsetnxsetex getmsetmget计数操作incr、incrby、decr、decrby、incrbyfloatincrincrbyincrbyfloat 拼接(append)、获取(getrange)、修改字符串(setrange)、获取字符串长度(strlen)操作appendgetrangesetrangest…...

UI操作总结
该类 SolarWebx 继承自 Webx 和 IUixLikeMixin,主要用于扩展 giraffe.EasyUILibrary 的功能,提供了一系列与网页操作、元素定位、截图、图片处理等相关的方法。以下是对该类中每个方法的简要总结: __init__ 方法 作用:初始化 Sola…...

数据结构——实验八·学生管理系统
嗨~~欢迎来到Tubishu的博客🌸如果你也是一名在校大学生,正在寻找各种编程资源,那么你就来对地方啦🌟 Tubishu是一名计算机本科生,会不定期整理和分享学习中的优质资源,希望能为你的编程之路添砖加瓦⭐&…...

力扣hot100-->滑动窗口、贪心
你好呀,欢迎来到 Dong雨 的技术小栈 🌱 在这里,我们一同探索代码的奥秘,感受技术的魅力 ✨。 👉 我的小世界:Dong雨 📌 分享我的学习旅程 🛠️ 提供贴心的实用工具 💡 记…...

Linux 内核中的高效并发处理:深入理解 hlist_add_head_rcu 与 NAPI 接口
在 Linux 内核的开发中,高效处理并发任务和数据结构的管理是提升系统性能的关键。特别是在网络子系统中,处理大量数据包的任务对性能和并发性提出了极高的要求。本文将深入探讨 Linux 内核中的 hlist_add_head_rcu 函数及其在 NAPI(网络接收处理接口)中的应用,揭示这些机制…...

centos哪个版本建站好?centos最稳定好用的版本
在信息化飞速发展的今天,服务器操作系统作为构建网络架构的基石,其稳定性和易用性成为企业和个人用户关注的重点。CentOS作为一款广受欢迎的开源服务器操作系统,凭借其强大的性能、出色的稳定性和丰富的软件包资源,成为众多用户建…...

软件越跑越慢的原因分析
如果是qt软件,可以用Qt Creator Profiler 作性能监控如果是通过web请求,可以用JMeter监控。 软件运行过程中逐渐变慢的现象,通常是因为系统资源(如 CPU、内存、磁盘 I/O 等)逐渐被消耗或软件中存在性能瓶颈。这个问题…...

LeetCode 力扣热题100 二叉树的直径
class Solution { public:// 定义一个变量 maxd,用于存储当前二叉树的最大直径。int maxd 0; // 主函数,计算二叉树的直径。int diameterOfBinaryTree(TreeNode* root) {// 调用 maxDepth 函数进行递归计算,并更新 maxd。maxDepth(root);// …...

【图文详解】lnmp架构搭建Discuz论坛
安装部署LNMP 系统及软件版本信息 软件名称版本nginx1.24.0mysql5.7.41php5.6.27安装nginx 我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客: 关闭防火墙 systemctl stop firewalld &&a…...

小哆啦解题记:整数转罗马数字
小哆啦解题记:整数转罗马数字 小哆啦开始力扣每日一题的第十四天 https://leetcode.cn/problems/integer-to-roman/submissions/595220508/ 第一章:神秘的任务 一天,哆啦A梦接到了一项任务——将一个整数转换为罗马数字。他心想:…...

【Java数据结构】排序
【Java数据结构】排序 一、排序1.1 排序的概念1.2 排序的稳定性1.3 内部排序和外部排序1.3.1 内部排序1.3.2 外部排序 二、插入排序2.1 直接插入排序2.2 希尔排序 三、选择排序3.1 选择排序3.2 堆排序 四、交换排序4.1 冒泡排序4.2 快速排序Hoare法:挖坑法ÿ…...

我的求职之路合集
我把我秋招和春招的一些笔面试经验在这里发一下,网友们也可以参考一下。 我的求职之路:(1)如何谈自己的缺点 我的求职之路:(2)找工作时看重的点 我的求职之路:(3&…...

数据结构(四) B树/跳表
目录 1. LRU 2. B树 3. 跳表 1. LRU: 1.1 概念: 最近最少使用算法, 就是cache缓存的算法. 因为cache(位于内存和cpu之间的存储设备)是一种容量有限的缓存, 有新的数据进入就需要将原本的数据进行排出. 1.2 LRU cache实现: #include <iostream> #include <list>…...

Arcgis国产化替代:Bigemap Pro正式发布
在数字化时代,数据如同新时代的石油,蕴含着巨大的价值。从商业决策到科研探索,从城市规划到环境监测,海量数据的高效处理、精准分析与直观可视化,已成为各行业突破发展瓶颈、实现转型升级的关键所在。历经十年精心打磨…...

LBS 开发微课堂|AI向导接口服务:重塑用户的出行体验
为了让广大开发者 更深入地了解 百度地图开放平台的 技术能力 轻松掌握满满的 技术干货 更加简单地接入 位置服务 我们特别推出了 “位置服务(LBS)开发微课堂” 系列技术案例 第六期的主题是 《AI向导接口服务的能力与接入方案》 随着地图应…...

AI导航工具我开源了利用node爬取了几百条数据
序言 别因今天的懒惰,让明天的您后悔。输出文章的本意并不是为了得到赞美,而是为了让自己能够学会总结思考;当然,如果有幸能够给到你一点点灵感或者思考,那么我这篇文章的意义将无限放大。 背景 随着AI的发展市面上…...

openstack单机安装
openstack单机安装 网卡配置安装依赖开启虚拟环境修改配置文件 部署openstack部署openstack客户端访问可视化界面Horizon补充 本篇主要讲述Ubuntu2204单机安装openstackstable/2024.2。其他版本的Linux系统或者openstack版本,请参考openstack官网。 网卡配置 需要配…...

Vue3实现小红书瀑布流布局任意组件动态更新页面方法实践
思路 1.首先定义一个瀑布流容器,它的高度暂定(后面会更新)。把需要布局的组件(这里叫做waterfall-item)放在瀑布流容器里面渲染出来。使用绝对定位(position: absolute),把它移到屏幕…...

深度学习项目--基于LSTM的糖尿病预测探究(pytorch实现)
🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 前言 LSTM模型一直是一个很经典的模型,一般用于序列数据预测,这个可以很好的挖掘数据上下文信息,本文将使用LSTM进行糖尿病…...

Next.js 实战 (十):中间件的魅力,打造更快更安全的应用
什么是中间件? 在 Next.js 中,中间件(Middleware)是一种用于处理每个传入请求的功能。它允许你在请求到达页面之前对其进行修改或响应。 通过中间件,你可以实现诸如日志记录、身份验证、重定向、CORS配置、压缩等任务…...

python+playwright自动化测试(四):元素操作(键盘鼠标事件)、文件上传
目录 鼠标事件 悬停 移动 按键 点击 滚轮操作 拖拽 键盘事件 输入文本内容 type输入内容 fill输入内容 按键操作press 文件上传 下拉选/单选框/复选框 滚动条操作 鼠标事件 悬停 page.get_by_text(设置,exactTrue).nth(1).hover() 移动 page.mouse.move(x33…...

【论文+源码】Diffusion-LM 改进了可控文本生成
这篇论文探讨了如何在不重新训练的情况下控制语言模型(LM)的行为,这是自然语言生成中的一个重大开放问题。尽管近期一些研究在控制简单句子属性(如情感)方面取得了成功,但在复杂的细粒度控制(如…...