Human-in-the-Loop через middleware: README.md

This commit is contained in:
2026-05-28 05:44:17 +00:00
parent 51db484cf0
commit a4190d7713
@@ -1,139 +1,140 @@
# HumanintheLoop Agent via Middleware
# HumanintheLoop Agent via Middleware (LangGraph)
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).
This repository contains a minimal example of a **HumanintheLoop** agent built on top of LangGraph.
The agent pauses whenever it needs to call an external tool, prints the tool request and waits for a user decision (`approve` or `reject`). After the decision is supplied, execution resumes automatically.
> **Why this matters** In many realworld scenarios you want an LLM to ask for human confirmation before performing potentially sensitive actions (e.g., sending emails, accessing databases, calling external APIs).
> ⚠️ The example uses OpenAIs API (or any compatible LLM). Make sure you have an API key set in the environment variable `OPENAI_API_KEY`.
---
## Table of Contents
- [Project Structure](#project-structure)
- [Features](#features)
- [Prerequisites](#prerequisites)
- [Installation](#installation)
- [Running the Agent](#running-the-agent)
- [Interactive Mode](#interactive-mode)
- [Scripted Example](#scripted-example)
- [Interactive Demo (`solution.py`)](#interactive-demo-solutionpy)
- [Unit Tests (`tests/test_solution.py`)](#unit-tests-test_solutionpy)
- [Example Usage](#example-usage)
- [Extending the Agent](#extending-the-agent)
- [License](#license)
---
## Project Structure
## Features
| Feature | Description |
|---------|-------------|
| **HumanintheLoop** | Agent stops before calling any tool, prints the request and waits for user input. |
| **Interrupts via `interrupt_before=["tools"]`** | Configurable interruption point in LangGraph. |
| **Tool Example** | Simple `get_weather(city, date)` function that returns a mock weather string. |
| **Checkpointing** | Uses an inmemory checkpoint (`MemorySaver`) to preserve state across interruptions. |
---
## Prerequisites
- Python 3.10+
- An OpenAI API key (or any compatible LLM endpoint)
```bash
export OPENAI_API_KEY="sk-..."
```
├── 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**
1. **Clone the repository**
```bash
git clone https://github.com/your-username/human-in-the-loop-agent.git
cd human-in-the-loop-agent
git clone https://github.com/yourusername/human-in-loop-langgraph.git
cd human-in-loop-langgraph
```
2. **Create a virtual environment (optional but recommended)**
```bash
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
source .venv/bin/activate # On Windows: .venv\Scripts\activate
```
3. **Install dependencies**
```bash
pip install --upgrade pip
pip install langchain langgraph openai
pip install -r requirements.txt
```
4. **Set your OpenAI API key**
*If you dont have a `requirements.txt`, create one with the following content:*
```bash
export OPENAI_API_KEY="sk-..."
# Windows: setx OPENAI_API_KEY "sk-..."
```text
langchain-openai>=0.2.0
langgraph>=0.1.0
```
---
## Running the Agent
### Interactive Mode
### Interactive Demo (`solution.py`)
Simply run the script:
The main script demonstrates how to start the agent and handle interruptions.
```bash
python solution.py
```
You will see a prompt like:
**What happens:**
```
Agent wants to call tool `get_weather` with arguments:
city = "Moscow"
date = "2025-10-01"
1. The user enters a prompt (e.g., “Whats the weather in Paris on 20241201?”).
2. The agent processes the request, decides it needs to call `get_weather`, and pauses.
3. The tool request is printed:
Please type one of: approve / reject (or edit <new_args>)
>
```
```
Tool requested: get_weather
Arguments: {'city': 'Paris', 'date': '2024-12-01'}
```
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`.
4. You are prompted to type `approve` or `reject`.
5. After your decision, the agent resumes and prints the final answer.
The conversation continues until the user types `stop` or the agent finishes its plan.
---
### Scripted Example
### Unit Tests (`tests/test_solution.py`)
You can also run a quick demo that automatically approves all calls:
Run the test suite to verify that the interruption logic works as expected:
```bash
python - <<'PY'
from solution import agent, llm, memory
# Override middleware to autoapprove for demonstration
agent.middleware[0].interrupt_on = {"get_weather": False}
print(agent.run("What's the weather in New York tomorrow?"))
PY
pytest tests/test_solution.py
```
The tests simulate a user approving the tool call automatically and check that the final output contains the weather string.
---
## 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"
Below is a quick snippet you can paste into a Python REPL or another script to see the agent in action:
Please type one of: approve / reject (or edit <new_args>)
> approve
```python
from solution import agent, memory # assuming solution.py defines them
Assistant: Погода в Париже на 2025‑10‑06: солнечно 25°C.
User: Thank you!
# Start a new thread/session
config = {"configurable": {"thread_id": "demo-session"}}
# Invoke with a user message
response = agent.invoke(
{"messages": [{"role": "user", "content": "What's the weather in Tokyo on 2025-01-15?"}]},
config=config,
)
print("\nFinal response:")
print(response["messages"][-1]["content"])
```
---
## 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 longterm memory.
4. **Custom prompts** tweak `system_prompt` or add a custom `description_prefix`.
When you run this, youll see the same interruption prompt as described above.
---
Happy hacking! 🚀
## License
MIT © 2026. Feel free to fork and adapt for your own projects.