You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用Google Vertex AI多模态嵌入API遇500错误求排查

问题:调用Vertex AI多模态嵌入模型返回500错误,疑为图片编码问题

最终目标:获取Embeddings并存储至向量数据库

我在Next.js API路由(Node.js环境)中,通过导出的POST函数处理POST请求,尝试调用Vertex AI的多模态嵌入模型。

后端代码(Next.js API路由)

import { UploadImageResponseBody } from "@/types";
/* 
本来打算用这个客户端库重构,但文档看得一头雾水,
如果用这个客户端调用同个模型更简单的话,欢迎给示例
*/
import { PredictionServiceClient } from "@google-cloud/aiplatform";


/**
 * 虽然项目在`us-east1`,但只有`us-central1`能正常工作
 */
const LOCATION = "us-central1";
const PROJECT_ID = "...";
const MODEL = "multimodalembedding";
const RPC_CALL = "predict";
const G_CLOUD_ACCESS_TOKEN ="...";

const REQUEST_URL = `https://${LOCATION}-aiplatform.googleapis.com/v1/projects/${PROJECT_ID}/locations/${LOCATION}/publishers/google/models/${MODEL}@001:${RPC_CALL}`;

type Instances = { image: { bytesBase64Encoded: string } }[];

async function getEmbedding(instances: Instances) {
  try {
    const response = await fetch(REQUEST_URL, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${G_CLOUD_ACCESS_TOKEN}`,
      },
      body: JSON.stringify({
        instances,
      }),
    });

    return response;
  } catch (error) {
    console.error(error);
    return null;
  }
}

export const POST = async (req: Request) => {

  const formData = await req.formData();
  const imageBlobs = formData.getAll("images") as Blob[];

  const base64Images = await Promise.allSettled(
    imageBlobs.map(async (imgBlob) => {
      const binaryArr = await imgBlob.arrayBuffer();
      const buffer = Buffer.from(binaryArr);
      const base64EncodedImage = buffer.toString("base64");
      const fileName = (imgBlob as File).name;
      return { base64EncodedImage, fileName };
    })
  );

  const successfulEncodings = base64Images.filter(
    (imgRes) => imgRes.status === "fulfilled"
  ) as PromiseFulfilledResult<{
    base64EncodedImage: string;
    fileName: string;
  }>[];

  const instances = successfulEncodings.map((imgRes) => {
    return {
      image: {
        bytesBase64Encoded: imgRes.value.base64EncodedImage,
      },
    };
  });


  const embeddings = await getEmbedding(instances);

  // ...后续逻辑
 

  return response;
};

收到的错误响应

{
  embeddings: Response {
    [Symbol(realm)]: null,
    [Symbol(state)]: {
      aborted: false,
      rangeRequested: false,
      timingAllowPassed: true,
      requestIncludesCredentials: true,
      type: 'default',
      status: 500,
      timingInfo: [Object],
      cacheState: '',
      statusText: 'Internal Server Error',
      headersList: [HeadersList],
      urlList: [Array],
      body: [Object]
    },
    [Symbol(headers)]: HeadersList {
      [Symbol(headers map)]: [Map],
      [Symbol(headers map sorted)]: null
    }
  }
}
// 以下是我自己加的日志,看到timingAllowPassed为true所以记录了耗时
getEmbeddingTime: 50.414s

之前的文本嵌入功能完全正常,所以我怀疑问题出在图片的处理/编码环节。我知道500内部服务器错误通常不是客户端的问题,但还是觉得可能是自己操作有误。

前端上传代码参考

"use client";
import { ChangeEvent, useState, EventHandler, MouseEvent } from "react";
import { Button } from "../Button";
import axios from "axios";
import ImageFileList from "./ImageFileList";

export const FileUpload: React.FC = () => {
  const [selectedImageFiles, setSelectedImageFiles] = useState<File[]>([]);

  const handleFileChange: EventHandler<ChangeEvent<HTMLInputElement>> = (
    event
  ) => {
    const { files } = event.target;

    if (!files || !files.length) return;

    const fileArr = Array.from(files);
    const MAX_BYTES_2MB = 2_000_000;

    for (const f of fileArr) {
      if (f.size > MAX_BYTES_2MB) {
        const warning = `File ${f.name} is too large. Please upload a file less than 2MB`;
        alert(warning);
      }
    }

    const acceptedFiles = fileArr.filter((f) => f.size <= MAX_BYTES_2MB);
    const newFiles = [...selectedImageFiles, ...acceptedFiles];
    setSelectedImageFiles(newFiles);
  };

  const handleUploadImagesClick: EventHandler<
    MouseEvent<HTMLButtonElement>
  > = async () => {

    if (selectedImageFiles.length === 0) {
      alert("Please select some images to upload");
      return;
    }

    const formData = new FormData();
    for (let i = 0; i < selectedImageFiles.length; i++) {
      formData.append("images", selectedImageFiles[i]);
    }

    try {
      const response = await axios.postForm("/api/images", formData);
    } catch (error) {
      console.error(error);
    }
  };

  const hasFilesSelected =
    !!selectedImageFiles && selectedImageFiles.length > 0;

  return (
    <div >
      <label >
        <span>
          {hasFilesSelected ? (
            <ImageFileList files={selectedImageFiles} />
          ) : (
            "Upload Images"
          )}
        </span>
        <input
          type="file"
          onChange={handleFileChange}
          accept=".jpg,.jpeg,.png"
        />
      </label>
      <Button
        onClick={handleUploadImagesClick}
        disabled={!hasFilesSelected}
      >
        Upload
      </Button>
    </div>
  );
};

内容的提问来源于stack exchange,提问作者Preston Bourne

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 07:50:19