import os import asyncio from langchain_openai import ChatOpenAI from langchain_core.messages import HumanMessage from langchain.tools import tool from deepagents import create_deep_agent from deepagents.backends import FilesystemBackend, LocalShellBackend, CompositeBackend from langchain.agents.middleware import HumanInTheLoopMiddleware from langgraph.checkpoint.memory import MemorySaver from langgraph.types import Command # --- LLM initialization (OpenRouter) ------------------------------------------------- llm = ChatOpenAI( model="openai/gpt-oss-20b:free", base_url="https://openrouter.ai/api/v1", api_key=os.getenv("OPENAI_API_KEY"), temperature=0.0, ) # --- Backend setup ------------------------------------------------- backend = CompositeBackend([ LocalShellBackend(workspace_dir="./workspace"), FilesystemBackend(), ]) # --- Tool definition ------------------------------------------------- @tool def get_weather(city: str, date: str) -> str: """Return a mock weather report for the given city and date.""" # In a real scenario this would call an external API. return f"The weather in {city} on {date} is sunny with a high of 25°C." # --- Agent creation ------------------------------------------------- agent = create_deep_agent( model=llm, tools=[get_weather], backend=backend, system_prompt="You are a helpful assistant.", middleware=[ HumanInTheLoopMiddleware( interrupt_on={"get_weather": True}, description_prefix="Подтвердите вызов инструмента", ), ], checkpointer=MemorySaver(), ) # --- Helper functions ------------------------------------------------- async def prompt_user_for_decisions(action_requests, review_configs): decisions = [] for idx, action in enumerate(action_requests): print(f"\n--- Подтверждение ---") print(f"Инструмент: {action.get('name')}\n") print(f"Аргументы: {action.get('args')}\n") if "description" in action: print(f"Описание: {action['description']}\n") allowed = review_configs[idx].get("allowed_decisions", ["approve", "reject", "edit"]) prompt = f"a = approve, r = reject{', e = edit' if 'edit' in allowed else ''}: " while True: choice = input(prompt).strip().lower() if choice == "a" and "approve" in allowed: decisions.append({"type": "approve"}) break elif choice == "r" and "reject" in allowed: msg = input("Сообщение для агента (причина отказа): ") decisions.append({"type": "reject", "message": msg}) break elif choice == "e" and "edit" in allowed: # Simple edit: ask for new JSON args new_args = input("Введите отредактированные аргументы в формате JSON: ") try: import json edited = json.loads(new_args) decisions.append({"type": "edit", "edited_action": {"name": action['name'], "args": edited}}) break except json.JSONDecodeError: print("Неверный JSON. Попробуйте снова.") else: print("Неверный выбор. Попробуйте снова.") return decisions # --- Main interaction loop ------------------------------------------------- async def main(): config = {"configurable": {"thread_id": "session-1"}} # Initial user message user_input = input("Вы: ") result = await agent.ainvoke( {"messages": [HumanMessage(content=user_input)]}, config, ) # Process possible interrupts while "__interrupt__" in result: interrupt = result["__interrupt__"][0].value action_requests = interrupt.get("action_requests", []) review_configs = interrupt.get("review_configs", []) decisions = await prompt_user_for_decisions(action_requests, review_configs) result = await agent.ainvoke(Command(resume={"decisions": decisions}), config) # Final answer final_message = result["messages"][-1].content print(f"\nАгент: {final_message}") if __name__ == "__main__": asyncio.run(main())