当前位置: 首页 > news >正文

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在上述交互中是如何被提示的。

如果这是一个简单的问答机器人,我们可能会对上述结果感到满意。由于我们的客户支持机器人是代表用户采取行动,因此它的一些行为有点令人担忧:

  1. 当我们专注于住宿时,助手预订了一辆车,然后又不得不取消并稍后重新预订:哎呀!在预订之前,应该让用户有最终决定权,以避免不需要的预订。

  2. 助手在寻找推荐方面遇到了困难。我们可以通过添加更多的详细指令和使用工具的示例来改进这一点,但为每个工具都这样做可能会导致提示过长,让代理感到不知所措。

  3. 助手不得不进行明确的搜索才能获取用户的相关信息。我们可以通过立即获取用户的旅行详细信息,让助手能够直接回应,从而节省大量时间。

如何学习大模型 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实战:从零分阶打造人工智能航空客服助手

❝ 通过本指南&#xff0c;你将学习构建一个专为航空公司设计的客服助手&#xff0c;它将协助用户查询旅行信息并规划行程。在此过程中&#xff0c;你将掌握如何利用LangGraph的中断机制、检查点技术以及更为复杂的状态管理功能&#xff0c;来优化你的助手工具&#xff0c;同时…...

R可视化:R语言基础图形合集

R语言基础图形合集 欢迎大家关注全网生信学习者系列&#xff1a; WX公zhong号&#xff1a;生信学习者Xiao hong书&#xff1a;生信学习者知hu&#xff1a;生信学习者CDSN&#xff1a;生信学习者2 基础图形可视化 数据分析的图形可视化是了解数据分布、波动和相关性等属性必…...

mysql导入sql文件失败及解决措施

1.报错找不到表 1.1 原因 表格创建失败&#xff0c;编码问题mysql8相较于mysql5出现了新的编码集 1.2解决办法&#xff1a; 使用vscode打开sql文件ctrlh&#xff0c;批量替换&#xff0c;替换到你所安装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)

引言 容器非常神奇。它们允许简单的进程表现得像虚拟机。在这种优雅的底层是一组模式和实践&#xff0c;最终使一切运作起来。在设计的根本是层。层是存储和分发容器化文件系统内容的基本方式。这种设计既出人意料地简单&#xff0c;同时又非常强大。在今天的帖子[1]中&#xf…...

使用Python爬取temu商品与评论信息

【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python与爬虫领域研究与开发工作&#xff01; 【&…...

mybatis学习--自定义映射resultMap

1.1、resultMap处理字段和属性的映射关系 如果字段名和实体类中的属性名不一致的情况下&#xff0c;可以通过resultMap设置自定义映射。 常规写法 /***根据id查询员工信息* param empId* return*/ Emp getEmpByEmpId(Param("empId") Integer empId);<select id…...

Elasticsearch之写入原理以及调优

1、ES 的写入过程 1.1 ES支持四种对文档的数据写操作 create&#xff1a;如果在PUT数据的时候当前数据已经存在&#xff0c;则数据会被覆盖&#xff0c;如果在PUT的时候加上操作类型create&#xff0c;此时如果数据已存在则会返回失败&#xff0c;因为已经强制指定了操作类型…...

python中装饰器的用法

最近发现装饰器是一个非常有意思的东西&#xff0c;很高级&#xff01; 允许你在不修改函数或类的源代码的情况下&#xff0c;为它们添加额外的功能或修改它们的行为。装饰器本质上是一个接受函数作为参数的可调用对象&#xff08;通常是函数或类&#xff09;&#xff0c;并返…...

php实现一个简单的MySQL分页

一、案例演示&#xff1a; 二、php 代码 <?php $servername "localhost"; // MySQL服务器名称或IP地址 $username "root"; // MySQL用户名 $password "123456"; // MySQL密码 $dbname "test"; // 要连接…...

算法训练营day23补签

题目1&#xff1a;530. 二叉搜索树的最小绝对差 - 力扣&#xff08;LeetCode&#xff09; 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和植物大战僵尸&#xff0c;点CE中文件下面红框位置&#xff0c;选择植物大战僵尸&#xff0c;点击打开 修改冷却&#xff1a; 等冷却完毕&#xff0c;首次扫描0安放植物&#xff0c;再次扫描变动值等冷却完…...

python内置模块之queue(队列)用法

queue是python3的内置模块&#xff0c;创建堆栈队列&#xff0c;用来处理多线程通信&#xff0c;队列对象构造方法如下&#xff1a; queue.Queue(maxsize0) 是先进先出&#xff08;First In First Out: FIFO&#xff09;队列。 入参 maxsize 是一个整数&#xff0c;用于设置…...

Spring Security——结合JWT实现令牌的验证与授权

目录 JWT&#xff08;JSON Web Token&#xff09; 项目总结 新建一个SpringBoot项目 pom.xml PayloadDto JwtUtil工具类 MyAuthenticationSuccessHandler&#xff08;验证成功处理器&#xff09; JwtAuthenticationFilter&#xff08;自定义token过滤器&#xff09; W…...

Vector的底层结构剖析

vector的介绍&#xff1a; 1.Vector实现了List接口的集合。 2.Vector的底层也是一个数组,protected Object[] elementData; 3.Vector 是线程同步的&#xff0c;即线程安全&#xff0c;Vector类的操作方法带有Synchronized. 4.在开发中&#xff0c;需要线程同步时&#xff0…...

实现抖音视频滑动功能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关于文件名 哈喽&#xff0c;大家好。今天我们学习文件系统&#xff0c;我们之前在Linux基础IO中研究的是进程和被打开文件…...

JAVA学习笔记DAY5——Spring_Ioc

文章目录 Bean配置注解方式配置注解配置文件调用组件 注解方法作用域 DI注入注解引用类型自动装配文件结构自动装配实现 基本数据类型DI装配 Bean配置 注解方式配置 类上添加Ioc注解配置文件中告诉SpringIoc容器要检查哪些包 注解仅是一个标记 注解 不同注解仅是为了方便开…...

WPF中的隧道路由和冒泡路由事件

文章目录 简介&#xff1a;一、事件最基本的用法二、理解路由事件 简介&#xff1a; WPF中使用路由事件升级了传统应用开发中的事件&#xff0c;在WPF中使用路由事件能更好的处理事件相关的逻辑&#xff0c;我们从这篇开始整理事件的用法和什么是直接路由&#xff0c;什么是冒…...

ISO七层模型 tcp/ip

OSI七层模型&#xff08;重点例子&#xff09; OSI&#xff08;Open Systems Interconnection&#xff09;模型&#xff0c;也称为开放系统互连模型&#xff0c;是一个理论模型&#xff0c;由国际标准化组织&#xff08;ISO&#xff09;制定&#xff0c;用于描述和理解不同网络…...

MySQL的三种重要的日志

日志 Mysql有三大日志系统 Undo Log&#xff08;回滚日志&#xff09;&#xff1a;记录修改前的数据&#xff0c;用于事务回滚和 MVCC&#xff08;多版本并发控制&#xff09;。 Redo Log&#xff08;重做日志&#xff09;&#xff1a;记录数据变更&#xff0c;用于崩溃恢复&…...

神经网络学习2

张量&#xff08;Tensor&#xff09;是深度学习和科学计算中的基本数据结构&#xff0c;用于表示多维数组。张量可以看作是一个更广义的概念&#xff0c;涵盖了标量、向量、矩阵以及更高维度的数据结构。具体来说&#xff0c;张量的维度可以是以下几种形式&#xff1a; 标量&am…...

Spring Boot整合Redis通过Zset数据类型+定时任务实现延迟队列

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…...

Android入门第69天-AndroidStudio中的Gradle使用国内镜像最强教程

背景 AndroidStudio默认连接的是dl.google的gadle仓库。 每次重新build时: 下载速度慢;等待了半天总时build faild;build到一半connection timeout;即使使用了魔法也难以一次build好;这严重影响了我们的学习、开发效率。 当前网络上的使用国内镜像的教程不全 网上的教程…...

深入浅出 Qt 中 QListView 的设计思想,并掌握大规模、高性能列表的实现方法

在大规模列表控件的显示需求中&#xff0c;必须解决2个问题才能获得较好的性能&#xff1a; 第一就是数据存在哪里&#xff0c; 避免出现数据的副本。第二就是如何展示Item&#xff0c;如何复用或避免创建大量的Item控件。 在QListView体系里&#xff0c;QAbstractListModel解…...

课设--学生成绩管理系统

欢迎来到 Papicatch的博客 文章目录 &#x1f349;技术核心 &#x1f349;引言 &#x1f348;标识 &#x1f348;背景 &#x1f348;项目概述 &#x1f348; 文档概述 &#x1f349;可行性分析的前提 &#x1f348;项目的要求 &#x1f348;项目的目标 &#x1f348;…...

MySQL性能分析

一、查看执行频率 sql执行频率,执行下述指令可以看到select&#xff0c;update,delete等操作的次数 show global status like Com_______; 具体我们在终端登录mysql看下&#xff0c;使用下述命令登录mysql&#xff0c;并输入命令 mysql -u 用户名 -p 上述查询&#xff0c;删…...