diff --git a/README.md b/README.md index 2ba5b98..a01fe2c 100644 --- a/README.md +++ b/README.md @@ -1,148 +1,86 @@ -# Human‑in‑the‑Loop Middleware Demo +# Human‑in‑the‑Loop Agent with LangGraph -This repository contains a minimal FastAPI application that demonstrates how to implement a **Human‑in‑the‑Loop (HITL)** mechanism using custom middleware. -The middleware allows you to pause the processing of an incoming request and resume it later, which is useful for scenarios where a human operator needs to review or approve data before the LLM generates a final response. +This repository contains a minimal example of a **Human‑in‑the‑Loop** (HITL) agent built on top of [LangGraph](https://github.com/langchain-ai/langgraph). +The agent pauses whenever it needs to call an external tool, presents the tool output to the user and waits for a decision (`approve` or `reject`). After the decision is made the conversation continues automatically. -> **TL;DR** – Send a request with `X-HITL-Interrupt: true` to pause. -> Then send another request with `X-HITL-Resume: ` to resume processing. +> **⚠️ Prerequisites** – The example uses OpenAI‑compatible APIs. +> Make sure you have an API key set in the environment variable `OPENAI_API_KEY`. --- ## 📦 Installation ```bash -# 1️⃣ Clone the repo -git clone https://github.com/yourname/hitl-middleware-demo.git -cd hitl-middleware-demo +# 1️⃣ Clone the repo (or copy solution.py into a new folder) +git clone https://github.com/your-username/hitl-langgraph.git +cd hitl-langgraph -# 2️⃣ Create a virtual environment (recommended) +# 2️⃣ Create and activate a virtual environment (optional but recommended) python -m venv .venv -source .venv/bin/activate # On Windows: .\.venv\Scripts\activate +source .venv/bin/activate # Windows: .\.venv\Scripts\activate # 3️⃣ Install dependencies -pip install -r requirements.txt +pip install --upgrade pip +pip install langchain-openai langgraph langchain-tools ``` -> **Dependencies** -> * `fastapi` – Web framework. -> * `uvicorn[standard]` – ASGI server. -> * `langchain` – LLM wrapper (OpenAI). -> * `qdrant-client` – Vector store client. -> * `rich` – Pretty console output. +> **Tip** – If you want to use a different LLM (e.g., Anthropic, Gemini), replace the `ChatOpenAI` import with the appropriate wrapper and adjust the model name. -If you don't have a Qdrant instance running locally, install it via Docker: +--- + +## 🚀 Running the Agent + +The main logic is in `solution.py`. +Run it directly: ```bash -docker run -p 6333:6333 qdrant/qdrant +python solution.py ``` +### What Happens? + +1. The agent starts a conversation. +2. When it decides to call the `get_weather` tool, the execution pauses. +3. The tool output (e.g., `"Погода в Москва на 2024-05-28: солнечно 25°C."`) is printed to the console. +4. You are prompted to type **approve** or **reject**: + - `approve`: the agent resumes with the tool result as normal input. + - `reject`: the agent receives a `ToolMessage` indicating rejection and can decide what to do next. + --- -## 🚀 Running the Application +## 🔧 Example Interaction -```bash -uvicorn solution:app --reload +```text +$ python solution.py +Agent: Какую погоду вы хотите узнать? +User: Москва на 2024-05-28 +Agent (calling tool): get_weather(city='Москва', date='2024-05-28') +Tool output: Погода в Москва на 2024-05-28: солнечно 25°C. +Please type 'approve' or 'reject': approve +Agent: Спасибо! Как ещё могу помочь? ``` -The API will be available at `http://127.0.0.1:8000`. - -### Endpoints - -| Method | Path | Description | -|--------|------|-------------| -| `POST /process` | Accepts a JSON body with a `text` field. The request is processed by the HITL middleware and forwarded to an OpenAI LLM via LangChain. | +If you type `reject`, the agent will receive a rejection message and can, for example, ask for clarification. --- -## 📄 Example Usage +## 📁 Project Structure -Below are curl examples that illustrate how to interrupt and resume a request. - -### 1️⃣ Send a request that will be **interrupted** - -```bash -curl -X POST http://127.0.0.1:8000/process \ - -H "Content-Type: application/json" \ - -H "X-HITL-Interrupt: true" \ - -d '{"text":"Explain quantum entanglement."}' -``` - -**Response (queued)** - -```json -{ - "status": "queued", - "request_id": "abcd1234" -} -``` - -> The middleware stores the request in memory and returns a `request_id` that can be used to resume later. - -### 2️⃣ Resume the queued request - -```bash -curl -X POST http://127.0.0.1:8000/process \ - -H "Content-Type: application/json" \ - -H "X-HITL-Resume: abcd1234" \ - -d '{"text":"Explain quantum entanglement."}' -``` - -**Response (processed)** - -```json -{ - "status": "completed", - "response": "Quantum entanglement is a physical phenomenon..." -} -``` - -> The LLM processes the text and returns the answer. +| File | Purpose | +|------|---------| +| `solution.py` | Full implementation of the HITL agent. | +| `README.md` | This documentation file. | --- -## 📚 How It Works +## 🛠️ Customization -1. **Middleware (`HumanInLoopMiddleware`)** - * Checks for `X-HITL-Interrupt` or `X-HITL-Resume` headers. - * If interrupted, stores the request body in a dictionary keyed by a generated UUID. - * If resumed, retrieves the stored body and forwards it to the downstream route. - -2. **Route (`/process`)** - * Receives the text payload. - * Calls `llm.invoke()` from LangChain to generate a response. - * Returns the LLM output in JSON. - -3. **Qdrant** - * The example includes an initialized Qdrant client, but it is not used in this minimal demo. - * In a real-world scenario you could store embeddings or metadata there. +- **Add more tools** – Decorate any function with `@tool` and add it to the `tools=[...]` list. +- **Change prompt** – Edit `system_prompt` in `create_react_agent`. +- **Persist memory** – Replace `MemorySaver()` with a file‑based or database checkpoint if you need persistence across runs. --- -## 📦 Project Structure +## 📜 License -``` -. -├── solution.py # Main FastAPI app with HITL middleware -├── requirements.txt # Python dependencies -└── README.md # This file -``` - ---- - -## 🔧 Customization - -- **LLM** – Replace `OpenAI(api_key="YOUR_OPENAI_API_KEY")` with another provider supported by LangChain. -- **Storage** – Swap the in‑memory dict for Redis, PostgreSQL, or any persistence layer to survive restarts. -- **Security** – Add authentication/authorization headers before allowing resume operations. - ---- - -## 🎯 Use Cases - -- **Content moderation** – Pause a request until a human moderator approves it. -- **Legal review** – Let lawyers vet LLM outputs before they are sent to clients. -- **Data privacy** – Inspect sensitive data for compliance before processing. - ---- - -Happy hacking! 🚀 \ No newline at end of file +MIT © 2026. Feel free to fork and adapt for your own projects! \ No newline at end of file