""" Agent configuration for the Human‑in‑the‑Loop example. The agent uses :class:`langchain.agents.middleware.HumanInTheLoopMiddleware` to pause whenever a tool is called. The middleware automatically builds an interrupt payload that contains the name of the tool, its arguments and the allowed decisions (approve / reject / edit). The caller can then resume execution by sending a ``Command`` with the chosen decisions. """ import os from langchain_openai import ChatOpenAI from langchain.agents import create_agent from langchain.agents.middleware import HumanInTheLoopMiddleware from langgraph.checkpoint.memory import MemorySaver from langgraph.types import Command # LLM – BroJS llm = ChatOpenAI( model="openai/gpt-oss-20b:free", base_url="https://platform.brojs.ru/jrnl-bh/api/inference/v1", api_key=os.getenv("JOURNAL_MCP_PAT"), temperature=0.5, ) # Tool import – defined in tools.py from tools import get_weather memory = MemorySaver() agent = create_agent( llm=llm, tools=[get_weather], system_prompt="Ты полезный ассистент.", middleware=[ HumanInTheLoopMiddleware( interrupt_on={"get_weather": True}, description_prefix="Подтвердите вызов инструмента", ), ], checkpointer=memory, ) # Helper to run a single user query with HIL loop def run_query(user_msg: str, thread_id: str = "session-1"): config = {"configurable": {"thread_id": thread_id}} result = agent.invoke({"messages": [{"role": "human", "content": user_msg}]}, config) # Loop while the agent is paused for a decision while "__interrupt__" in result: interrupt_value = result["__interrupt__"][0].value action_requests = interrupt_value.get("action_requests", []) decisions = [] print("\n--- Подтверждение вызова инструмента ---") for idx, act in enumerate(action_requests): name = act["name"] args = act.get("args", {}) desc = act.get("description", "") print(f"{idx+1}. Инструмент: {name}") print(f" Аргументы: {args}") if desc: print(f" Описание: {desc}") # Simple approve/reject per action for idx, act in enumerate(action_requests): while True: choice = input("a=approve, r=reject (e=edit не поддерживается): ").strip().lower() if choice == "a": decisions.append({"type": "approve"}) break elif choice == "r": msg = input("Причина отказа: ") decisions.append({"type": "reject", "message": msg}) break # Resume execution with collected decisions result = agent.invoke(Command(resume={"decisions": decisions}), config) # Final answer final_msg = result["messages"][-1].content print("\nОтвет агента:") print(final_msg) return final_msg if __name__ == "__main__": run_query("Какая погода в Казани сегодня?")