From 117ed8a64b247e0b03a8cfdc25e22c40c8d54e51 Mon Sep 17 00:00:00 2001 From: lonpatovaadelina Date: Thu, 28 May 2026 14:22:08 +0000 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20client.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client.py | 135 +++++++++++++++++------------------------------------- 1 file changed, 43 insertions(+), 92 deletions(-) diff --git a/client.py b/client.py index f4a5bbe..694db59 100644 --- a/client.py +++ b/client.py @@ -1,120 +1,71 @@ # client.py - """ -Интерактивный CLI для LangChain‑агента с потоковым выводом. -Используем только стек из условия: - - langchain.agents.create_agent - - langchain.tools.tool - - langchain_ollama.ChatOllama - - rich.print (rprint) +Интерактивный CLI для LangChain‑агента, работающего в режиме stream. """ -from langchain.agents import create_agent -from langchain.tools import tool -from langchain_ollama import ChatOllama -from rich import print as rprint +from langchain.agents import create_agent # обязательный стек +from langchain.tools import tool # обязательный стек +from rich import print as rprint # обязательный стек # ------------------------------------------------------------------ -# 1. Определяем LLM‑обёртку – Ollama +# Определяем простейший инструмент (для демонстрации) # ------------------------------------------------------------------ -llm = ChatOllama(model="llama3") # модель, которую можно заменить - -# ------------------------------------------------------------------ -# 2. Создаём инструмент get_price -# В реальном проекте он бы вызывал подагент или API, -# здесь просто возвращаем фиктивную таблицу. -# ------------------------------------------------------------------ -@tool("get_price", "Получить цену товара в указанном городе") -def get_price(product: str, city: str) -> str: +@tool("echo") +def echo_tool(input: str) -> str: """ - Возвращает таблицу с ценой продукта в заданном городе. + Возвращает введённую строку без изменений. """ - # В реальном коде здесь могла бы быть логика обращения к API - return f"| Продукт | Цена (руб.) | Магазин |\n| {product} | 89 | Магнит |" + return input + # ------------------------------------------------------------------ -# 3. Создаём агента, который использует LLM и инструмент get_price +# Создаём агента с одним инструментом # ------------------------------------------------------------------ agent = create_agent( - llm=llm, - tools=[get_price], - verbose=False, # будем выводить вручную + tools=[echo_tool], # список доступных инструментов + verbose=True, # включаем вывод логов (необязательно) ) # ------------------------------------------------------------------ -# 4. Функции форматирования чанков +# Основной цикл CLI # ------------------------------------------------------------------ -def format_message(message) -> str: +def main() -> None: """ - Форматируем сообщение для вывода. - Если в сообщении есть content – возвращаем его, - иначе (tool call) формируем строку вызова инструмента. + Запускает бесконечный цикл чтения пользовательского ввода и + передаёт его агенту в режиме stream. Ответы печатаются токен за токеном. """ - if message.content: - return message.content - # Предполагаем, что tool_calls содержит один элемент - tc = message.tool_calls[0] - name = tc["name"] - args = tc.get("args", {}) - return f"{name}({args})" + rprint("[bold green]LangChain Stream‑Agent CLI[/bold green]") + rprint("Введите сообщение (Ctrl+C для выхода):") -def format_chunk_message(chunk): - """ - Выводим токен текста в режиме stream. - При смене шага выводим разделитель. - """ - global step - message, meta = chunk - if meta["langgraph_step"] != step: - step = meta["langgraph_step"] - rprint("\n--- --- ---\n") - if message.content: - # flush=False – чтобы не добавлять перевод строки после каждого токена - print(message.content, end="", flush=True) + try: + while True: + # Читаем ввод пользователя + user_input = input("> ").strip() + if not user_input: + continue -# ------------------------------------------------------------------ -# 5. Основной цикл CLI -# ------------------------------------------------------------------ -def main(): - """ - Запускаем интерактивный режим: пользователь вводит запрос, - агент отвечает в режиме stream. - """ - rprint("[bold cyan]LangChain Stream‑Agent[/bold cyan]") - rprint("Введите ваш вопрос (Ctrl+C для выхода):") - while True: - try: - user_input = input("\n> ") - except KeyboardInterrupt: - rprint("\n[red]Выход из программы.[/red]") - break + # Формируем запрос к агенту + stream = agent.stream( + {"messages": [{"role": "human", "content": user_input}]}, + stream_mode=["messages", "updates"], + ) - if not user_input.strip(): - continue + # Перебираем события из потока и выводим их + for event in stream: + # Событие может быть либо токеном сообщения, либо обновлением состояния + if isinstance(event, dict) and "type" in event: + # Обновление (например, вызов инструмента) + rprint(f"[yellow]Update: {event}[/yellow]") + else: + # Токен текста + rprint(event, end="", flush=True) - # Запускаем потоковый вывод - stream = agent.stream( - {"messages": [{"role": "human", "content": user_input}]}, - stream_mode=["messages", "updates"], - ) + # После завершения потока выводим перевод строки + rprint("\n") - step = 1 # номер текущего шага агента + except KeyboardInterrupt: + rprint("\n[bold red]Выход из программы.[/bold red]") - for chunk in stream: - chunk_type, chunk_data = chunk - - if chunk_type == "messages": - format_chunk_message(chunk_data) - - elif chunk_type == "updates": - # При завершении шага выводим итоговое сообщение - model_update = chunk_data.get("model") - if model_update: - last_msg = model_update["messages"][-1] - rprint("\n" + format_message(last_msg)) - - # После завершения потока добавляем перевод строки для следующего запроса - print() if __name__ == "__main__": main() \ No newline at end of file