📱

Get Our Mobile App

Take your business learning on the go!

Download on the App StoreGet it on Google Play

Learn AI Skills - Here's everything you need to know about RAG!

Mckay Wrigley4:33:08

Transcription

欢迎大家来到 Takeoff RAG 课程。在本课程中,我们将教您如何使用 AI 模型进行检索技术。让我们开始吧。首先,我想谈谈一些先决条件,以确保您能从本课程中获得最大收益并跟上我们在这里将要进行的所有内容。

所以,第一点是您需要具备一些基本的编码技能。如果您熟悉该平台,那么您很可能已经准备就绪。如果您到目前为止都能跟上所有内容,那么您不需要太多。如果您没有任何编码技能,请务必使用像 Cursor 这样的工具。我们当然有我们非常受欢迎的 Cursor 课程,所以去看看吧。所有这些 AI 工具的优点是,如果您从未写过代码,或者类似的东西,那么入门非常容易。所以您需要能够编写一些代码来跟上进度,但不要太复杂。

您需要一个 OpenAI 账户。在本课程中,我们将经常使用 OpenAI GPT-4o mini。要使用一个非常好的廉价模型,您需要 API 密钥才能访问他们的 API 并使用像他们的嵌入模型这样的东西。所以您需要确保您注册一个 OpenAI 账户,如果您还没有的话。我们还建议您注册一个 Superbase 账户。

关于成本,简单说一下,几美元就足够了。如果您在 OpenAI 账户中有 1 或 2 美元,您将拥有足够的积分。我们不会大量消耗 API 积分。Superbase,您可以注册他们的免费套餐。我们在这里将要做的所有事情,您都可以在向量数据库方面免费使用 Superbase。所以您实际上不需要升级到专业套餐。

所以您唯一可能需要花钱的地方就是在 OpenAI 上支付一点 API 积分。但是,如果您乐于使用某种现有的免费模型,那么也可以随意使用。但我们大部分时间将使用 OpenAI 模型。如果您想深入研究一些高级内容,我们将使用一些 Cohere 模型。如果您听说过 Cohere,我们将在课程后期使用他们的一些重新排序 API 来进行一些高级检索技术。如果您真的想深入研究,我也推荐一个 Cohere 账户。但这些是您真正需要担心的核心三件事。

话虽如此,让我们开始讨论本课程的目标。好的,到最后,我们希望确保您能够自信地在实际项目中构建一个完整的端到端 RAG 系统。无论您是为公司构建内部工具以帮助您更好地运营业务,还是只想构建个人项目,因为您已经成为一名 AI 工程师,并且想对事物有更好的感觉,或者您有客户期望您构建项目并实际交付这些实际的检索系统。目标是到最后,您将了解所有组成部分,了解它们如何协同工作,并且您将能够自信地走向世界并构建这些工具。

为什么您需要学习 RAG?RAG 非常重要。它是 AI 工具构建的基础部分之一,如果您不知道如何构建检索系统,那么很难继续构建真正高质量、真正有用的工具。所以 RAG,如果您不熟悉,代表检索增强生成。它基本上是向 LLM(大型语言模型,AI 模型)提供额外信息的过程,无论是来自知识库,还是来自互联网上的实时信息,以便 LLM 可以根据特定的上下文提供答案。

这在各种地方都很有用,对吧?所以一些常见的 RAG 示例是,您知道,将 LLM 与知识库结合使用。对吧?所以假设您有数百甚至数千个 Google 文档,可能在您自己的个人图书馆里,可能在您整个公司里。然后有人问了一个关于某种 HR 政策的问题,而这可能记录在一个 Google 文档中。那么您将需要一个 RAG 系统来根据用户的查询进行检索,对吧?假设您正在处理某种公司聊天机器人或类似的东西,然后您的一位员工说:“嘿,我们的 PTO 政策是什么?”那么您希望您的 RAG 系统能够从所有这些文档中找到最相关的文档,以便将其添加到其上下文中。这样,当您调用 API 并从 LLM、从这些 AI 实验室之一获取答案时,它实际上可以拥有回答问题所需的上下文。所以知识库问答是您将看到的一种非常流行的用例。

您知道,另一个我们经常看到的用例是为内部工具获取关键业务信息。所以,这可能是客户数据、销售数据,对吧?假设您正在构建一个客户支持代理,那么您需要的一件事是,当客户说:“嘿,我想从月度套餐升级到年度套餐”时,您将需要能够从 Stripe 或类似的地方获取实时数据,并且您将需要 RAG 来做到这一点。

另一个流行的用例是访问互联网上最新的实时来源。所以,这对于天气、股票信息等很有用。对吧?如果您问 ChatGPT,例如,苹果的股票价格是多少,它不会知道,它不在其训练数据中。OpenAI 当然,如果您最近使用过 ChatGPT,它有一些网络浏览功能。OpenAI 在后台所做的就是他们有一个检索系统,可以从互联网上获取最新的信息来帮助回答这个问题。所以获取实时数据是 RAG 的一个很好的用例。

而且,最后一个非常非常核心的常见示例是将 RAG 用作代理工作流的长期记忆系统。所以,这有点复杂。我们将在我们的代理课程中更侧重于这一点。我们可能会在代理的内存方面更多地涉及这一点。但这基本上是您看到的一些 RAG 的主要用例。

显然,市面上一个更受欢迎的产品,它使用了一个非常复杂的检索系统,那就是 Perplexity。所以 Perplexity 就是,如果您愿意,就是 AI 版的 Google,您问一个问题,它不会只给您一堆链接,而是使用链接来实际生成一个 AI 生成的答案。所以,您在这里看到的例子,我有一个例子,我们问“英伟达的价格是多少?”您可以看到他们正在从互联网上的信息中抓取实时来源。所以他们正在做这个,他们正在使用这些来为您提供最准确的数据,并用 AI 生成的答案来补充,而这个答案就在这个图表下面。我可能应该在这里包含它,但我认为您大概明白了。所以您拥有这些价值数十亿美元的巨头公司,它们正在构建超级先进的检索系统。如果您想构建真正出色的 AI 产品和工具,学习如何构建这些系统非常关键。

所以,在我们开始学习所有这些东西之前,让我们快速谈谈课程大纲。我们已经将事情分为三个部分。这些是核心的三部分,然后我们可能会有一个第四部分,包含一些一次性视频,供那些想要额外资源但又有点分散的人使用。但核心的三部分将是学习基础知识。所以,我们将学习嵌入、向量数据库、相似性搜索。我们还将学习一些基本的提示技术,以帮助您从 RAG 相关的 AI 查询中获得最大收益。所以,所有这些基础知识,当您看到像 Perplexity 这样的产品时,所有这些东西都在发挥作用。所以学习这些部分的每一个如何融入这个拼图非常重要。

我们在第二部分将要做的是,我们将实际创建一个完整的端到端 RAG 系统。所以,这将更多地侧重于后端,我们将尝试从头开始编写代码。所以,我们将在第二部分和第三部分编写大量代码。而在第一部分,我们将更多地关注概念性内容,并解释我已经编写的代码,以强调重点。在第三部分,我们将构建一个完整的实际检索项目。所以,我们将整合我们从前两个系统中学习到的所有内容。我们将构建一个基本的工具,带有一个实际的 UI,一个实际的应用程序,它集成了我们将要构建的 RAG 系统。所以,到最后,您应该能够再次做到这一点。您应该能够构建任何类型的 RAG 系统,您拥有的任何工具,无论是为了您自己、您的业务还是客户,您知道,等等。我们将确保您学到所有需要的东西,以便您能够出去构建所有这些很酷的检索系统。因为一旦您学会了如何构建检索系统,它就会真正提升您作为一名 AI 工程师的经验水平,以及作为一名能够出去执行这些产品创意的人的能力。

话虽如此,让我们开始学习基础知识。在本节中,我们将涵盖嵌入。我们将涵盖向量数据库,这是您如何实际存储带有嵌入数字的内容。如何对我们的向量数据库执行相似性搜索,以便根据给定的查询检索最相关的信息。以及一些您可以使用的提示技巧,以帮助从您的检索系统中获得更多信息。

所以,让我们先对 RAG 进行一个简短的概述。我知道我已经稍微提到了这一点,但我想确保每个人都理解 RAG 是什么,以及为什么检索系统在构建 AI 应用程序方面如此重要。所以,再次,RAG 只是一个花哨的首字母缩写词,代表检索增强生成。在本课程中,您会经常听到我使用 RAG 或检索。我个人更喜欢检索系统而不是 RAG 这个词。我认为 RAG 有点……我认为当您开始引入一堆首字母缩写词时,它只会让人们感到困惑。您知道,很多研究人员喜欢它,这显然是它的起源。检索,如果我说“嘿,我们正在进行文档检索”,我认为对于普通人来说,这要清楚得多。所以您会经常听到我在本课程中将检索与 RAG 互换使用。

而检索系统只是试图增强您的语言模型。所以,如果您正在调用,例如 GPT-4,也许您正在使用 Cloud 3.5 Sonnet 作为您的模型。您试图用检索系统做的是,您试图从外部来源访问额外信息,将其注入您的查询中,以便您获得一个更好、更详细的答案,该答案由与您的查询相关的任何内容补充。

所以,我们已经在上一节中涵盖了一些实时示例,但是,您知道,RAG 的一个很好的用例是根据实时天气数据回答问题。为什么呢?您知道,如果一个一年前训练的模型被问到今天的天气问题,它显然无法给出准确的答案。您需要用额外的信息来补充 LLM。所以,这就是检索系统的全部前提。

那么 RAG 解决了什么问题?第一点是准确性和可靠性。大型语言模型的一个问题是幻觉。有时它会自信地给您一个明显错误的答案。也许,例如,如果您问一个模型法国的首都是什么,然后它给您答案是伦敦,显然这是错误的。这就是我们所说的幻觉。模型现在应该能正确回答如此简单的问题,但观点是,也许您不想让 AI 模型直接给您答案,也许您想用更多信息来补充您给它的提示。也许您会检索……您有一个某种 RAG 系统,它根据您提供的国家会实际检索出正确的首都答案。所以这将是使用 RAG 系统来减少幻觉的一个例子。

所以,您知道,另一个例子,我们提到了股票信息,最新的财务信息,最新的天气数据。使用最新的数据可以为 Perplexity、GPT 网络搜索等令人难以置信的产品提供动力。所以,如果您需要访问尽可能最新的信息,对吧?假设您正在问一个关于 NBA 比赛比分的问题,也许您想知道勇士队今晚的比分是多少?您将需要一个 RAG 系统来实际获取该信息。

另一个是事实核查。您知道,所以也许您想……您想根据……一份科学论文或类似的东西来问一个问题。那么您可以做的是,您可以使用检索系统来根据某种参考文档检查模型给出的答案。所以,RAG 系统的优点是,您可以通过补充您的提示以额外的外部信息来真正提高您答案的准确性和可靠性。

这基本上是同一枚硬币的两面,那就是知识增强。所以,这包括访问您的专有数据。所以,假设您正在为您的公司构建内部工具。您知道,也许您有一些内部的 CRM,您的所有团队成员都可以使用。您可能想构建一个 AI 工具,您可以在聊天界面中快速获取该信息。所以,这将是使用检索系统访问专有数据的一个很好的例子。

您可以组合多个数据源,这真的很酷。也许您保存了大量的 PDF,并且您想将它们与……一些 Notion 页面结合起来。也许您在 Notion 中使用公司维基或类似的东西。所以,您可以开始混合搭配所有这些不同的来源。

这让我想到第三点,您也可以构建多模态检索系统。所以,也许您不仅有大量的文本文档,您还有……大量的图像。您知道,也许您有一些图表,您知道,假设您有一个报告您上个季度销售数字的工具,或者类似的东西。也许您里面有一些图表,您想包含进去,而这更多是基于图像而不是文本。所以,您可以做的是,您可以组合所有这些不同的数据片段。最终,我们将进入视频、音频等领域,这实际上已经开始发生了。所以,您可以组合所有这些,不仅是数据来源,还有实际的数据类型本身。

而检索系统真正有用的第三件事是成本和效率。所以,LLM 模型正在获得越来越长的上下文窗口,这意味着在提示中,在对话历史记录中,您可以将更多信息塞进这些请求中。但是,如果您最大化上下文窗口,这些请求将变得更昂贵,速度会更慢。所以,通过使用检索机制,您可以做一些事情,比如,而不是将 50 个文档包含到某个东西中,您可以实际只获取……最相关的五个文档,将成本降低 10 倍,获得更快的响应时间。所以,根据您要构建的内容以及您如何管理您的成本和延迟等内容,RAG 系统可以真正带来好处。

所以,为了更深入地探讨我刚才提到的上下文窗口这一点,让我先解释一下,如果您出于某种原因不熟悉上下文窗口是什么。上下文窗口的概念。所以,这里我们只是在 OpenAI 的模型页面上,我们正在查看他们的 GPT-4o 模型,您可以看到它的上下文窗口是 128,000 个 token。所以,为了简要回顾一下,当 GPT-4 最初发布时,我认为上下文窗口是 4,000 或 8,000 个 token,我不记得是哪个了。但它基本上比现在小得多。所以,现在一年半后,当我们有了 GPT-4o,现在我们有 128,000 个 token 的上下文。所以,这意味着我们可以将更多的文本、更多的图像放入这些 AI 模型的请求中。这意味着我们有能力……我相信我可以说,整个哈利波特系列,或者类似的东西,大约是 100,000 个 token,大概在这个范围内。我可能记错了确切的数字,但重点是,您知道,一年半前您可能只能装一本书,而现在您可以装一整套书。

所以,未来,这些上下文窗口将继续增长。所以,您听到人们争论的一个问题是,RAG 是否已经过时?上下文窗口……当它们达到,例如,Google Gemini 模型已经有百万 token 的上下文窗口时,会发生什么?当这些达到 1000 万、1 亿、10 亿时,RAG 还有意义吗?我的答案是,RAG 与上下文窗口协同工作,这不是非此即彼的情况。RAG,因为我们列出的所有这些原因,准确性和可靠性、知识增强、成本和效率,RAG 仍然是未来会很重要的事情。

所以,如果您听到人们说“嘿,上下文窗口正在增长,您将不再需要担心 RAG 了”,我的回答是,那根本不是真的。任何在现实世界中实际构建这些系统、构建这些产品的人,RAG 都是他们基础设施中非常关键的一部分。所以,上下文窗口让您能够基本上在 AI 请求中使用更多的内存,这很棒。我们想要更大的上下文窗口,但我们也希望能够继续构建检索系统,以便我们可以利用尽可能多或尽可能少的上下文窗口,并拥有最终的灵活性来管理上下文窗口。所以,在很多方面,RAG 是一种管理上下文窗口的有用性和效率的方法。

所以,在我们开始单独深入研究这些特定部分之前,让我们快速回顾一下整个系统的概览,以便您对我们将在接下来的课程中探索的过程有一个更好的感觉。所以,检索系统的工作方式是,您想输入一个用户查询。所以,在这种情况下,您知道,想象一下我们有一个聊天机器人,它根据维基百科页面或其他东西回答问题。所以,假设用户问:“哈勃太空望远镜是哪一年发射的?”所以,在用户这边,他们输入查询。在后端,我们为他们构建了大量的事情,他们不需要担心。他们的查询会补充这个系统的输出,然后它将查询与……相关的维基百科页面以及可能来自哈勃太空望远镜维基百科页面本身的相应部分结合起来,以从语言模型中获得答案。

所以,这个过程在后端的工作方式,这就是我们将要学习如何构建的系统,是第一步,它将用户的查询发送到嵌入模型。我不会深入探讨每个部分的具体细节,因为我们显然会有即将到来的课程,这些课程将深入探讨每个部分。但想法是,查询被发送到嵌入模型,这是一个 AI 模型,它基本上将文本或图像(为了简单起见,我们将只坚持文本)转化为一个巨大的数字序列。这些数字的含义,这些数字序列的含义是,它基本上试图编码存储在文本中的信息,并将其转换为数字格式,我们可以对其进行各种不同的操作。这就是查询嵌入将要做的。所以,您将您的查询发送到嵌入模型,该嵌入模型吐出一个查询嵌入,这只是一系列数字。我们通常会做的是,我们将这个嵌入存储在某种向量数据库中。我们将在详细介绍向量数据库,并向您展示如何设置所有这些。并且您通常会进行某种检索相似性搜索,将您从嵌入模型中获得的嵌入与您存储在向量数据库中的所有嵌入和内容进行比较。所以,输出是,您经历了从查询到相关片段的整个过程,并且您能够将该查询与您存储的最相关信息进行匹配。再次,您将这些结合起来,发送到……Cloud 3.5 Sonnet,或者 GPT-4L,或者 Gemini 1.5,无论您偏爱哪个模型,然后您就会得到答案。

所以,这就是我们将在那里探索的旅程。我们将从头开始构建这些步骤中的每一个,向您展示这些部分的每一个如何契合,解释这些部分是什么,为什么它们很重要,如何充分利用它们,我们应该能够为您设置好所有这些。

所以,让我们在这里结束这一课,实际上开始设置一些代码,以便我们可以真正深入研究这个问题。

好的,让我们设置一些代码。我们将需要您导航到下面的链接。这将是存储库链接,我将为您链接好。您看到它时它将是公开的。但我们将要做的是,如果您一直在关注我们 Takeoff 的所有其他课程,这是一个非常标准的过程。我们将使用 Git 来实际克隆我们将要用于编写本课程所有代码的存储库。所以,您需要做两件事之一。您可以下载 zip 文件并直接下载该代码,或者我推荐的方式是直接使用 Git。这只是一个很好的技能,而且这是我们在这里 Takeoff 上处理业务的典型方式。所以,您需要点击这个存储库上的大绿色按钮,您需要将这个 URL 复制到您的剪贴板。我们将进入终端。所以我现在在 Mac 上。我目前在我的终端里。我们将运行 `git clone`,粘贴这个 URL。您将看到我们已经将 Takeoff RAG 课程克隆到了我的电脑上。

所以,我现在要做的是,我将通过更改目录到 `takeoff-rag-course` 来导航。您可以看到我已经在我的终端中打开了它。您需要做的是,用您选择的 IDE 打开它。我将使用 Cursor。这是一个我设置的快捷方式来打开它。您可能需要手动打开它,只需打开 Cursor 应用,然后打开一个新文件夹,打开文件夹。您只需要确保您在这里设置好。再次,我强烈建议您实际上使用 Cursor。这就是我们在这里 Takeoff 上处理所有项目的方式。如果将来发生变化,当然我们会调整,但目前我认为 Cursor 是迄今为止最好的 AI 编码工具。

所以,一旦您在这里将存储库设置在 Cursor 中,我们需要做的是,为了确保您已经……所有都已启动。我们将在这里打开一个终端。如果您更喜欢使用您的……您自定义的终端,那很好。我只是喜欢使用 IDE 中的这个,非常方便。我们将运行 `npm install`。这是 npm install,它将安装我们将要在这个项目中使用的各种包。所以,像 OpenAI SDK、Cohere SDK 之类的东西。

所以,您还需要在这里做的是,对于第一部分,学习基础知识部分,您需要两样东西。所以,如果我们转到我们的 `sections` 文件夹,您会看到我们有一个文件夹,它是 `1`。`section one learn the basics`。这将是我们在这里将要使用的所有代码。再次,正如我在本课程开始时提到的,第一部分主要是您只需要观看我。我们将讨论概念性内容。而在第二部分,我们将实际从头开始构建一个 RAG 系统。如果您想自己做,并跟着我一起写代码,一边解释发生了什么,这绝对是一个很好的练习。您写的代码越多,就越能巩固概念,越能让您对正在发生的事情有一个更基础的理解。如果您真的做了,但如果您不想这样做,我们将在第二部分做所有事情。所以,练习越多越好,但现在不用担心。

您需要担心的是,您会看到这个 `env.example` 文件,在 `learn the basics` 部分。这是我们需要的那两个密钥。所以,我们需要一个 OpenAI API 密钥,然后我们需要一个来自 Superbase 的数据库 URL。如果您只是跟着我,您不需要这些密钥。所以,如果您只想观看这些部分,从概念上理解第一部分发生了什么,不用担心。我在这里会涵盖它,只是为了那些确实想运行代码并可能自己写出这些练习的人。但基本上,您需要在这里获取一个 OpenAI 密钥。我将在之后做这件事,因为我现在不想暴露我的密钥。您只需在此处粘贴该密钥值,然后在 Superbase 端,再次,我提到您可以免费使用。您可以在这里创建一个新项目。填写项目名称。您可以在免费套餐中使用微型大小。您只需要在这里输入一个数据库密码并保存。您也可以生成一个,这通常是我做的。然后您需要点击创建新项目。这将带您到一个设置屏幕。通常需要等待一两分钟才能让数据库实际启动。但是一旦一切都设置好了,您需要做什么呢?您需要转到项目设置,然后您需要转到数据库。您需要做以下事情。您需要复制这个连接字符串 URL。所以,它将是顶部的这个长字符串。您可以点击复制按钮。您可以将其粘贴在这里。然后您需要输入您输入的或生成的密码,然后您需要将其放在这里的值中。所以,如果我的密码是“这是我的密码”,那么这就是我的值。

到目前为止,您将拥有这两者都已设置好。所以,我在这里要做的是,您会注意到我们实际上是在 `env.example` 文件中。我刚注意到您实际上需要在这个文件夹中创建一个新文件,它叫做 `.env.local`。所以,您应该在 `env.example` 旁边有一个 `.env.local` 文件,它将是灰色的,因为我们已经将其从 Git 中忽略了,这样您就不会提交它们。而您实际上想要做的是,确保您删除 `example` 中的这些值。您不想提交它们,您想将它们放在您的 `.env.local` 文件中。所以,事情应该看起来像这样。您应该在这里有您的 OpenAI 密钥,并且您应该有这个数据库连接字符串,以及您输入的任何密码值。所以我将把我的放进去,我马上回来。

好的,我已经将我的环境变量放入了我的 `.env.local` 文件中。我们将要做的是,为了测试它是否设置正确,如果您转到这个 `one-setup` 文件。我给它们编号了,这样它们在我们整个部分中可以更好地排序。但您会注意到我们有一个非常基本的功能。我们正在做一个非常基本的调用 OpenAI API,使用 GPT-4o mini。我们使用 40 mini,因为它是一个非常便宜的模型。您可以为测试目的获得最多的 API 积分。您应该能够复制这里的路径。所以,如果我复制路径,顺便说一句,我只是右键单击。所以,我们只是复制路径,然后我们将运行 `tsx`,然后我们将粘贴这个值。我们应该看到……您会注意到我实际上收到了一些错误。我怀疑……哦,您知道是什么。所以,我们需要做的是,我们只需要导航到这个部分。所以,您会看到我在终端中的 `rag-course`。我们将 `cd` 进入 `sections`。所以,这只是更改目录。然后我们将……也进入这个第一部分。所以,您可以看到我现在在那个部分。如果我点击我的向上箭头键,这实际上会填充。如果我按三次,我们将再次获得该命令。我应该能够运行它。现在我们可以看到,我们在终端中打印出“你好,有什么可以帮您的吗?”所以,我们消除了错误消息,这是由于我们的配置方式。所以,您需要确保您在终端中导航到这个文件夹,我们将在那里运行本节的代码。如果您使用 `tsx`,也就是 TypeScript execute,然后是文件名,您将能够获得响应。您可以看到第 19 行,我们正在控制台记录响应。我们正在记录来自 OpenAI API 的消息内容。这意味着您的 OpenAI 密钥正在工作,您应该一切就绪。

所以,如果您成功了,这意味着您很可能已经准备好继续了。再次,您不需要在这里跟着我写代码。您将在第二部分这样做。所以,如果您只是想获得概念上的理解,请随时跳过。但我知道很多人喜欢练习,喜欢额外的练习。这是我推荐的。如果您有时间的话。

话虽如此,让我们继续讨论嵌入。让我们谈谈嵌入。嵌入是一种编码数据的方式,可以使它们在各种用例中更容易使用。OpenAI 在其开发者文档站点上有一个关于向量嵌入的非常好的页面。您可以看到这里有很多信息可以阅读。有很多不同的代码示例,很多不同的用例,它涵盖了。我推荐阅读这个页面。我认为它让您对嵌入为何重要以及您通常会看到的一些常见用例有一个非常好的了解。

所以我认为这句话真正点明了重点,也是我们想在本课程中强调的重点是:OpenAI 的文本和/或嵌入总体上衡量文本字符串的相关性。所以,这里有一个小提示,我们将专注于文本。您可以使用嵌入来处理图像等内容,但为了简单起见,我们将专注于文本。这里的想法是,我们试图获取一段文本,并尝试对其进行编码,并将其转换为数字形式。

所以,让我们谈谈嵌入的核心。您可以看到我的幻灯片实际上没有更新。我需要确保我可能实际上为您更新它。但正如我所说,嵌入是一种……这很有趣。我们将其保留下来。我们在 Takeoff 玩得很开心。我们得时不时地给人们一点理由微笑。但尤其是在本课程中,我们将专注于嵌入的搜索方面。所以,我们将要做的是,我们将……获取大量嵌入文本。所以,假设您有上千份不同的文档。我们将嵌入所有这些不同的文档。然后我们可以做什么呢?我们可以再次获取该嵌入,那一系列数字,它现在代表了我们已经编码的文本数据,并将其转换为一串数字。我们将将其用于搜索目的。所以,在本课中,我们想谈谈我们如何实际生成这些嵌入,以及我们可以用这些嵌入做什么。

所以,让我们回到幻灯片。这是一个嵌入空间的绝佳示例。所以,一开始有点棘手的是,您试图理解,好的,我知道嵌入将文本转换为一堆数字,但我不理解的是嵌入。这很常见。如果感到困惑,您并不孤单。人们通常会问,这些数字是如何相互比较的?我们不会深入研究……数学。我们将尽量简化它。您不需要理解……嵌入工作原理背后的所有数学,以及为什么有这么多数字,以及所有这些不同的浮点数意味着什么。您需要理解的是,假设我们嵌入了像“花园”这样的东西。所以,我们只是字面意思,文本,单词“花园”,我们将其发送到嵌入模型。例如,OpenAI 的 text-embedding-3-small 模型,我们将在稍后使用它。它将返回给我们一串数字。这些数字的含义是,您可以基本上在 3D 空间中绘制该单词的位置。当您开始嵌入所有这些其他单词时,您会得到……图表上的所有这些不同的点。所以,例如,如果我们嵌入单词“软管”,我们将得到一个嵌入,在相似性搜索方面,基于它们的数字,它会非常接近。花园和软管非常接近。这很有道理。花园和软管是相当相似的词。它们不完全一样,但如果您说“软管”这个词,您可以说“也许您的软管在您的花园里”。但是,例如,如果有人说“花园”和“厨房”这两个词,它们将不那么相似。所以,您会看到花园和厨房之间的距离,以及花园和软管之间的距离要远得多。

再举一个例子,颜色和油漆。颜色和油漆。油漆总是有某种颜色。所以,这些将是相当接近的术语。但是,如果我们使用“颜色”和“微波炉”这两个词,它们将远得多。您还可以看到,您正在获得这些分组。花园、软管和洒水器在这里分组。然后我们有梳妆台、水槽、浴室、厨房、马桶。这些都聚集在这里。所以,您会注意到,如果我们进行某种相似性搜索,然后说“嘿,您知道……我们需要问一个关于冰箱之类的问题”。那么您的相似性搜索将聚集在这里,并抓取这个区域的文档。所以,您可以在图表上直观地看到嵌入是如何有意义的。想法是,这是一个很好的心智模型,嵌入是将所有这些不同的单词绘制出来。在我们的例子中,它将更像是段落或整个文档,但它们将尝试将它们相似地绘制在一起。当我们进行相似性搜索时,您知道,如果我们搜索“混凝土”,我们将抓取这个区域的东西。所以,这就是嵌入有用的基础。您可以获取文本,将其嵌入数字中,然后执行所有这些不同的操作。我们试图获取文本,我们试图将语义信息编码成数字形式,以便我们可以对其执行检索操作。

所以,让我们实际深入了解如何生成嵌入。所以,我们实际上需要看看,也许我们现在开始在概念上理解它了。如果您仍然感到有点迷茫,请坚持下去。当我们开始实际做这些事情时,事情会变得更清晰。您知道,这通常在不同的阶段以不同的方式对人们来说变得清晰。所以,对你们中的一些人来说,这可能会在这里变得清晰。我们想打开两个文件,`two-generate-embeddings`。我们将要做的是,我们将解释这个文件中的内容。所以,在最上面,我们只是导入 OpenAI,我们导入 `dotenv`。我们只需要导入环境变量。我们正在初始化 OpenAI SDK。所以,这将允许我们……非常容易地使用任何 API 端点,使用我们保存为变量的 OpenAI 客户端。在这种情况下,我们正在获取一个响应,并试图发送一个特定的……文本数组。所以,我们将发送很多不同的文档。在这种情况下,您可以看到我们使用的示例是“你好,世界”和“再见,世界”。所以,我们正在获取这两个字符串,我们将它们传递给 OpenAI 的 `create_embeddings` 端点,通过 OpenAI SDK。我们正在使用 OpenAI 的 `text-embedding-3-small` 模型。我们正在使用 256 维度的嵌入。我将在未来几节中更深入地讨论这一点。我们将讨论为什么嵌入维度很重要,以及您如何根据您的用例进行优化。我们甚至会讨论何时可能想使用 `text-embedding-3-small` 而不是 OpenAI `text-embedding-3-large` 或其他嵌入模型。但重点是,我们正在获取这两个文本片段,我们将它们发送到 OpenAI 嵌入模型。我选择这些数字的原因是,这是最便宜的方式。顺便说一句,嵌入非常便宜。所以,如果我跳转到 OpenAI 的定价页面,特别是您会看到 `text-embedding-3-small` 大约比 `text-embedding-3-large` 便宜大约六倍。然后我们使用更小的维度来使我们的操作……更快,更容易存储,当我们最终设置我们的向量数据库时。所以,这就是我们现在使用这个特定模型和这个维度数量的原因。再次,当我们进入更高级的 RAG 系统时,我们将更深入地讨论这一点。但重点是,我们输入了这两个字符串,我们都得到了嵌入。

所以,为了举例说明,我们正在做的是,我们试图获取那些词,那些词集,在我们的用例中不是单个词。然后将其绘制到图表中。所以,我实际上要做的是,如果我们复制文件路径,然后运行 `tsx` 来运行该文件,您将看到两个……

所以你会看到我们正在记录我们的响应,以便在某些情况下,我们将记录嵌入,你可以看到我们有所有这些数字,那就是一个嵌入,你也可以看到我们也在记录长度,所以我们在这里得到两个嵌入,因为我们传入了两个字符串,这很有意义,嗯,你知道,如果我在这里做第三个,那就是我的名字是好的,如果我们再运行一次,你会看到我们会得到三个,嗯,如果你在这里向上滚动,你看到所有这些都是黄色的,对你来说,颜色可能会不同,这取决于你的编辑器主题,我们将把它做得更大,你在这里看到的是我们得到了那些嵌入,好的,所以这是一个256个数字的数组,好的,那个256个数字来自维度,所以维度翻译过来就是我们将在嵌入中使用的数字的数量,好的,所以如果你使用默认数字,我认为默认数字是1536左右,我们使用256是为了让事情稍微小一点,但是,但是我们在这里得到了所有这些浮点数,它们只是大十进制数,所有这些东西结合起来形成我们在这个图上的位置,这些句子,这些术语,这些词应该如何放置,好的,所以一旦我们有了这个的嵌入,在接下来的课程中,我们将要弄清楚,好的,我们如何存储这段文本及其完整的嵌入在一个向量数据库中,好的,然后一旦我们将信息存储在向量中,我们如何执行某种相似性搜索,好的,所以所有这些部分都将是我们将在接下来的视频中涵盖的内容,所以你可以看到,你知道,我们将要上传文档,我们将进行检索,好的,所以我们将了解所有这些嵌入是如何使用的,但总的来说,这就是嵌入,它是一种将文本编码数据的方法,我们希望能够使用输出的数字来衡量这些字符串的相关性,所以我建议你看看OpenAI的页面,他们的文档在这方面非常好,如果你想更深入地了解其中一些参数,你也可以查看API参考,我们将在构建这些越来越高级的RAG系统时详细介绍这些,你甚至可以点击嵌入模型,好的,我们将使用三系列,所以我们将在本视频中使用text-embedding-3-small,但他们有一个更强大的模型,那就是大版本,它只是再次,它只是更贵,而且这里的价格非常便宜,所以对于使用GPT-4o mini和text-embedding-3-small来说,事情会非常便宜且易于处理,好的,所以希望你对嵌入是什么以及我们如何编码信息有了更多的了解,以及它如何融入,你知道,再次,我们的图表,我们只是试图构建某种嵌入空间,我们试图获取所有这些单词,所有这些句子,所有我们正在处理的文本信息,得到我们可以使用的数字,然后对其执行操作并存储数据,以便我们可以构建高级检索系统,所以让我们开始讨论它的实用性,而不是仅仅讨论嵌入是什么,我们如何实际使用它,所以现在你对嵌入是什么有了更多的了解,你知道如何使用像OpenAI的text-embedding-3-small或large模型这样的嵌入模型来生成嵌入,让我们来谈谈如何将这些嵌入及其相关的文本存储在向量数据库中,好的,所以向量数据库将允许你存储你的嵌入数据,让我们来谈谈向量数据库是什么以及为什么我们需要它们,所以核心来说,向量数据库或向量DB基本上是专门设计的数据库,用于存储向量数据,好的,所以当我们说高维向量数据时,再次,我们指的是我们特定嵌入的维度,在这种情况下,在我们的例子中,我们使用256个维度,通过OpenAI API,这是你可以使用的最小维度数量,再次,这些维度对应于你的实际嵌入中的数字数量,对吧,所以如果我们将其设为1000,我们将得到1000个数字,如果是1500,我们将得到1500个数字,等等,向量数据库是非常高效的数据库,你希望将其用于嵌入,好的,所以你不想使用传统数据库,因为问题在于存储变得非常昂贵,你的查询非常慢,效率非常低,它很快就会变成一场绝对的噩梦,即使你不需要扩展到数百万用户,你开始扩展到数百个用户,使用传统数据库,你就会开始遇到各种问题,这些数据库中的列类型从技术角度来看根本不是为了处理这些高维向量而设计的,所以我想在这里首先说明一点,嵌入和向量你可以互换使用,虽然有一些细微差别,但你可以几乎用嵌入数据库替换向量数据库,就我们在这里所做的而言,这两个基本上意味着相同的事情,但是我们想做的是,我们想谈谈存在的不同类型的向量数据库,为什么我们将使用一个非常基本的Postgres数据库,带有一个名为PGVector的扩展作为我们的数据库,我只是想给你一个关于向量数据库景观和市场景观的概述,所以再次,我们将使用Superbase,它只是一个Postgres提供商,我们之所以在Takeoff上使用Superbase,是因为我所有的个人项目都使用它,对大多数人来说,这是开始使用Postgres数据库的最简单方法,它是最受欢迎的数据库之一,如果不是最受欢迎的,我喜欢为我的向量数据库使用Postgres的原因是,它利用了我已经熟悉的现有技术堆栈,它已经与我的整个技术堆栈一起工作,它可靠,它对所有东西都有广泛的支持,我们将简单地在此之上添加一个名为PGVector的扩展,它将为我们提供一个额外的列类型,它将把我们喜欢的所有SQL数据库,关系数据库的东西,并允许我们现在能够利用这项技术并添加向量列的能力,好的,所以我们将能够实际存储一个特定的向量列类型,它将允许我们以高效的方式存储所有我们将要生成的嵌入,它将允许我们启动一个索引,以便我们对具有向量列类型的表进行的查询将是高效的,它将是可扩展的,所以,所以,所以,我喜欢使用这个特定的技术堆栈的原因,并且你看到很多人甚至公司在很大程度上以各种方式使用它,因为它开箱即用,Postgres和PGVector的开源社区非常强大,它就是这样工作的,话虽如此,不同的问题需要不同的工具,这就是为什么我们有所有这些其他不同的数据库,像Pinecone,像Weaviate,Chroma,好的,这些是更传统的向量数据库,PGVector方法基本上利用了我们喜欢并熟悉的现有技术,并使其易于访问,并使其能够处理你将遇到的向量数据库的典型问题集,Pinecone,Weaviate以及所有这些其他的,这些将更倾向于非常独立的向量数据库,好的,这些将是你可能不会使用传统数据库的东西,你只会使用你的非常特定的嵌入工作负载,使用这些不同的东西将取决于你正在做什么,你知道你想解决什么问题,你在什么规模上解决这个问题,对吧,你有一个,你知道,这是一个内部工具,如果这只是,你知道你自己的公司,这将是很好的,Postgres将是很好的,如果你正在做一些事情,比如为数百万用户构建一个记忆代理,你可能需要一种更专门针对实际向量用例的技术,所以,所以这些对话,如果你处于需要进行这些对话的境地,或者如果你真的在那个规模上运作,你可能需要咨询在每个环节都有专业知识的人,无论是你,你知道,CTO正在选择技术堆栈,你是一个工程团队正在进行这些讨论,你将需要真正地逐案评估,但是对于绝大多数观看此视频的人来说,并且在Takeoff上,你将与我们在这里使用的技术堆栈一起做得很好,Postgres的优点是,如果你不想使用Supabase,没问题,世界上显然有许多许多Postgres提供商,你可以使用,再次,我们将在我们的Postgres设置中使用Supabase,只是因为我们在Takeoff上使用它,它真的很容易设置,它非常熟悉,工具很棒,任何你想在Supabase生态系统之外使用的东西都可以,因为Supabase是开源的,它只是Postgres,所以如果你想在其他地方使用Postgres,完全没问题,所以,所以,这些是一些更受欢迎的Postgres数据库,或者更受欢迎的向量数据库,我们将使用Postgres和PGVector,所以我们将在Takeoff上所做的一切都将与本课程中你将看到的内容相匹配,好的,所以让我们开始设置一个向量DB,我们回到我们的存储库,我们将转到第三个文件,这个上传文档,所以我们将要做的是,我们将把这10个不同的文档上传到一个向量数据库,我将向你展示一些我们将要使用的部分,再次,在第二部分,我们将从头开始设置它,所以如果你正在跟进,你不确定发生了什么,不用担心,这里的想法是,我们只是想钻研概念,我们想在这里钻研如何在一个Postgres环境中设置向量DB的各个部分,以及,你知道,一些你需要注意的小事情,所以再次,我建议如果你有时间,自己从头开始编写这段代码,并获得一些额外的练习,也许运行这些文件,但如果你只是想回顾概念,这并非绝对必要,我们将使用一个名为Drizzle的工具,再次,我们在Takeoff上所有的数据库需求都使用它,它基本上被称为ORM,它为我们提供了一种非常简单的方式来处理迁移和构建我们的不同表模式等等,所以我们将要做的是,我们将转到这个DB文件夹,我在这里有一个名为schema的文件夹,我们有一个名为documents.schema.ts的单个文件,这是一个TypeScript文件,它定义了我们想要上传或想要在我们的Supabase数据库中创建的表,好的,所以,如果我们去Supabase,这是我启动的项目,它只是叫做playground,如果我们去侧面的表编辑器,你会看到我们没有表,所以我们需要做的是,我们需要实际生成一个文档,我们需要生成SQL迁移文件,我们可以运行它,以便我们可以实际使用这个表,带有嵌入向量列,正如我在幻灯片中提到的,它由PGVector启用,我们将要做的是,我们将创建那个表,然后我们将创建一个索引,它将允许我们在这个表上执行诸如相似性搜索之类的操作,这是Drizzle,你只需要做的事情来实际构建这些表,你将通常调用你的列embedding,正如你所看到的,我只是称之为embedding,它被包装在这个Vector助手里,我们所要做的就是定义我们要使用的维度数量,再次,这只是直接对应于我们在嵌入文件中指定的嵌入维度,好的,所以我们将持续使用256个维度,一旦你选择了你的维度,它必须是一致的,所以你不能有,你知道,1000个维度和500个维度和256个,你必须是标准的,所以请确保你设置了那个数字,并且它在你生成的嵌入和你实际存储在表中的内容之间是相同的,所以这是我们真正需要担心的唯一列类型来使用PGVector,这很棒,这是PGVector的优点之一,它很容易设置,我们还需要创建一个索引,以便我们可以执行诸如相似性搜索之类的操作,我们将使用余弦相似性,所以你可以看到这是Drizzle中我们需要使用的语法,如果你不熟悉Drizzle,没问题,我们将实际生成SQL,我将向你展示它是什么,所以如果我们去package.json文件,你会看到我们有两个脚本,它们对应于我们第一部分需要执行的操作,所以如果我们使用`db:generate:basics`和`db:migrate:basics`,它将负责将这个Drizzle文件转换为实际的SQL代码,所以我们可以做`npm run db:generate:basics`,它将再次,我们只是在这里的`one-learn-the-basics`文件夹里,你可以看到我们现在有一个migrations文件夹,当你看到这个方式时,我实际上可能会把它上传到Git,所以你可能不需要为这一部分生成它,但是你可以看到我们现在几乎拥有了设置所需的所有SQL代码,所以这就是Drizzle中的代码样子,但是一旦我们将其从Drizzle语法转换为SQL语法,这就是你使用的,所以你唯一需要添加的是,你必须实际启用Vector扩展,所以我们将`CREATE EXTENSION IF NOT EXISTS vector`,这将是启用PGVector扩展的代码,大多数Postgres提供商目前都与PGVector兼容,我真的很惊讶你正在使用不支持它的东西,如果他们不支持,那可能是一个坏迹象,老实说,可能不是一个很好的供应商,但是这就是你所需的所有SQL代码,所以这里的好处是,无论你使用我们的技术堆栈,你知道,我们使用Drizzle是因为它很容易使用,但如果你不想使用它,你只需复制所有SQL代码,然后你就有了你的表,在任何Postgres提供商上,这就是你所需要的,所以再次,它有三个部分,你必须启用Vector扩展,好的,有道理,你有一个Postgres数据库,我们想添加那个扩展,很好,另一件事我们需要做的是,在我们创建的表中,我们需要创建那个Vector列,这就是使用PGVector的全部意义,它允许使用PGVector作为列类型,所以我们有这个Vector类型,我们称之为embedding,我们指定我们正在使用256个维度来嵌入,最后但同样重要的是,我们实际上需要创建这个索引,以便我们可以高效地在这个表上执行操作,并开始进行实际检索和余弦相似性以及我们将在下一课中介绍的一些事情,所以一旦你设置好了,我们可以做`npm run db:migrate:basics`,它将应用我们的迁移,现在如果我回到Supabase的表编辑器区域,你会看到我们现在有一个表,那个表叫做documents,我们有所有的列类型,包括这个叫做embedding的列,它的类型是Vector,再次,这是PGVector启用的,这是使用Postgres和PGVector设置的全部意义,这很棒,我们现在有了具有向量数据库功能的Postgres,喜欢它,现在我们可以实际上传信息,好的,嵌入和我们想要的文本到我们的表中,所以你会在第三个文件上传文档中看到,我们将要做的是,我们将把所有这些模拟文档,它们只是一堆假的动物事实,我不知道,这是AI为我生成的,但是我们将要做的就是,我们将把所有这些模拟文档通过这个上传文档函数运行,我们将再次生成一个嵌入,使用与上一课相同的维度和模型,我们得到了那些嵌入,我们将把这些嵌入与内容,文档的名称,也就是有趣的动物事实编号,以及嵌入一起插入,我们将把所有这些上传到我们的数据库,在我们运行这个文件之后,如果我们复制这个路径并输入`tsx <文件名>`然后回车,我们将生成10个嵌入,然后我们将把数据插入到我们的数据库中,所以如果我们回到我们的文件,你会注意到这里,这个操作已经完成,这真的很酷,我们现在有了10个有趣的动物事实,嵌入在我们的向量数据库中,所以你可以看到,如果我稍微展开一点,你会看到我真的无法点击它,因为它有256个数字长,我们存储了大量数据在这里,但是重点是我们嵌入了事实的实际内容,所以你会看到,如果我们回到对OpenAI API的调用,我们嵌入的东西,实际的输入是我们嵌入文档的内容,所以那是实际的事实本身,而不是名字,只是事实本身,所以我们再次做一点回顾,回到嵌入部分,我们把这个内容内容传递给OpenAI嵌入模型,我们得到了每个事实的嵌入,所以每一对都匹配,我们所做的是,我们在这里的Postgres表中设置了一个向量数据库,一个标准的SQL表,我们可以访问这个很棒的向量列类型,因为我们启用了PGVector扩展,我们启用了那个扩展,我们定义了256个维度,所以我们的嵌入与我们的向量数据库期望的数据存储方式相匹配,然后我们现在有了这个构建好的索引,它将允许我们在下一课中实际进行检索,所以这是设置向量数据库的基础,非常简单,非常简洁,代码不多,再次,这是所有SQL代码,它需要一个支持嵌入的表运行,很酷,所以在下一个视频中,我们将进入相似性搜索方面,以及我们如何利用这个设置,现在我们已经完成了所有的设置步骤,我们如何实际应用我们学到的东西来做有用的事情,再补充一点,我想提一下Supabase在他们的文档中有一个AI和向量部分,我认为这是关于向量数据库和向量和嵌入的最好的资源之一,他们有很多很好的资源,所以我只是把它放在视频的最后,只是为了展示一下,如果你想多读一些,他们有很多代码示例,他们有很多关于不同类型索引的深入讨论,我只是使用了最常见的那个,为了保持简单,我们以后可能会讨论,比如生产,如果你需要为规模工程化东西,如果你是那种操作数百万,数亿,甚至数十亿向量的人,你可能想更熟悉其中一些其他的东西,然后我唯一真正想谈的是,我们回顾了一些其他的向量数据库,但我确实想强调,概念上,我们刚刚回顾的所有步骤都将成立,无论你使用什么数据库,所以我们显然使用Postgres和PGVector,我认为这很棒,我认为这是最容易开始的方式,而且我认为它也非常灵活,因为它很容易过渡到其他东西,如果你需要一些更偏向向量数据库的东西,也许你有不同的工作负载,你和你的团队可能需要解决一些不同的权衡,但是,如果我们快速浏览一下Pinecone数据库的快速入门,Pinecone是更受欢迎的,所谓的纯向量数据库之一,你可以看到事情很简单,你生成你的向量,你创建一个索引,你将这些向量插入数据库,然后你执行某种搜索,某种检索步骤,所以这是相同的步骤,唯一改变的是特定的代码,SQL代码将与你期望的任何Pinecone不同,显然它不是一个SQL数据库,它有点自己的东西,但是概念上,所有步骤都是相同的,你获取文本,你生成文本的嵌入,你将文本及其嵌入上传到某种向量存储,在我们的例子中,它是Postgres和PGVector,在这个特定的例子中,它是Pinecone,用他们自己的版本来做事情,然后执行某种搜索,所以概念上,整个循序渐进的过程都是相同的,只是中间的技术实现会有所不同,所以一旦你理解了整个系统的运作方式,很容易在这些提供商之间切换,并找出他们的方式,但是所有的核心步骤都是相同的,所以我只是想在这里最后补充一下,以防对这些其他选项有任何困惑,所以一个很好的练习是,例如,做一个向量数据库,就像我们刚才做的那样,然后选择Pinecone,去尝试用Pinecone做,看看你更喜欢什么,看看成本如何,我得说,这些通常会贵得多,可能效率更高,尤其是在规模上,你将为此付出高昂的代价,再次,所有这些都将是你需要与你的工程团队讨论的,如果你在规模上运作,这显然是一个好问题,恭喜你,这很棒,那么你就可以进行这些讨论,也许咨询一些与不同技术堆栈合作过的人,看看什么适合你,但总的来说,再次,95%以上的人将与我们这里的解决方案配合得很好,我只是想在这里最后补充一下,到目前为止,我们已经完成了所有设置基础架构的麻烦,我们的向量数据库,我们学会了如何嵌入我们的文本,如何将其存储在向量数据库中,所有这些东西,所以让我们开始做一些有用的事情,那就是相似性搜索,所以我们将要介绍如何从你的向量存储中检索嵌入数据,所以什么是相似性搜索,基本上,当我们谈论向量数据库中的搜索时,我们实际上是在尝试比较嵌入,比较那些文本嵌入以找到最语义相似的项目,所以当我们谈论这个例子,关于守卫和软管,以及你知道,如何比较电池和钻头与电池和颜色,这就是相似性搜索的计算发生的地方,所以我们显然需要做的事情,你知道,在这个例子中,我们可以直观地看到事物之间的接近程度,但是当我们实际处理数字形式的嵌入时,我们需要某种操作来执行计算并执行相似性搜索,以便我们能够获得一个实际的计算数字,说明每个嵌入有多相似,所以这就是相似性搜索的用武之地,我们将要展示如何实际使用它,所以我们将要做的是,我们将获取我们查询的嵌入,所以我们将要做的是,我们将获取查询,我们将将其转换为向量嵌入,我们将要做的是,我们将计算查询与我们表中存储的嵌入的余弦相似性,所以我们将要做的是,你将首先获取那些嵌入,在这种情况下,我们只是嵌入查询,然后我们进行相似性计算,Drizzle的一个很棒的地方是,我们开箱即用地得到了余弦距离,所以基本上,余弦相似性函数在,你知道,数学层面上的作用是,你有效地试图通过计算它们之间的角度的余弦来计算向量之间的相似性,再次,我们希望尽可能远离数学的东西,只关注实际应用,但实际上我们所做的是,我们只是试图衡量我们的查询与我们表中存储的嵌入的相似性,所以我们回到我们的SQL表,你知道,如果我们说,你知道,关于犀牛的事实,我们将要做的是,我们将获取,你知道,这个嵌入,然后我们将,你知道,将它与我们为“告诉我关于犀牛”的查询得到的嵌入进行比较,这个不会太相似,因为我们正在谈论火烈鸟,但它会有点相似,因为我们正在谈论动物,所以会有一些相似性,但是如果我们向下滚动到事实六,你会看到,哦,我们直接有一个关于犀牛的事实,所以那个嵌入会非常接近,所以如果我们回到我们的代码,我们将对它们进行排序并返回所有这些文档,然后我们将对它们进行一点感觉,如果我们实际记录下来,什么是最相似的文档,以及在RAG系统中会发生什么,你就是根据相似性分数进行排序,你知道,也许你抓取最相似的五个,等等,你可以看到我们正在按降序排序相似性,所以如果我们做类似的事情,再次,让我们实际使用一个犀牛的例子,告诉我关于犀牛,然后让我们限制为三个,我们实际上可以做的是,如果我们复制文件路径,我们将要做的是,我们将去TSX,我们在这里运行它,你可以看到正在发生的是,我们实际上正在取回文档,所以,所以,在我实际向你展示这些结果之前,让我们再回顾一遍,以使其清晰明了,正在发生的事情是,我们正在运行这个检索文档函数,查询是“告诉我关于犀牛”,所以正在发生的是,我们正在获取那个查询,我们正在为它生成嵌入,我们正在做的是,我们正在计算这个嵌入之间的余弦距离,所以我们查询的嵌入,以及我们表中的所有嵌入,在这种情况下,我们正在计算这10件事之间的相似性,所以正在发生的是,我们正在运行那个查询,我们试图,我们试图找到三个最相关的文档,因为我们使用的是限制为三个,试图找到三个最相关的文档,基于相似性分数,分数越高,越接近一,我们的相似性分数越接近一,越相关,在我们的用例中,我们的事实与犀牛越相似,所以我认为我们的犀牛应该排在前面,所以让我们看看我们的结果,让我们去最上面的那个,你实际上可以看到,这就是一切的开始,这里的所有蓝色部分,我将突出显示它,以便你更容易看到,你可以看到,果然,有趣的事实六,犀牛的角是由毛发制成的,这是最相关的,这是我们表中最重要的相关文档,出于某种原因,我以为鼻涕虫有四个鼻子,等一下,第三个是象,所以你可以看到,有时你会得到意想不到的结果,我直观地认为,也许大象的事实更相似,我会把这两者颠倒过来,但是,你知道,有时你永远不知道嵌入,但是,你看,我们得到的最相似的事实是犀牛的角是由毛发制成的,所以你可以做的一件事是在RAG系统的上下文中,你可以获取那个事实,你可以把它注入到一个提示中,将查询与你得到的事实结合起来,在这种情况下是,伙计们,我在这里太激动了,是犀牛的角是由毛发制成的,然后如果我们回到我们的幻灯片,我们将回到很久以前,回到我们几课前做的RAG概述的上下文中,你知道,我们终于完成了所有这些步骤,我们给了它“告诉我关于犀牛”的查询,我们得到了它的嵌入,我们将其与我们数据库中的文档进行了比较,我们得到了最相关的文档,我们将查询与片段结合起来,然后我们没有实际调用AI模型,我们将在第二部分进行,但是我们已经完成了整个检索过程,我们完成了所有步骤,到目前为止,我们可以调用GPT-4或任何其他模型,以便我们能够得到一个基于我们的小事实列表的答案,所以这显然是一个非常愚蠢的小例子,但是它展示了驱动更强大系统的核心概念,所以一旦你掌握了这个简单的例子,你就可以很快地看到,如果将这个表从愚蠢的、关于一些不同动物的有趣事实切换到什么,如果这里有成千上万个文档,它们都与收益报告有关,如果你想做一个收益报告聊天机器人,而每一个都是收益报告的PDF,你想提取最相关的,比如,嘿,谷歌在2024年第三季度的收益是多少,或者类似的东西,好了,现在你有了构建那个项目所需的所有基础知识,所以显然,我们想开始构建更有用的东西,我们想从头开始构建这些系统,所以这是第一部分的内容,再次,这是所有基础知识,它是嵌入,它是向量数据库,它是相似性搜索,它是学习所有拼图的组成部分如何组合在一起,以便你可以实际构建检索系统,所以在第二部分,我们将从头开始做这件事,我们将学习如何从头开始构建一个端到端的RAG系统,我们将介绍一个与此类似的用例,但是我们将介绍一些更高级的检索技术,你可以用来真正优化你最终得到的结果,在本节的最后,我们将从头开始创建一个完整的端到端RAG系统,所以我们将回顾我们在第一部分所做的所有步骤,但是这次你将和我一起从头开始编写代码,这样你就可以学习如何从一个想法到一个实际构建一个RAG系统,显然你们都在这里,因为你们可能有一个想要执行的想法,到我们完成这一节时,你应该对执行你的想法和构建一个相当高级的检索系统感到非常舒适,我们将介绍很多内容,但首先,我们想做一个快速的介绍性课程,以确保每个人都设置妥当,所以第一步,我们只是想确保你拥有本节的代码,所以如果我们回到光标,你可能已经或可能没有你的代码,所以如果你回到上一节,我们有代码设置课程,确保你到达拥有RAG课程代码存储库的这一点,所有下载的,我们想做的是,因为你们中的一些人可能,事实上,大多数人可能已经设置好了,我们想确保你拉取最新的代码,因为我们将继续更新它,以反映最新的变化,所以如果你去你的终端,你会看到我在这里,就在这个项目存储库的最顶层,我们将做一个小的`git pull origin main`,然后回车,你应该能够获取最新的代码,如果由于某种原因它没有获取,你总是可以直接去GitHub存储库,它将有链接给你下载zip文件并获取第二部分的代码,确保你拥有它,你将需要去左边,在你的文件导航器中,点击sections,并确保你拥有reference code文件夹,它有我们将要编写的所有参考代码,然后你拥有your code文件夹,所以你将需要确保你拥有这个`create-a-rag-system`文件夹,以及所有这些好东西,因为这就是我们将要工作的,我认为这一节将是10到12课之间,所以我们将,我们将做很多事情,所以确保你拥有代码,以便你可以跟进,好的,所以我们接下来想做的是,我们想谈谈我们在这里将要构建的东西,所以在本节中,我们将借鉴上一节的主题,我们将继续使用愚蠢的动物事实的例子,我知道它很简单,所以我们为什么要使用如此简单的例子,为什么不使用一些更有趣的东西,所以这里的目标是,我们想从头开始介绍所有设置步骤和核心概念,所以这些是学习如何编写生成嵌入的代码,设置你的向量DB,做文档重排序之类的事情,我发现,如果你试图将我们将在本节中介绍的所有内容与一个非常高级的概念结合起来,人们可能会有点迷失,第一次做这件事时,最好将数据范围缩小,所以我们将继续使用一个非常简单的数据集,它也有帮助,因为这样你就不用处理数据集了,所以我们已经做了,你会看到我们有一个`mock-data.ts`文件,我们有一个巨大的数组,大约有100个不同的动物事实,我猜我们有大约20种不同的动物,每种有五种事实,你会看到我们有一个大象维基的东西,我们稍后也会用到它,重点是,一旦你学会了所有这些不同的组成部分如何与一个非常简单的小例子一起工作,很容易就增加你正在处理的数据的复杂性,所以重点在于具体的数据集,而更多的是实际的概念,我们将在下一节中扩展复杂性,当我们实际构建一个具有更高级数据集的真实世界项目时,但这就是为什么我们将坚持使用那个简单的例子,所以我们将在本节中学到什么,所以正如我提到的,这将是一个充满课程的章节,我们将学习如何从头开始设置向量DB,基本的检索技术,我们将做一些RAG提示,查询优化,文档重排序,元数据过滤,这很有用,然后我们还将简要地触及文本分割,我们将在第三节中更深入地讨论文档分割等等,但这都是一些我们将要介绍的内容,这些都是非常核心的概念,你将想了解它们是如何工作的,如何从头开始构建它们,编写代码,并将所有这些部分组合在一起,以便你可以实际构建RAG系统,这些非常普遍,我们已经挑选了我们认为你需要知道的所有主要概念,以便构建最好的检索系统,所以这就是我们将要学习的,再快速说几点设置说明,你可能已经有了OpenAI密钥,从上一节开始,但如果你没有,你需要确保你去了OpenAI的开发者平台,并从他们那里获取一个OpenAI密钥,因为你需要它,所以我实际上在我的代码中,我们需要做的是,实际上,这是上一节,我想让你跟着我做的每一件事,所以目标是我们实际上想编写代码并做到这一点,所以你会看到在你的代码文件中,作为快速说明,参考文件夹是为了存放所有参考代码,所以如果我点击这个`optimized-query`文件,它包含了我们将要编写的函数,我们将从头开始做,但是我们有每个都写好了,所以如果你想要完整的代码,你可以参考它,你可以查看,我们甚至有注释来解释一些周围的事情,这就是这个,所以你可以运行任何你想要的东西,这是我们将在本节中做的完整代码,然后your code文件夹是我希望你跟着我一起做的,所以这就是我们将如何进行本节,所以我们要做的第一件事是创建一个名为`.env.local`的新文件,在那里,所以你想做的是进入`.env.example`,复制所有这些值,粘贴到`.env.local`中,你应该看到它被灰显了,所以我们不会把我们的API密钥提交到Git或其他任何东西,你想在那里放你的OpenAI密钥,你想把你的数据库URL放在这里,你的Cohere API密钥放在这里,我们稍后会处理另外两个,所以确保你在这里获取你的OpenAI密钥,并粘贴这个值,这样它就可以准备好了,我们也将使用Supabase,就像我们在上一节中讨论基础设施方面一样,所以我们将使用它

再次,所以请确保如果您没有跟上该部分的代码,您实际上会花时间再次设置一个 Supabase 帐户。这是免费的,它们有一个免费套餐供您使用,这对于我们在这门课程中要做的所有工作来说已经足够了,这很好。我们将在接下来的部分中处理实际的密钥,届时我们将设置向量数据库。您现在可以跳过此数据库 URL。然后,您需要做的第三件事,这有点可选,但我推荐它,就是注册 Cohere API。所以,如果您注册了 Cohere API,它会带您到一个仪表板,如果您在那里点击 API 密钥,您将能够创建一个 API 密钥。我相信他们允许您免费使用一些,所以我相信您实际上不需要为 Cohere 花钱。如果需要,它的使用量是基于的,与 OpenAI API 的工作方式非常相似,所以您不会花费很多钱。我们在这里要做的所有事情都非常便宜。所以,一旦您有了它,您就需要在这里粘贴您的 API 密钥,无论它是什么。好的,我们显然会私下处理我的,以确保安全,但这些将是我们需要的三个值。我们将处理数据库 URL 和向量数据库课程,所以暂时不用担心。我只是想确保您拥有它。所以,一旦您拥有了它,您将拥有您的 OpenAI 帐户、您的 Supabase 帐户,以及可选的您的 Cohere API。我们将将其用于文档重新排序部分,它还为您提供了额外的灵活性,如果您想尝试另一个嵌入模型,如果您不想使用 OpenAI。所以它只是为您提供了一点额外的灵活性。但是一旦您有了它,请回来,因为我们将介绍如何从头开始设置向量数据库。让我们实际设置好我们的向量数据库。所以,您应该对我们为什么需要执行此设置步骤有一个基本的了解。所以,再次,设置向量数据库允许我们使用向量嵌入来存储我们从 OpenAI API 生成的所有数字,当我们获取所有这些嵌入时,并允许我们实际对其执行操作。好的,让我们实际设置好这个。所以,我们将在这三个技术组件中使用我们的向量数据库堆栈。我们将继续使用 PostgreSQL 和 PGVector。再次,我们只是想在这里使用经典的 PostgreSQL 来处理我们喜欢 SQL 数据库的所有方面,我们将使用 PGVector 扩展,以便我们支持向量嵌入。因为 PostgreSQL 目前不自带此功能。我们将使用 Supabase 来轻松设置 PostgreSQL 数据库。所以,这将非常容易使用。再次,它也将免费提供给你们所有人,这非常方便。最后但同样重要的是,我们将在我们的代码中使用一个 ORM,称为 Drizzle。它是一种非常简单的方式,特别是当您使用 TypeScript 时,可以轻松地管理数据模式和迁移文件,并使您的代码保持类型安全。Drizzle 还附带一些非常好的实用函数,用于计算余弦相似度等。再次,我们在上一节中对此进行了一些介绍,但这将构成我们文本堆栈的核心三个部分。所以,让我们实际开始编写代码。我们将需要做很多不同的事情。所以,您首先要确保您在这里做的事情是,我们将实际导航到我们的终端。所以,无论您使用的是操作系统上的原生终端,还是 Cursor 中的终端,我将使用后者,我们需要做的是,我们实际上需要导航到“创建 RAG 系统”部分内的代码。所以,让我们进行 CD(更改目录),然后我们将进入“sections”,然后进入“create a rag system”,然后我们将进入您的代码。可选地,如果您愿意,您也可以跟随我。我将提前向您展示其中一些将如何工作。我可能会使用一些参考代码。所以,我将再次点击这个加号。您不一定需要这样做,但我只是这样做,因为它有助于教学。我将直接进入参考代码终端。太棒了。所以,我将点击回到您的代码终端。我们将开始在这里工作。所以,在我们开始编写代码之前,让我们实际设置好我们的 Supabase 项目,以便我们实际上可以使用我在上一课中提到的数据库 URL。所以,我们将要做的是,我们将转到 Supabase。我将点击我的仪表板。您将需要创建一个新项目。我们,让我们看看,您将需要为它选择一个组织。所以我将选择“takeoff projects”。我将称之为“animal facts”,因为这就是我们将要做的。我将生成一个密码,因为 Supabase 有一个很好的算法来安全地生成它们。我将复制该值。我将跳回这个 `.env.example` 文件中,我将暂时将其保存在这里。请稍等片刻,因为我们将一起完成这个过程。再次,您应该能够以微型套餐或免费套餐创建项目。我相信 Supabase 允许您免费创建最多两个项目。所以,如果您已经有两个,您可能需要删除一个。但是,您将需要创建一个新项目。这需要一两分钟才能设置好。所以,一旦这个项目设置好,我就会回来。现在我们的项目已经设置好了,我们将转到下面的项目设置。您需要做的是,您需要转到左侧边栏中的数据库。数据库,您将获取您的连接字符串。所以,这将是我们连接所需的数据库 URL。所以,您将需要复制该值。我们实际上要做的是,我们将粘贴在这里,我们将获取我们的数据库密码。您实际上需要做的是,看看括号中的“your password”。您将需要在这里粘贴您的密码。然后,您需要做的就是剪切它,然后转到 `.env.local`。在您的数据库 URL 中,您将需要粘贴完整的该值。我只是做个例子,这样我就不会暴露我其他的密钥了。我现在实际上必须更改我的密码,因为我向你们展示了。但是,您只需要确保在您的 `.env.example` 文件中,而不是在您的 `.env.local` 文件中,您拥有您的 OpenAI 密钥、您的数据库 URL(其中密码已替换)以及您的 Cohere API。所以,一旦您拥有了这三样东西,您就准备好了。所以我将快速更新一下,重置我的密码,粘贴它,我马上回来。现在我已经成功地将我的所有密钥输入到 `.env.local` 文件中,我们可以开始工作,并编写我们的 Drizzle 代码,以便我们实际上可以编写我们需要的 TypeScript 代码来生成我们的迁移,我们的数据库迁移,并实际迁移它,以便它显示在我们的 Supabase 项目中。所以,您首先需要做的是,再次,确保您在正确的目录中非常重要。再次,您现在需要导航到您的 `.env.local` 文件夹。否则,您将遇到一些问题,并可能遇到一些恼人的调试错误。但是,您首先要做的是,我实际上已经花时间设置了我们在此处所需的所有脚本和包。您只需要使用 `npm install` 来安装它们。那就是 `npm install`。这将安装我们将在本节中使用的所有不同的 SDK。一旦您完成了,我们实际上要做的第一件事是,在您的代码文件夹中,我们将创建一个新文件夹,我们将其称为 `db`。然后,在 `db` 文件夹中,我们将创建另一个名为 `schema` 的文件夹。这就是我们的模式将要存储的地方。在 `schema` 文件夹中,我们将创建一个,我们称之为 `fa-schema`。我相信我们在参考代码中称之为它,所以我们将保持一致,这样对你们来说更容易理解。然后,在 `db` 文件夹中,您不在 `schema` 文件夹中,您只需要创建另一个名为 `index.ts` 的文件。所以,这个结构应该是您的 `database` 文件夹。该文件夹应该有一个名为 `index.ts` 的文件,一个名为 `schema` 的文件夹,以及在该 `schema` 文件夹中有一个名为 `fa-schema.ts` 的文件。我们需要在您的代码中再创建一个文件,而不是在数据库文件夹中。我们将称之为 `drizzle.config.ts`。我们将设置我们需要 Drizzle 设置的配置。再次,我们将使用 Drizzle,因为我们开箱即用地获得了一套非常好的实用工具,例如类型安全,因为我们正在使用 TypeScript,一种非常简单的方式来管理迁移,而且我们在 Takeoff 上到处都使用它,所以我们将保持一致。所以,让我们实际设置它。所以,我们将需要做的第一件事,再次,任何时候您想引用任何这些文件的完整代码,只需点击参考代码文件夹。例如,这是 `drizzle.config` 文件的参考代码。您实际上可以将其复制粘贴到这里,您就准备好了。我们将从头开始做,这样我就可以谈论每一行,这样您就能理解发生了什么。再次,我们在上一节中做了无代码的东西。本节的目标是编写一些代码。所以,要在这里设置 Drizzle,首先,我们将导入我们的配置。您会看到我得到了一些灰色的填充文本,这是 Cursor 的一个功能。如果您不熟悉 Cursor,我建议您查看我们在 Takeoff 上提供的 Cursor 课程,因为我们将在其中使用 Cursor 的一些功能,我强烈推荐。所以,我将点击 Tab,它将填充。我们还将从 `drizzle-kit` 导入 `defineConfig`。我们将一行一行地进行,我不会过度打扰你们。然后,在顶部,我们将设置我们的环境配置。所以,我们只需要指定一个名为 `path` 的参数,它将定义我们正在从我们之前设置的 `.env.local` 文件中拉取我们的环境变量。所以,现在一切都准备好了。我们所要做的就是设置我们的 Drizzle 配置。这将设置一些不同的东西。所以,我们首先要做的就是指定一个方言。在这种情况下,我们使用的是 PostgreSQL 数据库,所以我们将指定 PostgreSQL。如果您想使用 Drizzle 和 MySQL 等数据库,您也可以这样做。MySQL 是一个很棒的数据库。但是,我们将在本课程中坚持使用 PostgreSQL,因为这是我们在 Takeoff 上最喜欢使用的。所以,我们还需要在这里添加数据库凭据。所以,这将是您在 Supabase 上辛苦获取的 URL。所以,我们将在这里导入那个环境变量。很棒。我们必须指定两种文件输出。第一个是模式,我们的模式文件在哪里?它将在数据库文件夹中,在那个模式文件夹中。所以,这就是我们的模式所在。这很好。Drizzle 将自动获取我们在此文件夹中使用的所有文件。然后,我们还想指定我们要将迁移输出到哪里。所以,我们只是说我们希望它在 `db/migrations` 中。所以,您的代码应该看起来完全像这样。再次,这将与您在参考文件中看到的内容完全匹配。您会看到,哦,这很好,我实际上得到了完全相同的,很高兴。不需要编辑。但是,再次,您可以查看这些参考文件中的每一个,当我们实时进行时。好的,我们已经设置好了 Drizzle 文件。让我们实际进入 `fa-schema` 文件,因为我们现在需要的是,如果我们回到 Supabase 并点击我们的表编辑器,我们没有任何表,这是问题所在。我们需要设置支持向量列的表,以便我们实际上可以存储我们即将处理的所有嵌入。所以,我们将在这里做的是,我们将导出 `const factsTable`。所以,我们正在创建一个名为 `factsTable` 的变量,它将存储我们的表。我们将称之为,就称它为 `facts`,保持简单。我们现在需要做的是,我们实际上必须指定我们将要使用的所有不同的列。所以,首先,我们将使用一个非常基本的序列化 ID 列作为此表的主键。我们不会深入探讨 SQL 的细微差别。如果您想了解更多关于 SQL、PostgreSQL 和后端系统的信息,我们在 Takeoff 上有我们的应用程序构建课程,它提供了更深入的探讨,如果您对此感到困惑。再次,如果您对任何事情感到困惑,请将其放入这里的 Cursor 聊天中,放入 ChatGPT,放入 Cloud,无论您最喜欢的工具是什么,然后复制粘贴代码并提问。如果您需要放慢速度,放慢速度,不用着急,按照自己的节奏学习,这是 Takeoff 的优点。您可以随时暂停。我们录制视频是有原因的。我们不想实时进行,因为那样,您,您很难按照自己的速度工作。但是,我们将创建一个名为 `name` 的列。我们将使用文本数据类型。这应该是一个非空列,我们不希望它是空的。这里的 `content` 也将是一个文本列。我们将称之为 `content`,这也不应该是空的。有趣的地方来了。所以,我们将创建一个名为 `embedding` 的列。这将是向量列类型。所以,这是由 PGVector 扩展启用的列类型。我们将称之为 `embedding` 列,我们只需要指定我们将要使用的嵌入的维度。在这种情况下,我们将使用 256。我稍后会详细介绍维度大小。最后但同样重要的是,我们将做一些非常基本的元数据列,我几乎总是喜欢添加的,那就是 `created_at` 和 `updated_at`。它们将默认。这是我们喜欢看到的。所以,我们最后需要做的是,当您处理使用向量列的表时,您将需要在此列上创建索引。所以,我们将创建一个 HNSW 索引。它将为您节省细节,以便我们能够开始。它只是我们检索系统将要使用的操作的最佳实践索引。所以,我们将称之为 `embedding_index`,然后指定我们的索引类型。太棒了。然后,我们还需要指定我们希望在此索引上允许的操作,即我们想要 `VectorOps`。所以,这将允许我们执行所有我们将在检索步骤中需要执行的相似性搜索。所以,这实际上是我们所需的一切。您会看到我导入了一些东西。我使用自动导入系统。所以,我们必须确保所有这些都从 `pgcore` 导入。所以,再次,如果我们回到参考代码,点击 `fa-schema` 文件,您会看到它。哦,这是一个很好的提醒,这就是参考代码如此有用的原因。我实际上忘记了在这里进行类型声明。所以,让我们回到文件中。我们还需要导出这两个类型 `InsertFacts` 和 `SelectFacts`,因为这将使我们能够使用我们非常喜欢的类型安全。所以,在这一点上,我们已经构建了 `factsTable`。我们支持向量嵌入,维度为 256。再次,如果您观看了上一节,否则您将有点迷失,维度只是对应于我们从嵌入模型返回的向量中有多少个数字。所以,最后,让我们实际进入 `index.ts` 文件。再次,它将位于 `db` 文件夹中。让我们关闭 `db` 文件夹中的参考代码。这将定义我们实际数据库的一些内容。所以,我们将需要导入,我将稍微加快速度,因为这主要是样板代码,不是您通常需要过多担心的东西。这在您的不同项目之间,大部分将是相同的。所以,我们将要做的是,我们将指定我们的 `dbSchema`,以便我们可以使用 Drizzle 查询语言,这有时非常方便。它允许您使用非常好的 Drizzle API 来执行查询、选择和插入等操作,这样您就不必编写原始 SQL,这非常棒,特别是如果您是经常使用 TypeScript 的人,我们绝对在 Takeoff 上一直这样做。然后,我们只需要初始化我们的 DB。使用一些参数。我们需要创建一个客户端,它将是一个 PostgreSQL 客户端,我们从几分钟前安装的 PostgreSQL 包中导入。我们将使用 `prepare: false`。这与 Supabase 的工作方式有关。然后,我们将返回 Drizzle 的一个实例,其中包含该客户端和我们指定的模式,其中包含我们的表。再次,如果您真的想了解更多关于后端内容以及 Drizzle 和 PostgreSQL 的工作原理,请观看我们的应用程序构建课程。我们在那里有关于后端开发的所有这些部分,它涵盖了这些内容更深入。但这对现在来说已经足够了。所以,只要您有这个 `drizzle.config` 文件,这个 `index` 文件在您的 `db` 文件夹中,以及您的 `fa-schema`,以便我们实际上可以找到支持嵌入的表。非常快速地,各位,我犯了一个小错别字,我只想确保您在继续之前进行更改。我不小心写了 `consignOps`,所以我有一个 `consign`。确保它只是 `cosineOps`。再次,这个文件应该直接与您在参考文件中找到的文件匹配。所以,如果您想确保它正常工作,您甚至可以复制粘贴它。但是,一旦您有了 `fa-schema`,`index.ts` 文件在这里配置,以及您的 `drizzle.config`,我们应该能够运行 `npm run db:generate`。这将生成我们所需的 SQL 迁移文件。您可以看到我正在查看参考代码。有一件事要确保您注意,不要混合参考代码和您的代码。所以我刚才不小心点击了错误的文件。所以,如果我向下点击,您会看到我们新生成的 SQL 文件。我们需要做的是,Drizzle 的一个很酷之处在于,这就是我们使用它的原因。Drizzle 正在使用我们在这里编写的 TypeScript 代码。它允许跨项目进行类型安全,并生成适当的 SQL。但是,有一件事我们需要做,再次,因为我们正在使用 PGVector,我们实际上需要执行 `CREATE EXTENSION IF NOT EXISTS vector`。因为这将解锁我们在 PostgreSQL 中使用向量列类型的能力。所以,如果您实际上没有将此行添加到您刚刚获得的生成 SQL 文件的顶部,您将无法获得嵌入支持,这将破坏一切。所以,一旦您生成了该 SQL 文件并在此顶部进行了修改,我们就可以运行 `npm run db:migrate`。当您运行 `npm run db:migrate` 时,假设您已正确地将连接字符串和密码输入到 `.env.local` 文件中,您将能够看到成功消息。如果我们导航回我们的 Supabase 数据库,您会看到我们现在有一个名为 `facts` 的表。我们有所有这些列类型,包括我们命名为 `embedding` 的向量列类型。所以,您现在应该准备好了。此时,您的向量数据库已 100% 设置完毕,我们已准备好构建此 RAG 系统。让我们直接开始使用 OpenAI API 生成嵌入。所以,您应该已经设置好了您的 API 密钥,并输入到 `.env.local` 文件中,因为您需要它才能继续。所以,我们将分两步进行。第一步是,我们将设置一个小的客户端文件,然后我们将创建一个生成嵌入的文件。作为关于我如何设置参考代码文件夹结构的快速说明,我添加了数字前缀,以便更容易按顺序理解,我们将在您的代码中也这样做。所以,我们要做的第一件事是,我们将创建一个名为 `0-api-clients.ts` 的文件。我们将在这里做的就是,我们将使用我们在此部分开始时安装的 SDK 设置 OpenAI 和 Cohere 客户端,以便我们可以在所有我们将要使用它们的不同文件中使用它们,因为我们将在这里大量使用它们。所以,让我们开始设置。所以,我们将需要设置配置。再次,这是您在编写更多代码时会非常熟悉的。基本上,我们将再次,我设置了自动导入,所以您可能需要自己输入它们。您会看到我将继续使用 Cursor Tab 来加快速度。但是,基本上,我们在这里做的是,我们正在配置我们的环境变量的来源,这很重要,因为我们需要 OpenAI API 和 Cohere API 的 API 密钥,我们将在下面处理。您会看到这实际上有点幻觉。实际上,让我们检查一下参考文件。我不记得那是否是正确的包。好的,看起来是这样。我们只需要确保我们获取了 Cohere 客户端的导入。所以,您将拥有 OpenAI 客户端设置为一个变量,您可以在本节中访问,以及 Cohere 客户端,我们也可以访问。再次,OpenAI 使用该 API 密钥语法,Cohere 使用令牌。所以,我们将确保这些 API 密钥正确地从我们 `.env.local` 文件中导入到此文件夹中。一旦您设置好了这些 API 客户端,我们将创建一个另一个文件,我们称之为 `1-generate-embeddings.ts`。这就是我们将开始编写代码的地方,以便我们可以获取我们的文本,对其进行嵌入,然后在下一节中,我们将能够获取嵌入运行,并且我们可以让数据填充到我们制作的 SQL 表中。所以,我们将使用 OpenAI 的嵌入模型。我们将使用它们的 Text Embedding 3 系列模型。所以,OpenAI 有很好的资源。我们已经在上一节中引用了这一点。您可以查看他们所有的很棒的文档,其中包含所有不同的示例和 API 参考,如果您想直接查看一些代码。但是,我们在这里要做的就是,我们实际上要编写我们需要生成的嵌入的代码。所以,我们将要做的是,我们将在这里编写一个名为 `generateEmbeddings` 的函数。我们将经常在所有这些文件中使用它。这个函数将接受一个名为 `text` 的单个参数。您可以称之为 `data`,您可以称之为 `documents`。但这将是我们传递给的字符串数组。您会看到我这里有一个错别字。我们将传递给 API 的字符串数组。所以,让我删除这段代码。实际上,Cursor Tab 输入了它。好的。所以,我们有这个名为 `generateEmbeddings` 的单个函数。再次,您将需要导出它,以便我们可以在即将到来的所有不同文件中使用它。然后,我们有这个单个 `text` 参数。所以,在里面,我们将创建一个名为 `response` 的新变量,我们将将其保存到 OpenAI 嵌入响应中。所以,我们将访问那个 OpenAI 客户端。您会看到我们得到了自动导入,这很好。这是 Cursor Tab 的一个功能。再次,Cursor 是最好的,我非常喜欢 Cursor。但是,我们将要做的是,我们将使用嵌入端点。我们将使用,让我们看看,创建。我有点凭记忆在做这件事。抱歉,如果我犯了几个错别字。但是,我们将在这里使用,我稍后会详细介绍这些参数。我们将再次使用 Text Embedding 3 系列模型。我们将使用较小的模型,所以它更便宜。我们将使用 256 维。再次,我们设置了,这就是我们在 SQL 表中设置的。所以,这些值确实需要匹配。您不能在这里改变主意。如果您改变主意,您将不得不重新做。否则,您将遇到很多错误。然后,作为我们输入到模型中的内容,我们想传入文本数组。所以,它将嵌入所有这些,我们给它的各种字符串数组。它将获取嵌入。然后,我们想从响应中获取 `response.data`。我们将映射这些数据,对于数组中的每个项,我们将返回该项及其嵌入。现在,我将再次导入它。您将需要确保直接从这个 API 客户端文件导入它。确保直接从这个文件夹中的那个导入。如果您使用自动导入系统,它可能会意外地从参考代码中导入。所以,请确保,再次,我们只是使用我们的文件输入,或者我们的目录结构,它正在进入上层目录。我们正在重新使用同一目录中的文件。抱歉,各位,我已经录制了好几个小时了。我有点开始失去理智了。但是,我们正在导入我们在上一步中构建的 OpenAI 客户端。我们在这里使用它。我们正在保存 OpenAI 嵌入 API 的响应。我们正在使用 Text Embedding 3 Small 模型,使用 256 维。所有这些嵌入,我们正在传入我们的文本。然后,我们只是返回嵌入。所以,为什么我们要使用这个模型,为什么我们要使用这个维度大小?所以,让我们最后来谈谈这个问题。所以,OpenAI 在 Text Embedding 3 系列中有两个嵌入模型。它们有 Small 和 Large。Small 是更快、更小的模型,这就是为什么它叫 Small。而 Large 模型更大,但更慢,更贵。所以,两者之间的成本差异是,Small 模型的使用成本将是 Large 模型的大约六倍多。使用 Large 嵌入模型的优点是,当您执行检索步骤时,Large 模型实际上会更准确。所以,您基本上会获得更多的数据编码。我正在尝试稍微简化一下,所以一些技术人员会有点生气。但基本上,可以这样想:Small 模型不如 Large 模型好,但它便宜得多。它仍然非常好,只是为了清楚起见。当我说的“不如 Large 模型好”时,这是比较而言。它仍然是一个非常好的模型。Text Embedding 3 Large 模型更好,但它会贵得多。所以,如果准确性对您正在做的事情至关重要,您想要绝对最好的,您将需要使用 Large 模型。也许您愿意支付差价,那很好,使用 Large。我们将在本视频中使用 Small,因为我们希望确保对您来说尽可能便宜。而且,我们不需要担心结果。另外,关于维度,维度越小,您实际上可以看到 OpenAI 文档中的范围。您会看到,这可能需要您去查看嵌入文档。我不记得维度上限是多少。好的,看起来是这样。您可以在文档中看到。再次,这就是为什么您要阅读文档,它们有很多好信息。但是,Small 的默认值是 1536,Large 的是 3072。我不记得上限是多少。但是,您可以查看我为您提供的链接,如果您想了解更多细节。但是,当您使用向量数据库时,维度越大,您存储的数据就越多。所以,您需要,您正在使用数据库上的更多资源,这将更昂贵,它将更慢。所以,如果您使用最小值 256,您基本上是在优化您的数据库,使其在规模上更便宜,显着更便宜。所以,如果您正在构建一个检索系统,它将处理相当大的规模,可能有大量的用户,大量的向量嵌入,您正在处理,只是要知道,维度计数越高,它将真正影响您在向量数据库上的资源。所以,例如,当我们设置 Supabase 项目时,我们有那个微型规模,我们有那种最小的计算机,只是为了让事情简单化。那个数据库正在运行。如果您使用非常高的维度计数,您通常需要更多的内存。您的数据库以及运行它的任何服务器。所以,您将需要为那种系统支付更多费用。所以,这就是为什么我们使用 256 并使用 Small,因为这样我们就不用担心这些问题了。而且,我们仍然可以掌握所有核心概念。我知道这有点技术性,可能比某些人需要的信息要多一些。但我只是想解决这个问题。所以,如果您真的想扩展这些系统,显然,您需要与人们交谈,并可能就这些决定进行一些咨询。而且,显然,您可以使用很多不同的嵌入模型。我们只是在这里使用 OpenAI 的一个,以保持简单。所以,如果我们实际测试一下,我们将要做的是,我们将创建一个小的异步函数。我们将称之为 `main`。再次,我可能需要休息一下打字了,我遇到了一些麻烦。我们将创建一个名为 `main` 的函数,我们将运行它,以确保一切都正常。我们将要做的是,我们将生成嵌入。我们将只测试一个嵌入。我们将说“Hello world”。所以,我们将嵌入这个文本并得到结果。我们将记录下来,然后我们将在这个文件的最底部调用这个 `main` 函数。所以,我们可以运行它的方法是,右键单击 `generateEmbeddings`。我们可以复制路径。如果我输入 `tsx` 然后是文件名,它将运行该文件。假设您已经正确设置了您的 API 密钥,并且编写了所有这些代码,您应该会得到所有这些数字。再次,这是“Hello world”文本的向量嵌入。所以,这是 `generateEmbeddings` 函数。在这一点上,您已经了解了其中一些模型和维度是什么,它们为什么重要。我们编写了这个函数,以便我们可以在接下来的部分中使用它,因为我们将生成大量的嵌入。然后,我们验证了它有效。所以,我们将在此基础上进行构建。所以,请确保您不要跳过任何一个,因为每个都将建立在下一个之上,直到我们到达最后。在您继续下一课之前,有一个快速说明:您将需要注释掉它,这样我们导入它时就不会运行此代码。您会在参考文件中看到,我说“取消注释以进行测试,然后再次注释”。所以,想法是,如果您想测试文件,您可以像下面那样运行它,使用 `tsx`,也就是 TypeScript 执行。然后,一旦您完成,您只需要再次注释掉。再次,我们有所有参考文件,这样您就可以确保您的代码与我们为您提供的示例匹配,这样您就可以确保事情正常运行。我们显然已经验证了所有文件都有效。但是,让我们继续。我们现在将开始上传和嵌入您的数据,并将其放入您的向量数据库。让我们实际开始处理我们的模拟数据,并嵌入所有这些信息,所有这些事实,并将其插入到我们的向量数据库中。所以,我们现在需要做的是,我们实际上需要构建一个新文件,我们将在这里重用 `generateEmbeddings` 函数。所以,我们将创建一个新文件。我们称之为 `upload-data.ts`。我将继续用数字前缀,以便保持整洁有序。所以,我们需要做的是,我们需要获取所有数据。所以,这些事实中的每一个。您可以看到这是一个包含 100 个对象的数组。这些对象中的每一个都有两个属性。我们有一个 `content` 属性,这是我们要嵌入的事实。然后,我们还有一个 `name` 属性,这是它对应的动物的名称。当我们到达元数据过滤部分时,我们将使用它。所以,让我们回到我们的 `upload-data` 文件。所以,我们将继续,所有这些函数都非常相似。我们将创建许多异步函数,我们将导出它们。再次,我们将在此基础上进行构建,并在未来的课程中使用我们编写的每个函数。所以,再次,我们可以称这个函数为任何我们想要的。我猜我们在参考文件中称它为 `docs`,所以我将保持一致。`docs` 将是一个对象,它有一个字符串和一个 `name`,它也是一个字符串。这将是一个数组。太棒了。所以,这个 `uploadData` 函数接受一个参数,它只是一个文档数组,匹配我们在这个模拟数据数组中使用的 `dataType`。很好。所以,我们最终将调用这个函数。所以,我们将要做的是,另一个异步 `main`。再次,您将在运行后注释掉它。但我们将在本课的最后进行。最终,我们将用这个模拟数据运行 `uploadData` 函数。您只需要导入那个模拟数据数组。所以,一旦我们为这个函数编写了代码,我们将运行这个文件,它将把所有数据插入到我们的数据库中,并带有嵌入。所以,话虽如此,我们实际上需要生成,对,嵌入。所以,我们将创建一个名为 `embeddings` 的新变量。我们将将其保存到我们在这里使用的 `generateEmbeddings` 函数的结果中。我们只需要映射我们的 `docs`。我们需要,让我们看看,`doc` 到 `doc.content`。我们实际上将把它从 `docs` 改为 `doc`。再次,我们这样做的原因是,在我们的 `generateEmbeddings` 函数中,请记住,我们接受一个名为 `text` 的字符串数组。所以,在这里,我们只是保持数据类型与我们拥有的模拟数据一致。但是,我们实际上只在 `generateEmbeddings` 函数中使用 `content` 属性,因为事实本身是我们真正想要嵌入的。嵌入动物的名称对我们没有多大好处。所有这些嵌入都会相同。所以,我们想嵌入事实,它在 `content` 属性中。所以,我们将运行 `uploadData`。它将生成所有这些事实的嵌入。再次,我们只是将其传递给下面的函数。它将把嵌入保存到这个 `embeddings` 变量中。然后,我们需要将所有这些数据实际插入。所以,我们将要做的是,我们将做 `db.insert`。我们将插入到 `facts` 表中。然后,值。所以,我们将做 `values`。这将允许我们定义值。我们想要做的是,我们想要获取从 OpenAI 响应中返回的对象。再次,它将包含我们所需的所有嵌入。我们将想要做两件事。我们想要获取每一个嵌入,然后我们还想要获取索引,因为我们需要使用索引来获取这些 `docs` 的信息。我们将要做的是,我们将返回一个对象,其中 `embedding` 是一个属性。它来自 OpenAI API。这是通过该模型发送的整个目的。然后,我们将获取每个索引处的 `doc` 及其 `name`。然后,我们将获取每个索引处的 `doc` 及其 `content`。所以,我们在这里做的步骤。当然,我们需要从我们的数据库设置中导入 `db`。它来自 `index.ts` 文件。记住,我们在上一个课程中导出了那个 `db` 和我们做的小设置。所以,让我们回顾一下即将发生的事情,然后我们将实际运行代码。所以,我们将调用 `uploadData` 函数。我们将传入我们所有的模拟数据。再次,它们只是那些带有 `content` 和 `name` 的事实。有 100 个。我们将生成 `generateEmbeddings` 函数。这是我们在上一课中做的。我们将上传数据到数据库,以及它的 `name` 和 `content`。所以,我们将。

正在添加这三个不同的属性并将它们插入到我们的数据库中,所以我们能做的就是再次复制这里的路径 TSX 粘贴文件路径,你应该会得到,我们当然需要等一秒钟,因为这是一个异步操作,它将上传我们的数据,你在这里可以做的是,我们去我们的数据库,在这里刷新,你会注意到,砰,我们现在在我们的数据库中有 100 条记录,你会注意到几件事,我们有自动生成的序列 ID,我们有名字,哦,你知道吗,我实际上在这里犯了一个错误,嗯,让我们回去看看,你会看到在内容上我做了名字而不是内容,所以也许这实际上是有帮助的,以防有人做错了,嗯,所以让我们在这里进行更改并保存它,你实际上可以做的是,如果你点击这个按钮,这是 subase 的一个很棒的地方,它们不仅让设置 postrest 数据库变得非常容易,而且它们还有各种各样非常好的图形用户界面,用于处理你的数据,所以我们可以做的就是选择所有这些行,嗯,我们将删除它们,嗯,希望你注意到了拼写错误,或者我之前运行这个时那个小小的错误,嗯,但如果我在 Mac 上按 Ctrl+C,嗯,我们可以再次运行它,这次我们将真正上传我们的内容,所以我们应该希望,嗯,再次,我将在这里刷新,嗯,你会看到,因为,嗯,嗯,我们的序列 ID 从 100 开始而不是从 101 开始而不是从 1,它们会有点不同,但你可以看到我们有名字,我们有内容,我们有那个嵌入存储在我们从 PG Vector 获得的那个漂亮的向量列类型中,然后当然,由于我们之前设置了模式,我们自动获得了创建时间和更新时间,好的,你可以看到,嗯,我们得到了默认值,所以当我们插入时,它将默认为“现在”值,所以在这个时候,我们已经嵌入了我们所有的事实,所有 100 个事实,你可以看到我们有 100 条记录,我们已经正确地完成了嵌入,只是为了验证我在这里使用了内容而不是名字,就像我在这里犯的错误一样,一切看起来都准备就绪了,我们最后要做的是将它注释掉,这样它就不会在将来运行,所以我们现在已经上传了我们的数据,我们已经生成了所有这些嵌入,我们现在可以开始做下一步,也就是做一个非常基本的检索,所以让我们继续在下一步处理基本的检索,好的,我们将做一个最基本、最常见的检索类型,你将使用嵌入来完成,那就是非常基本的相似性搜索,所以让我们直接进入代码,这次我们将创建一个文件三,我们将称之为检索数据,好的,现在我们有了检索数据,在这个文件中我们将做另一个函数,称为检索数据,这次我们将接受两个参数而不是一个,第一个参数将是我们的输入,它将是一个字符串,所以输入将等同于我们正在使用的用户查询,好的,所以例如,嗯,在这种情况下,我们可以问一个关于,我认为我们将使用的主题是像大象的问题,所以你知道,如果我们说大象吃什么,那就是我们的输入,而我们需要它的原因是因为我们最终需要为输入生成嵌入,并将其与我们数据库中的记录进行比较,我们需要在输入和我们向量数据库中所有嵌入的值之间执行相似性搜索,第二个我们将使用的参数是选项,这些将是一些可选参数,我们可以用来帮助我们的查询,我们将做一个限制,这将限制我们从搜索中获得的结果数量,我们将做一个最小相似度,这将允许我们选择性地,我需要在这里添加一个问号来使其成为可选的,所以我们可以选择性地按最小相似度分数进行过滤,这是有用的,也许你只想要与查询极其相似的文档,所以这是过滤掉一些文档的好方法,我们还将做一个名字,它可以是字符串或 null,或者 null,我们将选项默认为一个空对象,一旦我们进入元数据过滤,我们将重新审视名字,所以我们可能不会在这里停留太久,但是我们想做的是我们称之为解构,所以我们将从该对象中获取这些值并设置一些默认值,默认情况下我们将接受 10 的限制,所以我们每次至少会得到 10 个事实,除非我们另有说明,对于我们的最小相似度,让我们做,让我们做像 0.3 这样的值,所以我们只接受计算出的相似度分数高于 0.3 的记录,我们将名字默认为 null,当然我们必须从选项对象中获取它,好的,现在我们可以开始第一步了,那就是我们需要为我们的查询生成嵌入,所以再次,我们将使用我们在这里的好朋友,生成嵌入函数,它将是一个数组,只有一个值,那就是输入,所以我们将只生成一个输入,而不是我们用所有这些上传生成的所有输入,再次,你将继续从我们一直在使用的文件中导入这些,一旦你有了那个嵌入,你就可以计算相似度,所以我们将创建一个新的变量,称为相似度,我们将做一个 SQL 类型,我们需要一些反引号,这将是 1 减去,所以如果你看了上一节,你会记得我们现在正在尝试计算输入和数据库中的记录之间的余弦相似度,为了做到这一点,我们将使用余弦距离函数,记住我们从 drizzle om 库中免费获得它,这非常方便,它需要两个参数,我们将获取列,在这种情况下,它是那个事实表.嵌入,再次,事实表将从我们的,如果我能让这个导入工作的话,事实表将来自我们的数据库模式,然后当然,我们需要将其与实际的嵌入进行比较,在这种情况下,它将只是那个索引中的第一个项目,这就是它抱怨的原因,那里有一个额外的 s,记住代码语法很重要,拼写很重要,你必须正确地写出这些字符,但这将计算向量列的情况,它将把我们的嵌入查询与例如第一个事实进行比较,与这个第一个嵌入进行比较,并将计算其中一个记录的相似度分数,然后我们可以用它来进行相似性搜索,所以我们现在要做的是,我们将实际获取我们的文档,所以我们将做一个小查询,将有几件事正在进行,首先我们需要一个选择,这是一个 SQL 选择操作,我们想选择名字,事实表.名字,我们将做内容,事实表.内容,这只是指定内容类型,或者更确切地说,列类型,我们也想获取相似度分数,当然,这是 drizzle 语法,带有 SQL 查询,如果你不熟悉,那么如果你熟悉 SQL,你会发现其中一些听起来非常熟悉,我们将选择来自,选择来自事实表,其中,好的,这是 where 子句,这会有点棘手,所以我们将根据名字是否为 null 来有条件地执行 where 子句,再次,当我们进入元数据过滤部分时,我们将回到这里在,但现在,如果名字有值,那么,如果名字被识别,如果我们正在进行元数据过滤,我们将用最小相似度来计算相似度,然后我们将做一些花哨的语法,这将帮助我们处理大小写敏感的值,一旦我们进入元数据过滤部分,这再次将是有意义的,你将需要确保它被写成我正在做的样子,因为语法在这个查询的东西上很重要,所以如果你需要去检查那些参考文件,以确保你都准备好了,所以这个问号的意思基本上是,如果名字有值,如果它不是 null,我们希望这是我们的 where 子句,否则,这就是冒号代表的,否则我们只想大于,大于代表大于,如果你想知道那是什么意思,好的,所以你可以看到大于,你可以看到,悬停在这里,它只是测试第一个表达式是否大于第二个,所以我们给它相似度是因为那是相似度分数,然后我们只是确保它大于最小相似度,再一次,我在这里犯了一个 s,必须停止这样做,好的,我们有了 where 子句,让我们做一个基本的 order by,只是因为我们想按相似度分数对结果进行排序,所以我们将从最高相似度分数到最低排序,然后我们将根据任何限制值进行限制,再次记住默认值是 10,最小相似度分数默认是 0.3,然后一旦我们有了所有这些文档,我们就可以返回它,所以,再次,我必须确保我拼写这些东西是正确的,所以让我们在实际运行代码之前快速回顾一下,我们创建了一个检索数据函数,它接受两个输入,或者说接受函数中的两个参数,它将接受用户输入,它将接受我们的选项,这是一个对象,有几个不同的值,我们将获取这些值或设置默认值,我们将为我们的输入生成嵌入,我们将把这个输入与我们表中的所有内容进行比较,计算相似度分数,再次,我们建立的索引,当我们构建 SQL 迁移时,它允许我们大规模地高效地做到这一点,这很棒,喜欢余弦相似度与 PG Vector,非常非常有用,非常棒,然后我们只是抓取,我们正在抓取事实表中的名字,内容和相似度,其中,条件在这里满足,按最高到最低相似度排序,然后我们根据这个数字限制它,默认是 10 或我们指定的数字,我们可以做的是,我们可以做另一个异步函数 main,在这种情况下,我们将做常量文档等于 await,然后我们将调用检索数据,我将只说告诉我关于大象的事情,告诉我关于大象的事情,我们将计算这个的嵌入,将其与我们所有 100 条记录进行比较,找出最相似的 10 个,假设它们都至少大于 0.3,然后我们将在这里记录下来,所以我们可以实际看到我们将在此下方调用该函数,我们可以做的就是复制文件路径,然后进入 TSX 空间文件路径,按 Enter 键运行它,如果我把它放大一点,你会注意到我们现在有 10 条记录,1 2 3 4 5 6 7 8 9 10,所以你可以看到最低的相似度分数是 0.315,所以这里所有的 10 条记录都高于 0.3,你可以看到我们正在计算相似度分数,所以这就是我们非常基本的检索步骤所做的,它接受了我们的查询,即告诉我关于大象的事情,然后它抓取了它计算出的我们数据库中最相关的 10 个事实,而你知道我们的 RIK 系统开始工作的迹象之一是,我看看最相似的五个事实,它们都是关于大象的事实,而且你不仅看到它们都是关于大象的事实,而且你还可以看到这里的相似度分数实际上比这些其他分数要高得多,所以你刚刚完成了基本的检索,这很酷,再次,这就是我在本节开头提到的,使用非常基本的数据集,它很容易就能说明我们正在做什么,你可以明显地处理不同的数据,你可以处理,你知道,巨大的 PDF 文件,并且真的把很多内容塞进这些值中,等等,但它很容易就能,你知道,通过这个,并清楚地表明相似性搜索实际上正在工作,当我们有一个相对简单的数据集时,所以这就是非常基本的检索,很多人会在这里停止,但我们不会在这里停止,我们将教你很多不同的东西,你可以添加到你的 rag 系统中,我们希望确保你拥有最常见、最受欢迎的工具,以便当你走向现实世界并构建各种 rag 项目、构建这些 AI 项目和工具以及所有这些不同的东西时,你可以实际地采用像这样的基本 rag 系统并构建一个更高级的系统,这样你就会觉得你在构建非常有竞争力的工具和非常有竞争力的产品,所以让我们开始进入一些更高级的概念,最后但同样重要的是,请确保你再次注释掉 main 函数,我正在插入这个,我不得不编辑它,因为我忘了做,所以这会很快,请确保你注释掉那个函数,伙计们,我们将从查询优化开始,查询优化是我们今天要谈论的第一个高级检索技术,所以我们将介绍,我想是五种不同的技术,这是,我认为是最容易实现的,这是一个非常简单的过程,成本非常低,无论是延迟还是实际的金钱成本,你实际上可以通过查询优化获得相当多的性能提升,所以这是我在 rag 系统中喜欢使用的技术之一,用于我的实际工具集,所以让我们回到代码库,所以我们将要做的是,当然,我们将创建一个新文件,这是第四个,我们将称之为优化查询,让我们把它拼写对,好的,所以我们将拥有这个优化查询文件,我们将创建一个函数,我们称之为优化查询,这个函数实际上会很简单,这是一个相当快的函数,所以我将接受一个参数,那就是查询,因为目标是优化那个查询,我们将通过直接使用 OpenAI API 来做到这一点,所以实际上我将帮助,嗯,让我们先写代码,所以我将创建一个新的响应变量,我们将使用 openai chat completions API chat completions,让我们看看,它是创建,好的,现在让我们输入这些参数,让我们实际获取那个导入,顺便说一句,当你处理这些文件时,要留意的一件事是,如果你像我一样使用自动导入系统,它可能会直接导入 open AI SDK,我们实际上只想做,我们在那个 CL 文件中启动的那个,这将使我们的生活更容易,你只需要注意导入,我们将使用 40 mini,我将稍微谈谈一些差异,以及何时你可能需要一个更强大的模型,但让我们实际传入我们的消息,所以我们将需要一个系统提示,所以我们将需要一个带有系统角色的对象,对于这个内容,我将直接从屏幕上复制粘贴,我将确保在下面包含提示,我将记下包含提示在下面,以便确保我为你做到了,这显然可以在参考文件中找到,但我也会在课程笔记中提供,好的,我们将稍后讨论,我们只会做用户消息,你将看到它是一个用户角色,内容只是我们的查询,所以这就是我们将实际输入到模型的,然后让我们处理返回语句,那就是我们想要从消息响应中获取内容,如果出于某种原因它为空,我们将返回查询,这样我们就不会返回 null 值,我们需要确保我们实际上返回一个字符串,即使是那个没有被优化的查询,所以让我们来谈谈这个提示,然后我们将谈论模型以及这个过程中发生了什么,所以你可以看到,我不会全部读出来,让你坐在这里听我读你可以读的文字,但基本上我们在这里做的是,我们只是给出了五个简单的指导方针,告诉模型我们想要什么作为响应,这里的想法是查询优化,如果你知道,如果我们问一个非常复杂的问题,比如让我们实际打开我们的模拟数据并思考一下,好的,所以这里的一个事实是,章鱼可以再生失去的肢体,所以也许我们问一个非常复杂的问题,我们正在进行一项关于章鱼的研究问题,需要写一篇论文,我不知道,我只是即兴发挥,但其中一件事可能发生的是,突然,你知道你的查询是一段话,我敢肯定,如果你使用 chat gbt,你使用 cloud,任何 AI 应用,你知道有时你可以输入非常非常长的查询,有时你知道以代码为例,很多时候,如果我们使用光标,我们复制粘贴很多代码,然后发生的是,这些查询变得非常非常长,当你实际进入生成嵌入的步骤时,信息变得不那么密集了,添加了很多额外的东西,嵌入变得更难处理,用于检索目的等等,所以你可以用查询优化做什么,你几乎输入了我们正在使用的完整查询,并且你几乎试图得到一个结果,看起来类似于搜索引擎请求的样子,AI 当然,如果你阅读这个提示,例如,你知道删除不必要的词,确保查询清晰简洁,保持原始意图,识别关键概念,所以这里会发生的是,我们将在接下来的课程中看到一些例子,但它会尝试压缩我们非常非常长的查询,如果我们有一个非常长的查询,它会将其压缩成类似关键词的东西,然后你从这些关键词获得的嵌入,而不是完整的文本,将信息密度更高,更准确,它将更好地代表你实际上想从检索系统中获得的东西,所以它可以通过做这样的事情来极大地改善你从相似性搜索中获得的结果,通过做这样的事情,通过使用像 GPT 40 mini 这样便宜的模型,基本上提取几个关键词,这就是查询优化,它试图将一个非常长的查询减少到几个关键词,几个关键短语,这样你就能获得更好的检索结果,所以便宜的模型在这里非常好,因为它会非常快,而且会非常便宜,如果你试图获取大量文本并将其压缩成几个词或几句话,这会非常划算,而且延迟也很重要,因为当你堆叠一堆对不同模型的检索调用时,时间会累加,所以你知道有时用户体验会很糟糕,如果你将太多慢的东西串联起来,所以这就是我们使用 GPT 4 mini 的原因,我们不需要太多的推理能力,有时如果你使用更好的模型,我们只是使用 GPT 40 vanilla,可能会得到一些,你知道,关键词可能更好,特别是对于非常长的结果,但通常 mini 将是一个很好的模型,就像 Claude,hiu,那将是一个很好的模型,谷歌的 Gemini Flash 系列,那将是一个很好的模型,等等,所以我们将在这里停止,实际上,让我们运行一次,这样你就可以大致了解发生了什么,所以我们将做我们典型的操作,我们将创建一个异步 main 函数,我们将在这里创建一个名为 optimized query 的变量,我们将继续使用这个告诉我关于大象的例子,然后如果我们获取文件路径来运行它,让我们为你把终端放大一点,你会看到它接受告诉我关于大象的事情,然后它将其优化为关于大象的信息,所以,嗯,我可能更喜欢,老实说,这应该是关于大象的,所以如果我们去,你知道,有一些东西你可以玩,就像,让我们尝试一个更强大的模型,看看我们会得到什么,所以这实际上更长,所以让我们回到 mini,也许如果我们调整,让我们,好吧,让我们调整温度参数,将其设置为零,这样它就不那么有创意了,你可以再次看到关于大象的信息,所以也许你想做一些事情,比如提供例子,也许你想更喜欢,只有几个词,你知道,使用一到五个词,也许添加一条新规则,倾向于更短的查询,也许我们再次尝试一下,看看我们是否能得到,好的,现在我们只得到大象的信息,所以你可以看到你可以如何玩查询优化,这取决于你如何去做,你将需要根据你的整个 rag 系统的上下文来评估它,但我认为这可能是这个视频的一个好停止点,重点是当你有一个非常长而复杂的查询时,将其简化为几个关键词,你可以获得更好的检索结果,这就是你需要实现它的全部内容,我们将在未来的课程中使用它,但这基本上就是我们开始工作所需要的一切,当然,让我们确保我们注释掉这段代码,这样我们就可以继续进行下一个高级技术,也就是我们将使用 coher API 来进行文档重新排序,这是一个很酷的,这节课我们将学习如何使用文档重新排序器来对你的检索系统进行排序,以继续为你的 rag 系统添加高级检索技术,我们只是在这个阶段堆叠性能增益,所以我们将使用 coher API,所以希望在这一部分的开头,你已经去获取了你的 coher API 密钥,如果你没有,请确保去 coher dashboard,一旦你注册了,去 API 密钥,并确保它在你的 .looc 文件中,确保它在那里,你就可以开始了,所以我们将要做的是,我们将创建一个新文件,我们现在在文件五,我们将称之为重新排序文档,重新排序文档.TS,我们将在这个文件中要做的是,我们将利用 coheres 重新排序 API,所以这将要做的是,我们将把我们的基本检索过程,然后我们将进一步优化这些结果,通过使用另一个 API 端点,所以我们将进行相似性搜索,然后我们将进行文档重新排序,有不同的方法可以做到这一点,有些人喜欢以不同的顺序进行,等等,我真的很喜欢先进行检索,然后进行重新排序,这可能是压倒性更常见的情况,但基本上这个端点将要做的是,我们只是使用 coh 的 V2 API,我们将要做的是,我们将给出一个查询,它将需要一个文本列表,在这种情况下,它将只是我们从相似性搜索中获得的那些记录,我们的数据库结果,我们将传递这个查询和这些文本列表,而 coher 将为我们做的是,它将对我们提供的文档进行排序,在这种情况下,我们通常会做那个限制,我们从相似性搜索步骤中获得的 10 条记录,然后它将对这 10 个文档进行排序,所以也许我们想,你知道,通过这两个步骤运行它,然后只产生,我不知道,三到五篇最相似的,所以只是另一种获得更好排序的方法,所以让我们实际开始编写这个东西的代码,关于编写代码,我们将从创建一个 rank documents 函数开始,为我们编写代码的好处是,它将更清晰地说明实际发生的事情,所以如果你觉得有点迷失,没关系,代码将使这个的有用性非常清楚,所以这个函数将接受两个参数,与我们的检索数据函数非常相似,我们将接受一个查询和一个文档,我们将使这些文档的数据类型等同于我们的事实,具有内容和名字,它将是一个数组,我们还将有一个第二个参数,它将是一个限制,限制为三个,所以我提到,你知道,文档重新排序器的伟大之处,这是一个说十遍的短语,是你可以,你知道,首先通过余弦相似性搜索,然后进行第二次通过来真正优化文档,所以你可以将其缩减到最相关的文档,并将限制从,我们将使用从 10 到 3 的例子,所以我们有这三个参数,查询,文档和限制,所以让我们实际使用 coher API 来编写重新排序代码,我们将使用重新排序端点,我们将再次传递,我们需要传递文本,然后我们需要传递查询,因为你实际上需要有东西可以相互比较,当然,你需要导入 coher API,并且你还需要拼写正确,原始的,因为它不会是正确的,但请确保导入你在 API 客户端中设置的 coher 客户端,我们终于要使用它了,我们需要将查询作为第二个参数传递,然后我们还有另外两个参数,我们需要做 top end,这只是记录的数量,在这种情况下,这将是我们的限制,在这种情况下,我们将默认使用三个,然后我们还需要指定模型,我们将使用 re rank english- v3.0,这就是从 coher API 获取重新排序结果所需的一切,基于我们的查询和文本,来自我们余弦相似性步骤的那些事实,当然,我们只想返回它们,我将快速过一遍,我们将使用光标标签,所以我们想获取名字,我们想获取内容,然后我们也想获取相关分数,这是 coher API 如何为我们重新排序,那就是我们将从该 API 获得的分数,为了测试这一点,我们需要做一个小的 main 函数,当然,我们将在下面调用它,让我们创建一个名为 retrieved docs 的变量,在这种情况下,我们将做第一步,也就是我们将进行良好的相似性搜索,我们将使用我们已经花时间编写的 retrieve data 函数,当然我们需要等待它,这将是 retrieve data,我们将继续使用告诉我关于大象的事情,当然你需要从那个特定的文件中导入它,然后一旦我们有了 retrieved docs,将其保存为那个变量,记住我们只是将其作为返回的文档,这就是我们如何访问它的,现在我们将要做的是,我们将创建一个名为 documents 的变量,这次我们将把检索到的文档传递给 rank documents,首先,我们需要告诉我关于大象的事情,再次,我们正在重用同一个查询,然后我们需要传递文档,来自检索步骤的文档,然后我们将控制台记录这些文档,这样你就可以看到这个 API 结果是什么样的,所以让我们复制文件路径并运行它,我将执行一个 clear 来清除它,我们应该能够找到的是,我们得到一个包含三个记录的数组,所以你可以看到我们正在获取名字,我们正在获取事实,然后我们正在获取来自 coher API 的相关分数,所以基本上发生的事情是,你知道,实际上我也会记录 retrieved docs,这样你就可以看到它是如何被缩减的,让我们做一个小的 Ctrl+C,再次运行它,让我们把它放大,这样我就可以真正强调这一点,所以我们得到的第一个结果,第一个这里将是来自我们相似性搜索的原始 10 条记录,所以我们已经在之前的课程中看到过这个结果,当我们第一次测试 retrieve data 函数时,我们得到了所有五个关于大象的事实,然后我们得到了其他五个,你知道,我们得到了长颈鹿,犀牛,甚至一只海豚也混进来了,你可以看到它们带有相似度分数,所以我们用 coher reranking API 做的是,我们正在传递我们的查询,即告诉我关于大象的事情,我们正在传递所有文档列表,除了我们只获取内容属性,以简化事情,而 coher 正在做的是,它正在根据它们计算的相关分数将这些从 1 到 10 进行排序,并返回最相似的三个事实,所以你可以看到,如果我们比较这两个列表,你会发现第一个结果,大象是唯一不能跳跃的动物,这实际上是我们相似度分数中的第四个最相似的结果,当我们添加一个重新排序器时,它就跳到了第一位,所以你可以看到,重新排序器一定做得很好,因为它保留了所有关于大象的事实,这绝对是你应该期望的,而且这个查询不是,我可能应该做一个更好的例子来真正强调这一点,比如,让我们看看我们的模拟数据,实际上,让我们再做一个小例子,看看我们能不能用企鹅做些什么,好的,有一些关于睡眠的问题,比如让我们试试关于睡眠的事情,让我们实际上改变这个查询,我想了解动物睡眠,让我们再运行一次,我将按 Ctrl+C 再次运行它,我们可以再看一个例子,看看发生了什么,所以,嗯,有趣的是,我们实际上没有得到 10 条记录,这告诉我,如果你还记得我们编写的 retrieve data 函数,我们有那个默认的 0.3 相似度分数,所以通过使用最小相似度,我们可能已经删除了几条记录,这实际上对于检索系统来说非常好,你基本上设定了一个硬性界限,我们只想要与 X 相关的东西,所以我们得到了这六条,然后我们将其缩减到这三条,所以你可以看到顺序又有点变化了,所以你会看到,当我们进入更多的例子时,特别是下一节,当我们做一个生产级别的系统,使用一些更复杂的数据集时,重新排序器可以带来更好的结果,coher 的重新排序 API 非常便宜,所以你可以获得很多性能,而且成本很低,所以这就是关于重新排序器的方法,我们将像往常一样注释掉这段代码,继续快乐地做这件事,但这将在以后变得更加明显,为什么这很有用,所以请继续关注我,接下来将是,我们的下一个高级技术,这将是元数据过滤,所以我们将终于谈谈为什么我们一直使用名字,是时候终于进入元数据过滤了,所以在这节课中我们将要做的是,我们将使用 openai 函数调用,所以我们将使用 LLM 加函数调用来为我们的查询添加额外的查询或额外的 where 子句,当我们搜索时,演示它的最好方法就是直接开始,所以我要做的是,我将清除我之前正在做的事情,让我们直接创建一个新文件,我们将称之为过滤元数据元数据,而这里将发生的是,我们将基本上构建一个小的名字提取器,带有一个函数调用,所以如果你不熟悉函数调用,在 AI 世界里,我们将把它作为我们这样做的时候进行一点点介绍,所以我们需要做的是,我们将创建一个新的异步函数,我们称之为提取名字,它将接受一个查询参数,这就是这个函数所需要的,所以我们将做几件事,我们将实际调用 open AI API,所以我们将使用我们最好的朋友,聊天完成端点来做到这一点,我们需要做几件事,像往常一样,我们将使用 GPT 40 mini 来处理消息,我们实际上不会使用系统消息,我们只会使用用户消息和查询,这将是一个非常非常基本的调用,我们还需要添加工具参数,它将是一个工具数组,我将直接复制粘贴我在这里的工具,这样我们就不用输入所有这些了,再次,我想我在上一课或前一课中提到过,你想要确保你正确地导入了客户端,你想要导入你制作的客户端,而不是直接从其库中导入 open AI 包,所以我们将要做的是,实际上让我们再做一件事,作为工具调用的一部分,你实际上需要解析你的工具调用,我将在这里自动完成,然后我们想要返回解析的工具调用或 null 值,以防我们实际上没有名字实体,所以让我们实际上退一步,现在我们有了所有代码,让我解释一下发生了什么,所以我们正在使用 gp40 mini,我们正在将一个消息发送到 open AI chat completions API,使用那个查询,而这个工具正在做什么,所以你可以看到,我们必须定义一个函数调用,函数调用基本上是 AI 模型识别我可以使用所有这些工具的能力,何时使用它们,你知道,在 AI 世界中最常见的工具之一,用于教授函数调用的是获取天气函数,我们将,函数调用不是这个视频的重点,所以我只是会稍微快一点,但想法是,假设你在这里有一个获取天气函数,你问了一个关于天气的问题,那么模型可能会知道,嘿,我需要调用获取天气函数,而你在这里定义的模式将定义模型作为该工具调用的一部分实际返回的内容,在这种情况下,我们有一个按名字过滤的函数,你可以看到我们有一个描述,它被粘贴到 GPT 40 mini,因为它正在工作,所以它几乎就像这个特定函数调用的提示,你可以看到我们试图从查询中提取动物或实体的名字,它必须是动物的完整名字,并且是复数,所以你知道,如果我们使用我们的,你知道,我想了解动物睡眠,可能不会得到一个特定的动物,因为我们没有问任何事情,但如果我们使用类似告诉我关于大象的事情,我们之前用过,这个函数调用将返回,因为你可以看到我们只有一个属性,名字,动物或实体的名字,并且它是必需的,它将返回动物的名字,而 LLM 将会推理出来,你知道,鉴于像告诉我关于大象的事情这样的查询,它可能会返回大象,为了向你证明这是怎么回事,让我们实际测试一下,这样我们就可以看到它在行动,我们将要做的是,我们将创建一个查询,我们将只说告诉我关于大象的事情,我们将继续使用它,我们想要做的是,我们实际上想要创建一个名为 name 的新变量,我们将用这个查询调用 extract name 函数,所以希望我们应该能够记录这个名字,并得到大象,所以如果我们实际运行它,让我们复制文件路径,在我们的终端中运行它,希望我们应该得到大象的记录,太棒了,果然我们做到了,所以这就是名字提取器的作用,如果你做了,你知道,告诉我关于长颈鹿的事情,如果我们运行这个,我们应该得到长颈鹿,所以你大概明白了,它基本上是在尝试提取哪种动物,所以我们可以做的是,因为我们在 where 子句中内置了可选的名字过滤,作为 retrieve data 函数的可选参数,我们将能够传递动物的名字,以确保我们只获取与该特定动物相关的记录,所以让我们实际测试一下,所以我们将要做的是,我们将创建一个名为 documents 的新变量,我们将调用 retrieve data,这次我们将使用可选的第二个参数,名字,我们将直接传递我们提取的名字

从我们的函数调用中提取。好的,那么如果我实际打印出这个,然后我们再运行一次这个函数,我们应该在这里看到的是,我们应该看到 um,我们应该看到 uh,关于 um 大象的记录。好的,所以你可以在这里看到 um,这是可能的。好的,有趣的是,模型实际上给了我们一个空值,这有点有趣。让我们再运行一次,看看这次我们是否能得到正确答案。好的,所以我们一遍又一遍地得到空值,所以这告诉我,我们可能可以把它做得更健壮一些。所以,例如,我们可以做的一件事是尝试使用一个更强大的模型。让我们尝试使用 GPT 40 而不是 mini,看看这是否会对任何事情产生影响。好的,看起来这个模型可能好一点。好的,你可以看到我们得到了 um,这是可能的。我真的犯了一个小错误吗?哦,我犯了。让我们修复它。我将保存它。我想你实际上不需要改变任何东西。我想当我查看检索数据文件时,我不小心按到了我的键盘。让我们再运行一次。好的,太棒了。这就是我想要的。好的,嗯,我不知道为什么 GPT 40 mini 几次都返回空值,这有点奇怪。可能是因为我只需要构建一个更好的描述。也许是更好的属性描述。在提示方面有很多事情可以做,比如也许我们再用 mini 试一次。我只是,我的意思是,这是一个很好的练习,只是看看底层模型本身实际上如何极大地影响你的查询。好的,嗯,无论出于何种原因,mini 现在都不想工作了,而且我们也不想让 um 提示成为这个视频的重点。我只是想向你展示元数据过滤是如何工作的。所以你可以在这个例子中看到。好的,我们确实进行了大象扫描,然后你就可以看到,我们只从我们的检索系统,从我们的 rag 系统中获取了与大象有关的事实,因为正在发生什么。好的,如果我回到代码,我们正在从这个查询中提取名称,将该名称保存到一个方便地称为 name 的变量中,然后在 retrieve R data 函数中,我们正在做的是,我们正在传入那个可选的 name 参数,而 retrieve data 中正在发生什么。好的,回到几个课程之前,对吧?我们在这里提取了 name,然后如果 name 存在,所以你会记得我们有一个叫做三元运算符的东西,那就是如果这个 name 有一个值。好的,所以之前,之前我们只是运行这个大于这个相似度分数,但现在我们运行大于这个相似度分数,它被包装在这个 and 函数中,这个 and 助手在这里。而我们正在做的是,我们正在检查 if fax table.name,所以如果 fax table 中的值,或者列中的值,或者更确切地说,name 列的值。好的,所以我们看看我们的数据库,只是为了让这个更清楚一点,对吧?我们有这个 name 列,里面有动物的名字。好的,所以在这个例子中,它对应于大象。所以它只是获取这些记录。它实际上根本不看这些。它只是获取大象的记录。这些确实很方便,对吧?因为之前发生的是,我们运行了一些查询,你知道,可能只是针对特定的动物。在这种情况下,获取关于考拉的事实对我们来说完全没有意义。所以,你可以用元数据过滤做的一件事是,为了让它更具体一点,对于一个真实的例子来说,假设你在这里有很多收益报告,而不是 name,它可能是公司名称。而且你可能有谷歌,你有英伟达,你有特斯拉,你知道,你有苹果,你知道,你有所有这些不同的公司,你知道,而且假设你问了一个关于微软的问题,例如。就像,嘿,给我一份微软收益报告的摘要。那么,你可以做的一件事是,你可以构建一个公司名称提取器,而不是一个动物名称提取器。然后你可以做的是,它会在这里提取微软,你就可以按公司名称为微软的记录进行过滤。这样你就不会意外地从,你知道,比如苹果那里拉取记录到你的检索系统中。所以元数据过滤允许你做各种有趣的事情来减少不相关的记录。你可以编写更精细化的查询,当你将它与函数调用结合起来时,这是一个很好的方法来构建这些提取器,它们可以接受你的查询,提取相关的元数据,然后你可以使用你提取的元数据来缩小你的查询范围。这是一个非常强大的概念,你可以在你的 rag 系统中使用它来帮助缩小结果范围并获得更准确的信息。所以这就是元数据过滤。我们将再次注释掉主函数,然后我们将继续进行下一个高级技术,即文本分割。我们将保持这一部分或这一课相对简短。但我确实想谈谈文本分割,文档分割器之类的。对于我们正在处理的数据集来说,这不是一个大问题,显然我们所有的模拟数据事实都相当短。所以文本分割并不是我们真正需要做的。当我们进入更长的文档并且我们必须分割事物时,我们将更多地关注这一点,因为我们想要管理这些记录上的 token 大小。但我确实想稍微提一下文本过滤。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码,你的代码,我们将创建一个新文件。让我们看看,这是一个文件 7,文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得有点太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得有点太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得有点太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得有点太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,对吧?这更代表了,你知道,你在真实世界环境中可能会处理的文档。你知道,假设你正在抓取维基百科或者你想在所有这些维基百科信息上构建一个问答机器人,问答 AI 机器人。你可能需要分割一些更长的文件。所以这就是文本分割器发挥作用的地方,你正在尝试做某种系统的分割,以防你试图分割或将特定文档分割成许多更小的文档,以便更容易地处理 rag 系统。就像我提到的,我们不会在这里深入细节,因为你可以,这是一个兔子洞。所以如果你想更多地了解文档分割器,你正在处理更多真实世界的数据集,你知道,你正在寻找方法将它们分割成更小的值。例如,一个常见的文档分割器是,如果你正在处理一个 PDF,你可能会根据页面来分割 PDF。你可能只为那个特定页面创建一个嵌入。所以,你知道,如果你有一个 100 页的 PDF,你把它分割成 100 个不同的文档,然后你嵌入每一个。这使得事情稍微好一点。所以如果你想进入那个兔子洞,那就去吧。我们只是展示一个非常基本的方法,你知道,你可以根据字符数来分割。所以让我们来做吧。实际上,让我们做单词计数。所以我们在这里要做的是,我们将在这里定义 content 数组或 chunks 数组,因为我们将基本上尝试获取那个大象维基并将其分割开。所以我们要做的就是,我们将决定我们想要一个 500 个单词的块大小。所以我们将要做的是,我们将把这个维基条目输入到我们的函数中,我们将尝试将其分割成尽可能多的 500 个单词的块。所以让我们开始吧。所以我们可以做的是,我们可以创建一个 for 循环。好的,所以我们将遍历我们在这里接收到的数据,遍历我们的数据数组。在这个例子中,当我们实际运行函数时,它将只是数组中的一个项目,因为我们只处理一个数据。但我们在这里想要做的是,我们实际上想要跟踪单词。我们将要做的是,我们将在这里做一个正则表达式,一个 Rex,这就是这行代码。我现在把它去掉。我在这里标签打得太多了。而这只是分割内容。所以那个 content 属性,它将按单词分割。所以如果我们回到我们的数据,它将,我的意思是,我不需要解释,你们明白分割成单词是什么意思。哦,天哪,这很有趣。但是我们要做的就是,我们将在这里做一个 for 循环。我们将遍历那些单词。所以我们将遍历 words.length。好的,然后我们将按块大小进行迭代。而在里面,我们将要做的是,我们将创建一个名为 chunk words 的变量。我们将切片那些单词,并将这些块作为我们切片并将它们推入 chunks 数组。所以你可以看到 chunk words.join,我们在这些单词上连接。所以我们正在做的是,我们基本上一次获取 500 个单词。一旦我们达到 500 个,就将其推入该数组,然后继续下一个。所以我们在这里结束时想要做的是,我们想要返回 chunks。我可能在这里犯了一个轻微的语法错误,那就是我需要把它变成一个数组。好了,我们已经消除了错误。现在我们要做的就是创建一个名为 main 的新函数,当然我们想调用它,这样我们就可以测试这个东西了。现在让我们获取这个第一个 bin。所以我们将创建所有这些块。所以让我们实际运行那个 split text 函数。而我们要做的就是,我们将传递一个包含单个对象和 content 的数组。我们将导入 elephant wiki。我们将导入那个文本,它将是 content。然后,你知道 name 将只是模拟,好像我们将整个 wiki 添加到我们的数据库中一样。我们可以做的是,我们可以做 console.log。我们可以记录那些块,我们还可以记录块的长度,这样我们就可以看到我们得到了多少 500 个单词的块。让我们运行这个东西。让我们看看我们得到了多少不同的块。好的,所以我将向下滚动,做一点 contrl C,这样我们就可以重置那个终端,清理一下。然后我将粘贴我们的文件名,加上 TSX。好的,所以看起来我们得到了三个不同的块。所以看起来我们填满了。让我们实际全屏显示它,这样对你们来说更容易看到。好的,所以我们得到了第一个完整的块。我们得到了第二个完整的块。所以它们每个都将是 500 个单词,对吧?这就是按 5 个单词分割的意义。然后最后一个将是剩余的单词。所以看起来我们还没有完全结束。所以这不一定必须是 500 个单词。右?假设维基只在这里这个句号结束了。那么这个块就会很小。碰巧的是,这看起来大约是 500 个。所以这些看起来都差不多。但重点是,我们将这段非常长的文本分割成了许多更小的文本。然后我们将嵌入每一个更小的块,并将其上传到我们的向量数据库。所以这就是我们现在要谈论的文本分割。我们实际上不会在我们的 rag 系统中使用它,对于这个特定的例子,但它非常普遍,特别是当你进入更高级的 rag 系统时。我只是想让每个人都知道。我们可能会在接下来的部分中更多地谈论它,当我们进入更长的文档时。所以让我们开始吧。我们将在这里做的是,我们将转到你的代码。你的代码。我们将创建一个新文件。让我们看看,这是一个文件 7。文件 7,我相信。我得记着我在哪儿。好的,所以文本分割点。而我们在这里要做的是,我们将做一个非常基本的分割文本函数。我们实际上不会使用它。我确实在这一部分的开头提到过,如果我们再次打开那个模拟数据,你会看到我们有这个大象维基。所以这是我们模拟数据文件中的第二个值。这是我让 AI 生成的一个很长的维基百科条目。你可以看到它有几百行文本。而我们要做的就是使用那个值来测试文本分割器是如何工作的。好的,所以我们要做的就是在这里导出异步函数来创建一个分割文本函数。让我们确保我们在这里得到正确的字母大小写。这将接受一个参数,它将是一个对象数组,匹配我们一直在处理的数据结构。所以对象中有两个字符串,content 和 name。而我们在这里需要做的是,我们实际上需要分割这个文本。所以在我们实际编写代码之前,我将稍微谈谈为什么文本分割器,为什么文档分割器很重要。所以当你处理像 elephant wiki 变量这样的东西时,所有这些文本,

举个最终产品的例子,那些文件中的代码将存在于那里,我们将与旁边的项目在同一个文件夹中工作,那是为你的代码准备的,所以这将是我们在此部分编写的项目代码,如果你学习了第二部分,这与它的格式非常相似,我们有你的代码,然后我们有参考代码,所以你将需要确保你获取最新的代码,然后你将再次需要三样东西,这些与我们为第一和第二部分使用的三样东西相同,所以你需要一个 OpenAI 密钥,所以你将需要前往 platform.openai.com 获取你的 API 密钥,因为你需要它,然后如果你想使用文档重排序,我强烈建议你使用,这是非常好的生产级实践,而且是学习的好东西,但你也需要一个 Cohere API 密钥,以便你可以使用他们的文档重排序 API,所以这很重要,然后当然,最后但并非最不重要的是,你将需要前往 Superbase,我们将在稍后进行设置,但你需要一个项目,同样,你可以使用免费的 Superbase 账户,如果你用完了免费项目,你可能需要删除一个旧项目,这没什么大不了的,我们在学习,所以你大概可以轻松地丢弃旧的东西,但让我们实际来看一个使用应用程序的快速小例子,这样我们就可以给你一个我们实际要做什么的预览,所以像这样的 RAG 系统可能非常有用的一个方面是,你知道,博客文章、论文之类的内容,这些内容可能没有直接包含在训练数据中,或者你只是想问一些关于它的非常具体的问题,你知道,如果一个模型是在六个月前训练的,它可能不会有新的数据,所以例如,这是一篇 Paul Graham 的文章,写于 2024 年 10 月,这篇文章没有包含在 GPT-4o 的训练数据中,所以如果你问关于这篇文章的问题,它将无法真正得到答案,它没有什么可以参考的,所以如果我,让我运行我的本地服务器,并在参考应用程序上运行 npm run dev,以确保一切正常运行,并向你展示正在发生的事情,所以我可以做的一件事是,你知道,我实际上已经嵌入了这篇文章,所以让我们试试之前的那个,这是一篇写于 2024 年 9 月的文章,所以这里我可以做的就是复制粘贴整个内容,我甚至会包含那个,我们将实际参考这个,这可能是一个很酷的例子,所以这里我将粘贴进去,你可以看到我们有所有这些文本,我将点击上传文档,这将把所有这些文本作为一个新的数据条目上传到我的向量数据库的文档表中,所以你在这里可以看到,这是实际的数据,所以这里我们可以做的是,如果我们看看这篇文章,你知道,让我们去尝试找到一些我们可以问问题的点,比如,我们在不确定性面前得到了什么?让我们尝试一下,让我们问这个问题,实际上,你知道,因为我们将复制完全相同的查询,这可能就像这个的嵌入基本上是相同的,所以让我们做一些事情,比如,我应该等到大学毕业后再弄清楚我要做什么,让我们看看它怎么说,好的,我们将发送出去,我们将在这里得到我们的查询,用绿色表示,你可以看到,如果我们向下滚动,让我们隐藏我们的来源一会儿,它说不,你不应该等到大学毕业后再弄清楚要做什么,你可以看到它似乎是从这三个来源提取的,所以你可以看到这些是我们检索过程结束时返回的前三个排名靠前的文档,所以你可以看到文章的这个片段被分割在这里,所以我们将在这里使用一种简单的分割技术,有很多更高级的方法,我将在最后作为奖励提供给你,并向你展示你可以在未来从哪里开始,我们可能会做一些视频,如果有需求的话,关于一些单独的课程,关于特定的文档加载器,但这涵盖了要点,如果你掌握了这一点,很容易转向其他内容,但话虽如此,让我们继续设置代码,这样我们就可以从头开始构建这个东西,让我们继续初始化我们的代码,这会相当简单,我将很快地导航回父目录,这样我就可以和你一起导航,就像你在跟着我一样,所以你要做的是,你要确保你回到代码仓库,我们将要做的是,我目前可以看到我打开了终端,我们将导航到我们将要构建项目的仓库中的位置,所以我们将进入 sections,然后进入 section three,这就是我们将运行初始设置脚本的地方,它将位于参考应用程序项目旁边,我们将在这里创建一个新的应用程序,我们将使用 Shad CN CLI,这是一个非常棒的项目设置方式,这也是我们在 Takeoff 中设置我们的 Next.js 全栈应用程序的方式,所以你可能很熟悉它,你可能需要实际运行某种安装命令,你的终端可能会告诉你有一个命令需要运行,然后你必须再次运行它,但我们将要做的是,我们将运行 npx Shad CN at latest init,这将在这里初始化一个新项目,看起来,我是否拼错了什么?是的,我拼错了,Shad CN,让我们再运行一次,所以它将识别出我们目前没有 Next.js,所以你可以看到 Y 是大写的,我将输入并将其命名为,你知道吗,让我们就叫它我的 RAG 应用,怎么样,我的 RAG 应用,所以这将在这里的这个文件夹中设置一个新的 Next.js 项目,正如你现在可以看到的,我们有参考应用,这是完成的代码,然后我们有我们将要从现在开始工作的 my-rag-app 文件夹,所以你必须在这里运行一点设置步骤,我们将使用默认的样式,我们将使用中性颜色作为基础,点击是,这将安装我们基本的依赖项,并让我们运行一个漂亮简单的小应用程序,所以一旦项目初始化完成,我们将进入那个 RAG 应用文件夹,如果你运行 npm run dev,你应该可以看到你的应用程序在 localhost 3000 上运行,所以如果我刷新一下,我们现在有了我们的入门应用程序,这是一个由 Shad CN CLI初始化的入门 Next.js 应用,这样我们就可以开箱即用地获得许多额外的功能,这是一个非常好的设置步骤,如果你不熟悉的话,但我们接下来要做的是,我们将添加一些 Shad CN 组件,所以如果我拉入 Shad CN 文档,我会确保我链接到这些文档,这将是你可能想要熟悉的,特别是如果你正在构建大量的全栈项目,所以我们实际上正在做的是,我们只是遵循这个 Next.js 指南,你可以看到这非常熟悉,我们要做的一件事是,我们将利用这个组件库,所以例如,如果我点击这个按钮组件,这里的一个很酷的事情是,我们可以直接安装这个按钮组件,我们就得到了一个看起来很棒的按钮,我们将对三个不同的组件做同样的事情,我们将回到光标这里,你可以看到你应该有一个正在运行你的 localhost 应用的终端,这很好,我将创建一个拆分终端,你可能只想创建一个新的,我喜欢拆分版本,在这里运行我的应用程序,在这里运行一些脚本,我们将要做的是,我们将运行 npx Shad CN at latest add,我们将添加 button, input, and textarea,所以我们想要这三个组件,如果我在这里输入,你会看到我们在文件浏览器中有一个新文件夹,我们有按钮组件,输入组件和文本区域组件,所以现在我们不必自己构建这些组件,我们开箱即用地获得了它们,Shad CN 有所有这些不同的组件供你使用,强烈推荐,它是目前世界上最受欢迎的库之一,它是热门的 UI 库,它将使你的生活更容易,所以习惯使用它,一旦你安装了这些组件,我们将安装一些我们将要在项目中使用到的软件库,这些将非常熟悉,我将复制粘贴,因为它有很多,我将把脚本放在下面,你也可以复制粘贴,但我们将运行 npm install,这些库,这是 OpenAI SDK,我们想使用 Cohere 的 SDK,我们将使用 Vercel AI SDK 来做一些事情,比如实现那个 AI 聊天功能,我们将使用 Tiktoken 来计数,在第二部分,我们按单词计数分割了我们的文档,这次我们将按 token 计数,所以我们将提高一个级别,所以我们需要所有这些不同的东西,然后你还需要运行以下脚本来获取你的开发依赖项,这些只是我们需要的一些开发工具方面的东西,以便获得像我们的迁移脚本,用 Drizzle 编写,好的,这是我们的一些代码设置,我们应该做的一件事是,我们需要设置我们的环境变量,所以如果我快速回到参考应用程序,你会看到这个 .env.example 文件,如果你一直在关注课程的其他部分,它会看起来很熟悉,所以你要做的是,你要回到我的 RAG 应用,你要创建一个名为 .env.local 的新文件,然后我们将获取那个示例文件,我们将把这些值复制粘贴到 .env.local 文件中,我将在屏幕外进行,这样我不会暴露我的 API 密钥,但你要做的是输入你的 OpenAI 密钥,你将把它放在那里,无论你的 Cohere API 密钥是什么,然后我们将开始我们的数据库设置步骤,在下一课中,所以请完成到这一点,获取 OpenAI 密钥,获取 Cohere API 密钥,确保你的应用程序在 localhost 上运行,确保你安装了这三个组件,这样你就可以使用它们了,然后让我们继续下一课,现在我们已经有了应用程序的基本代码,多亏了 Shad CN 的初始化步骤,我们可以设置我们的向量数据库,这与我们在第二部分所做的非常相似,实际上是相同的,所以到目前为止,按照我们在这里教授的方式设置向量数据库,使用 Postgres 和 PG Vector,这应该感觉很标准,你知道,任何时候你设置一个检索项目,你都需要做类似的事情,即使你使用不同的向量数据库,比如 Pinecone 或其他什么,同样的步骤都会适用,你需要去设置数据库,你需要去弄清楚你想要用于检索的表的模式,你必须去设置它,所以我们将按照我们在 Takeoff 上做的方式来做,使用 Postgres PG Vector,所以让我们直接开始,所以你在这里可以看到,我在 superb.com 上,我在创建新项目屏幕上,所以我们要做的就是创建一个,我将称之为,我将称之为 takeoff-rag-live,因为我正在直播,我们将生成一个密码,当然你可以选择自己的,我将使用生成的密码,提醒你,你需要保密,所以就像密码一样对待那个值,显然,它说数据库密码,所以请确保你保持私密,我显然只向你展示这些,以便我能够教授东西,所以我将复制粘贴那个值到那个,到那个 .env.local 文件中,我知道现在它不在正确的位置,因为我们必须更改数据库 URL,如果你看过第二部分,你可能很熟悉,但我们将启动这个项目,这通常需要一两分钟来初始化数据库,所以我将剪辑到完成,砰,我们的项目设置好了,让我们获取数据库 URL 来连接到它,你将前往侧边栏,进入项目设置,然后在项目设置中,你要进入数据库页面,这就是你将找到 Postgres 数据库连接字符串的地方,所以复制那个家伙,回到你的代码,你将把它粘贴到数据库 URL 的值中,一旦你有了那个值,现在我们将剪辑保存的值,我们将替换这个括号中的密码值,砰,现在我们有了应用程序所需的连接字符串,在我关闭这个文件之前,我想提醒你几件事,因为环境变量非常重要,你需要把它放在 .env.local 中,你需要更改任何,我这里只有一个测试值,我将在之后立即进行,但我将在这里输入我的 OpenAI 密钥,我将在这里输入我的 Cohere API 密钥,然后我将,我显然已经有了我的值,这很好,不要与任何人分享这些值,像对待密码一样对待它们,因为如果人们获得这些值的访问权限,他们就可以访问你的账户积分并进入你的数据库等等,所以请确保这些东西是安全的,我将去获取我的值,把它们放在那里,然后关闭这个文件,然后回来,在我输入我的密钥后关闭了文件,现在我们需要获取我们的 Drizzle 文件,我们在第二部分做了所有这些步骤,但我们将为这个项目再次做,重复是有帮助的,你需要习惯做这些事情,你可以使用你自己的技术栈,如果你想使用不同的向量数据库,那完全没问题,有很多选择,这是我使用的方法,我觉得它非常可靠,它通常是一个相当方便的技术,使用我们在 Takeoff 中使用的技术栈的一个优点是,AI 模型对它有很好的上下文,所以如果你使用像 Cursor、Vercel ChatGBT Cloud 这样的工具,它们将能够给你很好的答案,因为库在训练数据中占有一席之地,所以这是我们使用我们选择的技术栈的一个重要原因,如果你对这个决定感到好奇,但我们要做的就是,再次确保你在我的 RAG 应用中进行编码,而不是在参考应用中,参考应用是完成的代码,再次,在任何时候,如果你想参考我们要处理的任何文件,你可以进入参考应用,所以例如,我们要做的其中一件事是,我们将处理我们的 drizzle.config 文件,我将这样做,因为这些都是相当样板化的设置步骤,我将利用这一点,我们将进行一些复制粘贴,在我的 RAG 应用中,第一件事是创建一个 drizzle.config.yml 文件,下一步是我们需要在我的 RAG 应用文件夹中创建一个新文件夹,它将被称为 DB,我们需要在 DB 中做的是,我们将创建一个名为 db.ts 的新文件,实际上,我认为参考有时我做 db.ts 有时我做 index,所以我们将保持与参考应用程序一致,但我们将从参考应用程序获取代码,这又是非常样板化的东西,如果你是第一次设置项目,你可以复制粘贴,然后我们将要做的是,我们将创建一个新的文件夹,在 DB 文件夹内,称为 schema,我们将在这里创建一个名为 documents-schema.ts 的新文件,你会看到,我不知道为什么我称之为 documents-data,我们将坚持使用 document-schema,这是一个更好的命名约定,但我们将复制粘贴这个表,并将其粘贴到我们的中,所以到目前为止,你应该已经添加了 drizzle.config 文件,它位于项目的顶层,然后在 DB 文件夹内,我们有 index.ts 文件,你实际上需要修改那个命名,我可能会在仓库中更新它以使其一致,但如果你遇到那个小错误,请确保你更改了文件名以进行导入,否则你会遇到一些错误,然后我们将在这里的 schema 文件中定义我们的文档表,所以我们又一次快速地浏览了一下,因为我们在第二部分做了完全相同的事情,我们逐行解释了,但我们将再次快速浏览,我将在这里谈论一下这个表,因为它确实很重要,所以我们有一个 ID 列,这些都是列类型,我们将有一个 ID 列,我们将有我们的 content 列,这是我们的文本实际所在的地方,在我们的应用程序中,我们有那个大文本区域,我们上传我们的文本,那就是文本,我们将需要使用 vector 列,再次,这是我们从 PG Vector 获得的,我们在第二部分都涵盖了,所以我们将在这里稍微快一点,但我们将再次使用 256 维,只是为了保持速度,保持灵活性,保持成本低,如果你正在做一个生产数据库,我确实建议你增加维度,我们有 OpenAI 嵌入文档的链接,所以如果你想了解一个好的维度数量可能是什么,如果你想获得更好的检索结果,并且你对运行数据库的服务器端要求感到满意,请随意查看,但我们将保持简单,然后当然我们需要我们的嵌入索引,我实际上将把它从 data 改为 documents,所以这将是 documents,我们将要做的是,我们需要从这个 Drizzle 文件生成 SQL 迁移文件,我们基本上在这里用 TypeScript 编写所有数据库模式,它将使我们的数据库在整个应用程序中类型安全,这将对我们非常有帮助,特别是当我们构建全栈应用程序时,当你做一些后端工具时,比如我们在第二部分做的,它并不那么明显为什么这很好,但当我们处理前端时,我们需要到处插入和选择文档类型,它就变得更加明显了,所以我们可以做的是,我们实际上需要对我们的 package.json 文件进行一些小的调整,我们实际上需要创建两个脚本,我们需要做 db:generate,这将运行 drizzle-kit generate,实际上我们可以这样做,我们将再次参考我们的参考应用程序,因为我们要做的就是复制这两个脚本,generate 脚本和 migrate 脚本,这将与你做的所有应用程序保持一致,所以这又是一个非常样板化的步骤,我们已经处理好了,一旦我们有了它,我们可以回到我们的终端,再次,我有一个正在运行 npm run dev 的应用程序的终端,再次,你可以运行 npm run dev,这将运行这个 dev 脚本并启动你的应用程序,然后你可以在 localhost 3000 上访问它,然后在右边,我有一个另一个终端,我们可以运行一些脚本,我将做 npm run db:generate,这将运行这个脚本,所以我们将输入,你会看到我们现在有一个新文件夹,你会看到在我们的数据库文件夹中,我们现在有一个 migrations 文件夹,里面有一些东西,其中最主要的是我们的 SQL 迁移文件,再次,你需要运行这个文件来在你的 Superbase 项目上创建那个表,并提醒你,就像我们在第二部分做的那样,我们实际上必须创建,如果不存在,则创建扩展 vector,这是我们将 PG Vector 扩展添加到我们的 Postgres 数据库的方法,这样我们就可以利用那个向量嵌入类型,这就是我们将在基本检索中进行的整个基本的余弦相似度搜索步骤,所以你将需要确保你将这一行添加到生成的 SQL 文件中,然后如果你运行 npm run db:generate:migrate,这将应用你的迁移到你的数据库,所以如果我们回到我们的 Superbase 项目,然后我进入我的表编辑器,你应该看到这个文档表,所以左侧边栏表编辑器,你会看到这个新的文档表,你可以看到它有我们在 schema 文件中定义的所有列类型,所以如果这个步骤出现错误,很可能是因为你在 .env.local 文件中输入了错误的数据库 URL,也许你没有替换密码等等,所以如果你在那里遇到错误,通常 99% 的时间就是这样,但话虽如此,这就是我们现在开始构建 RAG 管道所需的一切,我们已经在 Superbase 中设置了向量数据库,再次,我们只是使用一个相当标准的 Postgres 和 PG Vector 设置,这是我用于所有项目的方法,包括个人项目、工具和实验,以及生产级的东西,你知道,我有各种生产级应用程序,有实际的真实用户,它们使用相同的设置,这是一种非常标准的事情,我们已经涵盖了一些细节,所以我们将继续,主要关注构建,但你应该确保你完成到这一步,然后我们将继续下一课,也就是实际构建 RAG 管道,我们在这一部分所做的将与我们在第二部分所做的非常相似,所以我们将实际构建我们的 RAG 管道,构建 RAG 管道的一个好处是,一旦你设置好一次,它就非常快速地再次设置好,因为你可以复制粘贴以前使用过的代码,你可以调整一些东西来更适合你的用例,但好处是,每一个 RAG 管道基本上都会有相同的你可以一遍又一遍地重用的基础,所以让我们进入我们的项目,看看我的意思,我们将大量引用我们在创建 RAG 系统时编写的代码,如果你没有跟随第二部分,那完全没关系,我们将在这一部分稍微快一点地讨论它,而且因为我们有所有的参考代码,你仍然可以很好地跟随,所以我们要做的就是,我们要进入我的 RAG 应用中的这个 lib 文件,我们将创建一个名为 rag 的文件夹,所以这里将包含所有这些实用函数,我们将把它组织成三个部分,我们将有一个 generate 文件夹,这里是一些生成函数,比如我们的嵌入生成和我们的上下文完成,最终的 API 调用到 OpenAI,它实际上会得到最终答案,我们想要两者,我们将需要一个 retrieval 文件夹,这里将有一些更偏向检索的函数,比如我们的查询优化等等,最后,我们将有一个 processing 文件夹,这里将有一些我们的,我们的,我们的分块,文本分割器,所以这里我们将实际使用我们安装的 Tiktoken 库来标记我们上传的文本,这样我们就可以将它们分割成 500 个 token 的块,这样对于非常非常长的条目或任何东西,它就不会,你不会有一个 15,000 个文本的文件被嵌入,你想把它分开,再次,我们在第二部分涵盖了这些技术,我们将把它们串联起来,稍微讨论一下,这样我们就可以构建一个完整的端到端 RAG 管道,我们将在下一课中使用 UI 来构建,所以让我们开始完成生成,所以我们要做的就是,正如我所说的,我们将首先创建文件,所以让我们创建一个 generate-completion.ts 文件,我们想要,这实际上不应该是 processing,所以我将把它拖到 generate,我们将有一个 generate-embeddings.ts 文件,所以让我们把它放大一点,这样你就可以看到完整的文件路径,所以在你的 generate 文件夹中,你应该有一个 generate-completion 和一个 generate-embeddings 文件,所以让我们开始处理嵌入,如果我们实际上去第二部分的参考代码,你将在这个仓库中拥有这个第一个文件,generate-embeddings,我们将复制函数,我们只想复制函数,如果你想保留注释,为了方便你回顾代码,那也完全没问题,所以我们将这样做,然后回到我们的新文件,我们将粘贴进去,我们将在文件的最顶部用一个叫做 use server 的指令来标记它,这将让我们的应用程序知道我们想在服务器端运行这段代码,而不是客户端,客户端在浏览器上,服务器端在服务器上,你可以看到 Cursor Tab 实际上准确地预测了我们所需要的,所以我将点击 Tab,这只是导入了 OpenAI SDK,然后我们只是设置好了,实际上在后台发生的是,OpenAI 使用我们 .env.local 文件中的环境变量,好处是它会自动提取,假设你将那个值命名为 OPENAI_API_KEY 等于你的密钥,这正是我们设置项目的方式,所以你将没问题,你实际上不需要在那里输入 API 密钥,你会看到,我们将使用与第二部分完全相同的函数,我们将继续使用小型模型,再次,这只是为了保持成本低,再次,我们的维度需要与我们在 schema 文件中设置的匹配,所以我们参考 schema 文件,256 这里,256 在嵌入函数那里,砰,我们都设置好了,我们将如何在我们的代码中调用这个函数,我们将提供文本字符串的数组,并将其传递给 API,OpenAI 使用他们的 text-embedding-3-small 模型对我们的文本进行编码,将其编码为 256 个浮点维度,以获取向量嵌入,然后我们返回这些值,所以这相当直接,generate-completion 文件夹或文件将非常简单,所以如果你实际上进入 -rag-prompting,这实际上将与我们将在这里使用的非常相似,所以我们要做的就是,我将复制那段代码,然后我将去这里,我们将复制这个 async main 函数,实际上我们需要比嵌入函数更多的调整,所以让我们这样做,首先我们需要做的是,你可以看到 Cursor Tab 再次准确地使用 Tab 补全,所以我们需要 use server 指令,我们需要导入 OpenAI 并初始化客户端,我们再次在每个文件中初始化客户端,只是为了保持简单,我们要做的就是导出这个,导出 async function,我们将称之为 generate-completion-with-context,它将接受两个参数,它将接受 context,这是一个字符串,它将接受 input,也就是用户的输入,作为一个字符串,然后,这将是所有的代码,你可以删除它,直到 completion 代码,所以你可以删除到那里,我们将改变一些事情,我们将实际改变,我们将保留 model gpt-4o-mini,再次,你可以使用任何你想要的模型,我们将使用 mini,因为它最便宜,这对于测试总是很好,我们将 temperature 设置为零,这样我们的答案会更确定性,我们将 max_tokens 设置为 1000,所以模型可以输出的最大 token 数是 1000 个 token,我们将稍微改变系统提示,再次,如果你想构建一个更高级的工具,这是一个很好的扩展目标,让这个提示更复杂,针对特定用例进行定制,随意发挥,我们将保持简单,我们将说,根据提供的上下文回答,然后我们将换行,然后是 context:,然后在下一行,我们将实际放入上下文,所以我们将有一个满足该标准的系统提示,然后对于用户消息,我们将使用 input 值,我们实际上不会流式传输它,我们将返回 completion,你需要返回 completion.choices[0].message.content,然后我们还需要删除 stream: true 来消除那个小错误,所以这将是,我们将能够用我们获得的任何上下文调用这个函数,再次,我们获得的上下文将是我们的检索管道的最终结果,这就是将传递给用户查询的东西,它将命中 OpenAI completion API,它将命中 GPT-4o mini,它将构建这些消息,然后我们将得到一个响应,我们将得到消息的文本,所以这就是生成函数的作用,这就是第一步,第二步是,我们实际上需要做一些处理函数,所以我们将做两件事,第一个函数是,我们需要一个 split-text 函数,所以让我们回到我们的文件夹,我们将做 split-text.ts,我们还想要一个 process-document 文件,再次,在 processing 文件夹中再有两个文件,所以这将是,如果我们回到 split-text 文件,我们将这样做,我们将用 use server 来标记它,我们希望它在服务器端运行,这是 Tiktoken 的工作要求,我们将导入一个叫做 get_encoding 的辅助函数,来自 Tiktoken,我们将在 text-splitter 函数中使用它,所以让我们初始化一个叫做 split_text 的函数,它接受一个参数,一个叫做 text 的字符串,我们将在函数中做的是,而不是在第二部分,我们根据单词计数分割文本,我们将根据 token 计数分割,这很酷,所以我们将要做的是,在这个特定的例子中,我们将把所有块分割成 500 个 token 的块,人们有很多不同的方法来分割他们的文档,所以我们可能会在最后更多地讨论这一点,但我想在这里教你的是如何根据 token 进行分割,因为这是生产级 RAG 系统中最流行的分割技术之一,所以我们将要做的是,我们将创建一个叫做 chunks 的字符串数组,我们将把它设置成一个空数组,接下来我们将定义一个 chunk_size,我建议在 500 到 1000 之间,这可能是你将看到的两个最常见的值,如果你设置了一个特定的限制,再次,很多人会以不同的方式分割,比如基于语义、标题或换行符,但为了简单起见,我们将只做 500 个 token 的块,你需要做的是,你需要初始化这个 encoding 变量,这将定义我们将使用的编码器,在这种情况下,我们将使用 cl100k_base,这是一个你可以使用的分词器,它与 OpenAI 模型相关,我们将要做的是,我们实际上需要用 try-catch 包裹我们的代码,因为我们将要做的是,我们将运行一些代码,然后在最后,我们将做这个 finally,在最后,我们实际上需要运行 encoding.free(),它基本上结束了编码序列,我们将在 try 块中创建一个叫做 tokens 的变量,我们将获取 encoding,encoding 有一个叫做 encode 的辅助函数,我们将编码我们的文本,这将对我们给这个函数的任何文本进行分词,我们可以这样做,我们可以计数,我们将创建一个 for 循环,我有一个非常基本的 for 循环,它只是迭代每个 token,我们将调用一个叫做 chunk_tokens 的变量,我们将对 tokens 调用 slice,这基本上就是 slice,它将每 500 个块切片,我们将在这里推送一个新的块到 chunks 数组,这就是它所做的,它迭代我们的编码值,它计算,我这里有 500 个块或 500 个 token 吗?如果没有,继续,否则我们想推送那个值,所以我们将 chunk.push,我们不需要这个返回值,你可以看到我们将得到一个错误,啊,我称之为 chunk,这应该是 chunks,所以我们将每 500 个 token 迭代一次,我们将推送一个新的块,所以这将把文本变成 500 个 token 的块,而不是一个大块,这对于学习 RAG 系统非常重要,因为你知道,假设你有一个 100,000 个 token 的 PDF,你需要把它分成很多块,所以分块,分割文本,我们在第二部分涵盖了它,但我们只是将单词改为 token,所以我知道有很多技术内容,但希望你能理解,如果你在 Cursor 中工作,可以问一些问题,如果你迷路了,但我们将继续,对于 process-document 函数,这里的想法是,我们想构建一个函数,它将接受我们在 UI 中输入的文本,它将使用我们刚刚编写的文本分割函数将其分割,它将嵌入所有这些不同的块

然后,它将把这个上传到我们的数据库,好的,所以我们将继续,我们再次将使用服务器,我们希望它在服务器端运行,我们将在这里做一个异步函数,处理文档,再次,这将接受一个名为文本的单个参数,这里是标准的,嗯,我们需要做三个步骤,好的,所以让我们从分块开始,好的,所以我们需要做的第一件事是创建一个名为 chunks 的变量,我们将要做的就是,我们将等待,我们将在这里获取那个 split text 函数,好的,所以我们将导入我们刚刚在这里编写的这个函数,我们将最终使用它,好的,我们将传入我们的文本,所以这将要做的是,它将把这个字符串分成 500 个 token 一块的块,当然除了最后一个,因为你知道,你不会在最后正好有 500 个,嗯,你接下来需要做的是,你实际上需要嵌入这些块,嗯,为了获得这些嵌入,我们可以创建一个 embeddings 变量,现在我们可以终于使用我们的 embeddings generate embeddings 函数了,我们可以直接传入 chunks,我们从 split text 获得的 chunks,所以你可以看到这些东西是如何开始协同工作的,这有点好,嗯,看起来嗯,我可能在这里犯了一个类型错误,非常快,让我弄清楚这个错误是什么,我马上回来,好的,我意外犯的错误是,我我我我需要保留那个 return chunks,我不小心把它删掉了,所以请确保你回到你的 split text 文件,嗯,并且在该 try 块的底部,你想要返回 chunks,然后如果你回到 process documents,你会看到那里消失的波浪线,嗯,如果你正在使用,嗯,eslm,嗯,我认为这个设置会自动为每个人完成,但是,嗯,是的,嗯,所以让我们实际上继续进行第三步,也就是将它插入到我们的数据库中,好的,所以我们将使用 db.insert,我们想插入到我们构建的 documents 表中,我们将使用的值是,我们将映射这些 chunks,所以我们实际上想要获取 chunks 和它们的索引,在这种情况下,这就是 I 代表的索引,然后对于这些记录中的每一个,嗯,将会发生的是 content 将等于那个 chunk 值,然后 embedding embedding 将是那个特定 chunk 索引处的 embeddings,所以这里相当直接,好的,我们将获取这些的导入,对,我们将在这里导入 document table,好的,所以再次,DB 将来自,嗯,我们之前做的 DB 设置,嗯,在这一点上,你有一个 process document 函数,所以这是一个,这是一个有点长的课程,因为我们在这里做很多不同的事情,我们最后要做的是,我们将在这里构建我们需要为我们的 retrieval 文件夹的辅助函数,我们将需要四个,让我们看看,三个或四个,我们将做四个,我们将在这里做四个文件,我们将需要 optimize query.ts,再次,这些将与我们在第二部分所做的非常相似,因为正如我所提到的,我们需要 retrieve documents.ts,rerank documents documents.ts 和 run rag pipeline TTS,所以正如我在本课开头提到的,我们将使用大量与我们在 retrieval 中使用的相同的东西,一旦我们设置了 rag 系统,一旦你可以重用很多那些,所以这就是我们将要做的,好的,所以让我们实际上从 optimize query 开始,我们将跳回第二部分,回到参考代码,你可以看到文件四,我们实际上将基本上复制,相同的东西在这里,所以我将实际复制,我们将复制注释,给你们一些注释,我们将把它粘贴在这里,再次,你将,嗯,我们实际上将,嗯,导入 open app from AI,我们实际上也将在这一点上使用 use server,我们希望它在这里在服务器端运行,好的,再次,这将完成我们的查询优化,我不会在这里详细介绍,因为我们在第二部分已经完成了,但是,嗯,这里的想法是,我们正在接受我们的输入,我们的用户输入,我们正在尝试将该输入优化为某种查询,我们可以嵌入,以获得更好的可检索结果,所以再次,我们涵盖了,嗯,为什么,以及背后的原理,所以我们将继续前进,再次,我们想要,我们想要专注于实现,我将添加的一件事是我们没有在第二部分添加的是以下内容,我将在这里放几个例子,因为正如我在第二部分提到的,我们看到的一个事情是,我们从模型获得的输出并不总是最好的,所以你可以做的一件事是在生产环境中,好的,当你试图让 rag 系统真正出色时,你可以做所谓的 few shot prompting,这基本上就是我们给模型一些例子,所以在这个例子中,我们有六个例子,你可以看到一些非常基本的用户输入,然后是一个箭头,后面跟着我们希望它们是什么的结果,所以你可以看到我们是如何处理的,你知道,告诉我关于 iPhone 的最新消息,我们只是将其缩小,我们使用像 GPT 4L mini 这样的廉价模型来优化查询,以获得更好的结果,我们可以实际传递给嵌入模型,它只是让,嗯,检索步骤更加准确,如果你这样做,好的,那就是 optimize query,让我们继续进行下一个,retrieve document 函数,好的,所以我们将做 retrieve documents,我们想在这里做什么,嗯,我们将做一些与我们在第二部分使用我们的 retrieval 函数所做的非常相似的事情,但实际上我们将稍微简化它,因为这次我们实际上不会进行元数据过滤,只是为了去掉一个不适用于我们用例的步骤,如果你想把它作为一个延伸目标,嗯,这是你可以肯定做的事情,但是我们将重用输入参数和 min similarity 参数,所以如果我们实际上跳回到,嗯,让我们看看,我们在哪里实际做到这一点,从第二部分检索数据函数,在参考代码文件夹中,这将是,第三个文件,我们实际上将从,这个 const similarity 东西这里开始,一直到 return documents 这里,我们将回到我们的文件,并粘贴到函数中,需要做一些调整,好的,所以我们将删除 name,就像我说的,我们不做元数据过滤,我们将需要调整我们的 where 子句,我们正在做一个条件 where 子句,我们可以实际上删除最后一个 similarity 检查之前的所有内容,然后当然,我们将需要做,嗯,快速更改,将 fact table 更改为 documents table,我们显然在使用不同的 documents table,我们不再使用 name 值,所以我们可以删除那个,然后你的文件在那时应该看起来像这样,好的,这就是你的函数应该看起来的样子,从高层次来看,我们正在传入我们的用户输入,我们正在传入一些可选参数,这是一个文档限制,默认为 10,以及一个可选的最小相似度分数,默认为 3,再次,你可以根据你的用例,根据你喜欢你的系统设置的方式来调整这些值,但是我们将保持这些值与我们在第二部分所做的相同,以保持一致性,然后我们正在做的是,我们正在计算输入的 embeddings,在这种情况下,这将是任何优化的查询,好的,所以它将被传递到这个函数,我们将为其生成一个嵌入,然后我们将生成,相似度分数,将嵌入的优化查询与我们向量数据库中的所有内容嵌入进行比较,然后我们将检索 10 个,在这种情况下,使用 10 的默认值,10 个最相似的文档,然后我们将返回它们,所以再次,我们在上一节中已经讲过了,所以我们将继续进行下一步,也就是我们完整 rag pipeline 的第三步,在这种情况下,它将是 optimize query,然后 retrieve documents,然后 rerank documents,所以在这种情况下,我们可以直接回到第二部分的参考代码,我们只需要抓取,文件五,rerank documents,我们只需要抓取 rank documents 函数,整个函数,好的,这个代码到这个,我们将回到我们的文件,我们将把它粘贴在这里,我们需要做几件事,好的,这将是另一个,另一个 use server 函数,所以我们将运行 coher API,这实际上是错误地导入了 coher,我们终于从 cursor 获得了幻觉,这实际上是 coher AI 的 coher 客户端,然后当然,我们需要初始化东西,无论出于何种原因,你实际上必须在这里放入 token,当你初始化 coher 客户端时,使用环境变量,我不知道这是否是我的设置方式的错误,但是请注意,对于 open AI,无论出于何种原因,你不需要这样做,对于 coher,你需要,所以请确保你在这里添加它,然后这将与我们之前使用的几乎相同,唯一的例外是,我们实际上将删除那个 name 参数,所以我们实际上可以删除那里的 name,以及上面的 name,因为我们只使用 content,在第二部分,我们使用 name 和 content,在第三部分,我们只使用 content 字段,这将运行我们从 retrieve documents 函数获得的文档,然后将运行 coher reranking API,以进一步,优化我们的检索器结果,从最相似到最不相似,然后最后,我知道这变得越来越长,伙计们,我们快结束了,再次,有些人希望你们通过这一点加速,因为我们在第二部分做了完全相同的事情,但是显然我们需要重复,以确保每个人都跟上我们,然后在 run rag pipeline 函数中,再次,这将是服务器函数,use server,我们将做 export async function run rag pipeline,这只会接受任何用户查询,他们将接受那个单一的用户查询,它将运行我们整个 rag pipeline,所以我们需要做的第一件事是,optimize query,我们终于将利用那个 get optimized query 函数,当然,我们将查询作为单个参数传递给该函数,让我们实际记录下来,这样我们就可以,嗯,当我们运行我们的应用程序时,我们可以实时看到那些结果,然后我们需要获取第二步,也就是我们将在我们的向量数据库上运行的基本相似性搜索,我们将传入两个参数,我们将传入那个优化的查询,然后我们将做一个限制,我们实际上将做一个 25 的限制,所以我们将实际覆盖那个 10 的默认值,在这个例子中,我们将获取 25 个,我们甚至可以做的是,让我们做 min similarity,让我们变得更慷慨一些,让我们使用 0.2,这样它们可以不那么相似,因为我们获得了更多的记录,让我们记录下这两件事,我们将记录下那些文档,然后只是它们的长度,这样我们就可以看到我们实际上得到了多少,记住,仅仅因为我们在这里定义了 25,并不意味着我们一定会得到 25 个,我们可能没有 25 条记录在我们的表中,在运行的时候,你知道,25 个可能不一定符合我们设定的标准,那个最小相似度分数,所以我们可能得到更少,我们将看看,最后,我们需要运行那些结果,那些文档,我们将称之为 ranked results,我们实际上需要运行所有那些文档,以及那个优化的查询,通过 ranker,我们要做的是,我们将获取五个,我们将覆盖它,并运行,这个,用五个,我们将记录下最终结果,然后当然,我们想要返回 ranked results,所以我们刚刚完成了所有这些,我们实际上做了四个,我们做了八个不同的文件,所以相当多的工作,但这整个 rag pipeline,它将接受用户查询,它将优化它,它将通过你的向量数据库中的余弦相似度进行基本相似性搜索,它将检索那些文档,基于我们设定的这些参数,然后它将把那些文档放入一个 ranker,再次,我们使用 coheres rank API 来完成,然后在那之后,我们将拥有那些 ranked results,我们将拥有来自我们数据库的 ranked records,希望是那五个最相似的,假设我们获得了完整的 fetch 结果,我们可以将那些结果馈送到 completions 函数中,以实际获得答案,所以再次,我知道这很多,你可能,想要参考第二部分,了解很多事情,如果你想要更深入的讨论,对某些步骤更深入的解释,但是那就是我们的 rag pipeline,那就是我们将在下一课中使用的,所以让我们实际上开始构建 UI,这通常是大家最喜欢的部分,所以我们将终于连接一个 rag pipeline,我们将连接这个你构建的东西到一个实际工作的 front end,这里的想法是,我们需要我在这节课开始时提到的两个基本部分,我们将需要左边的文档上传器,然后我们将使用右边的 AI 聊天,所以我们需要那两样东西,所以我们将在这里做的是,我们将进入我们的,嗯,进入我们的项目,所以在这个 rag app 中,在这个 app 文件夹中,这就是你的客户端 app 代码将要存放的地方,所以如果你实际上打开 layout,打开 page,我们将在这些两个文件中工作,我们需要两个自定义组件,我们自己制作,所以我们在 components 文件夹中,你需要制作两个文件,你需要做 AI chat.tsx,你也需要做一个 document uploader.tsx,所以我们需要制作这两个组件,以便我们可以将它们导入到我们的 page 中,并实际拥有一个像样的应用程序来实际使用,所以我们将在这里做两件事,我们将把它分成两部分,所以我将向你展示一种使用 AI 工具来原型化这种情况下的 UI 的好方法,这是一个非常有价值的工具,叫做 b0,你可以用得很好,这是 verell 的,所以就像我们使用他们的 AI SDK 一样,如果你学过我们的 app 课程,这就是我们用于部署的东西,等等,而 verel 是,他们有一个像 chat gbt 这样的应用程序,专门用于开发问题,所以如果你熟悉类似 cla artifact 的东西,或者 GPT,我相信他们称之为 canvas 或类似的东西,他们称之为 canvas 或类似的东西,我们在这里做的是,我们可以说,我需要一个文本区域来上传文档,我需要将一些文本复制粘贴到那个巨大的文本区域中,我需要能够点击上传按钮,我们可以发送这个,而 v0 将要做的是,我相信你应该能够免费尝试所有这些,我相信他们有一个消息限制,所以这是一个我实际上付费的专业工具,我从中获得了巨大的价值,你可以在右边看到,我们正在实时获得一个可以在我们的项目中使用的组件,而且很酷的是,它不仅生成 UI,而且如果我们说,太棒了,把它放在屏幕左边,右边,我们需要一个基本的 AI 聊天界面,你会在这里看到,然后 v0,我们可以实际上迭代这个我们得到的预览,而 v0 将为我们做的基本上是编写我们所需的所有代码,一旦它完成,我们就可以直接将代码复制到我们的 app 中,我将向你展示如何做到这一点,这是一个非常棒的工具,我强烈推荐它,如果你是专业人士,或者正在大量构建这些工具,我相信 vzer pro 就像每月 20 美元,这也不是赞助,它只是,它是我个人使用的工具之一,我认为它已经变得如此之好,我认为它是,如果你是专业人士,或者与之相关,我认为它值得付费,我认为你将获得巨大的价值,你可以看到,我们基本上拥有了我们实际在 app 中使用的两个组件,对,所以我想做的一件事是,我希望它占据与 chat 相同的高度,所以我只需要在 v0 中问,对,我只需要说,请让 document upload 占据页面的全部高度,就像 chat 一样,我们希望它一直向下,如果你看到了示例应用程序,你知道,参考应用程序,你也可以查看代码,我们两个都在占据那个完整的高度,所以我们可以继续迭代,如果我们想手动迭代,当然,我们可以直接复制代码,但是我要做的是,我们将直接复制过来,只是为了向你展示如何在 app 中使用它,对,所以我们可以做什么,当预览加载时,好的,所以现在这是整个页面,我们可以去到上面的代码选项卡,我们可以,看起来它为我们制作了两个,我猜我们要用的是这个,这很有趣,这可能是一个小错误,但是你可以下载文件,你可以复制它,所以在这个例子中,我们将复制这段代码,然后我们将把它粘贴到我们的 document uploader 组件中,然后当然,要在这里使用 uploader,我们需要进入我们的 page.tsx,我们将删除所有那些样板代码,再次,那只是渲染那种入门模板代码的 page 代码,我们需要做的是,我们将把这个包装在一个 div 中,我们将使用 document upload document upload,然后如果我们回到 Local Host 3000 并刷新,我们应该看到那个 document upload 组件,你可能会想的一个问题是,为什么是这个布局,我们可以回到 v0,去到那个 page,我们将复制这段代码,我们将用这段代码替换我们的 page 代码,当然,导入在我们的 app 中会略有不同,所以我们需要做的是,我们还需要去确保抓取那个 AI chat 代码,所以让我们复制它,把它放在 AI chat 中,这样我们也有那段代码,让我们获取我们的 util,然后如果我们回到 Local Host 3000,你会看到我们有 v0 为我们生成的这个漂亮的 कोड,我们可以直接在聊天界面中工作,并获得这些相当不错的入门组件,当然,你知道,如果你想进入这段代码,好的,优点是,此时你拥有代码,所以你可以进去,你可以调整样式,等等,但是这里的重点是,这是一个构建 UI 的好方法,我们只是想偷偷地加入这个小技巧,我知道这门课程的重点是 Rag 和 retrieval 和构建它,但是,你经常需要做的一件事是,当你构建 rag 工具,检索工具时,你需要实际为它构建 UI,如果你不是,你知道,如果你不是一个前端开发人员,等等,你只是,你知道,也许你是一个业务分析师,试图使用 cursor 来快速启动一个 rag 工具,或者其他什么,而所有这些 AI 工具让你第一次做到这一点,那么 bz 在很多方面都是你的前端开发伴侣,所以,我们要做的其中一件事是,为了让我们的参考应用程序和我们的应用程序保持一致,我们现在已经向你展示了如何自己做,我们将直接复制现有的代码,以保持简单,所以我们将进入那个参考应用程序,我们将首先抓取 page 代码,所以我将把参考应用程序的 page 代码复制到我的 rag app page,你可以看到它很相似,唯一改变的是一些样式,然后我们将关闭它,然后我们将去参考应用程序的两个组件,AI chat 和 document uploader,所以让我们开始,复制它们到我们的文件,所以我们将复制粘贴整个文件,我们将用这里的东西替换所有东西,所以我将抓取参考 document uploader,并将其替换到我们的 app document uploader 中,你应该看到,看起来我有一个轻微的导入错误,我们是否命名了其他东西,让我们检查一下,所以我们在 lib 文件夹中,在 Rag 和 generate lib rag generate 中,所以我们称之为 generate 而不是 generation,好了,消除了那个错误,现在如果我们回到 Local Host,你会看到或多或少看起来一样,但是我们现在拥有的是,我们实际上已经将我们的 rag 系统连接到了这个 UI,所以我们现在需要做的是,实际上解释它是如何连接起来的,这样你实际上就有了一些关于正在发生的事情的背景,所以我们将这样做,让我关闭几个文件,只是为了简化事情,所以从高层次来看,我们有我们的 page,这是我们顶级的 page,左边是 document uploader,右边是 AI chat 组件,很简单,然后接下来在左边,我们有我们的 document uploader,所以我们这里有什么呢,嗯,我们有一个巨大的文本区域,再次,你会记得之前我们从 Shaden 导入或安装了 button,input 和 text area 组件,所以我们的组件在这里做的是,它只是导入了我们从 Shaden 库获得的那些组件,再次,一个很好的方式来获得免费的开箱即用的代码,这就是它如此受欢迎的原因之一,所以你有一个小标题,我们有那个文本区域,所以当我们处理文本时,如果你想,你知道,说你有一个巨大的 Word 文档或其他什么,你知道,把它粘贴在这里,点击上传按钮,你会看到这个上传按钮调用这个 handle upload 函数,那么 handle upload 函数做什么呢,它获取文本区域中的任何文本,然后将其运行到我们的 process document 函数中,所以你记得这是将我们输入的文本嵌入、分块并插入数据库的管道,所以我们将在下一课中测试它,但我只是想提前谈谈正在发生的事情,所以 document uploader,你在这里粘贴你的文本,所以我们甚至可以做的是,我可以说,嗨,我的名字是 McKay,所以这将获取这个文本,当我们点击上传文档时,它将运行,它将分块,在这种情况下,这显然将少于 500 个 token,所以它不需要分块任何东西,它将只是一个块,它将嵌入它,然后它将上传到我们的数据库,你可以测试它是否有效的方式是,如果你去你的数据库,我实际上只需要刷新它,你会看到在我点击那个按钮之后,我们现在有了嗨,我的名字是 McKay,我们有了 embedding,一切都为检索过程设置好了,而检索过程由右侧处理,由 AI chat 区域处理,所以让我们进入那个组件,谈谈那里发生的事情,所以我们正在做的一件事,我们正在跟踪我们的消息,我们的输入,这只是前端状态的东西,如果你想更详细地了解它,我们有我们的 app building 课程,你可以去学习,它教你所有这些东西的来龙去脉,但是因为我们有那些参考资料,我不会,我不会把它变成一个完整的关于前端开发的讲座,我们将保持,我们将保持相当轻松,但是我们可以做的是,我们可以实际发送,嗯,我们可以实际发送消息,所以这里发生了什么,如果我说,我的名字是什么,我的名字是什么,这里发生的是,那个值正在被跟踪在这个输入中,当我们实际点击这个发送按钮时,所以如果我回到,在这里,你会看到我们在底部有一个按钮,你可以看到那个发送文本,你可以看到当这个按钮被点击时,它正在运行这个 handle send message 函数,那么 handle send message 函数做什么呢,所以从高层次来看,我们要做的第一件事是,我们将根据我们的输入运行我们的 rag pipeline,所以我们将获取这个,我的名字是什么,我甚至会加一个问号,我的名字是什么,然后我们点击发送,它将被传递到这个 handle send message 函数,并将运行我们的 rag pipeline,正如你所记得的,我们的 rag pipeline 由三个步骤组成,首先,我们将获取这个文本,我们将优化那个查询,所以它将运行查询优化,下一步是,我们将把优化的查询传递给我们的 retrieve documents 函数,我们将获取 25 个最相似的记录,最小相似度分数为 0.2,在这种情况下,我们目前数据库中只有一个记录,所以它只会获取一个,但是,接下来会发生的是,我们将获取那些检索到的文档和优化的查询,我们将运行我们的文档通过 coheres rerank API,在那时,我们将从最相似到最不相似,获得五个最相似的文档,然后我们将返回它们,如果我们回到我们的组件,你会看到它被设置为 relevant docs,一旦我们有了 relevant docs,再次,relevant docs 只是我们 rag pipeline 的最终输出,我们可以将其作为上下文传递给我们在几课前编写的 generate completion with context 函数,所以会发生的是,来自那五个来源的上下文将被插入到这里,所以发生的是,AI 模型,在这种情况下是 GPT 40 mini,它得到一个系统提示,说根据提供的上下文回答,以及任何上下文,来自那五个最相关的文档,以及我们的输入,在这种情况下,它将是,我的名字是什么,然后如果我们发送它,你会看到我们得到一个漂亮的 UI,显示那些来源,所以如果我实际上点击那个按钮并打开它,你会看到数字一,再次,我们现在只有一个来源,嗨,我的名字是 m,然后你可以看到,蓝色的,AI 回答,你的名字是 McKay,所以那就是我们的 rag pipeline,与这两个组件一起工作,完全构建好,所以我们将继续,实际上看看一个更高级的例子,所以为了证明这一点有效,我将要做的一件事是,我们可以获取,这是 2024 年美国大选的维基百科条目,我使用这个例子的唯一原因是,这是一个相当近期发生的事件,它不会在 LLM 的训练数据中,模型可能在几个月前就训练好了,所以它不会知道谁赢得了选举,所以,我们可以复制粘贴整个维基百科条目,你可以看到我选择这个是因为它是一个当前事件,它有一个非常长的维基百科条目,我们当然不会涉及政治,但这只是一个很好的 rag 例子,当然,我们可以粘贴它,你会看到我们有一个巨大的 P,这是整个维基百科页面,在我们的文本框中,显然这是一个非常长的文本文档,这个东西需要分块,你可以使用的很酷的工具之一是这个 open AI tokenizer,他们在 open eye 网站上有一个很酷的例子,所以如果我们实际上也将这段文本粘贴到 tokenizer 中,你会看到这段文本是 37514 个 token,这意味着它将被分成很多块,所以我们可以做的是,如果我们点击上传文档,它将运行那个 document uploader 函数,它将处理文档,如果我们去我们的 rag 数据库,嗯,我们将这样做,让我给它一个好的刷新,你会看到我们现在有了所有这些块,所以发生的是,因为那个维基百科页面有太多 token,它不得不运行,我们的 text splitter 不得不将东西分成很多 500 个 token 的块,来分割那个东西,你可以看到我们总共有 76 条记录,加上我们从示例中获得的一条,而这个很酷的是,它为每个记录生成了一个嵌入,现在我们可以做的,你知道,如果我们说,嗯,让我们看看,让我们看看我们是否能找到一些非常小众的东西,比如,埃里克·奎萨说了什么,让我们实际重置,这样我们就有了一个重置按钮,这个人说了什么,让我们看看我们的 rag pipeline 有多好,让我们点击发送,你可以看到我们有五个来源,我们可以隐藏它们,你可以看到这基本上是从维基百科页面中提取的精确段落,所以它能够运行我们的查询优化,然后是我们的基本检索,然后是我们的文档重新排序,以给我们这五个来源,记住,每个来源都将是 500 个 token,因为我们是这样分块的,所以我们将这五个来源作为上下文,发送给 AI 模型,它正在生成答案,正如你所看到的,我们使用了非常非常细微的东西,你知道,其中的一个微小细节,它实际上做对了,这很令人印象深刻,如果我们尝试另一件事,嗯,我们可以说,嗯,让我们去这里,让我们做一些非常基本的事情,比如,选举计数是多少,选举计数是多少,这是一个相当标准的查询,人们会想问,你可以看到,我没有使用选举,我没有引用文档,它只是继续查找结果,你可以在这里看到,这是最相关的,它似乎正在提取选举票数,你可以看到,它变成了 312 和 226,让我们验证一下是否正确,我猜这会排在前面,312 对 226,非常有趣,所以你可以看到我们的 rag pipeline 正如我们想要的这样工作,我们正在处理一个非常长的文本文档,37000 个 token,我们正在上传它,我们正在处理它,以便将其嵌入到我们的向量数据库中,然后我们能够成功地对最相关的文档进行排序,并实际获取我们想要的信息,所以,让我们,让我们远离政治,怎么样,再次,我们只是使用它,因为它是一个当前事件,它有一个非常长的维基百科条目,让我们做一些事情,比如,让我们把火星的维基百科页面放进来,然后稍微,一些不太爆炸性的东西,我们可以复制这个,我只是想再做一个例子,因为它能够演示你如何真正开始分离事物,所以让我们获取整个条目,让我们上传它,我需要一点时间来运行,如果我们再次进入我们的数据库,刷新它,我们应该看到更多记录,所以看起来我们获得了大约 30 条更多记录,我们可以做的是,好的,让我们在这里找到一个小事实,比如,火星上的环境条件是什么,比如,火星表面有多少热量传递,让我们重置一下,火星表面有多少热量传递,所以我们可能会期望答案得到,一些与这个段落有关的东西,所以让我们看看它是否能做到,你可以看到,由于大气稀薄,火星表面的热量传递很少,你可以看到,我在这里找不到我的位置,行星表面的热量传递很少,你可以看到,它在谈论大气压力,所以它基本上能够做到,通过我们有多少记录,104 条记录,再次,我们有整个选举的东西,我们有这个测试记录,我放进去,我们有火星数据库,它仍然能够获取最相关的结果,并给我们想要的答案,所以这是一个真实的 rag pipeline 的绝佳例子,任何时候你构建检索工具,这都是你将要工作的绝对基础,再次,会有一些不同的事情,你知道,你想要尝试的一件事,我把它留给你作为一个延伸目标,因为我认为这就是你真正学习的方式,对于很多 document uploader 的东西,你不想复制粘贴文本,你想要,你知道,获取一个,你知道,Google 文档或其他什么,下载它,然后可能上传到,某种文件上传器,而不是复制粘贴,所以学习如何做到这一点,并采用我们已经讨论过的所有原则,并学习如何使用不同的文档加载器,可能想按语义而不是按 token 计数来分割,这些是一些,嗯,用例相关的变量,你将需要习惯,但是我们之所以这样构建项目,是因为我们在这里做的方式将是,一级,基础层,你可以带到每个项目中,并且可以在任何地方使用,你知道,所有其他小东西,那将是与用例相关的,所以我们处理了检索工具的绝对核心,它将是某种上传文档的方式,将其放入向量数据库,某种实际使用,你知道,一个 AI 聊天界面或某种查询机制,以便你实际上可以运行一个 rag pipeline,获取最相关的结果,将其发送给 AI 模型,并获得你想要的结果,所以希望你能把你在这一节中学到的所有东西都用来构建你自己的应用程序,你自己的工具,以便你可以以各种有用的方式使用 retrieval 和 rag,希望你觉得这有帮助,这就是完成的项目,再次,我建议如果你有时间,尝试一些延伸目标,尝试,嗯,尝试 PDF,尝试,嗯,而不是按 token 计数分块,你知道,尝试按换行符,新段落等进行分块,所以希望每个人都觉得这很有帮助