在 LangChain Go 中使用 Google AlloyDB for PostgreSQL 实现向量相似度检索
【免费下载链接】langchaingoLangChain for Go, the easiest way to write LLM-based programs in Go项目地址: https://gitcode.com/GitHub_Trending/la/langchaingo
导读
本文基于 examples/google-alloydb-vectorstore-example 示例,完整讲解如何在 LangChain for Go(langchaingo)中接入 Google Cloud 的 AlloyDB for PostgreSQL:从环境变量准备、连接池创建、向量表初始化,到 VertexAI 文本嵌入模型的接入、文档批量写入与带元数据过滤的相似度检索。阅读完本文后,你将掌握一套可直接复制运行的「AlloyDB + VertexAI Embeddings」语义搜索 Go 代码方案,并理解其背后的源码实现原理。
示例概览:在 AlloyDB 上做语义搜索
AlloyDB for PostgreSQL 是 Google Cloud 提供的托管式 PostgreSQL 兼容数据库,内置 pgvector 向量扩展,可原生存储与检索高维向量。LangChain Go 通过vectorstores/alloydb与util/alloydbutil两个包为其提供了完整支持,核心能力包括:
- 创建 AlloyDB VectorStore:先通过
alloydbutil.PostgresEngine建立到 AlloyDB 数据库的连接池,再初始化一张存放嵌入向量的数据表,最后基于 VertexAI 嵌入模型构建alloydb.VectorStore; - 初始化 VertexAI Embeddings:调用
llms/googleai/vertex包创建文本嵌入客户端; - 写入样例文档:向向量库中批量插入多条城市数据(城市名、人口、面积等元数据);
- 执行相似度检索:先做一次针对 "Japan" 的基础向量检索,再演示如何通过元数据过滤器(如
"area" > 1500)做条件筛选。
完整的可运行代码位于 google_alloydb_vectorstore_example.go,其模块依赖声明见 go.mod(依赖 langchaingo v0.1.14-pre.4,以及cloud.google.com/go/alloydbconn、cloud.google.com/go/vertexai、pgx/v5、pgvector/pgvector-go等底层库)。
前置准备:环境变量与权限
必须设置的 9 个环境变量
运行示例前需设置以下环境变量(AlloyDB 的相关取值可在 Google Cloud Console 的 AlloyDB 集群页面中找到),其中 8 个为必填,缺失时程序会在getEnvVariables函数中通过log.Fatal直接退出:
| 环境变量 | 说明 | 示例 |
|---|---|---|
PROJECT_ID | GCP 项目 ID | my-gcp-project |
GOOGLE_CLOUD_LOCATION | VertexAI 模型所在区域(cloud location) | us-central1 |
ALLOYDB_USERNAME | 数据库用户名 | postgres |
ALLOYDB_PASSWORD | 数据库密码 | your-password |
ALLOYDB_REGION | AlloyDB 集群所在区域 | us-central1 |
ALLOYDB_CLUSTER | AlloyDB 集群名称 | my-cluster |
ALLOYDB_INSTANCE | AlloyDB 实例名称 | my-instance |
ALLOYDB_DATABASE | 目标数据库名 | postgres |
ALLOYDB_TABLE | 向量表名 | cities |
设置命令示例:
export PROJECT_ID=<your project Id> export GOOGLE_CLOUD_LOCATION=<your cloud location> export ALLOYDB_USERNAME=<your user> export ALLOYDB_PASSWORD=<your password> export ALLOYDB_REGION=<your region> export ALLOYDB_CLUSTER=<your cluster> export ALLOYDB_INSTANCE=<your instance> export ALLOYDB_DATABASE=<your database> export ALLOYDB_TABLE=<your tablename>前置开通项
参照 vectorstores/alloydb/README.md 的 Quick Start 说明,使用本包前需要依次完成:
- 创建或选择 GCP 项目;
- 为项目启用结算(billing);
- 启用 AlloyDB API(
alloydb.googleapis.com); - 通过 Cloud SDK 完成应用默认凭证认证(
gcloud auth application-default login)。
包内对 Go 版本的要求为Go >= 1.22.0。
分步拆解:示例运行流程
第一步:创建 PostgresEngine 连接池
示例首先调用alloydbutil.NewPostgresEngine建立与 AlloyDB 的连接池:
pgEngine, err := alloydbutil.NewPostgresEngine(ctx, alloydbutil.WithUser(username), alloydbutil.WithPassword(password), alloydbutil.WithDatabase(database), alloydbutil.WithAlloyDBInstance(projectID, region, cluster, instance), alloydbutil.WithIPType("PUBLIC"), ) if err != nil { log.Fatal(err) }从源码看,util/alloydbutil/engine.go 中的createPool函数完成核心工作:它使用alloydbconn.NewDialer创建连接拨号器,将实例 URI 组装为projects/{project}/locations/{region}/clusters/{cluster}/instances/{instance}格式,并通过pgxpool.NewWithConfig构建连接池。WithIPType("PUBLIC")决定使用公网 IP(alloydbconn.WithPublicIP())还是私网 IP(alloydbconn.WithPrivateIP(),需设置PRIVATE),默认值为PUBLIC。
关于鉴权,engine.go 中的getUser支持三种方式:
- 同时提供
WithUser+WithPassword:直接使用账号密码认证; - 提供
WithIAMAccountEmail:使用 IAM 账号邮箱并启用 IAM 认证(alloydbconn.WithIAMAuthN()); - 都不提供时:通过
getServiceAccountEmail从应用默认凭证(ADC)自动获取 IAM 主体邮箱。
此外还提供了WithPool(pool *pgxpool.Pool)选项,可传入自建连接池(如连接 AlloyDB Omni 或自定义池化参数),详见 util/alloydbutil/options.go。
第二步:初始化向量表
通过InitVectorstoreTable创建用于存储向量的数据表,该操作只需在首次使用时执行一次:
vectorstoreTableoptions := alloydbutil.VectorstoreTableOptions{ TableName: table, VectorSize: 768, StoreMetadata: true, OverwriteExisting: true, MetadataColumns: []alloydbutil.Column{ {Name: "area", DataType: "int"}, {Name: "population", DataType: "int"}, }, } err = pgEngine.InitVectorstoreTable(ctx, vectorstoreTableoptions)需要注意VectorSize必须与所用嵌入模型的输出维度一致——示例使用的text-embedding-005输出 768 维,因此这里设置为 768。OverwriteExisting: true表示若表已存在则先DROP TABLE再重建。
根据 engine.go 的validateVectorstoreTableOptions与建表逻辑,各字段存在以下默认值:
| 字段 | 默认值 | 说明 |
|---|---|---|
SchemaName | public | 数据库 schema |
ContentColumnName | content | 文本内容列(TEXT NOT NULL) |
EmbeddingColumn | embedding | 向量列(vector(N)NOT NULL) |
MetadataJSONColumn | langchain_metadata | JSON 元数据列(StoreMetadata为 true 时创建) |
IDColumn.Name | langchain_id | 主键列名 |
IDColumn.DataType | UUID | 主键类型 |
建表时会先执行CREATE EXTENSION IF NOT EXISTS vector确保 pgvector 扩展存在,MetadataColumns中每个Column(含Name、DataType、Nullable)都会成为表中的独立元数据列,Nullable默认按零值处理(即 false 对应NOT NULL)。
第三步:初始化 VertexAI 嵌入模型
示例通过llms/googleai/vertex包初始化 VertexAI 客户端,再包装成embeddings.Embedder:
llm, err := vertex.New(ctx, googleai.WithCloudProject(projectID), googleai.WithCloudLocation(cloudLocation), googleai.WithDefaultModel("text-embedding-005"), ) if err != nil { log.Fatal(err) } e, err := embeddings.NewEmbedder(llm) if err != nil { log.Fatal(err) }这里使用的模型为 Google 的text-embedding-005,区域由GOOGLE_CLOUD_LOCATION指定。embeddings.NewEmbedder将 LLM 包装为统一的Embedder接口(提供EmbedDocuments与EmbedQuery),使得上层 VectorStore 无需关心具体嵌入模型实现。
第四步:创建 VectorStore
vs, err := alloydb.NewVectorStore(pgEngine, e, table, alloydb.WithMetadataColumns([]string{"area", "population"}), ) if err != nil { log.Fatal(err) }NewVectorStore位于 vectorstores/alloydb/vectorstore.go,内部通过 vectorstore_options.go 的applyAlloyDBVectorStoreOptions校验三个必填项(连接池、嵌入器、表名)并填充默认配置:默认 schemapublic、默认列名langchain_id/content/embedding/langchain_metadata、默认返回条数k=4、默认距离策略为余弦距离(CosineDistance)。
WithMetadataColumns是关键选项:它声明哪些元数据字段以独立列存储。结合 vectorstore.go 的generateAddDocumentsQuery可知,插入文档时这些字段会作为单独的 SQL 列写入,而其余元数据则会序列化为 JSON 存入langchain_metadata列。相比把所有元数据塞进 JSON,独立列在过滤检索时能获得显著的查询性能提升——这也是该包「Improved metadata handling」的核心设计之一。
第五步:批量写入文档
_, err = vs.AddDocuments(ctx, []schema.Document{ {PageContent: "Tokyo", Metadata: map[string]any{"population": 38, "area": 2190}}, {PageContent: "Paris", Metadata: map[string]any{"population": 11, "area": 105}}, {PageContent: "Sao Paulo", Metadata: map[string]any{"population": 22.6, "area": 1523}}, }) if err != nil { log.Fatal(err) }从 vectorstore.go 的实现看,AddDocuments的执行流程为:
- 抽取所有文档的
PageContent调用embedder.EmbedDocuments一次性生成向量; - 为每个文档生成 ID:优先读取
Metadata["id"],否则使用uuid.New()生成 UUID; - 将内容、向量、元数据组装为 INSERT 语句,并通过
pgx.Batch批量提交到连接池,保证写入效率。
第六步:相似度检索与元数据过滤
基础检索:
docs, err := vs.SimilaritySearch(ctx, "Japan", 0) if err != nil { log.Fatal(err) } fmt.Println("Docs:", docs)带过滤条件的检索:
filter := "\"area\" > 1500" filteredDocs, err := vs.SimilaritySearch(ctx, "Japan", 0, vectorstores.WithFilters(filter)) if err != nil { log.Fatal(err) } fmt.Println("FilteredDocs:", filteredDocs)第二个参数0表示 top-K 条数,由于 VectorStore 默认k=4且SimilaritySearch内部忽略该参数(见 vectorstore.go),实际返回条数由WithK选项控制。
从源码看,SimilaritySearch的底层 SQL 形如:
SELECT content, langchain_metadata, cosine_distance(embedding, '<query_vector>') AS distance FROM "public"."<table>" WHERE "area" > 1500 ORDER BY embedding <=> '<query_vector>' LIMIT $1::int;其中cosine_distance、<=>运算符由距离策略决定。默认的余弦距离对应CosineDistance;包内还提供了欧氏距离(Euclidean,l2_distance/<->)与内积(InnerProduct,inner_product/<#>),可通过WithDistanceStrategy切换,三种策略定义于 distance_strategy.go。过滤条件opts.Filters会被直接拼接进WHERE子句(仅当非空时生效),因此可以书写任意的 SQL 条件表达式。
检索结果的Score字段即距离值,Metadata由langchain_metadataJSON 列反序列化得到(见 processResultsToDocuments)。
如何运行示例
进入示例目录后执行:
go run google_alloydb_vectorstore_example.go运行成功后,控制台会依次打印基础检索结果(Docs:)与过滤检索结果(FilteredDocs:)。由于写入的 Tokyo(area 2190)与 Sao Paulo(area 1523)满足"area" > 1500条件而 Paris(area 105)不满足,两条检索结果集合将呈现明显差异,直观展示元数据过滤的效果。
更进一步:AlloyDB 的索引与扩展能力
除示例覆盖的能力外,vectorstores/alloydb还面向大规模检索场景提供索引管理能力(见 vectorstore.go):
ApplyVectorIndex/DropVectorIndex/IsValidIndex/ReIndex:创建、删除、校验、重建向量索引;- 支持 HNSW、IVFFlat、IVF 与 AlloyDB 专属的ScaNN索引(创建 ScaNN 时自动执行
CREATE EXTENSION IF NOT EXISTS alloydb_scann),各索引的参数结构(如HNSWOptions{M, EfConstruction}、IVFOptions{Lists, Quantizer}、SCANNOptions{NumLeaves, Quantizer})定义于 distance_strategy.go; - 支持
CONCURRENTLY并发建索引与部分索引(partial index),以充分利用 AlloyDB 的扩展索引能力。
此外,同一PostgresEngine还提供InitChatHistoryTable方法(见 engine.go),可初始化聊天历史表,与 memory 模块配合实现会话记忆持久化,适合在此基础上构建带记忆的 RAG 应用。
小结
通过本示例可以确认:在 LangChain Go 中接入 AlloyDB for PostgreSQL 完成语义检索,只需「环境变量 → PostgresEngine → 建表 → VertexAI Embedder → VectorStore → 增/查」六步。其价值在于将 IAM 安全连接、pgvector 向量存储、独立元数据列过滤与 VertexAI 嵌入能力封装成统一、简洁的 Go API,开发者无需手工编写连接管理代码或 SQL 建表语句。如需深入了解接口细节,可继续阅读 vectorstores/alloydb/README.md、vectorstores/alloydb/vectorstore.go 与 util/alloydbutil/engine.go。
【免费下载链接】langchaingoLangChain for Go, the easiest way to write LLM-based programs in Go项目地址: https://gitcode.com/GitHub_Trending/la/langchaingo
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考