139 lines
3.8 KiB
Markdown
139 lines
3.8 KiB
Markdown
# Human‑in‑the‑Loop Agent via Middleware
|
||
|
||
This repository contains a minimal example of an LLM agent that pauses whenever it wants to call a tool and asks the user for approval before proceeding.
|
||
The core idea is to use **`HumanInTheLoopMiddleware`** from LangChain, which intercepts every tool invocation, prints a prompt with the action details, and waits for the user to respond (`approve`, `reject`, or optionally edit the request).
|
||
|
||
> **Why this matters** – In many real‑world scenarios you want an LLM to ask for human confirmation before performing potentially sensitive actions (e.g., sending emails, accessing databases, calling external APIs).
|
||
|
||
---
|
||
|
||
## Table of Contents
|
||
|
||
- [Project Structure](#project-structure)
|
||
- [Installation](#installation)
|
||
- [Running the Agent](#running-the-agent)
|
||
- [Interactive Mode](#interactive-mode)
|
||
- [Scripted Example](#scripted-example)
|
||
- [Example Usage](#example-usage)
|
||
- [Extending the Agent](#extending-the-agent)
|
||
|
||
---
|
||
|
||
## Project Structure
|
||
|
||
```
|
||
├── solution.py # Main script with the agent implementation
|
||
└── README.md # This file
|
||
```
|
||
|
||
`solution.py` contains:
|
||
|
||
1. **LLM configuration** – uses `ChatOpenAI`.
|
||
2. **A simple tool** (`get_weather`) that returns a fake weather string.
|
||
3. **Memory checkpoint** via `MemorySaver`.
|
||
4. **Agent creation** with `create_react_agent` and the middleware.
|
||
5. **Execution loop** that keeps asking for user input until the conversation ends.
|
||
|
||
---
|
||
|
||
## Installation
|
||
|
||
1. **Clone the repo**
|
||
|
||
```bash
|
||
git clone https://github.com/your-username/human-in-the-loop-agent.git
|
||
cd human-in-the-loop-agent
|
||
```
|
||
|
||
2. **Create a virtual environment (optional but recommended)**
|
||
|
||
```bash
|
||
python -m venv .venv
|
||
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
||
```
|
||
|
||
3. **Install dependencies**
|
||
|
||
```bash
|
||
pip install --upgrade pip
|
||
pip install langchain langgraph openai
|
||
```
|
||
|
||
4. **Set your OpenAI API key**
|
||
|
||
```bash
|
||
export OPENAI_API_KEY="sk-..."
|
||
# Windows: setx OPENAI_API_KEY "sk-..."
|
||
```
|
||
|
||
---
|
||
|
||
## Running the Agent
|
||
|
||
### Interactive Mode
|
||
|
||
Simply run the script:
|
||
|
||
```bash
|
||
python solution.py
|
||
```
|
||
|
||
You will see a prompt like:
|
||
|
||
```
|
||
Agent wants to call tool `get_weather` with arguments:
|
||
city = "Moscow"
|
||
date = "2025-10-01"
|
||
|
||
Please type one of: approve / reject (or edit <new_args>)
|
||
>
|
||
```
|
||
|
||
Type **`approve`** to let the agent proceed, or **`reject`** to stop it.
|
||
If you want to modify the arguments before approval, use `edit city=London date=2025-12-25`.
|
||
|
||
The conversation continues until the user types `stop` or the agent finishes its plan.
|
||
|
||
### Scripted Example
|
||
|
||
You can also run a quick demo that automatically approves all calls:
|
||
|
||
```bash
|
||
python - <<'PY'
|
||
from solution import agent, llm, memory
|
||
# Override middleware to auto‑approve for demonstration
|
||
agent.middleware[0].interrupt_on = {"get_weather": False}
|
||
print(agent.run("What's the weather in New York tomorrow?"))
|
||
PY
|
||
```
|
||
|
||
---
|
||
|
||
## Example Usage
|
||
|
||
```bash
|
||
$ python solution.py
|
||
User: What's the weather in Paris next Friday?
|
||
Agent wants to call tool `get_weather` with arguments:
|
||
city = "Paris"
|
||
date = "2025-10-06"
|
||
|
||
Please type one of: approve / reject (or edit <new_args>)
|
||
> approve
|
||
|
||
Assistant: Погода в Париже на 2025‑10‑06: солнечно 25°C.
|
||
User: Thank you!
|
||
```
|
||
|
||
---
|
||
|
||
## Extending the Agent
|
||
|
||
1. **Add more tools** – decorate any function with `@tool` and add it to the `tools` list in `create_react_agent`.
|
||
2. **Change the interrupt policy** – modify `interrupt_on` dict (e.g., `{ "get_weather": True, "send_email": False }`).
|
||
3. **Persist conversation state** – replace `MemorySaver()` with a database checkpoint if you need long‑term memory.
|
||
4. **Custom prompts** – tweak `system_prompt` or add a custom `description_prefix`.
|
||
|
||
---
|
||
|
||
Happy hacking! 🚀 |