From d7ffa797c46388828e23fe52c18d4cb4ccad25be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=9A=D1=83=D1=82?= =?UTF-8?q?=D0=BB=D0=B0=D1=85=D0=BC=D0=B5=D1=82=D0=BE=D0=B2?= Date: Tue, 26 May 2026 13:46:58 +0000 Subject: [PATCH] add agent.py --- agent.py | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 agent.py diff --git a/agent.py b/agent.py new file mode 100644 index 0000000..947586a --- /dev/null +++ b/agent.py @@ -0,0 +1,78 @@ +""" +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("Какая погода в Казани сегодня?")