Skip to content

在 ADK 中使用不同的模型

Note

Java ADK 目前支持 Gemini 和 Anthropic 模型。更多模型支持即将推出。

Agent Development Kit (ADK) 设计具有灵活性,允许你将各种大语言模型 (LLMs) 集成到你的智能体中。虽然 Google Gemini 模型的设置已在设置基础模型指南中介绍,但本页详细说明了如何有效利用 Gemini 并集成其他流行模型,包括那些外部托管或本地运行的模型。

ADK 主要使用两种机制进行模型集成:

  1. 直接字符串 / 注册表: 适用于与 Google Cloud 紧密集成的模型(如通过 Google AI Studio 或 Vertex AI 访问的 Gemini 模型)或托管在 Vertex AI 端点上的模型。你通常直接向 LlmAgent 提供模型名称或端点资源字符串。ADK 的内部注册表将此字符串解析为适当的后端客户端,通常利用 google-genai 库。
  2. 包装类: 为了更广泛的兼容性,特别是对于 Google 生态系统之外的模型或需要特定客户端配置的模型(如通过 LiteLLM 访问的模型)。你实例化一个特定的包装类(例如 LiteLlm)并将此对象作为 model 参数传递给你的 LlmAgent

以下部分将根据你的需求指导你使用这些方法。

使用 Google Gemini 模型

这是在 ADK 中使用 Google 旗舰模型的最直接方式。

集成方法: 直接将模型的标识符字符串传递给 LlmAgent(或其别名 Agent)的 model 参数。

后端选项与设置:

ADK 内部用于 Gemini 的 google-genai 库可以通过 Google AI Studio 或 Vertex AI 连接。

语音/视频流的模型支持

为了在 ADK 中使用语音/视频流式处理,你需要使用支持 Live API 的 Gemini 模型。你可以在文档中找到支持 Gemini Live API 的模型 ID

Google AI Studio

  • 使用场景: Google AI Studio 是开始使用 Gemini 最简单的方式。 你只需要 API 密钥。最适合快速原型开发和开发。
  • 设置: 通常需要 API 密钥:
    • 设置为环境变量或
    • 在模型初始化期间通过 Client 传递(见下面的示例)
export GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY"
export GOOGLE_GENAI_USE_VERTEXAI=FALSE

Vertex AI

  • 使用场景: 推荐用于生产应用,利用 Google Cloud 基础设施。Vertex AI 上的 Gemini 支持企业级功能、安全性和合规控制。
  • 设置:

    • 使用应用默认凭证 (ADC) 进行身份验证:

      gcloud auth application-default login
      
    • 将这些变量配置为环境变量或在初始化模型时直接提供它们。

      设置你的 Google Cloud 项目和位置:

      export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"
      export GOOGLE_CLOUD_LOCATION="YOUR_VERTEX_AI_LOCATION" # 例如,us-central1
      

      明确告诉库使用 Vertex AI:

      export GOOGLE_GENAI_USE_VERTEXAI=TRUE
      
  • 模型:Vertex AI 文档 中查找可用的模型 ID。

示例:

from google.adk.agents import LlmAgent

# --- 使用稳定的 Gemini Flash 模型的示例 ---
agent_gemini_flash = LlmAgent(
    # 使用最新稳定的 Flash 模型标识符
    model="gemini-2.0-flash",
    name="gemini_flash_agent",
    instruction="你是一个快速且有帮助的 Gemini 助手。",
    # ... 其他智能体参数
)

# --- 使用强大的 Gemini Pro 模型的示例 ---
# 注意:始终查看官方 Gemini 文档以获取最新的模型名称,
# 包括特定的预览版本(如果需要)。预览模型可能有
# 不同的可用性或配额限制。
agent_gemini_pro = LlmAgent(
    # 使用最新普遍可用的 Pro 模型标识符
    model="gemini-2.5-pro-preview-03-25",
    name="gemini_pro_agent",
    instruction="你是一个强大且知识渊博的 Gemini 助手。",
    # ... 其他智能体参数
)
// --- 示例 #1:使用稳定的 Gemini Flash 模型和环境变量 ---
LlmAgent agentGeminiFlash =
    LlmAgent.builder()
        // 使用最新稳定的 Flash 模型标识符
        .model("gemini-2.0-flash") // 设置环境变量以使用此模型
        .name("gemini_flash_agent")
        .instruction("你是一个快速且有帮助的 Gemini 助手。")
        // ... 其他智能体参数
        .build();

// --- 示例 #2:使用强大的 Gemini Pro 模型并在模型中设置 API 密钥 ---
LlmAgent agentGeminiPro =
    LlmAgent.builder()
        // 使用最新普遍可用的 Pro 模型标识符
        .model(new Gemini("gemini-2.5-pro-preview-03-25",
            Client.builder()
                .vertexAI(false)
                .apiKey("API_KEY") // 设置 API 密钥(或)项目/位置
                .build()))
        // 或者,你也可以直接传递 API_KEY
        // .model(new Gemini("gemini-2.5-pro-preview-03-25", "API_KEY"))
        .name("gemini_pro_agent")
        .instruction("你是一个强大且知识渊博的 Gemini 助手。")
        // ... 其他智能体参数
        .build();

// 注意:始终查看官方 Gemini 文档以获取最新的模型名称,
// 包括特定的预览版本(如果需要)。预览模型可能有
// 不同的可用性或配额限制。

使用 Anthropic 模型

java_only

你可以通过使用其 API 密钥或从 Vertex AI 后端将 Anthropic 的 Claude 模型直接集成到你的 Java ADK 应用程序中,使用 ADK 的 Claude 包装类。

对于 Vertex AI 后端,请参阅 Vertex AI 上的第三方模型 部分。

先决条件:

  1. 依赖项:

    • Anthropic SDK 类(传递性): Java ADK 的 com.google.adk.models.Claude 包装器依赖于 Anthropic 官方 Java SDK 的类。这些通常作为传递依赖项包含。
  2. Anthropic API 密钥:

    • 从 Anthropic 获取 API 密钥。使用秘密管理器安全地管理此密钥。

集成:

实例化 com.google.adk.models.Claude,提供所需的 Claude 模型名称和配置有你的 API 密钥的 AnthropicOkHttpClient。然后,将此 Claude 实例传递给你的 LlmAgent

示例:

import com.anthropic.client.AnthropicClient;
import com.google.adk.agents.LlmAgent;
import com.google.adk.models.Claude;
import com.anthropic.client.okhttp.AnthropicOkHttpClient; // 来自 Anthropic 的 SDK

public class DirectAnthropicAgent {

  private static final String CLAUDE_MODEL_ID = "claude-3-7-sonnet-latest"; // 或你首选的 Claude 模型

  public static LlmAgent createAgent() {

    // 建议从安全配置加载敏感密钥
    AnthropicClient anthropicClient = AnthropicOkHttpClient.builder()
        .apiKey("ANTHROPIC_API_KEY")
        .build();

    Claude claudeModel = new Claude(
        CLAUDE_MODEL_ID,
        anthropicClient
    );

    return LlmAgent.builder()
        .name("claude_direct_agent")
        .model(claudeModel)
        .instruction("你是一个由 Anthropic Claude 提供支持的有帮助的 AI 助手。")
        // ... 其他 LlmAgent 配置
        .build();
  }

  public static void main(String[] args) {
    try {
      LlmAgent agent = createAgent();
      System.out.println("成功创建直接 Anthropic 智能体:" + agent.name());
    } catch (IllegalStateException e) {
      System.err.println("创建智能体时出错:" + e.getMessage());
    }
  }
}

通过 LiteLLM 使用云和专有模型

python_only

要访问来自 OpenAI、Anthropic(非 Vertex AI)、Cohere 和许多其他提供商的大量 LLM,ADK 通过 LiteLLM 库提供集成。

集成方法: 实例化 LiteLlm 包装类并将其传递给 LlmAgentmodel 参数。

LiteLLM 概述: LiteLLM 充当翻译层,为 100 多个 LLM 提供标准化的、与 OpenAI 兼容的接口。

设置:

  1. 安装 LiteLLM:
    pip install litellm
    
  2. 设置提供商 API 密钥: 为你打算使用的特定提供商配置 API 密钥作为环境变量。

    • OpenAI 示例:

      export OPENAI_API_KEY="YOUR_OPENAI_API_KEY"
      
    • Anthropic 示例(非 Vertex AI):

      export ANTHROPIC_API_KEY="YOUR_ANTHROPIC_API_KEY"
      
    • 查阅 LiteLLM 提供商文档 了解其他提供商的正确环境变量名称。

      示例:

      from google.adk.agents import LlmAgent
      from google.adk.models.lite_llm import LiteLlm
      
      # --- 使用 OpenAI 的 GPT-4o 的智能体示例 ---
      # (需要 OPENAI_API_KEY)
      agent_openai = LlmAgent(
          model=LiteLlm(model="openai/gpt-4o"), # LiteLLM 模型字符串格式
          name="openai_agent",
          instruction="你是一个由 GPT-4o 提供支持的乐于助人的助手。",
          # ... 其他智能体参数
      )
      
      # --- 使用 Anthropic 的 Claude Haiku(非 Vertex)的智能体示例 ---
      # (需要 ANTHROPIC_API_KEY)
      agent_claude_direct = LlmAgent(
          model=LiteLlm(model="anthropic/claude-3-haiku-20240307"),
          name="claude_direct_agent",
          instruction="你是一个由 Claude Haiku 提供支持的助手。",
          # ... 其他智能体参数
      )
      

Windows 用户注意事项

在 Windows 上避免 LiteLLM UnicodeDecodeError

在 Windows 上使用带有 LiteLlm 的 ADK 智能体时,用户可能会遇到以下错误:

UnicodeDecodeError: 'charmap' codec can't decode byte...
此问题的发生是因为 litellm(被 LiteLlm 使用)使用默认的 Windows 编码(cp1252)而不是 UTF-8 读取缓存文件(例如,模型定价信息)。 Windows 用户可以通过将 PYTHONUTF8 环境变量设置为 1 来防止此问题。这会强制 Python 全局使用 UTF-8。 示例(PowerShell):
# 为当前会话设置
$env:PYTHONUTF8 = "1"
# 为用户持久设置
[System.Environment]::SetEnvironmentVariable('PYTHONUTF8', '1', [System.EnvironmentVariableTarget]::User)
应用此设置确保 Python 使用 UTF-8 读取缓存文件避免解码错误

通过 LiteLLM 使用开源和本地模型

python_only

为了获得最大控制权、节省成本、隐私或离线使用场景,你可以在本地运行开源模型或自托管它们,并使用 LiteLLM 集成它们。

集成方法: 实例化 LiteLlm 包装类,配置为指向你的本地模型服务器。

Ollama 集成

Ollama 允许你轻松在本地运行开源模型。

模型选择

如果你的智能体依赖工具,请确保从 Ollama 网站 选择支持工具的模型。

为了获得可靠的结果,我们建议使用具有工具支持的适当大小的模型。

可以使用以下命令检查模型的工具支持:

ollama show mistral-small3.1
  Model
    architecture        mistral3
    parameters          24.0B
    context length      131072
    embedding length    5120
    quantization        Q4_K_M

  Capabilities
    completion
    vision
    tools

你应该在能力下看到列出的 tools

你还可以查看模型使用的模板,并根据你的需求对其进行调整。

ollama show --modelfile llama3.2 > model_file_to_modify

例如,上述模型的默认模板本质上建议模型应该始终调用函数。这可能导致函数调用的无限循环。

Given the following functions, please respond with a JSON for a function call
with its proper arguments that best answers the given prompt.

Respond in the format {"name": function name, "parameters": dictionary of
argument name and its value}. Do not use variables.

你可以将这样的提示替换为更具描述性的提示,以防止无限工具调用循环。

例如:

Review the user's prompt and the available functions listed below.
First, determine if calling one of these functions is the most appropriate way to respond. A function call is likely needed if the prompt asks for a specific action, requires external data lookup, or involves calculations handled by the functions. If the prompt is a general question or can be answered directly, a function call is likely NOT needed.

If you determine a function call IS required: Respond ONLY with a JSON object in the format {"name": "function_name", "parameters": {"argument_name": "value"}}. Ensure parameter values are concrete, not variables.

If you determine a function call IS NOT required: Respond directly to the user's prompt in plain text, providing the answer or information requested. Do not output any JSON.

然后你可以使用以下命令创建一个新模型:

ollama create llama3.2-modified -f model_file_to_modify

使用 ollama_chat 提供商

我们的 LiteLLM 包装器可用于创建使用 Ollama 模型的智能体。

root_agent = Agent(
    model=LiteLlm(model="ollama_chat/mistral-small3.1"),
    name="dice_agent",
    description=(
        "可以掷 8 面骰子并检查素数的 hello world 智能体。"
    ),
    instruction="""
      你掷骰子并回答关于骰子掷出结果的问题。
    """,
    tools=[
        roll_die,
        check_prime,
    ],
)

重要的是设置提供商为 ollama_chat 而不是 ollama。使用 ollama 将导致意外行为,如无限工具调用循环和忽略先前上下文。

虽然可以在 LiteLLM 内部提供 api_base 用于生成,但截至 v1.65.5,LiteLLM 库在完成后调用其他 API 时依赖于环境变量。因此,此时我们建议设置环境变量 OLLAMA_API_BASE 来指向 ollama 服务器。

export OLLAMA_API_BASE="http://localhost:11434"
adk web

使用 openai 提供商

或者,可以使用 openai 作为提供商名称。但这也需要设置 OPENAI_API_BASE=http://localhost:11434/v1OPENAI_API_KEY=anything 环境变量,而不是 OLLAMA_API_BASE请注意,api base 现在末尾有 /v1

root_agent = Agent(
    model=LiteLlm(model="openai/mistral-small3.1"),
    name="dice_agent",
    description=(
        "可以掷 8 面骰子并检查素数的 hello world 智能体。"
    ),
    instruction="""
      你掷骰子并回答关于骰子掷出结果的问题。
    """,
    tools=[
        roll_die,
        check_prime,
    ],
)
export OPENAI_API_BASE=http://localhost:11434/v1
export OPENAI_API_KEY=anything
adk web

调试

你可以通过在智能体代码中的导入后添加以下内容来查看发送到 Ollama 服务器的请求。

import litellm
litellm._turn_on_debug()

查找类似于以下的行:

Request Sent from LiteLLM:
curl -X POST \
http://localhost:11434/api/chat \
-d '{'model': 'mistral-small3.1', 'messages': [{'role': 'system', 'content': ...

自托管端点(例如 vLLM)

python_only

诸如 vLLM 之类的工具允许你高效地托管模型,并且通常公开与 OpenAI 兼容的 API 端点。

设置:

  1. 部署模型: 使用 vLLM(或类似工具)部署你选择的模型。记下 API 基础 URL(例如 https://your-vllm-endpoint.run.app/v1)。
    • 对于 ADK 工具很重要: 部署时,确保服务工具支持并启用与 OpenAI 兼容的工具/函数调用。对于 vLLM,这可能涉及标志,如 --enable-auto-tool-choice 和可能特定的 --tool-call-parser,取决于模型。参考 vLLM 关于工具使用的文档。
  2. 认证: 确定你的端点如何处理认证(例如,API 密钥、承载令牌)。

    集成示例:

    import subprocess
    from google.adk.agents import LlmAgent
    from google.adk.models.lite_llm import LiteLlm
    
    # --- 使用托管在 vLLM 端点上的模型的智能体示例 ---
    
    # 由你的 vLLM 部署提供的端点 URL
    api_base_url = "https://your-vllm-endpoint.run.app/v1"
    
    # 你的 vLLM 端点配置识别的模型名称
    model_name_at_endpoint = "hosted_vllm/google/gemma-3-4b-it" # vllm_test.py 的示例
    
    # 认证(示例:为 Cloud Run 部署使用 gcloud 身份令牌)
    # 根据你的端点的安全性调整这一点
    try:
        gcloud_token = subprocess.check_output(
            ["gcloud", "auth", "print-identity-token", "-q"]
        ).decode().strip()
        auth_headers = {"Authorization": f"Bearer {gcloud_token}"}
    except Exception as e:
        print(f"警告:无法获取 gcloud 令牌 - {e}。端点可能未受保护或需要不同的认证。")
        auth_headers = None # 或适当处理错误
    
    agent_vllm = LlmAgent(
        model=LiteLlm(
            model=model_name_at_endpoint,
            api_base=api_base_url,
            # 如果需要,传递认证头
            extra_headers=auth_headers
            # 或者,如果端点使用 API 密钥:
            # api_key="YOUR_ENDPOINT_API_KEY"
        ),
        name="vllm_agent",
        instruction="你是一个在自托管 vLLM 端点上运行的乐于助人的助手。",
        # ... 其他智能体参数
    )
    

使用 Vertex AI 上的托管和微调模型

为了企业级可扩展性、可靠性和与 Google Cloud 的 MLOps 生态系统集成,你可以使用部署到 Vertex AI 端点的模型。这包括来自模型库的模型或你自己的微调模型。

集成方法: 将完整的 Vertex AI 端点资源字符串(projects/PROJECT_ID/locations/LOCATION/endpoints/ENDPOINT_ID)直接传递给 LlmAgentmodel 参数。

Vertex AI 设置(综合):

确保你的环境已配置为 Vertex AI:

  1. 认证: 使用应用默认凭证 (ADC):

    gcloud auth application-default login
    
  2. 环境变量: 设置你的项目和位置:

    export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"
    export GOOGLE_CLOUD_LOCATION="YOUR_VERTEX_AI_LOCATION" # 例如,us-central1
    
  3. 启用 Vertex 后端: 关键是,确保 google-genai 库针对 Vertex AI:

    export GOOGLE_GENAI_USE_VERTEXAI=TRUE
    

模型库部署

python_only

你可以从 Vertex AI 模型库将各种开源和专有模型部署到端点。

示例:

from google.adk.agents import LlmAgent
from google.genai import types # 用于配置对象

# --- 使用从模型库部署的 Llama 3 模型的智能体示例 ---

# 替换为你实际的 Vertex AI 端点资源名称
llama3_endpoint = "projects/YOUR_PROJECT_ID/locations/us-central1/endpoints/YOUR_LLAMA3_ENDPOINT_ID"

agent_llama3_vertex = LlmAgent(
    model=llama3_endpoint,
    name="llama3_vertex_agent",
    instruction="你是一个基于 Llama 3 的有帮助的助手,托管在 Vertex AI 上。",
    generate_content_config=types.GenerateContentConfig(max_output_tokens=2048),
    # ... 其他智能体参数
)

微调模型端点

python_only

部署你的微调模型(无论是基于 Gemini 还是 Vertex AI 支持的其他架构)会产生一个可以直接使用的端点。

示例:

from google.adk.agents import LlmAgent

# --- 使用 Vertex AI 上部署的 Claude 模型示例 ---
agent_claude_vertex = LlmAgent(
    # 完整的 Vertex AI 端点资源字符串
    model="projects/my-project/locations/us-central1/endpoints/1234567890",
    name="claude_vertex_agent",
    instruction="你是一个有帮助的助手,在 Vertex AI 上运行。",
    # ... 其他智能体参数
)

通过 LiteLLM 在 Vertex AI 上使用 Claude 模型

一些提供商,如 Anthropic,直接通过 Vertex AI 提供他们的模型。

集成方法: 使用直接模型字符串(例如,"claude-3-sonnet@20240229"),但需要在 ADK 内手动注册

为什么需要注册? ADK 的注册表自动识别 gemini-* 字符串和标准 Vertex AI 端点字符串(projects/.../endpoints/...)并通过 google-genai 库路由它们。对于通过 Vertex AI 直接使用的其他模型类型(如 Claude),你必须明确告诉 ADK 注册表哪个特定的包装类(在这种情况下是 Claude)知道如何使用 Vertex AI 后端处理该模型标识符字符串。

设置:

  1. Vertex AI 环境: 确保综合的 Vertex AI 设置(ADC、环境变量、GOOGLE_GENAI_USE_VERTEXAI=TRUE)已完成。

  2. 安装提供商库: 安装为 Vertex AI 配置的必要客户端库。

    pip install "anthropic[vertex]"
    
  3. 注册模型类: 在你的应用程序开始时,使用 Claude 模型字符串创建智能体之前,添加此代码:

    # 通过 Vertex AI 与 LlmAgent 直接使用 Claude 模型字符串所需
    from google.adk.models.anthropic_llm import Claude
    from google.adk.models.registry import LLMRegistry
    
    LLMRegistry.register(Claude)
    

示例:

from google.adk.agents import LlmAgent
from google.adk.models.anthropic_llm import Claude # 注册所需的导入
from google.adk.models.registry import LLMRegistry # 注册所需的导入
from google.genai import types

# --- 注册 Claude 类(在启动时执行一次)---
LLMRegistry.register(Claude)

# --- 在 Vertex AI 上使用 Claude 3 Sonnet 的示例智能体 ---

# Vertex AI 上 Claude 3 Sonnet 的标准模型名称
claude_model_vertexai = "claude-3-sonnet@20240229"

agent_claude_vertexai = LlmAgent(
    model=claude_model_vertexai, # 注册后传递直接字符串
    name="claude_vertexai_agent",
    instruction="你是一个由 Vertex AI 上的 Claude 3 Sonnet 提供支持的助手。",
    generate_content_config=types.GenerateContentConfig(max_output_tokens=4096),
    # ... 其他智能体参数
)

集成方法: 直接实例化特定于提供商的模型类(例如,com.google.adk.models.Claude)并使用 Vertex AI 后端配置它。

为什么直接实例化? Java ADK 的 LlmRegistry 默认主要处理 Gemini 模型。对于 Vertex AI 上的第三方模型(如 Claude),你直接向 LlmAgent 提供 ADK 的包装类(例如,Claude)的实例。此包装类负责通过其特定的客户端库与模型交互,配置为 Vertex AI。

设置:

  1. Vertex AI 环境:

    • 确保你的 Google Cloud 项目和区域设置正确。
    • 应用默认凭证 (ADC): 确保 ADC 在你的环境中正确配置。这通常通过运行 gcloud auth application-default login 来完成。Java 客户端库将使用这些凭证来向 Vertex AI 进行身份验证。有关详细设置,请参阅 ADC 上的 Google Cloud Java 文档
  2. 提供商库依赖项:

    • 第三方客户端库(通常是传递性的): ADK 核心库通常包括 Vertex AI 上常见第三方模型(如 Anthropic 的必需类)的必要客户端库作为传递依赖项。这意味着你可能不需要在你的 pom.xmlbuild.gradle 中为 Anthropic Vertex SDK 明确添加单独的依赖项。
  3. 实例化并配置模型: 在创建你的 LlmAgent 时,实例化 Claude 类(或另一个提供商的等效类)并配置其 VertexBackend

示例:

import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.vertex.backends.VertexBackend;
import com.google.adk.agents.LlmAgent;
import com.google.adk.models.Claude; // ADK 的 Claude 包装器
import com.google.auth.oauth2.GoogleCredentials;
import java.io.IOException;

// ... 其他导入

public class ClaudeVertexAiAgent {

    public static LlmAgent createAgent() throws IOException {
        // Vertex AI 上 Claude 3 Sonnet 的模型名称(或其他版本)
        String claudeModelVertexAi = "claude-3-7-sonnet"; // 或任何其他 Claude 模型

        // 使用 VertexBackend 配置 AnthropicOkHttpClient
        AnthropicClient anthropicClient = AnthropicOkHttpClient.builder()
            .backend(
                VertexBackend.builder()
                    .region("us-east5") // 指定你的 Vertex AI 区域
                    .project("your-gcp-project-id") // 指定你的 GCP 项目 ID
                    .googleCredentials(GoogleCredentials.getApplicationDefault())
                    .build())
            .build();

        // 使用 ADK Claude 包装器实例化 LlmAgent
        LlmAgent agentClaudeVertexAi = LlmAgent.builder()
            .model(new Claude(claudeModelVertexAi, anthropicClient)) // 传递 Claude 实例
            .name("claude_vertexai_agent")
            .instruction("你是一个由 Vertex AI 上的 Claude 3 Sonnet 提供支持的助手。")
            // .generateContentConfig(...) // 可选:如果需要,添加生成配置
            // ... 其他智能体参数
            .build();

        return agentClaudeVertexAi;
    }

    public static void main(String[] args) {
        try {
            LlmAgent agent = createAgent();
            System.out.println("成功创建智能体:" + agent.name());
            // 在这里你通常会设置 Runner 和 Session 来与智能体交互
        } catch (IOException e) {
            System.err.println("创建智能体失败:" + e.getMessage());
            e.printStackTrace();
        }
    }
}