#!/usr/bin/env python3 """ Внешний Гиппокамп Montana ========================= Цифровая эмуляция биологического механизма памяти. Переживает смерть носителя. Компоненты: - Детектор новизны (is_raw_thought) - Pattern separation (save_to_stream) - Просмотр потока (view_stream) - Статистика памяти (memory_stats) Использование: python hippocampus.py --view # Просмотр последних мыслей python hippocampus.py --stats # Статистика памяти python hippocampus.py --test # Запуск тестов python hippocampus.py --disney # Анализ по стратегии Диснея """ import json import os from dataclasses import dataclass from datetime import datetime, timedelta from pathlib import Path from typing import Optional import argparse @dataclass class Thought: """Единица памяти — координата в 4D пространстве""" user_id: int username: str timestamp: str thought: str lang: str # Дополнительные якоря (опционально) location: Optional[str] = None # GPS координаты music_track: Optional[str] = None # Музыка момента image_url: Optional[str] = None # Визуальный якорь class ExternalHippocampus: """ Внешний Гиппокамп Montana Эмулирует биологический механизм памяти: - Pattern separation: каждая мысль = отдельная координата - Детектор новизны: фильтрует мысли от вопросов/команд - Консолидация: синхронизация каждые 12 сек Критическое отличие: переживает смерть носителя """ def __init__(self, data_dir: Optional[str] = None): self.data_dir = Path(data_dir) if data_dir else Path(__file__).parent / "data" self.data_dir.mkdir(exist_ok=True) self.stream_file = self.data_dir / "stream.jsonl" # Триггеры для базы знаний self.knowledge_triggers = ['гиппокамп', 'память', 'поток', 'паттерн', 'днк'] # === ДЕТЕКТОР НОВИЗНЫ === def is_raw_thought(self, text: str) -> bool: """ Детектор новизны — определяет, является ли текст сырой мыслью Эмулирует работу биологического гиппокампа: - Сравнивает с известными паттернами (вопросы, команды) - Возвращает True если это НОВАЯ мысль Критерии: - Длина < 500 символов - Не вопрос (без ?) - Не команда (покажи/расскажи/помоги) """ text = text.strip() # Слишком длинное — не мысль if len(text) > 500: return False # Вопрос — не мысль if text.endswith("?"): return False # Команды — не мысли command_patterns = [ "покажи", "расскажи", "помоги", "объясни", "найди", "открой", "запусти", "сделай", "/start", "/help", "/level", "/cognitive" ] text_lower = text.lower() for pattern in command_patterns: if text_lower.startswith(pattern): return False # Слишком короткое — скорее всего не мысль if len(text) < 5: return False # Это сырая мысль return True # === PATTERN SEPARATION === def save_to_stream( self, user_id: int, username: str, thought: str, lang: str = "ru", location: Optional[str] = None, music_track: Optional[str] = None ) -> Thought: """ Pattern Separation — сохраняет мысль как уникальную координату Эмулирует биологический паттерн: - Каждая новая мысль кодируется отдельно - Append-only (необратимость времени) - Временная метка UTC """ entry = Thought( user_id=user_id, username=username, timestamp=datetime.utcnow().isoformat() + "Z", thought=thought, lang=lang, location=location, music_track=music_track ) # Append-only запись with open(self.stream_file, "a", encoding="utf-8") as f: data = { "user_id": entry.user_id, "username": entry.username, "timestamp": entry.timestamp, "thought": entry.thought, "lang": entry.lang } if entry.location: data["location"] = entry.location if entry.music_track: data["music_track"] = entry.music_track f.write(json.dumps(data, ensure_ascii=False) + "\n") return entry # === ПРОСМОТР ПОТОКА === def view_stream(self, limit: int = 10, user_id: Optional[int] = None) -> list[Thought]: """ Просмотр потока мыслей Args: limit: количество последних мыслей user_id: фильтр по пользователю (опционально) """ if not self.stream_file.exists(): return [] thoughts = [] with open(self.stream_file, "r", encoding="utf-8") as f: for line in f: if line.strip(): data = json.loads(line) if user_id is None or data.get("user_id") == user_id: thoughts.append(Thought( user_id=data.get("user_id", 0), username=data.get("username", "unknown"), timestamp=data.get("timestamp", ""), thought=data.get("thought", ""), lang=data.get("lang", "ru"), location=data.get("location"), music_track=data.get("music_track") )) # Последние N мыслей return thoughts[-limit:] # === СТАТИСТИКА === def memory_stats(self, user_id: Optional[int] = None) -> dict: """ Статистика памяти Возвращает: - total_thoughts: общее количество мыслей - unique_users: уникальные пользователи - languages: распределение по языкам - density: плотность кодирования (мыслей в день) """ thoughts = self.view_stream(limit=10000, user_id=user_id) if not thoughts: return { "total_thoughts": 0, "unique_users": 0, "languages": {}, "density": 0 } # Базовая статистика total = len(thoughts) users = set(t.user_id for t in thoughts) langs = {} for t in thoughts: langs[t.lang] = langs.get(t.lang, 0) + 1 # Плотность кодирования if total >= 2: first = datetime.fromisoformat(thoughts[0].timestamp.replace("Z", "")) last = datetime.fromisoformat(thoughts[-1].timestamp.replace("Z", "")) days = max(1, (last - first).days) density = round(total / days, 2) else: density = total return { "total_thoughts": total, "unique_users": len(users), "languages": langs, "density": density, "first_thought": thoughts[0].timestamp if thoughts else None, "last_thought": thoughts[-1].timestamp if thoughts else None } # === ПОИСК === def search(self, query: str, limit: int = 10) -> list[Thought]: """ Простой поиск по мыслям Для семантического поиска используйте RAG систему. """ thoughts = self.view_stream(limit=10000) query_lower = query.lower() results = [] for thought in thoughts: if query_lower in thought.thought.lower(): results.append(thought) return results[:limit] # === ТЕСТЫ === def run_tests(self) -> dict: """ Запуск тестов детектора новизны """ test_cases = [ # (текст, ожидаемый результат, описание) ("Время не движется, я движусь", True, "Сырая мысль"), ("Маска тяжелее лица", True, "Сырая мысль"), ("Я создаю свою игру", True, "Сырая мысль"), ("Что такое ACP?", False, "Вопрос"), ("Как работает Montana?", False, "Вопрос"), ("Покажи документацию", False, "Команда"), ("Расскажи про гиппокамп", False, "Команда"), ("/start", False, "Telegram команда"), ("/help", False, "Telegram команда"), ("Ок", False, "Слишком короткое"), ("Да", False, "Слишком короткое"), ("A" * 600, False, "Слишком длинное"), ] passed = 0 failed = 0 results = [] for text, expected, description in test_cases: actual = self.is_raw_thought(text) status = "✓" if actual == expected else "✗" if actual == expected: passed += 1 else: failed += 1 results.append({ "text": text[:50] + "..." if len(text) > 50 else text, "expected": expected, "actual": actual, "status": status, "description": description }) return { "total": len(test_cases), "passed": passed, "failed": failed, "results": results } # === ЭКСПОРТ === def export_markdown(self, user_id: Optional[int] = None) -> str: """ Экспорт памяти в Markdown Для потомков. """ thoughts = self.view_stream(limit=10000, user_id=user_id) stats = self.memory_stats(user_id) lines = [ f"# Память Montana", f"", f"**Всего мыслей:** {stats['total_thoughts']}", f"**Плотность кодирования:** {stats['density']} мыслей/день", f"", f"---", f"", ] current_date = None for thought in thoughts: # Группировка по дням date = thought.timestamp[:10] if date != current_date: current_date = date lines.append(f"## {date}") lines.append("") time = thought.timestamp[11:16] lines.append(f"**[{time}]** {thought.thought}") lines.append("") return "\n".join(lines) def print_thoughts(thoughts: list[Thought]): """Красивый вывод мыслей""" for thought in thoughts: time = thought.timestamp[:16].replace("T", " ") print(f"[{time}] @{thought.username} ({thought.lang})") print(f" {thought.thought}") print() def print_stats(stats: dict): """Красивый вывод статистики""" print(f"Ɉ Статистика памяти Montana") print() print(f" Всего мыслей: {stats['total_thoughts']}") print(f" Пользователей: {stats['unique_users']}") print(f" Плотность: {stats['density']} мыслей/день") print() print(f" Языки:") for lang, count in stats.get('languages', {}).items(): print(f" {lang}: {count}") def print_tests(results: dict): """Красивый вывод результатов тестов""" print(f"Ɉ Тест детектора новизны") print() for r in results['results']: expected = "МЫСЛЬ" if r['expected'] else "НЕ МЫСЛЬ" actual = "МЫСЛЬ" if r['actual'] else "НЕ МЫСЛЬ" status = r['status'] print(f"{status} [{r['description']}]") print(f" Текст: \"{r['text']}\"") print(f" Ожидалось: {expected}, Получено: {actual}") print() print(f"Итого: {results['passed']} из {results['total']} тестов пройдено") if results['failed'] == 0: print("✅ Все тесты пройдены") else: print(f"❌ {results['failed']} тестов провалено") def main(): """CLI интерфейс""" parser = argparse.ArgumentParser( description="Внешний Гиппокамп Montana", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Примеры: python hippocampus.py --view # Последние 10 мыслей python hippocampus.py --view 50 # Последние 50 мыслей python hippocampus.py --stats # Статистика памяти python hippocampus.py --test # Тесты детектора python hippocampus.py --search "маска" # Поиск по мыслям python hippocampus.py --export # Экспорт в Markdown """ ) parser.add_argument( '--view', '-v', type=int, nargs='?', const=10, help='Просмотр последних N мыслей (по умолчанию 10)' ) parser.add_argument( '--stats', '-s', action='store_true', help='Статистика памяти' ) parser.add_argument( '--test', '-t', action='store_true', help='Запуск тестов детектора новизны' ) parser.add_argument( '--search', '-q', type=str, help='Поиск по мыслям' ) parser.add_argument( '--export', '-e', action='store_true', help='Экспорт памяти в Markdown' ) parser.add_argument( '--user', '-u', type=int, help='Фильтр по user_id' ) parser.add_argument( '--data-dir', '-d', type=str, help='Путь к папке данных' ) args = parser.parse_args() # Создаём экземпляр гиппокампа hippocampus = ExternalHippocampus(data_dir=args.data_dir) # Выполняем команду if args.view is not None: thoughts = hippocampus.view_stream(limit=args.view, user_id=args.user) print(f"Ɉ Поток мыслей Montana ({len(thoughts)} из {args.view})") print() print_thoughts(thoughts) elif args.stats: stats = hippocampus.memory_stats(user_id=args.user) print_stats(stats) elif args.test: results = hippocampus.run_tests() print_tests(results) elif args.search: thoughts = hippocampus.search(args.search) print(f"Ɉ Поиск: \"{args.search}\" ({len(thoughts)} результатов)") print() print_thoughts(thoughts) elif args.export: markdown = hippocampus.export_markdown(user_id=args.user) print(markdown) else: parser.print_help() if __name__ == "__main__": main()