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

Android应急APP集成Gemini微调模型遇403权限错误的解决方法

解决Gemini微调模型在安卓APP中403权限错误及无响应问题

一、权限问题排查步骤

  • 验证API Key权限:确保API Key所属的Google Cloud项目已启用Vertex AI API(微调模型托管在Vertex AI),且API Key关联的身份拥有Vertex AI User或Vertex AI Predictor角色,允许访问目标微调模型。
  • 核对模型归属:确认微调模型tunedModels/first-aid-123example123与API Key所在项目为同一项目,跨项目使用需将模型共享给目标项目并配置对应权限。
  • 检查模型ID拼写:确认ID完全匹配(注意大小写、特殊字符,无多余空格)。

二、代码核心调整

安卓Gemini SDK调用Vertex AI托管的微调模型时,需指定模型所在区域的Endpoint,不能直接使用基础模型的默认构造逻辑。同时需兼容微调模型的响应结构,避免序列化异常。

1. 正确初始化微调模型

替换原GenerativeModel初始化代码,添加ModelConfiguration指定Vertex AI区域和模型Endpoint:

String apiKey = "GeminiApiKey";
String tunedModelId = "tunedModels/first-aid-123example123";
String projectId = "你的Google Cloud项目ID"; // 替换为实际项目ID
String vertexRegion = "us-central1"; // 替换为模型创建时的区域

ModelConfiguration config = new ModelConfiguration.Builder()
        .setEndpoint(String.format("https://%s-aiplatform.googleapis.com/v1/projects/%s/locations/%s/%s",
                vertexRegion, projectId, vertexRegion, tunedModelId))
        .build();

GenerativeModel gm = new GenerativeModel(tunedModelId, apiKey, config);
model = GenerativeModelFutures.from(gm);

2. 修复序列化异常

微调模型响应结构与基础模型略有差异,需遍历候选内容获取文本,避免直接依赖getText():

@Override
public void onSuccess(GenerateContentResponse result) {
    StringBuilder resultText = new StringBuilder();
    // 遍历所有候选响应及内容部分,兼容微调模型格式
    for (GenerateContentResponse.Candidate candidate : result.getCandidates()) {
        for (Content.Part part : candidate.getContent().getParts()) {
            if (part instanceof TextPart) {
                resultText.append(((TextPart) part).getText());
            }
        }
    }
    // 后续格式处理逻辑保持不变
}

三、完整修改后的代码

// 保留原imports,需确保导入com.google.ai.client.generativeai.type.ModelConfiguration、TextPart等类

public class AssistanceActivity extends AppCompatActivity {

    private EditText emergencyInput;
    private TextView responseView;
    private Button sendButton;
    private GenerativeModelFutures model;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_assistance);

        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });

        emergencyInput = findViewById(R.id.emergencyInput);
        responseView = findViewById(R.id.response);
        sendButton = findViewById(R.id.send);

        // 初始化微调模型
        String apiKey = "GeminiApiKey";
        String tunedModelId = "tunedModels/first-aid-123example123";
        String projectId = "你的Google Cloud项目ID"; // 替换为实际项目ID
        String vertexRegion = "us-central1"; // 替换为模型所在区域

        ModelConfiguration config = new ModelConfiguration.Builder()
                .setEndpoint(String.format("https://%s-aiplatform.googleapis.com/v1/projects/%s/locations/%s/%s",
                        vertexRegion, projectId, vertexRegion, tunedModelId))
                .build();

        GenerativeModel gm = new GenerativeModel(tunedModelId, apiKey, config);
        model = GenerativeModelFutures.from(gm);

        sendButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String query = emergencyInput.getText().toString().trim();
                if (!query.isEmpty()) {
                    modelCall(query);
                } else {
                    responseView.setText("Por favor ingrese una consulta.");
                }
            }
        });
    }

    private void modelCall(String query) {
        Content content = new Content.Builder()
                .addText(query)
                .build();

        ListenableFuture<GenerateContentResponse> response = model.generateContent(content);
        Futures.addCallback(
                response,
                new FutureCallback<GenerateContentResponse>() {
                    @Override
                    public void onSuccess(GenerateContentResponse result) {
                        StringBuilder resultText = new StringBuilder();
                        // 遍历候选响应,兼容微调模型结构
                        for (GenerateContentResponse.Candidate candidate : result.getCandidates()) {
                            for (Content.Part part : candidate.getContent().getParts()) {
                                if (part instanceof TextPart) {
                                    resultText.append(((TextPart) part).getText());
                                }
                            }
                        }

                        SpannableStringBuilder spannable = new SpannableStringBuilder();
                        String[] lines = resultText.toString().split("\\n");

                        for (String line : lines) {
                            if (line.startsWith("**")) {
                                String boldText = line.replace("**", "");
                                SpannableString spannableBold = new SpannableString(boldText + "\n");
                                spannableBold.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, boldText.length(), 0);
                                spannable.append(spannableBold);
                            } else if (line.startsWith("*")) {
                                String listItem = "• " + line.replace("*", "") + "\n";
                                spannable.append(listItem);
                            } else {
                                spannable.append(line + "\n");
                            }
                        }

                        runOnUiThread(() -> responseView.setText(spannable));
                    }

                    @Override
                    public void onFailure(Throwable t) {
                        t.printStackTrace();
                        // 优化错误提示,便于调试
                        String errorMsg = "Error al obtener respuesta: " + t.getMessage();
                        if (t instanceof ApiException) {
                            ApiException apiEx = (ApiException) t;
                            errorMsg += " | 状态码: " + apiEx.getStatusCode();
                        }
                        runOnUiThread(() -> responseView.setText(errorMsg));
                    }
                }, getMainExecutor());
    }
}

注意:生产环境请勿硬编码API Key,建议使用Android安全存储方案管理敏感信息。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 19:05:00