Human-in-the-Loop через middleware: README.md
This commit is contained in:
@@ -1,123 +1,120 @@
|
|||||||
# Human‑in‑the‑Loop через Middleware
|
# Human‑in‑the‑Loop Agent via Middleware
|
||||||
|
|
||||||
## Описание проекта
|
This repository contains a minimal example of an **LLM agent** that pauses every time it wants to call an external tool and asks the user for confirmation (`approve` or `reject`).
|
||||||
В этом проекте реализован агент на базе LangChain, который использует **HumanInTheLoopMiddleware** для ручного подтверждения вызовов инструментов. Каждый раз, когда агент планирует использовать инструмент (например, `get_weather`), он останавливается и выводит в терминал сообщение с просьбой подтвердить действие. Пользователь вводит одно из решений (`approve`, `reject` или `edit`) и только после этого выполнение возобновляется.
|
The pause is implemented with LangChain’s built‑in `HumanInTheLoopMiddleware`, which automatically generates the prompt, captures the user input, and resumes execution via a `Command`.
|
||||||
|
|
||||||
> **Преимущество**: Middleware сам формирует запрос на подтверждение, обрабатывает ответ и управляет переходом к следующему шагу через `Command(resume={…})`. Это упрощает интеграцию Human‑in‑the‑Loop по сравнению с ручным использованием `interrupt_before=['tools']`.
|
> **Why use middleware?**
|
||||||
|
> Unlike the `interrupt_before=['tools']` approach that requires manual handling of interruptions, the middleware handles everything internally: it creates the confirmation request, processes the response, and resumes the agent with the chosen decision.
|
||||||
## Структура проекта
|
|
||||||
```
|
|
||||||
├── solution.py # Основной скрипт, реализующий агента
|
|
||||||
└── README.md # Текущий файл
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Установка зависимостей
|
## 📦 Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Создайте виртуальное окружение (рекомендуется)
|
# Create a virtual environment (optional but recommended)
|
||||||
python -m venv .venv
|
python -m venv .venv
|
||||||
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
||||||
|
|
||||||
# Установите необходимые библиотеки
|
# Install required packages
|
||||||
pip install langchain langgraph langchain-community openai
|
pip install langchain langgraph openai rich
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Важно**
|
> **OpenAI API key**
|
||||||
> * Если у вас нет ключа OpenAI, замените `OpenAI` на любой другой LLM (например, `DummyLLM`, который возвращает фиксированный ответ).
|
> The example uses an OpenAI model. Set your key in the environment:
|
||||||
> * Для работы с `HumanInTheLoopMiddleware` требуется версия LangChain ≥ 0.2 и LangGraph ≥ 0.1.
|
|
||||||
|
```bash
|
||||||
|
export OPENAI_API_KEY="sk-…"
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Запуск проекта
|
## 📁 Project Structure
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `solution.py` | Main script that creates the agent, defines a simple tool (`get_weather`), and runs an interactive loop. |
|
||||||
|
|
||||||
|
> The repository contains only one Python file for clarity.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Running the Example
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python solution.py
|
python solution.py
|
||||||
```
|
```
|
||||||
|
|
||||||
### Что происходит при запуске?
|
You will see something like:
|
||||||
|
|
||||||
1. Инициализируется LLM (по умолчанию OpenAI).
|
|
||||||
2. Создаётся инструмент `get_weather`.
|
|
||||||
3. Агент создаётся с Middleware, который перехватывает вызовы инструмента и запрашивает подтверждение у пользователя.
|
|
||||||
4. В терминале выводятся сообщения вида:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
[HumanInTheLoop] Подтвердите вызов инструмента:
|
User: What's the weather in London?
|
||||||
- get_weather
|
Agent: (pauses) Подтвердите вызов инструмента get_weather
|
||||||
Введите решение (approve/reject/edit):
|
- approve
|
||||||
|
- reject
|
||||||
|
Your choice:
|
||||||
```
|
```
|
||||||
|
|
||||||
5. После ввода решения агент продолжает работу.
|
Type `approve` to let the agent call the tool, or `reject` to skip it.
|
||||||
|
After your decision, the agent continues its reasoning and eventually returns a final answer.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Пример использования
|
## 🛠️ Customizing
|
||||||
|
|
||||||
```bash
|
### Changing the Tool
|
||||||
$ python solution.py
|
|
||||||
> Какую погоду в Москве сегодня?
|
|
||||||
|
|
||||||
[HumanInTheLoop] Подтвердите вызов инструмента:
|
Replace the `get_weather` function with any other LangChain tool:
|
||||||
- get_weather
|
|
||||||
Введите решение (approve/reject/edit): approve
|
|
||||||
|
|
||||||
Ответ агента: Сегодня в Москве солнечно, температура 22°C.
|
|
||||||
```
|
|
||||||
|
|
||||||
Если пользователь введёт `reject`, агент отменит вызов и попытается найти другой способ ответа. В случае `edit` можно изменить параметры запроса к инструменту.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Как добавить собственный инструмент
|
|
||||||
|
|
||||||
1. Определите функцию‑инструмент:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def get_time(location: str) -> str:
|
@tool
|
||||||
# Возвращает текущее время в указанном месте
|
def get_time() -> str:
|
||||||
|
"""Return current UTC time."""
|
||||||
|
return datetime.utcnow().isoformat()
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Добавьте её в список `tools` при создании агента и настройте Middleware:
|
Add it to the `tools` list when creating the agent.
|
||||||
|
|
||||||
|
### Adjusting Middleware Settings
|
||||||
|
|
||||||
|
- **Interrupt on specific decisions**
|
||||||
|
```python
|
||||||
|
interrupt_on={
|
||||||
|
"get_weather": {"allowed_decisions": ["approve", "reject"]} # no edit option
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Custom prompt prefix**
|
||||||
|
```python
|
||||||
|
description_prefix="Please confirm the tool call:"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using a Different LLM
|
||||||
|
|
||||||
|
Swap `llm` for any LangChain-compatible model (e.g., GPT‑4o, Claude, etc.):
|
||||||
|
|
||||||
```python
|
```python
|
||||||
agent = create_agent(
|
from langchain_openai import ChatOpenAI
|
||||||
model=llm,
|
llm = ChatOpenAI(model_name="gpt-4o-mini")
|
||||||
tools=[get_weather, get_time],
|
|
||||||
middleware=[
|
|
||||||
HumanInTheLoopMiddleware(
|
|
||||||
interrupt_on={"get_time": True},
|
|
||||||
description_prefix="Подтвердите вызов инструмента",
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Тестирование
|
## 📖 Example Interaction
|
||||||
|
|
||||||
Для быстрой проверки можно использовать `DummyLLM`, который всегда возвращает один и тот же ответ. Это удобно, если у вас нет доступа к OpenAI.
|
```
|
||||||
|
User: Tell me the weather in Paris.
|
||||||
```python
|
Agent: (pauses) Подтвердите вызов инструмента get_weather
|
||||||
class DummyLLM:
|
- approve
|
||||||
def __call__(self, *args, **kwargs):
|
- reject
|
||||||
return "dummy response"
|
Your choice: approve
|
||||||
|
Agent: The current temperature in Paris is 18°C with clear skies.
|
||||||
|
Final answer: It’s sunny and mild in Paris today.
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## FAQ
|
## 📜 License
|
||||||
|
|
||||||
| Вопрос | Ответ |
|
This project is provided under the MIT license. Feel free to adapt it for your own experiments.
|
||||||
|--------|-------|
|
|
||||||
| Как отключить Human‑in‑the‑Loop? | Удалите Middleware из списка `middleware` при создании агента. |
|
|
||||||
| Можно ли задать только `approve` и `reject`, без `edit`? | Да, передайте в `interrupt_on`: `{"get_weather": {"allowed_decisions": ["approve", "reject"]}}`. |
|
|
||||||
| Что делать, если агент не сохраняет состояние после паузы? | Убедитесь, что вы передали `checkpointer=MemorySaver()` при создании агента. |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Лицензия
|
|
||||||
|
|
||||||
MIT © 2026
|
|
||||||
Reference in New Issue
Block a user