Слой рефлексии для Ollama: опыт-engine на Python и JSON | AiManual
AiManual Logo Ai / Manual.
18 Фев 2026 Инструмент

Как создать слой рефлексии для локальных LLM: опыт-engine для Ollama (Python, JSON)

Пошаговый гайд по созданию слоя рефлексии для локальных LLM в Ollama. Используем Python и JSON для персонализации модели.

Ваша локальная модель глупее золотой рыбки. Пора это исправить

Вы запустили Llama 3.2 через Ollama, задали вопрос, получили ответ. Повторили. И каждый раз модель начинает с чистого листа. Она не помнит, что вы только что спорили о квантовой физике. Не знает, что вы терпеть не можете маркированные списки. Забывает, что вчера она сама предложила блестящую идею для скрипта. Это не интеллект. Это продвинутый автодополнение.

Память — это только половина дела. Вторая половина — рефлексия. Способность модели анализировать свои прошлые действия, извлекать из них паттерны и применять их в будущем. Именно этот слой превращает статичный инструмент в персонального ассистента, который учится на ваших поправках, предугадывает стиль и знает ваши тараканы.

На заметку: если вы только начинаете работать с локальными моделями, сначала разберитесь с базовой установкой. Наша статья "Ollama vs другие: полный гид по запуску LLM офлайн" поможет не утонуть в деталях.

Опыт-engine: не память, а привычки

Забудьте про векторные базы данных на каждый чих. Рефлексия работает иначе. Она не хранит каждый диалог дословно. Она выжимает из них сухие, применимые правила. «Пользователь всегда исправляет термин "нейросеть" на "LLM"». «На технические вопросы нужно давать ответ с примером кода на Python». «При обсуждении философии избегать категоричных утверждений».

Эти паттерны — когнитивные отпечатки вашего взаимодействия. Experience-engine — это простой Python-пакет, который сидит между вами и Ollama API, делает три вещи:

  • Анализирует завершенный диалог: ваш промпт, ответ модели, ваши последующие правки или реакцию.
  • Извлекает правило, если диалог его содержит. (Вы поправили формат? Указали на ошибку? Похвалили за конкретный стиль?).
  • Инжектирует накопленные правила в системный промпт для следующих запросов, делая модель чуть более «вашей».

1 Голая архитектура: JSON, файл и немного логики

Вся магия умещается в одну директорию. Никаких внешних баз данных. Ollama и так кушает оперативку, зачем усложнять?

experience_engine/
├── engine.py          # Основная логика
├── patterns.json      # Хранилище паттернов
└── config.yaml        # Настройки (какая модель, порог уверенности)

Сердце системы — файл patterns.json. Это не лог чата. Это список правил, каждое из которых имеет вес (confidence). Чем чаще правило подтверждается, тем выше вес и тем ближе к началу промпта оно встает.

{
  "user_patterns": [
    {
      "id": "001",
      "condition": "user_query_contains: 'напиши код'",
      "action": "response_format: 'code_block_with_explanation'",
      "confidence": 0.9,
      "last_used": "2026-02-17T15:30:00"
    },
    {
      "id": "002",
      "condition": "user_feedback: 'слишком пространно'",
      "action": "response_style: 'concise_bullet_points'",
      "confidence": 0.75,
      "last_used": "2026-02-16T11:20:00"
    }
  ]
}
💡
Ключевое отличие от RAG: здесь мы храним не факты, а мета-инструкции. Мы не говорим модели «столица Франции — Париж». Мы говорим «когда пользователь спрашивает про столицы, отвечай кратко и добавляй население». Это уровень выше.

2 Пишем движок: 150 строк кода, которые всё меняют

Открываем engine.py. Нам нужен класс, который умеет загружать паттерны, анализировать историю одного обмена (prompt + response + feedback) и сохранять новые находки. Логика анализа — самая тонкая часть. Можно начать с простого: искать ключевые фразы в фидбеке пользователя.

import json
from datetime import datetime
from typing import Dict, List, Optional
import ollama  # Используем актуальный клиент Ollama 2026 года

class ExperienceEngine:
    def __init__(self, patterns_file: str = "patterns.json"):
        self.patterns_file = patterns_file
        self.patterns = self._load_patterns()
        # Актуальные модели на февраль 2026: llama-3.3, command-r-4.2, gemma-3
        self.preferred_model = "llama-3.3:latest"

    def _load_patterns(self) -> Dict:
        try:
            with open(self.patterns_file, 'r', encoding='utf-8') as f:
                return json.load(f)
        except FileNotFoundError:
            return {"user_patterns": [], "system_patterns": []}

    def analyze_interaction(self, user_prompt: str, model_response: str, user_feedback: str = "") -> Optional[Dict]:
        """Грубая, но работающая эвристика для извлечения правила из фидбека."""
        new_pattern = None
        feedback_lower = user_feedback.lower()

        # Пример простых правил
        if "короче" in feedback_lower or "слишком много текста" in feedback_lower:
            new_pattern = {
                "id": str(datetime.now().timestamp()),
                "condition": f"user_query_topic: '{self._guess_topic(user_prompt)}'",
                "action": "response_style: 'concise'",
                "confidence": 0.7,
                "last_used": datetime.now().isoformat()
            }
        elif "пример кода" in feedback_lower:
            new_pattern = {
                "id": str(datetime.now().timestamp()),
                "condition": "user_query_contains: 'как' or 'реализовать'",
                "action": "response_must_include: 'practical_code_example'",
                "confidence": 0.8,
                "last_used": datetime.now().isoformat()
            }
        # Можно добавить ML-модельку для классификации фидбека, но это уже опционально
        return new_pattern

    def inject_patterns_into_prompt(self, base_prompt: str) -> str:
        """Добавляем правила к промпту. Топ-3 по уверенности."""
        if not self.patterns.get("user_patterns"):
            return base_prompt
        sorted_patterns = sorted(self.patterns["user_patterns"], key=lambda x: x["confidence"], reverse=True)
        top_patterns = sorted_patterns[:3]
        if not top_patterns:
            return base_prompt
        patterns_text = "\n".join([f"- Если {p['condition']}, то {p['action']}." for p in top_patterns])
        enhanced_prompt = f"""{base_prompt}

Учти следующие предпочтения пользователя, выведенные из предыдущих взаимодействий:
{patterns_text}
"""
        return enhanced_prompt

    def save_pattern(self, pattern: Dict):
        """Сохраняем новый паттерн, избегая дубликатов."""
        # Простая проверка на схожесть
        for existing in self.patterns["user_patterns"]:
            if existing["condition"] == pattern["condition"] and existing["action"] == pattern["action"]:
                existing["confidence"] = min(1.0, existing["confidence"] + 0.1)  # Увеличиваем уверенность
                existing["last_used"] = datetime.now().isoformat()
                break
        else:
            self.patterns["user_patterns"].append(pattern)
        self._save_patterns()

    def _save_patterns(self):
        with open(self.patterns_file, 'w', encoding='utf-8') as f:
            json.dump(self.patterns, f, ensure_ascii=False, indent=2)

    def _guess_topic(self, prompt: str) -> str:
        # Упрощенная заглушка. В реальности можно использовать embedding модели.
        keywords = ["код", "наука", "философия", "история", "совет"]
        for kw in keywords:
            if kw in prompt.lower():
                return kw
        return "general"

Внимание: код выше — скелет. Логика анализа фидбека — самое слабое место. В продакшене ее нужно усиливать, как минимум, с помощью маленькой классифицирующей LLM (например, Qwen2.5-Coder-1.5B, которая отлично работает локально). Иначе engine будет генерировать мусорные правила.

3 Интеграция с Ollama: клей и скотч

Теперь нужно связать движок с запросами к модели. Если вы используете простые скрипты, достаточно обернуть вызов Ollama. Используем актуальный Python-клиент ollama версии 0.5.x+.

def chat_with_reflection(engine: ExperienceEngine, user_input: str, conversation_history: list):
    """Основной цикл чата с рефлексией."""
    # 1. Улучшаем базовый промпт (системную инструкцию) нашими паттернами
    base_system_prompt = "Ты полезный ассистент."
    enhanced_system_prompt = engine.inject_patterns_into_prompt(base_system_prompt)
    
    # 2. Формируем сообщения для Ollama API
    messages = [
        {"role": "system", "content": enhanced_system_prompt},
        *conversation_history,
        {"role": "user", "content": user_input}
    ]
    
    # 3. Отправляем запрос
    response = ollama.chat(model=engine.preferred_model, messages=messages)
    model_reply = response['message']['content']
    
    # 4. В реальном приложении здесь был бы интерфейс для получения фидбека
    # Например, кнопки "Отлично" / "Плохо" или текстовое поле для правки
    # Для примера - симулируем фидбек
    user_feedback = input("Ваш фидбек на ответ (или Enter для пропуска): ")
    
    if user_feedback.strip():
        # 5. Анализируем взаимодействие и сохраняем новое правило
        new_pattern = engine.analyze_interaction(user_input, model_reply, user_feedback)
        if new_pattern:
            engine.save_pattern(new_pattern)
            print(f"[Engine] Выучено новое правило: {new_pattern['action']}")
    
    return model_reply

Вот и весь каркас. Запускаете цикл чата, и с каждым вашим «давай покороче» или «покажи пример» модель становится чуть более послушной. Не идеально, но работает уже через 10 минут диалога.

Чем этот костыль лучше векторной памяти?

В 2025 году все ринулись встраивать ChromaDB или Qdrant в каждый локальный проект. Итог: гигабайты диска под эмбеддинги, лишняя латенция, а модель все равно не умнеет. Она просто ищет похожие куски текста в прошлом.

Опыт-engine работает на уровне выше:

Векторная память (RAG) Слой рефлексии (наш engine)
Отвечает: «В прошлый раз вы обсуждали X, вот цитата». Отвечает: «Я заметил, вы любите краткие ответы по теме X, вот суть».
Требует вычисления эмбеддингов для каждого диалога. Хранит 5 КБ правил в JSON после недели использования.
Пассивна. Ждет, пока вы спросите что-то похожее. Активна. Меняет поведение модели до того, как вы попросите.

Это не замена RAG. Это другой инструмент. RAG — для фактов. Рефлексия — для манер. Нужно и то, и другое? Свяжите их. Например, используйте техники управления контекстом из RLM, чтобы чистить историю, а наш engine пусть управляет стилем.

Кому стоит возиться с этим слоем?

Если вы просто хотите спросить у модели факт раз в неделю — не нужно. Если вы каждый день работаете с локальной LLM как с коллегой, вот ваша аудитория:

  • Разработчики, которые устали каждый раз объяснять модели стиль кода своей команды.
  • Исследователи, ведущие долгие диалоги по одной теме и не желающие повторять «используй термины из статьи».
  • Писатели, которые хотят, чтобы модель запомнила тон и структуру их текстов.
  • Параноики, которые прочитали нашу статью про утечки в локальных LLM и хотят, чтобы весь опыт оставался в их JSON-файле, а не улетал в облака.

Главный прогноз на 2026-2027 год: встроенный механизм рефлексии станет стандартной фичей для продвинутых локальных менеджеров моделей вроде Ollama или LM Studio. Пока его нет — собирайте свой. Начните с этого скрипта, допишите логику анализа под свои задачи. Через месяц ваша модель будет знать вас лучше, чем некоторые родственники.

Неочевидный совет: Не храните паттерны вечно. Напишите cron-задачу, которая раз в месяц понижает confidence у правил, которые давно не использовались, и удаляет те, у кого вес упал ниже 0.2. Модель, как и человек, должна забывать устаревшие привычки. Иначе она закостенеет.