[{"data":1,"prerenderedAt":59},["ShallowReactive",2],{"snippet-lambda-durable-ai-agent-with-tools":3},{"id":4,"title":5,"contributors":6,"description":8,"extension":9,"gitHub":10,"introBox":13,"languages":19,"meta":22,"resources":23,"services":28,"snippets":31,"stem":49,"tags":50,"type":57,"__hash__":58},"snippets\u002Fsnippets\u002Flambda-durable-ai-agent-with-tools.json","Agent with Tools using AWS Lambda durable functions",[7],"content\u002Fcontributors\u002Fconnor-kirkpatrick.json","Agentic loop where the LLM can call tools, including tools that suspend for human input, using AWS Lambda durable functions","json",{"template":11},{"repoURL":12},"https:\u002F\u002Fgithub.com\u002Faws-samples\u002Fsample-ai-workflows-in-aws-lambda-durable-functions",{"headline":14,"text":15},"How it works",[16,17,18],"This pattern demonstrates an agentic loop with AWS Lambda durable functions. The LLM can call tools, and each model call and tool execution is checkpointed for deterministic replay.","Tools can use durable context features like waitForCallback to suspend the agent while awaiting external input.","Benefits: Complex agent loops are expressed as simple sequential code. Each tool call is checkpointed for resilient execution. Tools can suspend for human input without consuming compute resources.",[20,21],"Python","TypeScript",{},{"bullets":24},[25],{"text":26,"link":27},"AWS Lambda durable functions","https:\u002F\u002Faws.amazon.com\u002Flambda\u002Flambda-durable-functions\u002F",[29,30],"lambda-event-functions","bedrock",[32],{"title":33,"codeTabs":34},"Runtimes",[35,42],{"id":20,"title":36,"description":37,"snippets":38},"Usage Example with Python:","Agent with tools using Lambda durable functions in Python.",[39],{"language":40,"code":41},"py","from typing import Any, Callable\n\nimport boto3\nfrom aws_durable_execution_sdk_python import DurableContext, durable_execution\n\nMODEL_ID = \"us.amazon.nova-pro-v1:0\"\nbedrock = boto3.client(\"bedrock-runtime\")\n\n\nclass AgentTool:\n    def __init__(self, tool_spec: dict, execute: Callable[[dict, DurableContext], str]):\n        self.tool_spec = tool_spec\n        self.execute = execute\n\n\nTOOLS: list[AgentTool] = [\n    AgentTool(\n        tool_spec={\n            \"name\": \"get_weather\",\n            \"description\": \"Get the current weather for a location.\",\n            \"inputSchema\": {\n                \"json\": {\n                    \"type\": \"object\",\n                    \"properties\": {\"location\": {\"type\": \"string\"}},\n                    \"required\": [\"location\"],\n                }\n            },\n        },\n        execute=lambda input, ctx: f\"The weather in {input.get('location', 'unknown')} is sunny, 72°F.\",\n    ),\n    AgentTool(\n        tool_spec={\n            \"name\": \"wait_for_human_review\",\n            \"description\": \"Request human review and wait for response.\",\n            \"inputSchema\": {\n                \"json\": {\n                    \"type\": \"object\",\n                    \"properties\": {\"question\": {\"type\": \"string\"}},\n                    \"required\": [\"question\"],\n                }\n            },\n        },\n        execute=lambda input, ctx: ctx.wait_for_callback(\n            lambda callback_id, _: print(f\"Review needed: {input.get('question')}\"),\n            \"human_review\",\n        ),\n    ),\n]\n\n\n@durable_execution\ndef handler(event: dict, context: DurableContext):\n    prompt = event.get(\"prompt\", \"What's the weather in Seattle?\")\n    messages: list[Any] = [{\"role\": \"user\", \"content\": [{\"text\": prompt}]}]\n    tools_by_name = {t.tool_spec[\"name\"]: t for t in TOOLS}\n\n    while True:\n        response = context.step(\n            lambda _: bedrock.converse(\n                modelId=MODEL_ID,\n                messages=messages,\n                toolConfig={\"tools\": [{\"toolSpec\": t.tool_spec} for t in TOOLS]},\n            ),\n            \"converse\",\n        )\n\n        output = response.get(\"output\", {}).get(\"message\", {})\n        messages.append(output)\n\n        if response.get(\"stopReason\") == \"end_turn\":\n            for block in output.get(\"content\", []):\n                if \"text\" in block:\n                    return block[\"text\"]\n            return \"\"\n\n        tool_results = []\n        for block in output.get(\"content\", []):\n            if \"toolUse\" in block:\n                tool_use = block[\"toolUse\"]\n                tool = tools_by_name[tool_use[\"name\"]]\n                result = context.run_in_child_context(\n                    lambda child_ctx: tool.execute(tool_use.get(\"input\", {}), child_ctx),\n                    f\"tool:{tool_use['name']}\",\n                )\n                tool_results.append({\n                    \"toolResult\": {\n                        \"toolUseId\": tool_use[\"toolUseId\"],\n                        \"content\": [{\"text\": result}],\n                    }\n                })\n\n        messages.append({\"role\": \"user\", \"content\": tool_results})\n",{"id":21,"title":43,"description":44,"snippets":45},"Usage Example with TypeScript:","Agent with tools using Lambda durable functions in TypeScript.",[46],{"language":47,"code":48},"ts","import {\n  type DurableContext,\n  withDurableExecution,\n} from \"@aws\u002Fdurable-execution-sdk-js\";\nimport {\n  BedrockRuntimeClient,\n  type ContentBlock,\n  ConverseCommand,\n  type Message,\n  type Tool,\n} from \"@aws-sdk\u002Fclient-bedrock-runtime\";\n\nconst MODEL_ID = \"us.amazon.nova-pro-v1:0\";\nconst bedrock = new BedrockRuntimeClient({});\n\ntype AgentTool = {\n  toolSpec: NonNullable&#60;Tool[\"toolSpec\"]&#62;;\n  execute: (input: Record&#60;string, string&#62;, context: DurableContext) =&#62; Promise&#60;string&#62;;\n};\n\nconst tools: AgentTool[] = [\n  {\n    toolSpec: {\n      name: \"get_weather\",\n      description: \"Get the current weather for a location.\",\n      inputSchema: {\n        json: {\n          type: \"object\",\n          properties: { location: { type: \"string\" } },\n          required: [\"location\"],\n        },\n      },\n    },\n    execute: async (input) =&#62; `The weather in ${input.location} is sunny, 72°F.`,\n  },\n  {\n    toolSpec: {\n      name: \"wait_for_human_review\",\n      description: \"Request human review and wait for response.\",\n      inputSchema: {\n        json: {\n          type: \"object\",\n          properties: { question: { type: \"string\" } },\n          required: [\"question\"],\n        },\n      },\n    },\n    execute: async (input, context) =&#62;\n      context.waitForCallback&#60;string&#62;(\"human_review\", async (callbackId) =&#62; {\n        console.log(`Review needed: ${input.question}`);\n      }),\n  },\n];\n\nexport const handler = withDurableExecution(\n  async (event: { prompt?: string }, context: DurableContext) =&#62; {\n    const prompt = event.prompt ?? \"What's the weather in Seattle?\";\n    const messages: Message[] = [{ role: \"user\", content: [{ text: prompt }] }];\n    const toolsByName = Object.fromEntries(tools.map((t) =&#62; [t.toolSpec.name, t]));\n\n    while (true) {\n      const response = await context.step(\"converse\", async () =&#62; {\n        return bedrock.send(\n          new ConverseCommand({\n            modelId: MODEL_ID,\n            messages,\n            toolConfig: { tools: tools.map((t) =&#62; ({ toolSpec: t.toolSpec })) },\n          })\n        );\n      });\n\n      const output = response.output!.message!;\n      messages.push(output);\n\n      if (response.stopReason === \"end_turn\") {\n        const textBlock = output.content?.find((b): b is ContentBlock.TextMember =&#62; \"text\" in b);\n        return textBlock?.text ?? \"\";\n      }\n\n      const toolResults: ContentBlock[] = [];\n      for (const block of output.content ?? []) {\n        if (\"toolUse\" in block && block.toolUse) {\n          const { toolUseId, name, input } = block.toolUse;\n          const tool = toolsByName[name!];\n          const result = await context.runInChildContext(`tool:${name}`, async (childContext) =&#62; {\n            return tool.execute(input as Record&#60;string, string&#62;, childContext);\n          });\n          toolResults.push({ toolResult: { toolUseId, content: [{ text: result }] } });\n        }\n      }\n\n      messages.push({ role: \"user\", content: toolResults });\n    }\n  }\n);\n","snippets\u002Flambda-durable-ai-agent-with-tools",[51,52,30,53,54,55,56],"ai","durable-functions","genai","agent","tools","function-calling","Integration","LSdsxK_pg2mqWucu70NgKbGTev1YlMqiHDd_i240az8",1792578864364]