《Learning Langchain》阅读笔记8-RAG(4)在vector store中存储embbdings

什么是 vector store?

与专门用于存储结构化数据(如 JSON 文档或符合关系型数据库模式的数据)的传统数据库不同,vector stores处理的是非结构化数据,包括文本和图像。像传统数据库一样,vector stores也能执行创建、读取、更新、删除(CRUD)以及搜索操作。

Vector stores解锁了各种各样的用例,包括可扩展的应用程序,这些应用程序利用AI来回答有关大型文档的问题,如图2-4所示。

在这里插入图片描述

目前,有大量的向量存储提供商可供选择,每一家都擅长不同的功能。你应根据应用程序的关键需求来进行选择,这些需求包括多租户支持、元数据筛选能力、性能、成本以及可扩展性。

尽管 vector store是专门用于管理向量数据的细分数据库,但使用它们仍存在一些缺点:

  • 大多数 vector store相对较新,可能无法经受时间的考验。

  • 管理和优化 vector store可能存在相对陡峭的学习曲线。

  • 管理一个独立的数据库会增加应用程序的复杂性,并可能消耗宝贵的资源。

幸运的是,向量存储的能力最近已经通过 pgvector 扩展,被扩展到 PostgreSQL(一个流行的开源关系型数据库)。这使你可以使用已经熟悉的同一个数据库,来同时支持事务表(例如用户表)和向量搜索表。

使用 PGVector

要使用 Postgres 和 PGVector,你需要按照以下步骤进行设置:

  • 确保在您的计算机上安装了 Docker

  • 在终端中运行以下命令; 它将在您的计算机上启动一个在端口 6024 运行的 Postgres 实例:

    docker run \
    --name pgvector-container \
    -e POSTGRES_USER=langchain \
    -e POSTGRES_PASSWORD=langchain \
    -e POSTGRES_DB=langchain \
    -p 6024:5432 \
    -d pgvector/pgvector:pg16
    
  • 打开您的 Docker 仪表板容器,您应该在 pgvector-container 旁边看到一个绿色的运行状态。

  • 将连接字符串保存到代码中以供以后使用;我们以后需要使用它:

    postgresql+psycopg://langchain:langchain@localhost:6024/langchain
    

如果你是刚开始接触向量数据库或 LangChain,强烈建议先用 Docker,后期再考虑更复杂的本地部署或云服务集成(如 AWS RDS + pgvector)。Docker 是一个“容器”工具,它可以把应用程序和它运行所需要的一切(比如操作系统、依赖、库等)打包在一个“箱子”里运行。

假设你要运行一个 PostgreSQL + PGVector 的数据库,你需要:

  • 安装 PostgreSQL

  • 配置好数据库用户和端口

  • 安装 pgvector 插件

  • 保证版本兼容

这些都很麻烦,对吧?

但用了 Docker,只需要一条命令,它就帮你:

  • 自动下载带 pgvector 的 PostgreSQL

  • 自动配置好用户密码端口

  • 独立运行在它自己的“容器”里,不影响你电脑上其他软件

现在我们从第一步安装Docker开始,

1. 安装 Docker

去官网下载并安装:Docker官网

2. 运行 Docker 容器

```bash
docker run \
--name pgvector-container \
-e POSTGRES_USER=langchain \
-e POSTGRES_PASSWORD=langchain \
-e POSTGRES_DB=langchain \
-p 6024:5432 \
-d pgvector/pgvector:pg16
```

这会创建一个名为 pgvector-container 的容器,在本地的 6024 端口 启动 PostgreSQL 数据库,数据库名和账户密码均为 langchain。

3. 确认数据库是否运行正常

打开 Docker 桌面,确认容器 pgvector-container 显示为绿色状态(Running)。

在这里插入图片描述

4. LangChain 中连接 pgvector
确保你已经安装了必要的包:

    pip install langchain psycopg2-binary sqlalchemy

上面的步骤全都配置结束,大约花费50分钟。

现在我们来在langchain中连接pgvector

记得首先pip install langchain-postgres

from langchain_community.document_loaders import TextLoader
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_postgres.vectorstores import PGVector
from langchain_core.documents import Document
import uuid# Load the document, split it into chunks
loader = TextLoader("./test.txt", encoding="utf-8")
raw_documents =  loader.load()text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
documents = text_splitter.split_documents(raw_documents)# embed each chunk and insert it into the vector store
model_name = "sentence-transformers/all-mpnet-base-v2"
model_kwargs = {'device': 'cpu'}
encode_kwargs = {'normalize_embeddings': False}embeddings_model = HuggingFaceEmbeddings(model_name=model_name,model_kwargs=model_kwargs,encode_kwargs=encode_kwargs
)connection = 'postgresql+psycopg://langchain:langchain@localhost:6024/langchain'db = PGVector.from_documents(documents, embeddings_model, connection=connection)

报错:ModuleNotFoundError: No module named ‘psycopg’

你这个错误信息非常明确,是因为 psycopg 无法正确加载 libpq 库(PostgreSQL 的底层通信库),所以在导入 PGVector 时报错。

你使用的是新版的 psycopg(也叫 psycopg3),它和旧版 psycopg2 不一样,需要本地系统有 PostgreSQL 的 C 库支持(libpq)。

改写为以下用法:

  1. 确保你安装了官方 vectorstore 依赖

    pip install langchain psycopg2-binary sqlalchemy

  2. 修改你的 import 语句

    换成官方推荐的 PGVector 接入方式:from langchain.vectorstores.pgvector import PGVector

from langchain_community.document_loaders import TextLoader
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain.vectorstores.pgvector import PGVector
from langchain_core.documents import Document
import uuid# Load the document, split it into chunks
loader = TextLoader("./test.txt", encoding="utf-8")
raw_documents =  loader.load()text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
documents = text_splitter.split_documents(raw_documents)# embed each chunk and insert it into the vector store
model_name = "sentence-transformers/all-mpnet-base-v2"
model_kwargs = {'device': 'cpu'}
encode_kwargs = {'normalize_embeddings': False}embeddings_model = HuggingFaceEmbeddings(model_name=model_name,model_kwargs=model_kwargs,encode_kwargs=encode_kwargs
)connection = 'postgresql+psycopg2://langchain:langchain@localhost:6024/langchain'db = PGVector.from_documents(documents=documents,embedding=embeddings_model,connection_string=connection,  # ✅ 这里改成 connection_stringcollection_name="my_collection",  # 可选,但推荐加
)

这个查询会在你之前加载的文档中,返回与“为什么会有“上下文窗口”(context window)”这个问题最相似的段落,如果它能正常返回结果,就说明数据确实已经存入 pgvector 并可用啦。

# 查询一下看看有没有数据,k表示打印几条结果
results = db.similarity_search("为什么会有“上下文窗口”(context window)", k=2)for i, doc in enumerate(results, 1):print(f"【第{i}条结果】", doc.page_content)
【第1条结果】 > [!NOTE] **为什么会有“上下文窗口”(context window)这个限制?**
>
>1. 资源限制:计算成本高>   每次你和大语言模型对话,它其实是在处理一个非常复杂的数学计算过程,背后是巨大的矩阵运算。 >   输入越长,占用的 显存(GPU memory) 和 计算时间 就越多。
>输入+输出的长度太长,会让显卡扛不住,或者处理速度变得非常慢。
>所以模型必须设置一个“上限”——这就是 context window,也叫“上下文窗口”。>   
>
>2. 模型设计:Transformer 架构的限制>   大语言模型(如 GPT)基于 Transformer 架构。>  Transformer 是靠“注意力机制”处理每一个 token 的。>  每加一个 token,就要考虑它和之前所有 token 的关系,所以计算量是平方级别增长的(O(n²))。> 这意味着:>  上下文越长,注意力机制越慢,成本越高,效果也可能下降。
>3. 训练方式决定了这个限制> 模型训练时,是在固定长度的上下文窗口中学习的,比如 2048、4096 或 8192 个 token。> 如果你测试时突然给它一个比训练时长得多的输入,模型可能根本不知道怎么处理。>   所以训练时设置了窗口,使用时就要遵守这个规则。
>    
>    ---------------------
>一个简单类比:
>
>想象你在读一本书,但你只能记住最近的一页内容(上下文窗口),太久以前的内容就记不清了。
>
>模型也是这样,它记得的“最近内容”数量是有限的。
【第2条结果】 > [!NOTE] **为什么会有“上下文窗口”(context window)这个限制?**
>
>1. 资源限制:计算成本高>   每次你和大语言模型对话,它其实是在处理一个非常复杂的数学计算过程,背后是巨大的矩阵运算。 >   输入越长,占用的 显存(GPU memory) 和 计算时间 就越多。
>输入+输出的长度太长,会让显卡扛不住,或者处理速度变得非常慢。
>所以模型必须设置一个“上限”——这就是 context window,也叫“上下文窗口”。>   
>
>2. 模型设计:Transformer 架构的限制>   大语言模型(如 GPT)基于 Transformer 架构。>  Transformer 是靠“注意力机制”处理每一个 token 的。>  每加一个 token,就要考虑它和之前所有 token 的关系,所以计算量是平方级别增长的(O(n²))。> 这意味着:>  上下文越长,注意力机制越慢,成本越高,效果也可能下降。
>3. 训练方式决定了这个限制> 模型训练时,是在固定长度的上下文窗口中学习的,比如 2048、4096 或 8192 个 token。> 如果你测试时突然给它一个比训练时长得多的输入,模型可能根本不知道怎么处理。>   所以训练时设置了窗口,使用时就要遵守这个规则。
>    
>    ---------------------
>一个简单类比:
>
>想象你在读一本书,但你只能记住最近的一页内容(上下文窗口),太久以前的内容就记不清了。
>
>模型也是这样,它记得的“最近内容”数量是有限的。

db.similarity_search() 这个方法将通过以下过程找到最相关的文档(之前已对其进行索引):

  • 搜索查询(在这种情况下是一个词或短语)将被发送到嵌入模型以生成其向量表示。

  • 接着,系统会在 Postgres 数据库中运行查询,找出与该向量最相似的 N 个已存储的嵌入(本例中为2个)。

  • 最后,它会提取每个相似嵌入所对应的原始文本内容和元数据

  • 模型将返回一个文档列表,并按照它们与查询向量的相似度高低进行排序——最相似的排在前面,依此类推。

[!NOTE] 如何找出与该向量最相似的 N 个已存储的嵌入?

你现在已经进入了「向量数据库的核心机制」——**相似度搜索(vector similarity search)**的本质。

简要回答:是通过 向量之间的相似度计算公式 + pgvector 插件中的索引与比较操作符 实现的。

步骤拆解如下:

查询向量的生成(embedding)

你输入一段查询(query,比如“向量数据库的作用是什么?”),LangChain 会:

query_embedding = embeddings_model.embed_query("向量数据库的作用是什么?")

这个 query_embedding 是一个 高维向量数组,比如:

[0.123, -0.341, ..., 0.442] # 维度可能是 384、768、1024...

和数据库中所有嵌入进行相似度对比

PostgreSQL + pgvector 使用 SQL 语句执行相似度计算,例如:

SELECT *, embedding <#> '[0.123, 0.341, ..., 0.442]'
FROM langchain_pg_embedding_my_collection
ORDER BY embedding <#> '[...]'
LIMIT 2;

<#> 是 pgvector 提供的 “余弦距离”操作符。值越小,表示越相似。

相似度通常用 余弦相似度(cosine) 或 欧氏距离(L2)

查找最相似的 Top-N 条

执行 SQL 时用 ORDER BY + LIMIT N,就能找出最相似的 N 条记录。

可视化向量数据库中的数据:使用pgAdmin

假如我们想可视化向量数据库中的数据,可以下载单独的可视化软件,比如:

  • pgAdmin

  • dbeaver

这里我选择第一个免费开源软件的windows版。

下载安装好后,连接到server。

随后我想要查看一下向量数据库存储的是什么数据,接下来手把手带你操作如何查看:

方法一:图形化方式(适合浏览数据)

步骤:

1.左侧树状结构中,点击展开:

  • langchain → Databases → langchain

  • 然后继续展开:Schemas → public → Tables

2.找到你插入文档时自动创建的表,名称通常是类似于你设定的 collection_name,例如:

langchain_pg_embedding

3.右键点击这个表 → 选择 View/Edit Data → All Rows

然后你就可以看到你之前插入的向量、文本内容、metadata 等字段了!

一共有16条数据:
在这里插入图片描述

方法二:用 SQL 查询(适合精准操作)

步骤:
在 pgAdmin 顶部点击:🧾 SQL(或按快捷键 F5快速执行)

输入并执行下面这句 SQL:

SELECT * FROM langchain_pg_embedding LIMIT 10;

在这里插入图片描述

可以看到已经显示前10条记录了。

接下来介绍几条常用的SQL语句(以表名langchain_pg_embedding为例):

1.查询(Read)

-- 查询所有数据
SELECT * FROM langchain_pg_embedding;-- 查询包含关键词 “向量” 的记录(全文内容)
SELECT * FROM langchain_pg_embedding
WHERE document LIKE '%向量%';-- 查询某个 ID 的向量内容
SELECT * FROM langchain_pg_embedding
WHERE id = 'your-vector-id';
  1. 插入(Insert)
    一般不建议手写插入向量(除非你很熟),但可以这样手动插入一个文档:
INSERT INTO langchain_pg_embedding (document, metadata, embedding)
VALUES ('这是一段测试文本','{"source": "manual"}','[0.1, 0.2, 0.3, ...]'  -- 注意这里向量必须是 float[],长度必须匹配模型维度
);
  1. 删除(Delete)
-- 根据 ID 删除
DELETE FROM langchain_pg_embedding
WHERE id = 'your-vector-id';-- 删除所有数据(⚠️危险操作)
DELETE FROM langchain_pg_embedding;
  1. 修改(Update)
-- 修改 metadata
UPDATE langchain_pg_embedding
SET metadata = '{"source": "updated"}'
WHERE id = 'your-vector-id';-- 修改文档内容
UPDATE langchain_pg_embedding
SET document = '这是新的文档内容'
WHERE id = 'your-vector-id';

我们的数据库中有这些字段名:

在这里插入图片描述

还可以使用langchain语句向现有数据库添加更多文档。在这个例子中,我们使用可选的 ids 参数为每个文档分配标识符,这使我们可以在以后更新或删除它们。

让我们看这个例子:

# 生成两个唯一的 UUID 作为向量的主键(id)
# ids 是可选的,如果你不传,LangChain 会自动生成 UUID
# 但传 ids 的好处是后面可以用:db.delete(ids=["your-id"])  # 删除指定向量
# 这样你能精确管理向量库中的内容
ids = [str(uuid.uuid4()), str(uuid.uuid4())]# 将新的文本片段(两个句子)嵌入,并插入到你已经连接的 pgvector 数据表中
db.add_documents([Document(page_content="there are cats in the pond",metadata={"location": "pond", "topic": "animals"},),Document(page_content="ducks are also found in the pond",metadata={"location": "pond", "topic": "animals"},),],ids=ids, 
)
['0cdf295d-5e3c-4757-bc8c-457c05927eb7','a7a67123-a7bf-4b16-990b-56d430a78f67']

我们的table中的embedding数量从16增加到了18。

在这里插入图片描述

我们在这里使用的db.add_documents() 方法将遵循与PGVector.from_documents() 类似的过程:

  • 为传递的每个文档创建嵌入,使用选择的模型。
  • 将嵌入、文档元数据和文档文本内容存储在 Postgres 中,以便进行搜索。

删除操作的例子:

db.delete(ids=['0cdf295d-5e3c-4757-bc8c-457c05927eb7'])
db.delete(ids=['a7a67123-a7bf-4b16-990b-56d430a78f67'])

这样就从向量数据库中删除指定 ID 的嵌入向量记录,就是我们刚刚插入的那两条。

总结

本小节我们学习了如何在vector store中存储embeddings,并且使用了PostgreSQL数据库。

通过docker连接数据库,还通过插件PGVector这个为 PostgreSQL 数据库设计的向量(embedding)存储与相似度搜索插件,使得你可以在 PostgreSQL 中直接存储向量,并进行高效的相似度检索(vector similarity search)。

最后还使用了免费的可视化PostgreSQL数据库的软件pgAdmin对数据库的tables中的数据进行查看。

收获满满的一章。感谢阅读!下一节我们来讲讲如何跟踪文档的更改。

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

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

相关文章

用api的方式调用本地下载好的大模型(以llama为例,不是ollama!!!)

目录 1、创建虚拟环境2、激活虚拟环境3、安装相关库4、编写脚本&#xff08;test.py&#xff09;调用脚本5、bash中测试通信完美结果 1、创建虚拟环境 conda create -n myenv python3.12 -y2、激活虚拟环境 conda activate myenv3、安装相关库 pip install vllm fastapi uvi…

算力网络(CFN)在跨校联合科研中的应用:安全性挑战与联邦调度实践

引言&#xff1a;科研协作的算力困境 上海交通大学与麻省理工学院联合开展的高能物理模拟实验&#xff0c;因算力资源分配不均导致部分节点连续72小时处于空转状态。这个典型案例揭示了当前跨机构科研协作的痛点&#xff1a;‌算力资源无法实现安全可信的细粒度共享‌。算力网…

高防IP+CDN组合:电商大促的“双保险”防护方案

引言 电商大促期间&#xff0c;平台流量呈爆发式增长&#xff0c;既要应对瞬时激增的访问量&#xff0c;又要防范黑客趁机发起的DDoS攻击、恶意爬虫等威胁。单一防护手段往往难以兼顾性能与安全&#xff0c;而高防IPCDN组合通过“流量清洗加速分发”的双重机制&#xff0c;为电…

# 构建词汇表:自然语言处理中的关键步骤

构建词汇表&#xff1a;自然语言处理中的关键步骤 在自然语言处理&#xff08;NLP&#xff09;任务中&#xff0c;词汇表&#xff08;Vocabulary&#xff09;是文本数据预处理的核心组件之一。它将文本中的单词或字符映射为数值索引&#xff0c;从而让计算机能够理解和处理语言…

SQL进阶知识:七、数据库设计

今天介绍下关于数据库设计的详细介绍&#xff0c;并结合MySQL数据库提供实际例子。 数据库设计是确保数据库能够高效、安全地存储和管理数据的关键环节。良好的数据库设计可以提高查询性能、减少数据冗余、确保数据完整性&#xff0c;并简化数据维护。以下是关于数据库设计的详…

python如何取消word中的缩进

在python-docx中&#xff0c;取消缩进可以通过将相应的缩进属性设置为None或0来实现。以下是取消不同类型缩进的方法&#xff1a; 取消左缩进 from docx import Documentdoc Document(existing_document.docx)for paragraph in doc.paragraphs:# 取消左缩进paragraph.paragr…

Docker拉取镜像代理配置实践与经验分享

Docker拉取镜像代理配置实践与经验分享 一、背景概述 在企业内网环境中&#xff0c;我们部署了多台用于测试与学习的服务器。近期&#xff0c;接到领导安排&#xff0c;需在其中一台服务器上通过Docker安装n8n应用程序。然而在实际操作过程中&#xff0c;遭遇Docker官方镜像库…

【数字图像处理】立体视觉基础(1)

成像 成像过程&#xff1a;三维空间坐标到二维图像坐标的变换 相机矩阵&#xff1a;建立三维到二维的投影关系 相机的使用步骤&#xff08;模型-视图变换&#xff09;&#xff1a; &#xff08;1&#xff09;视图变换 &#xff08;2&#xff09;模型变换 &#xff08;3&…

实验4:列表与字典应用

目的 &#xff1a;熟练操作组合数据类型。 试验任务&#xff1a; 1. 基础&#xff1a;生日悖论分析。如果一个房间有23人或以上&#xff0c;那么至少有两个人的生日相同的概率大于50%。编写程序&#xff0c;输出在不同随机样本数量下&#xff0c;23 个人中至少两个人生日相同的…

c++之网络编程

网络编程&#xff1a;使得计算机程序能够在网络中发送和接受数据&#xff0c;从而实现分布式系统和网络服务的功能。 作用&#xff1a;使应用程序能够通过网络协议与其他计算机程序进行数据交换 基本概念 套接字&#xff08;socket&#xff09;&#xff1a; 套接字是网络通信…

【Harmony_Bug】forEach + asyncawait 的异步陷阱

一、问题描述 今天在做一个RDB的小项目时&#xff0c;遇到一个问题&#xff0c;因为没报错其实也是不算是BUG&#xff0c;以下描述时我就直接说关键点&#xff0c;其他代码忽略。 我的数据模型初始化有六条数据如图 在持久化层&#xff0c;通过initUserData这个方法执行插入。…

大肠杆菌诱导蛋白时OD600=0.6-0.8添加IPTG的思考-实验操作系列-009

一、为什么用OD600表示菌液浓度&#xff1f; 1. 光密度与吸光值的关系 OD600是指在600纳米波长下的光密度&#xff08;Optical Density&#xff09;&#xff0c;也就是通过细菌悬浮液的光的吸收程度。根据比尔-朗伯定律&#xff0c;光密度与溶液中光学活性物质&#xff08;如…

OpenHarmony - 小型系统内核(LiteOS-A)(十),魔法键使用方法,用户态异常信息说明

OpenHarmony - 小型系统内核&#xff08;LiteOS-A&#xff09;&#xff08;十&#xff09; 十四、魔法键使用方法 使用场景 在系统运行出现无响应等情况时&#xff0c;可以通过魔法键功能确定系统是否被锁中断&#xff08;魔法键也无响应&#xff09;或者查看系统任务运行状态…

CUDA编程之Grid、Block、Thread线程模型

一、线程模型:Grid、Block、Thread概念 ‌1. 层级定义‌ ‌Thread(线程)‌ CUDA中最基本的执行单元,对应GPU的单个CUDA核心(SP)。每个线程独立执行核函数指令,拥有独立的寄存器和局部内存空间‌。 ‌Block(线程块)‌ 由多个线程组成(通常为32的倍数),是逻辑上的并…

实战交易策略 篇十九:君山居士熊市交易策略

文章目录 系列文章熊市三大特征熊市操作思维强势重势,弱势重质抢反弹重要前提和五大原则反弹逃顶操盘其他炒股的至高境界力戒“三进三出”八大心理误区八大戒律股市不败之法系列文章 实战交易策略 篇一:奥利弗瓦莱士短线交易策略 实战交易策略 篇二:杰西利弗莫尔股票大作手…

Flutter IOS 真机 Widget 错误。Widget 安装后系统中没有

错误信息&#xff1a; SendProcessControlEvent:toPid: encountered an error: Error Domaincom.apple.dt.deviceprocesscontrolservice Code8 "Failed to show Widget com.xxx.xxx.ServerStatus error: Error DomainFBSOpenApplicationServiceErrorDomain Code1 "T…

【计算机视觉】CV实战项目 - 深入解析基于HOG+SVM的行人检测系统:Pedestrian Detection

深入解析基于HOGSVM的行人检测系统&#xff1a;从理论到实践 技术核心&#xff1a;HOGSVM检测框架HOG特征原理SVM分类器 项目架构与数据准备INRIA Person数据集目录结构 实战指南&#xff1a;从零构建检测系统环境配置完整训练流程检测应用 关键技术问题与解决方案1. 难例挖掘不…

day01_编程语言介绍丶Java语言概述丶开发环境搭建丶常用DOS命令

编程语言介绍 ‌编程语言是一种用于人与计算机之间通信的语言&#xff0c;允许程序员编写代码&#xff0c;这些代码告诉计算机要执行哪些操作‌。编程语言可以被视为计算机可以理解并执行的指令集合&#xff0c;它是一种标准化的交流技巧&#xff0c;用于向计算机发出指令。‌…

告别默认配置!Xray自定义POC开发指南

文章涉及操作均为测试环境,未授权时切勿对真实业务系统进行测试! 下载与解压 官网地址: Xray GitHub Releases 根据系统选择对应版本: Windows:xray_windows_amd64.exe.zipLinux:xray_linux_amd64.zipmacOS:xray_darwin_amd64.zip解压后得到可执行文件(如 xray_linux_…

C语言编程--17.有效的括号

题目&#xff1a; 给定一个只包括 ‘(’&#xff0c;‘)’&#xff0c;‘{’&#xff0c;‘}’&#xff0c;‘[’&#xff0c;‘]’ 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序…