Skip to content

获取 ADK 工具的操作确认

Supported in ADKPython v1.14.0TypeScript v0.2.0Go v0.3.0Experimental

某些智能体工作流需要确认来进行决策、验证、安全或一般监督。在这些情况下,你希望在工作流继续之前从人类或监督系统获得响应。智能体开发工具包(ADK)中的 工具确认 功能允许 ADK 工具暂停其执行,并与用户或其他系统交互以获得确认或在继续之前收集结构化数据。你可以通过以下方式在 ADK 工具中使用工具确认:

  • 布尔确认 你可以使用确认标志或提供者配置工具。此选项暂停工具以等待是或否的确认响应。
  • 高级确认 对于需要结构化数据响应的场景,你可以使用文本提示配置工具以说明确认内容及预期响应。

实验性功能

工具确认功能是实验性的,有一些已知限制。 我们欢迎你的反馈

你可以配置如何向用户传达请求,系统也可以使用通过 ADK 服务器的 REST API 发送的远程响应。当在 ADK Web 用户界面中使用确认功能时,智能体工作流会向用户显示一个对话框来请求输入,如图 1 所示:

Screenshot of default user interface for tool confirmation

图 1。 使用高级工具响应实现的确认响应请求对话框示例。

以下章节描述了如何在确认场景中使用此功能。有关完整的代码示例,请参阅 human_tool_confirmation 示例。还有其他方法可以将人工输入集成到你的智能体工作流中,更多详细信息,请参阅人在回路 (Human-in-the-loop) 智能体模式。

布尔确认

当你的工具只需要用户的简单"是"或"否"时,你可以附加一个确认步骤。在 Python、Go 和 Java 中,你可以通过使用 FunctionTool 类包装工具并将 require_confirmation 参数(或等效参数)设置为 True 来启用此功能。在 TypeScript 中,你可以在 execute 函数中使用 ToolContext 手动实现此逻辑。

以下示例展示了如何启用布尔确认:

root_agent = Agent(
    # ...
    tools = [
        # Set require_confirmation to True to require user confirmation
        # for the tool call.
        FunctionTool(reimburse, require_confirmation=True),
    ],
    # ...
)

# 此实现方法需要最少的代码,但仅限于来自用户或确认系统的简单
# 审批。有关此方法的完整示例,请参阅以下代码示例以获取更详细的示例:
# https://github.com/google/adk-python/blob/main/contributing/samples/human_tool_confirmation/agent.py

Note

ADK TypeScript 版本目前需要在工具的 execute 函数中手动实现确认逻辑。

/**
 * A reimbursement tool with dynamic confirmation logic.
 */
export const reimburseTool = new FunctionTool({
  name: 'reimburse',
  description: 'Reimburse an amount. Large amounts (>1000) require manager approval.',
  parameters: z.object({
    amount: z.coerce.number().describe('The amount to reimburse.'),
  }),
  execute: async ({amount}, toolContext) => {
    // 1. Check if we already have a confirmed response.
    if (toolContext?.toolConfirmation?.confirmed) {
      const isLarge = amount > 1000;
      return {
        status: 'SUCCESS',
        message: isLarge 
          ? `Large reimbursement of ${amount} approved by manager and processed.`
          : `Reimbursement of ${amount} has been successfully processed.`,
      };
    }

    // 2. Request a tool confirmation.
    const isLarge = amount > 1000;
    toolContext?.requestConfirmation({
      hint: isLarge 
        ? `The amount ${amount} exceeds the $1000 limit and requires manager approval.`
        : `Do you want to reimburse ${amount}?`,
      payload: {amount},
    });

    // 3. Return a status that tells the agent we are waiting.
    // Note: The model won't see this until the turn resumes after confirmation.
    return {
      status: isLarge ? 'AWAITING_MANAGER_APPROVAL' : 'AWAITING_CONFIRMATION',
      message: 'This request requires approval to proceed.',
    };
  },
});

export const rootAgent = new LlmAgent({
  name: 'Finance_Assistant',
  model: 'gemini-flash-latest',
  instruction: `You are a Finance Assistant. 
  - You MUST use the 'reimburse' tool for ALL reimbursement requests.
  - MANDATORY: Every tool call MUST be accompanied by a text response in the same message.
  - THRESHOLD LOGIC:
    - For amounts <= 1000: Say "I am initiating the reimbursement request for [amount]. Please confirm it to proceed."
    - For amounts > 1000: Say "I am initiating the reimbursement request for [amount]. Since this exceeds $1000, manager approval is required. Please confirm the request to submit it for review."
  - EXAMPLES:
    User: "Reimburse me $45"
    Model: "I am initiating the reimbursement request for 45. Please confirm it to proceed." [Tool Call: reimburse(amount=45)]

    User: "Reimburse me $2500"
    Model: "I am initiating the reimbursement request for 2500. Since this exceeds $1000, manager approval is required. Please confirm the request to submit it for review." [Tool Call: reimburse(amount=2500)]
  - If the user provides a currency symbol (like $), ignore it and pass only the number to the tool.
  - In the Web UI, the user will see a 'Confirm' button. In the terminal, the user should simulate a confirmation response.`,
  tools: [reimburseTool],
});
reimburseTool, _ := functiontool.New(functiontool.Config{
    Name:        "reimburse",
    Description: "Reimburse an amount",
    // Set RequireConfirmation to true to require user confirmation
    // for the tool call.
    RequireConfirmation: true,
}, func(ctx tool.Context, args ReimburseArgs) (ReimburseResult, error) {
    // actual implementation
    return ReimburseResult{Status: "ok"}, nil
})

rootAgent, _ := llmagent.New(llmagent.Config{
    // ...
    Tools: []tool.Tool{reimburseTool},
})
LlmAgent rootAgent = LlmAgent.builder()
    // ...
    .tools(
        // Set requireConfirmation to true to require user confirmation
        // for the tool call.
        FunctionTool.create(myClassInstance, "reimburse", true)
    )
    // ...
    .build();

需要确认函数

你可以通过使用基于工具输入返回布尔响应的函数来修改确认要求的行为。在 TypeScript 中,这通过向 execute 函数添加条件逻辑来处理。

async def confirmation_threshold(
    amount: int, tool_context: ToolContext
) -> bool:
  """如果金额大于 1000,则返回 true。"""
  return amount > 1000

root_agent = Agent(
    # ...
    tools = [
        # Pass the threshold function to dynamically require confirmation
        FunctionTool(reimburse, require_confirmation=confirmation_threshold),
    ],
    # ...
)
/* 
  Note: In TypeScript, dynamic threshold logic is implemented 
  directly within the tool's 'execute' function as shown above.
*/
reimburseTool, _ := functiontool.New(functiontool.Config{
    Name:        "reimburse",
    Description: "Reimburse an amount",
    // RequireConfirmationProvider allows for dynamic determination
    // of whether user confirmation is needed.
    RequireConfirmationProvider: func(args ReimburseArgs) bool {
        return args.Amount > 1000
    },
}, func(ctx tool.Context, args ReimburseArgs) (ReimburseResult, error) {
    // actual implementation
    return ReimburseResult{Status: "ok"}, nil
})
// In ADK Java, dynamic threshold confirmation logic is evaluated directly
// inside the tool logic using the ToolContext rather than via a lambda parameter.
public Map<String, Object> reimburse(
    @Schema(name="amount") int amount, ToolContext toolContext) {

  // 1. Dynamic threshold check
  if (amount > 1000) {
    Optional<ToolConfirmation> toolConfirmation = toolContext.toolConfirmation();
    if (toolConfirmation.isEmpty()) {
       toolContext.requestConfirmation("Amount > 1000 requires approval.");
       return Map.of("status", "Pending manager approval.");
    } else if (!toolConfirmation.get().confirmed()) {
       return Map.of("status", "Reimbursement rejected.");
    }
  }

  // 2. Proceed with actual tool logic
  return Map.of("status", "ok", "reimbursedAmount", amount);
}

LlmAgent rootAgent = LlmAgent.builder()
    // ...
    .tools(
        // No requireConfirmation flag is set because the custom threshold
        // logic is already handled inside the method!
        FunctionTool.create(this, "reimburse")
    )
    // ...
    .build();

高级确认

当工具确认需要更多用户详细信息或更复杂的响应时,请使用 tool_confirmation 实现。这种方法扩展了 ToolContext 对象,为用户添加请求的文本描述,并允许更复杂的响应数据。当以这种方式实现工具确认时,你可以暂停工具的执行,请求特定信息,然后使用提供的数据恢复工具。

此确认流程有一个请求阶段,系统在此阶段组装并发送输入请求人类响应,以及一个响应阶段,系统在此阶段接收和处理返回的数据。

确认定义

当创建带有高级确认的工具时,使用Tool Context Request Confirmation 方法和 hintpayload 参数:

  • hint:向用户解释需要什么信息的描述性消息。
  • payload:你期望返回的数据结构。这必须是可序列化为 JSON 格式字符串的结构。

有关此方法的完整示例,请参阅 human_tool_confirmation 代码示例。请记住,智能体工作流工具执行在获得确认时会暂停。收到确认后,你可以在 tool_confirmation.payload 对象中访问确认响应,然后继续执行工作流。

以下代码展示了一个处理员工休假请求的工具的示例实现:

def request_time_off(days: int, tool_context: ToolContext):
    """Request day off for the employee."""
    # ...
    tool_confirmation = tool_context.tool_confirmation
    if not tool_confirmation:
        tool_context.request_confirmation(
            hint=(
                'Please approve or reject the tool call request_time_off() by'
                ' responding with a FunctionResponse with an expected'
                ' ToolConfirmation payload.'
            ),
            payload={
                'approved_days': 0,
            },
        )
        # Return intermediate status indicating that the tool is waiting for
        # a confirmation response:
        return {'status': 'Manager approval is required.'}

    approved_days = tool_confirmation.payload['approved_days']
    approved_days = min(approved_days, days)
    if approved_days == 0:
        return {'status': 'The time off request is rejected.', 'approved_days': 0}
    return {
        'status': 'ok',
        'approved_days': approved_days,
    }
/**
 * A tool that requests time off for an employee.
 * It uses the Advanced Confirmation pattern to request manager approval.
 */
export const requestTimeOffTool = new FunctionTool({
  name: 'request_time_off',
  description: 'Request days off for the employee.',
  parameters: z.object({
    days: z.number().describe('The number of days requested.'),
  }),
  execute: async ({days}, toolContext) => {
    const confirmation = toolContext?.toolConfirmation;

    if (!confirmation) {
      // Step 1: Request confirmation with a payload
      toolContext?.requestConfirmation({
        hint:
          'Please approve or reject the tool call request_time_off() by ' +
          'responding with a FunctionResponse with an expected ' +
          'ToolConfirmation payload.',
        payload: {
          approved_days: 0,
        },
      });

      // Return a descriptive status to the agent
      return {
        status: 'PENDING_MANAGER_APPROVAL',
        message: `A request for ${days} days is pending manager approval.`,
      };
    }

    // Step 2: Process the confirmation response
    if (!confirmation.confirmed) {
      return {
        status: 'CANCELLED',
        message: 'The request was cancelled by the user.',
      };
    }

    let approvedDays = (confirmation.payload as any)['approved_days'] as number;
    approvedDays = Math.min(approvedDays, days);

    if (approvedDays === 0) {
      return {
        status: 'REJECTED',
        message: 'The time off request was rejected by the manager.',
        approved_days: 0,
      };
    }

    return {
      status: 'SUCCESS',
      message: `The request for ${days} days was approved (Total approved: ${approvedDays}).`,
      approved_days: approvedDays,
    };
  },
});

export const rootAgent = new LlmAgent({
  name: 'HR_Assistant',
  model: 'gemini-flash-latest',
  instruction: `You are an HR Assistant. 
  1. Use the 'request_time_off' tool to help employees with leave requests.
  2. MANDATORY: Every tool call MUST be accompanied by a text response in the same message.
  3. EXAMPLE:
     User: "I want 5 days off"
     Model: "I am initiating your leave request for 5 days. Management approval is required, so please confirm this request." [Tool Call: request_time_off(days=5)]
  4. In the terminal, if they want to 'confirm', tell them to simulate a confirmation response. 
  5. Once confirmed, the system will automatically provide the result of the approval.`,
  tools: [requestTimeOffTool],
});
func requestTimeOff(ctx tool.Context, args RequestTimeOffArgs) (map[string]any, error) {
    confirmation := ctx.ToolConfirmation()
    if confirmation == nil {
        ctx.RequestConfirmation(
            "Please approve or reject the tool call requestTimeOff() by "+
            "responding with a FunctionResponse with an expected "+
            "ToolConfirmation payload.",
            map[string]any{"approved_days": 0},
        )
        return map[string]any{"status": "Manager approval is required."}, nil
    }

    payload := confirmation.Payload.(map[string]any)
    // Values in map[string]any from JSON are float64 by default in Go
    approvedDays := int(payload["approved_days"].(float64))
    approvedDays = min(approvedDays, args.Days)

    if approvedDays == 0 {
        return map[string]any{"status": "The time off request is rejected.", "approved_days": 0}, nil
    }

    return map[string]any{
        "status": "ok",
        "approved_days": approvedDays,
    }, nil
}
public Map<String, Object> requestTimeOff(
    @Schema(name="days") int days,
    ToolContext toolContext) {
    // Request day off for the employee.
    // ...
    Optional<ToolConfirmation> toolConfirmation = toolContext.toolConfirmation();
    if (toolConfirmation.isEmpty()) {
        toolContext.requestConfirmation(
            "Please approve or reject the tool call requestTimeOff() by " +
            "responding with a FunctionResponse with an expected " +
            "ToolConfirmation payload.",
            Map.of("approved_days", 0)
        );
        // Return intermediate status indicating that the tool is waiting for
        // a confirmation response:
        return Map.of("status", "Manager approval is required.");
    }

    Map<String, Object> payload = (Map<String, Object>) toolConfirmation.get().payload();
    int approvedDays = (int) payload.get("approved_days");
    approvedDays = Math.min(approvedDays, days);

    if (approvedDays == 0) {
        return Map.of("status", "The time off request is rejected.", "approved_days", 0);
    }

    return Map.of(
        "status", "ok",
        "approved_days", approvedDays
    );
}

通过 REST API 进行远程确认

如果没有用于智能体工作流人工确认的活动用户界面,你可以通过命令行界面或通过电子邮件或聊天应用程序等其他渠道路由来处理确认。要确认工具调用,用户或调用应用程序需要发送带有工具确认数据的 FunctionResponse 事件。

你可以将请求发送到 ADK API 服务器的 /run/run_sse 端点,或直接发送到 ADK 运行器。以下示例使用 curl 命令将确认发送到 /run_sse 端点:

 curl -X POST http://localhost:8000/run_sse \
 -H "Content-Type: application/json" \
 -d '{
    "app_name": "human_tool_confirmation",
    "user_id": "user",
    "session_id": "7828f575-2402-489f-8079-74ea95b6a300",
    "new_message": {
        "parts": [
            {
                "function_response": {
                    "id": "adk-13b84a8c-c95c-4d66-b006-d72b30447e35",
                    "name": "adk_request_confirmation",
                    "response": {
                        "confirmed": true,
                        "payload": {
                            "approved_days": 5
                        }
                    }
                }
            }
        ],
        "role": "user"
    }
}'

基于 REST 的确认响应必须满足以下要求:

  • function_response 中的 id 应与 adk_request_confirmation FunctionCall 事件中的 function_call_id 匹配。
  • name 应为 adk_request_confirmation
  • response 对象包含 confirmed 状态以及任何附加的 payload 数据。

    注意:带 Resume 功能的确认

    如果你的 ADK 智能体工作流配置了 Resume 功能,你还必须在确认响应中包含调用 ID (invocation_id) 参数。你提供的调用 ID 必须与生成确认请求的调用相同,否则系统会使用确认响应启动一个新的调用。如果你的智能体使用 Resume 功能,请考虑将调用 ID 作为参数包含在你的确认请求中,以便它可以被包含在响应中。有关使用 Resume 功能的更多详细信息,请参阅恢复已停止的智能体

已知限制

工具确认功能具有以下限制:

下一步

有关为智能体工作流构建 ADK 工具的更多信息,请参阅函数工具