Retrieval模块的设计意义

大模型的幻觉问题

拥有记忆后,确实扩展了AI工程的应用场景。

但是在专有领域,LLM无法学习到所有的专业知识细节,因此在 面向专业领域知识的提问时,无法给出 可靠准确的回答,甚至会“胡言乱语”,这种现象称之为 LLM的“幻觉”

大模型生成内容的不可控,尤其是在金融和医疗领域等领域,一次金额评估的错误,一次医疗诊断的失误,哪怕只出现一次都是致命的。但,对于非专业人士来说可能难以辨识。目前还没有能够百分之百解决这种情况的方案。

当前大家普遍达成共识的一个方案
首先,为大模型提供一定的上下文信息,让其输出会变得更稳定。
其次,利用本章的RAG,将检索出来的 文档和提示词输送给大模型,生成更可靠的答案。

RAG的优缺点

  • RAG的优点

    1. 相比提示词工程,RAG有 更丰富的上下文和数据样本,可以不需要用户提供过多的背景描述,就能生 成比较符合用户预期的答案。
    2. 相比于模型微调,RAG可以提升问答内容的 时效性和 可靠性
    3. 在一定程度上保护了业务数据的 隐私性。
  • RAG的缺点

    1. 由于每次问答都涉及外部系统数据检索,因此RAG的 响应时延相对较高
    2. 引用的外部知识数据会 消耗大量的模型Token 资源。

Retrieval流程

环节1:Source(数据源)

指的是RAG架构中所外挂的知识库。这里有三点说明:

  1. 原始数据源类型多样:如:视频、图片、文本、代码、文档等
  2. 形式的多样性:
    • 可以是上百个.csv文件,可以是上千个.json文件,也可以是上万个.pdf文件
    • 可以是某一个业务流程外放的API,可以是某个网站的实时数据等

环节2:Load(加载)

文档加载器(Document Loaders)负责将来自不同数据源的非结构化文本,加载到 内存成为(Document)对象 。

文档对象包含 文档内容和相关元数据信息 ,例如TXT、CSV、HTML、JSON、Markdown、PDF,甚至 YouTube 视频转录等。

文档加载器还支持“ 延迟加载”模式,以缓解处理大文件时的内存压力。
文档加载器的编程接口使用起来非常简单,以下给出加载TXT格式文档的例子。

1
2
3
4
5
6
from langchain.document_loadersimport TextLoader 

text_loader = TextLoader( "./test.txt" )

docs = text_loader.load() #返回List列表(Document对象)
print (docs)

环节3:Transform(转换)

文档转换器(Document Transformers) 负责对加载的文档进行转换和处理,以便更好地适应下游任务的 需求。

文档转换器提供了一致的接口(工具)来操作文档,主要包括以下几类:

  • 文本拆分器(Text Splitters) :将长文本拆分成语义上相关的小块,以适应语言模型的上下文窗口限 制。
  • 冗余过滤器(Redundancy Filters) :识别并过滤重复的文档。
  • 元数据提取器(Metadata Extractors) :从文档中提取标题、语调等结构化元数据。
  • 多语言转换器(Multi-lingual Transformers) :实现文档的机器翻译。
  • 对话转换器(Conversational Transformers) :将非结构化对话转换为问答格式的文档。

总的来说,文档转换器是 LangChain 处理管道中非常重要的一个组件,它丰富了框架对文档的表示和 操作能力。

环节3.1:Text Splitting(文档拆分)

  • 向量化并存入数据库中。 拆分/分块的必要性:前一个环节加载后的文档对象可以直接传入文档拆分器进行拆分,而文档切块 后才能
  • 文档拆分器的多样性:LangChain提供了丰富的文档拆分器,不仅能够切分普通文本,还能切分 Markdown、JSON、HTML、代码等特殊格式的文本。
  • 拆分/分块的挑战性:实际拆分操作中需要处理许多细节问题, 需要采用不同的分块策略。
    • 可以按照 数据类型进行切片处理,比如针对 不同类型的文本、 不同的使用场景都 文本类数据,可以直接按照字符、段落进行切 片;代 码类数据则需要进一步细分以保证代码的功能性;
    • 可以直接根据 token 进行切片处理

在构建RAG应用程序的整个流程中,拆分/分块是最具挑战性的环节之一,它显著影响检索效果。目前 还没有通用的方法可以明确指出哪一种分块策略最为有效。不同的使用场景和数据类型都会影响分块策 略的选择。

环节4:Embed(嵌入)

文档嵌入模型(Text Embedding Models)负责将 文本转换为 向量表示,即模型赋予了文本计算机可 理解的数值表示,使文本可用于向量空间中的各种运算,大大拓展了文本分析的可能性,是自然语言处 理领域非常重要的技术。

举例:

  • 实现原理:通过 特定算法(如Word2Vec)将语义信息编码为固定维度的向量,具体算法细节需后 续深入。
  • 关键特性:相似的词在向量空间中距离相近,例如”猫”和”犬”的向量夹角小于”猫”和”汽车”。

文本嵌入为 LangChain 中的问答、检索、推荐等功能提供了重要支持。具体为:

  • 语义匹配:通过计算两个文本的向量余弦相似度,判断它们在语义上的相似程度,实现语义匹配。
  • 文本检索:通过计算不同文本之间的向量相似度,可以实现语义搜索,找到向量空间中最相似的文 本。
  • 信息推荐:根据用户的历史记录或兴趣嵌入生成用户向量,计算不同信息的向量与用户向量的相似 度,推荐相似的信息。
  • 知识挖掘:可以通过聚类、降维等手段分析文本向量的分布,发现文本之间的潜在关联,挖掘知 识。
  • 自然语言处理:将词语、句子等表示为稠密向量,为神经网络等下游任务提供输入。

环节5:Store(存储)

LangChain 还支持把文本嵌入存储到向量存储或临时缓存,以避免需要重新计算它们。这里就出现了数 据库,支持这些嵌入的高效 存储和搜索的需求。

环节6:Retrieve(检索)

检索器(Retrievers)是一种用于 响应非结构化查询的接口,它可以返回符合查询要求的文档。

LangChain 提供了一些常用的检索器,如 向量检索器、 文档检索器、 网站研究检索器等。

通过配置不同的检索器,LangChain 可以灵活地平衡检索的精度、召回率与效率。检索结果将为后续的 问答生成提供信息支持,以产生更加准确和完整的回答。

文档加载器 Document Loaders

LangChain的设计:对于Source中多种不同的数据源,我们可以用一种统一的形式读取、调用。

加载txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from langchain_community.document_loaders import TextLoader, PyPDFLoader  

# 指明txt文档的路径
file_path = "./asset/load/01-langchain-utf-8.txt"

# 创建一个TextLoader的实例
text_loader = TextLoader(
file_path=file_path,
encoding="utf-8",
)

# 调用load(),返回一个list[Document]
docs = text_loader.load()

print(docs)

print(len(docs)) # 查看列表中元素的个数‘

print(docs[0])

结果:

1
2
3
4
5
[Document(metadata={'source': './asset/load/01-langchain-utf-8.txt'}, page_content='LangChain 是一个用于构建基于大语言模型(LLM)应用的开发框架,旨在帮助开发者更高效地集成、管理和增强大语言模型的能力,构建端到端的应用程序。它提供了一套模块化工具和接口,支持从简单的文本生成到复杂的多步骤推理任务')]

1

page_content='LangChain 是一个用于构建基于大语言模型(LLM)应用的开发框架,旨在帮助开发者更高效地集成、管理和增强大语言模型的能力,构建端到端的应用程序。它提供了一套模块化工具和接口,支持从简单的文本生成到复杂的多步骤推理任务' metadata={'source': './asset/load/01-langchain-utf-8.txt'}

Documment对象中有两个重要的属性:

  • page_content:真正的文档内容
  • metadata:文档内容的原数据
1
2
3
4
5
6
7
8
9
10
# 显示Document对象的元数据  
print(docs[0].metadata)
"""
{'source': './asset/load/01-langchain-utf-8.txt'}
"""
# 显示文档中的内容信息
print(docs[0].page_content)
"""
LangChain 是一个用于构建基于大语言模型(LLM)应用的开发框架,旨在帮助开发者更高效地集成、管理和增强大语言模型的能力,构建端到端的应用程序。它提供了一套模块化工具和接口,支持从简单的文本生成到复杂的多步骤推理任务
"""

结果:

加载pdf

举例1:

LangChain加载PDF文件使用的是pypdf,先安装

1
pip install pypdf
1
2
3
4
5
6
7
8
9
10
11
12
# 1.导入相关的依赖 PyPDFLoader()
from langchain_community.document_loaders.pdf import PyPDFLoader

pdf_loader = PyPDFLoader(
file_path="./asset/load/02-load.pdf"
)

docs = pdf_loader.load()

print(docs)

print(len(docs))

结果:

1
2
[Document(metadata={'producer': 'Microsoft® Word 2019', 'creator': 'Microsoft® Word 2019', 'creationdate': '2025-06-20T17:18:19+08:00', 'moddate': '2025-06-20T17:18:19+08:00', 'source': './asset/load/02-load.pdf', 'total_pages': 1, 'page': 0, 'page_label': '1'}, page_content='"他的车,他的命! 他忽然想起来,一年,二年,至少有三四年;一滴汗,两滴汗,不\n知道多少万滴汗,才挣出那辆车。从风里雨里的咬牙,从饭里茶里的自苦,才赚出那辆车。\n那辆车是他的一切挣扎与困苦的总结果与报酬,像身经百战的武士的一颗徽章。……他老想\n着远远的一辆车,可以使他自由,独立,像自己的手脚的那么一辆车。" \n \n"他吃,他喝,他嫖,他赌,他懒,他狡猾, 因为他没了心,他的心被人家摘了去。他\n只剩下那个高大的肉架子,等着溃烂,预备着到乱死岗子去。……体面的、要强的、好梦想\n的、利己的、个人的、健壮的、伟大的祥子,不知陪着人家送了多少回殡;不知道何时何地\n会埋起他自己来, 埋起这堕落的、 自私的、 不幸的、 社会病胎里的产儿, 个人主义的末路鬼!\n"')]
1

同样的:

1
2
3
4
5
type (pages[0]) #langchain_core.documents.base.Document

pages[0].page_content #只获取本页内容

pages[0].metadata # {...,'source': './asset/load/load.pdf',.., 'page': 0}

举例2:

1
2
3
4
5
6
7
8
9
10
11
12
from langchain_community.document_loaders.pdf import PyPDFLoader  

pdf_loader = PyPDFLoader(
file_path="https://arxiv.org/pdf/2302.03803"
)

docs = pdf_loader.load()

print(len(docs))

for doc in docs:
print(doc)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
8
page_content='arXiv:2302.03803v1 [math.AG] 7 Feb 2023
A WEAK (k, k )-LEFSCHETZ THEOREM FOR PROJECTIVE
TORIC ORBIFOLDS
William D. Montoya
Instituto de Matem´ atica, Estat´ ıstica e Computa¸ c˜ ao Cient´ ıfica,
Universidade Estadual de Campinas (UNICAMP),
Rua S´ ergio Buarque de Holanda 651, 13083-859, Campinas, SP , Brazil
February 9, 2023
Abstract
Firstly we show a generalization of the (1,1)-Lefschetz theorem for projective
toric orbifolds and secondly we prove that on 2k-dimensional quasi-smooth hyper-
surfaces coming from quasi-smooth intersection surfaces, under the Cayley trick,
every rational (k, k)-cohomology class is algebraic, i.e., the Hodge conjectureholds
on them.
1 Introduction
In [3] we proved that, under suitable conditions, on a very general codimension s quasi-
smooth intersection subvariety X in a projective toric orbifold Pd
Σ with d +s = 2(k +1)
the Hodge conjecture holds, that is, every (p, p )-cohomology class, under the Poincar´ e
duality is a rational linear combination of fundamental classes of alge braic subvarieties
of X. The proof of the above-mentioned result relies, for p ≠ d +1 − s, on a Lefschetz
Date: February 9, 2023
2020 Mathematics Subject Classification: 14C30, 14M10, 14J70, 14M25
Keywords: (1,1)- Lefschetz theorem, Hodge conjecture, toric varieties, complete intersection
Email: wmontoya@ime.unicamp.br
1' metadata={'producer': 'dvips + GPL Ghostscript GIT PRERELEASE 9.22', 'creator': 'LaTeX with hyperref', 'creationdate': '2023-02-08T20:27:28-05:00', 'moddate': '2023-02-08T20:27:28-05:00', 'title': '', 'subject': '', 'author': '', 'keywords': '', 'source': 'https://arxiv.org/pdf/2302.03803', 'total_pages': 8, 'page': 0, 'page_label': '1'}
page_content='theorem ([7]) and the Hard Lefschetz theorem for projective orb ifolds ([11]). When p =
d +1 −s the proof relies on the Cayley trick, a trick which associates to X a quasi-smooth
hypersurface Y in a projective vector bundle, and the Cayley Proposition (4.3) which
gives an isomorphism of some primitive cohomologies (4.2) of X and Y . The Cayley
trick, following the philosophy of Mavlyutov in [7], reduces results kn own for quasi-smooth
hypersurfaces to quasi-smooth intersection subvarieties. The id ea in this paper goes the
other way around, we translate some results for quasi-smooth int ersection subvarieties to
quasi-smooth hypersurfaces, mainly the (1, 1)-Lefschetz theorem.
Acknowledgement. I thank Prof. Ugo Bruzzo and Tiago Fonseca for useful discus-
sions. I also acknowledge support from FAPESP postdoctoral gra nt No. 2019/23499-7.
2 Preliminaries and Notation
2.1 Toric varieties
Let M be a free abelian group of rank d, let N =Hom(M, Z ), and NR =N ⊗Z R.
Definition 2.1. • A convex subset σ ⊂NR is a rational k-dimensional simplicial cone
if there exist k linearly independent primitive elements e1, . . . , e k ∈ N such that σ =
{µ1e1 +⋯+ µkek}.
• The generators ei are integral if for every i and any nonnegative rational number µ
the product µei is in N only if µ is an integer.
• Given two rational simplicial cones σ, σ′ one says that σ′ is a face of σ (σ′ < σ) if
the set of integral generators of σ′ is a subset of the set of integral generators of σ.
• A finite set Σ ={σ1, . . . , σ t} of rational simplicial cones is called a rational simplicia l
complete d-dimensional fan if:
1. all faces of cones in Σ are in Σ ;
2. if σ, σ ′ ∈Σ then σ ∩σ′ <σ and σ ∩σ′ <σ′;
3. NR =σ1 ∪⋅⋅⋅∪ σt.
A rational simplicial complete d-dimensional fan Σ defines a d-dimensional toric variety
Pd
Σ having only orbifold singularities which we assume to be projective. Mo reover, T ∶=
N ⊗Z C∗ ≃ (C∗)d is the torus action on Pd
Σ . We denote by Σ (i) the i-dimensional cones
2' metadata={'producer': 'dvips + GPL Ghostscript GIT PRERELEASE 9.22', 'creator': 'LaTeX with hyperref', 'creationdate': '2023-02-08T20:27:28-05:00', 'moddate': '2023-02-08T20:27:28-05:00', 'title': '', 'subject': '', 'author': '', 'keywords': '', 'source': 'https://arxiv.org/pdf/2302.03803', 'total_pages': 8, 'page': 1, 'page_label': '2'}
page_content='of Σ and each ρ ∈Σ corresponds to an irreducible T -invariant Weil divisor Dρ on Pd
Σ . Let
Cl(Σ ) be the group of Weil divisors on Pd
Σ module rational equivalences.
The total coordinate ring of Pd
Σ is the polynomial ring S = C[xρ /divides.alt0 ρ ∈ Σ (1)], S has the
Cl(Σ )-grading, a Weil divisor D =∑ρ∈Σ (1) uρDρ determines the monomial xu ∶=∏ρ∈Σ (1) xuρ
ρ ∈
S and conversely deg (xu)=[D]∈Cl(Σ ).
For a cone σ ∈ Σ, ˆσ is the set of 1-dimensional cone in Σ that are not contained in σ
and xˆσ ∶=∏ρ∈ˆσ xρ is the associated monomial in S.
Definition 2.2. The irrelevant ideal of Pd
Σ is the monomial ideal BΣ ∶=< xˆσ /divides.alt0 σ ∈ Σ > and
the zero locus Z(Σ )∶=V(BΣ ) in the affine space Ad ∶=Spec(S) is the irrelevant locus.
Proposition 2.3 (Theorem 5.1.11 [5]) . The toric variety Pd
Σ is a categorical quotient
Ad ∖Z(Σ ) by the group Hom(Cl(Σ ), C∗) and the group action is induced by the Cl(Σ )-
grading of S.
2.2 Orbifolds
Now we give a brief introduction to complex orbifolds and we mention th e needed theorems
for the next section. Namely: de Rham theorem and Dolbeault theor em for complex
orbifolds.
Definition 2.4. A complex orbifold of complex dimension d is a singular complex space
whose singularities are locally isomorphic to quotient sin gularities Cd/slash.left G, for finite sub-
groups G ⊂Gl(d, C).
Definition 2.5. A differential form on a complex orbifold Z is defined locally at z ∈Z as
a G-invariant differential form on Cd where G ⊂ Gl(d, C) and Z is locally isomorphic to
Cd/slash.left G around z.
Roughly speaking the local geometry of orbifolds reduces to local G-invariant geometry.
We have a complex of differential forms (A●(Z), d )and a double complex (A●, ●(Z), ∂, ¯∂)
of bigraded differential forms which define the de Rham and the Dolbe ault cohomology
groups (for a fixed p ∈N) respectively:
H●
dR(Z, C)∶= kerd
im d and Hp, ●(Z, ¯∂)∶= ker ¯∂
im ¯∂
Theorem 2.6 (Theorem 3.4.4 in [4] and Theorem 1.2 in [1] ) . Let Z be a compact complex
orbifold. There are natural isomorphisms:
3' metadata={'producer': 'dvips + GPL Ghostscript GIT PRERELEASE 9.22', 'creator': 'LaTeX with hyperref', 'creationdate': '2023-02-08T20:27:28-05:00', 'moddate': '2023-02-08T20:27:28-05:00', 'title': '', 'subject': '', 'author': '', 'keywords': '', 'source': 'https://arxiv.org/pdf/2302.03803', 'total_pages': 8, 'page': 2, 'page_label': '3'}
page_content='• H●
dR(Z, C)≃H●(Z, C)
• Hp, ●(Z, ¯∂)≃H●(X, Ω p
Z )
3 (1,1)-Lefschetz theorem for projective toric orbifolds
Definition 3.1. A subvariety X ⊂Pd
Σ is quasi-smooth if V(IX )⊂A#Σ (1) is smooth outside
Z(Σ ).
Example 3.2. Quasi-smooth hypersurfaces or more generally quasi-smooth inte rsection sub-
varieties are quasi-smooth subvarieties (see [2] or [7] for more det ails).

Remark 3.3. Quasi-smooth subvarieties are suborbifolds of Pd
Σ in the sense of Satake in [8].
Intuitively speaking they are subvarieties whose only singularities co me from the ambient
space.

Theorem 3.4. Let X ⊂ Pd
Σ be a quasi-smooth subvariety. Then every (1, 1)-cohomology
class λ ∈H1, 1(X)∩H2(X, Z) is algebraic
Proof. From the exponential short exact sequence
0 →Z →OX →O∗
X →0
we have a long exact sequence in cohomology
H1(O∗
X )→H2(X, Z) →H2(OX )≃H0, 2(X)
where the last isomorphisms is due to Steenbrink in [9]. Now, it is enoug h to prove the
commutativity of the next diagram
H2(X, Z) → →
↓ ↓
H2(X, OX )
≃ Dolbeault
↓ ↓
H2(X, C)
de Rham ≃
↓ ↓
H2
dR(X, C) → → H0, 2
¯∂ (X)
4' metadata={'producer': 'dvips + GPL Ghostscript GIT PRERELEASE 9.22', 'creator': 'LaTeX with hyperref', 'creationdate': '2023-02-08T20:27:28-05:00', 'moddate': '2023-02-08T20:27:28-05:00', 'title': '', 'subject': '', 'author': '', 'keywords': '', 'source': 'https://arxiv.org/pdf/2302.03803', 'total_pages': 8, 'page': 3, 'page_label': '4'}
page_content='The key points are the de Rham and Dolbeault’s isomorphisms for orbif olds. The rest
of the proof follows as the (1, 1)-Lefschetz theorem in [6].
Remark 3.5. For k = 1 and Pd
Σ as the projective space, we recover the classical (1, 1)-
Lefschetz theorem.

By the Hard Lefschetz Theorem for projective orbifolds (see [11] for details) we get an
isomorphism of cohomologies :
H●(X, Q) ≃H2 dim X−●(X, Q)
given by the Lefschetz morphism and since it is a morphism of Hodge st ructures, we have:
H1, 1(X, Q) ≃Hdim X−1, dim X−1(X, Q)
For X as before:
Corollary 3.6. If the dimension of X is 1, 2 or 3. The Hodge conjecture holds on X.
Proof. If the dimCX = 1 the result is clear by the Hard Lefschetz theorem for projective
orbifolds. The dimension 2 and 3 cases are covered by Theorem 3.5 an d the Hard Lefschetz.
theorem.4 Cayley trick and Cayley proposition
The Cayley trick is a way to associate to a quasi-smooth intersection subvariety a quasi-
smooth hypersurface. Let L1, . . . , L s be line bundles on Pd
Σ and let π ∶ P(E) → Pd
Σ be the
projective space bundle associated to the vector bundle E =L1 ⊕⋯⊕ Ls. It is known that
P(E) is a (d +s −1)-dimensional simplicial toric variety whose fan depends on the degre es
of the line bundles and the fan Σ. Furthermore, if the Cox ring, witho ut considering the
grading, of Pd
Σ is C[x1, . . . , x m] then the Cox ring of P(E) is
C[x1, . . . , x m, y 1, . . . , y s]
Moreover for X a quasi-smooth intersection subvariety cut off by f1, . . . , f s with deg(fi)=
[Li] we relate the hypersurface Y cut off by F = y1f1 +⋅⋅⋅+ ysfs which turns out to be
quasi-smooth. For more details see Section 2 in [7].
5' metadata={'producer': 'dvips + GPL Ghostscript GIT PRERELEASE 9.22', 'creator': 'LaTeX with hyperref', 'creationdate': '2023-02-08T20:27:28-05:00', 'moddate': '2023-02-08T20:27:28-05:00', 'title': '', 'subject': '', 'author': '', 'keywords': '', 'source': 'https://arxiv.org/pdf/2302.03803', 'total_pages': 8, 'page': 4, 'page_label': '5'}
page_content='We will denote P(E) as Pd+s−1
Σ ,X to keep track of its relation with X and Pd
Σ .
The following is a key remark.
Remark 4.1. There is a morphism ι∶X →Y ⊂Pd+s−1
Σ ,X . Moreover every point z ∶=(x, y )∈Y
with y ≠ 0 has a preimage. Hence for any subvariety W = V(IW ) ⊂ X ⊂ Pd
Σ there exists
W ′ ⊂Y ⊂Pd+s−1
Σ ,X such that π(W ′)=W , i.e., W ′ ={z =(x, y ) /divides.alt0 x ∈W }.

For X ⊂ Pd
Σ a quasi-smooth intersection variety the morphism in cohomology indu ced
by the inclusion i∗ ∶Hd−s(Pd
Σ , C)→Hd−s(X, C) is injective by Proposition 1.4 in [7].
Definition 4.2. The primitive cohomology of Hd−s
prim(X)is the quotient Hd−s(X, C)/slash.left i∗(Hd−s(Pd
Σ , C))
and Hd−s
prim(X, Q) with rational coefficients.
Hd−s(Pd
Σ , C) and Hd−s(X, C) have pure Hodge structures, and the morphism i∗ is com-
patible with them, so that Hd−s
prim(X) gets a pure Hodge structure.
The next Proposition is the Cayley proposition.
Proposition 4.3. [Proposition 2.3 in [3] ] Let X =X1 ∩⋅⋅⋅∩ Xs be a quasi-smooth intersec-
tion subvariety in Pd
Σ cut off by homogeneous polynomials f1 . . . f s. Then for p ≠ d+s−1
2 , d+s−3
2
Hp−1,d +s−1−p
prim (Y )≃Hp−s,d −p
prim (X).
Corollary 4.4. If d +s =2(k +1),
Hk+1−s,k +1−s
prim (X)≃Hk,k
prim(Y )
Remark 4.5. The above isomorphisms are also true with rational coefficients since H●(X, C) =
H●(X, Q)⊗Q C. See the beginning of Section 7.1 in [10] for more details.

5 Main result
Theorem 5.1. Let Y ={F =y1f1 +⋯+ ykfk =0}⊂P2k+1
Σ ,X be the quasi-smooth hypersurface
associated to the quasi-smooth intersection surface X = Xf1 ∩⋅⋅⋅∩ Xfk ⊂ Pk+2
Σ . Then on Y
the Hodge conjecture holds.
Proof. If Hk,k
prim(X, Q) = 0 we are done. So let us assume Hk,k
prim(X, Q) ≠ 0. By the Cayley
proposition Hk,k
prim(Y, Q) ≃ H1, 1
prim(X, Q) and by the (1, 1)-Lefschetz theorem for projective
6' metadata={'producer': 'dvips + GPL Ghostscript GIT PRERELEASE 9.22', 'creator': 'LaTeX with hyperref', 'creationdate': '2023-02-08T20:27:28-05:00', 'moddate': '2023-02-08T20:27:28-05:00', 'title': '', 'subject': '', 'author': '', 'keywords': '', 'source': 'https://arxiv.org/pdf/2302.03803', 'total_pages': 8, 'page': 5, 'page_label': '6'}
page_content='toric orbifolds there is a non-zero algebraic basis λC1 , . . . , λ Cn with rational coefficients of
H1, 1
prim(X, Q), that is, there are n ∶=h1, 1
prim(X, Q) algebraic curves C1, . . . , C n in X such that
under the Poincar´ e duality the class in homology [Ci] goes to λCi , [Ci] ↦ λCi . Recall
that the Cox ring of Pk+2 is contained in the Cox ring of P2k+1
Σ ,X without considering the
grading. Considering the grading we have that if α ∈ Cl(Pk+2
Σ ) then (α, 0) ∈ Cl(P2k+1
Σ ,X ). So
the polynomials defining Ci ⊂ Pk+2
Σ can be interpreted in P2k+1
X, Σ but with different degree.
Moreover, by Remark 4.1 each Ci is contained in Y = {F = y1f1 + ⋯ + ykfk = 0} and
furthermore it has codimension k.
Claim: {λCi }n
i=1 is a basis of Hk,k
prim(Y, Q).
It is enough to prove that λCi is different from zero in Hk,k
prim(Y, Q) or equivalently that the
cohomology classes {λCi }n
i=1 do not come from the ambient space. By contradiction, let us
assume that there exists a j and C ⊂P2k+1
Σ ,X such that λC ∈Hk,k (P2k+1
Σ ,X , Q) with i∗(λC )=λCj
or in terms of homology there exists a (k +2)-dimensional algebraic subvariety V ⊂ P2k+1
Σ ,X
such that V ∩Y = Cj so they are equal as a homology class of P2k+1
Σ ,X ,i.e., [V ∩Y ] = [Cj] .
It is easy to check that π(V )∩X =Cj as a subvariety of Pk+2
Σ where π ∶ (x, y )↦x. Hence
[π(V )∩X] = [Cj] which is equivalent to say that λCj comes from Pk+2
Σ which contradicts
the choice of [Cj].
Remark 5.2. Into the proof of the previous theorem, the key fact was that on X the
Hodge conjecture holds and we translate it to Y by contradiction. So, using an analogous
argument we have:

Proposition 5.3. Let Y ={F =y1fs+⋯+ysfs =0}⊂P2k+1
Σ ,X be the quasi-smooth hypersurface
associated to a quasi-smooth intersection subvariety X = Xf1 ∩ ⋅⋅⋅ ∩ Xfs ⊂ Pd
Σ such that
d +s =2(k +1). If the Hodge conjecture holds on X then it holds as well on Y .
Corollary 5.4. If the dimension of Y is 2s −1, 2s or 2s +1 then the Hodge conjecture
holds on Y .
Proof. By Proposition 5.3 and Corollary 3.6.
7' metadata={'producer': 'dvips + GPL Ghostscript GIT PRERELEASE 9.22', 'creator': 'LaTeX with hyperref', 'creationdate': '2023-02-08T20:27:28-05:00', 'moddate': '2023-02-08T20:27:28-05:00', 'title': '', 'subject': '', 'author': '', 'keywords': '', 'source': 'https://arxiv.org/pdf/2302.03803', 'total_pages': 8, 'page': 6, 'page_label': '7'}
page_content='References
[1] Angella, D. Cohomologies of certain orbifolds. Journal of Geometry and Physics
71 (2013), 117–126.
[2] Batyrev, V. V., and Cox, D. A.On the Hodge structure of projective hypersur-
faces in toric varieties. Duke Mathematical Journal 75, 2 (Aug 199 4).
[3] Bruzzo, U., and Montoya, W. On the Hodge conjecture for quasi-smooth in-
tersections in toric varieties. S˜ ao Paulo J. Math. Sci. Special Section: Geometry in
Algebra and Algebra in Geometry (2021).
[4] Caramello Jr, F. C.Introduction to orbifolds. arXiv:1909.08699v6 (2019).
[5] Cox, D., Little, J., and Schenck, H.Toric varieties, vol. 124. American Math-
ematical Soc., 2011.
[6] Griffiths, P., and Harris, J. Principles of Algebraic Geometry. John Wiley &
Sons, Ltd, 1978.
[7] Mavlyutov, A. R. Cohomology of complete intersections in toric varieties. Pub-
lished in Pacific J. of Math. 191 No. 1 (1999), 133–144.
[8] Satake, I. On a Generalization of the Notion of Manifold. Proceedings of the
National Academy of Sciences of the United States of America 42, 6 (1956), 359–363.
[9] Steenbrink, J. H. M.Intersection form for quasi-homogeneous singularities. Com-
positio Mathematica 34 , 2 (1977), 211–223.
[10] Voisin, C. Hodge Theory and Complex Algebraic Geometry I, vol. 1 of Cambridge
Studies in Advanced Mathematics . Cambridge University Press, 2002.
[11] W ang, Z. Z., and Zaffran, D.A remark on the Hard Lefschetz theorem for K¨ ahler
orbifolds. Proceedings of the American Mathematical Society 137 , 08 (Aug 2009).
8' metadata={'producer': 'dvips + GPL Ghostscript GIT PRERELEASE 9.22', 'creator': 'LaTeX with hyperref', 'creationdate': '2023-02-08T20:27:28-05:00', 'moddate': '2023-02-08T20:27:28-05:00', 'title': '', 'subject': '', 'author': '', 'keywords': '', 'source': 'https://arxiv.org/pdf/2302.03803', 'total_pages': 8, 'page': 7, 'page_label': '8'}

举例3:使用load_and_split()

1
2
3
4
5
6
7
8
9
10
# 1.导入相关的依赖 PyPDFLoader() 
from langchain.document_loaders import PyPDFLoader

# 2.定义PyPDFLoader
py_pdfLoader = PyPDFLoader(file_path= "./asset/load/load.pdf" )

# 3.加载
docs = py_pdfLoader.load_and_split() #底层默认使用了递归字符文本切分器

print (docs)

同样,对于 PyPDFLoader ,依然是使用 .page_content 和 .metadata 去访问数据,也就是说,每一个 文档加载器虽然代码逻辑不同,应用需求不同,但使用方式是相同的。

加载CSV

举例1:加载csv所有列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from langchain_community.document_loaders import CSVLoader  

csv_loader = CSVLoader(
file_path="./asset/load/03-load.csv"
)

docs = csv_loader.load()

print(len(docs))


print(docs)

for doc in docs:
print(doc)

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
4
[Document(metadata={'source': './asset/load/03-load.csv', 'row': 0}, page_content='id: 1\ntitle: Introduction to Python\ncontent: Python is a popular programming language.\nauthor: John Doe'), Document(metadata={'source': './asset/load/03-load.csv', 'row': 1}, page_content='id: 2\ntitle: Data Science Basics\ncontent: Data science involves statistics and machine learning.\nauthor: Jane Smith'), Document(metadata={'source': './asset/load/03-load.csv', 'row': 2}, page_content='id: 3\ntitle: Web Development\ncontent: HTML, CSS and JavaScript are core web technologies.\nauthor: Mike Johnson'), Document(metadata={'source': './asset/load/03-load.csv', 'row': 3}, page_content='id: 4\ntitle: Artificial Intelligence\ncontent: AI is transforming many industries.\nauthor: Sarah Williams')]
page_content='id: 1
title: Introduction to Python
content: Python is a popular programming language.
author: John Doe' metadata={'source': './asset/load/03-load.csv', 'row': 0}
page_content='id: 2
title: Data Science Basics
content: Data science involves statistics and machine learning.
author: Jane Smith' metadata={'source': './asset/load/03-load.csv', 'row': 1}
page_content='id: 3
title: Web Development
content: HTML, CSS and JavaScript are core web technologies.
author: Mike Johnson' metadata={'source': './asset/load/03-load.csv', 'row': 2}
page_content='id: 4
title: Artificial Intelligence
content: AI is transforming many industries.
author: Sarah Williams' metadata={'source': './asset/load/03-load.csv', 'row': 3}

举例2:加载指定列

使用 source_column 参数指定文件加载的列,保存在source变量中。

1
2
3
4
5
6
7
8
9
10
11
from langchain_community.document_loaders import CSVLoader  

csv_loader = CSVLoader(
file_path="./asset/load/03-load.csv",
source_column="author"
)

docs = csv_loader.load()


print(docs)

结果:

1
[Document(metadata={source: John Doe, row: 0}, page_content=id: 1\ntitle: Introduction to Python\ncontent: Python is a popular programming language.\nauthor: John Doe), Document(metadata={source: Jane Smith, row: 1}, page_content=id: 2\ntitle: Data Science Basics\ncontent: Data science involves statistics and machine learning.\nauthor: Jane Smith), Document(metadata={source: Mike Johnson, row: 2}, page_content=id: 3\ntitle: Web Development\ncontent: HTML, CSS and JavaScript are core web technologies.\nauthor: Mike Johnson), Document(metadata={source: Sarah Williams, row: 3}, page_content=id: 4\ntitle: Artificial Intelligence\ncontent: AI is transforming many industries.\nauthor: Sarah Williams)]

加载JSON

LangChain提供的JSON格式的文档加载器是JSONLoader。在实际应用场景中,JSON格式的数据占有 很大比例,而且JSON的形式也是多样的。我们需要特别关注。

JSONLoader 使用指定的 jq结构来解析 JSON 文件。jq是一个轻量级的命令行 JSON 处理器 ,可以对 JSON 格式的数据进行各种复杂的处理,包括数据过滤、映射、减少和转换,是处理 JSON 数据的首选 工具之一。

1
pip install jq

举例1:使用JSONLoader文档加载器加载

1
2
3
4
5
6
7
8
9
10
11
from langchain_community.document_loaders import JSONLoader  

json_loader = JSONLoader(
file_path="./asset/load/04-load.json",
jq_schema=".", #表示加载所有的字段
text_content=False, #将加载的json对象转换为json字符串
)

docs = json_loader.load()

print(docs)
1
[Document(metadata={'source': 'E:\\myjupyte\\尚硅谷LangChain\\chapter07-RAG\\asset\\load\\04-load.json', 'seq_num': 1}, page_content='{"messages": [{"sender": "Alice", "content": "Hello, how are you today?", "timestamp": "2023-05-15T10:00:00"}, {"sender": "Bob", "content": "I\'m doing well, thanks for asking!", "timestamp": "2023-05-15T10:02:00"}, {"sender": "Alice", "content": "Would you like to meet for lunch?", "timestamp": "2023-05-15T10:05:00"}, {"sender": "Bob", "content": "Sure, that sounds great!", "timestamp": "2023-05-15T10:07:00"}], "conversation_id": "conv_12345", "participants": ["Alice", "Bob"]}')]

举例2:加载json文件中messages[]中的所有的content字段

1
2
3
4
5
6
7
8
9
10
11
12
from langchain_community.document_loaders import JSONLoader  

json_loader = JSONLoader(
file_path="./asset/load/04-load.json",
jq_schema=".messages[].content", #加载messages[]的所有的content字段
#text_content=False, #将加载的json对象转换为json字符串
)

docs = json_loader.load()

for doc in docs:
print(doc.page_content)

结果:

1
2
3
4
Hello, how are you today?
I'm doing well, thanks for asking!
Would you like to meet for lunch?
Sure, that sounds great!

举例3:提取04-response.json文件中嵌套在 data.items[].content 的文本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from langchain_community.document_loaders import JSONLoader  

# 方式1:
# json_loader = JSONLoader(
# file_path="./asset/load/04-response.json",
# jq_schema=".data.items[].content", #data.items[].content
# )

# 方式2:
json_loader = JSONLoader(
file_path="./asset/load/04-response.json",
jq_schema=".data.items[]", #data.items[].content
content_key=".content",
is_content_key_jq_parsable=True, #用jq解析content_key
)

docs = json_loader.load()

for doc in docs:
print(doc.page_content)

结果:

1
2
3
This article explains how to parse API responses...
Learn to handle nested structures with...
Best practices for preserving metadata...

举例4:提取04-response.json文件中嵌套在 data.items[] 里的 title、content 和 其文本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 1.导入相关依赖  
from langchain_community.document_loaders import JSONLoader
from pprint import pprint

# 2.定义json文件的路径
file_path = 'asset/load/04-response.json'

# 3.定义JSONLoader对象
# 提取嵌套在 data.items[].content 的文本,并保留其他字段作为元数据
# loader = JSONLoader(
# file_path=file_path,
# # jq_schema=".data.items[] | {id, author, text: (.title + '\n' + .content)}",
# jq_schema='''.data.items[] | {
# id,
# author,
# created_at,
# title, # 保留title字段
# text: (.title + "\n" + .content)
# }''',
# content_key=".text", # 再从条目中提取 content 字段
# is_content_key_jq_parsable=True # 用jq解析content_key
# )
loader = JSONLoader(
file_path=file_path,
# jq_schema=".data.items[] | {id, author, text: (.title + '\n' + .content)}",
jq_schema=".data.items[]",
content_key='.title + "\\n\\n" + .content',
is_content_key_jq_parsable=True # 用jq解析content_key
)

# loader = JSONLoader(
# file_path=file_path,
# # jq_schema=".data.items[] | {id, author, text: (.title + '\n' + .content)}",
# jq_schema='''
# .data.items[] | {
# metadata: {
# id,
# author,
# created_at
# },
# content: (.title + "\n\n" + .content)
# }
# ''', # 构建新结构
# content_key='.title + "\\n\\n" + .content',
# is_content_key_jq_parsable=True # 用jq解析content_key
# )

# 4.加载
data = loader.load()

for doc in data:
print(doc.page_content)

结果:

1
2
3
4
5
6
7
8
9
Understanding JSONLoader

This article explains how to parse API responses...
Advanced jq Schema Patterns

Learn to handle nested structures with...
LangChain Metadata Handling

Best practices for preserving metadata...

加载HTML(了解)

1
pip install unstructured

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 1.导入相关的依赖
from langchain.document_loaders import UnstructuredHTMLLoader

# 2.定义UnstructuredHTMLLoader对象
# strategy:
# "fast" 解析加载html文件速度是比较快(但可能丢失部分结构或元数据)
# "hi_res": (高分辨率解析) 解析精准(速度慢一些)
# "ocr_only" 强制使用ocr提取文本,仅仅适用于图像(对HTML无效)
# mode :one of `{'paged', 'elements', 'single'}
# "elements" 按语义元素(标题、段落、列表、表格等)拆分成多个独立的小文档
html_loader = UnstructuredHTMLLoader(
file_path= "asset/load/05-load.html" ,
mode= "elements" ,
strategy= "fast" )

# 3.加载
docs = html_loader.load()
print ( len (docs)) # 16

# 4.打印
for doc in docs:
print (doc)

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
16
page_content='首发于自然语言处理算法与实践' metadata={'source': 'asset/load/05-load.html', 'last_modified': '2025-09-24T15:26:37', 'languages': ['zho'], 'file_directory': 'asset/load', 'filename': '05-load.html', 'filetype': 'text/html', 'category': 'UncategorizedText', 'element_id': 'b082a3e1f4714ffa5f25741f39d82c17'}
page_content='RAG:将检索与生成方式相结合来做生成任务' metadata={'source': 'asset/load/05-load.html', 'category_depth': 0, 'last_modified': '2025-09-24T15:26:37', 'languages': ['kor'], 'file_directory': 'asset/load', 'filename': '05-load.html', 'filetype': 'text/html', 'category': 'Title', 'element_id': '46103fd31eae47ed36481d13185af8a9'}
page_content='烛之文' metadata={'source': 'asset/load/05-load.html', 'last_modified': '2025-09-24T15:26:37', 'languages': ['kor'], 'file_directory': 'asset/load', 'filename': '05-load.html', 'filetype': 'text/html', 'parent_id': '46103fd31eae47ed36481d13185af8a9', 'category': 'UncategorizedText', 'element_id': 'e02798c2e2bb964165a9e9356b82a3f6'}
page_content='1、前言' metadata={'source': 'asset/load/05-load.html', 'category_depth': 1, 'last_modified': '2025-09-24T15:26:37', 'languages': ['zho'], 'file_directory': 'asset/load', 'filename': '05-load.html', 'filetype': 'text/html', 'parent_id': '46103fd31eae47ed36481d13185af8a9', 'category': 'Title', 'element_id': '683a24e897e3a9b862ead6c7979a58dc'}
page_content='在上一篇<kNN-NER:利用knn近邻算法来做命名实体识别>提及到文中提出kNN-NER框架是一种检索式增强的方法(retrieval augmented methods),就去查看有关retrieval augmented的paper,了解其核心思想,觉得检索式增强的方法很适合许多业务场景使用,因其以一种简捷的方式将外部知识融于模型中去。今天就分享一篇来自Facebook AI Research的paper<Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks>,论文提出一种检索式增强生成方法,应用于知识密集型的NLP任务(如问答生成),该篇论文被2020年NeurIPS 会议接收。' metadata={'source': 'asset/load/05-load.html', 'last_modified': '2025-09-24T15:26:37', 'languages': ['nor'], 'file_directory': 'asset/load', 'filename': '05-load.html', 'filetype': 'text/html', 'parent_id': '683a24e897e3a9b862ead6c7979a58dc', 'category': 'NarrativeText', 'element_id': 'd345b4a58c84984eb1acf1105fd9f214'}
page_content='文中说到,以BERT之类的大规模预训练模型将很多事实知识信息存入模型中,可以看着是pre-trained parametric类型,尽管以fine-tuned方式在下游任务取得显著的成效,但这类方法仍存在无法精准地获取和操作知识的缺陷。而在上述提及的问题上,传统知识检索的方法能很好的应对,这类方法可以看着是non-parametric memory类型。于是,论文提出检索式增强生成方法(retrieval-augmented generation,RAG),主要思想就是将pre-trained parametric与non-parametric memory结合起来做语言生成任务,将两类模型集成起来提高任务处理效果。' metadata={'source': 'asset/load/05-load.html', 'last_modified': '2025-09-24T15:26:37', 'languages': ['nor'], 'file_directory': 'asset/load', 'filename': '05-load.html', 'filetype': 'text/html', 'parent_id': '683a24e897e3a9b862ead6c7979a58dc', 'category': 'UncategorizedText', 'element_id': '2fe8d146b5803ec72e5173bf15599710'}
page_content='2、RAG方法' metadata={'source': 'asset/load/05-load.html', 'category_depth': 1, 'last_modified': '2025-09-24T15:26:37', 'languages': ['zho'], 'file_directory': 'asset/load', 'filename': '05-load.html', 'filetype': 'text/html', 'parent_id': '46103fd31eae47ed36481d13185af8a9', 'category': 'Title', 'element_id': '22ca96c9bf71395b8fbbf0928bd7f292'}
page_content='上图为论文提出RAG模型的整体示意图。主要包括两大模块:一个检索器(Retriever, p_\\eta(z|x) ) + 一个生成器(Generator, p_\\theta(y_i|x,z,y_{1:i-1}) )。前者包括query encoder和document index,分别负责query的编码和文档的索引;后者是一个seq2seq的生成模型。在检索中,使用的是最大内积搜索的方法(MIPS)来检索top-K相关文档。' metadata={'source': 'asset/load/05-load.html', 'last_modified': '2025-09-24T15:26:37', 'languages': ['cat', 'nor'], 'file_directory': 'asset/load', 'filename': '05-load.html', 'filetype': 'text/html', 'parent_id': '22ca96c9bf71395b8fbbf0928bd7f292', 'category': 'NarrativeText', 'element_id': '6bbc63aaa7b3afb8d2685e9b3de78a4c'}
page_content='3、实验' metadata={'source': 'asset/load/05-load.html', 'category_depth': 1, 'last_modified': '2025-09-24T15:26:37', 'languages': ['zho'], 'file_directory': 'asset/load', 'filename': '05-load.html', 'filetype': 'text/html', 'parent_id': '46103fd31eae47ed36481d13185af8a9', 'category': 'Title', 'element_id': '4df308cd6991fb9e3f0592371bae26be'}
page_content='论文在四类Knowledge-Intensive 任务上进行实验,具体包括开放问答(Open-domain Question Answering )、摘要式问答(Abstractive Question Answering) 、开放问题生成(Jeopardy Question Generation)、事实判断(Fact Verification ),并使用维基百科(包含2100万个文档)作为检索库。' metadata={'source': 'asset/load/05-load.html', 'last_modified': '2025-09-24T15:26:37', 'languages': ['eng'], 'file_directory': 'asset/load', 'filename': '05-load.html', 'filetype': 'text/html', 'parent_id': '4df308cd6991fb9e3f0592371bae26be', 'category': 'UncategorizedText', 'element_id': 'ed6e043c7fd99ec0824b91725c66e0ba'}
page_content='4、结语' metadata={'source': 'asset/load/05-load.html', 'category_depth': 1, 'last_modified': '2025-09-24T15:26:37', 'languages': ['zho'], 'file_directory': 'asset/load', 'filename': '05-load.html', 'filetype': 'text/html', 'parent_id': '46103fd31eae47ed36481d13185af8a9', 'category': 'Title', 'element_id': '95bc5bffaa5cfd41f5242f9f8b330761'}
page_content='本次分享基于检索增强方式将外部知识融于生成任务中一个新的框架――RAG。对比T5 和 BART这类擅长处理生成任务的模型来说,RAG更新外部知识是不需要重新预训练,成本低;而对比pipeline方法,RAG利用外部知识并不需要构造负责的特征工程。总的来说,RAG方法可作为外部知识融合框架的一种有效实例。' metadata={'source': 'asset/load/05-load.html', 'last_modified': '2025-09-24T15:26:37', 'languages': ['zho', 'kor'], 'file_directory': 'asset/load', 'filename': '05-load.html', 'filetype': 'text/html', 'parent_id': '95bc5bffaa5cfd41f5242f9f8b330761', 'category': 'UncategorizedText', 'element_id': '232dc4ae399e0a8847bfcdeb2e64e215'}
page_content='有兴趣可关注笔者公众号:自然语言处理算法与实践' metadata={'source': 'asset/load/05-load.html', 'last_modified': '2025-09-24T15:26:37', 'languages': ['zho', 'kor'], 'file_directory': 'asset/load', 'filename': '05-load.html', 'filetype': 'text/html', 'parent_id': '95bc5bffaa5cfd41f5242f9f8b330761', 'category': 'UncategorizedText', 'element_id': '41f28a1034fdc4291daf652054c20bd2'}
page_content='编辑于 2022-04-06 10:47' metadata={'source': 'asset/load/05-load.html', 'last_modified': '2025-09-24T15:26:37', 'languages': ['zho'], 'file_directory': 'asset/load', 'filename': '05-load.html', 'filetype': 'text/html', 'parent_id': '95bc5bffaa5cfd41f5242f9f8b330761', 'category': 'UncategorizedText', 'element_id': 'f70255f8bf13d39885508fd845c22382'}
page_content='深度学习(Deep Learning)' metadata={'source': 'asset/load/05-load.html', 'last_modified': '2025-09-24T15:26:37', 'languages': ['nld', 'eng'], 'file_directory': 'asset/load', 'filename': '05-load.html', 'filetype': 'text/html', 'parent_id': '95bc5bffaa5cfd41f5242f9f8b330761', 'category': 'UncategorizedText', 'element_id': '1818d3e8e3a4ce395732bba3428a111d'}
page_content='自然语言处理算法与实践' metadata={'source': 'asset/load/05-load.html', 'category_depth': 2, 'last_modified': '2025-09-24T15:26:37', 'languages': ['kor', 'zho'], 'file_directory': 'asset/load', 'filename': '05-load.html', 'filetype': 'text/html', 'parent_id': '95bc5bffaa5cfd41f5242f9f8b330761', 'category': 'Title', 'element_id': '95058f2148219d97462c5c4bfe175502'}

加载Markdown(了解)

1
2
pip install markdown 
pip install unstructured

举例1:使用MarkDownLoader加载md文件

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1.导入相关的依赖
from langchain.document_loaders import UnstructuredMarkdownLoader
from pprint import pprint

# 2.定义UnstructuredMarkdownLoader对象
md_loader = UnstructuredMarkdownLoader( file_path= "asset/load/06-load.md" , strategy= "fast" )
# 3.加载
docs = md_loader.load()
print ( len (docs))

# 4.打印
for doc in docs:
pprint(doc)

结果:

1
2
3
1
Document(metadata={'source': 'asset/load/06-load.md'}, page_content='自然语言处理技术文档\n\n本文档用于测试UnstructuredMarkdownLoader的中文处理能力。\n\n第一章:简介\n\n自然语言处理(NLP)是人工智能的重要分支,主要技术包括:\n\n文本分类\n\n命名实体识别\n\n机器翻译\n\n情感分析\n\n问答系统\n\n第二章:关键技术\n\n2.1 预训练模型\n\nBERT:双向Transformer编码器\n\nGPT:自回归语言模型\n\nT5:文本到文本转换框架\n\n2.2 代码示例\n\n```python from transformers import pipeline\n\n创建文本分类管道\n\nclassifier = pipeline("text-classification", model="bert-base-chinese")\n\nresult = classifier("这家餐厅的服务很棒!") print(result)')

举例2:精细分割文档,保留结构信息

将Markdown文档按语义元素(标题、段落、列表、表格等)拆分成多个独立的小文档(Element对 象),而不是返回单个大文档。通过指定mode=”elements”轻松保持这种分离。
每个分割后的元素会包含元数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1.导入相关的依赖
from langchain.document_loaders import UnstructuredMarkdownLoader
from pprint import pprint

# 2.定义UnstructuredMarkdownLoader对象
md_loader = UnstructuredMarkdownLoader(
file_path= "./asset/load/06-load.md" ,
mode= "elements" ,
strategy= "fast" )

# 3.加载
docs = md_loader.load()
print ( len (docs))

# 4.打印
for doc in docs:
# pprint(doc)
pprint(doc.page_content)

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
19
Document(metadata={'source': 'asset/load/06-load.md', 'category_depth': 0, 'languages': ['eng'], 'file_directory': 'asset/load', 'filename': '06-load.md', 'filetype': 'text/markdown', 'last_modified': '2025-09-24T15:26:39', 'category': 'Title', 'element_id': '184afae73069130590c7608c471f63f4'}, page_content='自然语言处理技术文档')
Document(metadata={'source': 'asset/load/06-load.md', 'languages': ['eng'], 'file_directory': 'asset/load', 'filename': '06-load.md', 'filetype': 'text/markdown', 'last_modified': '2025-09-24T15:26:39', 'parent_id': '184afae73069130590c7608c471f63f4', 'category': 'UncategorizedText', 'element_id': '18f55c7a014f88171f86dc848b58a83b'}, page_content='本文档用于测试UnstructuredMarkdownLoader的中文处理能力。')
Document(metadata={'source': 'asset/load/06-load.md', 'category_depth': 1, 'languages': ['eng'], 'file_directory': 'asset/load', 'filename': '06-load.md', 'filetype': 'text/markdown', 'last_modified': '2025-09-24T15:26:39', 'parent_id': '184afae73069130590c7608c471f63f4', 'category': 'Title', 'element_id': '6a6844d806798af924a74eecc9bf3c1f'}, page_content='第一章:简介')
Document(metadata={'source': 'asset/load/06-load.md', 'languages': ['eng'], 'file_directory': 'asset/load', 'filename': '06-load.md', 'filetype': 'text/markdown', 'last_modified': '2025-09-24T15:26:39', 'parent_id': '6a6844d806798af924a74eecc9bf3c1f', 'category': 'UncategorizedText', 'element_id': 'e7b3e94da2b42ec341e42747172c2fb1'}, page_content='自然语言处理(NLP)是人工智能的重要分支,主要技术包括:')
Document(metadata={'source': 'asset/load/06-load.md', 'category_depth': 1, 'languages': ['eng'], 'file_directory': 'asset/load', 'filename': '06-load.md', 'filetype': 'text/markdown', 'last_modified': '2025-09-24T15:26:39', 'parent_id': '6a6844d806798af924a74eecc9bf3c1f', 'category': 'ListItem', 'element_id': 'eb53d308db7e96fa7bf107143411c209'}, page_content='文本分类')
Document(metadata={'source': 'asset/load/06-load.md', 'category_depth': 1, 'languages': ['eng'], 'file_directory': 'asset/load', 'filename': '06-load.md', 'filetype': 'text/markdown', 'last_modified': '2025-09-24T15:26:39', 'parent_id': '6a6844d806798af924a74eecc9bf3c1f', 'category': 'ListItem', 'element_id': 'd3cd19f3de6c7be342b6d45249ed5936'}, page_content='命名实体识别')
Document(metadata={'source': 'asset/load/06-load.md', 'category_depth': 1, 'languages': ['eng'], 'file_directory': 'asset/load', 'filename': '06-load.md', 'filetype': 'text/markdown', 'last_modified': '2025-09-24T15:26:39', 'parent_id': '6a6844d806798af924a74eecc9bf3c1f', 'category': 'ListItem', 'element_id': '4af0dd9da13771840a9696cdcbb502e1'}, page_content='机器翻译')
Document(metadata={'source': 'asset/load/06-load.md', 'category_depth': 1, 'languages': ['eng'], 'file_directory': 'asset/load', 'filename': '06-load.md', 'filetype': 'text/markdown', 'last_modified': '2025-09-24T15:26:39', 'parent_id': '6a6844d806798af924a74eecc9bf3c1f', 'category': 'ListItem', 'element_id': '862050b1daeb945947f4c00587f16954'}, page_content='情感分析')
Document(metadata={'source': 'asset/load/06-load.md', 'category_depth': 1, 'languages': ['eng'], 'file_directory': 'asset/load', 'filename': '06-load.md', 'filetype': 'text/markdown', 'last_modified': '2025-09-24T15:26:39', 'parent_id': '6a6844d806798af924a74eecc9bf3c1f', 'category': 'ListItem', 'element_id': '7f221beeb60cc8089f9878c70230b9a5'}, page_content='问答系统')
Document(metadata={'source': 'asset/load/06-load.md', 'category_depth': 1, 'languages': ['eng'], 'file_directory': 'asset/load', 'filename': '06-load.md', 'filetype': 'text/markdown', 'last_modified': '2025-09-24T15:26:39', 'parent_id': '184afae73069130590c7608c471f63f4', 'category': 'Title', 'element_id': 'c2bf8c7c4e88556b7afb7b5407d4fbf2'}, page_content='第二章:关键技术')
Document(metadata={'source': 'asset/load/06-load.md', 'category_depth': 2, 'languages': ['eng'], 'file_directory': 'asset/load', 'filename': '06-load.md', 'filetype': 'text/markdown', 'last_modified': '2025-09-24T15:26:39', 'parent_id': 'c2bf8c7c4e88556b7afb7b5407d4fbf2', 'category': 'Title', 'element_id': 'd32a685b6777cb3277c7732ca2603203'}, page_content='2.1 预训练模型')
Document(metadata={'source': 'asset/load/06-load.md', 'category_depth': 1, 'emphasized_text_contents': ['BERT'], 'emphasized_text_tags': ['b'], 'languages': ['eng'], 'file_directory': 'asset/load', 'filename': '06-load.md', 'filetype': 'text/markdown', 'last_modified': '2025-09-24T15:26:39', 'parent_id': 'd32a685b6777cb3277c7732ca2603203', 'category': 'ListItem', 'element_id': '3efd83a33d46349a06c9489309f23af0'}, page_content='BERT:双向Transformer编码器')
Document(metadata={'source': 'asset/load/06-load.md', 'category_depth': 1, 'emphasized_text_contents': ['GPT'], 'emphasized_text_tags': ['b'], 'languages': ['eng'], 'file_directory': 'asset/load', 'filename': '06-load.md', 'filetype': 'text/markdown', 'last_modified': '2025-09-24T15:26:39', 'parent_id': 'd32a685b6777cb3277c7732ca2603203', 'category': 'ListItem', 'element_id': '042b24863d1db56d10e6222413089a30'}, page_content='GPT:自回归语言模型')
Document(metadata={'source': 'asset/load/06-load.md', 'category_depth': 1, 'emphasized_text_contents': ['T5'], 'emphasized_text_tags': ['b'], 'languages': ['eng'], 'file_directory': 'asset/load', 'filename': '06-load.md', 'filetype': 'text/markdown', 'last_modified': '2025-09-24T15:26:39', 'parent_id': 'd32a685b6777cb3277c7732ca2603203', 'category': 'ListItem', 'element_id': '7da9d82f5274c3edb7ae917995c08db2'}, page_content='T5:文本到文本转换框架')
Document(metadata={'source': 'asset/load/06-load.md', 'category_depth': 2, 'languages': ['eng'], 'file_directory': 'asset/load', 'filename': '06-load.md', 'filetype': 'text/markdown', 'last_modified': '2025-09-24T15:26:39', 'parent_id': 'c2bf8c7c4e88556b7afb7b5407d4fbf2', 'category': 'Title', 'element_id': '58fa664fefbefa6e86ee4fc8dd128878'}, page_content='2.2 代码示例')
Document(metadata={'source': 'asset/load/06-load.md', 'languages': ['eng'], 'file_directory': 'asset/load', 'filename': '06-load.md', 'filetype': 'text/markdown', 'last_modified': '2025-09-24T15:26:39', 'parent_id': '58fa664fefbefa6e86ee4fc8dd128878', 'category': 'NarrativeText', 'element_id': '2d72807f991457ffe38dad8b46480b0b'}, page_content='```python from transformers import pipeline')
Document(metadata={'source': 'asset/load/06-load.md', 'category_depth': 0, 'languages': ['eng'], 'file_directory': 'asset/load', 'filename': '06-load.md', 'filetype': 'text/markdown', 'last_modified': '2025-09-24T15:26:39', 'category': 'Title', 'element_id': 'f5a25376ceb9e9392b4e0b2b60153cca'}, page_content='创建文本分类管道')
Document(metadata={'source': 'asset/load/06-load.md', 'languages': ['eng'], 'file_directory': 'asset/load', 'filename': '06-load.md', 'filetype': 'text/markdown', 'last_modified': '2025-09-24T15:26:39', 'parent_id': 'f5a25376ceb9e9392b4e0b2b60153cca', 'category': 'UncategorizedText', 'element_id': '9745c5d59a60933121ca91a9b71e6acf'}, page_content='classifier = pipeline("text-classification", model="bert-base-chinese")')
Document(metadata={'source': 'asset/load/06-load.md', 'languages': ['eng'], 'file_directory': 'asset/load', 'filename': '06-load.md', 'filetype': 'text/markdown', 'last_modified': '2025-09-24T15:26:39', 'parent_id': 'f5a25376ceb9e9392b4e0b2b60153cca', 'category': 'UncategorizedText', 'element_id': '7ebd846af465424c8c01cb58cfa79c86'}, page_content='result = classifier("这家餐厅的服务很棒!") print(result)')

加载File Directory(了解)

除了上述的单个文件加载,我们也可以批量加载一个文件夹内的所有文件。

1
pip install unstructured

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 1.导入相关的依赖  
from langchain.document_loaders import DirectoryLoader
from langchain.document_loaders import PythonLoader
from pprint import pprint

# 2.定义DirectoryLoader对象,指定要加载的文件夹路径、要加载的文件类型和是否使用多线程
directory_loader = DirectoryLoader(
path="./asset/load",
glob="*.py",
use_multithreading=True,
show_progress=True,
loader_cls=PythonLoader
)

# 3.加载
docs = directory_loader.load()

# 4.打印
print(len(docs))
for doc in docs:
pprint(doc)

结果:

1
2
3
4
5
6
7
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:00<00:00, 227.64it/s]
4
Document(metadata={'source': 'asset\\load\\07-fun_retun.py'}, page_content='"""\n四 函数的返回值\n"""\n# 1.返回表达式\n# 2.不带表达式的 return 语句,返回 None。\n# 3.函数中如果没有 return 语句,在函数运行结束后也会返回 None。\n# 4.用变量接收返回结果\n# 5.return 语句可以返回多个值,多个值会放在一个元组中。\n\ndef f(a, b, c):\n return a, b, c, [a, b, c]\nprint(f(1, 2, 3)) # (1, 2, 3, [1, 2, 3])\n')
Document(metadata={'source': 'asset\\load\\07-param_form.py'}, page_content='"""\n三 函数参数形式\n"""\n# 1.位置参数\n# 2.关键字参数\n# 3.默认参数\n# 4.不定长参数\n# 4.1 带一个*\ndef printInfo(num,*vartuple):\n print(num)\n print(vartuple)\n\nprintInfo(70,60,50)\n\nprint("-" * 20)\n# 如果不定长的参数后面还有参数,必须通过关键字参数传参\ndef printInfo1(num1,*vartuple,num) :\n print(num)\n print(num1)\n print(vartuple)\n\nprintInfo1(10,20,num = 40)\n\nprint("-" * 20)\n# 如果没有给不定长的参数传参,那么得到的是空元组\nprintInfo1(70,num = 60)\n# 4.2 带二个*\ndef printInfo(num,**vardict):\n print(num)\n print(vardict)\n # return\n\nprintInfo(10,key1 = 20,key2 = 30)')
Document(metadata={'source': 'asset\\load\\07-fun.py'}, page_content='"""\n一 函数入门\n"""\n# 1.不使用函数\n# 打印欢迎信息1\nprint("********************************")\nprint("* *")\nprint("* 欢迎来到Python世界 *")\nprint("* *")\nprint("********************************")\n\n# 打印欢迎信息2\nprint("********************************")\nprint("* *")\nprint("* 欢迎来到Python世界 *")\nprint("* *")\nprint("********************************")\n\n# 打印欢迎信息3\nprint("********************************")\nprint("* *")\nprint("* 欢迎来到Python世界 *")\nprint("* *")\nprint("********************************")\n\n# 2.使用函数\ndef print_welcome():\n """打印欢迎信息"""\n print("********************************")\n print("* *")\n print("* 欢迎来到Python世界 *")\n print("* *")\n print("********************************")\n\n# 多次调用函数打印欢迎信息\nprint_welcome()\nprint_welcome()\nprint_welcome()')
Document(metadata={'source': 'asset\\load\\07-fun_param.py'}, page_content='"""\n二 函数参数\n"""\n\n\n# 1. 无参数版本 - 只能计算固定的购物车\ndef calculate_total_no_params():\n """计算固定购物车总价"""\n prices = [100, 50, 30] # 商品价格固定写死在函数内\n total = 0\n for price in prices:\n total += price\n return total\n\n# 只能计算一个固定的购物车\nprint(f"购物车总价:{calculate_total_no_params()}")\n\n# 2.有参数版本 - 可以计算任意购物车\ndef calculate_total(prices):\n """计算任意购物车总价"""\n total = 0\n for price in prices:\n total += price\n return total\n\n# 可以计算任意购物车\ncart1 = [100, 50, 30]\ncart2 = [200, 80, 45, 60]\ncart3 = [75, 90, 120]\n\nprint("第一个购物车总价:{calculate_total(cart1)}:")\nprint("第二个购物车总价:{calculate_total(cart2)}")\nprint(f"第三个购物车总价:{calculate_total(cart3)}")\n\n\n# 3.参数传递\n# 3.1 不可变类型 函数传递不可变对象\n\ndef changeInt(a) :\n print("函数体中未改变前a的内存地址",id(a))\n a = 10 #底层会创建一个新对象 然后给新对象一个新值\n print("函数体中改变后a的内存地址",id(a))\n\na = 2 # 创建一个对象 然后给这个对象一个值\nchangeInt(a)\nprint(a)\nprint("函数外b的内存地址",id(a))\n\n\n\n# 输出结果\n# 函数体中未改变前a的内存地址 140729722661336\n# 函数体中改变后a的内存地址 140729722661592\n# 2\n# 函数外b的内存地址 140729722661336\n\n\n# 3.2 可变类型 函数传递不可变对象\n\ndef changeList(myList) :\n myList[1] = 50\n print("函数内的值",myList) # [1,50,3]\n print("函数内列表的内存",id(myList)) # 0111111\n\nmlist = [1,2,3] # 底层创建一个对象 地址0111111\nchangeList(mlist)\nprint("函数外的值",mlist) # # [1,50,3]\nprint("函数外列表的内存",id(mlist))\n\n# 输出结果\n# 函数内的值 [1, 50, 3]\n# 函数内列表的内存 1380193079680\n# 函数外的值 [1, 50, 3]\n# 函数外列表的内存 1380193079680\n\n')

文档拆分器 Text Splitters

为什么要拆分/分块/切分

当拿到统一的一个Document对象后,接下来需要切分成Chunks。如果不切分,而是考虑作为一个整体 的Document对象,会存在两点问题:

  1. 假设提问的Query的答案出现在某一个Document对象中,那么将检索到的整个Document对象 直接放入Prompt中并不是最优的选择,因为其中一定会包含非常多无关的信息,而无效信息越 多,对大模型后续的推理影响越大。
  2. 任何一个大模型都存在最大输入的Token限制,如果一个Document非常大,比如一个几百兆的 PDF,那么大模型肯定无法容纳如此多的信息。

基于此,一个有效的解决方案就是将完整的Document对象进行分块处理(Chunking)。无论是在存储 还是检索过程中,都将以这些块(chunks) 为基本单位,这样有效地避免内容不相关性问题和超出最大输 入限制的问题。

Chunking拆分的策略

方法1:根据句子切分:这种方法按照自然句子边界进行切分,以保持语义完整性。

方法2:按照固定字符数来切分:这种策略根据特定的字符数量来划分文本,但可能会在不适当的位置 切断句子。

方法3:按固定字符数来切分,结合重叠窗口(overlapping windows):此方法与按字符数切分相 似,但通过重叠窗口技术避免切分关键内容,确保信息连贯性。

方法4:递归字符切分方法:通过递归字符方式动态确定切分点,这种方法可以根据文档的复杂性和内 容密度来调整块的大小。

方法5:根据语义内容切分:这种 高级策略依据文本的语义内容来划分块,旨在保持相关信息的集中和 完整,适用于需要高度语义保持的应用场景。

[!tip]
第2种⽅法(按照字符数切分)和第3种⽅法(按固定字符数切分结合重叠窗口)主要基于字符进⾏ ⽂本的切分,而不考虑⽂章的实际内容和语义。这种⽅式虽简单,但可能会导致 主题或语义上的断 裂

相对而⾔,第4种递归⽅法更加灵活和⾼效,它结合了固定⻓度切分和语义分析。通常是 首选策 略 ,因为它能够更好地确保每个段落包含⼀个完整的主题。

而第5种⽅法,基于语义的分割虽然能精确地切分出完整的主题段落,但这种⽅法效率较低。它需 要运⾏复杂的分段算法(segmentation algorithm), 处理速度较慢 ,并且 段落长度可能极不均 匀 (有的主题段落可能很⻓,而有的则较短)。因此,尽管它在某些需要⾼精度语义保持的场景 下有其应⽤价值,但并 不适合所有情况

这些方法各有优势和局限,选择适当的分块策略取决于具体的应用需求和预期的检索效果。接下来我们 依次尝试用常规手段应该如何实现上述几种方法的文本切分

具体实现

LangChain提供了许多不同类型的文档切分器

官网地址:https://python.langchain.com/api_reference/text_splitters/index.html

使用细节:

① TextSplitter作为各种具体的文档拆分器的父类

② 内部定义了一些常用的属性:

chunk_size: 返回块的最大尺寸,单位是字符数。默认值为4000(由长度函数测量)

chunk_overlap: 相邻两个块之间的字符重叠数,避免信息在边界处被切断而丢失。默认值为200,通常会设置为chunk_size的10% - 20%。

length_function: 用于测量给定块字符数的函数。默认赋值为len函数。len函数在Python中按Unicode字符计数,所以一个汉字、一个英文字母、一个符号都算一个字符。

keep_separator: 是否在块中保留分隔符,默认值为False

add_start_index: 如果为 True,则在元数据中包含块的起始索引。默认值为False

strip_whitespace: 如果为 True,则从每个文档的开始和结束处去除空白字符。默认值为True

② 内部定义的常用的方法:

情况1:按照字符串进行拆分:

split_text(xxx) : 传入的参数类型:字符串 ; 返回值的类型:List[str]

create_documents(xxx) : 传入的参数类型:List[str] ; 返回值的类型:List[Document]。底层调用了split_text(xxx)

情况2:按照Document对象进行拆分:

split_documents(xxx) : 传入的参数类型:List[Document] ; 返回值的类型:List[Document]。底层调用了create_documents(xxx)

2、Document对象 与 Str 是什么关系?

文档切分器可以按照字符进行切分,也可以按照Document进行切分。其中,Str 可以理解为是Document对象的page_content属性。

CharacterTextSplitter:Split by character

参数情况说明:

  • chunk_size :每个切块的最大token数量,默认值为4000。
  • chunk_overlap :相邻两个切块之间的最大重叠token数量,默认值为200。
  • separator :分割使用的分隔符,默认值为”\n\n”。
  • length_function :用于计算切块长度的方法。默认赋值为父类TextSplitter的len函数。

举例1:字符串文本的分割

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 1.导入相关依赖  
from langchain.text_splitter import CharacterTextSplitter
from zipp.glob import separate

# 2.示例文本
text = """
LangChain 是一个用于开发由语言模型驱动的应用程序的框架的。它提供了一套工具和抽象,使开发者能够更容易地构建复杂的应用程序。
"""

# 3.定义字符分割器
splitter = CharacterTextSplitter(
chunk_size=51, # 每块大小
chunk_overlap=7,# 块与块之间的重复字符数
#length_function=len,
separator="" # 设置为空字符串时,表示禁用分隔符
)

# 4.分割文本
texts = splitter.split_text(text)

# 5.打印结果
for i, chunk in enumerate(texts):
print(f"块 {i+1}:长度:{len(chunk)}")
print(chunk)
print("-" * 50)

结果:

1
2
3
4
5
6
块 1:长度:50
LangChain 是一个用于开发由语言模型驱动的应用程序的框架的。它提供了一套工具和抽象,使开发者
--------------------------------------------------
块 2:长度:23
抽象,使开发者能够更容易地构建复杂的应用程序。
--------------------------------------------------

说明:若必须禁用分隔符(如处理无空格文本),需容忍实际块长略小于 chunk_size (尤其对中文)

举例2:指定分割符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 1.导入相关依赖  
from langchain.text_splitter import CharacterTextSplitter

# 2.定义要分割的文本
text = "这是一个示例文本啊。我们将使用CharacterTextSplitter将其分割成小块。分割基于字符数。"

# text = """
# LangChain 是一个用于开发由语言模型。驱动的应用程序的框架的。它提供了一套工具和抽象。使开发者能够更容易地构建复杂的应用程序。
# """

# 3.定义分割器实例
text_splitter = CharacterTextSplitter(
chunk_size=30, # 每个块的最大字符数
# chunk_size=43, # 每个块的最大字符数
chunk_overlap=0, # 块之间的重叠字符数
separator="。", # 按句号分割 (分隔符优先)
)

# 4.开始分割
chunks = text_splitter.split_text(text)

# 5.打印效果
for i,chunk in enumerate(chunks):
print(f"块 {i + 1}:长度:{len(chunk)}")
print(chunk)
print("-"*50)

结果:

1
2
3
4
5
6
7
8
9
10
11
Created a chunk of size 33, which is longer than the specified 30

块 1:长度:9
这是一个示例文本啊
--------------------------------------------------
块 2:长度:33
我们将使用CharacterTextSplitter将其分割成小块
--------------------------------------------------
块 3:长度:7
分割基于字符数
--------------------------------------------------

注意:无重叠。

separator优先原则:当设置了 separator (如”。”),分割器会首先尝试在分隔符处分割,然后再考 虑 chunk_size。这是为了避免在句子中间硬性切断。这种设计是为了:

  1. 优先保持语义完整性(不切断句子)
  2. 避免产生无意义的碎片(如半个单词/不完整句子)
  3. 如果 chunk_size 比片段小,无法拆分片段,导致 overlap失效。
  4. chunk_overlap仅在合并后的片段之间生效(如果 chunk_size 足够大)。如果没有合并的片 段,则 overlap失效。见举例3。

举例3:指定分割符

注意:有重叠。此时,文本“这是第二段内容。”的token正好就是8。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 1.导入相关依赖  
from langchain.text_splitter import CharacterTextSplitter

# 2.定义要分割的文本
text = "这是第一段文本。这是第二段内容。最后一段结束。"

# 3.定义字符分割器
text_splitter = CharacterTextSplitter(
separator="。",
chunk_size=20,
chunk_overlap=8,
keep_separator=True #chunk中是否保留切割符
)

# 4.分割文本
chunks = text_splitter.split_text(text)

# 5.打印结果
for i,chunk in enumerate(chunks):
print(f"块 {i + 1}:长度:{len(chunk)}")
print(chunk)
print("-"*50)

结果:

1
2
3
4
5
6
块 1:长度:15
这是第一段文本。这是第二段内容
--------------------------------------------------
块 2:长度:16
。这是第二段内容。最后一段结束。
--------------------------------------------------

RecursiveCharacterTextSplitter:最常用

文档切分器中较常用的是 RecursiveCharacterTextSplitter (递归字符文本切分器) ,遇特定字符时进行分割。默认情况下,它尝试进行切割的字符包括[“\n\n”, “\n”, “ “, “”] 。

具体为:根据第一个字符进行切块,但如果任何切块太大,则会继续移动到下一个字符继续切块,以此 类推。
此外,还可以考虑添加,。等分割字符。
特点

  • 保留上下文:优先在自然语言边界(如段落、句子结尾)处分割, 减少信息碎片化
  • 智能分段:通过递归尝试多种分隔符,将文本分割为大小接近chunk_size的片段。
  • 灵活适配:适用于多种文本类型(代码、Markdown、普通文本等),是LangChain中最通用的 文本拆分器。

此外,还可以指定的参数包括:

  • chunk_size:同TextSplitter(父类) 。
  • chunk_overlap:同TextSplitter(父类) 。
  • length_function:同TextSplitter(父类) 。
  • add_start_index:同TextSplitter(父类) 。

举例1:使用split_text()方法演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 1.导入相关依赖  
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 2.定义RecursiveCharacterTextSplitter分割器对象
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=10,
chunk_overlap=0,
#add_start_index=True,
)

# 3.定义拆分的内容
text="LangChain框架特性\n\n多模型集成(GPT/Claude)\n记忆管理功能\n链式调用设计。文档分析场景示例:需要处理PDF/Word等格式。"

# 4.拆分器分割
paragraphs = text_splitter.split_text(text)

for para in paragraphs:
print(para)
print('-------')

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
LangChain框
-------
架特性
-------
多模型集成(GPT
-------
/Claude)
-------
记忆管理功能
-------
链式调用设计。文档
-------
分析场景示例:需要处
-------
理PDF/Word等
-------
格式。
-------

举例2:使用create_documents()方法演示,传入字符串列表,返回Document对象列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 1.导入相关依赖  
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 2.定义RecursiveCharacterTextSplitter分割器对象
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=10,
chunk_overlap=0,
add_start_index=True,
)

# 3.定义拆分的内容
text_list = ["LangChain框架特性\n\n多模型集成(GPT/Claude)\n记忆管理功能\n链式调用设计。文档分析场景示例:需要处理PDF/Word等格式。"]

# 4.拆分器分割
paragraphs = text_splitter.create_documents(text_list)

for para in paragraphs:
print(para)
print('-------')

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
page_content='LangChain框' metadata={'start_index': 0}
-------
page_content='架特性' metadata={'start_index': 10}
-------
page_content='多模型集成(GPT' metadata={'start_index': 15}
-------
page_content='/Claude)' metadata={'start_index': 24}
-------
page_content='记忆管理功能' metadata={'start_index': 33}
-------
page_content='链式调用设计。文档' metadata={'start_index': 40}
-------
page_content='分析场景示例:需要处' metadata={'start_index': 49}
-------
page_content='理PDF/Word等' metadata={'start_index': 59}
-------
page_content='格式。' metadata={'start_index': 69}
-------

逐步分割过程

第一阶段:顶级分割(按\n\n)

  1. 首次分割:
    1
    2
    3
    text.split( "\n\n" ) → 
    [ "LangChain框架特性" ,
    "多模型集成(GPT/Claude)\n记忆管理功能\n链式调用设计。文档分析场景示例:需要处理 PDF/Word等格式。" ]
  • 第一部分长度:13字符 > 10 → 需要继续分割
  • 第二部分长度:79字符 > 10 → 需要继续分割

第二阶段:递归分割第一部分 “LangChain框架特性”

  1. 尝试 \n :无匹配
  2. 尝试(空格):
    • 检查字符串: “LangChain框架特性” (无空格)
  3. 回退到””(字符级分割):
    1
    2
    list ( "LangChain框架特性" ) → 
    [ 'L' , 'a' , 'n' , 'g' , 'C' , 'h' , 'a' , 'i' , 'n' , '框' , '架' , '特' , '性' ]
    • 前10字符: “LangChain框”
    • 剩余部分: “架特性”

第三阶段:递归分割第二部分(长段落)

  1. 按 \n 分割:
    1
    2
    3
    4
    5
    "多模型集成(GPT/Claude)\n记忆管理功能\n链式调用设计。文档..." .split( "\n" ) → 
    [ "多模型集成(GPT/Claude)" , # 17字符
    "记忆管理功能" , # 6字符
    "链式调用设计。文档分析场景示例:需要处理PDF/Word等格式。" # 36字符
    ]
  • 第1块:17字符 > 10 → 继续分割
  • 第2块:6字符 ≤ 10 → 直接保留
  • 第3块:36字符 > 10 → 继续分割
  1. 分割 “多模型集成(GPT/Claude)” :
    • 尝试:无空格
    • 回退到””:
      • 前10字符: “多模型集成(GPT”
      • 剩余7字符: “/Claude)”
  2. 分割 “链式调用设计。文档分析场景示例:需要处理PDF/Word等格式。”:
    • 尝试:无空格
    • 回退到””:
      • 按10字符分段:
        1
        2
        3
        4
        5
        6
        7
        "链式调用设计。文档分析场景示例:需要处理PDF/Word等格式。"
        [
        "链式调用设计。文档" ,
        "分析场景示例:需要处" ,
        "理PDF/Word等" ,
        "格式。"
        ]

举例3:使用create_documents()方法演示,将本地文件内容加载成字符串,进行拆分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 1.导入相关依赖  
from langchain_text_splitters import RecursiveCharacterTextSplitter

# 2.打开.txt文件
with open("asset/load/08-ai.txt", encoding="utf-8") as f:
state_of_the_union = f.read() #返回的是字符串

print(type(state_of_the_union)) # <class 'str'>

# 3.定义RecursiveCharacterTextSplitter(递归字符分割器)
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=100,
chunk_overlap=20,
#chunk_overlap=0,
length_function=len
)

# 4.分割文本
texts = text_splitter.create_documents([state_of_the_union])

# 5.打印分割文本
for document in texts:
print(f"🔥{document.page_content}")

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<class 'str'>
🔥人工智能(AI)是什么?
🔥人工智能(Artificial
🔥Intelligence,简称AI)是指由计算机系统模拟人类智能的技术,使其能够执行通常需要人类认知能力的任务,如学习、推理、决策和语言理解。AI的核心目标是让机器具备感知环境、处理信息并自主行动的
🔥让机器具备感知环境、处理信息并自主行动的能力。
🔥1. AI的技术基础
AI依赖多种关键技术:

机器学习(ML):通过算法让计算机从数据中学习规律,无需显式编程。例如,推荐系统通过用户历史行为预测偏好。
🔥深度学习:基于神经网络的机器学习分支,擅长处理图像、语音等复杂数据。AlphaGo击败围棋冠军便是典型案例。

自然语言处理(NLP):使计算机理解、生成人类语言,如ChatGPT的对话能力。
🔥2. AI的应用场景
AI已渗透到日常生活和各行各业:

医疗:辅助诊断(如AI分析医学影像)、药物研发加速。

交通:自动驾驶汽车通过传感器和AI算法实现安全导航。
🔥金融:欺诈检测、智能投顾(如风险评估模型)。

教育:个性化学习平台根据学生表现调整教学内容。

3. AI的挑战与未来
尽管前景广阔,AI仍面临问题:
🔥伦理争议:数据隐私、算法偏见(如招聘AI歧视特定群体)。

就业影响:自动化可能取代部分人工岗位,但也会创造新职业。

技术瓶颈:通用人工智能(AGI)尚未实现,当前AI仅擅长特定任务。
🔥未来,AI将与人类协作而非替代:医生借助AI提高诊断效率,教师利用AI定制课程。其发展需平衡技术创新与社会责任,确保技术造福全人类。

举例4:使用split_documents()方法演示,利用PDFLoader加载文档,对文档的内容用递归切割器切割

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 1.导入相关依赖  
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 2.定义PyPDFLoader加载器
loader = PyPDFLoader("./asset/load/02-load.pdf")

# 3.加载和切割文档对象
docs = loader.load() # 返回Document对象构成的list
# print(f"第0页:\n{docs[0]}")

# 4.定义切割器
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=200,
#chunk_size=120,
chunk_overlap=0,
# chunk_overlap=100,
length_function=len,
add_start_index=True,
)

# 5.对pdf内容进行切割得到文档对象
paragraphs = text_splitter.split_documents(docs)
#paragraphs = text_splitter.create_documents([text])
for para in paragraphs:
print(para.page_content)
print('-------')

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
"他的车,他的命! 他忽然想起来,一年,二年,至少有三四年;一滴汗,两滴汗,不
知道多少万滴汗,才挣出那辆车。从风里雨里的咬牙,从饭里茶里的自苦,才赚出那辆车。
那辆车是他的一切挣扎与困苦的总结果与报酬,像身经百战的武士的一颗徽章。……他老想
着远远的一辆车,可以使他自由,独立,像自己的手脚的那么一辆车。"

"他吃,他喝,他嫖,他赌,他懒,他狡猾, 因为他没了心,他的心被人家摘了去。他
-------
只剩下那个高大的肉架子,等着溃烂,预备着到乱死岗子去。……体面的、要强的、好梦想
的、利己的、个人的、健壮的、伟大的祥子,不知陪着人家送了多少回殡;不知道何时何地
会埋起他自己来, 埋起这堕落的、 自私的、 不幸的、 社会病胎里的产儿, 个人主义的末路鬼!
"
-------

举例5:自定义分隔符

有些书写系统没有单词边界,例如中文、日文和泰文。使用默认分隔符列表[“\n\n”, “\n”, “ “, “”]分割文 本可能导致单词错误的分割。为了保持单词在一起,你可以自定义分割字符,覆盖分隔符列表以包含额 外的标点符号。

1
2
3
4
text_splitter = RecursiveCharacterTextSplitter( chunk_size=200, chunk_overlap=20, # 增加重叠字符 
separators=[ "\n\n" , "\n" , "。" , "!" , "?" , "……" , "," , "" ], # 添加中文标点
length_function= len , keep_separator=True #保留句尾标点(如 ……),避免切割后丢失语气和逻辑
)

TokenTextSplitter/CharacterTextSplitter:Split by tokens

当我们将文本拆分为块时,除了字符以外,还可以: 按Token的数量分割 (而非字符或单词数),将长 文本切分成多个小块。

什么是Token?

  • 对模型而言,Token是文本的最小处理单位。例如:
    • 英文: “hello” → 1个Token,
    • 中文: “ChatGPT” → 2个Token( “Chat” + “GPT” )。 “人工智能” → 可能拆分为2-3个Token(取决于分词器)。

为什么按Token分割?

  • 语言模型对输入长度的限制是基于Token数(如GPT-4的8k/32k Token上限),直接按字符或单 词分割可能导致实际Token数超限。(确保每个文本块不超过模型的Token上限)
  • 大语言模型(LLM)通常是以token的数量作为其计量(或收费)的依据,所以采用token分割也有助于 我们在使用时更方便的控制成本。

TokenTextSplitter 使用说明

  • 核心依据:Token数量 + 自然边界。(TokenTextSplitter 严格按照 token 数量进行分割,但同时 会优先在自然边界(如句尾)处切断,以尽量保证语义的完整性。)
  • 优点:与LLM的Token计数逻辑一致,能尽量保持语义完整
  • 缺点:对非英语或特定领域文本,Token化效果可能不佳
  • 典型场景:需要精确控制Token数输入LLM的场景

举例1:使用TokenTextSplitter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1.导入相关依赖  
from langchain_text_splitters import TokenTextSplitter

# 2.初始化 TokenTextSplittertext_splitter = TokenTextSplitter(
chunk_size=33, #最大 token 数为 32 chunk_overlap=0, #重叠 token 数为 0 encoding_name="cl100k_base", # 使用 OpenAI 的编码器,将文本转换为 token 序列

)
# 3.定义文本
text = "人工智能是一个强大的开发框架。它支持多种语言模型和工具链。人工智能是指通过计算机程序模拟人类智能的一门科学。自20世纪50年代诞生以来,人工智能经历了多次起伏。"

# 4.开始切割
texts = text_splitter.split_text(text)

# 打印分割结果
print(f"原始文本被分割成了 {len(texts)} 个块:")
for i, chunk in enumerate(texts):
print(f"块 {i+1}: 长度:{len(chunk)} 内容:{chunk}")
print("-" * 50)

结果:

1
2
3
4
5
6
7
原始文本被分割成了 3 个块:
块 1: 长度:29 内容:人工智能是一个强大的开发框架。它支持多种语言模型和工具链。
--------------------------------------------------
块 2: 长度:32 内容:人工智能是指通过计算机程序模拟人类智能的一门科学。自20世纪50
--------------------------------------------------
块 3: 长度:19 内容:年代诞生以来,人工智能经历了多次起伏。
--------------------------------------------------

为什么会出现这样的分割?
1、第一块 (29字符) :内容是一个完整的句子,以句号结尾。TokenTextSplitter识别到这是一个自然的 语义边界,即使这里的 token 数量可能尚未达到 33,它也选择在此处切割,以保证第一块语义的完整 性。

2、第二块 (32字符) :内容包含了另一个完整句子 “人工智能是指…一门科学。”以及下一句的开头 20世纪50” 。分割器在处理完第一个句子的 token 后,可能 token 数量已经接近 “自 chunk_size ,于是 在下一个自然边界(这里是句号)之后继续读取了少量 token(“自20世纪50”),直到非常接近 33 token 的限制。

注意:“50” 之后被切断,是因为编码器很可能将“50”识别为一个独立的 token,而“年代”是另 一个 token。为了保证 token 的完整性,它不会在“50”字符中间切断。

3、第三块 (19字符) :是第二块中断内容的剩余部分,形成了一个较短的块。这是因为剩余内容本身的 token 数量就较少。
特别注意:字符长度不等于 Token 数量

举例2:使用CharacterTextSplitter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 1.导入相关依赖  
from langchain_text_splitters import CharacterTextSplitter
import tiktoken # 用于计算Token数量


# 2.定义通过Token切割器
text_splitter = CharacterTextSplitter.from_tiktoken_encoder(
encoding_name="cl100k_base", # 使用 OpenAI 的编码器
chunk_size=18, #设置最大的token数
chunk_overlap=0,
separator="。", # 指定中文句号为分隔符
keep_separator=False, # chunk中是否保留分隔符
)
# 3.定义文本
text = "人工智能是一个强大的开发框架。它支持多种语言模型和工具链。今天天气很好,想出去踏青。但是又比较懒不想出去,怎么办"

# 4.开始切割
texts = text_splitter.split_text(text)
print(f"分割后的块数: {len(texts)}")

# 5.初始化tiktoken编码器(用于Token计数)
encoder = tiktoken.get_encoding("cl100k_base") # 确保与CharacterTextSplitter的encoding_name一致

# 6.打印每个块的Token数和内容
for i, chunk in enumerate(texts):
tokens = encoder.encode(chunk) # 现在encoder已定义
print(f"块 {i + 1}: {len(tokens)} Token\n内容: {chunk}\n")

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
分割后的块数: 4
块 1: 17 Token
内容: 人工智能是一个强大的开发框架

块 2: 14 Token
内容: 它支持多种语言模型和工具链

块 3: 18 Token
内容: 今天天气很好,想出去踏青

块 4: 21 Token
内容: 但是又比较懒不想出去,怎么办

SemanticChunker:语义分块

SemanticChunking(语义分块)是 LangChain 中一种更高级的文本分割方法,它超越了传统的基于字 符或固定大小的分块方式,而是根据文本的语义结构进行智能分块,使每个分块保持语义完整性,从而 提高检索增强生成(RAG)等应用的效果。

语义分割 vs 传统分割

特性 语义分割(SemanticChunker) 传统字符分割(RecursiveCharacter)
分割依据 嵌入向量相似度 固定字符/换行符
语义完整性 ✅ 保持主题连贯 ❌ 可能切断句子逻辑
计算成本 ❌ 高(需嵌入模型) ✅ 低
适用场景 需要高语义一致性的任务 简单文本预处理

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from langchain_experimental.text_splitter import SemanticChunker  
from langchain_openai.embeddings import OpenAIEmbeddings
import os
import dotenv

dotenv.load_dotenv()

# 加载文本
with open("asset/load/09-ai1.txt", encoding="utf-8") as f:
state_of_the_union = f.read() #返回字符串

# 获取嵌入模型
os.environ['OPENAI_API_KEY'] = "sk-jdwjsxnkyahrzybuiorfenmjmvaakupdxxxxxxxxx"
os.environ['OPENAI_BASE_URL'] = "https://api.siliconflow.cn/v1"
embed_model = OpenAIEmbeddings(
model="Qwen/Qwen3-Embedding-0.6B"
)

# 获取切割器
text_splitter = SemanticChunker(
embeddings=embed_model,
breakpoint_threshold_type="percentile",#断点阈值类型:字面值["百分位数", "标准差", "四分位距", "梯度"] 选其一
breakpoint_threshold_amount=65.0 #断点阈值数量 (极低阈值 → 高分割敏感度)
)

# 切分文档
docs = text_splitter.create_documents(texts = [state_of_the_union])
print(len(docs))
for doc in docs:
print(f"🔍 文档 {doc}:")

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
4
🔍 文档 page_content='人工智能综述:发展、应用与未来展望

摘要
人工智能(Artificial Intelligence,AI)作为计算机科学的一个重要分支,近年来取得了突飞猛进的发展。本文综述了人工智能的发展历程、核心技术、应用领域以及未来发展趋势。通过对人工智能的定义、历史背景、主要技术(如机器学习、深度学习、自然语言处理等)的详细介绍,探讨了人工智能在医疗、金融、教育、交通等领域的应用,并分析了人工智能发展过程中面临的挑战与机遇。最后,本文对人工智能的未来发展进行了展望,提出了可能的突破方向。

1.':
🔍 文档 page_content='引言
人工智能是指通过计算机程序模拟人类智能的一门科学。自20世纪50年代诞生以来,人工智能经历了多次起伏,近年来随着计算能力的提升和大数据的普及,人工智能技术取得了显著的进展。人工智能的应用已经渗透到日常生活的方方面面,从智能手机的语音助手到自动驾驶汽车,从医疗诊断到金融分析,人工智能正在改变着人类社会的运行方式。

2. 人工智能的发展历程
2.1 早期发展
人工智能的概念最早可以追溯到20世纪50年代。1956年,达特茅斯会议(Dartmouth Conference)被认为是人工智能研究的正式开端。在随后的几十年里,人工智能研究经历了多次高潮与低谷。早期的研究主要集中在符号逻辑和专家系统上,但由于计算能力的限制和算法的不足,进展缓慢。
2.2 机器学习的兴起
20世纪90年代,随着统计学习方法的引入,机器学习逐渐成为人工智能研究的主流。支持向量机(SVM)、决策树、随机森林等算法在分类和回归任务中取得了良好的效果。这一时期,机器学习开始应用于数据挖掘、模式识别等领域。
2.3 深度学习的突破
2012年,深度学习在图像识别领域取得了突破性进展,标志着人工智能进入了一个新的阶段。深度学习通过多层神经网络模拟人脑的工作方式,能够自动提取特征并进行复杂的模式识别。卷积神经网络(CNN)、循环神经网络(RNN)和长短期记忆网络(LSTM)等深度学习模型在图像处理、自然语言处理、语音识别等领域取得了显著成果。

3.':
🔍 文档 page_content='人工智能的核心技术
3.1 机器学习
机器学习是人工智能的核心技术之一,通过算法使计算机从数据中学习并做出决策。常见的机器学习算法包括监督学习、无监督学习和强化学习。监督学习通过标记数据进行训练,无监督学习则从未标记数据中寻找模式,强化学习则通过与环境交互来优化决策。
3.2 深度学习
深度学习是机器学习的一个子领域,通过多层神经网络进行特征提取和模式识别。深度学习在图像识别、自然语言处理、语音识别等领域取得了显著成果。常见的深度学习模型包括卷积神经网络(CNN)、循环神经网络(RNN)和长短期记忆网络(LSTM)。
3.3 自然语言处理
自然语言处理(NLP)是人工智能的一个重要分支,致力于使计算机能够理解和生成人类语言。NLP技术广泛应用于机器翻译、情感分析、文本分类等领域。近年来,基于深度学习的NLP模型(如BERT、GPT)在语言理解任务中取得了突破性进展。
3.4 计算机视觉
计算机视觉是人工智能的另一个重要分支,致力于使计算机能够理解和处理图像和视频。计算机视觉技术广泛应用于图像识别、目标检测、人脸识别等领域。深度学习模型(如CNN)在计算机视觉任务中取得了显著成果。

4. 人工智能的应用领域
4.1 医疗健康
人工智能在医疗健康领域的应用包括疾病诊断、药物研发、个性化医疗等。通过分析医学影像和患者数据,人工智能可以帮助医生更准确地诊断疾病,提高治疗效果。
4.2 金融
人工智能在金融领域的应用包括风险评估、欺诈检测、算法交易等。通过分析市场数据和交易记录,人工智能可以帮助金融机构做出更明智的决策,提高运营效率。
4.3 教育
人工智能在教育领域的应用包括个性化学习、智能辅导、自动评分等。通过分析学生的学习数据,人工智能可以为学生提供个性化的学习建议,提高学习效果。
4.4 交通
人工智能在交通领域的应用包括自动驾驶、交通管理、智能导航等。通过分析交通数据和路况信息,人工智能可以帮助优化交通流量,提高交通安全。

5. 人工智能的挑战与机遇
5.1 挑战
人工智能发展过程中面临的主要挑战包括数据隐私、算法偏见、安全性问题等。数据隐私问题涉及到个人数据的收集和使用,算法偏见问题则涉及到算法的公平性和透明度,安全性问题则涉及到人工智能系统的可靠性和稳定性。
5.2 机遇
尽管面临挑战,人工智能的发展也带来了巨大的机遇。人工智能技术的进步将推动各行各业的创新,提高生产效率,改善生活质量。未来,人工智能有望在更多领域取得突破,为人类社会带来更多的便利和福祉。

6.':
🔍 文档 page_content='未来展望
6.1 技术突破
未来,人工智能技术有望在以下几个方面取得突破:一是算法的优化和创新,提高模型的效率和准确性;二是计算能力的提升,支持更复杂的模型和更大规模的数据处理;三是跨学科研究的深入,推动人工智能与其他领域的融合。
6.2 应用拓展
随着技术的进步,人工智能的应用领域将进一步拓展。未来,人工智能有望在更多领域发挥重要作用,如环境保护、能源管理、智能制造等。人工智能将成为推动社会进步的重要力量。

7. 结论
人工智能作为一门快速发展的科学,正在改变着人类社会的运行方式。通过不断的技术创新和应用拓展,人工智能将为人类社会带来更多的便利和福祉。然而,人工智能的发展也面临着诸多挑战,需要社会各界共同努力,推动人工智能的健康发展。':

关于参数的说明:

  1. breakpoint_threshold_type (断点阈值类型)
  • 作用:定义文本语义边界的检测算法,决定何时分割文本块。
  • 可选值及原理:
类型 原理说明 适用场景
percentile 计算相邻句子嵌入向量的余弦距离,取距离分 布的第N百分位值作为阈值,高于此值则分割 常规文本(如文 章、报告)
standard_deviation 均值 + N倍标准差为阈值,识别语义突变 点 语义变化剧烈的 文档(如技术手 册)
interquartile 四分位距(IQR) 定义异常值边界,超过则 分割 长文档(如书 籍)
gradient 基于嵌入向量变化的梯度检测分割点(需自定 义实现) 实验性需求
  1. breakpoint_threshold_amount (断点阈值量)
  • 作用:控制分割的粒度敏感度,值越小分割越细(块越多),值越大分割越粗(块越少)。 取值范围与示例:
    • percentile 模式:0.0~100.0,用户代码设 65.0 表示仅当余弦距离 > 所有距离中最低的 65.0%值时分割 。默认值是:95.0,兼顾语义完整性与检索效率。值过小(比如0.1),会产 生大量小文本块,过度分割可能导致上下文断裂。
    • standard_deviation 模式:浮点数(如 1.5 表示均值+1.5倍标准差)。
    • interquartile 模式:倍数(如1.5 是IQR标准值)。

其它拆分器

类型1:HTMLHeaderTextSplitter:Split by HTML header

HTMLHeaderTextSplitter是一种专门用于处理HTML文档的文本分割方法,它根据HTML的标题标签(如<h1>,<h2>等) 将文档划分为逻辑分块,同时保留标题的层级结构信息。
举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 1.导入相关依赖 
from langchain.text_splitter import HTMLHeaderTextSplitter

# 2.定义HTML文件
html_string = """

次处是html文件内容

"""
# 4.用于指定要根据哪些HTML标签来分割文本
headers_to_split_on = [
( "h1" , "标题1" ),
( "h2" , "标题2" ),
( "h3" , "标题3" ),
]

# 5.定义HTMLHeaderTextSplitter分割器
html_splitter = HTMLHeaderTextSplitter(headers_to_split_on=headers_to_split_on)

# 6.分割器分割
html_header_splits = html_splitter.split_text(html_string)
html_header_splits

说明:

  • 标题下文本内容所属标题的层级信息保存在元数据中。
  • 每个分块会自动继承父级标题的上下文,避免信息割裂。

类型2:CodeTextSplitter:Split code

CodeTextSplitter是一个 专为代码文件设计的文本分割器(Text Splitter),支持代码的语言包括[‘cpp’, ‘go’, ‘java’, ‘js’, ‘php’, ‘proto’, ‘python’, ‘rst’, ‘ruby’, ‘rust’, ‘scala’, ‘swift’, ‘markdown’, ‘latex’, ‘html’, ‘sol’]。它能够根据编程语言的语法结构(如函数、类、代码块等)智能地拆分代码,保持代码逻辑的完 整性。

与递归文本分割器(如RecursiveCharacterTextSplitter)不同,CodeTextSplitter 针对代码的特性进 行了优化, 避免在函数或类的中间截断

举例1:支持的语言

1
pip install langchain-text-splitters
1
2
3
4
5
6
7
8
from langchain.text_splitter '
import Language

# 支持分割语言类型
# Full list of supported languages '

langs = [e.value for e in Language]
print (langs)
1
[cpp, go, java, kotlin, js, ts, php, proto, python, rst, ruby, rust, scala, swift, markdown, latex, html, sol, csharp, cobol, c, lua, perl, haskell, elixir, powershell]

举例2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 # 1.导入相关依赖  
from langchain.text_splitter import (
Language,
RecursiveCharacterTextSplitter,
)
from pprint import pprint
# 2.定义要分割的python代码片段
PYTHON_CODE = """
def hello_world():
print("Hello, World!")

def hello_world1():
print("Hello, World1!") """ # 3.定义递归字符切分器
python_splitter = RecursiveCharacterTextSplitter.from_language(
language=Language.PYTHON,
chunk_size=50,
chunk_overlap=0
)
# 4.文档切分
python_docs = python_splitter.create_documents(texts=[PYTHON_CODE])
pprint(python_docs)

结果:

1
2
[Document(metadata={}, page_content='def hello_world():\n   print("Hello, World!")'),
Document(metadata={}, page_content='def hello_world1():\n print("Hello, World1!")')]

类型3:MarkdownTextSplitter:md数据类型

因为Markdown格式有特定的语法,一般整体内容由 h1、h2、h3 等多级标题组织,所以 MarkdownHeaderTextSplitter的切分策略就是根据 标题来分割文本内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from langchain.text_splitter import MarkdownTextSplitter  
markdown_text = """
# 一级标题\n
这是一级标题下的内容\n\n
## 二级标题\n- 二级下列表项1\n- 二级下列表项2\n
""" # 关键步骤:直接修改实例属性
splitter = MarkdownTextSplitter(chunk_size=30, chunk_overlap=0)
splitter._is_separator_regex = True # 强制将分隔符视为正则表达式
# print(len(docs))
# 执行分割
docs = splitter.create_documents(texts = [markdown_text])
for i, doc in enumerate(docs):
print(f"\n🔍 分块 {i + 1}:")
print(doc.page_content)

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13

🔍 分块 1:
# 一级标题

这是一级标题下的内容

🔍 分块 2:
## 二级标题
- 二级下列表项1

🔍 分块 3:
- 二级下列表项2

文档嵌入模型 Text Embedding Models

嵌入模型概述

Text Embedding Models:文档嵌入模型,提供将文本编码为向量的能力,即文档向量化文档写入用户 查询匹配前都会先执行文档嵌入编码,即向量化。
500

LangChain中针对向量化模型的封装提供了两种接口,一种针对文档的向量化(embed_documents),一 种针对句子的向量化embed_query。

句子的向量化(embed_query)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from langchain_openai import OpenAIEmbeddings  
import os
import dotenv

dotenv.load_dotenv()

os.environ['OPENAI_API_KEY'] = os.getenv("SILICONFLOW_API_KEY")
os.environ['OPENAI_BASE_URL'] = os.getenv("BASE_URL")

embedding_model = OpenAIEmbeddings(
# model="text-embedding-ada-002"
model = "Qwen/Qwen3-Embedding-0.6B"
)

text = "Nice to meet you!"

embed_query = embedding_model.embed_query(text = text,)

print(len(embed_query)) # 1536 --> 3072

print(embed_query[:10])

结果:

1
2
1024
[-0.014332670718431473, -0.02295164205133915, -0.01733478531241417, -0.006827387027442455, 0.04202958941459656, -0.028665341436862946, -0.023823224008083344, -0.031376928091049194, -0.09374341368675232, 0.08986972272396088]

文档的向量化(embed_documents)

文档的向量化,接收的参数是字符串数组。

举例1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from langchain_openai import OpenAIEmbeddings  
import numpy as np
import pandas as pd
import os
import dotenv

dotenv.load_dotenv()

os.environ['OPENAI_API_KEY'] = os.getenv("SILICONFLOW_API_KEY")
os.environ['OPENAI_BASE_URL'] = os.getenv("BASE_URL")

# 初始化嵌入模型
embeddings_model = OpenAIEmbeddings(model="Qwen/Qwen3-Embedding-0.6B")

# 待嵌入的文本列表
texts = [
"Hi there!",
"Oh, hello!",
"What's your name?",
"My friends call me World",
"Hello World!"
]

# 生成嵌入向量
embeddings = embeddings_model.embed_documents(texts)


for i in range(len(texts)):
print(f"{texts[i]}:{embeddings[i][:3]}",end="\n\n")

结果:

1
2
3
4
5
6
7
8
9
Hi there!:[-0.005018789321184158, -0.02362913265824318, -0.015080382116138935]

Oh, hello!:[-0.016794802621006966, 0.0015314100310206413, -0.013306026346981525]

What's your name?:[-0.0057384539395570755, -0.011427008546888828, -0.018862050026655197]

My friends call me World:[0.01912776567041874, 0.0315045565366745, -0.013761605136096478]

Hello World!:[0.0033330952282994986, 0.017623262479901314, -0.011646676808595657]

举例2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from dotenv import load_dotenv  
from langchain_community.document_loaders import CSVLoader
from langchain_openai import OpenAIEmbeddings


embeddings_model = OpenAIEmbeddings(
model="Qwen/Qwen3-Embedding-0.6B",
)

# 情况1:
loader = CSVLoader("./asset/load/03-load.csv", encoding="utf-8")
docs = loader.load_and_split()

#print(len(docs))

# 存放的是每一个chrunk的embedding。
embeded_docs = embeddings_model.embed_documents([doc.page_content for doc in docs])
print(len(embeded_docs))
# 表示的是每一个chrunk的embedding的维度
print(len(embeded_docs[0]))
print(embeded_docs[0][:10])

结果:

1
2
3
4
1024
[0.020796308293938637, -0.06914985924959183, -0.016174905002117157, -0.02148095890879631, 0.004043726250529289, 0.018742350861430168, 0.04381773620843887, 0.07394243031740189, -0.06641125679016113, 0.03834051638841629]

向量存储(Vector Stores)

理解向量存储

将文本向量化之后,下一步就是进行向量的存储。这部分包含两块:

  • 向量的存储:将非结构化数据向量化后,完成存储
  • 向量的查询:查询时,嵌入非结构化查询并检索与嵌入查询“最相似”的嵌入向量。即具有相似性 检索能力

500

常用的向量数据库

LangChain提供了超过 50种 不同向量存储(Vector Stores)的集成,从开源的 本地向量存储到 云托管的私有向量存储,允许你选择最适合需求的向量存储。
LangChain支持的向量存储参考 VectorStore 接口和实现。
500

典型的介绍如下:

向量数据库 描述
Chroma 开源、免费的嵌入式数据库
FAISS Meta出品,开源、免费,Facebook AI相似性搜索服务。(Facebook AI Similarity Search,Facebook AI 相似性搜索库) /fæs/
Milvus 用于存储、索引和管理由深度神经网络和其他ML模型产生的大量嵌入向量的数据库
Pinecone 具有广泛功能的向量数据库
Redis 基于Redis的检索器

代码实现

使用向量数据库组件时需要同时传入包含文本块的Document类对象以及文本向量化组件,向量数据库组 件会自动完成将文本向量化的工作,并写入数据库中。

数据的存储

举例1:从TXT文档中加载数据,向量化后存储到Chroma数据库

安装模块:

1
2
pip install chromadb 
pip install langchain-chroma
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from langchain_chroma import Chroma  
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import CharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
import os
import dotenv

dotenv.load_dotenv()

os.environ['OPENAI_API_KEY'] = os.getenv("SILICONFLOW_API_KEY")
os.environ['OPENAI_BASE_URL'] = os.getenv("BASE_URL")

# 步骤1:创建一个TextLoader的实例,并将指定的文档加载
loader = TextLoader(
file_path="./asset/load/09-ai1.txt",
encoding="utf-8",
)

docs = loader.load()


# 步骤2:创建文本拆分器,并拆分文档
text_splitter = CharacterTextSplitter(
chunk_size=1000,
chunk_overlap=100,
)

splitter_docs = text_splitter.split_documents(docs)

# print(len(splitter_docs))

# 步骤3:创建嵌入模型
embedding_model = OpenAIEmbeddings(model="Qwen/Qwen3-Embedding-0.6B")

# 步骤4:将文档及嵌入模型传入到Chroma相关的结构中,进行数据的存储
db = Chroma.from_documents(
documents=splitter_docs,
embedding=embedding_model,
)

思考:此时数据存储在哪里呢?
注意:Chroma主要有两种存储模式:内存模式持久化模式。当使用persist_directory参数时,数据 会保存到指定目录;如果没有指定,则默认使用内存存储。

1
2
3
4
5
db1 = Chroma.from_documents(  
documents=splitter_docs,
embedding=embedding_model,
persist_directory="./asset/chroma-1",
)

2、需要明确,在向量数据库中,不仅存储了数据(或文档)的向量,而且还存储了数据(或文档)本身。


演示一下:检索的需求

1
2
3
4
5
query = "人工智能的核心技术有哪些呢?"  

docs = db.similarity_search(query)

print(docs[0].page_content)

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
5. 人工智能的挑战与机遇
5.1 挑战
人工智能发展过程中面临的主要挑战包括数据隐私、算法偏见、安全性问题等。数据隐私问题涉及到个人数据的收集和使用,算法偏见问题则涉及到算法的公平性和透明度,安全性问题则涉及到人工智能系统的可靠性和稳定性。
5.2 机遇
尽管面临挑战,人工智能的发展也带来了巨大的机遇。人工智能技术的进步将推动各行各业的创新,提高生产效率,改善生活质量。未来,人工智能有望在更多领域取得突破,为人类社会带来更多的便利和福祉。

6. 未来展望
6.1 技术突破
未来,人工智能技术有望在以下几个方面取得突破:一是算法的优化和创新,提高模型的效率和准确性;二是计算能力的提升,支持更复杂的模型和更大规模的数据处理;三是跨学科研究的深入,推动人工智能与其他领域的融合。
6.2 应用拓展
随着技术的进步,人工智能的应用领域将进一步拓展。未来,人工智能有望在更多领域发挥重要作用,如环境保护、能源管理、智能制造等。人工智能将成为推动社会进步的重要力量。

7. 结论
人工智能作为一门快速发展的科学,正在改变着人类社会的运行方式。通过不断的技术创新和应用拓展,人工智能将为人类社会带来更多的便利和福祉。然而,人工智能的发展也面临着诸多挑战,需要社会各界共同努力,推动人工智能的健康发展。

举例2:操作csv文档,并向量化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from langchain.text_splitter import CharacterTextSplitter  
from langchain_community.document_loaders import CSVLoader
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma

import os
import dotenv

dotenv.load_dotenv()
os.environ['OPENAI_API_KEY'] = os.getenv("SILICONFLOW_API_KEY")
os.environ['OPENAI_BASE_URL'] = os.getenv("BASE_URL")

# 获取嵌入模型
embeddings = OpenAIEmbeddings(model="Qwen/Qwen3-Embedding-0.6B")

# 加载文档并拆分(第1次拆分)
loader = CSVLoader("./asset/load/03-load.csv", encoding='utf-8')
pages = loader.load_and_split()
#print(len(pages)) # 4

# 文本拆分(第2次拆分)
text_spliter = CharacterTextSplitter.from_tiktoken_encoder(chunk_size=500)
docs = text_spliter.split_documents(pages)

# 向量存储
db_path = './asset/chroma-2'
db = Chroma.from_documents(docs, embeddings, persist_directory=db_path)

数据的检索

举例:一个包含构建Chroma向量数据库以及向量检索的代码
前置代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# 1.导入相关依赖  
from langchain_chroma import Chroma
from langchain_core.documents import Document
from langchain_openai import OpenAIEmbeddings

# 2.定义文档
raw_documents = [
Document(
page_content="葡萄是一种常见的水果,属于葡萄科葡萄属植物。它的果实呈圆形或椭圆形,颜色有绿色、紫色、红色等多种。葡萄富含维生素C和抗氧化物质,可以直接食用或酿造成葡萄酒。",
metadata={"source": "水果", "type": "植物"}
),
Document(
page_content="白菜是十字花科蔬菜,原产于中国北方。它的叶片层层包裹形成紧密的球状,口感清脆微甜。白菜富含膳食纤维和维生素K,常用于制作泡菜、炒菜或煮汤。",
metadata={"source": "蔬菜", "type": "植物"}
),
Document(
page_content="狗是人类最早驯化的动物之一,属于犬科。它们具有高度社会性,能理解人类情绪,常被用作宠物、导盲犬或警犬。不同品种的狗在体型、毛色和性格上有很大差异。",
metadata={"source": "动物", "type": "哺乳动物"}
),
Document(
page_content="猫是小型肉食性哺乳动物,性格独立但也能与人类建立亲密关系。它们夜视能力极强,擅长捕猎老鼠。家猫的品种包括波斯猫、暹罗猫等,毛色和花纹多样。",
metadata={"source": "动物", "type": "哺乳动物"}
),
Document(
page_content="人类是地球上最具智慧的生物,属于灵长目人科。现代人类(智人)拥有高度发达的大脑,创造了语言、工具和文明。人类的平均寿命约70-80年,分布在全球各地。",
metadata={"source": "生物", "type": "灵长类"}
),
Document(
page_content="太阳是太阳系的中心恒星,直径约139万公里,主要由氢和氦组成。它通过核聚变反应产生能量,为地球提供光和热。太阳活动周期约为11年,会影响地球气候。",
metadata={"source": "天文", "type": "恒星"}
),
Document(
page_content="长城是中国古代的军事防御工程,总长度超过2万公里。它始建于春秋战国时期,秦朝连接各段,明朝大规模重修。长城是世界文化遗产和人类建筑奇迹。",
metadata={"source": "历史", "type": "建筑"}
),
Document(
page_content="量子力学是研究微观粒子运动规律的物理学分支。它提出了波粒二象性、测不准原理等概念,彻底改变了人类对物质世界的认知。量子计算机正是基于这一理论发展而来。",
metadata={"source": "物理", "type": "科学"}
),
Document(
page_content="《红楼梦》是中国古典文学四大名著之一,作者曹雪芹。小说以贾、史、王、薛四大家族的兴衰为背景,描绘了贾宝玉与林黛玉的爱情悲剧,反映了封建社会的种种矛盾。",
metadata={"source": "文学", "type": "小说"}
),
Document(
page_content="新冠病毒(SARS-CoV-2)是一种可引起呼吸道疾病的冠状病毒。它通过飞沫传播,主要症状包括发热、咳嗽、乏力。疫苗和戴口罩是有效的预防措施。",
metadata={"source": "医学", "type": "病毒"}
)
]
# 3. 创建嵌入模型
embedding = OpenAIEmbeddings(model="Qwen/Qwen3-Embedding-0.6B")

# 4.创建向量数据库
db = Chroma.from_documents(
documents=raw_documents,
embedding=embedding,
persist_directory="./asset/chroma-3",
)

① 相似性检索(similarity_search)

1
2
3
4
5
6
7
8
# 5. 检索示例(返回前3个最相关结果)  
query = "哺乳动物"
docs = db.similarity_search(query, k=3) # k=3表示返回3个最相关文档
print(f"查询: '{query}' 的结果:")
for i, doc in enumerate(docs, 1):
print(f"\n结果 {i}:")
print(f"内容: {doc.page_content}")
print(f"元数据: {doc.metadata}")

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
查询: '哺乳动物' 的结果:

结果 1:
内容: 狗是人类最早驯化的动物之一,属于犬科。它们具有高度社会性,能理解人类情绪,常被用作宠物、导盲犬或警犬。不同品种的狗在体型、毛色和性格上有很大差异。
元数据: {'type': '哺乳动物', 'source': '动物'}

结果 2:
内容: 狗是人类最早驯化的动物之一,属于犬科。它们具有高度社会性,能理解人类情绪,常被用作宠物、导盲犬或警犬。不同品种的狗在体型、毛色和性格上有很大差异。
元数据: {'source': '动物', 'type': '哺乳动物'}

结果 3:
内容: 狗是人类最早驯化的动物之一,属于犬科。它们具有高度社会性,能理解人类情绪,常被用作宠物、导盲犬或警犬。不同品种的狗在体型、毛色和性格上有很大差异。
元数据: {'type': '哺乳动物', 'source': '动物'}

② 支持直接对问题向量查询(similarity_search_by_vector)

1
2
3
4
5
6
7
8
9
10
query = "哺乳动物"  
embedding_vector = embedding.embed_query(query)

docs = db.similarity_search_by_vector(embedding_vector, k=3)

print(f"查询: '{query}' 的结果:")
for i, doc in enumerate(docs, 1):
print(f"\n结果 {i}:")
print(f"内容: {doc.page_content}")
print(f"元数据: {doc.metadata}")

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
查询: '哺乳动物' 的结果:

结果 1:
内容: 狗是人类最早驯化的动物之一,属于犬科。它们具有高度社会性,能理解人类情绪,常被用作宠物、导盲犬或警犬。不同品种的狗在体型、毛色和性格上有很大差异。
元数据: {'source': '动物', 'type': '哺乳动物'}

结果 2:
内容: 狗是人类最早驯化的动物之一,属于犬科。它们具有高度社会性,能理解人类情绪,常被用作宠物、导盲犬或警犬。不同品种的狗在体型、毛色和性格上有很大差异。
元数据: {'type': '哺乳动物', 'source': '动物'}

结果 3:
内容: 狗是人类最早驯化的动物之一,属于犬科。它们具有高度社会性,能理解人类情绪,常被用作宠物、导盲犬或警犬。不同品种的狗在体型、毛色和性格上有很大差异。
元数据: {'source': '动物', 'type': '哺乳动物'}

③ 相似性检索,支持过滤元数据(filter)

1
2
3
4
5
6
7
8
9
10
11
query = "哺乳动物"  

docs = db.similarity_search(
query=query,
k=3,
filter={"source": "动物"})

for i, doc in enumerate(docs, 1):
print(f"\n结果 {i}:")
print(f"内容: {doc.page_content}")
print(f"元数据: {doc.metadata}")

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13

结果 1:
内容: 狗是人类最早驯化的动物之一,属于犬科。它们具有高度社会性,能理解人类情绪,常被用作宠物、导盲犬或警犬。不同品种的狗在体型、毛色和性格上有很大差异。
元数据: {'source': '动物', 'type': '哺乳动物'}

结果 2:
内容: 狗是人类最早驯化的动物之一,属于犬科。它们具有高度社会性,能理解人类情绪,常被用作宠物、导盲犬或警犬。不同品种的狗在体型、毛色和性格上有很大差异。
元数据: {'type': '哺乳动物', 'source': '动物'}

结果 3:
内容: 狗是人类最早驯化的动物之一,属于犬科。它们具有高度社会性,能理解人类情绪,常被用作宠物、导盲犬或警犬。不同品种的狗在体型、毛色和性格上有很大差异。
元数据: {'type': '哺乳动物', 'source': '动物'}

④ 通过L2距离分数进行搜索(similarity_search_with_score)

说明:分数值越小,检索到的文档越和问题相似。分值取值范围:[0,正无穷]

1
2
3
4
5
docs = db.similarity_search_with_score(  
"量子力学是什么?"
)
for doc, score in docs:
print(f" [L2距离得分={score:.3f}] {doc.page_content} [{doc.metadata}]")

结果:

1
2
3
4
[L2距离得分=1.055] 狗是人类最早驯化的动物之一,属于犬科。它们具有高度社会性,能理解人类情绪,常被用作宠物、导盲犬或警犬。不同品种的狗在体型、毛色和性格上有很大差异。 [{'source': '动物', 'type': '哺乳动物'}]
[L2距离得分=1.057] 狗是人类最早驯化的动物之一,属于犬科。它们具有高度社会性,能理解人类情绪,常被用作宠物、导盲犬或警犬。不同品种的狗在体型、毛色和性格上有很大差异。 [{'source': '动物', 'type': '哺乳动物'}]
[L2距离得分=1.058] 狗是人类最早驯化的动物之一,属于犬科。它们具有高度社会性,能理解人类情绪,常被用作宠物、导盲犬或警犬。不同品种的狗在体型、毛色和性格上有很大差异。 [{'source': '动物', 'type': '哺乳动物'}]
[L2距离得分=1.071] 人类是地球上最具智慧的生物,属于灵长目人科。现代人类(智人)拥有高度发达的大脑,创造了语言、工具和文明。人类的平均寿命约70-80年,分布在全球各地。 [{'source': '生物', 'type': '灵长类'}]

⑤ 通过余弦相似度分数进行搜索(_similarity_search_with_relevance_scores)

说明:分数值越接近1(上限),检索到的文档越和问题相似。

1
2
3
4
5
docs = db._similarity_search_with_relevance_scores(  
"量子力学是什么?"
)
for doc, score in docs:
print(f"* [余弦相似度得分={score:.3f}] {doc.page_content} [{doc.metadata}]")

结果:

1
2
3
4
5
* [余弦相似度得分=0.254] 狗是人类最早驯化的动物之一,属于犬科。它们具有高度社会性,能理解人类情绪,常被用作宠物、导盲犬或警犬。不同品种的狗在体型、毛色和性格上有很大差异。 [{'type': '哺乳动物', 'source': '动物'}]
* [余弦相似度得分=0.253] 狗是人类最早驯化的动物之一,属于犬科。它们具有高度社会性,能理解人类情绪,常被用作宠物、导盲犬或警犬。不同品种的狗在体型、毛色和性格上有很大差异。 [{'type': '哺乳动物', 'source': '动物'}]
* [余弦相似度得分=0.252] 狗是人类最早驯化的动物之一,属于犬科。它们具有高度社会性,能理解人类情绪,常被用作宠物、导盲犬或警犬。不同品种的狗在体型、毛色和性格上有很大差异。 [{'source': '动物', 'type': '哺乳动物'}]
* [余弦相似度得分=0.243] 人类是地球上最具智慧的生物,属于灵长目人科。现代人类(智人)拥有高度发达的大脑,创造了语言、工具和文明。人类的平均寿命约70-80年,分布在全球各地。 [{'type': '灵长类', 'source': '生物'}]

⑥ MMR(最大边际相关性,max_marginal_relevance_search)

MMR 是一种平衡相关性多样性的检索策略,避免返回高度相似的冗余结果。

参数说明: lambda_mult 参数值介于 0 到 1 之间,用于确定结果之间的多样性程度,其中 0 对应最大 多样性,1 对应最小多样性。默认值为 0.5。

1
2
3
4
5
6
7
8
9
10
11
docs = db.max_marginal_relevance_search(  
query="量子力学是什么",
lambda_mult=0.8, # 侧重相似性
)

print("🔍 关于【量子力学是什么】的搜索结果:")
print("=" * 50)
for i, doc in enumerate(docs):
print(f"\n📖 结果 {i+1}:")
print(f"📌 内容: {doc.page_content}")
print(f"🏷️ 标签: {', '.join(f'{k}={v}' for k, v in doc.metadata.items())}")

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
🔍 关于【量子力学是什么】的搜索结果:
==================================================

📖 结果 1:
📌 内容: 狗是人类最早驯化的动物之一,属于犬科。它们具有高度社会性,能理解人类情绪,常被用作宠物、导盲犬或警犬。不同品种的狗在体型、毛色和性格上有很大差异。
🏷️ 标签: type=哺乳动物, source=动物

📖 结果 2:
📌 内容: 狗是人类最早驯化的动物之一,属于犬科。它们具有高度社会性,能理解人类情绪,常被用作宠物、导盲犬或警犬。不同品种的狗在体型、毛色和性格上有很大差异。
🏷️ 标签: source=动物, type=哺乳动物

📖 结果 3:
📌 内容: 狗是人类最早驯化的动物之一,属于犬科。它们具有高度社会性,能理解人类情绪,常被用作宠物、导盲犬或警犬。不同品种的狗在体型、毛色和性格上有很大差异。
🏷️ 标签: type=哺乳动物, source=动物

📖 结果 4:
📌 内容: 人类是地球上最具智慧的生物,属于灵长目人科。现代人类(智人)拥有高度发达的大脑,创造了语言、工具和文明。人类的平均寿命约70-80年,分布在全球各地。
🏷️ 标签: type=灵长类, source=生物

检索器(召回器) Retrievers

从“向量存储组件”的代码实现5.4.2中可以看到,向量数据库本身已经包含了实现召回功能的函数方法 (similarity_search )。该函数通过计算原始查询向量与数据库中存储向量之间的相似度来实现召回。

LangChain还提供了 更加复杂的召回策略,这些策略被集成在Retrievers(检索器或召回器)组件中。

Retrievers(检索器)是一种用于从大量文档中检索与给定查询相关的文档或信息片段的工具。检索器 不需要存储文档,只需要 返回(或检索)文档即可。

Retrievers 的执行步骤:

  • 步骤1:将输入查询转换为向量表示。
  • 步骤2:在向量存储中搜索与查询向量最相似的文档向量(通常使用余弦相似度或欧几里得距离等度量方 法)。
  • 步骤3:返回与查询最相关的文档或文本片段,以及它们的相似度得分。

代码实现

Retriever 一般和 VectorStore 配套实现,通过as_retriever() 方法获取。
举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 1.导入相关依赖  
from langchain_community.document_loaders import TextLoader
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import CharacterTextSplitter
import os
import dotenv
dotenv.load_dotenv()


# 2.定义文档加载器
loader = TextLoader(file_path='./asset/load/09-ai1.txt',encoding="utf-8")

# 3.加载文档
documents = loader.load()

# 4.定义文本切割器
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=100)

# 5.切割文档
docs = text_splitter.split_documents(documents)

# 6.定义嵌入模型
os.environ['OPENAI_API_KEY'] = os.getenv("SILICONFLOW_API_KEY")
os.environ['OPENAI_BASE_URL'] = os.getenv("BASE_URL")
embeddings = OpenAIEmbeddings(
model="Qwen/Qwen3-Embedding-0.6B"
)

# 获取向量数据库
db = FAISS.from_documents(documents=docs,embedding=embeddings)

# 基于向量数据库获取检索器
retriever = db.as_retriever()

# 进行数据的检索
docs = retriever.invoke(input = "深度学习是什么?")

print(len(docs))

for doc in docs:
print(f"------{doc}")

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
3
------page_content='5. 人工智能的挑战与机遇
5.1 挑战
人工智能发展过程中面临的主要挑战包括数据隐私、算法偏见、安全性问题等。数据隐私问题涉及到个人数据的收集和使用,算法偏见问题则涉及到算法的公平性和透明度,安全性问题则涉及到人工智能系统的可靠性和稳定性。
5.2 机遇
尽管面临挑战,人工智能的发展也带来了巨大的机遇。人工智能技术的进步将推动各行各业的创新,提高生产效率,改善生活质量。未来,人工智能有望在更多领域取得突破,为人类社会带来更多的便利和福祉。

6. 未来展望
6.1 技术突破
未来,人工智能技术有望在以下几个方面取得突破:一是算法的优化和创新,提高模型的效率和准确性;二是计算能力的提升,支持更复杂的模型和更大规模的数据处理;三是跨学科研究的深入,推动人工智能与其他领域的融合。
6.2 应用拓展
随着技术的进步,人工智能的应用领域将进一步拓展。未来,人工智能有望在更多领域发挥重要作用,如环境保护、能源管理、智能制造等。人工智能将成为推动社会进步的重要力量。

7. 结论
人工智能作为一门快速发展的科学,正在改变着人类社会的运行方式。通过不断的技术创新和应用拓展,人工智能将为人类社会带来更多的便利和福祉。然而,人工智能的发展也面临着诸多挑战,需要社会各界共同努力,推动人工智能的健康发展。' metadata={'source': './asset/load/09-ai1.txt'}
------page_content='3. 人工智能的核心技术
3.1 机器学习
机器学习是人工智能的核心技术之一,通过算法使计算机从数据中学习并做出决策。常见的机器学习算法包括监督学习、无监督学习和强化学习。监督学习通过标记数据进行训练,无监督学习则从未标记数据中寻找模式,强化学习则通过与环境交互来优化决策。
3.2 深度学习
深度学习是机器学习的一个子领域,通过多层神经网络进行特征提取和模式识别。深度学习在图像识别、自然语言处理、语音识别等领域取得了显著成果。常见的深度学习模型包括卷积神经网络(CNN)、循环神经网络(RNN)和长短期记忆网络(LSTM)。
3.3 自然语言处理
自然语言处理(NLP)是人工智能的一个重要分支,致力于使计算机能够理解和生成人类语言。NLP技术广泛应用于机器翻译、情感分析、文本分类等领域。近年来,基于深度学习的NLP模型(如BERT、GPT)在语言理解任务中取得了突破性进展。
3.4 计算机视觉
计算机视觉是人工智能的另一个重要分支,致力于使计算机能够理解和处理图像和视频。计算机视觉技术广泛应用于图像识别、目标检测、人脸识别等领域。深度学习模型(如CNN)在计算机视觉任务中取得了显著成果。

8. 人工智能的应用领域
4.1 医疗健康
人工智能在医疗健康领域的应用包括疾病诊断、药物研发、个性化医疗等。通过分析医学影像和患者数据,人工智能可以帮助医生更准确地诊断疾病,提高治疗效果。
4.2 金融
人工智能在金融领域的应用包括风险评估、欺诈检测、算法交易等。通过分析市场数据和交易记录,人工智能可以帮助金融机构做出更明智的决策,提高运营效率。
4.3 教育
人工智能在教育领域的应用包括个性化学习、智能辅导、自动评分等。通过分析学生的学习数据,人工智能可以为学生提供个性化的学习建议,提高学习效果。
4.4 交通
人工智能在交通领域的应用包括自动驾驶、交通管理、智能导航等。通过分析交通数据和路况信息,人工智能可以帮助优化交通流量,提高交通安全。' metadata={'source': './asset/load/09-ai1.txt'}
------page_content='人工智能综述:发展、应用与未来展望

摘要
人工智能(Artificial Intelligence,AI)作为计算机科学的一个重要分支,近年来取得了突飞猛进的发展。本文综述了人工智能的发展历程、核心技术、应用领域以及未来发展趋势。通过对人工智能的定义、历史背景、主要技术(如机器学习、深度学习、自然语言处理等)的详细介绍,探讨了人工智能在医疗、金融、教育、交通等领域的应用,并分析了人工智能发展过程中面临的挑战与机遇。最后,本文对人工智能的未来发展进行了展望,提出了可能的突破方向。

1. 引言
人工智能是指通过计算机程序模拟人类智能的一门科学。自20世纪50年代诞生以来,人工智能经历了多次起伏,近年来随着计算能力的提升和大数据的普及,人工智能技术取得了显著的进展。人工智能的应用已经渗透到日常生活的方方面面,从智能手机的语音助手到自动驾驶汽车,从医疗诊断到金融分析,人工智能正在改变着人类社会的运行方式。

2. 人工智能的发展历程
2.1 早期发展
人工智能的概念最早可以追溯到20世纪50年代。1956年,达特茅斯会议(Dartmouth Conference)被认为是人工智能研究的正式开端。在随后的几十年里,人工智能研究经历了多次高潮与低谷。早期的研究主要集中在符号逻辑和专家系统上,但由于计算能力的限制和算法的不足,进展缓慢。
2.2 机器学习的兴起
20世纪90年代,随着统计学习方法的引入,机器学习逐渐成为人工智能研究的主流。支持向量机(SVM)、决策树、随机森林等算法在分类和回归任务中取得了良好的效果。这一时期,机器学习开始应用于数据挖掘、模式识别等领域。
2.3 深度学习的突破
2012年,深度学习在图像识别领域取得了突破性进展,标志着人工智能进入了一个新的阶段。深度学习通过多层神经网络模拟人脑的工作方式,能够自动提取特征并进行复杂的模式识别。卷积神经网络(CNN)、循环神经网络(RNN)和长短期记忆网络(LSTM)等深度学习模型在图像处理、自然语言处理、语音识别等领域取得了显著成果。' metadata={'source': './asset/load/09-ai1.txt'}

使用相关检索策

1
pip install faiss-cpu

前置代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# 1.导入相关依赖  
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
from langchain_core.documents import Document

# 2.定义文档
document_1 = Document(
page_content="经济复苏:美国经济正在从疫情中强劲复苏,失业率降至历史低点。!",
)
document_2 = Document(
page_content="基础设施:政府将投资1万亿美元用于修复道路、桥梁和宽带网络。",
)
document_3 = Document(
page_content="气候变化:承诺到2030年将温室气体排放量减少50%。",
)
document_4 = Document(
page_content=" 医疗保健:降低处方药价格,扩大医疗保险覆盖范围。",
)
document_5 = Document(
page_content="教育:提供免费的社区大学教育。。",
)
document_6 = Document(
page_content="科技:增加对半导体产业的投资以减少对外国供应链的依赖。。",
)
document_7 = Document(
page_content="外交政策:继续支持乌克兰对抗俄罗斯的侵略。",
)
document_8 = Document(
page_content="枪支管制:呼吁国会通过更严格的枪支管制法律。",
)
document_9 = Document(
page_content="移民改革:提出全面的移民改革方案。",
)
document_10 = Document(
page_content="社会正义:承诺解决系统性种族歧视问题。",
)
documents = [
document_1,
document_2,
document_3,
document_4,
document_5,
document_6,
document_7,
document_8,
document_9,
document_10,
]

# 3.创建向量存储
embeddings = OpenAIEmbeddings(
model="Qwen/Qwen3-Embedding-0.6B"
)

# 4.将文档向量化,添加到向量数据库索引中,得到向量数据库对象
db = FAISS.from_documents(documents, embeddings)

① 默认检索器使用相似性搜索

1
2
3
4
5
6
7
# 获取检索器  
retriever = db.as_retriever(search_kwargs={"k": 3}) #这里设置返回的文档数

docs = retriever.invoke("经济政策")

for i, doc in enumerate(docs):
print(f"\n结果 {i+1}:\n{doc.page_content}\n")

结果:

1
2
3
4
5
6
7
8
9
结果 1:
社会正义:承诺解决系统性种族歧视问题。

结果 2:
外交政策:继续支持乌克兰对抗俄罗斯的侵略。

结果 3:
教育:提供免费的社区大学教育。。

② 分数阈值查询

只有相似度超过这个值才会召回

1
2
3
4
5
6
7
8
retriever = db.as_retriever(  
search_type="similarity_score_threshold",
search_kwargs={"score_threshold": 0.1}
)
docs = retriever.invoke("经济政策")

for doc in docs:
print(f"📌 内容: {doc.page_content}")

结果:

1
2
3
4
📌 内容: 社会正义:承诺解决系统性种族歧视问题。
📌 内容: 外交政策:继续支持乌克兰对抗俄罗斯的侵略。
📌 内容: 教育:提供免费的社区大学教育。。
📌 内容: 科技:增加对半导体产业的投资以减少对外国供应链的依赖。。

注意只会返回满足阈值分数的文档,不会获取文档的得分。如果想查询文档的得分是否满足阈值,可以 使用向量数据库的 similarity_search_with_relevance_scores 查看

1
2
3
4
5
docs_with_scores = db.similarity_search_with_relevance_scores("经济政策")  

for doc, score in docs_with_scores:
print(f"\n相似度分数: {score:.4f}")
print(f"📌 内容: {doc.page_content}")

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13

相似度分数: 0.2922
📌 内容: 社会正义:承诺解决系统性种族歧视问题。

相似度分数: 0.2544
📌 内容: 外交政策:继续支持乌克兰对抗俄罗斯的侵略。

相似度分数: 0.2302
📌 内容: 教育:提供免费的社区大学教育。。

相似度分数: 0.2160
📌 内容: 科技:增加对半导体产业的投资以减少对外国供应链的依赖。。

③ MMR搜索

1
2
3
4
5
6
7
8
9
10
11
retriever = db.as_retriever(  
search_type="mmr",
search_kwargs={"fetch_k":2}
)

docs = retriever.invoke("经济政策")

print(len(docs))

for doc in docs:
print(f"📌 内容: {doc.page_content}")

结果:

1
2
3
2
📌 内容: 社会正义:承诺解决系统性种族歧视问题。
📌 内容: 外交政策:继续支持乌克兰对抗俄罗斯的侵略。

结合大模型的使用

举例1:通过FAISS构建一个可搜索的向量索引数据库,并结合RAG技术让LLM去回答问题。

情况1:不用RAG给LLM灌输上下文数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from langchain_openai import ChatOpenAI  
import os
import dotenv
dotenv.load_dotenv()

os.environ['OPENAI_API_KEY'] = os.getenv("SILICONFLOW_API_KEY")
os.environ['OPENAI_BASE_URL'] = os.getenv("BASE_URL")

# 创建大模型实例
llm = ChatOpenAI(model="deepseek-ai/DeepSeek-V3")

# 调用
response = llm.invoke("北京有什么著名的建筑?")
print(response.content)

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
北京作为中国的首都,拥有丰富的历史和现代建筑,以下是一些著名的代表:

### **历史建筑**
1. **故宫(紫禁城)**
- 明清两代的皇家宫殿,世界文化遗产,中国现存最大、最完整的古建筑群。

2. **天坛**
- 明清皇帝祭天的场所,以祈年殿和圜丘坛闻名,世界文化遗产。

3. **颐和园**
- 清代皇家园林,以昆明湖、万寿山和长廊著称,世界文化遗产。

4. **长城(八达岭、慕田峪段)**
- 世界七大奇迹之一,北京周边保存完好的段落包括八达岭和慕田峪。

5. **圆明园**
- 曾为“万园之园”,现存遗址公园,见证近代历史。

6. **雍和宫**
- 北京最大的藏传佛教寺院,曾是雍正皇帝的府邸。

7. **钟鼓楼**
- 古代报时中心,位于中轴线北端,保留元代格局。

### **近现代建筑**
1. **天安门广场及周边**
- **人民大会堂**:国家政治活动中心。
- **国家博物馆**:中国最大的综合性博物馆。
- **人民英雄纪念碑**:纪念近代革命烈士的标志。
- **毛主席纪念堂**:安放毛泽东遗体的纪念建筑。

2. **奥林匹克公园**
- **鸟巢(国家体育场)**:2008年奥运会主体育场,现代钢结构代表作。
- **水立方(国家游泳中心)**:独特的膜结构设计,现为“冰立方”。

3. **中央电视台总部大楼(“大裤衩”)**
- 由荷兰建筑师雷姆·库哈斯设计,后现代地标建筑。

4. **国家大剧院**
- 法国建筑师保罗·安德鲁设计,钛金属穹顶宛如“水上明珠”。

5. **北京大兴国际机场**
- 扎哈·哈迪德设计,全球最大单体航站楼,以“凤凰展翅”为造型。

6. **中信大厦(中国尊)**
- 北京最高建筑(528米),CBD核心区的现代摩天大楼。

### **特色建筑**
- **胡同与四合院**:如南锣鼓巷、什刹海周边,展现老北京民居风貌。
- **北京坊**:前门附近的现代文化街区,融合中西建筑风格。
- **琉璃厂文化街**:传统书画古玩街区,仿古建筑风格。

这些建筑不仅代表了北京的历史底蕴,也展现了其作为国际大都市的现代活力。如果有具体兴趣方向,可以深入推荐!

情况2:使用RAG给LLM灌输上下文数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# 1. 导入所有需要的包  
from langchain.prompts import PromptTemplate
from langchain_openai import ChatOpenAI,OpenAIEmbeddings
from langchain_community.document_loaders import TextLoader
from langchain.text_splitter import CharacterTextSplitter
from langchain_community.vectorstores import FAISS
import os
import dotenv

dotenv.load_dotenv()

# 2. 创建自定义提示词模板
prompt_template = """请使用以下提供的文本内容来回答问题。仅使用提供的文本信息,如果文本中没有相关信息,请回答"抱歉,提供的文本中没有这个信息"。

文本内容:
{context}

问题:{question}

回答:
"
"""

prompt = PromptTemplate.from_template(prompt_template)

# 3. 初始化模型
os.environ['OPENAI_API_KEY'] = os.getenv("SILICONFLOW_API_KEY")
os.environ['OPENAI_BASE_URL'] = os.getenv("BASE_URL")
llm = ChatOpenAI(
model="deepseek-ai/DeepSeek-V3",
temperature=0
)

embedding_model = OpenAIEmbeddings(model="Qwen/Qwen3-Embedding-0.6B")

# 4. 加载文档
loader = TextLoader("./asset/load/10-test_doc.txt", encoding='utf-8')
documents = loader.load()

# 5. 分割文档
text_splitter = CharacterTextSplitter(
chunk_size=1000,
chunk_overlap=100,
)
texts = text_splitter.split_documents(documents)

#print(f"文档个数:{len(texts)}")

# 6. 创建向量存储
vectorstore = FAISS.from_documents(
documents=texts,
embedding=embedding_model
)

# 7.获取检索器
retriever = vectorstore.as_retriever()

docs = retriever.invoke("北京有什么著名的建筑?")

# 8. 创建Runnable链
chain = prompt | llm

# 9. 提问
result = chain.invoke(input={"question":"北京有什么著名的建筑?","context":docs})
print("\n回答:", result.content)

结果:

1
2
3
4
5
6
7
8
9
10
11
12
回答: 根据提供的文本内容,北京有以下著名建筑:

1. 故宫(紫禁城) - 明清皇家宫殿,世界文化遗产
2. 天安门 - 标志性建筑,拥有世界最大城市广场
3. 颐和园 - 清代皇家园林,世界文化遗产
4. 天坛 - 明清祭天场所,世界文化遗产
5. 八达岭长城 - 最著名的长城段落
6. 国家体育场(鸟巢) - 2008奥运主体育场
7. 中央电视台总部大楼("大裤衩"
8. 国家大剧院("巨蛋"
9. 北京大兴国际机场 - "新世界七大奇迹"之一
10. 鼓楼和钟楼 - 古代报时中心

举例2:使用Chroma数据库 (与举例1类似)

阶段1:文档的切分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
## 1. 文档加载  
from langchain_community.document_loaders import UnstructuredMarkdownLoader
from langchain.text_splitter import MarkdownTextSplitter
markdown_path = "asset/load/11-langchain.md"
# 2.定义UnstructuredMarkdownLoader对象
loader = UnstructuredMarkdownLoader(markdown_path)

# 3.加载
data = loader.load()
splitter = MarkdownTextSplitter(chunk_size=1000, chunk_overlap=100)
# 4.执行分割

documents = splitter.split_documents(data)
print(len(documents))

for i, doc in enumerate(documents):
print(f"\n🔍 分块 {i + 1}:")
print(doc.page_content)

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
13

🔍 分块 1:
How-to guides

Here you’ll find answers to “How do I….?” types of questions. These guides are goal-oriented and concrete; they're meant to help you complete a specific task. For conceptual explanations see the Conceptual guide. For end-to-end walkthroughs see Tutorials. For comprehensive descriptions of every class and function see the API Reference.

Installation

How to: install LangChain packages

How to: use LangChain with different Pydantic versions

Key features

This highlights functionality that is core to using LangChain.

How to: return structured data from a model

How to: use a model to call tools

How to: stream runnables

How to: debug your LLM apps

Components

These are the core building blocks you can use when building applications.

Chat models

Chat Models are newer forms of language models that take messages in and output a message. See supported integrations for details on getting started with chat models from a specific provider.

How to: do function/tool calling

🔍 分块 2:
How to: do function/tool calling

How to: get models to return structured output

How to: cache model responses

How to: get log probabilities

How to: create a custom chat model class

How to: stream a response back

How to: track token usage

How to: track response metadata across providers

How to: use chat model to call tools

How to: stream tool calls

How to: handle rate limits

How to: few shot prompt tool behavior

How to: bind model-specific formatted tools

How to: force a specific tool call

How to: work with local models

How to: init any model in one line

How to: pass multimodal data directly to models

Messages

Messages are the input and output of chat models. They have some content and a role, which describes the source of the message.

How to: trim messages

How to: filter messages

How to: merge consecutive messages of the same type

Prompt templates

Prompt Templates are responsible for formatting user input into a format that can be passed to a language model.

🔍 分块 3:
How to: use few shot examples

How to: use few shot examples in chat models

How to: partially format prompt templates

How to: compose prompts together

How to: use multimodal prompts

Example selectors

Example Selectors are responsible for selecting the correct few shot examples to pass to the prompt.

How to: use example selectors

How to: select examples by length

How to: select examples by semantic similarity

How to: select examples by semantic ngram overlap

How to: select examples by maximal marginal relevance

How to: select examples from LangSmith few-shot datasets

LLMs

What LangChain calls LLMs are older forms of language models that take a string in and output a string.

How to: cache model responses

How to: create a custom LLM class

How to: stream a response back

How to: track token usage

How to: work with local models

Output parsers

Output Parsers are responsible for taking the output of an LLM and parsing into more structured format.

🔍 分块 4:
How to: parse text from message objects

How to: use output parsers to parse an LLM response into structured format

How to: parse JSON output

How to: parse XML output

How to: parse YAML output

How to: retry when output parsing errors occur

How to: try to fix errors in output parsing

How to: write a custom output parser class

Document loaders

Document Loaders are responsible for loading documents from a variety of sources.

How to: load PDF files

How to: load web pages

How to: load CSV data

How to: load data from a directory

How to: load HTML data

How to: load JSON data

How to: load Markdown data

How to: load Microsoft Office data

How to: write a custom document loader

Text splitters

Text Splitters take a document and split into chunks that can be used for retrieval.

How to: recursively split text

How to: split HTML

How to: split by character

How to: split code

How to: split Markdown by headers

How to: recursively split JSON

🔍 分块 5:
How to: split code

How to: split Markdown by headers

How to: recursively split JSON

How to: split text into semantic chunks

How to: split by tokens

Embedding models

Embedding Models take a piece of text and create a numerical representation of it. See supported integrations for details on getting started with embedding models from a specific provider.

How to: embed text data

How to: cache embedding results

How to: create a custom embeddings class

Vector stores

Vector stores are databases that can efficiently store and retrieve embeddings. See supported integrations for details on getting started with vector stores from a specific provider.

How to: use a vector store to retrieve data

Retrievers

Retrievers are responsible for taking a query and returning relevant documents.

How to: use a vector store to retrieve data

How to: generate multiple queries to retrieve data for

How to: use contextual compression to compress the data retrieved

🔍 分块 6:
How to: use contextual compression to compress the data retrieved

How to: write a custom retriever class

How to: add similarity scores to retriever results

How to: combine the results from multiple retrievers

How to: reorder retrieved results to mitigate the "lost in the middle" effect

How to: generate multiple embeddings per document

How to: retrieve the whole document for a chunk

How to: generate metadata filters

How to: create a time-weighted retriever

How to: use hybrid vector and keyword retrieval

Indexing

Indexing is the process of keeping your vectorstore in-sync with the underlying data source.

How to: reindex data to keep your vectorstore in-sync with the underlying data source

Tools

LangChain Tools contain a description of the tool (to pass to the language model) as well as the implementation of the function to call. Refer here for a list of pre-built tools.

How to: create tools

How to: use built-in tools and toolkits

How to: use chat models to call tools

🔍 分块 7:
How to: use built-in tools and toolkits

How to: use chat models to call tools

How to: pass tool outputs to chat models

How to: pass run time values to tools

How to: add a human-in-the-loop for tools

How to: handle tool errors

How to: force models to call a tool

How to: disable parallel tool calling

How to: access the RunnableConfig from a tool

How to: stream events from a tool

How to: return artifacts from a tool

How to: convert Runnables to tools

How to: add ad-hoc tool calling capability to models

How to: pass in runtime secrets

Multimodal

How to: pass multimodal data directly to models

How to: use multimodal prompts

Agents

note

For in depth how-to guides for agents, please check out LangGraph documentation.

How to: use legacy LangChain Agents (AgentExecutor)

How to: migrate from legacy LangChain agents to LangGraph

Callbacks

Callbacks allow you to hook into the various stages of your LLM application's execution.

How to: pass in callbacks at runtime

🔍 分块 8:
How to: pass in callbacks at runtime

How to: attach callbacks to a module

How to: pass callbacks into a module constructor

How to: create custom callback handlers

How to: use callbacks in async environments

How to: dispatch custom callback events

Custom

All of LangChain components can easily be extended to support your own versions.

How to: create a custom chat model class

How to: create a custom LLM class

How to: create a custom embeddings class

How to: write a custom retriever class

How to: write a custom document loader

How to: write a custom output parser class

How to: create custom callback handlers

How to: define a custom tool

How to: dispatch custom callback events

Serialization

How to: save and load LangChain objects

Use cases

These guides cover use-case specific details.

Q&A with RAG

Retrieval Augmented Generation (RAG) is a way to connect LLMs to external sources of data. For a high-level tutorial on RAG, check out this guide.

How to: add chat history

🔍 分块 9:
How to: add chat history

How to: stream

How to: return sources

How to: return citations

How to: do per-user retrieval

Extraction

Extraction is when you use LLMs to extract structured information from unstructured text. For a high level tutorial on extraction, check out this guide.

How to: use reference examples

How to: handle long text

How to: do extraction without using function calling

Chatbots

Chatbots involve using an LLM to have a conversation. For a high-level tutorial on building chatbots, check out this guide.

How to: manage memory

How to: do retrieval

How to: use tools

How to: manage large chat history

Query analysis

Query Analysis is the task of using an LLM to generate a query to send to a retriever. For a high-level tutorial on query analysis, check out this guide.

How to: add examples to the prompt

How to: handle cases where no queries are generated

How to: handle multiple queries

How to: handle multiple retrievers

How to: construct filters

🔍 分块 10:
How to: handle multiple queries

How to: handle multiple retrievers

How to: construct filters

How to: deal with high cardinality categorical variables

Q&A over SQL + CSV

You can use LLMs to do question answering over tabular data. For a high-level tutorial, check out this guide.

How to: use prompting to improve results

How to: do query validation

How to: deal with large databases

How to: deal with CSV files

Q&A over graph databases

You can use an LLM to do question answering over graph databases. For a high-level tutorial, check out this guide.

How to: add a semantic layer over the database

How to: construct knowledge graphs

Summarization

LLMs can summarize and otherwise distill desired information from text, including large volumes of text. For a high-level tutorial, check out this guide.

How to: summarize text in a single LLM call

How to: summarize text through parallelization

How to: summarize text through iterative refinement

LangChain Expression Language (LCEL)

🔍 分块 11:
How to: summarize text through iterative refinement

LangChain Expression Language (LCEL)

Should I use LCEL?

LCEL is an orchestration solution. See our concepts page for recommendations on when to use LCEL.

LangChain Expression Language is a way to create arbitrary custom chains. It is built on the Runnable protocol.

LCEL cheatsheet: For a quick overview of how to use the main LCEL primitives.

Migration guide: For migrating legacy chain abstractions to LCEL.

How to: chain runnables

How to: stream runnables

How to: invoke runnables in parallel

How to: add default invocation args to runnables

How to: turn any function into a runnable

How to: pass through inputs from one chain step to the next

How to: configure runnable behavior at runtime

How to: add message history (memory) to a chain

How to: route between sub-chains

How to: create a dynamic (self-constructing) chain

How to: inspect runnables

How to: add fallbacks to a runnable

🔍 分块 12:
How to: inspect runnables

How to: add fallbacks to a runnable

How to: pass runtime secrets to a runnable

LangGraph

LangGraph is an extension of LangChain aimed at building robust and stateful multi-actor applications with LLMs by modeling steps as edges and nodes in a graph.

LangGraph documentation is currently hosted on a separate site. You can peruse LangGraph how-to guides here.

LangSmith

LangSmith allows you to closely trace, monitor and evaluate your LLM application. It seamlessly integrates with LangChain and LangGraph, and you can use it to inspect and debug individual steps of your chains and agents as you build.

LangSmith documentation is hosted on a separate site. You can peruse LangSmith how-to guides here, but we'll highlight a few sections that are particularly relevant to LangChain below:

Evaluation

🔍 分块 13:
Evaluation

Evaluating performance is a vital part of building LLM-powered applications. LangSmith helps with every step of the process from creating a dataset to defining metrics to running evaluators.

To learn more, check out the LangSmith evaluation how-to guides.

Tracing

Tracing gives you observability inside your chains and agents, and is vital in diagnosing issues.

How to: trace with LangChain

How to: add metadata and tags to traces

You can see general tracing-related how-tos in this section of the LangSmith docs.

阶段2:向量存储与检索

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from langchain_openai import OpenAIEmbeddings  
from langchain_community.vectorstores import Chroma

# 5. 获取嵌入模型import os
import dotenv
dotenv.load_dotenv()

os.environ['OPENAI_API_KEY'] = os.getenv("SILICONFLOW_API_KEY")
os.environ['OPENAI_BASE_URL'] = os.getenv("BASE_URL")

embeddings = OpenAIEmbeddings(
model="Qwen/Qwen3-Embedding-0.6B",
)
# 6. 向量数据存储(默认存储到内存中)
db = Chroma.from_documents(documents, embeddings)

# 7. 向量检索
retriever = db.as_retriever()
docs = retriever.invoke("what is Chat Models?")

for i, doc in enumerate(docs):
print(f"\n🔍 分块 {i + 1}:")
print(doc.page_content)

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183

🔍 分块 1:
How to: use few shot examples

How to: use few shot examples in chat models

How to: partially format prompt templates

How to: compose prompts together

How to: use multimodal prompts

Example selectors

Example Selectors are responsible for selecting the correct few shot examples to pass to the prompt.

How to: use example selectors

How to: select examples by length

How to: select examples by semantic similarity

How to: select examples by semantic ngram overlap

How to: select examples by maximal marginal relevance

How to: select examples from LangSmith few-shot datasets

LLMs

What LangChain calls LLMs are older forms of language models that take a string in and output a string.

How to: cache model responses

How to: create a custom LLM class

How to: stream a response back

How to: track token usage

How to: work with local models

Output parsers

Output Parsers are responsible for taking the output of an LLM and parsing into more structured format.

🔍 分块 2:
How to: summarize text through iterative refinement

LangChain Expression Language (LCEL)

Should I use LCEL?

LCEL is an orchestration solution. See our concepts page for recommendations on when to use LCEL.

LangChain Expression Language is a way to create arbitrary custom chains. It is built on the Runnable protocol.

LCEL cheatsheet: For a quick overview of how to use the main LCEL primitives.

Migration guide: For migrating legacy chain abstractions to LCEL.

How to: chain runnables

How to: stream runnables

How to: invoke runnables in parallel

How to: add default invocation args to runnables

How to: turn any function into a runnable

How to: pass through inputs from one chain step to the next

How to: configure runnable behavior at runtime

How to: add message history (memory) to a chain

How to: route between sub-chains

How to: create a dynamic (self-constructing) chain

How to: inspect runnables

How to: add fallbacks to a runnable

🔍 分块 3:
How to: do function/tool calling

How to: get models to return structured output

How to: cache model responses

How to: get log probabilities

How to: create a custom chat model class

How to: stream a response back

How to: track token usage

How to: track response metadata across providers

How to: use chat model to call tools

How to: stream tool calls

How to: handle rate limits

How to: few shot prompt tool behavior

How to: bind model-specific formatted tools

How to: force a specific tool call

How to: work with local models

How to: init any model in one line

How to: pass multimodal data directly to models

Messages

Messages are the input and output of chat models. They have some content and a role, which describes the source of the message.

How to: trim messages

How to: filter messages

How to: merge consecutive messages of the same type

Prompt templates

Prompt Templates are responsible for formatting user input into a format that can be passed to a language model.

🔍 分块 4:
How to: pass in callbacks at runtime

How to: attach callbacks to a module

How to: pass callbacks into a module constructor

How to: create custom callback handlers

How to: use callbacks in async environments

How to: dispatch custom callback events

Custom

All of LangChain components can easily be extended to support your own versions.

How to: create a custom chat model class

How to: create a custom LLM class

How to: create a custom embeddings class

How to: write a custom retriever class

How to: write a custom document loader

How to: write a custom output parser class

How to: create custom callback handlers

How to: define a custom tool

How to: dispatch custom callback events

Serialization

How to: save and load LangChain objects

Use cases

These guides cover use-case specific details.

Q&A with RAG

Retrieval Augmented Generation (RAG) is a way to connect LLMs to external sources of data. For a high-level tutorial on RAG, check out this guide.

How to: add chat history

阶段3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from langchain_openai import ChatOpenAI  
from langchain.prompts import PromptTemplate

llm = ChatOpenAI(model="deepseek-ai/DeepSeek-V3")
# 8.定义提示词模版
template = """
你是一个问答机器人。
你的任务是根据下述给定的已知信息回答用户问题。
确保你的回复完全依据下述已知信息。不要编造答案。
如果下述已知信息不足以回答用户的问题,请直接回复"我无法回答您的问题"。
已知信息:
{context}用户问:
{question}
请用中文回答用户问题。
"""
# 7.得到提示词模版对象
prompt_template = PromptTemplate.from_template(template=template)
# 8.得到提示词对象
prompt = prompt_template.invoke({"question":"what is Chat Models?","context":docs})
# 9. 调用LLM
response = llm.invoke(prompt)
print(response.content)

结果:

1
2
3
4
5
6
7
8
根据已知信息,"Chat Models"(聊天模型)是LangChain中的一个组件,它们以**消息**(Messages)作为输入和输出。每条消息包含内容(content)和角色(role),角色用于描述消息的来源(例如用户、系统等)。  

已知信息中提到:
- 聊天模型的输入输出是结构化的消息(而非传统LLM的纯文本字符串)。
- 支持通过消息处理功能(如合并连续同类型消息、过滤或修剪消息)。
- 其他功能包括工具调用(tool calling)、流式返回响应(streaming)、跟踪令牌使用情况(token usage)等。

如果需要更具体的定义或实现细节,当前信息不足以回答。

聊天模型(Chat Models)是新型的语言模型,它接收消息并输出消息。请查看特定提供者的支持集成以 了解如何开始使用聊天模型。