在此应用程序中,所有示例文档的录入都是通过flask create-index 命令触发的。该命令的实现在 api 目录中的 app.py 文件中,它只需从main() 数据 目录中导入 index_data.py 模块并调用其 函数,就能完整导入存储在 data.json 文件中的所有文档。
文件结构
每份文件的结构如下
name文件标题url:外部网站托管文件的 URLsummary文件内容简介content:文件正文created_on创建日期updated_at:更新日期(如果文件从未更新,则可能缺失)category文件类别:文件的类别,可以是github、sharepoint或teamsrolePermissions:角色权限列表
本示例应用程序使用content 字段作为要索引的文本,并添加name,summary,url,category 和updated_at 作为相关元数据。
下面的 Python 代码片段展示了如何导入文档:
这里使用 Python 标准库中的json 模块来读取数据文件,然后从 Langchain 中为每个包含的文档创建一个Document 对象。文件有一个page_content 属性,该属性定义了要转换成向量并进行搜索的内容,另外还有一些作为元数据存储的附加字段。metadata_keys 决定将源内容中的哪些字段存储为文档元数据。
根据您的摄取需求,可以改进或改变摄取方法。Langchain 项目提供了大量可供选择的文档加载器,可根据源内容的格式加以使用。
弹性学习稀疏编码模型 (ELSER)
此应用程序中使用的 Elasticsearch 索引被配置为自动为插入的所有文档创建稀疏向量嵌入。index_data.py中的install_elser() 函数会确保 ELSER 模型已安装并部署到正在使用的 Elasticsearch 实例上。
文本分割
这些文件中的content 字段很长,这意味着单一的嵌入将无法完全表示它。在处理大量文本时,标准的解决方案是将文本分割成较短的段落,然后获取单个段落的嵌入,并对所有段落进行存储和索引。
在这个应用中,我们使用了Langchain库中的RecursiveCharacterTextSplitter 类,并搭配 OpenAI 的tiktoken编码器,该编码器以标记为单位计算段落的长度,这与 LLM 使用的单位相同。
请看下面的示例,它演示了在应用程序中如何进行文本分割:
通过设置文本分割器的chunk_size 参数,可以控制分割后的段落长度。chunk_overlap 允许段落之间有一定程度的重叠,这通常有助于获得更好的嵌入。
在实际应用中,分流器使用以下参数初始化:
欢迎您更改这些值,看看更改会如何影响聊天机器人的质量。每次更改分割器配置时,都应运行flask create-index 命令重新生成索引。
请注意,在结合 ELSER 模型进行文本分割时,还有更多的注意事项需要注意。对于生产用例,您可能需要选择本教程中的另一种标记化方法。
文件存储
文件存储在 Elasticsearch 索引中。索引名称由ES_INDEX 环境变量控制,该变量在.env锉刀默认情况下,该索引的名称是workplace-app-docs 。
该应用程序使用ElasticsearchStore 类,该类是Langchain 中 Elasticsearch 集成的一部分,并使用 Python 官方 Elasticsearch 客户端库。
处理 Elasticsearch 索引的完整逻辑如下所示:
ElasticsearchStore.from_documents() 方法会导入workplace_docs 中存储的所有Document 实例,并将它们写入index_name 参数中给出的索引。所有操作都通过es_connection 参数中给出的客户端执行。
strategy 参数定义了使用该索引的方式。在此应用中,SparseVectorRetrievalStrategy 类表示要为每个文档维护稀疏向量嵌入。这将在索引中添加一个管道,通过请求的模型(本例中为 ELSER 版本 2)生成嵌入。
Elasticsearch 与 Langchain 的集成提供了其他策略,可根据使用情况使用。尤其是在使用密集向量嵌入时,可以使用ApproxRetrievalStrategy。