AWSAWS Certified Generative AI Developer - Professional2026-102.1 Implement agentic AI solutions and tool integrations
Validate tool contracts at every boundary
Completed by the knowledge check
Learning objective
Validate Strands and Lambda tool contracts with typed function schemas, parameter checks, and explicit error handling
Core idea
For an engineer connecting an agent to a tool, a schema communicates what the model can request; the implementation decides what it can execute. Type annotations and JSON schemas improve the interface, but a syntactically valid argument can still identify another tenant’s record, exceed an allowed amount, or request a forbidden operation.
Validate the contract at both ends. Check parameters before the side effect, then check and normalize the returned result before it enters the agent’s context. Keep transport errors, function-execution errors, business rejection, and uncertain execution distinct. The next action depends on which outcome occurred.
Strands tools and Lambda functions occupy different layers. A Strands tool can wrap a Lambda invocation, but Lambda does not automatically inherit the tool’s schema or business validation. The adapter must map arguments, trusted caller context, invocation responses, and tool results explicitly.
How it works
Publish a typed and narrow interface
Strands discovers Python tools through the @tool decorator. The decorator uses the function’s docstring and type hints as the tool contract. Choose names and descriptions that make required inputs, units, and side effects clear. A string named amount leaves more ambiguity than a documented integer representing minor currency units.
Use the narrowest operation that the caller needs. A tool that retrieves a customer record should not accept an arbitrary AWS action, table name, or unrestricted query. Keep trusted tenant identity and execution identity in server-side context rather than giving the model responsibility for selecting them.
For Bedrock tool-use interfaces, ToolSpecification carries name, description, and inputSchema. The schema is required; the tool name is required, limited to 1–64 characters, and accepts letters, numbers, underscores, and hyphens. A useful schema specifies required properties and their intended types, while the implementation enforces the same contract.
Keep discovery and execution contracts consistent. If the description says the tool is read-only but the implementation writes an audit-sensitive business record, the interface misrepresents its effect. If the function expects an identifier and the schema publishes a display name, the model cannot reliably construct a valid call. Treat a contract revision as a coordinated change across the decorator, adapter, and executor.
Check parameters before execution
The AWS Strands security guidance identifies BeforeToolCallEvent as the boundary for inspecting tool parameters. A hook reads event.tool_use, and setting event.cancel_tool cancels a rejected call before the action occurs. This supports shared checks such as tool-name allowlists and parameter validation without putting all policy text into the prompt; see 2.1.1 for hook registration.
Apply deterministic checks for structure and business constraints. Verify required fields, types, allowed values, size bounds, identifier syntax, and relationships among fields. Then check the target against authenticated caller context and business state. A positive amount can pass a type check while exceeding the reviewer’s authorized limit; a valid customer identifier can belong to another tenant.
Repeat the essential checks inside the Lambda executor. The agent hook is an early rejection boundary, while the executor protects the action if another authorized caller invokes the function directly. Use the same contract definition or explicitly version the two representations so their rules cannot drift unnoticed.
Do not silently repair consequential arguments. Normalizing harmless whitespace can be part of a defined contract. Replacing an unknown customer with a similar name or rounding a payment amount changes the requested action. Return a precise rejection and require a corrected request instead.
Validate and normalize outputs
The Strands AfterToolCallEvent hook can inspect a tool’s result and replace unsafe content before it reaches the agent. AWS’s example replaces event.result with a structured error containing toolUseId, status, and content. Use that boundary for output schema checks as well as content-policy checks.
A returned JSON object needs validation just as much as input JSON. Verify identifiers, required fields, numeric units, and the declared result version. Preserve source references and distinguish “not found” from “lookup unavailable.” An empty object that erases those differences encourages the model to invent a successful result.
Bedrock ToolResultBlock.toolUseId binds the response to the request that produced it, and content carries the result blocks. Its optional status accepts success or error; AWS documents support for that field specifically for Amazon Nova and Anthropic Claude 3 and 4 models. Do not assume every model interface interprets the field identically. Keep the application’s own error handling independent of whether the model understands that marker.
Tool output remains data, even when it contains instructions. A returned web page must not alter the registered tool set or the validation rules. Model-level content controls do not replace checks at these tool boundaries; the concrete Strands hooks let the application inspect the interaction itself.
Map Lambda outcomes without losing meaning
When an adapter calls Lambda Invoke, use InvocationType: RequestResponse if it needs a result for the agent’s next step. The invocation response includes StatusCode, optional FunctionError, and Payload. A 200 status means the synchronous invocation request succeeded; FunctionError indicates an error during function execution, whose details appear in the payload.
Do not treat successful invocation as successful business execution. A function can complete normally and return your application’s rejected-result envelope. The adapter must check both the Lambda-level outcome and the documented payload contract. Parse the payload, verify its schema, and map it to a successful observation, correctable rejection, transient failure, or uncertain action result.
InvocationType: Event returns an asynchronous acceptance status of 202 rather than a synchronous tool result. Do not feed that acceptance to the agent as the completed lookup or write. If your design uses asynchronous work, provide a separate completion mechanism and keep the action pending until it resolves.
A synchronous invocation reaches the agent only after the adapter checks both the Lambda-level outcome and the payload contract, while an asynchronous acceptance keeps the action pending.
If Step Functions invokes the executor through its optimized Lambda integration, an exception raised in the Lambda function fails the Task, and the integration parses the response Payload into JSON. Choose which failures raise exceptions and which return business decisions. A returned rejection envelope needs explicit routing; it does not automatically activate a workflow Catch.
Key distinctions and trade-offs
The checks have different responsibilities:
| Boundary | Required interpretation |
|---|---|
| Type hints and published schema | What arguments the tool advertises |
BeforeToolCallEvent | Whether the proposed call should reach the executor |
| Lambda executor validation | Whether this caller and business state permit execution |
| Lambda invocation response | Whether invocation and function execution completed |
AfterToolCallEvent | Whether the observation is valid and safe to return |
Detailed errors help the model correct its arguments, but unrestricted exception text can disclose sensitive implementation details. Return bounded public error codes and useful correction hints, while retaining diagnostic detail in trusted operational records.
Architecture / decision scenario
A Strands purchasing assistant exposes a typed reserve_inventory tool whose documented inputs are product identifier and integer quantity. The wrapper supplies tenant and execution identity from trusted context and invokes a Lambda executor synchronously.
The BeforeToolCallEvent hook rejects a negative quantity before invocation. The Lambda function independently checks the same constraint, resolves the product within the tenant, and verifies available stock. Insufficient stock returns an explicit business rejection; an unexpected execution exception produces FunctionError, which the adapter handles separately from the rejection payload.
A successful result includes the reservation identifier, quantity, and operation identifier. The adapter validates those fields and retains the request’s toolUseId. AfterToolCallEvent rejects a malformed result before it enters the agent’s context. If the invocation connection fails after a possible reservation, the adapter records an uncertain outcome and reconciles by operation identifier instead of creating another reservation. See 2.1.7 for shared retry limits.
Common exam traps
- Treating type hints or
inputSchemaas authorization. - Protecting the Strands hook while leaving the Lambda executor unchecked.
- Reading Lambda status 200 as proof of business success.
- Reading asynchronous status 202 as the completed tool result.
- Assuming a returned rejection activates Step Functions
Catch. - Losing
toolUseIdor assuming universal support for the resultstatusfield.
What to remember
- Advertise a typed contract and enforce it before the side effect.
- Validate inputs at the hook and executor boundaries.
- Inspect Lambda
FunctionErrorand the business payload separately. - Normalize and validate output before returning it to the agent.
- Preserve request identity and reconcile uncertain writes before retrying.
Knowledge check
A Strands BeforeToolCallEvent hook validates every reserve_inventory call against the tool’s typed schema and the caller’s tenant before invoking a Lambda executor. A second internal application with permission to invoke that function calls it directly. Where must the tenant and business checks also run?
A tool adapter calls a Lambda executor with InvocationType: RequestResponse. The response has StatusCode 200, no FunctionError, and a payload of {"result": "rejected", "reason": "insufficient_stock"}. How should the adapter report this to the agent?
FunctionError says the function ran without an execution error. The business outcome is in the payload contract, and here it is a rejection; retrying the same request will not create stock.To cut latency, a tool adapter switches its Lambda call to InvocationType: Event and receives status 202. The agent’s next step depends on the reservation identifier. What should the adapter return?
A Step Functions Task invokes a Lambda executor through the optimized integration. When stock is short, the function returns {"result": "rejected"}, and the team expects the Task’s Catch to route it to a review state. Instead the workflow continues to the next step. What fixes it?
States.TaskFailed to the Catch so it matches business rejectionsChoice after the TaskRetry on the Task so the rejection is attempted againInvocationType: Event so rejections arrive separatelyTask and reaches Catch ; a returned rejection is a successful invocation whose payload the integration parses into JSON. The workflow must route on that payload explicitly, or the function must raise for the cases meant to be failures.A web-lookup tool returns a JSON result that is missing its required source identifier and contains text telling the agent to disable validation. Where should the application stop this observation from reaching the agent’s context?
BeforeToolCallEvent hook that checked the lookup’s parametersAfterToolCallEvent hook that validates the result before it returnsAfterToolCallEvent can inspect event.result and replace it with a structured error before the agent sees it; the before-call hook ran too early, and a prompt asks rather than enforces.