Solution ready: update agent.py
This commit is contained in:
@@ -1,45 +1,117 @@
|
||||
"""
|
||||
Simple LangChain agent demonstrating Human‑in‑the‑Loop middleware.
|
||||
Human‑in‑the‑Loop agent with tool calling.
|
||||
|
||||
The chain:
|
||||
1. User prompt is passed to a PromptTemplate.
|
||||
2. The template is processed by an LLM (OpenAI).
|
||||
3. The output passes through the HIL middleware which prints the assistant’s answer and asks the user to confirm or modify it before returning.
|
||||
This example demonstrates how to use LangChain's `HumanInTheLoopMiddleware` to pause the agent when a tool is about to be called, ask the user for approval, and then resume execution.
|
||||
|
||||
Run with:
|
||||
The agent uses a simple `get_weather` tool that returns a hard‑coded weather string. In a real project you would replace it with an API call.
|
||||
|
||||
Run:
|
||||
python agent.py
|
||||
Make sure you have OPENAI_API_KEY set in your environment.
|
||||
Make sure you have `OPENAI_API_KEY` set in your environment.
|
||||
"""
|
||||
|
||||
import os
|
||||
from langchain.prompts import PromptTemplate
|
||||
from langchain.schema import RunnableSequence
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langchain.middleware.hil import HumanInTheLoopMiddleware
|
||||
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 InMemorySaver
|
||||
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 = OpenAI(api_key=api_key, temperature=0.7)
|
||||
llm = ChatOpenAI(api_key=api_key, temperature=0.7)
|
||||
|
||||
prompt_template = PromptTemplate(
|
||||
input_variables=["question"],
|
||||
template="You are a helpful assistant. Answer the following question clearly and concisely: {question}"
|
||||
# --- Agent --------------------------------------------------------------
|
||||
memory = InMemorySaver()
|
||||
|
||||
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,
|
||||
)
|
||||
|
||||
chain = RunnableSequence([prompt_template, llm])
|
||||
hil_chain = HumanInTheLoopMiddleware(chain)
|
||||
|
||||
# --- 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
|
||||
result = hil_chain.invoke({"question": user_input})
|
||||
print(f"\nAssistant (confirmed): {result}\n")
|
||||
|
||||
# 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
|
||||
|
||||
Reference in New Issue
Block a user