feat: solution for 'Human-in-the-Loop через middleware'
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
node_modules/
|
||||
.env
|
||||
dist/
|
||||
build/
|
||||
*.log
|
||||
@@ -0,0 +1,34 @@
|
||||
# Human-in-the-Loop через middleware
|
||||
|
||||
Главная
|
||||
Мои задания
|
||||
Human-in-the-Loop через middleware
|
||||
5Д
|
||||
EN
|
||||
Human-in-the-Loop через middleware
|
||||
Зачёт
|
||||
Версия 1
|
||||
Дедлайн сдачи: 31.08.2026
|
||||
|
||||
В работе
|
||||
|
||||
Редактирование ответа
|
||||
|
||||
Заполните ответ и отправьте работу на проверку преподавателю.
|
||||
|
||||
Тип ответа
|
||||
Текст
|
||||
Ссылка
|
||||
Файлы
|
||||
Текст ответа
|
||||
Прикреплённые файлы
|
||||
Загрузить файл
|
||||
Отправить на проверку
|
||||
Отменить
|
||||
|
||||
Задание
|
||||
|
||||
Задание: Human-in-the-Loop через middleware
|
||||
Цель
|
||||
|
||||
Доработать агента с HumanInTheLoopMiddleware: при каждом вызове инструмента агент останавливается
|
||||
@@ -0,0 +1,3 @@
|
||||
langchain==0.2.0
|
||||
langgraph==0.0.1
|
||||
openai==1.3.0
|
||||
+164
@@ -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)
|
||||
Reference in New Issue
Block a user