在nodejs中使用ElasticSearch(三)通过ES语义检索,实现RAG

RAG(Retrieval-Augmented Generation)是一种结合了信息检索和生成模型的技术,旨在提高生成模型的知识获取和生成能力。它通过在生成的过程中引入外部知识库或文档(如数据库、搜索引擎或文档存储),帮助生成更为准确和丰富的答案。 RAG 在自然语言处理(NLP)领域,特别是在对话生成、问答系统和文本摘要等任务中,具有非常重要的应用。它的核心思想是,生成模型不仅依赖于模型内部的知识,还可以在生成过程中动态地检索外部信息,从而增强生成内容的准确性和信息丰富度。

示例

下面这段代码实现了一个完整的工作流,涉及到以下几个关键步骤:

1. 创建 Elasticsearch 索引

  • 首先,代码检查是否已经存在名为 my_index4 的 Elasticsearch 索引。如果存在,则先删除该索引,确保创建的是新的索引。
  • 接着,创建一个新的索引 my_index4,并定义其映射(mappings):
    • id 字段是文本类型。
    • content 字段是 text 类型,并指定了 ik_max_word 分词器,用于中文分词。
    • content_vector 是 dense_vector 类型,表示用于存储文档的嵌入向量,并使用余弦相似度进行向量检索。

2. 加载和处理 PDF 文件

  • 使用 pdfjsLib 库加载本地的 PDF 文件(t.pdf),并从中提取文本内容。文本按页面分批读取,直到所有页面的内容都被提取出来。
  • 提取的文本内容会经过 sentence-splitter 库进行分句处理,将文档拆分成句子节点,便于后续的处理。

3. 嵌入向量化

  • 为了让文本内容可以进行语义检索,代码使用了 OpenAI 的 Embeddings API(调用 openai.embeddings.create)来生成文本的嵌入向量(embedding)。
  • 该嵌入向量将被存储在 Elasticsearch 中,以便后续进行基于向量的检索。
  • 将每个句子的文本与对应的嵌入向量一同批量插入 Elasticsearch 索引(my_index4)中。

4. 批量插入数据到 Elasticsearch

  • 为每一个分句生成一个 id(使用 UUID)以及 content(文本内容)和 content_vector(嵌入向量)。这些数据通过 es.bulk 方法批量插入到 Elasticsearch 中。
  • bulk 方法使用批量操作提高插入效率。

5. 查询和检索

  • 用户的查询被定义为 query(例子中是 'how many parameters does llama 2 have?')。
  • 使用混合检索方式进行查询,结合了 文本搜索match)和 向量检索knn):
    • 文本搜索:查找与查询文本最相似的文档。
    • 向量检索:将查询文本转换为嵌入向量,并通过 Elasticsearch 的 knn 查询来查找与查询向量最相似的文档。
    • num_candidates: 100 指定了检索时考虑的候选文档数量,以提高准确性和性能平衡。
  • Elasticsearch 会返回匹配度最高的前 5 条文档(size: 5)。

6. 生成回答

  • 基于 Elasticsearch 检索到的文档内容,构建一个提示模板 (promptTemplate),并向 OpenAI 提交请求,生成回答:
    • 提示模板包括用户的查询和从 Elasticsearch 中检索到的相关文档信息。生成的回答应该是基于已知信息的。
    • 如果检索结果中没有足够的信息来回答问题,生成的回答将是“我无法回答您的问题”。

7. 输出回答

  • 通过 get_completion 函数,OpenAI 生成最终的回答,并打印在控制台中。
import { Client } from 'npm:@elastic/elasticsearch';
import * as pdfjsLib from 'npm:pdfjs-dist';
import { split, TxtSentenceNodeChildren } from "npm:sentence-splitter";
import fs from "node:fs";
import process from "node:process";
import OpenAI from "npm:openai";
import { v4 } from "npm:uuid";
import 'npm:dotenv/config';const es = new Client({ node: 'http://localhost:9200' });const exists = await es.indices.exists({ index: 'my_index4' });if (exists) {await es.indices.delete({ index: 'my_index4' });
}await es.indices.create({index: 'my_index4',aliases: {'mi4': {},},mappings: {properties: {id: { type: 'text' },content: {type: 'text',analyzer: "ik_max_word",fields: { keyword: { type: 'keyword' } },},content_vector: {type: "dense_vector",dims: 1024,index: true,  // 启用对 content_vector 字段的索引功能similarity: "cosine", // 使用余弦相似度来评估不同向量之间的相似性},}},settings: {number_of_replicas: 1,number_of_shards: 1,},
});// 加载PDF文件
const uint8Array = new Uint8Array(fs.readFileSync('t.pdf'));
const pdf = await pdfjsLib.getDocument(uint8Array).promise;let article = '';
let finish = false;
let page = 1;
do {try {const res = await pdf.getPage(page++);const textContent = await res.getTextContent();const str = textContent.items.reduce((sum, item) => {if (!('str' in item)) {return sum;} else {return sum + item.str;}}, '');article += str;} catch {finish = true;}
} while (!finish);let PDFContent = split(article);const openai = new OpenAI({// apiKey: process.env.DASHSCOPE_API_KEY,apiKey: process.env.OPENAI_API_KEY,baseURL: process.env.OPENAI_BASE_URL}
);async function getEmbedding(text: string) {const response = await openai.embeddings.create({model: String(process.env.OPEN_MODEL_EMBEDDING),  // 使用嵌入模型input: [text],});return response.data;  // 返回向量
}PDFContent = PDFContent.flatMap((doc) => {if ('children' in doc && Array.isArray(doc.children)) {const tmp = doc.children.filter((text: { type?: string }) => text?.type === 'Str');// console.log(tmp);// const embedding = await getEmbedding(doc.raw);return tmp;} else {return null;}
}).filter(item => item) as TxtSentenceNodeChildren[];const operations = (await Promise.all(PDFContent.map(async (doc) => {try {const content = (doc as { value: string }).value;const content_vector = (await getEmbedding(content))[0].embedding;// console.log(content);return [{ index: { _index: "my_index4" } },{id: v4(),content,content_vector},];} catch {return false;}})
)).filter(item => item).flat(9);// 批量添加
await es.bulk({refresh: true,operations,
});async function get_completion(prompt: string, model: string = String(process.env.OPENAI_MODEL)) {const response = await openai.chat.completions.create({model,temperature: 0,messages: [{ "role": "user", "content": prompt }],});return response.choices[0].message.content;
}const query = 'how many parameters does llama 2 have?';// const context = await es.search({
//   index: 'my_index4',
//   body: {
//     knn: {
//       field: 'content_vector',  // 向量字段名
//       query_vector: (await getEmbedding(query))[0].embedding,
//       k: 5,                     // 返回最近邻数量
//       num_candidates: 100,      // 候选池大小(精度与性能的平衡)
//     },
//     _source: ['id', 'content'], // 返回的字段
//   }
// });// 混合检索
const context = await es.search({index: 'my_index4',body: {query: {bool: {should: [// 语义搜索部分{knn: {field: 'content_vector',query_vector: (await getEmbedding(query))[0].embedding,k: 50,//  定义了候选文档的数量。num_candidates: 100,那么 Elasticsearch 会首先从 100 个候选文档中选择最相似的。// 这个数量的选择会影响 KNN 查询的性能与准确性。较大的候选数量可以增加精度,但也会增加计算量。num_candidates: 100, boost: 0.5 // 权重}},// 文本搜索部分{match: {content: {query: query,boost: 0.5}}}]}},_source: ['id', 'content'],size: 5}
});console.log(context);
const promptTemplate = `
你是一个问答机器人。
你的任务是根据下述给定的已知信息回答用户问题。已知信息:
${JSON.stringify(context)}用户问:
${query}如果已知信息不包含用户问题的答案,或者已知信息不足以回答用户的问题,请直接回复"我无法回答您的问题"。
请不要输出已知信息中不包含的信息或答案。
请用中文回答用户问题。
`;console.log(await get_completion(promptTemplate));

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/24634.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【构建工具】Gradle Kotlin DSL中的大小写陷阱:BuildConfigField

在Android开发当中,BuildConfig是一个非常有用的功能,它允许我们在构建过程中定义常量,并在运行时使用它们。But!!当我们从传统的Groovy DSL迁移到Kotlin DSL时或者被Android Studio坑的时候,有一些细微的差…

AI如何改变传统工厂的生产模式?

随着第四次工业革命的浪潮席卷全球,制造业的数字化转型成为企业在竞争中脱颖而出的关键。过去,传统制造业往往依赖于大量的人工操作和低效率的管理流程,而如今,智能化、自动化、数据化已经成为未来制造业的必由之路。从车间到云端…

Redis

redis启动命令 默认端口启动redis: redis-server redis.windows.conf 指定端口9001和9002启动redis(需要新建配置文件,并修改配置文件port属性): redis-server .\redis-9001.conf redis-server .\redis-9002.conf 检查是否启动Redis &#…

洛谷 P8705:[蓝桥杯 2020 省 B1] 填空题之“试题 E :矩阵” ← 卡特兰数

【题目来源】 https://www.luogu.com.cn/problem/P8705 【题目描述】 把 1∼2020 放在 21010 的矩阵里。要求同一行中右边的比左边大,同一列中下边的比上边的大。一共有多少种方案? 答案很大,你只需要给出方案数除以 2020 的余数即可。 【答案提交】 …

ARM 处理器平台 eMMC Flash 存储磨损测试示例

By Toradex秦海 1). 简介 目前工业嵌入式 ARM 平台最常用的存储器件就是 eMMC Nand Flash 存储,而由于工业设备一般生命周期都比较长,eMMC 存储器件的磨损寿命对于整个设备来说至关重要,因此本文就基于 NXP i.MX8M Mini ARM 处理器平台演示…

14.二叉搜索树

二叉搜索树 1.概念 ⼆叉搜索树⼜称⼆叉排序树,它或者是⼀棵空树,或者是具有以下性质的⼆叉树: *若它的左⼦树不为空,则左⼦树上所有结点的值都⼩于等于根结点的值 *若它的右⼦树不为空,则右⼦树上所有结点的值都⼤于等于根结点…

8、HTTP/1.0和HTTP/1.1的区别【高频】

第一个是 长连接: HTTP/1.0 默认 短连接,(它也可以指定 Connection 首部字段的值为 Keep-Alive实现 长连接)而HTTP/1.1 默认支持 长连接,HTTP/1.1是基于 TCP/IP协议的,创建一个TCP连接是需要经过三次握手的…

【爬虫基础】第二部分 爬虫基础理论 P1/3

上节内容回顾:【爬虫基础】第一部分 网络通讯 P1/3-CSDN博客 【爬虫基础】第一部分 网络通讯-Socket套接字 P2/3-CSDN博客 【爬虫基础】第一部分 网络通讯-编程 P3/3-CSDN博客 爬虫相关文档,希望互相学习,共同进步 风123456789&#xff…

nss刷题5(misc)

[HUBUCTF 2022 新生赛]最简单的misc 打开后是一张图片,没有其他东西,分离不出来,看看lsb,红绿蓝都是0,看到头是png,重新保存为png,得到一张二维码 扫码得到flag [羊城杯 2021]签到题 是个动图…

清华大学DeepSeek文档下载,清华大学deepseek下载(完成版下载)

文章目录 前言一、清华大学DeepSeek使用手册下载二、清华大学DeepSeek使用手册思维导图 前言 这是一篇关于清华大学deepseek使用手册pdf的介绍性文章,主要介绍了DeepSeek的定义、功能、使用方法以及如何通过提示语设计优化AI性能。以下是对这些核心内容的简要概述&…

强化学习演进:GRPO 从何而来

强化学习(Reinforcement Learning, RL)是机器学习的一个分支,其核心是让智能体(Agent)通过与环境(Environment)的交互,学习如何采取最优行动(Action)以最大化…

树和二叉树

文章目录 树和二叉树1.树的概念1.1特点1.2基本概念 2.二叉树2.1二叉树的定义2.2特殊的树2.3 二叉树的性质2.4二叉树的存储 二叉树的遍历 树和二叉树 1.树的概念 树是一种非线性的数据结构,它是由n个有限结点组成一个有具体层次关系的集合 1.1特点 没有前驱结点的…

ubuntu离线安装Ollama并部署Llama3.1 70B INT4

文章目录 1.下载Ollama2. 下载安装Ollama的安装命令文件install.sh3.安装并验证Ollama4.下载所需要的大模型文件4.1 加载.GGUF文件(推荐、更容易)4.2 加载.Safetensors文件(不建议使用) 5.配置大模型文件 参考: 1、 如…

15.代码随想录算法训练营第十五天|(递归)110. 平衡二叉树,257. 二叉树的所有路径*,404. 左叶子之和,222.完全二叉树的节点个数[打卡自用]

15.代码随想录算法训练营第十五天|(递归)110. 平衡二叉树,257. 二叉树的所有路径*,404. 左叶子之和,222.完全二叉树的节点个数 给定一个二叉树,判断它是否是 平衡二叉树 示例 1: 输入&#xf…

GateWay

文章目录 创建网关配置路由规则工作原理 断言过滤器默认filter全局跨域 左边的是响应式网关,右边是传统网关(Servlet年代) 推荐左边的 需求 创建网关 在服务模块外 新建一个gateway模块 导入依赖,nacos和gateway和负载均衡 配置一下 这里网关默认占80…

十一、大数据治理平台总体功能架构

大数据治理平台的功能架构图中心主题:数据治理 核心重点是建立健全大数据资产管理框架,确保数据质量、安全性、可访问性和合规性。 大数据治理平台总体功能架构图 关键功能领域 1.数据资产平台(左侧) 此部分主要关注数据资产本身…

网络安全 机器学习算法 计算机网络安全机制

(一)网络操作系统 安全 网络操作系统安全是整个网络系统安全的基础。操作系统安全机制主要包括访问控制和隔离控制。 访问控制系统一般包括主体、客体和安全访问政策 访问控制类型: 自主访问控制强制访问控制 访问控制措施: 入…

PDF扫描档智能方向识别:多模型投票机制的实践测试 救活古典书籍

2025-02-22 20:10物联全栈123 尊敬的诸位!我是一名物联网工程师。关注我,持续分享最新物联网与AI资讯和开发实战。期望与您携手探寻物联网与 AI 的无尽可能 RAG知识库搭建的过程中,扫描档pdf的支持和准确率一直是个大家都不愿主动提起的事情…

初会学习记录

【25初级会计《实务》】第一章:权责发生制举例_哔哩哔哩_bilibili 务实: 第一章 (1)会计概念,职能和目标: 2025年2月25日: (2)会计假设: 2025年2月26日: (3)会计核算基础: 202…

【FL0091】基于SSM和微信小程序的社区二手物品交易小程序

🧑‍💻博主介绍🧑‍💻 全网粉丝10W,CSDN全栈领域优质创作者,博客之星、掘金/知乎/b站/华为云/阿里云等平台优质作者、专注于Java、小程序/APP、python、大数据等技术领域和毕业项目实战,以及程序定制化开发…