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:
|
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.
|
||||||
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.
|
|
||||||
|
|
||||||
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
|
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
|
import os
|
||||||
from langchain.prompts import PromptTemplate
|
from typing import List, Dict
|
||||||
from langchain.schema import RunnableSequence
|
|
||||||
from langchain_openai import ChatOpenAI
|
|
||||||
from langchain.middleware.hil import HumanInTheLoopMiddleware
|
|
||||||
|
|
||||||
|
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")
|
api_key = os.getenv("OPENAI_API_KEY")
|
||||||
if not api_key:
|
if not api_key:
|
||||||
raise RuntimeError("OPENAI_API_KEY environment variable is required.")
|
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(
|
# --- Agent --------------------------------------------------------------
|
||||||
input_variables=["question"],
|
memory = InMemorySaver()
|
||||||
template="You are a helpful assistant. Answer the following question clearly and concisely: {question}"
|
|
||||||
|
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])
|
# --- Interactive loop -----------------------------------------------
|
||||||
hil_chain = HumanInTheLoopMiddleware(chain)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
config = {"configurable": {"thread_id": "session-1"}}
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
user_input = input("\nUser: ")
|
try:
|
||||||
|
user_input = input("\nUser: ")
|
||||||
|
except EOFError:
|
||||||
|
print("\nNo input provided. Exiting.")
|
||||||
|
break
|
||||||
if user_input.lower() in {"exit", "quit", "q"}:
|
if user_input.lower() in {"exit", "quit", "q"}:
|
||||||
print("Goodbye!")
|
print("Goodbye!")
|
||||||
break
|
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:
|
except KeyboardInterrupt:
|
||||||
print("\nInterrupted. Exiting.")
|
print("\nInterrupted. Exiting.")
|
||||||
break
|
break
|
||||||
|
|||||||
Reference in New Issue
Block a user