diff --git a/cli_client.py b/cli_client.py
new file mode 100644
index 0000000..bc5e72f
--- /dev/null
+++ b/cli_client.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+"""
+cli_client.py
+
+Интерактивный клиент для работы с агентом RAG.
+Поддерживает команды:
+ /add
– добавить документ в базу знаний
+ /search – выполнить семантический поиск
+ /quit – выйти из программы
+"""
+
+import sys
+from typing import List
+
+# Rich для красивого вывода
+try:
+ from rich.console import Console
+ from rich.table import Table
+except ImportError: # pragma: no cover
+ print("Необходимо установить пакет 'rich'")
+ sys.exit(1)
+
+# Инструменты агента (предполагается, что они реализованы в другом модуле)
+try:
+ from rag_tools import search_knowledge_base, add_to_knowledge_base
+except Exception as exc: # pragma: no cover
+ print(f"Ошибка при импорте инструментов: {exc}")
+ sys.exit(1)
+
+console = Console()
+
+
+def _parse_command(line: str) -> List[str]:
+ """
+ Разбирает строку ввода пользователя.
+ Возвращает список токенов, разделённых пробелами,
+ но сохраняет аргументы в кавычках как один элемент.
+ """
+ import shlex
+
+ try:
+ return shlex.split(line)
+ except ValueError as exc: # pragma: no cover
+ console.print(f"[red]Ошибка разбора команды:[/red] {exc}")
+ return []
+
+
+def _handle_add(tokens: List[str]) -> None:
+ """
+ Обработчик команды /add.
+ Ожидает два аргумента: title и content (в кавычках).
+ """
+ if len(tokens) < 3:
+ console.print("[red]Неверное количество аргументов. Используйте:/add \"title\" \"content\"[/red]")
+ return
+ title = tokens[1]
+ content = " ".join(tokens[2:])
+ try:
+ add_to_knowledge_base(content=content, title=title)
+ console.print(f"[green]Документ '{title}' успешно добавлен.[/green]")
+ except Exception as exc: # pragma: no cover
+ console.print(f"[red]Ошибка при добавлении:[/red] {exc}")
+
+
+def _handle_search(tokens: List[str]) -> None:
+ """
+ Обработчик команды /search.
+ Ожидает один аргумент – запрос (в кавычках).
+ """
+ if len(tokens) < 2:
+ console.print("[red]Неверное количество аргументов. Используйте:/search \"query\"[/red]")
+ return
+ query = " ".join(tokens[1:])
+ try:
+ results = search_knowledge_base(query=query, max_results=5)
+ if not results:
+ console.print("[yellow]Результаты не найдены.[/yellow]")
+ return
+
+ table = Table(title="Поиск", show_lines=True)
+ table.add_column("Номер", style="cyan")
+ table.add_column("Заголовок", style="magenta")
+ table.add_column("Содержание")
+
+ for idx, res in enumerate(results, start=1):
+ # Предполагается, что результат – словарь с ключами title и content
+ title = res.get("title", "Без заголовка")
+ content = res.get("content", "")
+ snippet = (content[:200] + "...") if len(content) > 200 else content
+ table.add_row(str(idx), title, snippet)
+
+ console.print(table)
+ except Exception as exc: # pragma: no cover
+ console.print(f"[red]Ошибка при поиске:[/red] {exc}")
+
+
+def main() -> None:
+ """
+ Основной цикл CLI.
+ """
+ console.print("[bold green]Добро пожаловать в RAG-агент![/bold green]")
+ console.print("Доступные команды:")
+ console.print("/add \"title\" \"content\" – добавить документ")
+ console.print("/search \"query\" – выполнить поиск")
+ console.print("/quit – выйти из программы")
+
+ while True:
+ try:
+ line = console.input("\n[bold blue]Введите команду[/bold blue]> ")
+ except (KeyboardInterrupt, EOFError): # pragma: no cover
+ console.print("\n[bold red]Выход...[/bold red]")
+ break
+
+ if not line.strip():
+ continue
+
+ tokens = _parse_command(line)
+ if not tokens:
+ continue
+
+ command = tokens[0].lower()
+ if command == "/add":
+ _handle_add(tokens)
+ elif command == "/search":
+ _handle_search(tokens)
+ elif command in {"/quit", "exit"}:
+ console.print("[bold red]Выход...[/bold red]")
+ break
+ else:
+ console.print(f"[red]Неизвестная команда: {command}[/red]")
+
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file