Skip to content

Support Approval Func in BaseTool in AgentChat #5891

@victordibia

Description

@victordibia

Background

Agents can act via tools. The BaseTool interface in AutoGen provides an excellent scaffold for building tools across the framework. Extensions of BaseTool like FunctionTool, PythonCodeExecutionTool, and others demonstrate the flexibility and value of this tool.

The Approval Challenge and Opportunity

An important capability for a framework is the ability to allow users to "approve" actions before they are "executed". The opportunity here is to build this into the BaseTool library by adding an approval mechanism that maintains backward compatibility.

Proposed Design Sketch [discussion welcome]

1. Add Approval Function to BaseTool

# Add to BaseTool init
def __init__(
    self,
    args_type: Type[ArgsT],
    return_type: Type[ReturnT],
    name: str,
    description: str,
    strict: bool = False,
    approval_func: Optional[Callable[[str, Mapping[str, Any]], Awaitable[ToolApprovalResult]]] = None,
) -> None:
    # ... existing initialization ...
    self._approval_func = approval_func or default_approval

2. Create an Approval Result Model

class ToolApprovalResult(BaseModel):
    """Result of a tool approval request."""
    approved: bool
    reason: Optional[str] = None

# Default approval function that always approves
async def default_approval(tool_name: str, arguments: Mapping[str, Any]) -> ToolApprovalResult:
    return ToolApprovalResult(approved=True)

3. Modify run_json to Check Approval

async def run_json(self, args: Mapping[str, Any], cancellation_token: CancellationToken) -> Any:
    # Approval check
    approval_result = await self._approval_func(self.name, args)
    
    # Log the approval event
    event = ToolCallApprovalEvent(
        tool_name=self.name,
        arguments=dict(args),
        approved=approval_result.approved,
        reason=approval_result.reason
    )
    logger.info(event)
    
    # If not approved, return early
    if not approval_result.approved:
        return f"Tool execution not approved: {approval_result.reason or 'No reason provided'}" 
   
        
    # Execute tool if approved (existing functionality)
    return_value = await self.run(self._args_type.model_validate(args), cancellation_token)
    # ... existing logging ...
    return return_value

4. Add to AssistantAgent

def __init__(
    self,
    # ... existing parameters ...
    tool_approval_func: Optional[Callable[[str, Mapping[str, Any]], Awaitable[ToolApprovalResult]]] = None,
):
    # ... existing initialization ...
    self._tool_approval_func = tool_approval_func
    
    # When processing tools, apply the approval func
    for tool in tools:
        if isinstance(tool, BaseTool) and self._tool_approval_func:
            tool._approval_func = self._tool_approval_func
        elif callable(tool) and self._tool_approval_func:
            # For function tools, wrap with approval
            function_tool._approval_func = self._tool_approval_func

Benefits

  1. Maintains backward compatibility
  2. Provides consistent approval mechanism across all tools
  3. Allows for centralized approval at agent level or per-tool
  4. Enables logging of approval decisions

Implementation Notes / Open Questions

  • Default behavior should match current behavior (auto-approve)
  • Should emit events to notify of approval requests and decisions
  • Both BaseTool and AssistantAgent should accept approval functions
  • We need to check for side effects (e.g, if reflect_on_tool call , we should skpi this if tool denied)
  • Should we add tool_call_approval_func to assistant agent?
  • what happens with parrallel tool calls?
  • can approval/disapproval be constrained .. e.g,. provide feedback e.g., on poor tool call parameters or let the approval function modify the parameters ?
  • How do we pass context to the approval_function? E.g., if the function is soemthign that uses an LLM, we might need to provide plumbing for the context to be passed to the approval func

Thoughts welcome @ekzhu @jackgerrits @husseinmozannar

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-designA design needs to be created and agreed upoproj-coretool-usagesuggestion and execution of function/tool call

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions