AI и автоматизация

Система памяти для Gemini CLI с использованием Git hooks и локальной базы данных

Система памяти для Gemini CLI с использованием Git hooks и локальной базы данных

Предлагаю создать систему памяти для Gemini CLI с использованием Git hooks и локальной базы данных. Вот полное решение:

Структура системы памяти

1. Основной скрипт установки

#!/bin/bash
# install_memory_system.sh

# Создание директории для системы памяти
mkdir -p ~/.gemini-memory
mkdir -p ~/.gemini-memory/projects
mkdir -p ~/.gemini-memory/templates

# Установка зависимостей
echo "Установка зависимостей..."
sudo apt-get update
sudo apt-get install -y python3-pip sqlite3 jq
pip3 install --user gitpython watchdog pyyaml

# Копирование скриптов
cp gemini_memory.py ~/.gemini-memory/
cp memory_hook.sh ~/.gemini-memory/
chmod +x ~/.gemini-memory/*.sh
chmod +x ~/.gemini-memory/*.py

echo "Система памяти установлена!"

2. Основной Python модуль системы памяти

#!/usr/bin/env python3
# ~/.gemini-memory/gemini_memory.py

import os
import json
import sqlite3
import hashlib
import datetime
from pathlib import Path
import git
import yaml
from typing import Dict, List, Optional

class GeminiMemory:
def __init__(self, project_path: str = None):
self.base_dir = Path.home() / '.gemini-memory'
self.base_dir.mkdir(exist_ok=True)

if project_path:
self.project_path = Path(project_path).resolve()
self.project_id = self._get_project_id()
self.db_path = self.base_dir / 'projects' / f'{self.project_id}.db'
self._init_database()

def _get_project_id(self) -> str:
"""Генерирует уникальный ID для проекта"""
return hashlib.md5(str(self.project_path).encode()).hexdigest()[:12]

def _init_database(self):
"""Инициализирует базу данных для проекта"""
self.db_path.parent.mkdir(exist_ok=True)
conn = sqlite3.connect(str(self.db_path))
cursor = conn.cursor()

# Таблица изменений кода
cursor.execute('''
CREATE TABLE IF NOT EXISTS code_changes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
file_path TEXT,
change_type TEXT,
diff TEXT,
commit_hash TEXT,
branch TEXT,
summary TEXT
)
''')

# Таблица взаимодействий с AI
cursor.execute('''
CREATE TABLE IF NOT EXISTS ai_interactions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
prompt TEXT,
response TEXT,
context TEXT,
tags TEXT,
token_count INTEGER
)
''')

# Таблица задач и решений
cursor.execute('''
CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
task_description TEXT,
status TEXT,
solution TEXT,
files_affected TEXT,
complexity INTEGER
)
''')

# Таблица архитектурных решений
cursor.execute('''
CREATE TABLE IF NOT EXISTS architecture_decisions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
decision TEXT,
reasoning TEXT,
alternatives TEXT,
impact TEXT
)
''')

conn.commit()
conn.close()

def record_change(self, file_path: str, change_type: str, diff: str, summary: str = None):
"""Записывает изменение в коде"""
conn = sqlite3.connect(str(self.db_path))
cursor = conn.cursor()

try:
repo = git.Repo(self.project_path)
commit_hash = repo.head.commit.hexsha if repo.head.is_valid() else None
branch = repo.active_branch.name if not repo.head.is_detached else 'detached'
except:
commit_hash = None
branch = 'unknown'

cursor.execute('''
INSERT INTO code_changes (file_path, change_type, diff, commit_hash, branch, summary)
VALUES (?, ?, ?, ?, ?, ?)
''', (file_path, change_type, diff, commit_hash, branch, summary))

conn.commit()
conn.close()

def record_ai_interaction(self, prompt: str, response: str, context: str = None, tags: List[str] = None):
"""Записывает взаимодействие с AI"""
conn = sqlite3.connect(str(self.db_path))
cursor = conn.cursor()

tags_str = json.dumps(tags) if tags else '[]'
token_count = len(prompt.split()) + len(response.split())

cursor.execute('''
INSERT INTO ai_interactions (prompt, response, context, tags, token_count)
VALUES (?, ?, ?, ?, ?)
''', (prompt, response, context, tags_str, token_count))

conn.commit()
conn.close()

def get_project_context(self, limit: int = 10) -> Dict:
"""Получает контекст проекта для AI"""
conn = sqlite3.connect(str(self.db_path))
cursor = conn.cursor()

# Последние изменения
cursor.execute('''
SELECT * FROM code_changes
ORDER BY timestamp DESC
LIMIT ?
''', (limit,))
recent_changes = cursor.fetchall()

# Последние взаимодействия
cursor.execute('''
SELECT * FROM ai_interactions
ORDER BY timestamp DESC
LIMIT ?
''', (limit,))
recent_interactions = cursor.fetchall()

# Активные задачи
cursor.execute('''
SELECT * FROM tasks
WHERE status != 'completed'
ORDER BY timestamp DESC
''')
active_tasks = cursor.fetchall()

# Архитектурные решения
cursor.execute('''
SELECT * FROM architecture_decisions
ORDER BY timestamp DESC
LIMIT 5
''')
architecture = cursor.fetchall()

conn.close()

return {
'project_path': str(self.project_path),
'recent_changes': self._format_records(recent_changes, 'code_changes'),
'recent_interactions': self._format_records(recent_interactions, 'ai_interactions'),
'active_tasks': self._format_records(active_tasks, 'tasks'),
'architecture': self._format_records(architecture, 'architecture_decisions')
}

def _format_records(self, records: List, table_name: str) -> List[Dict]:
"""Форматирует записи из БД"""
formatted = []
for record in records:
if table_name == 'code_changes':
formatted.append({
'timestamp': record[1],
'file': record[2],
'type': record[3],
'diff': record[4][:200] + '...' if len(record[4]) > 200 else record[4],
'commit': record[5],
'branch': record[6],
'summary': record[7]
})
elif table_name == 'ai_interactions':
formatted.append({
'timestamp': record[1],
'prompt': record[2][:100] + '...' if len(record[2]) > 100 else record[2],
'response': record[3][:200] + '...' if len(record[3]) > 200 else record[3],
'context': record[4],
'tags': json.loads(record[5]) if record[5] else []
})
elif table_name == 'tasks':
formatted.append({
'timestamp': record[1],
'description': record[2],
'status': record[3],
'solution': record[4],
'files': record[5]
})
elif table_name == 'architecture_decisions':
formatted.append({
'timestamp': record[1],
'decision': record[2],
'reasoning': record[3]
})
return formatted

def generate_summary(self) -> str:
"""Генерирует саммари проекта для AI"""
context = self.get_project_context()

summary = f"""
# Контекст проекта: {context['project_path']}

## Последние изменения в коде:
"""
for change in context['recent_changes'][:5]:
summary += f"- [{change['timestamp']}] {change['file']}: {change['type']}\n"
if change['summary']:
summary += f"  Описание: {change['summary']}\n"

summary += "\n## Активные задачи:\n"
for task in context['active_tasks'][:3]:
summary += f"- [{task['status']}] {task['description']}\n"

summary += "\n## Архитектурные решения:\n"
for decision in context['architecture'][:3]:
summary += f"- {decision['decision']}\n"
summary += f"  Обоснование: {decision['reasoning']}\n"

return summary

3. Git Hook для отслеживания изменений

#!/bin/bash
# ~/.gemini-memory/git_hook.sh

PROJECT_PATH=$(git rev-parse --show-toplevel)
MEMORY_SCRIPT=~/.gemini-memory/gemini_memory.py

# Получаем информацию о коммите
COMMIT_MSG=$(git log -1 --pretty=%B)
CHANGED_FILES=$(git diff-tree --no-commit-id --name-only -r HEAD)

# Записываем изменения в память
python3 -c "
import sys
sys.path.insert(0, '$HOME/.gemini-memory')
from gemini_memory import GeminiMemory

memory = GeminiMemory('$PROJECT_PATH')
commit_msg = '''$COMMIT_MSG'''
files = '''$CHANGED_FILES'''.split('\n')

for file in files:
if file:
diff = '$(git diff HEAD~1 HEAD -- {} 2>/dev/null | head -100)'
memory.record_change(file, 'commit', diff, commit_msg)
"

4. CLI интерфейс для Gemini

#!/usr/bin/env python3
# ~/.gemini-memory/gemini_cli_wrapper.py

import os
import sys
import json
import subprocess
from pathlib import Path
from gemini_memory import GeminiMemory
import argparse

class GeminiCLIWrapper:
def __init__(self):
self.current_project = self._detect_project()
if self.current_project:
self.memory = GeminiMemory(self.current_project)

def _detect_project(self) -> Optional[str]:
"""Определяет текущий проект по git репозиторию"""
try:
result = subprocess.run(
['git', 'rev-parse', '--show-toplevel'],
capture_output=True, text=True
)
if result.returncode == 0:
return result.stdout.strip()
except:
pass
return None

def enhance_prompt(self, original_prompt: str) -> str:
"""Добавляет контекст к промпту"""
if not self.memory:
return original_prompt

context = self.memory.generate_summary()
enhanced = f"""
{context}

---
Текущий запрос: {original_prompt}

Учитывай контекст проекта и предыдущие решения при ответе.
"""
return enhanced

def run_gemini(self, prompt: str, save_interaction: bool = True):
"""Запускает Gemini с улучшенным промптом"""
enhanced_prompt = self.enhance_prompt(prompt)

# Здесь вызов Gemini CLI (адаптируйте под вашу версию)
result = subprocess.run(
['gemini', 'ask', enhanced_prompt],
capture_output=True, text=True
)

response = result.stdout

if save_interaction and self.memory:
self.memory.record_ai_interaction(
prompt=prompt,
response=response,
context=self.current_project
)

return response

def init_project(self):
"""Инициализирует память для проекта"""
if not self.current_project:
print("Не найден git репозиторий")
return

# Установка git hooks
hooks_dir = Path(self.current_project) / '.git' / 'hooks'
post_commit = hooks_dir / 'post-commit'

with open(post_commit, 'w') as f:
f.write('#!/bin/bash\n')
f.write('~/.gemini-memory/git_hook.sh\n')

os.chmod(post_commit, 0o755)
print(f"Память инициализирована для проекта: {self.current_project}")

def main():
parser = argparse.ArgumentParser(description='Gemini CLI с системой памяти')
parser.add_argument('command', choices=['init', 'ask', 'context', 'summary'])
parser.add_argument('prompt', nargs='?', help='Промпт для Gemini')
parser.add_argument('--no-save', action='store_true', help='Не сохранять взаимодействие')

args = parser.parse_args()
wrapper = GeminiCLIWrapper()

if args.command == 'init':
wrapper.init_project()
elif args.command == 'ask':
if not args.prompt:
print("Необходим промпт для команды ask")
sys.exit(1)
response = wrapper.run_gemini(args.prompt, not args.no_save)
print(response)
elif args.command == 'context':
if wrapper.memory:
context = wrapper.memory.get_project_context()
print(json.dumps(context, indent=2, ensure_ascii=False))
elif args.command == 'summary':
if wrapper.memory:
print(wrapper.memory.generate_summary())

if __name__ == '__main__':
main()

5. Bash алиас для удобства

# Добавить в ~/.bashrc или ~/.zshrc

# Алиас для Gemini с памятью
alias gmem='python3 ~/.gemini-memory/gemini_cli_wrapper.py'

# Функция для быстрого запроса
gask() {
python3 ~/.gemini-memory/gemini_cli_wrapper.py ask "$*"
}

# Инициализация памяти в текущем проекте
ginit() {
python3 ~/.gemini-memory/gemini_cli_wrapper.py init
}

# Показать контекст проекта
gcontext() {
python3 ~/.gemini-memory/gemini_cli_wrapper.py context
}

6. Конфигурационный файл

# ~/.gemini-memory/config.yaml

memory:
max_context_items: 20
max_token_count: 4000
auto_summarize: true

projects:
default_tags:
- architecture
- bugfix
- feature
- refactoring

storage:
cleanup_days: 90
backup_enabled: true
backup_path: ~/.gemini-memory/backups

gemini:
model: gemini-pro
temperature: 0.7
max_tokens: 2048

Установка и использование

  • Установка системы:
# Клонируйте репозиторий или создайте файлы
chmod +x install_memory_system.sh
./install_memory_system.sh
  • Инициализация в проекте:
cd /path/to/your/project
ginit
  • Использование:

# Обычный запрос с контекстом

gask "Как оптимизировать функцию обработки данных?"

# Просмотр контекста

gcontext

# Просмотр саммари

gmem summary

Особенности системы:

  • Автоматическое отслеживание: Git hooks автоматически записывают все изменения

  • Изоляция проектов: Каждый проект имеет свою БД

  • Контекстуализация: Каждый запрос дополняется историей проекта

  • Легкость: SQLite не требует сервера БД

  • Расширяемость: Легко добавить новые типы данных

Эта система обеспечит полную память для Gemini CLI при работе с разными проектами.

Инструкция по установке системы памяти для Gemini CLI

Что это даст вам:

  • Gemini будет помнить всю историю работы над каждым проектом

  • Не нужно будет повторять контекст при каждом вопросе

  • Автоматическое отслеживание всех изменений в коде

  • Отдельная память для каждого проекта

Предварительные требования:

✅ У вас установлена Zorin OS (уже есть) ✅ Установлен GitHub (уже есть) ✅ Установлен Gemini CLI (должен быть установлен) ✅ Есть хотя бы один проект с Git репозиторием


📋 Пошаговая установка

Шаг 1: Создание структуры папок

  • Откройте терминал (Ctrl+Alt+T)

  • Создайте основную папку для системы памяти:

mkdir -p ~/.gemini-memory
mkdir -p ~/.gemini-memory/projects
mkdir -p ~/.gemini-memory/templates

Шаг 2: Установка необходимых программ

В терминале выполните по очереди:

sudo apt-get update
sudo apt-get install -y python3-pip sqlite3 jq
pip3 install --user gitpython watchdog pyyaml

Что это установит:

  • python3-pip - менеджер пакетов Python

  • sqlite3 - база данных для хранения памяти

  • jq - инструмент для работы с JSON

  • Библиотеки Python для работы с Git и файлами

Шаг 3: Создание файлов системы

Вам нужно создать 5 файлов. Для каждого:

  • Создайте файл установки install_memory_system.sh

  • Откройте текстовый редактор: nano ~/.gemini-memory/install_memory_system.sh

  • Скопируйте код из раздела 1 предыдущего ответа

  • Сохраните: Ctrl+O, Enter, Ctrl+X

  • Создайте основной модуль gemini_memory.py

  • Команда: nano ~/.gemini-memory/gemini_memory.py

  • Скопируйте код из раздела 2 (Python модуль системы памяти)

  • Сохраните: Ctrl+O, Enter, Ctrl+X

  • Создайте Git hook git_hook.sh

  • Команда: nano ~/.gemini-memory/git_hook.sh

  • Скопируйте код из раздела 3

  • Сохраните: Ctrl+O, Enter, Ctrl+X

  • Создайте CLI обертку gemini_cli_wrapper.py

  • Команда: nano ~/.gemini-memory/gemini_cli_wrapper.py

  • Скопируйте код из раздела 4

  • Сохраните: Ctrl+O, Enter, Ctrl+X

  • Создайте конфигурацию config.yaml

  • Команда: nano ~/.gemini-memory/config.yaml

  • Скопируйте код из раздела 6

  • Сохраните: Ctrl+O, Enter, Ctrl+X

Шаг 4: Делаем файлы исполняемыми

В терминале выполните:

chmod +x ~/.gemini-memory/*.sh
chmod +x ~/.gemini-memory/*.py

Шаг 5: Настройка быстрых команд

  • Откройте файл настроек bash:
nano ~/.bashrc
  • В конец файла добавьте текст из раздела 5 (Bash алиасы)

  • Сохраните файл: Ctrl+O, Enter, Ctrl+X

  • Активируйте новые команды:

source ~/.bashrc

🚀 Начало работы

Инициализация в проекте

  • Перейдите в папку вашего проекта:
cd /путь/к/вашему/проекту
  • Инициализируйте систему памяти:
ginit

Вы увидите сообщение: “Память инициализирована для проекта”

Использование

Задать вопрос с учетом контекста проекта:

gask "Ваш вопрос о коде"

Посмотреть, что помнит система о проекте:

gcontext

Получить краткую сводку по проекту:

gmem summary

🔧 Проверка установки

  • Проверьте, что все файлы созданы:
ls -la ~/.gemini-memory/

Должны быть видны все .py и .sh файлы

  • Проверьте, что команды работают:
gmem --help

Должна появиться справка

  • Тестовый запрос (в папке с проектом):
gask "тестовый запрос"

❗ Частые проблемы и решения

Проблема: Команда gask не найдена

  • Решение: Выполните source ~/.bashrc или откройте новый терминал

Проблема: Ошибка “No module named ‘git’”

  • Решение: Установите библиотеку: pip3 install --user gitpython

Проблема: Ошибка при инициализации проекта

  • Решение: Убедитесь, что вы находитесь в папке с Git репозиторием (git status должен работать)

Проблема: Gemini CLI не отвечает

  • Решение: Проверьте, что Gemini CLI установлен и работает сам по себе

📝 Важные замечания

  • Для каждого проекта нужно выполнить ginit один раз

  • История сохраняется в папке ~/.gemini-memory/projects/

  • Резервные копии рекомендуется делать периодически:

cp -r ~/.gemini-memory/projects ~/.gemini-memory/backup_$(date +%Y%m%d)
  • Настройки Gemini CLI могут потребовать корректировки в файле gemini_cli_wrapper.py (строка с вызовом gemini)

🎯 Что дальше?

После успешной установки:

  • Инициализируйте систему во всех ваших проектах

  • Используйте gask вместо прямого вызова Gemini

  • Система будет автоматически накапливать знания о каждом проекте

  • При возврате к старому проекту, вся история будет доступна

Если возникнут проблемы, проверьте, что все файлы скопированы правильно и содержат весь код из предыдущего ответа.


Хотите внедрить ИИ-агентов или автоматизировать бизнес-процессы? Мы создаем интеллектуальных помощников, внедряем RAG-системы и настраиваем интеграции (n8n, Make, API). Свяжитесь с нами, чтобы обсудить ваш проект и рассчитать окупаемость.

🚀 Нужна помощь с сайтом на 1С-Битрикс или Аспро?

Я работаю удалённо по всей России и СНГ. Узнайте цены и условия для вашего города:

Все регионы →