Files
task-69a86305c46fd26feae6bcaa/main.py
T

113 lines
4.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 ------------------------------------------------------------
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 --------------------------------------------------------
backend = CompositeBackend([
LocalShellBackend(workspace_dir="./workspace"),
FilesystemBackend(),
])
# --- Tool ------------------------------------------------------------
@tool
def get_weather(city: str, date: str = "today") -> str:
"""Return a mock weather report for the given city and date."""
return f"The weather in {city} on {date} is sunny with a high of 25°C."
# --- Agent ----------------------------------------------------------
# DESIGN DECISION: Using HumanInTheLoopMiddleware with interrupt_on for get_weather
# NECESSITY: Middleware automatically pauses before tool execution and asks for approval.
# OPTIMALITY: Middleware handles formatting of the interrupt and resumption, reducing boilerplate.
# ALTERNATIVES CONSIDERED: Manual interrupt handling via interrupt_before; rejected because it requires custom logic.
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 invoke_agent(message: str, thread_id: str):
config = {"configurable": {"thread_id": thread_id}}
result = await agent.ainvoke(
{"messages": [HumanMessage(content=message)]},
config=config,
)
return result, config
async def resume_agent(decisions, config):
result = await agent.ainvoke(Command(resume={"decisions": decisions}), config=config)
return result
def print_interrupt(interrupt):
action_requests = interrupt['action_requests']
review_configs = interrupt['review_configs']
print("\n--- Подтверждение ---")
for idx, action in enumerate(action_requests):
name = action.get("name")
args = action.get("args")
description = action.get("description", "")
print(f"{idx+1}. Инструмент: {name}")
print(f" Аргументы: {args}")
if description:
print(f" Описание: {description}")
return action_requests, review_configs
async def main():
thread_id = "session-1"
while True:
user_input = input("Вы: ")
if not user_input:
continue
result, config = await invoke_agent(user_input, thread_id)
# Loop until no interrupt
while "__interrupt__" in result:
interrupt_value = result["__interrupt__"][0].value
action_requests, review_configs = print_interrupt(interrupt_value)
decisions = []
for idx, action in enumerate(action_requests):
while True:
choice = input("a = approve, r = reject: ").strip().lower()
if choice == "a":
decisions.append({"type": "approve"})
break
elif choice == "r":
msg = input("Сообщение для агента (причина отказа): ")
decisions.append({"type": "reject", "message": msg})
break
else:
print("Неверный ввод. Попробуйте снова.")
result = await resume_agent(decisions, config)
# No more interrupts print final answer
final_message = result["messages"][-1].content
print(f"\nАгент: {final_message}\n")
# Ask if user wants another query in the same session
again = input("Хотите задать ещё вопрос? (y/n): ").strip().lower()
if again != "y":
break
if __name__ == "__main__":
asyncio.run(main())