RAG技术全面解析:Langchain4j如何实现智能问答的跨越式进化?

LLM 的知识仅限于其训练数据。如希望使 LLM 了解特定领域的知识或专有数据,可:

  • 使用本节介绍的 RAG
  • 使用你的数据对 LLM 进行微调
  • 结合使用 RAG 和微调

1 啥是 RAG?

RAG 是一种在将提示词发送给 LLM 之前,从你的数据中找到并注入相关信息的方式。这样,LLM 希望能获得相关的信息并利用这些信息作出回应,从而减少幻觉概率。

可通过各种信息检索方法找到相关信息。这些方法包括但不限于:

  • 全文(关键词)搜索。该方法使用 TF-IDF 和 BM25 等技术,通过匹配查询(例如用户提问)中的关键词与文档数据库中的内容来搜索文档。它根据这些关键词在每个文档中的频率和相关性对结果进行排名
  • 向量搜索,也称“语义搜索”。文本文档通过嵌入模型转换为数值向量。然后根据查询向量与文档向量之间的余弦相似度或其他相似度/距离度量,查找并对文档进行排名,从而捕捉更深层次的语义含义
  • 混合搜索。结合多种搜索方法(例如全文搜索 + 向量搜索)通常能提高搜索效果

本文主要关注向量搜索。全文搜索和混合搜索目前仅通过 Azure AI Search 集成支持,详情参见 AzureAiSearchContentRetriever。计划在不久的将来扩展 RAG 工具箱,以包含全文搜索和混合搜索。

2 RAG 的阶段

RAG 过程分为两个不同阶段:索引和检索。LangChain4j 提供用于两个阶段的工具。

2.1 索引

文档会进行预处理,以便在检索阶段实现高效搜索。

该过程可能因使用的信息检索方法而有所不同。对向量搜索,通常包括清理文档,利用附加数据和元数据对其进行增强,将其拆分为较小的片段(即“分块”),对这些片段进行嵌入,最后将它们存储在嵌入存储库(即向量数据库)。

通常在离线完成,即用户无需等待该过程的完成。可通过例如每周末运行一次的定时任务来重新索引公司内部文档。负责索引的代码也可以是一个仅处理索引任务的单独应用程序。

但某些场景,用户可能希望上传自定义文档以供 LLM 访问。此时,索引应在线进行,并成为主应用程序的一部分。

索引阶段的简化流程图

2.2 检索

通常在线进行,当用户提交一个问题时,系统会使用已索引的文档来回答问题。

该过程可能会因所用的信息检索方法不同而有所变化。对于向量搜索,通常包括嵌入用户的查询(问题),并在嵌入存储库中执行相似度搜索。然后,将相关片段(原始文档的部分内容)注入提示词并发送给 LLM。

检索阶段的简化流程图

3 简单 RAG

LangChain4j 提供了“简单 RAG”功能,使你尽可能轻松使用 RAG。无需学习嵌入技术、选择向量存储、寻找合适的嵌入模型、了解如何解析和拆分文档等操作。只需指向你的文档,LangChain4j 就会自动处理!

若需定制化RAG,请跳到rag-apis。

当然,这种“简单 RAG”的质量会比定制化 RAG 设置的质量低一些。然而,这是学习 RAG 或制作概念验证的最简单方法。稍后,您可以轻松地从简单 RAG 过渡到更高级的 RAG,逐步调整和自定义各个方面。

3.1 导入 langchain4j-easy-rag 依赖

<dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-easy-rag</artifactId><version>0.34.0</version>
</dependency>

3.2 加载文档

List<Document> documents = FileSystemDocumentLoader.loadDocuments("/home/langchain4j/documentation");

这将加载指定目录下的所有文件。

底层发生了什么?

Apache Tika 库被用于检测文档类型并解析它们。由于我们没有显式指定使用哪个 DocumentParser,因此 FileSystemDocumentLoader 将加载 ApacheTikaDocumentParser,该解析器由 langchain4j-easy-rag 依赖通过 SPI 提供。

咋自定义加载文档?

若想加载所有子目录中的文档,可用 loadDocumentsRecursively

List<Document> documents = FileSystemDocumentLoader.loadDocumentsRecursively("/home/langchain4j/documentation");

还可通过使用 glob 或正则表达式过滤文档:

PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher("glob:*.pdf");
List<Document> documents = FileSystemDocumentLoader.loadDocuments("/home/langchain4j/documentation", pathMatcher);

使用 loadDocumentsRecursively 时,可能要在 glob 中使用双星号(而不是单星号):glob:**.pdf

3.3 预处理

并将文档存储在专门的嵌入存储中也称向量数据库。这是为了在用户提出问题时快速找到相关信息片段。可用 15+ 种支持的嵌入存储,但为简化操作,使用内存存储:

InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
EmbeddingStoreIngestor.ingest(documents, embeddingStore);
底层发生了啥?
  • EmbeddingStoreIngestor 通过 SPI 从 langchain4j-easy-rag 依赖中加载 DocumentSplitter。每个 Document 被拆分成较小的片段(即 TextSegment),每个片段不超过 300 个 token,且有 30 个 token 的重叠部分。
  • EmbeddingStoreIngestor 通过 SPI 从 langchain4j-easy-rag 依赖中加载 EmbeddingModel。每个 TextSegment 都使用 EmbeddingModel 转换为 Embedding

选择 bge-small-en-v1.5 作为简单 RAG 的默认嵌入模型。该模型在 MTEB 排行榜 上取得了不错的成绩,其量化版本仅占用 24 MB 空间。因此,我们可以轻松将其加载到内存中,并在同一进程中通过 ONNX Runtime 运行。

可在完全离线的情况下,在同一个 JVM 进程中将文本转换为嵌入。LangChain4j 提供 5 种流行的嵌入模型开箱即用。

  1. 所有 TextSegmentEmbedding 对被存储在 EmbeddingStore

  2. 创建一个AI 服务,它将作为我们与 LLM 交互的 API:

interface Assistant {String chat(String userMessage);
}ChatLanguageModel chatModel = OpenAiChatModel.builder().apiKey(System.getenv("OPENAI_API_KEY")).modelName(GPT_4_O_MINI).build();Assistant assistant = AiServices.builder(Assistant.class).chatLanguageModel(chatModel).chatMemory(MessageWindowChatMemory.withMaxMessages(10)).contentRetriever(EmbeddingStoreContentRetriever.from(embeddingStore)).build();

配置 Assistant 使用 OpenAI 的 LLM 来回答用户问题,记住对话中的最近 10 条消息,并从包含我们文档的 EmbeddingStore 中检索相关内容。

  1. 对话!
String answer = assistant.chat("如何使用 LangChain4j 实现简单 RAG?");

4 访问源信息

如希望访问增强消息的检索源,可将返回类型包装在 Result 类中:

interface Assistant {Result<String> chat(String userMessage);
}Result<String> result = assistant.chat("如何使用 LangChain4j 实现简单 RAG?");String answer = result.content();
List<Content> sources = result.sources();

流式传输时,可用 onRetrieved() 指定一个 Consumer<List<Content>>

interface Assistant {TokenStream chat(String userMessage);
}assistant.chat("如何使用 LangChain4j 实现简单 RAG?").onRetrieved(sources -> ...).onNext(token -> ...).onError(error -> ...).start();

5 RAG API

LangChain4j 提供丰富的 API 让你可轻松构建从简单到高级的自定义 RAG 流水线。本节介绍主要的领域类和 API。

5.1 文档(Document)

Document 类表示整个文档,例如单个 PDF 文件或网页。当前,Document 只能表示文本信息,但未来的更新将支持图像和表格。

package dev.langchain4j.data.document;/*** 表示通常对应于单个文件内容的非结构化文本。此文本可能来自各种来源,如文本文件、PDF、DOCX 或网页 (HTML)。* 每个文档都可能具有关联的元数据,包括其来源、所有者、创建日期等*/
public class Document {/*** Common metadata key for the name of the file from which the document was loaded.*/public static final String FILE_NAME = "file_name";/*** Common metadata key for the absolute path of the directory from which the document was loaded.*/public static final String ABSOLUTE_DIRECTORY_PATH = "absolute_directory_path";/*** Common metadata key for the URL from which the document was loaded.*/public static final String URL = "url";private final String text;private final Metadata metadata;
API
  • Document.text() 返回 Document 的文本内容
  • Document.metadata() 返回 Document 的元数据(见下文)
  • Document.toTextSegment()Document 转换为 TextSegment(见下文)
  • Document.from(String, Metadata) 从文本和 Metadata 创建一个 Document
  • Document.from(String) 从文本创建一个带空 MetadataDocument

5.2 元数据(Metadata)

每个 Document 都包含 Metadata,用于存储文档的元信息,如名称、来源、最后更新时间、所有者或任何其他相关细节。

Metadata 以KV对形式存储,其中键是 String 类型,值可为 StringIntegerLongFloatDouble 中的任意一种。

用途
  • 在将文档内容包含到 LLM 的提示词中时,可以将元数据条目一并包含,向 LLM 提供额外信息。例如,提供文档名称和来源可以帮助 LLM 更好地理解内容。
  • 在搜索相关内容以包含在提示词中时,可以根据元数据条目进行过滤。例如,您可以将语义搜索范围限制为属于特定所有者的文档。
  • 当文档的来源被更新(例如文档的特定页面),您可以通过其元数据条目(例如“id”、“source”等)轻松找到相应的文档,并在嵌入存储中更新它,以保持同步。
API
  • Metadata.from(Map)Map 创建 Metadata
  • Metadata.put(String key, String value) / put(String, int) / 等方法添加元数据条目
  • Metadata.getString(String key) / getInteger(String key) / 等方法返回元数据条目的值,并转换为所需类型
  • Metadata.containsKey(String key) 检查元数据中是否包含指定键的条目
  • Metadata.remove(String key) 从元数据中删除指定键的条目
  • Metadata.copy() 返回元数据的副本
  • Metadata.toMap() 将元数据转换为 Map

5.3 文档加载器(Document Loader)

可从 String 创建一个 Document,但更简单的是使用库中包含的文档加载器之一:

  • FileSystemDocumentLoader 来自 langchain4j 模块
  • UrlDocumentLoader 来自 langchain4j 模块
  • AmazonS3DocumentLoader 来自 langchain4j-document-loader-amazon-s3 模块
  • AzureBlobStorageDocumentLoader 来自 langchain4j-document-loader-azure-storage-blob 模块
  • GitHubDocumentLoader 来自 langchain4j-document-loader-github 模块
  • TencentCosDocumentLoader 来自 langchain4j-document-loader-tencent-cos 模块

5.4 文本片段转换器

TextSegmentTransformer 类似于 DocumentTransformer(如上所述),但它用于转换 TextSegment

DocumentTransformer 类似,没有统一的解决方案,建议根据您的数据自定义实现 TextSegmentTransformer

提高检索效果的有效方法是将 Document 的标题或简短摘要包含在每个 TextSegment

5.5 嵌入

Embedding 类封装了一个数值向量,表示嵌入内容(通常是文本,如 TextSegment)的“语义意义”。

关于向量嵌入的内容:

  • https://www.elastic.co/what-is/vector-embedding
  • https://www.pinecone.io/learn/vector-embeddings/
  • https://cloud.google.com/blog/topics/developers-practitioners/meet-ais-multitool-vector-embeddings
API
  • Embedding.dimension() 返回嵌入向量的维度(即长度)
  • CosineSimilarity.between(Embedding, Embedding) 计算两个 Embedding 之间的余弦相似度
  • Embedding.normalize() 对嵌入向量进行归一化(就地操作)

嵌入模型

EmbeddingModel 接口代表一种特殊类型的模型,将文本转换为 Embedding

当前支持的嵌入模型可以在这里找到。

API
  • EmbeddingModel.embed(String) 嵌入给定的文本
  • EmbeddingModel.embed(TextSegment) 嵌入给定的 TextSegment
  • EmbeddingModel.embedAll(List<TextSegment>) 嵌入所有给定的 TextSegment
  • EmbeddingModel.dimension() 返回该模型生成的 Embedding 的维度

嵌入存储

EmbeddingStore 接口表示嵌入存储,也称为向量数据库。它用于存储和高效搜索相似的(在嵌入空间中接近的)Embedding

当前支持的嵌入存储可以在这里找到。

EmbeddingStore 可以单独存储 Embedding,也可以与相应的 TextSegment 一起存储:

  • 它可以仅按 ID 存储 Embedding,嵌入的数据可以存储在其他地方,并通过 ID 关联。
  • 它可以同时存储 Embedding 和被嵌入的原始数据(通常是 TextSegment)。
API
  • EmbeddingStore.add(Embedding) 将给定的 Embedding 添加到存储中并返回随机 ID
  • EmbeddingStore.add(String id, Embedding) 将给定的 Embedding 以指定 ID 添加到存储中
  • EmbeddingStore.add(Embedding, TextSegment) 将给定的 Embedding 和关联的 TextSegment 添加到存储中,并返回随机 ID
  • EmbeddingStore.addAll(List<Embedding>) 将一组 Embedding 添加到存储中,并返回一组随机 ID
  • EmbeddingStore.addAll(List<Embedding>, List<TextSegment>) 将一组 Embedding 和关联的 TextSegment 添加到存储中,并返回一组随机 ID
  • EmbeddingStore.search(EmbeddingSearchRequest) 搜索最相似的 Embedding
  • EmbeddingStore.remove(String id) 按 ID 从存储中删除单个 Embedding
  • EmbeddingStore.removeAll(Collection<String> ids) 按 ID 从存储中删除多个 Embedding
  • EmbeddingStore.removeAll(Filter) 删除存储中与指定 Filter 匹配的所有 Embedding
  • EmbeddingStore.removeAll() 删除存储中的所有 Embedding
嵌入搜索请求(EmbeddingSearchRequest)

EmbeddingSearchRequest 表示在 EmbeddingStore 中的搜索请求。其属性如下:

  • Embedding queryEmbedding: 用作参考的嵌入。
  • int maxResults: 返回的最大结果数。这是一个可选参数,默认为 3。
  • double minScore: 最低分数,范围为 0 到 1(含)。仅返回得分 >= minScore 的嵌入。这是一个可选参数,默认为 0。
  • Filter filter: 搜索时应用于 Metadata 的过滤器。仅返回 Metadata 符合 FilterTextSegment
过滤器(Filter)

关于 Filter 的更多细节可以在这里找到。

嵌入搜索结果(EmbeddingSearchResult)

EmbeddingSearchResult 表示在 EmbeddingStore 中的搜索结果,包含 EmbeddingMatch 列表。

嵌入匹配(Embedding Match)

EmbeddingMatch 表示一个匹配的 Embedding,包括其相关性得分、ID 和嵌入的原始数据(通常是 TextSegment)。

嵌入存储导入器

EmbeddingStoreIngestor 表示一个导入管道,负责将 Document 导入到 EmbeddingStore

在最简单的配置中,EmbeddingStoreIngestor 使用指定的 EmbeddingModel 嵌入提供的 Document,并将它们与其 Embedding 一起存储在指定的 EmbeddingStore 中:

EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder().embeddingModel(embeddingModel).embeddingStore(embeddingStore).build();ingestor.ingest(document1);
ingestor.ingest(document2, document3);
ingestor.ingest(List.of(document4, document5, document6));

可选地,EmbeddingStoreIngestor 可以使用指定的 DocumentTransformer 来转换 Document。这在您希望在嵌入之前对文档进行清理、增强或格式化时非常有用。

可选地,EmbeddingStoreIngestor 可以使用指定的 DocumentSplitterDocument 拆分为 TextSegment。这在文档较大且您希望将其拆分为较小的 TextSegment 时非常有用,以提高相似度搜索的质量并减少发送给 LLM 的提示词的大小和成本。

可选地,EmbeddingStoreIngestor 可以使用指定的 TextSegmentTransformer 来转换 TextSegment。这在您希望在嵌入之前对 TextSegment 进行清理、增强或格式化时非常有用。

示例:

EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()// 为每个 Document 添加 userId 元数据条目,便于后续过滤.documentTransformer(document -> {document.metadata().put("userId", "12345");return document;})// 将每个 Document 拆分为 1000 个 token 的 TextSegment,具有 200 个 token 的重叠.documentSplitter(DocumentSplitters.recursive(1000, 200, new OpenAiTokenizer()))// 为每个 TextSegment 添加 Document 的名称,以提高搜索质量.textSegmentTransformer(textSegment -> TextSegment.from(textSegment.metadata("file_name") + "\n" + textSegment.text(),textSegment.metadata())).embeddingModel(embeddingModel).embeddingStore(embeddingStore).build();

关注我,紧跟本系列专栏文章,咱们下篇再续!

作者简介:魔都架构师,多家大厂后端一线研发经验,在分布式系统设计、数据平台架构和AI应用开发等领域都有丰富实践经验。

各大技术社区头部专家博主。具有丰富的引领团队经验,深厚业务架构和解决方案的积累。

负责:

  • 中央/分销预订系统性能优化

  • 活动&券等营销中台建设

  • 交易平台及数据中台等架构和开发设计

  • 车联网核心平台-物联网连接平台、大数据平台架构设计及优化

  • LLM Agent应用开发

  • 区块链应用开发

  • 大数据开发挖掘经验

  • 推荐系统项目

    目前主攻市级软件项目设计、构建服务全社会的应用系统。

参考:

  • 编程严选网

    本文由博客一文多发平台 OpenWrite 发布!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/430506.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux-DHCP服务器搭建

环境 服务端&#xff1a;192.168.85.136 客户端&#xff1a;192.168.85.138 1. DHCP工作原理 DHCP动态分配IP地址。 2. DHCP服务器安装 2.1前提准备 # systemctl disable --now firewalld // 关闭firewalld自启动 # setenforce 0 # vim /etc/selinux/config SELINU…

828华为云征文|华为云Flexus云服务器X实例 基于CentOS系统镜像快速部署Laravel开源论坛

最近公司可热闹了&#xff01;大家都在为搭建博客论坛系统忙得不可开交&#xff0c;尤其是在选服务器这件事儿上&#xff0c;那叫一个纠结。 同事 A 说&#xff1a;“咱得选个厉害的服务器&#xff0c;不然这论坛以后卡得跟蜗牛爬似的可咋办&#xff1f;” 同事 B 回应道&#…

【AcWing】【C++】模板之区间和与区间合并

最近在对程序设计算法进行复习&#xff0c;终于复习完了AcWing基础算法课的第一章&#xff0c;在此对第一章最后两个模板区间和与区间合并进行记录与分享。 区间和 题目描述与输入输出样例 题目来自于AcWing 802. 区间和。 思路 从题目描述来说&#xff0c;第一眼看来这是…

Fyne ( go跨平台GUI )中文文档-入门(一)

本文档注意参考官网(developer.fyne.io/) 编写, 只保留基本用法go代码展示为Go 1.16 及更高版本, ide为goland2021.2 这是一个系列文章&#xff1a; Fyne ( go跨平台GUI )中文文档-入门(一)-CSDN博客 Fyne ( go跨平台GUI )中文文档-Fyne总览(二)-CSDN博客 Fyne ( go跨平台GUI )…

镭射限高防外破预警装置-线路防外破可视化监控,安全尽在掌握中

镭射限高防外破预警装置-线路防外破可视化监控&#xff0c;安全尽在掌握中 在城市化浪潮的汹涌推进中&#xff0c;电力如同现代社会的生命之脉&#xff0c;其安全稳定运行直接关系到每一个人的生活质量和社会的整体发展。然而&#xff0c;随着建设的加速&#xff0c;电力设施通…

部署wordpress项目

一、先部署mariadb 二、在远程登录工具上进行登录测试&#xff0c;端口号为30117&#xff0c;用户为 root&#xff0c;密码为123 三、使用测试工具&#xff1a; [rootk8s-master aaa]# kubectl exec -it pods/cluster-test0-58689d5d5d-7c49r -- bash 四、部署wordpress [root…

论文阅读 | 基于流模型和可逆噪声层的鲁棒水印框架(AAAI 2023)

Flow-based Robust Watermarking with Invertible Noise Layer for Black-box DistortionsAAAI, 2023&#xff0c;新加坡国立大学&中国科学技术大学本论文提出一种基于流的鲁棒数字水印框架&#xff0c;该框架采用了可逆噪声层来抵御黑盒失真。 一、问题 基于深度神经网络…

【AI算法岗面试八股面经【超全整理】——NLP】

AI算法岗面试八股面经【超全整理】 概率论【AI算法岗面试八股面经【超全整理】——概率论】信息论【AI算法岗面试八股面经【超全整理】——信息论】机器学习【AI算法岗面试八股面经【超全整理】——机器学习】深度学习【AI算法岗面试八股面经【超全整理】——深度学习】NLP【A…

教师管理系统小程序+ssm论文源码调试讲解

第二章 开发工具及关键技术介绍 2.1 JAVA技术 Java主要采用CORBA技术和安全模型&#xff0c;可以在互联网应用的数据保护。它还提供了对EJB&#xff08;Enterrise JavaBeans&#xff09;的全面支持&#xff0c;java servlet AI&#xff0c;JS&#xff08;java server ages&…

Unity 热更新(HybridCLR+Addressable)-创建Addressable资源

三、创建Addressable资源 创建三个文件夹&#xff0c;放Addressable资源&#xff0c;里面对应放程序集&#xff0c;预制体以及场景 拖拽到Addressable Groups对应组中 其中文件名太长&#xff0c;带着路径&#xff0c;可以简化名字 创建一个脚本&#xff0c;对于这个脚本进行一…

vue3自定义hooks

引言 Vue3引入了组合式API&#xff0c;使得代码逻辑更自由、灵活。其中自定义Hooks能让我们将客服用的逻辑抽离成一个独立的函数&#xff0c;以实现在多个组件中复用的目的。可以简单理解成封装成一个模块&#xff0c;以方便其他地方调用。 实现 自定义hooks useDog impor…

杰发科技——Eclipse环境安装

文件已传到网盘&#xff1a; 1. 安装文件准备 2. 安装Make 默认路径&#xff1a;C:\Program Files (x86)\GnuWin32\bin\ 不复制的话会报错 Error: Program "make" not found in PATH 3. 安装工具链 默认路径&#xff1a;C:\Program Files (x86)\Arm GNU Toolchain…

闯关leetcode——69. Sqrt(x)

大纲 题目地址内容 解题代码地址 题目 地址 https://leetcode.com/problems/sqrtx/description/ 内容 Given a non-negative integer x, return the square root of x rounded down to the nearest integer. The returned integer should be non-negative as well. You mu…

计算机毕业设计之:基于微信小程序的中药材科普系统(源码+文档+讲解)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

模拟实现(优先级队列)priority_queue:优先级队列、仿函数、 反向迭代器等的介绍

文章目录 前言一、优先级队列二、仿函数三、 反向迭代器总结 前言 模拟实现&#xff08;优先级队列&#xff09;priority_queue&#xff1a;优先级队列、仿函数、 反向迭代器等的介绍 一、优先级队列 优先级队列本质是一个堆&#xff0c;使用vector容器进一步改进进行实现&am…

vue使用PDF.JS踩的坑--部署到服务器上显示pdf.mjs viewer.mjs找不到资源

之前项目使用的pdf.js 是2.15.349版本&#xff0c;最近换了一个4.6.82的版本&#xff0c;在本地上浏览文件运行的好好的&#xff0c;但是发布到服务器&#xff08;IIS&#xff09;上打不开文件&#xff0c;控制台提示找不到pdf.mjs viewer.mjs。 之前使用的2.15.349pdf和viewer…

mock虚拟接口技术

一、什么是mock mock指的就是使用mock创建出来的一个虚拟的接口 二、对于测试人员而言&#xff0c;我们为什么要使用mock 当我们进行接口测试时&#xff0c;如果对应的接口还没有开发好&#xff0c;但是我们又需要用到这个接口响应的信息&#xff0c;这个时候我们就可以使用…

邮件发送高级功能详解:HTML格式、附件添加与SSL/TLS加密连接

目录 一、邮件HTML格式设置 1.1 HTML邮件的优势 1.2 HTML邮件的编写 二、添加附件 2.1 附件的重要性 2.2 添加附件的代码示例 2.3 注意事项 三、使用SSL/TLS加密连接 3.1 SSL/TLS加密的重要性 3.2 SSL/TLS加密的工作原理 3.3 在邮件发送中启用SSL/TLS 3.3.1 邮件客…

智能算法躲避拥堵,高德企业用车上线“动态选路服务”为出行提效

近日&#xff0c;高德企业用车正式上线了一项全新服务——“动态选路服务”&#xff0c;旨在基于智能算法&#xff0c;动态规避突发拥堵路线&#xff0c;为企业用车用户提供更便捷、智能的出行方案。 以技术着眼细节&#xff0c;高德企业用车在帮助企业用车用户节约出行时间和…

飞睿智能实时雷达活体探测传感器模块,智能家居静止检测实时感知人员有无

随着科技的飞速发展&#xff0c;我们的生活正在经历着未有的创新。在这个创新的浪潮中&#xff0c;实时雷达活体探测传感器模块的技术正逐渐崭露头角&#xff0c;以其独特的优势为我们的生活带来安全与便捷。今天&#xff0c;我们就来详细探讨一下这项技术&#xff0c;看看它是…