Human-in-the-Loop через middleware: README.md
This commit is contained in:
@@ -1,51 +1,53 @@
|
|||||||
# Human‑in‑the‑Loop Middleware
|
# Human‑in‑the‑Loop Middleware Demo
|
||||||
|
|
||||||
Human‑in‑the‑Loop (HITL) middleware is a lightweight Python framework that lets you build conversational agents on top of **Qdrant** vector store and **Ollama** LLMs.
|
A minimal Python project that demonstrates how to add a **Human‑in‑the‑Loop (HITL)** experience to a LangChain agent using the built‑in `HumanInTheLoopMiddleware`.
|
||||||
The project demonstrates how to:
|
When the agent is about to invoke an external tool, it pauses and asks for user approval (`approve` or `reject`) before continuing.
|
||||||
|
|
||||||
* Store and retrieve documents with Qdrant.
|
> **Why use middleware?**
|
||||||
* Embed text using the `nomic-embed-text` model from Ollama.
|
> Unlike the older `interrupt_before=['tools']` approach, the middleware automatically builds the confirmation prompt, handles the response, and resumes execution via a `Command(resume={…})`. This keeps your agent logic clean and declarative.
|
||||||
* Generate responses with the `llama3` chat model via LangChain.
|
|
||||||
* Wrap everything in a simple HTTP client that can be used by external services or UI front‑ends.
|
|
||||||
|
|
||||||
The core logic lives in two files:
|
|
||||||
|
|
||||||
| File | Purpose |
|
|
||||||
|------|---------|
|
|
||||||
| **agent.py** | Implements the LangChain pipeline: embedding → vector search → LLM generation. |
|
|
||||||
| **client.py** | Exposes a minimal FastAPI server that accepts user queries and returns responses from `agent.py`. |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📦 Prerequisites
|
## 📦 Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
├── agent.py # Agent definition + HITL middleware
|
||||||
|
├── client.py # Simple CLI that runs the agent
|
||||||
|
├── requirements.txt
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
- `agent.py` – creates a LangChain agent with a single tool (`get_weather`) and attaches the HITL middleware.
|
||||||
|
- `client.py` – launches the agent in an interactive loop.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ Prerequisites
|
||||||
|
|
||||||
| Component | Minimum Version | Notes |
|
| Component | Minimum Version | Notes |
|
||||||
|-----------|-----------------|-------|
|
|-----------|-----------------|-------|
|
||||||
| Python | 3.10+ | Tested on 3.12 |
|
| Python | 3.11+ | Tested on 3.12 |
|
||||||
| pip | – | Use the system package manager or `pipx` |
|
| Ollama | latest | Local LLM (e.g., `llama3`) |
|
||||||
| **Ollama** | Latest | Install from https://ollama.ai/ |
|
| LangChain | 0.2.x or newer | Provides agents & middleware |
|
||||||
| **Qdrant** | 1.7+ | Run locally (`docker run -p 6333:6333 qdrant/qdrant`) or use a managed instance |
|
| LangGraph | 0.1.x or newer | For checkpointing (optional) |
|
||||||
|
|
||||||
> **Important:**
|
> **Ollama** must be running locally and the model (`llama3` in this repo) should be pulled beforehand:
|
||||||
> * The Ollama image must expose the `llama3` and `nomic-embed-text` models.
|
> ```bash
|
||||||
> ```bash
|
> ollama pull llama3
|
||||||
> ollama pull llama3
|
> ```
|
||||||
> ollama pull nomic-embed-text
|
|
||||||
> ```
|
|
||||||
> * Qdrant should be reachable at `http://localhost:6333` (or set via the `QDRANT_URL` env var).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ⚙️ Installation
|
## 📦 Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone the repo
|
# Clone the repository
|
||||||
git clone https://github.com/your-org/hitl-middleware.git
|
git clone https://github.com/your-username/hitl-middleware-demo.git
|
||||||
cd hitl-middleware
|
cd hitl-middleware-demo
|
||||||
|
|
||||||
# Create a virtual environment (optional but recommended)
|
# Create a virtual environment (recommended)
|
||||||
python -m venv .venv
|
python -m venv .venv
|
||||||
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
||||||
|
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
@@ -55,115 +57,76 @@ pip install -r requirements.txt
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
langchain==0.2.*
|
langchain==0.2.*
|
||||||
langchain-ollama==0.1.*
|
langgraph==0.1.*
|
||||||
langchain-qdrant==0.1.*
|
langchain-ollama==0.2.*
|
||||||
fastapi==0.* # for client.py
|
|
||||||
uvicorn==0.* # ASGI server
|
|
||||||
python-dotenv==1.*
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 Running the Project
|
## 🚀 Running the Project
|
||||||
|
|
||||||
### 1️⃣ Start Qdrant (if not already running)
|
### 1️⃣ Start the Agent (CLI)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -d --name qdrant \
|
python client.py
|
||||||
-p 6333:6333 \
|
|
||||||
qdrant/qdrant
|
|
||||||
```
|
```
|
||||||
|
|
||||||
> Make sure the container is healthy before proceeding.
|
You will see a prompt like:
|
||||||
|
|
||||||
### 2️⃣ Run the Agent
|
|
||||||
|
|
||||||
The agent can be executed as a script or imported into other code.
|
|
||||||
It will automatically load embeddings, connect to Qdrant, and expose a `process_query` function.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python agent.py
|
|
||||||
```
|
|
||||||
|
|
||||||
> The script prints a simple “Agent ready” message and waits for input if run directly.
|
|
||||||
|
|
||||||
### 3️⃣ Run the Client (FastAPI)
|
|
||||||
|
|
||||||
The client exposes an HTTP endpoint `/query`.
|
|
||||||
It forwards incoming requests to the agent and returns the LLM response.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uvicorn client:app --host 0.0.0.0 --port 8000
|
|
||||||
```
|
|
||||||
|
|
||||||
You should see:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
INFO: Started server process [12345]
|
Agent: What would you like to know?
|
||||||
|
> Tell me the weather in Paris tomorrow.
|
||||||
...
|
...
|
||||||
INFO: Application startup complete.
|
[HITL] The agent wants to call tool `get_weather` with arguments:
|
||||||
|
{
|
||||||
|
"city": "Paris",
|
||||||
|
"date": "2024-06-01"
|
||||||
|
}
|
||||||
|
Approve? (approve/reject):
|
||||||
```
|
```
|
||||||
|
|
||||||
Now you can send requests to `http://localhost:8000/query`.
|
Type **`approve`** or **`reject`** and press Enter.
|
||||||
|
If approved, the tool runs; if rejected, the agent will re‑think.
|
||||||
|
|
||||||
---
|
### 2️⃣ Run a Single File Directly
|
||||||
|
|
||||||
## 📄 Example Usage
|
You can also run each file individually:
|
||||||
|
|
||||||
### Using the HTTP API
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST http://localhost:8000/query \
|
# Agent definition (no output)
|
||||||
-H "Content-Type: application/json" \
|
python agent.py
|
||||||
-d '{"question": "What is the capital of France?"}'
|
|
||||||
```
|
|
||||||
|
|
||||||
**Response**
|
# Client that uses the agent
|
||||||
|
python client.py
|
||||||
```json
|
|
||||||
{
|
|
||||||
"answer": "The capital of France is Paris."
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using the Agent Directly (Python)
|
|
||||||
|
|
||||||
```python
|
|
||||||
from agent import process_query
|
|
||||||
|
|
||||||
response = process_query("Explain quantum computing in simple terms.")
|
|
||||||
print(response)
|
|
||||||
# Output: "Quantum computing uses qubits..."
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔧 Configuration
|
## 🔍 Example Interaction
|
||||||
|
|
||||||
All configuration values can be overridden via environment variables or a `.env` file placed at the project root.
|
|
||||||
|
|
||||||
| Variable | Default | Description |
|
|
||||||
|----------|---------|-------------|
|
|
||||||
| `QDRANT_URL` | `http://localhost:6333` | Qdrant endpoint |
|
|
||||||
| `OLLAMA_HOST` | `http://localhost:11434` | Ollama API host |
|
|
||||||
| `LLM_MODEL` | `llama3` | Chat model name |
|
|
||||||
| `EMBEDDING_MODEL` | `nomic-embed-text` | Embedding model name |
|
|
||||||
|
|
||||||
Example `.env`:
|
|
||||||
|
|
||||||
```dotenv
|
|
||||||
QDRANT_URL=http://qdrant:6333
|
|
||||||
OLLAMA_HOST=http://ollama:11434
|
|
||||||
LLM_MODEL=llama3
|
|
||||||
EMBEDDING_MODEL=nomic-embed-text
|
|
||||||
```
|
```
|
||||||
|
Agent: Tell me the weather in Tokyo tomorrow.
|
||||||
|
[HITL] The agent wants to call tool `get_weather` with arguments:
|
||||||
|
{
|
||||||
|
"city": "Tokyo",
|
||||||
|
"date": "2024-06-01"
|
||||||
|
}
|
||||||
|
Approve? (approve/reject): approve
|
||||||
|
Tool output: "Tomorrow in Tokyo, expect a high of 28°C and light showers."
|
||||||
|
Agent: The weather in Tokyo tomorrow will be around 28°C with light showers.
|
||||||
|
```
|
||||||
|
|
||||||
|
If you type `reject`, the agent will ask for clarification or try a different approach.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📚 Further Reading
|
## 📚 Further Reading
|
||||||
|
|
||||||
* [LangChain Docs](https://langchain.com/)
|
- [Human‑in‑the‑Loop — LangChain](https://docs.langchain.com/docs/middleware/human_in_the_loop)
|
||||||
* [Ollama Quickstart](https://github.com/ollama/ollama)
|
- [LangGraph Checkpoints](https://langgraph.org/docs/checkpointing)
|
||||||
* [Qdrant Documentation](https://qdrant.tech/documentation/)
|
|
||||||
|
|
||||||
Feel free to extend the middleware with custom prompts, retrieval strategies, or additional LLMs. Happy hacking!
|
Feel free to extend the toolset or replace `ChatOllama` with an OpenAI model by swapping imports.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Happy hacking! 🚀
|
||||||
Reference in New Issue
Block a user