Files

110 lines
4.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# HumanintheLoop (HITL) middleware example
# interrupt decision types: approve, edit, reject, respond
# respond decision can be used to supply a human reply as tool result
import os
from typing import List, Dict
from langchain_openai import ChatOpenAI
from langchain.agents import create_agent
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langchain.tools import BaseTool
from langgraph.checkpoint.memory import MemorySaver
from langgraph.types import Command
# --- Tool ---------------------------------------------------------------
class GetWeatherTool(BaseTool):
name: str = "get_weather"
description: str = "Get the weather for a city on a given date."
def _run(self, city: str, date: str) -> str:
return f"The weather in {city} on {date} is sunny with a high of 25°C."
# --- LLM ---------------------------------------------------------------
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
raise RuntimeError("OPENAI_API_KEY environment variable is required.")
llm = ChatOpenAI(api_key=api_key, temperature=0.7)
# --- Agent --------------------------------------------------------------
memory = MemorySaver()
agent = create_agent(
model=llm,
tools=[GetWeatherTool()],
system_prompt="You are a helpful assistant.",
middleware=[
HumanInTheLoopMiddleware(
interrupt_on={
"get_weather": True, # allow approve, edit, reject
},
description_prefix="Подтвердите вызов инструмента",
),
],
checkpointer=memory,
)
# --- Interactive loop -----------------------------------------------
if __name__ == "__main__":
config = {"configurable": {"thread_id": "session-1"}}
while True:
try:
try:
user_input = input("\nUser: ")
except EOFError:
print("\nNo input provided. Exiting.")
break
if user_input.lower() in {"exit", "quit", "q"}:
print("Goodbye!")
break
# First invocation
result = agent.invoke({"messages": [{"role": "human", "content": user_input}]}, config=config)
# Process interrupts until finished
while "__interrupt__" in result:
interrupt = result["__interrupt__"][0].value
action_requests = interrupt.get("action_requests", [])
review_configs = interrupt.get("review_configs", [])
decisions: List[Dict] = []
print("\n--- Подтверждение ---")
for idx, action in enumerate(action_requests):
name = action.get("name")
args = action.get("args", {})
description = action.get("description", "")
print(f"Инструмент: {name}")
print(f"Аргументы: {args}")
if description:
print(f"Описание: {description}")
# Determine allowed decisions
allowed = ["approve", "reject"]
# Check if edit is allowed
for cfg in review_configs:
if cfg.get("name") == name:
allowed = cfg.get("allowed_decisions", allowed)
break
# Prompt user
while True:
choice = input("a = approve, r = reject: ").strip().lower()
if choice == "a" and "approve" in allowed:
decisions.append({"type": "approve"})
break
if choice == "r" and "reject" in allowed:
msg = input("Сообщение для агента (причина отказа): ")
decisions.append({"type": "reject", "message": msg})
break
print("Неверный ввод. Попробуйте снова.")
# Resume
result = agent.invoke(Command(resume={"decisions": decisions}), config=config) # Command(resume={"decisions":[…]})
# Finished
final_message = result.get("messages", [])[-1].get("content", "")
print(f"\nAssistant: {final_message}\n")
except KeyboardInterrupt:
print("\nInterrupted. Exiting.")
break