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

大模型的开发应用(十二):RAG 与 LlamaIndex基础

这里写目录标题

  • 1 LlamaIndex 简要介绍
    • 1.1 核心价值
    • 1.2 核心组件
    • 1.3 核心流程
    • 1.4 为什么要用 LlamaIndex?
    • 1.5 典型应用场景
    • 1.6 与类似工具对比
    • 1.7 安装
    • 1.8 学习资源
  • 2 文档解析与 Document 对象
    • 2.1 示例文件与代码
    • 2.2 Document 对象的核心特性
    • 2.3 在 RAG 工作流程中的角色
    • 2.4 多文件解析与专业解析工具
    • 2.5 在线寻找解析工具
  • 3 文档切分与 Node 对象
    • 3.1 常用分块方式
    • 3.2 示例
    • 3.2 TextNode 对象
      • 3.2.1 TextNode 的核心特性和结构
      • 3.2.2 关键属性详解
      • 3.2.3 节点处理流程
      • 3.2.4 实际使用示例
      • 3.2.5 节点与文档的关系
      • 3.2.6 高级应用技巧
      • 3.2.7 为什么需要TextNode?
  • 4 文本嵌入与向量数据库
    • 4.1 文本嵌入模型
    • 4.2 ChromaDB简介
    • 4.3 ChromaDB基本使用
  • 5 模型响应
  • 6 官方文档的使用

1 LlamaIndex 简要介绍

LlamaIndex 是连接私有数据与大型语言模型的桥梁,核心目标是弥合私有数据与大型语言模型(如 GPT、Claude 或本地模型)之间的鸿沟,它能够轻松构建基于检索增强生成 (RAG)的强大应用。

1.1 核心价值

  • 数据接入:无缝连接各种私有或专有数据源(如 PDF、文档、数据库、API、云存储),让 LLM 能够访问和处理这些 LLM 本身无法看到的信息。
  • 弥补 LLM 固有缺陷
    • 实时信息缺失:LLM 训练数据通常滞后,LlamaIndex 可以利用最新数据。
    • 无法访问私有数据:使 LLM 可以基于您的私有信息进行推理和生成。
    • 幻觉问题:通过提供事实性基础数据,减少 LLM 产生无根据信息的风险。
  • 构建上下文感知应用:基于您的特定数据构建问答系统、智能聊天机器人、代理助手和知识管理系统。

1.2 核心组件

  1. 数据连接器(Ingestion/Data Loaders):
    • 支持从 150+ 种数据源加载数据(本地文件、S3、Notion、数据库等)。
  2. 索引(Indexing):
    • 将原始数据处理成优化的数据结构,便于快速查询检索。
    • 常用索引类型
      • 向量存储索引(VectorStoreIndex:基于语义嵌入(Embeddings)进行相似性搜索(最常见)。
      • 摘要索引(SummaryIndex:提取文本摘要,适合摘要型查询。
      • 树索引(TreeIndex:构建树状结构,实现多级查询(如摘要查询后下钻)。
      • 关键词表索引(KeywordTableIndex:基于关键词匹配进行搜索(稀疏检索)。
      • 知识图谱索引(KnowledgeGraphIndex:构建实体关系图进行结构化查询(较少用)。
  3. 检索器(Retrievers):
    • 根据查询,从索引中高效、准确地检索出最相关的上下文片段。
    • 支持高级检索技术:混合搜索(Hybrid Search)、重排序(Re-ranking)、元数据过滤(Metadata Filtering)等,提升召回率和精准度。
  4. 响应合成器(Response Synthesis):
    • 将检索到的上下文片段输入给 LLM,指导 LLM 生成最终的自然语言响应。
    • 提供多种合成策略(如 refine, compact, tree_summarize),平衡响应质量和成本。

1.3 核心流程

RAG一般分成两个阶段:1. 索引阶段:构建知识库;2. 查询阶段:从知识库检索相关上下文信息,以辅助 LLM 回答问题。

  1. 索引阶段
    索引阶段有几个关键步骤:文档解析、文本切分、文本块向量化、保存为本地知识库。在LlamaIndex中,流程如下:

在这里插入图片描述
Data Source 是数据源,可以是本地文档,也可以是数据库,或者从API接口中获得的数据;Data Connectors 是数据连接器,它负责将来自不同数据源的不同格式的数据解析出来,并转换为 LlamaIndex 支持的文档(Document)表现形式(不管什么数据进来,都会转化为这个格式),其中包含了文本和元数据;元数据经过 Embedding 模型进行文本向量化,并保存到向量数据库(例如ChromaDB、FAISS等)中,称为知识库(KnowledgeBase)。

  1. 查询阶段
    查询阶段的几个步骤:提示词文本向量化(和构建知识库时使用同一个 Embedding model)、知识库检索、检索结果后处理、模型响应。在 LlamaIndex中,过程示意图如下:

在这里插入图片描述
图中 Retrievers 是检索器,它定义如何高效地从知识库检索相关上下文信息;Node Postprocessors 是 Node后处理器,它对一系列文档节点(Node)实施转换、过滤或排名;Response Synthesizers 是响应合成器,它将用户的提示词和一组检索到的文本块合并形成上下文,然后利用 LLM 生成响应。

1.4 为什么要用 LlamaIndex?

  • 高效智能检索:自动化处理数据块切分(Chunking)、嵌入计算和高效检索,避免您重复造轮子。
  • 高度模块化与灵活
    • 兼容多种 LLM 提供商(OpenAI、Anthropic、Hugging Face 等)。
    • 支持主流向量数据库(Pinecone、Chroma、Qdrant、Milvus、FAISS 等)。
    • 可与 LangChain 等框架结合使用。
  • 功能先进强大
    • 支持多文档/多步骤复杂查询(代理、多跳问答)。
    • 提供工具增强代理(Agent Tooling)。
    • 集成评估和微调功能(Fine-tuning)。
  • 降低成本:通过优化上下文检索和合成策略,有效减少向 LLM 发送的 token 数量。
  • 专注于核心任务:抽象化底层复杂逻辑,让开发者聚焦于应用构建本身。

1.5 典型应用场景

  • 基于私有文档(手册、合同、报告)的 问答系统
  • 企业知识助手,让员工轻松访问内部知识库。
  • 数据感知型聊天机器人,能回答关于公司特定数据的问题。
  • 自动化研究工具,整合多个来源的信息。
  • 定制化业务分析,对报告数据进行自然语言查询。

1.6 与类似工具对比

工具核心侧重点最适合场景
LlamaIndex端到端 RAG 流程需要复杂上下文检索、连接私有数据与 LLM 的应用
LangChain通用 LLM 应用开发编排需要链接多个组件(模型、工具、记忆)的复杂流程
FAISS纯向量相似度搜索库仅需底层高效向量检索功能
Chroma开源向量数据库需要轻量级向量数据库进行本地嵌入存储和检索

关键洞察LlamaIndex 精于高效、结构化的上下文检索,并将其融入 LLM 生成流程(RAG)。LangChain 则擅长编排复杂的、多步骤的 LLM 任务链(例如使用工具、管理记忆)。两者功能重叠,可结合使用(LlamaIndex 常作为 LangChain 的检索模块)。

1.7 安装

新建一个 python 3.12 的环境

conda create -n llama_index python=3.12 -y
pip install llama-index		# 不要带版本号,我们直接安装最新的

上面只是基本安装了核心包,具体使用的时候,还需要安装很多附加包。

1.8 学习资源

  • 官方文档 (英文,最权威)
  • GitHub 仓库
  • 搜索中文社区(如知乎、微信公众号)中的相关教程和案例。

总结: LlamaIndex 是简化构建私有数据驱动的 LLM 应用的关键利器,尤其擅长解决 RAG 中的高效上下文检索与整合问题。它让您充分利用大模型能力,同时避免信息孤岛和不准确信息的问题。

2 文档解析与 Document 对象

2.1 示例文件与代码

以读取单个文本为例,假设在 data 目录下有一个名为 report_with_table.pdf 的文件,文件中只有一页,内容如下:

在这里插入图片描述

运行如下代码:

from llama_index import SimpleDirectoryReader# 读取单个文件,需要将文件路径放到列表里,然后传给 input_files 
reader = SimpleDirectoryReader(input_files=["data/report_with_table.pdf"]
)docs = reader.load_data()       # 返回的是一个列表,该列表只有一个 Document 对象
print(f"Loaded {len(docs)} docs")
print(docs)
print('-'*80)
print(docs[0])

输出:

Loaded 1 docs
[Document(id_='64f93aff-d379-4357-bc42-a994e0b5e144', embedding=None, metadata={'page_label': '1', 'file_name': 'report_with_table.pdf', 'file_path': 'data/report_with_table.pdf', 'file_type': 'application/pdf', 'file_size': 58537, 'creation_date': '2025-06-18', 'last_modified_date': '2025-06-18'}, excluded_embed_metadata_keys=['file_name', 'file_type', 'file_size', 'creation_date', 'last_modified_date', 'last_accessed_date'], excluded_llm_metadata_keys=['file_name', 'file_type', 'file_size', 'creation_date', 'last_modified_date', 'last_accessed_date'], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text_resource=MediaResource(embeddings=None, data=None, text='# 季度销售报告(示例内容)\n## 销售业绩\n本季度总销售额达到$1.2M,环比增长 15%。主要增长来自亚太地区...\n## 数据明细\n地区 Q1 销售额 Q2 销售额 增长率\n北美 $400K $420K 5%\n欧洲 $300K $330 10%\n亚太 $350K $450K 28.6%', path=None, url=None, mimetype=None), image_resource=None, audio_resource=None, video_resource=None, text_template='{metadata_str}\n\n{content}')]
--------------------------------------------------------------------------------
Doc ID: 64f93aff-d379-4357-bc42-a994e0b5e144
Text: # 季度销售报告(示例内容) ## 销售业绩 本季度总销售额达到$1.2M,环比增长 15%。主要增长来自亚太地区... ##
数据明细 地区 Q1 销售额 Q2 销售额 增长率 北美 $400K $420K 5% 欧洲 $300K $330 10% 亚太 $350K
$450K 28.6%

在 LlamaIndex 框架中,Document 对象是一个核心数据结构,它表示被处理数据的最小逻辑单元。

我们来解析 Document 对象的结构:

# 解析 Document 对象的结构
for key, field in docs[0].__dict__.items():print(f"{key} ====>", field)

输出

id_ ====> 64f93aff-d379-4357-bc42-a994e0b5e144
embedding ====> None
metadata ====> {'page_label': '1', 'file_name': 'report_with_table.pdf', 'file_path': 'data/report_with_table.pdf', 'file_type': 'application/pdf', 'file_size': 58537, 'creation_date': '2025-06-18', 'last_modified_date': '2025-06-18'}
excluded_embed_metadata_keys ====> ['file_name', 'file_type', 'file_size', 'creation_date', 'last_modified_date', 'last_accessed_date']
excluded_llm_metadata_keys ====> ['file_name', 'file_type', 'file_size', 'creation_date', 'last_modified_date', 'last_accessed_date']
relationships ====> {}
metadata_template ====> {key}: {value}
metadata_separator ====> text_resource ====> embeddings=None data=None text='# 季度销售报告(示例内容)\n## 销售业绩\n本季度总销售额达到$1.2M,环比增长 15%。主要增长来自亚太地区...\n## 数据明细\n地区 Q1 销售额 Q2 销售额 增长率\n北美 $400K $420K 5%\n欧洲 $300K $330 10%\n亚太 $350K $450K 28.6%' path=None url=None mimetype=None
image_resource ====> None
audio_resource ====> None
video_resource ====> None
text_template ====> {metadata_str}{content}

从 Document 对象的 text_resource 可以看到原始文本

2.2 Document 对象的核心特性

  1. 文本内容容器

    • 包含从原始文档中解析出的所有文本内容(包含表格转换后的文本)
    • 示例:print(docs[0].text[:200]) 会显示 PDF 前 200 个字符
  2. 元数据存储

    • 包含文件信息的字典:document.metadata
    • 自动填充的关键信息:
      {'page_label': '1',	# 页数'file_path': 'data/report_with_table.pdf','file_name': 'report_with_table.pdf','file_type': 'application/pdf',		# 文件类型'file_size': 58537,  # 字节大小'creation_date': '2023-01-15', # 创建日期'last_modified': '2023-10-01'  # 最后修改日期
      }
      
  3. 唯一标识符

    • 自动生成的文档 ID:document.id_
    • 用于在索引中唯一标识该文档

2.3 在 RAG 工作流程中的角色

原始文件
Document 对象
索引处理
节点 Node
文本块+元数据+嵌入向量
查询检索
  1. 数据准备阶段

    • 每个文件被转换为一个 Document 对象,即使 PDF 有多页也属于同一 Document
    • PDF/Word/HTML 等格式都会被解析为统一文本格式,图片/图表等非文本内容默认会被忽略(若不想被忽略则需要 OCR 扩展)
  2. 索引构建阶段

    • Document 被分割为更小的 Node (节点) 对象,每个对象对应一个文本块
  3. 元数据继承

    • 所有从 Document 分割出的 Node 都会继承原始元数据
    • 支持后续的元数据过滤查询:

总结:Document 对象是 LlamaIndex 数据处理流程中的原子单位,作为连接原始数据与向量索引的关键桥梁,封装了内容、元数据和身份信息,为后续的检索增强生成(RAG)提供结构化数据基础。

2.4 多文件解析与专业解析工具

2.2.1的示例只解析了一个文件,假如我有多个文件,该如何解析?

假设 /data/coding/llama-index/data 目录下的文档有6个,包含 txt、json、markdown、docx、pdf 等格式,如下图所示:

在这里插入图片描述

# 读取多个文件,此时写入文件夹路径
reader = SimpleDirectoryReader("/data/coding/llama-index/data")	docs = reader.load_data()       # 返回的是由 Document 构成的列表
print(f"Loaded {len(docs)} docs")

输出:

Loaded 6 docs

上面的示例表明,Simple Directory Reader 能解析大部分常用文档,但从 2.2.1 的示例来看,对PDF中的表格解析的很粗糙:

地区 Q1 销售额 Q2 销售额 增长率\n北美 $400K $420K 5%\n欧洲 $300K $330 10%\n亚太 $350K $450K 28.6%

这个结果很粗糙,真给模型用,它未必能读出结果来。

其实,针对不同格式的文件,推荐使用专业的解析工具,比如PDF,可以使用 pdfplumber

import pdfplumber
from pathlib import Path
from llama_index.core.schema import Document  # 注意核心 Document 类的导入路径def extract_text_with_pdfplumber(pdf_path: str) -> str:"""使用 pdfplumber 提取 PDF 文本内容(含表格)"""full_text = ""with pdfplumber.open(pdf_path) as pdf:for page in pdf.pages:# 提取普通文本page_text = page.extract_text()if page_text:full_text += page_text + "\n\n"print(full_text)  print('-'*80)# 提取表格数据(转换为文本表格)tables = page.extract_tables()for table in tables:print("\n表格内容:")for row in table:print(row)print('-'*80)return full_text.strip()# 使用 pdfplumber 处理 PDF
pdf_path = "data/report_with_table.pdf"
pdf_content = extract_text_with_pdfplumber(pdf_path)# 构建元数据字典
pdf_metadata = {"file_path": pdf_path,"file_name": Path(pdf_path).name,"file_type": "application/pdf","file_size": Path(pdf_path).stat().st_size,
}# 创建 LlamaIndex 的 Document 对象
document = Document(text=pdf_content,metadata=pdf_metadata,# 以下为可选参数id_=f"pdfplumber_{Path(pdf_path).stem}",  # 自定义文档IDexcluded_embed_metadata_keys=["file_size"],  # 元数据中不参与嵌入的字段
)# 现在可以用于构建索引
documents = [document]  # LlamaIndex 需要文档列表print(f"文档文本前200字符: {document.text[:200]}...")
print(f"元数据: {document.metadata}")

输出:

# 季度销售报告(示例内容)
## 销售业绩
本季度总销售额达到$1.2M,环比增长15%。主要增长来自亚太地区...
## 数据明细
地区 Q1销售额 Q2销售额 增长率
北美 $400K $420K 5%
欧洲 $300K $330 10%
亚太 $350K $450K 28.6%--------------------------------------------------------------------------------表格内容:
['地区', 'Q1销售额', 'Q2销售额', '增长率']
['北美', '$400K', '$420K', '5%']
['欧洲', '$300K', '$330', '10%']
['亚太', '$350K', '$450K', '28.6%']
--------------------------------------------------------------------------------
文档文本前200字符: # 季度销售报告(示例内容)
## 销售业绩
本季度总销售额达到$1.2M,环比增长15%。主要增长来自亚太地区...
## 数据明细
地区 Q1销售额 Q2销售额 增长率
北美 $400K $420K 5%
欧洲 $300K $330 10%
亚太 $350K $450K 28.6%...
元数据: {'file_path': 'data/report_with_table.pdf', 'file_name': 'report_with_table.pdf', 'file_type': 'application/pdf', 'file_size': 58537}

2.5 在线寻找解析工具

对于网页、数据库、API数据源的解析,可以在 LlamaIndex 官网寻找解析工具,假如我们想解析一个网页,内容如下:
在这里插入图片描述

下面讲解步骤:

先进入官网:
在这里插入图片描述

点击最上方的 Examples 选项卡:
在这里插入图片描述

在侧边栏找到 Data Connectors,展开就是 llama-index 官方针对各种数据源提供的解析工具
在这里插入图片描述
在这里插入图片描述

我们找到 Web Page Reader:
在这里插入图片描述

进去可以看到用法和示例,我尝试了一下示例,使用下面的代码提取信息:

from llama_index.readers.web import SimpleWebPageReader
from IPython.display import Markdown, displaydocuments = SimpleWebPageReader(html_to_text=True).load_data(["http://paulgraham.com/worked.html"]
)display(Markdown(f"<b>{documents[0].text_resource.text}</b>"))

上面的程序在控制台打印不出来,最好在 jupyter 中打印,效果很明显:

在这里插入图片描述
在这里插入图片描述

最简单、最基础的是 Simple Directory Reader,对于纯文档(没有表格、流程图、图片等)非常合适:

在这里插入图片描述

3 文档切分与 Node 对象

一个文档可能会非常大,比例硕士博士的毕业论文,动辄几十上百页,必须对齐进行分块处理。

3.1 常用分块方式

分块的常用方法有以下几种:
(1)按固定字数/token数分块,这种一般用于格式化数据,例如古诗词、对联;
(2)按段落分块,对于每个自然段落的含义相对独立的文章,这种分块方式很适合;
(3)按语义分块,这是使用文本嵌入模型,让模型计算每条句子的语义,然后与前一句进行相似度比较,如果低于阈值就作为新的一块开始,这种分块方式较优,是最常用的方式,但计算资源消耗较大;
(4)按业务逻辑分块,若上述分块方式都无法对文档进行合理分块,那么只能根据业务逻辑,自己设计一种分块方式;
(5)人工分块,对于无法使用固定规则处理的文档,那么只能采用人工了,比如,一些散文、歌词等,你很难用程序去划分,因为划分的结果总少了那么一丝“感觉”,但这种感觉又很难用规则描述出来,这个时候只能上人工。

总的来说,文档切分是为了更好的适配后续的检索,要尽量做到每个分块的含义相对完整且只包含一个知识点,避免知识点被打断,没有固定的方案,都是要结合项目需求来设计切分方案。

此外,对于小说、新闻等内容,切分的时候要保留每个分块的上下文关系,这样AI才能理解块与块之间的顺序关系,因此需要一定的重叠度,即第一块和第二块有10%~20%的字数重叠,第二块和第三块有10%~20%的字数重叠,以此类推。分块是否需要重叠,关键是要看块与块之间是否需要包含上下文关系,即是否需要有关联。

3.2 示例

假设现有一篇文章(文件名为 ai.txt),内容如下:

人工智能的发展史是一个充满探索与突破的历程,以下按照时间线为您梳理其关键阶段和里程碑事件:
1. 起源阶段(20世纪50年代)
1950年:英国数学家艾伦·图灵(Alan Turing)提出“图灵测试”,这是首次尝试定义机器智能的标准,标志着人工智能概念的萌芽。
1956年:美国达特茅斯学院召开第一次人工智能研讨会,正式提出“人工智能”这一术语,这被认为是人工智能作为一门学科的诞生标志。
早期研究:这一时期的研究主要集中在符号主义和逻辑推理,例如机器定理证明和跳棋程序等。
2. 第一次低谷(20世纪70年代)
原因:由于技术限制和资金不足,人工智能研究未能达到预期的高期望,导致第一次“人工智能寒冬”。
影响:研究重点转向更具体的应用领域,如专家系统和自然语言处理。
3. 复苏与初步应用(20世纪80-90年代)
1980年代:专家系统(如医疗诊断系统)开始被广泛应用,人工智能在商业领域初露锋芒。
1997年:IBM的“深蓝”计算机在国际象棋比赛中战胜世界冠军加里·卡斯帕罗夫,这是人工智能在复杂决策任务中的重大突破。
4. 第二次低谷(20世纪90年代末至21世纪初)
原因:尽管有“深蓝”等成功案例,但人工智能的整体发展仍受限于计算能力和数据规模,导致第二次“人工智能寒冬”。
转折点:随着互联网的普及和大数据技术的发展,人工智能研究逐渐复苏。
5. 深度学习与复兴(21世纪初至今)
2006年:加拿大科学家杰弗里·辛顿(Geoffrey Hinton)提出深度学习理论,为人工智能的突破奠定了基础。
2012年:深度学习在图像识别任务中首次超越人类表现,标志着人工智能进入实用化阶段。
2016年:谷歌的AlphaGo击败围棋世界冠军李世石,展示了人工智能在复杂策略游戏中的强大能力。
2020年代:大型语言模型(如GPT-3、GPT-4)的出现,使人工智能在自然语言处理、代码生成等领域取得突破性进展。
6. 当前与未来展望
当前应用:人工智能已广泛应用于医疗、交通、金融、教育等领域,例如自动驾驶、智能客服和个性化推荐系统。
未来趋势:随着算力的提升和算法的优化,人工智能有望在更多复杂任务中实现突破,同时伦理与安全问题也成为研究重点。

现在要将其分块,我们这里演示一下固定token数分块和语义分块。

固定token数分块

from llama_index.core import SimpleDirectoryReader
from llama_index.embeddings.huggingface import HuggingFaceEmbedding# 加载文档
documents = SimpleDirectoryReader(input_files=["/data/coding/llama-index/data/test.txt"]).load_data()# 案例1:固定token数分块 
from llama_index.core.node_parser import TokenTextSplitter 
fixed_splitter = TokenTextSplitter(chunk_size=200, chunk_overlap=20) 
fixed_nodes = fixed_splitter.get_nodes_from_documents(documents)  # 返回的是一个 TextNode 对象构成的列表
print("固定分块示例:", [len(n.text) for n in fixed_nodes]) 

输出:

固定分块示例: [85, 155, 97, 127, 159, 123, 62, 117]

LlamaIndex 的分割器使用 tiktoken 等 tokenizer,其规则复杂(如合并空格、分割词缀),这里输出中的 [85, 155, 97, …] 是字符数统计,与 token 数无关,每个块的实际 token 数会接近 200,但字符数取决于文本密度(英文平均 1 token ≈ 4 字符,中文 ≈ 2 字符)。如果还是搞不明白,那就先忽略,因为实际项目中很少会按固定 token 数来分割文档。

我们来看看每个分块的内容:

for n in fixed_nodes:print(n.text)print('-'*80)

输出

人工智能的发展史是一个充满探索与突破的历程,以下按照时间线为您梳理其关键阶段和里程碑事件:
1. 起源阶段(20世纪50年代)
1950年:英国数学家艾伦·图灵(Alan
--------------------------------------------------------------------------------
Turing)提出“图灵测试”,这是首次尝试定义机器智能的标准,标志着人工智能概念的萌芽。
1956年:美国达特茅斯学院召开第一次人工智能研讨会,正式提出“人工智能”这一术语,这被认为是人工智能作为一门学科的诞生标志。
早期研究:这一时期的研究主要集中在符号主义和逻辑推理,例如机器定理证明和跳棋程序等。
2.
--------------------------------------------------------------------------------
第一次低谷(20世纪70年代)
原因:由于技术限制和资金不足,人工智能研究未能达到预期的高期望,导致第一次“人工智能寒冬”。
影响:研究重点转向更具体的应用领域,如专家系统和自然语言处理。
3.
--------------------------------------------------------------------------------
复苏与初步应用(20世纪80-90年代)
1980年代:专家系统(如医疗诊断系统)开始被广泛应用,人工智能在商业领域初露锋芒。
1997年:IBM的“深蓝”计算机在国际象棋比赛中战胜世界冠军加里·卡斯帕罗夫,这是人工智能在复杂决策任务中的重大突破。
4.
--------------------------------------------------------------------------------
第二次低谷(20世纪90年代末至21世纪初)
原因:尽管有“深蓝”等成功案例,但人工智能的整体发展仍受限于计算能力和数据规模,导致第二次“人工智能寒冬”。
转折点:随着互联网的普及和大数据技术的发展,人工智能研究逐渐复苏。
5. 深度学习与复兴(21世纪初至今)
2006年:加拿大科学家杰弗里·辛顿(Geoffrey
--------------------------------------------------------------------------------
...
6. 当前与未来展望
当前应用:人工智能已广泛应用于医疗、交通、金融、教育等领域,例如自动驾驶、智能客服和个性化推荐系统。
未来趋势:随着算力的提升和算法的优化,人工智能有望在更多复杂任务中实现突破,同时伦理与安全问题也成为研究重点。
--------------------------------------------------------------------------------

语义分块
这里我们使用 paraphrase-multilingual-MiniLM-L12-v2 作为文本嵌入模型:

from llama_index.core.node_parser import SemanticSplitterNodeParser# 初始化模型和解析器
embed_model = HuggingFaceEmbedding(#指定了一个预训练的sentence-transformer模型的路径model_name="/data/coding/model_weights/sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
)semantic_parser = SemanticSplitterNodeParser(buffer_size=1,breakpoint_percentile_threshold=90,embed_model=embed_model
)# 执行语义分割
semantic_nodes = semantic_parser.get_nodes_from_documents(documents)	# 分割的结果是由 TextNode 对象构成的列表# 打印结果
print(f"语义分割节点数: {len(semantic_nodes)}")
for i, node in enumerate(semantic_nodes[:2]):  # 只打印前两个节点print(f"\n节点{i+1}:\n{node.text}")print("-"*50)

输出:

语义分割节点数: 2节点1:
人工智能的发展史是一个充满探索与突破的历程,以下按照时间线为您梳理其关键阶段和里程碑事件:
1. 起源阶段(20世纪50年代)
1950年:英国数学家艾伦·图灵(Alan Turing)提出“图灵测试”,这是首次尝试定义机器智能的标准,标志着人工智能概念的萌芽。
1956年:美国达特茅斯学院召开第一次人工智能研讨会,正式提出“人工智能”这一术语,这被认为是人工智能作为一门学科的诞生标志。
早期研究:这一时期的研究主要集中在符号主义和逻辑推理,例如机器定理证明和跳棋程序等。
2. 第一次低谷(20世纪70年代)
原因:由于技术限制和资金不足,人工智能研究未能达到预期的高期望,导致第一次“人工智能寒冬”。
影响:研究重点转向更具体的应用领域,如专家系统和自然语言处理。
3. 复苏与初步应用(20世纪80-90年代)
1980年代:专家系统(如医疗诊断系统)开始被广泛应用,人工智能在商业领域初露锋芒。
1997年:IBM的“深蓝”计算机在国际象棋比赛中战胜世界冠军加里·卡斯帕罗夫,这是人工智能在复杂决策任务中的重大突破。
4. 第二次低谷(20世纪90年代末至21世纪初)
原因:尽管有“深蓝”等成功案例,但人工智能的整体发展仍受限于计算能力和数据规模,导致第二次“人工智能寒冬”。
转折点:随着互联网的普及和大数据技术的发展,人工智能研究逐渐复苏。
5. 
--------------------------------------------------节点2:
深度学习与复兴(21世纪初至今)
2006年:加拿大科学家杰弗里·辛顿(Geoffrey Hinton)提出深度学习理论,为人工智能的突破奠定了基础。
2012年:深度学习在图像识别任务中首次超越人类表现,标志着人工智能进入实用化阶段。
2016年:谷歌的AlphaGo击败围棋世界冠军李世石,展示了人工智能在复杂策略游戏中的强大能力。
...
6. 当前与未来展望
当前应用:人工智能已广泛应用于医疗、交通、金融、教育等领域,例如自动驾驶、智能客服和个性化推荐系统。
未来趋势:随着算力的提升和算法的优化,人工智能有望在更多复杂任务中实现突破,同时伦理与安全问题也成为研究重点。
--------------------------------------------------

上面的程序中,出现了TextNode对象,它的全称是 llama_index.core.schema.TextNode,稍后会详细介绍。

3.2 TextNode 对象

在 LlamaIndex 中,TextNode 对象是构建索引的基本单元,它是 Document 对象经过分割处理后生成的更小文本块。以下是 TextNode 的详细介绍:

3.2.1 TextNode 的核心特性和结构

class TextNode(BaseNode):text: str               # 节点实际的文本内容embedding: List[float]  # 文本对应的嵌入向量metadata: Dict[str, Any] # 元数据字典excluded_embed_metadata_keys: List[str] # 不参与嵌入计算的元数据键excluded_llm_metadata_keys: List[str]  # 不传递给LLM的元数据键relationships: Dict[NodeRelationship, RelatedNodeInfo] # 节点间关系hash: str               # 内容哈希值class_name: str         # 类名标识(固定为"TextNode")

3.2.2 关键属性详解

  1. text (str)

    • 当前节点包含的实际文本内容(通常是200-500个token)
    • 从原始文档分割出来的连贯语义片段
    • 示例:"LlamaIndex 是一个开源的 Python 框架,用于..."
  2. embedding (List[float])

    • 文本对应的向量表示(128-1536维浮点数)
    • 用于相似性搜索的数学表示
    • 默认情况下这个属性是空的,需要调用嵌入模型生成
  3. metadata (Dict[str, Any])

    • 继承自父Document的元数据
    • 自动包含的属性:
      {'file_path': '/data/coding/llama-index/data/ai.txt','file_name': 'ai.txt','document_id': 'd758a8a4-fc0b...', # 指向父Document'page_label': '1',               # 如果文档分页'chunk_size': 200,                # 分块大小'chunk_overlap': 20               # 重叠大小
      }
      
  4. relationships (节点关系)

    • 定义节点间的逻辑关系:
      {NodeRelationship.PREVIOUS: RelatedNodeInfo(node_id="node1"),NodeRelationship.NEXT: RelatedNodeInfo(node_id="node3"),NodeRelationship.PARENT: RelatedNodeInfo(node_id="doc_root"),NodeRelationship.SOURCE: RelatedNodeInfo(node_id="doc_id")
      }
      
    • 维护文本块的先后顺序和文档结构
  5. excluded_xxx_metadata_keys

    • 精细化控制元数据的使用:
    • excluded_embed_metadata_keys:指定哪些元数据不参与嵌入向量计算
    • excluded_llm_metadata_keys:指定哪些元数据不传递给语言模型

3.2.3 节点处理流程

Document
文本分割器
TextNode 1
TextNode 2
TextNode ...n
嵌入模型
向量存储

3.2.4 实际使用示例

# 查看分割后的第一个节点
first_node = fixed_nodes[0]		# first_node 是按固定token数分块得到的结果print(f"节点ID: {first_node.id_}")
print(f"文本内容: {first_node.text[:50]}...")
print(f"文本长度: {len(first_node.text)}字符")
print(f"元数据: {first_node.metadata}")
print(f"来源文档ID: {first_node.ref_doc_id}")# 手动设置嵌入向量(通常在索引时自动完成)
embed_model = HuggingFaceEmbedding(model_name="/data/coding/model_weights/sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2")
first_node.embedding = embed_model.get_text_embedding(first_node.text)
print(f"嵌入向量维度: {len(first_node.embedding)}")

输出:

节点ID: 5695e0c9-9080-4fde-a158-71eca5ee48a4
文本内容: 人工智能的发展史是一个充满探索与突破的历程,以下按照时间线为您梳理其关键阶段和里程碑事件:
1....
文本长度: 85字符
元数据: {'file_path': '/data/coding/llama-index/data/ai.txt', 'file_name': 'ai.txt', 'file_type': 'text/plain', 'file_size': 2504, 'creation_date': '2025-06-21', 'last_modified_date': '2025-06-18'}
来源文档ID: 92509469-6d0c-4f24-a101-151cfcf662ca
嵌入向量维度: 384

3.2.5 节点与文档的关系

Document
Node 1
Node 2
Node 3
嵌入向量
嵌入向量
嵌入向量
向量数据库

3.2.6 高级应用技巧

  1. 自定义元数据

    for node in fixed_nodes:node.metadata["custom_tag"] = "important"node.excluded_embed_metadata_keys = ["file_size"]  # 文件大小不参与向量计算
    
  2. 关系维护

    # 建立双向链接
    for i in range(len(fixed_nodes)-1):fixed_nodes[i].relationships[NodeRelationship.NEXT] = fixed_nodes[i+1].id_fixed_nodes[i+1].relationships[NodeRelationship.PREVIOUS] = fixed_nodes[i].id_
    
  3. 混合节点类型

    from llama_index.core.schema import ImageNode
    # 可以与图像节点等组成混合索引
    
  4. 精细检索控制(了解即可)

    query_engine = index.as_query_engine(node_postprocessors=[MetadataReplacementPostProcessor(target_metadata_key="custom_tag")]
    )
    

3.2.7 为什么需要TextNode?

  1. 粒度控制:相比整个文档,节点级检索更精准
  2. 上下文管理:解决LLM的上下文长度限制
  3. 效率优化:避免每次查询处理整个文档
  4. 关系建模:保持文本块的逻辑顺序
  5. 多模态扩展:统一处理文本、图像等多类型内容

TextNode 是 LlamaIndex 架构中的原子操作单元,它使大文档处理、精准检索和高效生成成为可能,构成了 RAG(检索增强生成)应用的核心基础结构。

4 文本嵌入与向量数据库

文档切分之后,就是输入到文本嵌入模型中,将其转成固定长度的向量,然后存入向量数据库中。

4.1 文本嵌入模型

文本嵌入模型都可以理解成 BERT,用于对文本进行编码,将长度变化的文本转换成固定长度的向量。目前用的最多的三个向量维度是 384、768、1024,维度越大,进行嵌入计算时消耗的算力就越大。

转成固定长度向量的目的,一是为了方便存储,二是为了方便进行文本相似度计算。文本相似度计算一般都是用余弦相似度,因为余弦相似度自带归一化,取值范围为 [-1, 1],当然,一般情况下,检索的时候向量数据库都会对负值进行处理。例如,ChromaDB 评估两个文本的相似度的方式是通过距离来计算的,即 distance = 1 - cosine_similarity(a, b),这样就避开了负数的情况,两个向量距离越近,则说明对应的文本越相似;另外,也可以通过元数据过滤排除负相关文档,代码如下(看不懂没关系,知道有这么回事就可以):

# 添加元数据标识
collection.add(embeddings=[[0.8, 0.6], [-0.8, -0.5]],metadatas=[{"polarity":"pos"}, {"polarity":"neg"}],ids=["pos", "neg"]
)# 只检索正向相关文档
results = collection.query(query_embeddings=[[0.9, 0.5]],where={"polarity": {"$eq": "pos"}}  # 元数据过滤
)

文本嵌入模型的选型上,一般只需要考虑两个方面,语言和维度。如果纯中文或者中文占主导地位,那就用中文语料训练出来的,如果是多语言场景,那就用多语言的模型;需要检索精度高就选1024,需要速度快就选384或者768。

工业上用的比较多的嵌入模型有:BGE系列(例如合同/政策文件等,选择BGE-M3)、 M3E-base(社交媒体分析)、paraphrase-multilingual-MiniLM-L12-v2(多语言)。

可以在魔搭上,根据 句子相似度 和 文本向量 去找文本嵌入模型:
在这里插入图片描述

4.2 ChromaDB简介

目前 RAG 存储向量数据库,用的最多的就是 ChromaDB,所以我们这里不介绍其他向量数据库了。

Chroma 是一款开源的向量数据库,专为高效存储和检索高维向量数据设计。其核心能力在于语义相似性搜索,支持文本、图像等嵌入向量的快速匹配,广泛应用于大模型上下文增强(RAG)、推荐系统、多模态检索等场景。与传统关系型数据库不同,Chroma 基于向量距离(如余弦相似度、欧氏距离)衡量数据关联性,而非关键词匹配。

核心优势

  • 轻量易用:以 Python/JS 包形式嵌入代码,无需独立部署,适合快速原型开发。
  • 灵活集成:支持自定义嵌入模型(如 OpenAI、HuggingFace),兼容 LangChain 等框架。
  • 高性能检索:采用 HNSW 算法优化索引,支持百万级向量毫秒级响应。
  • 多模式存储:内存模式用于开发调试,持久化模式支持生产环境数据落地。

安装

pip install chromadb

4.3 ChromaDB基本使用

ChromaDB 只需要记住几个操作:创建客户端与集合,还有对数据库增删改查,以及统计条目数。

下面是创建客户端与集合,以及数据库的增加与查询操作:

import chromadb
from sentence_transformers import SentenceTransformer# chromadb 现在已经不支持直接加载 SentenceTransformer 模型了,因此需要用 __call__ 函数封装
# 但 llama-index 已经将 chromadb 和 SentenceTransformer 做了集成,不需要我们自己写 __call__ 函数了
class SentenceTransformerEmbeddingFunction:def __init__(self, model_path: str, device: str = "cuda"):self.model = SentenceTransformer(model_path, device=device)def __call__(self, input: list[str]) -> list[list[float]]:if isinstance(input, str):input = [input]return self.model.encode(input, convert_to_numpy=True).tolist()# 创建/加载集合(含自定义嵌入函数)
embed_model = SentenceTransformerEmbeddingFunction(model_path="/data/coding/EmotionalDialogue/model_weights/sungw111/text2vec-base-chinese-sentence",# model_path="/data/coding/model_weights/sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2",device="cuda"  # 无 GPU 改为 "cpu"
)# 创建客户端和集合
client = chromadb.Client()  # 临时存放在内存中(内存模式)
# client = chromadb.PersistentClient(path="/path/to/save") # 数据保存至本地目录(持久化模式)
collection = client.create_collection("my_knowledge_base",metadata={"hnsw:space": "cosine"},    # 当前特征嵌入空间中的数据,使用余弦相似度进行搜索embedding_function=embed_model)# 添加文档
collection.add(documents=["RAG是一种检索增强生成技术", "向量数据库存储文档的嵌入表示","三英战吕布"],       # 文档metadatas=[{"source": "tech_doc"}, {"source": "tutorial"}, {"source": "tutorial1"}],    # 元数据,给每个文档取一个元数据的名称(名称要唯一),方便后续查询ids=["doc1", "doc2","doc3"]     # 向量的索引名称
)
# 存的时候就会去衡量向量与向量之间的相似度,把相关性高的向量放到了一起,以提高后续检索速度# 查询相似文档
results = collection.query(query_texts=["什么是RAG技术?"],n_results=3     # 返回最相似的3个向量
)print(results)

输出

{'ids': [['doc1', 'doc2', 'doc3']], 'embeddings': None, 'documents': [['RAG是一种检索增强生成技术', '向量数据库存储文档的嵌入表示', '三英战吕布']], 'uris': None, 'included': ['metadatas', 'documents', 'distances'], 'data': None, 'metadatas': [[{'source': 'tech_doc'}, {'source': 'tutorial'}, {'source': 'tutorial1'}]], 'distances': [[0.09666752815246582, 0.2053605318069458, 0.2142857313156128]]}

结果中 ids 是与待查寻文本最接近的三个文档的索引名称,distance 是三个文档与待查文本的距离(不是相似度)。

接下来是修改、删除、统计条目:

# 修改更新
collection.update(ids=["doc1"],  # 使用已存在的IDdocuments=["更新后的RAG技术内容"]
)# 查看更新后的内容 - 方法1:获取特定ID的内容
updated_docs = collection.get(ids=["doc1"])
print("更新后的文档内容:", updated_docs["documents"])# 查看更新后的内容 - 方法2:查询所有文档
all_docs = collection.get()
print("集合中所有文档:", all_docs["documents"])# 删除内容
collection.delete(ids=["doc1"])
all_docs = collection.get()
print("集合中所有文档:", all_docs["documents"])#统计条目
print(collection.count())

输出:

更新后的文档内容: ['更新后的RAG技术内容']
集合中所有文档: ['更新后的RAG技术内容', '向量数据库存储文档的嵌入表示', '三英战吕布']
集合中所有文档: ['向量数据库存储文档的嵌入表示', '三英战吕布']
2

5 模型响应

这是RAG的最后一步,实际上就是把检索结果合并,然后一起喂给模型,获得模型的回复。我们用一个程序示例来讲解检索得到向量后如何获得响应。

文档为 Xtuner 在Github上的说明文档(即readme),下面是程序:

from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.core import Settings,SimpleDirectoryReader,VectorStoreIndex
from llama_index.llms.huggingface import HuggingFaceLLM#初始化一个HuggingFaceEmbedding对象,用于将文本转换为向量表示,
# 在此之前需要安装 llama_index.embeddings.huggingface,pip install llama_index.embeddings.huggingface
embed_model = HuggingFaceEmbedding(#指定一个预训练的sentence-transformer模型的路径,只能加载 sentence-transformer 的模型,其他模型都不行model_name="/data/coding/model_weights/sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2" # L12-V2 模型
)#将创建的嵌入模型赋值给全局设置的embed_model属性,这样在后续的索引构建过程中,就会使用这个模型
Settings.embed_model = embed_model#使用HuggingFaceLLM加载本地大模型
llm = HuggingFaceLLM(model_name="/data/coding/model_weights/Qwen/Qwen1.5-1.8B-Chat",tokenizer_name="/data/coding/model_weights/Qwen/Qwen1.5-1.8B-Chat",model_kwargs={"trust_remote_code":True},tokenizer_kwargs={"trust_remote_code":True})
# model_name 和 tokenizer_name 是本地模型的路径,model_kwargs 和 tokenizer_kwargs 这两个参数先不管#设置全局的llm属性,这样在索引查询时会使用这个模型。
Settings.llm = llm#读取文档,将数据加载到内存,会转化为 document 对象,这里需要安装 Markdown 的解析工具
documents = SimpleDirectoryReader(input_files=["/data/coding/llama-index/data/README_zh-CN.md"]).load_data()  
# print(documents)#创建一个VectorStoreIndex,并使用之前加载的文档来构建向量索引
#此索引将文档转换为向量,并存储这些向量(内存)以便于快速检索
index = VectorStoreIndex.from_documents(documents)#创建一个查询引擎,这个引擎可以接收查询并返回相关文档的响应。
query_engine = index.as_query_engine()          # 通过 index 构建一个查询引擎
rsp = query_engine.query("xtuner是什么?")      # 通过查询引擎来查询用户的问题,
# 查询的时候,会根据向量索引做一个相似度匹配,匹配的结果再扔给大模型,最后生成响应
# 如果问题与知识库有相关性,则答复的质量会非常高print(rsp)
# 怎么判断模型生成的内容是幻觉?你得对这个领域有所了解,但不需要很深入,这样就有一定的判别能力

输出:

问题描述中的 "XTuner" 是一个工具,用于高效地调整大型语言模型 (LLM) 的超参数,该工具具有以下特点:
1. 自动分配高性能计算单元 (如 FlashAttention、Triton kernels 等) 加速模型训练。
2. 兼容 DeepSpeed 平台,可以轻松应用于各种 ZeRO 训练优化策略。
3. 支持灵活且多样的大规模语言模型,如 InternLM、Mixtral-8x7B、Llama 2、ChatGLM、Qwen 和 Baichuan。
4. 支持多种文本图模型 LLaVA 的预训练与微调,通过 XTuner 训得模型 LLaVA-InternLM2-20B 表现优异。
5. 设计了数据管道,可以支持任意数据格式,并具有开源数据或自定义数据资源快速获取的能力。
6. 支持增量预训练、指令微调与 Agent 微调,以及预先定义的对话模板和大规模评测工具库。
7. 支持多种微调算法,如 ZeRO-1、ZeRO-2、ZeRO-3 等,可根据实际需求进行最佳微

作为对比,我们来看看不带知识库时,模型的回答:

from llama_index.core.llms import ChatMessage
from llama_index.llms.huggingface import HuggingFaceLLM#使用HuggingFaceLLM加载本地大模型,需要先安装 llama_index.llms.huggingface
llm = HuggingFaceLLM(model_name="/data/coding/model_weights/Qwen/Qwen1.5-1.8B-chat",tokenizer_name="/data/coding/model_weights/Qwen/Qwen1.5-1.8B-chat",model_kwargs={"trust_remote_code":True},tokenizer_kwargs={"trust_remote_code":True})#借助llama-index调用本地大模型进行提问
#调用模型chat引擎得到回复,消息必须封装成 llama-index 的 message,即 ChatMessage
rsp = llm.chat(messages=[ChatMessage(content="xtuner是什么?")])    # 提问的时候,不需要自己指定角色
print(rsp)  # 你问了就会给你答案,假如模型不知道,也会给你要给答案,即幻觉

输出:

assistant: "xtuner"是一个中文词语,原意是指"音源搜索器""音乐搜索器",通常用于在线音乐播放平台或其他音频应用程序中。在实际应用中,xtuner通常指的是一个能够自动发现和推荐音乐的系统,它使用机器学习、深度学习等技术,根据用户的听歌历史、喜好、行为习惯等多种因素,为用户推荐最符合其口味的新歌曲或专辑。以下是一个简单的 xtuner 的基本工作原理:1. 数据收集:xtuner 首先需要从各种来源获取用户的音乐数据,如 Spotify 等音乐平台的歌曲库、在线流媒体服务(如 Apple Music、Tidal、YouTube Music)的歌曲列表、电台节目推荐等。2. 建立模型:利用深度学习、自然语言处理(NLP)等技术,建立一个音乐情感分析模型。该模型可以识别用户喜欢的音乐类型、情感色彩(如愉悦、悲伤、平静)、歌手、流派等特征,并将这些信息编码成特征向量。3. 用户画像构建:基于用户的音乐历史、行为习惯等数据,通过机器学习算法对用户进行聚类,创建个性化的用户画像。这包括用户的听歌

Qwen1.5 在预训练的时候,数据集中没有关于 Xtuner 的资料,所以 Qwen1.5 不知道这个工具,而模型获得输入之后就必然会有输出,所以它就根据自己的理解(或想象)进行回答,这个回答就是幻觉,通俗点说,就是一本正经的胡说八道。

6 官方文档的使用

假如我想看看LlamaIndex是如何集成 ChromaDB 的,可以按照下面的步骤:

  1. 点击右上角的搜索框
    在这里插入图片描述
    2.搜索

在这里插入图片描述
3.点击chromadb

在这里插入图片描述

  1. 可以看到LlamaIndex中如何chromadb的文档了,往下翻能看到示例

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

相关文章:

大模型的开发应用(十二):RAG 与 LlamaIndex基础

这里写目录标题 1 LlamaIndex 简要介绍1.1 核心价值1.2 核心组件1.3 核心流程1.4 为什么要用 LlamaIndex&#xff1f;1.5 典型应用场景1.6 与类似工具对比1.7 安装1.8 学习资源 2 文档解析与 Document 对象2.1 示例文件与代码2.2 Document 对象的核心特性2.3 在 RAG 工作流程中…...

Java面试题025:一文深入了解数据库Redis(1)

欢迎大家关注我的JAVA面试题专栏&#xff0c;该专栏会持续更新&#xff0c;从原理角度覆盖Java知识体系的方方面面。 一文吃透JAVA知识体系&#xff08;面试题&#xff09;https://blog.csdn.net/wuxinyan123/category_7521898.html?fromshareblogcolumn&sharetypeblogco…...

Web攻防-XSS跨站Cookie盗取数据包提交网络钓鱼BEEF项目XSS平台危害利用

知识点&#xff1a; 1、Web攻防-XSS跨站-手工代码&框架工具&在线平台 2、Web攻防-XSS跨站-Cookie盗取&数据提交&网络钓鱼 演示案例-WEB攻防-XSS跨站-Cookie盗取&数据提交&网络钓鱼&Beef工具 1、XSS跨站-攻击利用-凭据盗取 条件&#xff1a;无防…...

(LeetCode 面试经典 150 题) 169. 多数元素(哈希表 || 二分查找)

题目&#xff1a;169. 多数元素 方法一&#xff1a;二分法&#xff0c;最坏的时间复杂度0(nlogn)&#xff0c;但平均0(n)即可。空间复杂度为0(1)。 C版本&#xff1a; int nnums.size();int l0,rn-1;while(l<r){int mid(lr)/2;int ans0;for(auto x:nums){if(xnums[mid]) a…...

71、单元测试-Junit5简介

71、单元测试-Junit5简介 # JUnit 5 简介 JUnit 5 是 Java 平台上最流行的单元测试框架之一&#xff0c;是 JUnit 的重大升级版本&#xff0c;引入了许多新特性和改进&#xff0c;旨在提供更现代化、灵活和强大的测试体验。 ## 主要组成部分 JUnit 5 由三个模块组成&#xff1a…...

IEC61850 一致性测试中的 UCA 测试

一、IEC61850 与 UCA 的关系背景 标准演进&#xff1a;IEC61850 是电力系统自动化领域的国际通信标准&#xff0c;其发展与美国 UCA&#xff08;User Communications Architecture&#xff09;标准密切相关。2001 年&#xff0c;UCA 国际用户组织与 IEC 合作&#xff0c;将 UC…...

ProtoBuf:proto3 语法详解

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;ProtoBuf 在语法详解部分&#xff0c;依旧使⽤项⽬推进的⽅式完成讲解。这个部分会对通讯录进⾏多次升级&#xff0c;使⽤2.x表⽰升级的版本&#xff0c;最终将会升级如下内容&#xff1a; 不再打…...

博图SCL语言GOTO语句深度解析:精准跳转

在SCL编程中&#xff0c;**GOTO语句**是控制流程的底层工具&#xff0c;它允许程序无条件跳转到指定的**标签位置**。虽然现代编程中较少使用&#xff0c;但在特定工业场景下仍能发挥独特价值。 GOTO语句核心机制 基本语法结构 // 定义标签 <标签名>: // 跳转指令 GOTO…...

面试题-在ts中有两个类型,一个是a,一个是b,这两个联合起来就是c,如何实现联合

在 TypeScript 中&#xff0c;若要将两个类型 a 和 b 联合成一个新类型 c&#xff0c;可以使用 联合类型&#xff08;Union Type&#xff09; 或 交叉类型&#xff08;Intersection Type&#xff09;&#xff0c;具体取决于你的需求&#xff1a; 一、联合类型&#xff08;Unio…...

Mac电脑-触摸板增强工具-BetterTouchTool

BetterTouchTool mac 触摸板增强工具&#xff0c;允许用户使用各种手势来控制其计算机。 Bettertouchtool mac是一个小而高效的macOS应用程序&#xff0c;旨在帮助您为手势定义快捷方式。 此外&#xff0c;Bettertouchtool可用于使用常规鼠标和键盘快捷键&#xff0c;并提供伴…...

MySQL误删数据急救指南:基于Binlog日志的实战恢复详解

背景 数据误删是一个比较严重的场景 1.典型误操作场景 场景1&#xff1a;DELETE FROM orders WHERE status0 → 漏写AND create_time>‘2025-06-20’ 场景2&#xff1a;DROP TABLE customer → 误执行于生产环境 认识 binlog 1.binlog 的核心作用 记录所有 DDL/DML 操…...

API网关Apisix管理接口速查

&#x1f9ed; 管理接口总体分类&#xff08;基于 REST API&#xff09; 资源类别接口路径前缀功能说明路由&#xff08;Routes&#xff09;/apisix/admin/routes/{id}定义 HTTP 请求的匹配规则及转发目标服务&#xff08;Services&#xff09;/apisix/admin/services/{id}封装…...

React 组件通信

父传子 函数式组件 function Footer(props){const [count,setCount] useState(0)const {name,age} propsconst onClick ()>{setCount(count1)}return (<div><button onClick{()>{onClick()}}>点此1</button><div>{count}</div><di…...

Zephyr 电源管理机制深度解析:从 Tickless Idle 到平台 Suspend 实践

本文系统解析 Zephyr 的电源管理机制&#xff0c;包括 Tickless Idle 模式、系统 suspend/resume 生命周期管理、平台级功耗优化 Hook、自定义设备电源域&#xff0c;以及如何结合低功耗 SoC 实现最小化功耗设计。全文超过 5000 字&#xff0c;适合构建对功耗敏感的 IoT、BLE、…...

clickhouse-server连不上clickhouse-keeper的问题记录

背景 想简单部署一个1 shard 2 replica&#xff0c;1keeper的集群。 有两个虚拟机&#xff1a;192.168.1.3&#xff0c;192.168.1.6。 192.168.1.3&#xff1a;部署1个ck&#xff0c;1个keeper 192.168.1.6&#xff1a;部署1个ck 192.168.1.3和192.168.1.6的ck组成1个shar…...

Python 数据分析与可视化 Day 3 - Pandas 数据筛选与排序操作

&#x1f3af; 今日目标 掌握 DataFrame 的条件筛选&#xff08;布尔索引&#xff09;学会多条件筛选、逻辑运算熟练使用排序&#xff08;sort_values&#xff09;提升数据组织力结合列选择进行数据提取分析 &#x1f9ea; 一、列选择与基本筛选 ✅ 选择单列 / 多列 df[&quo…...

Android NDK下载链接及配置版本

Android NDK下载链接及配置版本 https://github.com/android/ndk/releases 在build.gralde里面这样配置ndk具体版本号&#xff1a; android {ndkVersion "27.0.12077973" } Android Studio报错&#xff1a;Could not move temporary workspace () to immutable locat…...

Mac Parallels Desktop Kali 2025 代理设置

Mac Parallels Desktop Kali 2025 代理设置 核心步骤&#xff1a; kali设置桥接wifi 查看kali和主机ip 运行命令ifconfig查看kali ip&#xff1a; mac主机ip&#xff1a; kali设置proxy ip填写主机ip&#xff0c;port为主机proxy端口 enjoy...

Python 的内置函数 hash

Python 内建函数列表 > Python 的内置函数 hash Python 的内置函数 hash() 是一个非常有用的工具函数&#xff0c;主要用于获取对象的哈希值。哈希值是一个固定长度的整数&#xff0c;代表该对象的唯一标识。在 Python 中&#xff0c;hash() 函数常用于字典键值、集合元素等…...

文生视频(Text-to-Video)

&#x1f552; 生成时间&#xff1a;每张图大概 10–60 秒&#xff08;取决于设备&#xff09; ✅ 二、文生视频&#xff08;Text-to-Video&#xff09; 以下项目中&#xff0c;很多都基于 SD 模型扩展&#xff0c;但视频生成复杂度高&#xff0c;生成时间一般 超过 30 秒&am…...

(LeetCode 面试经典 150 题) 80. 删除有序数组中的重复项 II (双指针、栈)

题目&#xff1a;80. 删除有序数组中的重复项 II 思路&#xff1a;左指针 left 类似于指向栈顶的下一个待填的元素&#xff0c;每次遍历只需看当前元素nums[i]和栈顶的下一个元素nums[left-2]是否相等&#xff0c;不等就可以插入栈当中。时间复杂度0(n)。 C版本&#xff1a; …...

【舞蹈】编排:如何对齐拍子并让小节倍数随BPM递减

音的强弱关系 当前划分编排最小单位的代码的分析 📊 代码逻辑分析 ✅ 完整性方面 代码逻辑相对完整,包含了: 结构段落分析(intro, verse, chorus等)强拍时间点提取歌词时间轴处理AI增强的编舞建议生成⚠️ 主要问题 1. 强拍对齐逻辑不够精确 # 当前代码只是简单提取…...

LangGraph--基础学习(工具调用)

本节将详细学习大模型是怎么调用工具的&#xff0c;为什么可以调用工具等等&#xff0c;手写一个工具调用&#xff0c;后续可以通过mcp自己调用即可&#xff0c;没必要自己写&#xff0c;但是学习过程中需要手写&#xff0c;通常怎么使用第三方工具调用呢&#xff1f; import o…...

华为云 Flexus+DeepSeek 实战:华为云单机部署 Dify-LLM 开发平台全流程指南【服务部署、模型配置、知识库构建全流程】

华为云 FlexusDeepSeek 实战&#xff1a;华为云单机部署 Dify-LLM 开发平台全流程指南【服务部署、模型配置、知识库构建全流程】 文章目录 华为云 FlexusDeepSeek 实战&#xff1a;华为云单机部署 Dify-LLM 开发平台全流程指南【服务部署、模型配置、知识库构建全流程】前言1、…...

【appium】2.初始连接脚本配置

连接配置 from appium import webdriver desired_caps {platformName: Android,automationName: UIAutomator2,deviceName: ZTEB880,appPackage: com.taobao.taobao,appActivity: com.taobao.tao.welcome.Welcome,noReset: True }driver webdriver.Remote(http://localhost:…...

磁性传感器在电机控制闭环系统中的反馈作用

磁性传感器的基本原理和类型 基本原理 &#xff1a;磁性传感器是基于磁学原理来检测磁场强度、方向或其他与磁场相关的物理量。常见的磁性传感器有霍尔传感器、磁阻传感器等。霍尔传感器是利用霍尔效应工作的&#xff0c;当电流通过置于磁场中的半导体材料时&#xff0c;在垂直…...

Python:.py文件如何变成双击可执行的windows程序?(版本1)

1、如下.py文件&#xff0c;右键重命名文件后缀名&#xff1a;py改为&#xff1a;pyw 2、修改时&#xff0c;提示如下&#xff1a;不用管点击&#xff1a;是即可 3、之后双击&#xff0c;即可执行python代码文件。 好的&#xff0c;我们来详细介绍一下 Python 的 .pyw 文件。 简…...

Spring Boot + MyBatis + Vue:全栈开发的深度剖析与实践指南

一、技术栈深度剖析 &#xff08;一&#xff09;Spring Boot&#xff1a;后端开发的加速器 Spring Boot 是基于 Spring 框架的一个开源 Java 项目&#xff0c;旨在简化基于 Spring 的应用开发。它通过自动配置机制&#xff0c;能够根据项目中添加的依赖自动配置 Spring 和相关…...

学习C++、QT---03(C++的输入输出、C++的基本数据类型介绍)

每日一言 你比想象中更强大&#xff0c;那些咬牙坚持的瞬间&#xff0c;都在雕刻更好的你。 案例&#xff1a;C的输入输出 但是我也会用c语言的方式来回顾c语言的写法&#xff0c;因为两种语言都是密不可分的&#xff0c;所以不能忘记&#xff0c;所以两个一起写 注意点&#…...

八、Redis的主从原理、哨兵

简介&#xff1a; 想要了解Redis的主从原理&#xff0c;首先得认识一个基本的分布式理论-CAP理论。要理解这个理论&#xff0c;其实也非常简单。 CAP理论 C&#xff1a;Consistency、A&#xff1a;Available、P&#xff1a;Partition tolerance 。这是CAP三个字母的全称。C&…...

springboot通过独立事务管理器实现资源隔离与精准控制​

安心流转站核心业务模块&#xff0c;为什么&#xff01;我们考虑这样设计&#xff0c;下面讲讲专用事务管理器的设计与必要性​&#xff01; 一、为什么需要专属事务管理器&#xff1f;​​ 在安心流转站模块中&#xff0c;存在 ​​「多资源混合操作」​​ 和 ​​「业务高敏…...

59-Oracle 10046事件-知识准备

上一篇说到了autotrace&#xff0c;SQL调试时候的获取性能和参数数据&#xff0c;直接用上trace&#xff0c;还有个更全能的工具10046。是不是很多小伙伴会对这么个数字&#xff0c;觉得起名很奇怪&#xff0c;数字起名任性。“10046”本质是Oracle内核事件的随机性技术编号&am…...

2025年渗透测试面试题总结-2025年HW(护网面试) 03(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 2025年HW(护网面试) 03 1. 同源策略&#xff08;Same-Origin Policy&#xff09; 2. XSS攻击用途 3. XSS类…...

嵌入式开发之嵌入式系统硬件架构设计时,如何选择合适的微处理器/微控制器?

在嵌入式系统硬件架构设计中&#xff0c;选择合适的微处理器 / 微控制器&#xff08;MCU/MPU&#xff09;是关键环节&#xff0c;需从多维度综合评估。以下是系统化的选择策略及核心考量因素&#xff1a; 一、明确应用需求与核心指标 1. 性能需求 处理能力&#xff1a;根据任…...

C++(面向对象编程——继承)

继承基础概念 1.什么是继承&#xff1f; 继承是C三大特性之一&#xff1b;继承是一个已经存在的类的基础上新建一个类&#xff0c;新建的类拥有已经存在的类的特性。主要提现的是代码复用的思想。新的类继承了基类的所有成员变量和成员函数&#xff0c;包括不显示的函数&…...

Unity Shader开发-着色器变体(2)-定义着色器变体

一.定义着色器变体 定义一个着色器变体&#xff08;Shader Variant&#xff09;从概念和实现上讲&#xff0c;主要包括以下几个核心部分 1.使用预编译指令来声明变体关键字 关键字是驱动变体生成的“开关”。它们是简单的字符串标识符&#xff0c;用于在 Shader 代码中标记不…...

Cookie和Session的作用和区别

Cookie 客户端持久化保存服务器数据的一种机制&#xff08;持久化存储就是存硬盘里&#xff09;。Cookie文件数据为键值对形式&#xff0c;客户端根据服务器域名的不同分别存储Cookie&#xff0c;不同域名的Cookie不同&#xff0c;不会产生冲突。 典型应用场景&#xff1a; 保…...

Redis集群部署终极指南:架构选型、生产部署与深度优化

第一部分&#xff1a;Redis集群技术全景解析 1.1 Redis集群演进史 单机时代&#xff08;2009-2012&#xff09;&#xff1a;Redis 2.8之前&#xff0c;纯单机模式复制时代&#xff08;2012-2015&#xff09;&#xff1a;Redis 2.8引入PSYNC改进复制哨兵时代&#xff08;2015-…...

腾讯云IM即时通讯:开启实时通信新时代

一、引言 在当今数字化浪潮席卷全球的时代&#xff0c;即时通讯已然成为互联网世界中不可或缺的关键元素。无论是个人日常生活中的社交互动&#xff0c;还是企业运营里的高效协作&#xff0c;即时通讯都发挥着举足轻重的作用&#xff0c;已然渗透到人们生活与工作的每一个角落…...

基于Qt的UDP主从服务器设计与实现

概述 一个基于Qt框架实现的UDP主从服务器系统&#xff0c;该系统具备自动主机选举、故障转移和状态同步等关键功能&#xff0c;适用于分布式能源管理系统中的设备通信与协调。 系统核心功能 1. 自动主机选举与故障转移 系统通过优先级机制实现自动主机选举&#xff0c;当主机…...

JVM(8)——详解分代收集算法

JVM 的分代收集算法不是一种具体的垃圾收集算法实现&#xff0c;而是一种指导思想和设计原则&#xff0c;是现代 JVM 垃圾收集器的基石。其核心思想源于对程序运行过程中对象生命周期分布的观察&#xff08;即弱分代假说&#xff09;。 核心思想与理论基础&#xff1a;分代假说…...

深入Java面试:从Spring Boot到微服务

深入Java面试&#xff1a;从Spring Boot到微服务 在准备互联网大厂的Java岗位面试时&#xff0c;掌握核心技术栈是关键。本文将从技术栈中选取几个重要的技术点进行探讨&#xff0c;帮助你在面试中脱颖而出。 问题一&#xff1a;Spring Boot的核心特性是什么&#xff1f; 面…...

【软考高级系统架构论文】论无服务器架构及其应用

论文真题 近年来,随着信息技术的迅猛发展和应用需求的快速更迭,传统的多层企业应用系统架构面临越来越多的挑战,已经难以适应这种变化。在这一背景下,无服务器架构(Serverless Architecture) 逐渐流行,它强调业务逻辑由事件触发,具有短暂的生命周期,运行于无状态的轻量…...

Snapchat矩阵运营新策略:亚矩阵云手机打造高效社交网络

1. Snapchat平台特性与风控挑战​​ Snapchat作为全球领先的即时社交平台&#xff0c;其独特的阅后即焚功能和强社交属性使其风控系统极为严格&#xff1a; ​​核心风控机制​​ ​​设备指纹检测​​&#xff1a;记录设备ID、系统版本、IP地址等硬件信息​​行为模式分析​…...

BGP路由反射器(RR)实验详解,结尾有详细脚本

目录 路由反射器基础概念 实验拓扑与设计 实验配置步骤 配置验证与排错 实验总结 完整配置命令集 路由反射器基础概念 在传统的IBGP网络中&#xff0c;为了防止路由环路&#xff0c;BGP规定通过IBGP学到的路由不能再传递给其他IBGP对等体&#xff0c;这导致所有IBGP路由…...

【JAVA】数组的使用

文章目录 前言一、数组的基本概念1.1 数组的创建和初始化1.2 数组的基本使用 二、数组是引用类型2.1 初始JVM的内存分布JVM内存划分&#xff08;按功能分区&#xff09; 2.2 基本类型变量与引用类型变量的区别2.3 再谈引用变量2.4 认识null 三、数组作为函数的参数和返回值四、…...

Python的6万张图像数据集CIFAR-10和CIFAR-100说明

CIFAR-10和CIFAR-100数据集是8000万张微小图像数据集的标记子集。CIFAR-10和CIFAR-100都是由AlexKrizhevsky、VinodNair和GeoffreyHinton创建。数据集说明的网页&#xff1a;https://www.cs.toronto.edu/~kriz/cifar.html 一、CIFAR-10数据集 &#xff08;一&#xff09;CIFA…...

CTF--PhP Web解题(走入CTF)

前情提要 分享有趣CTF题目&#xff0c;记录学习过程 题目&#xff08;带注释,方便理解&#xff09; <?php // 开启PHP源代码高亮显示&#xff0c;输出当前文件内容&#xff08;用于调试/展示&#xff09; highlight_file(__FILE__);// 关闭所有错误报告&#xff0c;防止敏感…...

【Linux仓库】进程概念与基本操作【进程·贰】

&#x1f31f; 各位看官好&#xff0c;我是&#xff01; &#x1f30d; Linux Linux is not Unix &#xff01; &#x1f680; 今天来学习Linux中进程概念与基本操作。 &#x1f44d; 如果觉得这篇文章有帮助&#xff0c;欢迎您一键三连&#xff0c;分享给更多人哦&#xff01…...

Z-Ant开源程序是简化了微处理器上神经网络的部署和优化

​一、软件介绍 文末提供程序和源码下载 Z-Ant &#xff08;Zig-Ant&#xff09; 是一个全面的开源神经网络框架&#xff0c;专门用于在微控制器和边缘设备上部署优化的 AI 模型。Z-Ant 使用 Zig 构建&#xff0c;为资源受限的硬件上的模型优化、代码生成和实时推理提供端到端…...