160 lines
4.3 KiB
Markdown
160 lines
4.3 KiB
Markdown
# Human‑in‑the‑Loop Middleware Demo
|
||
|
||
This repository contains a minimal LangChain/LangGraph example that demonstrates how to pause an agent whenever it wants to call a tool and ask the user for approval (`approve` / `reject`).
|
||
The pausing logic is handled by **`HumanInTheLoopMiddleware`**, which automatically generates a prompt, receives the user’s decision, and resumes execution via a `Command(resume={…})`.
|
||
|
||
> **Why this matters** – In many real‑world scenarios an LLM should not act autonomously. By inserting a human checkpoint you can keep control over every tool call, ensuring safety, compliance or simply giving the user a chance to correct mistakes.
|
||
|
||
---
|
||
|
||
## 📦 Installation
|
||
|
||
```bash
|
||
# 1️⃣ Clone the repo
|
||
git clone https://github.com/your‑org/human‑in‑the‑loop-demo.git
|
||
cd human‑in‑the‑loop-demo
|
||
|
||
# 2️⃣ Create a virtual environment (recommended)
|
||
python -m venv .venv
|
||
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
||
|
||
# 3️⃣ Install dependencies
|
||
pip install -r requirements.txt
|
||
```
|
||
|
||
> **Requirements**
|
||
> * Python ≥ 3.10
|
||
> * `langchain` (>=0.2)
|
||
> * `langgraph` (>=0.1)
|
||
> * `openai` or any LLM provider you prefer
|
||
|
||
The `requirements.txt` file contains the exact versions used for this demo.
|
||
|
||
---
|
||
|
||
## 📁 Project Structure
|
||
|
||
```
|
||
.
|
||
├── agent.py # Agent definition with HumanInTheLoopMiddleware
|
||
├── main.py # CLI entry point – runs the agent interactively
|
||
├── tools.py # Example tool(s) (e.g., get_weather)
|
||
└── README.md # This file
|
||
```
|
||
|
||
---
|
||
|
||
## 🚀 Running the Demo
|
||
|
||
### 1️⃣ Start the Agent
|
||
|
||
```bash
|
||
python main.py
|
||
```
|
||
|
||
You will see a prompt like:
|
||
|
||
```
|
||
Assistant: What would you like to do?
|
||
User: Tell me the weather in London.
|
||
Assistant: (Thinking...)
|
||
```
|
||
|
||
When the agent decides to call `get_weather`, it pauses and prints:
|
||
|
||
```
|
||
Human-in-the-loop checkpoint:
|
||
The assistant wants to execute tool 'get_weather' with arguments {'location': 'London'}.
|
||
Please type one of the following decisions: approve / reject
|
||
```
|
||
|
||
Type **`approve`** to let the tool run, or **`reject`** to cancel it.
|
||
After your decision, the agent continues:
|
||
|
||
```
|
||
Assistant: The weather in London is 18°C and sunny.
|
||
```
|
||
|
||
### 2️⃣ Using a Different Tool
|
||
|
||
If you want to test another tool (e.g., `get_time`), add it to `tools.py`, import it in `agent.py`, and adjust the middleware configuration accordingly.
|
||
|
||
---
|
||
|
||
## 📚 Example Usage
|
||
|
||
```python
|
||
# main.py
|
||
from agent import agent
|
||
|
||
def run():
|
||
print("Welcome! Type 'exit' to quit.")
|
||
while True:
|
||
user_input = input("\nUser: ")
|
||
if user_input.lower() == "exit":
|
||
break
|
||
response = agent.invoke({"input": user_input})
|
||
print(f"Assistant: {response['output']}")
|
||
|
||
if __name__ == "__main__":
|
||
run()
|
||
```
|
||
|
||
```python
|
||
# agent.py
|
||
from langchain.agents import create_agent
|
||
from langchain.agents.middleware import HumanInTheLoopMiddleware
|
||
from langgraph.checkpoint.memory import MemorySaver
|
||
|
||
from tools import get_weather # <-- your tool(s)
|
||
|
||
memory = MemorySaver()
|
||
|
||
agent = create_agent(
|
||
model="gpt-4o-mini", # or any LLM you have access to
|
||
tools=[get_weather],
|
||
system_prompt="You are a helpful assistant.",
|
||
middleware=[
|
||
HumanInTheLoopMiddleware(
|
||
interrupt_on={"get_weather": True},
|
||
description_prefix="Please confirm the tool call:",
|
||
),
|
||
],
|
||
checkpointer=memory,
|
||
)
|
||
```
|
||
|
||
```python
|
||
# tools.py
|
||
from langchain.tools import BaseTool
|
||
|
||
class GetWeather(BaseTool):
|
||
name = "get_weather"
|
||
description = "Returns current weather for a location."
|
||
args_schema = ... # define your schema here
|
||
|
||
def _run(self, location: str) -> str:
|
||
# Dummy implementation – replace with real API call
|
||
return f"The weather in {location} is 18°C and sunny."
|
||
|
||
get_weather = GetWeather()
|
||
```
|
||
|
||
---
|
||
|
||
## 🔧 Customization Tips
|
||
|
||
| Feature | How to change |
|
||
|---------|---------------|
|
||
| **Allowed decisions** | `interrupt_on={"get_weather": {"allowed_decisions": ["approve", "reject"]}}` |
|
||
| **Prompt prefix** | Change `description_prefix` in the middleware. |
|
||
| **Tool list** | Add or remove tools in the `tools=[...]` array. |
|
||
| **LLM model** | Pass a different model name or a custom LLM instance to `create_agent`. |
|
||
|
||
---
|
||
|
||
## 📜 License
|
||
|
||
This project is licensed under the MIT License – see the [LICENSE](LICENSE) file for details.
|
||
|
||
--- |