Docs 菜单
Docs 主页
/ /

教程:Vector Search 与 AWS 基岩集成

本教程向您展示如何使用Go、 MongoDB Vector Search 和 AWS Bedrock AI模型构建由AI驱动的后端来匹配人脸。该应用程序程序处理上传的图像,生成向量嵌入,在MongoDB Atlas中搜索相似的人脸,并返回三个最接近的匹配项并附上解释。

后端执行以下操作:

  • 将图像标准化为 800x600 JPEG 格式

  • 使用 AWS 基岩版生成 1024 维嵌入

  • 使用MongoDB Vector Search 查询MongoDB Atlas

  • 使用 Claude 生成相似性解释

嵌入是对图像特征进行编码的点向量。相似的图像会生成相似的向量,使您能够通过比较 n 维空间中的向量邻近度来查找匹配项。 1024 维嵌入包含 (-1.0, 1.0)范围内的值。

MongoDB Atlas存储参考图像的预先计算的嵌入。当用户上传图像时,后端会生成其嵌入,并使用MongoDB Vector Search 来查找最接近的匹配项。

应用程序执行以下步骤来进程请求:

  1. 前端捕获图像并将其作为基本64编码的JSON请求发送到后端。

  2. 后端将图像标准化为 800x600 JPEG 格式。

  3. AWS Bedrock 的 amazon.titan-embed-image-v1 模型从图像生成嵌入。

  4. MongoDB Vector Search 可找到三个最接近匹配的嵌入。

  5. AWS Bedrock 的 anthropic.claude-3-sonnet-20240229-v1:0 模型会生成对相似性的自然语言解释。

  6. 后端将匹配的图像和说明返回给前端。

您可以在 mongodb-celeb-search.com 上测试该应用程序。

本教程向您展示如何执行以下操作:

  • 创建HTTP服务器来处理图像处理请求

  • 将上传的图像标准化为一致的格式

  • 使用 AWS 基岩生成向量嵌入

  • 查询MongoDB Atlas以查找相似图像

  • 生成图像相似度的自然语言解释

1

在开始本教程之前,请确保您拥有以下组件:

  • 已配置集群的MongoDB Atlas帐户。要学习;了解如何设立Atlas 集群,请参阅MongoDB入门指南。

  • 具有基岩访问权限的有效 AWS 帐户。

  • ~/.aws/config 和 中配置的 AWS凭证。要学习;了解有关设置~/.aws/credentials AWS凭证的更多信息,请参阅 AWS 凭证文件文档。

  • Go 已安装。

2

为项目创建一个新目录并导航到该目录。

要初始化模块,请从项目目录运行以下命令:

go mod init github.com/jdortiz/goai

然后,在与基本应用程序结构相同的目录中创建一个 server.go文件:

package main
import (
"context"
"log"
"net/http"
"os"
"github.com/joho/godotenv"
)
type App struct {
}
func (app App) Start() error {
const serverAddr string = "0.0.0.0:3001"
log.Printf("Starting HTTP server: %s\n", serverAddr)
return http.ListenAndServe(serverAddr, nil)
}
func main() {
app := App{}
log.Println(app.Start())
}
3

要将处理程序方法添加到 App 类型并将其注册到路由器,请将 imageSearch() 方法添加到 server.go文件,然后在 Start() 方法中注册:

func (app App) imageSearch(w http.ResponseWriter, r *http.Request) {
log.Println("Image search invoked")
}
func (app App) Start() error {
const serverAddr string = "0.0.0.0:3001"
http.HandleFunc("POST /api/search", app.imageSearch)
log.Printf("Starting HTTP server: %s\n", serverAddr)
return http.ListenAndServe(serverAddr, nil)
}

从项目目录运行以下命令来启动应用程序:

go mod tidy
go run server.go

要测试端点,请命令行运行以下命令:

curl -IX POST localhost:3001/api/search
4

要定义请求结构并添加图像标准化逻辑,请将以下代码添加到 server.go文件中:

type CelebMatchRequest struct {
Image64 string `json:"img"`
}
// Receives a base64 encoded image
func standardizeImage(imageB64 string) (*string, error) {
// Get the base64 decoder as an io.Reader and use it to decode the image from the data
b64Decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(imageB64))
origImg, _, err := image.Decode(b64Decoder)
if err != nil {
return nil, fmt.Errorf("standardizing image failed: %w", err)
}
// Resize to 800x600
resizedImg := image.NewRGBA(image.Rect(0, 0, 800, 600))
draw.NearestNeighbor.Scale(resizedImg, resizedImg.Rect, origImg, origImg.Bounds(), draw.Over, nil)
// Reencode the image to JPEG format with Q=85
var jpegToSend bytes.Buffer
if err = jpeg.Encode(&jpegToSend, resizedImg, &jpeg.Options{Quality: 85}); err != nil {
return nil, fmt.Errorf("standardizing image failed: %w", err)
}
// Re-encode to base64
stdImgB64 := base64.StdEncoding.EncodeToString(jpegToSend.Bytes())
return &stdImgB64, nil
}

要更新处理程序以对图像进行解码和标准化,请修改 imageSearch() 方法,如以下代码所示:

func (app App) imageSearch(w http.ResponseWriter, r *http.Request) {
// Deserialize request
var imgReq CelebMatchRequest
err := json.NewDecoder(r.Body).Decode(&imgReq)
if err != nil {
log.Println("ERR: parsing json data", err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Split image into metadata and data
imgParts := strings.Split(imgReq.Image64, ",")
parts := len(imgParts)
if parts != 2 {
log.Printf("ERR: expecting metadata and data. Got %d parts\n", parts)
http.Error(w, fmt.Sprintf("expecting metadata and data. Got %d parts", parts), http.StatusBadRequest)
return
}
// Decode image from base 64, resize image to 800x600 with Q=85, and re-encode to base64
stdImage, err := standardizeImage(imgParts[1])
if err != nil {
log.Println("ERR:", err)
http.Error(w, "Error standardizing image", http.StatusInternalServerError)
return
}
}

通过从项目目录运行以下命令来安装图像编辑模块:

go get golang.org/x/image/draw
5

要安装 AWS SDK 并将配置信息添加到 App 结构,请从项目目录运行以下命令:

go get github.com/aws/aws-sdk-go-v2/config
go get github.com/aws/aws-sdk-go-v2/service/bedrockruntime

然后,将以下 AWS 配置字段添加到 server.go文件的App 结构中:

type App struct {
config *aws.Config
bedrock *bedrockruntime.Client
}

要初始化 AWS 配置,请将以下方法添加到 server.go文件中:

func connectToAWS(ctx context.Context) (*aws.Config, error) {
const dfltRegion string = "us-east-1"
const credAccount string = "your-account-name"
cfg, err := config.LoadDefaultConfig(ctx,
config.WithSharedConfigProfile(credAccount),
config.WithRegion(dfltRegion),
)
return &cfg, err
}
func NewApp(ctx context.Context) (*App, error) {
cfg, err := connectToAWS(ctx)
if err != nil {
log.Println("ERR: Couldn't load default configuration. Have you set up your AWS account?", err)
return nil, err
}
bedrockClient := bedrockruntime.NewFromConfig(*cfg)
return &App{
config: cfg,
bedrock: bedrockClient,
}, nil
}

最后,通过添加以下代码更新main() 方法以使用构造函数:

func main() {
ctx := context.Background()
app, err := NewApp(ctx)
if err != nil {
panic(err)
}
log.Println(app.Start())
}
6

要定义请求结构并创建计算嵌入的方法,请将以下代码添加到您的 server.go文件中:

const titanEmbedImgV1ModelId string = "amazon.titan-embed-image-v1"
const contentTypeJson = "application/json"
type EmbeddingConfig struct {
OutputEmbeddingLength int `json:"outputEmbeddingLength"`
}
type BedrockRequest struct {
InputImage string `json:"inputImage"`
EmbeddingConfig EmbeddingConfig `json:"embeddingConfig"`
InputText *string `json:"inputText,omitempty"`
}
func (app App) computeImageEmbedding(ctx context.Context, image string) ([]float64, error) {
payload := BedrockRequest{
InputImage: image,
EmbeddingConfig: EmbeddingConfig{
OutputEmbeddingLength: 1024,
},
InputText: nil,
}
bedrockBody, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("failed to get embedding from bedrock: %w", err)
}
bedrockReq := bedrockruntime.InvokeModelInput{
ModelId: aws.String(titanEmbedImgV1ModelId),
Body: bedrockBody,
ContentType: aws.String(contentTypeJson),
}
embeddingResp, err := app.bedrock.InvokeModel(ctx, &bedrockReq)
if err != nil {
return nil, fmt.Errorf("failed to get embedding from bedrock: %w", err)
}
result := gjson.GetBytes(embeddingResp.Body, "embedding")
var embedding []float64
result.ForEach(func(key, value gjson.Result) bool {
embedding = append(embedding, value.Float())
return true
})
return embedding, nil
}

接下来,通过从项目目录运行以下命令来安装 GJSON:

go get github.com/tidwall/gjson

要更新处理程序以计算嵌入,请修改 imageSearch 方法,如以下代码所示:

func (app App) imageSearch(w http.ResponseWriter, r *http.Request) {
// ...existing code...
// Compute the embedding using titan-embed-image-v1
embedding, err := app.computeImageEmbedding(r.Context(), *stdImage)
if err != nil {
log.Println("ERR:", err)
http.Error(w, "Error computing embedding", http.StatusInternalServerError)
return
}
}
7

在项目目录中,创建.env 文件。将以下代码添加到此文件中,以存储MongoDB Atlas连接 URI。将占位符替换为实际集群和档案信息。要学习;了解如何检索连接 URI,请参阅MongoDB入门指南。

MONGODB_URI=mongodb+srv://<username>:<password>@<cluster>.mongodb.net/

接下来,通过从项目目录运行以下命令来安装所需的模块:

go get github.com/joho/godotenv
go get go.mongodb.org/mongo-driver/v2

将MongoDB客户端字段添加到 App 结构:

type App struct {
client *mongo.Client
config *aws.Config
bedrock *bedrockruntime.Client
}

要创建初始化MongoDB客户端的方法,请将以下代码添加到 server.go文件中:

func newDBClient(uri string) (*mongo.Client, error) {
serverAPI := options.ServerAPI(options.ServerAPIVersion1)
opts := options.Client().ApplyURI(uri).SetServerAPIOptions(serverAPI)
client, err := mongo.Connect(opts)
if err != nil {
return nil, err
}
return client, nil
}
func (app *App) Close() {
if err := app.client.Disconnect(context.Background()); err != nil {
panic(err)
}
}

要更新构造函数签名和实施,请修改 NewApp() 方法,如以下代码所示:

func NewApp(ctx context.Context, uri string) (*App, error) {
cfg, err := connectToAWS(ctx)
if err != nil {
log.Println("ERR: Couldn't load default configuration. Have you set up your AWS account?", err)
return nil, err
}
bedrockClient := bedrockruntime.NewFromConfig(*cfg)
client, err := newDBClient(uri)
if err != nil {
log.Println("ERR: connecting to MongoDB cluster:", err)
return nil, err
}
return &App{
client: client,
config: cfg,
bedrock: bedrockClient,
}, nil
}

最后,要加载环境变量,更新main() 方法,如以下代码所示:

func main() {
var uri string
err := godotenv.Load()
if err != nil {
log.Fatal("Unable to load .env file")
}
if uri = os.Getenv("MONGODB_URI"); uri == "" {
log.Fatal("You must set your 'MONGODB_URI' environment variable. See\n\t https://mongodb.com/zh-cn/docs/drivers/go/current/usage-examples/")
}
ctx := context.Background()
app, err := NewApp(ctx, uri)
if err != nil {
panic(err)
}
defer func() {
app.Close()
}()
log.Println(app.Start())
}
8

要创建在MongoDB中查找相似图像的方法,请将以下代码添加到您的 server.go文件中:

func (app App) findSimilarImages(ctx context.Context, embedding []float64) ([]string, error) {
imgCollection := app.client.Database("celebrity_matcher").Collection("celeb_images")
vectorSchStage := bson.D{{"$vectorSearch", bson.D{{"index", "vector_index"},
{"path", "embeddings"},
{"queryVector", embedding},
{"numCandidates", 15},
{"limit", 3}}}}
projectStage := bson.D{{"$project", bson.D{{"image", 1}}}}
pipeline := mongo.Pipeline{vectorSchStage, projectStage}
imgCursor, err := imgCollection.Aggregate(ctx, pipeline)
if err != nil {
return nil, fmt.Errorf("failed to get similar images from the database: %w", err)
}
similarImgs := []struct {
Id bson.ObjectID `bson:"_id,omitempty"`
Image string `bson:"image"`
}{}
if err = imgCursor.All(ctx, &similarImgs); err != nil {
return nil, fmt.Errorf("failed to get similar images from the database: %w", err)
}
var images []string
var stdImage *string
for _, item := range similarImgs {
stdImage, err = standardizeImage(item.Image)
if err != nil {
return nil, fmt.Errorf("failed to standardize similar images: %w", err)
}
images = append(images, *stdImage)
}
return images, nil
}

要更新处理程序以查找相似图像,请按如下方式修改 imageSearch 方法:

func (app App) imageSearch(w http.ResponseWriter, r *http.Request) {
// ...existing code...
// Find similar images using vector search in MongoDB
images, err := app.findSimilarImages(r.Context(), embedding)
if err != nil {
log.Println("ERR:", err)
http.Error(w, "Error getting similar images", http.StatusInternalServerError)
return
}
}
9

要定义 Claude请求结构并创建生成解释的方法,请将以下代码添加到您的 server.go文件中:

const claude3SonnetV1ModelId string = "anthropic.claude-3-sonnet-20240229-v1:0"
type ClaudeBodyMsgSource struct {
Type string `json:"type"`
MediaType *string `json:"media_type,omitempty"`
Data *string `json:"data,omitempty"`
}
type ClaudeBodyMsgContent struct {
Type string `json:"type"`
Source *ClaudeBodyMsgSource `json:"source,omitempty"`
Text *string `json:"text,omitempty"`
}
type ClaudeBodyMsg struct {
Role string `json:"role"`
Content []ClaudeBodyMsgContent `json:"content"`
}
type ClaudeBody struct {
AnthropicVersion string `json:"anthropic_version"`
MaxTokens int `json:"max_tokens"`
System string `json:"system"`
Messages []ClaudeBodyMsg `json:"messages"`
}
func (app App) getImageSimilaritiesDescription(ctx context.Context, imgB64 string, similarImgB64 []string) (*string, error) {
const mediaTypeImage = "image/jpeg"
prompt := "Please let the user know how their first image is similar to the other 3 and which one is the most similar?"
payload := ClaudeBody{
AnthropicVersion: "bedrock-2023-05-31",
MaxTokens: 1000,
System: "Please act as face comparison analyzer.",
Messages: []ClaudeBodyMsg{
{
Role: "user",
Content: []ClaudeBodyMsgContent{
{
Type: "image",
Source: &ClaudeBodyMsgSource{
Type: "base64",
MediaType: aws.String(mediaTypeImage),
Data: &imgB64,
},
},
{
Type: "image",
Source: &ClaudeBodyMsgSource{
Type: "base64",
MediaType: aws.String(mediaTypeImage),
Data: &similarImgB64[0],
},
},
{
Type: "image",
Source: &ClaudeBodyMsgSource{
Type: "base64",
MediaType: aws.String(mediaTypeImage),
Data: &similarImgB64[1],
},
},
{
Type: "image",
Source: &ClaudeBodyMsgSource{
Type: "base64",
MediaType: aws.String(mediaTypeImage),
Data: &similarImgB64[2],
},
},
{
Type: "text",
Text: &prompt,
},
},
},
},
}
bedrockBody, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("failed to get embedding from bedrock: %w", err)
}
bedrockReq := bedrockruntime.InvokeModelInput{
ModelId: aws.String(claude3SonnetV1ModelId),
Body: bedrockBody,
ContentType: aws.String(contentTypeJson),
Accept: aws.String(contentTypeJson),
}
bedrockResp, err := app.bedrock.InvokeModel(ctx, &bedrockReq)
if err != nil {
return nil, fmt.Errorf("failed to get embedding from bedrock: %w", err)
}
description := gjson.GetBytes(bedrockResp.Body, "content.0.text").String()
return &description, nil
}

要更新处理程序以生成描述,请修改 imageSearch 方法,如以下代码所示:

func (app App) imageSearch(w http.ResponseWriter, r *http.Request) {
// ...existing code...
description, err := app.getImageSimilaritiesDescription(r.Context(), *stdImage, images)
if err != nil {
log.Println("ERR: failed to describe similarities with images", err)
http.Error(w, "Error describing similarities with images", http.StatusInternalServerError)
return
}
}
10

要定义响应结构并完成处理程序,请将以下代码添加到 server.go文件中:

type CelebMatchResponse struct {
Description string `json:"description"`
Images []string `json:"images"`
}
func (app App) imageSearch(w http.ResponseWriter, r *http.Request) {
// ...existing code...
response := CelebMatchResponse{
Description: *description,
Images: images,
}
jData, err := json.Marshal(response)
if err != nil {
log.Println("error serializing json", err)
http.Error(w, "error serializing json", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", contentTypeJson)
w.Header().Set("Content-Length", strconv.Itoa(len(jData)))
w.WriteHeader(http.StatusOK)
w.Write(jData)
}

从项目目录运行以下命令,再次启动应用程序:

go mod tidy
go run server.go

在网络浏览器中导航到 http://localhost:3001,并使用基本64编码的图像向 /api/search 发送 POST请求来测试API 。

要学习;了解有关MongoDB Vector Search 的更多信息,请参阅MongoDB Server手册中的MongoDB Vector Search 文档。

要学习;了解有关 AWS 基岩的更多信息,请参阅 基岩 文档。

后退

TLS 安全协议

在此页面上