LangGraph实战:从零分阶打造人工智能航空客服助手
❝
通过本指南,你将学习构建一个专为航空公司设计的客服助手,它将协助用户查询旅行信息并规划行程。在此过程中,你将掌握如何利用LangGraph的中断机制、检查点技术以及更为复杂的状态管理功能,来优化你的助手工具,同时有效管理用户的机票预订、酒店住宿、租车服务以及旅游活动。
客服助手机器人能够帮助团队更高效地处理日常咨询,但要打造一个能够稳定应对各种任务且不会让用户感到烦恼的机器人并非易事。
完成本教程后,你不仅会拥有一个功能完备的机器人,还将深入理解LangGraph的核心理念和架构设计。这些知识将帮助你在其他人工智能项目中运用相似的设计模式。
由于内容较多,本文将由浅入深,分四个阶段进行讲解,每个阶段都将打造出一个具备以上描述所有能力的机器人。但受限于LLM的能力,初期阶段的机器人的运行可能存在各类问题,但都将在后续阶段得到解决。
你最终完成的聊天机器人将类似于以下示意图:

最终示意图
现在,让我们开启这第一阶段段的学习之旅吧!
准备工作
在开始之前,我们需要搭建好环境。本教程将安装一些必要的先决条件,包括下载测试用的数据库,并定义一些在后续各部分中会用到的工具。
我们会使用 Claude 作为语言模型(LLM),并创建一些定制化的工具。这些工具大多数会连接到本地的 SQLite 数据库,无需额外依赖。此外,我们还会通过 Tavily 为代理提供网络搜索功能。
%%capture --no-stderr % pip install -U langgraph langchain-community langchain-anthropic tavily-python pandas
数据库初始化
接下来,执行下面的脚本来获取我们为这个教程准备的 SQLite 数据库,并更新它以反映当前的数据状态。具体细节不是重点。
import os import requests import sqlite3 import pandas as pd import shutil # 下载数据库文件 db_url = "https://storage.googleapis.com/benchmarks-artifacts/travel-db/travel2.sqlite" local_file = "travel2.sqlite" backup_file = "travel2.backup.sqlite" overwrite = False if not os.path.exists(local_file) or overwrite: response = requests.get(db_url) response.raise_for_status() # 确保请求成功 with open(local_file, "wb") as file: file.write(response.content) # 创建数据库备份,以便在每个教程部分开始时重置数据库状态 shutil.copy(local_file, backup_file) # 将航班数据更新为当前时间,以适应我们的教程 conn = sqlite3.connect(local_file) cursor = conn.cursor() # 读取数据库中的所有表 tables = pd.read_sql( "SELECT name FROM sqlite_master WHERE type='table';", conn ).name.tolist() tdf = {} for table_name in tables: tdf[table_name] = pd.read_sql(f"SELECT * from {table_name}", conn) # 找到最早的出发时间,并计算时间差 example_time = pd.to_datetime( tdf["flights"]["actual_departure"].replace("\\N", pd.NaT) ).max() current_time = pd.to_datetime("now").tz_localize(example_time.tz) time_diff = current_time - example_time # 更新预订日期和航班时间 for column in ["book_date", "scheduled_departure", "scheduled_arrival", "actual_departure", "actual_arrival"]: tdf["flights"][column] = pd.to_datetime( tdf["flights"][column].replace("\\N", pd.NaT) ) + time_diff # 将更新后的数据写回数据库 for table_name, df in tdf.items(): df.to_sql(table_name, conn, if_exists="replace", index=False) conn.commit() conn.close() # 在本教程中,我们将使用这个本地文件作为数据库 db = local_file
工具定义
现在,我们来定义一些工具,以便助手可以搜索航空公司的政策手册,以及搜索和管理航班、酒店、租车和远足活动的预订。这些工具将在教程的各个部分中重复使用,具体的实现细节不是关键。
查询公司政策
助手需要检索政策信息来回答用户的问题。请注意,这些政策的实施还需要在工具或 API 中进行,因为语言模型可能会忽略这些信息。以下工具受限于篇幅将仅提供定义及描述,详细代码[1]可在github上获取。
import re import numpy as np import openai from langchain_core.tools import tool @tool def lookup_policy(query): """查询公司政策,以确定某些选项是否允许。"""
航班管理
定义一个工具来获取用户的航班信息,然后定义一些工具来搜索航班和管理用户的预订信息,这些信息存储在 SQL 数据库中。
我们使用 ensure_config 来通过配置参数传递 passenger_id。语言模型不需要显式提供这些信息,它们会在图的每次调用中提供,以确保每个用户无法访问其他乘客的预订信息。
from langchain_core.runnables import ensure_config from typing import Optional import sqlite3 import pytz from datetime import datetime, timedelta, date @tool def fetch_user_flight_information(): """获取用户的所有机票信息,包括航班详情和座位分配。""" @tool def search_flights( departure_airport=None, arrival_airport=None, start_time=None, end_time=None, limit=20, ): """根据出发机场、到达机场和出发时间范围来搜索航班。""" @tool def update_ticket_to_new_flight(ticket_no, new_flight_id): """将用户的机票更新到一个新的有效航班上。""" @tool def cancel_ticket(ticket_no): """取消用户的机票,并从数据库中移除。"""
租车服务
用户预订了航班后,可能需要租车服务。定义一些工具,让用户能够在目的地搜索和预订汽车。
from typing import Optional, Union from datetime import datetime, date @tool def search_car_rentals( location=None, name=None, price_tier=None, start_date=None, end_date=None, ): """ 根据位置、公司名称、价格等级、开始日期和结束日期来搜索租车服务。 参数: location (Optional[str]): 租车服务的位置。 name (Optional[str]): 租车公司的名称。 price_tier (Optional[str]): 租车的价格等级。 start_date (Optional[Union[datetime, date]]): 租车的开始日期。 end_date (Optional[Union[datetime, date]]): 租车的结束日期。 返回: list[dict]: 匹配搜索条件的租车服务列表。 """ @tool def book_car_rental(rental_id): """ 通过租车ID来预订租车服务。 参数: rental_id (int): 要预订的租车服务的ID。 返回: str: 预订成功与否的消息。 """ @tool def update_car_rental( rental_id, start_date=None, end_date=None, ): """ 通过租车ID来更新租车服务的开始和结束日期。 参数: rental_id (int): 要更新的租车服务的ID。 start_date (Optional[Union[datetime, date]]): 新的租车开始日期。 end_date (Optional[Union[datetime, date]]): 新的租车结束日期。 返回: str: 更新成功与否的消息。 """ @tool def cancel_car_rental(rental_id): """ 通过租车ID来取消租车服务。 参数: rental_id (int): 要取消的租车服务的ID。 返回: str: 取消成功与否的消息。 """
酒店预订
用户需要住宿,因此定义一些工具来搜索和管理酒店预订。
@tool def search_hotels( location=None, name=None, price_tier=None, checkin_date=None, checkout_date=None, ): """ 根据位置、名称、价格等级、入住日期和退房日期来搜索酒店。 参数: location (Optional[str]): 酒店的位置。 name (Optional[str]): 酒店的名称。 price_tier (Optional[str]): 酒店的价格等级。 checkin_date # 入住日期和退房日期,用于搜索酒店 checkin_date (Optional[Union[datetime, date]]): 酒店的入住日期。 checkout_date (Optional[Union[datetime, date]]): 酒店的退房日期。 返回: list[dict]: 符合搜索条件的酒店列表。 """ @tool def book_hotel(hotel_id): """ 通过酒店ID进行预订。 参数: hotel_id (int): 要预订的酒店的ID。 返回: str: 预订成功与否的消息。 """ @tool def update_hotel( hotel_id, checkin_date=None, checkout_date=None, ): """ 通过酒店ID更新酒店预订的入住和退房日期。 参数: hotel_id (int): 要更新预订的酒店的ID。 checkin_date (Optional[Union[datetime, date]]): 新的入住日期。 checkout_date (Optional[Union[datetime, date]]): 新的退房日期。 返回: str: 更新成功与否的消息。 """ @tool def cancel_hotel(hotel_id): """ 通过酒店ID取消酒店预订。 参数: hotel_id (int): 要取消预订的酒店的ID。 返回: str: 取消成功与否的消息。 """
远足活动
最后,定义一些工具,让用户在到达目的地后搜索活动并进行预订。
@tool def search_trip_recommendations( location=None, name=None, keywords=None, ): """ 根据位置、名称和关键词搜索旅行推荐。 参数: location (Optional[str]): 旅行推荐的地点。 name (Optional[str]): 旅行推荐的名字。 keywords (Optional[str]): 与旅行推荐相关的关键词。 返回: list[dict]: 符合搜索条件的旅行推荐列表。 """ @tool def book_excursion(recommendation_id): """ 通过推荐ID预订远足活动。 参数: recommendation_id (int): 要预订的旅行推荐的ID。 返回: str: 预订成功与否的消息。 """ @tool def update_excursion(recommendation_id, details): """ 通过推荐ID更新旅行推荐的细节。 参数: recommendation_id (int): 要更新的旅行推荐的ID。 details (str): 旅行推荐的新细节。 返回: str: 更新成功与否的消息。 """ @tool def cancel_excursion(recommendation_id): """ 通过推荐ID取消旅行推荐。 参数: recommendation_id (int): 要取消的旅行推荐的ID。 返回: str: 取消成功与否的消息。 """
实用工具
定义一些辅助函数,以便在调试过程中美化图形中的消息显示,并为工具节点添加错误处理(通过将错误添加到聊天记录中)。
from langgraph.prebuilt import ToolNode from langchain_core.runnables import RunnableLambda def handle_tool_error(state): error = state.get("error") tool_calls = state["messages"][-1].tool_calls return { "messages": [ ToolMessage( content=f"错误: {repr(error)}\n请修正你的错误。", tool_call_id=tc["id"], ) for tc in tool_calls ] } def create_tool_node_with_fallback(tools): return ToolNode(tools).with_fallbacks( [RunnableLambda(handle_tool_error)], exception_key="error" ) def _print_event(event, _printed, max_length=1500): current_state = event.get("dialog_state") if current_state: print(f"当前状态: ", current_state[-1]) message = event.get("messages") if message: if isinstance(message, list): message = message[-1] if message.id not in _printed: msg_repr = message.pretty_repr(html=True) if len(msg_repr) > max_length: msg_repr = msg_repr[:max_length] + " ... (内容已截断)" print(msg_repr) _printed.add(message.id)
第一部分:零样本代理
在构建任何系统时,最佳实践是从最简单的可行方案开始,并通过使用类似LangSmith这样的评估工具来测试其有效性。在条件相同的情况下,我们倾向于选择简单且可扩展的解决方案,而不是复杂的方案。然而,单一图谱方法存在一些限制,比如机器人可能在未经用户确认的情况下执行不希望的操作,处理复杂查询时可能遇到困难,或者在回答时缺乏针对性。这些问题我们会在后续进行改进。 在这部分,我们将定义一个简单的零样本代理作为用户的助手,并将所有工具赋予给它。我们的目标是引导它明智地使用这些工具来帮助用户。 我们的简单两节点图如下所示:

第一部分图解
首先,我们定义状态。
状态
我们将StateGraph的状态定义为一个包含消息列表的类型化字典。这些消息构成了聊天的记录,也就是我们简单助手所需要的全部状态信息。
from langgraph.graph.message import add_messages, AnyMessage from typing_extensions import TypedDict from typing import Annotated class State(TypedDict): messages: Annotated[list[AnyMessage], add_messages]
代理
然后,我们定义助手函数。这个函数接收图的状态,将其格式化为提示,然后调用一个大型语言模型(LLM)来预测最佳的响应。
from langchain_core.runnables import Runnable, RunnableConfig from langchain_community.tools.tavily_search import TavilySearchResults from langchain_anthropic import ChatAnthropic from langchain_core.prompts import ChatPromptTemplate class Assistant: def __init__(self, runnable: Runnable): self.runnable = runnable def __call__(self, state: State, config: RunnableConfig): while True: passenger_id = config.get("passenger_id", None) state = {**state, "user_info": passenger_id} result = self.runnable.invoke(state) # 如果大型语言模型返回了一个空响应,我们将重新提示它给出一个实际的响应。 if ( not result.content or isinstance(result.content, list) and not result.content[0].get("text") ): messages = state["messages"] + [("user", "请给出一个真实的输出。")] state = {**state, "messages": messages} else: break return {"messages": result} # Haiku模型更快、成本更低,但准确性稍差 # llm = ChatAnthropic(model="claude-3-haiku-20240307") llm = ChatAnthropic(model="claude-3-sonnet-20240229", temperature=1) primary_assistant_prompt = ChatPromptTemplate.from_messages( [ ( "system", "你是一个为瑞士航空提供帮助的客户支持助手。" "使用提供的工具来搜索航班、公司政策和其他信息以帮助回答用户的查询。" "在搜索时,要有毅力。如果第一次搜索没有结果,就扩大你的查询范围。" "如果搜索结果为空,不要放弃,先扩大搜索范围。" "\n\n当前用户:\n<User>\n{user_info}\n</User>" "\n当前时间:{time}。", ), ("placeholder", "{messages}"), ] ).partial(time=datetime.now()) part_1_tools = [ TavilySearchResults(max_results=1), fetch_user_flight_information, search_flights, lookup_policy, update_ticket_to_new_flight, cancel_ticket, search_car_rentals, book_car_rental, update_car_rental, cancel_car_rental, search_hotels, book_hotel, update_hotel, cancel_hotel, search_trip_recommendations, book_excursion, update_excursion, cancel_excursion, ] part_1_assistant_runnable = primary_assistant_prompt | llm.bind_tools(part_1_tools)
定义图
现在,我们来创建图。这张图是我们这部分的最终助手。
from langgraph.checkpoint.sqlite import SqliteSaver from langgraph.graph import StateGraph, END from langgraph.prebuilt import tools_condition, ToolNode builder = StateGraph(State) # 定义节点:这些节点执行具体的工作 builder.add_node("assistant", Assistant(part_1_assistant_runnable)) builder.add_node("action", create_tool_node_with_fallback(part_1_tools)) # 定义边:这些边决定了控制流程如何移动 builder.set_entry_point("assistant") builder.add_conditional_edges( "assistant", tools_condition, # "action"调用我们的工具之一。END导致图终止(并向用户做出响应) {"action": "action", END: END}, ) builder.add_edge("action", "assistant") # 检查点器允许图保存其状态 # 这是整个图的完整记忆。 memory = SqliteSaver.from_conn_string(":memory:") part_1_graph = builder.compile(checkpointer=memory)
from IPython.display import Image, display try: display(Image(part_1_graph.get_graph(xray=True).draw_mermaid_png())) except: # 这需要一些额外的依赖项,是可选的 pass

示例对话
现在,让我们通过一系列对话示例来测试我们的聊天机器人。
import uuid import shutil # 假设这是用户与助手之间可能发生的对话示例 tutorial_questions = [ "你好,我的航班是什么时候?", "我可以把我的航班改签到更早的时间吗?我想今天晚些时候离开。", "那就把我的航班改签到下周某个时间吧", "下一个可用的选项很好", "住宿和交通方面有什么建议?", "我想在为期一周的住宿中选择一个经济实惠的酒店(7天),并且我还想租一辆车。", "好的,你能为你推荐的酒店预订吗?听起来不错。", "是的,去预订任何中等价位且有可用性的酒店。", "对于汽车,我有哪些选择?", "太棒了,我们只选择最便宜的选项。预订7天。", "那么,你对我的旅行有什么建议?", "在我在那里的时候,有哪些活动是可用的?", "有趣 - 我喜欢博物馆,有哪些选择?", "好的,那就为我在那里的第二天预订一个。", ] # 使用备份文件以便我们可以从每个部分的原始位置重新启动 shutil.copy(backup_file, db) thread_id = str(uuid.uuid4()) config = { "configurable": { # passenger_id 在我们的航班工具中使用 # 以获取用户的航班信息 "passenger_id": "3442 587242", # 检查点通过 thread_id 访问 "thread_id": thread_id, } } _printed = set() for question in tutorial_questions: events = part_1_graph.stream( {"messages": ("user", question)}, config, stream_mode="values" ) for event in events: _print_event(event, _printed)
第一部分回顾
我们的简单助手表现得还不错!它能够合理地回答所有问题,快速地在上下文中做出回应,并成功地执行了我们所有的任务。你可以通过查看LangSmith的示例跟踪[2]来更好地了解LLM在上述交互中是如何被提示的。
如果这是一个简单的问答机器人,我们可能会对上述结果感到满意。由于我们的客户支持机器人是代表用户采取行动,因此它的一些行为有点令人担忧:
-
当我们专注于住宿时,助手预订了一辆车,然后又不得不取消并稍后重新预订:哎呀!在预订之前,应该让用户有最终决定权,以避免不需要的预订。
-
助手在寻找推荐方面遇到了困难。我们可以通过添加更多的详细指令和使用工具的示例来改进这一点,但为每个工具都这样做可能会导致提示过长,让代理感到不知所措。
-
助手不得不进行明确的搜索才能获取用户的相关信息。我们可以通过立即获取用户的旅行详细信息,让助手能够直接回应,从而节省大量时间。
如何学习大模型 AI ?
由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

第一阶段(10天):初阶应用
该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。
- 大模型 AI 能干什么?
- 大模型是怎样获得「智能」的?
- 用好 AI 的核心心法
- 大模型应用业务架构
- 大模型应用技术架构
- 代码示例:向 GPT-3.5 灌入新知识
- 提示工程的意义和核心思想
- Prompt 典型构成
- 指令调优方法论
- 思维链和思维树
- Prompt 攻击和防范
- …
第二阶段(30天):高阶应用
该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。
- 为什么要做 RAG
- 搭建一个简单的 ChatPDF
- 检索的基础概念
- 什么是向量表示(Embeddings)
- 向量数据库与向量检索
- 基于向量检索的 RAG
- 搭建 RAG 系统的扩展知识
- 混合检索与 RAG-Fusion 简介
- 向量模型本地部署
- …
第三阶段(30天):模型训练
恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。
到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?
- 为什么要做 RAG
- 什么是模型
- 什么是模型训练
- 求解器 & 损失函数简介
- 小实验2:手写一个简单的神经网络并训练它
- 什么是训练/预训练/微调/轻量化微调
- Transformer结构简介
- 轻量化微调
- 实验数据集的构建
- …
第四阶段(20天):商业闭环
对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。
- 硬件选型
- 带你了解全球大模型
- 使用国产大模型服务
- 搭建 OpenAI 代理
- 热身:基于阿里云 PAI 部署 Stable Diffusion
- 在本地计算机运行大模型
- 大模型的私有化部署
- 基于 vLLM 部署大模型
- 案例:如何优雅地在阿里云私有部署开源大模型
- 部署一套开源 LLM 项目
- 内容安全
- 互联网信息服务算法备案
- …
学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。
如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。
这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】

相关文章:
LangGraph实战:从零分阶打造人工智能航空客服助手
❝ 通过本指南,你将学习构建一个专为航空公司设计的客服助手,它将协助用户查询旅行信息并规划行程。在此过程中,你将掌握如何利用LangGraph的中断机制、检查点技术以及更为复杂的状态管理功能,来优化你的助手工具,同时…...
R可视化:R语言基础图形合集
R语言基础图形合集 欢迎大家关注全网生信学习者系列: WX公zhong号:生信学习者Xiao hong书:生信学习者知hu:生信学习者CDSN:生信学习者2 基础图形可视化 数据分析的图形可视化是了解数据分布、波动和相关性等属性必…...
mysql导入sql文件失败及解决措施
1.报错找不到表 1.1 原因 表格创建失败,编码问题mysql8相较于mysql5出现了新的编码集 1.2解决办法: 使用vscode打开sql文件ctrlh,批量替换,替换到你所安装mysql支持的编码集。 2.timestmp没有设置默认值 Error occured at:20…...
JS:获取鼠标点击位置
一、获取鼠标在目标元素中的点击位置 getClickPos.ts: export const getClickPos (e: MouseEvent) > {return {x: e.offsetX,y: e.offsetY,}; };二、获取鼠标在页面中的点击位置 getClickPos.ts: export const getPageClickPos (e: MouseEvent) > {return {x: e.pa…...
使用开源的zip.cpp和unzip.cpp实现压缩包的创建与解压(附源码)
目录 1、使用场景 2、压缩包的创建 3、压缩包的解压 4、CloseZipZ和CloseZipU两接口的区别...
npm 异常:peer eslint@“>=1.6.0 <7.0.0“ from eslint-loader@2.2.1
node 用16版本 npm install npm6.14.15 -g将版本降级到6...
Docker|了解容器镜像层(2)
引言 容器非常神奇。它们允许简单的进程表现得像虚拟机。在这种优雅的底层是一组模式和实践,最终使一切运作起来。在设计的根本是层。层是存储和分发容器化文件系统内容的基本方式。这种设计既出人意料地简单,同时又非常强大。在今天的帖子[1]中…...
使用Python爬取temu商品与评论信息
【🏠作者主页】:吴秋霖 【💼作者介绍】:擅长爬虫与JS加密逆向分析!Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python与爬虫领域研究与开发工作! 【&…...
mybatis学习--自定义映射resultMap
1.1、resultMap处理字段和属性的映射关系 如果字段名和实体类中的属性名不一致的情况下,可以通过resultMap设置自定义映射。 常规写法 /***根据id查询员工信息* param empId* return*/ Emp getEmpByEmpId(Param("empId") Integer empId);<select id…...
Elasticsearch之写入原理以及调优
1、ES 的写入过程 1.1 ES支持四种对文档的数据写操作 create:如果在PUT数据的时候当前数据已经存在,则数据会被覆盖,如果在PUT的时候加上操作类型create,此时如果数据已存在则会返回失败,因为已经强制指定了操作类型…...
python中装饰器的用法
最近发现装饰器是一个非常有意思的东西,很高级! 允许你在不修改函数或类的源代码的情况下,为它们添加额外的功能或修改它们的行为。装饰器本质上是一个接受函数作为参数的可调用对象(通常是函数或类),并返…...
php实现一个简单的MySQL分页
一、案例演示: 二、php 代码 <?php $servername "localhost"; // MySQL服务器名称或IP地址 $username "root"; // MySQL用户名 $password "123456"; // MySQL密码 $dbname "test"; // 要连接…...
算法训练营day23补签
题目1:530. 二叉搜索树的最小绝对差 - 力扣(LeetCode) class Solution { public:int reslut INT_MAX;TreeNode* pre NULL;void trackingback(TreeNode* node) {if(node NULL) return;trackingback(node->left);if(pre ! NULL) {reslut…...
国密SM2JS加密后端解密
1.前端加密 前端加密开源库 sm-crypto 1.1 传统web,下载 sm-crypto 进行打包为 dist/sm2.js 相关打包命令 npm install --save sm-crypto npm install npm run prepublish在web页面引用打包后的文件 <script type"text/javascript" src"<%path %>…...
Cheat Engine.exe修改植物大战僵尸阳光与冷却
Cheat Engine.exe修改植物大战僵尸阳光与冷却 打开Cheat Engine.exe和植物大战僵尸,点CE中文件下面红框位置,选择植物大战僵尸,点击打开 修改冷却: 等冷却完毕,首次扫描0安放植物,再次扫描变动值等冷却完…...
python内置模块之queue(队列)用法
queue是python3的内置模块,创建堆栈队列,用来处理多线程通信,队列对象构造方法如下: queue.Queue(maxsize0) 是先进先出(First In First Out: FIFO)队列。 入参 maxsize 是一个整数,用于设置…...
Spring Security——结合JWT实现令牌的验证与授权
目录 JWT(JSON Web Token) 项目总结 新建一个SpringBoot项目 pom.xml PayloadDto JwtUtil工具类 MyAuthenticationSuccessHandler(验证成功处理器) JwtAuthenticationFilter(自定义token过滤器) W…...
Vector的底层结构剖析
vector的介绍: 1.Vector实现了List接口的集合。 2.Vector的底层也是一个数组,protected Object[] elementData; 3.Vector 是线程同步的,即线程安全,Vector类的操作方法带有Synchronized. 4.在开发中,需要线程同步时࿰…...
实现抖音视频滑动功能vue3+swiper
首先,你需要安装和引入Swiper库。可以使用npm或者yarn进行安装。 pnpm install swiper然后在Vue组件中引入Swiper库和样式。 // 导入Swiper组件和SwiperSlide组件,用于创建轮播图 import {Swiper, SwiperSlide } from swiper/vue; // 导入Swiper的CSS样式,确保轮播图的正确…...
Linux文件系统【真的很详细】
目录 一.认识磁盘 1.1磁盘的物理结构 1.2磁盘的存储结构 1.3磁盘的逻辑存储结构 二.理解文件系统 2.1如何管理磁盘 2.2如何在磁盘中找到文件 2.3关于文件名 哈喽,大家好。今天我们学习文件系统,我们之前在Linux基础IO中研究的是进程和被打开文件…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
Ascend NPU上适配Step-Audio模型
1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤)&#x…...
重启Eureka集群中的节点,对已经注册的服务有什么影响
先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...
安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
Unity UGUI Button事件流程
场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...
十九、【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建
【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建 前言准备工作第一部分:回顾 Django 内置的 `User` 模型第二部分:设计并创建 `Role` 和 `UserProfile` 模型第三部分:创建 Serializers第四部分:创建 ViewSets第五部分:注册 API 路由第六部分:后端初步测…...
在 Visual Studio Code 中使用驭码 CodeRider 提升开发效率:以冒泡排序为例
目录 前言1 插件安装与配置1.1 安装驭码 CodeRider1.2 初始配置建议 2 示例代码:冒泡排序3 驭码 CodeRider 功能详解3.1 功能概览3.2 代码解释功能3.3 自动注释生成3.4 逻辑修改功能3.5 单元测试自动生成3.6 代码优化建议 4 驭码的实际应用建议5 常见问题与解决建议…...
