最佳实践和模式

本指南分享了经过验证的最佳实践和设计模式,可帮助您优化和扩缩代理应用。 这些内容可以帮助您降低设计时费用、减少运行时费用,并提高代理的可靠性。

常规

本部分介绍了代理开发和说明编写方面的常规最佳实践。

从简单入手

首次构建代理应用时,您应从简单的应用场景入手。 在简单的应用场景正常运行后,继续构建更复杂的应用场景。

说明应具体

代理说明应具体且明确。 说明应井井有条,并按主题分组。 避免以随意的方式分散特定主题的说明。 说明还应便于用户遵循。

使用结构化说明

编写完说明后, 您应使用 重组说明 功能来设置说明的格式。 采用这种格式,您的代理将更加可靠。

工具

本部分介绍了定义和使用工具的最佳实践,包括封装外部 API 和链接工具调用。

使用 Python 工具封装 API

外部 API 架构可能会定义许多与您的代理无关的输入和输出参数。 如果您在类似情况下使用 OpenAPI 工具,则可能会向模型提供不必要的上下文,这可能会降低可靠性。 例如,假设 OpenAPI 规范工具接受 3 个参数作为输入,并返回一个包含 100 个键值对的大型 JSON 对象。 代理会预测此工具的 3 个输入实参,并在返回时看到包含所有 100 个键值对的完整 JSON 载荷。 如果其中只有 3 个键值对与对话实际相关,则其他 97 个键值对是不相关的数据,会向对话历史记录添加令牌。虽然这看起来无害,但可能会给代理带来不必要的困惑,增加推理时间并增加延迟时间。

最佳实践是使用 Python 工具封装 API 调用。 封装可让您从代理和上下文历史记录中混淆不必要的数据。您可以仅返回在特定时间点与代理相关的数据,从而控制代理看到的具体上下文。这样一来,您就可以完全控制工具定义的输入和输出参数,这些参数会与模型共享。 这种做法是使用工具进行 上下文工程 的一种形式。

示例代码:

def python_wrapper(arg_a: str, arg_b: str) -> dict:
  """
  Call the scheduling service to schedule an appointment,
  returning only relevant fields.
  """
  res = complicated_external_api_call(...)
  # Process result to extract only relevant key-value pairs.
  processed_res = {
      "appointment_time": res.json()["appointment_time"],
      "appointment_location": res.json()["appointment_location"],
      "confirmation_id": res.json()["confirmation_id"],
  }
  return processed_res

使用工具和回调实现确定性行为

在某些对话场景中,您可能需要代理应用提供更具确定性的行为。 在这些情况下,您应使用工具或回调。

回调通常是实现完全确定性控制的最佳选择。 回调发生在代理的权限范围之外,因此代理不会参与回调的执行。

工具的内部结构完全是确定性的,但由代理编排的工具调用不是确定性的。 代理决定调用工具、准备工具输入实参并解读工具结果。 代理可能会虚构此编排。

链接工具调用

使用工具封装 API 调用类似, 如果需要在对话轮次期间执行多个工具, 您应指示代理调用一个工具, 并实现该工具以调用其他工具。 或者,您可以指示代理调用第一个工具,并定义 after_tool_callback 回调以调用其余工具。

链接工具调用的不良模式

指示代理在对话轮次期间调用多个工具以实现共同目标被认为是一种不良模式。

模型必须预测每个工具调用以及该工具调用中的每个参数。 然后,它还必须确保按顺序预测工具调用。 这意味着您严重依赖模型(模型本质上是不确定的)来执行确定性任务。

例如,请考虑以下工具序列:

  • tool_1(arg_a, arg_b) -> 输出 c
  • tool_2(arg_c) -> 输出 d
  • tool_3(arg_d) -> 输出 e

如果您为这三个工具调用定义说明,最终会得到如下所示的运行时事件序列:

  • 用户输入
  • 模型 -> 代理预测 tool_1(arg_a, arg_b)
  • 返回 tool_1_response.json()
  • 代理解读 tool_1_response.json() 并提取 arg_c
  • 模型 -> 代理预测 tool_2(arg_c)
  • 返回 tool_2_response.json()
  • 代理解读 tool_2_response.json() 并提取 arg_d
  • 模型 -> 代理预测 tool_3(arg_d)
  • 返回 tool_3_response.json()
  • 模型 -> 代理提供最终回答

有 4 个模型调用、3 个工具预测和 4 个输入实参。

链接工具调用的良好模式

当您需要调用多个工具时,指示代理调用单个工具,并实现该工具以调用其他工具被认为是一种良好模式。

以下工具调用了其他三个工具:

def python_wrapper(arg_a: str, arg_b: str) -> dict:
  """Makes some sequential API calls."""
  res1 = tools.tool_1({"arg_a": arg_a, "arg_b": arg_b})
  res2 = tools.tool_2(res1.json())
  res3 = tools.tool_3(res2.json())

  return res3.json()

请考虑单个工具调用的事件序列:

  • 用户输入
  • 模型 -> 代理预测 python_wrapper(arg_a, arg_b)
  • 返回 python_wrapper_response.json()
  • 模型 -> 代理提供最终回答

这种方法可以减少令牌数量并降低虚构的可能性。

清晰且不同的工具定义

对于工具定义,应应用以下最佳实践:

  • 不同的工具不应具有相似的名称。 使您的工具名称彼此明显不同。
  • 应从代理节点中移除未使用的工具。
  • 对于参数名称,请使用蛇形命名法、描述性名称,并避免使用不常见的缩写。

    良好示例:first_namephone_numberurl

    不良示例:iarg1fnpnumrqst

  • 参数应使用扁平结构,而不是嵌套结构。 结构的嵌套程度越高, 您就越依赖模型来预测键值对 及其正确的类型。

开发工作流程

本部分介绍了代理开发期间团队协作、版本控制和测试方面的最佳实践。

为代理协作定义开发流程

在与团队协作开发代理应用时,您应定义开发流程。 以下是可能的协作实践示例:

  • 使用第三方版本控制: 使用导入和恢复功能将更改与 第三方版本控制系统同步。 就同步、审核和合并流程达成一致。 明确负责人和接受更改的明确步骤(例如,提供评估结果)。
  • 使用内置版本控制: 设置使用内置版本控制的流程。 就如何使用快照进行版本控制达成一致。 例如,您可以要求在达到里程碑(一组评估通过)时或在完成新功能开发之前创建快照。 就同步、审核和合并更改的流程达成一致。

使用版本保存代理状态

借助版本 ,您可以记录在 代理应用中完成的工作或更改。在对说明、工具、变量和其他项进行更改后,您可以在进行任何其他更改之前保存该状态。 版本是代理的不可变时间点快照。当您对某些更改感到满意,并且代理应用按您设计的方式运行时,您应创建版本,尤其是在通过评估验证更改后。创建版本后,您可以随时回滚到该版本。

您应经常创建版本,或许每进行 10-15 次重大更改后就创建一个版本。 以语义方式命名版本也很有帮助,您应与开发团队一起决定要使用的命名惯例。示例包括描述性名称,如 pre-prod-instruction-changesprod-ready-for-testing。您还可以使用 语义版本控制等标准,使用 v1.0.0v1.0.1 等名称。版本还有一个说明字段,可让您添加更多详细信息,类似于提交消息正文。版本名称和说明应简短、有意义且易于理解,以防您需要回滚到该版本。

执行端到端测试

您的代理应用开发流程应包括端到端测试,以验证与外部系统的集成。

评估

本部分介绍了使用评估来确保代理可靠性的最佳实践。

使用评估

评估 有助于确保代理的可靠性。 使用它们来设置对代理和代理调用的 API 的预期。

会话处理

本部分介绍了管理会话生命周期的模式。

使用静态响应实现确定性问候语并缩短延迟时间

您可以将代理配置为在会话开始时说出确定性回答。 这种方法可以节省模型调用和令牌,并缩短延迟时间。

使用 before_model_callback 可让您拦截传入的输入,然后使用静态 问候语消息进行响应。

def before_model_callback(
    callback_context: CallbackContext,
    llm_request: LlmRequest
) -> Optional[LlmResponse]:
  for part in callback_context.get_last_user_input():
    # Or other events or texts
    if part.text == "<event>session start</event>":
      return LlmResponse.from_parts(parts=[
          Part.from_text(text="Hello how can I help you today?")
      ])
  return None

在模型工作时使用前缀消息快速响应

当会话开始时,避免强制用户等待模型生成。 您可以为回答添加前缀,以便代理可以快速提供友好的品牌问候语(例如“您好,我是 Gemini,您的个人助理”),而模型同时在后台处理用户的主要请求。

这依赖于 partial = True 设置。 通常,非 FuctionCall 回答被视为终端回答。 使用 partial = True 会强制代理在回答后继续处理。

在以下示例中,代理应用会快速提供欢迎辞,然后继续处理主要请求。这消除了尴尬的“输入”暂停,使代理感觉响应迅速。

def before_model_callback(callback_context: CallbackContext, llm_request: LlmRequest) -> Optional[LlmResponse]:
  for part in callback_context.get_last_user_input():
    if part.text == "<event>session start</event>":
      response = LlmResponse.from_parts([Part.from_text("Hello, I'm Gemini, your personal AI assistant.")])
      response.partial = True
      return response
  return None

验证和强制执行强制性内容

在某些情况下,您可能希望指示代理提供特定的强制性内容(例如法律免责声明),但也要验证代理是否实际包含该内容。这种模式可让您在模型正常工作时依赖模型的自然生成,但在模型失败时以确定性的方式强制执行内容。

您可以使用 after_model_callback 检查模型的输出。如果存在强制性内容,回调会返回 None(让模型的回答通过)。如果缺少强制性内容,回调会构建包含强制性内容的新回答。

变量示例:

变量名称 默认值
first_turn True
DISCLAIMER = "THIS CONVERSATION MAY BE RECORDED FOR LEGAL PURPOSES."

def after_model_callback(
    callback_context: CallbackContext,
    llm_response: LlmResponse
) -> Optional[LlmResponse]:
  if callback_context.variables.get("first_turn"):
    callback_context.variables["first_turn"] = False

    # Check if the agent's response already contains the disclaimer.
    # The agent might have produced it based on instructions.
    for part in callback_context.get_last_agent_output():
      if part.text and DISCLAIMER in part.text:
        return None

    # If the agent failed to produce the disclaimer, force it.
    return LlmResponse.from_parts(parts=[
        Part.from_text(DISCLAIMER),
        *llm_response.content.parts
    ])

  return None

在会话结束时调用自定义工具

您可以将代理配置为在会话结束时调用特定工具。 这对于通话后总结事件非常有用,例如在退出时同步数据、将数据发送到外部 API、完成后端任务或记录通话元数据。

例如,假设您有一个现有工具(如 post_call_logging),您希望在会话结束前调用该工具:

def post_call_logging(session_id: str) -> dict:
  """Logs the session ID to external API."""
  API_URL = "https://api.example.com"
  response = ces_requests.post(
    url=API_URL,
    data={"session_id": session_id}
  )

  return response.json()

您可以使用 after_model_callback 执行以下序列:

  1. 检查代理的回答中是否存在 end_session 工具调用。
  2. 创建 post_call_logging 工具部分。
  3. end_session 工具调用之前插入 post_call_logging 工具调用。

这可确保代理在终止会话之前执行日志记录工具。

def after_model_callback(
    callback_context: CallbackContext,
    llm_response: LlmResponse
) -> Optional[LlmResponse]:
  for index, part in enumerate(llm_response.content.parts):
    if part.has_function_call('end_session'):
      # Add an additional "post_call_logging" function call before "end_session",
      # so the agent will execute the tool before ending the session.
      tool_call = Part.from_function_call(
          name="post_call_logging",
          args={"sessionId": callback_context.session_id}
      )
      return LlmResponse.from_parts(
          parts=llm_response.content.parts[:index] + [tool_call] + llm_response.content.parts[index:]
      )
  return None

使用部分回答进行实时界面更新

当代理执行操作(例如更新订单状态)时,模型处理最终回答可能会出现延迟。 使用部分回答可让您在工具完成执行时向客户端界面发送通知,从而将视觉更新与模型的文本生成分离。

您的界面可以实时刷新状态栏、跟踪器或收据。

这依赖于 partial = True 设置。 通常,非 FuctionCall 回答被视为终端回答。 使用 partial = True 会强制代理在回答后继续处理。

JSON 载荷不会发送给模型。 因此,代理在生成回答时不会知道载荷的存在。

def before_model_callback(
    callback_context: CallbackContext,
    llm_request: LlmRequest
) -> Optional[LlmResponse]:
  if  llm_request.contents[-1].parts[-1].has_function_response('update_order'):
    order_state =  llm_request.contents[-1].parts[-1].function_response.response['result']['order_state']
    # Return a custom JSON payload before calling the model to generate the final agent response.
    response = LlmResponse.from_parts([Part.from_json(data=json.dumps(order_state))])
    response.partial = True
    return response
  return None

客户端集成

本部分介绍了与客户端应用集成的模式。

使用自定义载荷驱动界面

用户希望获得动态、互动式的界面。 您可以使用 自定义载荷 驱动客户端呈现, 从而弥合代理与完善的应用之间的差距。您可以将代理配置为检测回答中的特定模式(例如选项列表),并将其转换为高转化率的互动式界面元素(例如可点击的芯片或按钮),而不是提供纯文本选项。

使用 after_model_callback 扫描代理回答中的特定触发器。 例如,如果模型输出为“可用选项包括:退款、跟踪订单、与代理交谈”,则以下回调会拦截这些选项并将其提取为 JSON 载荷,该载荷可用于界面呈现。

import json

def after_model_callback(
    callback_context: CallbackContext,
    llm_response: LlmResponse
) -> Optional[LlmResponse]:
  prefix = 'Available options are:'
  payload = {}
  for part in llm_response.content.parts:
    if part.text is not None and part.text.startswith(prefix):
      # Return available options as chip list
      payload['chips'] = part.text[len(prefix):].split(',')
      break

  new_parts = []
  # Keep the original agent response part, as the custom payload won't be sent
  # back to the model in the next turn.
  new_parts.extend(llm_response.content.parts)
  new_parts.append(Part.from_json(data=json.dumps(payload)))
  return LlmResponse.from_parts(parts=new_parts)

显示 Markdown 和 HTML

如果您的对话界面支持 Markdown 和 HTML 作为代理回答, 您可以使用 模拟器 测试这些回答, 因为模拟器也支持 Markdown 和 HTML。

说明示例:

<role>
    You are a "Markdown Display Assistant," an AI agent designed to demonstrate
    various rich content formatting options like images, videos, and deep links
    using HTML-style markdown. Your purpose is to generate and display this
    content directly to the user based on their requests.
</role>
<persona>
    Your primary goal is to showcase the rich content rendering capabilities of
    the platform by generating HTML markdown for elements like images, videos,
    and hyperlinks. You are a helpful and direct assistant. When asked to show
    something, you generate the markdown for it and present it.
    You should not engage in conversations outside the scope of generating and
    displaying markdown. If the user asks for something unrelated, politely
    state that you can only help with displaying rich content. Adhere strictly
    to the defined constraints and task flow.
</persona>
<constraints>
    1.  **Scope Limitation:** Only handle requests related to displaying
        markdown content (images, videos, links, etc.). Do not answer general
        knowledge questions or perform other tasks.
    2.  **Tool Interaction Protocol:** You must use the \`display_markdown\`
        tool to generate the formatted content string.
    3.  **Direct Output:** Your final response to the user must be the raw
        markdown string returned by the \`display_markdown\` tool. Do not add
        any conversational text around it unless the tool returns an error.
        For example, if the tool returns \`"<img src='...'>"\`, your response
        should be exactly \`"<img src='...'>"\`.
    4.  **Clarity and Defaults:** If a user's request is vague (e.g., "show me
        an image"), use the tool's default values to generate a response. There
        is no need to ask for clarification.
    5.  **Error Handling:** If the tool call fails or returns an error, inform
        the user about the issue in a conversational manner.
</constraints>
<taskflow>
    These define the conversational subtasks that you can take. Each subtask
    has a sequence of steps that should be taken in order.
    <subtask name="Generate and Display Markdown">
        <step name="Parse Request and Call Tool">
            <trigger>
                User initiates a request to see any form of rich content (image,
                video, link, etc.).
            </trigger>
            <action>
                1.  Identify the types of content the user wants to see (e.g.,
                    image, video, deep link).
                2.  Call the \`display_markdown\` tool. Set the corresponding
                    boolean arguments to \`True\` based on the user's request.
                    For example, if the user asks for a video and a link, call
                    \`display_markdown(show_video=True, show_deep_link=True)\`.
                3.  If the user makes a general request like "show me something
                    cool", you can enable all flags.
            </action>
        </step>
        <step name="Output Tool Response">
            <trigger>
                The \`display_markdown\` tool returns a successful response
                containing a \`markdown_string\`.
            </trigger>
            <action>
                1.  Extract the value of the \`markdown_string\` key from the
                    tool's output.
                2.  Use this value as your direct and final response to the
                    user, without any additional text or formatting.
            </action>
        </step>
    </subtask>
</taskflow>

Python 工具示例:

from typing import Any

def display_markdown(show_image: bool, show_video: bool, show_deep_link: bool) -> dict[str, Any]:
    """
    Constructs a markdown string containing HTML for various rich media elements.

    This function generates an HTML-formatted string based on the boolean flags provided.
    It can include an image, a video, and a hyperlink (deep link). The content for
    these elements is pre-defined.

    Args:
        show_image (bool): If True, an <img> tag will be included in the output.
        show_video (bool): If True, a <video> tag will be included in the output.
        show_deep_link (bool): If True, an <a> tag will be included in the output.

    Returns:
        dict[str, Any]: A dictionary with a single key 'markdown_string' containing the
                        generated HTML markdown. If no flags are set, it returns a
                        message indicating nothing was requested.
    """
    # MOCK: This is a mock implementation. It does not fetch any dynamic content.
    # It assembles a markdown string from hardcoded HTML snippets to demonstrate
    # the agent's ability to render rich content.

    markdown_parts = []

    if show_image:
        image_html = "This is a sample image:\n<img src='https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png' alt='Google Logo' width='272' height='92' />"
        markdown_parts.append(image_html)

    if show_video:
        video_html = "This is a sample video:\n<video controls width='320' height='240'><source src='https://www.w3schools.com/html/mov_bbb.mp4' type='video/mp4'>Sorry, your browser does not support embedded videos.</video>"
        markdown_parts.append(video_html)

    if show_deep_link:
        link_html = "This is a sample deep link:\n<a href='https://www.google.com'>Click here to go to Google</a>"
        markdown_parts.append(link_html)

    if not markdown_parts:
        return {"markdown_string": "You did not request any content to be displayed. Please specify if you want to see an image, video, or link."}

    return {"markdown_string": "\n\n".join(markdown_parts)}

语音和音频频道控件

本部分介绍了控制语音和音频频道的模式,包括预先录制的音频、等待音乐和插话设置。

注意:

  • 音频文件支持 Linear16、mulaw 和 alaw 音频编码。
  • 如果使用属于其他云项目的 Cloud Storage 存储桶, 则必须明确向 Customer Engagement Suite 服务账号 service-<PROJECT-NUMBER>@gcp-sa-ces. 授予对 目标 Cloud Storage 存储桶的 storage.objects.get 权限。
  • 您可以使用 interruptable 输入实参来配置最终用户是否可以中断预先录制的音频。
  • 对于音乐播放,您可以使用 cancellable 输入实参来指明当代理生成新回答时应停止音乐播放。

播放品牌专属的预先录制的音频

您可以将代理配置为在处理用户请求之前播放预先录制的音频文件。 您可以在会话开始时使用此功能播放品牌批准的问候语或强制性法律披露信息。

使用 "transcript": "yyy" 向代理提供音频播放文本, 确保代理具有生成后续回答所需的上下文。

使用 "interrupable": false 可确保用户无法中断音频播放。

def before_model_callback(
    callback_context: CallbackContext,
    llm_request: LlmRequest
) -> Optional[LlmResponse]:
  for part in callback_context.get_last_user_input():
    if part.text == "<event>session start</event>":
      return LlmResponse.from_parts(parts=[
          Part.from_json(data='{"audioUri": "gs://path/to/audio/file", "transcript": "transcript for the audio file", "interruptable": false}')
      ])
  return None

在执行速度较慢的工具时播放预先录制的音乐(不允许插话)

您可以将代理配置为在运行速度较慢的“阻塞”工具(例如账号验证和激活)时播放音乐。 工具执行完毕后,音乐会自动停止。 在播放音乐时,用户无法与代理互动。

def after_model_callback(
    callback_context: CallbackContext,
    llm_response: LlmResponse
) -> Optional[LlmResponse]:
  for index, part in enumerate(llm_response.content.parts):
    if part.has_function_call("slow_tool"):
      play_music = Part.from_json(
          data='{"audioUri": "gs://path/to/music/file", "cancellable": true}'
      )
      return LlmResponse.from_parts(
          parts=llm_response.content.parts[:index] +
          [play_music] + llm_response.content.parts[index:]
      )
  return None

在执行异步工具时播放预先录制的音乐(允许插话)

您可以将代理配置为在异步执行工具(例如在用户账号验证和激活期间)时播放音乐。 如果用户尚未中断音乐,则在异步工具完成时音乐会自动终止。 最终用户可以随时中断音乐,以便继续与代理互动。

def before_model_callback(
    callback_context: CallbackContext,
    llm_request: LlmRequest
) -> Optional[LlmResponse]:
  for part in llm_request.contents[-1].parts:
    if part.has_function_response("async_tool"):
      text = Part.from_text(text="I'm submitting your order, it may take a while.")
      music = Part.from_json(
          data='{"audioUri": "gs://path/to/music/file", "cancellable": true}'
      )
      return LlmResponse.from_parts(parts=[text, music])
  return None

禁止用户对某些回答插话

您可以禁止用户在代理读出重要信息(例如法律免责声明)时中断代理,但允许用户对代理回答的其余部分插话。

这会使用 customize_response 系统工具。

您可以采用两种方式实现此行为,具体取决于您是否需要确定性结果:

  1. 回调(确定性): 强制回调提供回答,如示例所示。
  2. 说明(代理驱动): 提示代理在其说明中使用 customize_response 工具。
def before_model_callback(
    callback_context: CallbackContext,
    llm_request: LlmRequest
) -> Optional[LlmResponse]:
  for part in callback_context.get_last_user_input():
    if part.text == "<event>session start</event>":
      return LlmResponse.from_parts(parts=[
          Part.from_customized_response(
              content=("Hello, I'm Gemini. Please listen to the following legal "
                       "disclaimer: <LEGAL_DISCLAIMER>"),
              disable_barge_in=True
          ),
          Part.from_text("How can I help you today?")
      ])
  return None

无输入时的自定义回答

当代理等待输入超时时 (请参阅静默超时代理应用设置中), 系统默认使用生成式回答。 不过,您可以在模型前的回调中检查用户是否收到了输入,并有条件地提供回答。

def before_model_callback(
    callback_context: CallbackContext,
    llm_request: LlmRequest
) -> Optional[LlmResponse]:
  for part in callback_context.get_last_user_input():
    if part.text:
      if "no user activity detected" in part.text:
        return LlmResponse.from_parts(parts=[
            Part.from_text(text="Hi, are you still there?")
        ])

  return None

错误处理

本部分介绍了处理工具错误的模式。

在工具失败时转移到其他代理

当特定工具执行失败时,您可以确定性地移交给其他代理来处理对话。这是在运行时错误期间保护用户体验的关键安全保障。

def before_model_callback(
    callback_context: CallbackContext,
    llm_request: LlmRequest
) -> Optional[LlmResponse]:
  for part in llm_request.contents[-1].parts:
    if (part.has_function_response('authentication') and
        'error' in part.function_response.response['result']):
      return LlmResponse.from_parts(parts=[
          Part.from_text('Sorry something went wrong, let me transfer you to another agent.'),
          Part.from_agent_transfer(agent='escalation agent')
      ])
  return None

在工具失败时正常终止会话

当特定工具执行失败时,您可以正常终止会话。 这可以防止在发生严重工具故障时出现无限循环和令人困惑的回答。

回调示例:

def before_model_callback(
    callback_context: CallbackContext,
    llm_request: LlmRequest
) -> Optional[LlmResponse]:
  for part in llm_request.contents[-1].parts:
    if (part.has_function_response('authentication') and
        'error' in part.function_response.response['result']):
      return LlmResponse.from_parts(parts=[
          Part.from_text('Sorry something went wrong, please call back later.'),
          Part.from_end_session(reason='Failure during user authentication.')
      ])
  return None

上下文和变量

本部分介绍了使用上下文变量的模式。

将上下文变量传递给 OpenAPI 工具

个性化 AI 需要工具来访问用户会话数据。 依赖模型手动回忆和传递重要详细信息(例如会话 ID 或用户变量)本质上是不可靠且缓慢的。 相反,代理可以将特定上下文变量传递给 OpenAPI 工具。 您可以使用 x-ces-session-context 指明该值不需要由模型生成(并且其架构对模型不可见),而是来自上下文变量。

下表列出了可用的值:

说明
$context.project_id 项目 ID。 Google Cloud
$context.project_number 项目编号。 Google Cloud
$context.location 代理的位置(区域)。
$context.app_id 代理应用 ID。
$context.session_id 会话的唯一标识符。
$context.variables 所有上下文变量值(作为对象)。
$context.variables.variable_name 特定上下文变量的值。将 variable_name 替换为变量的名称。
openapi: 3.0.0
info:
  title: test-title
  description: test-description
  version: 1.0.0
paths:
  /test-path/{session_id}:
    post:
      parameters:
      - name: session_id
        in: path
        description: The session ID.
        required: true
        schema:
          type: string
        x-ces-session-context: $context.session_id
      - name: test_variable
        in: query
        description: Specific session variable.
        required: true
        schema:
          type: string
        x-ces-session-context: $context.variables.test_variable
      requestBody:
        description: test-description
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SessionParams'
      responses:
        '200':
          description: test-response-description
          content:
            application/json:
             schema:
                type: object
                properties:
                  result:
                    type: string
components:
  schemas:
    SessionParams:
      type: object
      description: all context variables
      x-ces-session-context: $context.variables

动态提示

您可以使用以下方式构建具有发送给模型的动态提示的代理:

例如,您可以根据用户是律师还是海盗来更改代理说明:

变量:

变量名称 默认值
current_instructions 您是 Gemini,为 Google 工作。
lawyer_instructions 您是一名律师,您的工作是讲爸爸式笑话,但要带点律师的风格。
pirate_instructions 您是一名海盗,您的工作是以海盗的身份讲笑话。
username 未知

说明:

The current user is: {username}
You can use {@TOOL: update_username} to update the user's name if they provide
it.

Follow the current instruction set below exactly.

{current_instructions}

Python 工具:

from typing import Optional

def update_username(username: str) -> Optional[str]:
  """Updates the current user's name."""
  set_variable("username", username)

回调:

def before_model_callback(
  callback_context: CallbackContext,
  llm_request: LlmRequest
) -> Optional[LlmResponse]:
  username = callback_context.get_variable("username", None)

  if username == "Jenn":
    new_instructions = callback_context.get_variable("pirate_instructions")

  elif username == "Gary":
    new_instructions = callback_context.get_variable("lawyer_instructions")

  callback_context.set_variable("current_instructions", new_instructions)