来源:互联网 更新时间:2026-05-30 20:16
构建基于大型语言模型的应用程序,核心挑战之一,就是如何把LLM生成的通识性回答,和我们行业里那些具体的、专有的领域数据真正结合起来。这可不是一件简单的事。

一个常见的思路是微调LLM,让它更懂我们的领域。但即便经过微调,不准确和“一本正经地胡说八道”(也就是幻觉)的问题依然存在。所以,业界才催生了检索增强生成(RAG)技术。它的逻辑很清晰:让LLM的回答不再凭空想象,而是基于你提供的具体数据来生成,并且能指出信息来源。
RAG的核心,是为你想使用的数据片段创建文本嵌入。这样一来,就能把源文本的一部分“塞进”LLM用来生成回答的语义空间里。同时,RAG系统还会把原文本一并返回,这意味着LLM的回答背后有真实的人类撰写的文本作为支撑,还能附带引用。
首先,我们需要把数据准备好。通常是先将文本数据进行分块,然后把这些块构建成索引,以便后续能快速检索到相关的部分。
from sklearn.feature_extraction.text import TfidfVectorizer
def build_index(chunks):
vectorizer = TfidfVectorizer()
X = vectorizer.fit_transform(chunks)
return vectorizer, X
有了索引,当用户提出一个查询时,系统就可以根据这个查询,在索引里快速找到最相关的几个文本块。
def retrieve_chunks(query, vectorizer, X, top_k=5):
query_vec = vectorizer.transform([query])
scores = (X * query_vec.T).toarray()
top_indices = scores.flatten().argsort()[-top_k:][::-1]
return top_indices
最后一步,把检索到的相关文本块作为上下文,一并提供给LLM,让它基于这些新鲜的材料生成最终的回应。
def generate_response(query, chunks, vectorizer, X, model):
top_indices = retrieve_chunks(query, vectorizer, X)
relevant_chunks = [chunks[i] for i in top_indices]
context = .join(relevant_chunks)
response = model.generate_response(query, context)
return response
在整个RAG系统里,有一个细节特别值得留意:数据片段的大小。怎么划分数据,也就是所谓的“分块”,其实远比直接把整篇文档一股脑嵌入要复杂得多。
分块,就是把长文档或者大数据集,切割成更小、更独立的单元。这么做是为了方便后续的处理、存储和检索。尤其是在处理大规模文本数据时,分块几乎是必须的,因为LLM对长文本的处理能力终究是有限的。
在构建LLM应用时,分块数据的大小,直接关系到搜索结果的准确性。当你把一段数据嵌入成向量,如果这个块包含的内容太多,这个向量就会变得“面目模糊”,难以精确描述其中的特定内容;反之,如果块切得太小,又会丢失关键的上下文信息。
Pinecone公司的Roie Schwaber-Cohen对此有个精辟的总结:“我开始思考如何将内容分成更小块的原因是,这样当我检索时,它实际上能够命中正确的内容。你将用户的查询嵌入,然后将其与内容的嵌入进行比较。如果你嵌入的内容大小与用户查询的大小差异很大,你就更可能得到较低的相似度得分。”
所以,不光要考虑查询和响应时用的文本块大小,还要想清楚最终返回给用户的响应块大小。举个例子,如果你嵌入的是整章的内容,而不是一页或一段,向量数据库或许能在查询和整章之间找到一些语义相似性。但问题是,整章都相关吗?很可能不是。更重要的是,LLM能从这个检索到的庞然大物和用户问题中,生成一个精准的回应吗?
说到底,分块不是一个能简单套公式的问题。行业内并没有一个放之四海而皆准的标准。最佳的分块策略,完全取决于你具体的应用场景。
不过也别太担心,你并非只能对着原始数据“撞大运”。你还有元数据这个利器。元数据就像是一个小标签,可以是指向原始块或更大文档的链接、类别、标签,甚至是任何文本。正如Schwaber-Cohen所说:“这有点像一个 JSON blob,你可以用它来过滤东西。如果你只是在寻找特定子集的数据,你可以大大减少搜索空间,并且可以使用元数据将你在响应中使用的内容链接回原始内容。”
总而言之,块的大小很重要。而选择合适的分块策略,加上对元数据的巧妙运用,能显著提升检索和响应的效率和准确性。
实践中,主要有以下几种分块策略可供选择:
这是最直观的方法,就是把文本切成固定大小的块。如果数据集里的内容格式都比较统一,比如新闻文章或博客帖子,那这个方法就挺合适。它的优点是成本低、实现简单,但缺点也很明显——完全没考虑文本本身的上下文,在某些场景下可能会影响效果。
示例代码:
def chunk_text(text, chunk_size=500):
words = text.split()
chunks = []
current_chunk = []
current_length = 0
for word in words:
current_length += len(word) + 1
if current_length > chunk_size:
chunks.append(.join(current_chunk))
current_chunk = [word]
current_length = len(word) + 1
else:
current_chunk.append(word)
chunks.append(.join(current_chunk))
return chunks
如果你的数据集里混着好几种不同类型的文档,那试试随机大小的块或许是个办法。这种方法可能会无意中捕捉到更广泛的语义上下文。但风险也显而易见:块切得随机,很容易把一句话或一个概念打断,产生一些毫无意义的片段。
滑动窗口是个很常见也很聪明的做法。它的关键是,让新的块和前一个块有一部分内容重叠。这样一来,能更好地保留每个块周围的上下文信息,提高语义相关性。但代价是需要更多存储空间,也可能带来冗余信息,让检索过程变慢,甚至让RAG系统在识别正确来源时犯迷糊。
这是目前比较精细的方法。它会根据标点符号、Markdown或HTML标签等语义标记来分割文本。可以递归地将文档分解成更小、更完整的片段,每个片段都尽量保持逻辑和上下文的完整性。效果通常很不错,但问题在于需要额外的预处理步骤,计算量也上去了,会拖慢分块的速度。
要找到最适合你业务场景的分块策略,免不了要花些功夫去测试和验证。你可以尝试不同的方法,然后通过人工审核或者LLM评估器来给它们打分。当你发现哪个方法表现更好时,还可以通过基于余弦相似度分数对结果做进一步过滤,让最终输出的质量再上一个台阶。
分块只是生成式AI技术拼图中的一块。完整的RAG系统还需要LLM、向量数据库和存储等组件的协同配合。但说到底,最重要的还是你得有一个明确的目标。目标清晰了,这个项目才能走得稳、走得远。
下饭影视APP下载安装指南
灵宝派对手游下载安装地址推荐
和平精英如何做到压枪稳-和平精英怎样才能压枪稳
下载浏览器app下载安装选择推荐
初中英语同步课文跟读APP推荐|免费下载高口碑跟读软件排行榜
BuuPo官网在哪下载 最新官方下载安装地址
4D采矿者官网在哪下载 最新官方下载安装地址
阅读app安卓版下载推荐
碎片人偶Vragmeet官网在哪下载 最新官方下载安装地址
Elysium Above 履云录官网在哪下载 最新官方下载安装地址
无尽花界时装合辑
免费影视剧APP推荐
喧哗番长乙女 2nd Rumble !!官网在哪下载 最新官方下载安装地址
纸嫁衣9官网在哪下载 最新官方下载安装地址
萌神契约手游下载安装
好用的手环阅读app下载安装
儿子穿新中式现身大会堂 马斯克罕见用中文回应:他正在学习普通话
人声接近真人!OpenAI一口气更新三款超强语音AI
名单曝光!库克、马斯克等将随团到访中国 黄仁勋不在其中
短视频软件推荐
手机号码测吉凶
本站所有软件,都由网友上传,如有侵犯你的版权,请发邮件haolingcc@hotmail.com 联系删除。 版权所有 Copyright@2012-2013 haoling.cc