From 1e693d571ebc42936c8e8b0efe1990ce0f3333ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D0=B5=D0=BC=20=D0=92=D0=BB=D0=B0=D0=B4?= =?UTF-8?q?=D0=B8=D0=BC=D0=B8=D1=80=D0=BE=D0=B2=D0=B8=D1=87=20=D0=91=D0=B0?= =?UTF-8?q?=D0=B1=D0=B0=D0=B9=D0=BA=D0=B8=D0=BD?= Date: Tue, 19 May 2026 14:59:23 +0000 Subject: [PATCH] feat: solution for task-003 --- solutions/task-003/solution.py | 74 ++++++++++++++-------------------- 1 file changed, 31 insertions(+), 43 deletions(-) diff --git a/solutions/task-003/solution.py b/solutions/task-003/solution.py index 7f30380..0462d34 100644 --- a/solutions/task-003/solution.py +++ b/solutions/task-003/solution.py @@ -29,7 +29,7 @@ memory = MemorySaver() agent = create_agent( model=llm, tools=tools, - system_prompt="Ты полезный помощкиник. Если пользователь спрашивает о погоде, используй инструмент get_price.", + system_prompt="Ты полезный помощмущник. Если пользователь спрашивает о погоде, используй инструмент get_price.", checkpointer=memory, interrupt_before=['tools'], ) @@ -39,39 +39,32 @@ config = {"configurable": {"thread_id": "chat-session-123"}} def ask_and_run(user_input, config): """Основная функция обработки сообщений и управления циклом подтверждения.""" + # Если user_input is None, мы просто продолжаем выполнение (возобновление после паузы) - input_payload = {"messages": [{"role": "human", "content": user_input}]} if user_input else None - - # stream_mode=['messages', 'updates'] позволяет обрабатывать и токены, и изменения состояния - stream = agent.stream(input_payload, config=config, stream_mode=['messages', 'updates']) + # В LangGraph для возобновления через stream передается None или пустой список сообщений + stream_input = user_input if user_input is not None else [] + # Используем stream_mode=['messages', 'updates'] согласно заданию try: - for chunk in stream: - chunk_type, chunk_data = chunk + for chunk in agent.stream(stream_input, config=config, stream_mode=['messages', 'updates']): state = agent.get_state(config) + chunk_type, chunk_data = chunk if chunk_type == 'messages': - # Потоковый вывод текста (токены) - # В новых версиях LangGraph/LangChain чанк сообщения может быть объектом BaseMessage - content = "" - if hasattr(chunk_data, 'content'): - content = chunk_data.content - elif isinstance(chunk_data, str): - content = chunk_data - - if content: - console.print(content, end="", flush=True) + # Потоковый вывод токенов (сообщений) + message, metadata = chunk_data + if hasattr(message, "content") and message.content: + print(message.append if hasattr(message, 'append') else message.content, end="", flush=True) elif chunk_type == 'updates': - # Здесь можно обрабатывать обновления узлов, если нужно + # Вывод информации об обновлениях (например, вызовы инструментов) pass - # Проверка на прерывание (интеррупт перед инструментом) - # В LangGraph при interrupt_before ключ '__interrupt__' появляется в данных чанка + # Проверка на прерывание перед вызовом инструмента if '__interrupt__' in chunk_data and state.next == ('tools',): - console.print("\n" + "---" * 10) + console.print("\n" + "---" * 15) - # Извлекаем информацию о том, какой инструмент вызывается + # Извлекаем информацию о том, какой инструмент хочет вызвать агент last_message = state.values['messages'][-1] if hasattr(last_message, 'tool_calls') and last_message.tool_calls: tool_call = last_message.tool_calls[0] @@ -81,39 +74,34 @@ def ask_and_run(user_input, config): answer = input("Разрешить? (Y/n): ") if answer.lower().strip() == 'y': - # Рекурсивный вызов с None для продолжения выполнения + # Рекурсивный вызов для продолжения без нового сообщения пользователя ask_and_run(None, config) else: console.print("Отменено") - return # Выход из текущей итерации функции - - # Печать переноса строки после завершения стрима (если не было прерывания) - if user_input is not None: - console.print() + return # Выходим из текущей итерации стрима + if user_input: # Печать переноса строки только если был новый ввод + print() + except Exception as e: - # Обработка ошибок или остановки потока - pass + # Если произошла ошибка в процессе стрима (например, при рекурсии), пробрасываем её выше + raise e -def main(): +if __name__ == "__main__": console.print("[bold blue]Чат запущен. Напишите 'exit' для выхода.[/bold blue]") while True: try: user_text = input("\nВы: ") if user_text.lower().strip() == 'exit': - console.print("До свидания!") break - - # Запуск агента с новым сообщением - ask_and_run( - {"messages": [{"role": "human", "content": user_text}]}, - config, - ) - except EOFError: + + # Формируем входные данные для агента + input_payload = {"messages": [{"role": "human", "content": user_text}]} + + ask_and_run(input_payload, config) + + except KeyboardInterrupt: break except Exception as e: - console.print(f"Ошибка: {e}") - -if __name__ == "__main__": - main() \ No newline at end of file + console.print(f"[bold red]Ошибка: {e}[/bold red]") \ No newline at end of file