From 32632ede1b9e8b10697f10c6c7a211d5a1be79fd Mon Sep 17 00:00:00 2001 From: lonpatovaadelina Date: Wed, 27 May 2026 14:51:44 +0000 Subject: [PATCH] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=BA=D1=82=D0=B8=D1=87?= =?UTF-8?q?=D0=B5=D1=81=D0=BA=D0=BE=D0=B5=20=D0=B7=D0=B0=D0=B4=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=E2=84=963:=20=D0=9F=D0=B0=D0=BC=D1=8F=D1=82?= =?UTF-8?q?=D1=8C=20=D0=B8=20=D0=BF=D0=BE=D0=B4=D1=82=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D0=B6=D0=B4=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B4=D0=B5=D0=B9=D1=81?= =?UTF-8?q?=D1=82=D0=B2=D0=B8=D0=B9:=20client.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client.py | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 solutions/69a474cdc46fd26feae69896_Практическое_задание__3__Память_и_подтве/client.py diff --git a/solutions/69a474cdc46fd26feae69896_Практическое_задание__3__Память_и_подтве/client.py b/solutions/69a474cdc46fd26feae69896_Практическое_задание__3__Память_и_подтве/client.py new file mode 100644 index 0000000..554814c --- /dev/null +++ b/solutions/69a474cdc46fd26feae69896_Практическое_задание__3__Память_и_подтве/client.py @@ -0,0 +1,118 @@ +# client.py + +""" +Интерактивный клиент для агента, реализованного в agent.py. +Поддерживает команды: + /add – добавить текст в базу Qdrant + /search – выполнить поиск по базе и вывести результаты + /quit – выйти из программы +""" + +import sys +from typing import Dict + +# rich для красивого вывода +from rich.console import Console +from rich.table import Table +from rich.prompt import Prompt + +console = Console() + +# Импортируем агент, созданный в agent.py +try: + from agent import create_agent, llm # llm – объект модели Ollama +except Exception as e: # pragma: no cover + console.print(f"[red]Ошибка при импорте агента: {e}[/red]") + sys.exit(1) + +# Создаём память и агент с паузой перед инструментом +from langgraph.checkpoint.memory import MemorySaver + +memory = MemorySaver() + +agent = create_agent( + model=llm, + tools=[ # инструменты, которые определены в agent.py + "search", + "add", + ], + system_prompt="You are a helpful assistant that can search and add documents.", + checkpointer=memory, + interrupt_before=["tools"], # пауза перед вызовом любого инструмента +) + +# Текущий thread_id (разговор) +thread_id = "cli-thread" +config = {"configurable": {"thread_id": thread_id}} + + +def ask_and_run(user_input: Dict, config: Dict): + """ + Отправляем запрос агенту и обрабатываем потоковые ответы. + При паузе перед инструментом спрашиваем подтверждение у пользователя. + """ + for chunk in agent.stream( + user_input, + config=config, + stream_mode=["messages", "updates"], + ): + state = agent.get_state(config) + chunk_type, chunk_data = chunk + + # Печатаем токены сообщения + if chunk_type == "messages": + console.print(chunk_data, end="") + + # Печатаем вызовы инструментов + if chunk_type == "updates": + for update in chunk_data: + if isinstance(update, dict) and "tool_calls" in update.get("values", {}): + tool_call = update["values"]["messages"][-1].tool_calls[0] + console.print(f"\n[bold cyan]Агент хочет вызвать утилиту {tool_call['name']}({tool_call['args']})[/bold cyan]") + # Спрашиваем подтверждение + answer = Prompt.ask("Разрешить? (Y/n)", default="y") + if answer.lower().strip() in ("y", "yes"): + # Возобновляем работу с тем же состоянием + ask_and_run(None, config) + else: + console.print("[red]Отменено[/red]") + return + + # Обрабатываем паузу перед инструментом + if "__interrupt__" in chunk_data and state.next == ("tools",): + # Пауза – уже обработана в блоке выше + continue + + +def main(): + console.print("[bold green]Привет! Введите команду (/add, /search, /quit).[/bold green]") + while True: + try: + user_input = Prompt.ask("\nВы") + except KeyboardInterrupt: # pragma: no cover + console.print("\n[red]Выход...[/red]") + break + + if not user_input.strip(): + continue + + if user_input.lower() == "/quit": + console.print("[green]До свидания![/green]") + break + + if user_input.startswith("/add "): + text = user_input[len("/add ") :] + ask_and_run( + {"messages": [{"role": "human", "content": f"/add {text}"}]}, config + ) + elif user_input.startswith("/search "): + query = user_input[len("/search ") :] + ask_and_run( + {"messages": [{"role": "human", "content": f"/search {query}"}]}, config + ) + else: + console.print("[yellow]Неизвестная команда. Попробуйте /add, /search или /quit.[/yellow]") + + +if __name__ == "__main__": + main() \ No newline at end of file