Проблема, которая сводит с ума: забытые флаги и потерянные часы
Вы стоите перед терминалом. В голове - четкая задача: "распаковать архив tar.gz, посмотреть что внутри, удалить старые логи старше недели". Руки замирают над клавиатурой. tar -xzf? tar -xvzf? А как там смотреть содержимое без распаковки? tar -tzf? Или -tf? Гугл уже открыт в соседней вкладке. Снова.
Это не недостаток знаний. Это проклятие современного разработчика: слишком много команд, слишком много флагов, слишком много вариантов. Особенно когда дело доходит до одноразовых операций вроде конвертации видео, работы с сетью или анализа логов.
Статистика 2025 года от JetBrains показывает: разработчики тратят в среднем 17 минут в день на поиск забытых команд и синтаксиса. За год это 85 часов. Больше двух рабочих недель.
Решение: не запоминать, а описывать
Вместо того чтобы зубрить man-страницы, давайте заставим компьютер понимать нас на человеческом языке. Не "как сделать tar с прогресс-баром", а просто сказать: "распаковать архив project.tar.gz и показать прогресс".
Ключевая идея проста до безобразия: берем любой LLM-бэкенд (локальный через API или облачный), пишем к нему тонкую обертку на чистом Python, и получаем переводчика с человеческого на bash.
Архитектура: минимализм как искусство
Весь скрипт укладывается в 50 строк потому что мы отрезаем все лишнее:
- Нет веб-сервера
- Нет баз данных
- Нет сложных фреймворков
- Нет даже argparse - используем sys.argv
Только три компонента:
- Клиент к LLM API (любому)
- Парсер запроса пользователя
- Интерактивный подтверждатель
Если вам интересны более сложные архитектуры с несколькими агентами и контекстами, посмотрите это руководство по фреймворкам для локальных агентов.
1 Выбираем LLM-бэкенд: локальный vs облачный
На февраль 2026 года у нас есть изобилие вариантов:
| Тип | Примеры | Плюсы | Минусы |
|---|---|---|---|
| Локальный | Ollama (Llama 3.2), LM Studio, KoboldCpp | Конфиденциальность, нет лимитов | Требует ресурсов, настройки |
| Облачный API | OpenAI GPT-4o, Anthropic Claude 3.5, Google Gemini 2.0 | Простота, качество | Стоимость, зависимость от интернета |
| Самодельный | Запуск через локальные LLM с KoboldCpp | Полный контроль | Сложность настройки |
Для нашего скрипта подойдет любой вариант с HTTP API. Я буду использовать локальную Ollama с моделью Llama 3.2 7B - она отлично справляется с генерацией команд и работает даже на ноутбуке.
2 Пишем костяк: 50 строк чистого Python
Вот полный скрипт. Разберем его по косточкам:
#!/usr/bin/env python3
"""
Terminal AI Assistant - переводчик с человеческого на bash
Использование: ./ai_assist.py "найти все файлы .log старше 7 дней и удалить"
"""
import sys
import json
import subprocess
from http.client import HTTPConnection
from urllib.parse import urlencode
def query_llama(prompt, api_url="localhost:11434"):
"""Запрос к локальной Ollama API"""
conn = HTTPConnection(api_url)
data = json.dumps({
"model": "llama3.2",
"prompt": f"""Ты - эксперт по Linux командам.
Преобразуй запрос в одну команду bash.
Запрос: {prompt}
Команда: """,
"stream": False,
"options": {"temperature": 0.1}
})
conn.request("POST", "/api/generate", data, {"Content-Type": "application/json"})
response = conn.getresponse()
if response.status == 200:
result = json.loads(response.read().decode())
# Извлекаем только команду, убирая пояснения
command = result["response"].strip()
# Удаляем все после первого перевода строки
if "\n" in command:
command = command.split("\n")[0]
return command
else:
print(f"Ошибка API: {response.status}")
return None
def confirm_and_execute(command):
"""Показать команду и спросить подтверждение"""
print(f"\nКоманда: {command}")
print("Выполнить? (y/n): ", end="")
try:
choice = input().strip().lower()
except KeyboardInterrupt:
print("\nОтменено")
return
if choice == 'y':
print("Выполняю...\n")
try:
result = subprocess.run(command, shell=True, check=True, text=True)
print(f"Успешно. Код возврата: {result.returncode}")
except subprocess.CalledProcessError as e:
print(f"Ошибка выполнения: {e}")
else:
print("Отменено")
def main():
if len(sys.argv) < 2:
print("Использование: ai_assist.py 'ваш запрос на естественном языке'")
sys.exit(1)
user_query = " ".join(sys.argv[1:])
print(f"Запрос: {user_query}")
command = query_llama(user_query)
if command:
confirm_and_execute(command)
if __name__ == "__main__":
main()
Что здесь важно:
- Строки 14-35: HTTP клиент без внешних библиотек. Используем стандартные модули.
- Строки 20-23: Промпт заточен под генерацию команд. Температура 0.1 для детерминированности.
- Строки 30-33: Чистим ответ LLM. Модели любят добавлять пояснения.
- Строки 37-55: Защита от дурака. Всегда спрашиваем подтверждение.
НИКОГДА не убирайте confirm_and_execute! Я видел проекты, где AI сразу выполнял команды. В одном случае это закончилось удалением продакшн-базы. В другом - отправкой тестовых писем реальным клиентам.
3 Настройка и первые тесты
Сначала запускаем Ollama (если используем локальную модель):
# Устанавливаем Ollama если еще нет
curl -fsSL https://ollama.ai/install.sh | sh
# Скачиваем модель (на февраль 2026 актуальна llama3.2)
ollama pull llama3.2
# Запускаем сервер в фоне
ollama serve &
Делаем скрипт исполняемым и тестируем:
chmod +x ai_assist.py
# Простой тест
./ai_assist.py "показать текущую директорию"
# Должен предложить: pwd
# Более сложный
./ai_assist.py "найти все Python файлы измененные сегодня"
# Должен предложить: find . -name \"*.py\" -mtime 0
Почему это работает лучше гугла?
Потому что контекст. Когда вы гуглите "как удалить старые логи", вы получаете общий ответ. Когда вы говорите AI "удали логи nginx старше 30 дней из /var/log", он генерирует конкретную команду для вашего случая.
Пример из реальной жизни:
./ai_assist.py \
"создать резервную копию базы данных postgres 'myapp', \
сжать gzip, положить в /backups с именем включающим дату"
AI сгенерирует что-то вроде:
pg_dump myapp | gzip > /backups/myapp_$(date +%Y%m%d_%H%M%S).sql.gz
Попробуйте получить такой конкретный ответ из StackOverflow за 10 секунд.
Расширяем функциональность: три полезных улучшения
1. Кэширование частых запросов
Добавляем простой файловый кэш:
import hashlib
import os
CACHE_FILE = os.path.expanduser("~/.ai_assist_cache.json")
def get_cache_key(query):
return hashlib.md5(query.encode()).hexdigest()
def get_cached_command(query):
if not os.path.exists(CACHE_FILE):
return None
with open(CACHE_FILE, 'r') as f:
cache = json.load(f)
return cache.get(get_cache_key(query))
def cache_command(query, command):
cache = {}
if os.path.exists(CACHE_FILE):
with open(CACHE_FILE, 'r') as f:
cache = json.load(f)
cache[get_cache_key(query)] = command
with open(CACHE_FILE, 'w') as f:
json.dump(cache, f)
2. Поддержка разных LLM провайдеров
Модифицируем query_llama в query_ai:
def query_ai(prompt, provider="ollama"):
if provider == "ollama":
return query_ollama(prompt)
elif provider == "openai":
return query_openai(prompt)
elif provider == "claude":
return query_claude(prompt)
# ...
3. История браузера как контекст
Самое интересное улучшение. Если вы недавно гуглили что-то связанное с командой, AI может использовать это как контекст.
Для Chrome на Linux история хранится в ~/.config/google-chrome/Default/History. Читаем последние 10 запросов:
import sqlite3
import time
def get_chrome_history():
history_path = os.path.expanduser(
"~/.config/google-chrome/Default/History"
)
if not os.path.exists(history_path):
return []
# Копируем файл, потому что Chrome его блокирует
temp_path = "/tmp/chrome_history_temp"
subprocess.run(["cp", history_path, temp_path])
conn = sqlite3.connect(temp_path)
cursor = conn.cursor()
# Ищем поисковые запросы (обычно в urls.title)
cursor.execute("""
SELECT title FROM urls
WHERE title LIKE '%google%search%' OR title LIKE '%how%to%'
ORDER BY last_visit_time DESC
LIMIT 10
""")
results = [row[0] for row in cursor.fetchall()]
conn.close()
os.remove(temp_path)
return results
Теперь в промпт можно добавить: "Пользователь недавно искал в Google: [история]. Учти это при генерации команды."
Ошибки, которые все совершают (и как их избежать)
| Ошибка | Последствия | Решение |
|---|---|---|
| Без подтверждения | AI выполнит rm -rf /* если неправильно поймет | Всегда confirm_and_execute |
| Слишком креативный промпт | Модель генерирует поэмы вместо команд | Температура 0.1, четкие инструкции |
| Нет обработки ошибок API | Скрипт падает если модель недоступна | try/except вокруг HTTP запросов |
| Игнорирование shell injection | Уязвимость к атакам | Не передавайте пользовательский ввод напрямую в shell |
А что если хочется голосового ассистента?
Этот скрипт - отличная основа для более сложных систем. Добавьте голосовой ввод через AnyTTS для работы с буфером обмена или соберите полноценного локального Jarvis за вечер.
Но предупреждаю: как только вы попробуете говорить с терминалом, обратного пути не будет. Особенно если подключите Whisper для распознавания речи и Ollama для локальных моделей.
Финал: почему это изменит ваш workflow
Через неделю использования этого скрипта произойдет странное: вы перестанете бояться терминала. Вместо "ой, как там работает ffmpeg для конвертации видео" вы просто скажете: "конвертировать video.mov в mp4 с битрейтом 5M".
Это не замена знаниям. Это костыль для памяти. Как калькулятор для математика: вы все еще знаете таблицу умножения, но зачем напрягаться?
Самое ироничное: после месяца использования вы начнете запоминать команды, которые AI генерирует чаще всего. Потому что видите их каждый день. В итоге станете лучше знать терминал, используя костыль.
Парадокс? Да. Эффективно? Еще как.
P.S. Если соберетесь делать что-то более амбициозное, посмотрите предостережения о безопасности локальных AI-ассистентов. Или как построить AI-монстра со всем функционалом в одной коробке.
А теперь откройте терминал и скажите ему, что делать. Он поймет.