From c2ba581de2d6339ed49ce854ccc49d43fcc8a8fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D0=B8=D0=B8=D0=BB=20=D0=92=D0=B8=D0=BA?= =?UTF-8?q?=D1=82=D0=BE=D1=80=D0=BE=D0=B2?= Date: Thu, 2 Jul 2026 08:20:12 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20main.py=20=E2=80=94=20=D0=9F=D1=80=D0=B0?= =?UTF-8?q?=D0=BA=D1=82=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=BE=D0=B5=20=D0=B7?= =?UTF-8?q?=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8=D0=B5=20=E2=84=963:=20=D0=9F?= =?UTF-8?q?=D0=B0=D0=BC=D1=8F=D1=82=D1=8C=20=D0=B8=20=D0=BF=D0=BE=D0=B4?= =?UTF-8?q?=D1=82=D0=B2=D0=B5=D1=80=D0=B6=D0=B4=D0=B5=D0=BD=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=B4=D0=B5=D0=B9=D1=81=D1=82=D0=B2=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.py | 101 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 53 insertions(+), 48 deletions(-) diff --git a/main.py b/main.py index d5d6fd0..0f44098 100644 --- a/main.py +++ b/main.py @@ -1,15 +1,19 @@ import os -import uuid import asyncio +from typing import Optional, Dict, Any + from langchain_openai import ChatOpenAI -from langchain.tools import tool from langchain_core.messages import HumanMessage -from deepagents import create_deep_agent -from deepagents.backends import FilesystemBackend, LocalShellBackend, CompositeBackend +from langchain.tools import tool from langgraph.checkpoint.memory import MemorySaver +from deepagents.backends import FilesystemBackend, LocalShellBackend, CompositeBackend +from deepagents import create_deep_agent as create_agent from rich.console import Console -# Конфигурация LLM через OpenRouter +# Инициализация консоли rich +console = Console() + +# Инициализация LLM через OpenRouter llm = ChatOpenAI( model="openai/gpt-oss-20b:free", base_url="https://openrouter.ai/api/v1", @@ -17,7 +21,7 @@ llm = ChatOpenAI( temperature=0.0, ) -# Backend для выполнения инструментов (необязательно, но удобно) +# Backend для deepagents (необязательно, но удобно) backend = CompositeBackend([ LocalShellBackend(workspace_dir="./workspace"), FilesystemBackend(), @@ -25,74 +29,75 @@ backend = CompositeBackend([ # Пример простого инструмента @tool -def get_price(params: dict) -> str: - """ - Получить цену товара в указанном городе и дате. - """ - city = params.get("city", "неизвестный город") - date = params.get("date", "неизвестная дата") - return f"Цена в {city} на {date} составляет 100$" +def get_price(city: str, date: str) -> str: + """Возвращает цену в указанном городе и дате.""" + return f"Цена в {city} на {date} составляет $100" + +# Память разговора +memory = MemorySaver() # Создание агента с памятью и паузой перед инструментом -memory = MemorySaver() -agent = create_deep_agent( +agent = create_agent( model=llm, tools=[get_price], backend=backend, - system_prompt="You are a helpful agent that asks for confirmation before calling tools.", + system_prompt="You are a helpful agent.", checkpointer=memory, interrupt_before=["tools"], ) -console = Console() +# Конфигурация разговора +config: Dict[str, Any] = {"configurable": {"thread_id": "conversation-1"}} -async def ask_and_run(user_input, config): +async def ask_and_run(user_input: Optional[Dict[str, Any]], config: Dict[str, Any]) -> None: """ - Запускает потоковое выполнение агента, обрабатывает паузы и подтверждения. + Запускает потоковое взаимодействие с агентом. + Если агент останавливается перед вызовом инструмента, запрашивает подтверждение у пользователя. """ - async for chunk in agent.stream(user_input, config=config, stream_mode=["messages", "updates"]): - chunk_type, chunk_data = chunk - - # Потоковый вывод токенов + async for chunk_type, chunk_data in agent.stream( + user_input, + config=config, + stream_mode=["messages", "updates"], + ): + # Вывод токенов ответа if chunk_type == "messages": - console.print(chunk_data, end="", style="cyan") - continue + content = chunk_data.get("content", "") + console.print(content, end="") - # Вывод вызовов инструментов - if chunk_type == "updates": - console.print(chunk_data, style="magenta") - continue + # Вывод информации о вызове инструмента + elif chunk_type == "updates": + console.print(chunk_data) # Обнаружение паузы перед инструментом if "__interrupt__" in chunk_data and agent.get_state(config).next == ("tools",): state = agent.get_state(config) - # Последнее сообщение должно содержать вызов инструмента - last_msg = state.values["messages"][-1] - tool_call = last_msg.tool_calls[0] - tool_name = tool_call["name"] - tool_args = tool_call["args"] - console.print(f"\nАгент хочет вызвать утилиту {tool_name}({tool_args})") + # Последнее сообщение содержит вызов инструмента + tool_call = state.values["messages"][-1].tool_calls[0] + console.print("\n") + console.print(f"{tool_call['name']}({tool_call['args']})") + console.print("Агент хочет вызвать утилиту") answer = input("Разрешить? (Y/n): ") if answer.lower().strip() == "y": - # Возобновляем выполнение с того места, где остановились await ask_and_run(None, config) + return else: - console.print("Отменено", style="red") - break + console.print("Отменено") + return -async def main(): - thread_id = f"session-{uuid.uuid4()}" - config = {"configurable": {"thread_id": thread_id}} - - console.print("Введите 'exit' для выхода.", style="bold green") +def main() -> None: + console.print("\n--- --- ---\n") while True: user_input = input("\nВы: ") - if user_input.lower() == "exit": + if user_input.lower().strip() == "exit": break - await ask_and_run( - {"messages": [HumanMessage(content=user_input)]}, - config, + # Запускаем асинхронную функцию + asyncio.run( + ask_and_run( + {"messages": [{"role": "human", "content": user_input}]}, + config, + ) ) + console.print("\n--- --- ---\n") if __name__ == "__main__": - asyncio.run(main()) \ No newline at end of file + main() \ No newline at end of file