# DESIGN DECISION: We use langgraph's create_agent instead of deepagents' create_deep_agent because the assignment's technical analysis requires replacing deepagents agent with LangGraph's create_agent. This satisfies the updated requirement and ensures compatibility with MemorySaver and interrupt_before features. # NECESSITY: The course's latest guidelines explicitly state to replace deepagents agent with LangGraph's create_agent. Using deepagents would violate this instruction and could lead to failing tests. # OPTIMALITY: LangGraph's create_agent provides native support for MemorySaver, interrupt_before, and stream_mode, simplifying implementation and reducing dependencies. # ALTERNATIVES CONSIDERED: Keeping deepagents would require additional wrappers to emulate MemorySaver and interrupt behavior, increasing complexity and risk of bugs. import os from typing import Optional from langchain_openai import ChatOpenAI from langchain.tools import tool from langgraph import create_agent from langgraph.checkpoint.memory import MemorySaver from rich.console import Console # Initialize LLM 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, ) # Define a simple tool @tool def get_price(query: str) -> str: """Get price for a city and date.""" return f"Price for {query} is $100" # Create agent with memory and interrupt before tools agent = create_agent( model=llm, tools=[get_price], system_prompt="You are a helpful agent.", checkpointer=MemorySaver(), interrupt_before=["tools"], ) console = Console() config = {"configurable": {"thread_id": "conversation-1"}} def ask_and_run(user_input: Optional[dict], cfg: dict) -> None: """ Stream agent output, handle pauses before tool calls, and ask for user confirmation. """ for chunk in agent.stream( user_input, config=cfg, stream_mode=["messages", "updates"], ): chunk_type, chunk_data = chunk # Handle message tokens if chunk_type == "messages": content = chunk_data.get("content", "") console.print(content, end="") # Handle tool call results or other updates if chunk_type == "updates": console.print(chunk_data) # Detect pause before tool invocation if "__interrupt__" in chunk_data and agent.get_state(cfg).next == ("tools",): state = agent.get_state(cfg) last_msg = state.values["messages"][-1] tool_call = last_msg.tool_calls[0] name = tool_call["name"] args = tool_call["arguments"] console.print(f"{name}({args})") console.print(f"Агент хочет вызвать утилиту {name}({args})") answer = input("Разрешить? (Y/n): ") if answer.lower().strip() == "y": ask_and_run(None, cfg) else: console.print("Отменено") break while True: user_input = input("\nВы: ") if user_input.lower() == "exit": break ask_and_run( {"messages": [{"role": "human", "content": user_input}]}, config, )