diff --git a/index.toml b/index.toml index 17c9618..0a3cfd1 100644 --- a/index.toml +++ b/index.toml @@ -249,7 +249,7 @@ notebook = "47_Human_in_the_Loop_Agent.ipynb" aliases = [] completion_time = "20 min" created_at = 2025-10-30 -dependencies = ["haystack-experimental>=0.16.0"] +dependencies = ["haystack-ai>=2.23.0", "rich"] featured = true [[tutorial]] diff --git a/tutorials/47_Human_in_the_Loop_Agent.ipynb b/tutorials/47_Human_in_the_Loop_Agent.ipynb index 0066a34..87fe88a 100644 --- a/tutorials/47_Human_in_the_Loop_Agent.ipynb +++ b/tutorials/47_Human_in_the_Loop_Agent.ipynb @@ -1,863 +1,860 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "9a4d67cf-afb5-413b-bcae-eba61a8144ef", - "metadata": { - "id": "9a4d67cf-afb5-413b-bcae-eba61a8144ef" - }, - "source": [ - "# Tutorial: Human-in-the-Loop with Haystack Agents\n", - "\n", - "- **Level**: Advanced\n", - "- **Time to complete**: 20 minutes\n", - "- **Components Used**: [`OpenAIChatGenerator`](https://docs.haystack.deepset.ai/docs/openaichatgenerator)\n", - "- **Experimental Components Used**: [`Agent`](https://docs.haystack.deepset.ai/docs/agent)\n", - "- **Prerequisites**: You need an [OpenAI API Key](https://platform.openai.com/api-keys)\n", - "- **Goal**: After completing this tutorial, you'll have learned how to implement human-in-the-loop workflows in Haystack agents using confirmation strategies, create custom confirmation policies, and control tool execution approval flows.\n", - "\n", - "## Overview\n", - "This tutorial introduces how to use **confirmation strategies** to create **human-in-the-loop interactions** in Haystack's `Agent` component.\n", - "\n", - "**Why is this useful?** Imagine an AI agent that can access your bank account, send emails, or make purchases. You probably want human approval before it executes sensitive operations. Confirmation strategies let you define exactly when the agent should ask for permission.\n", - "\n", - "> 🧪 **Beta Feature Notice**:\n", - ">\n", - "> The **Human-in-the-Loop** feature is currently in **beta**, available in the [haystack-experimental](https://github.com/deepset-ai/haystack-experimental) repository. We'd love your feedback, join the conversation in [this GitHub discussion](https://github.com/deepset-ai/haystack-experimental/discussions/381) and help us shape this feature!" - ] - }, - { - "cell_type": "markdown", - "id": "4c2ab603-fdb5-43bb-8e34-12280a6b91cc", - "metadata": { - "id": "4c2ab603-fdb5-43bb-8e34-12280a6b91cc" - }, - "source": [ - "## How Confirmation Strategies Work\n", - "1. User sends a query to the agent\n", - "2. Agent determines which tool(s) to use\n", - "3. Before executing, the confirmation policy checks if approval is needed\n", - "4. If needed, the UI prompts the user for confirmation\n", - "5. Based on user response (confirm/reject/modify), the agent proceeds or adjusts\n", - "6. Agent returns the final answer to the user" - ] - }, - { - "cell_type": "markdown", - "id": "a311d46a-6688-4dbf-b851-dfa56f09e842", - "metadata": { - "id": "a311d46a-6688-4dbf-b851-dfa56f09e842" - }, - "source": [ - "## Preparing the Environment\n", - "\n", - "First, let's install required packages:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "39749563-ef6d-433f-859f-174a47357590", - "metadata": { - "id": "39749563-ef6d-433f-859f-174a47357590" - }, - "outputs": [], - "source": [ - "%%bash\n", - "\n", - "pip install -q haystack-experimental" - ] - }, - { - "cell_type": "markdown", - "id": "a8bcfa68-9255-45f5-b87f-f1dcbe53f644", - "metadata": { - "id": "a8bcfa68-9255-45f5-b87f-f1dcbe53f644" - }, - "source": [ - "### Enter API Keys\n", - "\n", - "Enter API keys required for this tutorial." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "82bdb67e-7187-45d8-8408-49102c8432c4", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "82bdb67e-7187-45d8-8408-49102c8432c4", - "outputId": "6d2cb151-20bd-4972-84d1-b93ef2c5a65c" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Enter your OpenAI API key:··········\n" - ] - } + "cells": [ + { + "cell_type": "markdown", + "id": "9a4d67cf-afb5-413b-bcae-eba61a8144ef", + "metadata": { + "id": "9a4d67cf-afb5-413b-bcae-eba61a8144ef" + }, + "source": [ + "# Tutorial: Human-in-the-Loop with Haystack Agents\n", + "\n", + "- **Level**: Advanced\n", + "- **Time to complete**: 20 minutes\n", + "- **Components Used**: [`Agent`](https://docs.haystack.deepset.ai/docs/agent), [`OpenAIChatGenerator`](https://docs.haystack.deepset.ai/docs/openaichatgenerator)\n", + "- **Prerequisites**: You need an [OpenAI API Key](https://platform.openai.com/api-keys)\n", + "- **Goal**: After completing this tutorial, you'll have learned how to implement human-in-the-loop workflows in Haystack agents using confirmation strategies, create custom confirmation policies, and control tool execution approval flows.\n", + "\n", + "## Overview\n", + "This tutorial introduces how to use **confirmation strategies** to create **human-in-the-loop interactions** in Haystack's `Agent` component.\n", + "\n", + "**Why is this useful?** Imagine an AI agent that can access your bank account, send emails, or make purchases. You probably want human approval before it executes sensitive operations. Confirmation strategies let you define exactly when the agent should ask for permission." + ] + }, + { + "cell_type": "markdown", + "id": "4c2ab603-fdb5-43bb-8e34-12280a6b91cc", + "metadata": { + "id": "4c2ab603-fdb5-43bb-8e34-12280a6b91cc" + }, + "source": [ + "## How Confirmation Strategies Work\n", + "1. User sends a query to the agent\n", + "2. Agent determines which tool(s) to use\n", + "3. Before executing, the confirmation policy checks if approval is needed\n", + "4. If needed, the UI prompts the user for confirmation\n", + "5. Based on user response (confirm/reject/modify), the agent proceeds or adjusts\n", + "6. Agent returns the final answer to the user" + ] + }, + { + "cell_type": "markdown", + "id": "a311d46a-6688-4dbf-b851-dfa56f09e842", + "metadata": { + "id": "a311d46a-6688-4dbf-b851-dfa56f09e842" + }, + "source": [ + "## Preparing the Environment\n", + "\n", + "First, let's install required packages:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "39749563-ef6d-433f-859f-174a47357590", + "metadata": { + "id": "39749563-ef6d-433f-859f-174a47357590" + }, + "outputs": [], + "source": [ + "%%bash\n", + "\n", + "pip install -q haystack-ai rich" + ] + }, + { + "cell_type": "markdown", + "id": "a8bcfa68-9255-45f5-b87f-f1dcbe53f644", + "metadata": { + "id": "a8bcfa68-9255-45f5-b87f-f1dcbe53f644" + }, + "source": [ + "### Enter API Keys\n", + "\n", + "Enter API keys required for this tutorial." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "82bdb67e-7187-45d8-8408-49102c8432c4", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "82bdb67e-7187-45d8-8408-49102c8432c4", + "outputId": "6d2cb151-20bd-4972-84d1-b93ef2c5a65c" + }, + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Enter your OpenAI API key: ········\n" + ] + } + ], + "source": [ + "from getpass import getpass\n", + "import os\n", + "\n", + "if not os.environ.get(\"OPENAI_API_KEY\"):\n", + " os.environ[\"OPENAI_API_KEY\"] = getpass(\"Enter your OpenAI API key:\")" + ] + }, + { + "cell_type": "markdown", + "id": "7b2579dc-2c59-4c3b-9691-73f5803d2add", + "metadata": { + "id": "7b2579dc-2c59-4c3b-9691-73f5803d2add" + }, + "source": [ + "## Setup and Imports\n", + "\n", + "We begin by importing Haystack classes, UI helpers, and the experimental Agent component." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "541cf667-a6b6-4e5b-beb1-65745c3f6ac2", + "metadata": { + "id": "541cf667-a6b6-4e5b-beb1-65745c3f6ac2" + }, + "outputs": [], + "source": [ + "from haystack.components.generators.chat import OpenAIChatGenerator\n", + "from haystack.dataclasses import ChatMessage\n", + "from haystack.tools import create_tool_from_function\n", + "from rich.console import Console\n", + "\n", + "from haystack.components.agents.agent import Agent\n", + "from haystack.human_in_the_loop import (\n", + " AlwaysAskPolicy,\n", + " AskOncePolicy,\n", + " BlockingConfirmationStrategy,\n", + " NeverAskPolicy,\n", + " RichConsoleUI,\n", + " SimpleConsoleUI,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "ebcafd9d-aad1-47fd-80cb-db0738a5e604", + "metadata": { + "id": "ebcafd9d-aad1-47fd-80cb-db0738a5e604" + }, + "source": [ + "## Define Agent Tools\n", + "\n", + "Create three simple tools for demonstration: `addition`, `get_bank_balance`, and `get_phone_number` with [`create_tool_from_function`](https://docs.haystack.deepset.ai/docs/tool#create_tool_from_function). Alternatively, you can use [`@tool` decorator](https://docs.haystack.deepset.ai/docs/tool#tool-decorator) to define your tools." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "16c6972f-20ae-4315-b47f-047902e4fca3", + "metadata": { + "id": "16c6972f-20ae-4315-b47f-047902e4fca3" + }, + "outputs": [], + "source": [ + "def addition(a: float, b: float) -> float:\n", + " \"\"\"Add two numbers.\"\"\"\n", + " return a + b\n", + "\n", + "\n", + "addition_tool = create_tool_from_function(function=addition, name=\"addition\", description=\"Add two floats together.\")\n", + "\n", + "\n", + "def get_bank_balance(account_id: str) -> str:\n", + " \"\"\"Simulate fetching a bank balance.\"\"\"\n", + " return f\"Balance for account {account_id} is $1,234.56\"\n", + "\n", + "\n", + "balance_tool = create_tool_from_function(\n", + " function=get_bank_balance, name=\"get_bank_balance\", description=\"Get the bank balance for a given account ID.\"\n", + ")\n", + "\n", + "\n", + "def get_phone_number(name: str) -> str:\n", + " \"\"\"Simulate fetching a phone number.\"\"\"\n", + " return f\"The phone number for {name} is (123) 456-7890\"\n", + "\n", + "\n", + "phone_tool = create_tool_from_function(\n", + " function=get_phone_number, name=\"get_phone_number\", description=\"Get the phone number for a given name.\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "5c2e21f4-b84f-4411-a5f1-7312afd44188", + "metadata": { + "id": "5c2e21f4-b84f-4411-a5f1-7312afd44188" + }, + "source": [ + "## Instantiate the Agent\n", + "\n", + "Instantiate the experimental [`Agent`](https://github.com/deepset-ai/haystack-experimental/blob/b2abe5486ee4ad03b6f3c136ca54aee482b9ed01/haystack_experimental/components/agents/agent.py#L69) with multiple tools and assign each tool a **confirmation strategy**." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "b6c81b8b-437e-4234-a6e8-8704708f5df9", + "metadata": { + "id": "b6c81b8b-437e-4234-a6e8-8704708f5df9" + }, + "outputs": [], + "source": [ + "cons = Console()\n", + "\n", + "agent = Agent(\n", + " chat_generator=OpenAIChatGenerator(model=\"gpt-4.1-mini\"),\n", + " tools=[balance_tool, addition_tool, phone_tool],\n", + " system_prompt=\"You are a helpful financial assistant. Use the provided tools to answer user questions.\",\n", + " confirmation_strategies={\n", + " balance_tool.name: BlockingConfirmationStrategy(\n", + " confirmation_policy=AlwaysAskPolicy(), confirmation_ui=RichConsoleUI(console=cons)\n", + " ),\n", + " addition_tool.name: BlockingConfirmationStrategy(\n", + " confirmation_policy=NeverAskPolicy(), confirmation_ui=RichConsoleUI(console=cons)\n", + " ),\n", + " phone_tool.name: BlockingConfirmationStrategy(\n", + " confirmation_policy=AskOncePolicy(), confirmation_ui=RichConsoleUI(console=cons)\n", + " ),\n", + " },\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "4a1e1fc9-bf38-4207-a50f-dded827c4ae6", + "metadata": { + "id": "4a1e1fc9-bf38-4207-a50f-dded827c4ae6" + }, + "source": [ + "### Explanation\n", + "\n", + "Each [`BlockingConfirmationStrategy`](https://github.com/deepset-ai/haystack-experimental/blob/b2abe5486ee4ad03b6f3c136ca54aee482b9ed01/haystack_experimental/components/agents/human_in_the_loop/strategies.py#L32) defines when and how the agent asks for confirmation before executing a tool:\n", + "\n", + "* **`AlwaysAskPolicy`** – Always asks for approval before running.\n", + "* **`NeverAskPolicy`** – Executes automatically without user confirmation.\n", + "* **`AskOncePolicy`** – Asks once per tool, remembers approval for future runs.\n", + "\n", + "The UI can be either:\n", + "\n", + "* `RichConsoleUI` for colorized and more aesthetic prompts.\n", + "* `SimpleConsoleUI` for basic prompts.\n", + "\n", + "\n", + "Both UIs will present three options:\n", + "\n", + "- **y**: Proceed with the tool execution as planned\n", + "- **n**: Cancel the tool execution; agent will try alternative approaches\n", + "- **m**: Edit the tool parameters before execution\n", + "\n", + "**NOTE**: Custom UIs and Policies can also be implemented by following the respective `ConfirmationUI` and `ConfirmationPolicy` protocols. This allows full flexibility for domain-specific workflows." + ] + }, + { + "cell_type": "markdown", + "id": "19a20a0b-dcd0-4b87-8e88-49f0e784abd8", + "metadata": { + "id": "19a20a0b-dcd0-4b87-8e88-49f0e784abd8" + }, + "source": [ + "## Run the Agent\n", + "\n", + "Now we can run the agent with different confirmation behaviors.\n", + "\n", + "Try different confirmation strategies and explore the available options returned by the UIs: \"y\", \"n\", \"m\". This lets you test how the agent reacts to different human feedback scenarios." + ] + }, + { + "cell_type": "markdown", + "id": "ccd9b2c6-cc02-40af-a02d-ce50867e259a", + "metadata": { + "id": "ccd9b2c6-cc02-40af-a02d-ce50867e259a" + }, + "source": [ + "### Always Ask Policy (Rich UI)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "5a29facf-abbb-4aea-8671-339e03c0a807", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 185 + }, + "id": "5a29facf-abbb-4aea-8671-339e03c0a807", + "outputId": "a6d487a2-9aa3-40e2-8d07-b6ac2bf35a5d" + }, + "outputs": [ + { + "data": { + "text/html": [ + "
╭─ 🔧 Tool Execution Request ─────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Tool: get_bank_balance │\n", + "│ Description: Get the bank balance for a given account ID. │\n", + "│ │\n", + "│ Arguments: │\n", + "│ account_id: 56789 │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n", + "\n" ], - "source": [ - "from getpass import getpass\n", - "import os\n", - "\n", - "if not os.environ.get(\"OPENAI_API_KEY\"):\n", - " os.environ[\"OPENAI_API_KEY\"] = getpass(\"Enter your OpenAI API key:\")" - ] - }, - { - "cell_type": "markdown", - "id": "7b2579dc-2c59-4c3b-9691-73f5803d2add", - "metadata": { - "id": "7b2579dc-2c59-4c3b-9691-73f5803d2add" - }, - "source": [ - "## Setup and Imports\n", - "\n", - "We begin by importing Haystack classes, UI helpers, and the experimental Agent component." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "541cf667-a6b6-4e5b-beb1-65745c3f6ac2", - "metadata": { - "id": "541cf667-a6b6-4e5b-beb1-65745c3f6ac2" - }, - "outputs": [], - "source": [ - "from haystack.components.generators.chat import OpenAIChatGenerator\n", - "from haystack.dataclasses import ChatMessage\n", - "from haystack.tools import create_tool_from_function\n", - "from rich.console import Console\n", - "\n", - "from haystack_experimental.components.agents.agent import Agent\n", - "from haystack_experimental.components.agents.human_in_the_loop import (\n", - " AlwaysAskPolicy,\n", - " AskOncePolicy,\n", - " BlockingConfirmationStrategy,\n", - " NeverAskPolicy,\n", - " RichConsoleUI,\n", - " SimpleConsoleUI,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "ebcafd9d-aad1-47fd-80cb-db0738a5e604", - "metadata": { - "id": "ebcafd9d-aad1-47fd-80cb-db0738a5e604" - }, - "source": [ - "## Define Agent Tools\n", - "\n", - "Create three simple tools for demonstration: `addition`, `get_bank_balance`, and `get_phone_number` with [`create_tool_from_function`](https://docs.haystack.deepset.ai/docs/tool#create_tool_from_function). Alternatively, you can use [`@tool` decorator](https://docs.haystack.deepset.ai/docs/tool#tool-decorator) to define your tools." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "16c6972f-20ae-4315-b47f-047902e4fca3", - "metadata": { - "id": "16c6972f-20ae-4315-b47f-047902e4fca3" - }, - "outputs": [], - "source": [ - "def addition(a: float, b: float) -> float:\n", - " \"\"\"Add two numbers.\"\"\"\n", - " return a + b\n", - "\n", - "\n", - "addition_tool = create_tool_from_function(function=addition, name=\"addition\", description=\"Add two floats together.\")\n", - "\n", - "\n", - "def get_bank_balance(account_id: str) -> str:\n", - " \"\"\"Simulate fetching a bank balance.\"\"\"\n", - " return f\"Balance for account {account_id} is $1,234.56\"\n", - "\n", - "\n", - "balance_tool = create_tool_from_function(\n", - " function=get_bank_balance, name=\"get_bank_balance\", description=\"Get the bank balance for a given account ID.\"\n", - ")\n", - "\n", - "\n", - "def get_phone_number(name: str) -> str:\n", - " \"\"\"Simulate fetching a phone number.\"\"\"\n", - " return f\"The phone number for {name} is (123) 456-7890\"\n", - "\n", - "\n", - "phone_tool = create_tool_from_function(\n", - " function=get_phone_number, name=\"get_phone_number\", description=\"Get the phone number for a given name.\"\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "5c2e21f4-b84f-4411-a5f1-7312afd44188", - "metadata": { - "id": "5c2e21f4-b84f-4411-a5f1-7312afd44188" - }, - "source": [ - "## Instantiate the Agent\n", - "\n", - "Instantiate the experimental [`Agent`](https://github.com/deepset-ai/haystack-experimental/blob/b2abe5486ee4ad03b6f3c136ca54aee482b9ed01/haystack_experimental/components/agents/agent.py#L69) with multiple tools and assign each tool a **confirmation strategy**." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "b6c81b8b-437e-4234-a6e8-8704708f5df9", - "metadata": { - "id": "b6c81b8b-437e-4234-a6e8-8704708f5df9" - }, - "outputs": [], - "source": [ - "cons = Console()\n", - "\n", - "agent = Agent(\n", - " chat_generator=OpenAIChatGenerator(model=\"gpt-4.1-mini\"),\n", - " tools=[balance_tool, addition_tool, phone_tool],\n", - " system_prompt=\"You are a helpful financial assistant. Use the provided tools to answer user questions.\",\n", - " confirmation_strategies={\n", - " balance_tool.name: BlockingConfirmationStrategy(\n", - " confirmation_policy=AlwaysAskPolicy(), confirmation_ui=RichConsoleUI(console=cons)\n", - " ),\n", - " addition_tool.name: BlockingConfirmationStrategy(\n", - " confirmation_policy=NeverAskPolicy(), confirmation_ui=RichConsoleUI(console=cons)\n", - " ),\n", - " phone_tool.name: BlockingConfirmationStrategy(\n", - " confirmation_policy=AskOncePolicy(), confirmation_ui=RichConsoleUI(console=cons)\n", - " ),\n", - " },\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "4a1e1fc9-bf38-4207-a50f-dded827c4ae6", - "metadata": { - "id": "4a1e1fc9-bf38-4207-a50f-dded827c4ae6" - }, - "source": [ - "### Explanation\n", - "\n", - "Each [`BlockingConfirmationStrategy`](https://github.com/deepset-ai/haystack-experimental/blob/b2abe5486ee4ad03b6f3c136ca54aee482b9ed01/haystack_experimental/components/agents/human_in_the_loop/strategies.py#L32) defines when and how the agent asks for confirmation before executing a tool:\n", - "\n", - "* **`AlwaysAskPolicy`** – Always asks for approval before running.\n", - "* **`NeverAskPolicy`** – Executes automatically without user confirmation.\n", - "* **`AskOncePolicy`** – Asks once per tool, remembers approval for future runs.\n", - "\n", - "The UI can be either:\n", - "\n", - "* `RichConsoleUI` for colorized and more aesthetic prompts.\n", - "* `SimpleConsoleUI` for basic prompts.\n", - "\n", - "\n", - "Both UIs will present three options:\n", - "\n", - "- **y**: Proceed with the tool execution as planned\n", - "- **n**: Cancel the tool execution; agent will try alternative approaches\n", - "- **m**: Edit the tool parameters before execution\n", - "\n", - "**NOTE**: Custom UIs and Policies can also be implemented by following the respective `ConfirmationUI` and `ConfirmationPolicy` protocols. This allows full flexibility for domain-specific workflows." - ] - }, - { - "cell_type": "markdown", - "id": "19a20a0b-dcd0-4b87-8e88-49f0e784abd8", - "metadata": { - "id": "19a20a0b-dcd0-4b87-8e88-49f0e784abd8" - }, - "source": [ - "## Run the Agent\n", - "\n", - "Now we can run the agent with different confirmation behaviors.\n", - "\n", - "Try different confirmation strategies and explore the available options returned by the UIs: \"y\", \"n\", \"m\". This lets you test how the agent reacts to different human feedback scenarios." - ] - }, - { - "cell_type": "markdown", - "id": "ccd9b2c6-cc02-40af-a02d-ce50867e259a", - "metadata": { - "id": "ccd9b2c6-cc02-40af-a02d-ce50867e259a" - }, - "source": [ - "### Always Ask Policy (Rich UI)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "5a29facf-abbb-4aea-8671-339e03c0a807", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 185 - }, - "id": "5a29facf-abbb-4aea-8671-339e03c0a807", - "outputId": "a6d487a2-9aa3-40e2-8d07-b6ac2bf35a5d" - }, - "outputs": [ - { - "data": { - "text/html": [ - "
╭─ 🔧 Tool Execution Request ─────────────────────────────────────────────────────────────────────────────────────╮\n", - "│ Tool: get_bank_balance │\n", - "│ Description: Get the bank balance for a given account ID. │\n", - "│ │\n", - "│ Arguments: │\n", - "│ account_id: 56789 │\n", - "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n", - "\n" - ], - "text/plain": [ - "╭─ 🔧 Tool Execution Request ─────────────────────────────────────────────────────────────────────────────────────╮\n", - "│ \u001b[1;33mTool:\u001b[0m get_bank_balance │\n", - "│ \u001b[1;33mDescription:\u001b[0m Get the bank balance for a given account ID. │\n", - "│ │\n", - "│ \u001b[1;33mArguments:\u001b[0m │\n", - "│ \u001b[36maccount_id:\u001b[0m 56789 │\n", - "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "Your choice [y/n/m] (y):\n" - ], - "text/plain": [ - "\n", - "Your choice \u001b[1;35m[y/n/m]\u001b[0m \u001b[1;36m(y)\u001b[0m: " - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "y\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "Agent Result: The balance of account 56789 is $1,234.56.\n", - "\n" - ], - "text/plain": [ - "\n", - "\u001b[1;32mAgent Result:\u001b[0m The balance of account \u001b[1;36m56789\u001b[0m is $\u001b[1;36m1\u001b[0m,\u001b[1;36m234.56\u001b[0m.\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } + "text/plain": [ + "╭─ 🔧 Tool Execution Request ─────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ \u001b[1;33mTool:\u001b[0m get_bank_balance │\n", + "│ \u001b[1;33mDescription:\u001b[0m Get the bank balance for a given account ID. │\n", + "│ │\n", + "│ \u001b[1;33mArguments:\u001b[0m │\n", + "│ \u001b[36maccount_id:\u001b[0m 56789 │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "Your choice [y/n/m] (y):\n" ], - "source": [ - "result = agent.run([ChatMessage.from_user(\"What's the balance of account 56789?\")])\n", - "cons.print(f\"\\n[bold green]Agent Result:[/bold green] {result['last_message'].text}\")" + "text/plain": [ + "\n", + "Your choice \u001b[1;35m[y/n/m]\u001b[0m \u001b[1;36m(y)\u001b[0m: " ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "markdown", - "id": "99177d80-040a-4f97-8115-dff4a99019ba", - "metadata": { - "id": "99177d80-040a-4f97-8115-dff4a99019ba" - }, - "source": [ - "### Never Ask Policy\n", - "\n", - "This is the default behavior when no confirmation strategy is defined. The tool executes without asking for confirmation." - ] + "name": "stdin", + "output_type": "stream", + "text": [ + " y\n" + ] }, { - "cell_type": "code", - "execution_count": 7, - "id": "073bc908-bcfe-46d5-b5c1-7b19fb95252a", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 34 - }, - "id": "073bc908-bcfe-46d5-b5c1-7b19fb95252a", - "outputId": "77e32ff2-e779-410c-d4f1-ec5357e031a4" - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "Agent Result: The sum of 5.5 and 3.2 is 8.7.\n", - "\n" - ], - "text/plain": [ - "\n", - "\u001b[1;32mAgent Result:\u001b[0m The sum of \u001b[1;36m5.5\u001b[0m and \u001b[1;36m3.2\u001b[0m is \u001b[1;36m8.7\u001b[0m.\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } + "data": { + "text/html": [ + "
\n", + "Agent Result: The balance of account 56789 is $1,234.56.\n", + "\n" ], - "source": [ - "result = agent.run([ChatMessage.from_user(\"What is 5.5 + 3.2?\")])\n", - "cons.print(f\"\\n[bold green]Agent Result:[/bold green] {result['last_message'].text}\")" - ] - }, - { - "cell_type": "markdown", - "id": "abdd5af2-2056-4189-81dc-8ccaf3b33ba4", - "metadata": { - "id": "abdd5af2-2056-4189-81dc-8ccaf3b33ba4" - }, - "source": [ - "### Ask Once Policy" + "text/plain": [ + "\n", + "\u001b[1;32mAgent Result:\u001b[0m The balance of account \u001b[1;36m56789\u001b[0m is $\u001b[1;36m1\u001b[0m,\u001b[1;36m234.56\u001b[0m.\n" ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "9f60ce1e-c892-41f6-a031-255a4b7b53c1", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 185 - }, - "id": "9f60ce1e-c892-41f6-a031-255a4b7b53c1", - "outputId": "04db2a1b-0134-4a22-c0a9-6d2003165ddc" - }, - "outputs": [ - { - "data": { - "text/html": [ - "
╭─ 🔧 Tool Execution Request ─────────────────────────────────────────────────────────────────────────────────────╮\n", - "│ Tool: get_phone_number │\n", - "│ Description: Get the phone number for a given name. │\n", - "│ │\n", - "│ Arguments: │\n", - "│ name: Alice │\n", - "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n", - "\n" - ], - "text/plain": [ - "╭─ 🔧 Tool Execution Request ─────────────────────────────────────────────────────────────────────────────────────╮\n", - "│ \u001b[1;33mTool:\u001b[0m get_phone_number │\n", - "│ \u001b[1;33mDescription:\u001b[0m Get the phone number for a given name. │\n", - "│ │\n", - "│ \u001b[1;33mArguments:\u001b[0m │\n", - "│ \u001b[36mname:\u001b[0m Alice │\n", - "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "Your choice [y/n/m] (y):\n" - ], - "text/plain": [ - "\n", - "Your choice \u001b[1;35m[y/n/m]\u001b[0m \u001b[1;36m(y)\u001b[0m: " - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "y\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "Agent Result: The phone number of Alice is (123) 456-7890.\n", - "\n" - ], - "text/plain": [ - "\n", - "\u001b[1;32mAgent Result:\u001b[0m The phone number of Alice is \u001b[1m(\u001b[0m\u001b[1;36m123\u001b[0m\u001b[1m)\u001b[0m \u001b[1;36m456\u001b[0m-\u001b[1;36m7890\u001b[0m.\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "result = agent.run([ChatMessage.from_user(\"What's the balance of account 56789?\")])\n", + "cons.print(f\"\\n[bold green]Agent Result:[/bold green] {result['last_message'].text}\")" + ] + }, + { + "cell_type": "markdown", + "id": "99177d80-040a-4f97-8115-dff4a99019ba", + "metadata": { + "id": "99177d80-040a-4f97-8115-dff4a99019ba" + }, + "source": [ + "### Never Ask Policy\n", + "\n", + "This is the default behavior when no confirmation strategy is defined. The tool executes without asking for confirmation." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "073bc908-bcfe-46d5-b5c1-7b19fb95252a", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 + }, + "id": "073bc908-bcfe-46d5-b5c1-7b19fb95252a", + "outputId": "77e32ff2-e779-410c-d4f1-ec5357e031a4" + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "Agent Result: 5.5 + 3.2 is 8.7.\n", + "\n" ], - "source": [ - "result = agent.run([ChatMessage.from_user(\"What is the phone number of Alice?\")])\n", - "cons.print(f\"\\n[bold green]Agent Result:[/bold green] {result['last_message'].text}\")" - ] - }, - { - "cell_type": "markdown", - "id": "21b2b57b-4c5a-44cd-a2ff-bc9bfaa95d71", - "metadata": { - "id": "21b2b57b-4c5a-44cd-a2ff-bc9bfaa95d71" - }, - "source": [ - "### Ask Once: Cached Confirmation\n", - "\n", - "If you answered \"y\" (yes) to the previous phone number request, the agent will not ask for confirmation again for subsequent requests to the same tool called with the same parameters." + "text/plain": [ + "\n", + "\u001b[1;32mAgent Result:\u001b[0m \u001b[1;36m5.5\u001b[0m + \u001b[1;36m3.2\u001b[0m is \u001b[1;36m8.7\u001b[0m.\n" ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "d63207c7-f3fb-4bec-aaf7-5b8a25d5eb3b", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 34 - }, - "id": "d63207c7-f3fb-4bec-aaf7-5b8a25d5eb3b", - "outputId": "6abd466c-09ca-4c8d-9257-0d32d09edc1e" - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "Agent Result: The phone number of Alice is (123) 456-7890.\n", - "\n" - ], - "text/plain": [ - "\n", - "\u001b[1;32mAgent Result:\u001b[0m The phone number of Alice is \u001b[1m(\u001b[0m\u001b[1;36m123\u001b[0m\u001b[1m)\u001b[0m \u001b[1;36m456\u001b[0m-\u001b[1;36m7890\u001b[0m.\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "result = agent.run([ChatMessage.from_user(\"What is 5.5 + 3.2?\")])\n", + "cons.print(f\"\\n[bold green]Agent Result:[/bold green] {result['last_message'].text}\")" + ] + }, + { + "cell_type": "markdown", + "id": "abdd5af2-2056-4189-81dc-8ccaf3b33ba4", + "metadata": { + "id": "abdd5af2-2056-4189-81dc-8ccaf3b33ba4" + }, + "source": [ + "### Ask Once Policy" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "9f60ce1e-c892-41f6-a031-255a4b7b53c1", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 185 + }, + "id": "9f60ce1e-c892-41f6-a031-255a4b7b53c1", + "outputId": "04db2a1b-0134-4a22-c0a9-6d2003165ddc" + }, + "outputs": [ + { + "data": { + "text/html": [ + "
╭─ 🔧 Tool Execution Request ─────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Tool: get_phone_number │\n", + "│ Description: Get the phone number for a given name. │\n", + "│ │\n", + "│ Arguments: │\n", + "│ name: Alice │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n", + "\n" ], - "source": [ - "result = agent.run([ChatMessage.from_user(\"What is the phone number of Alice?\")])\n", - "cons.print(f\"\\n[bold green]Agent Result:[/bold green] {result['last_message'].text}\")" + "text/plain": [ + "╭─ 🔧 Tool Execution Request ─────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ \u001b[1;33mTool:\u001b[0m get_phone_number │\n", + "│ \u001b[1;33mDescription:\u001b[0m Get the phone number for a given name. │\n", + "│ │\n", + "│ \u001b[1;33mArguments:\u001b[0m │\n", + "│ \u001b[36mname:\u001b[0m Alice │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "Your choice [y/n/m] (y):\n" + ], + "text/plain": [ + "\n", + "Your choice \u001b[1;35m[y/n/m]\u001b[0m \u001b[1;36m(y)\u001b[0m: " ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "markdown", - "id": "bbee834d-8006-4f4d-a952-d83dd0bf7483", - "metadata": { - "id": "bbee834d-8006-4f4d-a952-d83dd0bf7483" - }, - "source": [ - "## Run an Agent with a Custom Policy\n", - "\n", - "Now, let's create a custom confirmation policy that asks for confirmation only when certain conditions are met. For example, you can create a policy that asks for confirmation when the tool operation involves expenses above a certain threshold." - ] + "name": "stdin", + "output_type": "stream", + "text": [ + " y\n" + ] }, { - "cell_type": "markdown", - "id": "b308ccd1-d086-4a50-b11a-d6c180f1c03f", - "metadata": { - "id": "b308ccd1-d086-4a50-b11a-d6c180f1c03f" - }, - "source": [ - "### Create a Custom Budget Based Policy\n", - "\n", - "First, define a `BudgetBasedPolicy` that asks for confirmation if the cost exceeds a defined threshold." + "data": { + "text/html": [ + "
\n", + "Agent Result: The phone number for Alice is (123) 456-7890.\n", + "\n" + ], + "text/plain": [ + "\n", + "\u001b[1;32mAgent Result:\u001b[0m The phone number for Alice is \u001b[1m(\u001b[0m\u001b[1;36m123\u001b[0m\u001b[1m)\u001b[0m \u001b[1;36m456\u001b[0m-\u001b[1;36m7890\u001b[0m.\n" ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "2c650eb7-244d-4a58-b024-e79a1b79e4cd", - "metadata": { - "id": "2c650eb7-244d-4a58-b024-e79a1b79e4cd" - }, - "outputs": [], - "source": [ - "from haystack_experimental.components.agents.human_in_the_loop import ConfirmationPolicy\n", - "from typing import Any\n", - "\n", - "class BudgetBasedPolicy(ConfirmationPolicy):\n", - " \"\"\"Ask for confirmation when operations exceed a cost threshold.\"\"\"\n", - "\n", - " def __init__(self, cost_threshold: float = 10.0):\n", - " self.cost_threshold = cost_threshold\n", - "\n", - " def should_ask(self, tool_name: str, tool_description: str, tool_params: dict[str, Any]) -> bool:\n", - " \"\"\"Ask if the operation cost exceeds the threshold.\"\"\"\n", - " # Check for cost-related parameters\n", - " cost = tool_params.get(\"cost\", 0.0)\n", - " amount = tool_params.get(\"amount\", 0.0)\n", - " price = tool_params.get(\"price\", 0.0)\n", - "\n", - " return max(cost, amount, price) > self.cost_threshold" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "result = agent.run([ChatMessage.from_user(\"What is the phone number of Alice?\")])\n", + "cons.print(f\"\\n[bold green]Agent Result:[/bold green] {result['last_message'].text}\")" + ] + }, + { + "cell_type": "markdown", + "id": "21b2b57b-4c5a-44cd-a2ff-bc9bfaa95d71", + "metadata": { + "id": "21b2b57b-4c5a-44cd-a2ff-bc9bfaa95d71" + }, + "source": [ + "### Ask Once: Cached Confirmation\n", + "\n", + "If you answered \"y\" (yes) to the previous phone number request, the agent will not ask for confirmation again for subsequent requests to the same tool called with the same parameters." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "d63207c7-f3fb-4bec-aaf7-5b8a25d5eb3b", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 + }, + "id": "d63207c7-f3fb-4bec-aaf7-5b8a25d5eb3b", + "outputId": "6abd466c-09ca-4c8d-9257-0d32d09edc1e" + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "Agent Result: The phone number of Alice is (123) 456-7890.\n", + "\n" + ], + "text/plain": [ + "\n", + "\u001b[1;32mAgent Result:\u001b[0m The phone number of Alice is \u001b[1m(\u001b[0m\u001b[1;36m123\u001b[0m\u001b[1m)\u001b[0m \u001b[1;36m456\u001b[0m-\u001b[1;36m7890\u001b[0m.\n" ] - }, - { - "cell_type": "markdown", - "id": "18a59695-dc6e-4690-a184-6e361b54d792", - "metadata": { - "id": "18a59695-dc6e-4690-a184-6e361b54d792" - }, - "source": [ - "### Define an Agent with Expense Tool\n", - "\n", - "Define an `expense` tool that simulates submitting an expense report and use the `BudgetBasedPolicy` for confirmation." + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "result = agent.run([ChatMessage.from_user(\"What is the phone number of Alice?\")])\n", + "cons.print(f\"\\n[bold green]Agent Result:[/bold green] {result['last_message'].text}\")" + ] + }, + { + "cell_type": "markdown", + "id": "bbee834d-8006-4f4d-a952-d83dd0bf7483", + "metadata": { + "id": "bbee834d-8006-4f4d-a952-d83dd0bf7483" + }, + "source": [ + "## Run an Agent with a Custom Policy\n", + "\n", + "Now, let's create a custom confirmation policy that asks for confirmation only when certain conditions are met. For example, you can create a policy that asks for confirmation when the tool operation involves expenses above a certain threshold." + ] + }, + { + "cell_type": "markdown", + "id": "b308ccd1-d086-4a50-b11a-d6c180f1c03f", + "metadata": { + "id": "b308ccd1-d086-4a50-b11a-d6c180f1c03f" + }, + "source": [ + "### Create a Custom Budget Based Policy\n", + "\n", + "First, define a `BudgetBasedPolicy` that asks for confirmation if the cost exceeds a defined threshold." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "2c650eb7-244d-4a58-b024-e79a1b79e4cd", + "metadata": { + "id": "2c650eb7-244d-4a58-b024-e79a1b79e4cd" + }, + "outputs": [], + "source": [ + "from haystack.human_in_the_loop.types import ConfirmationPolicy\n", + "from typing import Any\n", + "\n", + "\n", + "class BudgetBasedPolicy(ConfirmationPolicy):\n", + " \"\"\"Ask for confirmation when operations exceed a cost threshold.\"\"\"\n", + "\n", + " def __init__(self, cost_threshold: float = 10.0):\n", + " self.cost_threshold = cost_threshold\n", + "\n", + " def should_ask(self, tool_name: str, tool_description: str, tool_params: dict[str, Any]) -> bool:\n", + " \"\"\"Ask if the operation cost exceeds the threshold.\"\"\"\n", + " # Check for cost-related parameters\n", + " cost = tool_params.get(\"cost\", 0.0)\n", + " amount = tool_params.get(\"amount\", 0.0)\n", + " price = tool_params.get(\"price\", 0.0)\n", + "\n", + " return max(cost, amount, price) > self.cost_threshold" + ] + }, + { + "cell_type": "markdown", + "id": "18a59695-dc6e-4690-a184-6e361b54d792", + "metadata": { + "id": "18a59695-dc6e-4690-a184-6e361b54d792" + }, + "source": [ + "### Define an Agent with Expense Tool\n", + "\n", + "Define an `expense` tool that simulates submitting an expense report and use the `BudgetBasedPolicy` for confirmation." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "7810bdd3-7592-412e-a15b-106b5d4af44e", + "metadata": { + "id": "7810bdd3-7592-412e-a15b-106b5d4af44e" + }, + "outputs": [], + "source": [ + "from haystack.components.generators.utils import print_streaming_chunk\n", + "\n", + "\n", + "def expense(cost: float, description: str) -> float:\n", + " \"\"\"Submit an expense report that has a `cost` and `description`\"\"\"\n", + " # This is where we would add a real submission request\n", + " return \"Expense report submitted successfully!\"\n", + "\n", + "\n", + "expense_tool = create_tool_from_function(\n", + " function=expense, name=\"expense\", description=\"Submit an expense report that has a `cost` and `description`\"\n", + ")\n", + "\n", + "cons = Console()\n", + "\n", + "agent = Agent(\n", + " chat_generator=OpenAIChatGenerator(model=\"gpt-4.1\"),\n", + " tools=[expense_tool],\n", + " system_prompt=(\n", + " \"You are a helpful financial assistant that can submit expense reports for users. \"\n", + " \"Use the `expense` tool which only requires a cost amount and short description of the expense (e.g. 'busines lunch'). \"\n", + " \"Only respond with whether the report was submitted successfully. \"\n", + " ),\n", + " confirmation_strategies={\n", + " expense_tool.name: BlockingConfirmationStrategy(\n", + " confirmation_policy=BudgetBasedPolicy(cost_threshold=100.0), confirmation_ui=RichConsoleUI(console=cons)\n", + " )\n", + " },\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "4f88d315-2c92-48b5-82b0-d8123aa64ed9", + "metadata": { + "id": "4f88d315-2c92-48b5-82b0-d8123aa64ed9" + }, + "source": [ + "### Run the Agent\n", + "\n", + "Now, submit an expense report that's below the threshold." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "8061d79b-faaa-4ce4-baba-4c4cef8de637", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 + }, + "id": "8061d79b-faaa-4ce4-baba-4c4cef8de637", + "outputId": "6db71bc6-616b-4a55-a313-521d4903b021" + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "Agent Result: Expense report for business lunch ($10) was submitted successfully.\n", + "\n" + ], + "text/plain": [ + "\n", + "\u001b[1;32mAgent Result:\u001b[0m Expense report for business lunch \u001b[1m(\u001b[0m$\u001b[1;36m10\u001b[0m\u001b[1m)\u001b[0m was submitted successfully.\n" ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "7810bdd3-7592-412e-a15b-106b5d4af44e", - "metadata": { - "id": "7810bdd3-7592-412e-a15b-106b5d4af44e" - }, - "outputs": [], - "source": [ - "from haystack.components.generators.utils import print_streaming_chunk\n", - "\n", - "def expense(cost: float, description: str) -> float:\n", - " \"\"\"Submit an expense report that has a `cost` and `description`\"\"\"\n", - " # This is where we would add a real submission request\n", - " return \"Expense report submitted successfully!\"\n", - "\n", - "\n", - "expense_tool = create_tool_from_function(\n", - " function=expense, name=\"expense\", description=\"Submit an expense report that has a `cost` and `description`\"\n", - ")\n", - "\n", - "cons = Console()\n", - "\n", - "agent = Agent(\n", - " chat_generator=OpenAIChatGenerator(model=\"gpt-4.1\"),\n", - " tools=[expense_tool],\n", - " system_prompt=(\n", - " \"You are a helpful financial assistant that can submit expense reports for users. \"\n", - " \"Use the `expense` tool which only requires a cost amount and short description of the expense (e.g. 'busines lunch'). \"\n", - " \"Only respond with whether the report was submitted successfully. \"\n", - " ),\n", - " confirmation_strategies={\n", - " expense_tool.name: BlockingConfirmationStrategy(\n", - " confirmation_policy=BudgetBasedPolicy(cost_threshold=100.0), confirmation_ui=RichConsoleUI(console=cons)\n", - " )\n", - " },\n", - ")" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "result = agent.run([ChatMessage.from_user(\"Submit an expense report for business lunch that cost $10\")])\n", + "cons.print(f\"\\n[bold green]Agent Result:[/bold green] {result['last_message'].text}\")" + ] + }, + { + "cell_type": "markdown", + "id": "8b6c644f-74c9-4add-9da7-7f02a79a99b6", + "metadata": { + "id": "8b6c644f-74c9-4add-9da7-7f02a79a99b6" + }, + "source": [ + "Next, submit a request that's above the threshold to get confirmation." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "e89ca219-2e0f-4840-be65-82ca4f782586", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 253 + }, + "id": "e89ca219-2e0f-4840-be65-82ca4f782586", + "outputId": "f0d4127a-30a1-4f2e-841d-5a06ec023425" + }, + "outputs": [ + { + "data": { + "text/html": [ + "
╭─ 🔧 Tool Execution Request ─────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Tool: expense │\n", + "│ Description: Submit an expense report that has a `cost` and `description` │\n", + "│ │\n", + "│ Arguments: │\n", + "│ cost: 200 │\n", + "│ description: business travel │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n", + "\n" + ], + "text/plain": [ + "╭─ 🔧 Tool Execution Request ─────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ \u001b[1;33mTool:\u001b[0m expense │\n", + "│ \u001b[1;33mDescription:\u001b[0m Submit an expense report that has a `cost` and `description` │\n", + "│ │\n", + "│ \u001b[1;33mArguments:\u001b[0m │\n", + "│ \u001b[36mcost:\u001b[0m 200 │\n", + "│ \u001b[36mdescription:\u001b[0m business travel │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "Your choice [y/n/m] (y):\n" + ], + "text/plain": [ + "\n", + "Your choice \u001b[1;35m[y/n/m]\u001b[0m \u001b[1;36m(y)\u001b[0m: " ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "markdown", - "id": "4f88d315-2c92-48b5-82b0-d8123aa64ed9", - "metadata": { - "id": "4f88d315-2c92-48b5-82b0-d8123aa64ed9" - }, - "source": [ - "### Run the Agent\n", - "\n", - "Now, submit an expense report that's below the threshold." - ] + "name": "stdin", + "output_type": "stream", + "text": [ + " n\n" + ] }, { - "cell_type": "code", - "execution_count": 12, - "id": "8061d79b-faaa-4ce4-baba-4c4cef8de637", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 34 - }, - "id": "8061d79b-faaa-4ce4-baba-4c4cef8de637", - "outputId": "6db71bc6-616b-4a55-a313-521d4903b021" - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "Agent Result: Expense report for the business lunch ($10) was submitted successfully.\n", - "\n" - ], - "text/plain": [ - "\n", - "\u001b[1;32mAgent Result:\u001b[0m Expense report for the business lunch \u001b[1m(\u001b[0m$\u001b[1;36m10\u001b[0m\u001b[1m)\u001b[0m was submitted successfully.\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } + "data": { + "text/html": [ + "
Feedback message (optional) (): \n"
],
- "source": [
- "result = agent.run([ChatMessage.from_user(\"Submit an expense report for business lunch that cost $10\")])\n",
- "cons.print(f\"\\n[bold green]Agent Result:[/bold green] {result['last_message'].text}\")"
+ "text/plain": [
+ "Feedback message (optional) \u001b[1;36m()\u001b[0m: "
]
+ },
+ "metadata": {},
+ "output_type": "display_data"
},
{
- "cell_type": "markdown",
- "id": "8b6c644f-74c9-4add-9da7-7f02a79a99b6",
- "metadata": {
- "id": "8b6c644f-74c9-4add-9da7-7f02a79a99b6"
- },
- "source": [
- "Next, submit a request that's above the threshold to get confirmation."
- ]
+ "name": "stdin",
+ "output_type": "stream",
+ "text": [
+ " This is too high\n"
+ ]
},
{
- "cell_type": "code",
- "execution_count": 14,
- "id": "e89ca219-2e0f-4840-be65-82ca4f782586",
- "metadata": {
- "colab": {
- "base_uri": "https://localhost:8080/",
- "height": 253
- },
- "id": "e89ca219-2e0f-4840-be65-82ca4f782586",
- "outputId": "f0d4127a-30a1-4f2e-841d-5a06ec023425"
- },
- "outputs": [
- {
- "data": {
- "text/html": [
- "╭─ 🔧 Tool Execution Request ─────────────────────────────────────────────────────────────────────────────────────╮\n", - "│ Tool: expense │\n", - "│ Description: Submit an expense report that has a `cost` and `description` │\n", - "│ │\n", - "│ Arguments: │\n", - "│ cost: 200 │\n", - "│ description: business travel │\n", - "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n", - "\n" - ], - "text/plain": [ - "╭─ 🔧 Tool Execution Request ─────────────────────────────────────────────────────────────────────────────────────╮\n", - "│ \u001b[1;33mTool:\u001b[0m expense │\n", - "│ \u001b[1;33mDescription:\u001b[0m Submit an expense report that has a `cost` and `description` │\n", - "│ │\n", - "│ \u001b[1;33mArguments:\u001b[0m │\n", - "│ \u001b[36mcost:\u001b[0m 200 │\n", - "│ \u001b[36mdescription:\u001b[0m business travel │\n", - "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "Your choice [y/n/m] (y):\n" - ], - "text/plain": [ - "\n", - "Your choice \u001b[1;35m[y/n/m]\u001b[0m \u001b[1;36m(y)\u001b[0m: " - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "n\n" - ] - }, - { - "data": { - "text/html": [ - "
Feedback message (optional) (): \n"
- ],
- "text/plain": [
- "Feedback message (optional) \u001b[1;36m()\u001b[0m: "
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "This is too high\n"
- ]
- },
- {
- "data": {
- "text/html": [
- "\n",
- "Agent Result: The expense report submission was not successful because the cost was considered too high. Please let\n",
- "me know if you'd like to adjust the amount or provide additional details.\n",
- "\n"
- ],
- "text/plain": [
- "\n",
- "\u001b[1;32mAgent Result:\u001b[0m The expense report submission was not successful because the cost was considered too high. Please let\n",
- "me know if you'd like to adjust the amount or provide additional details.\n"
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
+ "data": {
+ "text/html": [
+ "\n", + "Agent Result: The expense report was not submitted because the amount ($200) was considered too high. Please \n", + "provide the correct amount, and I can resubmit the report for you.\n", + "\n" ], - "source": [ - "result = agent.run([ChatMessage.from_user(\"Submit an expense report for business travel that cost $200\")])\n", - "cons.print(f\"\\n[bold green]Agent Result:[/bold green] {result['last_message'].text}\")" - ] - }, - { - "cell_type": "markdown", - "id": "dbc50a09-13dd-4b16-abbd-b303d0b558d2", - "metadata": { - "id": "dbc50a09-13dd-4b16-abbd-b303d0b558d2" - }, - "source": [ - "## What's next\n", - "\n", - "🎉 Congratulations! You've just built a Haystack Agent that incorporates human-in-the-loop workflows using confirmation strategies.\n", - "\n", - "Curious to keep exploring? Here are a few great next steps:\n", - "\n", - "* [Creating a Multi-Agent System with Haystack](https://haystack.deepset.ai/tutorials/45_creating_a_multi_agent_system)\n", - "* [Introduction to Multimodal Text Generation](https://haystack.deepset.ai/cookbook/multimodal_intro)\n", - "* [AI Guardrails: Content Moderation and Safety with Open Language Models](https://haystack.deepset.ai/cookbook/safety_moderation_open_lms)\n", - "\n", - "To stay up to date on the latest Haystack developments, you can [sign up for our newsletter](https://landing.deepset.ai/haystack-community-updates) or [join Haystack discord community](https://discord.gg/Dr63fr9NDS).\n", - "\n", - "(*Notebook by [Sebastian Husch Lee](https://github.com/sjrl)*)" - ] - } - ], - "metadata": { - "colab": { - "provenance": [] - }, - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.11" + "text/plain": [ + "\n", + "\u001b[1;32mAgent Result:\u001b[0m The expense report was not submitted because the amount \u001b[1m(\u001b[0m$\u001b[1;36m200\u001b[0m\u001b[1m)\u001b[0m was considered too high. Please \n", + "provide the correct amount, and I can resubmit the report for you.\n" + ] + }, + "metadata": {}, + "output_type": "display_data" } + ], + "source": [ + "result = agent.run([ChatMessage.from_user(\"Submit an expense report for business travel that cost $200\")])\n", + "cons.print(f\"\\n[bold green]Agent Result:[/bold green] {result['last_message'].text}\")" + ] + }, + { + "cell_type": "markdown", + "id": "dbc50a09-13dd-4b16-abbd-b303d0b558d2", + "metadata": { + "id": "dbc50a09-13dd-4b16-abbd-b303d0b558d2" + }, + "source": [ + "## What's next\n", + "\n", + "🎉 Congratulations! You've just built a Haystack Agent that incorporates human-in-the-loop workflows using confirmation strategies.\n", + "\n", + "Curious to keep exploring? Here are a few great next steps:\n", + "\n", + "* [Creating a Multi-Agent System with Haystack](https://haystack.deepset.ai/tutorials/45_creating_a_multi_agent_system)\n", + "* [Introduction to Multimodal Text Generation](https://haystack.deepset.ai/cookbook/multimodal_intro)\n", + "* [AI Guardrails: Content Moderation and Safety with Open Language Models](https://haystack.deepset.ai/cookbook/safety_moderation_open_lms)\n", + "\n", + "To stay up to date on the latest Haystack developments, you can [sign up for our newsletter](https://landing.deepset.ai/haystack-community-updates) or [join Haystack discord community](https://discord.gg/Dr63fr9NDS).\n", + "\n", + "(*Notebook by [Sebastian Husch Lee](https://github.com/sjrl)*)" + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" }, - "nbformat": 4, - "nbformat_minor": 5 + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 }