Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: Amazon Bedrock Agents Functions #6300

Open
2 tasks done
anafalcao opened this issue Mar 19, 2025 · 0 comments
Open
2 tasks done

Feature request: Amazon Bedrock Agents Functions #6300

anafalcao opened this issue Mar 19, 2025 · 0 comments

Comments

@anafalcao
Copy link
Contributor

anafalcao commented Mar 19, 2025

Use case

RFC: #6081
Issue in TS: aws-powertools/powertools-lambda-typescript/issues/3710

Bedrock Agents allows you to define action groups for your agents in two ways: OpenAPI schemas, and direct function integration. This issue focuses on the latter.

As a customer I can create Bedrock Agents that have tools at their disposal. These tools, or functions, can be defined as AWS Lambda functions. One Lambda function can hold one or more tools and when looked at together, they are what makes an action group.

When I build a Lambda function with multiple tools in it, I am responsible for parsing the payload sent by Bedrock and, based on certain fields, call the corresponding tool in my code (aka the tool use). The response of this tool use is then returned by my Lambda function handler according to a specific format that Bedrock expects.

This can result in some degree of boilerplate code that I have to repeat for each action group, specifically:

  • parsing/validating the incoming Bedrock Agent request payload
  • handling the event using the correct tool/function
  • building the response according to the response Bedrock Agent payload schema

Example

def get_current_time():
    ...

def greet_user(name):
    ...

def simple_calculator(a, b, operation):
    ...

def lambda_handler(event, context):
    agent = event['agent']
    actionGroup = event['actionGroup']
    function = event['function']
    parameters = event.get('parameters', [])

    # Execute the appropriate function based on the 'function' parameter
    if function == 'get_current_time':
        result = get_current_time()
    elif function == 'greet_user':
        name = parameters[0]['value'] if parameters else "User"
        result = greet_user(name)
    elif function == 'simple_calculator':
        if len(parameters) >= 3:
            a = float(parameters[0]['value'])
            b = float(parameters[1]['value'])
            operation = parameters[2]['value']
            result = simple_calculator(a, b, operation)
        else:
            result = "Error: Insufficient parameters for simple_calculator"
    else:
        result = f"Unknown function: {function}"

    responseBody = {
        "TEXT": {
            "body": str(result)
        }
    }

    action_response = {
        'actionGroup': actionGroup,
        'function': function,
        'functionResponse': {
            'responseBody': responseBody
        }
    }

    function_response = {'response': action_response, 'messageVersion': event['messageVersion']}
    print("Response: {}".format(function_response))

    return function_response

As a customer I would like to abstract all of that and instead focus primarily on building the tools for my agents, which are the only part of this that is specific to my business.

Solution/User Experience

When paired with a Lambda function via action group, Bedrock sends and expects payloads of known shapes.

Payload example

{
    "messageVersion": "1.0",
    "agent": {
        "alias": "PROD",
        "name": "hr-assistant-function-def",
        "version": "1",
        "id": "1234abcd-56ef-78gh-90ij-klmn12345678"
    },
    "sessionId": "87654321-abcd-efgh-ijkl-mnop12345678",
    "sessionAttributes": {
        "employeeId": "EMP123",
        "department": "Engineering"
    },
    "promptSessionAttributes": {
        "lastInteraction": "2024-02-01T15:30:00Z",
        "requestType": "vacation"
    },
    "inputText": "I want to request vacation from March 15 to March 20",
    "actionGroup": "VacationsActionGroup",
    "function": "submitVacationRequest",
    "parameters": [{
        "employeeId": "EMP123",
        "startDate": "2024-03-15",
        "endDate": "2024-03-20",
        "vacationType": "annual"
    }]
}

Documentation

Response payload example

{
   "response":{
      "actionGroup":"SmarterAgentActionGroup",
      "function":"submitVacationRequest",
      "functionResponse":{
         "responseBody":{
            "TEXT":{
               "body":"Your vacation was scheduled!"
            }
         }
      }
   },
   "messageVersion":"1.0"
}

Documentation

Since the input event includes both the function and parameters fields, we can abstract most/all the boilerplate and provide a more streamlined experience.

We could implement a BedrockAgentFunctionResolver resolver that provides a structured way to register functions, resolve function calls, and handle requests within Lambda.

from aws_lambda_powertools.event_handler import BedrockAgentFunctionResolver
from aws_lambda_powertools.utilities.typing import LambdaContext

app = BedrockAgentFunctionResolver()

@app.tool(name="currentTime", description="Gets the current UTC time")
def get_current_time():
    ... 
@app.tool(description="Greets the user using name")
def greet_user(name):
...
@app.tool(description="Add two numbers")
def simple_calculator(a, b, operation):
...
def lambda_handler(event: dict, context: LambdaContext) → dict:
    return app.resolve(event, context)

Similar to the experience for Bedrock Agents with OpenAPI, we need a decorator tool that have name parameter as Optional. If customers want to make this more resilient to refactoring or want to have special naming conventions they can optionally specify a name parameter and in that case that takes precedence over the actual function name.

The implementation above allows customers to focus on defining and implementing the tools within the action group rather than the undifferentiated boilerplate required to parse an event, resolve which tool to use, call it, and build the response payload.

Finally, customers can also access the current request event in the tool definitions, as app.current_event, ang get information like session_id or prompt_session_attributes similar to Bedrock Agents OpenAPI - accessing custom request fields

Alternative solutions

See this discussion for considerations on alternative solutions: #6081

Acknowledgment

@anafalcao anafalcao added feature-request feature request triage Pending triage from maintainers labels Mar 19, 2025
@anafalcao anafalcao moved this from Triage to Backlog in Powertools for AWS Lambda (Python) Mar 19, 2025
@anafalcao anafalcao added researching event_handlers and removed triage Pending triage from maintainers labels Mar 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Backlog
Development

No branches or pull requests

1 participant