feat: solution for 'Human-in-the-Loop через middleware'

This commit is contained in:
2026-06-24 15:06:13 +03:00
commit 082d5fb669
4 changed files with 206 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
node_modules/
.env
dist/
build/
*.log
+34
View File
@@ -0,0 +1,34 @@
# Human-in-the-Loop через middleware
Главная
Мои задания
Human-in-the-Loop через middleware
EN
Human-in-the-Loop через middleware
Зачёт
Версия 1
Дедлайн сдачи: 31.08.2026
В работе
Редактирование ответа
Заполните ответ и отправьте работу на проверку преподавателю.
Тип ответа
Текст
Ссылка
Файлы
Текст ответа
Прикреплённые файлы
Загрузить файл
Отправить на проверку
Отменить
Задание
Задание: Human-in-the-Loop через middleware
Цель
Доработать агента с HumanInTheLoopMiddleware: при каждом вызове инструмента агент останавливается
+3
View File
@@ -0,0 +1,3 @@
langchain==0.2.0
langgraph==0.0.1
openai==1.3.0
+164
View File
@@ -0,0 +1,164 @@
#!/usr/bin/env python3
"""
Human-in-the-Loop Agent Demo
This script demonstrates how to use LangChain's HumanInTheLoopMiddleware
to pause an agent when it wants to call a tool, let the user approve or
reject the call, and then resume execution.
Requirements:
- langchain
- langgraph
- openai (for ChatOpenAI)
"""
import os
import sys
from typing import List, Dict, Any
# LangChain imports
from langchain.chat_models import ChatOpenAI
from langchain.tools import tool
from langchain.agents import create_agent
from langchain.agents.middleware import HumanInTheLoopMiddleware
# LangGraph imports
from langgraph.checkpoint.memory import MemorySaver
from langgraph.schema import Command
# --------------------------------------------------------------------------- #
# Tool definition
# --------------------------------------------------------------------------- #
@tool
def get_weather(city: str) -> str:
"""
Return a simple weather description for the given city.
"""
# In a real scenario you might call an external API here.
return f"Sunny in {city}."
# --------------------------------------------------------------------------- #
# Agent setup
# --------------------------------------------------------------------------- #
def build_agent() -> Any:
"""
Build and return a LangChain agent configured with HumanInTheLoopMiddleware.
"""
# Ensure OpenAI API key is available
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
print("Error: OPENAI_API_KEY environment variable not set.")
sys.exit(1)
llm = ChatOpenAI(
model_name="gpt-3.5-turbo",
temperature=0,
openai_api_key=api_key,
)
agent = create_agent(
model=llm,
tools=[get_weather],
system_prompt="Ты полезный ассистент",
middleware=[
HumanInTheLoopMiddleware(
interrupt_on={"get_weather": True},
description_prefix="Подтвердите вызов инструмента",
),
],
checkpointer=MemorySaver(),
)
return agent
# --------------------------------------------------------------------------- #
# Human-in-the-loop loop
# --------------------------------------------------------------------------- #
def prompt_decision(request: Dict[str, Any]) -> str:
"""
Prompt the user for a decision on a tool call.
Returns 'approve' or 'reject'.
"""
name = request.get("name", "unknown")
args = request.get("args", {})
description = request.get("description", "")
print("\n=== Tool Call ===")
print(f"Name: {name}")
print(f"Args: {args}")
if description:
print(f"Description: {description}")
while True:
choice = input("Approve (a) / Reject (r) [a/r]: ").strip().lower()
if choice == "a":
return "approve"
elif choice == "r":
return "reject"
else:
print("Invalid input. Please enter 'a' to approve or 'r' to reject.")
def run_agent(agent: Any, user_message: str, thread_id: str = "session-1") -> None:
"""
Run the agent with Human-in-the-Loop, handling pauses and resumes.
"""
config = {"configurable": {"thread_id": thread_id}}
# Initial invocation
result = agent.invoke(
{"messages": [{"role": "human", "content": user_message}]},
config=config,
)
# Loop until the agent finishes (no '__interrupt__' key)
while "__interrupt__" in result:
interrupt = result["__interrupt__"][0]
# The interrupt value is a dict with action_requests and review_configs
interrupt_value = interrupt.get("value", {})
action_requests = interrupt_value.get("action_requests", [])
# review_configs = interrupt_value.get("review_configs", [])
decisions = []
for req in action_requests:
decision = prompt_decision(req)
decisions.append(
{
"name": req["name"],
"args": req["args"],
"decision": decision,
}
)
# Resume the agent with the collected decisions
result = agent.invoke(
Command(resume={"decisions": decisions}),
config=config,
)
# Agent finished; display the final response
messages = result.get("messages", [])
if messages:
# Find the last assistant message
for msg in reversed(messages):
if msg.get("role") == "assistant":
print("\n=== Agent Response ===")
print(msg.get("content", "").strip())
break
else:
print("\nNo messages returned by the agent.")
# --------------------------------------------------------------------------- #
# Main entry point
# --------------------------------------------------------------------------- #
if __name__ == "__main__":
agent = build_agent()
print("Human-in-the-Loop Agent Demo")
print("----------------------------")
user_input = input("Введите ваш запрос: ").strip()
if not user_input:
print("Empty input. Exiting.")
sys.exit(0)
run_agent(agent, user_input)