1.概述

最近,在研究LangChain时,发现一些比较有意思的点,今天笔者将给大家分享关于LangChain的一些内容。

2.内容

2.1 什么是LangChain?

LangChain是一项旨在赋能开发人员利用语言模型构建端到端应用程序的强大框架。它的设计理念在于简化和加速利用大型语言模型(LLM)和对话模型构建应用程序的过程。这个框架提供了一套全面的工具、组件和接口,旨在简化基于大型语言模型和对话模型的应用程序开发过程。

LangChain本质上可被视为类似于开源GPT的插件。它不仅提供了丰富的大型语言模型工具,还支持在开源模型的基础上快速增强模型的功能。通过LangChain,开发人员可以更轻松地管理与语言模型的交互,无缝地连接多个组件,并集成额外的资源,如API和数据库等,以加强应用的能力和灵活性。

LangChain的优势在于为开发人员提供了一个便捷的框架,使其能够更高效地构建应用程序,利用最先进的语言模型和对话系统,同时能够灵活地定制和整合其他必要的资源,为应用程序的开发和部署提供了更大的灵活性和便利性。

2.2 LangChain框架组成?

1.Model

任何语言模型应用的核心要素是…… 模型。LangChain为您提供了与任何语言模型进行接口的基本构件。

  • 提示:模板化、动态选择和管理模型输入
  • 语言模型:通过通用接口调用语言模型
  • 输出解析器:从模型输出中提取信息

此外,LangChain还提供了额外的关键功能,如:

  • 模型适配器:通过适配器将不同类型的语言模型无缝整合到应用程序中。
  • 模型优化器:优化和调整模型以提高性能和效率。
  • 自定义组件:允许开发人员根据特定需求创建和整合自定义组件,以扩展和改进模型的功能。

2.Retrieval

许多大型语言模型(LLM)应用需要用户特定的数据,这些数据并不属于模型的训练集。实现这一点的主要方法是通过检索增强生成(RAG)。在这个过程中,会检索外部数据,然后在生成步骤中传递给LLM。

LangChain为RAG应用提供了从简单到复杂的所有构建模块。文档的此部分涵盖与检索步骤相关的所有内容,例如数据的获取。尽管听起来简单,但实际上可能相当复杂。这包括几个关键模块。

文档加载器

从许多不同来源加载文档。LangChain提供超过100种不同的文档加载器,并与其他主要提供者(如AirByte和Unstructured)进行集成。我们提供与各种位置(私人S3存储桶、公共网站)的各种文档类型(HTML、PDF、代码)的加载集成。

文档转换器

检索的关键部分是仅获取文档的相关部分。这涉及几个转换步骤,以最佳准备文档进行检索。其中一个主要步骤是将大型文档分割(或分块)为较小的块。LangChain提供了几种不同的算法来执行此操作,同时针对特定文档类型(代码、Markdown等)进行了优化逻辑。

文本嵌入模型

检索的另一个关键部分是为文档创建嵌入。嵌入捕获文本的语义含义,使您能够快速高效地查找其他类似的文本。LangChain与超过25种不同的嵌入提供者和方法进行集成,涵盖了从开源到专有API的各种选择,使您可以选择最适合您需求的方式。LangChain提供了标准接口,使您能够轻松切换模型。

向量存储

随着嵌入技术的兴起,出现了对数据库的需求,以支持这些嵌入的有效存储和搜索。LangChain与超过50种不同的向量存储进行集成,涵盖了从开源本地存储到云托管专有存储的各种选择,使您可以选择最适合您需求的方式。LangChain提供标准接口,使您能够轻松切换向量存储。

检索器

一旦数据存储在数据库中,您仍需要检索它。LangChain支持许多不同的检索算法,这是我们增加最多价值的领域之一。我们支持易于入门的基本方法,即简单的语义搜索。然而,我们还在此基础上添加了一系列算法以提高性能。这些包括:

  • 父文档检索器:这允许您为每个父文档创建多个嵌入,使您能够查找较小的块,但返回更大的上下文。
  • 自查询检索器:用户的问题通常包含对不仅仅是语义的某些逻辑的引用,而是可以最好表示为元数据过滤器的逻辑。自查询允许您从查询中提取出语义部分以及查询中存在的其他元数据过滤器。
  • 组合检索器:有时您可能希望从多个不同来源或使用多种不同算法检索文档。组合检索器可以轻松实现此操作。

3.Chains

单独使用LLM适用于简单的应用程序,但更复杂的应用程序需要将LLM链接起来,要么彼此链接,要么与其他组件链接。

LangChain提供了两个高级框架来“链接”组件。传统方法是使用Chain接口。更新的方法是使用LangChain表达语言(LCEL)。在构建新应用程序时,我们建议使用LCEL进行链式组合。但是,我们继续支持许多有用的内置Chain,因此我们在这里对两个框架进行了文档化。正如我们将在下面提到的,Chain本身也可以用于LCEL,因此两者并不是互斥的。

新内容补充:
此外,LangChain还提供了其他关于链式组合的重要内容:

  • 模块兼容性:LangChain旨在确保不同模块之间的兼容性,使得可以无缝地将不同类型的LLMs和其他组件相互链接,以构建更加复杂和高效的应用程序。
  • 动态调整:框架允许动态调整不同模块和LLMs之间的连接方式,从而使得应用程序的功能和性能得到灵活的调整和优化。
  • 可扩展性:LangChain提供了灵活的扩展机制,允许开发人员根据具体需求定制新的链接策略和模块组合,以适应不同场景下的需求。

LCEL最显著的部分是它提供了直观且易读的组合语法。但更重要的是,它还提供了一流的支持。为一个简单且常见的例子,我们可以看到如何将提示(prompt)、模型和输出解析器结合起来:

from langchain.chat_models import ChatAnthropic
from langchain.prompts import ChatPromptTemplate
from langchain.schema import StrOutputParser model = ChatAnthropic()
prompt = ChatPromptTemplate.from_messages([
("system", "You're a very knowledgeable historian who provides accurate and eloquent answers to historical questions."),
("human", "{question}"),
])
runnable = prompt | model | StrOutputParser()

Chain 是“链式”应用程序的传统接口。我们通常很泛化地定义 Chain 为对组件的一系列调用,其中可能包括其他链。基本接口很简单:

class Chain(BaseModel, ABC):
"""Base interface that all chains should implement.""" memory: BaseMemory
callbacks: Callbacks def __call__(
self,
inputs: Any,
return_only_outputs: bool = False,
callbacks: Callbacks = None,
) -> Dict[str, Any]:
...

4.Memory

在与模型交互时,用于存储上下文状态。模型本身不具备上下文记忆,因此在与模型交互时,需要传递聊天内容的上下文。

大多数大型语言模型应用具有会话式界面。对话的一个基本组成部分是能够引用先前在对话中提到的信息。至少,会话系统应能直接访问一定范围内的先前消息。更复杂的系统需要具有一个持续更新的世界模型,使其能够保持关于实体及其关系的信息。

我们称存储关于先前互动的信息的能力为“记忆”。LangChain为向系统添加记忆提供了许多实用工具。这些实用工具可以单独使用,也可以无缝地整合到链中。

一个记忆系统需要支持两种基本操作:读取和写入。要记住,每个链定义了一些核心执行逻辑,期望得到某些输入。其中一些输入直接来自用户,但有些输入可能来自记忆。在给定运行中,一个链将两次与其记忆系统交互。

在接收到初始用户输入之后,但在执行核心逻辑之前,链将从其记忆系统中读取并增补用户输入。
在执行核心逻辑后,但在返回答案之前,链将把当前运行的输入和输出写入记忆,以便在将来的运行中引用。

让我们看一看如何在链中使用 ConversationBufferMemory。ConversationBufferMemory 是一种极其简单的内存形式,它只是在缓冲区中保留了一系列聊天消息,并将其传递到提示模板中。

from langchain.memory import ConversationBufferMemory

memory = ConversationBufferMemory()
memory.chat_memory.add_user_message("hi!")
memory.chat_memory.add_ai_message("what's up?")

在链中使用记忆时,有一些关键概念需要理解。请注意,这里我们介绍了适用于大多数记忆类型的一般概念。每种单独的记忆类型可能都有其自己的参数和概念,需要理解。

通常情况下,链接收或返回多个输入/输出键。在这些情况下,我们如何知道要保存到聊天消息历史记录的键是哪些?这通常可以通过记忆类型的 input_key 和 output_key 参数来控制。这些参数默认为 None - 如果只有一个输入/输出键,那么会自动使用该键。但是,如果有多个输入/输出键,则必须明确指定要使用哪一个的名称。最后,让我们来看看如何在链中使用这个功能。我们将使用一个 LLMChain,并展示如何同时使用 LLM 和 ChatModel。

使用一个LLM:

from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from langchain.memory import ConversationBufferMemory llm = OpenAI(temperature=0)
# Notice that "chat_history" is present in the prompt template
template = """You are a nice chatbot having a conversation with a human. Previous conversation:
{chat_history} New human question: {question}
Response:"""
prompt = PromptTemplate.from_template(template)
# Notice that we need to align the `memory_key`
memory = ConversationBufferMemory(memory_key="chat_history")
conversation = LLMChain(
llm=llm,
prompt=prompt,
verbose=True,
memory=memory
)

使用一个ChatModel:

from langchain.chat_models import ChatOpenAI
from langchain.prompts import (
ChatPromptTemplate,
MessagesPlaceholder,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
)
from langchain.chains import LLMChain
from langchain.memory import ConversationBufferMemory llm = ChatOpenAI()
prompt = ChatPromptTemplate(
messages=[
SystemMessagePromptTemplate.from_template(
"You are a nice chatbot having a conversation with a human."
),
# The `variable_name` here is what must align with memory
MessagesPlaceholder(variable_name="chat_history"),
HumanMessagePromptTemplate.from_template("{question}")
]
)
# Notice that we `return_messages=True` to fit into the MessagesPlaceholder
# Notice that `"chat_history"` aligns with the MessagesPlaceholder name.
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
conversation = LLMChain(
llm=llm,
prompt=prompt,
verbose=True,
memory=memory
)

5.Agents

Agents的核心理念是利用LLM选择要采取的一系列行动。在链式结构中,行动序列是硬编码的(在代码中)。而在Agents中,语言模型被用作推理引擎,决定要采取哪些行动以及顺序。

一些重要的术语(和模式)需要了解:

  • AgentAction:这是一个数据类,代表Agents应该执行的操作。它具有一个tool属性(应该调用的工具的名称)和一个tool_input属性(该工具的输入)。
  • AgentFinish:这是一个数据类,表示Agent已经完成并应该返回给用户。它有一个return_values参数,这是要返回的字典。通常它只有一个键 - output - 是一个字符串,因此通常只返回这个键。
  • intermediate_steps:这些表示传递的先前Agent操作和对应的输出。这些很重要,以便在将来的迭代中传递,以便Agent知道它已经完成了哪些工作。它的类型被定义为List[Tuple[AgentAction, Any]]。请注意,observation目前以Any类型留下以保持最大的灵活性。在实践中,这通常是一个字符串。

6.Callbacks

LangChain提供了回调系统,允许您连接到LLM应用程序的各个阶段。这对于日志记录、监控、流处理和其他任务非常有用。

您可以通过使用API中始终可用的callbacks参数来订阅这些事件。该参数是处理程序对象的列表,这些处理程序对象应该详细实现下面描述的一个或多个方法。

CallbackHandlers 是实现 CallbackHandler 接口的对象,该接口为可以订阅的每个事件都有一个方法。当事件触发时,CallbackManager 将在每个处理程序上调用适当的方法。

class BaseCallbackHandler:
"""Base callback handler that can be used to handle callbacks from langchain.""" def on_llm_start(
self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any
) -> Any:
"""Run when LLM starts running.""" def on_chat_model_start(
self, serialized: Dict[str, Any], messages: List[List[BaseMessage]], **kwargs: Any
) -> Any:
"""Run when Chat Model starts running.""" def on_llm_new_token(self, token: str, **kwargs: Any) -> Any:
"""Run on new LLM token. Only available when streaming is enabled.""" def on_llm_end(self, response: LLMResult, **kwargs: Any) -> Any:
"""Run when LLM ends running.""" def on_llm_error(
self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any
) -> Any:
"""Run when LLM errors.""" def on_chain_start(
self, serialized: Dict[str, Any], inputs: Dict[str, Any], **kwargs: Any
) -> Any:
"""Run when chain starts running.""" def on_chain_end(self, outputs: Dict[str, Any], **kwargs: Any) -> Any:
"""Run when chain ends running.""" def on_chain_error(
self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any
) -> Any:
"""Run when chain errors.""" def on_tool_start(
self, serialized: Dict[str, Any], input_str: str, **kwargs: Any
) -> Any:
"""Run when tool starts running.""" def on_tool_end(self, output: str, **kwargs: Any) -> Any:
"""Run when tool ends running.""" def on_tool_error(
self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any
) -> Any:
"""Run when tool errors.""" def on_text(self, text: str, **kwargs: Any) -> Any:
"""Run on arbitrary text.""" def on_agent_action(self, action: AgentAction, **kwargs: Any) -> Any:
"""Run on agent action.""" def on_agent_finish(self, finish: AgentFinish, **kwargs: Any) -> Any:
"""Run on agent end."""

LangChain提供了一些内置处理程序,供开发者快速开始使用。这些处理程序位于 langchain/callbacks 模块中。最基本的处理程序是 StdOutCallbackHandler,它简单地将所有事件记录到标准输出(stdout)。

注意:当对象的 verbose 标志设置为 true 时,即使未显式传入,StdOutCallbackHandler 也会被调用。

from langchain.callbacks import StdOutCallbackHandler
from langchain.chains import LLMChain
from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate handler = StdOutCallbackHandler()
llm = OpenAI()
prompt = PromptTemplate.from_template("1 + {number} = ") # Constructor callback: First, let's explicitly set the StdOutCallbackHandler when initializing our chain
chain = LLMChain(llm=llm, prompt=prompt, callbacks=[handler])
chain.run(number=2) # Use verbose flag: Then, let's use the `verbose` flag to achieve the same result
chain = LLMChain(llm=llm, prompt=prompt, verbose=True)
chain.run(number=2) # Request callbacks: Finally, let's use the request `callbacks` to achieve the same result
chain = LLMChain(llm=llm, prompt=prompt)
chain.run(number=2, callbacks=[handler])

结果如下:

    > Entering new LLMChain chain...
Prompt after formatting:
1 + 2 = > Finished chain. > Entering new LLMChain chain...
Prompt after formatting:
1 + 2 = > Finished chain. > Entering new LLMChain chain...
Prompt after formatting:
1 + 2 = > Finished chain. '\n\n3'

3.快速使用

3.1 环境设置

使用LangChain通常需要与一个或多个模型提供商、数据存储、API等进行集成。在本示例中,我们将使用OpenAI的模型API。

首先,我们需要安装他们的Python包:

pip install langchain
pip install openai

在初始化OpenAI LLM类时直接通过名为 openai_api_key 的参数传递密钥:

from langchain.llms import OpenAI

llm = OpenAI(openai_api_key="...")

3.2 LLM

在LangChain中有两种类型的语言模型,分别被称为:

  • LLMs:这是一个以字符串作为输入并返回字符串的语言模型。
  • ChatModels:这是一个以消息列表作为输入并返回消息的语言模型。

LLMs的输入/输出简单易懂 - 字符串。但ChatModels呢?那里的输入是一个ChatMessages列表,输出是一个单独的ChatMessage。ChatMessage有两个必需的组成部分:

  • content:这是消息的内容。
  • role:这是产生ChatMessage的实体的角色。

LangChain提供了几个对象来轻松区分不同的角色:

  • HumanMessage:来自人类/用户的ChatMessage。
  • AIMessage:来自AI/助手的ChatMessage。
  • SystemMessage:来自系统的ChatMessage。
  • FunctionMessage:来自函数调用的ChatMessage。

如果这些角色都不合适,还有一个ChatMessage类,可以手动指定角色。

LangChain提供了标准接口,但了解这种差异有助于构建特定语言模型的提示。LangChain提供的标准接口有两种方法:

  • predict:接受一个字符串,返回一个字符串。
  • predict_messages:接受一个消息列表,返回一个消息。
from langchain.llms import OpenAI
from langchain.chat_models import ChatOpenAI llm = OpenAI()
chat_model = ChatOpenAI() llm.predict("hi!")
>>> "Hi" chat_model.predict("hi!")
>>> "Hi"

OpenAI 和 ChatOpenAI 对象基本上只是配置对象。开发者可以使用诸如 temperature 等参数来初始化它们,并在各处传递它们。

接下来,让我们使用 predict 方法来处理一个字符串输入。

text = "What would be a good company name for a company that makes colorful socks?"

llm.predict(text)
# >> Feetful of Fun chat_model.predict(text)
# >> Socks O'Color

最后,让我们使用 predict_messages 方法来处理一个消息列表。

from langchain.schema import HumanMessage

text = "What would be a good company name for a company that makes colorful socks?"
messages = [HumanMessage(content=text)] llm.predict_messages(messages)
# >> Feetful of Fun chat_model.predict_messages(messages)
# >> Socks O'Color

3.3 Prompt 模版

PromptTemplates也可以用于生成消息列表。在这种情况下,提示不仅包含有关内容的信息,还包含每条消息的信息(其角色、在列表中的位置等)。在这里,最常见的情况是ChatPromptTemplate是ChatMessageTemplate列表。每个ChatMessageTemplate包含了如何格式化ChatMessage的指令 - 其角色以及内容。

from langchain.prompts.chat import ChatPromptTemplate

template = "You are a helpful assistant that translates {input_language} to {output_language}."
human_template = "{text}" chat_prompt = ChatPromptTemplate.from_messages([
("system", template),
("human", human_template),
]) chat_prompt.format_messages(input_language="English", output_language="French", text="I love programming.")

3.3 Output parsers

OutputParsers 将LLM的原始输出转换为可在下游使用的格式。其中主要的OutputParsers类型包括:

  • 将LLM的文本转换为结构化信息(例如 JSON)
  • 将 ChatMessage 转换为纯字符串
  • 将调用返回的除消息外的额外信息(例如 OpenAI 函数调用)转换为字符串。
from langchain.schema import BaseOutputParser

class CommaSeparatedListOutputParser(BaseOutputParser):
"""Parse the output of an LLM call to a comma-separated list.""" def parse(self, text: str):
"""Parse the output of an LLM call."""
return text.strip().split(", ") CommaSeparatedListOutputParser().parse("hi, bye")
# >> ['hi', 'bye']

3.4 PromptTemplate + LLM + OutputParser

我们现在可以将所有这些组合成一个链条。该链条将接收输入变量,将其传递到提示模板以创建提示,将提示传递给语言模型,然后通过(可选的)输出解析器传递输出。这是打包模块化逻辑的便捷方式。

from langchain.chat_models import ChatOpenAI
from langchain.prompts.chat import ChatPromptTemplate
from langchain.schema import BaseOutputParser class CommaSeparatedListOutputParser(BaseOutputParser):
"""Parse the output of an LLM call to a comma-separated list.""" def parse(self, text: str):
"""Parse the output of an LLM call."""
return text.strip().split(", ") template = """You are a helpful assistant who generates comma separated lists.
A user will pass in a category, and you should generate 5 objects in that category in a comma separated list.
ONLY return a comma separated list, and nothing more."""
human_template = "{text}" chat_prompt = ChatPromptTemplate.from_messages([
("system", template),
("human", human_template),
])
chain = chat_prompt | ChatOpenAI() | CommaSeparatedListOutputParser()
chain.invoke({"text": "colors"})
# >> ['red', 'blue', 'green', 'yellow', 'orange']

请注意,我们使用 | 符号将这些组件连接在一起。这种 | 符号称为 LangChain 表达语言。

4.结束语

这篇博客就和大家分享到这里,如果大家在研究学习的过程当中有什么问题,可以加群进行讨论或发送邮件给我,我会尽我所能为您解答,与君共勉!

另外,博主出书了《Kafka并不难学》和《Hadoop大数据挖掘从入门到进阶实战》,喜欢的朋友或同学, 可以在公告栏那里点击购买链接购买博主的书进行学习,在此感谢大家的支持。关注下面公众号,根据提示,可免费获取书籍的教学视频。

LangChain实战的更多相关文章

  1. SSH实战 · 唯唯乐购项目(上)

    前台需求分析 一:用户模块 注册 前台JS校验 使用AJAX完成对用户名(邮箱)的异步校验 后台Struts2校验 验证码 发送激活邮件 将用户信息存入到数据库 激活 点击激活邮件中的链接完成激活 根 ...

  2. GitHub实战系列汇总篇

    基础: 1.GitHub实战系列~1.环境部署+创建第一个文件 2015-12-9 http://www.cnblogs.com/dunitian/p/5034624.html 2.GitHub实战系 ...

  3. MySQL 系列(四)主从复制、备份恢复方案生产环境实战

    第一篇:MySQL 系列(一) 生产标准线上环境安装配置案例及棘手问题解决 第二篇:MySQL 系列(二) 你不知道的数据库操作 第三篇:MySQL 系列(三)你不知道的 视图.触发器.存储过程.函数 ...

  4. Asp.Net Core 项目实战之权限管理系统(4) 依赖注入、仓储、服务的多项目分层实现

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  5. 给缺少Python项目实战经验的人

    我们在学习过程中最容易犯的一个错误就是:看的多动手的少,特别是对于一些项目的开发学习就更少了! 没有一个完整的项目开发过程,是不会对整个开发流程以及理论知识有牢固的认知的,对于怎样将所学的理论知识应用 ...

  6. asp.net core 实战之 redis 负载均衡和"高可用"实现

    1.概述 分布式系统缓存已经变得不可或缺,本文主要阐述如何实现redis主从复制集群的负载均衡,以及 redis的"高可用"实现, 呵呵双引号的"高可用"并不是 ...

  7. Linux实战教学笔记08:Linux 文件的属性(上半部分)

    第八节 Linux 文件的属性(上半部分) 标签(空格分隔):Linux实战教学笔记 第1章 Linux中的文件 1.1 文件属性概述(ls -lhi) linux里一切皆文件 Linux系统中的文件 ...

  8. Linux实战教学笔记07:Linux系统目录结构介绍

    第七节 Linux系统目录结构介绍 标签(空格分隔):Linux实战教学笔记 第1章 前言 windows目录结构 C:\windows D:\Program Files E:\你懂的\精品 F:\你 ...

  9. Linux实战教学笔记06:Linux系统基础优化

    第六节 Linux系统基础优化 标签(空格分隔):Linux实战教学笔记-陈思齐 第1章 基础环境 第2章 使用网易163镜像做yum源 默认国外的yum源速度很慢,所以换成国内的. 第一步:先备份 ...

  10. Linux实战教学笔记05:远程SSH连接服务与基本排错(新手扫盲篇)

    第五节 远程SSH连接服务与基本排错 标签(空格分隔):Linux实战教学笔记-陈思齐 第1章 远程连接LInux系统管理 1.1 为什么要远程连接Linux系统 在实际的工作场景中,虚拟机界面或物理 ...

随机推荐

  1. 我和ChatGPT聊数字人

    ​ 近期,聊天机器人ChatGPT火了,写诗写文写代码,才艺狠狠拉满. 面对如此"会聊"的ChatGPT,很多人好奇相同的问题提问ChatGPT和真人,会有什么样的结果? 于是我们 ...

  2. Linux 文件系统的设计

    一 硬盘的最小存储单元是扇区,扇区大小为512B 文件系统将硬盘划分为固定大小的块(block),最常见的块大小为4KB 块是文件系统读写硬盘的最小单位 二 文件系统设计面临的基本问题: 用户怎么找到 ...

  3. 使用gulp.js打包layuiAdmin

    安装nvm 在nvm目录下,找到settings.txt,追加以下两行加速nvm(淘宝镜像)node_mirror: https://npm.taobao.org/mirrors/node/npm_m ...

  4. 如何用 ModelScope 实现 “AI 换脸” 视频

    前言 当下,视频内容火爆,带有争议性或反差大的换脸视频总能吸引人视线.虽然 AI 换脸在市面上已经流行了许久,相关制作工具或移动应用也是数不胜数.但是多数制作工具多数情况下不是会员就是收费,而且替换模 ...

  5. zabbix触发器标签提取监控项子字符串功能实现对应告警恢复

    0 实验环境 zabbix 6.0 1 监控项 1.1 监控项设置 通过zabbix agent自定义监控项,读取某文件内容模拟日志/trap告警,测试获取触发器标签中提取子字符串功能,以及相同标签的 ...

  6. EXP 一款 Java 插件化热插拔框架

    EXP 一款 Java 插件化热插拔框架 前言 多年以来,ToB 的应用程序都面临定制化需求应该怎么搞的问题. 举例,大部分本地化软件厂家,都有一个标准程序,这个程序支持大部分企业的功能需求,但面对世 ...

  7. WPF如何构建MVVM+模块化的桌面应用

    为何模块化 模块化是一种分治思想,不仅可以分离复杂的业务逻辑,还可以进行不同任务的分工.模块与模块之间相互独立,从而构建一种松耦合的应用程序,便于开发和维护. 开发技术 .Net 6 + WPF + ...

  8. 新一代Kerberos攻击 钻石票据与蓝宝石票据

    新一代Kerberos攻击 | 钻石票据与蓝宝石票据 0x01 前言 在了解票据攻击的过程中,看见了一篇文章使用Rubeus进行钻石票据攻击.但是没有原理,于是抱着学习的心态在Google上寻找文章发 ...

  9. LeetCode 周赛上分之旅 #40 结合特征压缩的数位 DP 问题

    ️ 本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 和 BaguTree Pro 知识星球提问. 学习数据结构与算法的关键在于掌握问题背后的算法思维框架,你的思考越 ...

  10. 四层负载均衡的NAT模型与DR模型推导

    导读 本文首先讲述四层负载均衡技术的特点,然后通过提问的方式推导出四层负载均衡器的NAT模型和DR模型的工作原理.通过本文可以了解到四层负载均衡的技术特点.NAT模型和DR模型的工作原理.以及NAT模 ...